diff --git a/.github/release-canary-v3 b/.github/release-canary-v3 index 56a6051..d00491f 100644 --- a/.github/release-canary-v3 +++ b/.github/release-canary-v3 @@ -1 +1 @@ -1 \ No newline at end of file +1 diff --git a/.github/release-nest-v3 b/.github/release-nest-v3 index b8626c4..d00491f 100644 --- a/.github/release-nest-v3 +++ b/.github/release-nest-v3 @@ -1 +1 @@ -4 +1 diff --git a/gnome-shell-extension-just-perfection/debian/changelog b/gnome-shell-extension-arc-menu/debian/changelog similarity index 58% rename from gnome-shell-extension-just-perfection/debian/changelog rename to gnome-shell-extension-arc-menu/debian/changelog index 6dd8ce5..c5793b4 100644 --- a/gnome-shell-extension-just-perfection/debian/changelog +++ b/gnome-shell-extension-arc-menu/debian/changelog @@ -1,4 +1,4 @@ -gnome-shell-extension-just-perfection (46.0-101pika1) pika; urgency=medium +gnome-shell-extension-arc-menu (61-101pika1) pika; urgency=medium * Initial Creation diff --git a/gnome-shell-extension-just-perfection/debian/compat b/gnome-shell-extension-arc-menu/debian/compat similarity index 100% rename from gnome-shell-extension-just-perfection/debian/compat rename to gnome-shell-extension-arc-menu/debian/compat diff --git a/gnome-shell-extension-arc-menu/debian/control b/gnome-shell-extension-arc-menu/debian/control new file mode 100644 index 0000000..49ca978 --- /dev/null +++ b/gnome-shell-extension-arc-menu/debian/control @@ -0,0 +1,32 @@ +Source: gnome-shell-extension-arc-menu +Section: gnome +Priority: optional +Maintainer: Marco Trevisan +Build-Depends: debhelper (>= 10), + eslint , + libglib2.0-bin, + node-chalk , + node-js-yaml , + node-strip-ansi , + libgettextpo-dev, + gettext, + sassc +Standards-Version: 4.1.1 + +Package: gnome-shell-extension-arc-menu +Architecture: all +Depends: + gnome-shell (>= 45~), + gnome-shell (<< 48~), + gir1.2-gmenu-3.0, + ${misc:Depends}, + ${shlibs:Depends}, +Description: shell extension designed to replace the standard menu found in GNOME + Arc Menu is a GNOME shell extension designed to replace the standard menu + found in GNOME 3 this application menu extension has some added benefits + over the standard menu found in GNOME 3, these include the long awaited + search functionality as well as quick access to files on your system and + also the current logged in user along with quick access to the software + centre and system settings and other features which can be accessed from the + settings menu. + diff --git a/gnome-shell-extension-arc-menu/debian/copyright b/gnome-shell-extension-arc-menu/debian/copyright new file mode 100644 index 0000000..ad03c37 --- /dev/null +++ b/gnome-shell-extension-arc-menu/debian/copyright @@ -0,0 +1,33 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ArcMenu +Source: https://gitlab.com/arcmenu/ArcMenu + +Files: * +Copyright: 2019-2021 Andrew Zaech + 2017-2020 LinxGem33 (https://gitlab.com/LinxGem33) + 2017 Alexander Rüedlinger + 2016 Zorin OS + 2015 Giovanni Campagna +License: GPL-2+ + +Files: debian/* +Copyright: 2019-2021 Jonathan Carter +License: GPL-2+ + +License: GPL-2+ + This package 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 package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". + diff --git a/gnome-shell-extension-just-perfection/debian/gnome-shell-extension-just-perfection.install b/gnome-shell-extension-arc-menu/debian/gnome-shell-extension-arc-menu.install similarity index 100% rename from gnome-shell-extension-just-perfection/debian/gnome-shell-extension-just-perfection.install rename to gnome-shell-extension-arc-menu/debian/gnome-shell-extension-arc-menu.install diff --git a/gnome-shell-extension-just-perfection/debian/postinst b/gnome-shell-extension-arc-menu/debian/postinst similarity index 100% rename from gnome-shell-extension-just-perfection/debian/postinst rename to gnome-shell-extension-arc-menu/debian/postinst diff --git a/gnome-shell-extension-just-perfection/debian/prerm b/gnome-shell-extension-arc-menu/debian/prerm similarity index 100% rename from gnome-shell-extension-just-perfection/debian/prerm rename to gnome-shell-extension-arc-menu/debian/prerm diff --git a/gnome-shell-extension-just-perfection/debian/rules b/gnome-shell-extension-arc-menu/debian/rules similarity index 100% rename from gnome-shell-extension-just-perfection/debian/rules rename to gnome-shell-extension-arc-menu/debian/rules diff --git a/gnome-shell-extension-just-perfection/debian/source/format b/gnome-shell-extension-arc-menu/debian/source/format similarity index 100% rename from gnome-shell-extension-just-perfection/debian/source/format rename to gnome-shell-extension-arc-menu/debian/source/format diff --git a/gnome-shell-extension-arc-menu/usr/share/glib-2.0/schemas/org.gnome.shell.extensions.arcmenu.gschema.xml b/gnome-shell-extension-arc-menu/usr/share/glib-2.0/schemas/org.gnome.shell.extensions.arcmenu.gschema.xml new file mode 100644 index 0000000..4311e18 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/glib-2.0/schemas/org.gnome.shell.extensions.arcmenu.gschema.xml @@ -0,0 +1,809 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [] + Pinned Apps in the folder + + + + + 50 + Menu position alignment + + + 'Default' + menu layout + + + false + Override ArcMenu theme + + + "rgba(48,48,49,0.98)" + ArcMenu background color. Active when 'override-menu-theme' enabled. + + + "rgb(223,223,223)" + ArcMenu foreground color. Active when 'override-menu-theme' enabled. + + + "rgb(60,60,60)" + ArcMenu border color. Active when 'override-menu-theme' enabled. + + + 1 + ArcMenu border width. Active when 'override-menu-theme' enabled. + + + 14 + ArcMenu border radius. Active when 'override-menu-theme' enabled. + + + 11 + ArcMenu font size. Active when 'override-menu-theme' enabled. + + + "rgba(255,255,255,0.1)" + ArcMenu separator color. Active when 'override-menu-theme' enabled. + + + "rgb(21,83,158)" + ArcMenu menu item hover bg color. Active when 'override-menu-theme' enabled. + + + "rgb(255,255,255)" + ArcMenu menu item hover fg color. Active when 'override-menu-theme' enabled. + + + "rgb(25,98,163)" + ArcMenu menu item active bg color. Active when 'override-menu-theme' enabled. + + + "rgb(255,255,255)" + ArcMenu menu item active fg color. Active when 'override-menu-theme' enabled. + + + (false, 6) + ArcMenu distance from panel and screen edge. + + + (false, "rgb(242,242,242)") + ArcMenu menu button fg color. + + + (false, "rgba(242,242,242,0.2)") + ArcMenu menu button bg color. + + + (false, 20) + ArcMenu menu button border radius. + + + (false, 3) + ArcMenu menu button border radius. + + + (false, "transparent") + ArcMenu menu button hover bg color. + + + (false, "rgba(242,242,242,0.15)") + ArcMenu menu button hover bg color. + + + (false, "rgb(242,242,242)") + ArcMenu menu button hover fg color. + + + (false, "rgba(242,242,242,0.2)") + ArcMenu menu button active bg color. + + + (false, "rgb(242,242,242)") + ArcMenu menu button active fg color. + + + + [['ArcMenu Style', 'rgba(48,48,49,0.98)', 'rgb(223,223,223)', 'rgb(60,60,60)', '1', '14', '11', + 'rgba(255,255,255,0.1)', 'rgb(21,83,158)', 'rgb(255,255,255)', 'rgb(25,98,163)', 'rgb(255,255,255)'], + ['Simply Dark', 'rgba(28,28,28,0.98)', 'rgb(211,218,227)', 'rgb(63,62,64)', '1', '14', '11', + 'rgb(63,62,64)', 'rgba(238,238,236,0.08)', 'rgb(255,255,255)', 'rgba(228,228,226,0.15)', 'rgb(255,255,255)'], + ['Dark Blue', 'rgb(30,37,41)', 'rgb(189,230,251)', 'rgb(41,50,55)', '1', '14', '11', + 'rgba(99,99,98,0.56)', 'rgba(189,230,251,0.08)', 'rgb(189,230,251)', 'rgba(189,230,251,0.15)', 'rgb(189,230,251)'], + ['Light Blue', 'rgb(245,247,250)', 'rgb(18,51,84)', 'rgba(18,51,84,0.2)', '1', '14', '11', + 'rgba(18,51,84,0.15)', 'rgba(18,51,84,0.08)', 'rgb(18,51,84)', 'rgba(18,51,84,0.15)', 'rgb(18,51,84)']] + List of color-themes + + + 'Off' + Force the menu to a specific location + + + 750 + ArcMenu Settings Width + + + 800 + ArcMenu Settings Height + + + 'Grid' + Raven Layout Search Display Style + + + 'Left' + Raven layout position. 'Left' or 'Right' side of screen + + + true + Enable Weather Widget raven layout + + + true + Enable Clocks Widget raven layout + + + false + Enable Weather Widget Unity layout + + + false + Enable Clocks Widget Unity layout + + + 'Symbolic' + Category icon type. Full_Color or Symbolic + + + 'Symbolic' + Shortcut icon type. Full_Color or Symbolic + + + false + disable the fade effect on scrollviews + + + false + Show a larger icon and extra app details + + + 5 + The amount of search results to display. + + + false + Highlight search results terms + + + false + Show search result description label + + + false + Show custom search provider - open windows + + + false + Show custom search provider - recent files + + + (true, 25) + Search entry border radius + + + false + Show hidden recent files + + + false + Activate Categories on Hover + + + false + disable recently installed apps + + + false + disable tooltips + + + false + Show category sub menus. + + + true + Separate All Programs by a Character Listing + + + true + Multi-lined labels for large icon application grid layouts + + + 'Pinned_Apps' + Set ArcMenu Layout Default View + + + 'ArcMenu' + Left Click Action on the Menu Button + + + 'ContextMenu' + Right Click Action on the Menu Button + + + 'ArcMenu' + Middle Click Action on the Menu Button + + + 'Categories_List' + Set Tognee Layout Default View + + + 'All_Programs' + Set Redmond Layout Default View + + + true + Set Unity layout to display "Home Screen" as default + + + false + Disable the user avatar icon from the menu + + + false + Enable an Activitities Overview Shortcut + + + 'Bottom' + Searchbar location for layouts with default searchbar on bottom + + + 'Top' + Searchbar location for layouts with default searchbar on top + + + false + Flip menu horizontally + + + 'Categories_List' + ArcMenu layout - 'All Apps' button action + + + false + Windows layout - disable frequent apps + + + false + Windows layout - disable pinned apps + + + false + GNOME Dash Layout - Show Applications + + + true + Plasma Menu Hover to activate item + + + false + Runner layout - show frequent apps + + + 400 + Runner layout - menu height + + + 500 + Runner layout - menu width + + + 0 + Runner layout - font size + + + false + Eleven Layout - disable frequent apps + + + false + Enable a vertical separator + + + 550 + Menu Height + + + 0 + Menu Width Adjustement. + + + 290 + Menu width + + + 205 + Right Panel width + + + false + Show External Devices + + + false + Show Bookmarks + + + [(0, true), (1, true), (2, true), (3, true), (4, false), (5, false), (6, false), (7, false)] + Power Options + + + 'Default' + Display type for power options + + + false + Display on all monitors + + + false + Show/Hide Activities Button in panel + + + ['Super_L'] + Keybind to open ArcMenu + + + [] + Keybind to open standalone runner menu. Setting a keybind enables the standalone runner menu. + + + false + Show ArcMenu on primary monitor when using hotkey + + + false + Show standalone Runner menu on primary monitor when using hotkey + + + 'Left' + The menu position in the panel + + + 'Top' + Runner Menu Position + + + 'List' + Runner Search Display Style + + + 'Icon' + Appearance of the menu button. + + + 'Round' + Appearance of the avatar icon. + + + 'Default' + Size of the menu items icon. + + + 'Default' + Size of the category icons. + + + 'Default' + Size of the menu grid icons. + + + {'width': 108, 'height': 108, 'iconSize': 62} + Custom "grid icon" size (width, height, iconSize). + + + 'Default' + Size of the button items in ArcMenu. + + + 'Default' + Size of misc items in ArcMenu. + + + 'Default' + Size of quicklink items in ArcMenu. + + + false + Force placement of ArcMenu on GNOME top panel. + + + false + Hide overview on startup + + + 'Apps' + Text of the menu button. + + + 'Menu_Icon' + Icon type for the menu button. + + + 0 + Icon integer value for the menu button. + + + 0 + Distro icon integer value for the menu button. + + + '' + File path of the custom icon for the menu button. + + + 20 + Size of the menu buton icon. + + + -1 + Menu Button Padding. + + + 0 + Menu button position offset for main panel + + + [(0, true), (1, true), (2, false), (3, false), (4, false)] + Array of extra categories. See Constants.js - Categories + + + [(0, false), (1, false), (2, false), (3, false), (4, false)] + Array of extra categories links for ArcMenu layout. See Constants.js - Categories + + + 'Bottom' + Display extra categories quick links on ArcMenu Layout home page + + + [] + List of recently installed apps + + + 225 + Sleek layout right panel width. + + + + + [ + {'id': 'firefox.desktop'}, + {'id': 'org.gnome.Nautilus.desktop'}, + {'id': 'org.gnome.Terminal.desktop'}, + {'id': 'gnome-extensions prefs arcmenu@arcmenu.com', 'name': 'ArcMenu Settings', 'icon': 'ArcMenu_ArcMenuIcon'} + ] + + Pinned Apps + + + + [ + {'id': 'ArcMenu_Home', 'name': 'Home', 'icon': 'ArcMenu_Home'}, + {'id': 'ArcMenu_Documents', 'name': 'Documents', 'icon': 'ArcMenu_Documents'}, + {'id': 'ArcMenu_Downloads', 'name': 'Downloads', 'icon': 'ArcMenu_Downloads'}, + {'id': 'ArcMenu_Music', 'name': 'Music', 'icon': 'ArcMenu_Music'}, + {'id': 'ArcMenu_Pictures', 'name': 'Pictures', 'icon': 'ArcMenu_Pictures'}, + {'id': 'ArcMenu_Videos', 'name': 'Videos', 'icon': 'ArcMenu_Videos'} + ] + + Array of directory shortcuts data. + + + + [ + {'id': 'ArcMenu_Software', 'name': 'Software'}, + {'id': 'org.gnome.Settings.desktop'}, + {'id': 'org.gnome.tweaks.desktop'}, + {'id': 'org.gnome.Terminal.desktop'}, + {'id': 'ArcMenu_ActivitiesOverview', 'name': 'Activities Overview', 'icon': 'view-fullscreen-symbolic'} + ] + + Array of application shortcuts data. + + + + [ + {'id': 'org.gnome.Nautilus.desktop'}, + {'id': 'org.gnome.Terminal.desktop'}, + {'id': 'org.gnome.Settings.desktop'} + ] + + 'AZ' layout extra shortcuts + + + + [ + {'id': 'ArcMenu_Software', 'name': 'Software'}, + {'id': 'org.gnome.Settings.desktop'} + ] + + 'Brisk' layout extra shortcuts + + + + [ + {'id': 'org.gnome.Nautilus.desktop'}, + {'id': 'org.gnome.Terminal.desktop'}, + {'id': 'org.gnome.Settings.desktop'} + ] + + '11' layout extra shortcuts + + + + [ + {'id': 'org.gnome.Nautilus.desktop'}, + {'id': 'org.gnome.Terminal.desktop'}, + {'id': 'org.gnome.Settings.desktop'} + ] + + 'Insider' layout extra shortcuts + + + + [ + {'id': 'firefox.desktop'}, + {'id': 'org.gnome.Terminal.desktop'}, + {'id': 'org.gnome.Settings.desktop'}, + {'id': 'ArcMenu_Software', 'name': 'Software'}, + {'id': 'org.gnome.Nautilus.desktop'}, + {'id': 'ArcMenu_Separator', 'name': 'Separator', 'icon': 'list-remove-symbolic'}, + {'id': 'ArcMenu_LogOut', 'name': 'Log Out...', 'icon': 'system-log-out-symbolic'}, + {'id': 'ArcMenu_Lock', 'name': 'Lock', 'icon': 'changes-prevent-symbolic'}, + {'id': 'ArcMenu_PowerOff', 'name': 'Power Off...', 'icon': 'system-shutdown-symbolic'} + ] + + 'Mint' layout extra shortcuts + + + + [ + {'id': 'ArcMenu_Home', 'name': 'Home', 'icon': 'ArcMenu_Home'}, + {'id': 'ArcMenu_Documents', 'name': 'Documents', 'icon': 'ArcMenu_Documents'}, + {'id': 'ArcMenu_Downloads', 'name': 'Downloads', 'icon': 'ArcMenu_Downloads'}, + {'id': 'ArcMenu_Music', 'name': 'Music', 'icon': 'ArcMenu_Music'}, + {'id': 'ArcMenu_Pictures', 'name': 'Pictures', 'icon': 'ArcMenu_Pictures'}, + {'id': 'ArcMenu_Software', 'name': 'Software'}, + {'id': 'org.gnome.Settings.desktop'}, + {'id': 'org.gnome.Terminal.desktop'} + ] + + 'Sleek' layout extra shortcuts + + + + [ + {'id': 'ArcMenu_Home', 'name': 'Home', 'icon': 'ArcMenu_Home'}, + {'id': 'ArcMenu_Documents', 'name': 'Documents', 'icon': 'ArcMenu_Documents'}, + {'id': 'ArcMenu_Downloads', 'name': 'Downloads', 'icon': 'ArcMenu_Downloads'}, + {'id': 'ArcMenu_Software', 'name': 'Software'}, + {'id': 'org.gnome.Nautilus.desktop'}, + {'id': 'ArcMenu_Separator', 'name': 'Separator', 'icon': 'list-remove-symbolic'}, + {'id': 'ArcMenu_LogOut', 'name': 'Log Out...', 'icon': 'system-log-out-symbolic'}, + {'id': 'ArcMenu_Lock', 'name': 'Lock', 'icon': 'changes-prevent-symbolic'}, + {'id': 'ArcMenu_PowerOff', 'name': 'Power Off...', 'icon': 'system-shutdown-symbolic'} + ] + + 'Unity' layout extra shortcuts + + + + [ + {'id': 'org.gnome.Nautilus.desktop'}, + {'id': 'org.gnome.Terminal.desktop'}, + {'id': 'org.gnome.Settings.desktop'} + ] + + 'Windows' layout extra shortcuts + + + + [ + {'id': 'org.gnome.Extensions.desktop'}, + {'id': 'ArcMenu_Settings', 'name': 'ArcMenu Settings', 'icon': 'ArcMenu_ArcMenuIcon'}, + {'id': 'ArcMenu_PanelExtensionSettings', 'name': 'Panel Extension Settings', 'icon': 'application-x-addon-symbolic'}, + {'id': 'ArcMenu_Separator', 'name': 'Separator', 'icon': 'list-remove-symbolic'}, + {'id': 'ArcMenu_PowerOptions', 'name': 'Power Options', 'icon': 'system-shutdown-symbolic'}, + {'id': 'ArcMenu_ActivitiesOverview', 'name': 'Activities Overview', 'icon': 'view-fullscreen-symbolic'}, + {'id': 'ArcMenu_ShowDesktop', 'name': 'Show Desktop', 'icon': 'computer-symbolic'} + ] + + Context menu shortcut items + + + + + [] + List of Apps Pinned to Menu + + + [] + List of default directory paths used for links on the right side of ArcMenu + + + [] + List of default directory paths used for links on the right side of ArcMenu + + + [] + List of AZ Layout extra buttons + + + [] + List of Eleven Layout extra buttons + + + [] + List of Insider Layout extra buttons + + + [] + List of Windows Layout extra buttons + + + [] + List of Unity Layout extra buttons + + + [] + List of Brisk Layout extra shortcuts + + + [] + List of Mint Layout extra buttons + + + [] + List of shortcuts on ArcMenu button context menu + + + + {} + List of org.gnome.desktop.app-folders id:name + + + 'Library Home' + The folder to display when opening the POP layout + + + 0 + Handles which page to open in ArcMenu Settings + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/COPYING b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/COPYING new file mode 100644 index 0000000..0cf672a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/COPYING @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. + + , 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 Library General +Public License instead of this License. + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/README.md b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/README.md new file mode 100644 index 0000000..1d615a7 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/README.md @@ -0,0 +1,170 @@ +![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/ArcMenu_WideBanner.png) + +----- + +![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/ArcMenu_Banner.png) + +----- + +### Introduction + +ArcMenu is an application menu for GNOME Shell, designed to provide a more familiar user experience and workflow. This extension has many features, including various menu layout styles, GNOME search, quick access to system shortcuts, and much more! + +**For ArcMenu to work properly, you may need [these dependencies](https://gitlab.com/arcmenu/ArcMenu/-/wikis/ArcMenu-Dependencies).** + +----- + +### Support ArcMenu + +ArcMenu is provided free of charge. If you enjoy using ArcMenu and wish to help support the project, feel free to use the link below! + +[![Donate via Paypal](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/paypal_donate.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=53CWA7NR743WC&item_name=Support+ArcMenu¤cy_code=USD&source=url) + + +----- + +### Installation + +#### Visit the [GNOME Extensions](https://extensions.gnome.org/extension/3628/arcmenu/) website to install the most recent release of ArcMenu. + +- **To install ArcMenu from source**: Please see the [Install From Source wiki Guide](https://gitlab.com/arcmenu/ArcMenu/-/wikis/Install-From-Source-Guide). + +

+ + + + +----- + +### Credits + +**@[AndrewZaech](https://gitlab.com/AndrewZaech)** (2019 - current) Project Maintainer and Developer + +**@[AndyC](https://gitlab.com/LinxGem33)** (2017 - 2020) ArcMenu Founder, Former Maintainer, Digital Art Designer + +**@[Alexander Rüedlinger](https://github.com/lexruee)** (2017) - Former Developer + +**Additional Credits**: +* The 'ArcMenu' layout design is insipired by the Zorin OS menu extension. +* ArcMenu utilizes various code from GNOME Shell and the GNOME Shell extensions package. +* ArcMenu Logo and other ArcMenu icon assets created by [AndyC](https://gitlab.com/LinxGem33). Licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) +* Fallback category icons included in ArcMenu are copied from [Ubuntu Yaru Icons](https://github.com/ubuntu/yaru) and licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) + +----- + +### History + +ArcMenu was first released in 2017 by [AndyC](https://gitlab.com/LinxGem33). The original ArcMenu project can be found [here](https://gitlab.com/LinxGem33/Arc-Menu). + +In 2017, ArcMenu _started_ as a fork of the Zorin menu extension by Zorin OS. As it's own separate project, ArcMenu rapidly began developing innovative features and quickly diverged away from Zorin menu thanks to the works of [AndyC](https://gitlab.com/LinxGem33) (2017 - 2020), [AndrewZaech](https://gitlab.com/AndrewZaech) (2019 - current), [Alexander Rüedlinger](https://github.com/lexruee) (2017), and other contributors. ArcMenu has been rewritten from the ground up since it's inception and has it's own separate, original, and unique code base, unrelated to that of Zorin menu. + +----- + +### Contributors + +**@[Thomas Ingvarsson](https://gitlab.com/tingvarsson)** | **@[fishears](https://github.com/fishears)** | **@[JasonLG1979](https://github.com/JasonLG1979)** | **@[itmitica](https://github.com/itmitica)** | **@[vikaig](https://gitlab.com/vikaig)** | **@[MagneFire](https://gitlab.com/MagneFire)** + +**@[tognee](https://gitlab.com/tognee)** | **@[hlechner](https://gitlab.com/hlechner)** | **@[vanillajonathan](https://gitlab.com/vanillajonathan)** | **@[epskampie](https://gitlab.com/epskampie)** | **@[GnomeBook](https://gitlab.com/GnomeBook)** + +**@[charlesg99](https://github.com/charlesg99)** | **@[harshadgavali](https://gitlab.com/harshadgavali)** | **@[O. Giesmann ](https://gitlab.com/Etamuk)** | **@[aleayr ](https://gitlab.com/aleayr)** + +**@[JasonLG1979](https://github.com/JasonLG1979)** | **@[lexruee](https://github.com/lexruee)** | **@[aleayr](https://gitlab.com/aleayr)** | **@[osmancoskun](https://gitlab.com/osmancoskun) !184** | **@[Balló György](https://gitlab.com/City-busz) !189, !215** + +----- + +### Translators + +| Language | Translators | +| ------ | ------ | +| **Belarusian** | **@[Latgardi](https://gitlab.com/Latgardi)** | +| **Brazilian Portuguese** | **@[frnogueira](https://gitlab.com/frnogueira), @[Z Bed](https://gitlab.com/zbed), @[perry.werneck](https://gitlab.com/perry.werneck)** | +| **Catalan** | **@[jordim](https://gitlab.com/jordim)** | +| **Chinese (Taiwan)** | **@[plesry](https://gitlab.com/plesry)** | +| **Czech** | **@[jiri.doubravsky](https://gitlab.com/jiri.doubravsky), @[panmourovaty](https://gitlab.com/panmourovaty), @[Amerey ](https://gitlab.com/Amerey.eu)** | +| **Danish** | **@[cbsvitzer](https://gitlab.com/cbsvitzer)** | +| **Dutch** | **@[Vistaus](https://gitlab.com/Vistaus), @[vinno97](https://gitlab.com/vinno97)** | +| **Estonian** | **@[rimasx](https://gitlab.com/rimasx)** | +| **Finnish** | **@[artnay](https://gitlab.com/artnay)** | +| **French** | **@[Nilvus](https://gitlab.com/Nilvus), @[steve.kossouho](https://gitlab.com/steve.kossouho)** | +| **German** | **@[Etamuk](https://gitlab.com/Etamuk), @[daPhipz](https://gitlab.com/daPhipz)** | +| **Hebrew** | **@[omeritzics](https://gitlab.com/omeritzics)** | +| **Hungarian** | **@[stevendobay](https://gitlab.com/stevendobay), @[Balázs Meskó](https://gitlab.com/meskobalazs)** | +| **Italian** | **@[Vombato](https://gitlab.com/Vombato), @[matguidi](https://gitlab.com/matguidi)** | +| **Japanese** | **@[Hayao0819](https://gitlab.com/Hayao0819)** | +| **Korean** | **@[HyeonWol](https://gitlab.com/hyeonwol123)** | +| **Norwegian** | **@[sigbjokj](https://gitlab.com/sigbjokj), @[sssandum](https://gitlab.com/sssandum)** | +| **Occitan** | **@[Mejans](https://gitlab.com/Mejans), @[Quentin](https://gitlab.com/quenty_occitania)** | +| **Polish** | **@[alex4401](https://gitlab.com/alex4401), @[Karpfly](https://gitlab.com/MacKarp), @[Piotr Strebski](https://gitlab.com/strebski), @[Adam Lewicki](https://gitlab.com/a.lewicki95)** | +| **Russian** | **@[Ivan](https://gitlab.com/vantu5z), @[Ser82-png](https://gitlab.com/Ser82-png)** | +| **Silesian** | **@[gkkulik](https://gitlab.com/gkkulik)** | +| **Simplified Chinese** | **@[skygmin](https://gitlab.com/skygmin), @[doufu3344](https://gitlab.com/doufu3344), @[wxtewx](https://gitlab.com/wxtewx), @[Anduin2017](https://gitlab.com/Anduin2017)** | +| **Sinhalese** | **@[sayuri](https://gitlab.com/sayuri-), @[helabasa](https://gitlab.com/helabasa)** | +| **Slovak** | **@[prescott66](https://gitlab.com/prescott66)** | +| **Spanish** | **@[juanlucer](https://gitlab.com/juanlucer), @[pjunyent](https://gitlab.com/pjunyent), @[Sergio](https://gitlab.com/IngrownMink4), @[Luis Zurro](https://gitlab.com/nyaran)** | +| **Swedish** | **@[MorganAntonsson](https://gitlab.com/MorganAntonsson)** | +| **Turkish** | **@[aylinux](https://gitlab.com/aylinux), @[Sabri Ünal](https://gitlab.com/sabriunal)** | +| **Ukrainian** | **@[vikaig](https://gitlab.com/vikaig), @[Denys Nykula](https://gitlab.com/nykula)** | + +----- + +### License & Terms + +ArcMenu is available under the terms of the GPL-2.0 license See [`COPYING`](https://gitlab.com/arcmenu/ArcMenu/blob/master/COPYING) for details. + +----- + +#### Distro Icons + +Legal disclaimer for brand icons and trademarks: + +*All brand icons are trademarks of their respective owners. The use of these trademarks does not indicate endorsement of the trademark holder by ArcMenu project, nor vice versa. Please do not use brand logos for any purpose except to represent the company, product, or service to which they refer.* + +

+Read More... + +

+ +* **UBUNTU ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg)** - Ubuntu name and Ubuntu logo is a trademark of Canonical ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg) Ltd. + +* **FEDORA ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg)** - Fedora and the Infinity design logo are trademarks of Red Hat, Inc. + +* **DEBIAN ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg)** - is a registered trademark owned by Software in the Public Interest, Inc. Debian trademark is a registered United States trademark of Software in the Public Interest, Inc., managed by the Debian project. + +* **MANJARO ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg)** - (logo) and name is a trademark of Manjaro GmbH & Co. KG + +* **POP_OS! ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg)** - Name and (logo) is a trademark of system 76 ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg) Inc. + +* **ARCH LINUX ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg)** - The stylized Arch Linux logo are recognised trademarks of Arch Linux, copyright 2002-2017 Judd Vinet and Aaron Griffin + +* **SOLUS** - name and logo is Copyright ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg) 2014-2018 by Solus Project + +* **KALI LINUX** - logo and name is part of ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg) OffSec Services Limited 2020 + +* **PUREOS** - name and logo is developed by members of the Purism community + +* **RASPBERRY PI** ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg) - Logo and name is part of Raspberry Pi Foundation +UK Registered Charity 1129409 + +* **Gentoo Authors![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg)** 2001–2020 +Gentoo is a trademark of the Gentoo Foundation, Inc. + +* **Voyager ![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg) Linux** +(name) and (logo) + +* **MXLinux![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg)** 2020 - +Linux - is the registered trademark of Linus Torvalds in the U.S. and other countries. + +* **Red Hat, Inc.![](https://gitlab.com/arcmenu/arcmenu-assets/raw/master/images/copyright2.svg)** Copyright 2020 +(name) and (logo) + +* **ZORIN OS** - The "Z" logomark is a registered trademark of Zorin Technology Group Ltd. Copyright © 2019 - 2021 Zorin Technology Group Ltd + +* **Pardus** - Pardus is a trademark of the TUBITAK ULAKBIM. Copyright © 2003–2023 TUBITAK ULAKBIM + +* **NixOS** - The logo is made available under a CC-BY license. Logo designed by Tim Cuthbertson. + + +

+
+ +----- diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/appMenu.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/appMenu.js new file mode 100644 index 0000000..eab6ffc --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/appMenu.js @@ -0,0 +1,457 @@ +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +import Clutter from 'gi://Clutter'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import St from 'gi://St'; + +import {AppMenu} from 'resource:///org/gnome/shell/ui/appMenu.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; + +import * as Utils from './utils.js'; + +const DESKTOP_ICONS_UUIDS = [ + 'ding@rastersoft.com', 'gtk4-ding@smedius.gitlab.com', + 'desktopicons-neo@darkdemon', +]; + +/** + * + * @param {Actor} child + */ +function isPopupMenuItemVisible(child) { + if (child._delegate instanceof PopupMenu.PopupMenuSection) { + if (child._delegate.isEmpty()) + return false; + } + return child.visible; +} + +export const AppContextMenu = class ArcMenuAppContextMenu extends AppMenu { + constructor(sourceActor, menuLayout) { + super(sourceActor, St.Side.TOP); + + this._settings = menuLayout.settings; + this._menuButton = menuLayout.menuButton; + + this._menuLayout = menuLayout; + this._enableFavorites = true; + this._showSingleWindows = true; + this.actor.add_style_class_name('arcmenu-menu app-menu'); + + this._pinnedAppData = this.sourceActor.pinnedAppData; + + this._scrollBox = new St.ScrollView({ + clip_to_allocation: true, + hscrollbar_policy: St.PolicyType.NEVER, + vscrollbar_policy: St.PolicyType.AUTOMATIC, + }); + this._boxPointer.bin.set_child(this._scrollBox); + Utils.addChildToParent(this._scrollBox, this.box); + + Main.uiGroup.add_child(this.actor); + this._menuLayout.contextMenuManager.addMenu(this); + + this.sourceActor.connect('destroy', () => { + if (this.isOpen) + this.close(); + Main.uiGroup.remove_child(this.actor); + this.destroy(); + }); + this.actor.connect('key-press-event', this._menuKeyPress.bind(this)); + + this._newWindowItem.connect('activate', () => this.closeMenus()); + this._onGpuMenuItem.connect('activate', () => this.closeMenus()); + this._detailsItem.connect('activate', () => this.closeMenus()); + + this._arcMenuPinnedItem = this._createMenuItem(_('Pin to ArcMenu'), 8, () => { + this.close(); + + if (this._pinnedAppData) { + let sourceSettings; + const isFolder = this.sourceActor.folderSettings; + if (isFolder) + sourceSettings = this.sourceActor.folderSettings; + else + sourceSettings = this._settings; + + const pinnedAppsList = sourceSettings.get_value('pinned-apps').deepUnpack(); + for (let i = 0; i < pinnedAppsList.length; i++) { + if (pinnedAppsList[i].id === this._app.get_id()) { + pinnedAppsList.splice(i, 1); + sourceSettings.set_value('pinned-apps', new GLib.Variant('aa{ss}', pinnedAppsList)); + break; + } + } + } else { + const pinnedAppsList = this._settings.get_value('pinned-apps').deepUnpack(); + const newPinnedAppData = { + id: this._app.get_id(), + }; + pinnedAppsList.push(newPinnedAppData); + this._settings.set_value('pinned-apps', new GLib.Variant('aa{ss}', pinnedAppsList)); + } + }); + + this._createDesktopShortcutItem = this._createMenuItem(_('Create Desktop Shortcut'), 7, () => { + const [exists, src, dst] = this.getDesktopShortcut(); + if (exists && src && dst) { + try { + dst.delete(null); + } catch (e) { + log(`Failed to delete shortcut: ${e.message}`); + } + } else if (src && dst) { + try { + src.copy(dst, Gio.FileCopyFlags.OVERWRITE, null, null); + } catch (e) { + log(`Failed to copy to desktop: ${e.message}`); + } + } + this.close(); + this._updateDesktopShortcutItem(); + }); + this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem(), 8); + + this._settings.connectObject('changed::pinned-apps', () => this._updateArcMenuPinnedItem(), this.actor); + this.desktopExtensionStateChangedId = + Main.extensionManager.connect('extension-state-changed', (data, changedExtension) => { + if (DESKTOP_ICONS_UUIDS.includes(changedExtension.uuid)) + this._updateDesktopShortcutItem(); + }); + + this.connect('active-changed', () => this._activeChanged()); + this.connect('destroy', () => this._onDestroy()); + } + + _activeChanged() { + if (this._activeMenuItem) + Utils.ensureActorVisibleInScrollView(this._activeMenuItem); + } + + open(animate) { + if (this._menuButton.tooltipShowingID) { + GLib.source_remove(this._menuButton.tooltipShowingID); + this._menuButton.tooltipShowingID = null; + this._menuButton.tooltipShowing = false; + } + if (this.sourceActor.tooltip) { + this.sourceActor.tooltip.hide(); + this._menuButton.tooltipShowing = false; + } + + // clear the max height style for next recalculation + this._scrollBox.style = null; + + const {needsScrollbar, maxHeight} = this._needsScrollbar(); + this._scrollBox.style = `max-height: ${maxHeight}px;`; + + this._scrollBox.vscrollbar_policy = + needsScrollbar ? St.PolicyType.AUTOMATIC : St.PolicyType.NEVER; + + if (needsScrollbar) + this.actor.add_style_pseudo_class('scrolled'); + else + this.actor.remove_style_pseudo_class('scrolled'); + + super.open(animate); + this.sourceActor.add_style_pseudo_class('active'); + } + + _needsScrollbar() { + const monitorIndex = Main.layoutManager.findIndexForActor(this.sourceActor); + + this._sourceExtents = this.sourceActor.get_transformed_extents(); + this._workArea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex); + + const sourceTopLeft = this._sourceExtents.get_top_left(); + const sourceBottomRight = this._sourceExtents.get_bottom_right(); + const [, , , boxHeight] = this._scrollBox.get_preferred_size(); + const workarea = this._workArea; + + switch (this._arrowSide) { + case St.Side.TOP: { + const maxHeight = (workarea.y + workarea.height) - sourceBottomRight.y - 16; + if (sourceBottomRight.y + boxHeight > workarea.y + workarea.height) + return {needsScrollbar: true, maxHeight}; + return {needsScrollbar: false, maxHeight}; + } + case St.Side.BOTTOM: { + const maxHeight = sourceTopLeft.y - workarea.y - 16; + if (sourceTopLeft.y - boxHeight < workarea.y) + return {needsScrollbar: true, maxHeight}; + return {needsScrollbar: false, maxHeight}; + } + default: + return {needsScrollbar: false, maxHeight: 0}; + } + } + + _onDestroy() { + this.destroyed = true; + this._disconnectSignals(); + if (this.desktopExtensionStateChangedId) { + Main.extensionManager.disconnect(this.desktopExtensionStateChangedId); + this.desktopExtensionStateChangedId = null; + } + } + + closeMenus() { + this.close(); + this._menuLayout.arcMenu.toggle(); + } + + _createMenuItem(labelText, position, callback) { + const item = new PopupMenu.PopupMenuItem(labelText); + item.connect('activate', () => callback()); + this.addMenuItem(item, position); + return item; + } + + setApp(app) { + if (this._app === app) + return; + + this._app?.disconnectObject(this); + + if (this.destroyed) + return; + + this._app = app; + + this._app?.connectObject('windows-changed', + () => this._queueUpdateWindowsSection(), this); + + this._updateWindowsSection(); + + const appInfo = app?.app_info; + const actions = appInfo?.list_actions() ?? []; + + this._actionSection.removeAll(); + actions.forEach(action => { + const label = appInfo.get_action_name(action); + this._actionSection.addAction(label, event => { + if (action === 'new-window') + this._animateLaunch(); + + this._app.launch_action(action, event.get_time(), -1); + this.closeMenus(); + }); + }); + + this._updateQuitItem(); + this._updateNewWindowItem(); + this._updateFavoriteItem(); + this._updateGpuItem(); + this._updateArcMenuPinnedItem(); + this._updateDesktopShortcutItem(); + } + + isDesktopActive() { + let hasActiveDesktop = false; + + DESKTOP_ICONS_UUIDS.forEach(uuid => { + const extension = Main.extensionManager.lookup(uuid); + if (extension?.state === Utils.ExtensionState.ACTIVE) + hasActiveDesktop = true; + }); + + return hasActiveDesktop; + } + + getDesktopShortcut() { + if (!this._app) + return [false, null, null]; + + const fileDestination = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP); + const src = Gio.File.new_for_path(this._app.get_app_info().get_filename()); + const dst = Gio.File.new_for_path(GLib.build_filenamev([fileDestination, src.get_basename()])); + const exists = dst.query_exists(null); + return [exists, src, dst]; + } + + _updateDesktopShortcutItem() { + const isDesktopActive = this.isDesktopActive(); + + if (!this._app || !isDesktopActive) { + this._createDesktopShortcutItem.visible = false; + return; + } + this._createDesktopShortcutItem.visible = true; + + const [exists, src_, dst_] = this.getDesktopShortcut(); + + this._createDesktopShortcutItem.label.text = exists ? _('Delete Desktop Shortcut') + : _('Create Desktop Shortcut'); + } + + // For Custom Shortcuts in Pinned Apps category. ie ArcMenu Settings + addUnpinItem(id, folder = null) { + this._disconnectSignals(); + this.removeAll(); + this._id = id; + this._arcMenuPinnedItem = this._createMenuItem(_('Unpin from ArcMenu'), 0, () => { + this.close(); + + let sourceSettings; + if (!folder && this.sourceActor.folderSettings) + sourceSettings = this.sourceActor.folderSettings; + else + sourceSettings = this._settings; + + // Unpinned the folder, reset all folder settings keys + if (folder) { + let keys = folder.settings_schema.list_keys(); + for (const key of keys) + folder.reset(key); + + return; + } + + const pinnedAppsList = sourceSettings.get_value('pinned-apps').deepUnpack(); + for (let i = 0; i < pinnedAppsList.length; i++) { + if (pinnedAppsList[i].id === this._id) { + pinnedAppsList.splice(i, 1); + sourceSettings.set_value('pinned-apps', new GLib.Variant('aa{ss}', pinnedAppsList)); + break; + } + } + }); + } + + _updateArcMenuPinnedItem() { + if (!this._app) { + this._arcMenuPinnedItem.visible = false; + return; + } + + this._arcMenuPinnedItem.visible = this._menuLayout.hasPinnedApps; + + this._arcMenuPinnedItem.label.text = this._pinnedAppData ? _('Unpin from ArcMenu') : _('Pin to ArcMenu'); + } + + _updateWindowsSection() { + if (this._updateWindowsLaterId) { + const laters = global.compositor.get_laters(); + laters.remove(this._updateWindowsLaterId); + } + this._updateWindowsLaterId = 0; + + this._windowSection.removeAll(); + this._openWindowsHeader.hide(); + + if (!this._app) + return; + + const minWindows = this._showSingleWindows ? 1 : 2; + const windows = this._app.get_windows().filter(w => !w.skip_taskbar); + if (windows.length < minWindows) + return; + + this._openWindowsHeader.show(); + + windows.forEach(window => { + const title = window.title || this._app.get_name(); + const item = this._windowSection.addAction(title, event => { + this.closeMenus(); + Main.activateWindow(window, event.get_time()); + }); + window.connectObject('notify::title', () => { + item.label.text = window.title || this._app.get_name(); + }, item); + }); + } + + setFolderPath(path) { + this._disconnectSignals(); + this.removeAll(); + + this._openFolderLocationItem = this._createMenuItem(_('Open Folder Location'), 0, () => { + const file = Gio.File.new_for_path(path); + const context = global.create_app_launch_context(Clutter.get_current_event().get_time(), -1); + new Promise((resolve, reject) => { + Gio.AppInfo.launch_default_for_uri_async(file.get_uri(), context, null, (o, res) => { + try { + Gio.AppInfo.launch_default_for_uri_finish(res); + resolve(); + } catch (e) { + reject(e); + } + }); + }); + this.closeMenus(); + }); + } + + addAdditionalAction(name, action) { + if (!this._openFolderLocationItem) { + this._disconnectSignals(); + this.removeAll(); + } else { + this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + } + + this._additionalAction = new PopupMenu.PopupMenuItem(_(name)); + this._additionalAction.connect('activate', () => { + this.close(); + action(); + }); + this.addMenuItem(this._additionalAction); + } + + isEmpty() { + if (!this._app && !this._openFolderLocationItem && !this._id && !this._additionalAction) + return true; + + const hasVisibleChildren = this.box.get_children().some(child => { + if (child._delegate instanceof PopupMenu.PopupSeparatorMenuItem) + return false; + return isPopupMenuItemVisible(child); + }); + + return !hasVisibleChildren; + } + + centerBoxPointerPosition() { + this._boxPointer.setSourceAlignment(.50); + this._arrowAlignment = .5; + this._boxPointer._border.queue_repaint(); + } + + rightBoxPointerPosition() { + this._arrowSide = St.Side.LEFT; + this._boxPointer._arrowSide = St.Side.LEFT; + this._boxPointer._userArrowSide = St.Side.LEFT; + this._boxPointer.setSourceAlignment(.50); + this._arrowAlignment = .5; + this._boxPointer._border.queue_repaint(); + } + + _disconnectSignals() { + this._settings.disconnectObject(this.actor); + this._appSystem.disconnectObject(this.actor); + this._parentalControlsManager.disconnectObject(this.actor); + this._appFavorites.disconnectObject(this.actor); + global.settings.disconnectObject(this.actor); + global.disconnectObject(this.actor); + } + + close(animate) { + super.close(animate); + this.sourceActor.remove_style_pseudo_class('active'); + this.sourceActor.sync_hover(); + } + + _menuKeyPress(actor, event) { + const symbol = event.get_key_symbol(); + if (symbol === Clutter.KEY_Menu) { + this.toggle(); + this.sourceActor.sync_hover(); + } + } + + _onKeyPress() { + return Clutter.EVENT_PROPAGATE; + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/arcmenuManager.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/arcmenuManager.js new file mode 100644 index 0000000..6be3800 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/arcmenuManager.js @@ -0,0 +1,35 @@ +export class ArcMenuManager { + constructor(extension) { + if (ArcMenuManager._singleton) + throw new Error('ArcMenu has been already initialized'); + ArcMenuManager._singleton = extension; + } + + static getDefault() { + return ArcMenuManager._singleton; + } + + static get customStylesheet() { + return ArcMenuManager.getDefault().customStylesheet; + } + + static set customStylesheet(stylesheet) { + ArcMenuManager.getDefault().customStylesheet = stylesheet; + } + + static get extension() { + return ArcMenuManager.getDefault(); + } + + static get settings() { + return ArcMenuManager.getDefault().settings; + } + + static get settingsControllers() { + return ArcMenuManager.getDefault().settingsControllers; + } + + destroy() { + ArcMenuManager._singleton = null; + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/constants.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/constants.js new file mode 100644 index 0000000..626a5f8 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/constants.js @@ -0,0 +1,623 @@ +import {domain} from 'gettext'; +const {gettext: _} = domain('arcmenu'); + +export const DASH_TO_PANEL_UUID = 'dash-to-panel@jderose9.github.com'; +export const AZTASKBAR_UUID = 'aztaskbar@aztaskbar.gitlab.com'; + +export const SearchbarLocation = { + BOTTOM: 0, + TOP: 1, +}; + +export const MenuItemLocation = { + BOTTOM: 0, + TOP: 1, +}; + +export const DisplayType = { + LIST: 0, + GRID: 1, + BUTTON: 2, +}; + +export const AvatarStyle = { + ROUND: 0, + SQUARE: 1, +}; + +export const CategoryType = { + FAVORITES: 0, + FREQUENT_APPS: 1, + ALL_PROGRAMS: 2, + PINNED_APPS: 3, + RECENT_FILES: 4, + HOME_SCREEN: 5, + SEARCH_RESULTS: 6, + CATEGORIES_LIST: 7, +}; + +export const DefaultMenuView = { + PINNED_APPS: 0, + CATEGORIES_LIST: 1, + FREQUENT_APPS: 2, + ALL_PROGRAMS: 3, +}; + +export const SettingsPage = { + MAIN: 0, + MENU_LAYOUT: 1, + BUTTON_APPEARANCE: 2, + LAYOUT_TWEAKS: 3, + ABOUT: 4, + CUSTOMIZE_MENU: 5, + RUNNER_TWEAKS: 6, + GENERAL: 7, + MENU_THEME: 8, + DIRECTORY_SHORTCUTS: 9, + APPLICATION_SHORTCUTS: 10, + SEARCH_OPTIONS: 11, + POWER_OPTIONS: 12, + EXTRA_CATEGORIES: 13, + PINNED_APPS: 14, +}; + +export const DefaultMenuViewTognee = { + CATEGORIES_LIST: 0, + ALL_PROGRAMS: 1, +}; + +export const AllAppsButtonAction = { + CATEGORIES_LIST: 0, + ALL_PROGRAMS: 1, +}; + +export const DefaultMenuViewRedmond = { + ALL_PROGRAMS: 0, + PINNED_APPS: 1, +}; + +export const SoftwareManagerIDs = ['org.manjaro.pamac.manager.desktop', 'pamac-manager.desktop', + 'io.elementary.appcenter.desktop', 'snap-store_ubuntu-software.desktop', 'snap-store_snap-store.desktop', + 'org.gnome.Software.desktop', 'tr.org.pardus.software.desktop']; + +export const Categories = [ + {CATEGORY: CategoryType.FAVORITES, NAME: _('Favorites'), ICON: 'emblem-favorite-symbolic'}, + {CATEGORY: CategoryType.FREQUENT_APPS, NAME: _('Frequent Apps'), ICON: 'user-bookmarks-symbolic'}, + {CATEGORY: CategoryType.ALL_PROGRAMS, NAME: _('All Apps'), ICON: 'view-app-grid-symbolic'}, + {CATEGORY: CategoryType.PINNED_APPS, NAME: _('Pinned Apps'), ICON: 'view-pin-symbolic'}, + {CATEGORY: CategoryType.RECENT_FILES, NAME: _('Recent Files'), ICON: 'document-open-recent-symbolic'}, +]; + +export const TooltipLocation = { + TOP_CENTERED: 0, + BOTTOM_CENTERED: 1, + BOTTOM: 2, +}; + +export const ContextMenuLocation = { + DEFAULT: 0, + BOTTOM_CENTERED: 1, + RIGHT: 2, +}; + +export const SeparatorAlignment = { + VERTICAL: 0, + HORIZONTAL: 1, +}; + +export const SeparatorStyle = { + SHORT: 0, + MEDIUM: 1, + LONG: 2, + MAX: 3, + HEADER_LABEL: 4, + NORMAL: 5, + EMPTY: 6, +}; + +export const CaretPosition = { + END: -1, + START: 0, + MIDDLE: 2, +}; + +export const CategoryIconType = { + FULL_COLOR: 0, + SYMBOLIC: 1, +}; + +export const ForcedMenuLocation = { + OFF: 0, + TOP_CENTERED: 1, + BOTTOM_CENTERED: 2, + MONITOR_CENTERED: 3, +}; + +export const IconSize = { + DEFAULT: 0, + EXTRA_SMALL: 1, + SMALL: 2, + MEDIUM: 3, + LARGE: 4, + EXTRA_LARGE: 5, + HIDDEN: 6, +}; + +export const GridIconSize = { + DEFAULT: 0, + SMALL: 1, + MEDIUM: 2, + LARGE: 3, + SMALL_RECT: 4, + MEDIUM_RECT: 5, + LARGE_RECT: 6, + CUSTOM: 7, + EXTRA_LARGE: 8, +}; + +export const GridIconInfo = [ + {ENUM: GridIconSize.SMALL, WIDTH: 80, HEIGHT: 80, ICON_SIZE: 36}, + {ENUM: GridIconSize.MEDIUM, WIDTH: 87, HEIGHT: 87, ICON_SIZE: 42}, + {ENUM: GridIconSize.LARGE, WIDTH: 95, HEIGHT: 95, ICON_SIZE: 52}, + {ENUM: GridIconSize.SMALL_RECT, WIDTH: 85, HEIGHT: 70, ICON_SIZE: 28}, + {ENUM: GridIconSize.MEDIUM_RECT, WIDTH: 92, HEIGHT: 78, ICON_SIZE: 34}, + {ENUM: GridIconSize.LARGE_RECT, WIDTH: 95, HEIGHT: 85, ICON_SIZE: 42}, + {ENUM: GridIconSize.EXTRA_LARGE, WIDTH: 148, HEIGHT: 148, ICON_SIZE: 68}, +]; + +export const ICON_HIDDEN = 0; +export const EXTRA_SMALL_ICON_SIZE = 16; +export const SMALL_ICON_SIZE = 20; +export const MEDIUM_ICON_SIZE = 25; +export const LARGE_ICON_SIZE = 30; +export const EXTRA_LARGE_ICON_SIZE = 35; +export const MISC_ICON_SIZE = 24; + +export const SUPER_L = 'Super_L'; + +export const SECTIONS = [ + 'devices', + 'network', + 'bookmarks', +]; + +export const Direction = { + GO_NEXT: 0, + GO_PREVIOUS: 1, +}; + +export const MenuPosition = { + LEFT: 0, + CENTER: 1, + RIGHT: 2, +}; + +export const RavenPosition = { + LEFT: 0, + RIGHT: 1, +}; + +export const DiaglogType = { + DEFAULT: 0, + OTHER: 1, + APPLICATIONS: 2, + DIRECTORIES: 3, +}; + +export const MenuSettingsListType = { + PINNED_APPS: 0, + APPLICATIONS: 1, + DIRECTORIES: 2, + EXTRA_SHORTCUTS: 3, + POWER_OPTIONS: 4, + EXTRA_CATEGORIES: 5, + QUICK_LINKS: 6, + CONTEXT_MENU: 7, + FOLDER_PINNED_APPS: 8, +}; + +export const MenuButtonAppearance = { + ICON: 0, + TEXT: 1, + ICON_TEXT: 2, + TEXT_ICON: 3, + NONE: 4, +}; + +export const MenuButtonClickAction = { + ARCMENU: 0, + CONTEXT_MENU: 1, + NONE: 2, +}; + +export const PowerType = { + LOGOUT: 0, + LOCK: 1, + RESTART: 2, + POWER_OFF: 3, + SUSPEND: 4, + HYBRID_SLEEP: 5, + HIBERNATE: 6, + SWITCH_USER: 7, +}; + +export const PowerDisplayStyle = { + DEFAULT: 0, + IN_LINE: 1, + MENU: 2, +}; + +export const PowerOptions = [ + {TYPE: PowerType.LOGOUT, ICON: 'system-log-out-symbolic', NAME: _('Log Out...')}, + {TYPE: PowerType.LOCK, ICON: 'changes-prevent-symbolic', NAME: _('Lock')}, + {TYPE: PowerType.RESTART, ICON: 'system-reboot-symbolic', NAME: _('Restart...')}, + {TYPE: PowerType.POWER_OFF, ICON: 'system-shutdown-symbolic', NAME: _('Power Off...')}, + {TYPE: PowerType.SUSPEND, ICON: 'media-playback-pause-symbolic', NAME: _('Suspend')}, + {TYPE: PowerType.HYBRID_SLEEP, ICON: 'weather-clear-night-symbolic', NAME: _('Hybrid Sleep')}, + {TYPE: PowerType.HIBERNATE, ICON: 'document-save-symbolic', NAME: _('Hibernate')}, + {TYPE: PowerType.SWITCH_USER, ICON: 'system-switch-user-symbolic', NAME: _('Switch User')}, +]; + +// Icon type for the menu button +// set 'menu-button-icon' enum setting to value of desired icon type. +export const MenuIconType = { + MENU_ICON: 0, + DISTRO_ICON: 1, + CUSTOM: 2, +}; + +// Object order in MenuIcons array corresponds to the integer value of 'arc-menu-icon' setting +// i.e 'arc-menu-icon' value of 0 is arcmenu-logo-symbolic. +export const MenuIcons = [ + {PATH: 'icon-arcmenu-logo-symbolic'}, + {PATH: 'icon-arcmenu-logo-alt-symbolic'}, + {PATH: 'icon-arcmenu-old-symbolic'}, + {PATH: 'icon-arcmenu-old-alt-symbolic'}, + {PATH: 'icon-arcmenu-oldest-symbolic'}, + {PATH: 'icon-curved-a-symbolic'}, + {PATH: 'icon-focus-symbolic'}, + {PATH: 'icon-triple-dash-symbolic'}, + {PATH: 'icon-whirl-symbolic'}, + {PATH: 'icon-whirl-circle-symbolic'}, + {PATH: 'icon-sums-symbolic'}, + {PATH: 'icon-arrow-symbolic'}, + {PATH: 'icon-lins-symbolic'}, + {PATH: 'icon-diamond-square-symbolic'}, + {PATH: 'icon-octo-maze-symbolic'}, + {PATH: 'icon-search-glass-symbolic'}, + {PATH: 'icon-transform-symbolic'}, + {PATH: 'icon-toxic2-symbolic'}, + {PATH: 'icon-alien-symbolic'}, + {PATH: 'icon-cloud-symbolic'}, + {PATH: 'icon-dragon-symbolic'}, + {PATH: 'icon-fly-symbolic'}, + {PATH: 'icon-pacman-symbolic'}, + {PATH: 'icon-peaks-symbolic'}, + {PATH: 'icon-pie-symbolic'}, + {PATH: 'icon-pointer-symbolic'}, + {PATH: 'icon-toxic-symbolic'}, + {PATH: 'icon-tree-symbolic'}, + {PATH: 'icon-zegon-symbolic'}, + {PATH: 'icon-apps-symbolic'}, + {PATH: 'icon-bug-symbolic'}, + {PATH: 'icon-cita-symbolic'}, + {PATH: 'icon-dragonheart-symbolic'}, + {PATH: 'icon-eclipse-symbolic'}, + {PATH: 'icon-football-symbolic'}, + {PATH: 'icon-heddy-symbolic'}, + {PATH: 'icon-helmet-symbolic'}, + {PATH: 'icon-paint-palette-symbolic'}, + {PATH: 'icon-peeks-symbolic'}, + {PATH: 'icon-record-symbolic'}, + {PATH: 'icon-saucer-symbolic'}, + {PATH: 'icon-step-symbolic'}, + {PATH: 'icon-vancer-symbolic'}, + {PATH: 'icon-vibe-symbolic'}, + {PATH: 'icon-start-box-symbolic'}, + {PATH: 'icon-dimond-win-symbolic'}, + {PATH: 'icon-dolphin-symbolic'}, + {PATH: 'icon-dota-symbolic'}, + {PATH: 'icon-football2-symbolic'}, + {PATH: 'icon-loveheart-symbolic'}, + {PATH: 'icon-pyrimid-symbolic'}, + {PATH: 'icon-rewind-symbolic'}, + {PATH: 'icon-snap-symbolic'}, + {PATH: 'icon-time-symbolic'}, + {PATH: 'icon-3d-symbolic'}, + {PATH: 'icon-a-symbolic'}, + {PATH: 'icon-app-launcher-symbolic'}, + {PATH: 'icon-bat-symbolic'}, + {PATH: 'icon-dra-symbolic'}, + {PATH: 'icon-equal-symbolic'}, + {PATH: 'icon-gnacs-symbolic'}, + {PATH: 'icon-groove-symbolic'}, + {PATH: 'icon-kaaet-symbolic'}, + {PATH: 'icon-launcher-symbolic'}, + {PATH: 'icon-pac-symbolic'}, + {PATH: 'icon-robots-symbolic'}, + {PATH: 'icon-sheild-symbolic'}, + {PATH: 'icon-somnia-symbolic'}, + {PATH: 'icon-utool-symbolic'}, + {PATH: 'icon-swirl-symbolic'}, + {PATH: 'icon-round-symbolic'}, + {PATH: 'view-app-grid-symbolic'}, +]; + +// Object order in DistroIcons array corresponds to the integer value of 'distro-icon' setting +// i.e 'distro-icon' value of 3 is manjaro-logo. +export const DistroIcons = [ + {PATH: 'distro-gnome-symbolic', NAME: 'GNOME'}, + {PATH: 'distro-debian-symbolic', NAME: 'Debian'}, + {PATH: 'distro-fedora-symbolic', NAME: 'Fedora'}, + {PATH: 'distro-manjaro-symbolic', NAME: 'Manjaro'}, + {PATH: 'distro-pop-os-symbolic', NAME: 'Pop!_OS'}, + {PATH: 'distro-ubuntu-symbolic', NAME: 'Ubuntu'}, + {PATH: 'distro-arch-symbolic', NAME: 'Arch'}, + {PATH: 'distro-opensuse-symbolic', NAME: 'OpenSUSE'}, + {PATH: 'distro-raspbian-symbolic', NAME: 'Raspbian'}, + {PATH: 'distro-kali-linux-symbolic', NAME: 'Kali Linux'}, + {PATH: 'distro-pureos-symbolic', NAME: 'PureOS'}, + {PATH: 'distro-solus-symbolic', NAME: 'Solus'}, + {PATH: 'distro-budgie-symbolic', NAME: 'Budgie'}, + {PATH: 'distro-gentoo-symbolic', NAME: 'Gentoo'}, + {PATH: 'distro-mx-symbolic', NAME: 'MX Linux'}, + {PATH: 'distro-redhat-symbolic', NAME: 'Redhat'}, + {PATH: 'distro-voyager-symbolic', NAME: 'Voyager'}, + {PATH: 'distro-zorin-symbolic', NAME: 'Zorin OS'}, + {PATH: 'distro-endeavour-symbolic', NAME: 'Endeavour'}, + {PATH: 'distro-nobara-symbolic', NAME: 'Nobara'}, + {PATH: 'distro-pardus-symbolic', NAME: 'Pardus'}, + {PATH: 'distro-cachyos-symbolic', NAME: 'CachyOS'}, + {PATH: 'distro-nixos-symbolic', NAME: 'NixOS'}, + {PATH: 'distro-oreon-symbolic', NAME: 'Oreon OS'}, +]; + +export const MenuLayout = { + ARCMENU: 0, + BRISK: 1, + WHISKER: 2, + GNOME_MENU: 3, + MINT: 4, + ELEMENTARY: 5, + GNOME_OVERVIEW: 6, + REDMOND: 7, + UNITY: 8, + BUDGIE: 9, + INSIDER: 10, + RUNNER: 11, + CHROMEBOOK: 12, + RAVEN: 13, + TOGNEE: 14, + PLASMA: 15, + WINDOWS: 16, + ELEVEN: 17, + AZ: 18, + ENTERPRISE: 19, + POP: 20, + SLEEK: 21, +}; + +export const TraditionalMenus = [ + { + LAYOUT: MenuLayout.ARCMENU, + TITLE: _('ArcMenu'), + IMAGE: 'menu-arcmenu-symbolic', + }, + { + LAYOUT: MenuLayout.BRISK, + TITLE: _('Brisk'), + IMAGE: 'menu-brisk-symbolic', + }, + { + LAYOUT: MenuLayout.WHISKER, + TITLE: _('Whisker'), + IMAGE: 'menu-whisker-symbolic', + }, + { + LAYOUT: MenuLayout.GNOME_MENU, + TITLE: _('GNOME Menu'), + IMAGE: 'menu-gnomemenu-symbolic', + }, + { + LAYOUT: MenuLayout.MINT, + TITLE: _('Mint'), + IMAGE: 'menu-mint-symbolic', + }, + { + LAYOUT: MenuLayout.BUDGIE, + TITLE: _('Budgie'), + IMAGE: 'menu-budgie-symbolic', + }, +]; + +export const ModernMenus = [ + { + LAYOUT: MenuLayout.UNITY, + TITLE: _('Unity'), + IMAGE: 'menu-unity-symbolic', + }, + { + LAYOUT: MenuLayout.PLASMA, + TITLE: _('Plasma'), + IMAGE: 'menu-plasma-symbolic', + }, + { + LAYOUT: MenuLayout.TOGNEE, + TITLE: _('tognee'), + IMAGE: 'menu-tognee-symbolic', + }, + { + LAYOUT: MenuLayout.INSIDER, + TITLE: _('Insider'), + IMAGE: 'menu-insider-symbolic', + }, + { + LAYOUT: MenuLayout.REDMOND, + TITLE: _('Redmond'), + IMAGE: 'menu-redmond-symbolic', + }, + { + LAYOUT: MenuLayout.WINDOWS, + TITLE: _('Windows'), + IMAGE: 'menu-windows-symbolic', + }, + { + LAYOUT: MenuLayout.ELEVEN, + TITLE: _('11'), + IMAGE: 'menu-eleven-symbolic', + }, + { + LAYOUT: MenuLayout.AZ, + TITLE: _('a.z.'), + IMAGE: 'menu-az-symbolic', + }, + { + LAYOUT: MenuLayout.ENTERPRISE, + TITLE: _('Enterprise'), + IMAGE: 'menu-enterprise-symbolic', + }, + { + LAYOUT: MenuLayout.POP, + TITLE: _('Pop'), + IMAGE: 'menu-pop-symbolic', + }, + { + LAYOUT: MenuLayout.SLEEK, + TITLE: _('Sleek'), + IMAGE: 'menu-sleek-symbolic', + }, +]; + +export const TouchMenus = [ + { + LAYOUT: MenuLayout.ELEMENTARY, + TITLE: _('Elementary'), + IMAGE: 'menu-elementary-symbolic', + }, + { + LAYOUT: MenuLayout.CHROMEBOOK, + TITLE: _('Chromebook'), + IMAGE: 'menu-chromebook-symbolic', + }, +]; + +export const LauncherMenus = [ + { + LAYOUT: MenuLayout.RUNNER, + TITLE: _('Runner'), + IMAGE: 'menu-runner-symbolic', + }, + { + LAYOUT: MenuLayout.GNOME_OVERVIEW, + TITLE: _('GNOME Overview'), + IMAGE: 'menu-gnomeoverview-symbolic', + }, +]; + +export const AlternativeMenus = [ + { + LAYOUT: MenuLayout.RAVEN, + TITLE: _('Raven'), + IMAGE: 'menu-raven-symbolic', + }, +]; + +export const MenuStyles = [ + { + MENU_TYPE: TraditionalMenus, + TITLE: _('Traditional'), + IMAGE: 'menustyle-traditional-symbolic', + }, + { + MENU_TYPE: ModernMenus, + TITLE: _('Modern'), + IMAGE: 'menustyle-modern-symbolic', + }, + { + MENU_TYPE: TouchMenus, + TITLE: _('Touch'), + IMAGE: 'menustyle-touch-symbolic', + }, + { + MENU_TYPE: LauncherMenus, + TITLE: _('Launcher'), + IMAGE: 'menustyle-launcher-symbolic', + }, + { + MENU_TYPE: AlternativeMenus, + TITLE: _('Alternative'), + IMAGE: 'menustyle-alternative-symbolic', + }, +]; + +export const ArcMenuLogoSymbolic = '/icons/arcmenu-logo-symbolic.svg'; + +export const TranslatableSettingsStrings = [_('Software'), _('Settings'), _('Tweaks'), _('Terminal'), + _('Activities Overview'), _('ArcMenu Settings'), _('Files')]; + +export const ShortcutCommands = { + SUSPEND: 'ArcMenu_Suspend', + LOG_OUT: 'ArcMenu_LogOut', + POWER_OFF: 'ArcMenu_PowerOff', + LOCK: 'ArcMenu_Lock', + RESTART: 'ArcMenu_Restart', + HYBRID_SLEEP: 'ArcMenu_HybridSleep', + HIBERNATE: 'ArcMenu_Hibernate', + SWITCH_USER: 'ArcMenu_SwitchUser', + COMPUTER: 'ArcMenu_Computer', + NETWORK: 'ArcMenu_Network', + RECENT: 'ArcMenu_Recent', + SOFTWARE: 'ArcMenu_Software', + HOME: 'ArcMenu_Home', + DOCUMENTS: 'ArcMenu_Documents', + DOWNLOADS: 'ArcMenu_Downloads', + MUSIC: 'ArcMenu_Music', + PICTURES: 'ArcMenu_Pictures', + VIDEOS: 'ArcMenu_Videos', + ARCMENU_SETTINGS: 'gnome-extensions prefs arcmenu@arcmenu.com', + FOLDER: 'ArcMenu_Folder', + OVERVIEW: 'ArcMenu_ActivitiesOverview', + SHOW_APPS: 'ArcMenu_ShowAllApplications', + RUN_COMMAND: 'ArcMenu_RunCommand', + SEPARATOR: 'ArcMenu_Separator', + SPACER: 'ArcMenu_Spacer', + SETTINGS: 'ArcMenu_Settings', + SHOW_DESKTOP: 'ArcMenu_ShowDesktop', + POWER_OPTIONS: 'ArcMenu_PowerOptions', + SETTINGS_MENU: 'ArcMenu_SettingsMenu', + SETTINGS_LAYOUT: 'ArcMenu_SettingsLayout', + SETTINGS_BUTTON: 'ArcMenu_SettingsButton', + SETTINGS_ABOUT: 'ArcMenu_SettingsAbout', + SETTINGS_THEME: 'ArcMenu_SettingsTheme', + PANEL_EXTENSION_SETTINGS: 'ArcMenu_PanelExtensionSettings', + ARCMENU_ICON: 'ArcMenu_ArcMenuIcon', +}; + +export const DistroIconsDisclaimer = 'All brand icons are trademarks of their respective owners.' + + ' The use of these trademarks does not indicate endorsement of the trademark holder ' + + 'by ArcMenu project, nor vice versa.' + + ' Please do not use brand logos for any purpose except to represent the company, ' + + 'product, or service to which they refer.' + + '\n\n• GNOME® - The GNOME name and logo are trademarks of the GNOME Foundation.' + + '\n\n• Ubuntu® - Ubuntu name and Ubuntu logo are trademarks of Canonical© Ltd.' + + '\n\n• Fedora® - Fedora and the Infinity design logo are trademarks of Red Hat, Inc.' + + '\n\n• Debian® - is a registered trademark owned by Software in the Public Interest.' + + '\n\n• Manjaro® - logo and name are trademarks of Manjaro GmbH & Co. KG' + + '\n\n• Pop_OS!® - logo and name are trademarks of system 76© Inc.' + + '\n\n• Arch Linux™ - The stylized Arch Linux logo is a recognized trademark of Arch Linux, ' + + 'copyright 2002–2017 Judd Vinet and Aaron Griffin.' + + '\n\n• openSUSE® - logo and name 2001–2020 SUSE LLC, © 2005–2020 openSUSE Contributors & others.' + + '\n\n• Raspberry Pi® - logo and name are part of Raspberry Pi Foundation UK Registered Charity 1129409' + + '\n\n• Kali Linux™ - logo and name are part of © OffSec Services Limited 2020' + + '\n\n• PureOS - logo and name are developed by members of the Purism community' + + '\n\n• Solus - logo and name are copyright © 2014–2018 by Solus Project' + + '\n\n• Gentoo Authors© - 2001–2020 Gentoo is a trademark of the Gentoo Foundation, Inc.' + + '\n\n• Voyager© Linux - name and logo' + + '\n\n• MX Linux© - 2020 - Linux - is the registered trademark of Linus Torvalds ' + + 'in the U.S. and other countries.' + + '\n\n• Red Hat, Inc.© - Copyright 2020 name and logo' + + '\n\n• Pardus - name and logo are copyright © 2003-2023 by TUBITAK ULAKBIM' + + '\n\n• ZORIN OS - The "Z" logomark is a registered trademark of Zorin Technology Group Ltd. ' + + 'Copyright © 2019 - 2021 Zorin Technology Group Ltd' + + '\n\n• NixOS - The logo is made available under a CC-BY license. ' + + 'Logo designed by Tim Cuthbertson (@timbertson)'; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/controller.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/controller.js new file mode 100644 index 0000000..a52ce0f --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/controller.js @@ -0,0 +1,535 @@ +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import Shell from 'gi://Shell'; + +import {InputSourceManager} from 'resource:///org/gnome/shell/ui/status/keyboard.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +import {ArcMenuManager} from './arcmenuManager.js'; +import * as Constants from './constants.js'; +import * as Keybinder from './keybinder.js'; +import {MenuButton} from './menuButton.js'; +import * as Theming from './theming.js'; +import {StandaloneRunner} from './standaloneRunner.js'; +import * as Utils from './utils.js'; + +export const MenuSettingsController = class { + constructor(panelInfo, monitorIndex) { + this.panelInfo = panelInfo; + this.panel = panelInfo.panel; + this.monitorIndex = monitorIndex; + this.isPrimaryPanel = panelInfo.isPrimaryPanel; + + this._extension = ArcMenuManager.extension; + this._settingsControllers = ArcMenuManager.settingsControllers; + this._settings = ArcMenuManager.settings; + + // Allow other extensions and DBus command to open/close ArcMenu + if (!global.toggleArcMenu) { + global.toggleArcMenu = () => this.toggleMenus(); + this._service = new Utils.DBusService(); + this._service.ToggleArcMenu = () => { + this.toggleMenus(); + }; + } + + this._settingsConnections = new Utils.SettingsConnectionsHandler(this._settings); + this._menuButton = new MenuButton(panelInfo, this.monitorIndex); + + if (this.isPrimaryPanel) { + this._overrideOverlayKey = new Keybinder.OverrideOverlayKey(); + this._customKeybinding = new Keybinder.CustomKeybinding(this._settings); + this._appSystem = Shell.AppSystem.get_default(); + this._updateHotKeyBinder(); + this._initRecentAppsTracker(); + this._inputSourceManagerOverride(); + } + + this._setButtonAppearance(); + this._setButtonText(); + this._setButtonIcon(); + this._setButtonIconSize(); + this._setButtonIconPadding(); + this._configureActivitiesButton(); + } + + _inputSourceManagerOverride() { + // Add ArcMenu as a valid option for "per window" input source switching. + this._inputSourcesSettings = new Gio.Settings({schema_id: 'org.gnome.desktop.input-sources'}); + this._perWindowChangedId = this._inputSourcesSettings.connect('changed::per-window', () => { + if (this._inputSourcesSettings.get_boolean('per-window')) + return; + + const menus = this._getAllMenus(); + menus.forEach(menu => { + delete menu._inputSources; + delete menu._currentSource; + }); + }); + + this._inputSourceManagerProto = InputSourceManager.prototype; + this._origGetCurrentWindow = this._inputSourceManagerProto._getCurrentWindow; + + this._inputSourceManagerProto._getCurrentWindow = () => { + const openedArcMenu = this._getOpenedArcMenu(); + if (openedArcMenu) + return openedArcMenu; + else if (Main.overview.visible) + return Main.overview; + else + return global.display.focus_window; + }; + } + + _getOpenedArcMenu() { + const menus = this._getAllMenus(); + for (let i = 0; i < menus.length; i++) { + if (menus[i].isOpen) + return menus[i]; + } + + return null; + } + + _getAllMenus() { + const menus = []; + for (let i = 0; i < this._settingsControllers.length; i++) { + const menuButton = this._settingsControllers[i]._menuButton; + menus.push(menuButton.arcMenu); + } + if (this.runnerMenu) + menus.push(this.runnerMenu.arcMenu); + + return menus; + } + + connectSettingsEvents() { + this._settingsConnections.connect('override-menu-theme', 'menu-background-color', 'menu-foreground-color', + 'menu-border-color', 'menu-border-width', 'menu-border-radius', 'menu-font-size', 'menu-separator-color', + 'menu-item-hover-bg-color', 'menu-item-hover-fg-color', 'menu-item-active-bg-color', + 'menu-item-active-fg-color', 'menu-button-fg-color', 'menu-button-bg-color', + 'menu-button-hover-bg-color', 'menu-button-hover-fg-color', 'menu-button-active-bg-color', + 'menu-button-active-fg-color', 'menu-button-border-radius', 'menu-button-border-width', + 'menu-button-border-color', 'menu-arrow-rise', 'search-entry-border-radius', + this._overrideMenuTheme.bind(this)); + + this._settingsConnections.connect('arcmenu-hotkey', 'runner-hotkey', this._updateHotKeyBinder.bind(this)); + + this._settingsConnections.connect('position-in-panel', 'menu-button-position-offset', + this._setButtonPosition.bind(this)); + + this._settingsConnections.connect('menu-button-icon', 'distro-icon', 'arc-menu-icon', 'custom-menu-button-icon', + this._setButtonIcon.bind(this)); + + this._settingsConnections.connect('directory-shortcuts', 'application-shortcuts', 'extra-categories', + 'power-options', 'show-external-devices', 'show-bookmarks', 'disable-user-avatar', + 'avatar-style', 'enable-activities-shortcut', 'enable-horizontal-flip', 'power-display-style', + 'searchbar-default-bottom-location', 'searchbar-default-top-location', 'multi-lined-labels', + 'apps-show-extra-details', 'show-search-result-details', 'search-provider-open-windows', + 'search-provider-recent-files', 'misc-item-icon-size', 'windows-disable-pinned-apps', + 'disable-scrollview-fade-effect', 'windows-disable-frequent-apps', 'default-menu-view', + 'default-menu-view-tognee', 'alphabetize-all-programs', 'menu-item-grid-icon-size', + 'menu-item-icon-size', 'button-item-icon-size', 'quicklinks-item-icon-size', + 'menu-item-category-icon-size', 'category-icon-type', 'shortcut-icon-type', + 'arcmenu-extra-categories-links', 'arcmenu-extra-categories-links-location', + 'runner-show-frequent-apps', 'default-menu-view-redmond', 'disable-recently-installed-apps', + 'runner-search-display-style', 'raven-search-display-style', 'custom-grid-icon-size', 'show-category-sub-menus', + this._recreateMenuLayout.bind(this)); + + this._settingsConnections.connect('left-panel-width', 'right-panel-width', 'menu-width-adjustment', + this._updateMenuWidth.bind(this)); + + this._settingsConnections.connect('pinned-apps', 'enable-weather-widget-unity', 'enable-clock-widget-unity', + 'enable-weather-widget-raven', 'enable-clock-widget-raven', this._updatePinnedApps.bind(this)); + + this._settingsConnections.connect('menu-position-alignment', this._setMenuPositionAlignment.bind(this)); + this._settingsConnections.connect('menu-button-appearance', this._setButtonAppearance.bind(this)); + this._settingsConnections.connect('custom-menu-button-text', this._setButtonText.bind(this)); + this._settingsConnections.connect('custom-menu-button-icon-size', this._setButtonIconSize.bind(this)); + this._settingsConnections.connect('button-padding', this._setButtonIconPadding.bind(this)); + this._settingsConnections.connect('menu-height', this._updateMenuHeight.bind(this)); + this._settingsConnections.connect('enable-unity-homescreen', this._setDefaultMenuView.bind(this)); + this._settingsConnections.connect('menu-layout', this._changeMenuLayout.bind(this)); + this._settingsConnections.connect('runner-position', this._updateLocation.bind(this)); + this._settingsConnections.connect('show-activities-button', this._configureActivitiesButton.bind(this)); + this._settingsConnections.connect('force-menu-location', this._forceMenuLocation.bind(this)); + } + + _overrideMenuTheme() { + if (!this.isPrimaryPanel) + return; + + if (this._writeTimeoutId) + GLib.source_remove(this._writeTimeoutId); + + this._writeTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 300, () => { + Theming.updateStylesheet(); + this._writeTimeoutId = null; + return GLib.SOURCE_REMOVE; + }); + } + + _recreateMenuLayout() { + this._menuButton.createMenuLayout(); + if (this.runnerMenu) + this.runnerMenu.createMenuLayout(); + } + + _forceMenuLocation() { + this._menuButton.forceMenuLocation(); + } + + _initRecentAppsTracker() { + this._appList = this._listAllApps(); + + this._reloadApplicationsWorkId = Main.initializeDeferredWork(this._menuButton, () => this._reloadApplications()); + + this._installedChangedId = this._appSystem.connect('installed-changed', () => { + Main.queueDeferredWork(this._reloadApplicationsWorkId); + }); + } + + _reloadApplications() { + const isDisabled = this._settings.get_boolean('disable-recently-installed-apps'); + const appList = this._listAllApps(); + + // Filter to find if a new application has been installed + const newAppsList = appList.filter(app => !this._appList.includes(app)); + this._appList = appList; + + if (newAppsList.length && !isDisabled) { + // A new app has been installed, Save it in settings + const recentApps = this._settings.get_strv('recently-installed-apps'); + const newRecentApps = [...new Set(recentApps.concat(newAppsList))]; + this._settings.set_strv('recently-installed-apps', newRecentApps); + } + + for (let i = 0; i < this._settingsControllers.length; i++) { + const menuButton = this._settingsControllers[i]._menuButton; + menuButton.reloadApplications(); + } + + if (this.runnerMenu) + this.runnerMenu.reloadApplications(); + } + + _listAllApps() { + const appList = this._appSystem.get_installed().filter(appInfo => { + try { + appInfo.get_id(); // catch invalid file encodings + } catch (e) { + return false; + } + return appInfo.should_show(); + }); + return appList.map(app => app.get_id()); + } + + _updateLocation() { + this._menuButton.updateLocation(); + if (this.runnerMenu) + this.runnerMenu.updateLocation(); + } + + _changeMenuLayout() { + this._menuButton.createMenuLayout(); + } + + _setDefaultMenuView() { + this._menuButton.setDefaultMenuView(); + } + + toggleStandaloneRunner() { + this._closeAllArcMenus(); + if (this.runnerMenu) + this.runnerMenu.toggleMenu(); + } + + toggleMenus() { + if (this.runnerMenu && this.runnerMenu.arcMenu.isOpen) + this.runnerMenu.toggleMenu(); + if (global.dashToPanel || global.azTaskbar) { + const MultipleArcMenus = this._settingsControllers.length > 1; + const ShowArcMenuOnPrimaryMonitor = this._settings.get_boolean('hotkey-open-primary-monitor'); + if (MultipleArcMenus && ShowArcMenuOnPrimaryMonitor) + this._toggleMenuOnMonitor(Main.layoutManager.primaryMonitor); + else if (MultipleArcMenus && !ShowArcMenuOnPrimaryMonitor) + this._toggleMenuOnMonitor(Main.layoutManager.currentMonitor); + else + this._menuButton.toggleMenu(); + } else { + this._menuButton.toggleMenu(); + } + } + + _toggleMenuOnMonitor(monitor) { + let currentMonitorIndex = 0; + for (let i = 0; i < this._settingsControllers.length; i++) { + const menuButton = this._settingsControllers[i]._menuButton; + const {monitorIndex} = this._settingsControllers[i]; + + if (monitor.index === monitorIndex) { + currentMonitorIndex = i; + } else { + if (menuButton.arcMenu.isOpen) + menuButton.toggleMenu(); + menuButton.closeContextMenu(); + } + } + + // open the current monitors menu + this._settingsControllers[currentMonitorIndex]._menuButton.toggleMenu(); + } + + _closeAllArcMenus() { + for (let i = 0; i < this._settingsControllers.length; i++) { + const menuButton = this._settingsControllers[i]._menuButton; + if (menuButton.arcMenu.isOpen) + menuButton.toggleMenu(); + menuButton.closeContextMenu(); + } + } + + _updateMenuHeight() { + this._menuButton.updateHeight(); + } + + _updateMenuWidth() { + this._menuButton.updateWidth(); + } + + _updatePinnedApps() { + this._menuButton.loadPinnedApps(); + } + + _updateHotKeyBinder() { + if (!this.isPrimaryPanel) + return; + + const [runnerHotkey] = this._settings.get_strv('runner-hotkey'); + const [menuHotkey] = this._settings.get_strv('arcmenu-hotkey'); + + this._customKeybinding.unbind('ToggleArcMenu'); + this._customKeybinding.unbind('ToggleRunnerMenu'); + this._overrideOverlayKey.disable(); + + if (runnerHotkey) { + if (!this.runnerMenu) + this.runnerMenu = new StandaloneRunner(); + + if (runnerHotkey === Constants.SUPER_L) { + this._overrideOverlayKey.enable(() => this.toggleStandaloneRunner()); + } else { + this._customKeybinding.bind('ToggleRunnerMenu', 'runner-hotkey', + () => this.toggleStandaloneRunner()); + } + } else if (this.runnerMenu) { + this.runnerMenu.destroy(); + this.runnerMenu = null; + } + + if (menuHotkey === Constants.SUPER_L) { + this._overrideOverlayKey.disable(); + this._overrideOverlayKey.enable(() => this.toggleMenus()); + } else if (menuHotkey) { + this._customKeybinding.bind('ToggleArcMenu', 'arcmenu-hotkey', + () => this.toggleMenus()); + } + } + + _setButtonPosition() { + if (this._isButtonEnabled()) { + this._removeMenuButtonFromMainPanel(); + this._addMenuButtonToMainPanel(); + this._setMenuPositionAlignment(); + } + } + + _setMenuPositionAlignment() { + this._menuButton.setMenuPositionAlignment(); + } + + _setButtonAppearance() { + const menuButtonAppearance = this._settings.get_enum('menu-button-appearance'); + const {menuButtonWidget} = this._menuButton; + + this._menuButton.container.set_width(-1); + this._menuButton.container.set_height(-1); + menuButtonWidget.show(); + + switch (menuButtonAppearance) { + case Constants.MenuButtonAppearance.TEXT: + menuButtonWidget.showText(); + menuButtonWidget.setLabelStyle(null); + break; + case Constants.MenuButtonAppearance.ICON_TEXT: + menuButtonWidget.showIconText(); + menuButtonWidget.setLabelStyle('padding-left: 5px;'); + break; + case Constants.MenuButtonAppearance.TEXT_ICON: + menuButtonWidget.showTextIcon(); + menuButtonWidget.setLabelStyle('padding-right: 5px;'); + break; + case Constants.MenuButtonAppearance.NONE: + menuButtonWidget.hide(); + this._menuButton.container.set_width(0); + this._menuButton.container.set_height(0); + break; + case Constants.MenuButtonAppearance.ICON: + default: + menuButtonWidget.showIcon(); + } + } + + _setButtonText() { + const {menuButtonWidget} = this._menuButton; + const label = menuButtonWidget.getPanelLabel(); + + const customTextLabel = this._settings.get_string('custom-menu-button-text'); + label.set_text(customTextLabel); + } + + _setButtonIcon() { + const path = this._settings.get_string('custom-menu-button-icon'); + const {menuButtonWidget} = this._menuButton; + const stIcon = menuButtonWidget.getPanelIcon(); + + const iconString = Utils.getMenuButtonIcon(path); + stIcon.set_gicon(Gio.icon_new_for_string(iconString)); + } + + _setButtonIconSize() { + const {menuButtonWidget} = this._menuButton; + const stIcon = menuButtonWidget.getPanelIcon(); + const iconSize = this._settings.get_double('custom-menu-button-icon-size'); + const size = iconSize; + stIcon.icon_size = size; + } + + _setButtonIconPadding() { + const padding = this._settings.get_int('button-padding'); + if (padding > -1) + this._menuButton.style = `-natural-hpadding: ${padding * 2}px; -minimum-hpadding: ${padding}px;`; + else + this._menuButton.style = null; + + const parent = this._menuButton.get_parent(); + if (!parent) + return; + const children = parent.get_children(); + let actorIndex = 0; + + if (children.length > 1) + actorIndex = children.indexOf(this._menuButton); + + + parent.remove_child(this._menuButton); + parent.insert_child_at_index(this._menuButton, actorIndex); + } + + _getMenuPosition() { + const offset = this._settings.get_int('menu-button-position-offset'); + switch (this._settings.get_enum('position-in-panel')) { + case Constants.MenuPosition.CENTER: + return [offset, 'center']; + case Constants.MenuPosition.RIGHT: { + // get number of childrens in rightBox (without arcmenu) + let nChildren = this.panel._rightBox.get_n_children(); + nChildren -= this.panel.statusArea.ArcMenu !== undefined; + // position where icon should go, + // offset = 0, icon should be last + // offset = 1, icon should be second last + const order = Math.clamp(nChildren - offset, 0, nChildren); + return [order, 'right']; + } + case Constants.MenuPosition.LEFT: + default: + return [offset, 'left']; + } + } + + _configureActivitiesButton() { + const showActivities = this._settings.get_boolean('show-activities-button'); + if (this.panel.statusArea.activities) + this.panel.statusArea.activities.visible = showActivities; + } + + _addMenuButtonToMainPanel() { + const [position, box] = this._getMenuPosition(); + this.panel.addToStatusArea('ArcMenu', this._menuButton, position, box); + } + + _removeMenuButtonFromMainPanel() { + this.panel.menuManager.removeMenu(this._menuButton.arcMenu); + this.panel.menuManager.removeMenu(this._menuButton.arcMenuContextMenu); + this.panel.statusArea['ArcMenu'] = null; + } + + enableButton() { + this._addMenuButtonToMainPanel(); + this._menuButton.initiate(); + } + + _disableButton() { + this._removeMenuButtonFromMainPanel(); + if (this.panel.statusArea.activities) + this.panel.statusArea.activities.visible = true; + this._menuButton.destroy(); + } + + _isButtonEnabled() { + return this.panel && this.panel.statusArea['ArcMenu'] !== null; + } + + destroy() { + if (this._service) { + this._service.destroy(); + this._service = null; + } + + if (global.toggleArcMenu) + delete global.toggleArcMenu; + + if (this._inputSourceManagerProto) { + this._inputSourceManagerProto._getCurrentWindow = this._origGetCurrentWindow; + delete this._inputSourceManagerProto; + } + + if (this._perWindowChangedId) { + this._inputSourcesSettings.disconnect(this._perWindowChangedId); + this._perWindowChangedId = null; + } + + if (this._writeTimeoutId) { + GLib.source_remove(this._writeTimeoutId); + this._writeTimeoutId = null; + } + + if (this._installedChangedId) { + this._appSystem.disconnect(this._installedChangedId); + this._installedChangedId = null; + } + + if (this.runnerMenu) { + this.runnerMenu.destroy(); + this.runnerMenu = null; + } + + this._settingsConnections.destroy(); + this._settingsConnections = null; + + if (this._isButtonEnabled()) + this._disableButton(); + else + this._menuButton.destroy(); + + if (this.isPrimaryPanel) { + this._overrideOverlayKey.destroy(); + this._customKeybinding.destroy(); + } + + this._menuButton = null; + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/extension.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/extension.js new file mode 100644 index 0000000..ea5d677 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/extension.js @@ -0,0 +1,237 @@ +import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +import {ArcMenuManager} from './arcmenuManager.js'; +import * as Constants from './constants.js'; +import {MenuSettingsController} from './controller.js'; +import {SearchProviderEmitter} from './searchProviders/searchProviderEmitter.js'; +import * as Theming from './theming.js'; + +import * as Utils from './utils.js'; + +export default class ArcMenu extends Extension { + constructor(metaData) { + super(metaData); + this._realHasOverview = Main.sessionMode.hasOverview; + } + + enable() { + this._arcmenuManager = new ArcMenuManager(this); + this.settings = this.getSettings(); + + this._convertOldSettings(this.settings); + this.searchProviderEmitter = new SearchProviderEmitter(); + + const hideOverviewOnStartup = this.settings.get_boolean('hide-overview-on-startup'); + if (hideOverviewOnStartup && Main.layoutManager._startingUp) { + Main.sessionMode.hasOverview = false; + Main.layoutManager.connectObject('startup-complete', () => { + Main.sessionMode.hasOverview = this._realHasOverview; + }, this); + // handle Ubuntu's method + if (Main.layoutManager.startInOverview) + Main.layoutManager.startInOverview = false; + } + + this.settings.connectObject('changed::multi-monitor', () => this._reload(), this); + this.settings.connectObject('changed::dash-to-panel-standalone', () => this._reload(), this); + this.settingsControllers = []; + + Theming.createStylesheet(); + + this._enableButtons(); + + // dash to panel might get enabled after ArcMenu + Main.extensionManager.connectObject('extension-state-changed', (data, extension) => { + const isDtp = extension.uuid === Constants.DASH_TO_PANEL_UUID; + const isAzTaskbar = extension.uuid === Constants.AZTASKBAR_UUID; + const isEnabled = extension.state === Utils.ExtensionState.ACTIVE; + const isDisabled = extension.state === Utils.ExtensionState.INACTIVE; + + if ((isDtp || isAzTaskbar) && (isEnabled || isDisabled)) { + this._disconnectExtensionSignals(); + this._connectExtensionSignals(); + this._reload(); + } + }, this); + + // listen to dash to panel if they are compatible and already enabled + this._connectExtensionSignals(); + } + + disable() { + this.searchProviderEmitter.destroy(); + delete this.searchProviderEmitter; + + Main.sessionMode.hasOverview = this._realHasOverview; + + Theming.deleteStylesheet(); + + this._disconnectExtensionSignals(); + + this._disableButtons(); + this.settingsControllers = null; + + this._arcmenuManager.destroy(); + this._arcmenuManager = null; + + Main.layoutManager.disconnectObject(this); + Main.extensionManager.disconnectObject(this); + this.settings.disconnectObject(this); + this.settings = null; + } + + // Following settings changed in v52. + // Keep this for a few releases to convert old settings to new format + _convertOldSettings(settings) { + Utils.convertOldSetting(settings, 'pinned-app-list', 'pinned-apps'); + Utils.convertOldSetting(settings, 'directory-shortcuts-list', 'directory-shortcuts'); + Utils.convertOldSetting(settings, 'application-shortcuts-list', 'application-shortcuts'); + Utils.convertOldSetting(settings, 'az-extra-buttons', 'az-layout-extra-shortcuts'); + Utils.convertOldSetting(settings, 'eleven-extra-buttons', 'eleven-layout-extra-shortcuts'); + Utils.convertOldSetting(settings, 'insider-extra-buttons', 'insider-layout-extra-shortcuts'); + Utils.convertOldSetting(settings, 'windows-extra-buttons', 'windows-layout-extra-shortcuts'); + Utils.convertOldSetting(settings, 'unity-extra-buttons', 'unity-layout-extra-shortcuts'); + Utils.convertOldSetting(settings, 'brisk-extra-shortcuts', 'brisk-layout-extra-shortcuts'); + Utils.convertOldSetting(settings, 'mint-extra-buttons', 'mint-layout-extra-shortcuts'); + Utils.convertOldSetting(settings, 'context-menu-shortcuts', 'context-menu-items'); + } + + _isExtensionActive(uuid) { + const extension = Main.extensionManager.lookup(uuid); + if (extension?.state === Utils.ExtensionState.ACTIVE) + return true; + + return false; + } + + /** + * + * @param {*} panel Dash to Panel's 'panel' + * @param {boolean} panelExtensionEnabled is Dash to Panel enabled + * @returns {boolean} + * @description Returns true if `panel` isPrimary and isStandalone\ + * and ArcMenu setting 'dash-to-panel-standalone' is enabled. + */ + _isPrimaryStandalonePanel(panel, panelExtensionEnabled) { + const standalone = this.settings.get_boolean('dash-to-panel-standalone'); + if (!standalone) + return false; + + const dtpEnabled = global.dashToPanel && panelExtensionEnabled; + if (!dtpEnabled) + return false; + + const isPrimaryStandalone = panel.isPrimary && panel.isStandalone; + if (isPrimaryStandalone) + return true; + + return false; + } + + _connectExtensionSignals() { + const dtpActive = this._isExtensionActive(Constants.DASH_TO_PANEL_UUID); + if (dtpActive && global.dashToPanel) + global.dashToPanel._panelsCreatedId = global.dashToPanel.connect('panels-created', () => this._reload()); + + const azTaskbarActive = this._isExtensionActive(Constants.AZTASKBAR_UUID); + if (azTaskbarActive && global.azTaskbar) + global.azTaskbar._panelsCreatedId = global.azTaskbar.connect('panels-created', () => this._reload()); + } + + _disconnectExtensionSignals() { + if (global.dashToPanel?._panelsCreatedId) { + global.dashToPanel.disconnect(global.dashToPanel._panelsCreatedId); + delete global.dashToPanel._panelsCreatedId; + } + if (global.azTaskbar?._panelsCreatedId) { + global.azTaskbar.disconnect(global.azTaskbar._panelsCreatedId); + delete global.azTaskbar._panelsCreatedId; + } + } + + _reload() { + this._disableButtons(); + this._enableButtons(); + } + + _enableButtons() { + const multiMonitor = this.settings.get_boolean('multi-monitor'); + + let panelExtensionEnabled = false; + let panels; + + const azTaskbarActive = this._isExtensionActive(Constants.AZTASKBAR_UUID); + const dtpActive = this._isExtensionActive(Constants.DASH_TO_PANEL_UUID); + + if (dtpActive && global.dashToPanel?.panels) { + panels = global.dashToPanel.panels.filter(p => p); + panelExtensionEnabled = true; + } else if (azTaskbarActive && global.azTaskbar?.panels) { + panels = global.azTaskbar.panels.filter(p => p); + panelExtensionEnabled = true; + } else { + panels = [Main.panel]; + } + + const panelsCount = multiMonitor ? panels.length : Math.min(panels.length, 1); + for (var i = 0; i < panelsCount; i++) { + if (!panels[i]) { + console.log(`ArcMenu Error: panel ${i} not found. Skipping...`); + continue; + } + + let panel, panelBox, panelParent; + if (panelExtensionEnabled) { + panel = panels[i].panel; + panelBox = dtpActive ? panels[i].panelBox : panels[i]; + panelParent = panels[i]; + } else { + panel = panels[i]; + panelBox = Main.layoutManager.panelBox; + panelParent = Main.panel; + } + + const isPrimaryStandalone = this._isPrimaryStandalonePanel(panelParent, panelExtensionEnabled); + if (isPrimaryStandalone) + panel = Main.panel; + + const primaryPanelIndex = Main.layoutManager.primaryMonitor?.index; + + let monitorIndex = 0; + if (panelParent.monitor) // App Icons Taskbar 'panelParent' may be Main.panel, which doesnt have a 'monitor' property. + monitorIndex = panelParent.monitor.index; + else if (panel === Main.panel) + monitorIndex = primaryPanelIndex ?? 0; + + const isPrimaryPanel = monitorIndex === primaryPanelIndex; + const panelInfo = {panel, panelBox, panelParent, isPrimaryPanel}; + + const settingsController = new MenuSettingsController(panelInfo, monitorIndex); + + if (panelExtensionEnabled) + panel._amDestroyId = panel.connect('destroy', () => this._disableButton(settingsController)); + + settingsController.enableButton(); + settingsController.connectSettingsEvents(); + this.settingsControllers.push(settingsController); + } + } + + _disableButtons() { + for (let i = this.settingsControllers.length - 1; i >= 0; --i) { + const sc = this.settingsControllers[i]; + this._disableButton(sc); + } + } + + _disableButton(controller) { + if (controller.panel?._amDestroyId) { + controller.panel.disconnect(controller.panel._amDestroyId); + delete controller.panel._amDestroyId; + } + + this.settingsControllers.splice(this.settingsControllers.indexOf(controller), 1); + controller.destroy(); + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/iconGrid.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/iconGrid.js new file mode 100644 index 0000000..0f494d7 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/iconGrid.js @@ -0,0 +1,587 @@ +import Clutter from 'gi://Clutter'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {ArcMenuManager} from './arcmenuManager.js'; +import * as MW from './menuWidgets.js'; + +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; +import * as Params from 'resource:///org/gnome/shell/misc/params.js'; + +export const DragLocation = { + INVALID: 0, + START_EDGE: 1, + ON_ICON: 2, + END_EDGE: 3, + EMPTY_SPACE: 4, + TOP_EDGE: 5, + BOTTOM_EDGE: 6, +}; + +// eslint-disable-next-line jsdoc/require-jsdoc +function swap(value, length) { + return length - value - 1; +} + +// eslint-disable-next-line jsdoc/require-jsdoc +function animateIconPosition(icon, box, nChangedIcons) { + if (!icon.has_allocation() || icon.allocation.equal(box) || icon.opacity === 0) { + icon.allocate(box); + return false; + } + + icon.save_easing_state(); + icon.set_easing_mode(Clutter.AnimationMode.EASE_OUT_QUAD); + icon.set_easing_delay(nChangedIcons * 10); + + icon.allocate(box); + + icon.restore_easing_state(); + + return true; +} + +export const IconGridLayout = GObject.registerClass({ + Properties: { + 'column-spacing': GObject.ParamSpec.int('column-spacing', + 'Column spacing', 'Column spacing', + GObject.ParamFlags.READWRITE, + 0, GLib.MAXINT32, 0), + 'row-spacing': GObject.ParamSpec.int('row-spacing', + 'Row spacing', 'Row spacing', + GObject.ParamFlags.READWRITE, + 0, GLib.MAXINT32, 0), + 'columns': GObject.ParamSpec.int('columns', + 'Columns', 'Columns', + GObject.ParamFlags.READWRITE, + 1, GLib.MAXINT32, 1), + 'force-columns': GObject.ParamSpec.int('force-columns', + 'Force Columns', 'Force Columns', + GObject.ParamFlags.READWRITE, + 0, GLib.MAXINT32, 0), + 'halign': GObject.ParamSpec.enum('halign', + 'Horizontal page align', + 'Horizontal page align', + GObject.ParamFlags.READWRITE, + Clutter.ActorAlign.$gtype, + Clutter.ActorAlign.FILL), + 'first-row-align': GObject.ParamSpec.enum('first-row-align', + 'First row align', 'First row align', + GObject.ParamFlags.READWRITE, + Clutter.ActorAlign.$gtype, + Clutter.ActorAlign.CENTER), + + }, +}, class IconGridLayout extends Clutter.LayoutManager { + _init(params = {}) { + super._init(params); + + this._sizeChanged = false; + this._width = 0; + this._height = 0; + this._items = new Map(); + this._children = []; + this._childrenMaxSize = null; + } + + _getChildrenMaxSize() { + if (!this._childrenMaxSize) { + let minWidth = 0; + let minHeight = 0; + + this._children.forEach(item => { + const childMinHeight = item.get_preferred_height(-1)[1]; + const childMinWidth = item.get_preferred_width(-1)[1]; + + minWidth = Math.max(minWidth, childMinWidth); + minHeight = Math.max(minHeight, childMinHeight); + }); + + this._childrenMaxSize = [minWidth, minHeight]; + } + return this._childrenMaxSize; + } + + _calculateSpacing(width, childWidth) { + if (this.halign !== Clutter.ActorAlign.CENTER) + return 0; + + let columns = 0; + if (this.firstRowAlign === Clutter.ActorAlign.CENTER) { + const visibleChildren = this._children.filter(child => child.visible); + // if the amount of visiblechildren is less than the amount of columns + // set columns to visiblechildren.length in order to center the items + columns = visibleChildren.length < this.columns ? visibleChildren.length : this.columns; + } else { + columns = this.columns; + } + + const nColumns = columns; + const usedWidth = childWidth * nColumns; + const columnSpacing = this.columnSpacing * (nColumns - 1); + + const emptyHSpace = width - usedWidth - columnSpacing; + const leftEmptySpace = Math.floor(emptyHSpace / 2); + + return leftEmptySpace; + } + + _unlinkItem(item) { + const itemData = this._items.get(item); + + item.disconnect(itemData.destroyId); + item.disconnect(itemData.visibleId); + item.disconnect(itemData.queueRelayoutId); + + this._items.delete(item); + } + + _removeItemData(item) { + this._unlinkItem(item); + + const itemIndex = this._children.indexOf(item); + this._children.splice(itemIndex, 1); + } + + _addItem(item, index) { + if (index === -1) + index = this._children.length; + + this._items.set(item, { + actor: item, + destroyId: item.connect('destroy', () => this._removeItemData(item)), + visibleId: item.connect('notify::visible', () => { + this._shouldEaseItems = true; + }), + queueRelayoutId: item.connect('queue-relayout', () => { + this._childrenMaxSize = null; + }), + }); + + this._children.splice(index, 0, item); + } + + vfunc_set_container(container) { + this._container = container; + } + + vfunc_get_preferred_width() { + return [5, this._width]; + } + + vfunc_get_preferred_height() { + const children = this._children; + const totalColumns = this.columns; + + let minRowHeight = 0; + let natRowHeight = 0; + let natHeight = 0; + let firstRow = true; + + for (let i = 0; i < children.length; i += 1) { + const child = children[i]; + if (!child.visible) + continue; + + const [childMinHeight, childNatHeight] = child.get_preferred_height(-1); + + minRowHeight = Math.max(minRowHeight, childMinHeight); + natRowHeight = Math.max(natRowHeight, childNatHeight); + + const newRow = i % totalColumns === 0; + if (firstRow && newRow) { + firstRow = false; + natHeight = natRowHeight; // + this.rowSpacing + PADDING; + natRowHeight = 0; + } else if (newRow) { + natHeight += natRowHeight + this.rowSpacing; + natRowHeight = 0; + } + } + + this._height = natHeight; + return [natHeight, minRowHeight]; + } + + vfunc_allocate() { + const children = this._children; + const shouldEaseItems = this._shouldEaseItems; + const sizeChanged = this._sizeChanged; + const isRtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL; + + const [childWidth, childHeight_] = this._getChildrenMaxSize(); + const xOffset = this._calculateSpacing(this._width, childWidth); + + const childBox = new Clutter.ActorBox(); + + let nChangedIcons = 0; + let rowHeight = 0; + let y = 0; + + for (let i = 0; i < children.length; i += 1) { + const child = children[i]; + + let column = i % this.columns; + if (isRtl) + column = swap(column, this.columns); + + const newRow = column === 0; + + let [,, naturalWidth, naturalHeight] = child.get_preferred_size(); + + const xFill = child.x_align === Clutter.ActorAlign.FILL; + if (xFill) + naturalWidth = this._width / this.columns; + + const x = xOffset + column * (naturalWidth + this.columnSpacing); + + // The first item in a row will determine the row height + if (newRow) { + y += rowHeight; + rowHeight = naturalHeight + this.rowSpacing; + } + + childBox.set_origin(Math.floor(x), Math.floor(y)); + childBox.set_size(naturalWidth, naturalHeight); + + if (!shouldEaseItems || sizeChanged) + child.allocate(childBox); + else if (animateIconPosition(child, childBox, nChangedIcons)) + nChangedIcons++; + } + + this._sizeChanged = false; + this._shouldEaseItems = false; + } + + addItem(item, index = -1) { + if (this._items.has(item)) + throw new Error(`Item ${item} already added to IconGridLayout`); + + if (!this._container) + return; + + this._shouldEaseItems = true; + + this._container.add_child(item); + this._addItem(item, index); + } + + appendChild(item) { + this.addItem(item); + } + + moveItem(item, newPosition) { + if (!this._items.has(item)) + throw new Error(`Item ${item} is not part of the IconGridLayout`); + + this._shouldEaseItems = true; + + this._removeItemData(item); + + this._addItem(item, newPosition); + } + + removeItem(item) { + if (!this._items.has(item)) + throw new Error(`Item ${item} is not part of the IconGridLayout`); + + if (!this._container) + return; + + this._shouldEaseItems = true; + + this._container.remove_child(item); + this._removeItemData(item); + } + + removeAllItems() { + for (let i = this._children.length - 1; i >= 0; --i) { + const item = this._children[i]; + this.removeItem(item); + } + } + + getItemPosition(item) { + if (!this._items.has(item)) + return -1; + + return this._children.indexOf(item); + } + + getItemAt(position) { + if (position < 0 || position >= this._children.length) + return null; + + return this._children[position]; + } + + getDropTarget(x, y) { + const emptySpace = 5; + + const isRtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL; + + const gridWidth = this._width; + const gridHeight = this._height; + + const inTopEmptySpace = y < -emptySpace; + const inLeftEmptySpace = x < -emptySpace; + const inRightEmptySpace = x > emptySpace + gridWidth; + const inBottomEmptySpace = y > emptySpace + gridHeight; + + if (inTopEmptySpace || inBottomEmptySpace || inRightEmptySpace || inLeftEmptySpace) + return [0, DragLocation.INVALID]; + + const halfHSpacing = this.rowSpacing / 2; + const halfVSpacing = this.columnSpacing / 2; + const nRows = Math.ceil(this.nChildren / this.columns); + const singleColumnGrid = this.columns === 1; + + for (let i = 0; i < this._children.length; i++) { + const item = this._children[i]; + + // skip hidden item. the item current being dragged. + if (item.opacity === 0 || !item.visible) + continue; + + const childBox = item.allocation; + + const firstInRow = i % this.columns === 0; + const lastInRow = i % this.columns === this.columns - 1; + const firstInColumn = Math.floor(i / this.columns) === 0; + const lastInColumn = Math.floor(i / this.columns) === nRows - 1; + + // Check icon boundaries + if (!singleColumnGrid && ((inLeftEmptySpace && firstInRow) || + (inRightEmptySpace && lastInRow))) { + if (y < childBox.y1 - halfVSpacing || + y > childBox.y2 + halfVSpacing) + continue; + } else if (singleColumnGrid && ((inTopEmptySpace && firstInColumn) || + (inBottomEmptySpace && lastInColumn))) { + if (x < childBox.x1 - halfHSpacing || + x > childBox.x2 + halfHSpacing) + continue; + } else { + // eslint-disable-next-line no-lonely-if + if (x < childBox.x1 - halfHSpacing || + x > childBox.x2 + halfHSpacing || + y < childBox.y1 - halfVSpacing || + y > childBox.y2 + halfVSpacing) + continue; + } + + const leftDividerLeeway = Math.round(item.get_preferred_width(-1)[1] / 5); + const rightDividerLeeway = Math.round(item.get_preferred_width(-1)[1] / 5); + const topDividerLeeway = Math.round(item.get_preferred_height(-1)[1] / 5); + const bottomDividerLeeway = Math.round(item.get_preferred_height(-1)[1] / 5); + + let dragLocation; + if (!singleColumnGrid && x < childBox.x1 + leftDividerLeeway) + dragLocation = DragLocation.START_EDGE; + else if (!singleColumnGrid && x > childBox.x2 - rightDividerLeeway) + dragLocation = DragLocation.END_EDGE; + else if (singleColumnGrid && y < childBox.y1 + topDividerLeeway) + dragLocation = DragLocation.TOP_EDGE; + else if (singleColumnGrid && y > childBox.y2 - bottomDividerLeeway) + dragLocation = DragLocation.BOTTOM_EDGE; + else + dragLocation = DragLocation.ON_ICON; + + if (isRtl) { + if (dragLocation === DragLocation.START_EDGE) + dragLocation = DragLocation.END_EDGE; + else if (dragLocation === DragLocation.END_EDGE) + dragLocation = DragLocation.START_EDGE; + } + + return [i, dragLocation]; + } + + return [-1, DragLocation.EMPTY_SPACE]; + } + + adaptToSize(width) { + if (this._width === width) + return; + + this._width = width; + this._sizeChanged = true; + } + + getChildren() { + return this._children; + } + + get nChildren() { + return this._children.length; + } +}); + +export const IconGrid = GObject.registerClass( +class IconGrid extends St.Widget { + _init(layoutParams = {}) { + const acceptDrop = layoutParams.accept_drop; + delete layoutParams.accept_drop; + + layoutParams = Params.parse(layoutParams, { + columns: 1, + column_spacing: 0, + row_spacing: 0, + force_columns: 0, + halign: Clutter.ActorAlign.FILL, + first_row_align: Clutter.ActorAlign.CENTER, + }); + + const layoutManager = new IconGridLayout(layoutParams); + + super._init({ + layoutManager, + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + }); + + this._settings = ArcMenuManager.settings; + + // only need acceptDrop for the main pinned-apps grid + if (acceptDrop) { + // DND requires this to be set + this._delegate = this; + } + } + + _canAccept(source) { + if (!(source instanceof MW.DraggableMenuItem)) + return false; + + if (this.contains(source)) + return false; + + if (!source.folderId) + return false; + + const pinnedAppsList = this._settings.get_value('pinned-apps').deepUnpack(); + for (let i = 0; i < pinnedAppsList.length; i++) { + if (pinnedAppsList[i].id === source.pinnedAppData.id) + return false; + } + + return true; + } + + handleDragOver(source, _actor, _x, _y) { + if (!this._canAccept(source)) + return DND.DragMotionResult.CONTINUE; + + return DND.DragMotionResult.MOVE_DROP; + } + + acceptDrop(source, _actor, _x, _y) { + if (!this._canAccept(source)) + return false; + + const sourceData = source.pinnedAppData; + + source.cancelActions(); + + // remove app from folder pinned app list + const parent = source.get_parent(); + const layoutManager = parent.layout_manager; + const index = layoutManager.getItemPosition(source); + + const folderSettings = source.folderSettings; + + const sourceParentChildren = layoutManager.getChildren(); + const folderPinnedApps = []; + for (let i = 0; i < sourceParentChildren.length; i++) + folderPinnedApps.push(sourceParentChildren[i].pinnedAppData); + + folderPinnedApps.splice(index, 1); + folderSettings.set_value('pinned-apps', new GLib.Variant('aa{ss}', folderPinnedApps)); + + // add app to main pinned apps + const pinnedAppsList = this._settings.get_value('pinned-apps').deepUnpack(); + pinnedAppsList.push(sourceData); + this._settings.set_value('pinned-apps', new GLib.Variant('aa{ss}', pinnedAppsList)); + + return true; + } + + /** + * @param {Clutter.ActorAlign} alignment + */ + set halign(alignment) { + this.layout_manager.halign = alignment; + this.queue_relayout(); + } + + /** + * @param {Clutter.ActorAlign} columnSpacing + */ + set column_spacing(columnSpacing) { + this.layout_manager.column_spacing = columnSpacing; + this.queue_relayout(); + } + + /** + * @param {number} rowSpacing + */ + set row_spacing(rowSpacing) { + this.layout_manager.row_spacing = rowSpacing; + this.queue_relayout(); + } + + vfunc_allocate(box) { + const [width, height_] = box.get_size(); + this.layout_manager.adaptToSize(width); + super.vfunc_allocate(box); + } + + addItem(item, index = -1) { + this.layout_manager.addItem(item, index); + this.queue_relayout(); + } + + appendItem(item) { + this.layout_manager.addItem(item, -1); + this.queue_relayout(); + } + + moveItem(item, newPosition) { + this.layout_manager.moveItem(item, newPosition); + this.queue_relayout(); + } + + removeItem(item) { + if (!this.contains(item)) + return; + + this.layout_manager.removeItem(item); + } + + removeAllItems() { + this.layout_manager.removeAllItems(); + } + + setColumns(columns) { + this.layout_manager.columns = columns; + this.queue_relayout(); + } + + getItemPosition(item) { + if (!this.contains(item)) + return -1; + + return this.layout_manager.getItemPosition(item); + } + + getItemAt(position) { + return this.layout_manager.getItemAt(position); + } + + getDropTarget(x, y) { + return this.layout_manager.getDropTarget(x, y); + } +}); + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/arcmenu-logo-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/arcmenu-logo-symbolic.svg new file mode 100644 index 0000000..1179af3 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/arcmenu-logo-symbolic.svg @@ -0,0 +1,93 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-accessories-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-accessories-symbolic.svg new file mode 100644 index 0000000..8b544c0 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-accessories-symbolic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-community-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-community-symbolic.svg new file mode 100644 index 0000000..e6e571a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-community-symbolic.svg @@ -0,0 +1,3 @@ + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-development-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-development-symbolic.svg new file mode 100644 index 0000000..10fd3bc --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-development-symbolic.svg @@ -0,0 +1,3 @@ + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-electronics-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-electronics-symbolic.svg new file mode 100644 index 0000000..386addd --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-electronics-symbolic.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-engineering-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-engineering-symbolic.svg new file mode 100644 index 0000000..026d5f9 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-engineering-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-fonts-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-fonts-symbolic.svg new file mode 100644 index 0000000..a71b41d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-fonts-symbolic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-games-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-games-symbolic.svg new file mode 100644 index 0000000..44ea77b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-games-symbolic.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-graphics-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-graphics-symbolic.svg new file mode 100644 index 0000000..6c297ae --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-graphics-symbolic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-health-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-health-symbolic.svg new file mode 100644 index 0000000..36f6dff --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-health-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-internet-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-internet-symbolic.svg new file mode 100644 index 0000000..72b4d57 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-internet-symbolic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-multimedia-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-multimedia-symbolic.svg new file mode 100644 index 0000000..18a90f0 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-multimedia-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-office-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-office-symbolic.svg new file mode 100644 index 0000000..269cf41 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-office-symbolic.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-other-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-other-symbolic.svg new file mode 100644 index 0000000..b8a3ed6 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-other-symbolic.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-science-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-science-symbolic.svg new file mode 100644 index 0000000..1ae9af7 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-science-symbolic.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-system-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-system-symbolic.svg new file mode 100644 index 0000000..4f8465d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-system-symbolic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-utilities-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-utilities-symbolic.svg new file mode 100644 index 0000000..8b544c0 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/category-icons/applications-utilities-symbolic.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-arch-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-arch-symbolic.svg new file mode 100644 index 0000000..04f26a2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-arch-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-budgie-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-budgie-symbolic.svg new file mode 100644 index 0000000..1c821f2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-budgie-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-cachyos-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-cachyos-symbolic.svg new file mode 100644 index 0000000..3b6048d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-cachyos-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-debian-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-debian-symbolic.svg new file mode 100644 index 0000000..debaab6 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-debian-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-endeavour-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-endeavour-symbolic.svg new file mode 100644 index 0000000..ecaca32 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-endeavour-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-fedora-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-fedora-symbolic.svg new file mode 100644 index 0000000..cdd8400 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-fedora-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-gentoo-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-gentoo-symbolic.svg new file mode 100644 index 0000000..39b6ec5 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-gentoo-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-gnome-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-gnome-symbolic.svg new file mode 100644 index 0000000..aaae359 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-gnome-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-kali-linux-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-kali-linux-symbolic.svg new file mode 100644 index 0000000..68c82c1 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-kali-linux-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-manjaro-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-manjaro-symbolic.svg new file mode 100644 index 0000000..aee5bb6 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-manjaro-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-mx-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-mx-symbolic.svg new file mode 100644 index 0000000..24df076 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-mx-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-nixos-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-nixos-symbolic.svg new file mode 100644 index 0000000..2bd3101 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-nixos-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-nobara-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-nobara-symbolic.svg new file mode 100644 index 0000000..313fdb2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-nobara-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-opensuse-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-opensuse-symbolic.svg new file mode 100644 index 0000000..d4d4f68 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-opensuse-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-oreon-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-oreon-symbolic.svg new file mode 100644 index 0000000..75613a8 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-oreon-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-pardus-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-pardus-symbolic.svg new file mode 100644 index 0000000..ec3866b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-pardus-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-pop-os-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-pop-os-symbolic.svg new file mode 100644 index 0000000..6aac6db --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-pop-os-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-pureos-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-pureos-symbolic.svg new file mode 100644 index 0000000..159de5d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-pureos-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-raspbian-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-raspbian-symbolic.svg new file mode 100644 index 0000000..6a1e92b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-raspbian-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-redhat-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-redhat-symbolic.svg new file mode 100644 index 0000000..330bb2a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-redhat-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-solus-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-solus-symbolic.svg new file mode 100644 index 0000000..eec6cc3 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-solus-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-ubuntu-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-ubuntu-symbolic.svg new file mode 100644 index 0000000..e410343 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-ubuntu-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-voyager-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-voyager-symbolic.svg new file mode 100644 index 0000000..2cf49bd --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-voyager-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-zorin-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-zorin-symbolic.svg new file mode 100644 index 0000000..56da4c2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/distro-zorin-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-3d-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-3d-symbolic.svg new file mode 100644 index 0000000..a77dcb2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-3d-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-a-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-a-symbolic.svg new file mode 100755 index 0000000..e97d98b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-a-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-alien-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-alien-symbolic.svg new file mode 100755 index 0000000..8f7b168 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-alien-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-app-launcher-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-app-launcher-symbolic.svg new file mode 100755 index 0000000..4b8e364 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-app-launcher-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-apps-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-apps-symbolic.svg new file mode 100755 index 0000000..02a90eb --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-apps-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-logo-alt-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-logo-alt-symbolic.svg new file mode 100755 index 0000000..8295ad1 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-logo-alt-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-logo-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-logo-symbolic.svg new file mode 100755 index 0000000..2b47f87 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-logo-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-old-alt-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-old-alt-symbolic.svg new file mode 100755 index 0000000..26961b3 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-old-alt-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-old-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-old-symbolic.svg new file mode 100755 index 0000000..23744d1 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-old-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-oldest-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-oldest-symbolic.svg new file mode 100755 index 0000000..49ab2cd --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arcmenu-oldest-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arrow-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arrow-symbolic.svg new file mode 100755 index 0000000..5c2f40d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-arrow-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-bat-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-bat-symbolic.svg new file mode 100755 index 0000000..9e52c5c --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-bat-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-bug-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-bug-symbolic.svg new file mode 100755 index 0000000..9c81ca6 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-bug-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-cita-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-cita-symbolic.svg new file mode 100755 index 0000000..c285123 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-cita-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-cloud-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-cloud-symbolic.svg new file mode 100755 index 0000000..bd18419 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-cloud-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-curved-a-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-curved-a-symbolic.svg new file mode 100755 index 0000000..b1bf386 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-curved-a-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-diamond-square-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-diamond-square-symbolic.svg new file mode 100755 index 0000000..904fc28 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-diamond-square-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dimond-win-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dimond-win-symbolic.svg new file mode 100755 index 0000000..3218c14 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dimond-win-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dolphin-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dolphin-symbolic.svg new file mode 100755 index 0000000..f315716 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dolphin-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dota-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dota-symbolic.svg new file mode 100755 index 0000000..d51c9ff --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dota-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dra-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dra-symbolic.svg new file mode 100755 index 0000000..ad2cc8a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dra-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dragon-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dragon-symbolic.svg new file mode 100755 index 0000000..fb3db7a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dragon-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dragonheart-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dragonheart-symbolic.svg new file mode 100755 index 0000000..a4a9be1 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-dragonheart-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-eclipse-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-eclipse-symbolic.svg new file mode 100755 index 0000000..6fe9ca4 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-eclipse-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-equal-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-equal-symbolic.svg new file mode 100755 index 0000000..c4938aa --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-equal-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-fly-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-fly-symbolic.svg new file mode 100755 index 0000000..10096f3 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-fly-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-focus-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-focus-symbolic.svg new file mode 100755 index 0000000..5b72731 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-focus-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-football-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-football-symbolic.svg new file mode 100755 index 0000000..de6f6e3 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-football-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-football2-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-football2-symbolic.svg new file mode 100755 index 0000000..0b59753 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-football2-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-gnacs-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-gnacs-symbolic.svg new file mode 100755 index 0000000..1e960b8 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-gnacs-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-groove-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-groove-symbolic.svg new file mode 100755 index 0000000..4a8875d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-groove-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-heddy-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-heddy-symbolic.svg new file mode 100755 index 0000000..700b90a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-heddy-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-helmet-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-helmet-symbolic.svg new file mode 100755 index 0000000..8f3d365 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-helmet-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-kaaet-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-kaaet-symbolic.svg new file mode 100755 index 0000000..03f1a37 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-kaaet-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-launcher-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-launcher-symbolic.svg new file mode 100755 index 0000000..95c9474 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-launcher-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-lins-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-lins-symbolic.svg new file mode 100755 index 0000000..bd5e34f --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-lins-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-loveheart-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-loveheart-symbolic.svg new file mode 100755 index 0000000..8261943 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-loveheart-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-octo-maze-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-octo-maze-symbolic.svg new file mode 100755 index 0000000..bfdfd3c --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-octo-maze-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pac-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pac-symbolic.svg new file mode 100755 index 0000000..346cab6 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pac-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pacman-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pacman-symbolic.svg new file mode 100755 index 0000000..c321e68 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pacman-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-paint-palette-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-paint-palette-symbolic.svg new file mode 100755 index 0000000..ec5facf --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-paint-palette-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-peaks-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-peaks-symbolic.svg new file mode 100755 index 0000000..61a63c5 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-peaks-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-peeks-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-peeks-symbolic.svg new file mode 100755 index 0000000..a65c954 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-peeks-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pie-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pie-symbolic.svg new file mode 100755 index 0000000..853def7 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pie-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pointer-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pointer-symbolic.svg new file mode 100755 index 0000000..467b590 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pointer-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pyrimid-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pyrimid-symbolic.svg new file mode 100755 index 0000000..dc5778a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-pyrimid-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-record-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-record-symbolic.svg new file mode 100755 index 0000000..a9a4a4b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-record-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-rewind-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-rewind-symbolic.svg new file mode 100755 index 0000000..f8c1cc5 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-rewind-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-robots-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-robots-symbolic.svg new file mode 100755 index 0000000..2c0d504 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-robots-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-round-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-round-symbolic.svg new file mode 100644 index 0000000..2929043 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-round-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-saucer-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-saucer-symbolic.svg new file mode 100755 index 0000000..845346e --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-saucer-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-search-glass-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-search-glass-symbolic.svg new file mode 100755 index 0000000..e03091e --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-search-glass-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-sheild-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-sheild-symbolic.svg new file mode 100755 index 0000000..2b9fbf4 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-sheild-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-snap-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-snap-symbolic.svg new file mode 100755 index 0000000..4782767 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-snap-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-somnia-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-somnia-symbolic.svg new file mode 100755 index 0000000..b60469e --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-somnia-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-start-box-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-start-box-symbolic.svg new file mode 100755 index 0000000..e9493b5 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-start-box-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-step-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-step-symbolic.svg new file mode 100755 index 0000000..e3cf5e2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-step-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-sums-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-sums-symbolic.svg new file mode 100755 index 0000000..ef3d5e5 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-sums-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-swirl-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-swirl-symbolic.svg new file mode 100644 index 0000000..9dd1bc2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-swirl-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-time-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-time-symbolic.svg new file mode 100755 index 0000000..634e848 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-time-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-toxic-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-toxic-symbolic.svg new file mode 100755 index 0000000..0e75db9 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-toxic-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-toxic2-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-toxic2-symbolic.svg new file mode 100755 index 0000000..05b0b6a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-toxic2-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-transform-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-transform-symbolic.svg new file mode 100755 index 0000000..faafd30 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-transform-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-tree-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-tree-symbolic.svg new file mode 100755 index 0000000..79f641d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-tree-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-triple-dash-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-triple-dash-symbolic.svg new file mode 100755 index 0000000..83f6737 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-triple-dash-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-utool-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-utool-symbolic.svg new file mode 100755 index 0000000..a09a11b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-utool-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-vancer-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-vancer-symbolic.svg new file mode 100755 index 0000000..d7cd9fa --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-vancer-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-vibe-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-vibe-symbolic.svg new file mode 100755 index 0000000..12c2a99 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-vibe-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-whirl-circle-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-whirl-circle-symbolic.svg new file mode 100755 index 0000000..5b4b349 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-whirl-circle-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-whirl-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-whirl-symbolic.svg new file mode 100755 index 0000000..7f2b751 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-whirl-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-zegon-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-zegon-symbolic.svg new file mode 100755 index 0000000..f1cf82f --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/icon-zegon-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-arcmenu-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-arcmenu-symbolic.svg new file mode 100644 index 0000000..9c97fcd --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-arcmenu-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-az-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-az-symbolic.svg new file mode 100644 index 0000000..1873194 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-az-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-brisk-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-brisk-symbolic.svg new file mode 100644 index 0000000..60e6f32 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-brisk-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-budgie-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-budgie-symbolic.svg new file mode 100644 index 0000000..ecc4892 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-budgie-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-chromebook-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-chromebook-symbolic.svg new file mode 100644 index 0000000..7fb5cba --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-chromebook-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-elementary-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-elementary-symbolic.svg new file mode 100644 index 0000000..be2c53c --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-elementary-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-eleven-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-eleven-symbolic.svg new file mode 100644 index 0000000..b5431fd --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-eleven-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-enterprise-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-enterprise-symbolic.svg new file mode 100644 index 0000000..eaf97ac --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-enterprise-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-gnomemenu-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-gnomemenu-symbolic.svg new file mode 100644 index 0000000..0206302 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-gnomemenu-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-gnomeoverview-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-gnomeoverview-symbolic.svg new file mode 100644 index 0000000..12c64ec --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-gnomeoverview-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-insider-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-insider-symbolic.svg new file mode 100644 index 0000000..5de766f --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-insider-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-mint-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-mint-symbolic.svg new file mode 100644 index 0000000..a5484d6 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-mint-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-plasma-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-plasma-symbolic.svg new file mode 100644 index 0000000..f26a9ea --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-plasma-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-pop-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-pop-symbolic.svg new file mode 100644 index 0000000..0eb6c56 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-pop-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-raven-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-raven-symbolic.svg new file mode 100644 index 0000000..f40cc92 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-raven-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-redmond-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-redmond-symbolic.svg new file mode 100644 index 0000000..cc85867 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-redmond-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-runner-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-runner-symbolic.svg new file mode 100644 index 0000000..b5d640f --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-runner-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-sleek-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-sleek-symbolic.svg new file mode 100644 index 0000000..1d6d921 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-sleek-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-tognee-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-tognee-symbolic.svg new file mode 100644 index 0000000..560e673 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-tognee-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-unity-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-unity-symbolic.svg new file mode 100644 index 0000000..159d0f4 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-unity-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-whisker-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-whisker-symbolic.svg new file mode 100644 index 0000000..6698265 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-whisker-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-windows-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-windows-symbolic.svg new file mode 100644 index 0000000..98cd964 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menu-windows-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-alternative-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-alternative-symbolic.svg new file mode 100644 index 0000000..58c6acc --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-alternative-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-launcher-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-launcher-symbolic.svg new file mode 100644 index 0000000..434e59b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-launcher-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-modern-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-modern-symbolic.svg new file mode 100644 index 0000000..fd9bfa9 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-modern-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-touch-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-touch-symbolic.svg new file mode 100644 index 0000000..b1ee214 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-touch-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-traditional-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-traditional-symbolic.svg new file mode 100644 index 0000000..b63a4c9 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/menustyle-traditional-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-arcmenu-logo.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-arcmenu-logo.svg new file mode 100644 index 0000000..f3eac9b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-arcmenu-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-customicon-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-customicon-symbolic.svg new file mode 100644 index 0000000..da6b70d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-customicon-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-finetune-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-finetune-symbolic.svg new file mode 100755 index 0000000..95e490d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-finetune-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-keyboard.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-keyboard.svg new file mode 100644 index 0000000..fb78910 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-layouts-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-layouts-symbolic.svg new file mode 100644 index 0000000..cada730 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-layouts-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-settings-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-settings-symbolic.svg new file mode 100644 index 0000000..45f9d47 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-settings-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-theme-symbolic.svg b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-theme-symbolic.svg new file mode 100644 index 0000000..7619160 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/icons/hicolor/16x16/actions/settings-theme-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/keybinder.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/keybinder.js new file mode 100644 index 0000000..17aa9f5 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/keybinder.js @@ -0,0 +1,136 @@ +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; +import Meta from 'gi://Meta'; +import Shell from 'gi://Shell'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +import * as Constants from './constants.js'; + +const MUTTER_SCHEMA = 'org.gnome.mutter'; + +export const OverrideOverlayKey = class { + constructor() { + this.isOverrideOverlayEnabled = false; + this._ignoreHotKeyChangedEvent = false; + + this._mutterSettings = new Gio.Settings({'schema': MUTTER_SCHEMA}); + + this._oldOverlayKey = this._mutterSettings.get_value('overlay-key'); + + this._overlayKeyChangedID = this._mutterSettings.connect('changed::overlay-key', () => { + if (!this._ignoreHotKeyChangedEvent) + this._oldOverlayKey = this._mutterSettings.get_value('overlay-key'); + }); + + this._mainStartUpComplete = Main.layoutManager.connect('startup-complete', + () => this._overrideOverlayKey()); + } + + enable(toggleMenu) { + this._toggleMenu = toggleMenu; + + this._ignoreHotKeyChangedEvent = true; + + this._mutterSettings.set_string('overlay-key', Constants.SUPER_L); + Main.wm.allowKeybinding('overlay-key', Shell.ActionMode.ALL); + + this.isOverrideOverlayEnabled = true; + + if (!Main.layoutManager._startingUp) + this._overrideOverlayKey(); + + this._ignoreHotKeyChangedEvent = false; + } + + disable() { + this._ignoreHotKeyChangedEvent = true; + this._mutterSettings.set_value('overlay-key', this._oldOverlayKey); + if (this.overlayKeyID) { + global.display.disconnect(this.overlayKeyID); + this.overlayKeyID = null; + } + if (this.defaultOverlayKeyID) { + GObject.signal_handler_unblock(global.display, this.defaultOverlayKeyID); + this.defaultOverlayKeyID = null; + } + Main.wm.allowKeybinding('overlay-key', Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW); + + this.isOverrideOverlayEnabled = false; + this._ignoreHotKeyChangedEvent = false; + } + + _overrideOverlayKey() { + if (!this.isOverrideOverlayEnabled) + return; + + this.defaultOverlayKeyID = GObject.signal_handler_find(global.display, {signalId: 'overlay-key'}); + + if (!this.defaultOverlayKeyID) { + log('ArcMenu Error - Failed to set Super_L hotkey'); + return; + } + + GObject.signal_handler_block(global.display, this.defaultOverlayKeyID); + + Main.wm.allowKeybinding('overlay-key', Shell.ActionMode.ALL); + + this.overlayKeyID = global.display.connect('overlay-key', () => { + this._toggleMenu(); + // Workaround for PopShell extension conflicting with ArcMenu SUPER_L hotkey. + // PopShell extension removes ActionMode.POPUP from 'overlay-key', + // which prevents the use of the SUPER_L hotkey when popup menus are opened. + // Set 'overlay-key' action mode to ActionMode.ALL when ArcMenu is opened. + Main.wm.allowKeybinding('overlay-key', Shell.ActionMode.ALL); + }); + } + + destroy() { + if (this._overlayKeyChangedID) { + this._mutterSettings.disconnect(this._overlayKeyChangedID); + this._overlayKeyChangedID = null; + } + this.disable(); + if (this._mainStartUpComplete) { + Main.layoutManager.disconnect(this._mainStartUpComplete); + this._mainStartUpComplete = null; + } + } +}; + +export const CustomKeybinding = class { + constructor(settings) { + this._settings = settings; + this._keybindings = new Map(); + } + + bind(keybindingNameKey, keybindingValueKey, keybindingHandler) { + if (!this._keybindings.has(keybindingNameKey)) { + this._keybindings.set(keybindingNameKey, keybindingValueKey); + + Main.wm.addKeybinding(keybindingValueKey, this._settings, + Meta.KeyBindingFlags.IGNORE_AUTOREPEAT, + Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW | Shell.ActionMode.POPUP, + keybindingHandler); + } + } + + unbind(keybindingNameKey) { + if (this._keybindings.has(keybindingNameKey)) { + const keybindingValueKey = this._keybindings.get(keybindingNameKey); + Main.wm.removeKeybinding(keybindingValueKey); + this._keybindings.delete(keybindingNameKey); + } + } + + unbindAll() { + this._keybindings.forEach((value, key) => { + Main.wm.removeKeybinding(value); + this._keybindings.delete(key); + }); + } + + destroy() { + this.unbindAll(); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ar/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ar/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..f06173f Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ar/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/be/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/be/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..11c483b Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/be/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/bg/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/bg/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..9cb7eaf Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/bg/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ca/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ca/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..dd97be3 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ca/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/cs/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/cs/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..131dfea Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/cs/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/da/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/da/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..20bd0a4 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/da/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/de/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/de/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..a6a0b8f Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/de/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/el/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/el/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..c08d48e Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/el/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/es/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/es/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..59096af Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/es/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/et/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/et/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..b9d0c76 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/et/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/fi/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/fi/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..0d4495f Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/fi/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/fr/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/fr/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..0eff612 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/fr/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/he/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/he/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..ee7d15e Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/he/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/hi_IN/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/hi_IN/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..7b2cd8b Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/hi_IN/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/hu/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/hu/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..9818573 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/hu/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/id/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/id/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..25f1be6 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/id/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/it/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/it/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..0f4968c Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/it/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ja/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ja/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..9a6fbf5 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ja/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ko/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ko/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..e70df15 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ko/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/nb_NO/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/nb_NO/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..49572c0 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/nb_NO/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/nl/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/nl/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..4242014 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/nl/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/oc/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/oc/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..731801f Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/oc/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/pl/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/pl/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..8b83c20 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/pl/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/pt_BR/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/pt_BR/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..3d6ce27 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/pt_BR/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ru/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ru/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..0d0d9d1 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/ru/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/si/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/si/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..e143764 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/si/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sk/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sk/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..151addf Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sk/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sr/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sr/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..9f4b060 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sr/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sr@latin/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sr@latin/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..defd162 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sr@latin/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sv/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sv/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..9459341 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/sv/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/szl/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/szl/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..3ac057e Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/szl/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/tr/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/tr/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..2621a1c Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/tr/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/uk/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/uk/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..1f57f4f Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/uk/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/zh_CN/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/zh_CN/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..bc7c1eb Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/zh_CN/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/zh_TW/LC_MESSAGES/arcmenu.mo b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/zh_TW/LC_MESSAGES/arcmenu.mo new file mode 100644 index 0000000..68ad6a2 Binary files /dev/null and b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/locale/zh_TW/LC_MESSAGES/arcmenu.mo differ diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menuButton.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menuButton.js new file mode 100644 index 0000000..21bafc3 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menuButton.js @@ -0,0 +1,772 @@ +import Clutter from 'gi://Clutter'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Graphene from 'gi://Graphene'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; +import * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; +import * as SystemActions from 'resource:///org/gnome/shell/misc/systemActions.js'; + +import {ArcMenuManager} from './arcmenuManager.js'; +import * as Constants from './constants.js'; +import * as LayoutHandler from './menulayouts/layoutHandler.js'; +import * as MW from './menuWidgets.js'; +import * as Utils from './utils.js'; + +import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +class MenuButtonWidget extends St.BoxLayout { + static { + GObject.registerClass(this); + } + + constructor() { + super({ + style_class: 'panel-status-menu-box', + }); + + this._icon = new St.Icon({ + style_class: 'arcmenu-menu-button', + track_hover: true, + reactive: true, + }); + this._label = new St.Label({ + text: _('Apps'), + y_expand: true, + style_class: 'arcmenu-menu-button', + y_align: Clutter.ActorAlign.CENTER, + }); + + this.add_child(this._icon); + this.add_child(this._label); + } + + addStylePseudoClass(style) { + this._icon.add_style_pseudo_class(style); + this._label.add_style_pseudo_class(style); + } + + removeStylePseudoClass(style) { + this._icon.remove_style_pseudo_class(style); + this._label.remove_style_pseudo_class(style); + } + + showIcon() { + this._icon.show(); + this._label.hide(); + + this.set_child_at_index(this._icon, 0); + } + + showText() { + this._icon.hide(); + this._label.show(); + + this.set_child_at_index(this._label, 0); + } + + showIconText() { + this._icon.show(); + this._label.show(); + + this.set_child_at_index(this._icon, 0); + } + + showTextIcon() { + this._icon.show(); + this._label.show(); + + this.set_child_at_index(this._label, 0); + } + + getPanelLabel() { + return this._label; + } + + getPanelIcon() { + return this._icon; + } + + setLabelStyle(style) { + this._label.style = style; + } +} + +export const MenuButton = GObject.registerClass( +class ArcMenuMenuButton extends PanelMenu.Button { + _init(panelInfo, monitorIndex) { + super._init(0.5, null, true); + + this._destroyed = false; + + this.set({ + x_expand: false, + }); + + this.add_style_class_name('arcmenu-panel-menu'); + + this._extension = ArcMenuManager.extension; + this._settings = ArcMenuManager.settings; + + // Link search providers to this menu + this.searchProviderDisplayId = `ArcMenu_${monitorIndex}`; + + this._panel = panelInfo.panel; + this._panelBox = panelInfo.panelBox; + this._panelParent = panelInfo.panelParent; + this._monitorIndex = monitorIndex; + + this.menu.destroy(); + this.menu = null; + + this.tooltipShowing = false; + this.tooltipShowingID = null; + this.tooltip = new MW.Tooltip(this); + + this._dtpNeedsRelease = false; + + // Create Main Menus - ArcMenu and ArcMenu's context menu + this.arcMenu = new ArcMenu(this, 0.5, St.Side.TOP); + this.arcMenu.connect('open-state-changed', this._onOpenStateChanged.bind(this)); + + this.arcMenuContextMenu = new ArcMenuContextMenu(this, 0.5, St.Side.TOP); + this.arcMenuContextMenu.connect('open-state-changed', this._onOpenStateChanged.bind(this)); + + this.menuManager = new PopupMenu.PopupMenuManager(this._panel); + this.menuManager._changeMenu = () => {}; + this.menuManager.addMenu(this.arcMenu); + this.menuManager.addMenu(this.arcMenuContextMenu); + + // Context Menus for applications and other menu items + this.contextMenuManager = new PopupMenu.PopupMenuManager(this); + this.contextMenuManager._changeMenu = () => {}; + this.contextMenuManager._onMenuSourceEnter = menu => { + if (this.contextMenuManager.activeMenu && this.contextMenuManager.activeMenu !== menu) + return Clutter.EVENT_STOP; + + return Clutter.EVENT_PROPAGATE; + }; + + // Sub Menu Manager - Control all other popup menus + this.subMenuManager = new PopupMenu.PopupMenuManager(this); + this.subMenuManager._changeMenu = () => {}; + + this.menuButtonWidget = new MenuButtonWidget(); + this.add_child(this.menuButtonWidget); + } + + get extension() { + return this._extension; + } + + get settings() { + return this._settings; + } + + initiate() { + this._dtp = Main.extensionManager.lookup(Constants.DASH_TO_PANEL_UUID); + + if (this._dtp?.state === Utils.ExtensionState.ACTIVE && global.dashToPanel) + this.syncWithDashToPanel(); + + this._monitorsChangedId = Main.layoutManager.connect('monitors-changed', () => this.updateHeight()); + this._startupCompleteId = Main.layoutManager.connect('startup-complete', () => this.updateHeight()); + + this.setMenuPositionAlignment(); + this.createMenuLayout(); + } + + syncWithDashToPanel() { + const dtp = Extension.lookupByUUID(Constants.DASH_TO_PANEL_UUID); + this._dtpSettings = dtp.getSettings('org.gnome.shell.extensions.dash-to-panel'); + + const side = Utils.getDashToPanelPosition(this._dtpSettings, this._monitorIndex); + this.updateArrowSide(side); + + this.dtpPostionChangedID = this._dtpSettings.connect('changed::panel-positions', () => { + const newSide = Utils.getDashToPanelPosition(this._dtpSettings, this._monitorIndex); + this.updateArrowSide(newSide); + }); + } + + async createMenuLayout() { + this._clearTooltipShowingId(); + this._clearTooltip(); + + this._destroyMenuLayout(); + + const layout = this._settings.get_enum('menu-layout'); + + if (this._destroyed) + return; + + this._menuLayout = await LayoutHandler.createMenuLayout(this, layout); + + // MenuButton may be destroyed while createMenuLayout() is running + if (this._destroyed && this._menuLayout) { + this._menuLayout.destroy(); + return; + } + + if (this._menuLayout) { + this.arcMenu.box.add_child(this._menuLayout); + this.setMenuPositionAlignment(); + this.forceMenuLocation(); + this.updateHeight(); + } + } + + setMenuPositionAlignment() { + const layout = this._settings.get_enum('menu-layout'); + const arrowAlignment = 1 - (this._settings.get_int('menu-position-alignment') / 100); + const panelPosition = this._settings.get_enum('position-in-panel'); + + if (layout !== Constants.MenuLayout.RUNNER) { + if (panelPosition === Constants.MenuPosition.CENTER) { + this.arcMenuContextMenu._arrowAlignment = arrowAlignment; + this.arcMenu._arrowAlignment = arrowAlignment; + this.arcMenuContextMenu._boxPointer.setSourceAlignment(.5); + this.arcMenu._boxPointer.setSourceAlignment(.5); + } else if (this._dtp?.state === Utils.ExtensionState.ACTIVE) { + const side = Utils.getDashToPanelPosition(this._dtpSettings, this._monitorIndex); + this.updateArrowSide(side, false); + } else { + this.updateArrowSide(St.Side.TOP, false); + } + } else { + this.updateArrowSide(St.Side.TOP, false); + if (panelPosition === Constants.MenuPosition.CENTER) { + this.arcMenuContextMenu._arrowAlignment = arrowAlignment; + this.arcMenuContextMenu._boxPointer.setSourceAlignment(.5); + } + } + } + + updateArrowSide(side, setAlignment = true) { + let arrowAlignment; + if (side === St.Side.RIGHT || side === St.Side.LEFT) + arrowAlignment = 1.0; + else + arrowAlignment = 0.5; + + const menus = [this.arcMenu, this.arcMenuContextMenu]; + for (const menu of menus) { + menu._arrowSide = side; + menu._boxPointer._arrowSide = side; + menu._boxPointer._userArrowSide = side; + menu._boxPointer.setSourceAlignment(0.5); + menu._arrowAlignment = arrowAlignment; + menu._boxPointer._border.queue_repaint(); + } + + if (setAlignment) + this.setMenuPositionAlignment(); + } + + forceMenuLocation() { + const layout = this._settings.get_enum('menu-layout'); + if (layout === Constants.MenuLayout.RUNNER || + layout === Constants.MenuLayout.RAVEN || + layout === Constants.MenuLayout.GNOME_OVERVIEW) + return; + + this.arcMenu.actor.remove_style_class_name('bottomOfScreenMenu'); + + const newMenuLocation = this._settings.get_enum('force-menu-location'); + if (this._menuLocation !== newMenuLocation) { + this._menuLocation = newMenuLocation; + + if (newMenuLocation === Constants.ForcedMenuLocation.OFF) { + this.arcMenu.sourceActor = this.arcMenu.focusActor = this; + this.arcMenu._boxPointer.setPosition(this, 0.5); + this.setMenuPositionAlignment(); + return; + } + + this.arcMenu.sourceActor = this.arcMenu.focusActor = Main.layoutManager.dummyCursor; + this.arcMenu._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0.5); + this.arcMenu._boxPointer.setSourceAlignment(0.5); + this.arcMenu._arrowAlignment = 0.5; + } + + if (newMenuLocation === Constants.ForcedMenuLocation.OFF) + return; + + this.updateArrowSide(St.Side.TOP, false); + const rect = Main.layoutManager.getWorkAreaForMonitor(this._monitorIndex); + const positionX = Math.round(rect.x + (rect.width / 2)); + let positionY; + + if (newMenuLocation === Constants.ForcedMenuLocation.TOP_CENTERED) { + positionY = rect.y; + } else if (newMenuLocation === Constants.ForcedMenuLocation.BOTTOM_CENTERED) { + positionY = rect.y + rect.height - 1; + this.arcMenu.actor.add_style_class_name('bottomOfScreenMenu'); + } else if (newMenuLocation === Constants.ForcedMenuLocation.MONITOR_CENTERED) { + const menuHeight = this._settings.get_int('menu-height'); + const monitor = Main.layoutManager.findMonitorForActor(this); + positionY = Math.round(monitor.y + (monitor.height / 2) - (menuHeight / 2)); + Main.layoutManager.setDummyCursorGeometry(positionX, positionY, 0, 0); + } + + Main.layoutManager.setDummyCursorGeometry(positionX, positionY, 0, 0); + } + + vfunc_event(event) { + if (event.type() === Clutter.EventType.BUTTON_PRESS) { + const clickAction = this._getClickActionForButton(event.get_button()); + if (clickAction === Constants.MenuButtonClickAction.ARCMENU) + this.toggleMenu(); + else if (clickAction === Constants.MenuButtonClickAction.CONTEXT_MENU) + this.arcMenuContextMenu.toggle(); + } else if (event.type() === Clutter.EventType.TOUCH_BEGIN) { + this.toggleMenu(); + } + return Clutter.EVENT_PROPAGATE; + } + + _getClickActionForButton(button) { + if (button === Clutter.BUTTON_PRIMARY) + return this._settings.get_enum('menu-button-left-click-action'); + else if (button === Clutter.BUTTON_SECONDARY) + return this._settings.get_enum('menu-button-right-click-action'); + else if (button === Clutter.BUTTON_MIDDLE) + return this._settings.get_enum('menu-button-middle-click-action'); + else + return -1; + } + + closeOtherMenus() { + if (this.contextMenuManager.activeMenu) + this.contextMenuManager.activeMenu.toggle(); + if (this.subMenuManager.activeMenu) + this.subMenuManager.activeMenu.toggle(); + } + + closeContextMenu() { + if (this.arcMenuContextMenu.isOpen) + this.arcMenuContextMenu.toggle(); + } + + toggleMenu() { + this.closeOtherMenus(); + + this.forceMenuLocation(); + + const layout = this._settings.get_enum('menu-layout'); + if (layout === Constants.MenuLayout.GNOME_OVERVIEW) { + if (this._settings.get_boolean('gnome-dash-show-applications')) + Main.overview._overview._controls._toggleAppsPage(); + else + Main.overview.toggle(); + return; + } + + if (!this.arcMenu.isOpen) { + if (this._menuLayout?.updateLocation) + this._menuLayout.updateLocation(); + + if (this._menuLayout?.updateStyle) + this._menuLayout.updateStyle(); + } + + this.arcMenu.toggle(); + + if (this.arcMenu.isOpen) + this._menuLayout?.grab_key_focus(); + } + + _maybeShowPanel() { + if (this._panelParent.intellihide && this._panelParent.intellihide.enabled) { + this._panelParent.intellihide._revealPanel(true); + this._panelParent.intellihide.revealAndHold(1); + } else if (!this._panelBox.visible) { + this._panelBox.visible = true; + this._panelNeedsHiding = true; + } + } + + updateHeight() { + if (!this._menuLayout) + return; + + const layout = this._settings.get_enum('menu-layout'); + if (layout === Constants.MenuLayout.RUNNER || layout === Constants.MenuLayout.RAVEN) { + this._menuLayout.style = ''; + return; + } + + const height = this._settings.get_int('menu-height'); + this._menuLayout.style = `height: ${height}px;`; + } + + updateWidth() { + if (this._menuLayout?.updateWidth) + this._menuLayout.updateWidth(true); + } + + _clearTooltipShowingId() { + if (this.tooltipShowingID) { + GLib.source_remove(this.tooltipShowingID); + this.tooltipShowingID = null; + } + } + + _clearTooltip() { + this.tooltipShowing = false; + if (this.tooltip) { + this.tooltip.hide(); + this.tooltip.sourceActor = null; + } + } + + _onDestroy() { + this._destroyed = true; + this._stopTrackingMouse(); + + if (this._monitorsChangedId) { + Main.layoutManager.disconnect(this._monitorsChangedId); + this._monitorsChangedId = null; + } + + if (this._startupCompleteId) { + Main.layoutManager.disconnect(this._startupCompleteId); + this._startupCompleteId = null; + } + + this._clearTooltipShowingId(); + + if (this.dtpPostionChangedID && this._dtpSettings) { + this._dtpSettings.disconnect(this.dtpPostionChangedID); + this.dtpPostionChangedID = null; + } + + this._destroyMenuLayout(); + + this.tooltip?.destroy(); + this.tooltip = null; + this.arcMenu?.destroy(); + this.arcMenu = null; + this.arcMenuContextMenu?.destroy(); + + super._onDestroy(); + } + + _destroyMenuLayout() { + if (this._menuLayout) { + this._menuLayout.destroy(); + this._menuLayout = null; + } + } + + updateLocation() { + if (this._menuLayout && this._menuLayout.updateLocation) + this._menuLayout.updateLocation(); + } + + getActiveCategoryType() { + return this._menuLayout?.activeCategoryType; + } + + reloadApplications() { + this._menuLayout?.reloadApplications(); + } + + displayPinnedApps() { + this._menuLayout?.displayPinnedApps(); + } + + loadPinnedApps() { + this._menuLayout?.loadPinnedApps(); + } + + setDefaultMenuView() { + if (!this._menuLayout) + return; + + if (!this._menuLayout.reloadQueued) + this._menuLayout.setDefaultMenuView(); + } + + _onOpenStateChanged(_menu, open) { + if (open) { + this._maybeShowPanel(); + this.menuButtonWidget.addStylePseudoClass('active'); + this.add_style_pseudo_class('active'); + + if (Main.panel.menuManager && Main.panel.menuManager.activeMenu) + Main.panel.menuManager.activeMenu.toggle(); + + if (!this._dtpNeedsRelease && this._panelParent.intellihide?.enabled) + this._dtpNeedsRelease = true; + } else { + if (!this.arcMenu.isOpen) { + this._clearTooltipShowingId(); + this._clearTooltip(); + } + + if (!this.arcMenu.isOpen && !this.arcMenuContextMenu.isOpen) { + this.menuButtonWidget.removeStylePseudoClass('active'); + this.remove_style_pseudo_class('active'); + + if (this._dtpNeedsRelease && !this._panelNeedsHiding) { + this._dtpNeedsRelease = false; + const hidePanel = () => this._panelParent.intellihide?.release(1); + + const isMouseOnPanel = this._isMouseOnPanel(); + if (isMouseOnPanel) + this._startTrackingMouse(hidePanel); + else + hidePanel(); + } + if (this._panelNeedsHiding) { + this._panelNeedsHiding = false; + const hidePanel = () => (this._panelBox.visible = false); + + const isMouseOnPanel = this._isMouseOnPanel(); + if (isMouseOnPanel) + this._startTrackingMouse(hidePanel); + else + hidePanel(); + } + } + } + } + + _isMouseOnPanel() { + const [x, y] = global.get_pointer(); + + const mouseOnPanel = this._panelHasMousePointer(x, y); + if (mouseOnPanel) + return true; + + return false; + } + + _panelHasMousePointer(x, y) { + const panelBoxRect = this._panelBox.get_transformed_extents(); + const cursorLocation = new Graphene.Point({x, y}); + + if (panelBoxRect.contains_point(cursorLocation)) + return true; + else + return false; + } + + _startTrackingMouse(callback) { + if (this._pointerWatch) + return; + + this._pointerWatch = PointerWatcher.getPointerWatcher().addWatch(500, (pX, pY) => { + if (!this._panelHasMousePointer(pX, pY)) { + callback(); + this._stopTrackingMouse(); + } + }); + } + + _stopTrackingMouse() { + if (this._pointerWatch) { + PointerWatcher.getPointerWatcher()._removeWatch(this._pointerWatch); + this._pointerWatch = null; + } + } +}); + +export const ArcMenu = class ArcMenuArcMenu extends PopupMenu.PopupMenu { + constructor(sourceActor, arrowAlignment, arrowSide, parent) { + super(sourceActor, arrowAlignment, arrowSide); + this._menuButton = parent || sourceActor; + Main.uiGroup.add_child(this.actor); + this.actor.add_style_class_name('panel-menu arcmenu-menu'); + this.actor.hide(); + this._menuClosedID = this.connect('menu-closed', () => this._menuButton.setDefaultMenuView()); + this.connect('destroy', () => this._onDestroy()); + + this.actor.connectObject('captured-event', this._onCapturedEvent.bind(this), this); + + this._dimEffect = new Clutter.BrightnessContrastEffect({ + enabled: false, + }); + this._boxPointer.add_effect_with_name('dim', this._dimEffect); + } + + _onCapturedEvent(actor, event) { + if (Main.keyboard.maybeHandleEvent(event)) + return Clutter.EVENT_STOP; + + return Clutter.EVENT_PROPAGATE; + } + + open(animate) { + if (!this.isOpen) { + this._menuButton.arcMenu.actor._muteInput = false; + this._menuButton.arcMenu.actor._muteKeys = false; + } + super.open(animate); + } + + close(animate) { + if (this.isOpen) + this._menuButton.closeOtherMenus(); + + super.close(animate); + } + + _onDestroy() { + if (this._menuClosedID) { + this.disconnect(this._menuClosedID); + this._menuClosedID = null; + } + } +}; + +var ArcMenuContextMenu = class ArcMenuArcMenuContextMenu extends PopupMenu.PopupMenu { + constructor(sourceActor, arrowAlignment, arrowSide) { + super(sourceActor, arrowAlignment, arrowSide); + this._settings = ArcMenuManager.settings; + this._extension = ArcMenuManager.extension; + this._systemActions = SystemActions.getDefault(); + + this.actor.add_style_class_name('panel-menu app-menu'); + Main.uiGroup.add_child(this.actor); + this.actor.hide(); + + const menuItemsChangedId = this._settings.connect('changed::context-menu-items', + () => this.populateMenuItems()); + + this.populateMenuItems(); + this.connect('destroy', () => { + this.disconnectPowerOptions(); + this._settings.disconnect(menuItemsChangedId); + }); + } + + populateMenuItems() { + this.disconnectPowerOptions(); + this.removeAll(); + + const contextMenuShortcuts = this._settings.get_value('context-menu-items').deep_unpack(); + + for (let i = 0; i < contextMenuShortcuts.length; i++) { + const {name, id} = contextMenuShortcuts[i]; + + if (id.endsWith('.desktop')) { + this.addSettingsAction(name, id); + } else if (id === Constants.ShortcutCommands.SEPARATOR) { + this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + } else if (id === Constants.ShortcutCommands.SETTINGS) { + this.addAction(_('ArcMenu Settings'), () => this._extension.openPreferences()); + } else if (id.includes(Constants.ShortcutCommands.SETTINGS)) { + const settingsPage = id.replace(Constants.ShortcutCommands.SETTINGS, ''); + if (settingsPage === 'About') + this.addArcMenuSettingsItem(name, Constants.SettingsPage.ABOUT); + else if (settingsPage === 'Menu') + this.addArcMenuSettingsItem(name, Constants.SettingsPage.CUSTOMIZE_MENU); + else if (settingsPage === 'Layout') + this.addArcMenuSettingsItem(name, Constants.SettingsPage.MENU_LAYOUT); + else if (settingsPage === 'Button') + this.addArcMenuSettingsItem(name, Constants.SettingsPage.BUTTON_APPEARANCE); + else if (settingsPage === 'Theme') + this.addArcMenuSettingsItem(name, Constants.SettingsPage.MENU_THEME); + } else if (id === Constants.ShortcutCommands.OVERVIEW) { + this.addAction(_('Activities Overview'), () => Main.overview.toggle()); + } else if (id === Constants.ShortcutCommands.POWER_OPTIONS) { + this.addPowerOptionsMenuItem(); + } else if (id === Constants.ShortcutCommands.SHOW_DESKTOP) { + this.addShowDekstopItem(); + } else if (id === Constants.ShortcutCommands.PANEL_EXTENSION_SETTINGS) { + this.addExtensionSettings(); + } + } + } + + addArcMenuSettingsItem(title, prefsVisiblePage) { + const item = new PopupMenu.PopupMenuItem(_(title)); + item.connect('activate', () => { + this._settings.set_int('prefs-visible-page', prefsVisiblePage); + this._extension.openPreferences(); + }); + this.addMenuItem(item); + } + + disconnectPowerOptions() { + if (this.canSuspendId) + this._systemActions.disconnect(this.canSuspendId); + if (this.canSwitchUserId) + this._systemActions.disconnect(this.canSwitchUserId); + + this.canSuspendId = null; + this.canSwitchUserId = null; + } + + addShowDekstopItem() { + this.addAction(_('Show Desktop'), () => { + const currentWorkspace = global.workspace_manager.get_active_workspace(); + let windows = currentWorkspace.list_windows().filter(w => { + return w.showing_on_its_workspace() && !w.skip_taskbar; + }); + windows = global.display.sort_windows_by_stacking(windows); + + windows.forEach(w => { + w.minimize(); + }); + }); + } + + addPowerOptionsMenuItem() { + const powerOptionsItem = new PopupMenu.PopupSubMenuMenuItem(_('Power Off / Log Out')); + + const suspendItem = powerOptionsItem.menu.addAction(_('Suspend'), + () => this._systemActions.activateSuspend()); + suspendItem.visible = this._systemActions.canSuspend; + powerOptionsItem.menu.addAction(_('Restart...'), () => this._systemActions.activateRestart()); + powerOptionsItem.menu.addAction(_('Power Off...'), () => this._systemActions.activatePowerOff()); + + powerOptionsItem.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + + powerOptionsItem.menu.addAction(_('Lock'), () => this._systemActions.activateLockScreen()); + powerOptionsItem.menu.addAction(_('Log Out...'), () => this._systemActions.activateLogout()); + const switchUserItem = powerOptionsItem.menu.addAction(_('Switch User'), + () => this._systemActions.activateSwitchUser()); + switchUserItem.visible = this._systemActions.canSwitchUser; + + this.canSuspendId = this._systemActions.connect('notify::can-suspend', + () => (suspendItem.visible = this._systemActions.canSuspend)); + this.canSwitchUserId = this._systemActions.connect('notify::can-switch-user', + () => (switchUserItem.visible = this._systemActions.canSwitchUser)); + + this.addMenuItem(powerOptionsItem); + } + + addSettingsAction(title, desktopFile) { + const app = Shell.AppSystem.get_default().lookup_app(desktopFile); + if (!app) + return; + + if (!title) + title = app.get_name(); + + + super.addSettingsAction(title, desktopFile); + } + + addExtensionSettings() { + const dashToPanel = Main.extensionManager.lookup(Constants.DASH_TO_PANEL_UUID); + const azTaskbar = Main.extensionManager.lookup(Constants.AZTASKBAR_UUID); + + if (dashToPanel?.state === Utils.ExtensionState.ACTIVE && global.dashToPanel) { + const item = new PopupMenu.PopupMenuItem(_('Dash to Panel Settings')); + item.connect('activate', () => Utils.openPrefs(Constants.DASH_TO_PANEL_UUID)); + this.addMenuItem(item); + } else if (azTaskbar?.state === Utils.ExtensionState.ACTIVE && global.azTaskbar) { + const item = new PopupMenu.PopupMenuItem(_('App Icons Taskbar Settings')); + item.connect('activate', () => Utils.openPrefs(Constants.AZTASKBAR_UUID)); + this.addMenuItem(item); + } + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menuWidgets.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menuWidgets.js new file mode 100644 index 0000000..7b920a1 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menuWidgets.js @@ -0,0 +1,3462 @@ +import AccountsService from 'gi://AccountsService'; +import Atk from 'gi://Atk'; +import Clutter from 'gi://Clutter'; +import Cogl from 'gi://Cogl'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GMenu from 'gi://GMenu'; +import GObject from 'gi://GObject'; +import Graphene from 'gi://Graphene'; +import Pango from 'gi://Pango'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js'; +import * as DND from 'resource:///org/gnome/shell/ui/dnd.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Params from 'resource:///org/gnome/shell/misc/params.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; +import {showScreenshotUI} from 'resource:///org/gnome/shell/ui/screenshot.js'; +import * as SystemActions from 'resource:///org/gnome/shell/misc/systemActions.js'; +import * as Util from 'resource:///org/gnome/shell/misc/util.js'; + +import {AppContextMenu} from './appMenu.js'; +import * as Constants from './constants.js'; +import {DragLocation, IconGrid} from './iconGrid.js'; +import * as Utils from './utils.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +const GDateMenu = Main.panel.statusArea.dateMenu; +const GWeatherWidget = GDateMenu._weatherItem.constructor; +const GWorldClocksWidget = GDateMenu._clocksItem.constructor; + +const INDICATOR_ICON_SIZE = 18; +const USER_AVATAR_SIZE = 28; + +const TOOLTIP_SHOW_TIME = 150; +const TOOLTIP_HIDE_TIME = 100; + +/** + * @param {Constants.PowerType} powerType The power type to activate + */ +function activatePowerOption(powerType) { + const systemActions = SystemActions.getDefault(); + + switch (powerType) { + case Constants.PowerType.POWER_OFF: + systemActions.activatePowerOff(); + break; + case Constants.PowerType.RESTART: + systemActions.activateRestart(); + break; + case Constants.PowerType.LOCK: + systemActions.activateLockScreen(); + break; + case Constants.PowerType.LOGOUT: + systemActions.activateLogout(); + break; + case Constants.PowerType.SUSPEND: + systemActions.activateSuspend(); + break; + case Constants.PowerType.SWITCH_USER: + systemActions.activateSwitchUser(); + break; + case Constants.PowerType.HYBRID_SLEEP: + Utils.activateHibernateOrSleep(powerType); + break; + case Constants.PowerType.HIBERNATE: + Utils.activateHibernateOrSleep(powerType); + break; + } +} + +/** + * @param {PowerMenuItem} powerMenuItem Bind visibility of the powermenu item + */ +export function bindPowerItemVisibility(powerMenuItem) { + const {powerType} = powerMenuItem; + const systemActions = SystemActions.getDefault(); + + switch (powerType) { + case Constants.PowerType.POWER_OFF: + systemActions.bind_property('can-power-off', powerMenuItem, 'visible', + GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE); + break; + case Constants.PowerType.RESTART: + systemActions.bind_property('can-restart', powerMenuItem, 'visible', + GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE); + break; + case Constants.PowerType.LOCK: + systemActions.bind_property('can-lock-screen', powerMenuItem, 'visible', + GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE); + break; + case Constants.PowerType.LOGOUT: + systemActions.bind_property('can-logout', powerMenuItem, 'visible', + GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE); + break; + case Constants.PowerType.SUSPEND: + systemActions.bind_property('can-suspend', powerMenuItem, 'visible', + GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE); + break; + case Constants.PowerType.SWITCH_USER: + systemActions.bind_property('can-switch-user', powerMenuItem, 'visible', + GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE); + break; + case Constants.PowerType.HYBRID_SLEEP: + Utils.canHibernateOrSleep('CanHybridSleep', result => (powerMenuItem.visible = result)); + break; + case Constants.PowerType.HIBERNATE: + Utils.canHibernateOrSleep('CanHibernate', result => (powerMenuItem.visible = result)); + break; + } +} + +export class BaseMenuItem extends St.BoxLayout { + static [GObject.properties] = { + 'active': GObject.ParamSpec.boolean('active', 'active', 'active', + GObject.ParamFlags.READWRITE, + false), + 'sensitive': GObject.ParamSpec.boolean('sensitive', 'sensitive', 'sensitive', + GObject.ParamFlags.READWRITE, + true), + }; + + static [GObject.signals] = { + 'activate': {param_types: [Clutter.Event.$gtype]}, + }; + + static { + GObject.registerClass(this); + } + + constructor(menuLayout, params) { + params = Params.parse(params, { + reactive: true, + activate: true, + hover: true, + style_class: null, + can_focus: true, + }); + super({ + style_class: 'popup-menu-item arcmenu-menu-item', + reactive: params.reactive, + track_hover: params.reactive, + can_focus: params.can_focus, + pivot_point: new Graphene.Point({x: 0.5, y: 0.5}), + accessible_role: Atk.Role.MENU_ITEM, + }); + + this.hasContextMenu = false; + this._delegate = this; + + this._menuButton = menuLayout.menuButton; + this._settings = menuLayout.settings; + this._arcMenu = menuLayout.arcMenu; + this._extension = menuLayout.extension; + this._menuLayout = menuLayout; + + this.tooltipLocation = Constants.TooltipLocation.BOTTOM; + this.shouldShow = true; + this._parent = null; + this._active = false; + this._activatable = params.reactive && params.activate; + this._sensitive = true; + + this.x_align = Clutter.ActorAlign.FILL; + this.x_expand = true; + + if (!this._activatable) + this.add_style_class_name('popup-inactive-menu-item'); + + if (params.style_class) + this.add_style_class_name(params.style_class); + + if (params.hover) + this.connect('notify::hover', this._onHover.bind(this)); + if (params.reactive && params.hover) + this.bind_property('hover', this, 'active', GObject.BindingFlags.SYNC_CREATE); + + const textureCache = St.TextureCache.get_default(); + textureCache.connectObject('icon-theme-changed', () => this._updateIcon(), this); + + this._panAction = new Clutter.PanAction({interpolate: true}); + this._panAction.connect('pan', this._onPan.bind(this)); + this.add_action(this._panAction); + + this._clickAction = new Clutter.ClickAction({ + enabled: this._activatable, + }); + this._clickAction.connect('clicked', this._onClicked.bind(this)); + this._clickAction.connect('long-press', this._onLongPress.bind(this)); + this._clickAction.connect('notify::pressed', () => { + if (this._clickAction.pressed) + this.add_style_pseudo_class('active'); + else + this.remove_style_pseudo_class('active'); + }); + this.add_action(this._clickAction); + + this.connect('destroy', () => this._onDestroy()); + } + + _onPan(action) { + let parent = this.get_parent(); + while (!(parent instanceof St.ScrollView)) { + if (!parent) + return false; + parent = parent.get_parent(); + } + + this._clickAction.release(); + + return this._menuLayout._onPan(action, parent); + } + + _onClicked(action) { + const isPrimaryOrTouch = action.get_button() === Clutter.BUTTON_PRIMARY || action.get_button() === 0; + if (isPrimaryOrTouch) { + this.active = false; + this._menuLayout.grab_key_focus(); + this.remove_style_pseudo_class('active'); + this.activate(Clutter.get_current_event()); + } else if (action.get_button() === Clutter.BUTTON_SECONDARY) { + if (this.hasContextMenu) + this.popupContextMenu(); + else + this.remove_style_pseudo_class('active'); + } else if (action.get_button() === 8) { + const backButton = this._menuLayout.backButton; + if (backButton && backButton.visible) { + this.active = false; + this._menuLayout.grab_key_focus(); + this.remove_style_pseudo_class('active'); + backButton.activate(Clutter.get_current_event()); + } + } + } + + _onLongPress(action, theActor, state) { + const isPrimaryOrTouch = action.get_button() === Clutter.BUTTON_PRIMARY || action.get_button() === 0; + if (state === Clutter.LongPressState.QUERY) + return isPrimaryOrTouch && this._menuLayout.arcMenu.isOpen && this.hasContextMenu; + + if (state === Clutter.LongPressState.ACTIVATE && isPrimaryOrTouch) + this.popupContextMenu(); + + return true; + } + + _updateIcon() { + if (this.isDestroyed) + return; + + if (!this._iconBin || !this.createIcon) + return; + + const icon = this.createIcon(); + if (icon) + this._iconBin.set_child(icon); + } + + get actor() { + return this; + } + + get active() { + return this._active; + } + + set active(active) { + if (this.isDestroyed || !this.mapped) + return; + + // Prevent a mouse hover event from setting a new active menu item, until next mouse move event. + if (this.hover && this._menuLayout.blockHoverState) { + this.hover = false; + return; + } + + const activeChanged = active !== this.active; + if (activeChanged) { + this._active = active; + + if (active) { + const topSearchResult = this._menuLayout.searchResults?.getTopResult(); + if (topSearchResult) + topSearchResult.remove_style_pseudo_class('active'); + + // track the active menu item for keyboard navigation + if (this._menuLayout.activeMenuItem !== this) + this._menuLayout.activeMenuItem = this; + + this.add_style_class_name('selected'); + if (this.can_focus) + this.grab_key_focus(); + } else { + this.remove_style_class_name('selected'); + if (!this.isActiveCategory) + this.remove_style_pseudo_class('active'); + } + this.notify('active'); + } + } + + setShouldShow() { + // If a saved shortcut link is a desktop app, check if currently installed. + // Do NOT display if application not found. + if (this._command.endsWith('.desktop') && !Shell.AppSystem.get_default().lookup_app(this._command)) + this.shouldShow = false; + } + + _onHover() { + if (!this._menuLayout.blockHoverState && this.hover && (this.label || this.tooltipText)) { + const tooltipTitle = this.label || this.tooltipText; + let {description} = this; + if (this._app) + description = this._app.get_description(); + this._menuButton.tooltip.showTooltip(this, this.tooltipLocation, tooltipTitle, + description, this._displayType ? this._displayType : -1); + } else if (!this.hover || this._menuLayout.blockHoverState) { + this._menuButton.tooltip.hide(); + } + } + + vfunc_motion_event() { + // Prevent a mouse hover event from setting a new active menu item, until next mouse move event. + if (this._menuLayout.blockHoverState) { + this._menuLayout.blockHoverState = false; + this.hover = true; + } + return Clutter.EVENT_PROPAGATE; + } + + vfunc_key_focus_in() { + super.vfunc_key_focus_in(); + if (!this.hover) + this._menuLayout._keyFocusIn(this); + this.active = true; + } + + vfunc_key_focus_out() { + super.vfunc_key_focus_out(); + if (this.contextMenu && this.contextMenu.isOpen) + return; + + this.active = false; + this.hover = false; + } + + activate(event) { + this.emit('activate', event); + } + + vfunc_key_press_event(event) { + this._menuLayout.blockHoverState = true; + if (global.focus_manager.navigate_from_event(Clutter.get_current_event())) + return Clutter.EVENT_STOP; + + if (!this._activatable) + return super.vfunc_key_press_event(event); + + let state = event.get_state(); + + // if user has a modifier down (except capslock and numlock) + // then don't handle the key press here + state &= ~Clutter.ModifierType.LOCK_MASK; + state &= ~Clutter.ModifierType.MOD2_MASK; + state &= Clutter.ModifierType.MODIFIER_MASK; + + if (state) + return Clutter.EVENT_PROPAGATE; + + const symbol = event.get_key_symbol(); + if (symbol === Clutter.KEY_Return || symbol === Clutter.KEY_KP_Enter) { + this.active = false; + this._menuLayout.grab_key_focus(); + this.activate(Clutter.get_current_event()); + return Clutter.EVENT_STOP; + } else if (symbol === Clutter.KEY_Menu && this.hasContextMenu) { + this.popupContextMenu(); + } + + return Clutter.EVENT_PROPAGATE; + } + + _onDestroy() { + if (this._menuButton.tooltip && this._menuButton.tooltip.sourceActor === this) + this._menuButton.tooltip.hide(true); + + this.isDestroyed = true; + } +} + +export class ArcMenuSeparator extends PopupMenu.PopupBaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, separatorLength, separatorAlignment, text) { + super({ + style_class: 'popup-separator-menu-item', + reactive: false, + can_focus: false, + }); + this.reactive = true; + + this._settings = menuLayout.settings; + + this.label = new St.Label({ + text: text || '', + style: 'font-weight: bold', + }); + this.add_child(this.label); + this.label_actor = this.label; + + this.label.connect('notify::text', this._syncLabelVisibility.bind(this)); + this._syncLabelVisibility(); + + this._separator = new St.Widget({ + style_class: 'popup-separator-menu-item-separator separator-color-style', + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this._separator); + + if (separatorAlignment === Constants.SeparatorAlignment.HORIZONTAL) { + this.style = 'padding: 0px 5px; margin: 6px 0px;'; + switch (separatorLength) { + case Constants.SeparatorStyle.EMPTY: + this._separator.visible = false; + break; + case Constants.SeparatorStyle.SHORT: + this._separator.style = 'margin: 0px 45px;'; + break; + case Constants.SeparatorStyle.MEDIUM: + this._separator.style = 'margin: 0px 15px;'; + break; + case Constants.SeparatorStyle.LONG: + this._separator.style = 'margin: 0px 5px;'; + this.style = 'padding: 0px 5px; margin: 1px 0px;'; + break; + case Constants.SeparatorStyle.MAX: + this._separator.style = 'margin: 0px; padding: 0px;'; + break; + case Constants.SeparatorStyle.HEADER_LABEL: + this._separator.style = 'margin: 0px 20px 0px 10px;'; + this.style = 'padding: 5px 15px; margin: 6px 0px;'; + break; + } + } else if (separatorAlignment === Constants.SeparatorAlignment.VERTICAL) { + if (separatorLength === Constants.SeparatorStyle.LONG) { + this._separator.style = 'margin: 5px 0px; width: 1px;'; + this.style = 'padding: 5px 0px; margin: 1px 0px;'; + } else if (separatorLength === Constants.SeparatorStyle.MAX) { + this._separator.style = 'margin: 0px 0px; width: 1px;'; + this.style = 'padding: 0px 0px; margin: 0px 0px;'; + } else { + this._syncVisibility(); + this._settings.connectObject('changed::vert-separator', this._syncVisibility.bind(this), this); + this.style = 'padding: 0px 6px; margin: 6px 0px;'; + this._separator.style = 'margin: 0px; width: 1px; height: -1px;'; + } + + this.remove_child(this.label); + this.x_expand = this._separator.x_expand = true; + this.x_align = this._separator.x_align = Clutter.ActorAlign.CENTER; + this.y_expand = this._separator.y_expand = true; + this.y_align = this._separator.y_align = Clutter.ActorAlign.FILL; + } + } + + _syncLabelVisibility() { + this.label.visible = this.label.text !== ''; + } + + _syncVisibility() { + this._separator.visible = this._settings.get_boolean('vert-separator'); + } +} + +export class ActivitiesMenuItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super(menuLayout); + + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + + this._updateIcon(); + + this.label = new St.Label({ + text: _('Activities Overview'), + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this.label); + } + + createIcon() { + const IconSizeEnum = this._settings.get_enum('quicklinks-item-icon-size'); + const iconSize = Utils.getIconSize(IconSizeEnum, this._menuLayout.quicklinks_icon_size); + + return new St.Icon({ + icon_name: 'view-fullscreen-symbolic', + style_class: 'popup-menu-icon', + icon_size: iconSize, + }); + } + + activate(event) { + Main.overview.show(); + super.activate(event); + this._menuLayout.arcMenu.toggle(); + } +} + +export class Tooltip extends St.Label { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super({ + name: 'ArcMenu_Tooltip', + style_class: 'dash-label arcmenu-tooltip', + opacity: 0, + }); + const {clutterText} = this; + clutterText.set({ + line_wrap: true, + line_wrap_mode: Pango.WrapMode.WORD_CHAR, + }); + + this._menuButton = menuButton; + this._settings = menuButton.settings; + + global.stage.add_child(this); + this.hide(); + + this._useTooltips = !this._settings.get_boolean('disable-tooltips'); + this._settings.connectObject('changed::disable-tooltips', this.disableTooltips.bind(this), this); + this.connect('destroy', () => this._onDestroy()); + } + + showTooltip(sourceActor, location, titleLabel, description, displayType) { + if (!sourceActor) + return; + + if (this.sourceActor === sourceActor) { + this._showTimeout(titleLabel, description, displayType); + return; + } + + this.sourceActor = sourceActor; + this.location = location; + + this._showTimeout(titleLabel, description, displayType); + } + + disableTooltips() { + this._useTooltips = !this._settings.get_boolean('disable-tooltips'); + } + + _setToolTipText(titleLabel, description, displayType) { + let isEllipsized, titleText; + if (titleLabel instanceof St.Label) { + const lbl = titleLabel.clutter_text; + lbl.get_allocation_box(); + isEllipsized = lbl.get_layout().is_ellipsized(); + titleText = titleLabel.text.replace(/\n/g, ' '); + } else { + titleText = titleLabel; + } + + this.text = ''; + + if (displayType !== Constants.DisplayType.BUTTON) { + if (isEllipsized && description) { + const text = `${titleText}\n${description}`; + this.clutter_text.set_markup(text); + } else if (isEllipsized && !description) { + this.text = titleText ?? ''; + } else if (!isEllipsized && description) { + this.text = description ?? ''; + } + } else if (displayType === Constants.DisplayType.BUTTON) { + this.text = titleText ?? ''; + } + + return !!this.text; + } + + _showTimeout(titleLabel, description, displayType) { + if (this._useTooltips) { + this._menuButton.tooltipShowingID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 750, () => { + const shouldShow = this._setToolTipText(titleLabel, description, displayType); + + if (!shouldShow) { + this._menuButton.tooltipShowingID = null; + return GLib.SOURCE_REMOVE; + } + + this._show(); + this._menuButton.tooltipShowing = true; + this._menuButton.tooltipShowingID = null; + return GLib.SOURCE_REMOVE; + }); + } + } + + _show() { + if (!this.sourceActor) + return; + if (this._useTooltips) { + this.opacity = 0; + this.show(); + + const [stageX, stageY] = this.sourceActor.get_transformed_position(); + + const itemWidth = this.sourceActor.allocation.x2 - this.sourceActor.allocation.x1; + const itemHeight = this.sourceActor.allocation.y2 - this.sourceActor.allocation.y1; + + const labelWidth = this.get_width(); + const labelHeight = this.get_height(); + + let x, y; + const gap = 5; + + switch (this.location) { + case Constants.TooltipLocation.BOTTOM_CENTERED: + y = stageY + itemHeight + gap; + x = stageX + Math.floor((itemWidth - labelWidth) / 2); + break; + case Constants.TooltipLocation.TOP_CENTERED: + y = stageY - labelHeight - gap; + x = stageX + Math.floor((itemWidth - labelWidth) / 2); + break; + case Constants.TooltipLocation.BOTTOM: + default: + y = stageY + itemHeight + gap; + x = stageX + gap; + break; + } + + // keep the label inside the screen + const monitor = Main.layoutManager.findMonitorForActor(this.sourceActor); + if (x - monitor.x < gap) + x += monitor.x - x + gap; + else if (x + labelWidth > monitor.x + monitor.width - gap) + x -= x + labelWidth - (monitor.x + monitor.width) + gap; + else if (y - monitor.y < gap) + y += monitor.y - y + gap; + else if (y + labelHeight > monitor.y + monitor.height - gap) + y -= y + labelHeight - (monitor.y + monitor.height) + gap; + + this.set_position(x, y); + this.ease({ + opacity: 255, + duration: TOOLTIP_SHOW_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + } + } + + hide(instantHide) { + if (this._useTooltips) { + if (this._menuButton.tooltipShowingID) { + GLib.source_remove(this._menuButton.tooltipShowingID); + this._menuButton.tooltipShowingID = null; + } + this.sourceActor = null; + this.ease({ + opacity: 0, + duration: instantHide ? 0 : TOOLTIP_HIDE_TIME, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => super.hide(), + }); + } + } + + _onDestroy() { + if (this._menuButton.tooltipShowingID) { + GLib.source_remove(this._menuButton.tooltipShowingID); + this._menuButton.tooltipShowingID = null; + } + + global.stage.remove_child(this); + } +} + +export class ArcMenuButtonItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, tooltipText, iconName, gicon) { + super(menuLayout); + this.set({ + x_expand: false, + x_align: Clutter.ActorAlign.CENTER, + y_expand: false, + y_align: Clutter.ActorAlign.CENTER, + style_class: 'popup-menu-item arcmenu-button', + }); + + this.tooltipLocation = Constants.TooltipLocation.BOTTOM_CENTERED; + this.tooltipText = tooltipText; + this.iconName = iconName; + this.gicon = gicon; + this.toggleMenuOnClick = true; + this._displayType = Constants.DisplayType.BUTTON; + + if (this.iconName !== null) { + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + + this._updateIcon(); + } + } + + createIcon(overrideIconSize) { + const IconSizeEnum = this._settings.get_enum('button-item-icon-size'); + const iconSize = Utils.getIconSize(IconSizeEnum, this._menuLayout.buttons_icon_size); + + return new St.Icon({ + gicon: this.gicon ? this.gicon : Gio.icon_new_for_string(this.iconName), + icon_size: overrideIconSize ? overrideIconSize : iconSize, + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + } + + setIconSize(size) { + if (!this._iconBin) + return; + this._iconBin.set_child(this.createIcon(size)); + } + + activate(event) { + if (this.toggleMenuOnClick) + this._menuLayout.arcMenu.toggle(); + super.activate(event); + } +} + +export class PowerOptionsBox extends St.ScrollView { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, vertical = false) { + super({ + x_expand: false, + overlay_scrollbars: true, + clip_to_allocation: true, + }); + + this._settings = menuLayout.settings; + + this._orientation = vertical ? Clutter.Orientation.VERTICAL : Clutter.Orientation.HORIZONTAL; + + const box = new St.BoxLayout({ + vertical, + style: 'spacing: 6px;', + }); + Utils.addChildToParent(this, box); + + const powerOptions = this._settings.get_value('power-options').deep_unpack(); + for (let i = 0; i < powerOptions.length; i++) { + const [powerType, shouldShow] = powerOptions[i]; + if (shouldShow) { + const powerButton = new PowerButton(menuLayout, powerType); + powerButton.connectObject('key-focus-in', + () => Utils.ensureActorVisibleInScrollView(powerButton, this._orientation), this); + powerButton.style = 'margin: 0px;'; + box.add_child(powerButton); + } + } + } +} + +// 'Power Off / Log Out' button with popupmenu that shows lock, power off, restart, etc +export class LeaveButton extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, showLabel) { + super(menuLayout); + this.menuButton = menuLayout.menuButton; + + this.toggleMenuOnClick = false; + this.iconName = 'system-shutdown-symbolic'; + this.showLabel = showLabel; + + this._createLeaveMenu(); + + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + + this._updateIcon(); + + if (showLabel) { + this.label = new St.Label({ + text: _('Power Off / Log Out'), + y_expand: false, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this.label); + } else { + this.tooltipLocation = Constants.TooltipLocation.BOTTOM_CENTERED; + this.style_class = 'popup-menu-item arcmenu-button'; + this.set({ + x_expand: false, + x_align: Clutter.ActorAlign.CENTER, + y_expand: false, + y_align: Clutter.ActorAlign.CENTER, + }); + + this.toggleMenuOnClick = true; + this._displayType = Constants.DisplayType.BUTTON; + this.tooltipText = _('Power Off / Log Out'); + } + } + + createIcon(overrideIconSize) { + const IconSizeEnum = this.showLabel ? this._settings.get_enum('quicklinks-item-icon-size') + : this._settings.get_enum('button-item-icon-size'); + const defaultIconSize = this.showLabel ? this._menuLayout.quicklinks_icon_size + : this._menuLayout.buttons_icon_size; + const iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize); + + return new St.Icon({ + gicon: Gio.icon_new_for_string(this.iconName), + icon_size: overrideIconSize ? overrideIconSize : iconSize, + x_expand: !this.showLabel, + x_align: this.showLabel ? Clutter.ActorAlign.START : Clutter.ActorAlign.CENTER, + }); + } + + setIconSize(size) { + if (!this._iconBin) + return; + this._iconBin.set_child(this.createIcon(size)); + } + + _createLeaveMenu() { + this.leaveMenu = new PopupMenu.PopupMenu(this, 0.5, St.Side.BOTTOM); + this.leaveMenu.blockSourceEvents = true; + this.leaveMenu.actor.add_style_class_name('popup-menu arcmenu-menu'); + const section = new PopupMenu.PopupMenuSection(); + this.leaveMenu.addMenuItem(section); + + const box = new St.BoxLayout({vertical: true}); + box._delegate = box; + section.actor.add_child(box); + + const sessionBox = new St.BoxLayout({vertical: true}); + sessionBox.add_child(this._menuLayout.createLabelRow(_('Session'))); + box.add_child(sessionBox); + + const systemBox = new St.BoxLayout({vertical: true}); + systemBox.add_child(this._menuLayout.createLabelRow(_('System'))); + box.add_child(systemBox); + + let hasSessionOption, hasSystemOption; + const powerOptions = this._settings.get_value('power-options').deep_unpack(); + for (let i = 0; i < powerOptions.length; i++) { + const [powerType, shouldShow] = powerOptions[i]; + if (shouldShow) { + const powerButton = new PowerMenuItem(this._menuLayout, powerType); + if (powerType === Constants.PowerType.LOCK || powerType === Constants.PowerType.LOGOUT || + powerType === Constants.PowerType.SWITCH_USER) { + hasSessionOption = true; + sessionBox.add_child(powerButton); + } else { + hasSystemOption = true; + systemBox.add_child(powerButton); + } + } + } + + if (!hasSessionOption) + sessionBox.hide(); + if (!hasSystemOption) + systemBox.hide(); + + this._menuLayout.subMenuManager.addMenu(this.leaveMenu); + this.leaveMenu.actor.hide(); + Main.uiGroup.add_child(this.leaveMenu.actor); + this.leaveMenu.connect('open-state-changed', (menu, open) => { + if (open) { + this.add_style_pseudo_class('active'); + if (this.menuButton.tooltipShowingID) { + GLib.source_remove(this.menuButton.tooltipShowingID); + this.menuButton.tooltipShowingID = null; + this.menuButton.tooltipShowing = false; + } + if (this.tooltip) { + this.tooltip.hide(); + this.menuButton.tooltipShowing = false; + } + } else { + this.remove_style_pseudo_class('active'); + this.active = false; + this.sync_hover(); + this.hovered = this.hover; + } + }); + } + + _onDestroy() { + Main.uiGroup.remove_child(this.leaveMenu.actor); + this.leaveMenu.destroy(); + } + + activate(event) { + super.activate(event); + this.leaveMenu.toggle(); + } +} + +export class PowerButton extends ArcMenuButtonItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, powerType) { + super(menuLayout, Constants.PowerOptions[powerType].NAME, + Constants.PowerOptions[powerType].ICON); + this.powerType = powerType; + + bindPowerItemVisibility(this); + } + + activate() { + this._menuLayout.arcMenu.toggle(); + activatePowerOption(this.powerType); + } +} + +export class PowerMenuItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, type) { + super(menuLayout); + this.powerType = type; + + bindPowerItemVisibility(this); + + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + this._updateIcon(); + + this.label = new St.Label({ + text: _(Constants.PowerOptions[this.powerType].NAME), + y_expand: false, + y_align: Clutter.ActorAlign.CENTER, + }); + + this.add_child(this.label); + } + + createIcon() { + const IconSizeEnum = this._settings.get_enum('quicklinks-item-icon-size'); + const iconSize = Utils.getIconSize(IconSizeEnum, this._menuLayout.quicklinks_icon_size); + + return new St.Icon({ + gicon: Gio.icon_new_for_string(Constants.PowerOptions[this.powerType].ICON), + style_class: 'popup-menu-icon', + icon_size: iconSize, + }); + } + + activate() { + this._menuLayout.arcMenu.toggle(); + activatePowerOption(this.powerType); + } +} + +export class NavigationButton extends ArcMenuButtonItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, text, arrowSymbolic, activateAction, arrowSide) { + super(menuLayout, null, arrowSymbolic); + this.activateAction = activateAction; + + this.set({ + style: 'min-height: 28px; padding: 0px 8px;', + x_expand: true, + x_align: Clutter.ActorAlign.END, + }); + + this.toggleMenuOnClick = false; + + this._label = new St.Label({ + text: _(text), + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_expand: false, + y_align: Clutter.ActorAlign.CENTER, + }); + + if (arrowSide === St.Side.LEFT) + this.add_child(this._label); + else + this.insert_child_at_index(this._label, 0); + } + + createIcon() { + const IconSizeEnum = this._settings.get_enum('misc-item-icon-size'); + const iconSize = Utils.getIconSize(IconSizeEnum, Constants.EXTRA_SMALL_ICON_SIZE); + + return new St.Icon({ + gicon: this.gicon ? this.gicon : Gio.icon_new_for_string(this.iconName), + icon_size: iconSize, + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + } + + activate(event) { + super.activate(event); + this.activateAction(); + } +} + +export class GoNextButton extends NavigationButton { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, title, activateAction) { + super(menuLayout, _(title), 'go-next-symbolic', () => activateAction()); + } +} + +export class GoPreviousButton extends NavigationButton { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, activateAction) { + super(menuLayout, _('Back'), 'go-previous-symbolic', () => activateAction(), St.Side.LEFT); + } +} + +// Menu item to go back to category view +export class BackButton extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super(menuLayout); + + this._iconBin = new St.Bin({ + x_expand: false, + x_align: Clutter.ActorAlign.START, + }); + this.add_child(this._iconBin); + this._updateIcon(); + + const label = new St.Label({ + text: _('Back'), + x_expand: false, + x_align: Clutter.ActorAlign.START, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(label); + } + + createIcon() { + const IconSizeEnum = this._settings.get_enum('misc-item-icon-size'); + const iconSize = Utils.getIconSize(IconSizeEnum, Constants.MISC_ICON_SIZE); + + return new St.Icon({ + icon_name: 'go-previous-symbolic', + icon_size: iconSize, + style_class: 'popup-menu-icon', + }); + } + + activate(event) { + const layout = this._settings.get_enum('menu-layout'); + if (layout === Constants.MenuLayout.ARCMENU) { + // If the current page is inside a category and + // previous page was the categories page, + // go back to categories page + if (this._menuLayout.previousCategoryType === Constants.CategoryType.CATEGORIES_LIST && + (this._menuLayout.activeCategoryType <= 4 || + this._menuLayout.activeCategoryType instanceof GMenu.TreeDirectory)) + this._menuLayout.displayCategories(); + else + this._menuLayout.setDefaultMenuView(); + } else if (layout === Constants.MenuLayout.TOGNEE) { + this._menuLayout.setDefaultMenuView(); + } + super.activate(event); + } +} + +// Menu item to view all apps +export class ViewAllAppsButton extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super(menuLayout); + + const label = new St.Label({ + text: _('All Apps'), + x_expand: false, + x_align: Clutter.ActorAlign.START, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(label); + + this._iconBin = new St.Bin({ + x_expand: false, + x_align: Clutter.ActorAlign.START, + }); + this.add_child(this._iconBin); + this._updateIcon(); + } + + createIcon() { + const IconSizeEnum = this._settings.get_enum('misc-item-icon-size'); + const iconSize = Utils.getIconSize(IconSizeEnum, Constants.MISC_ICON_SIZE); + + return new St.Icon({ + icon_name: 'go-next-symbolic', + icon_size: iconSize, + x_align: Clutter.ActorAlign.START, + style_class: 'popup-menu-icon', + }); + } + + activate(event) { + const showAppsAction = this._settings.get_enum('all-apps-button-action'); + if (showAppsAction === Constants.AllAppsButtonAction.ALL_PROGRAMS) { + this._menuLayout.displayAllApps(); + super.activate(event); + return; + } + + const defaultMenuView = this._settings.get_enum('default-menu-view'); + if (defaultMenuView === Constants.DefaultMenuView.PINNED_APPS || + defaultMenuView === Constants.DefaultMenuView.FREQUENT_APPS) + this._menuLayout.displayCategories(); + else + this._menuLayout.displayAllApps(); + super.activate(event); + } +} + +export class ShortcutMenuItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, itemData, displayType, isContainedInCategory) { + super(menuLayout); + this._displayType = displayType; + this.isContainedInCategory = isContainedInCategory; + + let name = itemData.name ?? ''; + const {icon, id} = itemData; + this._command = id ?? ''; + this.iconName = icon ?? ''; + + const shortcutIconType = this._settings.get_enum('shortcut-icon-type'); + if (shortcutIconType === Constants.CategoryIconType.FULL_COLOR) + this.add_style_class_name('regular-icons'); + else + this.add_style_class_name('symbolic-icons'); + + // Check for default commands-------- + if (this._command === Constants.ShortcutCommands.SOFTWARE) + this._command = Utils.findSoftwareManager(); + + if (!this._app) + this._app = this._menuLayout.appSys.lookup_app(this._command); + + if (this._app && !this.iconName) { + const appIcon = this._app.create_icon_texture(Constants.MEDIUM_ICON_SIZE); + if (appIcon instanceof St.Icon) + this.iconName = appIcon.gicon.to_string(); + } + + if (!name && this._app) + name = this._app.get_name(); + // ------------------------------------- + + this.hasContextMenu = !!this._app; + + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + this._updateIcon(); + + this.label = new St.Label({ + text: _(name), + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + + const layout = this._settings.get_enum('menu-layout'); + if (layout === Constants.MenuLayout.PLASMA && + this._settings.get_boolean('apps-show-extra-details') && this._app) { + const labelBox = new St.BoxLayout({ + vertical: true, + }); + const descriptionLabel = new St.Label({ + text: this._app.get_description(), + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + style: 'font-weight: lighter;', + }); + labelBox.add_child(this.label); + if (this._app.get_description()) + labelBox.add_child(descriptionLabel); + this.add_child(labelBox); + } else { + this.add_child(this.label); + } + + if (this._displayType === Constants.DisplayType.GRID) + Utils.convertToGridLayout(this); + else if (this._displayType === Constants.DisplayType.BUTTON) + Utils.convertToButton(this); + + this.setShouldShow(); + } + + createIcon() { + let iconSizeEnum; + if (this.isContainedInCategory) + iconSizeEnum = this._settings.get_enum('menu-item-icon-size'); + else + iconSizeEnum = this._settings.get_enum('quicklinks-item-icon-size'); + + let defaultIconSize, iconSize; + if (this._displayType === Constants.DisplayType.BUTTON) { + iconSizeEnum = this._settings.get_enum('button-item-icon-size'); + defaultIconSize = this._menuLayout.buttons_icon_size; + iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize); + this.style = `min-width: ${iconSize}px; min-height: ${iconSize}px;`; + } else if (this._displayType === Constants.DisplayType.GRID) { + iconSizeEnum = this._settings.get_enum('menu-item-grid-icon-size'); + defaultIconSize = this._menuLayout.icon_grid_size; + ({iconSize} = Utils.getGridIconSize(iconSizeEnum, defaultIconSize)); + } else { + defaultIconSize = this.isContainedInCategory ? this._menuLayout.apps_icon_size + : this._menuLayout.quicklinks_icon_size; + iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize); + } + + return new St.Icon({ + icon_name: this.iconName, + gicon: Gio.icon_new_for_string(this.iconName), + style_class: this._displayType === Constants.DisplayType.LIST ? 'popup-menu-icon' : '', + icon_size: iconSize, + }); + } + + popupContextMenu() { + if (this._app && this.contextMenu === undefined) { + this.contextMenu = new AppContextMenu(this, this._menuLayout); + if (this._menuLayout.context_menu_location === Constants.ContextMenuLocation.BOTTOM_CENTERED) + this.contextMenu.centerBoxPointerPosition(); + else if (this._menuLayout.context_menu_location === Constants.ContextMenuLocation.RIGHT) + this.contextMenu.rightBoxPointerPosition(); + + if (this._app) + this.contextMenu.setApp(this._app); + else if (this.folderPath) + this.contextMenu.setFolderPath(this.folderPath); + } + if (this.contextMenu !== undefined) { + if (this.tooltip !== undefined) + this.tooltip.hide(); + this.contextMenu.open(BoxPointer.PopupAnimation.FULL); + } + } + + activate() { + switch (this._command) { + case Constants.ShortcutCommands.LOG_OUT: + case Constants.ShortcutCommands.LOCK: + case Constants.ShortcutCommands.POWER_OFF: + case Constants.ShortcutCommands.RESTART: + case Constants.ShortcutCommands.SUSPEND: + case Constants.ShortcutCommands.HIBERNATE: + case Constants.ShortcutCommands.HYBRID_SLEEP: + case Constants.ShortcutCommands.SWITCH_USER: { + const powerType = Utils.getPowerTypeFromShortcutCommand(this._command); + activatePowerOption(powerType); + break; + } + case Constants.ShortcutCommands.OVERVIEW: + Main.overview.show(); + break; + case Constants.ShortcutCommands.RUN_COMMAND: + Main.openRunDialog(); + break; + case Constants.ShortcutCommands.SHOW_APPS: + Main.overview._overview._controls._toggleAppsPage(); + break; + default: { + if (this._app) + this._app.open_new_window(-1); + else + Util.spawnCommandLine(this._command); + } + } + this._menuLayout.arcMenu.toggle(); + } +} + +// Menu item which displays the current user +export class UserMenuItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, displayType) { + super(menuLayout); + this._menuLayout = menuLayout; + this._displayType = displayType; + + if (this._settings.get_enum('avatar-style') === Constants.AvatarStyle.ROUND) + this.avatarStyle = 'arcmenu-avatar-round'; + else + this.avatarStyle = 'arcmenu-avatar-square'; + + if (this._displayType === Constants.DisplayType.BUTTON) { + const IconSizeEnum = this._settings.get_enum('button-item-icon-size'); + const defaultIconSize = this._menuLayout.buttons_icon_size; + this.iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize); + this.userMenuIcon.set_style_class_name(`${this.avatarStyle} user-icon`); + } else { + const IconSizeEnum = this._settings.get_enum('misc-item-icon-size'); + this.iconSize = Utils.getIconSize(IconSizeEnum, USER_AVATAR_SIZE); + } + + this.userMenuIcon = new UserMenuIcon(menuLayout, this.iconSize, false); + this.add_child(this.userMenuIcon); + this.label = this.userMenuIcon.label; + this.add_child(this.label); + + if (this._displayType === Constants.DisplayType.BUTTON) + Utils.convertToButton(this); + } + + activate(event) { + Util.spawnCommandLine('gnome-control-center user-accounts'); + this._menuLayout.arcMenu.toggle(); + super.activate(event); + } +} + +export class UserMenuIcon extends St.Bin { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, iconSize, hasTooltip) { + const {settings} = menuLayout; + + let avatarStyle; + if (settings.get_enum('avatar-style') === Constants.AvatarStyle.ROUND) + avatarStyle = 'arcmenu-avatar-round'; + else + avatarStyle = 'arcmenu-avatar-square'; + + super({ + style_class: `${avatarStyle} user-icon popup-menu-icon`, + track_hover: true, + reactive: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + style: `width: ${iconSize}px; height: ${iconSize}px;`, + }); + + this._menuButton = menuLayout.menuButton; + this._menuLayout = menuLayout; + this.iconSize = iconSize; + this.tooltipLocation = Constants.TooltipLocation.BOTTOM_CENTERED; + + this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name()); + + this.label = new St.Label({ + text: GLib.get_real_name(), + y_align: Clutter.ActorAlign.CENTER, + }); + + this._user.connectObject('notify::is-loaded', this._onUserChanged.bind(this), this); + this._user.connectObject('changed', this._onUserChanged.bind(this), this); + + if (hasTooltip) + this.connect('notify::hover', this._onHover.bind(this)); + + this._onUserChanged(); + } + + _onHover() { + if (this.hover) { + this._menuButton.tooltip.showTooltip(this, this.tooltipLocation, GLib.get_real_name(), + null, Constants.DisplayType.BUTTON); + } else { + this._menuButton.tooltip.hide(); + } + } + + _onUserChanged() { + if (this._user.is_loaded) { + this.label.set_text(this._user.get_real_name()); + if (this.tooltip) + this.tooltip.titleLabel.text = this._user.get_real_name(); + + let iconFile = this._user.get_icon_file(); + if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS)) + iconFile = null; + + if (iconFile) { + this.child = null; + this.add_style_class_name('user-avatar'); + this.style = `${'background-image: url("%s");'.format(iconFile)}width: ${this.iconSize}px; height: ${this.iconSize}px;`; + } else { + this.style = `width: ${this.iconSize}px; height: ${this.iconSize}px;`; + this.child = new St.Icon({ + icon_name: 'avatar-default-symbolic', + icon_size: this.iconSize, + style: `padding: 5px; width: ${this.iconSize}px; height: ${this.iconSize}px;`, + }); + } + } + } +} + +export class DraggableMenuItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, displayType, isDraggable = true) { + super(menuLayout); + this._menuButton = menuLayout.menuButton; + this._displayType = displayType; + this._folderPreviewId = 0; + this._otherIconIsHovering = false; + this._delayedMoveData = null; + + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + + this.label = new St.Label({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.CENTER, + }); + + if (isDraggable) { + this.remove_action(this._panAction); + this.remove_action(this._clickAction); + + this._draggable = DND.makeDraggable(this, {timeoutThreshold: 400}); + this._draggable.addClickAction(this._clickAction); + this._draggable._animateDragEnd = eventTime => { + this._draggable._animationInProgress = true; + this._draggable._onAnimationComplete(this._draggable._dragActor, eventTime); + }; + this._draggable.connect('drag-begin', this._onDragBegin.bind(this)); + this._draggable.connect('drag-end', this._onDragEnd.bind(this)); + } + + if (this._displayType === Constants.DisplayType.GRID) + Utils.convertToGridLayout(this); + } + + createIcon() { + throw new Error('createIcon() Not yet implemented'); + } + + _onDragBegin() { + this.isDragging = true; + if (this._menuButton.tooltipShowingID) { + GLib.source_remove(this._menuButton.tooltipShowingID); + this._menuButton.tooltipShowingID = null; + this._menuButton.tooltipShowing = false; + } + if (this.tooltip) { + this.tooltip.hide(); + this._menuButton.tooltipShowing = false; + } + + if (this.contextMenu && this.contextMenu.isOpen) + this.contextMenu.toggle(); + + this.scaleAndFade(); + + this._dragMonitor = { + dragMotion: this._onDragMotion.bind(this), + }; + DND.addDragMonitor(this._dragMonitor); + } + + _onDragMotion(dragEvent) { + const parent = this.get_parent(); + const layoutManager = parent.layout_manager; + + const [success, x, y] = parent.transform_stage_point(dragEvent.x, dragEvent.y); + + if (!success) + return DND.DragMotionResult.CONTINUE; + + const {source} = dragEvent; + const [index, dragLocation] = this._getDropTarget(x, y, source, layoutManager); + const item = index !== -1 ? layoutManager.getItemAt(index) : null; + + Utils.ensureActorVisibleInScrollView(this, Clutter.Orientation.VERTICAL, {x1: x, x2: x + 25, y1: y, y2: y + 25}); + + // Dragging over invalid parts of the grid cancels the timeout + if (!item || item === source || + dragLocation === DragLocation.INVALID || + dragLocation === DragLocation.ON_ICON) { + this._removeDelayedMove(); + return DND.DragMotionResult.CONTINUE; + } + + if (!this._delayedMoveData || + this._delayedMoveData.index !== index) { + // Update the item with a small delay + this._removeDelayedMove(); + this._delayedMoveData = { + index, + source, + destroyId: source.connect('destroy', () => this._removeDelayedMove()), + timeoutId: GLib.timeout_add(GLib.PRIORITY_DEFAULT, + 200, () => { + parent.moveItem(this, index); + this._delayedMoveData.timeoutId = 0; + this._removeDelayedMove(); + return GLib.SOURCE_REMOVE; + }), + }; + } + + return DND.DragMotionResult.CONTINUE; + } + + _removeDelayedMove() { + if (!this._delayedMoveData) + return; + + const {source, destroyId, timeoutId} = this._delayedMoveData; + + if (timeoutId > 0) + GLib.source_remove(timeoutId); + + if (destroyId > 0) + source.disconnect(destroyId); + + this._delayedMoveData = null; + } + + _getDropTarget(x, y, source, layoutManager) { + const sourceIndex = layoutManager.getItemPosition(source); + let [targetIndex, dragLocation] = layoutManager.getDropTarget(x, y); + + let reflowDirection = Clutter.ActorAlign.END; + + if (sourceIndex === targetIndex) + reflowDirection = -1; + + if (targetIndex > sourceIndex) + reflowDirection = Clutter.ActorAlign.START; + else + reflowDirection = Clutter.ActorAlign.END; + + if (dragLocation === DragLocation.START_EDGE && + reflowDirection === Clutter.ActorAlign.START) { + const nColumns = layoutManager.columns; + const targetColumn = targetIndex % nColumns; + + if (targetColumn > 0) { + targetIndex -= 1; + dragLocation = DragLocation.END_EDGE; + } + } else if (dragLocation === DragLocation.END_EDGE && + reflowDirection === Clutter.ActorAlign.END) { + const nColumns = layoutManager.columns; + const targetColumn = targetIndex % nColumns; + + if (targetColumn < nColumns - 1) { + targetIndex += 1; + dragLocation = DragLocation.START_EDGE; + } + } else if (dragLocation === DragLocation.TOP_EDGE && + reflowDirection === Clutter.ActorAlign.START) { + const nColumns = layoutManager.columns; + const targetRow = Math.floor(targetIndex / nColumns); + + if (targetRow > 0) { + targetIndex = Math.max(0, targetIndex - nColumns); + dragLocation = DragLocation.BOTTOM_EDGE; + } + } else if (dragLocation === DragLocation.BOTTOM_EDGE && + reflowDirection === Clutter.ActorAlign.END) { + const nChildren = layoutManager.nChildren; + const nColumns = layoutManager.columns; + const nRows = Math.ceil(nChildren / nColumns); + const targetRow = Math.floor(targetIndex / nColumns); + + if (targetRow < nRows - 1) { + targetIndex = Math.min(nChildren - 1, targetIndex + nColumns); + dragLocation = DragLocation.TOP_EDGE; + } + } + + return [targetIndex, dragLocation]; + } + + _onDragEnd() { + if (this._dragMonitor) { + DND.removeDragMonitor(this._dragMonitor); + this._dragMonitor = null; + } + + if (!this.movedToFolder) + this.undoScaleAndFade(); + } + + scaleAndFade() { + this.reactive = false; + this.ease({ + scale_x: 0.5, + scale_y: 0.5, + opacity: 0, + }); + } + + undoScaleAndFade() { + this.reactive = true; + this.ease({ + scale_x: 1.0, + scale_y: 1.0, + opacity: 255, + }); + } + + _setHoveringByDnd(hovering) { + if (this._otherIconIsHovering === hovering) + return; + + this._otherIconIsHovering = hovering; + + if (hovering) { + this._hoveringDragMonitor = { + dragMotion: this._onHoveringDragMotion.bind(this), + }; + DND.addDragMonitor(this._hoveringDragMonitor); + + if (this._folderPreviewId > 0) + return; + + this._folderPreviewId = + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => { + this.add_style_pseudo_class('drop'); + this._showFolderPreview(); + this._folderPreviewId = 0; + return GLib.SOURCE_REMOVE; + }); + } else { + DND.removeDragMonitor(this._hoveringDragMonitor); + if (this._folderPreviewId > 0) { + GLib.source_remove(this._folderPreviewId); + this._folderPreviewId = 0; + } + this._hideFolderPreview(); + this.remove_style_pseudo_class('drop'); + } + } + + _onHoveringDragMotion(dragEvent) { + if (!this.contains(dragEvent.targetActor)) + this._setHoveringByDnd(false); + + return DND.DragMotionResult.CONTINUE; + } + + _showFolderPreview() { + this.label.opacity = 0; + this._iconBin.ease({ + scale_x: .5, + scale_y: .5, + }); + } + + _hideFolderPreview() { + this.label.opacity = 255; + this._iconBin.ease({ + scale_x: 1.0, + scale_y: 1.0, + }); + } + + handleDragOver(source, _actor, x, y) { + if (source === this) + return DND.DragMotionResult.NO_DROP; + + if (!this._canAccept(source)) + return DND.DragMotionResult.CONTINUE; + + if (this._withinLeeways(x, y)) { + this._setHoveringByDnd(false); + return DND.DragMotionResult.CONTINUE; + } + + this._setHoveringByDnd(true); + + return DND.DragMotionResult.MOVE_DROP; + } + + _canAccept(source) { + return source !== this && !this.disableAcceptDrop && !source.disableAcceptDrop && !source.disableAcceptDropAsSource; + } + + _withinLeeways(x, y) { + const leftDividerLeeway = Math.round(this.get_preferred_width(-1)[1] / 5); + const rightDividerLeeway = Math.round(this.get_preferred_width(-1)[1] / 5); + const topDividerLeeway = Math.round(this.get_preferred_height(-1)[1] / 5); + const bottomDividerLeeway = Math.round(this.get_preferred_height(-1)[1] / 5); + return x < leftDividerLeeway || + x > this.width - rightDividerLeeway || y < topDividerLeeway || + y > this.height - bottomDividerLeeway; + } + + acceptDrop(source, _actor, x, y) { + this._setHoveringByDnd(false); + + if (!this._canAccept(source)) + return false; + + if (this._withinLeeways(x, y)) + return false; + + return true; + } + + getDragActor() { + throw new Error('getDragActor() Not yet implemented'); + } + + getDragActorSource() { + return this; + } + + cancelActions() { + if (this._draggable) + this._draggable.fakeRelease(); + + DND.removeDragMonitor(this._hoveringDragMonitor); + + if (this._dragMonitor) { + DND.removeDragMonitor(this._dragMonitor); + this._dragMonitor = null; + } + } + + _onDestroy() { + this.cancelActions(); + super._onDestroy(); + + if (this._folderPreviewId > 0) { + GLib.source_remove(this._folderPreviewId); + this._folderPreviewId = 0; + } + } +} + +export class PinnedAppsFolderMenuItem extends DraggableMenuItem { + static [GObject.signals] = {'pinned-apps-changed': {param_types: [GObject.TYPE_JSOBJECT]}}; + + static { + GObject.registerClass(this); + } + + constructor(menuLayout, pinnedAppData, folderSettings, folderAppList, displayType, isContainedInCategory) { + super(menuLayout, displayType); + this._menuButton = menuLayout.menuButton; + this._displayType = displayType; + this.isContainedInCategory = isContainedInCategory; + this._folderAppList = folderAppList; + this.folderSettings = folderSettings; + + this._name = pinnedAppData.name; + this._command = pinnedAppData.id; + + this.appList = []; + + this._subMenuPopup = new FolderDialog(this, this._menuLayout); + this.disableAcceptDropAsSource = true; + + this.folderSettings.connectObject('changed::pinned-apps', () => { + this._folderAppList = folderSettings.get_value('pinned-apps').deepUnpack(); + this._loadPinnedApps(); + this._updateIcon(); + }, this); + + this._loadPinnedApps(); + + this.hasContextMenu = true; + + this._addFolderNameEntry(); + + this.add_child(this.label); + + this.updateData(pinnedAppData); + } + + updateData(pinnedAppData) { + this.pinnedAppData = pinnedAppData; + + this._name = pinnedAppData.name; + this._command = pinnedAppData.id; + + this._folderNameLabel.text = this._name; + this._entry.text = this._name; + + this.label.text = this._name; + this._updateIcon(); + } + + _addFolderNameEntry() { + this._entryBox = new St.BoxLayout({ + style_class: 'folder-name-container', + style: 'padding: 10px 18px;', + }); + this._subMenuPopup.box.insert_child_at_index(this._entryBox, 0); + + // Empty actor to center the title + const ghostButton = new Clutter.Actor(); + this._entryBox.add_child(ghostButton); + + const stack = new Shell.Stack({ + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + this._entryBox.add_child(stack); + + // Folder name label + this._folderNameLabel = new St.Label({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + style_class: 'folder-header', + }); + + stack.add_child(this._folderNameLabel); + + // Folder name entry + this._entry = new St.Entry({ + opacity: 0, + reactive: false, + style_class: 'folder-header', + style: 'width: 12em;', + }); + this._entry.clutter_text.set({ + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + + this._entry.clutter_text.connect('activate', () => { + this._showFolderLabel(); + }); + this._entry.clutter_text.connect('key-focus-out', () => { + const hasKeyFocus = this._entry.clutter_text.has_key_focus(); + if (!hasKeyFocus && this._editButton.checked) + this._showFolderLabel(); + }); + + stack.add_child(this._entry); + + // Edit button + this._editButton = new St.Button({ + style_class: 'icon-button', + button_mask: St.ButtonMask.ONE, + toggle_mode: true, + reactive: true, + can_focus: true, + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.CENTER, + icon_name: 'document-edit-symbolic', + }); + + this._editButton.connect('notify::checked', () => { + if (this._editButton.checked) + this._showFolderEntry(); + else + this._showFolderLabel(); + }); + + this._entryBox.add_child(this._editButton); + + ghostButton.add_constraint(new Clutter.BindConstraint({ + source: this._editButton, + coordinate: Clutter.BindCoordinate.SIZE, + })); + + this._subMenuPopup.connect('open-state-changed', (menu, isOpen) => { + if (!isOpen) + this._showFolderLabel(); + }); + } + + _switchActor(from, to) { + to.reactive = true; + to.ease({ + opacity: 255, + duration: 300, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + from.ease({ + opacity: 0, + duration: 300, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + from.reactive = false; + }, + }); + } + + _showFolderLabel() { + if (this._editButton.checked) + this._editButton.checked = false; + + this._maybeUpdateFolderName(); + this._switchActor(this._entry, this._folderNameLabel); + } + + _showFolderEntry() { + this._switchActor(this._folderNameLabel, this._entry); + + this._entry.clutter_text.set_selection(0, -1); + this._entry.clutter_text.grab_key_focus(); + } + + _maybeUpdateFolderName() { + const folderName = this._name; + const newFolderName = this._entry.text.trim(); + + if (newFolderName.length === 0 || newFolderName === folderName) + return; + + const pinnedAppsList = this._settings.get_value('pinned-apps').deepUnpack(); + const parent = this.get_parent(); + const index = parent.getItemPosition(this); + pinnedAppsList[index].name = newFolderName; + this._settings.set_value('pinned-apps', new GLib.Variant('aa{ss}', pinnedAppsList)); + } + + _loadPinnedApps() { + if (this._menuButton.tooltipShowingID) { + GLib.source_remove(this._menuButton.tooltipShowingID); + this._menuButton.tooltipShowingID = null; + this._menuButton.tooltipShowing = false; + } + if (this.tooltip) { + this.tooltip.hide(); + this._menuButton.tooltipShowing = false; + } + for (let i = this.appList.length - 1; i >= 0; --i) { + const item = this.appList[i]; + item.disconnectObject(this); + item.destroy(); + this.appList[i] = null; + } + + this.appList = null; + this.appList = []; + + // No apps in folder, clear folder data and remove from pinned apps + if (this._folderAppList.length === 0) { + if (this._subMenuPopup.isOpen) + this._subMenuPopup.toggle(); + const keys = this.folderSettings.settings_schema.list_keys(); + for (const key of keys) + this.folderSettings.reset(key); + + const mainPinnedAppsList = this._settings.get_value('pinned-apps').deepUnpack(); + for (let i = 0; i < mainPinnedAppsList.length; i++) { + if (mainPinnedAppsList[i].id === this._command) { + mainPinnedAppsList.splice(i, 1); + this._settings.set_value('pinned-apps', new GLib.Variant('aa{ss}', mainPinnedAppsList)); + break; + } + } + return; + } + for (let i = 0; i < this._folderAppList.length; i++) { + const pinnedApp = this._folderAppList[i]; + const pinnedAppsMenuItem = new PinnedAppsMenuItem(this._menuLayout, pinnedApp, + Constants.DisplayType.GRID, this.isContainedInCategory); + + if (!pinnedAppsMenuItem.shouldShow) { + pinnedAppsMenuItem.destroy(); + continue; + } + + pinnedAppsMenuItem.folderSettings = this.folderSettings; + pinnedAppsMenuItem.disableAcceptDrop = true; + pinnedAppsMenuItem.folderId = this._command; + + pinnedAppsMenuItem.connectObject('pinned-apps-changed', (_self, pinnedAppsList) => { + const array = []; + for (let j = 0; j < pinnedAppsList.length; j++) + array.push(pinnedAppsList[j].pinnedAppData); + + this.folderSettings.set_value('pinned-apps', new GLib.Variant('aa{ss}', array)); + }, this); + this.appList.push(pinnedAppsMenuItem); + } + this.populateMenu(); + } + + createIcon() { + let iconSize; + if (this._displayType === Constants.DisplayType.GRID) { + this._iconBin.x_align = Clutter.ActorAlign.CENTER; + const iconSizeEnum = this._settings.get_enum('menu-item-grid-icon-size'); + const defaultIconSize = this._menuLayout.icon_grid_size; + ({iconSize} = Utils.getGridIconSize(iconSizeEnum, defaultIconSize)); + } else { + const iconSizeEnum = this._settings.get_enum('menu-item-icon-size'); + const defaultIconSize = this.isContainedInCategory ? this._menuLayout.apps_icon_size + : this._menuLayout.pinned_apps_icon_size; + iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize); + } + + if (!this.appList.length) { + const icon = new St.Icon({ + style_class: 'popup-menu-icon', + icon_size: iconSize, + icon_name: 'folder-directory-symbolic', + }); + return icon; + } + + const layout = new Clutter.GridLayout({ + row_homogeneous: true, + column_homogeneous: true, + }); + const iconWidget = new St.Widget({ + layout_manager: layout, + style: `width: ${iconSize}px; height: ${iconSize}px;`, + }); + + const subSize = Math.floor(.4 * iconSize); + + const numItems = this.appList.length; + const rtl = iconWidget.get_text_direction() === Clutter.TextDirection.RTL; + for (let i = 0; i < 4; i++) { + const style = `width: ${subSize}px; height: ${subSize}px;`; + const bin = new St.Bin({style}); + if (i < numItems) { + const icon = this.appList[i].createIcon(); + icon.icon_size = subSize; + bin.child = icon; + } + + layout.attach(bin, rtl ? (i + 1) % 2 : i % 2, Math.floor(i / 2), 1, 1); + } + + return iconWidget; + } + + popupContextMenu() { + if (this.contextMenu === undefined) { + this.contextMenu = new AppContextMenu(this, this._menuLayout); + if (this._displayType === Constants.DisplayType.GRID) + this.contextMenu.centerBoxPointerPosition(); + this.contextMenu.addUnpinItem(this._command, this.folderSettings); + } + if (this.tooltip !== undefined) + this.tooltip.hide(); + this.contextMenu.open(BoxPointer.PopupAnimation.FULL); + } + + getDragActor() { + return this.createIcon(); + } + + _onDragEnd() { + super._onDragEnd(); + + const parent = this.get_parent(); + if (!parent) + return; + + const layoutManager = parent.layout_manager; + const pinnedAppsArray = layoutManager.getChildren(); + this.emit('pinned-apps-changed', pinnedAppsArray); + } + + acceptDrop(source, actor, x, y) { + const acceptDrop = super.acceptDrop(source, actor, x, y); + if (!acceptDrop) + return false; + + const sourceData = source.pinnedAppData; + + source.cancelActions(); + + const parent = this.get_parent(); + const layoutManager = parent.layout_manager; + const pinnedAppsList = this._settings.get_value('pinned-apps').deepUnpack(); + + const index = layoutManager.getItemPosition(source); + pinnedAppsList.splice(index, 1); + this._settings.set_value('pinned-apps', new GLib.Variant('aa{ss}', pinnedAppsList)); + + // add to folder pinned app list + const folderPinnedAppsList = this.folderSettings.get_value('pinned-apps').deepUnpack(); + folderPinnedAppsList.push(sourceData); + return this.folderSettings.set_value('pinned-apps', new GLib.Variant('aa{ss}', folderPinnedAppsList)); + } + + _canAccept(source) { + const canAccept = super._canAccept(source); + + for (let i = 0; i < this._folderAppList.length; i++) { + if (this._folderAppList[i].id === source.pinnedAppData.id) + return false; + } + + return canAccept; + } + + _showFolderPreview() { + } + + _hideFolderPreview() { + } + + populateMenu() { + this._subMenuPopup.populateMenu(this.appList); + } + + activate(event) { + this._subMenuPopup.toggle(); + super.activate(event); + } + + _onDestroy() { + super._onDestroy(); + } +} + +export class PinnedAppsMenuItem extends DraggableMenuItem { + static [GObject.signals] = {'pinned-apps-changed': {param_types: [GObject.TYPE_JSOBJECT]}}; + + static { + GObject.registerClass(this); + } + + constructor(menuLayout, pinnedAppData, displayType, isContainedInCategory) { + super(menuLayout, displayType); + this._menuButton = menuLayout.menuButton; + this._displayType = displayType; + this.isContainedInCategory = isContainedInCategory; + + this.hasContextMenu = true; + + this.updateData(pinnedAppData); + + const showExtraDetails = this._settings.get_boolean('apps-show-extra-details'); + if (this._displayType === Constants.DisplayType.LIST && showExtraDetails && + this._app && this._app.get_description()) { + const labelBox = new St.BoxLayout({vertical: true}); + const descriptionLabel = new St.Label({ + text: this._app.get_description(), + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + style: 'font-weight: lighter;', + }); + labelBox.add_child(this.label); + labelBox.add_child(descriptionLabel); + this.add_child(labelBox); + } else { + this.add_child(this.label); + } + this.setShouldShow(); + } + + updateData(pinnedAppData) { + this.pinnedAppData = pinnedAppData; + this._name = pinnedAppData.name ?? ''; + this._icon = pinnedAppData.icon ?? ''; + this._command = pinnedAppData.id ?? ''; + this._iconString = this._icon; + + this._app = this._menuLayout.appSys.lookup_app(this._command); + + // Allows dragging the pinned app into the overview workspace thumbnail. + this.app = this._app; + + if (this._iconString === Constants.ShortcutCommands.ARCMENU_ICON || this._iconString === `${this._extension.path}/icons/arcmenu-logo-symbolic.svg`) + this._iconString = `${this._extension.path}/${Constants.ArcMenuLogoSymbolic}`; + + if (this._app && this._iconString === '') { + const appIcon = this._app.create_icon_texture(Constants.MEDIUM_ICON_SIZE); + if (appIcon instanceof St.Icon) { + this._iconString = appIcon.gicon ? appIcon.gicon.to_string() : appIcon.fallback_icon_name; + if (!this._iconString) + this._iconString = ''; + } + } + + if (this._app && !this._name) + this._name = this._app.get_name(); + + this.label.text = _(this._name); + this._updateIcon(); + this.setShouldShow(); + } + + createIcon() { + let iconSize; + if (this._displayType === Constants.DisplayType.GRID) { + const iconSizeEnum = this._settings.get_enum('menu-item-grid-icon-size'); + const defaultIconSize = this._menuLayout.icon_grid_size; + ({iconSize} = Utils.getGridIconSize(iconSizeEnum, defaultIconSize)); + } else if (this._displayType === Constants.DisplayType.LIST) { + const iconSizeEnum = this._settings.get_enum('menu-item-icon-size'); + const defaultIconSize = this.isContainedInCategory ? this._menuLayout.apps_icon_size + : this._menuLayout.pinned_apps_icon_size; + iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize); + } + + return new St.Icon({ + gicon: Gio.icon_new_for_string(this._iconString), + icon_size: iconSize, + style_class: this._displayType === Constants.DisplayType.GRID ? '' : 'popup-menu-icon', + }); + } + + popupContextMenu() { + if (this.contextMenu === undefined) { + this.contextMenu = new AppContextMenu(this, this._menuLayout); + if (this._displayType === Constants.DisplayType.GRID) + this.contextMenu.centerBoxPointerPosition(); + if (this._app) + this.contextMenu.setApp(this._app); + else + this.contextMenu.addUnpinItem(this._command); + } + if (this.tooltip !== undefined) + this.tooltip.hide(); + this.contextMenu.open(BoxPointer.PopupAnimation.FULL); + } + + getDragActor() { + const icon = new St.Icon({ + gicon: Gio.icon_new_for_string(this._iconString), + style_class: 'popup-menu-icon', + icon_size: this._iconBin.get_child().icon_size, + }); + return icon; + } + + _onDragEnd() { + super._onDragEnd(); + + const parent = this.get_parent(); + if (!parent) + return; + + const layoutManager = parent.layout_manager; + const pinnedAppsArray = layoutManager.getChildren(); + this.emit('pinned-apps-changed', pinnedAppsArray); + } + + acceptDrop(source, _actor, x, y) { + const acceptDrop = super.acceptDrop(source, _actor, x, y); + if (!acceptDrop) + return false; + + const folderId = GLib.uuid_string_random(); + // Create the new folder + let folderSettings; + try { + folderSettings = this._menuLayout.getSettings(`${this._settings.schema_id}.pinned-apps-folders`, `${this._settings.path}pinned-apps-folders/${folderId}/`); + } catch (e) { + console.log(`Error creating new pinned apps folder: ${e}`); + return false; + } + + const folderPinnedAppsList = [this.pinnedAppData, source.pinnedAppData]; + folderSettings.set_value('pinned-apps', new GLib.Variant('aa{ss}', folderPinnedAppsList)); + + const parent = this.get_parent(); + const layoutManager = parent.layout_manager; + + source.cancelActions(); + + const pinnedAppsList = this._settings.get_value('pinned-apps').deepUnpack(); + + const apps = []; + if (source._app) + apps.push(source._app); + if (this._app) + apps.push(this._app); + + let folderName; + if (apps.length) + folderName = Utils.findBestFolderName(apps); + if (!folderName) + folderName = _('Unnamed Folder'); + + let index = layoutManager.getItemPosition(this); + pinnedAppsList.splice(index, 1, {'id': folderId, 'name': folderName, 'isFolder': 'true'}); + index = layoutManager.getItemPosition(source); + pinnedAppsList.splice(index, 1); + + return this._settings.set_value('pinned-apps', new GLib.Variant('aa{ss}', pinnedAppsList)); + } + + activate(event) { + if (this._app) + this._app.open_new_window(-1); + else if (this._command === Constants.ShortcutCommands.SHOW_APPS) + Main.overview._overview._controls._toggleAppsPage(); + else + Util.spawnCommandLine(this._command); + + this._menuLayout.arcMenu.toggle(); + super.activate(event); + } +} + +export class ApplicationMenuItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, app, displayType, metaInfo, isContainedInCategory) { + super(menuLayout); + this._app = app; + this._displayType = displayType; + this.metaInfo = metaInfo || {}; + this.isContainedInCategory = isContainedInCategory; + + this.searchType = this._menuLayout.search_display_type; + this.hasContextMenu = !!this._app; + this.isSearchResult = !!Object.keys(this.metaInfo).length; + + if (this._app) { + const disableRecentAppsIndicator = this._settings.get_boolean('disable-recently-installed-apps'); + if (!disableRecentAppsIndicator) { + const recentApps = this._settings.get_strv('recently-installed-apps'); + this.isRecentlyInstalled = recentApps.some(appIter => appIter === this._app.get_id()); + } + } + + this._iconBin = new St.Bin({ + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this._iconBin); + + this._updateIcon(); + + this.label = new St.Label({ + text: this._app ? this._app.get_name() : this.metaInfo['name'], + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.CENTER, + }); + this.description = this._app ? this._app.get_description() : this.metaInfo['description']; + + const showSearchDescriptions = this._settings.get_boolean('show-search-result-details') && + this.isSearchResult; + const showAppDescriptions = this._settings.get_boolean('apps-show-extra-details') && + !this.isSearchResult; + const isCalculatorProvider = this.metaInfo['provider-id'] === 'org.gnome.Calculator.desktop'; + + if (this._displayType === Constants.DisplayType.LIST && this.description && + (showSearchDescriptions || showAppDescriptions || isCalculatorProvider)) { + const labelBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + }); + const [descriptionText] = this.description.split('\n'); + this.descriptionLabel = new St.Label({ + text: descriptionText, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + style: 'font-weight: lighter;', + }); + labelBox.add_child(this.label); + labelBox.add_child(this.descriptionLabel); + this.add_child(labelBox); + } else { + this.add_child(this.label); + } + + this.label_actor = this.label; + + if (this.isRecentlyInstalled) { + this._indicator = new St.Label({ + text: _('New'), + style_class: 'arcmenu-text-indicator', + x_expand: true, + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this._indicator); + } + if (this._displayType === Constants.DisplayType.GRID) + Utils.convertToGridLayout(this); + + this.connect('notify::hover', () => this.removeIndicator()); + this.connect('key-focus-in', () => this.removeIndicator()); + } + + set folderPath(value) { + this.hasContextMenu = value; + this._folderPath = value; + } + + get folderPath() { + return this._folderPath; + } + + createIcon() { + let iconSize; + if (this._displayType === Constants.DisplayType.GRID) { + this._iconBin.x_align = Clutter.ActorAlign.CENTER; + + const iconSizeEnum = this._settings.get_enum('menu-item-grid-icon-size'); + const defaultIconSize = this._menuLayout.icon_grid_size; + ({iconSize} = Utils.getGridIconSize(iconSizeEnum, defaultIconSize)); + } else if (this._displayType === Constants.DisplayType.LIST) { + const iconSizeEnum = this._settings.get_enum('menu-item-icon-size'); + const defaultIconSize = this.isContainedInCategory || + this.isSearchResult ? this._menuLayout.apps_icon_size + : this._menuLayout.pinned_apps_icon_size; + iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize); + } + + const icon = this.isSearchResult ? this.metaInfo['createIcon'](iconSize) + : this._app.create_icon_texture(iconSize); + + if (icon) { + icon.style_class = this._displayType === Constants.DisplayType.GRID ? '' : 'popup-menu-icon'; + return icon; + } else { + return false; + } + } + + removeIndicator() { + if (this.isRecentlyInstalled) { + this.isRecentlyInstalled = false; + const recentApps = this._settings.get_strv('recently-installed-apps'); + const index = recentApps.indexOf(this._app.get_id()); + if (index > -1) + recentApps.splice(index, 1); + + this._settings.set_strv('recently-installed-apps', recentApps); + + this._indicator.hide(); + this._menuLayout.setNewAppIndicator(); + } + } + + popupContextMenu() { + this.removeIndicator(); + if (this.tooltip) + this.tooltip.hide(); + + if (!this._app && !this.folderPath) + return; + + if (this.contextMenu === undefined) { + this.contextMenu = new AppContextMenu(this, this._menuLayout); + if (this._app) + this.contextMenu.setApp(this._app); + else if (this.folderPath) + this.contextMenu.setFolderPath(this.folderPath); + if (this._displayType === Constants.DisplayType.GRID) + this.contextMenu.centerBoxPointerPosition(); + } + + this.contextMenu.open(BoxPointer.PopupAnimation.FULL); + } + + activateSearchResult(provider, metaInfo, terms) { + this._menuLayout.arcMenu.toggle(); + if (provider.activateResult) { + provider.activateResult(metaInfo.id, terms); + if (metaInfo.clipboardText) + St.Clipboard.get_default().set_text(St.ClipboardType.CLIPBOARD, metaInfo.clipboardText); + } else if (metaInfo.id.endsWith('.desktop')) { + const app = this._menuLayout.appSys.lookup_app(metaInfo.id); + if (app.can_open_new_window()) + app.open_new_window(-1); + else + app.activate(); + } else { + this._menuLayout.arcMenu.itemActivated(BoxPointer.PopupAnimation.NONE); + const systemActions = SystemActions.getDefault(); + + // SystemActions.activateAction('open-screenshot-ui') waits for + // Main.overview to be hidden before launching ScreenshotUI. + // Avoid that by directly calling Screenshot.showScreenshotUI(). + if (metaInfo.id === 'open-screenshot-ui') { + showScreenshotUI(); + return; + } + + systemActions.activateAction(metaInfo.id); + } + } + + activate(event) { + this.removeIndicator(); + + if (this.isSearchResult) { + this.activateSearchResult(this.provider, this.metaInfo, this.resultsView.terms, event); + } else { + this._app.open_new_window(-1); + this._menuLayout.arcMenu.toggle(); + super.activate(event); + } + } +} + +export class FolderDialog extends PopupMenu.PopupMenu { + constructor(sourceActor, menuLayout) { + const dummyCursor = new St.Widget({width: 0, height: 0, opacity: 0}); + super(dummyCursor, 0.5, St.Side.TOP); + + this.dummyCursor = dummyCursor; + Main.uiGroup.add_child(this.dummyCursor); + + this._sourceActor = sourceActor; + this._menuLayout = menuLayout; + this._menuButton = this._menuLayout.menuButton; + this._arcMenu = this._menuLayout.arcMenu; + + this.actor.add_style_class_name('popup-menu arcmenu-menu'); + this.box.add_style_class_name('arcmenu-folder-dialog'); + this.connect('open-state-changed', this._subMenuOpenStateChanged.bind(this)); + this._menuLayout.subMenuManager.addMenu(this); + Main.uiGroup.add_child(this.actor); + this.actor.hide(); + + this.connectObject('notify::mapped', () => { + if (!this.mapped) + this.close(); + }, this); + + const hasColumnSpacing = this._menuLayout.columnSpacing !== 0; + const hasRowSpacing = this._menuLayout.rowSpacing !== 0; + this._grid = new IconGrid({ + columns: 3, + halign: Clutter.ActorAlign.CENTER, + column_spacing: hasColumnSpacing ? this._menuLayout.columnSpacing : 4, + row_spacing: hasRowSpacing ? this._menuLayout.rowSpacing : 4, + }); + + this._scrollView = this._menuLayout._createScrollBox({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + style_class: this._menuLayout._disableFadeEffect ? '' : 'small-vfade', + }); + this._box = new St.BoxLayout({ + style: 'padding: 0px 18px;', + y_align: Clutter.ActorAlign.START, + }); + this._box.add_child(this._grid); + Utils.addChildToParent(this._scrollView, this._box); + + this.box.add_child(this._scrollView); + this.box.set({ + pivot_point: new Graphene.Point({x: 0.5, y: 0.5}), + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + style: 'border-radius: 20px; padding: 0px;', + }); + + this.connect('destroy', () => this._onDestroy()); + } + + _onDestroy() { + Main.uiGroup.remove_child(this.dummyCursor); + Main.uiGroup.remove_child(this.actor); + this.dummyCursor.destroy(); + } + + _subMenuOpenStateChanged(menu, isOpen) { + const [sourceX, sourceY] = + this._arcMenu.actor.get_transformed_position(); + + const positionX = sourceX + (this._arcMenu.actor.width / 2); + const positionY = sourceY + (this._arcMenu.actor.height / 2) - ((this.actor.height / 2)); + + this.dummyCursor.set_position(Math.round(positionX), Math.round(positionY)); + + this._setDimmed(isOpen); + if (isOpen) { + this.box.set({ + scale_x: .3, + scale_y: .3, + opacity: 0, + }); + this.box.ease({ + scale_x: 1, + scale_y: 1, + opacity: 255, + duration: 150, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + + this._sourceActor.add_style_pseudo_class('active'); + if (this._menuButton.tooltipShowingID) { + GLib.source_remove(this._menuButton.tooltipShowingID); + this._menuButton.tooltipShowingID = null; + this._menuButton.tooltipShowing = false; + } + if (this.tooltip) { + this.tooltip.hide(); + this._menuButton.tooltipShowing = false; + } + } else { + this.box.ease({ + scale_x: .3, + scale_y: .3, + opacity: 0, + duration: 150, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + }); + this._sourceActor.remove_style_pseudo_class('active'); + this._sourceActor.active = false; + this._sourceActor.sync_hover(); + } + } + + _setDimmed(dim) { + const DIM_BRIGHTNESS = -0.4; + const POPUP_ANIMATION_TIME = 400; + + const val = 127 * (1 + (dim ? 1 : 0) * DIM_BRIGHTNESS); + const colorValues = { + red: val, + green: val, + blue: val, + alpha: 255, + }; + const color = Clutter.Color ? new Clutter.Color(colorValues) : new Cogl.Color(colorValues); + + this._arcMenu._boxPointer.ease_property('@effects.dim.brightness', color, { + mode: Clutter.AnimationMode.LINEAR, + duration: POPUP_ANIMATION_TIME, + onStopped: () => (this._arcMenu._dimEffect.enabled = dim), + }); + this._arcMenu._dimEffect.enabled = true; + } + + populateMenu(appList) { + this.appList = appList; + + this._grid.removeAllItems(); + + for (let i = 0; i < appList.length; i++) { + const item = appList[i]; + this._grid.appendItem(item); + } + if (appList.length) + this._setSizeForChildSize(appList[0]); + } + + _setSizeForChildSize(child) { + const childHeight = child.get_height(); + const childWidth = child.get_width(); + const columnSpacing = this._grid.layoutManager.column_spacing; + const rowSpacing = this._grid.layoutManager.row_spacing; + const padding = 36; + + // Calculate a size to accommodate a 3x3 grid + const width = (childWidth * 3) + (columnSpacing * 2) + padding; + const height = (childHeight * 3) + (rowSpacing * 2); + + this._scrollView.style = `width: ${width}px; height: ${height}px; padding-bottom: 18px;`; + } +} + +export class SubCategoryMenuItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, parentDirectory, category, displayType) { + super(menuLayout); + + this._category = category; + this._parentDirectory = parentDirectory; + this._displayType = displayType; + + this.appList = []; + this._name = ''; + + const categoryIconType = this._settings.get_enum('category-icon-type'); + if (categoryIconType === Constants.CategoryIconType.FULL_COLOR) + this.add_style_class_name('regular-icons'); + else + this.add_style_class_name('symbolic-icons'); + + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + + this.label = new St.Label({ + text: this._name, + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this.label); + + if (this._displayType === Constants.DisplayType.GRID) + Utils.convertToGridLayout(this); + + this.label_actor = this.label; + + this.description = parentDirectory.get_name(); + + this._subMenuPopup = new FolderDialog(this, this._menuLayout); + + this._headerLabel = new St.Label({ + style: 'padding-top: 10px; padding-bottom: 10px; text-align: center;', + style_class: 'folder-header', + text: this._name, + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + }); + this._subMenuPopup.box.insert_child_at_index(this._headerLabel, 0); + } + + createIcon() { + let iconSize; + if (this._displayType === Constants.DisplayType.GRID) { + this._iconBin.x_align = Clutter.ActorAlign.CENTER; + + const iconSizeEnum = this._settings.get_enum('menu-item-grid-icon-size'); + const defaultIconSize = this._menuLayout.icon_grid_size; + ({iconSize} = Utils.getGridIconSize(iconSizeEnum, defaultIconSize)); + } else { + const iconSizeEnum = this._settings.get_enum('menu-item-icon-size'); + const defaultIconSize = this._menuLayout.apps_icon_size; + iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize); + } + + const [name, gicon, fallbackIcon] = Utils.getCategoryDetails(this._menuLayout.iconTheme, this._category); + this._name = `${this._parentDirectory.get_name()} - ${name}`; + this.label.text = `${name}`; + this._headerLabel.text = `${this._parentDirectory.get_name()}\n${name}`; + + if (!gicon) { + if (!this.appList.length) { + const icon = new St.Icon({ + style_class: 'popup-menu-icon', + icon_size: iconSize, + icon_name: 'folder-directory-symbolic', + }); + return icon; + } + + const layout = new Clutter.GridLayout({ + row_homogeneous: true, + column_homogeneous: true, + }); + const icon = new St.Widget({ + layout_manager: layout, + style: `width: ${iconSize}px; height: ${iconSize}px;`, + }); + + const subSize = Math.floor(.4 * iconSize); + + const numItems = this.appList.length; + const rtl = icon.get_text_direction() === Clutter.TextDirection.RTL; + for (let i = 0; i < 4; i++) { + const style = `width: ${subSize}px; height: ${subSize}px;`; + const bin = new St.Bin({style}); + if (i < numItems) + bin.child = this.appList[i]._app.create_icon_texture(subSize); + layout.attach(bin, rtl ? (i + 1) % 2 : i % 2, Math.floor(i / 2), 1, 1); + } + + return icon; + } + + const icon = new St.Icon({ + style_class: this._displayType === Constants.DisplayType.GRID ? '' : 'popup-menu-icon', + icon_size: iconSize, + gicon, + fallback_gicon: fallbackIcon, + }); + return icon; + } + + isExtraCategory() { + for (const entry of Constants.Categories) { + if (entry.CATEGORY === this._category) + return true; + } + return false; + } + + setNewAppIndicator() { + + } + + populateMenu() { + this._subMenuPopup.populateMenu(this.appList); + } + + activate(event) { + super.activate(event); + this._subMenuPopup.toggle(); + } +} + +export class CategoryMenuItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, category, displayType) { + super(menuLayout); + this._category = category; + this._displayType = displayType; + + this.appList = []; + this._name = ''; + + const categoryIconType = this._settings.get_enum('category-icon-type'); + if (categoryIconType === Constants.CategoryIconType.FULL_COLOR) + this.add_style_class_name('regular-icons'); + else + this.add_style_class_name('symbolic-icons'); + + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + + this.label = new St.Label({ + text: this._name, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this.label); + + this._updateIcon(); + + this._indicator = new St.Icon({ + icon_name: 'message-indicator-symbolic', + style_class: 'arcmenu-indicator', + icon_size: INDICATOR_ICON_SIZE, + x_expand: true, + y_expand: false, + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.CENTER, + }); + + if (this.isRecentlyInstalled) + this.setNewAppIndicator(true); + + if (this._displayType === Constants.DisplayType.BUTTON) + Utils.convertToButton(this); + + this.label_actor = this.label; + this.connect('motion-event', this._onMotionEvent.bind(this)); + this.connect('enter-event', this._onEnterEvent.bind(this)); + this.connect('leave-event', this._onLeaveEvent.bind(this)); + } + + createIcon() { + let iconSize; + if (this._displayType === Constants.DisplayType.BUTTON) { + const iconSizeEnum = this._settings.get_enum('button-item-icon-size'); + const defaultIconSize = this._menuLayout.buttons_icon_size; + iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize); + this.style = `min-width: ${iconSize}px; min-height: ${iconSize}px;`; + } else { + const iconSizeEnum = this._settings.get_enum('menu-item-category-icon-size'); + const defaultIconSize = this._menuLayout.category_icon_size; + iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize); + + if (iconSize === Constants.ICON_HIDDEN) { + this._iconBin.hide(); + this.style = 'padding-top: 8px; padding-bottom: 8px;'; + } + } + + const [name, gicon, fallbackIcon] = Utils.getCategoryDetails(this._menuLayout.iconTheme, this._category); + this._name = _(name); + this.label.text = _(name); + + const icon = new St.Icon({ + style_class: this._displayType === Constants.DisplayType.BUTTON ? '' : 'popup-menu-icon', + icon_size: iconSize, + gicon, + fallback_gicon: fallbackIcon, + }); + return icon; + } + + isExtraCategory() { + for (const entry of Constants.Categories) { + if (entry.CATEGORY === this._category) + return true; + } + return false; + } + + setNewAppIndicator(shouldShow) { + if (this._displayType === Constants.DisplayType.BUTTON) + return; + + this.isRecentlyInstalled = shouldShow; + if (shouldShow && !this.contains(this._indicator)) + this.add_child(this._indicator); + else if (!shouldShow && this.contains(this._indicator)) + this.remove_child(this._indicator); + } + + displayAppList() { + this._menuLayout.searchEntry?.clearWithoutSearchChangeEvent(); + this._menuLayout.activeCategoryName = this._name; + + switch (this._category) { + case Constants.CategoryType.HOME_SCREEN: + this._menuLayout.activeCategoryName = _('Pinned'); + this._menuLayout.displayPinnedApps(); + break; + case Constants.CategoryType.PINNED_APPS: + this._menuLayout.displayPinnedApps(); + break; + case Constants.CategoryType.RECENT_FILES: + this._menuLayout.displayRecentFiles(); + break; + default: + if (this._category === Constants.CategoryType.FREQUENT_APPS) + this._menuLayout.populateFrequentAppsList(this); + this._menuLayout.displayCategoryAppList(this.appList, this._category); + break; + } + + this._menuLayout.activeCategoryType = this._category; + } + + activate(event) { + super.activate(event); + if (this._menuLayout.supports_category_hover_activation) + this._menuLayout.setActiveCategory(this); + + this.displayAppList(); + } + + _clearLeaveEventTimeout() { + if (this._menuLayout.leaveEventTimeoutId) { + GLib.source_remove(this._menuLayout.leaveEventTimeoutId); + this._menuLayout.leaveEventTimeoutId = null; + } + } + + _shouldActivateOnHover() { + const activateOnHover = this._settings.get_boolean('activate-on-hover'); + const supportsActivateOnHover = this._menuLayout.supports_category_hover_activation; + + return activateOnHover && supportsActivateOnHover; + } + + _onEnterEvent() { + if (!this._shouldActivateOnHover()) + return; + + this._clearLeaveEventTimeout(); + } + + _onLeaveEvent() { + if (!this._shouldActivateOnHover()) + return; + + if (!this._menuLayout.leaveEventTimeoutId) { + this._menuLayout.leaveEventTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => { + this._menuLayout.initialMotionEventItem = null; + + if (this._menuLayout.activeCategoryType === Constants.CategoryType.SEARCH_RESULTS) + this._menuLayout.activeCategoryType = -1; + + this._menuLayout.leaveEventTimeoutId = null; + return GLib.SOURCE_REMOVE; + }); + } + } + + _onMotionEvent(actor, event) { + if (!this._shouldActivateOnHover()) + return; + + if (!this._menuLayout.initialMotionEventItem) + this._menuLayout.initialMotionEventItem = this; + + const inActivationZone = this._inActivationZone(event.get_coords()); + if (inActivationZone) { + this.activate(Clutter.get_current_event()); + this._menuLayout.initialMotionEventItem = this; + } + } + + _inActivationZone([x, y]) { + // no need to activate the category if its already active + if (this._menuLayout.activeCategoryType === this._category) { + this._menuLayout._oldX = x; + this._menuLayout._oldY = y; + return false; + } + + if (!this._menuLayout.initialMotionEventItem) + return false; + + const [posX, posY] = this._menuLayout.initialMotionEventItem.get_transformed_position(); + + // the mouse is on the initialMotionEventItem + const onInitialMotionEventItem = this._menuLayout.initialMotionEventItem === this; + if (onInitialMotionEventItem) { + this._menuLayout._oldX = x; + this._menuLayout._oldY = y; + if (this._menuLayout.activeCategoryType !== Constants.CategoryType.SEARCH_RESULTS) + return true; + } + + const {width} = this._menuLayout.initialMotionEventItem; + const {height} = this._menuLayout.initialMotionEventItem; + + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + const maxX = horizontalFlip ? posX : posX + width; + const maxY = posY + height; + + const distance = Math.abs(maxX - this._menuLayout._oldX); + const point1 = [this._menuLayout._oldX, this._menuLayout._oldY]; + const point2 = [maxX, posY - distance]; + const point3 = [maxX, maxY + distance]; + + const area = Utils.areaOfTriangle(point1, point2, point3); + const a1 = Utils.areaOfTriangle([x, y], point2, point3); + const a2 = Utils.areaOfTriangle(point1, [x, y], point3); + const a3 = Utils.areaOfTriangle(point1, point2, [x, y]); + const outsideTriangle = area !== a1 + a2 + a3; + + return outsideTriangle; + } + + _onDestroy() { + this._clearLeaveEventTimeout(); + super._onDestroy(); + } +} + +// Directory shorctuts. Home, Documents, Downloads, etc +export class PlaceMenuItem extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, info, displayType, isContainedInCategory) { + super(menuLayout); + this._displayType = displayType; + this._info = info; + this.isContainedInCategory = isContainedInCategory; + + this.hasContextMenu = false; + + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + this._updateIcon(); + + this.label = new St.Label({ + text: _(info.name), + x_expand: true, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this.label); + + if (this._displayType === Constants.DisplayType.BUTTON) + Utils.convertToButton(this); + + + if (info.isRemovable()) { + this.hasContextMenu = true; + + this._additionalAction = info.eject.bind(info); + + if (info.canUnmount()) + this._additionalActionName = _('Unmount Drive'); + else + this._additionalActionName = _('Eject Drive'); + } + + if (info.isRemovable()) { + this._ejectIcon = new St.Icon({ + icon_name: 'media-eject-symbolic', + style_class: 'popup-menu-icon', + }); + this._ejectButton = new St.Button({ + child: this._ejectIcon, + style_class: 'button arcmenu-small-button', + }); + this._ejectButton.connect('clicked', info.eject.bind(info)); + this.add_child(this._ejectButton); + } + + this._infoChangedId = this._info.connect('changed', this._propertiesChanged.bind(this), this); + } + + set folderPath(value) { + this.hasContextMenu = value; + this._folderPath = value; + } + + get folderPath() { + return this._folderPath; + } + + forceTitle(title) { + this._foreTitle = true; + if (this.label) + this.label.text = _(title); + } + + setAsRecentFile(recentFile, removeRecentFile) { + const parentPath = recentFile.get_parent()?.get_path(); + this.folderPath = parentPath; + this.description = parentPath; + this.fileUri = recentFile.get_uri(); + + this._additionalAction = () => { + removeRecentFile(); + this.destroy(); + }; + this._additionalActionName = _('Remove from Recent'); + } + + _onDestroy() { + if (this._infoChangedId) { + this._info.disconnect(this._infoChangedId); + this._infoChangedId = null; + } + + if (this._info) + this._info.destroy(); + super._onDestroy(); + } + + popupContextMenu() { + if (this.tooltip) + this.tooltip.hide(); + + if (this.contextMenu === undefined) { + this.contextMenu = new AppContextMenu(this, this._menuLayout); + if (this.folderPath) + this.contextMenu.setFolderPath(this.folderPath); + if (this._additionalAction) + this.contextMenu.addAdditionalAction(_(this._additionalActionName), this._additionalAction); + if (this._displayType === Constants.DisplayType.GRID) + this.contextMenu.centerBoxPointerPosition(); + } + this.contextMenu.toggle(); + } + + createIcon() { + let iconSizeEnum; + if (this.isContainedInCategory) + iconSizeEnum = this._settings.get_enum('menu-item-icon-size'); + else + iconSizeEnum = this._settings.get_enum('quicklinks-item-icon-size'); + + const defaultIconSize = this.isContainedInCategory ? this._menuLayout.apps_icon_size + : this._menuLayout.quicklinks_icon_size; + let iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize); + + if (this._displayType === Constants.DisplayType.BUTTON) { + const defaultButtonIconSize = this._menuLayout.buttons_icon_size; + const IconSizeEnum = this._settings.get_enum('button-item-icon-size'); + iconSize = Utils.getIconSize(IconSizeEnum, defaultButtonIconSize); + this.style = `min-width: ${iconSize}px; min-height: ${iconSize}px;`; + } + + return new St.Icon({ + gicon: this._info.icon, + icon_size: iconSize, + style_class: this._displayType === Constants.DisplayType.BUTTON ? '' : 'popup-menu-icon', + }); + } + + activate(event) { + this._info.launch(event.get_time()); + this._menuLayout.arcMenu.toggle(); + super.activate(event); + } + + _propertiesChanged(info) { + this._info = info; + this._iconBin.set_child(this.createIcon()); + if (this.label && !this._foreTitle) + this.label.text = _(info.name); + } +} + +export class SearchEntry extends St.Entry { + static [GObject.signals] = { + 'search-changed': {param_types: [GObject.TYPE_STRING]}, + 'entry-key-focus-in': { }, + 'entry-key-press': {param_types: [Clutter.Event.$gtype]}, + }; + + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super({ + hint_text: _('Search…'), + track_hover: true, + can_focus: true, + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + name: 'ArcMenuSearchEntry', + style_class: 'arcmenu-search-entry', + }); + + this._settings = menuLayout.settings; + this.searchResults = menuLayout.searchResults; + + this._menuLayout = menuLayout; + this.triggerSearchChangeEvent = true; + this._iconClickedId = 0; + const IconSizeEnum = this._settings.get_enum('misc-item-icon-size'); + const iconSize = Utils.getIconSize(IconSizeEnum, Constants.EXTRA_SMALL_ICON_SIZE); + + this._findIcon = new St.Icon({ + style_class: 'search-entry-icon', + icon_name: 'edit-find-symbolic', + icon_size: iconSize, + }); + + this._clearIcon = new St.Icon({ + style_class: 'search-entry-icon', + icon_name: 'edit-clear-symbolic', + icon_size: iconSize, + }); + + this.set_primary_icon(this._findIcon); + + this._text = this.get_clutter_text(); + this._text.connectObject('text-changed', this._onTextChanged.bind(this), this); + this._text.connectObject('key-press-event', this._onKeyPress.bind(this), this); + this._text.connectObject('key-focus-in', this._onKeyFocusIn.bind(this), this); + this._text.connectObject('key-focus-out', this._onKeyFocusOut.bind(this), this); + this.connect('destroy', this._onDestroy.bind(this)); + } + + getText() { + return this.get_text(); + } + + setText(text) { + this.set_text(text); + } + + clearWithoutSearchChangeEvent() { + this.triggerSearchChangeEvent = false; + this.set_text(''); + this.triggerSearchChangeEvent = true; + } + + hasKeyFocus() { + return this.contains(global.stage.get_key_focus()); + } + + clear() { + this.set_text(''); + } + + isEmpty() { + return this.get_text().length === 0; + } + + _onKeyFocusOut() { + if (!this.isEmpty()) { + this.add_style_pseudo_class('focus'); + return Clutter.EVENT_STOP; + } + return Clutter.EVENT_PROPAGATE; + } + + _onTextChanged() { + if (!this.isEmpty()) { + this.set_secondary_icon(this._clearIcon); + if (this._iconClickedId === 0) { + this._iconClickedId = this.connect('secondary-icon-clicked', + () => this._menuLayout.setDefaultMenuView()); + } + if (!this.hasKeyFocus()) + this.grab_key_focus(); + if (!this.searchResults.getTopResult()?.has_style_pseudo_class('active')) + this.searchResults.getTopResult()?.add_style_pseudo_class('active'); + this.add_style_pseudo_class('focus'); + } else { + if (this._iconClickedId > 0) { + this.disconnect(this._iconClickedId); + this._iconClickedId = 0; + } + if (!this.hasKeyFocus()) + this.remove_style_pseudo_class('focus'); + this.set_secondary_icon(null); + } + + if (this.triggerSearchChangeEvent) + this.emit('search-changed', this.get_text()); + } + + _onKeyPress(actor, event) { + const symbol = event.get_key_symbol(); + const searchResult = this.searchResults.getTopResult(); + + if (!this.isEmpty() && searchResult) { + if (symbol === Clutter.KEY_Return || symbol === Clutter.KEY_KP_Enter) { + searchResult.activate(event); + return Clutter.EVENT_STOP; + } else if (symbol === Clutter.KEY_Menu && searchResult.hasContextMenu) { + searchResult.popupContextMenu(); + return Clutter.EVENT_STOP; + } + } + this.emit('entry-key-press', event); + return Clutter.EVENT_PROPAGATE; + } + + _onKeyFocusIn() { + this.add_style_pseudo_class('focus'); + this.emit('entry-key-focus-in'); + return Clutter.EVENT_PROPAGATE; + } + + _onDestroy() { + if (this._iconClickedId) { + this.disconnect(this._iconClickedId); + this._iconClickedId = null; + } + } +} + +export const WorldClocksWidget = GObject.registerClass( +class ArcMenuWorldClocksWidget extends GWorldClocksWidget { + _init(menuLayout) { + super._init(); + this._menuLayout = menuLayout; + this.connect('destroy', () => this._onDestroy()); + + this._syncID = GObject.signal_handler_find(this._appSystem, {signalId: 'installed-changed'}); + this._clockChangedID = GObject.signal_handler_find(this._settings, {signalId: 'changed'}); + } + + _onDestroy() { + if (this._syncID) { + this._appSystem.disconnect(this._syncID); + this._syncID = null; + } + if (this._clockChangedID) { + this._settings.disconnect(this._clockChangedID); + this._clockChangedID = null; + } + if (this._clocksProxyID) { + this._clocksProxy.disconnect(this._clocksProxyID); + this._clocksProxyID = null; + } + if (this._clockNotifyId) { + this._clock.disconnect(this._clockNotifyId); + this._clockNotifyId = null; + } + if (this._tzNotifyId) { + this._clock.disconnect(this._tzNotifyId); + this._tzNotifyId = null; + } + } + + vfunc_clicked() { + this._menuLayout.arcMenu.toggle(); + if (this._clocksApp) + this._clocksApp.activate(); + } + + _onProxyReady(proxy, error) { + if (error) { + log(`Failed to create GNOME Clocks proxy: ${error}`); + return; + } + + this._clocksProxyID = this._clocksProxy.connect('g-properties-changed', + this._onClocksPropertiesChanged.bind(this)); + this._onClocksPropertiesChanged(); + } +}); + +export const WeatherWidget = GObject.registerClass( +class ArcMenuWeatherWidget extends GWeatherWidget { + _init(menuLayout) { + super._init(); + this._menuLayout = menuLayout; + + this.connect('destroy', () => this._onDestroy()); + } + + _onDestroy() { + this._weatherClient.disconnectAll(); + this._weatherClient = null; + delete this._weatherClient; + } + + vfunc_clicked() { + this._menuLayout.arcMenu.toggle(); + this._weatherClient.activateApp(); + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/arcmenu.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/arcmenu.js new file mode 100644 index 0000000..cbbb285 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/arcmenu.js @@ -0,0 +1,397 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; +import * as PlaceDisplay from '../placeDisplay.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class ArcMenuLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + is_dual_panel: true, + display_type: Constants.DisplayType.LIST, + search_display_type: Constants.DisplayType.LIST, + column_spacing: 0, + row_spacing: 0, + vertical: true, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.connect('button-press-event', (actor, event) => { + if (this.backButton.visible && event.get_button() === 8) + this.backButton.activate(event); + }); + + // mainBox stores left and right box + const mainBox = new St.BoxLayout({ + vertical: false, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + }); + this.add_child(mainBox); + + this.leftBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + vertical: true, + y_align: Clutter.ActorAlign.FILL, + }); + + this.rightBox = new St.BoxLayout({vertical: true}); + + // Applications Box - Contains Favorites, Categories or programs + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.leftBox.add_child(this.applicationsScrollBox); + + this.applicationsBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + + this.navigateBox = new St.BoxLayout({ + vertical: true, + x_expand: false, + y_expand: false, + y_align: Clutter.ActorAlign.END, + }); + this.leftBox.add_child(this.navigateBox); + + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.navigateBox.add_child(separator); + + this.backButton = new MW.BackButton(this); + this.navigateBox.add_child(this.backButton); + + this._viewAllAppsButton = new MW.ViewAllAppsButton(this); + this.navigateBox.add_child(this._viewAllAppsButton); + + const searchbarLocation = this._settings.get_enum('searchbar-default-bottom-location'); + if (searchbarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.add_style_class_name('arcmenu-search-top'); + this.insert_child_at_index(this.searchEntry, 0); + } else if (searchbarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + this.leftBox.add_child(this.searchEntry); + } + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + mainBox.add_child(horizontalFlip ? this.rightBox : this.leftBox); + mainBox.add_child(verticalSeparator); + mainBox.add_child(horizontalFlip ? this.leftBox : this.rightBox); + + const userAvatar = this._settings.get_boolean('disable-user-avatar'); + if (!userAvatar) { + const userMenuItem = new MW.UserMenuItem(this, Constants.DisplayType.LIST); + this.rightBox.add_child(userMenuItem); + const userAvatarSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.SHORT, + Constants.SeparatorAlignment.HORIZONTAL); + this.rightBox.add_child(userAvatarSeparator); + } + + this.shortcutsBox = new St.BoxLayout({vertical: true}); + this.shortcutsScrollBox = this._createScrollBox({ + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this._addChildToParent(this.shortcutsScrollBox, this.shortcutsBox); + this.rightBox.add_child(this.shortcutsScrollBox); + + // Add place shortcuts to menu (Home,Documents,Downloads,Music,Pictures,Videos) + this._displayPlaces(); + + const haveDirectoryShortcuts = this._settings.get_value('directory-shortcuts').deep_unpack().length > 0; + const haveApplicationShortcuts = this._settings.get_value('application-shortcuts').deep_unpack().length > 0; + + // check to see if should draw separator + const needsSeparator = haveDirectoryShortcuts && + (this._settings.get_boolean('show-external-devices') || haveApplicationShortcuts || + this._settings.get_boolean('show-bookmarks')); + if (needsSeparator) { + const shortcutsSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.SHORT, + Constants.SeparatorAlignment.HORIZONTAL); + this.shortcutsBox.add_child(shortcutsSeparator); + } + + // External Devices and Bookmarks Shortcuts + const externalDevicesBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + }); + this.shortcutsBox.add_child(externalDevicesBox); + + this._placesSections = {}; + this.placesManager = new PlaceDisplay.PlacesManager(); + for (let i = 0; i < Constants.SECTIONS.length; i++) { + const id = Constants.SECTIONS[i]; + this._placesSections[id] = new St.BoxLayout({vertical: true}); + this.placesManager.connectObject(`${id}-updated`, () => this._redisplayPlaces(id), this); + + this._createPlaces(id); + externalDevicesBox.add_child(this._placesSections[id]); + } + + const applicationShortcuts = this._settings.get_value('application-shortcuts').deep_unpack(); + for (let i = 0; i < applicationShortcuts.length; i++) { + const shortcutMenuItem = this.createMenuItem(applicationShortcuts[i], Constants.DisplayType.LIST, false); + if (shortcutMenuItem.shouldShow) + this.shortcutsBox.add_child(shortcutMenuItem); + else + shortcutMenuItem.destroy(); + } + + let powerOptionsDisplay; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.MENU) { + powerOptionsDisplay = new MW.LeaveButton(this, true); + } else { + powerOptionsDisplay = new MW.PowerOptionsBox(this); + powerOptionsDisplay.set({ + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + } + + powerOptionsDisplay.set({ + y_expand: true, + y_align: Clutter.ActorAlign.END, + }); + this.rightBox.add_child(powerOptionsDisplay); + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + _createExtraCategoriesLinks() { + this.extraCategoriesLinksBox = new St.BoxLayout({vertical: true}); + this.extraCategoriesLinksBox.visible = false; + + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.extraCategoriesLinksBox.add_child(separator); + + const extraCategoriesLinksLocation = this._settings.get_enum('arcmenu-extra-categories-links-location'); + if (extraCategoriesLinksLocation === Constants.MenuItemLocation.TOP) + this.leftBox.insert_child_below(this.extraCategoriesLinksBox, this.applicationsScrollBox); + else + this.navigateBox.insert_child_above(this.extraCategoriesLinksBox, this.navigateBox.get_child_at_index(0)); + + this.showExtraCategoriesLinksBox = false; + const extraCategories = this._settings.get_value('arcmenu-extra-categories-links').deep_unpack(); + const defaultMenuView = this._settings.get_enum('default-menu-view'); + + // Don't create extra categories quick links if + // the default menu view is the categories list + if (defaultMenuView === Constants.DefaultMenuView.CATEGORIES_LIST) + return; + + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + const extraCategoryItem = this.categoryDirectories.get(categoryEnum); + + // Don't show the extra category if the default menu view is the same category. + if (shouldShow && categoryEnum === Constants.CategoryType.PINNED_APPS && + defaultMenuView === Constants.DefaultMenuView.PINNED_APPS) + continue; + else if (shouldShow && categoryEnum === Constants.CategoryType.FREQUENT_APPS && + defaultMenuView === Constants.DefaultMenuView.FREQUENT_APPS) + continue; + else if (shouldShow && categoryEnum === Constants.CategoryType.ALL_PROGRAMS && + defaultMenuView === Constants.DefaultMenuView.ALL_PROGRAMS) + continue; + else if (!extraCategoryItem || !shouldShow) + continue; + + this.showExtraCategoriesLinksBox = true; + if (extraCategoryItem.get_parent()) + extraCategoryItem.get_parent().remove_child(extraCategoryItem); + + this.extraCategoriesLinksBox.insert_child_below(extraCategoryItem, separator); + } + this.extraCategoriesLinksBox.visible = this.showExtraCategoriesLinksBox; + } + + loadCategories() { + if (this.extraCategoriesLinksBox) + this.extraCategoriesLinksBox.destroy(); + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + const defaultMenuView = this._settings.get_enum('default-menu-view'); + if (defaultMenuView === Constants.DefaultMenuView.PINNED_APPS) + this.hasPinnedApps = true; + + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + + // Don't show the extra category if the default menu view is the same category. + if (shouldShow && categoryEnum === Constants.CategoryType.PINNED_APPS && + defaultMenuView === Constants.DefaultMenuView.PINNED_APPS) + continue; + else if (shouldShow && categoryEnum === Constants.CategoryType.FREQUENT_APPS && + defaultMenuView === Constants.DefaultMenuView.FREQUENT_APPS) + continue; + else if (!shouldShow) + continue; + + const categoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, categoryMenuItem); + } + + super.loadCategories(); + + this._createExtraCategoriesLinks(); + } + + displayPinnedApps() { + const defaultMenuView = this._settings.get_enum('default-menu-view'); + if (defaultMenuView === Constants.DefaultMenuView.PINNED_APPS) { + this._viewAllAppsButton.show(); + this.backButton.hide(); + } else { + this._viewAllAppsButton.hide(); + this.backButton.show(); + } + + this.extraCategoriesLinksBox.visible = this.showExtraCategoriesLinksBox; + + super.displayPinnedApps(); + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + } + + displayAllApps(showBackButton = true) { + super.displayAllApps(); + this._viewAllAppsButton.hide(); + + if (showBackButton) { + this.extraCategoriesLinksBox.visible = false; + this.backButton.show(); + } else { + this.extraCategoriesLinksBox.visible = this.showExtraCategoriesLinksBox; + this.backButton.hide(); + } + } + + displayCategories() { + const defaultMenuView = this._settings.get_enum('default-menu-view'); + if (defaultMenuView === Constants.DefaultMenuView.PINNED_APPS || + defaultMenuView === Constants.DefaultMenuView.FREQUENT_APPS) { + this._viewAllAppsButton.hide(); + this.backButton.show(); + } else { + this._viewAllAppsButton.show(); + this.backButton.hide(); + } + + this.extraCategoriesLinksBox.visible = false; + + super.displayCategories(); + this.activeCategoryType = Constants.CategoryType.CATEGORIES_LIST; + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + + const defaultMenuView = this._settings.get_enum('default-menu-view'); + if (defaultMenuView === Constants.DefaultMenuView.PINNED_APPS) + this.displayPinnedApps(); + else if (defaultMenuView === Constants.DefaultMenuView.CATEGORIES_LIST) + this.displayCategories(); + else if (defaultMenuView === Constants.DefaultMenuView.FREQUENT_APPS) + this.displayFrequentApps(); + else if (defaultMenuView === Constants.DefaultMenuView.ALL_PROGRAMS) + this.displayAllApps(false); + } + + displayCategoryAppList(appList, category) { + this.extraCategoriesLinksBox.visible = false; + this._viewAllAppsButton.hide(); + this.backButton.show(); + super.displayCategoryAppList(appList, category); + } + + displayFrequentApps() { + this._clearActorsFromBox(); + + this.extraCategoriesLinksBox.visible = this.showExtraCategoriesLinksBox; + this._viewAllAppsButton.show(); + this.backButton.hide(); + + const mostUsed = Shell.AppUsage.get_default().get_most_used(); + const appList = []; + for (let i = 0; i < mostUsed.length; i++) { + if (mostUsed[i] && mostUsed[i].get_app_info().should_show()) { + const isContainedInCategory = false; + const item = new MW.ApplicationMenuItem(this, mostUsed[i], Constants.DisplayType.LIST, + null, isContainedInCategory); + appList.push(item); + } + } + let activeMenuItemSet = false; + for (let i = 0; i < appList.length; i++) { + const item = appList[i]; + if (item.get_parent()) + item.get_parent().remove_child(item); + this.applicationsBox.add_child(item); + if (!activeMenuItemSet) { + activeMenuItemSet = true; + this.activeMenuItem = item; + } + } + } + + displayRecentFiles() { + this.extraCategoriesLinksBox.visible = false; + this.backButton.show(); + this._viewAllAppsButton.hide(); + super.displayRecentFiles(); + } + + _clearActorsFromBox(box) { + // keep track of the previous category for the back button. + this.previousCategoryType = this.activeCategoryType; + super._clearActorsFromBox(box); + } + + _onSearchEntryChanged(searchEntry, searchString) { + super._onSearchEntryChanged(searchEntry, searchString); + if (!searchEntry.isEmpty()) { + this.extraCategoriesLinksBox.visible = false; + this.backButton.show(); + this._viewAllAppsButton.hide(); + this.activeCategoryType = Constants.CategoryType.SEARCH_RESULTS; + } + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/az.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/az.js new file mode 100644 index 0000000..f627685 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/az.js @@ -0,0 +1,271 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class AzLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.GRID, + search_results_spacing: 4, + context_menu_location: Constants.ContextMenuLocation.BOTTOM_CENTERED, + column_spacing: 4, + row_spacing: 4, + vertical: true, + default_menu_width: 460, + icon_grid_size: Constants.GridIconSize.LARGE_RECT, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.connect('button-press-event', (actor, event) => { + if (this.backButton.visible && event.get_button() === 8) + this.backButton.activate(event); + }); + + this.searchEntry.style = 'margin: 5px 10px;'; + this.arcMenu.box.style = 'padding: 0px; margin: 0px;'; + + this._mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + this.add_child(this._mainBox); + + this.topBox = new St.BoxLayout({ + x_expand: false, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + vertical: false, + }); + this._mainBox.add_child(this.topBox); + + const navButtonsStyle = 'padding: 0px 10px 10px 15px;'; + this.backButton = this._createNavigationRow(_('All Apps'), Constants.Direction.GO_PREVIOUS, + _('Back'), () => this.setDefaultMenuView()); + this.backButton.set({ + style: navButtonsStyle, + visible: false, + }); + this._mainBox.add_child(this.backButton); + + this.allAppsButton = this._createNavigationRow(_('Pinned'), Constants.Direction.GO_NEXT, + _('All Apps'), () => this.displayAllApps()); + this.allAppsButton.set({ + style: navButtonsStyle, + visible: false, + }); + this._mainBox.add_child(this.allAppsButton); + + this.applicationsBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + style: 'padding-bottom: 10px;', + style_class: 'arcmenu-margin-box', + }); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this._mainBox.add_child(this.applicationsScrollBox); + + this.bottomBox = new St.BoxLayout({ + x_expand: true, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.END, + vertical: false, + }); + this._mainBox.add_child(this.bottomBox); + + this.actionsBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.CENTER, + vertical: false, + }); + this.actionsBox.style = 'margin: 0px 10px; spacing: 10px;'; + + const searchBarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchBarLocation === Constants.SearchbarLocation.TOP) { + this.topBox.add_child(this.searchEntry); + this.bottomBox.add_child(this.actionsBox); + } else { + this.topBox.add_child(this.actionsBox); + this.bottomBox.add_child(this.searchEntry); + } + + this._settings.connectObject('changed::az-layout-extra-shortcuts', () => this._createExtraButtons(), this); + this._createExtraButtons(); + + this.updateStyle(); + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + _createExtraButtons() { + this.actionsBox.destroy_all_children(); + + const userMenuItem = new MW.UserMenuItem(this, Constants.DisplayType.LIST); + this.actionsBox.add_child(userMenuItem); + + const isContainedInCategory = false; + const extraButtons = this._settings.get_value('az-layout-extra-shortcuts').deep_unpack(); + + for (let i = 0; i < extraButtons.length; i++) { + const {id} = extraButtons[i]; + if (id === Constants.ShortcutCommands.SEPARATOR) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.LONG, + Constants.SeparatorAlignment.VERTICAL); + separator.x_expand = false; + this.actionsBox.add_child(separator); + } else { + const button = this.createMenuItem(extraButtons[i], Constants.DisplayType.BUTTON, + isContainedInCategory); + if (button.shouldShow) + this.actionsBox.add_child(button); + else + button.destroy(); + } + } + + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + let leaveButton; + if (powerDisplayStyle === Constants.PowerDisplayStyle.IN_LINE) + leaveButton = new MW.PowerOptionsBox(this); + else + leaveButton = new MW.LeaveButton(this); + + this.actionsBox.add_child(leaveButton); + } + + loadPinnedApps() { + this.display_type = Constants.DisplayType.GRID; + super.loadPinnedApps(); + } + + setDefaultMenuView() { + this.setGridLayout(Constants.DisplayType.GRID, 4); + super.setDefaultMenuView(); + + this.displayPinnedApps(); + } + + displayAllApps() { + this.setGridLayout(Constants.DisplayType.LIST, 4); + super.displayAllApps(); + this.setGridLayout(Constants.DisplayType.GRID, 4, false); + } + + _setBoxStyle(box, additionalStyle) { + const style = `margin: 0px; spacing: 0px; background-color: rgba(10, 10, 15, 0.1); padding: 11px 0px; + border-color:rgba(186, 196,201, 0.2);`; + + if (box === this.topBox) + additionalStyle += 'border-bottom-width: 1px; margin-bottom: 10px;'; + else if (box === this.bottomBox) + additionalStyle += 'border-top-width: 1px;'; + + box.style = style + additionalStyle; + } + + updateStyle() { + const themeNode = this.arcMenu.box.get_theme_node(); + let borderRadius = themeNode.get_length('border-radius'); + const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + borderRadius /= scaleFactor; + + const roundBottomBorder = `border-radius: 0px 0px ${borderRadius}px ${borderRadius}px;`; + const roundTopBorder = `border-radius: ${borderRadius}px ${borderRadius}px 0px 0px;`; + this._setBoxStyle(this.bottomBox, roundBottomBorder); + this._setBoxStyle(this.topBox, roundTopBorder); + this.arcMenu.box.style = 'padding: 0px; margin: 0px;'; + } + + setGridLayout(displayType, spacing, setStyle = true) { + if (setStyle) { + if (displayType === Constants.DisplayType.LIST) + this.applicationsScrollBox.style_class = this._disableFadeEffect ? '' : 'small-vfade'; + else + this.applicationsScrollBox.style_class = this._disableFadeEffect ? '' : 'vfade'; + this.applicationsGrid.halign = displayType === Constants.DisplayType.LIST ? Clutter.ActorAlign.FILL + : Clutter.ActorAlign.CENTER; + } + + this.applicationsGrid.layout_manager.column_spacing = spacing; + this.applicationsGrid.layout_manager.row_spacing = spacing; + this.display_type = displayType; + } + + loadCategories() { + this.display_type = Constants.DisplayType.LIST; + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + this.hasPinnedApps = true; + super.loadCategories(); + } + + displayPinnedApps() { + super.displayPinnedApps(); + this._hideNavigationRow(); + + this.allAppsButton.visible = true; + } + + _displayAppList(apps, category, grid) { + super._displayAppList(apps, category, grid); + + this._hideNavigationRow(); + + if (category === Constants.CategoryType.PINNED_APPS) + this.allAppsButton.visible = true; + else if (category === Constants.CategoryType.ALL_PROGRAMS) + this.backButton.visible = true; + } + + _hideNavigationRow() { + this.allAppsButton.visible = false; + this.backButton.visible = false; + } + + _onSearchEntryChanged(searchEntry, searchString) { + if (!searchEntry.isEmpty()) + this._hideNavigationRow(); + super._onSearchEntryChanged(searchEntry, searchString); + } + + _onDestroy() { + this.arcMenu.box.style = null; + this.backButton.destroy(); + this.allAppsButton.destroy(); + super._onDestroy(); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/baseMenuLayout.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/baseMenuLayout.js new file mode 100644 index 0000000..f8ec0f7 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/baseMenuLayout.js @@ -0,0 +1,1275 @@ +import Clutter from 'gi://Clutter'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GMenu from 'gi://GMenu'; +import GObject from 'gi://GObject'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +import {ArcMenuManager} from '../arcmenuManager.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; +import * as PlaceDisplay from '../placeDisplay.js'; +import {RecentFilesManager} from '../recentFilesManager.js'; +import {SearchResults} from '../search.js'; +import * as Utils from '../utils.js'; + +import {IconGrid} from '../iconGrid.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +const MAX_RECENT_FILES = 25; + +// This class handles the core functionality of all the menu layouts. +// Each menu layout extends this class. +export const BaseMenuLayout = class ArcMenuBaseMenuLayout extends St.BoxLayout { + static [GObject.properties] = { + 'has-search': GObject.ParamSpec.boolean( + 'has-search', 'has-search', 'has-search', + GObject.ParamFlags.READWRITE, true), + 'display-type': GObject.ParamSpec.uint( + 'display-type', 'display-type', 'display-type', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'search-display-type': GObject.ParamSpec.uint( + 'search-display-type', 'search-display-type', 'search-display-type', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'context-menu-location': GObject.ParamSpec.uint( + 'context-menu-location', 'context-menu-location', 'context-menu-location', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'column-spacing': GObject.ParamSpec.uint( + 'column-spacing', 'column-spacing', 'column-spacing', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'row-spacing': GObject.ParamSpec.uint( + 'row-spacing', 'row-spacing', 'row-spacing', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'search-results-spacing': GObject.ParamSpec.uint( + 'search-results-spacing', 'search-results-spacing', + 'search-results-spacing', GObject.ParamFlags.READWRITE, + 0, GLib.MAXINT32, 0), + 'is_dual_panel': GObject.ParamSpec.boolean( + 'is_dual_panel', 'is_dual_panel', 'is_dual_panel', + GObject.ParamFlags.READWRITE, false), + 'supports_category_hover_activation': GObject.ParamSpec.boolean( + 'supports_category_hover_activation', 'supports_category_hover_activation', + 'supports_category_hover_activation', + GObject.ParamFlags.READWRITE, false), + 'is_standalone_runner': GObject.ParamSpec.boolean( + 'is_standalone_runner', 'is_standalone_runner', 'is_standalone_runner', + GObject.ParamFlags.READWRITE, false), + 'menu-width': GObject.ParamSpec.uint( + 'menu-width', 'menu-width', 'menu-width', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'default-menu-width': GObject.ParamSpec.uint( + 'default-menu-width', 'default-menu-width', 'default-menu-width', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'icon-grid-size': GObject.ParamSpec.uint( + 'icon-grid-size', 'icon-grid-size', 'icon-grid-size', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 5), + 'category-icon-size': GObject.ParamSpec.uint( + 'category-icon-size', 'category-icon-size', 'category-icon-size', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'apps-icon-size': GObject.ParamSpec.uint( + 'apps-icon-size', 'apps-icon-size', 'apps-icon-size', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'quicklinks-icon-size': GObject.ParamSpec.uint( + 'quicklinks-icon-size', 'quicklinks-icon-size', 'quicklinks-icon-size', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'buttons-icon-size': GObject.ParamSpec.uint( + 'buttons-icon-size', 'buttons-icon-size', 'buttons-icon-size', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + 'pinned-apps-icon-size': GObject.ParamSpec.uint( + 'pinned-apps-icon-size', 'pinned-apps-icon-size', 'pinned-apps-icon-size', + GObject.ParamFlags.READWRITE, 0, GLib.MAXINT32, 0), + }; + + static { + GObject.registerClass(this); + } + + constructor(menuButton, params) { + super({ + ...params, + reactive: true, + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + }); + this._delegate = this; + + this._menuButton = menuButton; + this._settings = ArcMenuManager.settings; + this._extension = ArcMenuManager.extension; + + this.contextMenuManager = menuButton.contextMenuManager; + this.subMenuManager = menuButton.subMenuManager; + this.arcMenu = menuButton.arcMenu; + + if (this.arcMenu === null) + throw new Error('ArcMenu null'); + + this._focusChild = null; + this.hasPinnedApps = false; + this.activeCategoryType = -1; + this._disableFadeEffect = this._settings.get_boolean('disable-scrollview-fade-effect'); + + this.connect('key-press-event', this._onMainBoxKeyPress.bind(this)); + + this.iconTheme = new St.IconTheme(); + this.appSys = Shell.AppSystem.get_default(); + this._tree = new GMenu.Tree({menu_basename: 'applications.menu'}); + this._reloadApplicationsWorkId = Main.initializeDeferredWork(this, () => this.reloadApplications()); + this._tree.connectObject('changed', () => Main.queueDeferredWork(this._reloadApplicationsWorkId), this); + + AppFavorites.getAppFavorites().connectObject('changed', () => { + if (this.categoryDirectories) { + const categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.FAVORITES); + if (categoryMenuItem) + this._loadGnomeFavorites(categoryMenuItem); + } + }, this); + + if (this.has_search) { + this.searchResults = new SearchResults(this); + this.searchEntry = new MW.SearchEntry(this); + this.searchEntry.connectObject('search-changed', this._onSearchEntryChanged.bind(this), this); + this.searchEntry.connectObject('entry-key-press', this._onSearchEntryKeyPress.bind(this), this); + } + + this.applicationsGrid = new IconGrid({ + halign: this.display_type === Constants.DisplayType.LIST ? Clutter.ActorAlign.FILL + : Clutter.ActorAlign.CENTER, + column_spacing: this.column_spacing, + row_spacing: this.row_spacing, + }); + + this._pinnedAppsGrid = new IconGrid({ + columns: 4, + halign: this.display_type === Constants.DisplayType.LIST ? Clutter.ActorAlign.FILL + : Clutter.ActorAlign.CENTER, + column_spacing: this.column_spacing, + row_spacing: this.row_spacing, + accept_drop: true, + }); + + this.connect('destroy', () => this._onDestroy()); + } + + get settings() { + return this._settings; + } + + get extension() { + return this._extension; + } + + get menuButton() { + return this._menuButton; + } + + setDefaultMenuView() { + if (this.has_search) { + this.searchEntry.clearWithoutSearchChangeEvent(); + this.searchResults.setTerms([]); + } + + this._clearActorsFromBox(); + this.resetScrollBarPosition(); + } + + _addChildToParent(parent, child) { + Utils.addChildToParent(parent, child); + } + + updateWidth(setDefaultMenuView, leftPanelWidthOffset = 0, rightPanelWidthOffset = 0) { + if (this.is_dual_panel) { + const leftPanelWidth = this._settings.get_int('left-panel-width') + leftPanelWidthOffset; + const rightPanelWidth = this._settings.get_int('right-panel-width') + rightPanelWidthOffset; + this.leftBox.style = `width: ${leftPanelWidth}px;`; + this.rightBox.style = `width: ${rightPanelWidth}px;`; + } else { + const widthAdjustment = this._settings.get_int('menu-width-adjustment'); + let menuWidth = this.default_menu_width + widthAdjustment; + // Set a 300px minimum limit for the menu width + menuWidth = Math.max(300, menuWidth); + this.applicationsScrollBox.style = `width: ${menuWidth}px;`; + this.menu_width = menuWidth; + } + + if (setDefaultMenuView) + this.setDefaultMenuView(); + } + + getBestFitColumnsForGrid(iconWidth, grid) { + const padding = 12; + const width = this.menu_width - padding; + const spacing = grid.layout_manager.column_spacing; + const columns = Math.floor(width / (iconWidth + spacing)); + return columns; + } + + getIconWidthFromSetting() { + const gridIconPadding = 10; + const iconSizeEnum = this._settings.get_enum('menu-item-grid-icon-size'); + + const {width, height_, iconSize_} = Utils.getGridIconSize(iconSizeEnum, this.icon_grid_size); + return width + gridIconPadding; + } + + _setGridColumns(grid) { + let columns = 1; + if (grid.layout_manager.forceColumns) { + columns = grid.layout_manager.forceColumns; + } else if (this.display_type === Constants.DisplayType.GRID) { + const iconWidth = this.getIconWidthFromSetting(); + columns = this.getBestFitColumnsForGrid(iconWidth, grid); + } + grid.setColumns(columns); + } + + resetScrollBarPosition() { + const scrollViews = [this.applicationsScrollBox, this.categoriesScrollBox, this.shortcutsScrollBox, this.actionsScrollBox]; + + scrollViews.forEach(scrollView => { + if (scrollView) + Utils.getScrollViewAdjustments(scrollView).vadjustment.set_value(0); + }); + } + + _disconnectReloadApps() { + if (this._reloadAppsOnMenuClosedID) { + this.arcMenu.disconnect(this._reloadAppsOnMenuClosedID); + this._reloadAppsOnMenuClosedID = null; + } + } + + reloadApplications(forceReload = false) { + // Only reload applications if the menu is closed. + if (!forceReload && this.arcMenu.isOpen) { + this.reloadQueued = true; + if (!this._reloadAppsOnMenuClosedID) { + this._reloadAppsOnMenuClosedID = this.arcMenu.connect('menu-closed', () => { + this.reloadApplications(); + this.reloadQueued = false; + this._disconnectReloadApps(); + }); + } + return; + } + + this.searchResults?.setTerms([]); + + if (this.applicationsMap) { + this.applicationsMap.forEach((value, _key, _map) => { + value.destroy(); + }); + this.applicationsMap = null; + } + + if (this.categoryDirectories) { + this.categoryDirectories.forEach((value, _key, _map) => { + value.destroy(); + }); + this.categoryDirectories = null; + } + this.activeCategoryItem = null; + this.activeMenuItem = null; + + this.loadCategories(); + + if (this._createExtraButtons) + this._createExtraButtons(); + if (this._createExtraShortcuts) + this._createExtraShortcuts(); + + this.loadPinnedApps(); + + this.setDefaultMenuView(); + } + + loadCategories(displayType = Constants.DisplayType.LIST) { + this.applicationsMap = new Map(); + this._tree.load_sync(); + const root = this._tree.get_root_directory(); + const iter = root.iter(); + let nextType; + while ((nextType = iter.next()) !== GMenu.TreeItemType.INVALID) { + if (nextType !== GMenu.TreeItemType.DIRECTORY) + continue; + + const dir = iter.get_directory(); + if (dir.get_is_nodisplay()) + continue; + + const categoryId = dir.get_menu_id(); + const categoryMenuItem = new MW.CategoryMenuItem(this, dir, displayType); + this.categoryDirectories.set(categoryId, categoryMenuItem); + this._loadCategory(categoryMenuItem, dir); + } + + let categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.ALL_PROGRAMS); + if (categoryMenuItem) { + const appList = []; + this.applicationsMap.forEach((value, key, _map) => { + appList.push(key); + // Show Recently Installed Indicator on All Programs category + if (value.isRecentlyInstalled && !categoryMenuItem.isRecentlyInstalled) + categoryMenuItem.setNewAppIndicator(true); + }); + appList.sort((a, b) => { + return a.get_name().toLowerCase() > b.get_name().toLowerCase(); + }); + categoryMenuItem.appList = appList; + } + + categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.FAVORITES); + if (categoryMenuItem) + this._loadGnomeFavorites(categoryMenuItem); + + categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.FREQUENT_APPS); + if (categoryMenuItem) { + const mostUsed = Shell.AppUsage.get_default().get_most_used(); + for (let i = 0; i < mostUsed.length; i++) { + if (mostUsed[i] && mostUsed[i].get_app_info().should_show()) + categoryMenuItem.appList.push(mostUsed[i]); + } + } + + categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.PINNED_APPS); + if (categoryMenuItem) { + this.hasPinnedApps = true; + categoryMenuItem.appList = categoryMenuItem.appList.concat(this.pinnedAppsArray); + } + + categoryMenuItem = this.categoryDirectories.get(Constants.CategoryType.RECENT_FILES); + if (categoryMenuItem) + this._loadRecentFiles(categoryMenuItem); + } + + _loadCategory(categoryMenuItem, dir) { + const showNewAppsIndicator = !this._settings.get_boolean('disable-recently-installed-apps'); + const iter = dir.iter(); + let nextType; + while ((nextType = iter.next()) !== GMenu.TreeItemType.INVALID) { + if (nextType === GMenu.TreeItemType.ENTRY) { + const entry = iter.get_entry(); + let id; + try { + id = entry.get_desktop_file_id(); + } catch (e) { + continue; + } + + let app = this.appSys.lookup_app(id); + if (!app) + app = new Shell.App({app_info: entry.get_app_info()}); + + const appInfo = app.get_app_info(); + if (!appInfo.should_show()) + continue; + + let item = this.applicationsMap.get(app); + if (!item) { + const isContainedInCategory = true; + item = new MW.ApplicationMenuItem(this, app, this.display_type, null, isContainedInCategory); + } + + if (categoryMenuItem instanceof MW.SubCategoryMenuItem) { + const subMenuItem = new MW.ApplicationMenuItem(this, app, Constants.DisplayType.GRID, null, true); + categoryMenuItem.appList.push(subMenuItem); + } else { + categoryMenuItem.appList.push(app); + this.applicationsMap.set(app, item); + } + + if (showNewAppsIndicator && item.isRecentlyInstalled) + categoryMenuItem.setNewAppIndicator(true); + } else if (nextType === GMenu.TreeItemType.DIRECTORY) { + const subdir = iter.get_directory(); + if (subdir.get_is_nodisplay()) + continue; + + const showSubMenus = this._settings.get_boolean('show-category-sub-menus'); + if (showSubMenus) { + // Only go one layer deep for sub menus + if (categoryMenuItem instanceof MW.SubCategoryMenuItem) { + this._loadCategory(categoryMenuItem, subdir); + } else { + const subCategoryMenuItem = new MW.SubCategoryMenuItem(this, dir, subdir, this.display_type); + categoryMenuItem.appList.push(subdir); + this.applicationsMap.set(subdir, subCategoryMenuItem); + + this._loadCategory(subCategoryMenuItem, subdir); + subCategoryMenuItem._updateIcon(); + } + } else { + this._loadCategory(categoryMenuItem, subdir); + } + } + } + if (categoryMenuItem instanceof MW.SubCategoryMenuItem) + categoryMenuItem.populateMenu(); + } + + setNewAppIndicator() { + const disabled = this._settings.get_boolean('disable-recently-installed-apps'); + if (!disabled && this.categoryDirectories) { + for (const categoryMenuItem of this.categoryDirectories.values()) { + categoryMenuItem.setNewAppIndicator(false); + for (let i = 0; i < categoryMenuItem.appList.length; i++) { + const item = this.applicationsMap.get(categoryMenuItem.appList[i]); + if (!item) + continue; + if (item.isRecentlyInstalled) { + categoryMenuItem.setNewAppIndicator(true); + break; + } + } + } + } + } + + displayCategories(categoriesBox) { + if (!categoriesBox) + categoriesBox = this.applicationsBox; + + this._clearActorsFromBox(categoriesBox); + + this._futureActiveItem = false; + let hasExtraCategory = false; + let separatorAdded = false; + + for (const categoryMenuItem of this.categoryDirectories.values()) { + if (categoryMenuItem.get_parent()) + continue; + + const isExtraCategory = categoryMenuItem.isExtraCategory(); + + if (!hasExtraCategory) { + hasExtraCategory = isExtraCategory; + } else if (!isExtraCategory && !separatorAdded) { + categoriesBox.add_child(new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL)); + separatorAdded = true; + } + + categoriesBox.add_child(categoryMenuItem); + if (!this._futureActiveItem) + this._futureActiveItem = categoryMenuItem; + } + + this.activeMenuItem = this._futureActiveItem; + } + + _loadGnomeFavorites(categoryMenuItem) { + const appList = AppFavorites.getAppFavorites().getFavorites(); + + // Show Recently Installed Indicator on GNOME favorites category + for (let i = 0; i < appList.length; i++) { + const item = this.applicationsMap.get(appList[i]); + if (item && item.isRecentlyInstalled && !categoryMenuItem.isRecentlyInstalled) + categoryMenuItem.setNewAppIndicator(true); + } + + categoryMenuItem.appList = appList; + if (this.activeCategoryType === Constants.CategoryType.FAVORITES) + categoryMenuItem.displayAppList(); + } + + _loadRecentFiles() { + if (!this.recentFilesManager) + this.recentFilesManager = new RecentFilesManager(); + } + + displayRecentFiles(box = this.applicationsBox) { + this._clearActorsFromBox(box); + this._futureActiveItem = false; + + const recentFiles = this.recentFilesManager.getRecentFiles(); + + let showMoreItem; + if (recentFiles.length > MAX_RECENT_FILES) { + // Display MAX_RECENT_FILES amount of most recent items. + // Show a 'More Recents Files...' menu item at bottom of list. + recentFiles.splice(MAX_RECENT_FILES); + const isContainedInCategory = true; + const placeInfo = new PlaceDisplay.PlaceInfo('special', Gio.File.new_for_uri('recent:///')); + showMoreItem = new MW.PlaceMenuItem(this, placeInfo, Constants.DisplayType.LIST, isContainedInCategory); + showMoreItem.forceTitle(_('More Recent Files...')); + box.add_child(showMoreItem); + } + + for (const fileUri of recentFiles) { + this.recentFilesManager.queryInfoAsync(fileUri).then(result => { + const {recentFile} = result; + const {error} = result; + + if (error) + return; + + const filePath = recentFile.get_path(); + const name = recentFile.get_basename(); + const mimeType = this.recentFilesManager.getMimeType(fileUri); + const icon = Gio.content_type_get_symbolic_icon(mimeType)?.to_string(); + const isContainedInCategory = true; + + const placeMenuItem = this.createMenuItem({name, icon, 'id': filePath}, + Constants.DisplayType.LIST, isContainedInCategory); + placeMenuItem.setAsRecentFile(recentFile, () => { + try { + this.recentFilesManager.removeItem(placeMenuItem.fileUri); + } catch (err) { + log(err); + } + box.remove_child(placeMenuItem); + box.queue_relayout(); + }); + if (showMoreItem) + box.insert_child_below(placeMenuItem, showMoreItem); + else + box.add_child(placeMenuItem); + + if (!this._futureActiveItem) { + this._futureActiveItem = placeMenuItem; + this.activeMenuItem = this._futureActiveItem; + } + }).catch(error => log(error)); + } + } + + _displayPlaces() { + const directoryShortcuts = this._settings.get_value('directory-shortcuts').deep_unpack(); + for (let i = 0; i < directoryShortcuts.length; i++) { + const directoryData = directoryShortcuts[i]; + const isContainedInCategory = false; + const placeMenuItem = this.createMenuItem(directoryData, Constants.DisplayType.LIST, isContainedInCategory); + if (placeMenuItem) + this.shortcutsBox.add_child(placeMenuItem); + } + } + + createMenuItem(itemData, displayType, isContainedInCategory) { + let {id} = itemData; + const {name, icon} = itemData; + let app; + + // Guard against undefined 'id' in itemData + if (id) + app = this.appSys.lookup_app(id); + + // Ubunutu 22.04 uses old version of GNOME settings + if (id === 'org.gnome.Settings.desktop' && !app) { + id = 'gnome-control-center.desktop'; + app = this.appSys.lookup_app(id); + } + + if (app) + return new MW.ShortcutMenuItem(this, itemData, displayType, isContainedInCategory); + + switch (id) { + case Constants.ShortcutCommands.SEPARATOR: { + const item = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.SHORT, + Constants.SeparatorAlignment.HORIZONTAL); + item.shouldShow = displayType === Constants.DisplayType.LIST; + + return item; + } + case Constants.ShortcutCommands.SPACER: { + const item = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.EMPTY, + Constants.SeparatorAlignment.HORIZONTAL); + item.shouldShow = displayType === Constants.DisplayType.LIST; + + return item; + } + case Constants.ShortcutCommands.SOFTWARE: { + const softwareId = Utils.findSoftwareManager(); + return new MW.ShortcutMenuItem(this, {id: softwareId, name, icon}, + displayType, isContainedInCategory); + } + case Constants.ShortcutCommands.SUSPEND: + case Constants.ShortcutCommands.LOG_OUT: + case Constants.ShortcutCommands.POWER_OFF: + case Constants.ShortcutCommands.LOCK: + case Constants.ShortcutCommands.RESTART: + case Constants.ShortcutCommands.HYBRID_SLEEP: + case Constants.ShortcutCommands.HIBERNATE: + case Constants.ShortcutCommands.SWITCH_USER: { + const item = new MW.ShortcutMenuItem(this, itemData, displayType, isContainedInCategory); + item.powerType = Utils.getPowerTypeFromShortcutCommand(id); + MW.bindPowerItemVisibility(item); + return item; + } + case Constants.ShortcutCommands.ARCMENU_SETTINGS: + case Constants.ShortcutCommands.OVERVIEW: + case Constants.ShortcutCommands.SHOW_APPS: + case Constants.ShortcutCommands.RUN_COMMAND: + return new MW.ShortcutMenuItem(this, itemData, displayType, isContainedInCategory); + default: { + const placeInfo = this._getPlaceInfo(id, name); + if (placeInfo) + return new MW.PlaceMenuItem(this, placeInfo, displayType, isContainedInCategory); + else + return new MW.ShortcutMenuItem(this, itemData, displayType, isContainedInCategory); + } + } + } + + _getPlaceInfo(shortcutCommand, shortcutName) { + let path; + switch (shortcutCommand) { + case Constants.ShortcutCommands.DOCUMENTS: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS); + break; + case Constants.ShortcutCommands.DOWNLOADS: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOWNLOAD); + break; + case Constants.ShortcutCommands.MUSIC: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC); + break; + case Constants.ShortcutCommands.PICTURES: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES); + break; + case Constants.ShortcutCommands.VIDEOS: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_VIDEOS); + break; + case Constants.ShortcutCommands.HOME: + path = GLib.get_home_dir(); + break; + case Constants.ShortcutCommands.NETWORK: + return new PlaceDisplay.PlaceInfo('network', Gio.File.new_for_uri('network:///')); + case Constants.ShortcutCommands.RECENT: + return new PlaceDisplay.PlaceInfo('special', Gio.File.new_for_uri('recent:///')); + case Constants.ShortcutCommands.COMPUTER: + return new PlaceDisplay.RootInfo(); + default: { + if (!shortcutCommand) + return null; + + const file = Gio.File.new_for_path(shortcutCommand); + if (file.query_exists(null)) + return new PlaceDisplay.PlaceInfo('special', file, _(shortcutName)); + else + return null; + } + } + if (!path) + return null; + + return new PlaceDisplay.PlaceInfo('special', Gio.File.new_for_path(path)); + } + + getSettings(schema, path) { + const schemaDir = this.extension.dir.get_child('schemas'); + let schemaSource; + if (schemaDir.query_exists(null)) { + schemaSource = Gio.SettingsSchemaSource.new_from_directory( + schemaDir.get_path(), + Gio.SettingsSchemaSource.get_default(), + false + ); + } else { + schemaSource = Gio.SettingsSchemaSource.get_default(); + } + + const schemaObj = schemaSource.lookup(schema, true); + if (!schemaObj) { + log( + `Schema ${schema} could not be found for extension ${ + this.extension.metadata.uuid}. Please check your installation.` + ); + return null; + } + + const args = {settings_schema: schemaObj}; + if (path) + args.path = path; + + return new Gio.Settings(args); + } + + _createPinnedAppItem(pinnedAppData) { + const categoryMenuItem = this.categoryDirectories + ? this.categoryDirectories.get(Constants.CategoryType.PINNED_APPS) : null; + const isContainedInCategory = !!categoryMenuItem; + + let pinnedAppsMenuItem; + if (pinnedAppData.isFolder) { + const folderSchema = `${this._settings.schema_id}.pinned-apps-folders`; + const folderPath = `${this._settings.path}pinned-apps-folders/${pinnedAppData.id}/`; + try { + const folderSettings = this.getSettings(folderSchema, folderPath); + + const folderAppList = folderSettings.get_value('pinned-apps').deepUnpack(); + pinnedAppsMenuItem = new MW.PinnedAppsFolderMenuItem(this, pinnedAppData, folderSettings, folderAppList, + this.display_type, isContainedInCategory); + } catch (e) { + log(`Error creating new pinned apps folder: ${e}`); + return null; + } + } else { + pinnedAppsMenuItem = new MW.PinnedAppsMenuItem(this, pinnedAppData, + this.display_type, isContainedInCategory); + } + + pinnedAppsMenuItem.connectObject('pinned-apps-changed', (_self, newPinnedAppsList) => { + this.pinnedAppsArray = newPinnedAppsList; + const array = []; + for (let j = 0; j < newPinnedAppsList.length; j++) + array.push(newPinnedAppsList[j].pinnedAppData); + + this._settings.set_value('pinned-apps', new GLib.Variant('aa{ss}', array)); + }, this); + + return pinnedAppsMenuItem; + } + + _loadPinnedApps() { + this.pinnedAppsArray = []; + + const pinnedAppsList = this._settings.get_value('pinned-apps').deepUnpack(); + + pinnedAppsList.forEach(pinnedAppData => { + const id = pinnedAppData.id; + + let item = this.pinnedAppsMap.get(id); + if (item) { + item.updateData(pinnedAppData); + } else { + item = this._createPinnedAppItem(pinnedAppData); + if (item.shouldShow) + this.pinnedAppsMap.set(id, item); + } + if (item.shouldShow && !this.pinnedAppsArray.includes(item)) + this.pinnedAppsArray.push(item); + }); + return this.pinnedAppsArray; + } + + _removePinnedApp(item) { + this.pinnedAppsMap.delete(item.pinnedAppData.id); + this._pinnedAppsGrid.removeItem(item); + } + + loadPinnedApps() { + if (!this.pinnedAppsMap) { + this.pinnedAppsMap = new Map(); + this.pinnedAppsArray = []; + this.activeCategoryType = Constants.CategoryType.PINNED_APPS; + } + + const oldPinnedApps = this.pinnedAppsArray; + const oldPinnedAppIds = this.pinnedAppsArray.map(item => item.pinnedAppData.id); + + const newPinnedApps = this._loadPinnedApps(); + const newPinnedAppIds = newPinnedApps.map(item => item.pinnedAppData.id); + + const addedPinnedApps = newPinnedApps.filter(item => !oldPinnedAppIds.includes(item.pinnedAppData.id)); + const removedPinnedApps = oldPinnedApps.filter(item => !newPinnedAppIds.includes(item.pinnedAppData.id)); + + // Remove old app icons + removedPinnedApps.forEach(item => { + this._removePinnedApp(item); + item.disconnectObject(this); + item.destroy(); + }); + + // Add new app icons, or move existing ones + newPinnedApps.forEach(item => { + const index = this.pinnedAppsArray.indexOf(item); + const position = this._pinnedAppsGrid.getItemPosition(item); + if (addedPinnedApps.includes(item)) + this._pinnedAppsGrid.addItem(item, index); + else if (position !== index) + this._pinnedAppsGrid.moveItem(item, index); + }); + } + + displayPinnedApps() { + this.activeCategoryType = Constants.CategoryType.PINNED_APPS; + this._clearActorsFromBox(this.applicationsBox); + + this.applicationsBox.add_child(this._pinnedAppsGrid); + this._setGridColumns(this._pinnedAppsGrid); + const firstItem = this._pinnedAppsGrid.getItemAt(0); + this.activeMenuItem = firstItem; + } + + _redisplayPlaces(id) { + this._placesSections[id].destroy_all_children(); + this._createPlaces(id); + } + + _createPlaces(id) { + const places = this.placesManager.get(id); + + const applicationShortcuts = this._settings.get_value('application-shortcuts').deep_unpack(); + const haveApplicationShortcuts = applicationShortcuts.length > 0; + const haveNetworkDevices = this.placesManager.get('network').length > 0; + const haveExternalDevices = this.placesManager.get('devices').length > 0; + const haveBookmarks = this.placesManager.get('bookmarks').length > 0; + + if (this._settings.get_boolean('show-bookmarks')) { + if (id === 'bookmarks' && haveBookmarks) { + const needsSeparator = haveApplicationShortcuts; + this._addPlacesToMenu(id, places, needsSeparator); + } + } + if (this._settings.get_boolean('show-external-devices')) { + if (id === 'devices' && haveExternalDevices) { + const needsSeparator = !haveNetworkDevices && (haveBookmarks || haveApplicationShortcuts); + this._addPlacesToMenu(id, places, needsSeparator); + } + if (id === 'network' && haveNetworkDevices) { + const needsSeparator = haveBookmarks || haveApplicationShortcuts; + this._addPlacesToMenu(id, places, needsSeparator); + } + } + } + + _addPlacesToMenu(id, places, needsSeparator) { + for (let i = 0; i < places.length; i++) { + const item = new MW.PlaceMenuItem(this, places[i]); + this._placesSections[id].add_child(item); + } + + if (needsSeparator) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.SHORT, + Constants.SeparatorAlignment.HORIZONTAL); + this._placesSections[id].add_child(separator); + } + } + + setActiveCategory(categoryItem, setActive = true) { + if (this.activeCategoryItem) { + this.activeCategoryItem.isActiveCategory = false; + this.activeCategoryItem.remove_style_pseudo_class('active'); + this.activeCategoryItem = null; + } + + if (!setActive) + return; + + this.activeCategoryItem = categoryItem; + this.activeCategoryItem.isActiveCategory = true; + this.activeCategoryItem.add_style_pseudo_class('active'); + } + + populateFrequentAppsList(categoryMenuItem) { + categoryMenuItem.appList = []; + const mostUsed = Shell.AppUsage.get_default().get_most_used(); + for (let i = 0; i < mostUsed.length; i++) { + if (mostUsed[i] && mostUsed[i].get_app_info().should_show()) + categoryMenuItem.appList.push(mostUsed[i]); + } + } + + _clearActorsFromBox(box) { + this.blockHoverState = true; + this.recentFilesManager?.cancelCurrentQueries(); + if (!box) { + box = this.applicationsBox; + this.activeCategoryType = -1; + } + const parent = box.get_parent(); + if (parent && parent instanceof St.ScrollView) + Utils.getScrollViewAdjustments(parent).vadjustment.set_value(0); + const actors = box.get_children(); + for (let i = 0; i < actors.length; i++) { + const actor = actors[i]; + box.remove_child(actor); + } + } + + displayCategoryAppList(appList, category) { + this._clearActorsFromBox(); + this._displayAppList(appList, category, this.applicationsGrid); + } + + _displayAppList(apps, category, grid) { + this.activeCategoryType = category; + if (grid.removeAllItems) + grid.removeAllItems(); + else + grid.remove_all_children(); + + this._futureActiveItem = false; + let currentCharacter; + const alphabetizeAllPrograms = this._settings.get_boolean('alphabetize-all-programs') && + this.display_type === Constants.DisplayType.LIST; + + this._setGridColumns(grid); + + for (let i = 0; i < apps.length; i++) { + const app = apps[i]; + let item; + + if (category === Constants.CategoryType.PINNED_APPS || category === Constants.CategoryType.HOME_SCREEN) { + item = app; + if (!item.shouldShow) + continue; + } else { + item = this.applicationsMap.get(app); + if (!item) { + item = new MW.ApplicationMenuItem(this, app, this.display_type); + this.applicationsMap.set(app, item); + } + } + + const parent = item.get_parent(); + if (parent) + parent.remove_child(item); + + if (alphabetizeAllPrograms && category === Constants.CategoryType.ALL_PROGRAMS) { + const appNameFirstChar = app.get_name().charAt(0).toLowerCase(); + if (currentCharacter !== appNameFirstChar) { + currentCharacter = appNameFirstChar; + + const label = this._createLabelWithSeparator(currentCharacter.toUpperCase()); + grid.appendItem(label); + } + } + + grid.appendItem(item); + + if (!this._futureActiveItem && grid === this.applicationsGrid) + this._futureActiveItem = item; + } + + if (this.applicationsBox && grid === this.applicationsGrid && !this.applicationsBox.contains(this.applicationsGrid)) + this.applicationsBox.add_child(this.applicationsGrid); + if (this._futureActiveItem) + this.activeMenuItem = this._futureActiveItem; + } + + displayAllApps() { + const appList = []; + this.applicationsMap.forEach((value, key, _map) => { + appList.push(key); + }); + appList.sort((a, b) => { + return a.get_name().toLowerCase() > b.get_name().toLowerCase(); + }); + this._clearActorsFromBox(); + this._displayAppList(appList, Constants.CategoryType.ALL_PROGRAMS, this.applicationsGrid); + } + + get activeMenuItem() { + return this._activeMenuItem; + } + + set activeMenuItem(item) { + // track the active menu item for keyboard navigation + const itemChanged = item !== this._activeMenuItem; + if (itemChanged) + this._activeMenuItem = item; + } + + _onSearchEntryChanged(searchEntry, searchString) { + if (searchEntry.isEmpty()) { + if (this.applicationsBox.contains(this.searchResults)) + this.applicationsBox.remove_child(this.searchResults); + + this.setDefaultMenuView(); + } else { + if (this.activeCategoryItem) + this.setActiveCategory(null, false); + + Utils.getScrollViewAdjustments(this.applicationsScrollBox).vadjustment.set_value(0); + + if (!this.applicationsBox.contains(this.searchResults)) { + this._clearActorsFromBox(); + this.applicationsBox.add_child(this.searchResults); + } + + this.activeCategoryType = Constants.CategoryType.SEARCH_RESULTS; + + searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, ''); + if (searchString === '') { + this.searchResults.setTerms([]); + } else { + // Prevent a mouse hover event from setting a new active menu item, until next mouse move event. + // Used to prevent the top search result from instantly changing + // if users mouse is over a differnt menu item. + this.blockHoverState = true; + + this.searchResults.setTerms(searchString.split(/\s+/)); + } + } + } + + _onSearchEntryKeyPress(searchEntry, event) { + const symbol = event.get_key_symbol(); + switch (symbol) { + case Clutter.KEY_Up: + case Clutter.KEY_Down: + case Clutter.KEY_Left: + case Clutter.KEY_Right: { + let direction; + if (symbol === Clutter.KEY_Down || symbol === Clutter.KEY_Up) + return Clutter.EVENT_PROPAGATE; + if (symbol === Clutter.KEY_Right) + direction = St.DirectionType.RIGHT; + if (symbol === Clutter.KEY_Left) + direction = St.DirectionType.LEFT; + + let cursorPosition = this.searchEntry.clutter_text.get_cursor_position(); + + if (cursorPosition === Constants.CaretPosition.END && symbol === Clutter.KEY_Right) + cursorPosition = Constants.CaretPosition.END; + else if (cursorPosition === Constants.CaretPosition.START && symbol === Clutter.KEY_Left) + cursorPosition = Constants.CaretPosition.START; + else + cursorPosition = Constants.CaretPosition.MIDDLE; + + if (cursorPosition === Constants.CaretPosition.END || cursorPosition === Constants.CaretPosition.START) { + let navigateActor = this.activeMenuItem; + if (this.searchResults.hasActiveResult()) { + navigateActor = this.searchResults.getTopResult(); + if (navigateActor.has_style_pseudo_class('active')) { + navigateActor.grab_key_focus(); + navigateActor.remove_style_pseudo_class('active'); + return this.navigate_focus(navigateActor, direction, false); + } + navigateActor.grab_key_focus(); + return Clutter.EVENT_STOP; + } + if (!navigateActor) + return Clutter.EVENT_PROPAGATE; + return this.navigate_focus(navigateActor, direction, false); + } + return Clutter.EVENT_PROPAGATE; + } + default: + return Clutter.EVENT_PROPAGATE; + } + } + + _onMainBoxKeyPress(actor, event) { + // Prevent a mouse hover event from setting a new active menu item, until next mouse move event. + this.blockHoverState = true; + + const symbol = event.get_key_symbol(); + const unicode = Clutter.keysym_to_unicode(symbol); + + /* + * Pass ctrl key event to searchEntry. + * Useful for paste event (ctrl+v), + * if searchEntry entry doesn't have key focus + */ + if (this.searchEntry && (symbol === Clutter.KEY_Control_L || symbol === Clutter.KEY_Control_R)) { + global.stage.set_key_focus(this.searchEntry.clutter_text); + this.searchEntry.clutter_text.event(event, false); + return Clutter.EVENT_PROPAGATE; + } + + switch (symbol) { + case Clutter.KEY_BackSpace: + if (this.searchEntry && !this.searchEntry.hasKeyFocus() && !this.searchEntry.isEmpty()) { + this.searchEntry.grab_key_focus(); + const newText = this.searchEntry.getText().slice(0, -1); + this.searchEntry.setText(newText); + } + return Clutter.EVENT_PROPAGATE; + case Clutter.KEY_Tab: + case Clutter.KEY_ISO_Left_Tab: + case Clutter.KEY_Up: case Clutter.KEY_KP_Up: + case Clutter.KEY_Down: case Clutter.KEY_KP_Down: + case Clutter.KEY_Left: case Clutter.KEY_KP_Left: + case Clutter.KEY_Right: case Clutter.KEY_KP_Right: { + let direction; + if (symbol === Clutter.KEY_Down || symbol === Clutter.KEY_KP_Down) + direction = St.DirectionType.DOWN; + else if (symbol === Clutter.KEY_Right || symbol === Clutter.KEY_KP_Right) + direction = St.DirectionType.RIGHT; + else if (symbol === Clutter.KEY_Up || symbol === Clutter.KEY_KP_Up) + direction = St.DirectionType.UP; + else if (symbol === Clutter.KEY_Left || symbol === Clutter.KEY_KP_Left) + direction = St.DirectionType.LEFT; + else if (symbol === Clutter.KEY_Tab) + direction = St.DirectionType.TAB_FORWARD; + else if (symbol === Clutter.KEY_ISO_Left_Tab) + direction = St.DirectionType.TAB_BACKWARD; + + if (this.has_search && this.searchEntry.hasKeyFocus() && + this.searchResults.hasActiveResult() && this.searchResults.get_parent()) { + const topSearchResult = this.searchResults.getTopResult(); + if (topSearchResult.has_style_pseudo_class('active')) { + topSearchResult.grab_key_focus(); + topSearchResult.remove_style_pseudo_class('active'); + return actor.navigate_focus(global.stage.key_focus, direction, false); + } + topSearchResult.grab_key_focus(); + return Clutter.EVENT_STOP; + } else if (global.stage.key_focus === this && symbol === Clutter.KEY_Up) { + return actor.navigate_focus(global.stage.key_focus, direction, true); + } else if (global.stage.key_focus === this) { + this.activeMenuItem.grab_key_focus(); + return Clutter.EVENT_STOP; + } + return actor.navigate_focus(global.stage.key_focus, direction, false); + } + case Clutter.KEY_KP_Enter: + case Clutter.KEY_Return: + case Clutter.KEY_Escape: + return Clutter.EVENT_PROPAGATE; + default: + if (unicode !== 0 && this.searchEntry) { + global.stage.set_key_focus(this.searchEntry.clutter_text); + this.searchEntry.clutter_text.event(event, false); + } + } + return Clutter.EVENT_PROPAGATE; + } + + _onDestroy() { + this._disconnectReloadApps(); + + if (this.recentFilesManager) { + this.recentFilesManager.destroy(); + this.recentFilesManager = null; + } + + this._tree.disconnectObject(this); + this._tree = null; + + if (this.applicationsBox) { + if (this.applicationsBox.contains(this.applicationsGrid)) + this.applicationsBox.remove_child(this.applicationsGrid); + } + + if (this.network) { + this.network.destroy(); + this.networkMenuItem.destroy(); + } + + if (this.computer) { + this.computer.destroy(); + this.computerMenuItem.destroy(); + } + + if (this.placesManager) { + for (const id in this._placesSections) { + if (Object.hasOwn(this._placesSections, id)) { + const children = this._placesSections[id].get_children(); + children.forEach(child => child.destroy()); + } + } + this.placesManager.destroy(); + this.placesManager = null; + } + + if (this.searchEntry) + this.searchEntry.destroy(); + + if (this.searchResults) { + this.searchResults.setTerms([]); + this.searchResults.destroy(); + this.searchResults = null; + } + + if (this.pinnedAppsArray) { + for (let i = 0; i < this.pinnedAppsArray.length; i++) + this.pinnedAppsArray[i].destroy(); + + this.pinnedAppsArray = null; + } + + if (this.applicationsMap) { + this.applicationsMap.forEach((value, _key, _map) => { + value.destroy(); + }); + this.applicationsMap = null; + } + + if (this.categoryDirectories) { + this.categoryDirectories.forEach((value, _key, _map) => { + value.destroy(); + }); + this.categoryDirectories = null; + } + } + + _createScrollBox(params) { + const scrollBox = new St.ScrollView({ + ...params, + clip_to_allocation: true, + hscrollbar_policy: St.PolicyType.NEVER, + vscrollbar_policy: St.PolicyType.AUTOMATIC, + overlay_scrollbars: true, + }); + + scrollBox.get_vscroll_bar().z_position = 1; + + const panAction = new Clutter.PanAction({interpolate: true}); + panAction.connect('pan', action => this._onPan(action, scrollBox)); + this.add_action(panAction); + + return scrollBox; + } + + _onPan(action, scrollBox) { + if (this._menuButton.tooltipShowingID) { + GLib.source_remove(this._menuButton.tooltipShowingID); + this._menuButton.tooltipShowingID = null; + } + if (this._menuButton.tooltip.visible) + this._menuButton.tooltip.hide(true); + + const [dist_, dx_, dy] = action.get_motion_delta(0); + const {vadjustment} = Utils.getScrollViewAdjustments(scrollBox); + vadjustment.value -= dy; + return false; + } + + _createLabelWithSeparator(headerLabel) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.HEADER_LABEL, + Constants.SeparatorAlignment.HORIZONTAL, headerLabel); + return separator; + } + + createLabelRow(title) { + const labelRow = new St.BoxLayout({ + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + style: 'padding-top: 9px; padding-bottom: 9px;', + style_class: 'popup-menu-item arcmenu-menu-item', + reactive: true, + track_hover: false, + can_focus: false, + }); + const label = new St.Label({ + text: _(title), + y_align: Clutter.ActorAlign.CENTER, + style: 'font-weight: bold;', + }); + labelRow.add_child(label); + labelRow.label = label; + return labelRow; + } + + _createNavigationRow(labelTitle, buttonDirection, buttonTitle, buttonAction) { + const navButton = this.createLabelRow(labelTitle); + + navButton.style = 'padding: 0px 25px;'; + + let button; + if (buttonDirection === Constants.Direction.GO_NEXT) + button = new MW.GoNextButton(this, buttonTitle, buttonAction); + else if (buttonDirection === Constants.Direction.GO_PREVIOUS) + button = new MW.GoPreviousButton(this, buttonAction); + + navButton.activate = event => button.activate(event); + navButton.add_child(button); + return navButton; + } + + _keyFocusIn(actor) { + if (this._focusChild === actor) + return; + this._focusChild = actor; + Utils.ensureActorVisibleInScrollView(actor); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/brisk.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/brisk.js new file mode 100644 index 0000000..62cafaf --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/brisk.js @@ -0,0 +1,192 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class BriskLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + is_dual_panel: true, + display_type: Constants.DisplayType.LIST, + search_display_type: Constants.DisplayType.LIST, + column_spacing: 0, + row_spacing: 0, + supports_category_hover_activation: true, + vertical: true, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + quicklinks_icon_size: Constants.MEDIUM_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this._mainBox = new St.BoxLayout({ + vertical: false, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + }); + this.add_child(this._mainBox); + + this.rightBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + vertical: true, + }); + + this.applicationsBox = new St.BoxLayout({vertical: true}); + this.applicationsScrollBox = this._createScrollBox({ + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this.rightBox.add_child(this.applicationsScrollBox); + + this.leftBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + this._mainBox.add_child(horizontalFlip ? this.rightBox : this.leftBox); + this._mainBox.add_child(verticalSeparator); + this._mainBox.add_child(horizontalFlip ? this.leftBox : this.rightBox); + + this.categoriesScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: false, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.leftBox.add_child(this.categoriesScrollBox); + + this.categoriesBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.categoriesScrollBox, this.categoriesBox); + + this.actionsBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.END, + }); + this.leftBox.add_child(this.actionsBox); + + let powerOptionsItem; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.MENU) { + powerOptionsItem = new MW.LeaveButton(this, true); + } else { + powerOptionsItem = new MW.PowerOptionsBox(this); + powerOptionsItem.x_align = Clutter.ActorAlign.CENTER; + } + + powerOptionsItem.y_align = Clutter.ActorAlign.END; + this.leftBox.add_child(powerOptionsItem); + + const searchBarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchBarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.add_style_class_name('arcmenu-search-top'); + this.searchEntry.style = 'margin-bottom: 0px;'; + this.insert_child_at_index(this.searchEntry, 0); + + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.insert_child_at_index(separator, 1); + } else if (searchBarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + this.add_child(this.searchEntry); + } + + this._settings.connectObject('changed::brisk-layout-extra-shortcuts', () => this._createExtraShortcuts(), this); + this._createExtraShortcuts(); + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + + this.setDefaultMenuView(); + } + + _createExtraShortcuts() { + this.actionsBox.destroy_all_children(); + const extraShortcuts = this._settings.get_value('brisk-layout-extra-shortcuts').deep_unpack(); + + if (extraShortcuts.length === 0) + return; + + const isContainedInCategory = false; + + for (let i = 0; i < extraShortcuts.length; i++) { + const {id} = extraShortcuts[i]; + if (id === Constants.ShortcutCommands.SEPARATOR) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.actionsBox.add_child(separator); + } else { + const item = this.createMenuItem(extraShortcuts[i], Constants.DisplayType.LIST, isContainedInCategory); + if (item.shouldShow) + this.actionsBox.add_child(item); + else + item.destroy(); + } + } + + let separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.actionsBox.insert_child_at_index(separator, 0); + separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.actionsBox.add_child(separator); + } + + updateWidth(setDefaultMenuView) { + const leftPanelWidthOffset = -70; + const rightPanelWidthOffset = 70; + super.updateWidth(setDefaultMenuView, leftPanelWidthOffset, rightPanelWidthOffset); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.displayCategories(); + + const topCategory = this.categoryDirectories.values().next().value; + topCategory.displayAppList(); + this.setActiveCategory(topCategory); + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (shouldShow) { + const categoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, categoryMenuItem); + } + } + + super.loadCategories(); + } + + displayCategories() { + super.displayCategories(this.categoriesBox); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/budgie.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/budgie.js new file mode 100644 index 0000000..5376ef2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/budgie.js @@ -0,0 +1,158 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class BudgieLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + is_dual_panel: true, + display_type: Constants.DisplayType.LIST, + search_display_type: Constants.DisplayType.LIST, + column_spacing: 0, + row_spacing: 0, + supports_category_hover_activation: true, + vertical: true, + category_icon_size: Constants.ICON_HIDDEN, + apps_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + quicklinks_icon_size: Constants.SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this._mainBox = new St.BoxLayout({ + vertical: false, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + }); + this.add_child(this._mainBox); + + this.rightBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + }); + + this.applicationsBox = new St.BoxLayout({vertical: true}); + this.applicationsScrollBox = this._createScrollBox({ + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.applicationsScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this.rightBox.add_child(this.applicationsScrollBox); + + this.leftBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + }); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + this._mainBox.add_child(horizontalFlip ? this.rightBox : this.leftBox); + this._mainBox.add_child(verticalSeparator); + this._mainBox.add_child(horizontalFlip ? this.leftBox : this.rightBox); + + this.categoriesScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.leftBox.add_child(this.categoriesScrollBox); + + this.categoriesBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.categoriesScrollBox, this.categoriesBox); + + if (this._settings.get_boolean('enable-activities-shortcut')) { + this.activitiesBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.END, + }); + this.activities = new MW.ActivitiesMenuItem(this); + this.activitiesBox.add_child(this.activities); + this.leftBox.add_child(this.activitiesBox); + } + + const searchBarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchBarLocation === Constants.SearchbarLocation.TOP) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MAX, + Constants.SeparatorAlignment.HORIZONTAL); + separator.style += 'margin-bottom: 6px;'; + + this.searchEntry.add_style_class_name('arcmenu-search-top'); + this.searchEntry.style = 'margin-bottom: 0px;'; + + this.insert_child_at_index(this.searchEntry, 0); + this.insert_child_at_index(separator, 1); + } else if (searchBarLocation === Constants.SearchbarLocation.BOTTOM) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MAX, + Constants.SeparatorAlignment.HORIZONTAL); + separator.style += 'margin-top: 6px;'; + + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + this.searchEntry.style = 'margin-top: 0px;'; + + this.add_child(separator); + this.add_child(this.searchEntry); + } + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + updateWidth(setDefaultMenuView) { + const leftPanelWidthOffset = -70; + const rightPanelWidthOffset = 70; + super.updateWidth(setDefaultMenuView, leftPanelWidthOffset, rightPanelWidthOffset); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.displayCategories(); + + const topCategory = this.categoryDirectories.values().next().value; + topCategory.displayAppList(); + this.setActiveCategory(topCategory); + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (shouldShow) { + const categoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, categoryMenuItem); + } + } + + super.loadCategories(); + } + + displayCategories() { + super.displayCategories(this.categoriesBox); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/chromebook.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/chromebook.js new file mode 100644 index 0000000..9111dc1 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/chromebook.js @@ -0,0 +1,71 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class ChromebookLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.GRID, + search_results_spacing: 4, + column_spacing: 10, + row_spacing: 10, + default_menu_width: 415, + icon_grid_size: Constants.GridIconSize.SMALL, + vertical: true, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.applicationsBox = new St.BoxLayout({ + vertical: true, + style: 'padding: 8px 0px;', + }); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + x_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this.add_child(this.applicationsScrollBox); + + const searchBarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchBarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.add_style_class_name('arcmenu-search-top'); + this.insert_child_at_index(this.searchEntry, 0); + } else if (searchBarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + this.add_child(this.searchEntry); + } + + this.updateWidth(); + this.loadCategories(); + this.setDefaultMenuView(); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.displayAllApps(); + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + super.loadCategories(); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/elementary.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/elementary.js new file mode 100644 index 0000000..f9df2c4 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/elementary.js @@ -0,0 +1,71 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class ElementaryLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.GRID, + search_results_spacing: 4, + column_spacing: 15, + row_spacing: 15, + default_menu_width: 750, + icon_grid_size: Constants.GridIconSize.LARGE, + vertical: true, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.EXTRA_LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.applicationsBox = new St.BoxLayout({ + vertical: true, + style: 'padding: 8px 0px;', + }); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + x_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this.add_child(this.applicationsScrollBox); + + const searchBarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchBarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.add_style_class_name('arcmenu-search-top'); + this.insert_child_at_index(this.searchEntry, 0); + } else if (searchBarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + this.add_child(this.searchEntry); + } + + this.updateWidth(); + this.loadCategories(); + this.setDefaultMenuView(); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.displayAllApps(); + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + super.loadCategories(); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/eleven.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/eleven.js new file mode 100644 index 0000000..be2d3c5 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/eleven.js @@ -0,0 +1,331 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import {IconGrid} from '../iconGrid.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class ElevenLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.GRID, + search_results_spacing: 5, + context_menu_location: Constants.ContextMenuLocation.BOTTOM_CENTERED, + column_spacing: 0, + row_spacing: 0, + vertical: true, + default_menu_width: 650, + icon_grid_size: Constants.GridIconSize.MEDIUM_RECT, + category_icon_size: Constants.LARGE_ICON_SIZE, + apps_icon_size: Constants.LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.LARGE_ICON_SIZE, + }); + + this.connect('button-press-event', (actor, event) => { + if (this.backButton.visible && event.get_button() === 8) + this.backButton.activate(event); + }); + + this.searchEntry.style = 'margin: 5px 15px 10px 15px;'; + + this._mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + this.add_child(this._mainBox); + + const navButtonsStyle = 'padding: 0px 20px 10px 25px;'; + this.backButton = this._createNavigationRow(_('All Apps'), Constants.Direction.GO_PREVIOUS, + _('Back'), () => this.setDefaultMenuView()); + this.backButton.set({ + style: navButtonsStyle, + visible: false, + }); + this._mainBox.add_child(this.backButton); + + this.allAppsButton = this._createNavigationRow(_('Pinned'), Constants.Direction.GO_NEXT, + _('All Apps'), () => this.displayAllApps()); + this.allAppsButton.set({ + style: navButtonsStyle, + visible: false, + }); + this._mainBox.add_child(this.allAppsButton); + + this.frequentAppsHeader = this.createLabelRow(_('Frequent')); + + this.frequentAppsHeader.label.y_align = Clutter.ActorAlign.CENTER; + this.frequentAppsHeader.style = 'padding: 10px 20px;'; + + const topBox = new St.BoxLayout({ + x_expand: false, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + vertical: false, + }); + topBox.add_child(this.searchEntry); + this.insert_child_at_index(topBox, 0); + + this.applicationsBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + style: 'padding-bottom: 10px; spacing: 8px;', + style_class: 'arcmenu-margin-box', + }); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this._mainBox.add_child(this.applicationsScrollBox); + + this.actionsContainerBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.END, + vertical: false, + }); + this._mainBox.add_child(this.actionsContainerBox); + + this.actionsBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.CENTER, + vertical: false, + }); + this.actionsBox.style = 'spacing: 10px;'; + this.actionsContainerBox.add_child(this.actionsBox); + + this.shortcutsBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.CENTER, + vertical: true, + style: 'padding: 0px 25px;', + }); + + this.shortcutsGrid = new IconGrid({ + halign: Clutter.ActorAlign.FILL, + column_spacing: 10, + row_spacing: 5, + force_columns: 2, + }); + this.shortcutsBox.add_child(this.shortcutsGrid); + + this.applicationsGrid.layout_manager.set({ + force_columns: 1, + column_spacing: 5, + row_spacing: 5, + }); + this.applicationsGrid.halign = Clutter.ActorAlign.FILL; + + this._settings.connectObject('changed::eleven-layout-extra-shortcuts', () => this._createExtraButtons(), this); + this._settings.connectObject('changed::eleven-disable-frequent-apps', () => this.setDefaultMenuView(), this); + + this._createExtraButtons(); + this.updateStyle(); + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + _createExtraButtons() { + this.actionsBox.destroy_all_children(); + + const userMenuItem = new MW.UserMenuItem(this, Constants.DisplayType.LIST); + this.actionsBox.add_child(userMenuItem); + + const isContainedInCategory = false; + const extraButtons = this._settings.get_value('eleven-layout-extra-shortcuts').deep_unpack(); + + for (let i = 0; i < extraButtons.length; i++) { + const {id} = extraButtons[i]; + if (id === Constants.ShortcutCommands.SEPARATOR) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.LONG, + Constants.SeparatorAlignment.VERTICAL); + separator.x_expand = false; + this.actionsBox.add_child(separator); + } else { + const button = this.createMenuItem(extraButtons[i], Constants.DisplayType.BUTTON, + isContainedInCategory); + if (button.shouldShow) + this.actionsBox.add_child(button); + else + button.destroy(); + } + } + + let leaveButton; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.IN_LINE) + leaveButton = new MW.PowerOptionsBox(this); + else + leaveButton = new MW.LeaveButton(this); + + this.actionsBox.add_child(leaveButton); + } + + loadPinnedApps() { + this.display_type = Constants.DisplayType.GRID; + super.loadPinnedApps(); + } + + loadFrequentApps() { + this.frequentAppsList = []; + + if (this._settings.get_boolean('eleven-disable-frequent-apps')) + return; + + const mostUsed = Shell.AppUsage.get_default().get_most_used(); + + if (mostUsed.length < 1) + return; + + const pinnedApps = this._settings.get_value('pinned-apps').deepUnpack(); + const pinnedAppsIds = pinnedApps.map(item => item.id); + + for (let i = 0; i < mostUsed.length; i++) { + if (!mostUsed[i]) + continue; + + const appInfo = mostUsed[i].get_app_info(); + if (appInfo.should_show() && !pinnedAppsIds.includes(appInfo.get_id())) { + const item = new MW.ApplicationMenuItem(this, mostUsed[i], Constants.DisplayType.LIST); + this.frequentAppsList.push(item); + } + } + + const MaxItems = 8; + if (this.frequentAppsList.length > MaxItems) + this.frequentAppsList.splice(MaxItems); + } + + setDefaultMenuView() { + this.setGridLayout(Constants.DisplayType.GRID, 0); + super.setDefaultMenuView(); + this.displayPinnedApps(); + } + + _clearActorsFromBox(box) { + super._clearActorsFromBox(box); + } + + displayAllApps() { + this.setGridLayout(Constants.DisplayType.LIST, 5); + super.displayAllApps(); + this.setGridLayout(Constants.DisplayType.GRID, 0, false); + } + + updateStyle() { + const themeNode = this.arcMenu.box.get_theme_node(); + let borderRadius = themeNode.get_length('border-radius'); + + const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + borderRadius /= scaleFactor; + + const borderRadiusStyle = `border-radius: 0px 0px ${borderRadius}px ${borderRadius}px;`; + const style = `margin: 0px; spacing: 0px; background-color:rgba(10, 10, 15, 0.1); padding: 12px 25px; + border-color: rgba(186, 196,201, 0.2); border-top-width: 1px;`; + + this.actionsContainerBox.style = style + borderRadiusStyle; + this.arcMenu.box.style = 'padding-bottom: 0px; padding-left: 0px; padding-right: 0px;'; + } + + setGridLayout(displayType, spacing, setStyle = true) { + if (setStyle) { + if (displayType === Constants.DisplayType.LIST) + this.applicationsScrollBox.style_class = this._disableFadeEffect ? '' : 'small-vfade'; + else + this.applicationsScrollBox.style_class = this._disableFadeEffect ? '' : 'vfade'; + this.applicationsGrid.halign = displayType === Constants.DisplayType.LIST ? Clutter.ActorAlign.FILL + : Clutter.ActorAlign.CENTER; + } + + this.applicationsGrid.layout_manager.column_spacing = spacing; + this.applicationsGrid.layout_manager.row_spacing = spacing; + this.display_type = displayType; + } + + loadCategories() { + this.display_type = Constants.DisplayType.LIST; + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + this.hasPinnedApps = true; + super.loadCategories(); + } + + displayPinnedApps() { + this._hideNavigationButtons(); + this.allAppsButton.visible = true; + + this.loadFrequentApps(); + super.displayPinnedApps(); + + if (this.frequentAppsList.length > 0 && !this._settings.get_boolean('eleven-disable-frequent-apps')) { + this._displayAppList(this.frequentAppsList, Constants.CategoryType.HOME_SCREEN, this.shortcutsGrid); + if (!this.applicationsBox.contains(this.shortcutsBox)) + this.applicationsBox.add_child(this.shortcutsBox); + } else if (this.applicationsBox.contains(this.shortcutsBox)) { + this.applicationsBox.remove_child(this.shortcutsBox); + } + } + + _displayAppList(apps, category, grid) { + super._displayAppList(apps, category, grid); + + this._hideNavigationButtons(); + + if (category === Constants.CategoryType.HOME_SCREEN || category === Constants.CategoryType.PINNED_APPS) { + this.allAppsButton.visible = true; + if (!this.applicationsBox.contains(this.frequentAppsHeader)) + this.applicationsBox.insert_child_at_index(this.frequentAppsHeader, 2); + } else if (category === Constants.CategoryType.ALL_PROGRAMS) { + this.backButton.visible = true; + } + } + + _hideNavigationButtons() { + this.allAppsButton.visible = false; + this.backButton.visible = false; + } + + _onSearchEntryChanged(searchEntry, searchString) { + if (!searchEntry.isEmpty()) + this._hideNavigationButtons(); + super._onSearchEntryChanged(searchEntry, searchString); + } + + _onDestroy() { + this.arcMenu.box.style = null; + this.backButton.destroy(); + this.allAppsButton.destroy(); + + super._onDestroy(); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/enterprise.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/enterprise.js new file mode 100644 index 0000000..ee62e6a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/enterprise.js @@ -0,0 +1,200 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class EnterpriseLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + is_dual_panel: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.GRID, + search_results_spacing: 4, + context_menu_location: Constants.ContextMenuLocation.BOTTOM_CENTERED, + supports_category_hover_activation: true, + column_spacing: 4, + row_spacing: 4, + vertical: true, + default_menu_width: 450, + icon_grid_size: Constants.GridIconSize.LARGE_RECT, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.topBox = new St.BoxLayout({ + x_expand: true, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + vertical: false, + style: 'spacing: 6px; margin: 0px 6px; padding: 3px 0px;', + }); + + this.userMenuBox = new St.BoxLayout({ + vertical: false, + x_expand: true, + }); + this.userMenuIcon = new MW.UserMenuIcon(this, 36, true); + this.userMenuIcon.set({ + x_expand: false, + x_align: Clutter.ActorAlign.START, + }); + this.userMenuIcon.label.set({ + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + style: 'font-weight: bold;', + }); + + this.searchEntry.set({ + x_expand: false, + y_align: Clutter.ActorAlign.CENTER, + }); + + this._mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: false, + }); + this.add_child(this._mainBox); + + this.rightBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + this.applicationsBox = new St.BoxLayout({vertical: true}); + this.applicationsScrollBox = this._createScrollBox({ + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this.rightBox.add_child(this.applicationsScrollBox); + + this.leftBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + this._mainBox.add_child(horizontalFlip ? this.rightBox : this.leftBox); + this._mainBox.add_child(verticalSeparator); + this._mainBox.add_child(horizontalFlip ? this.leftBox : this.rightBox); + this.topBox.add_child(horizontalFlip ? this.searchEntry : this.userMenuBox); + this.topBox.add_child(horizontalFlip ? this.userMenuBox : this.searchEntry); + this.userMenuBox.add_child(horizontalFlip ? this.userMenuIcon.label : this.userMenuIcon); + this.userMenuBox.add_child(horizontalFlip ? this.userMenuIcon : this.userMenuIcon.label); + this.userMenuIcon.label.style += horizontalFlip ? 'padding-right: 14px;' : 'padding-left: 14px;'; + this.userMenuBox.x_align = horizontalFlip ? Clutter.ActorAlign.END : Clutter.ActorAlign.START; + + this.categoriesScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + + this.leftBox.add_child(this.categoriesScrollBox); + this.categoriesBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.categoriesScrollBox, this.categoriesBox); + + let powerOptionsDisplay; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.MENU) { + powerOptionsDisplay = new MW.LeaveButton(this, true); + } else { + powerOptionsDisplay = new MW.PowerOptionsBox(this); + powerOptionsDisplay.set({ + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + } + this.leftBox.add_child(new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL)); + this.leftBox.add_child(powerOptionsDisplay); + + const searchbarLocation = this._settings.get_enum('searchbar-default-top-location'); + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + + if (searchbarLocation === Constants.SearchbarLocation.TOP) { + this.insert_child_at_index(separator, 0); + this.insert_child_at_index(this.topBox, 0); + } else if (searchbarLocation === Constants.SearchbarLocation.BOTTOM) { + this.add_child(separator); + this.add_child(this.topBox); + } + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + updateWidth(setDefaultMenuView) { + const leftPanelWidthOffset = 70; + const leftPanelWidth = this._settings.get_int('left-panel-width') - leftPanelWidthOffset; + this.leftBox.style = `width: ${leftPanelWidth}px;`; + + const widthAdjustment = this._settings.get_int('menu-width-adjustment'); + let menuWidth = this.default_menu_width + widthAdjustment; + // Set a 300px minimum limit for the menu width + menuWidth = Math.max(300, menuWidth); + this.applicationsScrollBox.style = `width: ${menuWidth}px;`; + this.menu_width = menuWidth; + + this.searchEntry.style = `width: ${menuWidth - 26}px;`; + + if (setDefaultMenuView) + this.setDefaultMenuView(); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.displayCategories(); + + const topCategory = this.categoryDirectories.values().next().value; + topCategory.displayAppList(); + this.setActiveCategory(topCategory); + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (shouldShow) { + const categoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, categoryMenuItem); + } + } + + super.loadCategories(); + } + + displayCategories() { + super.displayCategories(this.categoriesBox); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/gnomemenu.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/gnomemenu.js new file mode 100644 index 0000000..214be58 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/gnomemenu.js @@ -0,0 +1,132 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class GnomeMenuLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: false, + is_dual_panel: true, + display_type: Constants.DisplayType.LIST, + search_display_type: Constants.DisplayType.LIST, + column_spacing: 0, + row_spacing: 0, + supports_category_hover_activation: true, + vertical: true, + category_icon_size: Constants.ICON_HIDDEN, + apps_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + quicklinks_icon_size: Constants.SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this._mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: false, + }); + this.add_child(this._mainBox); + + this.rightBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + vertical: true, + }); + + this.applicationsBox = new St.BoxLayout({vertical: true}); + this.applicationsScrollBox = this._createScrollBox({ + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this.rightBox.add_child(this.applicationsScrollBox); + + this.leftBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + this._mainBox.add_child(horizontalFlip ? this.rightBox : this.leftBox); + this._mainBox.add_child(verticalSeparator); + this._mainBox.add_child(horizontalFlip ? this.leftBox : this.rightBox); + + this.categoriesScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.leftBox.add_child(this.categoriesScrollBox); + this.categoriesBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.categoriesScrollBox, this.categoriesBox); + + this.activitiesBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.END, + }); + const activities = new MW.ActivitiesMenuItem(this); + this.activitiesBox.add_child(activities); + this.leftBox.add_child(this.activitiesBox); + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + updateWidth(setDefaultMenuView) { + const leftPanelWidthOffset = 0; + const rightPanelWidthOffset = 45; + super.updateWidth(setDefaultMenuView, leftPanelWidthOffset, rightPanelWidthOffset); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.displayCategories(); + + const topCategory = this.categoryDirectories.values().next().value; + topCategory.displayAppList(); + this.setActiveCategory(topCategory); + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (shouldShow) { + const categoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, categoryMenuItem); + } + } + + super.loadCategories(); + } + + displayCategories() { + super.displayCategories(this.categoriesBox); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/insider.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/insider.js new file mode 100644 index 0000000..5317a6c --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/insider.js @@ -0,0 +1,285 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; +import * as Utils from '../utils.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class InsiderLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.GRID, + search_results_spacing: 4, + context_menu_location: Constants.ContextMenuLocation.RIGHT, + column_spacing: 10, + row_spacing: 10, + default_menu_width: 525, + icon_grid_size: Constants.GridIconSize.SMALL, + vertical: false, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.actionsBox = new St.BoxLayout({ + x_expand: false, + y_expand: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + style: 'spacing: 6px;', + }); + this.add_child(this.actionsBox); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + this.add_child(verticalSeparator); + + this._mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + vertical: true, + }); + this.add_child(this._mainBox); + + const userMenuBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.START, + vertical: true, + style: 'padding-top: 9px;', + }); + const userMenuIcon = new MW.UserMenuIcon(this, 75, true); + userMenuIcon.label.set({ + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + style: 'font-size: large;', + }); + userMenuBox.add_child(userMenuIcon); + userMenuBox.add_child(userMenuIcon.label); + this._mainBox.add_child(userMenuBox); + + this.searchEntry.style = 'margin: 10px;'; + this._mainBox.add_child(this.searchEntry); + + this.applicationsBox = new St.BoxLayout({vertical: true}); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: false, + y_expand: false, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this._mainBox.add_child(this.applicationsScrollBox); + + this._pinnedAppsGrid.layout_manager.set({ + column_spacing: 0, + row_spacing: 0, + halign: Clutter.ActorAlign.FILL, + }); + + this._settings.connectObject('changed::insider-layout-extra-shortcuts', () => this._createExtraButtons(), this); + this._createExtraButtons(); + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + + this._createPinnedAppsMenu(); + this.setDefaultMenuView(); + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + } + + _createExtraButtons() { + this.actionsBox.destroy_all_children(); + + this.pinnedAppsButton = new PinnedAppsButton(this); + this.pinnedAppsButton.y_expand = true; + this.pinnedAppsButton.y_align = Clutter.ActorAlign.START; + this.actionsBox.add_child(this.pinnedAppsButton); + + const isContainedInCategory = false; + const extraButtons = this._settings.get_value('insider-layout-extra-shortcuts').deep_unpack(); + + for (let i = 0; i < extraButtons.length; i++) { + const {id} = extraButtons[i]; + if (id === Constants.ShortcutCommands.SEPARATOR) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.LONG, + Constants.SeparatorAlignment.HORIZONTAL); + this.actionsBox.add_child(separator); + } else { + const button = this.createMenuItem(extraButtons[i], Constants.DisplayType.BUTTON, + isContainedInCategory); + if (button.shouldShow) + this.actionsBox.add_child(button); + else + button.destroy(); + } + } + + let leaveButton; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.IN_LINE) + leaveButton = new MW.PowerOptionsBox(this, true); + else + leaveButton = new MW.LeaveButton(this); + + this.actionsBox.add_child(leaveButton); + } + + loadPinnedApps() { + this.display_type = Constants.DisplayType.LIST; + super.loadPinnedApps(); + this.display_type = Constants.DisplayType.GRID; + } + + _createPinnedAppsMenu() { + this.pinnedAppsMenu = new PopupMenu.PopupMenu(Main.layoutManager.dummyCursor, 0, St.Side.TOP); + this.pinnedAppsMenu.actor.add_style_class_name('popup-menu arcmenu-menu'); + + const section = new PopupMenu.PopupMenuSection(); + this.pinnedAppsMenu.addMenuItem(section); + + const pinnedAppsPopupBox = new St.BoxLayout({vertical: true}); + pinnedAppsPopupBox._delegate = pinnedAppsPopupBox; + section.actor.add_child(pinnedAppsPopupBox); + + const headerBox = new St.BoxLayout({ + x_expand: false, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + vertical: true, + }); + pinnedAppsPopupBox.add_child(headerBox); + + this.backButton = new MW.BackButton(this); + this.backButton.connectObject('activate', () => this.togglePinnedAppsMenu(), this); + headerBox.add_child(this.backButton); + + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + headerBox.add_child(separator); + headerBox.add_child(this.createLabelRow(_('Pinned'))); + + this.pinnedAppsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + pinnedAppsPopupBox.add_child(this.pinnedAppsScrollBox); + + this.pinnedAppsBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.pinnedAppsScrollBox, this.pinnedAppsBox); + + this.displayPinnedApps(); + this.subMenuManager.addMenu(this.pinnedAppsMenu); + this.pinnedAppsMenu.actor.hide(); + Main.uiGroup.add_child(this.pinnedAppsMenu.actor); + this.pinnedAppsMenu.connectObject('open-state-changed', (menu, open) => { + if (!open) { + this.pinnedAppsButton.active = false; + this.pinnedAppsButton.sync_hover(); + this.pinnedAppsButton.hovered = this.pinnedAppsButton.hover; + } + }, this); + } + + togglePinnedAppsMenu() { + const {vadjustment} = Utils.getScrollViewAdjustments(this.pinnedAppsScrollBox); + vadjustment.set_value(0); + + const themeNode = this.arcMenu.actor.get_theme_node(); + const rise = themeNode.get_length('-arrow-rise'); + + this.arcMenu.actor.get_allocation_box(); + let [x, y] = this.arcMenu.actor.get_transformed_position(); + + if (this.arcMenu._arrowSide !== St.Side.TOP) + y -= rise; + if (this.arcMenu._arrowSide === St.Side.LEFT) + x += rise; + + Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0); + + const height = this._settings.get_int('menu-height'); + this.pinnedAppsMenu.box.style = `height: ${height}px;`; + + this.pinnedAppsMenu.toggle(); + if (this.pinnedAppsMenu.isOpen) { + this.activeMenuItem = this.backButton; + this.backButton.grab_key_focus(); + } + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.displayAllApps(); + this.activeMenuItem = this.applicationsGrid.getItemAt(0); + + if (!this.applicationsBox.contains(this.applicationsGrid)) + this.applicationsBox.add_child(this.applicationsGrid); + + const {vadjustment} = Utils.getScrollViewAdjustments(this.pinnedAppsScrollBox); + vadjustment.set_value(0); + + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + this.hasPinnedApps = true; + super.loadCategories(); + } + + _clearActorsFromBox(box) { + super._clearActorsFromBox(box); + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + } + + displayPinnedApps() { + this._clearActorsFromBox(this.pinnedAppsBox); + this.activeCategoryType = Constants.CategoryType.PINNED_APPS; + + if (!this.pinnedAppsBox.contains(this._pinnedAppsGrid)) + this.pinnedAppsBox.add_child(this._pinnedAppsGrid); + + this._pinnedAppsGrid.setColumns(1); + } +}; + +class PinnedAppsButton extends MW.ArcMenuButtonItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super(menuLayout, _('Pinned Apps'), 'open-menu-symbolic'); + this.toggleMenuOnClick = false; + } + + activate(event) { + super.activate(event); + this._menuLayout.togglePinnedAppsMenu(); + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/layoutHandler.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/layoutHandler.js new file mode 100644 index 0000000..50cee30 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/layoutHandler.js @@ -0,0 +1,47 @@ +import {MenuLayout} from '../constants.js'; + +/** + * + * @param {PanelMenu.Button} menuButton + * @param {MenuLayout} layoutEnum + * @param {boolean} isStandaloneRunner + */ +export async function createMenuLayout(menuButton, layoutEnum, isStandaloneRunner) { + if (layoutEnum === MenuLayout.GNOME_OVERVIEW) + return null; + + // Map each layout to its corresponding file path + const layoutMap = { + [MenuLayout.ARCMENU]: './arcmenu.js', + [MenuLayout.AZ]: './az.js', + [MenuLayout.BRISK]: './brisk.js', + [MenuLayout.BUDGIE]: './budgie.js', + [MenuLayout.CHROMEBOOK]: './chromebook.js', + [MenuLayout.ELEMENTARY]: './elementary.js', + [MenuLayout.ELEVEN]: './eleven.js', + [MenuLayout.ENTERPRISE]: './enterprise.js', + [MenuLayout.GNOME_MENU]: './gnomemenu.js', + [MenuLayout.INSIDER]: './insider.js', + [MenuLayout.MINT]: './mint.js', + [MenuLayout.PLASMA]: './plasma.js', + [MenuLayout.POP]: './pop.js', + [MenuLayout.RAVEN]: './raven.js', + [MenuLayout.REDMOND]: './redmond.js', + [MenuLayout.RUNNER]: './runner.js', + [MenuLayout.SLEEK]: './sleek.js', + [MenuLayout.TOGNEE]: './tognee.js', + [MenuLayout.UNITY]: './unity.js', + [MenuLayout.WHISKER]: './whisker.js', + [MenuLayout.WINDOWS]: './windows.js', + }; + + const modulePath = layoutMap[layoutEnum] || './arcmenu.js'; // Default to ArcMenu if layout isn't found + + // Dynamically import the required layout + try { + const {Layout} = await import(modulePath); + return new Layout(menuButton, isStandaloneRunner); + } catch (e) { + return null; + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/mint.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/mint.js new file mode 100644 index 0000000..3ee48f3 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/mint.js @@ -0,0 +1,195 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class MintLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + is_dual_panel: true, + display_type: Constants.DisplayType.LIST, + search_display_type: Constants.DisplayType.LIST, + context_menu_location: Constants.ContextMenuLocation.RIGHT, + column_spacing: 0, + row_spacing: 0, + supports_category_hover_activation: true, + vertical: false, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + quicklinks_icon_size: Constants.MEDIUM_ICON_SIZE, + buttons_icon_size: Constants.MEDIUM_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + // Stores the Pinned Icons on the left side + this.actionsScrollBox = this._createScrollBox({ + x_expand: false, + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + style_class: 'small-vfade', + style: `padding: 10px 0px; width: 62px; margin: 0px 8px 0px 0px; + background-color:rgba(10, 10, 15, 0.1); border-color:rgba(186, 196,201, 0.2); + border-width: 1px; border-radius: 8px;`, + }); + this.actionsScrollBox.set_policy(St.PolicyType.NEVER, St.PolicyType.EXTERNAL); + this.actionsBox = new St.BoxLayout({ + vertical: true, + style: 'spacing: 10px;', + }); + this._addChildToParent(this.actionsScrollBox, this.actionsBox); + this.add_child(this.actionsScrollBox); + + // contains searchbar, rightBox, leftBox + this.rightPanelParentBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + this.add_child(this.rightPanelParentBox); + + this._mainBox = new St.BoxLayout({ + vertical: false, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + }); + this.rightPanelParentBox.add_child(this._mainBox); + + this.rightBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + this.applicationsBox = new St.BoxLayout({vertical: true}); + this.applicationsScrollBox = this._createScrollBox({ + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this.rightBox.add_child(this.applicationsScrollBox); + + this.leftBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + this._mainBox.add_child(horizontalFlip ? this.rightBox : this.leftBox); + this._mainBox.add_child(verticalSeparator); + this._mainBox.add_child(horizontalFlip ? this.leftBox : this.rightBox); + + this.categoriesScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: false, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.leftBox.add_child(this.categoriesScrollBox); + this.categoriesBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.categoriesScrollBox, this.categoriesBox); + + this.searchEntry.style = 'margin: 0px;'; + const searchBarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchBarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.add_style_class_name('arcmenu-search-top'); + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MAX, + Constants.SeparatorAlignment.HORIZONTAL); + + this.rightPanelParentBox.insert_child_at_index(this.searchEntry, 0); + this.rightPanelParentBox.insert_child_at_index(separator, 1); + } else if (searchBarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MAX, + Constants.SeparatorAlignment.HORIZONTAL); + + this.rightPanelParentBox.add_child(separator); + this.rightPanelParentBox.add_child(this.searchEntry); + } + + this._settings.connectObject('changed::mint-layout-extra-shortcuts', () => this._createExtraButtons(), this); + this._createExtraButtons(); + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + _createExtraButtons() { + this.actionsBox.destroy_all_children(); + const extraButtons = this._settings.get_value('mint-layout-extra-shortcuts').deep_unpack(); + + if (extraButtons.length === 0) + return; + + const isContainedInCategory = false; + + for (let i = 0; i < extraButtons.length; i++) { + const {id} = extraButtons[i]; + if (id === Constants.ShortcutCommands.SEPARATOR) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.actionsBox.add_child(separator); + } else { + const item = this.createMenuItem(extraButtons[i], Constants.DisplayType.BUTTON, isContainedInCategory); + if (item.shouldShow) + this.actionsBox.add_child(item); + else + item.destroy(); + } + } + } + + updateWidth(setDefaultMenuView) { + const leftPanelWidthOffset = 0; + const rightPanelWidthOffset = 45; + super.updateWidth(setDefaultMenuView, leftPanelWidthOffset, rightPanelWidthOffset); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.displayCategories(); + + const topCategory = this.categoryDirectories.values().next().value; + topCategory.displayAppList(); + this.setActiveCategory(topCategory); + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (shouldShow) { + const categoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, categoryMenuItem); + } + } + + super.loadCategories(); + } + + displayCategories() { + super.displayCategories(this.categoriesBox); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/plasma.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/plasma.js new file mode 100644 index 0000000..ef27219 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/plasma.js @@ -0,0 +1,585 @@ +import Atk from 'gi://Atk'; +import Clutter from 'gi://Clutter'; +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; +import * as PlaceDisplay from '../placeDisplay.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class PlasmaLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.LIST, + search_display_type: Constants.DisplayType.LIST, + column_spacing: 0, + row_spacing: 0, + default_menu_width: 450, + vertical: true, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.MEDIUM_ICON_SIZE, + quicklinks_icon_size: Constants.MEDIUM_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + // Some menu items might not be on the menu at the time of destroy(); + // Track them here. + this._destroyableObjects = []; + + this.topBox = new St.BoxLayout({ + x_expand: true, + y_expand: false, + vertical: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + }); + this.leftTopBox = new St.BoxLayout({ + x_expand: false, + y_expand: false, + vertical: false, + y_align: Clutter.ActorAlign.CENTER, + style: 'padding-left: 10px; margin-left: 0.4em', + }); + this.rightTopBox = new St.BoxLayout({ + x_expand: true, + y_expand: false, + vertical: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + style_class: 'popup-menu-item', + style: 'padding: 0px; margin: 0px; spacing: 0px;', + }); + + const userMenuIcon = new MW.UserMenuIcon(this, 55, true); + userMenuIcon.set({ + x_expand: false, + y_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + }); + userMenuIcon.label.set({ + style: 'padding-left: 0.4em; margin: 0px 10px 0px 15px; font-weight: bold;', + y_expand: false, + x_expand: true, + x_align: Clutter.ActorAlign.START, + }); + + this.leftTopBox.add_child(userMenuIcon); + this.rightTopBox.add_child(userMenuIcon.label); + this.rightTopBox.add_child(this.searchEntry); + this.topBox.add_child(this.leftTopBox); + this.topBox.add_child(this.rightTopBox); + + // Applications Box - Contains Favorites, Categories or programs + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.applicationsBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + + this.navigateBoxContainer = new St.BoxLayout({ + x_expand: true, + y_expand: false, + vertical: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + }); + this.navigateBox = new St.BoxLayout({ + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + style: 'spacing: 6px;', + }); + + const layout = new Clutter.GridLayout({ + orientation: Clutter.Orientation.VERTICAL, + column_homogeneous: true, + column_spacing: 10, + row_spacing: 10, + }); + this.grid = new St.Widget({layout_manager: layout}); + layout.hookup_style(this.grid); + this.navigateBox.add_child(this.grid); + + this.pinnedAppsButton = new PlasmaMenuItem(this, _('Pinned'), `${this._extension.path}/${Constants.ArcMenuLogoSymbolic}`); + this.pinnedAppsButton.connect('activate', () => this.displayPinnedApps()); + this.grid.layout_manager.attach(this.pinnedAppsButton, 0, 0, 1, 1); + this.pinnedAppsButton.set_style_pseudo_class('active-item'); + + this.applicationsButton = new PlasmaMenuItem(this, _('Apps'), 'preferences-desktop-apps-symbolic'); + this.applicationsButton.connect('activate', () => this.displayCategories()); + this.grid.layout_manager.attach(this.applicationsButton, 1, 0, 1, 1); + + this.computerButton = new PlasmaMenuItem(this, _('Computer'), 'computer-symbolic'); + this.computerButton.connect('activate', () => this.displayComputerCategory()); + this.grid.layout_manager.attach(this.computerButton, 2, 0, 1, 1); + + this.leaveButton = new PlasmaMenuItem(this, _('Leave'), 'system-shutdown-symbolic'); + this.leaveButton.connect('activate', () => this.displayPowerItems()); + this.grid.layout_manager.attach(this.leaveButton, 3, 0, 1, 1); + + this.categoryHeader = new PlasmaCategoryHeader(this); + + const searchBarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchBarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.style = 'margin: 3px 10px 5px 10px;'; + this.topBox.style = 'padding-top: 0.5em;'; + this.navigateBoxContainer.set({ + y_expand: false, + y_align: Clutter.ActorAlign.START, + }); + + this.navigateBoxContainer.add_child(this.navigateBox); + + let separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.navigateBoxContainer.add_child(separator); + + this.add_child(this.navigateBoxContainer); + this.add_child(this.applicationsScrollBox); + + separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.add_child(separator); + + this.add_child(this.topBox); + } else if (searchBarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.style = 'margin: 3px 10px 10px 10px;'; + this.navigateBoxContainer.set({ + y_expand: true, + y_align: Clutter.ActorAlign.END, + }); + + this.add_child(this.topBox); + + let separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.add_child(separator); + + this.add_child(this.applicationsScrollBox); + + separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.navigateBoxContainer.add_child(separator); + + this.navigateBoxContainer.add_child(this.navigateBox); + this.add_child(this.navigateBoxContainer); + } + + const applicationShortcutsList = this._settings.get_value('application-shortcuts').deep_unpack(); + this.applicationShortcuts = []; + for (let i = 0; i < applicationShortcutsList.length; i++) { + const shortcutMenuItem = this.createMenuItem(applicationShortcutsList[i], + Constants.DisplayType.LIST, false); + if (shortcutMenuItem.shouldShow) + this.applicationShortcuts.push(shortcutMenuItem); + else + shortcutMenuItem.destroy(); + } + + const directoryShortcutsList = this._settings.get_value('directory-shortcuts').deep_unpack(); + this._loadPlaces(directoryShortcutsList); + + this.externalDevicesBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + }); + + this._placesSections = {}; + this.placesManager = new PlaceDisplay.PlacesManager(); + for (let i = 0; i < Constants.SECTIONS.length; i++) { + const id = Constants.SECTIONS[i]; + this._placesSections[id] = new St.BoxLayout({vertical: true}); + this.placesManager.connectObject(`${id}-updated`, () => this._redisplayPlaces(id), this); + + this._createPlaces(id); + this.externalDevicesBox.add_child(this._placesSections[id]); + } + + this.updateWidth(); + this._createPowerItems(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + populateFrequentAppsList(categoryMenuItem) { + categoryMenuItem.appList = []; + const mostUsed = Shell.AppUsage.get_default().get_most_used(); + for (let i = 0; i < mostUsed.length; i++) { + if (mostUsed[i] && mostUsed[i].get_app_info().should_show()) { + categoryMenuItem.appList.push(mostUsed[i]); + let item = this.applicationsMap.get(mostUsed[i]); + if (!item) { + item = new MW.ApplicationMenuItem(this, mostUsed[i], this.display_type); + this.applicationsMap.set(mostUsed[i], item); + } + } + } + } + + _clearActorsFromBox(box) { + this.categoryHeader.setActiveCategory(null); + if (this.contains(this.categoryHeader)) + this.remove_child(this.categoryHeader); + super._clearActorsFromBox(box); + } + + clearActiveItem() { + this.pinnedAppsButton.setActive(false); + this.computerButton.setActive(false); + this.applicationsButton.setActive(false); + this.leaveButton.setActive(false); + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + this.hasPinnedApps = true; + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (categoryEnum === Constants.CategoryType.PINNED_APPS || !shouldShow) + continue; + + const categoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, categoryMenuItem); + } + + super.loadCategories(); + } + + displayComputerCategory() { + this._clearActorsFromBox(this.applicationsBox); + this.applicationsBox.add_child(this.createLabelRow(_('Application Shortcuts'))); + + for (let i = 0; i < this.applicationShortcuts.length; i++) + this.applicationsBox.add_child(this.applicationShortcuts[i]); + + this.applicationsBox.add_child(this.createLabelRow(_('Places'))); + + for (let i = 0; i < this.directoryShortcuts.length; i++) + this.applicationsBox.add_child(this.directoryShortcuts[i]); + + this.applicationsBox.add_child(this.externalDevicesBox); + this.activeMenuItem = this.applicationShortcuts[0]; + } + + _createPlaces(id) { + const places = this.placesManager.get(id); + + if (places.length === 0) + return; + else if (id === 'bookmarks') + this._placesSections[id].add_child(this.createLabelRow(_('Bookmarks'))); + else if (id === 'devices') + this._placesSections[id].add_child(this.createLabelRow(_('Devices'))); + else if (id === 'network') + this._placesSections[id].add_child(this.createLabelRow(_('Network'))); + else + return; + + for (let i = 0; i < places.length; i++) { + const item = new MW.PlaceMenuItem(this, places[i], Constants.DisplayType.LIST); + this._placesSections[id].add_child(item); + } + } + + displayPinnedApps() { + this.activeCategoryType = Constants.CategoryType.PINNED_APPS; + super.displayPinnedApps(); + } + + _loadPlaces(directoryShortcutsList) { + this.directoryShortcuts = []; + for (let i = 0; i < directoryShortcutsList.length; i++) { + const isContainedInCategory = false; + const directoryData = directoryShortcutsList[i]; + const placeMenuItem = this.createMenuItem(directoryData, Constants.DisplayType.LIST, isContainedInCategory); + this.directoryShortcuts.push(placeMenuItem); + } + } + + _createPowerItems() { + this.sessionBox = new St.BoxLayout({vertical: true}); + this._destroyableObjects.push(this.sessionBox); + this.sessionBox.add_child(this.createLabelRow(_('Session'))); + + this.systemBox = new St.BoxLayout({vertical: true}); + this._destroyableObjects.push(this.systemBox); + this.systemBox.add_child(this.createLabelRow(_('System'))); + + this.hasSessionOption = false; + this.hasSystemOption = false; + + const powerOptions = this._settings.get_value('power-options').deep_unpack(); + for (let i = 0; i < powerOptions.length; i++) { + const powerType = powerOptions[i][0]; + const shouldShow = powerOptions[i][1]; + + if (!shouldShow) + continue; + + const powerButton = new MW.PowerMenuItem(this, powerType); + if (powerType === Constants.PowerType.LOCK || powerType === Constants.PowerType.LOGOUT || + powerType === Constants.PowerType.SWITCH_USER) { + this.hasSessionOption = true; + this.sessionBox.add_child(powerButton); + } else { + this.hasSystemOption = true; + this.systemBox.add_child(powerButton); + } + } + } + + displayPowerItems() { + this._clearActorsFromBox(this.applicationsBox); + if (this.hasSessionOption) + this.applicationsBox.add_child(this.sessionBox); + if (this.hasSystemOption) + this.applicationsBox.add_child(this.systemBox); + } + + displayCategories() { + this.activeCategoryType = Constants.CategoryType.CATEGORIES_LIST; + this._clearActorsFromBox(this.applicationsBox); + + this.categoryHeader.setActiveCategory(null); + this._insertCategoryHeader(); + + let isActiveMenuItemSet = false; + let hasExtraCategory = false; + let separatorAdded = false; + + for (const categoryMenuItem of this.categoryDirectories.values()) { + const isExtraCategory = categoryMenuItem.isExtraCategory(); + + if (!hasExtraCategory) { + hasExtraCategory = isExtraCategory; + } else if (!isExtraCategory && !separatorAdded) { + this.applicationsBox.add_child(new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL)); + separatorAdded = true; + } + + this.applicationsBox.add_child(categoryMenuItem); + if (!isActiveMenuItemSet) { + isActiveMenuItemSet = true; + this.activeMenuItem = categoryMenuItem; + } + } + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.clearActiveItem(); + this.pinnedAppsButton.set_style_pseudo_class('active-item'); + this.displayPinnedApps(); + } + + _insertCategoryHeader() { + if (this.contains(this.categoryHeader)) + this.remove_child(this.categoryHeader); + + const searchBarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchBarLocation === Constants.SearchbarLocation.BOTTOM) + this.insert_child_at_index(this.categoryHeader, 1); + else + this.insert_child_at_index(this.categoryHeader, 2); + } + + displayCategoryAppList(appList, category) { + this._clearActorsFromBox(); + this._insertCategoryHeader(); + this.categoryHeader.setActiveCategory(this.activeCategoryName); + this._displayAppList(appList, category, this.applicationsGrid); + } + + displayRecentFiles() { + super.displayRecentFiles(); + this._insertCategoryHeader(); + this.activeCategoryType = Constants.CategoryType.RECENT_FILES; + this.categoryHeader.setActiveCategory(this.activeCategoryName); + } + + _onSearchEntryChanged(searchEntry, searchString) { + super._onSearchEntryChanged(searchEntry, searchString); + if (!searchEntry.isEmpty()) { + this.clearActiveItem(); + this.activeCategoryType = Constants.CategoryType.SEARCH_RESULTS; + } + } + + _onDestroy() { + for (const obj of this._destroyableObjects) + obj.destroy(); + + + for (const item of this.applicationShortcuts) + item.destroy(); + + + super._onDestroy(); + } +}; + +class PlasmaMenuItem extends MW.BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, title, iconPath) { + super(menuLayout); + this.iconPath = iconPath; + + this.tooltipLocation = Constants.TooltipLocation.BOTTOM_CENTERED; + this.vertical = true; + + const searchbarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchbarLocation === Constants.SearchbarLocation.TOP) + this.name = 'arcmenu-plasma-button-top'; + else + this.name = 'arcmenu-plasma-button-bottom'; + + this._iconBin = new St.Bin({ + y_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this._iconBin); + + this._updateIcon(); + + this.label = new St.Label({ + text: _(title), + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + }); + + this.label.get_clutter_text().set_line_wrap(true); + this.add_child(this.label); + } + + createIcon() { + return new St.Icon({ + gicon: Gio.icon_new_for_string(this.iconPath), + icon_size: Constants.MEDIUM_ICON_SIZE, + }); + } + + _onHover() { + if (this.hover) { + const description = null; + this._menuButton.tooltip.showTooltip(this, this.tooltipLocation, this.label, + description, Constants.DisplayType.LIST); + } else { + this._menuButton.tooltip.hide(); + } + const shouldHover = this._settings.get_boolean('plasma-enable-hover'); + if (shouldHover && this.hover && !this.isActive) + this.activate(Clutter.get_current_event()); + } + + set active(active) { + const activeChanged = active !== this.active; + if (activeChanged) { + this._active = active; + if (active) { + this.add_style_class_name('selected'); + this._menuLayout.activeMenuItem = this; + if (this.can_focus) + this.grab_key_focus(); + } else { + this.remove_style_class_name('selected'); + } + this.notify('active'); + } + } + + setActive(active) { + if (active) { + this.isActive = true; + this.set_style_pseudo_class('active-item'); + } else { + this.isActive = false; + this.set_style_pseudo_class(null); + } + } + + activate(event) { + this._menuLayout.searchEntry.clearWithoutSearchChangeEvent(); + this._menuLayout.clearActiveItem(); + this.setActive(true); + super.activate(event); + } +} + +class PlasmaCategoryHeader extends St.BoxLayout { + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super({ + style_class: 'popup-menu-item', + style: 'padding: 0px;', + reactive: true, + track_hover: false, + can_focus: false, + accessible_role: Atk.Role.MENU_ITEM, + }); + this._menuLayout = menuLayout; + + this.backButton = new MW.BaseMenuItem(this._menuLayout); + this.backButton.set({ + x_expand: false, + x_align: Clutter.ActorAlign.CENTER, + }); + + this.label = new St.Label({ + text: _('Apps'), + y_expand: false, + y_align: Clutter.ActorAlign.CENTER, + style: 'font-weight: bold', + }); + + this.backButton.add_child(this.label); + + this.add_child(this.backButton); + this.backButton.connect('activate', () => this._menuLayout.displayCategories()); + + this.categoryLabel = new St.Label({ + text: '', + y_expand: true, + y_align: Clutter.ActorAlign.CENTER, + }); + + this.add_child(this.categoryLabel); + } + + setActiveCategory(categoryText) { + if (categoryText) { + this.categoryLabel.text = _(categoryText); + this.categoryLabel.show(); + } else { + this.categoryLabel.hide(); + } + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/pop.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/pop.js new file mode 100644 index 0000000..80bf1e2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/pop.js @@ -0,0 +1,1278 @@ +/* eslint-disable jsdoc/require-jsdoc */ + +import Clutter from 'gi://Clutter'; +import GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js'; +import * as Dialog from 'resource:///org/gnome/shell/ui/dialog.js'; +import {DragMotionResult} from 'resource:///org/gnome/shell/ui/dnd.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js'; +import * as ParentalControlsManager from 'resource:///org/gnome/shell/misc/parentalControlsManager.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; + +import {AppContextMenu} from '../appMenu.js'; +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import {IconGrid} from '../iconGrid.js'; +import * as MW from '../menuWidgets.js'; +import * as Utils from '../utils.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +function _getFolderName(folder) { + const name = folder.get_string('name'); + + if (folder.get_boolean('translate')) { + const translated = Shell.util_get_translated_folder_name(name); + if (translated !== null) + return translated; + } + + return name; +} + +function _listsIntersect(a, b) { + for (const itemA of a) { + if (b.includes(itemA)) + return true; + } + return false; +} + +export const Layout = class PopLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.GRID, + search_results_spacing: 4, + column_spacing: 12, + row_spacing: 12, + default_menu_width: 1050, + vertical: true, + icon_grid_size: Constants.GridIconSize.EXTRA_LARGE, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.EXTRA_LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.MEDIUM_ICON_SIZE, + buttons_icon_size: Constants.LARGE_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this._tree.disconnectObject(this); + + this._folders = new Map(); + this._orderedItems = []; + + this.draggableApps = true; + this.topBox = new St.BoxLayout({ + x_expand: true, + y_expand: false, + vertical: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + }); + this.topBox.add_child(this.searchEntry); + + // Applications Box - Contains Favorites, Categories or programs + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.applicationsBox = new St.BoxLayout({ + vertical: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + + this.foldersContainer = new St.BoxLayout({ + vertical: true, + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_expand: true, + y_align: Clutter.ActorAlign.END, + }); + this.foldersGrid = new IconGrid({ + column_spacing: 6, + row_spacing: 6, + halign: Clutter.ActorAlign.CENTER, + }); + this._setFolderGridColumns(); + this.foldersContainer.add_child(this.foldersGrid); + + const homeGroupMenuItem = new HomeFolderMenuItem(this); + this._addItem(homeGroupMenuItem); + this.placeHolderFolderItem = new GroupFolderMenuItem(this, 'New Folder', null); + this._addItem(this.placeHolderFolderItem); + + const searchBarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchBarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.style = 'margin: 10px 220px;'; + this.topBox.style = 'padding-top: 0.5em;'; + + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.add_child(this.foldersContainer); + this.foldersContainer.set({ + y_align: Clutter.ActorAlign.START, + y_expand: false, + }); + this.add_child(separator); + + this.add_child(this.applicationsScrollBox); + + this.add_child(this.topBox); + } else if (searchBarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.style = 'margin: 10px 220px;'; + this.topBox.style = 'padding-bottom: 0.5em;'; + this.add_child(this.topBox); + + this.add_child(this.applicationsScrollBox); + + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.foldersContainer.insert_child_at_index(separator, 0); + this.add_child(this.foldersContainer); + } + + this._redisplayWorkId = Main.initializeDeferredWork(this, () => this._redisplay()); + + this._folderSettings = new Gio.Settings({schema_id: 'org.gnome.desktop.app-folders'}); + this._folderSettings.connectObject('changed::folder-children', () => Main.queueDeferredWork(this._redisplayWorkId), this); + this._parentalControlsManager = ParentalControlsManager.getDefault(); + this._parentalControlsManager.connectObject('app-filter-changed', () => Main.queueDeferredWork(this._redisplayWorkId), this); + Shell.AppSystem.get_default().connectObject('installed-changed', () => Main.queueDeferredWork(this._redisplayWorkId), this); + + this.updateWidth(); + this._redisplay(); + this.setDefaultMenuView(); + this._setGridColumns(this.applicationsGrid); + + this._settings.connectObject('changed::pop-default-view', () => this.setDefaultMenuView(), this); + } + + reloadApplications() { + } + + _redisplay() { + let oldFolders = this._orderedItems.slice(); + let oldFoldersIds = oldFolders.map(icon => icon.folder_id); + + let newFolders = this._loadFolders(); + let newFoldersIds = newFolders.map(icon => icon.folder_id); + + let addedFolders = newFolders.filter(icon => !oldFoldersIds.includes(icon.folder_id)); + let removedFolders = oldFolders.filter(icon => !newFoldersIds.includes(icon.folder_id)); + + // Remove old app icons + removedFolders.forEach(item => { + this._removeItem(item); + item.destroy(); + }); + + // Add new app icons, or move existing ones + newFolders.forEach(item => { + const index = newFolders.indexOf(item); + const position = this.foldersGrid.getItemPosition(item); + if (addedFolders.includes(item)) + this._addItem(item, index); + else if (position !== index) + this._moveItem(item, index); + }); + + this._orderedItems.forEach(icon => { + icon._redisplay(); + }); + } + + _addItem(item, position = 0) { + this._folders.set(item.folder_id, item); + this.foldersGrid.addItem(item, position); + + this._orderedItems.splice(position, 0, item); + } + + _moveItem(item, newPosition) { + this.foldersGrid.moveItem(item, newPosition); + + // Update the _orderedItems array + this._orderedItems.splice(this._orderedItems.indexOf(item), 1); + this._orderedItems.splice(newPosition, 0, item); + } + + _removeItem(item) { + const iconIndex = this._orderedItems.indexOf(item); + + this._orderedItems.splice(iconIndex, 1); + this._folders.delete(item.folder_id); + this.foldersGrid.removeItem(item); + } + + activateHomeFolder() { + const homeFolder = this._folders.get('Library Home'); + this.setActiveCategory(homeFolder); + homeFolder.displayAppList(); + } + + updateWidth(setDefaultMenuView) { + const widthAdjustment = this._settings.get_int('menu-width-adjustment'); + let menuWidth = this.default_menu_width + widthAdjustment; + // Set a 300px minimum limit for the menu width + menuWidth = Math.max(300, menuWidth); + this.applicationsScrollBox.style = `width: ${menuWidth}px;`; + this.menu_width = menuWidth; + + if (setDefaultMenuView) + this.setDefaultMenuView(); + + this._setFolderGridColumns(); + this._setGridColumns(this.applicationsGrid); + } + + _loadFolders() { + const newFolders = []; + const foldersData = {'Library Home': _('Library Home')}; + const homeFolder = this._folders.get('Library Home'); + newFolders.push(homeFolder); + const placeHolder = this._folders.get('New Folder'); + + this._appInfoList = Shell.AppSystem.get_default().get_installed().filter(appInfo => { + try { + appInfo.get_id(); // catch invalid file encodings + } catch (e) { + return false; + } + return this._parentalControlsManager.shouldShowApp(appInfo); + }); + + let apps = this._appInfoList.map(app => app.get_id()); + + let appSys = Shell.AppSystem.get_default(); + + const appsInsideFolders = new Set(); + + // Load and Create GroupFolderMenuItems + const folders = this._folderSettings.get_strv('folder-children'); + folders.forEach(id => { + const path = `${this._folderSettings.path}folders/${id}/`; + let folderIcon = this._folders.get(id); + if (!folderIcon) { + folderIcon = new GroupFolderMenuItem(this, id, path); + folderIcon.connect('apps-changed', () => { + this._redisplay(); + }); + + this._folders.set(id, folderIcon); + } + + // Don't try to display empty folders + if (!folderIcon.visible) + return; + + folderIcon.getAppIds().forEach(appId => appsInsideFolders.add(appId)); + foldersData[id] = folderIcon.folder_name; + newFolders.push(folderIcon); + }); + + // Store the id and name of each folder in 'pop-folders-data' + this._settings.set_value('pop-folders-data', new GLib.Variant('a{ss}', foldersData)); + + // Find any remaining apps not contained within a folder. + const remainingApps = []; + apps.forEach(appId => { + if (appsInsideFolders.has(appId)) + return; + const app = appSys.lookup_app(appId); + if (app) + remainingApps.push(app); + }); + remainingApps.sort((a, b) => { + return a.get_name().toLowerCase() > b.get_name().toLowerCase(); + }); + homeFolder.appsList = remainingApps.map(app => app.id); + + placeHolder.set({ + visible: false, + opacity: 0, + scale_x: 0, + scale_y: 0, + }); + newFolders.push(placeHolder); + return newFolders; + } + + loadCategories() { + + } + + createNewFolder(app) { + const newFolderId = GLib.uuid_string_random(); + + const folders = this._folderSettings.get_strv('folder-children'); + folders.push(newFolderId); + + const newFolderPath = this._folderSettings.path.concat('folders/', newFolderId, '/'); + let newFolderSettings; + try { + newFolderSettings = new Gio.Settings({ + schema_id: 'org.gnome.desktop.app-folders.folder', + path: newFolderPath, + }); + } catch (e) { + log('Error creating new folder'); + return; + } + + const appIds = [app.id]; + let folderName = Utils.findBestFolderName([app]); + if (!folderName) + folderName = _('Unnamed Folder'); + + newFolderSettings.delay(); + newFolderSettings.set_string('name', folderName); + newFolderSettings.set_strv('apps', appIds); + newFolderSettings.apply(); + + this._folderSettings.set_strv('folder-children', folders); + } + + fadeInPlaceHolder() { + this.placeHolderFolderItem.visible = true; + this.placeHolderFolderItem.ease({ + opacity: 255, + duration: 200, + scale_x: 1, + scale_y: 1, + mode: Clutter.AnimationMode.EASE_IN_QUAD, + }); + } + + fadeOutPlaceHolder() { + this.placeHolderFolderItem.ease({ + opacity: 0, + scale_x: 0, + scale_y: 0, + duration: 200, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + this.placeHolderFolderItem.visible = false; + }, + }); + } + + _setFolderGridColumns() { + const gridIconPadding = 10; + const iconWidth = 110 + gridIconPadding; + + const padding = 12; + const totalWidth = this.menu_width - padding; + const spacing = this.foldersGrid.layout_manager.column_spacing; + const columns = Math.floor(totalWidth / (iconWidth + spacing)); + this.foldersGrid.setColumns(columns); + } + + getAppInfos() { + return this._appInfoList; + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + const defaultView = this._settings.get_string('pop-default-view'); + let category = this._folders.get(defaultView); + + if (!category) + category = this._folders.values().next().value; + + category.displayAppList(); + this.setActiveCategory(category, true); + } + + _onSearchEntryChanged(searchEntry, searchString) { + super._onSearchEntryChanged(searchEntry, searchString); + if (!searchEntry.isEmpty()) + this.activeCategoryType = Constants.CategoryType.SEARCH_RESULTS; + } + + _onDestroy() { + if (this._folders) { + this._folders.forEach((value, _key, _map) => { + value.destroy(); + }); + this._folders = null; + } + + this._orderedItems = null; + this._appInfoList = null; + super._onDestroy(); + } +}; + +class HomeFolderMenuItem extends MW.DraggableMenuItem { + static [GObject.properties] = { + 'folder-name': GObject.ParamSpec.string('folder-name', 'folder-name', 'folder-name', + GObject.ParamFlags.READWRITE, ''), + 'folder-id': GObject.ParamSpec.string('folder-id', 'folder-id', 'folder-id', + GObject.ParamFlags.READWRITE, ''), + 'home-folder': GObject.ParamSpec.boolean('home-folder', 'home-folder', 'home-folder', + GObject.ParamFlags.READWRITE, true), + }; + + static [GObject.signals] = { + 'apps-changed': {}, + }; + + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + const displayType = Constants.DisplayType.GRID; + super(menuLayout, displayType, false); + + this.folder_id = 'Library Home'; + this.folder_name = _('Library Home'); + + this._grid = this._menuLayout.applicationsGrid; + this.hasContextMenu = false; + + this._items = new Map(); + this._orderedItems = []; + + this.add_style_class_name('ArcMenuIconGrid ArcMenuGroupFolder'); + + this._iconBin = new St.Bin(); + this.add_child(this._iconBin); + + this.label.text = _('Library Home'); + this.add_child(this.label); + this._updateIcon(); + + this.set({ + vertical: true, + x_expand: false, + tooltipLocation: Constants.TooltipLocation.BOTTOM_CENTERED, + style: `width: ${110}px; height: ${72}px;`, + }); + } + + _isFolderDisplayed() { + return this._menuLayout.activeCategoryItem === this; + } + + _showFolderPreview() { + } + + _hideFolderPreview() { + } + + acceptDrop(source, actor, x, y) { + const acceptDrop = super.acceptDrop(source, actor, x, y); + if (!acceptDrop) + return false; + + const app = source._app; + const {folderMenuItem} = source; + + folderMenuItem?.removeApp(app); + + this._menuLayout.fadeOutPlaceHolder(); + return true; + } + + _canAccept(source) { + return source !== this && source instanceof ApplicationMenuItem && !this._apps.includes(source._app); + } + + createIcon() { + const iconSize = 32; + + const icon = new St.Icon({ + style_class: 'popup-menu-icon', + icon_size: iconSize, + icon_name: 'user-home-symbolic', + }); + return icon; + } + + getAppIds() { + return this._orderedItems.map(item => item._app.id); + } + + displayAppList() { + this._menuLayout._clearActorsFromBox(); + this._menuLayout.searchEntry?.clearWithoutSearchChangeEvent(); + this._grid.removeAllItems(); + this._menuLayout._setGridColumns(this._grid); + + for (let i = 0; i < this._orderedItems.length; i++) { + const item = this._orderedItems[i]; + const parent = item.get_parent(); + if (parent) + parent.remove_child(item); + + this._grid.appendItem(item); + } + + if (this._menuLayout.applicationsBox && !this._menuLayout.applicationsBox.contains(this._grid)) + this._menuLayout.applicationsBox.add_child(this._grid); + + this._menuLayout.activeMenuItem = this._orderedItems[0]; + } + + _loadApps() { + this._apps = []; + + const appSys = Shell.AppSystem.get_default(); + const addAppId = appId => { + const app = appSys.lookup_app(appId); + if (!app) + return; + + if (!ParentalControlsManager.getDefault().shouldShowApp(app.get_app_info())) + return; + + if (this._apps.indexOf(app) !== -1) + return; + + this._apps.push(app); + }; + + this.appsList.forEach(addAppId); + + let items = []; + this._apps.forEach(app => { + let icon = this._items.get(app.get_id()); + if (!icon) { + icon = new ApplicationMenuItem(this._menuLayout, app, this._menuLayout.display_type); + icon.setFolderGroup(this); + } + + items.push(icon); + }); + + return items; + } + + _compareItems(a, b) { + return a.name.localeCompare(b.name); + } + + _redisplay() { + let oldApps = this._orderedItems.slice(); + let oldAppIds = oldApps.map(icon => icon._app.id); + + let newApps = this._loadApps(); + let newAppIds = newApps.map(icon => icon._app.id); + + let addedApps = newApps.filter(icon => !oldAppIds.includes(icon._app.id)); + let removedApps = oldApps.filter(icon => !newAppIds.includes(icon._app.id)); + + // Remove old app icons + removedApps.forEach(item => { + this._removeItem(item); + item.destroy(); + }); + + // Add new app icons, or move existing ones + newApps.forEach(item => { + const index = newApps.indexOf(item); + const position = this._orderedItems.indexOf(item); + if (addedApps.includes(item)) + this._addItem(item, index); + else if (position !== index) + this._moveItem(item, index); + }); + } + + _addItem(item, position) { + this._items.set(item._app.id, item); + + if (this._isFolderDisplayed()) + this._grid.addItem(item, position); + + this._orderedItems.splice(position, 0, item); + } + + _moveItem(item, newPosition) { + if (this._isFolderDisplayed()) + this._grid.moveItem(item, newPosition); + + // Update the _orderedItems array + this._orderedItems.splice(this._orderedItems.indexOf(item), 1); + this._orderedItems.splice(newPosition, 0, item); + } + + _removeItem(item) { + const iconIndex = this._orderedItems.indexOf(item); + + this._orderedItems.splice(iconIndex, 1); + this._items.delete(item._app.id); + if (this._isFolderDisplayed()) + this._grid.removeItem(item); + } + + activate(event) { + super.activate(event); + this._menuLayout.setActiveCategory(this); + this.displayAppList(); + } +} + +class GroupFolderMenuItem extends MW.DraggableMenuItem { + static [GObject.properties] = { + 'folder-name': GObject.ParamSpec.string('folder-name', 'folder-name', 'folder-name', + GObject.ParamFlags.READWRITE, ''), + 'folder-id': GObject.ParamSpec.string('folder-id', 'folder-id', 'folder-id', + GObject.ParamFlags.READWRITE, ''), + 'new-folder': GObject.ParamSpec.boolean('new-folder', 'new-folder', 'new-folder', + GObject.ParamFlags.READWRITE, false), + }; + + static [GObject.signals] = { + 'folder-moved': {}, + 'apps-changed': {}, + }; + + static { + GObject.registerClass(this); + } + + constructor(menuLayout, id, path) { + const displayType = Constants.DisplayType.GRID; + super(menuLayout, displayType); + + if (path) { + this._folder = new Gio.Settings({ + schema_id: 'org.gnome.desktop.app-folders.folder', + path, + }); + } else { + this.new_folder = true; + } + this._apps = []; + this._grid = this._menuLayout.applicationsGrid; + this.folder_id = id; + this.hasContextMenu = true; + this._items = new Map(); + this._orderedItems = []; + + this.add_style_class_name('ArcMenuIconGrid ArcMenuGroupFolder'); + this.set({ + vertical: true, + x_expand: false, + tooltipLocation: Constants.TooltipLocation.BOTTOM_CENTERED, + style: `width: ${110}px; height: ${72}px;`, + }); + + this.add_child(this.label); + + + if (this._folder) { + this._folder.connectObject( + 'changed', this._sync.bind(this), this); + } + this._redisplay(); + this._sync(); + } + + _isFolderDisplayed() { + return this._menuLayout.activeCategoryItem === this; + } + + _updateName() { + if (this.new_folder) { + this.folder_name = _('New Folder'); + this.label.text = this.folder_name; + return; + } + + let name = _getFolderName(this._folder); + if (this.folder_name === name) + return; + + this.folder_name = name; + this.label.text = this.folder_name; + } + + _sync() { + if (this._deletingFolder) + return; + + this.emit('apps-changed'); + this._updateName(); + if (!this.new_folder) + this.visible = this._apps.length > 0; + + this._updateIcon(); + } + + addApp(app) { + const folderApps = this._folder.get_strv('apps'); + folderApps.push(app.id); + this._folder.set_strv('apps', folderApps); + + let excludedApps = this._folder.get_strv('excluded-apps'); + let index = excludedApps.indexOf(app.id); + if (index >= 0) { + excludedApps.splice(index, 1); + this._folder.set_strv('excluded-apps', excludedApps); + } + } + + removeApp(app) { + let folderApps = this._folder.get_strv('apps'); + let index = folderApps.indexOf(app.id); + if (index >= 0) + folderApps.splice(index, 1); + + // Remove the folder if this is the last app icon; otherwise, + // just remove the icon + if (folderApps.length === 0) { + this._deletingFolder = true; + + // Resetting all keys deletes the relocatable schema + let keys = this._folder.settings_schema.list_keys(); + for (const key of keys) + this._folder.reset(key); + + let settings = new Gio.Settings({schema_id: 'org.gnome.desktop.app-folders'}); + let folders = settings.get_strv('folder-children'); + folders.splice(folders.indexOf(this.folder_id), 1); + settings.set_strv('folder-children', folders); + + // if the folder is now deleted, activate the library home folder + this._menuLayout.activateHomeFolder(); + + this._deletingFolder = false; + } else { + // If this is a categories-based folder, also add it to + // the list of excluded apps + const categories = this._folder.get_strv('categories'); + if (categories.length > 0) { + const excludedApps = this._folder.get_strv('excluded-apps'); + excludedApps.push(app.id); + this._folder.set_strv('excluded-apps', excludedApps); + } + + this._folder.set_strv('apps', folderApps); + } + } + + popupContextMenu() { + if (this.home_folder) + return; + if (this.tooltip) + this.tooltip.hide(); + + if (this.contextMenu === undefined) { + this.contextMenu = new PopupMenu.PopupMenu(this, 0.5, St.Side.TOP); + this.contextMenu.connect('open-state-changed', (menu, isOpen) => { + if (isOpen) + this.add_style_pseudo_class('active'); + else if (!this.isActiveCategory) + this.remove_style_pseudo_class('active'); + }); + this.contextMenu.actor.add_style_class_name('arcmenu-menu app-menu'); + Main.uiGroup.add_child(this.contextMenu.actor); + this._menuLayout.contextMenuManager.addMenu(this.contextMenu); + + this.contextMenu.addAction(_('Rename Folder'), () => this._createRenameDialog()); + this.contextMenu.addAction(_('Delete Folder'), () => this._createDeleteDialog()); + } + + this.contextMenu.open(BoxPointer.PopupAnimation.FULL); + } + + _createDeleteDialog() { + this.contextMenu.close(); + const dialog = new ModalDialog.ModalDialog(); + const content = new Dialog.MessageDialogContent({ + title: _('Permanently delete %s folder?').format(this.folder_name), + }); + dialog.contentLayout.add_child(content); + + dialog.addButton({ + label: _('No'), + action: () => { + dialog.close(); + }, + default: true, + key: Clutter.KEY_Escape, + }); + dialog.addButton({ + label: _('Yes'), + action: () => { + this._menuLayout.removeFolder(this); + dialog.close(); + }, + default: false, + key: null, + }); + dialog.open(); + } + + _createRenameDialog() { + this.contextMenu.close(); + const dialog = new ModalDialog.ModalDialog(); + const content = new Dialog.MessageDialogContent({ + title: _('Rename %s folder').format(this.folder_name), + }); + dialog.contentLayout.add_child(content); + + const entry = new St.Entry({ + style_class: 'folder-name-entry', + text: this.folder_name, + reactive: true, + can_focus: true, + }); + entry.clutter_text.set({ + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + content.add_child(entry); + dialog.setInitialKeyFocus(entry); + + const saveName = () => { + const newFolderName = entry.text.trim(); + + if (newFolderName.length === 0 || newFolderName === this.folder_name) { + dialog.close(); + return; + } + + this.folderSettings.set_string('name', newFolderName); + this.folderSettings.set_boolean('translate', false); + dialog.close(); + }; + + entry.clutter_text.set_selection(0, -1); + entry.clutter_text.connect('activate', () => saveName()); + + dialog.addButton({ + label: _('Cancel'), + action: () => { + dialog.close(); + }, + default: false, + key: Clutter.KEY_Escape, + }); + dialog.addButton({ + label: _('Apply'), + action: () => saveName(), + default: false, + key: null, + }); + dialog.open(); + } + + getDragActor() { + return this.createIcon(); + } + + _showFolderPreview() { + } + + _hideFolderPreview() { + } + + acceptDrop(source, actor, x, y) { + const acceptDrop = super.acceptDrop(source, actor, x, y); + if (!acceptDrop) + return false; + + const app = source._app; + const {folderMenuItem} = source; + + if (!folderMenuItem.home_folder) + folderMenuItem?.removeApp(app); + + if (this.new_folder) { + this._menuLayout.createNewFolder(app); + return true; + } + + this.addApp(app); + + this._menuLayout.fadeOutPlaceHolder(); + + return true; + } + + _canAccept(source) { + return source !== this && source instanceof ApplicationMenuItem && !this._apps.includes(source._app); + } + + _getDropTarget(x, y, source, layoutManager) { + const [targetIndex, dragLocation] = super._getDropTarget(x, y, source, layoutManager); + + if (targetIndex === 0) + return [-1, 0]; + + return [targetIndex, dragLocation]; + } + + _onDragEnd() { + super._onDragEnd(); + + const parent = this.get_parent(); + if (!parent) + return; + + const layoutManager = parent.layout_manager; + const orderedList = layoutManager.getChildren(); + const orderedFolders = []; + orderedList.forEach(child => { + if (!child.home_folder && !child.new_folder) + orderedFolders.push(child.folder_id); + }); + this._menuLayout._folderSettings.set_strv('folder-children', orderedFolders); + } + + createIcon() { + const iconSize = 32; + + if (!this._apps.length || this.new_folder) { + const icon = new St.Icon({ + style_class: 'popup-menu-icon', + icon_size: iconSize, + icon_name: 'folder-directory-symbolic', + }); + return icon; + } + + const layout = new Clutter.GridLayout({ + row_homogeneous: true, + column_homogeneous: true, + }); + const icon = new St.Widget({ + layout_manager: layout, + x_align: Clutter.ActorAlign.CENTER, + style: `width: ${iconSize}px; height: ${iconSize}px;`, + }); + + const subSize = Math.floor(.4 * iconSize); + + const numItems = this._apps.length; + const rtl = icon.get_text_direction() === Clutter.TextDirection.RTL; + for (let i = 0; i < 4; i++) { + const style = `width: ${subSize}px; height: ${subSize}px;`; + const bin = new St.Bin({style}); + if (i < numItems) + bin.child = this._apps[i].create_icon_texture(subSize); + layout.attach(bin, rtl ? (i + 1) % 2 : i % 2, Math.floor(i / 2), 1, 1); + } + + return icon; + } + + getAppIds() { + return this._folder.get_strv('apps'); + } + + displayAppList() { + this._menuLayout._clearActorsFromBox(); + this._menuLayout.searchEntry?.clearWithoutSearchChangeEvent(); + + this._grid.removeAllItems(); + + this._menuLayout._setGridColumns(this._grid); + + for (let i = 0; i < this._orderedItems.length; i++) { + const item = this._orderedItems[i]; + const parent = item.get_parent(); + if (parent) + parent.remove_child(item); + + this._grid.appendItem(item); + } + + if (this._menuLayout.applicationsBox && !this._menuLayout.applicationsBox.contains(this._grid)) + this._menuLayout.applicationsBox.add_child(this._grid); + + this._menuLayout.activeMenuItem = this._orderedItems[0]; + } + + _loadApps() { + this._apps = []; + const excludedApps = this._folder.get_strv('excluded-apps'); + const appSys = Shell.AppSystem.get_default(); + const addAppId = appId => { + if (excludedApps.includes(appId)) + return; + + const app = appSys.lookup_app(appId); + if (!app) + return; + + if (!ParentalControlsManager.getDefault().shouldShowApp(app.get_app_info())) + return; + + if (this._apps.indexOf(app) !== -1) + return; + + this._apps.push(app); + }; + + const folderApps = this._folder.get_strv('apps'); + folderApps.forEach(addAppId); + + const folderCategories = this._folder.get_strv('categories'); + const appInfos = this._menuLayout.getAppInfos(); + appInfos.forEach(appInfo => { + let appCategories = Utils.getCategories(appInfo); + if (!_listsIntersect(folderCategories, appCategories)) + return; + + addAppId(appInfo.get_id()); + }); + + let items = []; + this._apps.forEach(app => { + let icon = this._items.get(app.get_id()); + if (!icon) { + icon = new ApplicationMenuItem(this._menuLayout, app, this._menuLayout.display_type); + icon.setFolderGroup(this); + } + + items.push(icon); + }); + + return items; + } + + _compareItems(a, b) { + return a.name.localeCompare(b.name); + } + + _redisplay() { + if (this.new_folder) + return; + let oldApps = this._orderedItems.slice(); + let oldAppIds = oldApps.map(icon => icon._app.id); + + let newApps = this._loadApps(); + let newAppIds = newApps.map(icon => icon._app.id); + + let addedApps = newApps.filter(icon => !oldAppIds.includes(icon._app.id)); + let removedApps = oldApps.filter(icon => !newAppIds.includes(icon._app.id)); + + // Remove old app icons + removedApps.forEach(item => { + this._removeItem(item); + item.destroy(); + }); + + // Add new app icons, or move existing ones + newApps.forEach(item => { + const index = newApps.indexOf(item); + const position = this._orderedItems.indexOf(item); + if (addedApps.includes(item)) { + this._addItem(item, index); + } else if (position !== index) { + this._moveItem(item, index); + } else { + this._orderedItems.splice(this._orderedItems.indexOf(item), 1); + this._orderedItems.splice(index, 0, item); + } + }); + } + + _addItem(item, position) { + this._items.set(item._app.id, item); + + if (this._isFolderDisplayed()) + this._grid.addItem(item, position); + + this._orderedItems.splice(position, 0, item); + } + + _moveItem(item, newPosition) { + if (this._isFolderDisplayed()) + this._grid.moveItem(item, newPosition); + + // Update the _orderedItems array + this._orderedItems.splice(this._orderedItems.indexOf(item), 1); + this._orderedItems.splice(newPosition, 0, item); + } + + _removeItem(item) { + const iconIndex = this._orderedItems.indexOf(item); + + this._orderedItems.splice(iconIndex, 1); + this._items.delete(item._app.id); + if (this._isFolderDisplayed()) + this._grid.removeItem(item); + } + + activate(event) { + super.activate(event); + this._menuLayout.setActiveCategory(this); + this.displayAppList(); + } +} + +export class ApplicationMenuItem extends MW.DraggableMenuItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout, app, displayType) { + super(menuLayout, displayType); + this._app = app; + this._displayType = displayType; + + this.hasContextMenu = !!this._app; + + const disableRecentAppsIndicator = this._settings.get_boolean('disable-recently-installed-apps'); + if (!disableRecentAppsIndicator) { + const recentApps = this._settings.get_strv('recently-installed-apps'); + this.isRecentlyInstalled = recentApps.some(appIter => appIter === this._app.get_id()); + } + + this._updateIcon(); + + this.label.text = this._app.get_name(); + this.description = this._app.get_description(); + + this.add_child(this.label); + + if (this.isRecentlyInstalled) { + this._indicator = new St.Label({ + text: _('New'), + style_class: 'arcmenu-text-indicator', + x_expand: true, + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.CENTER, + }); + this.add_child(this._indicator); + } + + this.connect('notify::hover', () => this.removeIndicator()); + this.connect('key-focus-in', () => this.removeIndicator()); + } + + setFolderGroup(folderMenuItem) { + this.folderMenuItem = folderMenuItem; + } + + getDragActor() { + const icon = this.createIcon(); + icon.set({ + scale_x: 0.8, + scale_y: 0.8, + }); + return icon; + } + + _onDragBegin() { + this._menuLayout.fadeInPlaceHolder(); + super._onDragBegin(); + } + + _onDragMotion(dragEvent) { + if (!this.folderMenuItem || this.folderMenuItem.home_folder) + return DragMotionResult.CONTINUE; + + return super._onDragMotion(dragEvent); + } + + _canAccept() { + return false; + } + + acceptDrop() { + return false; + } + + _onDragEnd() { + this._menuLayout.fadeOutPlaceHolder(); + super._onDragEnd(); + + const parent = this.get_parent(); + if (!parent || (this.folderMenuItem && this.folderMenuItem.home_folder)) + return; + + const layoutManager = parent.layout_manager; + const children = layoutManager.getChildren(); + const appIds = children.map(child => child._app.id); + + this.folderMenuItem._folder.set_strv('apps', appIds); + } + + set folderPath(value) { + this.hasContextMenu = value; + this._folderPath = value; + } + + get folderPath() { + return this._folderPath; + } + + createIcon() { + this._iconBin.x_align = Clutter.ActorAlign.CENTER; + + const iconSizeEnum = this._settings.get_enum('menu-item-grid-icon-size'); + const defaultIconSize = this._menuLayout.icon_grid_size; + const {iconSize} = Utils.getGridIconSize(iconSizeEnum, defaultIconSize); + + const icon = this._app.create_icon_texture(iconSize); + + if (icon) { + icon.style_class = this._displayType === Constants.DisplayType.GRID ? '' : 'popup-menu-icon'; + return icon; + } else { + return false; + } + } + + removeIndicator() { + if (this.isRecentlyInstalled) { + this.isRecentlyInstalled = false; + const recentApps = this._settings.get_strv('recently-installed-apps'); + const index = recentApps.indexOf(this._app.get_id()); + if (index > -1) + recentApps.splice(index, 1); + + this._settings.set_strv('recently-installed-apps', recentApps); + + this._indicator.hide(); + this._menuLayout.setNewAppIndicator(); + } + } + + popupContextMenu() { + this.removeIndicator(); + if (this.tooltip) + this.tooltip.hide(); + + if (!this._app && !this.folderPath) + return; + + if (this.contextMenu === undefined) { + this.contextMenu = new AppContextMenu(this, this._menuLayout); + this.contextMenu.setApp(this._app); + this.contextMenu.centerBoxPointerPosition(); + } + + this.contextMenu.open(BoxPointer.PopupAnimation.FULL); + } + + activate(event) { + this.removeIndicator(); + + this._app.open_new_window(-1); + this._menuLayout.arcMenu.toggle(); + super.activate(event); + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/raven.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/raven.js new file mode 100644 index 0000000..4d16114 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/raven.js @@ -0,0 +1,342 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import {IconGrid} from '../iconGrid.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class RavenLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + const {settings} = menuButton.extension; + + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: settings.get_enum('raven-search-display-style'), + search_results_spacing: 4, + context_menu_location: Constants.ContextMenuLocation.BOTTOM_CENTERED, + column_spacing: 10, + row_spacing: 10, + default_menu_width: 415, + icon_grid_size: Constants.GridIconSize.SMALL, + vertical: false, + supports_category_hover_activation: true, + category_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + apps_icon_size: Constants.LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.arcMenu.box.style = 'padding: 0px; margin: 0px; border-radius: 0px;'; + this.searchEntry.style = 'margin: 10px 10px 10px 10px;'; + + this._settings.connectObject('changed::raven-position', () => this._updatePosition(), this); + + this._settings.connectObject('changed::enable-clock-widget-raven', () => this._updateWidgets(), this); + this._settings.connectObject('changed::enable-weather-widget-raven', () => this._updateWidgets(), this); + + this.updateLocation(); + + // store old ArcMenu variables + this.oldSourceActor = this.arcMenu.sourceActor; + this.oldFocusActor = this.arcMenu.focusActor; + this.oldArrowAlignment = this.arcMenu.actor._arrowAlignment; + + this.arcMenu.sourceActor = Main.layoutManager.dummyCursor; + this.arcMenu.focusActor = Main.layoutManager.dummyCursor; + this.arcMenu._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0); + this.arcMenu.close(); + this.arcMenu._boxPointer.hide(); + + const homeScreen = this._settings.get_boolean('enable-unity-homescreen'); + this.activeCategoryName = homeScreen ? _('Pinned') : _('All Programs'); + + this.actionsBoxContainer = new St.BoxLayout({ + x_expand: false, + y_expand: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + this.actionsBox = new St.BoxLayout({ + x_expand: false, + y_expand: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.CENTER, + vertical: true, + style: 'spacing: 5px;', + }); + this.actionsBoxContainer.add_child(this.actionsBox); + + this.topBox = new St.BoxLayout({ + x_expand: true, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + vertical: false, + }); + this.topBox.add_child(this.searchEntry); + + this._mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + this._mainBox.add_child(this.topBox); + this.add_child(this._mainBox); + + this.applicationsBox = new St.BoxLayout({ + x_align: Clutter.ActorAlign.FILL, + vertical: true, + style: 'padding-bottom: 10px;', + style_class: 'arcmenu-margin-box', + }); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: false, + y_expand: false, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this._mainBox.add_child(this.applicationsScrollBox); + + this._widgetBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.END, + vertical: true, + style: 'margin: 0px 10px 10px 10px; spacing: 10px;', + }); + this._mainBox.add_child(this._widgetBox); + + this.appShortcuts = []; + this.shortcutsBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.CENTER, + vertical: true, + }); + + this.shortcutsGrid = new IconGrid({ + halign: Clutter.ActorAlign.CENTER, + column_spacing: this.column_spacing, + row_spacing: this.row_spacing, + }); + this.shortcutsBox.add_child(this.shortcutsGrid); + + const applicationShortcuts = this._settings.get_value('application-shortcuts').deep_unpack(); + for (let i = 0; i < applicationShortcuts.length; i++) { + const shortcutMenuItem = this.createMenuItem(applicationShortcuts[i], Constants.DisplayType.GRID, false); + if (shortcutMenuItem.shouldShow) + this.appShortcuts.push(shortcutMenuItem); + else + shortcutMenuItem.destroy(); + } + + this._updateWidgets(); + this.updateLocation(); + this.updateWidth(); + this._updatePosition(); + this.loadCategories(); + this.loadPinnedApps(); + + this.setDefaultMenuView(); + } + + _updateWidgets() { + const clockWidgetEnabled = this._settings.get_boolean('enable-clock-widget-raven'); + const weatherWidgetEnabled = this._settings.get_boolean('enable-weather-widget-raven'); + + if (clockWidgetEnabled && !this._clocksItem) { + this._clocksItem = new MW.WorldClocksWidget(this); + this._clocksItem.set({ + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + }); + this._widgetBox.add_child(this._clocksItem); + } else if (!clockWidgetEnabled && this._clocksItem) { + this._clocksItem.destroy(); + this._clocksItem = null; + } + + if (weatherWidgetEnabled && !this._weatherItem) { + this._weatherItem = new MW.WeatherWidget(this); + this._widgetBox.add_child(this._weatherItem); + } else if (!weatherWidgetEnabled && this._weatherItem) { + this._weatherItem.destroy(); + this._weatherItem = null; + } + } + + _updatePosition() { + const style = `margin: 0px 0px 0px 0px; spacing: 10px; background-color: rgba(10, 10, 15, 0.1); + padding: 5px 5px; border-color: rgba(186, 196,201, 0.2);`; + + if (this.contains(this.actionsBoxContainer)) + this.remove_child(this.actionsBoxContainer); + + const ravenPosition = this._settings.get_enum('raven-position'); + if (ravenPosition === Constants.RavenPosition.LEFT) { + this.insert_child_at_index(this.actionsBoxContainer, 0); + this.actionsBoxContainer.style = `border-right-width: 1px;${style}`; + } else if (ravenPosition === Constants.RavenPosition.RIGHT) { + this.insert_child_at_index(this.actionsBoxContainer, 1); + this.actionsBoxContainer.style = `border-left-width: 1px;${style}`; + } + } + + updateLocation() { + const ravenPosition = this._settings.get_enum('raven-position'); + + const alignment = ravenPosition === Constants.RavenPosition.LEFT ? 0 : 1; + this.arcMenu._boxPointer.setSourceAlignment(alignment); + this.arcMenu._arrowAlignment = alignment; + + const monitorIndex = Main.layoutManager.findIndexForActor(this.menuButton); + const monitorWorkArea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex); + + const positionX = ravenPosition === Constants.RavenPosition.LEFT ? monitorWorkArea.x + : monitorWorkArea.x + monitorWorkArea.width - 1; + const positionY = this.arcMenu._arrowSide === St.Side.BOTTOM ? monitorWorkArea.y + monitorWorkArea.height + : monitorWorkArea.y; + + Main.layoutManager.setDummyCursorGeometry(positionX, positionY, 0, 0); + const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + const screenHeight = monitorWorkArea.height; + + const height = Math.round(screenHeight / scaleFactor); + const style = '-arrow-base: 0px; -arrow-rise: 0px; -boxpointer-gap: 0px;' + + '-arrow-border-radius: 0px; margin: 0px;'; + this.arcMenu.actor.style = `height: ${height}px;${style}`; + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + const homeScreen = this._settings.get_boolean('enable-unity-homescreen'); + if (homeScreen) { + this.activeCategoryName = _('Pinned'); + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + this.displayPinnedApps(); + const topCategory = this.categoryDirectories.values().next().value; + this.setActiveCategory(topCategory); + } else { + this.activeCategoryName = _('All Programs'); + const isGridLayout = true; + this.displayAllApps(isGridLayout); + this.activeCategoryType = Constants.CategoryType.ALL_PROGRAMS; + } + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + const categoryMenuItem = new MW.CategoryMenuItem(this, Constants.CategoryType.HOME_SCREEN, + Constants.DisplayType.BUTTON); + this.categoryDirectories.set(Constants.CategoryType.HOME_SCREEN, categoryMenuItem); + this.hasPinnedApps = true; + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (categoryEnum === Constants.CategoryType.PINNED_APPS || !shouldShow) + continue; + + const extraCategoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.BUTTON); + this.categoryDirectories.set(categoryEnum, extraCategoryMenuItem); + } + + super.loadCategories(Constants.DisplayType.BUTTON); + this.displayCategories(); + } + + displayCategories() { + for (const categoryMenuItem of this.categoryDirectories.values()) + this.actionsBox.add_child(categoryMenuItem); + } + + displayPinnedApps() { + this.activeCategoryName = _('Pinned'); + + super.displayPinnedApps(); + const label = this._createLabelWithSeparator(this.activeCategoryName); + this.applicationsBox.insert_child_at_index(label, 0); + + this.activeCategoryName = _('Shortcuts'); + this._displayAppList(this.appShortcuts, Constants.CategoryType.HOME_SCREEN, this.shortcutsGrid); + + if (!this.applicationsBox.contains(this.shortcutsBox)) + this.applicationsBox.add_child(this.shortcutsBox); + + this._widgetBox.hide(); + const clockWidgetEnabled = this._settings.get_boolean('enable-clock-widget-raven'); + const weatherWidgetEnabled = this._settings.get_boolean('enable-weather-widget-raven'); + + if (clockWidgetEnabled || weatherWidgetEnabled) + this._widgetBox.show(); + } + + displayRecentFiles() { + super.displayRecentFiles(); + const label = this._createLabelWithSeparator(_('Recent Files')); + label.style += 'padding-left: 10px;'; + this.applicationsBox.insert_child_at_index(label, 0); + this.activeCategoryType = Constants.CategoryType.RECENT_FILES; + } + + displayCategoryAppList(appList, category) { + this._clearActorsFromBox(); + this._displayAppList(appList, category, this.applicationsGrid); + } + + _clearActorsFromBox(box) { + this._widgetBox.hide(); + + super._clearActorsFromBox(box); + } + + _displayAppList(apps, category, grid) { + super._displayAppList(apps, category, grid); + const label = this._createLabelWithSeparator(this.activeCategoryName); + + if (grid === this.applicationsGrid) { + label.style += 'padding-left: 10px;'; + this.applicationsBox.insert_child_at_index(label, 0); + } else { + label.style += 'padding-left: 10px; padding-top: 20px;'; + this.applicationsBox.insert_child_at_index(label, 2); + } + } + + _onDestroy() { + if (this._clocksItem) + this._clocksItem.destroy(); + if (this._weatherItem) + this._weatherItem.destroy(); + + this.arcMenu.actor.style = null; + this.arcMenu.box.style = null; + this.arcMenu.sourceActor = this.oldSourceActor; + this.arcMenu.focusActor = this.oldFocusActor; + this.arcMenu._boxPointer.setPosition(this.oldSourceActor, this.oldArrowAlignment); + this.arcMenu.close(); + this.arcMenu._boxPointer.hide(); + + super._onDestroy(); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/redmond.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/redmond.js new file mode 100644 index 0000000..ef65f88 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/redmond.js @@ -0,0 +1,265 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; +import * as PlaceDisplay from '../placeDisplay.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class RedmondLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.GRID, + search_results_spacing: 4, + column_spacing: 10, + row_spacing: 10, + default_menu_width: 415, + icon_grid_size: Constants.GridIconSize.SMALL, + vertical: false, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.connect('button-press-event', (actor, event) => { + if (this.backButton.visible && event.get_button() === 8) + this.backButton.activate(event); + }); + + const mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + this.rightBox = new St.BoxLayout({ + y_align: Clutter.ActorAlign.FILL, + y_expand: true, + vertical: true, + }); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + this.add_child(horizontalFlip ? this.rightBox : mainBox); + this.add_child(verticalSeparator); + this.add_child(horizontalFlip ? mainBox : this.rightBox); + this.rightBox.style += horizontalFlip ? 'margin-right: 0px' : 'margin-left: 0px'; + + this.navBox = new St.BoxLayout({ + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + vertical: true, + style: 'padding-bottom: 5px;', + }); + mainBox.add_child(this.navBox); + + const defaultMenuView = this._settings.get_enum('default-menu-view-redmond'); + if (defaultMenuView === Constants.DefaultMenuViewRedmond.PINNED_APPS) { + this.backButton = this._createNavigationRow(_('All Apps'), Constants.Direction.GO_PREVIOUS, + _('Back'), () => this.setDefaultMenuView()); + this._viewAllAppsButton = this._createNavigationRow(_('Pinned'), Constants.Direction.GO_NEXT, + _('All Apps'), () => this.displayAllApps()); + } else if (defaultMenuView === Constants.DefaultMenuViewRedmond.ALL_PROGRAMS) { + this.backButton = this._createNavigationRow(_('Pinned'), Constants.Direction.GO_PREVIOUS, + _('Back'), () => this.setDefaultMenuView()); + this._viewAllAppsButton = this._createNavigationRow(_('All Apps'), Constants.Direction.GO_NEXT, + _('Pinned'), () => this.displayPinnedApps()); + } + + this.backButton.style = 'padding: 0px 10px;'; + this._viewAllAppsButton.style = 'padding: 0px 10px;'; + + this.navBox.add_child(this.backButton); + this.navBox.add_child(this._viewAllAppsButton); + + this.applicationsBox = new St.BoxLayout({ + vertical: true, + style: 'margin: 2px 0px;', + }); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + mainBox.add_child(this.applicationsScrollBox); + + const searchbarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchbarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.add_style_class_name('arcmenu-search-top'); + mainBox.insert_child_at_index(this.searchEntry, 0); + } else if (searchbarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + mainBox.add_child(this.searchEntry); + } + + const userAvatar = this._settings.get_boolean('disable-user-avatar'); + if (!userAvatar) { + const userMenuItem = new MW.UserMenuItem(this, Constants.DisplayType.LIST); + this.rightBox.add_child(userMenuItem); + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.SHORT, + Constants.SeparatorAlignment.HORIZONTAL); + this.rightBox.add_child(separator); + } + + this.shortcutsBox = new St.BoxLayout({vertical: true}); + this.shortcutsScrollBox = this._createScrollBox({ + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this._addChildToParent(this.shortcutsScrollBox, this.shortcutsBox); + this.rightBox.add_child(this.shortcutsScrollBox); + + // Add place shortcuts to menu (Home,Documents,Downloads,Music,Pictures,Videos) + this._displayPlaces(); + + const haveDirectoryShortcuts = this._settings.get_value('directory-shortcuts').deep_unpack().length > 0; + const haveApplicationShortcuts = this._settings.get_value('application-shortcuts').deep_unpack().length > 0; + + // check to see if should draw separator + const needsSeparator = haveDirectoryShortcuts && + (this._settings.get_boolean('show-external-devices') || haveApplicationShortcuts || + this._settings.get_boolean('show-bookmarks')); + if (needsSeparator) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.SHORT, + Constants.SeparatorAlignment.HORIZONTAL); + this.shortcutsBox.add_child(separator); + } + + // External Devices and Bookmarks Shortcuts + const externalDevicesBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + }); + this.shortcutsBox.add_child(externalDevicesBox); + + this._placesSections = {}; + this.placesManager = new PlaceDisplay.PlacesManager(); + for (let i = 0; i < Constants.SECTIONS.length; i++) { + const id = Constants.SECTIONS[i]; + this._placesSections[id] = new St.BoxLayout({vertical: true}); + this.placesManager.connectObject(`${id}-updated`, () => this._redisplayPlaces(id), this); + + this._createPlaces(id); + externalDevicesBox.add_child(this._placesSections[id]); + } + + const applicationShortcuts = this._settings.get_value('application-shortcuts').deep_unpack(); + for (let i = 0; i < applicationShortcuts.length; i++) { + const shortcutMenuItem = this.createMenuItem(applicationShortcuts[i], Constants.DisplayType.LIST, false); + if (shortcutMenuItem.shouldShow) + this.shortcutsBox.add_child(shortcutMenuItem); + else + shortcutMenuItem.destroy(); + } + + let powerOptionsDisplay; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.MENU) { + powerOptionsDisplay = new MW.LeaveButton(this, true); + } else { + powerOptionsDisplay = new MW.PowerOptionsBox(this); + powerOptionsDisplay.set({ + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + } + + powerOptionsDisplay.set({ + y_expand: true, + y_align: Clutter.ActorAlign.END, + }); + this.rightBox.add_child(powerOptionsDisplay); + + this.hasPinnedApps = true; + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + updateWidth(setDefaultMenuView) { + const rightPanelWidth = this._settings.get_int('right-panel-width'); + this.rightBox.style = `width: ${rightPanelWidth}px;`; + + const widthAdjustment = this._settings.get_int('menu-width-adjustment'); + let menuWidth = this.default_menu_width + widthAdjustment; + // Set a 300px minimum limit for the menu width + menuWidth = Math.max(300, menuWidth); + this.applicationsScrollBox.style = `width: ${menuWidth}px;`; + this.menu_width = menuWidth; + + if (setDefaultMenuView) + this.setDefaultMenuView(); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + + this.navBox.show(); + this._viewAllAppsButton.show(); + this.backButton.hide(); + + const defaultMenuView = this._settings.get_enum('default-menu-view-redmond'); + if (defaultMenuView === Constants.DefaultMenuViewRedmond.PINNED_APPS) + this.displayPinnedApps(); + else if (defaultMenuView === Constants.DefaultMenuViewRedmond.ALL_PROGRAMS) + this.displayAllApps(); + } + + displayPinnedApps() { + const defaultMenuView = this._settings.get_enum('default-menu-view-redmond'); + if (defaultMenuView === Constants.DefaultMenuViewRedmond.PINNED_APPS) { + this._viewAllAppsButton.show(); + this.backButton.hide(); + } else if (defaultMenuView === Constants.DefaultMenuViewRedmond.ALL_PROGRAMS) { + this._viewAllAppsButton.hide(); + this.backButton.show(); + } + super.displayPinnedApps(); + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + } + + displayAllApps() { + super.displayAllApps(); + + const defaultMenuView = this._settings.get_enum('default-menu-view-redmond'); + if (defaultMenuView === Constants.DefaultMenuViewRedmond.PINNED_APPS) { + this._viewAllAppsButton.hide(); + this.backButton.show(); + } else if (defaultMenuView === Constants.DefaultMenuViewRedmond.ALL_PROGRAMS) { + this._viewAllAppsButton.show(); + this.backButton.hide(); + } + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + super.loadCategories(); + } + + _onSearchEntryChanged(searchEntry, searchString) { + if (!searchEntry.isEmpty()) + this.navBox.hide(); + super._onSearchEntryChanged(searchEntry, searchString); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/runner.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/runner.js new file mode 100644 index 0000000..ab891fe --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/runner.js @@ -0,0 +1,242 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +const padding = 10; + +export const Layout = class RunnerLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton, isStandalone) { + const {settings} = menuButton.extension; + + let displayType, searchDisplayType, columnSpacing, rowSpacing, defaultMenuWidth, iconGridSize; + const searchDisplayStyle = settings.get_enum('runner-search-display-style'); + + if (searchDisplayStyle === Constants.DisplayType.LIST) { + displayType = Constants.DisplayType.LIST; + searchDisplayType = Constants.DisplayType.LIST; + columnSpacing = 0; + rowSpacing = 0; + defaultMenuWidth = null; + iconGridSize = null; + } else { + displayType = Constants.DisplayType.GRID; + searchDisplayType = Constants.DisplayType.GRID; + columnSpacing = 15; + rowSpacing = 15; + defaultMenuWidth = settings.get_int('runner-menu-width'); + iconGridSize = Constants.GridIconSize.LARGE; + } + + super(menuButton, { + has_search: true, + display_type: displayType, + search_display_type: searchDisplayType, + column_spacing: columnSpacing, + row_spacing: rowSpacing, + vertical: true, + default_menu_width: defaultMenuWidth, + icon_grid_size: iconGridSize, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + is_standalone_runner: !!isStandalone, + }); + + this.activeMenuItem = null; + + this.updateLocation(); + + // store old ArcMenu variables + this.oldSourceActor = this.arcMenu.sourceActor; + this.oldFocusActor = this.arcMenu.focusActor; + this.oldArrowAlignment = this.arcMenu.actor._arrowAlignment; + + this.arcMenu.sourceActor = Main.layoutManager.dummyCursor; + this.arcMenu.focusActor = Main.layoutManager.dummyCursor; + this.arcMenu._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0.5); + + this.topBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + vertical: false, + style: `margin: ${padding}px ${padding}px 0px 0px; spacing: ${padding}px;`, + }); + this.runnerTweaksButton = new RunnerTweaksButton(this); + this.runnerTweaksButton.set({ + x_expand: false, + y_expand: true, + y_align: this.searchEntry.y_align = Clutter.ActorAlign.CENTER, + x_align: Clutter.ActorAlign.CENTER, + }); + + this.topBox.add_child(this.searchEntry); + this.topBox.add_child(this.runnerTweaksButton); + this.add_child(this.topBox); + + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: false, + y_align: Clutter.ActorAlign.START, + x_align: Clutter.ActorAlign.FILL, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + style: `padding: ${padding}px 0px 0px 0px;`, + }); + + this.add_child(this.applicationsScrollBox); + this.applicationsBox = new St.BoxLayout({ + vertical: true, + style: `padding: 0px ${padding}px 0px 0px;`, + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + + this.setDefaultMenuView(); + this.updateWidth(); + } + + updateWidth(setDefaultMenuView) { + const width = this._settings.get_int('runner-menu-width') - padding; + this.menu_width = width; + if (setDefaultMenuView) + this.setDefaultMenuView(); + } + + setDefaultMenuView() { + this.activeMenuItem = null; + super.setDefaultMenuView(); + if (this._settings.get_boolean('runner-show-frequent-apps')) + this.displayFrequentApps(); + } + + displayFrequentApps() { + const mostUsed = Shell.AppUsage.get_default().get_most_used(); + if (mostUsed.length < 1) + return; + + const labelRow = this.createLabelRow(_('Frequent Apps')); + this.applicationsBox.add_child(labelRow); + + const frequentAppsList = []; + for (let i = 0; i < mostUsed.length; i++) { + if (mostUsed[i] && mostUsed[i].get_app_info().should_show()) { + const item = new MW.ApplicationMenuItem(this, mostUsed[i], Constants.DisplayType.LIST); + frequentAppsList.push(item); + } + } + let activeMenuItemSet = false; + const maxApps = Math.min(10, frequentAppsList.length); + for (let i = 0; i < maxApps; i++) { + const item = frequentAppsList[i]; + if (item.get_parent()) + item.get_parent().remove_child(item); + this.applicationsBox.add_child(item); + if (!activeMenuItemSet) { + activeMenuItemSet = true; + this.activeMenuItem = item; + } + } + } + + /** + * if button is hidden, menu should appear on current monitor, + * unless preference is to always show on primary monitor + * + * @returns index of monitor where menu should appear + */ + _getMonitorIndexForPlacement() { + if (this.is_standalone_runner) { + return this._settings.get_boolean('runner-hotkey-open-primary-monitor') + ? Main.layoutManager.primaryMonitor.index : Main.layoutManager.currentMonitor.index; + } else if (this._settings.get_enum('menu-button-appearance') === Constants.MenuButtonAppearance.NONE) { + return this._settings.get_boolean('hotkey-open-primary-monitor') + ? Main.layoutManager.primaryMonitor.index : Main.layoutManager.currentMonitor.index; + } else { + return Main.layoutManager.findIndexForActor(this.menuButton); + } + } + + updateLocation() { + this.arcMenu._boxPointer.setSourceAlignment(0.5); + this.arcMenu._arrowAlignment = 0.5; + + const runnerHeight = this._settings.get_int('runner-menu-height'); + const runnerWidth = this._settings.get_int('runner-menu-width'); + const runnerFontSize = this._settings.get_int('runner-font-size'); + + const rect = Main.layoutManager.getWorkAreaForMonitor(this._getMonitorIndexForPlacement()); + + // Position the runner menu in the center of the current monitor, at top of screen. + const positionX = Math.round(rect.x + (rect.width / 2)); + let positionY = rect.y; + if (this._settings.get_enum('runner-position') === 1) + positionY = Math.round(rect.y + (rect.height / 2) - (runnerHeight / 2)); + Main.layoutManager.setDummyCursorGeometry(positionX, positionY, 0, 0); + + if (!this.topBox) + return; + + this.style = `max-height: ${runnerHeight}px; margin: 0px 0px 0px ${padding}px; width: ${runnerWidth}px;`; + if (runnerFontSize > 0) { + this.style += `font-size: ${runnerFontSize}pt;`; + this.searchEntry.style += `font-size: ${runnerFontSize}pt;`; + } + this.updateWidth(); + } + + loadPinnedApps() { + + } + + loadCategories() { + } + + _onDestroy() { + this.arcMenu.sourceActor = this.oldSourceActor; + this.arcMenu.focusActor = this.oldFocusActor; + this.arcMenu._boxPointer.setPosition(this.oldSourceActor, this.oldArrowAlignment); + super._onDestroy(); + } +}; + +class RunnerTweaksButton extends MW.ArcMenuButtonItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super(menuLayout, _('Configure Runner'), 'emblem-system-symbolic'); + this.style_class = 'button arcmenu-button'; + this.tooltipLocation = Constants.TooltipLocation.BOTTOM_CENTERED; + } + + set active(active) { + if (this.isDestroyed) + return; + + const activeChanged = active !== this.active; + if (activeChanged) { + this._active = active; + this.notify('active'); + } + } + + activate(event) { + super.activate(event); + this._settings.set_int('prefs-visible-page', Constants.SettingsPage.RUNNER_TWEAKS); + this._extension.openPreferences(); + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/sleek.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/sleek.js new file mode 100644 index 0000000..2bb8a41 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/sleek.js @@ -0,0 +1,283 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class RedmondLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.LIST, + search_results_spacing: 8, + column_spacing: 10, + row_spacing: 10, + default_menu_width: 375, + icon_grid_size: Constants.GridIconSize.LARGE_RECT, + vertical: false, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.MEDIUM_ICON_SIZE, + buttons_icon_size: Constants.MEDIUM_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.connect('button-press-event', (actor, event) => { + if (this.backButton.visible && event.get_button() === 8) + this.backButton.activate(event); + }); + + this.arcMenu.box.style = 'padding: 0px; margin: 0px;'; + + const mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + style: 'padding: 6px;', + }); + + this.rightBox = new St.BoxLayout({ + y_align: Clutter.ActorAlign.FILL, + y_expand: true, + vertical: true, + clip_to_allocation: true, + }); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MAX, + Constants.SeparatorAlignment.VERTICAL); + + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + this.add_child(horizontalFlip ? this.rightBox : mainBox); + this.add_child(verticalSeparator); + this.add_child(horizontalFlip ? mainBox : this.rightBox); + + this.navBox = new St.BoxLayout({ + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + vertical: true, + style: 'padding: 8px 0px;', + }); + mainBox.add_child(this.navBox); + + this.backButton = this._createNavigationRow(_('All Apps'), Constants.Direction.GO_PREVIOUS, + _('Back'), () => this.setDefaultMenuView()); + this._viewAllAppsButton = this._createNavigationRow(_('Pinned'), Constants.Direction.GO_NEXT, + _('All Apps'), () => this.displayAllApps()); + + this.backButton.style = 'padding: 0px 10px;'; + this._viewAllAppsButton.style = 'padding: 0px 10px;'; + + this.navBox.add_child(this.backButton); + this.navBox.add_child(this._viewAllAppsButton); + + this.applicationsBox = new St.BoxLayout({ + vertical: true, + style: 'margin: 2px 0px;', + }); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + mainBox.add_child(this.applicationsScrollBox); + + const searchbarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchbarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.add_style_class_name('arcmenu-search-top'); + mainBox.insert_child_at_index(this.searchEntry, 0); + } else if (searchbarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + mainBox.add_child(this.searchEntry); + } + + const userMenuBox = new St.BoxLayout({ + x_expand: true, + y_expand: false, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.START, + vertical: true, + style: 'padding-bottom: 16px; spacing: 4px;', + }); + const userMenuIcon = new MW.UserMenuIcon(this, 75, true); + userMenuIcon.label.set({ + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + style: 'font-size: large;', + }); + userMenuBox.add_child(userMenuIcon); + userMenuBox.add_child(userMenuIcon.label); + this.rightBox.add_child(userMenuBox); + + this.shortcutsBox = new St.BoxLayout({ + vertical: true, + style: 'spacing: 8px;', + }); + this.shortcutsScrollBox = this._createScrollBox({ + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this._addChildToParent(this.shortcutsScrollBox, this.shortcutsBox); + this.rightBox.add_child(this.shortcutsScrollBox); + + this._settings.connectObject('changed::sleek-layout-extra-shortcuts', () => this._createExtraShortcuts(), this); + this._createExtraShortcuts(); + + let powerOptionsDisplay; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.IN_LINE) { + powerOptionsDisplay = new MW.PowerOptionsBox(this); + powerOptionsDisplay.set({ + x_expand: true, + x_align: Clutter.ActorAlign.CENTER, + }); + } else { + powerOptionsDisplay = new MW.LeaveButton(this); + } + + powerOptionsDisplay.set({ + y_expand: true, + y_align: Clutter.ActorAlign.END, + }); + this.rightBox.add_child(powerOptionsDisplay); + + this.hasPinnedApps = true; + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + loadCategories() { + this.setGridLayout(Constants.DisplayType.LIST, 8); + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + this.hasPinnedApps = true; + super.loadCategories(); + } + + loadPinnedApps() { + this.setGridLayout(Constants.DisplayType.GRID, 10, false); + super.loadPinnedApps(); + } + + _createExtraShortcuts() { + this.shortcutsBox.destroy_all_children(); + const extraShortcuts = this._settings.get_value('sleek-layout-extra-shortcuts').deep_unpack(); + + if (extraShortcuts.length === 0) + return; + + const isContainedInCategory = false; + + for (let i = 0; i < extraShortcuts.length; i++) { + const {id} = extraShortcuts[i]; + if (id === Constants.ShortcutCommands.SEPARATOR) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.SHORT, + Constants.SeparatorAlignment.HORIZONTAL); + this.shortcutsBox.add_child(separator); + } else { + const item = this.createMenuItem(extraShortcuts[i], Constants.DisplayType.LIST, isContainedInCategory); + if (item.shouldShow) + this.shortcutsBox.add_child(item); + else + item.destroy(); + } + } + } + + updateWidth(setDefaultMenuView) { + const widthAdjustment = this._settings.get_int('menu-width-adjustment'); + let menuWidth = this.default_menu_width + widthAdjustment; + // Set a 300px minimum limit for the menu width + menuWidth = Math.max(300, menuWidth); + this.applicationsScrollBox.style = `width: ${menuWidth}px;`; + this.menu_width = menuWidth; + + if (setDefaultMenuView) + this.setDefaultMenuView(); + } + + updateStyle() { + const themeNode = this.arcMenu.box.get_theme_node(); + let borderRadius = themeNode.get_length('border-radius'); + const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + borderRadius /= scaleFactor; + + const panelWidth = this._settings.get_int('sleek-layout-panel-width'); + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + const rightRoundedCorners = `border-radius: 0px ${borderRadius}px ${borderRadius}px 0px;`; + const leftRoundedCorners = `border-radius: ${borderRadius}px 0px 0px ${borderRadius}px;`; + const roundedCorners = horizontalFlip ? leftRoundedCorners : rightRoundedCorners; + + this.rightBox.style = `${roundedCorners} padding: 6px; + background-color: rgba(10, 10, 15, 0.2); width: ${panelWidth}px;`; + this.arcMenu.box.style = 'padding: 0px; margin: 0px;'; + } + + setDefaultMenuView() { + this.setGridLayout(Constants.DisplayType.GRID, 10); + super.setDefaultMenuView(); + + this.navBox.show(); + this._viewAllAppsButton.show(); + this.backButton.hide(); + + this.displayPinnedApps(); + } + + displayPinnedApps() { + this._viewAllAppsButton.show(); + this.backButton.hide(); + + super.displayPinnedApps(); + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + } + + displayAllApps() { + this.setGridLayout(Constants.DisplayType.LIST, 8); + super.displayAllApps(); + + this._viewAllAppsButton.hide(); + this.backButton.show(); + } + + setGridLayout(displayType, spacing, setStyle = true) { + if (setStyle) { + if (displayType === Constants.DisplayType.LIST) + this.applicationsScrollBox.style_class = this._disableFadeEffect ? '' : 'small-vfade'; + else + this.applicationsScrollBox.style_class = this._disableFadeEffect ? '' : 'vfade'; + this.applicationsGrid.halign = displayType === Constants.DisplayType.LIST ? Clutter.ActorAlign.FILL + : Clutter.ActorAlign.CENTER; + } + + this.applicationsGrid.column_spacing = spacing; + this.applicationsGrid.row_spacing = spacing; + this.display_type = displayType; + } + + _onSearchEntryChanged(searchEntry, searchString) { + if (!searchEntry.isEmpty()) + this.navBox.hide(); + super._onSearchEntryChanged(searchEntry, searchString); + } + + _onDestroy() { + this.arcMenu.box.style = null; + super._onDestroy(); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/tognee.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/tognee.js new file mode 100644 index 0000000..c244b18 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/tognee.js @@ -0,0 +1,240 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class TogneeLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.LIST, + search_display_type: Constants.DisplayType.LIST, + context_menu_location: Constants.ContextMenuLocation.RIGHT, + column_spacing: 0, + row_spacing: 0, + default_menu_width: 290, + vertical: true, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.connect('button-press-event', (actor, event) => { + if (this.backButton.visible && event.get_button() === 8) + this.backButton.activate(event); + }); + + this._mainBox = new St.BoxLayout({ + vertical: false, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + }); + this.add_child(this._mainBox); + + // Contains the app list and the searchbar + this.appBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + vertical: true, + y_align: Clutter.ActorAlign.FILL, + }); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.appBox.add_child(this.applicationsScrollBox); + this.applicationsBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + + this.navigateBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.END, + }); + this.backButton = new MW.BackButton(this); + this.navigateBox.add_child(new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL)); + this.navigateBox.add_child(this.backButton); + this.appBox.add_child(this.navigateBox); + + const searchbarLocation = this._settings.get_enum('searchbar-default-bottom-location'); + if (searchbarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.add_style_class_name('arcmenu-search-top'); + this.appBox.insert_child_at_index(this.searchEntry, 0); + } else if (searchbarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + this.appBox.add_child(this.searchEntry); + } + + // Contains shortcutsBox and power buttons + this.quickBox = new St.BoxLayout({ + vertical: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + style: 'spacing: 6px;', + }); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + this._mainBox.add_child(horizontalFlip ? this.appBox : this.quickBox); + this._mainBox.add_child(verticalSeparator); + this._mainBox.add_child(horizontalFlip ? this.quickBox : this.appBox); + + this.shortcutsBox = new St.BoxLayout({ + vertical: true, + x_expand: false, + y_expand: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.FILL, + style: 'spacing: 6px;', + }); + this.shortcutsScrollBox = this._createScrollBox({ + x_expand: false, + y_expand: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.FILL, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this.shortcutsScrollBox.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.EXTERNAL); + this._addChildToParent(this.shortcutsScrollBox, this.shortcutsBox); + this.quickBox.add_child(this.shortcutsScrollBox); + + this._displayPlaces(); + + const haveDirectoryShortcuts = this._settings.get_value('directory-shortcuts').deep_unpack().length > 0; + const haveApplicationShortcuts = this._settings.get_value('application-shortcuts').deep_unpack().length > 0; + if (haveDirectoryShortcuts && haveApplicationShortcuts) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.LONG, + Constants.SeparatorAlignment.HORIZONTAL); + this.shortcutsBox.add_child(separator); + } + + const applicationShortcuts = this._settings.get_value('application-shortcuts').deep_unpack(); + for (let i = 0; i < applicationShortcuts.length; i++) { + const shortcutMenuItem = this.createMenuItem(applicationShortcuts[i], Constants.DisplayType.BUTTON, false); + if (shortcutMenuItem.shouldShow) + this.shortcutsBox.add_child(shortcutMenuItem); + else + shortcutMenuItem.destroy(); + } + + let leaveButton; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.IN_LINE) + leaveButton = new MW.PowerOptionsBox(this, true); + else + leaveButton = new MW.LeaveButton(this); + + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.LONG, + Constants.SeparatorAlignment.HORIZONTAL); + this.quickBox.add_child(separator); + this.quickBox.add_child(leaveButton); + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + updateWidth(setDefaultMenuView) { + const widthAdjustment = this._settings.get_int('menu-width-adjustment'); + let menuWidth = this.default_menu_width + widthAdjustment; + // Set a 175px minimum limit for the menu width + menuWidth = Math.max(175, menuWidth); + this.applicationsScrollBox.style = `width: ${menuWidth}px;`; + this.menu_width = menuWidth; + if (setDefaultMenuView) + this.setDefaultMenuView(); + } + + _displayPlaces() { + const directoryShortcuts = this._settings.get_value('directory-shortcuts').deep_unpack(); + for (let i = 0; i < directoryShortcuts.length; i++) { + const directoryData = directoryShortcuts[i]; + const isContainedInCategory = false; + const placeMenuItem = this.createMenuItem(directoryData, Constants.DisplayType.BUTTON, isContainedInCategory); + this.shortcutsBox.add_child(placeMenuItem); + } + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (shouldShow) { + const categoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, categoryMenuItem); + } + } + + super.loadCategories(); + } + + displayPinnedApps() { + super.displayPinnedApps(); + this.activeCategoryType = Constants.CategoryType.PINNED_APPS; + this.navigateBox.show(); + } + + displayAllApps() { + this.navigateBox.hide(); + super.displayAllApps(); + } + + displayCategories() { + super.displayCategories(); + this.activeCategoryType = Constants.CategoryType.CATEGORIES_LIST; + this.navigateBox.hide(); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + + const defaultMenuView = this._settings.get_enum('default-menu-view-tognee'); + if (defaultMenuView === Constants.DefaultMenuViewTognee.CATEGORIES_LIST) + this.displayCategories(); + else if (defaultMenuView === Constants.DefaultMenuViewTognee.ALL_PROGRAMS) + this.displayAllApps(); + } + + displayCategoryAppList(appList, category) { + super.displayCategoryAppList(appList, category); + this.navigateBox.show(); + } + + displayRecentFiles() { + super.displayRecentFiles(); + this.activeCategoryType = Constants.CategoryType.RECENT_FILES; + this.navigateBox.show(); + } + + _onSearchEntryChanged(searchEntry, searchString) { + super._onSearchEntryChanged(searchEntry, searchString); + if (searchEntry.isEmpty()) { + this.navigateBox.hide(); + } else if (!searchEntry.isEmpty()) { + this.navigateBox.show(); + this.activeCategoryType = Constants.CategoryType.SEARCH_RESULTS; + } + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/unity.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/unity.js new file mode 100644 index 0000000..9f49800 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/unity.js @@ -0,0 +1,426 @@ +import Clutter from 'gi://Clutter'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import {IconGrid} from '../iconGrid.js'; +import * as MW from '../menuWidgets.js'; +import * as Utils from '../utils.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class UnityLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + display_type: Constants.DisplayType.GRID, + search_display_type: Constants.DisplayType.GRID, + search_results_spacing: 4, + context_menu_location: Constants.ContextMenuLocation.BOTTOM_CENTERED, + column_spacing: 15, + row_spacing: 15, + vertical: true, + default_menu_width: 750, + icon_grid_size: Constants.GridIconSize.LARGE, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.EXTRA_LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + const homeScreen = this._settings.get_boolean('enable-unity-homescreen'); + this.activeCategoryName = homeScreen ? _('Pinned') : _('All Programs'); + + this.topBox = new St.BoxLayout({ + x_expand: false, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + vertical: false, + style: 'padding-bottom: 10px; padding-right: 15px;', + }); + this.add_child(this.topBox); + + this._mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + this.add_child(this._mainBox); + + this.searchEntry.set({ + y_align: Clutter.ActorAlign.CENTER, + y_expand: true, + style: 'margin: 0px 15px 0px 15px;', + }); + this.topBox.add_child(this.searchEntry); + + this.categoriesButton = new CategoriesButton(this); + this.topBox.add_child(this.categoriesButton); + + this.applicationsBox = new St.BoxLayout({ + vertical: true, + style_class: 'arcmenu-margin-box', + y_align: Clutter.ActorAlign.START, + }); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this._mainBox.add_child(this.applicationsScrollBox); + + this._widgetBox = new St.BoxLayout({ + x_expand: false, + y_expand: false, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.END, + vertical: false, + style_class: 'datemenu-displays-box', + style: 'margin: 0px; spacing: 10px; padding-bottom: 6px;', + }); + this._mainBox.add_child(this._widgetBox); + this._widgetBox.hide(); + + this.appShortcuts = []; + this.shortcutsBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.CENTER, + vertical: true, + }); + + this.shortcutsGrid = new IconGrid({ + halign: Clutter.ActorAlign.CENTER, + column_spacing: this.column_spacing, + row_spacing: this.row_spacing, + }); + this.shortcutsBox.add_child(this.shortcutsGrid); + this.shortcutsBox.add_child(this.shortcutsGrid); + + this.actionsContainerBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.END, + vertical: false, + }); + this._mainBox.add_child(this.actionsContainerBox); + + this.actionsBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + vertical: false, + style: 'spacing: 10px;', + }); + this.actionsContainerBox.add_child(this.actionsBox); + + const applicationShortcuts = this._settings.get_value('application-shortcuts').deep_unpack(); + for (let i = 0; i < applicationShortcuts.length; i++) { + const shortcutMenuItem = this.createMenuItem(applicationShortcuts[i], Constants.DisplayType.GRID, false); + if (shortcutMenuItem.shouldShow) + this.appShortcuts.push(shortcutMenuItem); + else + shortcutMenuItem.destroy(); + } + + this._settings.connectObject('changed::unity-layout-extra-shortcuts', () => this._createExtraButtons(), this); + this._settings.connectObject('changed::enable-clock-widget-unity', () => this._updateWidgets(), this); + this._settings.connectObject('changed::enable-weather-widget-unity', () => this._updateWidgets(), this); + + this._createExtraButtons(); + this._updateWidgets(); + + this.updateStyle(); + this.updateWidth(); + this._createCategoriesMenu(); + this.loadCategories(); + this.loadPinnedApps(); + + this.setDefaultMenuView(); + } + + _updateWidgets() { + const clockWidgetEnabled = this._settings.get_boolean('enable-clock-widget-unity'); + const weatherWidgetEnabled = this._settings.get_boolean('enable-weather-widget-unity'); + + if (clockWidgetEnabled && !this._clocksItem) { + this._clocksItem = new MW.WorldClocksWidget(this); + this._widgetBox.add_child(this._clocksItem); + } else if (!clockWidgetEnabled && this._clocksItem) { + this._clocksItem.destroy(); + this._clocksItem = null; + } + + if (weatherWidgetEnabled && !this._weatherItem) { + this._weatherItem = new MW.WeatherWidget(this); + this._widgetBox.add_child(this._weatherItem); + } else if (!weatherWidgetEnabled && this._weatherItem) { + this._weatherItem.destroy(); + this._weatherItem = null; + } + } + + _createExtraButtons() { + this.actionsBox.destroy_all_children(); + const extraButtons = this._settings.get_value('unity-layout-extra-shortcuts').deep_unpack(); + + if (extraButtons.length === 0) + return; + + const isContainedInCategory = false; + for (let i = 0; i < extraButtons.length; i++) { + const {id} = extraButtons[i]; + if (id === Constants.ShortcutCommands.SEPARATOR) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.LONG, + Constants.SeparatorAlignment.VERTICAL); + this.actionsBox.add_child(separator); + } else { + const item = this.createMenuItem(extraButtons[i], Constants.DisplayType.BUTTON, isContainedInCategory); + if (item.shouldShow) + this.actionsBox.add_child(item); + else + item.destroy(); + } + } + } + + updateWidth(setDefaultMenuView) { + super.updateWidth(setDefaultMenuView); + + if (!this.applicationsBox.get_stage()) + return; + + const width = this.menu_width - 80; + this._weatherItem.style = `width: ${Math.round(5 * width / 8)}px;`; + this._clocksItem.style = `width: ${Math.round(3 * width / 8)}px;`; + } + + _createCategoriesMenu() { + this.categoriesMenu = new PopupMenu.PopupMenu(this.categoriesButton, 0.5, St.Side.TOP); + this.categoriesMenu.actor.add_style_class_name('popup-menu arcmenu-menu'); + this.categoriesMenu.blockSourceEvents = true; + this.categoriesMenu.connect('open-state-changed', (menu, open) => { + if (open) { + this.categoriesButton.add_style_pseudo_class('active'); + if (this.menuButton.tooltipShowingID) { + GLib.source_remove(this.menuButton.tooltipShowingID); + this.menuButton.tooltipShowingID = null; + this.menuButton.tooltipShowing = false; + } + if (this.categoriesButton.tooltip) { + this.categoriesButton.tooltip.hide(); + this.menuButton.tooltipShowing = false; + } + } else { + this.categoriesButton.remove_style_pseudo_class('active'); + this.categoriesButton.active = false; + this.categoriesButton.sync_hover(); + this.categoriesButton.hovered = this.categoriesButton.hover; + } + }); + const section = new PopupMenu.PopupMenuSection(); + this.categoriesMenu.addMenuItem(section); + + const categoriesPopupBox = new St.BoxLayout({vertical: true}); + section.actor.add_child(categoriesPopupBox); + categoriesPopupBox._delegate = categoriesPopupBox; + + this.categoriesScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + categoriesPopupBox.add_child(this.categoriesScrollBox); + + this.categoriesBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.categoriesScrollBox, this.categoriesBox); + + const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + const height = Math.round(350 / scaleFactor); + + categoriesPopupBox.style = `max-height: ${height}px`; + + this.subMenuManager.addMenu(this.categoriesMenu); + this.categoriesMenu.actor.hide(); + Main.uiGroup.add_child(this.categoriesMenu.actor); + } + + toggleCategoriesMenu() { + const {vadjustment} = Utils.getScrollViewAdjustments(this.categoriesScrollBox); + vadjustment.set_value(0); + + this.categoriesMenu.toggle(); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + const homeScreen = this._settings.get_boolean('enable-unity-homescreen'); + if (homeScreen) { + this.activeCategoryName = _('Pinned'); + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + this.displayPinnedApps(); + } else { + this.activeCategoryName = _('All Programs'); + const isGridLayout = true; + this.displayAllApps(isGridLayout); + this.activeCategoryType = Constants.CategoryType.ALL_PROGRAMS; + } + } + + updateStyle() { + const themeNode = this.arcMenu.box.get_theme_node(); + let borderRadius = themeNode.get_length('border-radius'); + + const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; + borderRadius /= scaleFactor; + + const borderRadiusStyle = `border-radius: 0px 0px ${borderRadius}px ${borderRadius}px;`; + const style = `margin: 6px 0px 0px 0px; spacing: 0px; background-color: rgba(10, 10, 15, 0.1); padding: 5px 5px; + border-color: rgba(186, 196,201, 0.2); border-top-width: 1px;`; + + this.actionsContainerBox.style = style + borderRadiusStyle; + this.arcMenu.box.style = 'padding-bottom: 0px; padding-left: 0px; padding-right: 0px;'; + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + const categoryMenuItem = new MW.CategoryMenuItem(this, Constants.CategoryType.HOME_SCREEN, + Constants.DisplayType.LIST); + this.categoryDirectories.set(Constants.CategoryType.HOME_SCREEN, categoryMenuItem); + this.hasPinnedApps = true; + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (categoryEnum === Constants.CategoryType.PINNED_APPS || !shouldShow) + continue; + + const extraCategoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, + Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, extraCategoryMenuItem); + } + + super.loadCategories(); + this._displayCategories(); + } + + _displayCategories() { + this.categoriesBox.destroy_all_children(); + let hasExtraCategory = false; + let separatorAdded = false; + + for (const categoryMenuItem of this.categoryDirectories.values()) { + const isExtraCategory = categoryMenuItem.isExtraCategory(); + + if (!hasExtraCategory) { + hasExtraCategory = isExtraCategory; + } else if (!isExtraCategory && !separatorAdded) { + this.categoriesBox.add_child(new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL)); + separatorAdded = true; + } + + this.categoriesBox.add_child(categoryMenuItem); + } + } + + displayPinnedApps() { + this.activeCategoryName = _('Pinned'); + super.displayPinnedApps(); + const label = this._createLabelWithSeparator(this.activeCategoryName); + this.applicationsBox.insert_child_at_index(label, 0); + + this.activeCategoryName = _('Shortcuts'); + this._displayAppList(this.appShortcuts, Constants.CategoryType.HOME_SCREEN, this.shortcutsGrid); + + if (!this.applicationsBox.contains(this.shortcutsBox)) + this.applicationsBox.add_child(this.shortcutsBox); + + this._widgetBox.hide(); + + const clockWidgetEnabled = this._settings.get_boolean('enable-clock-widget-unity'); + const weatherWidgetEnabled = this._settings.get_boolean('enable-weather-widget-unity'); + + if (clockWidgetEnabled || weatherWidgetEnabled) + this._widgetBox.show(); + } + + displayRecentFiles() { + super.displayRecentFiles(); + const label = this._createLabelWithSeparator(_('Recent Files')); + this.applicationsBox.insert_child_at_index(label, 0); + this.activeCategoryType = Constants.CategoryType.RECENT_FILES; + } + + displayCategoryAppList(appList, category) { + this._clearActorsFromBox(); + this._displayAppList(appList, category, this.applicationsGrid); + } + + _clearActorsFromBox(box) { + if (this.categoriesMenu.isOpen) + this.categoriesMenu.toggle(); + + this._widgetBox.hide(); + + super._clearActorsFromBox(box); + } + + _displayAppList(apps, category, grid) { + super._displayAppList(apps, category, grid); + + const label = this._createLabelWithSeparator(this.activeCategoryName); + if (grid === this.applicationsGrid) + this.applicationsBox.insert_child_at_index(label, 0); + else if (grid === this.shortcutsGrid) + this.applicationsBox.insert_child_at_index(label, 2); + } + + _onDestroy() { + if (this._clocksItem) + this._clocksItem.destroy(); + if (this._weatherItem) + this._weatherItem.destroy(); + + this.arcMenu.box.style = null; + + super._onDestroy(); + } +}; + +export class CategoriesButton extends MW.ArcMenuButtonItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super(menuLayout, _('Categories'), 'open-menu-symbolic'); + this.toggleMenuOnClick = false; + } + + activate(event) { + super.activate(event); + this._menuLayout.toggleCategoriesMenu(); + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/whisker.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/whisker.js new file mode 100644 index 0000000..2748368 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/whisker.js @@ -0,0 +1,169 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class WhiskerLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + is_dual_panel: true, + display_type: Constants.DisplayType.LIST, + search_display_type: Constants.DisplayType.LIST, + context_menu_location: Constants.ContextMenuLocation.BOTTOM_CENTERED, + column_spacing: 0, + row_spacing: 0, + supports_category_hover_activation: true, + vertical: true, + category_icon_size: Constants.MEDIUM_ICON_SIZE, + apps_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.MEDIUM_ICON_SIZE, + }); + + this.actionsBox = new St.BoxLayout({ + x_expand: true, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + vertical: false, + style: 'spacing: 6px; margin: 0px 10px;', + }); + this.add_child(this.actionsBox); + + const userMenuItem = new MW.UserMenuItem(this, Constants.DisplayType.LIST); + userMenuItem.set({ + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + }); + this.actionsBox.add_child(userMenuItem); + + const settingsButton = this.createMenuItem({'id': 'org.gnome.Settings.desktop'}, + Constants.DisplayType.BUTTON, false); + if (settingsButton.shouldShow) + this.actionsBox.add_child(settingsButton); + else + settingsButton.destroy(); + + let powerOptionsBox; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.MENU) + powerOptionsBox = new MW.LeaveButton(this); + else + powerOptionsBox = new MW.PowerOptionsBox(this); + this.actionsBox.add_child(powerOptionsBox); + + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + this.add_child(separator); + + this._mainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: false, + }); + this.add_child(this._mainBox); + + this.rightBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + this.applicationsBox = new St.BoxLayout({vertical: true}); + this.applicationsScrollBox = this._createScrollBox({ + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this.rightBox.add_child(this.applicationsScrollBox); + + this.leftBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + }); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + + const horizontalFlip = this._settings.get_boolean('enable-horizontal-flip'); + this._mainBox.add_child(horizontalFlip ? this.rightBox : this.leftBox); + this._mainBox.add_child(verticalSeparator); + this._mainBox.add_child(horizontalFlip ? this.leftBox : this.rightBox); + + this.categoriesScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + + this.leftBox.add_child(this.categoriesScrollBox); + this.categoriesBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.categoriesScrollBox, this.categoriesBox); + + const searchbarLocation = this._settings.get_enum('searchbar-default-top-location'); + if (searchbarLocation === Constants.SearchbarLocation.TOP) { + this.searchEntry.add_style_class_name('arcmenu-search-top'); + this.searchEntry.style = 'margin-top: 0px; margin-bottom: 6px;'; + this.insert_child_at_index(this.searchEntry, 0); + } else if (searchbarLocation === Constants.SearchbarLocation.BOTTOM) { + this.searchEntry.add_style_class_name('arcmenu-search-bottom'); + this.add_child(this.searchEntry); + } + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + this.setDefaultMenuView(); + } + + updateWidth(setDefaultMenuView) { + const leftPanelWidthOffset = 0; + const rightPanelWidthOffset = 45; + super.updateWidth(setDefaultMenuView, leftPanelWidthOffset, rightPanelWidthOffset); + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + this.displayCategories(); + + const topCategory = this.categoryDirectories.values().next().value; + topCategory.displayAppList(); + this.setActiveCategory(topCategory); + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + + const extraCategories = this._settings.get_value('extra-categories').deep_unpack(); + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + if (shouldShow) { + const categoryMenuItem = new MW.CategoryMenuItem(this, categoryEnum, Constants.DisplayType.LIST); + this.categoryDirectories.set(categoryEnum, categoryMenuItem); + } + } + + super.loadCategories(); + } + + displayCategories() { + super.displayCategories(this.categoriesBox); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/windows.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/windows.js new file mode 100644 index 0000000..3855ba2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/menulayouts/windows.js @@ -0,0 +1,439 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; + +import {BaseMenuLayout} from './baseMenuLayout.js'; +import * as Constants from '../constants.js'; +import * as MW from '../menuWidgets.js'; +import * as PlaceDisplay from '../placeDisplay.js'; +import * as Utils from '../utils.js'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +export const Layout = class WindowsLayout extends BaseMenuLayout { + static { + GObject.registerClass(this); + } + + constructor(menuButton) { + super(menuButton, { + has_search: true, + search_display_type: Constants.DisplayType.LIST, + display_type: Constants.DisplayType.LIST, + context_menu_location: Constants.ContextMenuLocation.RIGHT, + column_spacing: 0, + row_spacing: 0, + default_menu_width: 315, + icon_grid_size: Constants.GridIconSize.SMALL, + vertical: false, + category_icon_size: Constants.LARGE_ICON_SIZE, + apps_icon_size: Constants.LARGE_ICON_SIZE, + quicklinks_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + buttons_icon_size: Constants.EXTRA_SMALL_ICON_SIZE, + pinned_apps_icon_size: Constants.LARGE_ICON_SIZE, + }); + + this._pinnedAppsGrid.layout_manager.set({ + column_spacing: 10, + row_spacing: 10, + halign: Clutter.ActorAlign.CENTER, + }); + + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + + this.actionsBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + style: 'spacing: 6px;', + }); + this.add_child(this.actionsBox); + + const verticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + this.add_child(verticalSeparator); + + this.subMainBox = new St.BoxLayout({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.FILL, + vertical: true, + style: 'spacing: 6px;', + }); + this.add_child(this.subMainBox); + + this.pinnedAppsScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'vfade', + }); + this.pinnedAppsBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + }); + this._addChildToParent(this.pinnedAppsScrollBox, this.pinnedAppsBox); + + this.pinnedAppsVerticalSeparator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.VERTICAL); + + this.applicationsBox = new St.BoxLayout({vertical: true}); + this.applicationsScrollBox = this._createScrollBox({ + x_expand: false, + y_expand: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + this._addChildToParent(this.applicationsScrollBox, this.applicationsBox); + this.subMainBox.add_child(this.applicationsScrollBox); + + this.subMainBox.add_child(this.searchEntry); + + const applicationShortcutsList = this._settings.get_value('application-shortcuts').deep_unpack(); + this.applicationShortcuts = []; + for (let i = 0; i < applicationShortcutsList.length; i++) { + const shortcutMenuItem = this.createMenuItem(applicationShortcutsList[i], + Constants.DisplayType.LIST, false); + if (shortcutMenuItem.shouldShow) + this.applicationShortcuts.push(shortcutMenuItem); + else + shortcutMenuItem.destroy(); + } + + const directoryShortcutsList = this._settings.get_value('directory-shortcuts').deep_unpack(); + this._loadPlaces(directoryShortcutsList); + + this.externalDevicesBox = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + }); + this._placesSections = {}; + this.placesManager = new PlaceDisplay.PlacesManager(); + for (let i = 0; i < Constants.SECTIONS.length; i++) { + const id = Constants.SECTIONS[i]; + this._placesSections[id] = new St.BoxLayout({vertical: true}); + this.placesManager.connectObject(`${id}-updated`, () => this._redisplayPlaces(id), this); + + this._createPlaces(id); + this.externalDevicesBox.add_child(this._placesSections[id]); + } + + this._settings.connectObject('changed::windows-layout-extra-shortcuts', () => this._createExtraButtons(), this); + this._createExtraButtons(); + + this.updateWidth(); + this.loadCategories(); + this.loadPinnedApps(); + + this._createExtrasMenu(); + this.setDefaultMenuView(); + } + + _createExtraButtons() { + this.actionsBox.destroy_all_children(); + + this.extrasButton = new ExtrasButton(this); + this.extrasButton.set({ + y_expand: true, + y_align: Clutter.ActorAlign.START, + }); + this.actionsBox.add_child(this.extrasButton); + + const isContainedInCategory = false; + + const extraButtons = this._settings.get_value('windows-layout-extra-shortcuts').deep_unpack(); + for (let i = 0; i < extraButtons.length; i++) { + const {id} = extraButtons[i]; + if (id === Constants.ShortcutCommands.SEPARATOR) { + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.LONG, + Constants.SeparatorAlignment.HORIZONTAL); + this.actionsBox.add_child(separator); + } else { + const button = this.createMenuItem(extraButtons[i], Constants.DisplayType.BUTTON, + isContainedInCategory); + if (button.shouldShow) + this.actionsBox.add_child(button); + else + button.destroy(); + } + } + + let leaveButton; + const powerDisplayStyle = this._settings.get_enum('power-display-style'); + if (powerDisplayStyle === Constants.PowerDisplayStyle.IN_LINE) + leaveButton = new MW.PowerOptionsBox(this, true); + else + leaveButton = new MW.LeaveButton(this); + + this.actionsBox.add_child(leaveButton); + } + + updateWidth(setDefaultMenuView) { + const leftPanelWidth = this._settings.get_int('left-panel-width'); + this.applicationsScrollBox.style = `width: ${leftPanelWidth}px;`; + + const widthAdjustment = this._settings.get_int('menu-width-adjustment'); + let menuWidth = this.default_menu_width + widthAdjustment; + // Set a 300px minimum limit for the menu width + menuWidth = Math.max(300, menuWidth); + this.pinnedAppsScrollBox.style = `width: ${menuWidth}px;`; + this.menu_width = menuWidth; + + if (setDefaultMenuView) + this.setDefaultMenuView(); + } + + loadPinnedApps() { + this.display_type = Constants.DisplayType.GRID; + super.loadPinnedApps(); + this.display_type = Constants.DisplayType.LIST; + } + + _createPlaces(id) { + const places = this.placesManager.get(id); + + if (places.length === 0) + return; + else if (id === 'bookmarks') + this._placesSections[id].add_child(this.createLabelRow(_('Bookmarks'))); + else if (id === 'devices') + this._placesSections[id].add_child(this.createLabelRow(_('Devices'))); + else if (id === 'network') + this._placesSections[id].add_child(this.createLabelRow(_('Network'))); + else + return; + + for (let i = 0; i < places.length; i++) { + const item = new MW.PlaceMenuItem(this, places[i], Constants.DisplayType.LIST); + this._placesSections[id].add_child(item); + } + } + + _loadPlaces(directoryShortcutsList) { + this.directoryShortcuts = []; + for (let i = 0; i < directoryShortcutsList.length; i++) { + const directoryData = directoryShortcutsList[i]; + const isContainedInCategory = false; + const placeMenuItem = this.createMenuItem(directoryData, Constants.DisplayType.LIST, isContainedInCategory); + this.directoryShortcuts.push(placeMenuItem); + } + } + + _createExtrasMenu() { + this.extrasMenu = new PopupMenu.PopupMenu(Main.layoutManager.dummyCursor, 0, St.Side.TOP); + this.extrasMenu.actor.add_style_class_name('popup-menu arcmenu-menu'); + + const section = new PopupMenu.PopupMenuSection(); + this.extrasMenu.addMenuItem(section); + + const extrasMenuPopupBox = new St.BoxLayout({vertical: true}); + extrasMenuPopupBox._delegate = extrasMenuPopupBox; + section.actor.add_child(extrasMenuPopupBox); + + const headerBox = new St.BoxLayout({ + x_expand: false, + y_expand: false, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.START, + vertical: true, + }); + extrasMenuPopupBox.add_child(headerBox); + + this.backButton = new MW.BackButton(this); + this.backButton.connect('activate', () => this.toggleExtrasMenu()); + headerBox.add_child(this.backButton); + + const separator = new MW.ArcMenuSeparator(this, Constants.SeparatorStyle.MEDIUM, + Constants.SeparatorAlignment.HORIZONTAL); + headerBox.add_child(separator); + + this.computerScrollBox = this._createScrollBox({ + x_expand: true, + y_expand: true, + y_align: Clutter.ActorAlign.START, + style_class: this._disableFadeEffect ? '' : 'small-vfade', + }); + + extrasMenuPopupBox.add_child(this.computerScrollBox); + + const computerBox = new St.BoxLayout({vertical: true}); + this._addChildToParent(this.computerScrollBox, computerBox); + + computerBox.add_child(this.createLabelRow(_('Application Shortcuts'))); + for (let i = 0; i < this.applicationShortcuts.length; i++) + computerBox.add_child(this.applicationShortcuts[i]); + + computerBox.add_child(this.createLabelRow(_('Places'))); + for (let i = 0; i < this.directoryShortcuts.length; i++) + computerBox.add_child(this.directoryShortcuts[i]); + + computerBox.add_child(this.externalDevicesBox); + + this.subMenuManager.addMenu(this.extrasMenu); + this.extrasMenu.actor.hide(); + Main.uiGroup.add_child(this.extrasMenu.actor); + this.extrasMenu.connect('open-state-changed', (menu, open) => { + if (!open) { + this.extrasButton.active = false; + this.extrasButton.sync_hover(); + this.extrasButton.hovered = this.extrasButton.hover; + } + }); + } + + toggleExtrasMenu() { + const {vadjustment} = Utils.getScrollViewAdjustments(this.computerScrollBox); + vadjustment.set_value(0); + + const themeNode = this.arcMenu.actor.get_theme_node(); + + let [x, y] = this.arcMenu.actor.get_transformed_position(); + const rise = themeNode.get_length('-arrow-rise'); + + if (this.arcMenu._arrowSide !== St.Side.TOP) + y -= rise; + if (this.arcMenu._arrowSide === St.Side.LEFT) + x += rise; + + Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0); + + const height = this._settings.get_int('menu-height'); + this.extrasMenu.box.style = `height: ${height}px;`; + + this.extrasMenu.toggle(); + if (this.extrasMenu.isOpen) { + this.activeMenuItem = this.backButton; + this.backButton.grab_key_focus(); + } + } + + setDefaultMenuView() { + super.setDefaultMenuView(); + + this.displayAllApps(); + if (!this._settings.get_boolean('windows-disable-pinned-apps')) + this.displayPinnedApps(); + + const {vadjustment} = Utils.getScrollViewAdjustments(this.pinnedAppsScrollBox); + vadjustment.set_value(0); + } + + displayFrequentApps() { + this._firstFrequentApp = null; + const mostUsed = Shell.AppUsage.get_default().get_most_used(); + if (mostUsed.length < 1) + return; + + const labelRow = this._createLabelWithSeparator(_('Frequent Apps')); + this.applicationsBox.add_child(labelRow); + + const frequentAppsList = []; + for (let i = 0; i < mostUsed.length; i++) { + if (mostUsed[i] && mostUsed[i].get_app_info().should_show()) { + const item = new MW.ApplicationMenuItem(this, mostUsed[i], Constants.DisplayType.LIST); + frequentAppsList.push(item); + } + } + let activeMenuItemSet = false; + const maxApps = Math.min(8, frequentAppsList.length); + for (let i = 0; i < maxApps; i++) { + const item = frequentAppsList[i]; + if (item.get_parent()) + item.get_parent().remove_child(item); + this.applicationsBox.add_child(item); + if (!activeMenuItemSet) { + activeMenuItemSet = true; + this.activeMenuItem = item; + this._firstFrequentApp = item; + } + } + } + + displayAllApps() { + this._clearActorsFromBox(); + + if (!this._settings.get_boolean('windows-disable-frequent-apps')) + this.displayFrequentApps(); + + const appList = []; + this.applicationsMap.forEach((value, key) => { + appList.push(key); + }); + appList.sort((a, b) => { + return a.get_name().toLowerCase() > b.get_name().toLowerCase(); + }); + this.display_type = Constants.DisplayType.LIST; + this._displayAppList(appList, Constants.CategoryType.ALL_PROGRAMS, this.applicationsGrid); + + if (this._firstFrequentApp) + this.activeMenuItem = this._firstFrequentApp; + } + + loadCategories() { + this.categoryDirectories = null; + this.categoryDirectories = new Map(); + this.hasPinnedApps = true; + super.loadCategories(); + } + + _clearActorsFromBox(box) { + super._clearActorsFromBox(box); + this.activeCategoryType = Constants.CategoryType.HOME_SCREEN; + } + + displayPinnedApps() { + super._clearActorsFromBox(this.pinnedAppsBox); + + const pinnedApps = this._settings.get_value('pinned-apps').deepUnpack(); + + if (pinnedApps.length < 1) { + if (this.contains(this.pinnedAppsScrollBox)) { + this.remove_child(this.pinnedAppsVerticalSeparator); + this.remove_child(this.pinnedAppsScrollBox); + } + + return; + } + + if (!this.contains(this.pinnedAppsScrollBox)) { + this.add_child(this.pinnedAppsVerticalSeparator); + this.add_child(this.pinnedAppsScrollBox); + } + + const label = this.createLabelRow(_('Pinned')); + this.pinnedAppsBox.add_child(label); + + const iconWidth = this.getIconWidthFromSetting(); + const columns = this.getBestFitColumnsForGrid(iconWidth, this._pinnedAppsGrid); + + this._pinnedAppsGrid.setColumns(columns); + + if (!this.pinnedAppsBox.contains(this._pinnedAppsGrid)) + this.pinnedAppsBox.add_child(this._pinnedAppsGrid); + } +}; + +export class ExtrasButton extends MW.ArcMenuButtonItem { + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super(menuLayout, _('Extras'), 'open-menu-symbolic'); + this.toggleMenuOnClick = false; + } + + activate(event) { + super.activate(event); + this._menuLayout.toggleExtrasMenu(); + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/metadata.json b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/metadata.json new file mode 100644 index 0000000..0000ca3 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/metadata.json @@ -0,0 +1,19 @@ +{ + "_generated": "Generated by SweetTooth, do not edit", + "description": "Application Menu Extension for GNOME", + "donations": { + "paypal": "azaech" + }, + "gettext-domain": "arcmenu", + "name": "ArcMenu", + "settings-schema": "org.gnome.shell.extensions.arcmenu", + "shell-version": [ + "45", + "46", + "47" + ], + "url": "https://gitlab.com/arcmenu/ArcMenu", + "uuid": "arcmenu@arcmenu.com", + "version": 61, + "version-name": "61" +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/placeDisplay.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/placeDisplay.js new file mode 100644 index 0000000..9d33b3e --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/placeDisplay.js @@ -0,0 +1,542 @@ +/* + * Credits: this file is a copy of GNOME's 'placeDisplay.js' file from the 'Places Status Indicator' extension. + * https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/blob/main/extensions/places-menu/placeDisplay.js + */ + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import Shell from 'gi://Shell'; +import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as ShellMountOperation from 'resource:///org/gnome/shell/ui/shellMountOperation.js'; + +Gio._promisify(Gio.AppInfo, 'launch_default_for_uri_async'); +Gio._promisify(Gio.File.prototype, 'mount_enclosing_volume'); + +const BACKGROUND_SCHEMA = 'org.gnome.desktop.background'; + +const Hostname1Iface = ' \ + \ + \ + \ +'; +const Hostname1 = Gio.DBusProxy.makeProxyWrapper(Hostname1Iface); + +export class PlaceInfo extends EventEmitter { + constructor(...params) { + super(); + + this._init(...params); + } + + _init(kind, file, name, icon) { + this.kind = kind; + this.file = file; + this.name = name || this._getFileName(); + this.icon = icon ? new Gio.ThemedIcon({name: icon}) : this.getIcon(); + } + + destroy() { + } + + isRemovable() { + return false; + } + + async _ensureMountAndLaunch(context, tryMount) { + try { + await Gio.AppInfo.launch_default_for_uri_async(this.file.get_uri(), context, null); + } catch (err) { + if (!err.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED)) { + Main.notifyError(_('Failed to launch “%s”').format(this.name), err.message); + return; + } + + const source = { + get_icon: () => this.icon, + }; + const op = new ShellMountOperation.ShellMountOperation(source); + try { + await this.file.mount_enclosing_volume(0, op.mountOp, null); + + if (tryMount) + this._ensureMountAndLaunch(context, false); + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED)) + Main.notifyError(_('Failed to mount volume for “%s”').format(this.name), e.message); + } finally { + op.close(); + } + } + } + + launch(timestamp) { + const launchContext = global.create_app_launch_context(timestamp, -1); + this._ensureMountAndLaunch(launchContext, true); + } + + getIcon() { + this.file.query_info_async('standard::symbolic-icon', + Gio.FileQueryInfoFlags.NONE, + 0, + null, + (file, result) => { + try { + const info = file.query_info_finish(result); + this.icon = info.get_symbolic_icon(); + this.emit('changed'); + } catch (e) { + if (e instanceof Gio.IOErrorEnum) + return; + throw e; + } + }); + + // return a generic icon for this kind for now, until we have the + // icon from the query info above + switch (this.kind) { + case 'network': + return new Gio.ThemedIcon({name: 'folder-remote-symbolic'}); + case 'devices': + return new Gio.ThemedIcon({name: 'drive-harddisk-symbolic'}); + case 'special': + case 'bookmarks': + default: + if (!this.file.is_native()) + return new Gio.ThemedIcon({name: 'folder-remote-symbolic'}); + else + return new Gio.ThemedIcon({name: 'folder-symbolic'}); + } + } + + _getFileName() { + if (this.file.get_path() === GLib.get_home_dir()) + return _('Home'); + try { + const info = this.file.query_info('standard::display-name', 0, null); + return info.get_display_name(); + } catch (e) { + if (e instanceof Gio.IOErrorEnum) + return this.file.get_basename(); + throw e; + } + } +} + +export class RootInfo extends PlaceInfo { + _init() { + super._init('devices', Gio.File.new_for_path('/'), _('Computer')); + + const busName = 'org.freedesktop.hostname1'; + const objPath = '/org/freedesktop/hostname1'; + new Hostname1(Gio.DBus.system, busName, objPath, (obj, error) => { + if (error) + return; + + this._proxy = obj; + this._proxy.connectObject('g-properties-changed', + this._propertiesChanged.bind(this), this); + this._propertiesChanged(obj); + }); + } + + getIcon() { + return new Gio.ThemedIcon({name: 'drive-harddisk-symbolic'}); + } + + _propertiesChanged(proxy) { + // GDBusProxy will emit a g-properties-changed when hostname1 goes down + // ignore it + if (proxy.g_name_owner) { + this.name = proxy.PrettyHostname || _('Computer'); + this.emit('changed'); + } + } + + destroy() { + this._proxy?.disconnectObject(this); + this._proxy = null; + super.destroy(); + } +} + +var PlaceDeviceInfo = class ArcMenuPlaceDeviceInfo extends PlaceInfo { + _init(kind, mount) { + this._mount = mount; + super._init(kind, mount.get_root(), mount.get_name()); + } + + getIcon() { + return this._mount.get_symbolic_icon(); + } + + isRemovable() { + return this._mount.can_eject() || this._mount.can_unmount(); + } + + canUnmount() { + return this._mount.can_unmount(); + } + + canEject() { + return this._mount.can_eject(); + } + + eject() { + const unmountArgs = [ + Gio.MountUnmountFlags.NONE, + new ShellMountOperation.ShellMountOperation(this._mount).mountOp, + null, // Gio.Cancellable + ]; + + if (this._mount.can_eject()) { + this._mount.eject_with_operation(...unmountArgs, + this._ejectFinish.bind(this)); + } else { + this._mount.unmount_with_operation(...unmountArgs, + this._unmountFinish.bind(this)); + } + } + + _ejectFinish(mount, result) { + try { + mount.eject_with_operation_finish(result); + } catch (e) { + this._reportFailure(e); + } + } + + _unmountFinish(mount, result) { + try { + mount.unmount_with_operation_finish(result); + } catch (e) { + this._reportFailure(e); + } + } + + _reportFailure(exception) { + const msg = _('Ejecting drive “%s” failed:').format(this._mount.get_name()); + Main.notifyError(msg, exception.message); + } +}; + +var PlaceVolumeInfo = class ArcMenuPlaceVolumeInfo extends PlaceInfo { + _init(kind, volume) { + this._volume = volume; + super._init(kind, volume.get_activation_root(), volume.get_name()); + } + + launch(timestamp) { + if (this.file) { + super.launch(timestamp); + return; + } + + this._volume.mount(0, null, null, (volume, result) => { + volume.mount_finish(result); + + const mount = volume.get_mount(); + this.file = mount.get_root(); + super.launch(timestamp); + }); + } + + getIcon() { + return this._volume.get_symbolic_icon(); + } +}; + +const DEFAULT_DIRECTORIES = [ + GLib.UserDirectory.DIRECTORY_DOCUMENTS, + GLib.UserDirectory.DIRECTORY_PICTURES, + GLib.UserDirectory.DIRECTORY_MUSIC, + GLib.UserDirectory.DIRECTORY_DOWNLOAD, + GLib.UserDirectory.DIRECTORY_VIDEOS, +]; + +export const PlacesManager = class ArcMenuPlacesManager extends EventEmitter { + constructor() { + super(); + + this._places = { + special: [], + devices: [], + bookmarks: [], + network: [], + }; + + this._settings = new Gio.Settings({schema_id: BACKGROUND_SCHEMA}); + this._settings.connectObject('changed::show-desktop-icons', + () => this._updateSpecials(), this); + this._updateSpecials(); + + /* + * Show devices, code more or less ported from nautilus-places-sidebar.c + */ + this._volumeMonitor = Gio.VolumeMonitor.get(); + this._volumeMonitor.connectObject( + 'volume-added', () => this._updateMounts(), + 'volume-removed', () => this._updateMounts(), + 'volume-changed', () => this._updateMounts(), + 'mount-added', () => this._updateMounts(), + 'mount-removed', () => this._updateMounts(), + 'mount-changed', () => this._updateMounts(), + 'drive-connected', () => this._updateMounts(), + 'drive-disconnected', () => this._updateMounts(), + 'drive-changed', () => this._updateMounts(), + this); + this._updateMounts(); + + this._bookmarksFile = this._findBookmarksFile(); + this._bookmarkTimeoutId = 0; + this._monitor = null; + + if (this._bookmarksFile) { + this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null); + this._monitor.connect('changed', () => { + if (this._bookmarkTimeoutId > 0) + return; + /* Defensive event compression */ + this._bookmarkTimeoutId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, 100, () => { + this._bookmarkTimeoutId = 0; + this._reloadBookmarks(); + return false; + }); + }); + + this._reloadBookmarks(); + } + } + + destroy() { + this._settings?.disconnectObject(this); + this._settings = null; + + this._volumeMonitor.disconnectObject(this); + + if (this._settings) + this._settings.disconnect(this._showDesktopIconsChangedId); + this._settings = null; + + if (this._monitor) + this._monitor.cancel(); + if (this._bookmarkTimeoutId) + GLib.source_remove(this._bookmarkTimeoutId); + } + + _updateSpecials() { + this._places.special.forEach(p => p.destroy()); + this._places.special = []; + + const homePath = GLib.get_home_dir(); + + this._places.special.push(new PlaceInfo( + 'special', + Gio.File.new_for_path(homePath), + _('Home'))); + + const specials = []; + const dirs = DEFAULT_DIRECTORIES.slice(); + + if (this._settings.get_boolean('show-desktop-icons')) + dirs.push(GLib.UserDirectory.DIRECTORY_DESKTOP); + + for (let i = 0; i < dirs.length; i++) { + const specialPath = GLib.get_user_special_dir(dirs[i]); + if (!specialPath || specialPath === homePath) + continue; + + const file = Gio.File.new_for_path(specialPath); + let info; + try { + info = new PlaceInfo('special', file); + } catch (e) { + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) + continue; + throw e; + } + + specials.push(info); + } + + specials.sort((a, b) => GLib.utf8_collate(a.name, b.name)); + this._places.special = this._places.special.concat(specials); + + this.emit('special-updated'); + } + + _updateMounts() { + const networkMounts = []; + const networkVolumes = []; + + this._places.devices.forEach(p => p.destroy()); + this._places.devices = []; + this._places.network.forEach(p => p.destroy()); + this._places.network = []; + + /* first go through all connected drives */ + const drives = this._volumeMonitor.get_connected_drives(); + for (let i = 0; i < drives.length; i++) { + const volumes = drives[i].get_volumes(); + + for (let j = 0; j < volumes.length; j++) { + const identifier = volumes[j].get_identifier('class'); + if (identifier && identifier.includes('network')) { + networkVolumes.push(volumes[j]); + } else { + const mount = volumes[j].get_mount(); + if (mount) + this._addMount('devices', mount); + } + } + } + + /* add all volumes that is not associated with a drive */ + const volumes = this._volumeMonitor.get_volumes(); + for (let i = 0; i < volumes.length; i++) { + if (volumes[i].get_drive()) + continue; + + const identifier = volumes[i].get_identifier('class'); + if (identifier && identifier.includes('network')) { + networkVolumes.push(volumes[i]); + } else { + const mount = volumes[i].get_mount(); + if (mount) + this._addMount('devices', mount); + } + } + + /* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */ + const mounts = this._volumeMonitor.get_mounts(); + for (let i = 0; i < mounts.length; i++) { + if (mounts[i].is_shadowed()) + continue; + + if (mounts[i].get_volume()) + continue; + + const root = mounts[i].get_default_location(); + if (!root.is_native()) { + networkMounts.push(mounts[i]); + continue; + } + this._addMount('devices', mounts[i]); + } + + for (let i = 0; i < networkVolumes.length; i++) { + const mount = networkVolumes[i].get_mount(); + if (mount) { + networkMounts.push(mount); + continue; + } + this._addVolume('network', networkVolumes[i]); + } + + for (let i = 0; i < networkMounts.length; i++) + this._addMount('network', networkMounts[i]); + + + this.emit('devices-updated'); + this.emit('network-updated'); + } + + _findBookmarksFile() { + const paths = [ + GLib.build_filenamev([GLib.get_user_config_dir(), 'gtk-3.0', 'bookmarks']), + GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']), + ]; + + for (let i = 0; i < paths.length; i++) { + if (GLib.file_test(paths[i], GLib.FileTest.EXISTS)) + return Gio.File.new_for_path(paths[i]); + } + + return null; + } + + _reloadBookmarks() { + this._bookmarks = []; + + const content = Shell.get_file_contents_utf8_sync(this._bookmarksFile.get_path()); + const lines = content.split('\n'); + + const bookmarks = []; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const components = line.split(' '); + const [bookmark] = components; + + if (!bookmark) + continue; + + const file = Gio.File.new_for_uri(bookmark); + if (file.is_native() && !file.query_exists(null)) + continue; + + let duplicate = false; + for (let j = 0; j < this._places.special.length; j++) { + if (file.equal(this._places.special[j].file)) { + duplicate = true; + break; + } + } + if (duplicate) + continue; + for (let j = 0; j < bookmarks.length; j++) { + if (file.equal(bookmarks[j].file)) { + duplicate = true; + break; + } + } + if (duplicate) + continue; + + let label = null; + if (components.length > 1) + label = components.slice(1).join(' '); + + bookmarks.push(new PlaceInfo('bookmarks', file, label)); + } + + this._places.bookmarks = bookmarks; + + this.emit('bookmarks-updated'); + } + + _addMount(kind, mount) { + let devItem; + + try { + devItem = new PlaceDeviceInfo(kind, mount); + } catch (e) { + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) + return; + throw e; + } + + this._places[kind].push(devItem); + } + + _addVolume(kind, volume) { + let volItem; + + try { + volItem = new PlaceVolumeInfo(kind, volume); + } catch (e) { + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) + return; + throw e; + } + + this._places[kind].push(volItem); + } + + get(kind) { + return this._places[kind]; + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/prefs.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/prefs.js new file mode 100644 index 0000000..a64008b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/prefs.js @@ -0,0 +1,123 @@ +import Gdk from 'gi://Gdk'; +import Gtk from 'gi://Gtk'; + +import * as Constants from './constants.js'; + +import {AboutPage} from './settings/AboutPage.js'; +import {GeneralPage} from './settings/GeneralPage.js'; +import {MenuButtonPage} from './settings/MenuButtonPage.js'; +import {MenuPage} from './settings/MenuPage.js'; + +import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export default class ArcMenuPrefs extends ExtensionPreferences { + constructor(metadata) { + super(metadata); + + const iconPath = `${this.path}/icons`; + const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()); + if (!iconTheme.get_search_path().includes(iconPath)) + iconTheme.add_search_path(iconPath); + } + + fillPreferencesWindow(window) { + const settings = this.getSettings(); + + window.set_search_enabled(true); + window.set_default_size(settings.get_int('settings-width'), settings.get_int('settings-height')); + window.set_title(_('ArcMenu Settings')); + + let pageChangedId = settings.connect('changed::prefs-visible-page', () => { + if (settings.get_int('prefs-visible-page') !== Constants.SettingsPage.MAIN) + this._setVisiblePage(window, settings); + }); + + let pinnedAppsChangedId = settings.connect('changed::pinned-apps', () => { + for (const page of window.pages) { + if (page instanceof MenuPage) { + const {settingPage} = page.pinnedAppsRow; + settingPage.updatePinnedApps(); + } + } + }); + + window.connect('notify::visible-page', () => { + const page = window.visible_page; + const maybeScrolledWindowChild = [...page][0]; + + if (maybeScrolledWindowChild instanceof Gtk.ScrolledWindow) + maybeScrolledWindowChild.vadjustment.value = 0; + }); + + window.connect('close-request', () => { + if (pageChangedId) { + settings.disconnect(pageChangedId); + pageChangedId = null; + } + + if (pinnedAppsChangedId) { + settings.disconnect(pinnedAppsChangedId); + pinnedAppsChangedId = null; + } + }); + + + this._populateWindow(window, settings); + } + + _populateWindow(window, settings) { + if (window.pages?.length > 0) + window.pages.forEach(page => window.remove(page)); + + window.pages = []; + + const generalPage = new GeneralPage(settings); + window.add(generalPage); + window.pages.push(generalPage); + + const menuPage = new MenuPage(settings, window); + window.add(menuPage); + window.pages.push(menuPage); + + const menuButtonPage = new MenuButtonPage(settings); + window.add(menuButtonPage); + window.pages.push(menuButtonPage); + + const aboutPage = new AboutPage(this.metadata); + window.add(aboutPage); + window.pages.push(aboutPage); + + this._setVisiblePage(window, settings); + } + + _setVisiblePage(window, settings) { + const prefsVisiblePage = settings.get_int('prefs-visible-page'); + + window.pop_subpage(); + if (prefsVisiblePage === Constants.SettingsPage.MAIN) { + window.set_visible_page_name('GeneralPage'); + } else if (prefsVisiblePage === Constants.SettingsPage.CUSTOMIZE_MENU) { + window.set_visible_page_name('MenuPage'); + } else if (prefsVisiblePage === Constants.SettingsPage.MENU_LAYOUT) { + window.set_visible_page_name('MenuPage'); + const page = window.get_visible_page(); + page.presentSubpage(Constants.SettingsPage.MENU_LAYOUT); + } else if (prefsVisiblePage === Constants.SettingsPage.MENU_THEME) { + window.set_visible_page_name('MenuPage'); + const page = window.get_visible_page(); + page.presentSubpage(Constants.SettingsPage.MENU_THEME); + } else if (prefsVisiblePage === Constants.SettingsPage.BUTTON_APPEARANCE) { + window.set_visible_page_name('MenuButtonPage'); + } else if (prefsVisiblePage === Constants.SettingsPage.RUNNER_TWEAKS) { + window.set_visible_page_name('MenuPage'); + const page = window.get_visible_page(); + page.presentSubpage(Constants.SettingsPage.RUNNER_TWEAKS); + } else if (prefsVisiblePage === Constants.SettingsPage.ABOUT) { + window.set_visible_page_name('AboutPage'); + } else if (prefsVisiblePage === Constants.SettingsPage.GENERAL) { + window.set_visible_page_name('GeneralPage'); + } + + settings.set_int('prefs-visible-page', Constants.SettingsPage.MAIN); + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/prefsWidgets.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/prefsWidgets.js new file mode 100644 index 0000000..4edd79e --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/prefsWidgets.js @@ -0,0 +1,492 @@ +import Adw from 'gi://Adw'; +import Gdk from 'gi://Gdk'; +import GdkPixbuf from 'gi://GdkPixbuf'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const DialogWindow = GObject.registerClass({ + Signals: { + 'response': {param_types: [GObject.TYPE_INT]}, + }, +}, class ArcMenuDialogWindow extends Adw.PreferencesWindow { + _init(title, parent) { + super._init({ + title, + transient_for: parent.get_root(), + modal: true, + search_enabled: true, + }); + this.page = new Adw.PreferencesPage(); + this.pageGroup = new Adw.PreferencesGroup(); + + this.add(this.page); + this.page.add(this.pageGroup); + } +}); + +export const SettingRow = GObject.registerClass( +class ArcMenuSettingRow extends Adw.ActionRow { + _init(params) { + super._init({ + activatable: true, + ...params, + }); + + const goNextImage = new Gtk.Image({ + gicon: Gio.icon_new_for_string('go-next-symbolic'), + halign: Gtk.Align.END, + valign: Gtk.Align.CENTER, + hexpand: false, + vexpand: false, + }); + + this.add_suffix(goNextImage); + } +}); + +export const DragRow = GObject.registerClass({ + Properties: { + 'shortcut-name': GObject.ParamSpec.string( + 'shortcut-name', 'shortcut-name', 'shortcut-name', + GObject.ParamFlags.READWRITE, + ''), + 'shortcut-icon': GObject.ParamSpec.string( + 'shortcut-icon', 'shortcut-icon', 'shortcut-icon', + GObject.ParamFlags.READWRITE, + ''), + 'shortcut-command': GObject.ParamSpec.string( + 'shortcut-command', 'shortcut-command', 'shortcut-command', + GObject.ParamFlags.READWRITE, + ''), + 'gicon': GObject.ParamSpec.object( + 'gicon', 'gicon', 'gicon', + GObject.ParamFlags.READWRITE, + Gio.Icon.$gtype), + 'pixbuf': GObject.ParamSpec.object( + 'pixbuf', 'pixbuf', 'pixbuf', + GObject.ParamFlags.READWRITE, + GdkPixbuf.Pixbuf.$gtype), + 'icon-pixel-size': GObject.ParamSpec.int( + 'icon-pixel-size', 'icon-pixel-size', 'icon-pixel-size', + GObject.ParamFlags.READWRITE, + 1, GLib.MAXINT32, 22), + 'switch-enabled': GObject.ParamSpec.boolean( + 'switch-enabled', 'switch-enabled', 'switch-enabled', + GObject.ParamFlags.READWRITE, + false), + 'switch-active': GObject.ParamSpec.boolean( + 'switch-active', 'switch-active', 'switch-active', + GObject.ParamFlags.READWRITE, + false), + }, + Signals: { + 'drag-drop-done': { }, + 'change-button-clicked': { }, + 'switch-toggled': { }, + }, +}, class ArcMenuDragRow extends Adw.ActionRow { + _init(params) { + super._init(params); + + this._params = params; + + this.icon = new Gtk.Image({ + gicon: this.gicon, + pixel_size: this.icon_pixel_size, + }); + this.add_prefix(this.icon); + + if (this.pixbuf) + this.icon.set_from_pixbuf(this.pixbuf); + + this.connect('notify::gicon', () => (this.icon.gicon = this.gicon)); + + this.dragIcon = new Gtk.Image({ + gicon: Gio.icon_new_for_string('list-drag-handle-symbolic'), + pixel_size: 12, + }); + this.add_prefix(this.dragIcon); + + if (this.switch_enabled) { + this.switch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + vexpand: false, + margin_start: 10, + active: this.switch_active, + }); + this.switch.connect('notify::active', () => { + this.switch_active = this.switch.get_active(); + this.emit('switch-toggled'); + }); + this.add_suffix(this.switch); + this.add_suffix(new Gtk.Separator({ + orientation: Gtk.Orientation.VERTICAL, + margin_top: 10, + margin_bottom: 10, + })); + } + + const dragSource = new Gtk.DragSource({actions: Gdk.DragAction.MOVE}); + this.add_controller(dragSource); + + const dropTarget = new Gtk.DropTargetAsync({actions: Gdk.DragAction.MOVE}); + this.add_controller(dropTarget); + + dragSource.connect('drag-begin', (self, gdkDrag) => { + this._dragParent = self.get_widget().get_parent(); + this._dragParent.dragRow = this; + + const alloc = this.get_allocation(); + const dragWidget = self.get_widget().createDragRow(alloc); + this._dragParent.dragWidget = dragWidget; + + const icon = Gtk.DragIcon.get_for_drag(gdkDrag); + icon.set_child(dragWidget); + + gdkDrag.set_hotspot(this._dragParent.dragX, this._dragParent.dragY); + }); + + dragSource.connect('prepare', (self, x, y) => { + this.set_state_flags(Gtk.StateFlags.NORMAL, true); + const parent = self.get_widget().get_parent(); + // store drag start cursor location + parent.dragX = x; + parent.dragY = y; + return new Gdk.ContentProvider(); + }); + + dragSource.connect('drag-end', (_self, _gdkDrag) => { + this._dragParent.dragWidget = null; + this._dragParent.drag_unhighlight_row(); + }); + + dropTarget.connect('drag-enter', self => { + const parent = self.get_widget().get_parent(); + const widget = self.get_widget(); + + parent.drag_highlight_row(widget); + }); + + dropTarget.connect('drag-leave', self => { + const parent = self.get_widget().get_parent(); + parent.drag_unhighlight_row(); + }); + + dropTarget.connect('drop', (_self, gdkDrop) => { + const parent = this.get_parent(); + const {dragRow} = parent; // The row being dragged. + const dragRowStartIndex = dragRow.get_index(); + const dragRowNewIndex = this.get_index(); + + gdkDrop.read_value_async(ArcMenuDragRow, 1, null, () => gdkDrop.finish(Gdk.DragAction.MOVE)); + + // The drag row hasn't moved + if (dragRowStartIndex === dragRowNewIndex) + return true; + + parent.remove(dragRow); + parent.show(); + parent.insert(dragRow, dragRowNewIndex); + + this.emit('drag-drop-done'); + return true; + }); + } + + createDragRow(alloc) { + const dragWidget = new Gtk.ListBox(); + dragWidget.set_size_request(alloc.width, alloc.height); + + const dragRow = new DragRow(this._params); + dragWidget.append(dragRow); + dragWidget.drag_highlight_row(dragRow); + + dragRow.title = _(this.title); + dragRow.css_classes = this.css_classes; + dragRow.icon.gicon = this.gicon; + + if (this.pixbuf) + dragRow.icon.set_from_pixbuf(this.pixbuf); + + const editButton = new Gtk.Button({ + icon_name: 'view-more-symbolic', + valign: Gtk.Align.CENTER, + }); + dragRow.add_suffix(editButton); + + return dragWidget; + } +}); + +const ModifyEntryType = { + MOVE_UP: 0, + MOVE_DOWN: 1, + REMOVE: 2, +}; + +export const EditEntriesBox = GObject.registerClass({ + Properties: { + 'allow-modify': GObject.ParamSpec.boolean( + 'allow-modify', 'allow-modify', 'allow-modify', + GObject.ParamFlags.READWRITE, + false), + 'allow-remove': GObject.ParamSpec.boolean( + 'allow-remove', 'allow-remove', 'allow-remove', + GObject.ParamFlags.READWRITE, + false), + 'row': GObject.ParamSpec.object( + 'row', 'row', 'row', + GObject.ParamFlags.READWRITE, + Gtk.Widget.$gtype), + }, + Signals: { + 'modify-button-clicked': {}, + 'entry-modified': {param_types: [GObject.TYPE_INT, GObject.TYPE_INT]}, + }, +}, class ArcMenuEditEntriesBox extends Gtk.MenuButton { + _init(params) { + super._init({ + icon_name: 'view-more-symbolic', + valign: Gtk.Align.CENTER, + popover: new Gtk.Popover(), + ...params, + }); + + const popoverBox = new Gtk.Box({ + orientation: Gtk.Orientation.VERTICAL, + spacing: 3, + }); + this.popover.set_child(popoverBox); + + const modifyEntryButton = new Gtk.Button({ + label: _('Modify'), + has_frame: false, + visible: this.allow_modify, + }); + modifyEntryButton.connect('clicked', () => { + this.popover.popdown(); + this.emit('modify-button-clicked'); + }); + popoverBox.append(modifyEntryButton); + + const topSeparator = Gtk.Separator.new(Gtk.Orientation.HORIZONTAL); + topSeparator.visible = this.allow_modify; + popoverBox.append(topSeparator); + + const moveUpButton = new Gtk.Button({ + label: _('Move Up'), + has_frame: false, + }); + moveUpButton.connect('clicked', () => this.modifyEntry(ModifyEntryType.MOVE_UP)); + popoverBox.append(moveUpButton); + + const moveDownButton = new Gtk.Button({ + label: _('Move Down'), + has_frame: false, + }); + moveDownButton.connect('clicked', () => this.modifyEntry(ModifyEntryType.MOVE_DOWN)); + popoverBox.append(moveDownButton); + + const removeEntryButton = new Gtk.Button({ + label: _('Remove'), + has_frame: false, + visible: this.allow_remove, + }); + removeEntryButton.connect('clicked', () => this.modifyEntry(ModifyEntryType.REMOVE)); + + const bottomSeparator = Gtk.Separator.new(Gtk.Orientation.HORIZONTAL); + bottomSeparator.visible = this.allow_remove; + popoverBox.append(bottomSeparator); + + popoverBox.append(removeEntryButton); + + this.connect('notify::allow-modify', () => { + modifyEntryButton.visible = this.allow_modify; + topSeparator.visible = this.allow_modify; + }); + this.connect('notify::allow-remove', () => { + removeEntryButton.visible = this.allow_remove; + bottomSeparator.visible = this.allow_remove; + }); + } + + modifyEntry(modifyEntryType) { + this.popover.popdown(); + + const startIndex = this.row.get_index(); + const parent = this.row.get_parent(); + const children = [...parent]; + let indexModification; + + if (modifyEntryType === ModifyEntryType.MOVE_DOWN) { + if (startIndex >= children.length - 1) + return; + + indexModification = 1; + } else if (modifyEntryType === ModifyEntryType.MOVE_UP) { + if (startIndex <= 0) + return; + + indexModification = -1; + } + if (modifyEntryType === ModifyEntryType.REMOVE) + indexModification = (startIndex + 1) * -1; // we want newIndex == -1 for a remove + + const newIndex = startIndex + indexModification; + + parent.remove(this.row); + if (newIndex !== -1) + parent.insert(this.row, newIndex); + parent.show(); + + this.emit('entry-modified', startIndex, newIndex); + } +}); + +export const IconGrid = GObject.registerClass(class ArcMenuIconGrid extends Gtk.FlowBox { + _init(spacing = 4) { + super._init({ + max_children_per_line: 15, + row_spacing: spacing, + column_spacing: spacing, + valign: Gtk.Align.START, + halign: Gtk.Align.CENTER, + homogeneous: true, + selection_mode: Gtk.SelectionMode.SINGLE, + }); + this._spacing = spacing; + this.childrenCount = 0; + this.connect('child-activated', (_self, child) => { + this.setActiveChild(child); + }); + } + + setActiveChild(child) { + if (this._previousSelectedChild) + this._previousSelectedChild.setActive(false); + + child.setActive(true); + this._previousSelectedChild = child; + } + + unselect_all() { + if (this._previousSelectedChild) + this._previousSelectedChild.setActive(false); + super.unselect_all(); + } + + select_child(child) { + this.setActiveChild(child); + super.select_child(child); + } + + add(widget) { + widget.margin_top = widget.margin_bottom = + widget.margin_start = widget.margin_end = this._spacing; + + this.append(widget); + this.childrenCount++; + } +}); + +export const MenuButtonIconTile = GObject.registerClass(class ArcMenuMenuButtonIconTile extends Gtk.FlowBoxChild { + _init(icon, name) { + super._init({ + css_classes: ['card', 'activatable'], + }); + + const box = new Gtk.Box({ + orientation: Gtk.Orientation.VERTICAL, + spacing: 4, + margin_top: 4, + margin_bottom: 4, + margin_start: 4, + margin_end: 4, + }); + this.set_child(box); + + const ICON_SIZE = 32; + this._image = new Gtk.Image({ + gicon: Gio.icon_new_for_string(icon), + pixel_size: ICON_SIZE, + }); + + this._label = new Gtk.Label({ + label: name ? _(name) : '', + hexpand: true, + css_classes: ['caption'], + visible: !!name, + }); + + box.append(this._image); + box.append(this._label); + } + + setIcon(icon) { + this._image.gicon = Gio.icon_new_for_string(icon); + } + + setActive(active) { + if (active) { + this._image.css_classes = ['accent']; + this._label.css_classes = ['caption', 'accent']; + } else { + this._image.css_classes = []; + this._label.css_classes = ['caption']; + } + } +}); + +export const MenuLayoutTile = GObject.registerClass(class ArcMenuMenuLayoutTile extends Gtk.FlowBoxChild { + _init(styleInfo) { + super._init({ + css_classes: ['card', 'activatable'], + margin_top: 4, + margin_bottom: 4, + margin_start: 4, + margin_end: 4, + halign: Gtk.Align.FILL, + hexpand: true, + }); + + const box = new Gtk.Box({ + orientation: Gtk.Orientation.VERTICAL, + margin_top: 4, + margin_bottom: 4, + margin_start: 8, + margin_end: 8, + }); + this.set_child(box); + + this.name = styleInfo.TITLE; + this.layout = styleInfo.LAYOUT; + + this._image = new Gtk.Image({ + gicon: Gio.icon_new_for_string(styleInfo.IMAGE), + pixel_size: 145, + }); + + this._label = new Gtk.Label({ + label: _(this.name), + hexpand: true, + css_classes: ['caption'], + }); + + box.append(this._image); + box.append(this._label); + } + + setActive(active) { + if (active) { + this._image.css_classes = ['accent']; + this._label.css_classes = ['caption', 'accent']; + } else { + this._image.css_classes = []; + this._label.css_classes = ['caption']; + } + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/recentFilesManager.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/recentFilesManager.js new file mode 100644 index 0000000..a9c0982 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/recentFilesManager.js @@ -0,0 +1,101 @@ +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +import {ArcMenuManager} from './arcmenuManager.js'; + +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; + +Gio._promisify(Gio.File.prototype, 'query_info_async'); + +export const RecentFilesManager = class ArcMenuRecentFilesManager { + constructor() { + this._bookmarksFile = new GLib.BookmarkFile(); + this._recentFile = GLib.build_filenamev([GLib.get_user_data_dir(), 'recently-used.xbel']); + this._queryCancellables = []; + + this._settings = ArcMenuManager.settings; + } + + getRecentFiles() { + try { + this._bookmarksFile.load_from_file(this._recentFile); + } catch (e) { + if (!e.matches(GLib.BookmarkFileError.FILE_NOT_FOUND)) { + log(`Could not open recent files: ${e.message}`); + return []; + } + } + + const recentFilesUris = this._bookmarksFile.get_uris(); + recentFilesUris.sort((a, b) => this._bookmarksFile.get_modified(b) - this._bookmarksFile.get_modified(a)); + return recentFilesUris; + } + + removeItem(uri) { + try { + this._bookmarksFile.remove_item(uri); + this._bookmarksFile.to_file(this._recentFile); + } catch (e) { + log(`Could not save recent file ${uri}: ${e.message}`); + } + } + + getMimeType(uri) { + return this._bookmarksFile.get_mime_type(uri); + } + + async queryInfoAsync(recentFileUri) { + const file = Gio.File.new_for_uri(recentFileUri); + const cancellable = new Gio.Cancellable(); + + if (file === null) + return {error: 'Recent file is null.'}; + + this._queryCancellables.push(cancellable); + + try { + const fileInfo = await file.query_info_async('standard::type,standard::is-hidden', + 0, 0, cancellable); + + this.removeCancellableFromList(cancellable); + + if (fileInfo) { + const isHidden = fileInfo.get_attribute_boolean('standard::is-hidden'); + const showHidden = this._settings.get_boolean('show-hidden-recent-files'); + + if (isHidden && !showHidden) + return {error: `${file.get_basename()} is hidden.`}; + + return {recentFile: file}; + } + return {error: 'No File Info Found.'}; + } catch (err) { + this.removeCancellableFromList(cancellable); + + return {error: err}; + } + } + + removeCancellableFromList(cancellable) { + const index = this._queryCancellables.indexOf(cancellable); + if (index !== -1) + this._queryCancellables.splice(index, 1); + } + + cancelCurrentQueries() { + if (this._queryCancellables.length === 0) + return; + + for (let cancellable of this._queryCancellables) { + cancellable.cancel(); + cancellable = null; + } + + this._queryCancellables = null; + this._queryCancellables = []; + } + + destroy() { + this.cancelCurrentQueries(); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/search.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/search.js new file mode 100644 index 0000000..8f74d03 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/search.js @@ -0,0 +1,814 @@ +/* + * Credits: This file leverages the work from GNOME Shell search.js file + * (https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/search.js) + */ +import Clutter from 'gi://Clutter'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +import {Highlighter} from 'resource:///org/gnome/shell/misc/util.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as RemoteSearch from 'resource:///org/gnome/shell/ui/remoteSearch.js'; + +import {ApplicationMenuItem, BaseMenuItem} from './menuWidgets.js'; +import * as Constants from './constants.js'; +import {RecentFilesManager} from './recentFilesManager.js'; +import {OpenWindowSearchProvider} from './searchProviders/openWindows.js'; +import {RecentFilesSearchProvider} from './searchProviders/recentFiles.js'; + +const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers'; +const FILE_PROVIDERS = ['org.gnome.Nautilus.desktop', 'arcmenu.recent-files', 'nemo.desktop']; + +class ListSearchResult extends ApplicationMenuItem { + static { + GObject.registerClass(this); + } + + constructor(provider, metaInfo, resultsView) { + const menuLayout = resultsView._menuLayout; + metaInfo['provider-id'] = provider.id; + + super(menuLayout, null, Constants.DisplayType.LIST, metaInfo); + + this.searchType = this._menuLayout.search_display_type; + this.metaInfo = metaInfo; + this.provider = provider; + this.resultsView = resultsView; + this.layout = this._settings.get_enum('menu-layout'); + + if (FILE_PROVIDERS.includes(this.provider.id)) + this.folderPath = this.metaInfo['description']; + + const highlightSearchResultTerms = this._settings.get_boolean('highlight-search-result-terms'); + if (highlightSearchResultTerms) { + this.resultsView.connectObject('terms-changed', this._highlightTerms.bind(this), this); + this._highlightTerms(); + } + + if (this.metaInfo['description']) + this.description = this.metaInfo['description'].split('\n')[0]; + } + + _highlightTerms() { + const showSearchResultDescriptions = this._settings.get_boolean('show-search-result-details'); + if (this.descriptionLabel && showSearchResultDescriptions) { + const descriptionMarkup = this.resultsView.highlightTerms(this.metaInfo['description'].split('\n')[0]); + this.descriptionLabel.clutter_text.set_markup(descriptionMarkup); + } + const labelMarkup = this.resultsView.highlightTerms(this.label.text.split('\n')[0]); + this.label.clutter_text.set_markup(labelMarkup); + } +} + +class AppSearchResult extends ApplicationMenuItem { + static { + GObject.registerClass(this); + } + + constructor(provider, metaInfo, resultsView) { + const menuLayout = resultsView._menuLayout; + const appSystem = Shell.AppSystem.get_default(); + const app = appSystem.lookup_app(metaInfo['id']) || appSystem.lookup_app(provider.id); + const displayType = menuLayout.search_display_type; + + super(menuLayout, app, displayType, metaInfo); + + this.app = app; + this.provider = provider; + this.metaInfo = metaInfo; + this.resultsView = resultsView; + + if (!this.app && this.metaInfo['description']) + this.description = this.metaInfo['description'].split('\n')[0]; + + const highlightSearchResultTerms = this._settings.get_boolean('highlight-search-result-terms'); + if (highlightSearchResultTerms) { + this.resultsView.connectObject('terms-changed', this._highlightTerms.bind(this), this); + this._highlightTerms(); + } + } + + _highlightTerms() { + const showSearchResultDescriptions = this._settings.get_boolean('show-search-result-details'); + if (this.descriptionLabel && showSearchResultDescriptions) { + const descriptionMarkup = this.resultsView.highlightTerms(this.descriptionLabel.text.split('\n')[0]); + this.descriptionLabel.clutter_text.set_markup(descriptionMarkup); + } + + const labelMarkup = this.resultsView.highlightTerms(this.label.text.split('\n')[0]); + this.label.clutter_text.set_markup(labelMarkup); + } +} + +class SearchResultsBase extends St.BoxLayout { + static [GObject.signals] = { + 'terms-changed': {}, + 'no-results': {}, + }; + + static { + GObject.registerClass(this); + } + + constructor(provider, resultsView) { + super({vertical: true}); + this.provider = provider; + this.resultsView = resultsView; + this._menuLayout = resultsView._menuLayout; + this._terms = []; + + this._resultDisplayBin = new St.Bin({ + x_expand: true, + y_expand: true, + }); + + this.add_child(this._resultDisplayBin); + + this._resultDisplays = {}; + this._clipboard = St.Clipboard.get_default(); + + this._cancellable = new Gio.Cancellable(); + this.connect('destroy', this._onDestroy.bind(this)); + } + + _onDestroy() { + this._cancellable.cancel(); + this._terms = []; + } + + _createResultDisplay(_meta) { + } + + clear() { + this._cancellable.cancel(); + for (const resultId in this._resultDisplays) { + if (Object.hasOwn(this._resultDisplays, resultId)) + this._resultDisplays[resultId].destroy(); + } + + this._resultDisplays = {}; + this._clearResultDisplay(); + this.hide(); + } + + _setMoreCount(_count) { + } + + async _ensureResultActors(results) { + const metasNeeded = results.filter( + resultId => this._resultDisplays[resultId] === undefined + ); + + if (metasNeeded.length === 0) + return; + + this._cancellable.cancel(); + this._cancellable.reset(); + + const metas = await this.provider.getResultMetas(metasNeeded, this._cancellable); + + if (this._cancellable.is_cancelled()) { + if (metas.length > 0) + throw new Error(`Search provider ${this.provider.id} returned results after the request was canceled`); + } + + if (metas.length !== metasNeeded.length) + throw new Error(`Wrong number of result metas returned by search provider ${this.provider.id}: expected ${metasNeeded.length} but got ${metas.length}`); + + + if (metas.some(meta => !meta.name || !meta.id)) + throw new Error(`Invalid result meta returned from search provider ${this.provider.id}`); + + metasNeeded.forEach((resultId, i) => { + const meta = metas[i]; + const display = this._createResultDisplay(meta); + this._resultDisplays[resultId] = display; + }); + } + + async updateSearch(providerResults, terms, callback) { + this._terms = terms; + if (providerResults.length === 0) { + this._clearResultDisplay(); + this.hide(); + callback(); + } else { + const maxResults = this._getMaxDisplayedResults(); + const results = maxResults > -1 + ? this.provider.filterResults(providerResults, maxResults) + : providerResults; + + const moreCount = Math.max(providerResults.length - results.length, 0); + + try { + await this._ensureResultActors(results); + + // To avoid CSS transitions causing flickering when + // the first search result stays the same, we hide the + // content while filling in the results. + this.hide(); + this._clearResultDisplay(); + results.forEach( + resultId => this._addItem(this._resultDisplays[resultId])); + this._setMoreCount(this.provider.canLaunchSearch ? moreCount : 0); + this.show(); + callback(); + } catch (e) { + this._clearResultDisplay(); + callback(); + } + } + } +} + +class ListSearchResults extends SearchResultsBase { + static { + GObject.registerClass(this); + } + + constructor(provider, resultsView) { + super(provider, resultsView); + this._menuLayout = resultsView._menuLayout; + this.searchType = this._menuLayout.search_display_type; + + this._settings = resultsView.settings; + + this.layout = this._settings.get_enum('menu-layout'); + + const spacing = this._menuLayout.search_results_spacing; + + this._container = new St.BoxLayout({ + vertical: true, + x_align: Clutter.ActorAlign.FILL, + y_align: Clutter.ActorAlign.FILL, + x_expand: true, + y_expand: true, + style: `margin-top: ${Math.max(spacing, 8)}px; spacing: ${Math.max(spacing, 2)}px;`, + }); + + this.providerInfo = new ArcSearchProviderInfo(provider, this._menuLayout); + this.providerInfo.connect('activate', () => { + if (provider.canLaunchSearch) { + provider.launchSearch(this._terms); + this._menuLayout.arcMenu.toggle(); + } + }); + this._container.add_child(this.providerInfo); + + this._content = new St.BoxLayout({ + vertical: true, + x_expand: true, + y_expand: true, + x_align: Clutter.ActorAlign.FILL, + style: `spacing: ${spacing}px;`, + }); + + this._container.add_child(this._content); + this._resultDisplayBin.set_child(this._container); + } + + _setMoreCount(count) { + this.providerInfo.setMoreCount(count); + } + + _getMaxDisplayedResults() { + return this._settings.get_int('max-search-results'); + } + + _clearResultDisplay() { + this._content.remove_all_children(); + } + + _createResultDisplay(meta) { + return new ListSearchResult(this.provider, meta, this.resultsView); + } + + _addItem(display) { + if (display.get_parent()) + display.get_parent().remove_child(display); + this._content.add_child(display); + } + + getFirstResult() { + if (this._content.get_n_children() > 0) + return this._content.get_child_at_index(0)._delegate; + else + return null; + } +} + +class AppSearchResults extends SearchResultsBase { + static { + GObject.registerClass(this); + } + + constructor(provider, resultsView) { + super(provider, resultsView); + this._parentContainer = resultsView; + this._menuLayout = resultsView._menuLayout; + this.searchType = this._menuLayout.search_display_type; + + this._settings = resultsView.settings; + + this.layout = this._settings.get_enum('menu-layout'); + + this.itemCount = 0; + this.gridTop = -1; + this.gridLeft = 0; + + this.rtl = this._menuLayout.get_text_direction() === Clutter.TextDirection.RTL; + + const layout = new Clutter.GridLayout({ + orientation: Clutter.Orientation.VERTICAL, + column_spacing: this.searchType === Constants.DisplayType.GRID ? this._menuLayout.column_spacing : this._menuLayout.search_results_spacing, + row_spacing: this.searchType === Constants.DisplayType.GRID ? this._menuLayout.row_spacing : this._menuLayout.search_results_spacing, + }); + this._grid = new St.Widget({ + x_expand: true, + x_align: this.searchType === Constants.DisplayType.LIST ? Clutter.ActorAlign.FILL + : Clutter.ActorAlign.CENTER, + layout_manager: layout, + }); + layout.hookup_style(this._grid); + + if (this.searchType === Constants.DisplayType.GRID) { + const spacing = this._menuLayout.column_spacing; + + this._grid.style = `spacing: ${spacing}px;`; + this._resultDisplayBin.x_align = Clutter.ActorAlign.CENTER; + } + + this._resultDisplayBin.set_child(this._grid); + } + + _getMaxDisplayedResults() { + let maxDisplayedResults; + if (this.searchType === Constants.DisplayType.GRID) { + const iconWidth = this._menuLayout.getIconWidthFromSetting(); + maxDisplayedResults = this._menuLayout.getBestFitColumnsForGrid(iconWidth, this._grid); + } else { + maxDisplayedResults = this._settings.get_int('max-search-results'); + } + return maxDisplayedResults; + } + + _clearResultDisplay() { + this.itemCount = 0; + this.gridTop = -1; + this.gridLeft = 0; + this._grid.remove_all_children(); + } + + _createResultDisplay(meta) { + return new AppSearchResult(this.provider, meta, this.resultsView); + } + + _addItem(display) { + let colums; + if (this.searchType === Constants.DisplayType.LIST) { + colums = 1; + } else { + const iconWidth = this._menuLayout.getIconWidthFromSetting(); + colums = this._menuLayout.getBestFitColumnsForGrid(iconWidth, this._grid); + } + + if (!this.rtl && (this.itemCount % colums === 0)) { + this.gridTop++; + this.gridLeft = 0; + } else if (this.rtl && (this.gridLeft === 0)) { + this.gridTop++; + this.gridLeft = colums; + } + this._grid.layout_manager.attach(display, this.gridLeft, this.gridTop, 1, 1); + + if (!this.rtl) + this.gridLeft++; + else if (this.rtl) + this.gridLeft--; + this.itemCount++; + } + + getFirstResult() { + if (this._grid.get_n_children() > 0) + return this._grid.get_child_at_index(0)._delegate; + else + return null; + } +} + +export class SearchResults extends St.BoxLayout { + static [GObject.signals] = { + 'terms-changed': {}, + 'have-results': {}, + 'no-results': {}, + }; + + static { + GObject.registerClass(this); + } + + constructor(menuLayout) { + super({ + vertical: true, + y_expand: true, + x_expand: true, + x_align: Clutter.ActorAlign.FILL, + }); + this._menuLayout = menuLayout; + + const {searchProviderDisplayId} = menuLayout.menuButton; + this._displayId = `display_${searchProviderDisplayId}`; + + this.searchType = this._menuLayout.search_display_type; + this._settings = menuLayout.settings; + this.layout = this._settings.get_enum('menu-layout'); + + this._content = new St.BoxLayout({ + vertical: true, + x_align: Clutter.ActorAlign.FILL, + }); + + this.add_child(this._content); + + this._statusText = new St.Label(); + this._statusBin = new St.Bin({ + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + x_expand: true, + y_expand: true, + }); + + this.add_child(this._statusBin); + this._statusBin.set_child(this._statusText); + + this._highlightDefault = true; + this._defaultResult = null; + this._startingSearch = false; + + this._terms = []; + this._results = {}; + + this._providers = []; + + this._highlighter = new Highlighter(); + + this.recentFilesManager = new RecentFilesManager(); + + this._searchSettings = new Gio.Settings({schema_id: SEARCH_PROVIDERS_SCHEMA}); + this._searchSettings.connectObject('changed::disabled', this._reloadRemoteProviders.bind(this), this); + this._searchSettings.connectObject('changed::enabled', this._reloadRemoteProviders.bind(this), this); + this._searchSettings.connectObject('changed::disable-external', this._reloadRemoteProviders.bind(this), this); + this._searchSettings.connectObject('changed::sort-order', this._reloadRemoteProviders.bind(this), this); + + const {extension} = menuLayout; + extension.searchProviderEmitter.connectObject('search-provider-added', + (_, provider) => this._registerProvider(provider), this); + extension.searchProviderEmitter.connectObject('search-provider-removed', + (_, provider) => this._unregisterProvider(provider), this); + + this._searchTimeoutId = null; + this._cancellable = new Gio.Cancellable(); + + const appSystem = Shell.AppSystem.get_default(); + appSystem.connectObject('installed-changed', this._reloadRemoteProviders.bind(this), this); + + this._registerGnomeShellProviders(); + this._reloadRemoteProviders(); + + this.connect('destroy', this._onDestroy.bind(this)); + } + + get terms() { + return this._terms; + } + + get settings() { + return this._settings; + } + + setStyle(style) { + if (this._statusText) + this._statusText.style_class = style; + } + + _onDestroy() { + this._clearSearchTimeout(); + + this._terms = []; + this._results = {}; + this._clearDisplay(); + this._defaultResult = null; + this._startingSearch = false; + + this._providers.forEach(provider => { + if (provider[this._displayId]) { + provider[this._displayId].destroy(); + delete provider[this._displayId]; + } + }); + + this.recentFilesManager.destroy(); + this.recentFilesManager = null; + } + + _registerGnomeShellProviders() { + const searchResults = Main.overview.searchController._searchResults; + const providers = searchResults._providers.filter(p => !p.isRemoteProvider); + providers.forEach(this._registerProvider.bind(this)); + + if (this._settings.get_boolean('search-provider-open-windows')) + this._registerProvider(new OpenWindowSearchProvider()); + if (this._settings.get_boolean('search-provider-recent-files')) + this._registerProvider(new RecentFilesSearchProvider(this.recentFilesManager)); + } + + _reloadRemoteProviders() { + const currentTerms = this._terms; + // cancel any active search + if (this._terms.length !== 0) + this._reset(); + + const remoteProviders = this._providers.filter(p => p.isRemoteProvider); + remoteProviders.forEach(provider => { + this._unregisterProvider(provider); + }); + + const providers = RemoteSearch.loadRemoteSearchProviders(this._searchSettings); + providers.forEach(this._registerProvider.bind(this)); + + // restart any active search + if (currentTerms.length > 0) + this.setTerms(currentTerms); + } + + _registerProvider(provider) { + provider.searchInProgress = false; + this._providers.push(provider); + this._ensureProviderDisplay(provider); + } + + _unregisterProvider(provider) { + const index = this._providers.indexOf(provider); + this._providers.splice(index, 1); + + if (provider[this._displayId]) { + provider[this._displayId].destroy(); + delete provider[this._displayId]; + } + } + + _clearSearchTimeout() { + if (this._searchTimeoutId) { + GLib.source_remove(this._searchTimeoutId); + this._searchTimeoutId = null; + } + } + + async _doProviderSearch(provider, previousResults) { + provider.searchInProgress = true; + + let results; + if (this._isSubSearch && previousResults) { + results = await provider.getSubsearchResultSet( + previousResults, + this._terms, + this._cancellable); + } else { + results = await provider.getInitialResultSet( + this._terms, + this._cancellable); + } + + this._results[provider.id] = results; + this._updateResults(provider, results); + } + + _reset() { + this._terms = []; + this._results = {}; + this._clearDisplay(); + this._clearSearchTimeout(); + this._defaultResult = null; + this._startingSearch = false; + + this._updateSearchProgress(); + } + + _doSearch() { + this._startingSearch = false; + + const previousResults = this._results; + this._results = {}; + + this._providers.forEach(provider => { + const previousProviderResults = previousResults[provider.id]; + this._doProviderSearch(provider, previousProviderResults); + }); + + this._updateSearchProgress(); + + this._clearSearchTimeout(); + } + + _onSearchTimeout() { + this._searchTimeoutId = null; + this._doSearch(); + return GLib.SOURCE_REMOVE; + } + + setTerms(terms) { + // Check for the case of making a duplicate previous search before + // setting state of the current search or cancelling the search. + // This will prevent incorrect state being as a result of a duplicate + // search while the previous search is still active. + const searchString = terms.join(' '); + const previousSearchString = this._terms.join(' '); + if (searchString === previousSearchString) + return; + + this._startingSearch = true; + + this.recentFilesManager.cancelCurrentQueries(); + + this._cancellable.cancel(); + this._cancellable.reset(); + + if (terms.length === 0) { + this._reset(); + return; + } + + let isSubSearch = false; + if (this._terms.length > 0) + isSubSearch = searchString.indexOf(previousSearchString) === 0; + + this._terms = terms; + this._isSubSearch = isSubSearch; + this._updateSearchProgress(); + + if (this._searchTimeoutId === null) + this._searchTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 150, this._onSearchTimeout.bind(this)); + + this._highlighter = new Highlighter(this._terms); + + this.emit('terms-changed'); + } + + _ensureProviderDisplay(provider) { + if (provider[this._displayId]) + return; + + let providerDisplay; + if (provider.appInfo) + providerDisplay = new ListSearchResults(provider, this); + else + providerDisplay = new AppSearchResults(provider, this); + providerDisplay.hide(); + this._content.add_child(providerDisplay); + provider[this._displayId] = providerDisplay; + } + + _clearDisplay() { + this._providers.forEach(provider => { + provider[this._displayId]?.clear(); + }); + } + + _maybeSetInitialSelection() { + let newDefaultResult = null; + + const providers = this._providers; + for (let i = 0; i < providers.length; i++) { + const provider = providers[i]; + const display = provider[this._displayId]; + + if (!display.visible) + continue; + + const firstResult = display.getFirstResult(); + if (firstResult) { + newDefaultResult = firstResult; + break; // select this one! + } + } + + if (newDefaultResult !== this._defaultResult) { + this._setSelected(this._defaultResult, false); + this._setSelected(newDefaultResult, this._highlightDefault); + + this._defaultResult = newDefaultResult; + } + } + + get searchInProgress() { + if (this._startingSearch) + return true; + + return this._providers.some(p => p.searchInProgress); + } + + _updateSearchProgress() { + const haveResults = this._providers.some(provider => { + const display = provider[this._displayId]; + return display.getFirstResult() !== null; + }); + + this._statusBin.visible = !haveResults; + if (haveResults) { + this.emit('have-results'); + } else if (!haveResults) { + if (this.searchInProgress) + this._statusText.set_text(_('Searching...')); + else + this._statusText.set_text(_('No results.')); + + this.emit('no-results'); + } + } + + _updateResults(provider, results) { + const terms = this._terms; + const display = provider[this._displayId]; + display.updateSearch(results, terms, () => { + provider.searchInProgress = false; + + this._maybeSetInitialSelection(); + this._updateSearchProgress(); + }); + } + + highlightDefault(highlight) { + this._highlightDefault = highlight; + this._setSelected(this._defaultResult, highlight); + } + + getTopResult() { + return this._defaultResult; + } + + _setSelected(result, selected) { + if (!result) + return; + + if (selected && !result.has_style_pseudo_class('active')) + result.add_style_pseudo_class('active'); + else if (!selected) + result.remove_style_pseudo_class('active'); + } + + hasActiveResult() { + return !!this._defaultResult && this._highlightDefault; + } + + highlightTerms(description) { + if (!description) + return ''; + + return this._highlighter.highlight(description); + } +} + +export class ArcSearchProviderInfo extends BaseMenuItem { + static { + GObject.registerClass(this); + } + + constructor(provider, menuLayout) { + super(menuLayout); + this.provider = provider; + this._menuLayout = menuLayout; + this._appInfo = provider.appInfo; + this.style = 'padding-top: 6px; padding-bottom: 6px;'; + this.x_expand = false; + this.x_align = Clutter.ActorAlign.START; + + this.description = this._appInfo.get_description ? this._appInfo.get_description() : null; + if (this.description) + this.description = this.description.split('\n')[0]; + + this.label = new St.Label({ + text: this._appInfo.get_name(), + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.CENTER, + style: 'font-weight: bold;', + }); + + this._moreLabel = new St.Label({ + x_align: Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.CENTER, + }); + + this.add_child(this.label); + this.add_child(this._moreLabel); + } + + setMoreCount(count) { + this._moreLabel.text = _('+ %d more', '+ %d more', count).format(count); + this._moreLabel.visible = count > 0; + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/searchProviders/openWindows.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/searchProviders/openWindows.js new file mode 100644 index 0000000..878dac1 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/searchProviders/openWindows.js @@ -0,0 +1,120 @@ + +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +import Gio from 'gi://Gio'; +import Meta from 'gi://Meta'; +import St from 'gi://St'; +import Shell from 'gi://Shell'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +/** + * @param {Gio.App} app The app + * @param {int} size App icon size + */ +function createIcon(app, size) { + return app + ? app.create_icon_texture(size) + : new St.Icon({icon_name: 'icon-missing', icon_size: size}); +} + +export const OpenWindowSearchProvider = class { + constructor() { + this.id = 'arcmenu.open-windows'; + this.isRemoteProvider = false; + this.canLaunchSearch = false; + + this._appWindows = []; + this._windowTracker = Shell.WindowTracker.get_default(); + + this.appInfo = { + get_description: () => _('List of open windows across all workspaces'), + get_name: () => _('Open Windows'), + get_id: () => 'arcmenu.open-windows', + get_icon: () => Gio.icon_new_for_string('focus-windows-symbolic'), + should_show: () => true, + }; + } + + getResultMetas(winIds) { + const metas = winIds.map(winId => { + const aw = this._getAppWindow(winId); + return aw ? { + id: winId, + name: aw.window.title ?? aw.app.get_name(), + description: aw.window.get_workspace() + ? _("'%s' on Workspace %d").format(aw.app.get_name(), aw.window.get_workspace().index() + 1) + : aw.app.get_name(), + createIcon: size => createIcon(aw.app, size), + } : undefined; + }).filter(m => m?.name !== undefined && m?.name !== null); + + return new Promise(resolve => resolve(metas)); + } + + filterResults(results, maxNumber) { + return results.slice(0, maxNumber); + } + + getInitialResultSet(terms, _cancellable) { + this._appWindows = getWindows(null).map(window => { + return { + window, + app: this._windowTracker.get_window_app(window), + }; + }); + + return new Promise(resolve => { + const results = this._getFilteredWindowIds(terms, this._appWindows); + resolve(results); + }); + } + + getSubsearchResultSet(previousResults, terms, cancellable) { + return this.getInitialResultSet(terms, cancellable); + } + + activateResult(winId, _terms) { + const window = this._getAppWindow(winId)?.window; + if (window) + Main.activateWindow(window); + } + + launchSearch() { + + } + + _getFilteredWindowIds(terms, appWindows) { + terms = terms.map(term => term.toLowerCase()); + appWindows = appWindows.filter(aw => { + const title = aw.window.title?.toLowerCase(); + const appName = aw.app.get_name()?.toLowerCase(); + const appDescription = aw.app.get_description()?.toLowerCase(); + return terms.some(term => title?.includes(term) || appName?.includes(term) || + appDescription?.includes(term)); + }); + + return appWindows.map(aw => aw.window.get_id().toString()); + } + + _getAppWindow(winId) { + winId = typeof winId === 'string' ? parseInt(winId) : winId; + return this._appWindows.find(aw => aw.window.get_id() === winId); + } +}; + +/** + * @param {Meta.Workspace} workspace + * @returns {Meta.Window} + */ +function getWindows(workspace) { + // We ignore skip-taskbar windows in switchers, but if they are attached + // to their parent, their position in the MRU list may be more appropriate + // than the parent; so start with the complete list ... + const windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, workspace); + // ... map windows to their parent where appropriate ... + return windows.map(w => { + return w.is_attached_dialog() ? w.get_transient_for() : w; + // ... and filter out skip-taskbar windows and duplicates + }).filter((w, i, a) => !w.skip_taskbar && a.indexOf(w) === i); +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/searchProviders/recentFiles.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/searchProviders/recentFiles.js new file mode 100644 index 0000000..3dd9fcf --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/searchProviders/recentFiles.js @@ -0,0 +1,114 @@ +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +import Gio from 'gi://Gio'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +/** + * @param {char} mimeType The MIME type of the resource + * @param {int} size Icon Size + */ +function createIcon(mimeType, size) { + const symbolicIcon = mimeType ? Gio.content_type_get_symbolic_icon(mimeType)?.to_string() : null; + return symbolicIcon + ? new St.Icon({gicon: Gio.icon_new_for_string(symbolicIcon), icon_size: size}) + : new St.Icon({icon_name: 'icon-missing', icon_size: size}); +} + +export const RecentFilesSearchProvider = class { + constructor(recentFilesManager) { + this.id = 'arcmenu.recent-files'; + this.isRemoteProvider = false; + this.canLaunchSearch = false; + this.recentFilesManager = recentFilesManager; + + this._recentFiles = []; + + this.appInfo = { + get_description: () => _('Recent Files'), + get_name: () => _('Recent Files'), + get_id: () => 'arcmenu.recent-files', + get_icon: () => Gio.icon_new_for_string('document-open-recent-symbolic'), + }; + } + + getResultMetas(fileUris) { + const metas = fileUris.map(fileUri => { + const rf = this._getRecentFile(fileUri); + const file = Gio.File.new_for_uri(rf.get_uri()); + return rf ? { + id: fileUri, + name: rf.get_basename(), + description: file.get_parent()?.get_path(), // can be null + createIcon: size => createIcon(this.recentFilesManager.getMimeType(fileUri), size), + } : undefined; + }).filter(m => m?.name !== undefined && m?.name !== null); + + return new Promise(resolve => resolve(metas)); + } + + filterResults(results, maxNumber) { + return results.slice(0, maxNumber); + } + + async getInitialResultSet(terms, _cancellable) { + this.recentFilesManager.cancelCurrentQueries(); + this._recentFiles = []; + const recentFiles = this.recentFilesManager.getRecentFiles(); + + await Promise.all(recentFiles.map(async file => { + try { + const result = await this.recentFilesManager.queryInfoAsync(file); + const recentFile = result.recentFile; + + if (recentFile) + this._recentFiles.push(recentFile); + } catch (e) { + log(e); + } + })); + + return this._getFilteredFileUris(terms, this._recentFiles); + } + + getSubsearchResultSet(previousResults, terms, cancellable) { + return this.getInitialResultSet(terms, cancellable); + } + + activateResult(fileUri, _terms) { + const recentFile = this._getRecentFile(fileUri); + if (recentFile) { + const context = global.create_app_launch_context(0, -1); + + new Promise((resolve, reject) => { + Gio.AppInfo.launch_default_for_uri_async(recentFile.get_uri(), context, null, (o, res) => { + try { + Gio.AppInfo.launch_default_for_uri_finish(res); + resolve(); + } catch (e) { + Main.notifyError(_('Failed to open “%s”').format(recentFile.get_basename()), e.message); + reject(e); + } + }); + }); + } + } + + launchSearch() { + } + + _getFilteredFileUris(terms, recentFiles) { + terms = terms.map(term => term.toLowerCase()); + recentFiles = recentFiles.filter(rf => { + const fileName = rf.get_basename()?.toLowerCase(); + return terms.some(term => fileName?.includes(term)); + }); + + return recentFiles.map(rf => rf.get_uri()); + } + + _getRecentFile(fileUri) { + return this._recentFiles.find(rf => rf.get_uri() === fileUri); + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/searchProviders/searchProviderEmitter.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/searchProviders/searchProviderEmitter.js new file mode 100644 index 0000000..dd1cb90 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/searchProviders/searchProviderEmitter.js @@ -0,0 +1,39 @@ +import {EventEmitter} from 'resource:///org/gnome/shell/misc/signals.js'; +import {InjectionManager} from 'resource:///org/gnome/shell/extensions/extension.js'; +import {SearchController} from 'resource:///org/gnome/shell/ui/searchController.js'; + +/** + * Override SearchController addProvider() and removeProvider() methods to emit signals + * when called. Allows ArcMenu to use custom search providers from extensions that use these methods. + */ + +export class SearchProviderEmitter extends EventEmitter { + constructor() { + super(); + + this._injectionManager = new InjectionManager(); + + this._injectionManager.overrideMethod(SearchController.prototype, 'addProvider', originalMethod => { + const searchProviderEmitter = this; + return function (provider) { + /* eslint-disable-next-line no-invalid-this */ + originalMethod.call(this, provider); + searchProviderEmitter.emit('search-provider-added', provider); + }; + }); + + this._injectionManager.overrideMethod(SearchController.prototype, 'removeProvider', originalMethod => { + const searchProviderEmitter = this; + return function (provider) { + /* eslint-disable-next-line no-invalid-this */ + originalMethod.call(this, provider); + searchProviderEmitter.emit('search-provider-removed', provider); + }; + }); + } + + destroy() { + this._injectionManager.restoreMethod(SearchController.prototype, 'addProvider'); + this._injectionManager.restoreMethod(SearchController.prototype, 'removeProvider'); + } +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/AboutPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/AboutPage.js new file mode 100644 index 0000000..f82c44f --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/AboutPage.js @@ -0,0 +1,348 @@ +import Adw from 'gi://Adw'; +import Gdk from 'gi://Gdk'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import * as Config from 'resource:///org/gnome/Shell/Extensions/js/misc/config.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const AboutPage = GObject.registerClass( +class ArcMenuAboutPage extends Adw.PreferencesPage { + _init(metadata) { + super._init({ + title: _('About'), + icon_name: 'help-about-symbolic', + name: 'AboutPage', + }); + + const PAYPAL_LINK = `https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=53CWA7NR743WC&item_name=Support+${metadata.name}&source=url`; + const PROJECT_DESCRIPTION = _('Application Menu Extension for GNOME'); + const PROJECT_IMAGE = 'settings-arcmenu-logo'; + const SCHEMA_PATH = '/org/gnome/shell/extensions/arcmenu/'; + + // Project Logo, title, description------------------------------------- + const projectHeaderGroup = new Adw.PreferencesGroup(); + const projectHeaderBox = new Gtk.Box({ + orientation: Gtk.Orientation.VERTICAL, + hexpand: false, + vexpand: false, + }); + + const projectImage = new Gtk.Image({ + margin_bottom: 5, + icon_name: PROJECT_IMAGE, + pixel_size: 100, + }); + + const projectTitleLabel = new Gtk.Label({ + label: _('ArcMenu'), + css_classes: ['title-1'], + vexpand: true, + valign: Gtk.Align.FILL, + }); + + const projectDescriptionLabel = new Gtk.Label({ + label: PROJECT_DESCRIPTION, + hexpand: false, + vexpand: false, + }); + projectHeaderBox.append(projectImage); + projectHeaderBox.append(projectTitleLabel); + projectHeaderBox.append(projectDescriptionLabel); + projectHeaderGroup.add(projectHeaderBox); + + this.add(projectHeaderGroup); + // ----------------------------------------------------------------------- + + // Extension/OS Info------------------------------------------------ + const infoGroup = new Adw.PreferencesGroup(); + this.add(infoGroup); + + const projectVersionRow = new Adw.ActionRow({ + title: _('ArcMenu Version'), + }); + projectVersionRow.add_suffix(new Gtk.Label({ + label: metadata['version-name'] ? metadata['version-name'] : metadata.version.toString(), + css_classes: ['dim-label'], + })); + infoGroup.add(projectVersionRow); + + if (metadata.commit) { + const commitRow = new Adw.ActionRow({ + title: _('Git Commit'), + }); + commitRow.add_suffix(new Gtk.Label({ + label: metadata.commit.toString(), + css_classes: ['dim-label'], + })); + infoGroup.add(commitRow); + } + + const gnomeVersionRow = new Adw.ActionRow({ + title: _('GNOME Version'), + }); + gnomeVersionRow.add_suffix(new Gtk.Label({ + label: Config.PACKAGE_VERSION.toString(), + css_classes: ['dim-label'], + })); + infoGroup.add(gnomeVersionRow); + + const osRow = new Adw.ActionRow({ + title: _('OS Name'), + }); + + const name = GLib.get_os_info('NAME'); + const prettyName = GLib.get_os_info('PRETTY_NAME'); + + osRow.add_suffix(new Gtk.Label({ + label: prettyName ? prettyName : name, + css_classes: ['dim-label'], + })); + infoGroup.add(osRow); + + const sessionTypeRow = new Adw.ActionRow({ + title: _('Windowing System'), + }); + sessionTypeRow.add_suffix(new Gtk.Label({ + label: GLib.getenv('XDG_SESSION_TYPE') === 'wayland' ? 'Wayland' : 'X11', + css_classes: ['dim-label'], + })); + infoGroup.add(sessionTypeRow); + // ----------------------------------------------------------------------- + + // Links ----------------------------------------------------------------- + const linksGroup = new Adw.PreferencesGroup(); + this.add(linksGroup); + + const gitlabRow = this._createLinkRow(_('Website'), `${metadata.url}`); + linksGroup.add(gitlabRow); + + const reportIssueRow = this._createLinkRow(_('Report an Issue'), `${metadata.url}/-/issues`); + linksGroup.add(reportIssueRow); + + const donateRow = this._createLinkRow(_('Donate via PayPal'), PAYPAL_LINK); + linksGroup.add(donateRow); + + // ----------------------------------------------------------------------- + + // Save/Load Settings---------------------------------------------------------- + const settingsGroup = new Adw.PreferencesGroup(); + const settingsRow = new Adw.ActionRow({ + title: _('ArcMenu Settings'), + }); + const loadButton = new Gtk.Button({ + label: _('Load'), + valign: Gtk.Align.CENTER, + }); + loadButton.connect('clicked', () => { + this._showFileChooser( + _('Load Settings'), + {action: Gtk.FileChooserAction.OPEN}, + '_Open', + filename => { + if (filename && GLib.file_test(filename, GLib.FileTest.EXISTS)) { + const settingsFile = Gio.File.new_for_path(filename); + const [success_, pid_, stdin, stdout, stderr] = + GLib.spawn_async_with_pipes( + null, + ['dconf', 'load', SCHEMA_PATH], + null, + GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD, + null + ); + + const outputStream = new Gio.UnixOutputStream({fd: stdin, close_fd: true}); + GLib.close(stdout); + GLib.close(stderr); + + outputStream.splice(settingsFile.read(null), + Gio.OutputStreamSpliceFlags.CLOSE_SOURCE | Gio.OutputStreamSpliceFlags.CLOSE_TARGET, null); + } + } + ); + }); + const saveButton = new Gtk.Button({ + label: _('Save'), + valign: Gtk.Align.CENTER, + }); + saveButton.connect('clicked', () => { + this._showFileChooser( + _('Save Settings'), + {action: Gtk.FileChooserAction.SAVE}, + '_Save', + filename => { + const file = Gio.file_new_for_path(filename); + const raw = file.replace(null, false, Gio.FileCreateFlags.NONE, null); + const out = Gio.BufferedOutputStream.new_sized(raw, 4096); + + out.write_all(GLib.spawn_command_line_sync(`dconf dump ${SCHEMA_PATH}`)[1], null); + out.close(null); + } + ); + }); + settingsRow.add_suffix(saveButton); + settingsRow.add_suffix(loadButton); + settingsGroup.add(settingsRow); + this.add(settingsGroup); + // ----------------------------------------------------------------------- + + // Credits / Legal ---------------------------------------------------------------- + const creditsLegalGroup = new Adw.PreferencesGroup(); + this.add(creditsLegalGroup); + + const {subpage: creditsSubPage, page: creditsPage} = this._createSubPage(_('Credits')); + const creditsRow = this._createSubPageRow(_('Credits'), creditsSubPage); + creditsLegalGroup.add(creditsRow); + + const codeByGroup = new Adw.PreferencesGroup({ + title: _('Brought to you by'), + }); + creditsPage.add(codeByGroup); + const creditsRow1 = this._createLinkRow('Andrew Zaech (2019 - current)', 'https://gitlab.com/AndrewZaech', 'ArcMenu maintainer and developer'); + codeByGroup.add(creditsRow1); + const creditsRow2 = this._createLinkRow('Andy C (2017 - 2020)', 'https://gitlab.com/LinxGem33', 'ArcMenu founder, maintainer, and digital art designer'); + codeByGroup.add(creditsRow2); + const creditsRow3 = this._createLinkRow('Alexander Rüedlinger (2017)', 'https://github.com/lexruee', 'Developer'); + codeByGroup.add(creditsRow3); + + const historyGroup = new Adw.PreferencesGroup({ + title: _('History'), + }); + creditsPage.add(historyGroup); + + const historyText = 'ArcMenu was first released in 2017 by Andy C. The original ArcMenu project can be found here.\n\n' + + 'In 2017, ArcMenu started as a fork of the Zorin menu extension by Zorin OS. ' + + "As it's own separate project, ArcMenu rapidly began developing innovative features and quickly diverged " + + 'away from Zorin menu thanks to the works of Andy C, ' + + 'Andrew Zaech, Alexander Rüedlinger, and other contributors. ' + + "ArcMenu has been rewritten from the ground up since it's inception and has it's own separate, original, and unique code base, unrelated to that of Zorin menu."; + + const historyRow = new Adw.ActionRow({ + title: historyText, + use_markup: true, + }); + historyGroup.add(historyRow); + + const contributionsByGroup = new Adw.PreferencesGroup({ + title: _('Contributions by'), + }); + creditsPage.add(contributionsByGroup); + const contributorsRow = this._createLinkRow(_('Contributors'), 'https://gitlab.com/arcmenu/ArcMenu#contributors'); + contributionsByGroup.add(contributorsRow); + const translatorsRow = this._createLinkRow(_('Translators'), 'https://gitlab.com/arcmenu/ArcMenu#translators'); + contributionsByGroup.add(translatorsRow); + + const artworkByGroup = new Adw.PreferencesGroup({ + title: _('Artwork by'), + }); + creditsPage.add(artworkByGroup); + const andycArtworkRow = this._createLinkRow('Andy C', 'https://gitlab.com/LinxGem33', 'ArcMenu logo and other ArcMenu icon assets'); + artworkByGroup.add(andycArtworkRow); + const azArtworkRow = this._createLinkRow('Andrew Zaech', 'https://gitlab.com/AndrewZaech', 'Modification and creation of some ArcMenu icon assets'); + artworkByGroup.add(azArtworkRow); + + const {subpage: legalSubPage, page: legalPage} = this._createSubPage(_('Legal')); + const legalRow = this._createSubPageRow(_('Legal'), legalSubPage); + creditsLegalGroup.add(legalRow); + + const gnuSoftwareGroup = new Adw.PreferencesGroup(); + legalPage.add(gnuSoftwareGroup); + + const warrantyLabel = _('This program comes with absolutely no warranty.'); + /* TRANSLATORS: this is the program license url; the string contains the name of the license as link text.*/ + const urlLabel = _('See the %s for details.').format('https://gnu.org/licenses/old-licenses/gpl-2.0.html', _('GNU General Public License, version 2 or later')); + + const gnuSofwareLabel = new Gtk.Label({ + label: `${_(warrantyLabel)}\n${_(urlLabel)}`, + use_markup: true, + justify: Gtk.Justification.CENTER, + }); + gnuSoftwareGroup.add(gnuSofwareLabel); + // ----------------------------------------------------------------------- + } + + _createSubPage(title) { + const subpage = new Adw.NavigationPage({ + title, + }); + + const headerBar = new Adw.HeaderBar(); + + const sidebarToolBarView = new Adw.ToolbarView(); + + sidebarToolBarView.add_top_bar(headerBar); + subpage.set_child(sidebarToolBarView); + const page = new Adw.PreferencesPage(); + sidebarToolBarView.set_content(page); + + return {subpage, page}; + } + + _createSubPageRow(title, subpage) { + const subpageRow = new Adw.ActionRow({ + title: _(title), + activatable: true, + }); + + subpageRow.connect('activated', () => { + this.get_root().push_subpage(subpage); + }); + + const goNextImage = new Gtk.Image({ + gicon: Gio.icon_new_for_string('go-next-symbolic'), + halign: Gtk.Align.END, + valign: Gtk.Align.CENTER, + hexpand: false, + vexpand: false, + }); + + subpageRow.add_suffix(goNextImage); + return subpageRow; + } + + _createLinkRow(title, uri, subtitle = null) { + const image = new Gtk.Image({ + icon_name: 'adw-external-link-symbolic', + valign: Gtk.Align.CENTER, + }); + const linkRow = new Adw.ActionRow({ + title: _(title), + activatable: true, + tooltip_text: uri, + subtitle: subtitle ? _(subtitle) : null, + }); + linkRow.connect('activated', () => { + Gtk.show_uri(this.get_root(), uri, Gdk.CURRENT_TIME); + }); + linkRow.add_suffix(image); + + return linkRow; + } + + _showFileChooser(title, params, acceptBtn, acceptHandler) { + const dialog = new Gtk.FileChooserDialog({ + title: _(title), + transient_for: this.get_root(), + modal: true, + action: params.action, + }); + dialog.add_button('_Cancel', Gtk.ResponseType.CANCEL); + dialog.add_button(acceptBtn, Gtk.ResponseType.ACCEPT); + + dialog.connect('response', (self, response) => { + if (response === Gtk.ResponseType.ACCEPT) { + try { + acceptHandler(dialog.get_file().get_path()); + } catch (e) { + log(`ArcMenu - Filechooser error: ${e}`); + } + } + dialog.destroy(); + }); + + dialog.show(); + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/GeneralPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/GeneralPage.js new file mode 100644 index 0000000..609a711 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/GeneralPage.js @@ -0,0 +1,552 @@ +import Adw from 'gi://Adw'; +import GObject from 'gi://GObject'; +import Gdk from 'gi://Gdk'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import Gtk from 'gi://Gtk'; + +import * as Constants from '../constants.js'; +import * as PW from '../prefsWidgets.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +const forbiddenKeyvals = [ + Gdk.KEY_Home, + Gdk.KEY_Left, + Gdk.KEY_Up, + Gdk.KEY_Right, + Gdk.KEY_Down, + Gdk.KEY_Page_Up, + Gdk.KEY_Page_Down, + Gdk.KEY_End, + Gdk.KEY_Tab, + Gdk.KEY_KP_Enter, + Gdk.KEY_Return, + Gdk.KEY_Mode_switch, +]; + +export const GeneralPage = GObject.registerClass( +class ArcMenuGeneralPage extends Adw.PreferencesPage { + _init(settings) { + super._init({ + title: _('General'), + icon_name: 'go-home-symbolic', + name: 'GeneralPage', + }); + this._settings = settings; + + const menuDisplayGroup = new Adw.PreferencesGroup({ + title: _('Panel Display Options'), + }); + this.add(menuDisplayGroup); + + // Show Activities Row---------------------------------------------------------------------------- + const showActivitiesSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + active: this._settings.get_boolean('show-activities-button'), + }); + showActivitiesSwitch.connect('notify::active', widget => { + this._settings.set_boolean('show-activities-button', widget.get_active()); + }); + const showActivitiesRow = new Adw.ActionRow({ + title: _('Show Activities Button'), + subtitle: _('Dash to Panel may conflict with this setting'), + activatable_widget: showActivitiesSwitch, + }); + showActivitiesRow.add_suffix(showActivitiesSwitch); + // ----------------------------------------------------------------------------------------------- + + // Position in Panel Row------------------------------------------------------------- + const menuPositions = new Gtk.StringList(); + menuPositions.append(_('Left')); + menuPositions.append(_('Center')); + menuPositions.append(_('Right')); + const menuPositionRow = new Adw.ComboRow({ + title: _('Position in Panel'), + model: menuPositions, + selected: this._settings.get_enum('position-in-panel'), + }); + menuPositionRow.connect('notify::selected', widget => { + if (widget.selected === Constants.MenuPosition.CENTER) + menuAlignmentRow.show(); + else + menuAlignmentRow.hide(); + this._settings.set_enum('position-in-panel', widget.selected); + }); + // -------------------------------------------------------------------------------------- + + // Menu Alignment row-------------------------------------------------------------------- + const menuAlignmentScale = new Gtk.Scale({ + valign: Gtk.Align.CENTER, + orientation: Gtk.Orientation.HORIZONTAL, + adjustment: new Gtk.Adjustment({lower: 0, upper: 100, step_increment: 1, page_increment: 1, page_size: 0}), + digits: 0, round_digits: 0, hexpand: true, + + }); + menuAlignmentScale.set_value(this._settings.get_int('menu-position-alignment')); + menuAlignmentScale.add_mark(0, Gtk.PositionType.BOTTOM, _('Left')); + menuAlignmentScale.add_mark(50, Gtk.PositionType.BOTTOM, _('Center')); + menuAlignmentScale.add_mark(100, Gtk.PositionType.BOTTOM, _('Right')); + + menuAlignmentScale.connect('value-changed', widget => { + this._settings.set_int('menu-position-alignment', widget.get_value()); + }); + const menuAlignmentRow = new Adw.ActionRow({ + title: _('Menu Alignment'), + activatable_widget: menuAlignmentScale, + visible: this._settings.get_enum('position-in-panel') === Constants.MenuPosition.CENTER, + }); + menuAlignmentRow.add_suffix(menuAlignmentScale); + // ------------------------------------------------------------------------------------- + + // Mulit Monitor Row ------------------------------------------------------------------- + const multiMonitorSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + active: this._settings.get_boolean('multi-monitor'), + }); + multiMonitorSwitch.connect('notify::active', widget => { + const multiMonitor = widget.get_active(); + this._settings.set_boolean('multi-monitor', multiMonitor); + this.menuHotkeyRow.setMultiMonitor(multiMonitor); + }); + + const multiMonitorRow = new Adw.ActionRow({ + title: _('Display ArcMenu on all Panels'), + subtitle: _('Dash to Panel or App Icons Taskbar extension required'), + activatable_widget: multiMonitorSwitch, + }); + multiMonitorRow.add_suffix(multiMonitorSwitch); + // -------------------------------------------------------------------------------------- + + // Prefer Top Panel ------------------------------------------------------------------- + const preferTopPanelSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + active: this._settings.get_boolean('dash-to-panel-standalone'), + }); + preferTopPanelSwitch.connect('notify::active', widget => { + this._settings.set_boolean('dash-to-panel-standalone', widget.get_active()); + }); + + const preferTopPanelRow = new Adw.ActionRow({ + title: _('Always Prefer Top Panel'), + subtitle: _("Useful with Dash to Panel setting 'Keep original gnome-shell top panel'"), + activatable_widget: multiMonitorSwitch, + }); + preferTopPanelRow.add_suffix(preferTopPanelSwitch); + // -------------------------------------------------------------------------------------- + + // Add the rows to the group + menuDisplayGroup.add(menuPositionRow); + menuDisplayGroup.add(menuAlignmentRow); + menuDisplayGroup.add(multiMonitorRow); + menuDisplayGroup.add(preferTopPanelRow); + menuDisplayGroup.add(showActivitiesRow); + + const generalGroup = new Adw.PreferencesGroup({ + title: _('General Settings'), + }); + this.add(generalGroup); + + this.menuHotkeyRow = this._createExpanderRow(_('ArcMenu Hotkey'), true); + this.standaloneRunnerRow = this._createExpanderRow(_('Standalone Runner Menu'), false); + generalGroup.add(this.menuHotkeyRow); + generalGroup.add(this.standaloneRunnerRow); + + const hideOverviewSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + active: this._settings.get_boolean('hide-overview-on-startup'), + }); + hideOverviewSwitch.connect('notify::active', widget => { + this._settings.set_boolean('hide-overview-on-startup', widget.get_active()); + }); + + const hideOverviewRow = new Adw.ActionRow({ + title: _('Hide Overview on Startup'), + activatable_widget: hideOverviewSwitch, + }); + hideOverviewRow.add_suffix(hideOverviewSwitch); + generalGroup.add(hideOverviewRow); + } + + _createExpanderRow(title, isMenuHotkey) { + const hotkeySetting = isMenuHotkey ? 'arcmenu-hotkey' : 'runner-hotkey'; + const primaryMonitorSetting = isMenuHotkey ? 'hotkey-open-primary-monitor' + : 'runner-hotkey-open-primary-monitor'; + const accelerator = this._settings.get_strv(hotkeySetting).toString(); + const hotkeyString = this.acceleratorToLabel(accelerator); + const hotkeyLabel = new Gtk.Label({ + label: hotkeyString, + css_classes: ['dim-label'], + }); + const customHotkeyRow = new Adw.ActionRow({ + title: _(title), + activatable: true, + }); + customHotkeyRow.add_suffix(hotkeyLabel); + + customHotkeyRow.connect('activated', () => { + const dialog = new HotkeyDialog(isMenuHotkey, this._settings, this); + dialog.show(); + dialog.inhibitSystemShortcuts(); + dialog.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + if (dialog.resultsText) + this._settings.set_strv(hotkeySetting, [dialog.resultsText]); + else + this._settings.set_strv(hotkeySetting, []); + hotkeyLabel.label = this.acceleratorToLabel(dialog.resultsText); + } + dialog.restoreSystemShortcuts(); + dialog.destroy(); + }); + }); + + const customizeButton = new Gtk.Button({ + icon_name: 'emblem-system-symbolic', + css_classes: ['flat'], + valign: Gtk.Align.CENTER, + visible: isMenuHotkey ? this._settings.get_boolean('multi-monitor') : true, + }); + customHotkeyRow.add_suffix(customizeButton); + + customizeButton.connect('clicked', () => { + const windowPreviewOptions = new HotkeyOptionsDialog(this._settings, _('Hotkey Options'), this.get_root(), primaryMonitorSetting); + windowPreviewOptions.show(); + }); + + customHotkeyRow.setMultiMonitor = bool => { + customizeButton.visible = bool; + }; + + return customHotkeyRow; + } + + acceleratorToLabel(accelerator) { + if (!accelerator) + return _('Disabled'); + const [ok, key, mods] = Gtk.accelerator_parse(accelerator); + if (!ok) + return ''; + + return Gtk.accelerator_get_label(key, mods); + } +}); + +var HotkeyOptionsDialog = GObject.registerClass( +class ArcMenuHotkeyOptionsDialog extends PW.DialogWindow { + _init(settings, title, parent, primaryMonitorSetting) { + super._init(title, parent); + this.set_default_size(460, 150); + this.search_enabled = false; + this.resizable = false; + + const primaryMonitorSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + active: settings.get_boolean(primaryMonitorSetting), + }); + primaryMonitorSwitch.connect('notify::active', widget => { + settings.set_boolean(primaryMonitorSetting, widget.get_active()); + }); + const primaryMonitorRow = new Adw.ActionRow({ + title: _('Open on Primary Monitor'), + activatable_widget: primaryMonitorSwitch, + }); + primaryMonitorRow.add_suffix(primaryMonitorSwitch); + + this.pageGroup.add(primaryMonitorRow); + } +}); + +var HotkeyDialog = GObject.registerClass({ + Signals: { + 'response': {param_types: [GObject.TYPE_INT]}, + }, +}, +class ArcMenuHotkeyDialog extends Adw.Window { + _init(isMenuHotkey, settings, parent) { + super._init({ + modal: true, + title: _('Modify Hotkey'), + transient_for: parent.get_root(), + resizable: false, + }); + this._settings = settings; + this._parentWindow = parent.get_root(); + + this.set_default_size(460, 275); + + const eventControllerKey = new Gtk.EventControllerKey(); + this.add_controller(eventControllerKey); + + const shortcutController = new Gtk.ShortcutController(); + this.add_controller(shortcutController); + const escapeShortcut = new Gtk.Shortcut({ + trigger: Gtk.ShortcutTrigger.parse_string('Escape'), + action: Gtk.ShortcutAction.parse_string('action(window.close)'), + }); + shortcutController.add_shortcut(escapeShortcut); + + this.connect('destroy', () => { + this.restoreSystemShortcuts(); + }); + + const sidebarToolBarView = new Adw.ToolbarView({ + top_bar_style: Adw.ToolbarStyle.RAISED, + }); + this.set_content(sidebarToolBarView); + + const headerBar = new Adw.HeaderBar({ + show_end_title_buttons: false, + show_start_title_buttons: false, + }); + sidebarToolBarView.add_top_bar(headerBar); + + const applyButton = new Gtk.Button({ + label: _('Apply'), + halign: Gtk.Align.END, + hexpand: false, + css_classes: ['suggested-action'], + visible: false, + }); + applyButton.connect('clicked', () => { + this.emit('response', Gtk.ResponseType.APPLY); + }); + headerBar.pack_end(applyButton); + + const cancelButton = new Gtk.Button({ + label: _('Cancel'), + halign: Gtk.Align.START, + hexpand: false, + visible: false, + }); + cancelButton.connect('clicked', () => this.close()); + headerBar.pack_start(cancelButton); + + const content = new Gtk.Box({ + orientation: Gtk.Orientation.VERTICAL, + spacing: 18, + margin_top: 12, + margin_bottom: 12, + margin_start: 12, + margin_end: 12, + }); + sidebarToolBarView.set_content(content); + + const keyLabel = new Gtk.Label({ + /* TRANSLATORS: %s is replaced with a description of the keyboard shortcut, don't translate/transliterate %s*/ + label: _('Enter a new shortcut to change %s').format(isMenuHotkey ? _('ArcMenu Hotkey') : _('Standalone Runner Hotkey')), + use_markup: true, + xalign: .5, + wrap: true, + }); + content.append(keyLabel); + + const directory = GLib.path_get_dirname(import.meta.url); + const rootDirectory = GLib.path_get_dirname(directory); + const iconPath = '/icons/hicolor/16x16/actions/settings-keyboard.svg'; + + const keyboardImage = new Gtk.Picture({ + file: Gio.File.new_for_uri(`${rootDirectory}${iconPath}`), + halign: Gtk.Align.CENTER, + valign: Gtk.Align.CENTER, + can_shrink: false, + }); + content.append(keyboardImage); + + const shortcutLabel = new Gtk.ShortcutLabel({ + hexpand: true, + halign: Gtk.Align.CENTER, + valign: Gtk.Align.START, + vexpand: false, + visible: false, + disabled_text: _('Disabled'), + }); + content.append(shortcutLabel); + + const conflictLabel = new Gtk.Label({ + label: _('Press Esc to cancel or Backspace to disable the keyboard shortcut.'), + use_markup: true, + wrap: true, + }); + content.append(conflictLabel); + + // Based on code from PaperWM prefsKeybinding.js https://github.com/paperwm/PaperWM + eventControllerKey.connect('key-pressed', (controller, keyval, keycode, state) => { + let modmask = state & Gtk.accelerator_get_default_mod_mask(); + let keyvalLower = Gdk.keyval_to_lower(keyval); + + // Normalize + if (keyvalLower === Gdk.KEY_ISO_Left_Tab) + keyvalLower = Gdk.KEY_Tab; + + // Put Shift back if it changed the case of the key + if (keyvalLower !== keyval) + modmask |= Gdk.ModifierType.SHIFT_MASK; + + const event = controller.get_current_event(); + const isModifier = event.is_modifier(); + + // Backspace deletes + if (!isModifier && modmask === 0 && keyvalLower === Gdk.KEY_BackSpace) { + shortcutLabel.accelerator = null; + shortcutLabel.visible = true; + cancelButton.visible = true; + keyboardImage.visible = false; + conflictLabel.visible = false; + applyButton.visible = true; + return Gdk.EVENT_STOP; + } + + // Remove CapsLock + modmask &= ~Gdk.ModifierType.LOCK_MASK; + + const combo = {mods: modmask, keycode, keyval: keyvalLower}; + if (!this._isValidBinding(combo)) + return Gdk.EVENT_STOP; + + this.resultsText = Gtk.accelerator_name(keyval, modmask); + const conflicts = this.findConflicts(this.resultsText); + + shortcutLabel.accelerator = this.resultsText; + shortcutLabel.visible = true; + cancelButton.visible = true; + keyboardImage.visible = false; + if (conflicts) { + applyButton.visible = false; + conflictLabel.css_classes = ['error']; + conflictLabel.visible = true; + conflictLabel.label = _('Conflict with %s hotkey').format(`${conflicts.conflict}`); + } else { + conflictLabel.visible = false; + applyButton.visible = true; + } + + return Gdk.EVENT_STOP; + }); + } + + // Based on code from PaperWM prefsKeybinding.js https://github.com/paperwm/PaperWM + _isValidBinding(combo) { + if ((combo.mods === 0 || combo.mods === Gdk.ModifierType.SHIFT_MASK) && combo.keycode !== 0) { + const keyval = combo.keyval; + if ((keyval >= Gdk.KEY_a && keyval <= Gdk.KEY_z) || + (keyval >= Gdk.KEY_A && keyval <= Gdk.KEY_Z) || + (keyval >= Gdk.KEY_0 && keyval <= Gdk.KEY_9) || + (keyval >= Gdk.KEY_kana_fullstop && keyval <= Gdk.KEY_semivoicedsound) || + (keyval >= Gdk.KEY_Arabic_comma && keyval <= Gdk.KEY_Arabic_sukun) || + (keyval >= Gdk.KEY_Serbian_dje && keyval <= Gdk.KEY_Cyrillic_HARDSIGN) || + (keyval >= Gdk.KEY_Greek_ALPHAaccent && keyval <= Gdk.KEY_Greek_omega) || + (keyval >= Gdk.KEY_hebrew_doublelowline && keyval <= Gdk.KEY_hebrew_taf) || + (keyval >= Gdk.KEY_Thai_kokai && keyval <= Gdk.KEY_Thai_lekkao) || + (keyval >= Gdk.KEY_Hangul_Kiyeog && keyval <= Gdk.KEY_Hangul_J_YeorinHieuh) || + (keyval === Gdk.KEY_space && combo.mods === 0) || + forbiddenKeyvals.includes(keyval)) + return false; + } + + // Empty binding + if (combo.keyval === 0 && combo.mods === 0 && combo.keycode === 0) + return false; + + // Allow use of Super_L and Super_R hotkeys + if (combo.keyval === Gdk.KEY_Super_L || combo.keyval === Gdk.KEY_Super_R) + return true; + + // Allow Tab in addition to accelerators allowed by GTK + if (!Gtk.accelerator_valid(combo.keyval, combo.mods) && + (combo.keyval !== Gdk.KEY_Tab || combo.mods === 0)) + return false; + + return true; + } + + getConflictSettings() { + if (!this._conflictSettings) { + this._conflictSettings = [ + new Gio.Settings({schema_id: 'org.gnome.mutter.keybindings'}), + new Gio.Settings({schema_id: 'org.gnome.mutter.wayland.keybindings'}), + new Gio.Settings({schema_id: 'org.gnome.shell.keybindings'}), + new Gio.Settings({schema_id: 'org.gnome.desktop.wm.keybindings'}), + ]; + } + + return this._conflictSettings; + } + + generateKeycomboMap(settings) { + const map = {}; + for (const name of settings.list_keys()) { + const value = settings.get_value(name); + if (value.get_type_string() !== 'as') + continue; + + for (const combo of value.deep_unpack()) { + if (combo === '0|0') + continue; + if (map[combo]) + map[combo].push(name); + else + map[combo] = [name]; + } + } + + return map; + } + + findConflicts(newHotkey) { + const schemas = this.getConflictSettings(); + let conflicts = null; + + const newHotkeyMap = {}; + newHotkeyMap[newHotkey] = ['New Hotkey']; + + for (const settings of schemas) { + const against = this.generateKeycomboMap(settings); + for (const combo in newHotkeyMap) { + if (against[combo]) { + conflicts = { + conflict: against[combo], + name: newHotkeyMap[combo], + }; + } + } + } + + const arcMenuHotkeys = {}; + const [runnerHotkey] = this._settings.get_strv('runner-hotkey'); + const [menuHotkey] = this._settings.get_strv('arcmenu-hotkey'); + + arcMenuHotkeys[menuHotkey] = [_('ArcMenu')]; + arcMenuHotkeys[runnerHotkey] = [_('Standlone Runner')]; + + for (const combo in newHotkeyMap) { + if (arcMenuHotkeys[combo]) { + conflicts = { + conflict: arcMenuHotkeys[combo], + name: newHotkeyMap[combo], + }; + } + } + + return conflicts; + } + + inhibitSystemShortcuts() { + this.grab_focus(); + + // Note - surface.inhibit_system_shortcuts() seems to need a different surface on X11 vs Wayland? + const isWayland = GLib.getenv('XDG_SESSION_TYPE') === 'wayland'; + const surface = isWayland ? this.get_surface() : this._parentWindow.get_surface(); + + surface.inhibit_system_shortcuts(null); + } + + restoreSystemShortcuts() { + // Note - surface.inhibit_system_shortcuts() seems to need a different surface on X11 vs Wayland? + const isWayland = GLib.getenv('XDG_SESSION_TYPE') === 'wayland'; + const surface = isWayland ? this.get_surface() : this._parentWindow.get_surface(); + + if (surface) + surface.restore_system_shortcuts(); + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/FineTunePage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/FineTunePage.js new file mode 100644 index 0000000..edcf74b --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/FineTunePage.js @@ -0,0 +1,210 @@ +import Adw from 'gi://Adw'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import {SubPage} from './SubPage.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const FineTunePage = GObject.registerClass( +class ArcMenuFineTunePage extends SubPage { + _init(settings, params) { + super._init(settings, params); + + this.disableFadeEffect = this._settings.get_boolean('disable-scrollview-fade-effect'); + this.alphabetizeAllPrograms = this._settings.get_boolean('alphabetize-all-programs'); + this.multiLinedLabels = this._settings.get_boolean('multi-lined-labels'); + this.disableTooltips = this._settings.get_boolean('disable-tooltips'); + this.disableRecentApps = this._settings.get_boolean('disable-recently-installed-apps'); + this.showHiddenRecentFiles = this._settings.get_boolean('show-hidden-recent-files'); + + const miscGroup = new Adw.PreferencesGroup(); + this.add(miscGroup); + + const subMenusSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + subMenusSwitch.set_active(this._settings.get_boolean('show-category-sub-menus')); + subMenusSwitch.connect('notify::active', widget => { + this._settings.set_boolean('show-category-sub-menus', widget.get_active()); + }); + const subMenusRow = new Adw.ActionRow({ + title: _('Show Category Sub Menus'), + activatable_widget: subMenusSwitch, + }); + subMenusRow.add_suffix(subMenusSwitch); + miscGroup.add(subMenusRow); + + const appDescriptionsSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + appDescriptionsSwitch.set_active(this._settings.get_boolean('apps-show-extra-details')); + appDescriptionsSwitch.connect('notify::active', widget => { + this._settings.set_boolean('apps-show-extra-details', widget.get_active()); + }); + const appDescriptionsRow = new Adw.ActionRow({ + title: _('Show Application Descriptions'), + activatable_widget: appDescriptionsSwitch, + }); + appDescriptionsRow.add_suffix(appDescriptionsSwitch); + miscGroup.add(appDescriptionsRow); + + const fadeEffectSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + fadeEffectSwitch.set_active(this._settings.get_boolean('disable-scrollview-fade-effect')); + fadeEffectSwitch.connect('notify::active', widget => { + this._settings.set_boolean('disable-scrollview-fade-effect', widget.get_active()); + }); + const fadeEffectRow = new Adw.ActionRow({ + title: _('Disable ScrollView Fade Effects'), + activatable_widget: fadeEffectSwitch, + }); + fadeEffectRow.add_suffix(fadeEffectSwitch); + miscGroup.add(fadeEffectRow); + + const tooltipSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + tooltipSwitch.set_active(this.disableTooltips); + tooltipSwitch.connect('notify::active', widget => { + this._settings.set_boolean('disable-tooltips', widget.get_active()); + }); + const tooltipRow = new Adw.ActionRow({ + title: _('Disable Tooltips'), + activatable_widget: tooltipSwitch, + }); + tooltipRow.add_suffix(tooltipSwitch); + miscGroup.add(tooltipRow); + + const alphabetizeAllProgramsSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + alphabetizeAllProgramsSwitch.set_active(this._settings.get_boolean('alphabetize-all-programs')); + alphabetizeAllProgramsSwitch.connect('notify::active', widget => { + this._settings.set_boolean('alphabetize-all-programs', widget.get_active()); + }); + const alphabetizeAllProgramsRow = new Adw.ActionRow({ + title: _("Alphabetize 'All Programs' Category"), + activatable_widget: alphabetizeAllProgramsSwitch, + }); + alphabetizeAllProgramsRow.add_suffix(alphabetizeAllProgramsSwitch); + miscGroup.add(alphabetizeAllProgramsRow); + + const hiddenFilesSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + hiddenFilesSwitch.set_active(this._settings.get_boolean('show-hidden-recent-files')); + hiddenFilesSwitch.connect('notify::active', widget => { + this._settings.set_boolean('show-hidden-recent-files', widget.get_active()); + }); + const hiddenFilesRow = new Adw.ActionRow({ + title: _('Show Hidden Recent Files'), + activatable_widget: hiddenFilesSwitch, + }); + hiddenFilesRow.add_suffix(hiddenFilesSwitch); + miscGroup.add(hiddenFilesRow); + + const multiLinedLabelSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + multiLinedLabelSwitch.set_active(this._settings.get_boolean('multi-lined-labels')); + multiLinedLabelSwitch.connect('notify::active', widget => { + this._settings.set_boolean('multi-lined-labels', widget.get_active()); + }); + const multiLinedLabelRow = new Adw.ActionRow({ + title: _('Multi-Lined Labels'), + subtitle: _('Allow application labels to span multiple lines on grid style layouts'), + activatable_widget: multiLinedLabelSwitch, + }); + multiLinedLabelRow.add_suffix(multiLinedLabelSwitch); + miscGroup.add(multiLinedLabelRow); + + const iconStyleGroup = new Adw.PreferencesGroup(); + this.add(iconStyleGroup); + + const iconTypes = new Gtk.StringList(); + iconTypes.append(_('Full Color')); + iconTypes.append(_('Symbolic')); + const categoryIconTypeRow = new Adw.ComboRow({ + title: _('Category Icon Type'), + subtitle: _('Some icon themes may not include selected icon type'), + model: iconTypes, + selected: this._settings.get_enum('category-icon-type'), + }); + categoryIconTypeRow.connect('notify::selected', widget => { + this._settings.set_enum('category-icon-type', widget.selected); + }); + iconStyleGroup.add(categoryIconTypeRow); + + const shortcutsIconTypeRow = new Adw.ComboRow({ + title: _('Shortcuts Icon Type'), + subtitle: _('Some icon themes may not include selected icon type'), + model: iconTypes, + selected: this._settings.get_enum('shortcut-icon-type'), + }); + shortcutsIconTypeRow.connect('notify::selected', widget => { + this._settings.set_enum('shortcut-icon-type', widget.selected); + }); + iconStyleGroup.add(shortcutsIconTypeRow); + + const recentAppsGroup = new Adw.PreferencesGroup(); + this.add(recentAppsGroup); + + const recentAppsSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + recentAppsSwitch.connect('notify::active', widget => { + if (widget.get_active()) + clearRecentAppsRow.hide(); + else + clearRecentAppsRow.show(); + this._settings.set_boolean('disable-recently-installed-apps', widget.get_active()); + }); + const recentAppsRow = new Adw.ActionRow({ + title: _('Disable New Apps Tracker'), + activatable_widget: recentAppsSwitch, + }); + recentAppsRow.add_suffix(recentAppsSwitch); + recentAppsGroup.add(recentAppsRow); + + const clearRecentAppsButton = new Gtk.Button({ + halign: Gtk.Align.END, + valign: Gtk.Align.CENTER, + label: _('Clear All'), + }); + const sensitive = this._settings.get_strv('recently-installed-apps').length > 0; + clearRecentAppsButton.set_sensitive(sensitive); + clearRecentAppsButton.connect('clicked', () => { + clearRecentAppsButton.set_sensitive(false); + this._settings.reset('recently-installed-apps'); + }); + const clearRecentAppsRow = new Adw.ActionRow({ + title: _("Clear Apps Marked 'New'"), + activatable_widget: clearRecentAppsButton, + }); + clearRecentAppsRow.add_suffix(clearRecentAppsButton); + recentAppsGroup.add(clearRecentAppsRow); + + recentAppsSwitch.set_active(this._settings.get_boolean('disable-recently-installed-apps')); + + this.restoreDefaults = () => { + this.alphabetizeAllPrograms = this._settings.get_default_value('alphabetize-all-programs').unpack(); + this.multiLinedLabels = this._settings.get_default_value('multi-lined-labels').unpack(); + this.disableTooltips = this._settings.get_default_value('disable-tooltips').unpack(); + this.disableFadeEffect = this._settings.get_default_value('disable-scrollview-fade-effect').unpack(); + this.disableRecentApps = this._settings.get_default_value('disable-recently-installed-apps').unpack(); + this.showHiddenRecentFiles = this._settings.get_default_value('show-hidden-recent-files').unpack(); + alphabetizeAllProgramsSwitch.set_active(this.alphabetizeAllPrograms); + multiLinedLabelSwitch.set_active(this.multiLinedLabels); + tooltipSwitch.set_active(this.disableTooltips); + fadeEffectSwitch.set_active(this.disableFadeEffect); + recentAppsSwitch.set_active(this.disableRecentApps); + hiddenFilesSwitch.set_active(this.showHiddenRecentFiles); + categoryIconTypeRow.selected = 0; + shortcutsIconTypeRow.selected = 1; + appDescriptionsSwitch.set_active(this._settings.get_default_value('apps-show-extra-details').unpack()); + subMenusSwitch.set_active(this._settings.get_default_value('show-category-sub-menus').unpack()); + }; + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/LayoutTweaksPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/LayoutTweaksPage.js new file mode 100644 index 0000000..c160b2a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/LayoutTweaksPage.js @@ -0,0 +1,961 @@ +import Adw from 'gi://Adw'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import * as Constants from '../../constants.js'; +import {ListPinnedPage} from './ListPinnedPage.js'; +import {ListOtherPage} from './ListOtherPage.js'; +import * as PW from '../../prefsWidgets.js'; +import * as SettingsUtils from '../SettingsUtils.js'; +import {SubPage} from './SubPage.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const LayoutTweaksPage = GObject.registerClass( +class ArcMenuLayoutTweaksPage extends SubPage { + _init(settings, params) { + super._init(settings, params); + + this.restoreDefaultsButton.visible = false; + this._createLayout(); + } + + setActiveLayout(menuLayout) { + const layoutName = SettingsUtils.getMenuLayoutName(menuLayout); + this.title = _('%s Layout Tweaks').format(_(layoutName)); + + for (const child of this.page.children) + this.page.remove(child); + + this.page.children = []; + this._createLayout(menuLayout); + } + + _createLayout(menuLayout) { + if (!menuLayout) + menuLayout = this._settings.get_enum('menu-layout'); + + switch (menuLayout) { + case Constants.MenuLayout.ARCMENU: + this._loadArcMenuTweaks(); + break; + case Constants.MenuLayout.BRISK: + this._loadBriskMenuTweaks(); + break; + case Constants.MenuLayout.WHISKER: + this._loadWhiskerMenuTweaks(); + break; + case Constants.MenuLayout.GNOME_MENU: + this._loadGnomeMenuTweaks(); + break; + case Constants.MenuLayout.MINT: + this._loadMintMenuTweaks(); + break; + case Constants.MenuLayout.ELEMENTARY: + this._loadElementaryTweaks(); + break; + case Constants.MenuLayout.GNOME_OVERVIEW: + this._loadGnomeOverviewTweaks(); + break; + case Constants.MenuLayout.REDMOND: + this._loadRedmondMenuTweaks(); + break; + case Constants.MenuLayout.UNITY: + this._loadUnityTweaks(); + break; + case Constants.MenuLayout.RAVEN: + this._loadRavenTweaks(); + break; + case Constants.MenuLayout.BUDGIE: + this._loadBudgieMenuTweaks(); + break; + case Constants.MenuLayout.INSIDER: + this._loadInsiderMenuTweaks(); + break; + case Constants.MenuLayout.RUNNER: + this._loadRunnerMenuTweaks(); + break; + case Constants.MenuLayout.CHROMEBOOK: + this._loadChromebookTweaks(); + break; + case Constants.MenuLayout.TOGNEE: + this._loadTogneeMenuTweaks(); + break; + case Constants.MenuLayout.PLASMA: + this._loadPlasmaMenuTweaks(); + break; + case Constants.MenuLayout.WINDOWS: + this._loadWindowsTweaks(); + break; + case Constants.MenuLayout.ELEVEN: + this._loadElevenTweaks(); + break; + case Constants.MenuLayout.AZ: + this._loadAZTweaks(); + break; + case Constants.MenuLayout.ENTERPRISE: + this._loadEnterpriseTweaks(); + break; + case Constants.MenuLayout.POP: + this._loadPopTweaks(); + break; + case Constants.MenuLayout.SLEEK: + this._loadSleekTweaks(); + break; + default: + this._loadPlaceHolderTweaks(); + break; + } + } + + _createExtraShortcutsRow(setting) { + const extraShortcutsPage = new ListPinnedPage(this._settings, { + title: _('Extra Shortcuts'), + setting_string: setting, + list_type: Constants.MenuSettingsListType.EXTRA_SHORTCUTS, + }); + const extraShortcutsRow = new PW.SettingRow({ + title: _('Extra Shortcuts'), + }); + extraShortcutsRow.settingPage = extraShortcutsPage; + + extraShortcutsRow.connect('activated', () => { + this.get_root().push_subpage(extraShortcutsPage); + extraShortcutsPage.resetScrollAdjustment(); + }); + return extraShortcutsRow; + } + + _createVertSeparatorRow() { + const vertSeparatorSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + active: this._settings.get_boolean('vert-separator'), + }); + vertSeparatorSwitch.connect('notify::active', widget => { + this._settings.set_boolean('vert-separator', widget.get_active()); + }); + const vertSeparatorRow = new Adw.ActionRow({ + title: _('Vertical Separator'), + activatable_widget: vertSeparatorSwitch, + }); + vertSeparatorRow.add_suffix(vertSeparatorSwitch); + return vertSeparatorRow; + } + + _createActivateOnHoverRow() { + const hoverOptions = new Gtk.StringList(); + hoverOptions.append(_('Mouse Click')); + hoverOptions.append(_('Mouse Hover')); + + const activateOnHoverRow = new Adw.ComboRow({ + title: _('Category Activation'), + model: hoverOptions, + }); + + if (this._settings.get_boolean('activate-on-hover')) + activateOnHoverRow.selected = 1; + else + activateOnHoverRow.selected = 0; + + activateOnHoverRow.connect('notify::selected', widget => { + let activateOnHover; + if (widget.selected === 0) + activateOnHover = false; + if (widget.selected === 1) + activateOnHover = true; + + this._settings.set_boolean('activate-on-hover', activateOnHover); + }); + return activateOnHoverRow; + } + + _createAvatarShapeRow() { + const avatarStyles = new Gtk.StringList(); + avatarStyles.append(_('Round')); + avatarStyles.append(_('Square')); + const avatarStyleRow = new Adw.ComboRow({ + title: _('Avatar Icon Shape'), + model: avatarStyles, + selected: this._settings.get_enum('avatar-style'), + }); + + avatarStyleRow.connect('notify::selected', widget => { + this._settings.set_enum('avatar-style', widget.selected); + }); + return avatarStyleRow; + } + + _createSearchBarLocationRow(bottomDefault) { + const searchBarLocationSetting = bottomDefault ? 'searchbar-default-bottom-location' + : 'searchbar-default-top-location'; + + const searchbarLocations = new Gtk.StringList(); + searchbarLocations.append(_('Bottom')); + searchbarLocations.append(_('Top')); + + const searchbarLocationRow = new Adw.ComboRow({ + title: _('Searchbar Location'), + model: searchbarLocations, + selected: this._settings.get_enum(searchBarLocationSetting), + }); + + searchbarLocationRow.connect('notify::selected', widget => { + this._settings.set_enum(searchBarLocationSetting, widget.selected); + }); + + return searchbarLocationRow; + } + + _createFlipHorizontalRow() { + const horizontalFlipSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + horizontalFlipSwitch.set_active(this._settings.get_boolean('enable-horizontal-flip')); + horizontalFlipSwitch.connect('notify::active', widget => { + this._settings.set_boolean('enable-horizontal-flip', widget.get_active()); + }); + const horizontalFlipRow = new Adw.ActionRow({ + title: _('Flip Layout Horizontally'), + activatable_widget: horizontalFlipSwitch, + }); + horizontalFlipRow.add_suffix(horizontalFlipSwitch); + return horizontalFlipRow; + } + + _disableAvatarRow() { + const disableAvatarSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + disableAvatarSwitch.set_active(this._settings.get_boolean('disable-user-avatar')); + disableAvatarSwitch.connect('notify::active', widget => { + this._settings.set_boolean('disable-user-avatar', widget.get_active()); + }); + const disableAvatarRow = new Adw.ActionRow({ + title: _('Disable User Avatar'), + activatable_widget: disableAvatarSwitch, + }); + disableAvatarRow.add_suffix(disableAvatarSwitch); + return disableAvatarRow; + } + + _loadEnterpriseTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createActivateOnHoverRow()); + tweaksGroup.add(this._createAvatarShapeRow()); + tweaksGroup.add(this._createSearchBarLocationRow()); + tweaksGroup.add(this._createFlipHorizontalRow()); + tweaksGroup.add(this._createVertSeparatorRow()); + this.add(tweaksGroup); + } + + _loadPopTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + this.add(tweaksGroup); + + const populateComboRow = () => { + const folderNamesList = new Gtk.StringList(); + + const foldersData = this._settings.get_value('pop-folders-data').deep_unpack(); + const defaultViewName = this._settings.get_string('pop-default-view'); + let defaultViewIndex; + let count = 0; + for (const [key, value] of Object.entries(foldersData)) { + if (key === defaultViewName) + defaultViewIndex = count; + + folderNamesList.append(value); + count++; + } + defaultViewRow.model = folderNamesList; + defaultViewRow.selected = defaultViewIndex; + }; + + const defaultViewRow = new Adw.ComboRow({ + title: _('Default View'), + }); + + populateComboRow(); + + this._settings.connect('changed::pop-folders-data', () => populateComboRow()); + + defaultViewRow.connect('notify::selected', widget => { + const foldersData = this._settings.get_value('pop-folders-data').deep_unpack(); + const selectedId = Object.keys(foldersData).find(key => foldersData[key] === widget.selected_item.string); + this._settings.set_string('pop-default-view', selectedId); + }); + + tweaksGroup.add(defaultViewRow); + tweaksGroup.add(this._createSearchBarLocationRow()); + } + + _loadElevenTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + const disableFrequentAppsSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + disableFrequentAppsSwitch.set_active(this._settings.get_boolean('eleven-disable-frequent-apps')); + disableFrequentAppsSwitch.connect('notify::active', widget => { + this._settings.set_boolean('eleven-disable-frequent-apps', widget.get_active()); + }); + const disableFrequentAppsRow = new Adw.ActionRow({ + title: _('Disable Frequent Apps'), + activatable_widget: disableFrequentAppsSwitch, + }); + disableFrequentAppsRow.add_suffix(disableFrequentAppsSwitch); + tweaksGroup.add(disableFrequentAppsRow); + this.add(tweaksGroup); + + const extraShortcutsGroup = new Adw.PreferencesGroup(); + const extraShortcutsRow = this._createExtraShortcutsRow('eleven-layout-extra-shortcuts'); + extraShortcutsGroup.add(extraShortcutsRow); + this.add(extraShortcutsGroup); + } + + _loadAZTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createSearchBarLocationRow()); + this.add(tweaksGroup); + + const extraShortcutsGroup = new Adw.PreferencesGroup(); + const extraShortcutsRow = this._createExtraShortcutsRow('az-layout-extra-shortcuts'); + extraShortcutsGroup.add(extraShortcutsRow); + this.add(extraShortcutsGroup); + } + + _loadGnomeOverviewTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + const appsGridSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + appsGridSwitch.set_active(this._settings.get_boolean('gnome-dash-show-applications')); + appsGridSwitch.connect('notify::active', widget => { + this._settings.set_boolean('gnome-dash-show-applications', widget.get_active()); + }); + const appsGridRow = new Adw.ActionRow({ + title: _('Show Apps Grid'), + activatable_widget: appsGridSwitch, + }); + appsGridRow.add_suffix(appsGridSwitch); + tweaksGroup.add(appsGridRow); + this.add(tweaksGroup); + } + + _loadWindowsTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createVertSeparatorRow()); + const frequentAppsSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + frequentAppsSwitch.set_active(this._settings.get_boolean('windows-disable-frequent-apps')); + frequentAppsSwitch.connect('notify::active', widget => { + this._settings.set_boolean('windows-disable-frequent-apps', widget.get_active()); + }); + const frequentAppsRow = new Adw.ActionRow({ + title: _('Disable Frequent Apps'), + activatable_widget: frequentAppsSwitch, + }); + frequentAppsRow.add_suffix(frequentAppsSwitch); + tweaksGroup.add(frequentAppsRow); + + const pinnedAppsSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + pinnedAppsSwitch.set_active(this._settings.get_boolean('windows-disable-pinned-apps')); + pinnedAppsSwitch.connect('notify::active', widget => { + this._settings.set_boolean('windows-disable-pinned-apps', widget.get_active()); + }); + const pinnedAppsRow = new Adw.ActionRow({ + title: _('Disable Pinned Apps'), + activatable_widget: pinnedAppsSwitch, + }); + pinnedAppsRow.add_suffix(pinnedAppsSwitch); + tweaksGroup.add(pinnedAppsRow); + + this.add(tweaksGroup); + + const extraShortcutsGroup = new Adw.PreferencesGroup(); + const extraShortcutsRow = this._createExtraShortcutsRow('windows-layout-extra-shortcuts'); + extraShortcutsGroup.add(extraShortcutsRow); + this.add(extraShortcutsGroup); + } + + _loadPlasmaMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createSearchBarLocationRow()); + + const hoverSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + hoverSwitch.set_active(this._settings.get_boolean('plasma-enable-hover')); + hoverSwitch.connect('notify::active', widget => { + this._settings.set_boolean('plasma-enable-hover', widget.get_active()); + }); + const hoverRow = new Adw.ActionRow({ + title: _('Activate on Hover'), + activatable_widget: hoverSwitch, + }); + hoverRow.add_suffix(hoverSwitch); + tweaksGroup.add(hoverRow); + + this.add(tweaksGroup); + } + + _loadBriskMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createActivateOnHoverRow()); + tweaksGroup.add(this._createSearchBarLocationRow()); + tweaksGroup.add(this._createFlipHorizontalRow()); + tweaksGroup.add(this._createVertSeparatorRow()); + this.add(tweaksGroup); + + const extraShortcutsGroup = new Adw.PreferencesGroup(); + const extraShortcutsRow = this._createExtraShortcutsRow('brisk-layout-extra-shortcuts'); + extraShortcutsGroup.add(extraShortcutsRow); + this.add(extraShortcutsGroup); + } + + _loadChromebookTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createSearchBarLocationRow()); + this.add(tweaksGroup); + } + + _loadElementaryTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createSearchBarLocationRow()); + this.add(tweaksGroup); + } + + _loadBudgieMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createActivateOnHoverRow()); + tweaksGroup.add(this._createSearchBarLocationRow()); + tweaksGroup.add(this._createFlipHorizontalRow()); + tweaksGroup.add(this._createVertSeparatorRow()); + + const enableActivitiesSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + enableActivitiesSwitch.set_active(this._settings.get_boolean('enable-activities-shortcut')); + enableActivitiesSwitch.connect('notify::active', widget => { + this._settings.set_boolean('enable-activities-shortcut', widget.get_active()); + }); + const enableActivitiesRow = new Adw.ActionRow({ + title: _('Enable Activities Overview Shortcut'), + activatable_widget: enableActivitiesSwitch, + }); + enableActivitiesRow.add_suffix(enableActivitiesSwitch); + tweaksGroup.add(enableActivitiesRow); + + this.add(tweaksGroup); + } + + _loadRunnerMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + const runnerPositions = new Gtk.StringList(); + runnerPositions.append(_('Top')); + runnerPositions.append(_('Centered')); + const runnerPositionRow = new Adw.ComboRow({ + title: _('Position'), + model: runnerPositions, + selected: this._settings.get_enum('runner-position'), + }); + + runnerPositionRow.connect('notify::selected', widget => { + this._settings.set_enum('runner-position', widget.selected); + }); + tweaksGroup.add(runnerPositionRow); + + const runnerSearchStyles = new Gtk.StringList(); + runnerSearchStyles.append(_('List')); + runnerSearchStyles.append(_('Grid')); + const runnerSearchStyleRow = new Adw.ComboRow({ + title: _('Search Results Display Style'), + model: runnerSearchStyles, + selected: this._settings.get_enum('runner-search-display-style'), + }); + + runnerSearchStyleRow.connect('notify::selected', widget => { + this._settings.set_enum('runner-search-display-style', widget.selected); + }); + tweaksGroup.add(runnerSearchStyleRow); + + const runnerWidthScale = new Gtk.SpinButton({ + orientation: Gtk.Orientation.HORIZONTAL, + adjustment: new Gtk.Adjustment({ + lower: 300, + upper: 1000, + step_increment: 15, + page_increment: 15, + page_size: 0, + }), + digits: 0, + valign: Gtk.Align.CENTER, + }); + runnerWidthScale.set_value(this._settings.get_int('runner-menu-width')); + runnerWidthScale.connect('value-changed', widget => { + this._settings.set_int('runner-menu-width', widget.get_value()); + }); + const runnerWidthRow = new Adw.ActionRow({ + title: _('Width'), + activatable_widget: runnerWidthScale, + }); + runnerWidthRow.add_suffix(runnerWidthScale); + tweaksGroup.add(runnerWidthRow); + + const runnerHeightScale = new Gtk.SpinButton({ + orientation: Gtk.Orientation.HORIZONTAL, + adjustment: new Gtk.Adjustment({ + lower: 300, + upper: 1000, + step_increment: 15, + page_increment: 15, + page_size: 0, + }), + digits: 0, + valign: Gtk.Align.CENTER, + }); + runnerHeightScale.set_value(this._settings.get_int('runner-menu-height')); + runnerHeightScale.connect('value-changed', widget => { + this._settings.set_int('runner-menu-height', widget.get_value()); + }); + const runnerHeightRow = new Adw.ActionRow({ + title: _('Height'), + activatable_widget: runnerHeightScale, + }); + runnerHeightRow.add_suffix(runnerHeightScale); + tweaksGroup.add(runnerHeightRow); + + const runnerFontSizeScale = new Gtk.SpinButton({ + orientation: Gtk.Orientation.HORIZONTAL, + adjustment: new Gtk.Adjustment({ + lower: 0, + upper: 30, + step_increment: 1, + page_increment: 1, + page_size: 0, + }), + digits: 0, + valign: Gtk.Align.CENTER, + }); + runnerFontSizeScale.set_value(this._settings.get_int('runner-font-size')); + runnerFontSizeScale.connect('value-changed', widget => { + this._settings.set_int('runner-font-size', widget.get_value()); + }); + const runnerFontSizeRow = new Adw.ActionRow({ + title: _('Font Size'), + subtitle: _('%d Default Theme Value').format(0), + activatable_widget: runnerFontSizeScale, + }); + runnerFontSizeRow.add_suffix(runnerFontSizeScale); + tweaksGroup.add(runnerFontSizeRow); + + const frequentAppsSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + frequentAppsSwitch.set_active(this._settings.get_boolean('runner-show-frequent-apps')); + frequentAppsSwitch.connect('notify::active', widget => { + this._settings.set_boolean('runner-show-frequent-apps', widget.get_active()); + }); + const frequentAppsRow = new Adw.ActionRow({ + title: _('Show Frequent Apps'), + activatable_widget: frequentAppsSwitch, + }); + frequentAppsRow.add_suffix(frequentAppsSwitch); + tweaksGroup.add(frequentAppsRow); + + this.add(tweaksGroup); + } + + _loadUnityTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + this.add(tweaksGroup); + + const defaulViews = new Gtk.StringList(); + defaulViews.append(_('Home')); + defaulViews.append(_('All Programs')); + const defaultViewRow = new Adw.ComboRow({ + title: _('Default View'), + model: defaulViews, + selected: this._settings.get_boolean('enable-unity-homescreen') ? 0 : 1, + }); + defaultViewRow.connect('notify::selected', widget => { + const enable = widget.selected === 0; + this._settings.set_boolean('enable-unity-homescreen', enable); + }); + tweaksGroup.add(defaultViewRow); + + const widgetGroup = this._createWidgetsRows(Constants.MenuLayout.UNITY); + this.add(widgetGroup); + + const extraShortcutsGroup = new Adw.PreferencesGroup(); + const extraShortcutsRow = this._createExtraShortcutsRow('unity-layout-extra-shortcuts'); + extraShortcutsGroup.add(extraShortcutsRow); + this.add(extraShortcutsGroup); + } + + _loadRavenTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + this.add(tweaksGroup); + + const defaulViews = new Gtk.StringList(); + defaulViews.append(_('Home')); + defaulViews.append(_('All Programs')); + const defaultViewRow = new Adw.ComboRow({ + title: _('Default View'), + model: defaulViews, + selected: this._settings.get_boolean('enable-unity-homescreen') ? 0 : 1, + }); + defaultViewRow.connect('notify::selected', widget => { + const enable = widget.selected === 0; + this._settings.set_boolean('enable-unity-homescreen', enable); + }); + tweaksGroup.add(defaultViewRow); + + const runnerSearchStyles = new Gtk.StringList(); + runnerSearchStyles.append(_('List')); + runnerSearchStyles.append(_('Grid')); + const runnerSearchStyleRow = new Adw.ComboRow({ + title: _('Search Results Display Style'), + model: runnerSearchStyles, + selected: this._settings.get_enum('raven-search-display-style'), + }); + + runnerSearchStyleRow.connect('notify::selected', widget => { + this._settings.set_enum('raven-search-display-style', widget.selected); + }); + tweaksGroup.add(runnerSearchStyleRow); + + const ravenPositions = new Gtk.StringList(); + ravenPositions.append(_('Left')); + ravenPositions.append(_('Right')); + const ravenPositionRow = new Adw.ComboRow({ + title: _('Position on Monitor'), + model: ravenPositions, + selected: this._settings.get_enum('raven-position'), + }); + ravenPositionRow.connect('notify::selected', widget => { + this._settings.set_enum('raven-position', widget.selected); + }); + tweaksGroup.add(ravenPositionRow); + tweaksGroup.add(this._createActivateOnHoverRow()); + const widgetGroup = this._createWidgetsRows(Constants.MenuLayout.RAVEN); + this.add(widgetGroup); + } + + _loadMintMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createActivateOnHoverRow()); + tweaksGroup.add(this._createSearchBarLocationRow()); + tweaksGroup.add(this._createFlipHorizontalRow()); + tweaksGroup.add(this._createVertSeparatorRow()); + this.add(tweaksGroup); + + const extraShortcutsGroup = new Adw.PreferencesGroup(); + const extraShortcutsRow = this._createExtraShortcutsRow('mint-layout-extra-shortcuts'); + extraShortcutsGroup.add(extraShortcutsRow); + this.add(extraShortcutsGroup); + } + + _loadWhiskerMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createActivateOnHoverRow()); + tweaksGroup.add(this._createAvatarShapeRow()); + tweaksGroup.add(this._createSearchBarLocationRow()); + tweaksGroup.add(this._createFlipHorizontalRow()); + tweaksGroup.add(this._createVertSeparatorRow()); + this.add(tweaksGroup); + } + + _loadSleekTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + + tweaksGroup.add(this._createAvatarShapeRow()); + tweaksGroup.add(this._createSearchBarLocationRow()); + tweaksGroup.add(this._createFlipHorizontalRow()); + + const rightPanelWidthSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: 150, upper: 500, step_increment: 25, page_increment: 50, page_size: 0, + }), + climb_rate: 25, + valign: Gtk.Align.CENTER, + digits: 0, + numeric: true, + }); + rightPanelWidthSpinButton.set_value(this._settings.get_int('sleek-layout-panel-width')); + rightPanelWidthSpinButton.connect('value-changed', widget => { + this._settings.set_int('sleek-layout-panel-width', widget.get_value()); + }); + const rightPanelWidthRow = new Adw.ActionRow({ + title: _('Right-Panel Width'), + activatable_widget: rightPanelWidthSpinButton, + }); + rightPanelWidthRow.add_suffix(rightPanelWidthSpinButton); + tweaksGroup.add(rightPanelWidthRow); + + this.add(tweaksGroup); + + const extraShortcutsGroup = new Adw.PreferencesGroup(); + const extraShortcutsRow = this._createExtraShortcutsRow('sleek-layout-extra-shortcuts'); + extraShortcutsGroup.add(extraShortcutsRow); + this.add(extraShortcutsGroup); + } + + _loadRedmondMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + + const defaulViews = new Gtk.StringList(); + defaulViews.append(_('All Programs')); + defaulViews.append(_('Pinned Apps')); + + const defaultViewRow = new Adw.ComboRow({ + title: _('Default View'), + model: defaulViews, + selected: this._settings.get_enum('default-menu-view-redmond'), + }); + defaultViewRow.connect('notify::selected', widget => { + this._settings.set_enum('default-menu-view-redmond', widget.selected); + }); + tweaksGroup.add(defaultViewRow); + + tweaksGroup.add(this._createAvatarShapeRow()); + tweaksGroup.add(this._createSearchBarLocationRow()); + tweaksGroup.add(this._createFlipHorizontalRow()); + tweaksGroup.add(this._disableAvatarRow()); + tweaksGroup.add(this._createVertSeparatorRow()); + + this.add(tweaksGroup); + + const placesGroup = new Adw.PreferencesGroup({ + title: _('Extra Shortcuts'), + }); + this.add(placesGroup); + + const externalDeviceButton = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + externalDeviceButton.set_active(this._settings.get_boolean('show-external-devices')); + externalDeviceButton.connect('notify::active', widget => { + this._settings.set_boolean('show-external-devices', widget.get_active()); + }); + const externalDeviceRow = new Adw.ActionRow({ + title: _('External Devices'), + activatable_widget: externalDeviceButton, + }); + externalDeviceRow.add_suffix(externalDeviceButton); + placesGroup.add(externalDeviceRow); + + const bookmarksButton = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + bookmarksButton.set_active(this._settings.get_boolean('show-bookmarks')); + bookmarksButton.connect('notify::active', widget => { + this._settings.set_boolean('show-bookmarks', widget.get_active()); + }); + const bookmarksRow = new Adw.ActionRow({ + title: _('Bookmarks'), + activatable_widget: bookmarksButton, + }); + bookmarksRow.add_suffix(bookmarksButton); + placesGroup.add(bookmarksRow); + } + + _loadInsiderMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createVertSeparatorRow()); + tweaksGroup.add(this._createAvatarShapeRow()); + this.add(tweaksGroup); + + const extraShortcutsGroup = new Adw.PreferencesGroup(); + const extraShortcutsRow = this._createExtraShortcutsRow('insider-layout-extra-shortcuts'); + extraShortcutsGroup.add(extraShortcutsRow); + this.add(extraShortcutsGroup); + } + + _loadGnomeMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + tweaksGroup.add(this._createActivateOnHoverRow()); + tweaksGroup.add(this._createFlipHorizontalRow()); + tweaksGroup.add(this._createVertSeparatorRow()); + this.add(tweaksGroup); + } + + _loadPlaceHolderTweaks() { + const placeHolderGroup = new Adw.PreferencesGroup(); + const placeHolderRow = new Adw.ActionRow({ + title: _('Nothing Yet!'), + }); + placeHolderGroup.add(placeHolderRow); + this.add(placeHolderGroup); + } + + _loadTogneeMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + + const defaulViews = new Gtk.StringList(); + defaulViews.append(_('Categories List')); + defaulViews.append(_('All Programs')); + const defaultViewRow = new Adw.ComboRow({ + title: _('Default View'), + model: defaulViews, + selected: this._settings.get_enum('default-menu-view-tognee'), + }); + defaultViewRow.connect('notify::selected', widget => { + this._settings.set_enum('default-menu-view-tognee', widget.selected); + }); + tweaksGroup.add(defaultViewRow); + + const searchBarBottomDefault = true; + tweaksGroup.add(this._createSearchBarLocationRow(searchBarBottomDefault)); + tweaksGroup.add(this._createFlipHorizontalRow()); + tweaksGroup.add(this._createVertSeparatorRow()); + this.add(tweaksGroup); + } + + _loadArcMenuTweaks() { + const tweaksGroup = new Adw.PreferencesGroup(); + + const defaulViews = new Gtk.StringList(); + defaulViews.append(_('Pinned Apps')); + defaulViews.append(_('Categories List')); + defaulViews.append(_('Frequent Apps')); + defaulViews.append(_('All Programs')); + const defaultViewRow = new Adw.ComboRow({ + title: _('Default View'), + model: defaulViews, + selected: this._settings.get_enum('default-menu-view'), + }); + defaultViewRow.connect('notify::selected', widget => { + this._settings.set_enum('default-menu-view', widget.selected); + }); + tweaksGroup.add(defaultViewRow); + + + const allAppsButtonActionsList = new Gtk.StringList(); + allAppsButtonActionsList.append(_('Categories List')); + allAppsButtonActionsList.append(_('All Programs')); + + const allAppsButtonActionRow = new Adw.ComboRow({ + title: _("'All Apps' Button Action"), + model: allAppsButtonActionsList, + selected: this._settings.get_enum('all-apps-button-action'), + }); + allAppsButtonActionRow.connect('notify::selected', widget => { + this._settings.set_enum('all-apps-button-action', widget.selected); + }); + tweaksGroup.add(allAppsButtonActionRow); + + const searchBarBottomDefault = true; + tweaksGroup.add(this._createAvatarShapeRow()); + tweaksGroup.add(this._createSearchBarLocationRow(searchBarBottomDefault)); + tweaksGroup.add(this._createFlipHorizontalRow()); + tweaksGroup.add(this._disableAvatarRow()); + tweaksGroup.add(this._createVertSeparatorRow()); + this.add(tweaksGroup); + + const placesGroup = new Adw.PreferencesGroup({ + title: _('Extra Shortcuts'), + }); + + const externalDeviceButton = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + externalDeviceButton.set_active(this._settings.get_boolean('show-external-devices')); + externalDeviceButton.connect('notify::active', widget => { + this._settings.set_boolean('show-external-devices', widget.get_active()); + }); + const externalDeviceRow = new Adw.ActionRow({ + title: _('External Devices'), + activatable_widget: externalDeviceButton, + }); + externalDeviceRow.add_suffix(externalDeviceButton); + placesGroup.add(externalDeviceRow); + + const bookmarksButton = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + bookmarksButton.set_active(this._settings.get_boolean('show-bookmarks')); + bookmarksButton.connect('notify::active', widget => { + this._settings.set_boolean('show-bookmarks', widget.get_active()); + }); + const bookmarksRow = new Adw.ActionRow({ + title: _('Bookmarks'), + activatable_widget: bookmarksButton, + }); + bookmarksRow.add_suffix(bookmarksButton); + placesGroup.add(bookmarksRow); + this.add(placesGroup); + + const extraCategoriesGroup = new Adw.PreferencesGroup({ + title: _('Category Quick Links'), + description: _('Display quick links of extra categories on the home page\n' + + "Must also be enabled in 'Menu -> Extra Categories' section"), + }); + const extraCategoriesLinksBox = new ListOtherPage(this._settings, { + preferences_page: false, + list_type: Constants.MenuSettingsListType.QUICK_LINKS, + }); + extraCategoriesGroup.add(extraCategoriesLinksBox); + this.add(extraCategoriesGroup); + + const extraCategoriesLocationGroup = new Adw.PreferencesGroup(); + const locations = new Gtk.StringList(); + locations.append(_('Bottom')); + locations.append(_('Top')); + const extraCategoriesLocationRow = new Adw.ComboRow({ + title: _('Quick Links Location'), + model: locations, + selected: this._settings.get_enum('arcmenu-extra-categories-links-location'), + }); + extraCategoriesLocationRow.connect('notify::selected', widget => { + this._settings.set_enum('arcmenu-extra-categories-links-location', widget.selected); + }); + extraCategoriesLocationGroup.add(extraCategoriesLocationRow); + this.add(extraCategoriesLocationGroup); + } + + _createWidgetsRows(layout) { + let weatherWidgetSetting = 'enable-weather-widget-raven'; + let clockWidgetSetting = 'enable-clock-widget-raven'; + if (layout === Constants.MenuLayout.RAVEN) { + weatherWidgetSetting = 'enable-weather-widget-raven'; + clockWidgetSetting = 'enable-clock-widget-raven'; + } else { + weatherWidgetSetting = 'enable-weather-widget-unity'; + clockWidgetSetting = 'enable-clock-widget-unity'; + } + + const widgetGroup = new Adw.PreferencesGroup(); + + const weatherWidgetSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + weatherWidgetSwitch.set_active(this._settings.get_boolean(weatherWidgetSetting)); + weatherWidgetSwitch.connect('notify::active', widget => { + this._settings.set_boolean(weatherWidgetSetting, widget.get_active()); + }); + const weatherWidgetRow = new Adw.ActionRow({ + title: _('Enable Weather Widget'), + activatable_widget: weatherWidgetSwitch, + }); + weatherWidgetRow.add_suffix(weatherWidgetSwitch); + widgetGroup.add(weatherWidgetRow); + + const clockWidgetSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + clockWidgetSwitch.set_active(this._settings.get_boolean(clockWidgetSetting)); + clockWidgetSwitch.connect('notify::active', widget => { + this._settings.set_boolean(clockWidgetSetting, widget.get_active()); + }); + const clockWidgetRow = new Adw.ActionRow({ + title: _('Enable Clock Widget'), + activatable_widget: clockWidgetSwitch, + }); + clockWidgetRow.add_suffix(clockWidgetSwitch); + widgetGroup.add(clockWidgetRow); + + return widgetGroup; + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/LayoutsPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/LayoutsPage.js new file mode 100644 index 0000000..b88e088 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/LayoutsPage.js @@ -0,0 +1,199 @@ +import Adw from 'gi://Adw'; +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import * as Constants from '../../constants.js'; +import * as PW from '../../prefsWidgets.js'; +import {SubPage} from './SubPage.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const LayoutsPage = GObject.registerClass({ + Signals: { + 'response': {param_types: [GObject.TYPE_INT]}, + }, +}, +class ArcMenuLayoutsPage extends SubPage { + _init(settings, params) { + super._init(settings, params); + + this.restoreDefaultsButton.visible = false; + + const currentLayoutGroup = new Adw.PreferencesGroup({ + title: _('Current Menu Layout'), + }); + + const menuLayoutInfo = this.getMenuLayoutInfo(this._settings.get_enum('menu-layout')); + + const currentLayoutName = menuLayoutInfo.TITLE; + const currentLayoutImagePath = menuLayoutInfo.IMAGE; + + const currentLayoutBoxRow = new CurrentLayoutRow(currentLayoutName, currentLayoutImagePath); + currentLayoutGroup.add(currentLayoutBoxRow); + + this.add(currentLayoutGroup); + + const menuLayoutGroup = new Adw.PreferencesGroup({ + title: _('Choose a new menu layout?'), + }); + this.add(menuLayoutGroup); + + Constants.MenuStyles.forEach(style => { + const tile = new Adw.ExpanderRow({ + title: _('%s Menu Layouts').format(_(style.TITLE)), + icon_name: style.IMAGE, + }); + tile.layout = style.MENU_TYPE; + + menuLayoutGroup.add(tile); + + const layoutsBox = new LayoutsBox(this._settings, tile); + + if (layoutsBox.selectedLayout) + this.activeLayoutBox = layoutsBox; + + const row = new Gtk.ListBoxRow({ + selectable: false, + activatable: false, + }); + row.set_child(layoutsBox); + layoutsBox.connect('menu-selected', (widget, response) => { + if (response === Gtk.ResponseType.OK) { + this._settings.set_enum('menu-layout', widget.menuLayout); + this.activeLayoutBox.clearSelection(); + + this.activeLayoutBox = widget; + this.activeLayoutBox.applySelection(); + this.selectedMenuLayout = widget.menuLayout; + + const newMenuLayoutInfo = this.getMenuLayoutInfo(this.selectedMenuLayout); + + currentLayoutBoxRow.label.label = newMenuLayoutInfo.TITLE; + currentLayoutBoxRow.image.gicon = Gio.icon_new_for_string(newMenuLayoutInfo.IMAGE); + + this.expandedRow.expanded = false; + this.emit('response', Gtk.ResponseType.APPLY); + } + }); + tile.connect('notify::expanded', () => { + if (this.expandedRow && this.expandedRow !== tile) + this.expandedRow.expanded = false; + + this.expandedRow = tile; + }); + tile.add_row(row); + }); + } + + getMenuLayoutInfo(index) { + for (const styles of Constants.MenuStyles) { + for (const style of styles.MENU_TYPE) { + if (style.LAYOUT === index) + return style; + } + } + return null; + } +}); + +var LayoutsBox = GObject.registerClass({ + Signals: { + 'menu-selected': {param_types: [GObject.TYPE_INT]}, + }, +}, class ArcMenuLayoutsBox extends PW.IconGrid { + _init(settings, tile) { + super._init(); + + this._settings = settings; + this.layoutStyle = tile.layout; + this.styles = tile.layout; + + // clamp max children per line. min = 1, max = 3; + this.max_children_per_line = Math.min(Math.max(tile.layout.length, 1), 3); + + this.connect('child-activated', () => { + const currentMenuLayout = this._settings.get_enum('menu-layout'); + const selectedChildren = this.get_selected_children(); + const selectedLayout = selectedChildren[0]; + + if (currentMenuLayout === selectedLayout.layout) + return; + + this.selectedLayout = selectedLayout; + this.menuLayout = selectedLayout.layout; + + this.emit('menu-selected', Gtk.ResponseType.OK); + }); + + this.styles.forEach(style => { + const currentMenuLayout = this._settings.get_enum('menu-layout'); + const layoutTile = new PW.MenuLayoutTile(style); + this.add(layoutTile); + + if (currentMenuLayout === style.LAYOUT) { + this.selectedLayout = layoutTile; + this.applySelection(); + } + }); + } + + clearSelection() { + const currentMenuLayout = this._settings.get_enum('menu-layout'); + this.unselect_all(); + + if (this.selectedLayout && currentMenuLayout !== this.selectedLayout.layout) + this.selectedLayout = null; + } + + applySelection() { + if (this.selectedLayout) + this.select_child(this.selectedLayout); + } +}); + +var CurrentLayoutRow = GObject.registerClass( +class ArcMenuMenuLayoutRow extends Gtk.Box { + _init(title, imagePath, layout) { + super._init({ + orientation: Gtk.Orientation.VERTICAL, + css_classes: ['card'], + hexpand: false, + spacing: 0, + halign: Gtk.Align.CENTER, + }); + + if (layout) + this.layout = layout.MENU_TYPE; + + const box = new Gtk.Box({ + margin_start: 15, + margin_end: 15, + margin_top: 8, + margin_bottom: 8, + orientation: Gtk.Orientation.VERTICAL, + hexpand: false, + }); + + this.image = new Gtk.Image({ + hexpand: false, + halign: Gtk.Align.CENTER, + gicon: Gio.icon_new_for_string(imagePath), + pixel_size: 145, + }); + + this.label = new Gtk.Label({ + label: _(title), + hexpand: true, + halign: Gtk.Align.CENTER, + vexpand: false, + valign: Gtk.Align.START, + css_classes: ['heading'], + }); + + box.append(this.image); + box.append(this.label); + + this.append(box); + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ListOtherPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ListOtherPage.js new file mode 100644 index 0000000..d0e60e1 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ListOtherPage.js @@ -0,0 +1,123 @@ +import Adw from 'gi://Adw'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import * as Constants from '../../constants.js'; +import * as PW from '../../prefsWidgets.js'; +import {SubPage} from './SubPage.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const ListOtherPage = GObject.registerClass( +class ArcMenuListOtherPage extends SubPage { + _init(settings, params) { + super._init(settings, params); + + this.frameRows = []; + + if (this.list_type === Constants.MenuSettingsListType.POWER_OPTIONS) + this.settingString = 'power-options'; + else if (this.list_type === Constants.MenuSettingsListType.EXTRA_CATEGORIES) + this.settingString = 'extra-categories'; + else if (this.list_type === Constants.MenuSettingsListType.QUICK_LINKS) + this.settingString = 'arcmenu-extra-categories-links'; + + this._mainGroup = new Adw.PreferencesGroup(); + this.add(this._mainGroup); + + this._addRowsToFrame(this._settings.get_value(this.settingString).deep_unpack()); + + if (this.list_type === Constants.MenuSettingsListType.POWER_OPTIONS) { + this._mainGroup.set({ + description: _('Actions will be hidden from ArcMenu if not available on your system.'), + }); + + const powerDisplayStyleGroup = new Adw.PreferencesGroup({ + title: _('Power Off / Log Out Buttons'), + }); + const powerDisplayStyles = new Gtk.StringList(); + powerDisplayStyles.append(_('Off')); + powerDisplayStyles.append(_('Power Buttons')); + powerDisplayStyles.append(_('Power Menu')); + this.powerDisplayStyleRow = new Adw.ComboRow({ + title: _('Override Display Style'), + model: powerDisplayStyles, + selected: this._settings.get_enum('power-display-style'), + }); + this.powerDisplayStyleRow.connect('notify::selected', widget => { + this._settings.set_enum('power-display-style', widget.selected); + }); + powerDisplayStyleGroup.add(this.powerDisplayStyleRow); + + this.add(powerDisplayStyleGroup); + } + + this.restoreDefaults = () => { + this.frameRows.forEach(child => { + this._mainGroup.remove(child); + }); + this.frameRows = []; + + if (this.powerDisplayStyleRow) + this.powerDisplayStyleRow.selected = 0; + + this._addRowsToFrame(this._settings.get_default_value(this.settingString).deep_unpack()); + this.saveSettings(); + }; + } + + saveSettings() { + const array = []; + this.frameRows.sort((a, b) => { + return a.get_index() - b.get_index(); + }); + this.frameRows.forEach(child => { + array.push([child.setting_type, child.switch_active]); + }); + + this._settings.set_value(this.settingString, new GLib.Variant('a(ib)', array)); + } + + _addRowsToFrame(extraCategories) { + for (let i = 0; i < extraCategories.length; i++) { + const [categoryEnum, shouldShow] = extraCategories[i]; + + let name, iconString; + if (this.list_type === Constants.MenuSettingsListType.POWER_OPTIONS) { + name = Constants.PowerOptions[categoryEnum].NAME; + iconString = Constants.PowerOptions[categoryEnum].ICON; + } else { + name = Constants.Categories[categoryEnum].NAME; + iconString = Constants.Categories[categoryEnum].ICON; + } + + const row = new PW.DragRow({ + gicon: Gio.icon_new_for_string(iconString), + switch_enabled: true, + switch_active: shouldShow, + }); + row.activatable_widget = row.switch; + row.setting_type = categoryEnum; + row.title = _(name); + + row.connect('drag-drop-done', () => this.saveSettings()); + row.connect('switch-toggled', () => this.saveSettings()); + + const editEntryButton = new PW.EditEntriesBox({row}); + editEntryButton.connect('entry-modified', (_self, startIndex, newIndex) => { + const splicedItem = this.frameRows.splice(startIndex, 1)[0]; + + if (newIndex >= 0) + this.frameRows.splice(newIndex, 0, splicedItem); + + this.saveSettings(); + }); + + row.add_suffix(editEntryButton); + this.frameRows.push(row); + this._mainGroup.add(row); + } + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ListPinnedPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ListPinnedPage.js new file mode 100644 index 0000000..1adc5f9 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ListPinnedPage.js @@ -0,0 +1,691 @@ +import Adw from 'gi://Adw'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import * as Constants from '../../constants.js'; +import * as PW from '../../prefsWidgets.js'; +import * as SettingsUtils from '../SettingsUtils.js'; +import {SubPage} from './SubPage.js'; + +import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +/** + * + * @param {string} schema + * @param {string} path + */ +function getSettings(schema, path) { + const extension = ExtensionPreferences.lookupByURL(import.meta.url); + const schemaDir = extension.dir.get_child('schemas'); + let schemaSource; + if (schemaDir.query_exists(null)) { + schemaSource = Gio.SettingsSchemaSource.new_from_directory( + schemaDir.get_path(), + Gio.SettingsSchemaSource.get_default(), + false + ); + } else { + schemaSource = Gio.SettingsSchemaSource.get_default(); + } + + const schemaObj = schemaSource.lookup(schema, true); + if (!schemaObj) { + log( + `Schema ${schema} could not be found for extension ${ + extension.metadata.uuid}. Please check your installation.` + ); + return null; + } + + const args = {settings_schema: schemaObj}; + if (path) + args.path = path; + + return new Gio.Settings(args); +} + +export const ListPinnedPage = GObject.registerClass( +class ArcMenuListPinnedPage extends SubPage { + _init(settings, params) { + super._init(settings, params); + + this._settings = settings; + this.frameRows = []; + let addMoreTitle; + + this.frame = new Adw.PreferencesGroup(); + + this.add(this.frame); + + if (this.list_type === Constants.MenuSettingsListType.PINNED_APPS) { + this.settingString = 'pinned-apps'; + addMoreTitle = _('Add More Apps'); + } else if (this.list_type === Constants.MenuSettingsListType.CONTEXT_MENU) { + this.settingString = 'context-menu-items'; + addMoreTitle = _('Add More Apps'); + } else if (this.list_type === Constants.MenuSettingsListType.DIRECTORIES) { + this.settingString = 'directory-shortcuts'; + addMoreTitle = _('Add Default User Directories'); + } else if (this.list_type === Constants.MenuSettingsListType.APPLICATIONS) { + this.settingString = 'application-shortcuts'; + addMoreTitle = _('Add More Apps'); + } else if (this.list_type === Constants.MenuSettingsListType.EXTRA_SHORTCUTS) { + this.settingString = this.setting_string; + addMoreTitle = _('Add More Shortcuts'); + } else if (this.list_type === Constants.MenuSettingsListType.FOLDER_PINNED_APPS) { + addMoreTitle = _('Add More Apps'); + const folderSchema = `${this._settings.schema_id}.pinned-apps-folders`; + const folderPath = `${this._settings.path}pinned-apps-folders/${this.setting_string}/`; + this.settingString = 'pinned-apps'; + this._settings = getSettings(folderSchema, folderPath); + this.restoreDefaultsButton.visible = false; + } + + const shortcutsArray = this._settings.get_value(this.settingString).deep_unpack(); + this._addRowsToFrame(shortcutsArray); + + const addMoreGroup = new Adw.PreferencesGroup(); + const addMoreButton = new Gtk.Button({ + icon_name: 'list-add-symbolic', + valign: Gtk.Align.CENTER, + }); + addMoreButton.connect('clicked', () => { + const dialog = new AddAppsToPinnedListWindow(this._settings, this, this.list_type, this.settingString); + dialog.show(); + dialog.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + this._addRowToFrame(dialog.shortcutData); + this.saveSettings(); + } + if (response === Gtk.ResponseType.REJECT) { + const command = dialog.shortcutData.id; + let frameRow; + this.frameRows.forEach(child => { + if (command === child.shortcutData.id) + frameRow = child; + }); + if (frameRow) { + this.frameRows.splice(this.frameRows.indexOf(frameRow), 1); + this.frame.remove(frameRow); + this.saveSettings(); + } + } + }); + }); + const addMoreRow = new Adw.ActionRow({ + title: _(addMoreTitle), + activatable_widget: addMoreButton, + }); + addMoreRow.add_suffix(addMoreButton); + addMoreGroup.add(addMoreRow); + + this.add(addMoreGroup); + if (this.list_type !== Constants.MenuSettingsListType.CONTEXT_MENU) { + const addCustomButton = new Gtk.Button({ + icon_name: 'list-add-symbolic', + valign: Gtk.Align.CENTER, + }); + addCustomButton.connect('clicked', () => { + const dialog = new AddCustomLinkDialogWindow(this._settings, this, this.list_type); + dialog.show(); + dialog.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + this._addRowToFrame(dialog.shortcutData); + dialog.destroy(); + this.saveSettings(); + } + }); + }); + const addCustomRow = new Adw.ActionRow({ + title: _('Add Custom Shortcut'), + activatable_widget: addCustomButton, + }); + addCustomRow.add_suffix(addCustomButton); + addMoreGroup.add(addCustomRow); + } + + this.restoreDefaults = () => { + this.frameRows.forEach(child => { + this.frame.remove(child); + }); + + this.frameRows = []; + const defaultData = this._settings.get_default_value(this.settingString).deep_unpack(); + + this._addRowsToFrame(defaultData); + this.saveSettings(); + }; + } + + updatePinnedApps() { + this.frameRows.forEach(child => { + this.frame.remove(child); + }); + this.frameRows = []; + + const pinnedApps = this._settings.get_value('pinned-apps').deep_unpack(); + this._addRowsToFrame(pinnedApps); + } + + saveSettings() { + const array = []; + this.frameRows.sort((a, b) => { + return a.get_index() - b.get_index(); + }); + this.frameRows.forEach(child => { + array.push(child.shortcutData); + }); + + this._settings.set_value(this.settingString, new GLib.Variant('aa{ss}', array)); + + // If in folder pinned app subpage, pop the subpage when no pinned apps left + if (this.list_type === Constants.MenuSettingsListType.FOLDER_PINNED_APPS && array.length === 0) + this.get_root().pop_subpage(); + } + + _setRowData(row, shortcutData) { + const id = shortcutData.id ?? ''; + const name = shortcutData.name ?? ''; + const icon = shortcutData.icon ?? ''; + + row.shortcutData = shortcutData; + + let appInfo = Gio.DesktopAppInfo.new(id); + let shortcutIcon = icon; + let rowTitle = name; + + if (shortcutIcon === Constants.ShortcutCommands.ARCMENU_ICON) { + const extension = ExtensionPreferences.lookupByURL(import.meta.url); + shortcutIcon = `${extension.path}/${Constants.ArcMenuLogoSymbolic}`; + } else if (id === 'org.gnome.Settings.desktop' && !appInfo) { + appInfo = Gio.DesktopAppInfo.new('gnome-control-center.desktop'); + } else if (id === Constants.ShortcutCommands.SOFTWARE) { + for (const softwareManagerID of Constants.SoftwareManagerIDs) { + const app = Gio.DesktopAppInfo.new(softwareManagerID); + if (app) { + const appIcon = app.get_icon(); + shortcutIcon = appIcon ? appIcon.to_string() : ''; + break; + } + } + } else if (this.list_type === Constants.MenuSettingsListType.DIRECTORIES || + this.list_type === Constants.MenuSettingsListType.EXTRA_SHORTCUTS) { + const shortcutArray = [name, icon, id]; + shortcutIcon = SettingsUtils.getIconStringFromListing(shortcutArray); + } + + if (appInfo && name === '') + rowTitle = appInfo.get_name(); + + if ((shortcutIcon === '' || shortcutIcon.length < 1) && appInfo) + shortcutIcon = appInfo.get_icon() ? appInfo.get_icon().to_string() : ''; + + if (shortcutData.isFolder) + shortcutIcon = 'folder-symbolic'; + + row.gicon = Gio.icon_new_for_string(shortcutIcon); + row.title = GLib.markup_escape_text(rowTitle, -1); + + if (id.endsWith('.desktop') && !appInfo) { + row.gicon = Gio.icon_new_for_string('dialog-warning-symbolic'); + row.title = `${_('Invalid Shortcut')} - ${row.title ? _(row.title) : id}`; + row.css_classes = ['error']; + } else { + row.css_classes = []; + } + } + + _addRowToFrame(shortcutData) { + const row = new PW.DragRow(); + + const editEntryButton = new PW.EditEntriesBox({ + row, + allow_modify: true, + allow_remove: true, + }); + + this._setRowData(row, shortcutData); + + row.connect('change-button-clicked', () => { + const dialog = new AddAppsToPinnedListWindow(this._settings, this.list_type, this.settingString); + dialog.show(); + dialog.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + this._setRowData(row, dialog.shortcutData); + dialog.destroy(); + this.saveSettings(); + } + }); + }); + row.connect('drag-drop-done', () => this.saveSettings()); + + editEntryButton.connect('modify-button-clicked', () => { + const dialog = new AddCustomLinkDialogWindow(this._settings, this, this.list_type, row.shortcutData); + dialog.show(); + dialog.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + this._setRowData(row, dialog.shortcutData); + dialog.destroy(); + this.saveSettings(); + } + }); + }); + editEntryButton.connect('entry-modified', (_self, startIndex, newIndex) => { + const splicedItem = this.frameRows.splice(startIndex, 1)[0]; + + if (newIndex >= 0) + this.frameRows.splice(newIndex, 0, splicedItem); + + this.saveSettings(); + }); + + if (shortcutData.isFolder) { + row.activatable = true; + editEntryButton.css_classes = ['flat']; + row.add_suffix(editEntryButton); + const goNextImage = new Gtk.Image({ + gicon: Gio.icon_new_for_string('go-next-symbolic'), + valign: Gtk.Align.CENTER, + }); + row.add_suffix(goNextImage); + row.connect('activated', () => { + const folderSubpage = new ListPinnedPage(this._settings, { + title: shortcutData.name, + setting_string: shortcutData.id, + list_type: Constants.MenuSettingsListType.FOLDER_PINNED_APPS, + }); + this.get_root().push_subpage(folderSubpage); + }); + } else { + row.add_suffix(editEntryButton); + row.activatable_widget = editEntryButton; + } + this.frameRows.push(row); + this.frame.add(row); + } + + _addRowsToFrame(shortcutsArray) { + shortcutsArray.forEach(shortcutData => { + this._addRowToFrame(shortcutData); + }); + } +}); + +var AddAppsToPinnedListWindow = GObject.registerClass( +class ArcMenuAddAppsToPinnedListWindow extends PW.DialogWindow { + _init(settings, parent, dialogType, settingString) { + this._settings = settings; + this._dialogType = dialogType; + this.settingString = settingString; + + if (this._dialogType === Constants.MenuSettingsListType.PINNED_APPS || + this._dialogType === Constants.MenuSettingsListType.FOLDER_PINNED_APPS) + super._init(_('Add to your Pinned Apps'), parent); + else if (this._dialogType === Constants.MenuSettingsListType.EXTRA_SHORTCUTS) + super._init(_('Add to your Extra Shortcuts'), parent); + else if (this._dialogType === Constants.MenuSettingsListType.APPLICATIONS) + super._init(_('Select Application Shortcuts'), parent); + else if (this._dialogType === Constants.MenuSettingsListType.DIRECTORIES) + super._init(_('Select Directory Shortcuts'), parent); + else if (this._dialogType === Constants.MenuSettingsListType.CONTEXT_MENU) + super._init(_('Add to the Context Menu'), parent); + + this._createShortcutsArray(); + + const extension = ExtensionPreferences.lookupByURL(import.meta.url); + + if (this._dialogType === Constants.MenuSettingsListType.PINNED_APPS || + this._dialogType === Constants.MenuSettingsListType.FOLDER_PINNED_APPS) { + const extraItem = [[_('ArcMenu Settings'), `${extension.path}/${Constants.ArcMenuLogoSymbolic}`, + Constants.ShortcutCommands.ARCMENU_SETTINGS]]; + this._loadExtraCategories(extraItem); + this._loadCategories(); + } else if (this._dialogType === Constants.MenuSettingsListType.DIRECTORIES) { + const extraLinks = this._getDirectoryLinksArray(); + extraLinks.unshift([_('Separator'), 'list-remove-symbolic', Constants.ShortcutCommands.SEPARATOR]); + extraLinks.unshift([_('Spacer'), 'list-remove-symbolic', Constants.ShortcutCommands.SPACER]); + this._loadExtraCategories(extraLinks); + } else if (this._dialogType === Constants.MenuSettingsListType.APPLICATIONS) { + const extraLinks = []; + extraLinks.push([_('ArcMenu Settings'), `${extension.path}/${Constants.ArcMenuLogoSymbolic}`, + Constants.ShortcutCommands.ARCMENU_SETTINGS]); + extraLinks.push([_('Run Command...'), 'system-run-symbolic', Constants.ShortcutCommands.RUN_COMMAND]); + extraLinks.push([_('Activities Overview'), 'view-fullscreen-symbolic', + Constants.ShortcutCommands.OVERVIEW]); + extraLinks.push([_('Show All Apps'), 'view-app-grid-symbolic', Constants.ShortcutCommands.SHOW_APPS]); + extraLinks.unshift([_('Separator'), 'list-remove-symbolic', Constants.ShortcutCommands.SEPARATOR]); + extraLinks.unshift([_('Spacer'), 'list-remove-symbolic', Constants.ShortcutCommands.SPACER]); + this._loadExtraCategories(extraLinks); + this._loadCategories(); + } else if (this._dialogType === Constants.MenuSettingsListType.CONTEXT_MENU) { + const extraLinks = []; + extraLinks.push([_('ArcMenu Settings'), `${extension.path}/${Constants.ArcMenuLogoSymbolic}`, + Constants.ShortcutCommands.SETTINGS]); + extraLinks.push([_('Menu Settings'), `${extension.path}/${Constants.ArcMenuLogoSymbolic}`, + Constants.ShortcutCommands.SETTINGS_MENU]); + extraLinks.push([_('Menu Theming'), `${extension.path}/${Constants.ArcMenuLogoSymbolic}`, + Constants.ShortcutCommands.SETTINGS_THEME]); + extraLinks.push([_('Change Menu Layout'), `${extension.path}/${Constants.ArcMenuLogoSymbolic}`, + Constants.ShortcutCommands.SETTINGS_LAYOUT]); + extraLinks.push([_('Menu Button Settings'), `${extension.path}/${Constants.ArcMenuLogoSymbolic}`, + Constants.ShortcutCommands.SETTINGS_BUTTON]); + extraLinks.push([_('About'), `${extension.path}/${Constants.ArcMenuLogoSymbolic}`, Constants.ShortcutCommands.SETTINGS_ABOUT]); + extraLinks.push([_('Panel Extension Settings'), 'application-x-addon-symbolic', + Constants.ShortcutCommands.PANEL_EXTENSION_SETTINGS]); + extraLinks.push([_('Activities Overview'), 'view-fullscreen-symbolic', + Constants.ShortcutCommands.OVERVIEW]); + extraLinks.push([_('Power Options'), 'system-shutdown-symbolic', Constants.ShortcutCommands.POWER_OPTIONS]); + extraLinks.push([_('Show Desktop'), 'computer-symbolic', Constants.ShortcutCommands.SHOW_DESKTOP]); + extraLinks.unshift([_('Separator'), 'list-remove-symbolic', Constants.ShortcutCommands.SEPARATOR]); + this._loadExtraCategories(extraLinks); + this._loadCategories(); + } else { + const extraLinks = this._getDirectoryLinksArray(); + extraLinks.unshift([_('Separator'), 'list-remove-symbolic', Constants.ShortcutCommands.SEPARATOR]); + extraLinks.push([_('Lock'), 'changes-prevent-symbolic', Constants.ShortcutCommands.LOCK]); + extraLinks.push([_('Log Out...'), 'system-log-out-symbolic', Constants.ShortcutCommands.LOG_OUT]); + extraLinks.push([_('Power Off...'), 'system-shutdown-symbolic', Constants.ShortcutCommands.POWER_OFF]); + extraLinks.push([_('Restart...'), 'system-reboot-symbolic', Constants.ShortcutCommands.RESTART]); + extraLinks.push([_('Suspend'), 'media-playback-pause-symbolic', Constants.ShortcutCommands.SUSPEND]); + extraLinks.push([_('Hybrid Sleep'), 'weather-clear-night-symbolic', + Constants.ShortcutCommands.HYBRID_SLEEP]); + extraLinks.push([_('Hibernate'), 'document-save-symbolic', Constants.ShortcutCommands.HIBERNATE]); + extraLinks.push([_('Switch User'), 'system-switch-user-symbolic', Constants.ShortcutCommands.SWITCH_USER]); + this._loadExtraCategories(extraLinks); + this._loadCategories(); + } + } + + _getDirectoryLinksArray() { + const directoryLinks = []; + directoryLinks.push([_('Home'), Constants.ShortcutCommands.HOME, Constants.ShortcutCommands.HOME]); + directoryLinks.push([_('Documents'), Constants.ShortcutCommands.DOCUMENTS, + Constants.ShortcutCommands.DOCUMENTS]); + directoryLinks.push([_('Downloads'), Constants.ShortcutCommands.DOWNLOADS, + Constants.ShortcutCommands.DOWNLOADS]); + directoryLinks.push([_('Music'), Constants.ShortcutCommands.MUSIC, Constants.ShortcutCommands.MUSIC]); + directoryLinks.push([_('Pictures'), Constants.ShortcutCommands.PICTURES, Constants.ShortcutCommands.PICTURES]); + directoryLinks.push([_('Videos'), Constants.ShortcutCommands.VIDEOS, Constants.ShortcutCommands.VIDEOS]); + directoryLinks.push([_('Computer'), Constants.ShortcutCommands.COMPUTER, Constants.ShortcutCommands.COMPUTER]); + directoryLinks.push([_('Network'), Constants.ShortcutCommands.NETWORK, Constants.ShortcutCommands.NETWORK]); + directoryLinks.push([_('Recent'), 'document-open-recent-symbolic', Constants.ShortcutCommands.RECENT]); + return directoryLinks; + } + + _createShortcutsArray() { + const appsList = this._settings.get_value(this.settingString).deep_unpack(); + + this.shortcutsArray = []; + for (let i = 0; i < appsList.length; i++) { + this.shortcutsArray.push(appsList[i].name); + this.shortcutsArray.push(appsList[i].icon); + this.shortcutsArray.push(appsList[i].id); + } + } + + findCommandMatch(command) { + for (let i = 2; i < this.shortcutsArray.length; i += 3) { + if (this.shortcutsArray[i] === command) + return true; + } + return false; + } + + _loadExtraCategories(extraCategories) { + for (const item of extraCategories) { + let subtitle = null; + if (item[2] === Constants.ShortcutCommands.PANEL_EXTENSION_SETTINGS) + subtitle = _('Dash to Panel or App Icons Taskbar'); + + const frameRow = new Adw.ActionRow({ + title: _(item[0]), + subtitle, + }); + + let iconString; + if (this._dialogType === Constants.MenuSettingsListType.DIRECTORIES || + this._dialogType === Constants.MenuSettingsListType.EXTRA_SHORTCUTS) + iconString = SettingsUtils.getIconStringFromListing([item[0], item[1], item[2]]); + else + iconString = item[1]; + + frameRow.shortcutData = { + name: _(item[0]), + icon: item[1], + id: item[2], + }; + + const iconImage = new Gtk.Image({ + gicon: Gio.icon_new_for_string(iconString), + pixel_size: 22, + }); + frameRow.add_prefix(iconImage); + let match = this.findCommandMatch(frameRow.shortcutData.id); + + if (frameRow.shortcutData.id === Constants.ShortcutCommands.SEPARATOR) + match = false; + + this.addButtonAction(frameRow, match); + this.pageGroup.add(frameRow); + } + } + + _loadCategories() { + const allApps = Gio.app_info_get_all(); + allApps.sort((a, b) => { + const _a = a.get_display_name().toLowerCase(); + const _b = b.get_display_name().toLowerCase(); + return GLib.strcmp0(_a, _b); + }); + const showAllApps = this._dialogType === Constants.MenuSettingsListType.CONTEXT_MENU; + for (let i = 0; i < allApps.length; i++) { + if (allApps[i].should_show() || showAllApps) { + const frameRow = new Adw.ActionRow({ + title: GLib.markup_escape_text(allApps[i].get_display_name(), -1), + }); + + frameRow.shortcutData = { + id: allApps[i].get_id(), + }; + + const icon = allApps[i].get_icon() ? allApps[i].get_icon().to_string() : 'dialog-information'; + + const iconImage = new Gtk.Image({ + gicon: Gio.icon_new_for_string(icon), + pixel_size: 22, + }); + frameRow.add_prefix(iconImage); + + const match = this.findCommandMatch(allApps[i].get_id()); + + this.addButtonAction(frameRow, match); + this.pageGroup.add(frameRow); + } + } + } + + addButtonAction(frameRow, match) { + const checkButton = new Gtk.Button({ + icon_name: match ? 'list-remove-symbolic' : 'list-add-symbolic', + valign: Gtk.Align.CENTER, + }); + checkButton.connect('clicked', () => { + this.shortcutData = frameRow.shortcutData; + + if (!match) { + this.currentToast?.dismiss(); + + this.currentToast = new Adw.Toast({ + title: _('%s has been added').format(frameRow.title), + timeout: 2, + }); + this.currentToast.connect('dismissed', () => (this.currentToast = null)); + + this.add_toast(this.currentToast); + this.emit('response', Gtk.ResponseType.APPLY); + } else { + this.currentToast?.dismiss(); + + this.currentToast = new Adw.Toast({ + title: _('%s has been removed').format(frameRow.title), + timeout: 2, + }); + this.currentToast.connect('dismissed', () => (this.currentToast = null)); + + this.add_toast(this.currentToast); + this.emit('response', Gtk.ResponseType.REJECT); + } + + if (frameRow.shortcutData.id === Constants.ShortcutCommands.SEPARATOR || + frameRow.shortcutData.id === Constants.ShortcutCommands.SPACER) + return; + + match = !match; + checkButton.icon_name = match ? 'list-remove-symbolic' : 'list-add-symbolic'; + }); + frameRow.add_suffix(checkButton); + frameRow.activatable_widget = checkButton; + } +}); + +var AddCustomLinkDialogWindow = GObject.registerClass( +class ArcMenuAddCustomLinkDialogWindow extends PW.DialogWindow { + _init(settings, parent, dialogType, shortcutData = null) { + let title = _('Add a Custom Shortcut'); + + const isPinnedApp = dialogType === Constants.MenuSettingsListType.PINNED_APPS || + dialogType === Constants.MenuSettingsListType.FOLDER_PINNED_APPS; + + let onlyNameChanges = false; + if (shortcutData !== null) { + if (isPinnedApp) { + if (shortcutData.isFolder) + onlyNameChanges = true; + title = _('Edit Pinned App'); + } else { + title = _('Edit Shortcut'); + } + } + + super._init(_(title), parent); + this.set_default_size(600, 325); + this.search_enabled = false; + this._settings = settings; + this._dialogType = dialogType; + this.shortcutData = shortcutData; + + const nameFrameRow = new Adw.ActionRow({ + title: _('Title'), + }); + + const nameEntry = new Gtk.Entry({ + valign: Gtk.Align.CENTER, + hexpand: true, + halign: Gtk.Align.FILL, + }); + nameFrameRow.add_suffix(nameEntry); + this.pageGroup.add(nameFrameRow); + + const iconFrameRow = new Adw.ActionRow({ + title: _('Icon'), + visible: !onlyNameChanges, + }); + const iconEntry = new Gtk.Entry({ + valign: Gtk.Align.CENTER, + hexpand: true, + halign: Gtk.Align.FILL, + }); + + const fileFilter = new Gtk.FileFilter(); + fileFilter.add_pixbuf_formats(); + const fileChooserButton = new Gtk.Button({ + icon_name: 'search-symbolic', + tooltip_text: _('Browse...'), + valign: Gtk.Align.CENTER, + }); + + fileChooserButton.connect('clicked', () => { + const dialog = new Gtk.FileChooserDialog({ + title: _('Select an Icon'), + transient_for: this.get_root(), + modal: true, + action: Gtk.FileChooserAction.OPEN, + }); + dialog.add_button('_Cancel', Gtk.ResponseType.CANCEL); + dialog.add_button('_Open', Gtk.ResponseType.ACCEPT); + + dialog.set_filter(fileFilter); + + dialog.connect('response', (self, response) => { + if (response === Gtk.ResponseType.ACCEPT) { + const iconFilepath = dialog.get_file().get_path(); + iconEntry.set_text(iconFilepath); + dialog.destroy(); + } else if (response === Gtk.ResponseType.CANCEL) { + dialog.destroy(); + } + }); + dialog.show(); + }); + iconFrameRow.add_suffix(iconEntry); + iconFrameRow.add_suffix(fileChooserButton); + this.pageGroup.add(iconFrameRow); + + if (this._dialogType === Constants.MenuSettingsListType.DIRECTORIES) + iconEntry.set_text(Constants.ShortcutCommands.FOLDER); + + const cmdFrameRow = new Adw.ActionRow({ + title: this._dialogType === Constants.MenuSettingsListType.DIRECTORIES ? _('Directory') : _('Command'), + visible: !onlyNameChanges, + }); + + const cmdEntry = new Gtk.Entry({ + valign: Gtk.Align.CENTER, + hexpand: true, + halign: Gtk.Align.FILL, + }); + cmdFrameRow.add_suffix(cmdEntry); + this.pageGroup.add(cmdFrameRow); + + const addButton = new Gtk.Button({ + label: this.shortcutData ? _('Apply') : _('Add'), + halign: Gtk.Align.END, + css_classes: ['suggested-action'], + }); + + if (this.shortcutData !== null) { + nameEntry.text = this.shortcutData.name ?? ''; + iconEntry.text = this.shortcutData.icon ?? ''; + cmdEntry.text = this.shortcutData.id ?? ''; + } else { + this.shortcutData = {}; + } + + addButton.connect('clicked', () => { + const name = nameEntry.get_text(); + const icon = iconEntry.get_text(); + const id = cmdEntry.get_text(); + + if (name.length) + this.shortcutData.name = name; + else if (this.shortcutData.name !== undefined) + delete this.shortcutData.name; + + if (icon.length) + this.shortcutData.icon = icon; + else if (this.shortcutData.icon !== undefined) + delete this.shortcutData.icon; + + if (id.length) + this.shortcutData.id = id; + else if (this.shortcutData.id !== undefined) + delete this.shortcutData.id; + + this.emit('response', Gtk.ResponseType.APPLY); + }); + + this.pageGroup.set_header_suffix(addButton); + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/SearchOptionsPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/SearchOptionsPage.js new file mode 100644 index 0000000..5fbbc83 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/SearchOptionsPage.js @@ -0,0 +1,175 @@ +import Adw from 'gi://Adw'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import {SubPage} from './SubPage.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const SearchOptionsPage = GObject.registerClass( +class ArcMenuSearchOptionsPage extends SubPage { + _init(settings, params) { + super._init(settings, params); + + this.searchResultsDetails = this._settings.get_boolean('show-search-result-details'); + this.openWindowsSearchProvider = this._settings.get_boolean('search-provider-open-windows'); + this.recentFilesSearchProvider = this._settings.get_boolean('search-provider-recent-files'); + this.highlightSearchResultTerms = this._settings.get_boolean('highlight-search-result-terms'); + this.maxSearchResults = this._settings.get_int('max-search-results'); + + const searchProvidersFrame = new Adw.PreferencesGroup({ + title: _('Extra Search Providers'), + }); + + const openWindowsSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + openWindowsSwitch.set_active(this.openWindowsSearchProvider); + openWindowsSwitch.connect('notify::active', widget => { + this._settings.set_boolean('search-provider-open-windows', widget.get_active()); + }); + const openWindowsRow = new Adw.ActionRow({ + title: _('Search for open windows across all workspaces'), + activatable_widget: openWindowsSwitch, + }); + openWindowsRow.add_suffix(openWindowsSwitch); + searchProvidersFrame.add(openWindowsRow); + + const recentFilesSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + recentFilesSwitch.set_active(this.recentFilesSearchProvider); + recentFilesSwitch.connect('notify::active', widget => { + this._settings.set_boolean('search-provider-recent-files', widget.get_active()); + }); + const recentFilesRow = new Adw.ActionRow({ + title: _('Search for recent files'), + activatable_widget: recentFilesSwitch, + }); + recentFilesRow.add_suffix(recentFilesSwitch); + searchProvidersFrame.add(recentFilesRow); + this.add(searchProvidersFrame); + + const searchOptionsFrame = new Adw.PreferencesGroup({ + title: _('Search Options'), + }); + + const searchDescriptionsSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + searchDescriptionsSwitch.set_active(this.searchResultsDetails); + searchDescriptionsSwitch.connect('notify::active', widget => { + this._settings.set_boolean('show-search-result-details', widget.get_active()); + }); + const searchDescriptionsRow = new Adw.ActionRow({ + title: _('Show Search Result Descriptions'), + activatable_widget: searchDescriptionsSwitch, + }); + searchDescriptionsRow.add_suffix(searchDescriptionsSwitch); + searchOptionsFrame.add(searchDescriptionsRow); + + const highlightSearchResultSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + highlightSearchResultSwitch.set_active(this.highlightSearchResultTerms); + highlightSearchResultSwitch.connect('notify::active', widget => { + this._settings.set_boolean('highlight-search-result-terms', widget.get_active()); + }); + + const highlightSearchResultRow = new Adw.ActionRow({ + title: _('Highlight search result terms'), + activatable_widget: highlightSearchResultSwitch, + }); + highlightSearchResultRow.add_suffix(highlightSearchResultSwitch); + searchOptionsFrame.add(highlightSearchResultRow); + + const maxSearchResultsScale = new Gtk.SpinButton({ + orientation: Gtk.Orientation.HORIZONTAL, + adjustment: new Gtk.Adjustment({ + lower: 2, + upper: 10, + step_increment: 1, + page_increment: 1, + page_size: 0, + }), + digits: 0, + valign: Gtk.Align.CENTER, + }); + const maxSearchResultsRow = new Adw.ActionRow({ + title: _('Max Search Results'), + activatable_widget: maxSearchResultsScale, + }); + maxSearchResultsScale.set_value(this.maxSearchResults); + maxSearchResultsScale.connect('value-changed', widget => { + this._settings.set_int('max-search-results', widget.get_value()); + }); + maxSearchResultsRow.add_suffix(maxSearchResultsScale); + searchOptionsFrame.add(maxSearchResultsRow); + this.add(searchOptionsFrame); + + const [searchBorderEnabled, searchBorderValue] = + this._settings.get_value('search-entry-border-radius').deep_unpack(); + + const searchBorderSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + searchBorderSwitch.connect('notify::active', widget => { + const [oldEnabled_, oldValue] = this._settings.get_value('search-entry-border-radius').deep_unpack(); + this._settings.set_value('search-entry-border-radius', + new GLib.Variant('(bi)', [widget.get_active(), oldValue])); + + searchBorderSpinButton.set_sensitive(widget.get_active()); + }); + const searchBorderSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: 0, + upper: 25, + step_increment: 1, + }), + climb_rate: 1, + digits: 0, + numeric: true, + valign: Gtk.Align.CENTER, + value: searchBorderValue, + sensitive: searchBorderEnabled, + }); + searchBorderSpinButton.connect('value-changed', widget => { + const [oldEnabled, oldValue_] = this._settings.get_value('search-entry-border-radius').deep_unpack(); + this._settings.set_value('search-entry-border-radius', + new GLib.Variant('(bi)', [oldEnabled, widget.get_value()])); + }); + + const searchBorderRow = new Adw.ActionRow({ + title: _('Search Box Border Radius'), + activatable_widget: searchBorderSwitch, + }); + searchBorderRow.add_suffix(searchBorderSwitch); + searchBorderRow.add_suffix(new Gtk.Separator({ + orientation: Gtk.Orientation.VERTICAL, + margin_top: 10, + margin_bottom: 10, + })); + searchBorderRow.add_suffix(searchBorderSpinButton); + searchBorderSwitch.set_active(searchBorderEnabled); + searchOptionsFrame.add(searchBorderRow); + + this.restoreDefaults = () => { + this.searchResultsDetails = this._settings.get_default_value('show-search-result-details').unpack(); + this.openWindowsSearchProvider = this._settings.get_default_value('search-provider-open-windows').unpack(); + this.recentFilesSearchProvider = this._settings.get_default_value('search-provider-recent-files').unpack(); + this.highlightSearchResultTerms = + this._settings.get_default_value('highlight-search-result-terms').unpack(); + this.maxSearchResults = this._settings.get_default_value('max-search-results').unpack(); + openWindowsSwitch.set_active(this.openWindowsSearchProvider); + recentFilesSwitch.set_active(this.recentFilesSearchProvider); + highlightSearchResultSwitch.set_active(this.highlightSearchResultTerms); + maxSearchResultsScale.set_value(this.maxSearchResults); + const [searchBorderEnabled_, defaultSearchBorderValue] = + this._settings.get_default_value('search-entry-border-radius').deep_unpack(); + searchBorderSpinButton.set_value(defaultSearchBorderValue); + searchBorderSwitch.set_active(true); + searchDescriptionsSwitch.set_active(this.searchResultsDetails); + }; + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/SubPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/SubPage.js new file mode 100644 index 0000000..fc3d88c --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/SubPage.js @@ -0,0 +1,98 @@ +import Adw from 'gi://Adw'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const SubPage = GObject.registerClass({ + Properties: { + 'setting-string': GObject.ParamSpec.string( + 'setting-string', 'setting-string', 'setting-string', + GObject.ParamFlags.READWRITE, + ''), + 'list-type': GObject.ParamSpec.int( + 'list-type', 'list-type', 'list-type', + GObject.ParamFlags.READWRITE, + 0, GLib.MAXINT32, 1), + 'preferences-page': GObject.ParamSpec.boolean( + 'preferences-page', 'preferences-page', 'preferences-page', + GObject.ParamFlags.READWRITE, + true), + }, +}, +class ArcMenuSubPage extends Adw.NavigationPage { + _init(settings, params) { + super._init({ + ...params, + }); + this._settings = settings; + + this.headerBar = new Adw.HeaderBar(); + + const sidebarToolBarView = new Adw.ToolbarView(); + + if (this.preferences_page) { + sidebarToolBarView.add_top_bar(this.headerBar); + this.set_child(sidebarToolBarView); + this.page = new PrefsPage(); + sidebarToolBarView.set_content(this.page); + } + + this.restoreDefaultsButton = new Gtk.Button({ + icon_name: 'view-refresh-symbolic', + tooltip_text: _('Reset settings'), + css_classes: ['flat'], + }); + this.restoreDefaultsButton.connect('clicked', () => { + const dialog = new Gtk.MessageDialog({ + text: `${_('Reset all %s settings?').format(this.title)}`, + secondary_text: _('All %s settings will be reset to the default value.').format(this.title), + use_markup: true, + buttons: Gtk.ButtonsType.YES_NO, + message_type: Gtk.MessageType.WARNING, + transient_for: this.get_root(), + modal: true, + }); + dialog.connect('response', (widget, response) => { + if (response === Gtk.ResponseType.YES) + this.restoreDefaults(); + dialog.destroy(); + }); + dialog.show(); + }); + + if (this.preferences_page) + this.headerBar.pack_end(this.restoreDefaultsButton); + } + + add(widget) { + if (this.preferences_page) + this.page.add(widget); + else + this.set_child(widget); + } + + resetScrollAdjustment() { + if (!this.preferences_page) + return; + + const maybeScrolledWindowChild = [...this.page][0]; + + if (maybeScrolledWindowChild instanceof Gtk.ScrolledWindow) + maybeScrolledWindowChild.vadjustment.value = 0; + } +}); + +var PrefsPage = GObject.registerClass( +class ArcMenuPrefsPage extends Adw.PreferencesPage { + _init(params) { + super._init(params); + this.children = []; + } + + add(page) { + this.children.push(page); + super.add(page); + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ThemePage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ThemePage.js new file mode 100644 index 0000000..a1334a8 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ThemePage.js @@ -0,0 +1,339 @@ +import Adw from 'gi://Adw'; +import Gdk from 'gi://Gdk'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import * as SettingsUtils from '../SettingsUtils.js'; +import {SubPage} from './SubPage.js'; +import {SaveThemeDialog, ManageThemesDialog} from './ThemingDialog.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const ThemePage = GObject.registerClass( +class ArcMenuThemePage extends SubPage { + _init(settings, params) { + super._init(settings, params); + this.restoreDefaultsButton.visible = false; + + const overrideThemeGroup = new Adw.PreferencesGroup(); + this.add(overrideThemeGroup); + + const overrideThemeSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + overrideThemeSwitch.connect('notify::active', widget => { + if (widget.get_active()) { + menuThemesGroup.show(); + menuGroup.show(); + menuItemsGroup.show(); + } else { + menuThemesGroup.hide(); + menuGroup.hide(); + menuItemsGroup.hide(); + } + this._settings.set_boolean('override-menu-theme', widget.get_active()); + }); + const overrideThemeRow = new Adw.ActionRow({ + title: _('Override Theme'), + subtitle: _('Results may vary with third party themes'), + activatable_widget: overrideThemeSwitch, + }); + overrideThemeRow.add_suffix(overrideThemeSwitch); + overrideThemeGroup.add(overrideThemeRow); + + const menuThemesGroup = new Adw.PreferencesGroup({ + title: _('Menu Themes'), + }); + this.add(menuThemesGroup); + + // Theme Drop Down Section---------- + const themeList = new Gio.ListStore(); + this.createIconList(themeList); + + const factory = new Gtk.SignalListItemFactory(); + factory.connect('setup', (factory_, listItem) => { + const box = new Gtk.Grid({ + column_spacing: 8, + valign: Gtk.Align.CENTER, + }); + + const image = new Gtk.Picture({ + content_fit: Gtk.ContentFit.SCALE_DOWN, + valign: Gtk.Align.CENTER, + }); + image.set_size_request(42, 14); + const label = new Gtk.Label({ + valign: Gtk.Align.CENTER, + }); + box.attach(image, 0, 0, 1, 1); + box.attach(label, 1, 0, 1, 1); + + listItem.set_child(box); + }); + + factory.connect('bind', (factory_, listItem) => { + const item = listItem.get_item(); + const box = listItem.get_child(); + const image = box.get_child_at(0, 0); + const label = box.get_child_at(1, 0); + + const pixbuf = item.pixbuf; + const texture = Gdk.Texture.new_for_pixbuf(pixbuf); + + label.set_label(item.name); + image.set_paintable(texture); + }); + + const themeDropDown = new Gtk.DropDown({ + factory, + model: themeList, + valign: Gtk.Align.CENTER, + }); + + themeDropDown.connect('notify::selected-item', widget => { + const index = widget.get_selected(); + if (index < 0 || index === Gtk.INVALID_LIST_POSITION) + return; + + const menuThemes = this._settings.get_value('menu-themes').deep_unpack(); + + menuBGColorRow.setColor(menuThemes[index][1]); + menuFGColorRow.setColor(menuThemes[index][2]); + menuBorderColorRow.setColor(menuThemes[index][3]); + + menuBorderWidthRow.setValue(menuThemes[index][4]); + menuBorderRadiusRow.setValue(menuThemes[index][5]); + menuFontSizeRow.setValue(menuThemes[index][6]); + + menuSeparatorColorRow.setColor(menuThemes[index][7]); + itemHoverBGColorRow.setColor(menuThemes[index][8]); + itemHoverFGColorRow.setColor(menuThemes[index][9]); + itemActiveBGColorRow.setColor(menuThemes[index][10]); + itemActiveFGColorRow.setColor(menuThemes[index][11]); + + this.checkIfThemeMatch(); + }); + const menuThemesRow = new Adw.ActionRow({ + title: _('Current Theme'), + activatable_widget: themeDropDown, + }); + menuThemesGroup.add(menuThemesRow); + // --------------------------------- + + // Manage Themes Section------------ + const manageThemesButton = new Gtk.Button({ + icon_name: 'emblem-system-symbolic', + valign: Gtk.Align.CENTER, + }); + manageThemesButton.connect('clicked', () => { + const manageThemesDialog = new ManageThemesDialog(this._settings, this); + manageThemesDialog.show(); + manageThemesDialog.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + themeList.remove_all(); + this.createIconList(themeList); + this.checkIfThemeMatch(); + } + }); + }); + menuThemesRow.add_suffix(manageThemesButton); + menuThemesRow.add_suffix(themeDropDown); + // --------------------------------- + + // Save Theme Section--------------- + const menuThemeSaveButton = new Gtk.Button({ + label: _('Save as Theme'), + valign: Gtk.Align.CENTER, + css_classes: ['suggested-action'], + }); + menuThemeSaveButton.connect('clicked', () => { + const saveThemeDialog = new SaveThemeDialog(this._settings, this); + saveThemeDialog.show(); + saveThemeDialog.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + const menuThemes = this._settings.get_value('menu-themes').deep_unpack(); + const currentSettingsArray = [saveThemeDialog.themeName, menuBGColorRow.getColor(), + menuFGColorRow.getColor(), menuBorderColorRow.getColor(), + menuBorderWidthRow.getValue().toString(), menuBorderRadiusRow.getValue().toString(), + menuFontSizeRow.getValue().toString(), menuSeparatorColorRow.getColor(), + itemHoverBGColorRow.getColor(), itemHoverFGColorRow.getColor(), + itemActiveBGColorRow.getColor(), itemActiveFGColorRow.getColor()]; + menuThemes.push(currentSettingsArray); + this._settings.set_value('menu-themes', new GLib.Variant('aas', menuThemes)); + + themeList.remove_all(); + this.createIconList(themeList); + this.checkIfThemeMatch(); + + saveThemeDialog.destroy(); + } + }); + }); + // --------------------------------- + + const menuGroup = new Adw.PreferencesGroup({ + title: _('Menu Styling'), + header_suffix: menuThemeSaveButton, + }); + this.add(menuGroup); + + const menuBGColorRow = this._createColorRow(_('Background Color'), 'menu-background-color'); + menuGroup.add(menuBGColorRow); + + const menuFGColorRow = this._createColorRow(_('Foreground Color'), 'menu-foreground-color'); + menuGroup.add(menuFGColorRow); + + const menuBorderColorRow = this._createColorRow(_('Border Color'), 'menu-border-color'); + menuGroup.add(menuBorderColorRow); + + const menuBorderWidthRow = this._createSpinButtonRow(_('Border Width'), 'menu-border-width', 0, 5); + menuGroup.add(menuBorderWidthRow); + + const menuBorderRadiusRow = this._createSpinButtonRow(_('Border Radius'), 'menu-border-radius', 0, 25); + menuGroup.add(menuBorderRadiusRow); + + const menuFontSizeRow = this._createSpinButtonRow(_('Font Size'), 'menu-font-size', 8, 18); + menuGroup.add(menuFontSizeRow); + + const menuSeparatorColorRow = this._createColorRow(_('Separator Color'), 'menu-separator-color'); + menuGroup.add(menuSeparatorColorRow); + + const menuItemsGroup = new Adw.PreferencesGroup({ + title: _('Menu Items Styling'), + }); + this.add(menuItemsGroup); + + const itemHoverBGColorRow = this._createColorRow(`${_('Hover')} ${_('Background Color')}`, 'menu-item-hover-bg-color'); + menuItemsGroup.add(itemHoverBGColorRow); + + const itemHoverFGColorRow = this._createColorRow(`${_('Hover')} ${_('Foreground Color')}`, 'menu-item-hover-fg-color'); + menuItemsGroup.add(itemHoverFGColorRow); + + const itemActiveBGColorRow = this._createColorRow(`${_('Active')} ${_('Background Color')}`, 'menu-item-active-bg-color'); + menuItemsGroup.add(itemActiveBGColorRow); + + const itemActiveFGColorRow = this._createColorRow(`${_('Active')} ${_('Foreground Color')}`, 'menu-item-active-fg-color'); + menuItemsGroup.add(itemActiveFGColorRow); + + const overrideMenuTheme = this._settings.get_boolean('override-menu-theme'); + overrideThemeSwitch.set_active(overrideMenuTheme); + if (!overrideMenuTheme) { + menuThemesGroup.hide(); + menuGroup.hide(); + menuItemsGroup.hide(); + } + + this.checkIfThemeMatch = () => { + const currentSettingsArray = ['', menuBGColorRow.getColor(), menuFGColorRow.getColor(), + menuBorderColorRow.getColor(), menuBorderWidthRow.getValue().toString(), + menuBorderRadiusRow.getValue().toString(), menuFontSizeRow.getValue().toString(), + menuSeparatorColorRow.getColor(), itemHoverBGColorRow.getColor(), itemHoverFGColorRow.getColor(), + itemActiveBGColorRow.getColor(), itemActiveFGColorRow.getColor()]; + + const menuThemes = this._settings.get_value('menu-themes').deep_unpack(); + let index = 0; + let matchFound = false; + for (const theme of menuThemes) { + for (let i = 1; i < currentSettingsArray.length - 1; i++) { + if (currentSettingsArray[i] !== theme[i]) { + matchFound = false; + break; + } + matchFound = true; + } + + if (matchFound) { + themeDropDown.set_selected(index); + menuThemeSaveButton.set_sensitive(false); + break; + } + index++; + } + if (!matchFound) { + menuThemeSaveButton.set_sensitive(true); + themeDropDown.set_selected(Gtk.INVALID_LIST_POSITION); + } + }; + + this.checkIfThemeMatch(); + } + + createIconList(store) { + const menuThemes = this._settings.get_value('menu-themes').deep_unpack(); + for (const theme of menuThemes) { + const pixbuf = SettingsUtils.createThemePreviewPixbuf(theme[1], theme[2], theme[3], theme[8]); + + const themeListItem = new ThemeListItem({ + name: theme[0], + }); + themeListItem.pixbuf = pixbuf; + + store.append(themeListItem); + } + } + + _createColorRow(title, setting) { + const colorButton = new Gtk.ColorButton({ + use_alpha: true, + valign: Gtk.Align.CENTER, + rgba: SettingsUtils.parseRGBA(this._settings.get_string(setting)), + }); + colorButton.connect('notify::rgba', widget => { + const colorString = widget.get_rgba().to_string(); + this._settings.set_string(setting, colorString); + this.checkIfThemeMatch(); + }); + const colorRow = new Adw.ActionRow({ + title: _(title), + activatable_widget: colorButton, + }); + colorRow.add_suffix(colorButton); + colorRow.setColor = color => { + colorButton.set_rgba(SettingsUtils.parseRGBA(color)); + }; + colorRow.getColor = () => { + return colorButton.get_rgba().to_string(); + }; + return colorRow; + } + + _createSpinButtonRow(title, setting, lower, upper) { + const spinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower, + upper, + step_increment: 1, + }), + climb_rate: 1, + digits: 0, + numeric: true, + valign: Gtk.Align.CENTER, + value: this._settings.get_int(setting), + }); + spinButton.connect('notify::value', widget => { + this.checkIfThemeMatch(); + this._settings.set_int(setting, widget.get_value()); + }); + + const spinRow = new Adw.ActionRow({ + title: _(title), + activatable_widget: spinButton, + }); + spinRow.add_suffix(spinButton); + spinRow.setValue = value => { + spinButton.set_value(value); + }; + spinRow.getValue = () => { + return spinButton.get_value(); + }; + return spinRow; + } +}); + +const ThemeListItem = GObject.registerClass({ + Properties: { + 'name': GObject.ParamSpec.string('name', 'Name', 'Name of the item', GObject.ParamFlags.READWRITE, ''), + }, +}, class Item extends GObject.Object {}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ThemingDialog.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ThemingDialog.js new file mode 100644 index 0000000..43ab3b3 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/ThemingDialog.js @@ -0,0 +1,405 @@ +import Adw from 'gi://Adw'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import * as PW from '../../prefsWidgets.js'; +import * as SettingsUtils from '../SettingsUtils.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const SaveThemeDialog = GObject.registerClass( +class ArcMenuSaveThemeDialog extends PW.DialogWindow { + _init(settings, parent, themeName) { + super._init(_('Save Theme As...'), parent); + this._settings = settings; + this.themeName = themeName; + this.search_enabled = false; + this.set_default_size(550, 220); + + const themeNameEntry = new Gtk.Entry({ + valign: Gtk.Align.CENTER, + hexpand: true, + halign: Gtk.Align.FILL, + }); + const themeNameRow = new Adw.ActionRow({ + title: _('Theme Name'), + activatable_widget: themeNameEntry, + }); + themeNameRow.add_suffix(themeNameEntry); + this.pageGroup.add(themeNameRow); + + if (this.themeName) + themeNameEntry.set_text(this.themeName); + + themeNameEntry.connect('changed', () => { + if (themeNameEntry.get_text().length > 0) + saveButton.set_sensitive(true); + else + saveButton.set_sensitive(false); + }); + + const saveButton = new Gtk.Button({ + label: _('Save Theme'), + sensitive: false, + halign: Gtk.Align.END, + css_classes: ['suggested-action'], + }); + saveButton.connect('clicked', () => { + this.themeName = themeNameEntry.get_text(); + this.emit('response', Gtk.ResponseType.APPLY); + }); + this.pageGroup.set_header_suffix(saveButton); + } +}); + +export const ManageThemesDialog = GObject.registerClass( +class ArcMenuManageThemesDialog extends PW.DialogWindow { + _init(settings, parent) { + super._init(_('Manage Themes'), parent); + this._settings = settings; + this.frameRows = []; + + // Save/Load Settings---------------------------------------------------------- + const saveLoadGroup = new Adw.PreferencesGroup(); + const saveLoadRow = new Adw.ActionRow({ + title: _('Menu Themes'), + }); + const loadButton = new Gtk.Button({ + label: _('Load'), + valign: Gtk.Align.CENTER, + }); + loadButton.connect('clicked', () => { + this._showFileChooser( + `${_('Load')} ${_('Menu Themes')}`, + {action: Gtk.FileChooserAction.OPEN}, + '_Open', + filename => { + if (filename && GLib.file_test(filename, GLib.FileTest.EXISTS)) { + const settingsFile = Gio.File.new_for_path(filename); + const [ok_, bytes] = settingsFile.load_contents(null); + + const themesJSON = JSON.parse(new TextDecoder().decode(bytes)); + + const importedMenuThemes = []; + + for (const theme of themesJSON) { + const menuTheme = []; + menuTheme.push(theme['Name']); + menuTheme.push(theme['Menu_Background_Color']); + menuTheme.push(theme['Menu_Foreground_Color']); + menuTheme.push(theme['Menu_Border_Color']); + menuTheme.push(theme['Menu_Border_Width']); + menuTheme.push(theme['Menu_Border_Radius']); + menuTheme.push(theme['Menu_Font_Size']); + menuTheme.push(theme['Menu_Separator_Color']); + menuTheme.push(theme['Item_Hover_Background_Color']); + menuTheme.push(theme['Item_Hover_Foreground_Color']); + menuTheme.push(theme['Item_Active_Background_Color']); + menuTheme.push(theme['Item_Active_Foreground_Color']); + importedMenuThemes.push(menuTheme); + } + + const dialog = new SaveLoadThemesPage(this._settings, this, + importedMenuThemes, SaveLoadType.LOAD); + this.push_subpage(dialog); + dialog.connect('response', (_w, response) => { + let menuThemes = this._settings.get_value('menu-themes').deep_unpack(); + const selectedThemesArray = dialog.selecetedThemesArray; + menuThemes = menuThemes.concat(selectedThemesArray); + this.pop_subpage(); + + if (response === Gtk.ResponseType.ACCEPT) { + this._settings.set_value('menu-themes', new GLib.Variant('aas', menuThemes)); + this.frameRows.forEach(child => { + this.pageGroup.remove(child); + }); + this.frameRows = []; + this.populateFrameRows(); + this.emit('response', Gtk.ResponseType.APPLY); + } + }); + } + } + ); + }); + const saveButton = new Gtk.Button({ + label: _('Save'), + valign: Gtk.Align.CENTER, + }); + saveButton.connect('clicked', () => { + const menuThemes = this._settings.get_value('menu-themes').deep_unpack(); + const dialog = new SaveLoadThemesPage(this._settings, this, menuThemes, SaveLoadType.SAVE); + this.push_subpage(dialog); + dialog.connect('response', (_w, response) => { + const selectedThemesArray = dialog.selecetedThemesArray; + this.pop_subpage(); + + if (response === Gtk.ResponseType.ACCEPT) { + this._showFileChooser( + `${_('Save')} ${_('Menu Themes')}`, + {action: Gtk.FileChooserAction.SAVE}, + '_Save', + + filename => { + const file = Gio.file_new_for_path(filename); + const raw = file.replace(null, false, Gio.FileCreateFlags.NONE, null); + const out = Gio.BufferedOutputStream.new_sized(raw, 4096); + + const json = this.getMenuThemesJSON(selectedThemesArray); + + out.write_all(json, null); + out.close(null); + } + ); + } + }); + }); + saveLoadRow.add_suffix(saveButton); + saveLoadRow.add_suffix(loadButton); + saveLoadGroup.add(saveLoadRow); + this.page.remove(this.pageGroup); + this.page.add(saveLoadGroup); + this.page.add(this.pageGroup); + + this.populateFrameRows(); + } + + getMenuThemesJSON(menuThemes) { + /* Order of elements in a theme: + [Theme Name, menuBGColor, menuFGColor, menuBorderColor, menuBorderWidth, menuBorderRadius, + menuFontSize, menuSeparatorColor, itemHoverBGColor, itemHoverFGColor, itemActiveBGColor, itemActiveFGColor]*/ + + const menuThemeDict = []; + for (let i = 0; i < menuThemes.length; i++) { + menuThemeDict.push({ + 'Name': menuThemes[i][0], + 'Menu_Background_Color': menuThemes[i][1], + 'Menu_Foreground_Color': menuThemes[i][2], + 'Menu_Border_Color': menuThemes[i][3], + 'Menu_Border_Width': menuThemes[i][4], + 'Menu_Border_Radius': menuThemes[i][5], + 'Menu_Font_Size': menuThemes[i][6], + 'Menu_Separator_Color': menuThemes[i][7], + 'Item_Hover_Background_Color': menuThemes[i][8], + 'Item_Hover_Foreground_Color': menuThemes[i][9], + 'Item_Active_Background_Color': menuThemes[i][10], + 'Item_Active_Foreground_Color': menuThemes[i][11], + }); + } + + return JSON.stringify(menuThemeDict, null, 2); + } + + populateFrameRows() { + const menuThemes = this._settings.get_value('menu-themes').deep_unpack(); + for (let i = 0; i < menuThemes.length; i++) { + const theme = menuThemes[i]; + const pixbuf = SettingsUtils.createThemePreviewPixbuf(theme[1], theme[2], theme[3], theme[8]); + + const row = new PW.DragRow({ + title: theme[0], + pixbuf, + icon_pixel_size: 42, + }); + this.pageGroup.add(row); + + row.theme = theme; + + row.connect('drag-drop-done', () => this.saveSettings()); + + const editEntryButton = new PW.EditEntriesBox({ + row, + allow_modify: true, + allow_remove: true, + }); + row.activatable_widget = editEntryButton; + row.add_suffix(editEntryButton); + + editEntryButton.connect('modify-button-clicked', () => { + const dialog = new SaveThemeDialog(this._settings, this, theme[0]); + dialog.show(); + dialog.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + theme.splice(0, 1, dialog.themeName); + row.title = dialog.themeName; + row.theme = theme; + this.saveSettings(); + dialog.destroy(); + } + }); + }); + + editEntryButton.connect('entry-modified', (_self, startIndex, newIndex) => { + const splicedItem = this.frameRows.splice(startIndex, 1)[0]; + + if (newIndex >= 0) + this.frameRows.splice(newIndex, 0, splicedItem); + + this.saveSettings(); + }); + + this.frameRows.push(row); + } + } + + saveSettings() { + const array = []; + + this.frameRows.sort((a, b) => { + return a.get_index() - b.get_index(); + }); + + this.frameRows.forEach(child => { + array.push(child.theme); + }); + + this._settings.set_value('menu-themes', new GLib.Variant('aas', array)); + this.emit('response', Gtk.ResponseType.APPLY); + } + + _showFileChooser(title, params, acceptBtn, acceptHandler) { + const dialog = new Gtk.FileChooserDialog({ + title: _(title), + transient_for: this.get_root(), + modal: true, + action: params.action, + }); + dialog.add_button('_Cancel', Gtk.ResponseType.CANCEL); + dialog.add_button(acceptBtn, Gtk.ResponseType.ACCEPT); + + dialog.connect('response', (self, response) => { + if (response === Gtk.ResponseType.ACCEPT) { + try { + acceptHandler(dialog.get_file().get_path()); + } catch (e) { + log(`ArcMenu - Filechooser error: ${e}`); + } + } + dialog.destroy(); + }); + + dialog.show(); + } +}); + +const SaveLoadType = { + SAVE: 0, + LOAD: 1, +}; + +var SaveLoadThemesPage = GObject.registerClass({ + Properties: { + 'selected-themes': GObject.ParamSpec.int( + 'selected-themes', 'selected-themes', 'selected-themes', + GObject.ParamFlags.READWRITE, + 0, GLib.MAXINT32, 0), + }, + Signals: { + 'response': {param_types: [GObject.TYPE_INT]}, + }, +}, class ArcMenuSaveLoadThemesPage extends Adw.NavigationPage { + _init(settings, parent, themesArray, saveLoadType) { + super._init(); + + this._parent = parent; + this._settings = settings; + this._saveLoadType = saveLoadType; + this._themesArray = themesArray; + this.selecetedThemesArray = []; + + this.headerBar = new Adw.HeaderBar(); + + const sidebarToolBarView = new Adw.ToolbarView(); + this.set_child(sidebarToolBarView); + sidebarToolBarView.add_top_bar(this.headerBar); + + this.page = new Adw.PreferencesPage(); + sidebarToolBarView.set_content(this.page); + + if (this._saveLoadType === SaveLoadType.SAVE) + this.title = _('Save Themes'); + else if (this._saveLoadType === SaveLoadType.LOAD) + this.title = _('Load Themes'); + + this.pageGroup = new Adw.PreferencesGroup(); + this.page.add(this.pageGroup); + + this._loadThemeRows(); + + const actionButton = new Gtk.Button({ + label: `${_(this.title)} (${this.selected_themes})`, + valign: Gtk.Align.CENTER, + sensitive: false, + css_classes: ['suggested-action'], + }); + actionButton.connect('clicked', () => this.emit('response', Gtk.ResponseType.ACCEPT)); + + this.pageGroup.set_header_suffix(actionButton); + + this.connect('notify::selected-themes', () => { + actionButton.sensitive = this.selected_themes > 0; + actionButton.label = `${_(this.title)} (${this.selected_themes})`; + }); + } + + _loadThemeRows() { + for (let i = 0; i < this._themesArray.length; i++) { + const theme = this._themesArray[i]; + const pixbuf = SettingsUtils.createThemePreviewPixbuf(theme[1], theme[2], theme[3], theme[8]); + const row = new Adw.ActionRow({ + title: theme[0], + }); + + const icon = new Gtk.Image({ + pixel_size: 42, + }); + icon.set_from_pixbuf(pixbuf); + row.add_prefix(icon); + + this.addButtonAction(row, theme); + this.pageGroup.add(row); + } + } + + addButtonAction(row, theme) { + let match = false; + const checkButton = new Gtk.Button({ + icon_name: match ? 'list-remove-symbolic' : 'list-add-symbolic', + valign: Gtk.Align.CENTER, + }); + checkButton.connect('clicked', () => { + if (!match) { + this.currentToast?.dismiss(); + + this.currentToast = new Adw.Toast({ + title: _('%s has been selected').format(row.title), + timeout: 2, + }); + this.currentToast.connect('dismissed', () => (this.currentToast = null)); + + this._parent.add_toast(this.currentToast); + this.selected_themes++; + this.selecetedThemesArray.push(theme); + } else { + this.currentToast?.dismiss(); + + this.currentToast = new Adw.Toast({ + title: _('%s has been unselected').format(row.title), + timeout: 2, + }); + this.currentToast.connect('dismissed', () => (this.currentToast = null)); + this.selected_themes--; + this._parent.add_toast(this.currentToast); + const index = this.selecetedThemesArray.indexOf(theme); + this.selecetedThemesArray.splice(index, 1); + } + + match = !match; + checkButton.icon_name = match ? 'list-remove-symbolic' : 'list-add-symbolic'; + }); + row.add_suffix(checkButton); + row.activatable_widget = checkButton; + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/VisualSettings.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/VisualSettings.js new file mode 100644 index 0000000..288dd1a --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/Menu/VisualSettings.js @@ -0,0 +1,376 @@ +import Adw from 'gi://Adw'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import * as Constants from '../../constants.js'; +import * as PW from '../../prefsWidgets.js'; +import {SubPage} from './SubPage.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const VisualSettingsPage = GObject.registerClass( +class ArcMenuVisualSettingsPage extends SubPage { + _init(settings, params) { + super._init(settings, params); + + const menuSizeFrame = new Adw.PreferencesGroup({ + title: _('Menu Size'), + }); + this.add(menuSizeFrame); + + const heightSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: 300, upper: 4320, step_increment: 25, page_increment: 50, page_size: 0, + }), + climb_rate: 25, + digits: 0, + numeric: true, + valign: Gtk.Align.CENTER, + }); + heightSpinButton.set_value(this._settings.get_int('menu-height')); + heightSpinButton.connect('value-changed', widget => { + this._settings.set_int('menu-height', widget.get_value()); + }); + const heightRow = new Adw.ActionRow({ + title: _('Height'), + activatable_widget: heightSpinButton, + }); + heightRow.add_suffix(heightSpinButton); + menuSizeFrame.add(heightRow); + + const menuWidthSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: 175, upper: 500, step_increment: 25, page_increment: 50, page_size: 0, + }), + climb_rate: 25, + digits: 0, + numeric: true, + valign: Gtk.Align.CENTER, + }); + menuWidthSpinButton.set_value(this._settings.get_int('left-panel-width')); + menuWidthSpinButton.connect('value-changed', widget => { + this._settings.set_int('left-panel-width', widget.get_value()); + }); + const menuWidthRow = new Adw.ActionRow({ + title: _('Left-Panel Width'), + subtitle: _('Traditional Layouts'), + activatable_widget: menuWidthSpinButton, + }); + menuWidthRow.add_suffix(menuWidthSpinButton); + menuSizeFrame.add(menuWidthRow); + + const rightPanelWidthSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: 200, upper: 500, step_increment: 25, page_increment: 50, page_size: 0, + }), + climb_rate: 25, + valign: Gtk.Align.CENTER, + digits: 0, + numeric: true, + }); + rightPanelWidthSpinButton.set_value(this._settings.get_int('right-panel-width')); + rightPanelWidthSpinButton.connect('value-changed', widget => { + this._settings.set_int('right-panel-width', widget.get_value()); + }); + const rightPanelWidthRow = new Adw.ActionRow({ + title: _('Right-Panel Width'), + subtitle: _('Traditional Layouts'), + activatable_widget: rightPanelWidthSpinButton, + }); + rightPanelWidthRow.add_suffix(rightPanelWidthSpinButton); + menuSizeFrame.add(rightPanelWidthRow); + + const widthSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: -350, upper: 600, step_increment: 25, page_increment: 50, page_size: 0, + }), + valign: Gtk.Align.CENTER, + climb_rate: 25, + digits: 0, + numeric: true, + }); + widthSpinButton.set_value(this._settings.get_int('menu-width-adjustment')); + widthSpinButton.connect('value-changed', widget => { + this._settings.set_int('menu-width-adjustment', widget.get_value()); + }); + const widthRow = new Adw.ActionRow({ + title: _('Width Offset'), + subtitle: _('Non-Traditional Layouts'), + activatable_widget: widthSpinButton, + }); + widthRow.add_suffix(widthSpinButton); + menuSizeFrame.add(widthRow); + + const generalSettingsFrame = new Adw.PreferencesGroup({ + title: _('Menu Location'), + }); + this.add(generalSettingsFrame); + + const menuLocations = new Gtk.StringList(); + menuLocations.append(_('Off')); + menuLocations.append(_('Top Centered')); + menuLocations.append(_('Bottom Centered')); + menuLocations.append(_('Monitor Centered')); + const menuLocationRow = new Adw.ComboRow({ + title: _('Override Menu Location'), + model: menuLocations, + selected: this._settings.get_enum('force-menu-location'), + }); + menuLocationRow.connect('notify::selected', widget => { + this._settings.set_enum('force-menu-location', widget.selected); + }); + generalSettingsFrame.add(menuLocationRow); + + const [menuArrowRiseEnabled, menuArrowRiseValue] = this._settings.get_value('menu-arrow-rise').deep_unpack(); + + const menuArrowRiseSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + menuArrowRiseSwitch.connect('notify::active', widget => { + const [oldEnabled_, oldValue] = this._settings.get_value('menu-arrow-rise').deep_unpack(); + this._settings.set_value('menu-arrow-rise', new GLib.Variant('(bi)', [widget.get_active(), oldValue])); + if (widget.get_active()) + menuArrowRiseSpinButton.set_sensitive(true); + else + menuArrowRiseSpinButton.set_sensitive(false); + }); + const menuArrowRiseSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: -50, + upper: 50, + step_increment: 1, + }), + climb_rate: 1, + digits: 0, + numeric: true, + valign: Gtk.Align.CENTER, + value: menuArrowRiseValue, + sensitive: menuArrowRiseEnabled, + }); + menuArrowRiseSpinButton.connect('value-changed', widget => { + const [oldEnabled, oldValue_] = this._settings.get_value('menu-arrow-rise').deep_unpack(); + this._settings.set_value('menu-arrow-rise', new GLib.Variant('(bi)', [oldEnabled, widget.get_value()])); + }); + + const menuArrowRiseRow = new Adw.ActionRow({ + title: _('Override Menu Rise'), + subtitle: _('Menu Distance from Panel and Screen Edge'), + activatable_widget: menuArrowRiseSwitch, + }); + menuArrowRiseRow.add_suffix(menuArrowRiseSwitch); + menuArrowRiseRow.add_suffix(new Gtk.Separator({ + orientation: Gtk.Orientation.VERTICAL, + margin_top: 10, + margin_bottom: 10, + })); + menuArrowRiseRow.add_suffix(menuArrowRiseSpinButton); + menuArrowRiseSwitch.set_active(menuArrowRiseEnabled); + generalSettingsFrame.add(menuArrowRiseRow); + + const iconsSizeFrame = new Adw.PreferencesGroup({ + title: _('Override Icon Sizes'), + description: _('Override the icon size of various menu items'), + }); + this.add(iconsSizeFrame); + + const iconSizes = new Gtk.StringList(); + iconSizes.append(_('Off')); + iconSizes.append(`${_('Small')} - ${_('Square')}`); + iconSizes.append(`${_('Medium')} - ${_('Square')}`); + iconSizes.append(`${_('Large')} - ${_('Square')}`); + iconSizes.append(`${_('Small')} - ${_('Wide')}`); + iconSizes.append(`${_('Medium')} - ${_('Wide')}`); + iconSizes.append(`${_('Large')} - ${_('Wide')}`); + iconSizes.append(_('Custom')); + const gridIconsSizeRow = new Adw.ComboRow({ + title: `${_('Grid Menu Items')} (${_('Non-Traditional Layouts')})`, + subtitle: _('Apps, Pinned Apps, Shortcuts, Grid Search Results'), + model: iconSizes, + selected: this._settings.get_enum('menu-item-grid-icon-size'), + }); + gridIconsSizeRow.use_markup = true; + gridIconsSizeRow.connect('notify::selected', widget => { + customGridIconButton.visible = widget.selected === Constants.GridIconSize.CUSTOM; + this._settings.set_enum('menu-item-grid-icon-size', widget.selected); + }); + iconsSizeFrame.add(gridIconsSizeRow); + + const customGridIconButton = new Gtk.Button({ + icon_name: 'emblem-system-symbolic', + valign: Gtk.Align.CENTER, + visible: gridIconsSizeRow.selected === Constants.GridIconSize.CUSTOM, + }); + gridIconsSizeRow.add_suffix(customGridIconButton); + customGridIconButton.connect('clicked', () => { + const dialog = new CustomGridIconDialogWindow(this._settings, this); + dialog.show(); + dialog.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + this._settings.set_value('custom-grid-icon-size', new GLib.Variant('a{si}', + {'width': dialog.iconWidth, 'height': dialog.iconHeight, 'iconSize': dialog.iconSize})); + } + dialog.destroy(); + }); + }); + + const menuItemIconSizeRow = this.createIconSizeRow({ + title: _('Applications'), + subtitle: _('Apps, Pinned Apps, Items within Category, List Search Results'), + setting: 'menu-item-icon-size', + }); + iconsSizeFrame.add(menuItemIconSizeRow); + + const quickLinksIconSizeRow = this.createIconSizeRow({ + title: _('Shortcuts'), + subtitle: _('Directory / Application / Other Shortcuts, Power Menu'), + setting: 'quicklinks-item-icon-size', + }); + iconsSizeFrame.add(quickLinksIconSizeRow); + + const menuCategoryIconSizeRow = this.createIconSizeRow({ + title: _('Application Categories'), + setting: 'menu-item-category-icon-size', + }); + iconsSizeFrame.add(menuCategoryIconSizeRow); + + const buttonIconSizeRow = this.createIconSizeRow({ + title: _('Button Widgets'), + subtitle: _('Power Buttons, Unity Bottom Bar, Mint Side Bar, etc'), + setting: 'button-item-icon-size', + }); + iconsSizeFrame.add(buttonIconSizeRow); + + const miscIconSizeRow = this.createIconSizeRow({ + title: _('Miscellaneous'), + subtitle: _('Avatar, Search, Navigation Icons'), + setting: 'misc-item-icon-size', + }); + iconsSizeFrame.add(miscIconSizeRow); + + this.restoreDefaults = () => { + heightSpinButton.set_value(this._settings.get_default_value('menu-height').unpack()); + widthSpinButton.set_value(this._settings.get_default_value('menu-width-adjustment').unpack()); + menuWidthSpinButton.set_value(this._settings.get_default_value('left-panel-width').unpack()); + rightPanelWidthSpinButton.set_value(this._settings.get_default_value('right-panel-width').unpack()); + gridIconsSizeRow.selected = 0; + menuItemIconSizeRow.selected = 0; + menuCategoryIconSizeRow.selected = 0; + buttonIconSizeRow.selected = 0; + quickLinksIconSizeRow.selected = 0; + miscIconSizeRow.selected = 0; + menuLocationRow.selected = 0; + const [menuRiseEnabled_, menuRiseDefault] = + this._settings.get_default_value('menu-arrow-rise').deep_unpack(); + menuArrowRiseSpinButton.set_value(menuRiseDefault); + menuArrowRiseSwitch.set_active(false); + }; + } + + createIconSizeRow(rowDetails) { + const iconSizes = new Gtk.StringList(); + iconSizes.append(_('Off')); + iconSizes.append(_('Extra Small')); + iconSizes.append(_('Small')); + iconSizes.append(_('Medium')); + iconSizes.append(_('Large')); + iconSizes.append(_('Extra Large')); + + if (rowDetails.setting === 'menu-item-category-icon-size') + iconSizes.append(_('Hidden')); + + const iconsSizeRow = new Adw.ComboRow({ + title: _(rowDetails.title), + subtitle: rowDetails.subtitle ? _(rowDetails.subtitle) : null, + model: iconSizes, + selected: this._settings.get_enum(rowDetails.setting), + }); + iconsSizeRow.use_markup = true; + iconsSizeRow.connect('notify::selected', widget => { + this._settings.set_enum(rowDetails.setting, widget.selected); + }); + return iconsSizeRow; + } +}); + +var CustomGridIconDialogWindow = GObject.registerClass( +class ArcMenuCustomGridIconDialogWindow extends PW.DialogWindow { + _init(settings, parent) { + super._init(_('Custom Grid Icon Size'), parent); + this.set_default_size(600, 325); + this.search_enabled = false; + this._settings = settings; + const {width, height, iconSize} = this._settings.get_value('custom-grid-icon-size').deep_unpack(); + this.iconWidth = width; + this.iconHeight = height; + this.iconSize = iconSize; + + const widthRow = new Adw.ActionRow({ + title: _('Width'), + }); + const widthSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: 60, upper: 250, step_increment: 1, page_increment: 50, page_size: 0, + }), + climb_rate: 1, + digits: 0, + numeric: true, + valign: Gtk.Align.CENTER, + }); + widthSpinButton.set_value(this.iconWidth); + widthSpinButton.connect('value-changed', widget => { + this.iconWidth = widget.get_value(); + }); + widthRow.add_suffix(widthSpinButton); + this.pageGroup.add(widthRow); + + const heightRow = new Adw.ActionRow({ + title: _('Height'), + }); + const heightSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: 60, upper: 250, step_increment: 1, page_increment: 50, page_size: 0, + }), + climb_rate: 1, + digits: 0, + numeric: true, + valign: Gtk.Align.CENTER, + }); + heightSpinButton.set_value(this.iconHeight); + heightSpinButton.connect('value-changed', widget => { + this.iconHeight = widget.get_value(); + }); + heightRow.add_suffix(heightSpinButton); + this.pageGroup.add(heightRow); + + const sizeRow = new Adw.ActionRow({ + title: _('Icon Size'), + }); + const sizeSpinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: 24, upper: 250, step_increment: 1, page_increment: 50, page_size: 0, + }), + climb_rate: 1, + digits: 0, + numeric: true, + valign: Gtk.Align.CENTER, + }); + sizeSpinButton.set_value(this.iconSize); + sizeSpinButton.connect('value-changed', widget => { + this.iconSize = widget.get_value(); + }); + sizeRow.add_suffix(sizeSpinButton); + this.pageGroup.add(sizeRow); + + const applyButton = new Gtk.Button({ + label: _('Apply'), + halign: Gtk.Align.END, + css_classes: ['suggested-action'], + }); + + applyButton.connect('clicked', () => { + this.emit('response', Gtk.ResponseType.APPLY); + }); + this.pageGroup.set_header_suffix(applyButton); + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/MenuButtonPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/MenuButtonPage.js new file mode 100644 index 0000000..b860c99 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/MenuButtonPage.js @@ -0,0 +1,768 @@ +import Adw from 'gi://Adw'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gdk from 'gi://Gdk'; +import Gtk from 'gi://Gtk'; +import Pango from 'gi://Pango'; + +import * as Constants from '../constants.js'; +import * as PW from '../prefsWidgets.js'; +import * as SettingsUtils from './SettingsUtils.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const MenuButtonPage = GObject.registerClass( +class ArcMenuMenuButtonPage extends Adw.PreferencesPage { + _init(settings) { + super._init({ + title: _('Menu Button'), + icon_name: 'icon-arcmenu-logo-symbolic', + name: 'MenuButtonPage', + }); + this._settings = settings; + + + const restoreDefaultsButton = new Gtk.Button({ + icon_name: 'view-refresh-symbolic', + vexpand: false, + valign: Gtk.Align.CENTER, + tooltip_text: _('Reset settings'), + css_classes: ['flat'], + halign: Gtk.Align.END, + hexpand: true, + }); + restoreDefaultsButton.connect('clicked', () => { + const pageName = this.title; + const dialog = new Gtk.MessageDialog({ + text: `${_('Reset all %s settings?').format(pageName)}`, + secondary_text: _('All %s settings will be reset to the default value.').format(pageName), + use_markup: true, + buttons: Gtk.ButtonsType.YES_NO, + message_type: Gtk.MessageType.WARNING, + transient_for: this.get_root(), + modal: true, + }); + dialog.connect('response', (widget, response) => { + if (response === Gtk.ResponseType.YES) + this.restoreDefaults(); + dialog.destroy(); + }); + dialog.show(); + }); + + const menuButtonAppearanceFrame = new Adw.PreferencesGroup({ + title: _('Appearance'), + header_suffix: restoreDefaultsButton, + }); + + const menuButtonAppearances = new Gtk.StringList(); + menuButtonAppearances.append(_('Icon')); + menuButtonAppearances.append(_('Text')); + menuButtonAppearances.append(_('Icon and Text')); + menuButtonAppearances.append(_('Text and Icon')); + menuButtonAppearances.append(_('Hidden')); + const menuButtonAppearanceRow = new Adw.ComboRow({ + title: _('Display Style'), + model: menuButtonAppearances, + selected: Gtk.INVALID_LIST_POSITION, + }); + menuButtonAppearanceRow.connect('notify::selected', widget => { + if (widget.selected === Constants.MenuButtonAppearance.NONE) { + menuButtonOffsetRow.hide(); + menuButtonPaddingRow.hide(); + menuButtonCustomTextBoxRow.hide(); + } else if (widget.selected === Constants.MenuButtonAppearance.ICON) { + menuButtonPaddingRow.show(); + menuButtonCustomTextBoxRow.hide(); + menuButtonOffsetRow.show(); + } else { + menuButtonPaddingRow.show(); + menuButtonOffsetRow.show(); + menuButtonCustomTextBoxRow.show(); + } + this._settings.set_enum('menu-button-appearance', widget.selected); + }); + + const paddingScale = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower: -1, + upper: 25, + step_increment: 1, + page_increment: 1, + page_size: 0, + }), + digits: 0, + valign: Gtk.Align.CENTER, + }); + paddingScale.set_value(this._settings.get_int('button-padding')); + paddingScale.connect('value-changed', () => { + this._settings.set_int('button-padding', paddingScale.get_value()); + }); + const menuButtonPaddingRow = new Adw.ActionRow({ + title: _('Padding'), + subtitle: _('%d Default Theme Value').format(-1), + activatable_widget: paddingScale, + }); + menuButtonPaddingRow.add_suffix(paddingScale); + + // /// Row for menu button offset ///// + const offsetScale = new Gtk.SpinButton({ + orientation: Gtk.Orientation.HORIZONTAL, + adjustment: new Gtk.Adjustment({ + lower: 0, + upper: 10, // arbitrary value + step_increment: 1, + page_increment: 1, + page_size: 0, + }), + digits: 0, + valign: Gtk.Align.CENTER, + }); + offsetScale.set_value(this._settings.get_int('menu-button-position-offset')); + offsetScale.connect('value-changed', () => { + this._settings.set_int('menu-button-position-offset', offsetScale.get_value()); + }); + const menuButtonOffsetRow = new Adw.ActionRow({ + title: _('Position in Panel'), + activatable_widget: offsetScale, + }); + menuButtonOffsetRow.add_suffix(offsetScale); + // ////////////////// + + const menuButtonCustomTextEntry = new Gtk.Entry({ + valign: Gtk.Align.CENTER, + hexpand: true, + halign: Gtk.Align.FILL, + }); + menuButtonCustomTextEntry.set_text(this._settings.get_string('custom-menu-button-text')); + menuButtonCustomTextEntry.connect('changed', widget => { + const customMenuButtonText = widget.get_text(); + this._settings.set_string('custom-menu-button-text', customMenuButtonText); + }); + const menuButtonCustomTextBoxRow = new Adw.ActionRow({ + title: _('Text'), + activatable_widget: menuButtonCustomTextEntry, + visible: false, + }); + menuButtonCustomTextBoxRow.add_suffix(menuButtonCustomTextEntry); + + menuButtonAppearanceFrame.add(menuButtonAppearanceRow); + menuButtonAppearanceFrame.add(menuButtonCustomTextBoxRow); + menuButtonAppearanceFrame.add(menuButtonPaddingRow); + menuButtonAppearanceFrame.add(menuButtonOffsetRow); + this.add(menuButtonAppearanceFrame); + + const menuButtonIconFrame = new Adw.PreferencesGroup({ + title: _('Icon'), + }); + const menuButtonIconButton = new Gtk.Button({ + label: _('Browse...'), + valign: Gtk.Align.CENTER, + }); + menuButtonIconButton.connect('clicked', () => { + const dialog = new ArcMenuIconsDialogWindow(this._settings, this); + dialog.show(); + dialog.connect('response', () => { + dialog.destroy(); + }); + }); + const menuButtonIconRow = new Adw.ActionRow({ + title: _('Choose a New Icon'), + activatable_widget: menuButtonIconButton, + }); + menuButtonIconRow.add_suffix(menuButtonIconButton); + menuButtonIconFrame.add(menuButtonIconRow); + + const menuButtonIconSizeScale = new Gtk.SpinButton({ + orientation: Gtk.Orientation.HORIZONTAL, + adjustment: new Gtk.Adjustment({ + lower: 14, + upper: 64, + step_increment: 1, + page_increment: 1, + page_size: 0, + }), + digits: 0, + valign: Gtk.Align.CENTER, + }); + menuButtonIconSizeScale.set_value(this._settings.get_double('custom-menu-button-icon-size')); + menuButtonIconSizeScale.connect('value-changed', () => { + this._settings.set_double('custom-menu-button-icon-size', menuButtonIconSizeScale.get_value()); + }); + const menuButtonIconSizeRow = new Adw.ActionRow({ + title: _('Icon Size'), + activatable_widget: menuButtonIconSizeScale, + }); + menuButtonIconSizeRow.add_suffix(menuButtonIconSizeScale); + menuButtonIconFrame.add(menuButtonIconSizeRow); + + menuButtonAppearanceRow.selected = this._settings.get_enum('menu-button-appearance'); + + this.add(menuButtonIconFrame); + + // Click Options ----------- + + const clickOptionsGroup = new Adw.PreferencesGroup({ + title: _('Click Options'), + }); + this.add(clickOptionsGroup); + + const clickOptionsList = new Gtk.StringList(); + clickOptionsList.append(_('ArcMenu')); + clickOptionsList.append(_('Context Menu')); + clickOptionsList.append(_('None')); + + const leftClickRow = new Adw.ComboRow({ + title: _('Left Click'), + model: clickOptionsList, + selected: this._settings.get_enum('menu-button-left-click-action'), + }); + leftClickRow.connect('notify::selected', widget => { + this._settings.set_enum('menu-button-left-click-action', widget.selected); + }); + clickOptionsGroup.add(leftClickRow); + + const rightClickRow = new Adw.ComboRow({ + title: _('Right Click'), + model: clickOptionsList, + selected: this._settings.get_enum('menu-button-right-click-action'), + }); + rightClickRow.connect('notify::selected', widget => { + this._settings.set_enum('menu-button-right-click-action', widget.selected); + }); + clickOptionsGroup.add(rightClickRow); + + const middleClickRow = new Adw.ComboRow({ + title: _('Middle Click'), + model: clickOptionsList, + selected: this._settings.get_enum('menu-button-middle-click-action'), + }); + middleClickRow.connect('notify::selected', widget => { + this._settings.set_enum('menu-button-middle-click-action', widget.selected); + }); + clickOptionsGroup.add(middleClickRow); + + // ----------- + + const stylingGroup = new Adw.PreferencesGroup({ + title: _('Styling'), + description: _('Results may vary with third party themes'), + }); + this.add(stylingGroup); + + const buttonFGColorRow = this._createButtonColorRow(_('Foreground Color'), 'menu-button-fg-color'); + stylingGroup.add(buttonFGColorRow); + + const buttonBGColorRow = this._createButtonColorRow(_('Background Color'), 'menu-button-bg-color'); + stylingGroup.add(buttonBGColorRow); + + const buttonHoverBGColorRow = this._createButtonColorRow(`${_('Hover')} ${_('Background Color')}`, 'menu-button-hover-bg-color'); + stylingGroup.add(buttonHoverBGColorRow); + + const buttonHoverFGColorRow = this._createButtonColorRow(`${_('Hover')} ${_('Foreground Color')}`, 'menu-button-hover-fg-color'); + stylingGroup.add(buttonHoverFGColorRow); + + const buttonActiveBGColorRow = this._createButtonColorRow(`${_('Active')} ${_('Background Color')}`, 'menu-button-active-bg-color'); + stylingGroup.add(buttonActiveBGColorRow); + + const buttonActiveFGColorRow = this._createButtonColorRow(`${_('Active')} ${_('Foreground Color')}`, 'menu-button-active-fg-color'); + stylingGroup.add(buttonActiveFGColorRow); + + const buttonBorderRadiusRow = this._createSpinButtonToggleRow(_('Border Radius'), + 'menu-button-border-radius', 0, 25); + stylingGroup.add(buttonBorderRadiusRow); + + const buttonBorderWidthRow = this._createSpinButtonToggleRow(_('Border Width'), + 'menu-button-border-width', 0, 5, _('Background colors required if set to 0')); + stylingGroup.add(buttonBorderWidthRow); + + const buttonBorderColorRow = this._createButtonColorRow(_('Border Color'), 'menu-button-border-color'); + stylingGroup.add(buttonBorderColorRow); + + this.restoreDefaults = () => { + menuButtonAppearanceRow.selected = 0; + menuButtonCustomTextEntry.set_text('Apps'); + paddingScale.set_value(-1); + menuButtonIconSizeScale.set_value(20); + offsetScale.set_value(0); + + buttonFGColorRow.restoreDefaults(); + buttonHoverBGColorRow.restoreDefaults(); + buttonHoverFGColorRow.restoreDefaults(); + buttonActiveBGColorRow.restoreDefaults(); + buttonActiveFGColorRow.restoreDefaults(); + buttonBorderRadiusRow.restoreDefaults(); + buttonBorderWidthRow.restoreDefaults(); + buttonBorderColorRow.restoreDefaults(); + + this._settings.reset('menu-button-icon'); + this._settings.reset('arc-menu-icon'); + this._settings.reset('distro-icon'); + this._settings.reset('custom-menu-button-icon'); + this._settings.reset('menu-button-position-offset'); + this._settings.reset('menu-button-fg-color'); + this._settings.reset('menu-button-bg-color'); + this._settings.reset('menu-button-hover-bg-color'); + this._settings.reset('menu-button-hover-fg-color'); + this._settings.reset('menu-button-active-bg-color'); + this._settings.reset('menu-button-active-fg-color'); + this._settings.reset('menu-button-border-radius'); + this._settings.reset('menu-button-border-width'); + this._settings.reset('menu-button-border-color'); + }; + } + + _createSpinButtonToggleRow(title, setting, lower, upper, subtitle = '') { + const [enabled, value] = this._settings.get_value(setting).deep_unpack(); + + const enabledSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + enabledSwitch.connect('notify::active', widget => { + const [oldEnabled_, oldValue] = this._settings.get_value(setting).deep_unpack(); + this._settings.set_value(setting, new GLib.Variant('(bi)', [widget.get_active(), oldValue])); + if (widget.get_active()) + spinButton.set_sensitive(true); + else + spinButton.set_sensitive(false); + }); + const spinButton = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + lower, + upper, + step_increment: 1, + }), + climb_rate: 1, + digits: 0, + numeric: true, + valign: Gtk.Align.CENTER, + value, + sensitive: enabled, + }); + spinButton.connect('value-changed', widget => { + const [oldEnabled, oldValue_] = this._settings.get_value(setting).deep_unpack(); + this._settings.set_value(setting, new GLib.Variant('(bi)', [oldEnabled, widget.get_value()])); + }); + + const spinRow = new Adw.ActionRow({ + title: _(title), + subtitle: subtitle ? _(subtitle) : '', + activatable_widget: enabledSwitch, + }); + spinRow.add_suffix(enabledSwitch); + spinRow.add_suffix(new Gtk.Separator({ + orientation: Gtk.Orientation.VERTICAL, + margin_top: 10, + margin_bottom: 10, + })); + spinRow.add_suffix(spinButton); + + enabledSwitch.set_active(enabled); + + spinRow.restoreDefaults = () => { + const [defaultEnabled, defaultValue] = this._settings.get_default_value(setting).deep_unpack(); + enabledSwitch.set_active(defaultEnabled); + spinButton.value = defaultValue; + }; + return spinRow; + } + + _createButtonColorRow(title, setting) { + const [enabled, color] = this._settings.get_value(setting).deep_unpack(); + + const enabledSwitch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + }); + enabledSwitch.connect('notify::active', widget => { + const [oldEnabled_, oldColor] = this._settings.get_value(setting).deep_unpack(); + this._settings.set_value(setting, new GLib.Variant('(bs)', [widget.get_active(), oldColor])); + if (widget.get_active()) + colorButton.set_sensitive(true); + else + colorButton.set_sensitive(false); + }); + + const colorButton = new Gtk.ColorButton({ + use_alpha: true, + valign: Gtk.Align.CENTER, + rgba: SettingsUtils.parseRGBA(color), + sensitive: enabled, + }); + colorButton.connect('notify::rgba', widget => { + const colorString = widget.get_rgba().to_string(); + const [oldEnabled, oldColor_] = this._settings.get_value(setting).deep_unpack(); + this._settings.set_value(setting, new GLib.Variant('(bs)', [oldEnabled, colorString])); + }); + + const colorRow = new Adw.ActionRow({ + title: _(title), + activatable_widget: enabledSwitch, + }); + colorRow.add_suffix(enabledSwitch); + colorRow.add_suffix(new Gtk.Separator({ + orientation: Gtk.Orientation.VERTICAL, + margin_top: 10, + margin_bottom: 10, + })); + colorRow.add_suffix(colorButton); + + enabledSwitch.set_active(enabled); + + colorRow.restoreDefaults = () => { + const [defaultEnabled, defaultColor] = this._settings.get_default_value(setting).deep_unpack(); + enabledSwitch.set_active(defaultEnabled); + colorButton.rgba = SettingsUtils.parseRGBA(defaultColor); + }; + return colorRow; + } +}); + +var ArcMenuIconsDialogWindow = GObject.registerClass( +class ArcMenuArcMenuIcons extends PW.DialogWindow { + _init(settings, parent) { + this._settings = settings; + super._init(_('Menu Button Icons'), parent); + this.set_default_size(475, 475); + this.search_enabled = false; + + this.page.title = _('Icons'); + this.page.icon_name = 'icon-arcmenu-logo-symbolic'; + + this._arcMenuIconsFlowBox = new PW.IconGrid(2); + this._arcMenuIconsFlowBox.connect('child-activated', (_self, child) => { + this._distroIconsFlowBox.unselect_all(); + this._customIconFlowBox.unselect_all(); + + const selectedChildIndex = child.get_index(); + this._settings.set_enum('menu-button-icon', Constants.MenuIconType.MENU_ICON); + this._settings.set_int('arc-menu-icon', selectedChildIndex); + }); + this.pageGroup.add(this._arcMenuIconsFlowBox); + + Constants.MenuIcons.forEach(icon => { + const iconTile = new PW.MenuButtonIconTile(icon.PATH); + this._arcMenuIconsFlowBox.add(iconTile); + }); + + this.distroIconsPage = new Adw.PreferencesPage({ + title: _('Distros'), + icon_name: 'distro-gnome-symbolic', + }); + this.add(this.distroIconsPage); + + const distroInfoButton = new Gtk.Button({ + label: _('Legal Disclaimer'), + css_classes: ['flat', 'accent'], + margin_bottom: 8, + halign: Gtk.Align.CENTER, + }); + distroInfoButton.connect('clicked', () => { + const dialog = new DistroIconsDisclaimerWindow(this._settings, this); + dialog.connect('response', () => dialog.destroy()); + dialog.show(); + }); + + const distroIconsGroup = new Adw.PreferencesGroup(); + distroIconsGroup.add(distroInfoButton); + this.distroIconsPage.add(distroIconsGroup); + + this._distroIconsFlowBox = new PW.IconGrid(2); + this._distroIconsFlowBox.connect('child-activated', (_self, child) => { + this._arcMenuIconsFlowBox.unselect_all(); + this._customIconFlowBox.unselect_all(); + + const selectedChildIndex = child.get_index(); + this._settings.set_enum('menu-button-icon', Constants.MenuIconType.DISTRO_ICON); + this._settings.set_int('distro-icon', selectedChildIndex); + }); + + Constants.DistroIcons.forEach(icon => { + const iconTile = new PW.MenuButtonIconTile(icon.PATH, icon.NAME); + this._distroIconsFlowBox.add(iconTile); + }); + distroIconsGroup.add(this._distroIconsFlowBox); + + this.customIconPage = new Adw.PreferencesPage({ + title: _('Custom'), + icon_name: 'settings-customicon-symbolic', + }); + this.add(this.customIconPage); + + const customIconGroup = new Adw.PreferencesGroup(); + this.customIconPage.add(customIconGroup); + + this._customIconFlowBox = new PW.IconGrid(2); + this._customIconFlowBox.set({ + vexpand: false, + homogeneous: false, + }); + customIconGroup.add(this._customIconFlowBox); + + this._customIconFlowBox.connect('child-activated', () => { + this._arcMenuIconsFlowBox.unselect_all(); + this._distroIconsFlowBox.unselect_all(); + + const customIconPath = this._settings.get_string('custom-menu-button-icon'); + this._settings.set_string('custom-menu-button-icon', customIconPath); + this._settings.set_enum('menu-button-icon', Constants.MenuIconType.CUSTOM); + }); + + const customIconTile = new PW.MenuButtonIconTile(this._settings.get_string('custom-menu-button-icon')); + this._customIconFlowBox.add(customIconTile); + + const fileChooserFrame = new Adw.PreferencesGroup(); + this.customIconPage.add(fileChooserFrame); + + const fileFilter = new Gtk.FileFilter(); + fileFilter.add_pixbuf_formats(); + + const fileChooserButton = new Gtk.Button({ + label: _('Browse Files...'), + valign: Gtk.Align.CENTER, + }); + fileChooserButton.connect('clicked', () => { + const dialog = new Gtk.FileChooserDialog({ + title: _('Select an Image File'), + transient_for: this.get_root(), + modal: true, + action: Gtk.FileChooserAction.OPEN, + filter: fileFilter, + }); + + dialog.add_button(_('Cancel'), Gtk.ResponseType.CANCEL); + dialog.add_button(_('Select'), Gtk.ResponseType.ACCEPT); + + dialog.connect('response', (_self, response) => { + if (response === Gtk.ResponseType.ACCEPT) { + this._arcMenuIconsFlowBox.unselect_all(); + this._distroIconsFlowBox.unselect_all(); + + const iconPath = dialog.get_file().get_path(); + customIconTile.setIcon(iconPath); + this._settings.set_string('custom-menu-button-icon', iconPath); + this._settings.set_enum('menu-button-icon', Constants.MenuIconType.CUSTOM); + this._customIconFlowBox.select_child(this._customIconFlowBox.get_child_at_index(0)); + } + + dialog.destroy(); + }); + dialog.show(); + }); + const iconChooserButton = new Gtk.Button({ + label: _('Browse Icons...'), + valign: Gtk.Align.CENTER, + }); + iconChooserButton.connect('clicked', () => { + const dialog = new IconChooserDialog(this._settings, this.get_root()); + dialog.connect('response', (_self, response) => { + if (response === Gtk.ResponseType.APPLY) { + this._arcMenuIconsFlowBox.unselect_all(); + this._distroIconsFlowBox.unselect_all(); + + const newIcon = dialog.iconString; + customIconTile.setIcon(newIcon); + this._settings.set_string('custom-menu-button-icon', newIcon); + this._settings.set_enum('menu-button-icon', Constants.MenuIconType.CUSTOM); + this._customIconFlowBox.select_child(this._customIconFlowBox.get_child_at_index(0)); + } + + dialog.destroy(); + }); + dialog.show(); + }); + const fileChooserRow = new Adw.ActionRow(); + fileChooserRow.add_prefix(fileChooserButton); + fileChooserRow.add_suffix(iconChooserButton); + fileChooserFrame.add(fileChooserRow); + + this.setVisiblePage(); + + this.connect('notify::visible-page', () => this._selectActiveIcon()); + this._selectActiveIcon(); + } + + _selectActiveIcon() { + this._arcMenuIconsFlowBox.unselect_all(); + this._distroIconsFlowBox.unselect_all(); + this._customIconFlowBox.unselect_all(); + const menuButtonIconType = this._settings.get_enum('menu-button-icon'); + if (menuButtonIconType === Constants.MenuIconType.MENU_ICON) { + const index = this._settings.get_int('arc-menu-icon'); + const selectedChild = this._arcMenuIconsFlowBox.get_child_at_index(index); + this._arcMenuIconsFlowBox.select_child(selectedChild); + } else if (menuButtonIconType === Constants.MenuIconType.DISTRO_ICON) { + const index = this._settings.get_int('distro-icon'); + const selectedChild = this._distroIconsFlowBox.get_child_at_index(index); + this._distroIconsFlowBox.select_child(selectedChild); + } else if (menuButtonIconType === Constants.MenuIconType.CUSTOM) { + const selectedChild = this._customIconFlowBox.get_child_at_index(0); + this._customIconFlowBox.select_child(selectedChild); + } + } + + setVisiblePage() { + if (this._settings.get_enum('menu-button-icon') === Constants.MenuIconType.MENU_ICON) + this.set_visible_page(this.page); + else if (this._settings.get_enum('menu-button-icon') === Constants.MenuIconType.DISTRO_ICON) + this.set_visible_page(this.distroIconsPage); + else if (this._settings.get_enum('menu-button-icon') === Constants.MenuIconType.CUSTOM) + this.set_visible_page(this.customIconPage); + } +}); + +var IconChooserDialog = GObject.registerClass( +class ArcMenuIconChooserDialog extends PW.DialogWindow { + _init(settings, parent) { + this._settings = settings; + super._init(_('Select an Icon'), parent); + this.set_default_size(475, 475); + this.search_enabled = false; + this.iconString = ''; + + this.page.title = _('System Icons'); + this.page.icon_name = 'go-home-symbolic'; + + const searchEntry = new Gtk.SearchEntry({ + placeholder_text: _('Search...'), + search_delay: 250, + margin_bottom: 12, + }); + searchEntry.connect('search-changed', () => { + const query = searchEntry.text.trim().toLowerCase(); + if (!query) { + filter.set_filter_func(null); + return; + } + filter.set_filter_func(item => { + return item.string.toLowerCase().includes(query); + }); + }); + this.pageGroup.add(searchEntry); + + const iconThemeDefault = Gtk.IconTheme.get_for_display(Gdk.Display.get_default()); + + const iconTheme = new Gtk.IconTheme({ + resource_path: iconThemeDefault.resource_path, + theme_name: iconThemeDefault.theme_name, + }); + const iconNames = iconTheme.get_icon_names(); + + iconNames.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + + const listStore = new Gtk.StringList({strings: iconNames}); + const filter = new Gtk.CustomFilter(); + const filterListModel = new Gtk.FilterListModel({ + model: listStore, + filter, + }); + + const factory = new Gtk.SignalListItemFactory(); + const iconGridView = new Gtk.ListView({ + model: new Gtk.SingleSelection({model: filterListModel, autoselect: false, selected: -1}), + factory, + vexpand: true, + valign: Gtk.Align.FILL, + }); + + const scrollWindow = new Gtk.ScrolledWindow({ + child: iconGridView, + hscrollbar_policy: Gtk.PolicyType.NEVER, + vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, + }); + + factory.connect('setup', (factory_, item) => { + item.connect('notify::selected', () => { + applyButton.sensitive = true; + }); + const box = new Gtk.Box({ + orientation: Gtk.Orientation.HORIZONTAL, + spacing: 12, + margin_top: 3, + margin_bottom: 3, + margin_start: 12, + margin_end: 12, + }); + + const image = new Gtk.Image({ + pixel_size: 32, + hexpand: false, + halign: Gtk.Align.START, + }); + box.image = image; + + const label = new Gtk.Label({ + hexpand: false, + halign: Gtk.Align.START, + wrap: true, + wrap_mode: Pango.WrapMode.WORD_CHAR, + }); + box.label = label; + + box.append(image); + box.append(label); + + item.set_child(box); + }); + factory.connect('bind', (factory_, {child, item}) => { + const iconName = item.string; + child.image.icon_name = iconName; + child.label.label = iconName; + }); + + this.pageGroup.add(scrollWindow); + + const applyButton = new Gtk.Button({ + label: _('Select'), + sensitive: false, + halign: Gtk.Align.END, + valign: Gtk.Align.END, + margin_top: 12, + css_classes: ['suggested-action'], + }); + applyButton.connect('clicked', () => { + this.iconString = iconGridView.model.get_selected_item().string; + this.emit('response', Gtk.ResponseType.APPLY); + }); + this.pageGroup.add(applyButton); + } +}); + +var DistroIconsDisclaimerWindow = GObject.registerClass( +class ArcMenuDistroIconsDisclaimerWindow extends Gtk.MessageDialog { + _init(settings, parent) { + this._settings = settings; + super._init({ + text: `${_('Legal Disclaimer for Distro Icons')}`, + use_markup: true, + message_type: Gtk.MessageType.OTHER, + transient_for: parent.get_root(), + modal: true, + buttons: Gtk.ButtonsType.OK, + }); + this.set_size_request(515, 470); + + const scrollWindow = new Gtk.ScrolledWindow(); + scrollWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); + this.get_content_area().append(scrollWindow); + + const box = new Gtk.Box({ + orientation: Gtk.Orientation.VERTICAL, + vexpand: true, + valign: Gtk.Align.FILL, + margin_top: 8, + margin_bottom: 8, + margin_start: 16, + margin_end: 16, + }); + scrollWindow.set_child(box); + + const bodyLabel = new Gtk.Label({ + label: Constants.DistroIconsDisclaimer, + use_markup: true, + hexpand: false, + halign: Gtk.Align.START, + wrap: true, + }); + box.append(bodyLabel); + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/MenuPage.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/MenuPage.js new file mode 100644 index 0000000..75ccdf2 --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/MenuPage.js @@ -0,0 +1,221 @@ +import Adw from 'gi://Adw'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; + +import * as Constants from '../constants.js'; +import * as PW from '../prefsWidgets.js'; +import * as SettingsUtils from './SettingsUtils.js'; + +import {FineTunePage} from './Menu/FineTunePage.js'; +import {LayoutsPage} from './Menu/LayoutsPage.js'; +import {LayoutTweaksPage} from './Menu/LayoutTweaksPage.js'; +import {ListOtherPage} from './Menu/ListOtherPage.js'; +import {ListPinnedPage} from './Menu/ListPinnedPage.js'; +import {SearchOptionsPage} from './Menu/SearchOptionsPage.js'; +import {ThemePage} from './Menu/ThemePage.js'; +import {VisualSettingsPage} from './Menu/VisualSettings.js'; + +import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +export const MenuPage = GObject.registerClass( +class ArcMenuMenuPage extends Adw.PreferencesPage { + _init(settings, window) { + super._init({ + title: _('Menu'), + icon_name: 'settings-settings-symbolic', + name: 'MenuPage', + }); + this._settings = settings; + this._window = window; + + const menuLooksGroup = new Adw.PreferencesGroup({ + title: _('How should the menu look?'), + }); + this.add(menuLooksGroup); + + const visibleRow = { }; + + this.layoutRow = new PW.SettingRow({ + title: _('Menu Layout'), + subtitle: _('Choose a layout style for the menu'), + icon_name: 'settings-layouts-symbolic', + }); + this._addSubPageToRow(this.layoutRow, { + pageClass: LayoutsPage, + }); + this.layoutRow.settingPage.connect('response', (_w, response) => { + if (response === Gtk.ResponseType.APPLY) { + const layoutName = SettingsUtils.getMenuLayoutName(this._settings.get_enum('menu-layout')); + this.tweaksRow.title = _('%s Layout Tweaks').format(_(layoutName)); + } + }); + menuLooksGroup.add(this.layoutRow); + + this.themeRow = new PW.SettingRow({ + title: _('Menu Theme'), + subtitle: _('Modify menu colors, font size, and border'), + icon_name: 'settings-theme-symbolic', + }); + this._addSubPageToRow(this.themeRow, { + pageClass: ThemePage, + }); + menuLooksGroup.add(this.themeRow); + + const visualSettingsRow = new PW.SettingRow({ + title: _('Menu Visual Appearance'), + subtitle: _('Change menu height, width, location, and icon sizes'), + icon_name: 'settings-settings-symbolic', + }); + this._addSubPageToRow(visualSettingsRow, { + pageClass: VisualSettingsPage, + }); + menuLooksGroup.add(visualSettingsRow); + + const fineTuneRow = new PW.SettingRow({ + title: _('Fine Tune'), + subtitle: _('Adjust less commonly used visual settings'), + icon_name: 'settings-finetune-symbolic', + }); + this._addSubPageToRow(fineTuneRow, { + pageClass: FineTunePage, + }); + menuLooksGroup.add(fineTuneRow); + + const whatToShowGroup = new Adw.PreferencesGroup({ + title: _('What should show on the menu?'), + }); + this.add(whatToShowGroup); + + const layoutName = SettingsUtils.getMenuLayoutName(this._settings.get_enum('menu-layout')); + this.tweaksRow = new PW.SettingRow({ + title: _('%s Layout Tweaks').format(_(layoutName)), + subtitle: _('Settings specific to the current menu layout'), + icon_name: 'emblem-system-symbolic', + }); + this._addSubPageToRow(this.tweaksRow, { + pageClass: LayoutTweaksPage, + }); + whatToShowGroup.add(this.tweaksRow); + + this.pinnedAppsRow = new PW.SettingRow({ + title: _('Pinned Apps'), + icon_name: 'view-pin-symbolic', + }); + this._addSubPageToRow(this.pinnedAppsRow, { + pageClass: ListPinnedPage, + pageClassParams: Constants.MenuSettingsListType.PINNED_APPS, + }); + whatToShowGroup.add(this.pinnedAppsRow); + visibleRow[Constants.SettingsPage.PINNED_APPS] = this.pinnedAppsRow; + + const directoryShortcutsRow = new PW.SettingRow({ + title: _('Directory Shortcuts'), + icon_name: 'folder-symbolic', + }); + this._addSubPageToRow(directoryShortcutsRow, { + pageClass: ListPinnedPage, + pageClassParams: Constants.MenuSettingsListType.DIRECTORIES, + }); + whatToShowGroup.add(directoryShortcutsRow); + visibleRow[Constants.SettingsPage.DIRECTORY_SHORTCUTS] = directoryShortcutsRow; + + const applicationShortcutsRow = new PW.SettingRow({ + title: _('Application Shortcuts'), + icon_name: 'view-grid-symbolic', + }); + this._addSubPageToRow(applicationShortcutsRow, { + pageClass: ListPinnedPage, + pageClassParams: Constants.MenuSettingsListType.APPLICATIONS, + }); + whatToShowGroup.add(applicationShortcutsRow); + visibleRow[Constants.SettingsPage.APPLICATION_SHORTCUTS] = applicationShortcutsRow; + + const searchOptionsRow = new PW.SettingRow({ + title: _('Search Options'), + icon_name: 'preferences-system-search-symbolic', + }); + this._addSubPageToRow(searchOptionsRow, { + pageClass: SearchOptionsPage, + }); + whatToShowGroup.add(searchOptionsRow); + visibleRow[Constants.SettingsPage.SEARCH_OPTIONS] = searchOptionsRow; + + const powerOptionsRow = new PW.SettingRow({ + title: _('Power Options'), + subtitle: _('Choose which power options to show and the display style'), + icon_name: 'gnome-power-manager-symbolic', + }); + this._addSubPageToRow(powerOptionsRow, { + pageClass: ListOtherPage, + pageClassParams: Constants.MenuSettingsListType.POWER_OPTIONS, + }); + whatToShowGroup.add(powerOptionsRow); + visibleRow[Constants.SettingsPage.POWER_OPTIONS] = powerOptionsRow; + + const extraCategoriesRow = new PW.SettingRow({ + title: _('Extra Categories'), + icon_name: 'view-list-symbolic', + subtitle: _('Add or remove additional custom categories'), + }); + this._addSubPageToRow(extraCategoriesRow, { + pageClass: ListOtherPage, + pageClassParams: Constants.MenuSettingsListType.EXTRA_CATEGORIES, + }); + whatToShowGroup.add(extraCategoriesRow); + visibleRow[Constants.SettingsPage.EXTRA_CATEGORIES] = extraCategoriesRow; + + const contextMenuGroup = new Adw.PreferencesGroup({ + title: _('What should show on the context menu?'), + }); + this.add(contextMenuGroup); + + const contextMenuRow = new PW.SettingRow({ + title: _('Modify ArcMenu Context Menu'), + icon_name: 'view-list-bullet-symbolic', + }); + this._addSubPageToRow(contextMenuRow, { + pageClass: ListPinnedPage, + pageClassParams: Constants.MenuSettingsListType.CONTEXT_MENU, + }); + contextMenuGroup.add(contextMenuRow); + + SettingsUtils.setVisibleRows(visibleRow, this._settings.get_enum('menu-layout')); + this._settings.connect('changed::menu-layout', () => + SettingsUtils.setVisibleRows(visibleRow, this._settings.get_enum('menu-layout'))); + } + + _addSubPageToRow(row, pageParams) { + const PageClass = pageParams.pageClass; + const pageClassParams = pageParams.pageClassParams ?? 0; + + const settingPage = new PageClass(this._settings, { + title: _(row.title), + list_type: pageClassParams, + }); + row.settingPage = settingPage; + + row.connect('activated', () => { + this._window.push_subpage(settingPage); + + if (settingPage.setActiveLayout) + settingPage.setActiveLayout(this._settings.get_enum('menu-layout')); + + settingPage.resetScrollAdjustment(); + }); + } + + presentSubpage(subpage) { + if (subpage === Constants.SettingsPage.MENU_LAYOUT) { + const row = this.layoutRow; + this._window.push_subpage(row.settingPage); + } + if (subpage === Constants.SettingsPage.MENU_THEME) { + const row = this.themeRow; + this._window.push_subpage(row.settingPage); + } else if (subpage === Constants.SettingsPage.RUNNER_TWEAKS) { + const row = this.tweaksRow; + this._window.push_subpage(row.settingPage); + row.settingPage.setActiveLayout(Constants.MenuLayout.RUNNER); + } + } +}); diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/SettingsUtils.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/SettingsUtils.js new file mode 100644 index 0000000..b820efb --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/settings/SettingsUtils.js @@ -0,0 +1,251 @@ +import Gdk from 'gi://Gdk'; +import GdkPixbuf from 'gi://GdkPixbuf'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; + +import * as Constants from '../constants.js'; + +/** + * + * @param {Constants.MenuLayout} menuLayout menulayout enum + * @returns 'name of menu layout' + */ +export function getMenuLayoutName(menuLayout) { + for (const styles of Constants.MenuStyles) { + for (const style of styles.MENU_TYPE) { + if (style.LAYOUT === menuLayout) + return style.TITLE; + } + } + return ''; +} + +/** + * + * @param {Array} rows the array of setting rows to show/hide + * @param {Constants.MenuLayout} menuLayout menulayout enum + */ +export function setVisibleRows(rows, menuLayout) { + for (const row in rows) + rows[row].visible = true; + + switch (menuLayout) { + case Constants.MenuLayout.PLASMA: + case Constants.MenuLayout.TOGNEE: + case Constants.MenuLayout.ARCMENU: + break; + case Constants.MenuLayout.ELEVEN: + case Constants.MenuLayout.AZ: + case Constants.MenuLayout.INSIDER: + case Constants.MenuLayout.SLEEK: + rows[Constants.SettingsPage.DIRECTORY_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.APPLICATION_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.EXTRA_CATEGORIES].visible = false; + break; + case Constants.MenuLayout.WHISKER: + case Constants.MenuLayout.BRISK: + case Constants.MenuLayout.ENTERPRISE: + rows[Constants.SettingsPage.DIRECTORY_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.APPLICATION_SHORTCUTS].visible = false; + break; + case Constants.MenuLayout.GNOME_MENU: + rows[Constants.SettingsPage.DIRECTORY_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.APPLICATION_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.SEARCH_OPTIONS].visible = false; + rows[Constants.SettingsPage.POWER_OPTIONS].visible = false; + break; + case Constants.MenuLayout.MINT: + case Constants.MenuLayout.BUDGIE: + rows[Constants.SettingsPage.DIRECTORY_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.APPLICATION_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.POWER_OPTIONS].visible = false; + break; + case Constants.MenuLayout.RAVEN: + case Constants.MenuLayout.UNITY: + rows[Constants.SettingsPage.DIRECTORY_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.POWER_OPTIONS].visible = false; + break; + case Constants.MenuLayout.ELEMENTARY: + case Constants.MenuLayout.CHROMEBOOK: + case Constants.MenuLayout.RUNNER: + case Constants.MenuLayout.POP: + rows[Constants.SettingsPage.PINNED_APPS].visible = false; + rows[Constants.SettingsPage.APPLICATION_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.DIRECTORY_SHORTCUTS].visible = false; + rows[Constants.SettingsPage.POWER_OPTIONS].visible = false; + rows[Constants.SettingsPage.EXTRA_CATEGORIES].visible = false; + break; + case Constants.MenuLayout.REDMOND: + case Constants.MenuLayout.WINDOWS: + rows[Constants.SettingsPage.EXTRA_CATEGORIES].visible = false; + break; + case Constants.MenuLayout.GNOME_OVERVIEW: + for (const row in rows) + rows[row].visible = false; + break; + default: + break; + } +} + +/** + * + * @param {string} colorString string representing a color + * @returns a new Gdk.RGBA() representing the colorString + */ +export function parseRGBA(colorString) { + const rgba = new Gdk.RGBA(); + rgba.parse(colorString); + return rgba; +} + +function rgbaToArray(rgba) { + return [Math.round(rgba.red * 255), Math.round(rgba.green * 255), Math.round(rgba.blue * 255), Math.round(rgba.alpha * 255)]; +} + +/** + * + * @param {string} color1 string representing a color + * @param {string} color2 string representing a color + * @param {string} color3 string representing a color + * @param {string} color4 string representing a color + * @returns GdkPixbuf.Pixbuf + */ +export function createThemePreviewPixbuf(color1, color2, color3, color4) { + color1 = rgbaToArray(parseRGBA(color1)); + color2 = rgbaToArray(parseRGBA(color2)); + color3 = rgbaToArray(parseRGBA(color3)); + color4 = rgbaToArray(parseRGBA(color4)); + const borderColor = [170, 170, 170, 255]; + + const hasAlpha = true; + const width = 42; + const height = 14; + const pixels = hasAlpha ? 4 : 3; + const rowStride = width * pixels; + + // Remove the alpha channel from the color arrays if hasAlpha = false + if (!hasAlpha) { + color1.pop(); + color2.pop(); + color3.pop(); + color4.pop(); + borderColor.pop(); + } + + const data = new Uint8Array(rowStride * height); + + // Top and Bottom border + for (let i = 0; i < width; i++) { + data.set(borderColor, i * pixels); + data.set(borderColor, (height - 1) * rowStride + i * pixels); + } + + // Middle section (color patterns) + for (let row = 1; row < height - 1; row++) { + // First pixel of the row (gray border) + data.set(borderColor, row * rowStride); + + // Patterned section (using color1, color2, color3, color4) + for (let col = 1; col < width - 1; col++) { + let color; + if (col <= 10) + color = color1; + else if (col <= 20) + color = color2; + else if (col <= 30) + color = color3; + else + color = color4; + + data.set(color, row * rowStride + col * pixels); + } + + // Last pixel of the row (gray border) + data.set(borderColor, row * rowStride + (width - 1) * pixels); + } + + const pixbuf = GdkPixbuf.Pixbuf.new_from_bytes( + data, + GdkPixbuf.Colorspace.RGB, + hasAlpha, + 8, + width, + height, + rowStride + ); + + return pixbuf; +} + +/** + * + * @param {Gdk.RGBA} color A Gdk.RGBA to convert to a string in hex format + * @returns String of Color in hex + */ +export function rgbToHex(color) { + const [r, g, b, a_] = [Math.round(color.red * 255), Math.round(color.green * 255), + Math.round(color.blue * 255), Math.round(color.alpha * 255)]; + return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; +} + +/** + * + * @param {object} listing The shortcut data listing + * @returns An icon name for the given listing data + */ +export function getIconStringFromListing(listing) { + let path; + const shortcutCommand = listing[2]; + const shortcutIconName = listing[1]; + + switch (shortcutCommand) { + case Constants.ShortcutCommands.DOCUMENTS: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOCUMENTS); + break; + case Constants.ShortcutCommands.DOWNLOADS: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOWNLOAD); + break; + case Constants.ShortcutCommands.MUSIC: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_MUSIC); + break; + case Constants.ShortcutCommands.PICTURES: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES); + break; + case Constants.ShortcutCommands.VIDEOS: + path = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_VIDEOS); + break; + case Constants.ShortcutCommands.HOME: + path = GLib.get_home_dir(); + break; + case Constants.ShortcutCommands.NETWORK: + return 'network-workgroup-symbolic'; + case Constants.ShortcutCommands.COMPUTER: + return 'drive-harddisk-symbolic'; + case Constants.ShortcutCommands.RECENT: + return 'document-open-recent-symbolic'; + default: + path = shortcutCommand ?? shortcutIconName; + } + + if (!path) + return shortcutIconName; + + const file = Gio.File.new_for_path(path); + + if (!file.query_exists(null)) + return shortcutIconName; + + try { + const info = file.query_info('standard::symbolic-icon', 0, null); + return info.get_symbolic_icon().to_string(); + } catch (e) { + if (e instanceof Gio.IOErrorEnum) { + if (!file.is_native()) + return new Gio.ThemedIcon({name: 'folder-remote-symbolic'}).to_string(); + else + return new Gio.ThemedIcon({name: 'folder-symbolic'}).to_string(); + } + } + return ''; +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/standaloneRunner.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/standaloneRunner.js new file mode 100644 index 0000000..2ce464d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/standaloneRunner.js @@ -0,0 +1,174 @@ +import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +import Clutter from 'gi://Clutter'; +import GLib from 'gi://GLib'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; + +import {ArcMenu} from './menuButton.js'; +import {ArcMenuManager} from './arcmenuManager.js'; +import * as Constants from './constants.js'; +import * as LayoutHandler from './menulayouts/layoutHandler.js'; +import * as MW from './menuWidgets.js'; + +export const StandaloneRunner = class ArcMenuStandaloneRunner { + constructor() { + this._destroyed = false; + this._settings = ArcMenuManager.settings; + this._extension = ArcMenuManager.extension; + + // Link search providers to this menu + this.searchProviderDisplayId = 'StandaloneRunner'; + + this.tooltipShowing = false; + this.tooltipShowingID = null; + this.tooltip = new MW.Tooltip(this); + + // Create Main Menus - ArcMenu and arcMenu's context menu + this.arcMenu = new ArcMenu(Main.layoutManager.dummyCursor, 0.5, St.Side.TOP, this); + this.arcMenu.connect('open-state-changed', this._onOpenStateChanged.bind(this)); + + this.menuManager = new PopupMenu.PopupMenuManager(Main.panel); + this.menuManager._changeMenu = () => {}; + this.menuManager.addMenu(this.arcMenu); + + // Context Menus for applications and other menu items + this.contextMenuManager = new PopupMenu.PopupMenuManager(this.arcMenu); + this.contextMenuManager._changeMenu = () => {}; + this.contextMenuManager._onMenuSourceEnter = menu => { + if (this.contextMenuManager.activeMenu && this.contextMenuManager.activeMenu !== menu) + return Clutter.EVENT_STOP; + + return Clutter.EVENT_PROPAGATE; + }; + + // Sub Menu Manager - Control all other popup menus + this.subMenuManager = new PopupMenu.PopupMenuManager(this.arcMenu); + this.subMenuManager._changeMenu = () => {}; + + this.createMenuLayout(); + } + + get extension() { + return this._extension; + } + + get settings() { + return this._settings; + } + + async createMenuLayout() { + this._clearTooltipShowingId(); + this._clearTooltip(); + + this._forcedMenuLocation = false; + + this._destroyMenuLayout(); + + if (this._destroyed) + return; + + const standaloneRunner = true; + this._menuLayout = await LayoutHandler.createMenuLayout(this, Constants.MenuLayout.RUNNER, standaloneRunner); + + // StandaloneRunner may be destroyed while createMenuLayout() is running + if (this._destroyed && this._menuLayout) { + this._menuLayout.destroy(); + this._menuLayout = null; + return; + } + + if (this._menuLayout) + this.arcMenu.box.add_child(this._menuLayout); + } + + closeOtherMenus() { + if (this.contextMenuManager.activeMenu) + this.contextMenuManager.activeMenu.toggle(); + if (this.subMenuManager.activeMenu) + this.subMenuManager.activeMenu.toggle(); + } + + toggleMenu() { + this.closeOtherMenus(); + + if (!this.arcMenu.isOpen) + this._menuLayout.updateLocation(); + + this.arcMenu.toggle(); + + if (this.arcMenu.isOpen) + this._menuLayout?.grab_key_focus(); + } + + _destroyMenuLayout() { + if (this._menuLayout) { + this._menuLayout.destroy(); + this._menuLayout = null; + } + } + + _clearTooltipShowingId() { + if (this.tooltipShowingID) { + GLib.source_remove(this.tooltipShowingID); + this.tooltipShowingID = null; + } + } + + _clearTooltip() { + this.tooltipShowing = false; + if (this.tooltip) { + this.tooltip.hide(); + this.tooltip.sourceActor = null; + } + } + + destroy() { + this._destroyed = true; + this._clearTooltipShowingId(); + this._clearTooltip(); + this._destroyMenuLayout(); + + this.tooltip?.destroy(); + this.tooltip = null; + + this.arcMenu?.destroy(); + this.arcMenu = null; + } + + updateLocation() { + this._menuLayout?.updateLocation(); + } + + getActiveCategoryType() { + return this._menuLayout?.activeCategoryType; + } + + reloadApplications() { + this._menuLayout?.reloadApplications(); + } + + displayPinnedApps() { + this._menuLayout?.displayPinnedApps(); + } + + loadPinnedApps() { + this._menuLayout?.loadPinnedApps(); + } + + setDefaultMenuView() { + this._menuLayout?.setDefaultMenuView(); + } + + _onOpenStateChanged(menu, open) { + if (open) { + if (Main.panel.menuManager && Main.panel.menuManager.activeMenu) + Main.panel.menuManager.activeMenu.toggle(); + } else if (!this.arcMenu.isOpen) { + this._clearTooltipShowingId(); + this._clearTooltip(); + } + } +}; diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/stylesheet.css b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/stylesheet.css new file mode 100644 index 0000000..73b465c --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/stylesheet.css @@ -0,0 +1,152 @@ +.ArcMenuIconGrid { + text-align: center; + border-radius: 8px; + padding: 5px; + spacing: 0px; + margin: 0px; +} + +.ArcMenuGroupFolder{ + border: 2px solid rgba(0, 0, 0, 0); +} + +.ArcMenuIconGrid:drop { + box-shadow: inset 0px 0px 0px 2px rgba(53, 132, 228, 0.8); + background-color: rgba(53, 132, 228, 0.2); +} + +.arcmenu-menu-item:drop { + box-shadow: inset 0px 0px 0px 2px rgba(53, 132, 228, 0.8); + background-color: rgba(53, 132, 228, 0.2); +} + +.folder-header { + font-weight: 800; + font-size: 16px; +} + +.arcmenu-folder-dialog { + border-width: 2px; +} + +.folder-name-container{ + spacing: 12px; +} + +.bottomOfScreenMenu{ + margin-bottom: 0px; +} + +.arcmenu-search-entry{ + min-height: 0px; + border-width: 1px; + padding: 7px 9px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1); +} + +.arcmenu-search-top{ + margin: 0px 0px 6px 0px; +} + +.arcmenu-search-bottom{ + margin: 6px 0px 0px 0px; +} + +.symbolic-icons{ + -st-icon-style: symbolic; +} +.regular-icons{ + -st-icon-style: regular; +} + +.arcmenu-menu-item{ + padding-top: 6px; + padding-bottom: 6px; +} + +#arcmenu-plasma-button-top{ + width: 90px; + height: 65px; + border-radius: 8px; + text-align: center; + padding: 5px; + spacing: 0px; + margin: 0px; + border-top-width: 2px; + border-color: transparent; +} +#arcmenu-plasma-button-top:active-item, .arcmenu-plasma-button-top:active{ + background-color: rgba(245, 245, 245, .05); + border-top-width: 2px; + border-color: rgb(61, 174, 235); +} +#arcmenu-plasma-button-bottom{ + width: 90px; + height: 65px; + border-radius: 8px; + text-align: center; + padding: 5px; + spacing: 0px; + margin: 0px; + border-bottom-width: 2px; + border-color: transparent; +} +#arcmenu-plasma-button-bottom:active-item, .arcmenu-plasma-button-bottom:active{ + background-color: rgba(245, 245, 245, .05); + border-bottom-width: 2px; + border-color: rgb(61, 174, 235); +} + +.small-vfade{ + -st-vfade-offset: 44px; +} + +.arcmenu-margin-box{ + margin: 0px 6px; +} + +.arcmenu-button{ + -st-icon-style: symbolic; + border-width: 1px; + border-radius: 8px; + padding: 8px; + min-height: 0px; +} + +.arcmenu-small-button { + border-radius: 8px; + padding: 1px 6px; + min-height: auto; +} + +.arcmenu-avatar-round{ + background-size: contain; + border-radius: 999px; +} +.arcmenu-avatar-square{ + background-size: contain; + border-radius: 0px; +} + +.arcmenu-tooltip{ + border: 1px solid rgba(255, 255, 255, 0.25); + padding: 4px 10px; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1); + max-width: 400px; + min-height: 0px; +} + +/* Recently Installed App Indicator on parent category */ +/* +.arcmenu-indicator{ + color: rgb(41, 165, 249); +} +*/ + +/* Recently Installed App Indicator Text */ +.arcmenu-text-indicator{ + font-size: small; + border-radius: 8px; + margin: 0px; + padding: 0px 10px; +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/theming.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/theming.js new file mode 100644 index 0000000..4f5e52d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/theming.js @@ -0,0 +1,315 @@ +import Clutter from 'gi://Clutter'; +import Cogl from 'gi://Cogl'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import St from 'gi://St'; + +import {ArcMenuManager} from './arcmenuManager.js'; + +Gio._promisify(Gio.File.prototype, 'replace_contents_bytes_async', 'replace_contents_finish'); +Gio._promisify(Gio.File.prototype, 'delete_async'); + +const FileName = 'XXXXXX-arcmenu-stylesheet.css'; + +/** + * Create and load a custom stylesheet file into global.stage St.Theme + */ +export function createStylesheet() { + try { + const [file] = Gio.File.new_tmp(FileName); + ArcMenuManager.customStylesheet = file; + updateStylesheet(); + } catch (e) { + log(`ArcMenu - Error creating custom stylesheet: ${e}`); + } +} + +/** + * Unload the custom stylesheet from global.stage St.Theme + */ +function unloadStylesheet() { + if (!ArcMenuManager.customStylesheet) + return; + + const theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); + theme.unload_stylesheet(ArcMenuManager.customStylesheet); +} + +/** + * Delete and unload the custom stylesheet file from global.stage St.Theme + */ +export async function deleteStylesheet() { + unloadStylesheet(); + + const {extension} = ArcMenuManager; + const stylesheet = ArcMenuManager.customStylesheet; + + try { + if (stylesheet.query_exists(null)) + await stylesheet.delete_async(GLib.PRIORITY_DEFAULT, null); + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) + log(`ArcMenu - Error deleting custom stylesheet: ${e}`); + } finally { + delete extension.customStylesheet; + } +} + +/** + * Write theme data to custom stylesheet and reload into global.stage St.Theme + */ +export async function updateStylesheet() { + const {settings} = ArcMenuManager; + const stylesheet = ArcMenuManager.customStylesheet; + + if (!stylesheet) { + log('ArcMenu - Warning: Custom stylesheet not found! Unable to set contents of custom stylesheet.'); + return; + } + + unloadStylesheet(); + + let customMenuThemeCSS = ''; + let extraStylingCSS = ''; + + const menuBGColor = settings.get_string('menu-background-color'); + const menuFGColor = settings.get_string('menu-foreground-color'); + const menuBorderColor = settings.get_string('menu-border-color'); + const menuBorderWidth = settings.get_int('menu-border-width'); + const menuBorderRadius = settings.get_int('menu-border-radius'); + const menuFontSize = settings.get_int('menu-font-size'); + const menuSeparatorColor = settings.get_string('menu-separator-color'); + const itemHoverBGColor = settings.get_string('menu-item-hover-bg-color'); + const itemHoverFGColor = settings.get_string('menu-item-hover-fg-color'); + const itemActiveBGColor = settings.get_string('menu-item-active-bg-color'); + const itemActiveFGColor = settings.get_string('menu-item-active-fg-color'); + + const [menuRise, menuRiseValue] = settings.get_value('menu-arrow-rise').deep_unpack(); + + const [buttonFG, buttonFGColor] = settings.get_value('menu-button-fg-color').deep_unpack(); + const [buttonBG, buttonBGColor] = settings.get_value('menu-button-bg-color').deep_unpack(); + const [buttonHoverBG, buttonHoverBGColor] = settings.get_value('menu-button-hover-bg-color').deep_unpack(); + const [buttonHoverFG, buttonHoverFGColor] = settings.get_value('menu-button-hover-fg-color').deep_unpack(); + const [buttonActiveBG, buttonActiveBGColor] = settings.get_value('menu-button-active-bg-color').deep_unpack(); + const [buttonActiveFG, buttonActiveFGColor] = settings.get_value('menu-button-active-fg-color').deep_unpack(); + const [buttonRadius, buttonRadiusValue] = settings.get_value('menu-button-border-radius').deep_unpack(); + const [buttonWidth, buttonWidthValue] = settings.get_value('menu-button-border-width').deep_unpack(); + const [buttonBorder, buttonBorderColor] = settings.get_value('menu-button-border-color').deep_unpack(); + const [searchBorder, searchBorderValue] = settings.get_value('search-entry-border-radius').deep_unpack(); + + if (buttonFG) { + extraStylingCSS += `.arcmenu-menu-button{ + color: ${buttonFGColor}; + }`; + } + if (buttonBG) { + extraStylingCSS += `.arcmenu-panel-menu{ + box-shadow: inset 0 0 0 100px transparent; + background-color: ${buttonBGColor}; + }`; + } + if (buttonHoverBG) { + extraStylingCSS += `.arcmenu-panel-menu:hover{ + box-shadow: inset 0 0 0 100px transparent; + background-color: ${buttonHoverBGColor}; + }`; + } + if (buttonHoverFG) { + extraStylingCSS += `.arcmenu-panel-menu:hover .arcmenu-menu-button{ + color: ${buttonHoverFGColor}; + }`; + } + if (buttonActiveFG) { + extraStylingCSS += `.arcmenu-menu-button:active{ + color: ${buttonActiveFGColor}; + }`; + } + if (buttonActiveBG) { + extraStylingCSS += `.arcmenu-panel-menu:active{ + box-shadow: inset 0 0 0 100px transparent; + background-color: ${buttonActiveBGColor}; + }`; + } + if (buttonRadius) { + extraStylingCSS += `.arcmenu-panel-menu{ + border-radius: ${buttonRadiusValue}px; + }`; + } + if (buttonWidth) { + extraStylingCSS += `.arcmenu-panel-menu{ + border-width: ${buttonWidthValue}px; + }`; + } + if (buttonBorder) { + extraStylingCSS += `.arcmenu-panel-menu{ + border-color: ${buttonBorderColor}; + }`; + } + if (menuRise) { + extraStylingCSS += `.arcmenu-menu{ + -arrow-rise: ${menuRiseValue}px; + }`; + } + if (searchBorder) { + extraStylingCSS += `#ArcMenuSearchEntry{ + border-radius: ${searchBorderValue}px; + }`; + } + + if (settings.get_boolean('override-menu-theme')) { + customMenuThemeCSS = ` + .arcmenu-menu{ + font-size: ${menuFontSize}pt; + color: ${menuFGColor}; + } + .arcmenu-menu .popup-menu-content { + background-color: ${menuBGColor}; + border-color: ${menuBorderColor}; + border-width: ${menuBorderWidth}px; + border-radius: ${menuBorderRadius}px; + } + .arcmenu-menu StButton { + color: ${menuFGColor}; + background-color: ${menuBGColor}; + border-width: 0px; + box-shadow: none; + border-radius: 8px; + } + .arcmenu-menu StScrollBar StButton#vhandle, .arcmenu-menu StScrollBar StButton#hhandle { + background-color: ${modifyColorLuminance(menuBGColor, 0.15)}; + } + .arcmenu-menu StScrollBar StButton#vhandle:hover, .arcmenu-menu StScrollBar StButton#hhandle:hover { + background-color: ${modifyColorLuminance(menuBGColor, 0.20)}; + } + .arcmenu-menu StScrollBar StButton#vhandle:active, .arcmenu-menu StScrollBar StButton#hhandle:active { + background-color: ${modifyColorLuminance(menuBGColor, 0.25)}; + } + .arcmenu-menu .popup-menu-item { + color: ${menuFGColor}; + } + .arcmenu-menu .popup-menu-item:focus, .arcmenu-menu .popup-menu-item:hover, + .arcmenu-menu .popup-menu-item:checked, .arcmenu-menu .popup-menu-item.selected, + .arcmenu-menu StButton:focus, .arcmenu-menu StButton:hover, .arcmenu-menu StButton:checked { + color: ${itemHoverFGColor}; + background-color: ${itemHoverBGColor}; + } + .arcmenu-menu .popup-menu-item:active, .arcmenu-menu StButton:active { + color: ${itemActiveFGColor}; + background-color: ${itemActiveBGColor}; + } + .arcmenu-menu .popup-menu-item:insensitive{ + color: ${modifyColorLuminance(menuFGColor, 0, 0.6)}; + font-size: ${menuFontSize - 2}pt; + } + .arcmenu-menu .world-clocks-header, .arcmenu-menu .world-clocks-timezone, + .arcmenu-menu .weather-header{ + color: ${modifyColorLuminance(menuFGColor, 0, 0.6)}; + } + .arcmenu-menu .world-clocks-time, .arcmenu-menu .world-clocks-city{ + color: ${menuFGColor}; + } + .arcmenu-menu .weather-forecast-time{ + color: ${modifyColorLuminance(menuFGColor, 0.1)}; + } + .arcmenu-menu .popup-separator-menu-item .popup-separator-menu-item-separator{ + background-color: ${menuSeparatorColor}; + } + .arcmenu-menu .popup-separator-menu-item StLabel{ + color: ${menuFGColor}; + } + .separator-color-style{ + background-color: ${menuSeparatorColor}; + } + .arcmenu-menu StEntry{ + font-size: ${menuFontSize}pt; + border-color: ${modifyColorLuminance(menuSeparatorColor, 0, .1)}; + color: ${menuFGColor}; + background-color: ${modifyColorLuminance(menuBGColor, 0.1, .4)}; + } + .arcmenu-menu StEntry:hover{ + border-color: ${itemHoverBGColor}; + background-color: ${modifyColorLuminance(menuBGColor, 0.15, .4)}; + } + .arcmenu-menu StEntry:focus{ + border-color: ${itemActiveBGColor}; + background-color: ${modifyColorLuminance(menuBGColor, 0.2, .4)}; + } + .arcmenu-menu StLabel.hint-text{ + color: ${modifyColorLuminance(menuFGColor, 0, 0.6)}; + } + #ArcMenu_Tooltip{ + font-size: ${menuFontSize}pt; + color: ${menuFGColor}; + background-color: ${modifyColorLuminance(menuBGColor, -0.125, 1)}; + border: 1px solid ${modifyColorLuminance(menuBorderColor, 0.025)}; + } + .arcmenu-menu .user-icon{ + border-color: ${modifyColorLuminance(menuFGColor, 0, .7)}; + } + `; + } + + const customStylesheetCSS = customMenuThemeCSS + extraStylingCSS; + + if (customStylesheetCSS.length === 0) + return; + + try { + const bytes = new GLib.Bytes(customStylesheetCSS); + + const [success, etag_] = await stylesheet.replace_contents_bytes_async(bytes, null, false, + Gio.FileCreateFlags.REPLACE_DESTINATION, null); + + if (!success) { + log('ArcMenu - Failed to replace contents of custom stylesheet.'); + return; + } + + ArcMenuManager.customStylesheet = stylesheet; + const theme = St.ThemeContext.get_for_stage(global.stage).get_theme(); + theme.load_stylesheet(ArcMenuManager.customStylesheet); + } catch (e) { + log(`ArcMenu - Error replacing contents of custom stylesheet: ${e}`); + } +} + +/** + * + * @param {string} colorString the color to modify + * @param {number} luminanceAdjustment luminance adjustment + * @param {number} overrideAlpha change the color alpha to this value + * @returns a string in rbga() format representing the new modified color + */ +function modifyColorLuminance(colorString, luminanceAdjustment, overrideAlpha = null) { + let color, hue, saturation, luminance; + + // GNOME 47 merged ClutterColor into CoglColor + if (Clutter.Color) { + color = Clutter.color_from_string(colorString)[1]; + [hue, luminance, saturation] = color.to_hls(); + } else { + color = Cogl.color_from_string(colorString)[1]; + [hue, saturation, luminance] = color.to_hsl(); + } + + let modifiedLuminance; + + if ((luminance >= .85 && luminanceAdjustment > 0) || (luminance <= .15 && luminanceAdjustment < 0)) + modifiedLuminance = Math.max(Math.min(luminance - luminanceAdjustment, 1), 0); + else + modifiedLuminance = Math.max(Math.min(luminance + luminanceAdjustment, 1), 0); + + let alpha = (color.alpha / 255).toPrecision(3); + if (overrideAlpha) + alpha = overrideAlpha; + + let modifiedColor; + + // GNOME 47 merged ClutterColor into CoglColor + if (Clutter.Color) + modifiedColor = Clutter.color_from_hls(hue, modifiedLuminance, saturation); + else + modifiedColor = Cogl.color_init_from_hsl(hue, saturation, modifiedLuminance); + + return `rgba(${modifiedColor.red}, ${modifiedColor.green}, ${modifiedColor.blue}, ${alpha})`; +} diff --git a/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/utils.js b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/utils.js new file mode 100644 index 0000000..971e00d --- /dev/null +++ b/gnome-shell-extension-arc-menu/usr/share/gnome-shell/extensions/arcmenu@arcmenu.com/utils.js @@ -0,0 +1,538 @@ +/* eslint-disable jsdoc/require-jsdoc */ +import Clutter from 'gi://Clutter'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import Pango from 'gi://Pango'; +import Shell from 'gi://Shell'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import {getLoginManager} from 'resource:///org/gnome/shell/misc/loginManager.js'; + +import * as Constants from './constants.js'; +import {ArcMenuManager} from './arcmenuManager.js'; + +import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +const InterfaceXml = ` + + + +`; + +export const DBusService = class { + constructor() { + this.ToggleArcMenu = () => {}; + + this._dbusExportedObject = Gio.DBusExportedObject.wrapJSObject(InterfaceXml, this); + + const onBusAcquired = (connection, _name) => { + try { + this._dbusExportedObject.export(connection, '/com/Extensions/ArcMenu'); + } catch (error) { + console.log(`ArcMenu Error - onBusAcquired export failed: ${error}`); + } + }; + + function onNameAcquired() { } + + function onNameLost() { } + + this._ownerId = Gio.bus_own_name(Gio.BusType.SESSION, 'com.Extensions.ArcMenu', Gio.BusNameOwnerFlags.NONE, + onBusAcquired, onNameAcquired, onNameLost); + } + + destroy() { + this._dbusExportedObject.unexport(); + Gio.bus_unown_name(this._ownerId); + } +}; + +export function convertOldSetting(settings, oldSetting, newSetting) { + const data = settings.get_value(oldSetting).deepUnpack(); + + if (!data.length) + return; + + const newDataArray = []; + + if (oldSetting === 'pinned-app-list') { + settings.set_strv(oldSetting, []); + + for (let i = 0; i < data.length; i += 3) { + const newData = {}; + if (data[i]) + newData.name = data[i]; + if (data[i + 1]) + newData.icon = data[i + 1]; + if (data[i + 2]) + newData.id = data[i + 2]; + + newDataArray.push(newData); + } + } else { + settings.set_value(oldSetting, new GLib.Variant('aas', [])); + + data.forEach(entry => { + const newData = {}; + if (entry[0]) + newData.name = entry[0]; + if (entry[1]) + newData.icon = entry[1]; + if (entry[2]) + newData.id = entry[2]; + + newDataArray.push(newData); + }); + } + + settings.set_value(newSetting, new GLib.Variant('aa{ss}', newDataArray)); + console.log(`Converted ${oldSetting} to ${newSetting}`); +} + +export function activateHibernateOrSleep(powerType) { + const loginManager = getLoginManager(); + let callName, activateCall; + + if (powerType === Constants.PowerType.HIBERNATE) { + callName = 'CanHibernate'; + activateCall = 'Hibernate'; + } else if (powerType === Constants.PowerType.HYBRID_SLEEP) { + callName = 'CanHybridSleep'; + activateCall = 'HybridSleep'; + } + + if (!loginManager._proxy) { + Main.notifyError(`ArcMenu - ${activateCall} Error!`, `System unable to ${activateCall}.`); + return; + } + + canHibernateOrSleep(callName, result => { + if (!result) { + Main.notifyError(`ArcMenu - ${activateCall} Error!`, `System unable to ${activateCall}.`); + return; + } + + loginManager._proxy.call(activateCall, + GLib.Variant.new('(b)', [true]), + Gio.DBusCallFlags.NONE, + -1, null, null); + }); +} + +export function canHibernateOrSleep(callName, asyncCallback) { + const loginManager = getLoginManager(); + + if (!loginManager._proxy) + asyncCallback(false); + + loginManager._proxy.call(callName, null, Gio.DBusCallFlags.NONE, -1, null, (proxy, asyncResult) => { + let result, error; + + try { + result = proxy.call_finish(asyncResult).deep_unpack(); + } catch (e) { + error = e; + } + + if (error) + asyncCallback(false); + else + asyncCallback(result[0] === 'yes'); + }); +} + +export const SettingsConnectionsHandler = class ArcMenuSettingsConnectionsHandler { + constructor(settings) { + this._connections = new Map(); + this._settings = settings; + } + + connect(...args) { + const callback = args.pop(); + for (const event of args) + this._connections.set(this._settings.connect(`changed::${event}`, callback), this._settings); + } + + destroy() { + this._connections.forEach((object, id) => { + object.disconnect(id); + id = null; + }); + + this._connections = null; + } +}; + +export function convertToButton(item) { + item.tooltipLocation = Constants.TooltipLocation.BOTTOM_CENTERED; + item.remove_child(item.label); + item.set({ + x_expand: false, + x_align: Clutter.ActorAlign.CENTER, + y_expand: false, + y_align: Clutter.ActorAlign.CENTER, + style_class: 'popup-menu-item arcmenu-button', + }); +} + +export function convertToGridLayout(item) { + const menuLayout = item._menuLayout; + const icon = item._iconBin; + + const {settings} = ArcMenuManager; + + const iconSizeEnum = settings.get_enum('menu-item-grid-icon-size'); + const defaultIconSize = menuLayout.icon_grid_size; + const {width, height, iconSize_} = getGridIconSize(iconSizeEnum, defaultIconSize); + + item.add_style_class_name('ArcMenuIconGrid'); + item.set({ + vertical: true, + x_align: Clutter.ActorAlign.CENTER, + tooltipLocation: Constants.TooltipLocation.BOTTOM_CENTERED, + style: `width: ${width}px; height: ${height}px;`, + }); + + icon?.set({ + y_align: Clutter.ActorAlign.CENTER, + y_expand: true, + }); + + if (item._indicator) { + item.remove_child(item._indicator); + item.insert_child_at_index(item._indicator, 0); + item._indicator.set({ + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.START, + y_expand: false, + }); + } + + if (!settings.get_boolean('multi-lined-labels')) + return; + + icon?.set({ + y_align: Clutter.ActorAlign.TOP, + y_expand: false, + }); + + const clutterText = item.label.get_clutter_text(); + clutterText.set({ + line_wrap: true, + line_wrap_mode: Pango.WrapMode.WORD_CHAR, + }); +} + +export function getIconSize(iconSizeEnum, defaultIconSize) { + switch (iconSizeEnum) { + case Constants.IconSize.DEFAULT: + return defaultIconSize; + case Constants.IconSize.EXTRA_SMALL: + return Constants.EXTRA_SMALL_ICON_SIZE; + case Constants.IconSize.SMALL: + return Constants.SMALL_ICON_SIZE; + case Constants.IconSize.MEDIUM: + return Constants.MEDIUM_ICON_SIZE; + case Constants.IconSize.LARGE: + return Constants.LARGE_ICON_SIZE; + case Constants.IconSize.EXTRA_LARGE: + return Constants.EXTRA_LARGE_ICON_SIZE; + case Constants.IconSize.HIDDEN: + return Constants.ICON_HIDDEN; + default: + return defaultIconSize; + } +} + +export function getGridIconSize(iconSizeEnum, defaultIconSize) { + const {settings} = ArcMenuManager; + + if (iconSizeEnum === Constants.GridIconSize.CUSTOM) { + const {width, height, iconSize} = settings.get_value('custom-grid-icon-size').deep_unpack(); + return {width, height, iconSize}; + } + + if (iconSizeEnum === Constants.GridIconSize.DEFAULT) + iconSizeEnum = defaultIconSize; + + let width, height, iconSize; + Constants.GridIconInfo.forEach(info => { + if (iconSizeEnum === info.ENUM) { + width = info.WIDTH; + height = info.HEIGHT; + iconSize = info.ICON_SIZE; + } + }); + return {width, height, iconSize}; +} + +export function getCategoryDetails(iconTheme, currentCategory) { + const extensionPath = ArcMenuManager.extension.path; + + let name = null, gicon = null, fallbackIcon = null; + + for (const entry of Constants.Categories) { + if (entry.CATEGORY === currentCategory) { + name = entry.NAME; + gicon = Gio.icon_new_for_string(entry.ICON); + return [name, gicon, fallbackIcon]; + } + } + + if (currentCategory === Constants.CategoryType.HOME_SCREEN) { + name = _('Home'); + gicon = Gio.icon_new_for_string('computer-symbolic'); + return [name, gicon, fallbackIcon]; + } else { + name = currentCategory.get_name(); + const categoryIcon = currentCategory.get_icon(); + const fallbackIconDirectory = `${extensionPath}/icons/category-icons/`; + + if (!categoryIcon) + return [name, gicon, fallbackIcon]; + + const categoryIconName = categoryIcon.to_string(); + const symbolicName = `${categoryIconName}-symbolic`; + const symbolicIconFile = `${symbolicName}.svg`; + + gicon = categoryIcon; + + const categoryIconType = ArcMenuManager.settings.get_enum('category-icon-type'); + if (categoryIconType === Constants.CategoryIconType.SYMBOLIC) { + const icon = iconTheme.lookup_icon(symbolicName, 26, St.IconLookupFlags.FORCE_SYMBOLIC); + if (icon) { + gicon = Gio.icon_new_for_string(symbolicName); + } else { + const filePath = `${fallbackIconDirectory}${symbolicIconFile}`; + const file = Gio.File.new_for_path(filePath); + if (file.query_exists(null)) + gicon = Gio.icon_new_for_string(`${fallbackIconDirectory}${symbolicIconFile}`); + } + } + + fallbackIcon = Gio.icon_new_for_string(`${fallbackIconDirectory}${symbolicIconFile}`); + return [name, gicon, fallbackIcon]; + } +} + +export function getPowerTypeFromShortcutCommand(command) { + switch (command) { + case Constants.ShortcutCommands.LOG_OUT: + return Constants.PowerType.LOGOUT; + case Constants.ShortcutCommands.LOCK: + return Constants.PowerType.LOCK; + case Constants.ShortcutCommands.POWER_OFF: + return Constants.PowerType.POWER_OFF; + case Constants.ShortcutCommands.RESTART: + return Constants.PowerType.RESTART; + case Constants.ShortcutCommands.SUSPEND: + return Constants.PowerType.SUSPEND; + case Constants.ShortcutCommands.HYBRID_SLEEP: + return Constants.PowerType.HYBRID_SLEEP; + case Constants.ShortcutCommands.HIBERNATE: + return Constants.PowerType.HIBERNATE; + case Constants.ShortcutCommands.SWITCH_USER: + return Constants.PowerType.SWITCH_USER; + default: + return Constants.PowerType.POWER_OFF; + } +} + +export function getMenuButtonIcon(path) { + const extensionPath = ArcMenuManager.extension.path; + const {settings} = ArcMenuManager; + + const iconType = settings.get_enum('menu-button-icon'); + const iconDirectory = `${extensionPath}/icons/hicolor/16x16/actions/`; + + if (iconType === Constants.MenuIconType.CUSTOM) { + if (path && GLib.file_test(path, GLib.FileTest.IS_REGULAR)) + return path; + else + return path; + } else if (iconType === Constants.MenuIconType.DISTRO_ICON) { + const iconEnum = settings.get_int('distro-icon'); + const iconPath = `${iconDirectory + Constants.DistroIcons[iconEnum].PATH}.svg`; + if (GLib.file_test(iconPath, GLib.FileTest.IS_REGULAR)) + return iconPath; + } else { + const iconEnum = settings.get_int('arc-menu-icon'); + const iconPath = `${iconDirectory + Constants.MenuIcons[iconEnum].PATH}.svg`; + if (Constants.MenuIcons[iconEnum].PATH === 'view-app-grid-symbolic') + return 'view-app-grid-symbolic'; + else if (GLib.file_test(iconPath, GLib.FileTest.IS_REGULAR)) + return iconPath; + } + + log('ArcMenu Error - Failed to set menu button icon. Set to System Default.'); + return 'start-here-symbolic'; +} + +export function findSoftwareManager() { + const appSys = Shell.AppSystem.get_default(); + + for (const softwareManagerID of Constants.SoftwareManagerIDs) { + if (appSys.lookup_app(softwareManagerID)) + return softwareManagerID; + } + + return 'ArcMenu_InvalidShortcut.desktop'; +} + +export function areaOfTriangle(p1, p2, p3) { + return Math.abs((p1[0] * (p2[1] - p3[1]) + p2[0] * (p3[1] - p1[1]) + p3[0] * (p1[1] - p2[1])) / 2.0); +} + +// modified from GNOME shell's ensureActorVisibleInScrollView() +export function ensureActorVisibleInScrollView(actor, axis = Clutter.Orientation.VERTICAL, dummyBox = null) { + let box = dummyBox ? dummyBox : actor.get_allocation_box(); + let {y1} = box, {y2} = box; + let {x1} = box, {x2} = box; + + let parent = actor.get_parent(); + while (!(parent instanceof St.ScrollView)) { + if (!parent) + return; + + box = parent.get_allocation_box(); + y1 += box.y1; + y2 += box.y1; + x1 += box.x1; + x2 += box.x1; + parent = parent.get_parent(); + } + + const isVertical = axis === Clutter.Orientation.VERTICAL; + const {hadjustment, vadjustment} = getScrollViewAdjustments(parent); + const adjustment = isVertical ? vadjustment : hadjustment; + const [startPoint, endPoint] = isVertical ? [y1, y2] : [x1, x2]; + const [value, lower_, upper, stepIncrement_, pageIncrement_, pageSize] = adjustment.get_values(); + + let offset = 0; + let newValue; + + const fade = parent.get_effect('fade'); + if (fade) + offset = isVertical ? fade.fade_margins.top : fade.fade_margins.left; + + if (startPoint < value + offset) + newValue = Math.max(0, startPoint - offset); + else if (endPoint > value + pageSize - offset) + newValue = Math.min(upper, endPoint + offset - pageSize); + else + return; + + adjustment.ease(newValue, { + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + duration: 100, + }); +} + +export function getCategories(info) { + const categoriesStr = info.get_categories(); + if (!categoriesStr) + return []; + return categoriesStr.split(';'); +} + +export function findBestFolderName(apps) { + const appInfos = apps.map(app => app.get_app_info()); + + const categoryCounter = {}; + const commonCategories = []; + + appInfos.reduce((categories, appInfo) => { + for (const category of getCategories(appInfo)) { + if (!(category in categoryCounter)) + categoryCounter[category] = 0; + + categoryCounter[category] += 1; + + // If a category is present in all apps, its counter will + // reach appInfos.length + if (category.length > 0 && + categoryCounter[category] === appInfos.length) + categories.push(category); + } + return categories; + }, commonCategories); + + for (const category of commonCategories) { + const directory = `${category}.directory`; + const translated = Shell.util_get_translated_folder_name(directory); + if (translated !== null) + return translated; + } + + return null; +} + +export function openPrefs(uuid) { + const extension = Extension.lookupByUUID(uuid); + if (extension !== null) + extension.openPreferences(); +} + +export function getDashToPanelPosition(settings, index) { + var positions = null; + var side = 'NONE'; + + try { + positions = JSON.parse(settings.get_string('panel-positions')); + side = positions[index]; + } catch (e) { + log(`Error parsing Dash to Panel positions: ${e.message}`); + } + + if (side === 'TOP') + return St.Side.TOP; + else if (side === 'RIGHT') + return St.Side.RIGHT; + else if (side === 'BOTTOM') + return St.Side.BOTTOM; + else if (side === 'LEFT') + return St.Side.LEFT; + else + return St.Side.BOTTOM; +} + +/** + * + * @param {Clutter.Actor} parent + * @param {Clutter.Actor} child + * @description GNOME 46 no longer supports add_actor() method.\ + * Check which method to use to maintain compatibility with GNOME 45 and 46. + */ +export function addChildToParent(parent, child) { + if (parent.add_actor) + parent.add_actor(child); + else if (parent instanceof St.Button || parent instanceof St.ScrollView) + parent.set_child(child); + else + parent.add_child(child); +} + +/** + * GNOME 46 renamed the extension states. Use this const instead. + */ +export const ExtensionState = { + ACTIVE: 1, + INACTIVE: 2, +}; + +/** + * + * @param {St.ScrollView} scrollView + * @description ScrollView.(hv)scroll was deprecated in GNOME 46.\ + * Check which ScrollView property to use to maintain compatibility with GNOME 45 and 46. + */ +export function getScrollViewAdjustments(scrollView) { + const hadjustment = scrollView.hadjustment ?? scrollView.hscroll.adjustment; + const vadjustment = scrollView.vadjustment ?? scrollView.vscroll.adjustment; + + return { + hadjustment, + vadjustment, + }; +} diff --git a/gnome-shell-extension-just-perfection/debian/control b/gnome-shell-extension-just-perfection/debian/control deleted file mode 100644 index 70f84ce..0000000 --- a/gnome-shell-extension-just-perfection/debian/control +++ /dev/null @@ -1,21 +0,0 @@ -Source: gnome-shell-extension-just-perfection -Section: gnome -Priority: optional -Maintainer: Marco Trevisan -Build-Depends: debhelper (>= 10), - eslint , - libglib2.0-bin, - node-chalk , - node-js-yaml , - node-strip-ansi , - libgettextpo-dev, - gettext, - sassc -Standards-Version: 4.1.1 - -Package: gnome-shell-extension-just-perfection -Architecture: all -Depends: gnome-shell, - ${misc:Depends}, - ${shlibs:Depends}, -Description: Tweak Tool to Customize GNOME Shell, Change the Behavior and Disable UI Elements diff --git a/gnome-shell-extension-just-perfection/debian/copyright b/gnome-shell-extension-just-perfection/debian/copyright deleted file mode 100644 index c4dc63a..0000000 --- a/gnome-shell-extension-just-perfection/debian/copyright +++ /dev/null @@ -1,675 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - Just Perfection GNOME Shell Desktop - Copyright (C) 2020-2024 Javad Rahmatzadeh - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Just Perfection GNOME Shell Desktop - Copyright (C) 2020-2024 Javad Rahmatzadeh - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/gnome-shell-extension-just-perfection/usr/share/glib-2.0/schemas/org.gnome.shell.extensions.just-perfection.gschema.xml b/gnome-shell-extension-just-perfection/usr/share/glib-2.0/schemas/org.gnome.shell.extensions.just-perfection.gschema.xml deleted file mode 100755 index b732d5f..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/glib-2.0/schemas/org.gnome.shell.extensions.just-perfection.gschema.xml +++ /dev/null @@ -1,409 +0,0 @@ - - - - - - - true - Panel - Panel Visibility Status - - - - false - Panel in Overview - Panel in Overview Visibility Status - - - - true - Background Menu - Background Menu Status - - - - true - Search Box - Search Box Visibility Status - - - - true - Workspace Switcher - Workspace Switcher Visibility Status - - - - true - Dash - Dash Visibility Status - - - - true - OSD - OSD Visibility Status - - - - true - Workspace Popup - Workspace Popup Visibility Status - - - - false - Theme - Theme Status - - - - true - Activities Button - Activities Button Visibility Status - - - - true - Clock Menu - Clock Menu Visibility Status - - - - true - Panel Notification Icon - Panel Notification Icon Visibility Status - - - - true - Keyboard Layout - Keyboard Layout Visibility Status - - - - true - Accessibility Menu - Accessibility Menu Visibility Status - - - - true - Quick Settings Menu - Quick Settings Visibility Status - - - - true - Power Icon - Power Icon Visibility Status - - - - true - Window Picker Icon - Window Picker Icon Visiblity - - - - true - Type to Search - Type to Search Behavior - - - - 0 - - 0 means use Shell theme, 1 means no border, 2 - 61 means border size - Panel Corner Size - - - - 0 - - Workspace Switcher Size in percent, 0 means use default size - Workspace Switcher Size - - - - 0 - - Top Panel Position - Top Panel Position Status - - - - 0 - - 0 means center, 1 means right, 2 means left - Clock Menu Position Status - - - - 0 - - 0 means start of the position - Clock Menu Position Offset - - - - true - Show Apps Button Visiblity - Show Apps Button Visiblity Status - - - - 1 - - 0 means disabled, 1 means default speed, animation speed otherwise - Animation Status - - - - false - Window Demands Attention Focus - Window Demands Attention Focus Status - - - - false - Window Maximized on Create - Window Maximized on Create Status - - - - 0 - - 0 means default, 16, 22, 24, 32, 40, 48, 56, 64 - Dash Icon Size - - - - 1 - - 0 means desktop, 1 means overview - Startup Status - - - - true - Workspaces Visiblity in App Grid - Workspaces Visiblity in App Grid Status - - - - 1 - - 0: top start, 1: top center, 2: top end, 3: bottom start, 4: bottom center, 5: bottom end - Notification Banner Position - - - - false - Always Show Workspaces Switcher - Always Show Workspace Switcher Status - - - - 0 - - 0 means use Shell theme, 1 - 64 means size in pixels - Panel Size - - - - 0 - - 0 means use Shell theme, 1 means no padding, 2 - 61 means padding size - Panel Button Padding Size - - - - 0 - - 0 means use Shell theme, 1 means no padding, 2 - 61 means padding size - Panel Indicator Padding Size - - - - true - Window Preview Caption - Window Preview Caption Status - - - - true - Window Preview Close Button - Window Preview Close Button Status - - - - 0 - - 0 means use default shell size, 1 means no radius, 2 - 61 means radius size - Workspace Background Corner Size - - - - false - Workspace Wrap Around - Workspace Wrap Around Status - - - - true - Ripple Box - Ripple Box Status - - - - true - Double Supper To App Grid - Double Supper To App Grid Status - - - - true - Overlay Key - Overlay Key to Overview Status - - - - true - Switcher Popup Delay - Removes the delay for all switcher popups like alt-tab, ctrl-alt-tab, keyboard layout, ... - - - - true - World Clock Visibility in Clock Menu - World Clock Visibility Status - - - - true - Weather Visibility in Clock Menu - Weather Visibility Status - - - - true - Events Button Visibility in Clock Menu - Events Button Visibility Status - - - - true - Calendar Visibility in Clock Menu - Calendar Visibility Status - - - - 0 - - 0 means use Shell theme, 1 - 60 means panel icon size - Panel Icon Size - - - - true - Dash Separator Visibility - Dash Separator Visibility Status - - - - true - Workspace Peek - Workspace Peek Status - - - - 0 - - Width Size in percent, 0 means use default size - Looking Glass Width size - - - - 0 - - Height Size in percent, 0 means use default size - Looking Glass Height size - - - - 0 - - OSD Position - OSD Position Status - - - - true - Window Menu Take Screenshot Button Visibility - Window Menu Take Screenshot Button Visibility Status - - - - 0 - - 0 means default size - Alt Tab Window Preview Size - - - - 0 - - 0 means default size - Alt Tab Small Icon Size (Window Preview Icon Size) - - - - 0 - - 0 means default size - Alt Tab Icon Size - - - - true - Screen Sharing Indicator Visibility in Panel - Screen Sharing Indicator Visibility Status - - - - true - Screen Recording Indicator Visibility in Panel - Screen Recording Indicator Visibility Status - - - - 0 - - 0 means default size - Controls manager spacing Size (overview spacing) - - - - true - Dash app running dot - Dash app running dot visibility status - - - - 1 - - 0 means default, max displayed search results otherwise - Max Displayed Search Results - - - - true - Dark Mode Toggle Button Visibility In Quick Settings - Dark Mode Toggle Button Visibility Status - - - - - diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/CHANGELOG.md b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/CHANGELOG.md deleted file mode 100755 index 5474176..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/CHANGELOG.md +++ /dev/null @@ -1,555 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -We go to the next version after each release on the -[GNOME Shell Extensions website](https://extensions.gnome.org/). - -## [Unreleased] - -## [30.0] - 2024-07-31 - -### Added - -- GNOME Shell 47 Support. - -## [29.0] - 2024-07-06 - -### Added - -- Dark Mode Toggle Button Visibility in Quick Settings. -- Maximum Displayed Search Results feature (GNOME Shell 45 and higher). -- Gresources compiled file for the preferences window to load faster. - -### Fixed - -- Alt-Tab Switcher list Item Background Color for Shell Theme Override (GNOME Shell 46). -- Hidden panel timeout priority on Wayland. -- Panel Menu not Showing Up On Bottom Panel When the Height is Larger than the Screen Height. -- The dash app running indicator visibility. -- Workspace Popup visibility CPU usage. -- Japanese Translation by [@gnuey56](https://gitlab.gnome.org/gnuey56). - -### Changed - -- The clock menu position widget to `AdwSpinRow`. -- The panel height maximum size to 64. - -## [28.0 Lear] - 2024-03-06 - -### Added - -- Buy Me a Coffee donation link. -- GNOME Shell 46 Support. -- Option to maximize windows on creation by [@droserasprout](https://gitlab.gnome.org/droserasprout). - -### Fixed - -- The world clock visibility coming back after reboot or install change. - -### Removed - -- Window Picker Shell Theme Override. -- Search Shell Theme Override. -- Workspace Animation Shell Theme Override. -- Dash Background Shell Theme Override. - -## [27.0 Durer] - 2023-09-14 - -### Changed - -- The world clocks visibility from CSS to JS. - -### Added - -- "Almost none" option to animation speed feature (GNOME Shell 45). -- GNOME Shell 45 Support. -- Japanese Translation by [@gnuey56](https://gitlab.gnome.org/gnuey56). - -### Removed - -- Activities button icon feature (GNOME Shell 45). -- App menu indicator (GNOME Shell 45). - -## [26.0 Rosa] - 2023-08-07 - -### Fixed - -- Remove not defined. - -## [25.0 Rosa] - 2023-08-07 - -### Changed - -- The behavior of the panel is shown only in the overview. - Rather than sliding in from the top, it is now rendered behind the workspaces - by [Schneegans](https://gitlab.gnome.org/Schneegans). - -### Fixed - -- a11y error in GNOME Shell mobile. -- Activities button error in GNOME Shell mobile. -- Marked as an urgent signal for window demand attention focus feature. -- Panel flickering when its position is at the bottom. -- `.set_icon_size` is not a function error (Date Menu). -- Unwanted padding on the disabled power icon. -- Unwanted shadow for workspace in the app grid. - -### Added - -- Dash app running visibility. -- Disable the overlay key. -- EGO donations. -- Workspace peek (GNOME Shell 42 and higher). - -### Removed - -- GNOME Shell support (3.36, 3.38, 40 and 41 no longer getting new features). - -## [24.0 Hals] - 2023-03-01 - -### Changed - -- Window preview close button for super minimal profile (to disabled). - -### Added - -- GNOME Shell 44 support. -- Transparent dash background for shell theme override (GNOME Shell 4x only). - -## [23.0 Goya] - 2023-02-10 - -### Changed - -- Clock menu offset maximum to 20. -- Fastest animation speed (twice as fast as before). - -### Fixed - -- App menu label visibility compatibility issue with open whether extension. -- Hidden workspace switcher padding in overview. -- Keep the clock menu position when another widget is added to the panel. -- Looking Glass API conflicts with the "Dash to Panel" extension. -- Looking Glass error when primary monitor doesn't exist. -- Window preview gap when window picker close button is disabled. -- Workspace boxes top margin in the app grid when the search entry is disabled. -- Italian translation by [@svityboy](https://gitlab.gnome.org/svityboy). - -### Added - -- More dash icon size (16px, 22px, 24px, 40px and 56px). -- Overview spacing size. -- Screen recording indicator visibility. -- Screen sharing indicator visibility. -- Switcher popup delay by [@koplo199](https://gitlab.gnome.org/koplo199). - -## [22.0 Millet] - 2022-09-10 - -### Fixed - -- Dash app button visibility height. -- Looking glass error after unlock. - -### Added - -- App menu label visibility. -- GNOME Shell 43 support. -- Quick settings menu visibility. - -### Removed - -- Aggregate menu for GNOME Shell 43 and higher. - -## [21.0 Reynolds] - 2022-08-06 - -### Changed - -- Prefs compatibility layer checking to GTK and Adw instead of GNOME Shell version. - -### Fixed - -- Application button visibility in Ubuntu 22.04. -- Prefs window size for scaled displays. -- Prefs window size for small displays in GNOME Shell 42. -- Racy prefs window size. -- Window caption going out of display area when dash is disabled in GNOME Shell 40 and higher. -- Russian translation by [@librusekus35790](https://gitlab.gnome.org/librusekus35790). -- Spanish translation by [@Luci](https://gitlab.gnome.org/Luci). - -### Added - -- Alt-Tab window preview icon size. -- Alt-Tab window preview size. -- Alt-Tab icon size. -- Dash separator visibility. -- Looking glass size by [@AdvendraDeswanta](https://gitlab.gnome.org/AdvendraDeswanta). -- OSD position. -- Take the screenshot button in the window menu visibility. - -### Removed - -- Gesture API for GNOME Shell 40 and higher. -- List box separators for GNOME Shell 40 and 41 (EOS). -- Prefs intro. - -## [20.0 Hayez] - 2022-04-01 - -### Fixed - -- Dynamic workspaces getting disabled by workspace popup. -- Flickering panel after Unlock. -- Notification banner position on GNOME Shell 42. -- Window demands attention focus on GNOME Shell 42. -- French translation by [@GeoffreyCoulaud](https://gitlab.gnome.org/GeoffreyCoulaud). -- Italian translation by [@svityboy](https://gitlab.gnome.org/svityboy). - -### Added - -- Events visibility in the clock menu. -- Calendar visibility in the clock menu. -- Dutch translation by [@Vistaus](https://gitlab.gnome.org/Vistaus). - -## [19.0 Ancher] - 2022-03-02 - -### Fixed - -- Blurry search entry on GNOME Shell themes with "box-shadow". -- Prefs file chooser recursion. -- SecondaryMonitorDisplay error on GNOME Shell 42. -- Shell theme overrides OSD on GNOME Shell 42. -- Shell theme overrides the workspace switcher on GNOME Shell 42. -- Workspace popup visibility in GNOME Shell 42. - -### Added - -- Libadwaita for GNOME Shell 42 prefs. -- Panel icon size. -- Panel world clock visibility. -- Weather visibility. - -## [18.0 Roslin] - 2022-02-12 - -### Fixed - -- GNOME 3.x prefs error. - -## [17.0 Roslin] - 2022-02-11 - -### Fixed - -- Emitting panel show when panel is visible. -- Looking glass not showing up. -- Looking glass position on startup when panel is hidden. -- Prefs height going off the screen in small displays. -- Prefs lunching url freeze on Wayland. -- Prefs padding in GNOME Shell 42. -- Prefs UI Improvement by [@muqtxdir](https://gitlab.gnome.org/muqtxdir). -- Startup animation for hiding panel when panel is disabled. -- Type to search when text entry content is replaced with another content. -- Window goes under panel after unlock on Wayland. -- Window picker caption visibility issue on Pop Shell. -- Galician translation by [@frandieguez](https://gitlab.gnome.org/frandieguez). - -### Added - -- Bottom to notification banner position. - -### Removed - -- Panel corner size option for GNOME Shell 42. - -## [16.0 Rembrandt] - 2021-11-15 - -### Fixed - -- Animation jump when search entry is disabled and entering app grid. -- Clock menu revealing in lockscreen when the position is left or right. -- Startup status for Ubuntu. -- Workspace switcher visibility in GNOME Shell 41. - -### Removed - -- Hot corner for GNOME Shell 41. -- Hot corner library for all supported Shell versions. - -### Added - -- Double supper to app grid for GNOME Shell 40 and 41. -- Panel corner size when panel is disabled. -- Panel visibility in overview when panel is disabled. -- Prefs window intro. -- Profile selector to the prefs window. -- Ripple box. - -## [15.0 Magnetized] - 2021-09-22 - -### Fixed - -- unlock recursion error. - -### Added - -- Hot corner support for GNOME Shell 41. - -## [14.0 Magnetized] - 2021-09-22 - -### Changed - -- Repo folder structure to have better organization. - -### Fixed - -- Bottom panel position for multi monitors by [@harshadgavali](https://gitlab.gnome.org/harshadgavali). -- First swipe up in desktop startup status. -- Looking glass position on bottom panel. -- Maximized window gap on Wayland. -- Search entry animation for type to search when search entry is disabled. -- Search entry API to avoid conflicting with other extensions. -- Window picker caption border on disable. -- Window picker disapearing on wayland with shell theme override. -- Galician translation by [@frandieguez](https://gitlab.gnome.org/frandieguez). -- Spanish translation by [@DiegoIvanME](https://gitlab.gnome.org/DiegoIvanME). - -### Removed - -- Donation popover in prefs. -- Hot corner for GNOME Shell 41. - -### Added - -- GNOME Shell 41 support. -- Panel indicator padding size. -- Window picker close button visibility. - -## [13.0 Ring] - 2021-08-10 - -### Changed - -- Search button position in prefs window. - -### Fixed - -- Accessing dash in case the original dash has been removed by third party extensions. -- API.monitorGetInfo for "pMonitor is null" error. -- Dropdown align in preferences dialog. -- Startup status blocking shortcut keys. -- Unwanted window demands attention focus. -- Russian translation by [@librusekus35790](https://gitlab.gnome.org/librusekus35790). - -### Removed - -- Settings and Translation library and using ExtensionUtils instead. - -### Added - -- Panel button padding size. -- Panel height. -- Window picker caption visibility. -- Workspace background corner size in overview. -- Workspace wraparound (modified version of WorkspaceSwitcherWrapAround by [@war1025](https://github.com/war1025)). - -## [12.0 Queen Red] - 2021-06-29 - -### Changed - -- Lighter background color for switcher list (alt+tab) in override theme. -- Workspace switcher max size now maxed out to 30%. - -### Fixed - -- Combobox scroll issue on GTK4. -- Window demands attention focus notification popup. -- French translation by [@GeoffreyCoulaud](https://gitlab.gnome.org/GeoffreyCoulaud). -- Russian translation by [@librusekus35790](https://gitlab.gnome.org/librusekus35790). - -### Added - -- Always show workspace switcher on dynamic workspaces. -- More descriptions to the preferences dialog. -- Notification banner position. -- Startup status for GNOME Shell 40. -- Workspace animation background color for shell theme override. -- Workspaces visibility in app grid by [@fmuellner](https://gitlab.gnome.org/fmuellner). -- Chinese (Taiwan) translation by [@r0930514](https://gitlab.com/r0930514). - -## [11.0 Whisper] - 2021-05-20 - -### Changed - -- App gesture now only works on GNOME 3.36 and 3.38. -- Donation icon to GTK4 non-compatible icon sets. -- Shell theme override is now disabled by default. -- Workspace switcher size for GNOME Shell 40 is now maxed out to 15%. - -### Fixed - -- Gap when panel position is at the bottom and shell override theme happens. -- Panel menu margin when panel is in bottom. -- Window picker icon visibility on drag. -- Workspace switcher size for multi monitor setup. -- Arabic translation by [@AliGalal](https://gitlab.com/AliGalal). -- Chinese translation by [@wsxy162](https://gitlab.com/wsxy162). -- Italian translation by [@l3nn4rt](https://gitlab.com/l3nn4rt). -- Swedish translation by [@MorganAntonsson](https://gitlab.com/MorganAntonsson). - -### Added - -- Activities button icon. -- Dash icon size. -- Window demands attention focus. - -## [10.0] - 2021-03-26 - -### Changed - -- Organized prefs UI for icons and behavior. -- Removed quotes and side bar image from prefs UI. - -### Fixed - -- Fake hot corner primary monitor position. -- Horizontal scroll in prefs. -- Primary Monitor Panel Position. -- Arabic translation by [@karem34](https://gitlab.com/karem34). -- Russian translation by [@librusekus35790](https://gitlab.com/librusekus35790). - -### Added - -- Clock menu position. -- Disable animation or change the animation speed. -- Disable applications button in dash. -- Disable app menu icon. -- Disable panel arrow in GNOME 3.36 and 3.38. -- Disable panel notification icon. -- No results found for prefs window. -- Brazilian Portuguese translation by [@Zelling](https://gitlab.com/Zelling). -- Catalan translation by [@juxuanu](https://gitlab.com/juxuanu). -- Galician translation by [@frandieguez](https://gitlab.com/frandieguez). - -## [9.0] - 2021-03-06 - -### Changed - -- Prefs interface. - -### Fixed - -- Default value for hot corner on extension disable. -- GNOME Shell 40.beta version. - -### Added - -- Disable power icon. -- Panel position. -- Support to prefs window. - -## [8.0] - 2021-02-22 - -### Changed - -- Holding back lonely overview until the final GNOME 40 release. - -### Fixed - -- Dash override theme on GNOME Shell 40 beta. -- Focus for find entry on prefs. -- Search controller for GNOME Shell 40 beta. -- Start search for GNOME Shell 40 beta. -- Workspace switcher enable related to workspace switcher size. -- Nepali translation filename by [@IBA4](https://gitlab.com/IBA4). - -## [7.0] - 2021-02-12 - -### Fixed - -- GNOME Shell 40 hidden side by side workspace preview. -- GNOME Shell 40 search padding when panel is disabled. -- Initial prefs window size. - -### Added - -- GNOME Shell 40 window picker icon visibility to the settings. -- GNOME Shell 40 workspace switcher size to the settings. -- Panel corner size to the settings. -- Search feature to the settings. -- Type to Search to the settings. -- Nepali translation by [@IBA4](https://gitlab.com/IBA4). -- Spanish translation by [@oscfdezdz](https://gitlab.com/oscfdezdz). - -## [6.0] - 2021-01-29 - -### Fixed - -- GNOME Shell 3.38 extra padding on no workspace switcher. -- GNOME Shell 40 and GTK4 support for prefs. -- GNOME Shell 40 support for search entry. -- GNOME Shell 40 support for workspace switcher. - -## [5.0] - 2021-01-05 - -### Added - -- Accessibility Menu visibility to the settings. -- Activities button visibility to the settings. -- App menu visibility to the settings. -- Clock menu visibility to the settings. -- Keyboard Layout visibility to the settings. -- System Menu (Aggregate Menu) visibility to the settings. - -### Changed - -- OSD in settings to "On Screen Display (OSD)". - -### Fixed - -- Hot corner when top panel is visible. -- Padding on no dash. -- Search top padding on no top panel. - -## [4.0] 2020-12-25 - -### Added - -- API to decouple all libraries from using GNOME Shell ui directly. -- Automate build process by [@daPhipz](https://gitlab.com/daPhipz). -- CHANGELOG.md file. -- Compatibility layer for API. -- Translation automation script by [@daPhipz](https://gitlab.com/daPhipz). - -### Changed - -- Default settings to enable. -- Displaying error for generate-mo.sh by [@daPhipz](https://gitlab.com/daPhipz). -- German translation by [@M4he](https://gitlab.com/M4he). - -### Fixed - -- Top padding on no search and no top panel. - -## [3.0] - 2020-12-21 - -### Added - -- CONTRIBUTING.md file. -- Decoupled library from GNOME Shell ui. -- Extension logo. -- Initial Translations. -- Prefs as extension settings. - -## [2.0] - 2020-11-18 - -### Fixed - -- Destroy hot corner on disable. - - -### Added - -- Disable app gesture. -- Disable background menu. -- Hide dash. -- Hide search. -- Hide top panel. -- Hide workspace switcher. -- Hot corner to toggle overview visibility. - diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/LICENSE b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/LICENSE deleted file mode 100755 index c4dc63a..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/LICENSE +++ /dev/null @@ -1,675 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - Just Perfection GNOME Shell Desktop - Copyright (C) 2020-2024 Javad Rahmatzadeh - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Just Perfection GNOME Shell Desktop - Copyright (C) 2020-2024 Javad Rahmatzadeh - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/extension.js b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/extension.js deleted file mode 100755 index 960017d..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/extension.js +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Extension - * - * @author Javad Rahmatzadeh - * @copyright 2020-2024 - * @license GPL-3.0-only - */ - -import Clutter from 'gi://Clutter'; -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; -import GObject from 'gi://GObject'; -import Meta from 'gi://Meta'; -import St from 'gi://St'; - -import * as AltTab from 'resource:///org/gnome/shell/ui/altTab.js'; -import * as BackgroundMenu from 'resource:///org/gnome/shell/ui/backgroundMenu.js'; -import * as LookingGlass from 'resource:///org/gnome/shell/ui/lookingGlass.js'; -import * as Main from 'resource:///org/gnome/shell/ui/main.js'; -import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js'; -import * as OSDWindow from 'resource:///org/gnome/shell/ui/osdWindow.js'; -import * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js'; -import * as Panel from 'resource:///org/gnome/shell/ui/panel.js'; -import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; -import * as Search from 'resource:///org/gnome/shell/ui/search.js'; -import * as SearchController from 'resource:///org/gnome/shell/ui/searchController.js'; -import * as SwitcherPopup from 'resource:///org/gnome/shell/ui/switcherPopup.js'; -import * as WindowMenu from 'resource:///org/gnome/shell/ui/windowMenu.js'; -import * as WindowPreview from 'resource:///org/gnome/shell/ui/windowPreview.js'; -import * as Workspace from 'resource:///org/gnome/shell/ui/workspace.js'; -import * as WorkspacesView from 'resource:///org/gnome/shell/ui/workspacesView.js'; -import * as WorkspaceSwitcherPopup from 'resource:///org/gnome/shell/ui/workspaceSwitcherPopup.js'; -import * as WorkspaceThumbnail from 'resource:///org/gnome/shell/ui/workspaceThumbnail.js'; - -import * as Config from 'resource:///org/gnome/shell/misc/config.js'; -import * as Util from 'resource:///org/gnome/shell/misc/util.js'; - -import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; - -import {API} from './lib/API.js'; -import {Manager} from './lib/Manager.js'; - -/** - * Extension entry point - */ -export default class JustPerfection extends Extension -{ - /** - * Instance of API - * - * @type {API|null} - */ - #api = null; - - /** - * Instance of Manager - * - * @type {Manager|null} - */ - #manager = null; - - /** - * Enable extension - * - * @returns {void} - */ - enable() - { - const shellVersion = parseFloat(Config.PACKAGE_VERSION); - - let InterfaceSettings = new Gio.Settings({schema_id: 'org.gnome.desktop.interface'}); - - this.#api = new API( - { - Main, - BackgroundMenu, - OverviewControls, - WorkspaceSwitcherPopup, - SwitcherPopup, - InterfaceSettings, - Search, - SearchController, - WorkspaceThumbnail, - WorkspacesView, - Panel, - PanelMenu, - WindowPreview, - Workspace, - LookingGlass, - MessageTray, - OSDWindow, - WindowMenu, - AltTab, - St, - GLib, - Clutter, - Util, - Meta, - GObject, - }, - shellVersion - ); - - this.#api.open(); - - let settings = this.getSettings(); - - this.#manager = new Manager( - { - API: this.#api, - Settings: settings, - }, - shellVersion - ); - - this.#manager.registerSettingsSignals(); - this.#manager.applyAll(); - } - - /** - * Disable extension - * - * @returns {void} - */ - disable() - { - this.#manager?.revertAll(); - this.#manager = null; - - this.#api?.close(); - this.#api = null; - } -} - diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/API.js b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/API.js deleted file mode 100755 index 707914a..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/API.js +++ /dev/null @@ -1,3226 +0,0 @@ -/** - * API Library - * - * @author Javad Rahmatzadeh - * @copyright 2020-2024 - * @license GPL-3.0-only - */ - -const XY_POSITION = { - TOP_START: 0, - TOP_CENTER: 1, - TOP_END: 2, - BOTTOM_START: 3, - BOTTOM_CENTER: 4, - BOTTOM_END: 5, - CENTER_START: 6, - CENTER_CENTER: 7, - CENTER_END: 8, -}; - -const PANEL_POSITION = { - TOP: 0, - BOTTOM: 1, -}; - -const PANEL_BOX_POSITION = { - CENTER: 0, - RIGHT: 1, - LEFT: 2, -}; - -const PANEL_HIDE_MODE = { - ALL: 0, - DESKTOP: 1, -}; - -const SHELL_STATUS = { - NONE: 0, - OVERVIEW: 1, -}; - -const DASH_ICON_SIZES = [16, 22, 24, 32, 40, 48, 56, 64]; - -/** - * API to avoid calling GNOME Shell directly - * and make all parts compatible with different GNOME Shell versions - */ -export class API -{ - /** - * Current shell version - * - * @type {number|null} - */ - #shellVersion = null; - - /** - * Originals holder - * - * @type {object} - */ - #originals = {}; - - /** - * Timeout ids - * - * @type {object} - */ - #timeoutIds = {}; - - /** - * Class Constructor - * - * @param {Object} dependencies - * 'Main' reference to ui::main - * 'BackgroundMenu' reference to ui::backgroundMenu - * 'OverviewControls' reference to ui::overviewControls - * 'WorkspaceSwitcherPopup' reference to ui::workspaceSwitcherPopup - * 'SwitcherPopup' reference to ui::switcherPopup - * 'InterfaceSettings' reference to Gio::Settings for 'org.gnome.desktop.interface' - * 'Search' reference to ui::search - * 'SearchController' reference to ui::searchController - * 'WorkspaceThumbnail' reference to ui::workspaceThumbnail - * 'WorkspacesView' reference to ui::workspacesView - * 'Panel' reference to ui::panel - * 'PanelMenu' reference to ui::panelMenu - * 'WindowPreview' reference to ui::windowPreview - * 'Workspace' reference to ui::workspace - * 'LookingGlass' reference to ui::lookingGlass - * 'MessageTray' reference to ui::messageTray - * 'OSDWindow' reference to ui::osdTray - * 'WindowMenu' reference to ui::windowMenu - * 'AltTab' reference to ui::altTab - * 'St' reference to St - * 'GLib' reference to GLib - * 'Clutter' reference to Clutter - * 'Util' reference to misc::util - * 'Meta' reference to Meta - * 'GObject' reference to GObject - * @param {number} shellVersion float in major.minor format - */ - constructor(dependencies, shellVersion) - { - this._main = dependencies['Main'] || null; - this._backgroundMenu = dependencies['BackgroundMenu'] || null; - this._overviewControls = dependencies['OverviewControls'] || null; - this._workspaceSwitcherPopup = dependencies['WorkspaceSwitcherPopup'] || null; - this._switcherPopup = dependencies['SwitcherPopup'] || null; - this._interfaceSettings = dependencies['InterfaceSettings'] || null; - this._search = dependencies['Search'] || null; - this._searchController = dependencies['SearchController'] || null; - this._workspaceThumbnail = dependencies['WorkspaceThumbnail'] || null; - this._workspacesView = dependencies['WorkspacesView'] || null; - this._panel = dependencies['Panel'] || null; - this._panelMenu = dependencies['PanelMenu'] || null; - this._windowPreview = dependencies['WindowPreview'] || null; - this._workspace = dependencies['Workspace'] || null; - this._lookingGlass = dependencies['LookingGlass'] || null; - this._messageTray = dependencies['MessageTray'] || null; - this._osdWindow = dependencies['OSDWindow'] || null; - this._windowMenu = dependencies['WindowMenu'] || null; - this._altTab = dependencies['AltTab'] || null; - this._st = dependencies['St'] || null; - this._glib = dependencies['GLib'] || null; - this._clutter = dependencies['Clutter'] || null; - this._util = dependencies['Util'] || null; - this._meta = dependencies['Meta'] || null; - this._gobject = dependencies['GObject'] || null; - - this.#shellVersion = shellVersion; - - /** - * whether search entry is visible - * - * @member {boolean} - */ - this._searchEntryVisibility = true; - } - - /** - * prepare everything needed for API - * - * @returns {void} - */ - open() - { - this.UIStyleClassAdd(this.#getAPIClassname('shell-version')); - } - - /** - * remove everything from GNOME Shell been added by this class - * - * @returns {void} - */ - close() - { - this.UIStyleClassRemove(this.#getAPIClassname('shell-version')); - this.#startSearchSignal(false); - this.#computeWorkspacesBoxForStateSetDefault(); - this.#altTabSizesSetDefault(); - - for (let [name, id] of Object.entries(this.#timeoutIds)) { - this._glib.source_remove(id); - delete(this.#timeoutIds[name]); - } - } - - /** - * get x and y align for position - * - * @param int pos position - * see XY_POSITION - * - * @returns {array} - * - 0 Clutter.ActorAlign - * - 1 Clutter.ActorAlign - */ - #xyAlignGet(pos) - { - if (XY_POSITION.TOP_START === pos) { - return [this._clutter.ActorAlign.START, this._clutter.ActorAlign.START]; - } - - if (XY_POSITION.TOP_CENTER === pos) { - return [this._clutter.ActorAlign.CENTER, this._clutter.ActorAlign.START]; - } - - if (XY_POSITION.TOP_END === pos) { - return [this._clutter.ActorAlign.END, this._clutter.ActorAlign.START]; - } - - if (XY_POSITION.CENTER_START === pos) { - return [this._clutter.ActorAlign.START, this._clutter.ActorAlign.CENTER]; - } - - if (XY_POSITION.CENTER_CENTER === pos) { - return [this._clutter.ActorAlign.CENTER, this._clutter.ActorAlign.CENTER]; - } - - if (XY_POSITION.CENTER_END === pos) { - return [this._clutter.ActorAlign.END, this._clutter.ActorAlign.CENTER]; - } - - if (XY_POSITION.BOTTOM_START === pos) { - return [this._clutter.ActorAlign.START, this._clutter.ActorAlign.END]; - } - - if (XY_POSITION.BOTTOM_CENTER === pos) { - return [this._clutter.ActorAlign.CENTER, this._clutter.ActorAlign.END]; - } - - if (XY_POSITION.BOTTOM_END === pos) { - return [this._clutter.ActorAlign.END, this._clutter.ActorAlign.END]; - } - } - - /** - * add to animation duration - * - * @param {number} duration in milliseconds - * - * @returns {number} - */ - #addToAnimationDuration(duration) - { - let settings = this._st.Settings.get(); - - return (settings.enable_animations) ? settings.slow_down_factor * duration : 1; - } - - /** - * get signal id of the event - * - * @param {Gtk.Widget} widget to find signal in - * @param {string} signalName signal name - * - * @returns {number} - */ - #getSignalId(widget, signalName) - { - return this._gobject.signal_handler_find(widget, {signalId: signalName}); - } - - /** - * get the css class name for API - * - * @param {string} type - * - * @returns {string} - */ - #getAPIClassname(type) - { - let starter = 'just-perfection-api-'; - - if (type === 'shell-version') { - let shellVerMajor = Math.trunc(this.#shellVersion); - return `${starter}gnome${shellVerMajor}`; - } - - return `${starter}${type}`; - } - - /** - * set panel size to default - * - * @returns {void} - */ - panelSetDefaultSize() - { - if (!this.#originals['panelHeight']) { - return; - } - - this.panelSetSize(this.#originals['panelHeight'], false); - } - - /** - * change panel size - * - * @param {number} size 0 to 100 - * @param {boolean} fake true means it shouldn't change the last size, - * false otherwise - * - * @returns {void} - */ - panelSetSize(size, fake) - { - if (!this.#originals['panelHeight']) { - this.#originals['panelHeight'] = this._main.panel.height; - } - - if (size > 100 || size < 0) { - return; - } - - this._main.panel.height = size; - - if (!fake) { - this._panelSize = size; - } - } - - /** - * get the last size of the panel - * - * @returns {number} - */ - panelGetSize() - { - if (this._panelSize !== undefined) { - return this._panelSize; - } - - if (this.#originals['panelHeight']) { - return this.#originals['panelHeight']; - } - - return this._main.panel.height; - } - - /** - * emit refresh styles - * this is useful when changed style doesn't emit change because doesn't have - * standard styles. for example, style with only `-natural-hpadding` - * won't notify any change. so you need to call this function - * to refresh that - * - * @returns {void} - */ - #emitRefreshStyles() - { - let classname = this.#getAPIClassname('refresh-styles'); - - this.UIStyleClassAdd(classname); - this.UIStyleClassRemove(classname); - } - - /** - * show panel - * - * @returns {void} - */ - panelShow() - { - this._panelVisibility = true; - - let classname = this.#getAPIClassname('no-panel'); - - if (!this.UIStyleClassContain(classname)) { - return; - } - - // The class name should be removed before addChrome the panelBox - // removing after can cause `st_theme_node_lookup_shadow` crash - this.UIStyleClassRemove(classname); - - let overview = this._main.overview; - let searchEntryParent = overview.searchEntry.get_parent(); - let panelBox = this._main.layoutManager.panelBox; - - panelBox.translation_y = 0; - - this._main.layoutManager.overviewGroup.remove_child(panelBox); - this._main.layoutManager.addChrome(panelBox, { - affectsStruts: true, - trackFullscreen: true, - }); - - if (this._hidePanelWorkareasChangedSignal) { - global.display.disconnect(this._hidePanelWorkareasChangedSignal); - delete(this._hidePanelWorkareasChangedSignal); - } - - if (this._hidePanelHeightSignal) { - panelBox.disconnect(this._hidePanelHeightSignal); - delete(this._hidePanelHeightSignal); - } - - searchEntryParent.set_style(`margin-top: 0;`); - - // hide and show can fix windows going under panel - panelBox.hide(); - panelBox.show(); - this.#fixLookingGlassPosition(); - - if (this.#timeoutIds.panelHide) { - this._glib.source_remove(this.#timeoutIds.panelHide); - delete(this.#timeoutIds.panelHide); - } - } - - /** - * hide panel - * - * @param {mode} hide mode see PANEL_HIDE_MODE. defaults to hide all - * @param {boolean} force apply hide even if it is hidden - * - * @returns {void} - */ - panelHide(mode) - { - this._panelVisibility = false; - this._panelHideMode = mode; - - let overview = this._main.overview; - let searchEntryParent = overview.searchEntry.get_parent(); - let panelBox = this._main.layoutManager.panelBox; - let panelHeight = this._main.panel.height; - let panelPosition = this.panelGetPosition(); - let direction = (panelPosition === PANEL_POSITION.BOTTOM) ? 1 : -1; - - if (panelBox.get_parent() === this._main.layoutManager.uiGroup) { - this._main.layoutManager.removeChrome(panelBox); - this._main.layoutManager.overviewGroup.insert_child_at_index(panelBox, 0); - } - - panelBox.translation_y = (mode === PANEL_HIDE_MODE.DESKTOP) ? 0 : panelHeight * direction; - - if (panelPosition === PANEL_POSITION.TOP) { - // when panel is hidden the first element gets too close to the top, - // so we fix it with top margin in search entry - let marginTop = (mode === PANEL_HIDE_MODE.ALL) ? 15 : panelHeight; - searchEntryParent.set_style(`margin-top: ${marginTop}px;`); - } else { - searchEntryParent.set_style(`margin-top: 0;`); - } - - // hide and show can fix windows going under panel - panelBox.hide(); - panelBox.show(); - this.#fixLookingGlassPosition(); - - if (this._hidePanelWorkareasChangedSignal) { - global.display.disconnect(this._hidePanelWorkareasChangedSignal); - delete(this._hidePanelWorkareasChangedSignal); - } - - this._hidePanelWorkareasChangedSignal = global.display.connect( - 'workareas-changed', - () => { - this.panelHide(this._panelHideMode); - } - ); - - if (!this._hidePanelHeightSignal) { - this._hidePanelHeightSignal = panelBox.connect( - 'notify::height', - () => { - this.panelHide(this._panelHideMode); - } - ); - } - - let classname = this.#getAPIClassname('no-panel'); - this.UIStyleClassAdd(classname); - - // update hot corners since we need to make them available - // outside overview - this._main.layoutManager._updateHotCorners(); - - // Maximized windows will have bad maximized gap after unlock in Wayland - // This is a Mutter issue, - // See https://gitlab.gnome.org/GNOME/mutter/-/issues/1627 - // TODO remove after the issue is fixed on Mutter - if (this._meta.is_wayland_compositor()) { - let duration = this.#addToAnimationDuration(180); - this.#timeoutIds.panelHide = this._glib.timeout_add( - this._glib.PRIORITY_DEFAULT, - duration, - () => { - panelBox.hide(); - panelBox.show(); - return this._glib.SOURCE_REMOVE; - } - ); - } - } - - /** - * check whether panel is visible - * - * @returns {boolean} - */ - isPanelVisible() - { - if (this._panelVisibility === undefined) { - return true; - } - - return this._panelVisibility; - } - - /** - * check whether dash is visible - * - * @returns {boolean} - */ - isDashVisible() - { - return this._dashVisibility === undefined || this._dashVisibility; - } - - /** - * show dash - * - * @returns {void} - */ - dashShow() - { - if (!this._main.overview.dash || this.isDashVisible()) { - return; - } - - this._dashVisibility = true; - - this._main.overview.dash.show(); - - this._main.overview.dash.height = -1; - this._main.overview.dash.setMaxSize(-1, -1); - - this.#updateWindowPreviewOverlap(); - } - - /** - * hide dash - * - * @returns {void} - */ - dashHide() - { - if (!this._main.overview.dash || !this.isDashVisible()) { - return; - } - - this._dashVisibility = false; - - this._main.overview.dash.hide(); - - this._main.overview.dash.height = 0; - - this.#updateWindowPreviewOverlap(); - } - - /** - * update window preview overlap - * - * @returns {void} - */ - #updateWindowPreviewOverlap() - { - let wpp = this._windowPreview.WindowPreview.prototype; - - if (this.isDashVisible() && wpp.overlapHeightsOld) { - wpp.overlapHeights = wpp.overlapHeightsOld; - delete(wpp.overlapHeightsOld); - return; - } - - if (!this.isDashVisible()) { - wpp.overlapHeightsOld = wpp.overlapHeights; - wpp.overlapHeights = function () { - let [top, bottom] = this.overlapHeightsOld(); - return [top + 24, bottom + 24]; - }; - } - } - - /** - * add class name to the UI group - * - * @param {string} classname class name - * - * @returns {void} - */ - UIStyleClassAdd(classname) - { - this._main.layoutManager.uiGroup.add_style_class_name(classname); - } - - /** - * remove class name from UI group - * - * @param {string} classname class name - * - * @returns {void} - */ - UIStyleClassRemove(classname) - { - this._main.layoutManager.uiGroup.remove_style_class_name(classname); - } - - /** - * check whether UI group has class name - * - * @param {string} classname class name - * - * @returns {boolean} - */ - UIStyleClassContain(classname) - { - return this._main.layoutManager.uiGroup.has_style_class_name(classname); - } - - /** - * enable background menu - * - * @returns {void} - */ - backgroundMenuEnable() - { - if (!this.#originals['backgroundMenuOpen']) { - return; - } - - this._backgroundMenu.BackgroundMenu.prototype.open = this.#originals['backgroundMenuOpen']; - } - - /** - * disable background menu - * - * @returns {void} - */ - backgroundMenuDisable() - { - let backgroundMenuProto = this._backgroundMenu.BackgroundMenu.prototype; - - if (!this.#originals['backgroundMenuOpen']) { - this.#originals['backgroundMenuOpen'] = backgroundMenuProto.open; - } - - backgroundMenuProto.open = () => {}; - } - - /** - * show search - * - * @param {boolean} fake true means it just needs to do the job but - * don't need to change the search visibility status - * - * @returns {void} - */ - searchEntryShow(fake) - { - let classname = this.#getAPIClassname('no-search'); - - if (!this.UIStyleClassContain(classname)) { - return; - } - - this.UIStyleClassRemove(classname); - - let searchEntry = this._main.overview.searchEntry; - let searchEntryParent = searchEntry.get_parent(); - - searchEntryParent.ease({ - height: searchEntry.height, - opacity: 255, - mode: this._clutter.AnimationMode.EASE, - duration: 110, - onComplete: () => { - searchEntryParent.height = -1; - searchEntry.ease({ - opacity: 255, - mode: this._clutter.AnimationMode.EASE, - duration: 700, - }); - }, - }); - - if (!fake) { - this._searchEntryVisibility = true; - } - - this.#computeWorkspacesBoxForStateChanged(); - } - - /** - * hide search - * - * @param {boolean} fake true means it just needs to do the job - * but don't need to change the search visibility status - * - * @returns {void} - */ - searchEntryHide(fake) - { - this.UIStyleClassAdd(this.#getAPIClassname('no-search')); - - let searchEntry = this._main.overview.searchEntry; - let searchEntryParent = searchEntry.get_parent(); - - searchEntry.ease({ - opacity: 0, - mode: this._clutter.AnimationMode.EASE, - duration: 50, - }); - - searchEntryParent.ease({ - height: 0, - opacity: 0, - mode: this._clutter.AnimationMode.EASE, - duration: 120, - }); - - if (!fake) { - this._searchEntryVisibility = false; - } - - this.#computeWorkspacesBoxForStateChanged(); - } - - /** - * enable start search - * - * @returns {void} - */ - startSearchEnable() - { - this.#startSearchSignal(true); - - if (!this.#originals['startSearch']) { - return; - } - - this._searchController.SearchController.prototype.startSearch = this.#originals['startSearch']; - } - - /** - * disable start search - * - * @returns {void} - */ - startSearchDisable() - { - this.#startSearchSignal(false); - - if (!this.#originals['startSearch']) { - this.#originals['startSearch'] = this._searchController.SearchController.prototype.startSearch - } - - this._searchController.SearchController.prototype.startSearch = () => {}; - } - - /** - * add search signals that needs to be show search entry when the - * search entry is hidden - * - * @param {boolean} add true means add the signal, false means remove - * the signal - * - * @returns {void} - */ - #startSearchSignal(add) - { - let controller - = this._main.overview.viewSelector || - this._main.overview._overview.viewSelector || - this._main.overview._overview.controls._searchController; - - // remove - if (!add) { - if (this._searchActiveSignal) { - controller.disconnect(this._searchActiveSignal); - this._searchActiveSignal = null; - } - return; - } - - // add - if (this._searchActiveSignal) { - return; - } - - this._searchActiveSignal = controller.connect('notify::search-active', () => { - if (this._searchEntryVisibility) { - return; - } - - let inSearch = controller.searchActive; - - if (inSearch) { - this.UIStyleClassAdd(this.#getAPIClassname('type-to-search')); - this.searchEntryShow(true); - } else { - this.UIStyleClassRemove(this.#getAPIClassname('type-to-search')); - this.searchEntryHide(true); - } - }); - } - - /** - * Set maximum displayed search result to default value - * - * @returns {void} - */ - setMaxDisplayedSearchResultToDefault() - { - if (!this.#originals['searchGetMaxDisplayedResults']) { - return; - } - - let ListSearchResultsProto = this._search.ListSearchResults.prototype; - - ListSearchResultsProto._getMaxDisplayedResults = this.#originals['searchGetMaxDisplayedResults']; - } - - /** - * Set maximum displayed search result - * - * @param {number} items max items - * - * @returns {void} - */ - setMaxDisplayedSearchResult(items) - { - let ListSearchResultsProto = this._search.ListSearchResults.prototype; - - if (!this.#originals['searchGetMaxDisplayedResults']) { - this.#originals['searchGetMaxDisplayedResults'] = ListSearchResultsProto._getMaxDisplayedResults; - } - - ListSearchResultsProto._getMaxDisplayedResults = () => { - return items; - } - } - - /** - * enable OSD - * - * @returns {void} - */ - OSDEnable() - { - if (!this.#originals['osdWindowManagerShow']) { - return; - } - - this._main.osdWindowManager.show = this.#originals['osdWindowManagerShow']; - } - - /** - * disable OSD - * - * @returns {void} - */ - OSDDisable() - { - if (!this.#originals['osdWindowManagerShow']) { - this.#originals['osdWindowManagerShow'] - = this._main.osdWindowManager.show; - } - - this._main.osdWindowManager.show = () => {}; - } - - /** - * enable workspace popup - * - * @returns {void} - */ - workspacePopupEnable() - { - if (!this.#originals['workspaceSwitcherPopupDisplay']) { - return; - } - - this._workspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype.display - = this.#originals['workspaceSwitcherPopupDisplay'] - } - - /** - * disable workspace popup - * - * @returns {void} - */ - workspacePopupDisable() - { - if (!this.#originals['workspaceSwitcherPopupDisplay']) { - this.#originals['workspaceSwitcherPopupDisplay'] - = this._workspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype.display; - } - - this._workspaceSwitcherPopup.WorkspaceSwitcherPopup.prototype.display = function (index) { - this.destroy(); - }; - } - - /** - * show workspace switcher - * - * @returns {void} - */ - workspaceSwitcherShow() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-workspace')); - - this.#workspaceSwitcherShouldShowSetToLast(); - } - - /** - * hide workspace switcher - * - * @returns {void} - */ - workspaceSwitcherHide() - { - this.workspaceSwitcherShouldShow(false, true); - - // should be after `this.workspaceSwitcherShouldShow()` - // since it checks whether it's visible or not - this.UIStyleClassAdd(this.#getAPIClassname('no-workspace')); - } - - /** - * check whether workspace switcher is visible - * - * @returns {boolean} - */ - isWorkspaceSwitcherVisible() - { - return !this.UIStyleClassContain(this.#getAPIClassname('no-workspace')); - } - - /** - * set workspace switcher to its default size - * - * @returns {void} - */ - workspaceSwitcherSetDefaultSize() - { - let thumbnailsBox = this._main.overview._overview._controls._thumbnailsBox; - let ThumbnailsBoxProto = this._workspaceThumbnail.ThumbnailsBox.prototype; - - if (!ThumbnailsBoxProto._initOld) { - return; - } - - ThumbnailsBoxProto._init = ThumbnailsBoxProto._initOld; - delete(ThumbnailsBoxProto._initOld); - - thumbnailsBox._maxThumbnailScale = this._workspaceThumbnail.MAX_THUMBNAIL_SCALE; - } - - /** - * set workspace switcher size - * - * @param {number} size in float - * - * @returns {void} - */ - workspaceSwitcherSetSize(size) - { - let thumbnailsBox = this._main.overview._overview._controls._thumbnailsBox; - let ThumbnailsBoxProto = this._workspaceThumbnail.ThumbnailsBox.prototype; - - thumbnailsBox._maxThumbnailScale = size; - - if (!ThumbnailsBoxProto._initOld) { - ThumbnailsBoxProto._initOld = ThumbnailsBoxProto._init; - } - - ThumbnailsBoxProto._init = function(...params) { - this._maxThumbnailScale = size; - this._initOld(...params); - }; - } - - /** - * add element to stage - * - * @param {St.Widget} element widget - * - * @returns {void} - */ - chromeAdd(element) - { - this._main.layoutManager.addChrome(element, { - affectsInputRegion : true, - affectsStruts : false, - trackFullscreen : true, - }); - } - - /** - * remove element from stage - * - * @param {St.Widget} element widget - * - * @returns {void} - */ - chromeRemove(element) - { - this._main.layoutManager.removeChrome(element); - } - - /** - * show activities button - * - * @returns {void} - */ - activitiesButtonShow() - { - let activities = this._main.panel.statusArea.activities; - - if (!this.isLocked() && activities) { - activities.container.show(); - } - } - - /** - * hide activities button - * - * @returns {void} - */ - activitiesButtonHide() - { - let activities = this._main.panel.statusArea.activities; - - if (activities) { - activities.container.hide(); - } - } - - /** - * show date menu - * - * @returns {void} - */ - dateMenuShow() - { - if (!this.isLocked()) { - this._main.panel.statusArea.dateMenu.container.show(); - } - } - - /** - * hide date menu - * - * @returns {void} - */ - dateMenuHide() - { - this._main.panel.statusArea.dateMenu.container.hide(); - } - - /** - * show keyboard layout - * - * @returns {void} - */ - keyboardLayoutShow() - { - this._main.panel.statusArea.keyboard.container.show(); - } - - /** - * hide keyboard layout - * - * @returns {void} - */ - keyboardLayoutHide() - { - this._main.panel.statusArea.keyboard.container.hide(); - } - - /** - * show accessibility menu - * - * @returns {void} - */ - accessibilityMenuShow() - { - this._main.panel.statusArea.a11y?.container.show(); - } - - /** - * hide accessibility menu - * - * @returns {void} - */ - accessibilityMenuHide() - { - this._main.panel.statusArea.a11y?.container.hide(); - } - - /** - * show quick settings menu - * - * @returns {void} - */ - quickSettingsMenuShow() - { - this._main.panel.statusArea.quickSettings.container.show(); - } - - /** - * hide quick settings menu - * - * @returns {void} - */ - quickSettingsMenuHide() - { - this._main.panel.statusArea.quickSettings.container.hide(); - } - - /** - * check whether lock dialog is currently showing - * - * @returns {boolean} - */ - isLocked() - { - return this._main.sessionMode.isLocked; - } - - /** - * enable window picker icon - * - * @returns {void} - */ - windowPickerIconEnable() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-window-picker-icon')); - } - - /** - * disable window picker icon - * - * @returns {void} - */ - windowPickerIconDisable() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-window-picker-icon')); - } - - /** - * show power icon - * - * @returns {void} - */ - powerIconShow() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-power-icon')); - } - - /** - * hide power icon - * - * @returns {void} - */ - powerIconHide() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-power-icon')); - } - - /** - * get primary monitor information - * - * @returns {false|Object} false when monitor does not exist | object - * x: int - * y: int - * width: int - * height: int - * geometryScale: float - */ - monitorGetInfo() - { - let pMonitor = this._main.layoutManager.primaryMonitor; - - if (!pMonitor) { - return false; - } - - return { - 'x': pMonitor.x, - 'y': pMonitor.y, - 'width': pMonitor.width, - 'height': pMonitor.height, - 'geometryScale': pMonitor.geometry_scale, - }; - } - - /** - * get panel position - * - * @returns {number} see PANEL_POSITION - */ - panelGetPosition() - { - if (this._panelPosition === undefined) { - return PANEL_POSITION.TOP; - } - - return this._panelPosition; - } - - /** - * move panel position - * - * @param {number} position see PANEL_POSITION - * @param {boolean} force allow to set even when the current position - * is the same - * - * @returns {void} - */ - panelSetPosition(position, force = false) - { - let monitorInfo = this.monitorGetInfo(); - let panelBox = this._main.layoutManager.panelBox; - - if (!force && position === this.panelGetPosition()) { - return; - } - - if (position === PANEL_POSITION.TOP) { - this._panelPosition = PANEL_POSITION.TOP; - if (this._workareasChangedSignal) { - global.display.disconnect(this._workareasChangedSignal); - this._workareasChangedSignal = null; - } - if (this._panelHeightSignal) { - panelBox.disconnect(this._panelHeightSignal); - this._panelHeightSignal = null; - } - let topX = (monitorInfo) ? monitorInfo.x : 0; - let topY = (monitorInfo) ? monitorInfo.y : 0; - panelBox.set_position(topX, topY); - this.UIStyleClassRemove(this.#getAPIClassname('bottom-panel')); - this.#fixPanelMenuSide(this._st.Side.TOP); - this.#fixLookingGlassPosition(); - return; - } - - this._panelPosition = PANEL_POSITION.BOTTOM; - - // only change it when a monitor detected - // 'workareas-changed' signal will do the job on next monitor detection - if (monitorInfo) { - let BottomX = monitorInfo.x; - let BottomY = monitorInfo.y + monitorInfo.height - this.panelGetSize(); - - panelBox.set_position(BottomX, BottomY); - this.UIStyleClassAdd(this.#getAPIClassname('bottom-panel')); - } - - if (!this._workareasChangedSignal) { - this._workareasChangedSignal - = global.display.connect('workareas-changed', () => { - this.panelSetPosition(PANEL_POSITION.BOTTOM, true); - }); - } - - if (!this._panelHeightSignal) { - this._panelHeightSignal = panelBox.connect('notify::height', () => { - this.panelSetPosition(PANEL_POSITION.BOTTOM, true); - }); - } - - this.#fixPanelMenuSide(this._st.Side.BOTTOM); - this.#fixLookingGlassPosition(); - } - - /** - * fix panel menu opening side based on panel position - * - * @param {number} position St.Side value - * is the same - * - * @returns {void} - */ - #fixPanelMenuSide(position) - { - let PanelMenuButton = this._panelMenu.Button; - let PanelMenuButtonProto = PanelMenuButton.prototype; - - // Set Instances - let findPanelMenus = (widget) => { - if (widget instanceof PanelMenuButton && widget.menu?._boxPointer) { - widget.menu._boxPointer._userArrowSide = position; - } - widget.get_children().forEach(subWidget => { - findPanelMenus(subWidget) - }); - } - - let panelBoxes = [ - this._main.panel._centerBox, - this._main.panel._rightBox, - this._main.panel._leftBox, - ]; - panelBoxes.forEach(panelBox => findPanelMenus(panelBox)); - - // Set Prototypes - if (position === this._st.Side.TOP) { - // reset to default since GNOME Shell panel is top by default - if (PanelMenuButtonProto._setMenuOld) { - PanelMenuButtonProto.setMenu = PanelMenuButtonProto._setMenuOld; - } - return; - } - - if (!PanelMenuButtonProto._setMenuOld) { - PanelMenuButtonProto._setMenuOld = PanelMenuButtonProto.setMenu; - } - - PanelMenuButtonProto.setMenu = function (menu) { - this._setMenuOld(menu); - if (menu) { - menu._boxPointer._userArrowSide = position; - } - } - } - - /** - * fix looking glass position - * - * @returns {void} - */ - #fixLookingGlassPosition() - { - let lookingGlassProto = this._lookingGlass.LookingGlass.prototype; - - if (this.#originals['lookingGlassResize'] === undefined) { - this.#originals['lookingGlassResize'] = lookingGlassProto._resize; - } - - if (this.panelGetPosition() === PANEL_POSITION.TOP && this.isPanelVisible()) { - - lookingGlassProto._resize = this.#originals['lookingGlassResize']; - delete(lookingGlassProto._oldResizeMethod); - delete(this.#originals['lookingGlassResize']); - - return; - } - - if (lookingGlassProto._oldResizeMethod === undefined) { - lookingGlassProto._oldResizeMethod = this.#originals['lookingGlassResize']; - - const Main = this._main; - - lookingGlassProto._resize = function () { - let panelHeight = Main.layoutManager.panelBox.height; - this._oldResizeMethod(); - this._targetY -= panelHeight; - this._hiddenY -= panelHeight; - }; - } - } - - /** - * enable panel notification icon - * - * @returns {void} - */ - panelNotificationIconEnable() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-panel-notification-icon')); - } - - /** - * disable panel notification icon - * - * @returns {void} - */ - panelNotificationIconDisable() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-panel-notification-icon')); - } - - /** - * disconnect all clock menu position signals - * - * @returns {void} - */ - #disconnectClockMenuPositionSignals() - { - let panelBoxes = [ - this._main.panel._centerBox, - this._main.panel._rightBox, - this._main.panel._leftBox, - ]; - - if (this._clockMenuPositionSignals) { - for (let i = 0; i <= 2; i++) { - panelBoxes[i].disconnect(this._clockMenuPositionSignals[i]); - } - delete(this._clockMenuPositionSignals); - } - } - - /** - * set the clock menu position to default - * - * @returns {void} - */ - clockMenuPositionSetDefault() - { - this.clockMenuPositionSet(0, 0); - this.#disconnectClockMenuPositionSignals(); - } - - /** - * set the clock menu position - * - * @param {number} pos see PANEL_BOX_POSITION - * @param {number} offset starts from 0 - * - * @returns {void} - */ - clockMenuPositionSet(pos, offset) - { - let dateMenu = this._main.panel.statusArea.dateMenu; - - let panelBoxes = [ - this._main.panel._centerBox, - this._main.panel._rightBox, - this._main.panel._leftBox, - ]; - - this.#disconnectClockMenuPositionSignals(); - - let fromPos = -1; - let fromIndex = -1; - let toIndex = -1; - let childLength = 0; - for (let i = 0; i <= 2; i++) { - let child = panelBoxes[i].get_children(); - let childIndex = child.indexOf(dateMenu.container); - if (childIndex !== -1) { - fromPos = i; - fromIndex = childIndex; - childLength = panelBoxes[pos].get_children().length; - toIndex = (offset > childLength) ? childLength : offset; - break; - } - } - - // couldn't find the from and to position because it has been removed - if (fromPos === -1 || fromIndex === -1 || toIndex === -1) { - return; - } - - if (pos === fromPos && toIndex === fromIndex) { - return; - } - - panelBoxes[fromPos].remove_child(dateMenu.container); - panelBoxes[pos].insert_child_at_index(dateMenu.container, toIndex); - - if (this.isLocked()) { - this.dateMenuHide(); - } - - if (!this._clockMenuPositionSignals) { - this._clockMenuPositionSignals = [null, null, null]; - for (let i = 0; i <= 2; i++) { - this._clockMenuPositionSignals[i] = panelBoxes[i].connect( - (this.#shellVersion >= 46) ? 'child-added' : 'actor-added', - () => { - this.clockMenuPositionSet(pos, offset); - } - ); - } - } - } - - /** - * enable show apps button - * - * @returns {void} - */ - showAppsButtonEnable() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-show-apps-button')); - } - - /** - * disable show apps button - * - * @returns {void} - */ - showAppsButtonDisable() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-show-apps-button')); - } - - /** - * set animation speed as default - * - * @returns {void} - */ - animationSpeedSetDefault() - { - if (this.#originals['StSlowDownFactor'] === undefined) { - return; - } - - this._st.Settings.get().slow_down_factor = this.#originals['StSlowDownFactor']; - } - - /** - * change animation speed - * - * @param {number} factor in float. bigger number means slower - * - * @returns {void} - */ - animationSpeedSet(factor) - { - if (this.#originals['StSlowDownFactor'] === undefined) { - this.#originals['StSlowDownFactor'] - = this._st.Settings.get().slow_down_factor; - } - - this._st.Settings.get().slow_down_factor = factor; - } - - /** - * set the enable animation as default - * - * @returns {void} - */ - enableAnimationsSetDefault() - { - if (this.#originals['enableAnimations'] === undefined) { - return; - } - - let status = this.#originals['enableAnimations']; - - this._interfaceSettings.set_boolean('enable-animations', status); - } - - /** - * set the enable animation status - * - * @param {boolean} status true to enable, false otherwise - * - * @returns {void} - */ - enableAnimationsSet(status) - { - if (this.#originals['enableAnimations'] === undefined) { - this.#originals['enableAnimations'] - = this._interfaceSettings.get_boolean('enable-animations'); - } - - this._interfaceSettings.set_boolean('enable-animations', status); - } - - /** - * enable focus when window demands attention happens - * - * @returns {void} - */ - windowDemandsAttentionFocusEnable() - { - if ( - this._displayWindowDemandsAttentionSignal || - this._displayWindowMarkedUrgentSignal - ) { - return; - } - - let display = global.display; - - let demandFunction = (display, window) => { - if (!window || window.has_focus() || window.is_skip_taskbar()) { - return; - } - this._main.activateWindow(window); - }; - - this._displayWindowDemandsAttentionSignal - = display.connect('window-demands-attention', demandFunction); - this._displayWindowMarkedUrgentSignal - = display.connect('window-marked-urgent', demandFunction); - - // since removing '_windowDemandsAttentionId' doesn't have any effect - // we remove the original signal and re-connect it on disable - let signalId = this.#getSignalId(global.display, 'window-demands-attention'); - let signalId2 = this.#getSignalId(global.display, 'window-marked-urgent'); - display.disconnect(signalId); - display.disconnect(signalId2); - } - - /** - * disable focus when window demands attention happens - * - * @returns {void} - */ - windowDemandsAttentionFocusDisable() - { - if ( - !this._displayWindowDemandsAttentionSignal || - !this._displayWindowMarkedUrgentSignal - ) { - return; - } - - let display = global.display; - - display.disconnect(this._displayWindowDemandsAttentionSignal); - display.disconnect(this._displayWindowMarkedUrgentSignal); - this._displayWindowDemandsAttentionSignal = null; - this._displayWindowMarkedUrgentSignal = null; - - let wah = this._main.windowAttentionHandler; - wah._windowDemandsAttentionId = display.connect( - 'window-demands-attention', - wah._onWindowDemandsAttention.bind(wah) - ); - wah._windowDemandsAttentionId = display.connect( - 'window-marked-urgent', - wah._onWindowDemandsAttention.bind(wah) - ); - } - - /** - * enable maximizing windows on creation - * - * @returns {void} - */ - windowMaximizedOnCreateEnable() - { - if (this._displayWindowCreatedSignal) { - return; - } - - let display = global.display; - - let createdFunction = (display, window) => { - if (window.can_maximize()) { - window.maximize(this._meta.MaximizeFlags.HORIZONTAL | this._meta.MaximizeFlags.VERTICAL); - } - }; - - this._displayWindowCreatedSignal = display.connect('window-created', createdFunction); - } - - /** - * disable maximizing windows on creation - * - * @returns {void} - */ - windowMaximizedOnCreateDisable() - { - if (!this._displayWindowCreatedSignal) { - return; - } - - let display = global.display; - - display.disconnect(this._displayWindowCreatedSignal); - delete(this._displayWindowCreatedSignal); - } - - /** - * set startup status - * - * @param {number} status see SHELL_STATUS for available status - * - * @returns {void} - */ - startupStatusSet(status) - { - let sessionMode = this._main.sessionMode; - let layoutManager = this._main.layoutManager; - - if (!layoutManager._startingUp) { - return; - } - - if (this.#originals['sessionModeHasOverview'] === undefined) { - this.#originals['sessionModeHasOverview'] = sessionMode.hasOverview; - } - - let ControlsState = this._overviewControls.ControlsState; - let Controls = this._main.overview._overview.controls; - - switch (status) { - - case SHELL_STATUS.NONE: - sessionMode.hasOverview = false; - layoutManager.startInOverview = false; - Controls._stateAdjustment.value = ControlsState.HIDDEN; - break; - - case SHELL_STATUS.OVERVIEW: - default: - sessionMode.hasOverview = true; - layoutManager.startInOverview = true; - break; - } - - if (!this._startupCompleteSignal) { - this._startupCompleteSignal - = layoutManager.connect('startup-complete', () => { - sessionMode.hasOverview = this.#originals['sessionModeHasOverview']; - }); - } - } - - /** - * set startup status to default - * - * @returns {void} - */ - startupStatusSetDefault() - { - if (this.#originals['sessionModeHasOverview'] === undefined) { - return; - } - - if (this._startupCompleteSignal) { - this._main.layoutManager.disconnect(this._startupCompleteSignal); - } - } - - /** - * set dash icon size to default - * - * @returns {void} - */ - dashIconSizeSetDefault() - { - let classnameStarter = this.#getAPIClassname('dash-icon-size'); - - DASH_ICON_SIZES.forEach(size => { - this.UIStyleClassRemove(classnameStarter + size); - }); - } - - /** - * set dash icon size - * - * @param {number} size in pixels - * see DASH_ICON_SIZES for available sizes - * - * @returns {void} - */ - dashIconSizeSet(size) - { - this.dashIconSizeSetDefault(); - - if (!DASH_ICON_SIZES.includes(size)) { - return; - } - - let classnameStarter = this.#getAPIClassname('dash-icon-size'); - - this.UIStyleClassAdd(classnameStarter + size); - } - - /** - * change ControlsManagerLayout._computeWorkspacesBoxForState - * base on the current state - * - * @returns {void} - */ - #computeWorkspacesBoxForStateChanged() - { - let controlsLayout = this._main.overview._overview._controls.layout_manager; - - if (!this.#originals['computeWorkspacesBoxForState']) { - this.#originals['computeWorkspacesBoxForState'] - = controlsLayout._computeWorkspacesBoxForState; - } - - controlsLayout._computeWorkspacesBoxForState = (state, box, searchHeight, ...args) => { - - let inAppGrid = state === this._overviewControls.ControlsState.APP_GRID; - - if (inAppGrid && !this._searchEntryVisibility) { - // We need some spacing on top of workspace box in app grid - // when the search entry is not visible. - searchHeight = 40; - } - - box = this.#originals['computeWorkspacesBoxForState'].call( - controlsLayout, state, box, searchHeight, ...args); - - if (inAppGrid && this._workspacesInAppGridHeight !== undefined) { - box.set_size( - box.get_width(), - this._workspacesInAppGridHeight - ); - } - - return box; - }; - - // Since workspace background has shadow around it, it can cause - // unwanted shadows in app grid when the workspace height is 0. - // so we are removing the shadow when we are in app grid - if (!this._appButtonForComputeWorkspacesSignal) { - this._appButtonForComputeWorkspacesSignal = - this._main.overview.dash.showAppsButton.connect( - 'notify::checked', - () => { - let checked = this._main.overview.dash.showAppsButton.checked; - let classname = this.#getAPIClassname('no-workspaces-in-app-grid'); - if (checked) { - this.UIStyleClassAdd(classname); - } else { - this.UIStyleClassRemove(classname); - } - } - ); - } - } - - /** - * change ControlsManagerLayout._computeWorkspacesBoxForState to its default - * - * @returns {void} - */ - #computeWorkspacesBoxForStateSetDefault() - { - if (!this.#originals['computeWorkspacesBoxForState']) { - return; - } - - let controlsLayout = this._main.overview._overview._controls.layout_manager; - - controlsLayout._computeWorkspacesBoxForState - = this.#originals['computeWorkspacesBoxForState']; - - if (this._appButtonForComputeWorkspacesSignal) { - let showAppsButton = this._main.overview.dash.showAppsButton; - showAppsButton.disconnect(this._appButtonForComputeWorkspacesSignal); - delete(this._appButtonForComputeWorkspacesSignal); - this.UIStyleClassRemove(this.#getAPIClassname('no-workspaces-in-app-grid')); - } - } - - /** - * disable workspaces in app grid - * - * @returns {void} - */ - workspacesInAppGridDisable() - { - this._workspacesInAppGridHeight = 0; - this.#computeWorkspacesBoxForStateChanged(); - } - - /** - * enable workspaces in app grid - * - * @returns {void} - */ - workspacesInAppGridEnable() - { - if (this._workspacesInAppGridHeight === undefined) { - return; - } - - delete(this._workspacesInAppGridHeight); - this.#computeWorkspacesBoxForStateChanged(); - } - - /** - * change notification banner position - * - * @param {number} pos - * see XY_POSITION for available positions - * - * @returns {void} - */ - notificationBannerPositionSet(pos) - { - let messageTray = this._main.messageTray; - let bannerBin = messageTray._bannerBin; - - if (this.#originals['bannerAlignmentX'] === undefined) { - this.#originals['bannerAlignmentX'] = messageTray.bannerAlignment; - } - - if (this.#originals['bannerAlignmentY'] === undefined) { - this.#originals['bannerAlignmentY'] = bannerBin.get_y_align(); - } - - if (this.#originals['hideNotification'] === undefined) { - this.#originals['hideNotification'] = messageTray._hideNotification; - } - - // TOP - messageTray._hideNotification = this.#originals['hideNotification']; - - bannerBin.set_y_align(this._clutter.ActorAlign.START); - - if (pos === XY_POSITION.TOP_START) { - messageTray.bannerAlignment = this._clutter.ActorAlign.START; - return; - } - - if (pos === XY_POSITION.TOP_END) { - messageTray.bannerAlignment = this._clutter.ActorAlign.END; - return; - } - - if (pos === XY_POSITION.TOP_CENTER) { - messageTray.bannerAlignment = this._clutter.ActorAlign.CENTER; - return; - } - - // BOTTOM - - // >> - // This block is going to fix the animation when the notification is - // in bottom area - // this is the same function from (ui.messageTray.messageTray._hideNotification) - // with clutter animation mode set to EASE. - // because the EASE_OUT_BACK (original code) causes glitch when - // the tray is on bottom - const State = this._messageTray.State; - const ANIMATION_TIME = this._messageTray.ANIMATION_TIME; - const Clutter = this._clutter; - - messageTray._hideNotification = function (animate) { - this._notificationFocusGrabber.ungrabFocus(); - this._banner.disconnectObject(this); - this._resetNotificationLeftTimeout(); - this._bannerBin.remove_all_transitions(); - - if (animate) { - this._notificationState = State.HIDING; - this._bannerBin.ease({ - opacity: 0, - duration: ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE, - }); - this._bannerBin.ease({ - opacity: 0, - y: this._bannerBin.height, - duration: ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE, - onComplete: () => { - this._notificationState = State.HIDDEN; - this._hideNotificationCompleted(); - this._updateState(); - }, - }); - } else { - this._bannerBin.y = this._bannerBin.height; - this._bannerBin.opacity = 0; - this._notificationState = State.HIDDEN; - this._hideNotificationCompleted(); - } - } - // << - - bannerBin.set_y_align(this._clutter.ActorAlign.END); - - if (pos === XY_POSITION.BOTTOM_START) { - messageTray.bannerAlignment = this._clutter.ActorAlign.START; - return; - } - - if (pos === XY_POSITION.BOTTOM_END) { - messageTray.bannerAlignment = this._clutter.ActorAlign.END; - return; - } - - if (pos === XY_POSITION.BOTTOM_CENTER) { - messageTray.bannerAlignment = this._clutter.ActorAlign.CENTER; - return; - } - } - - /** - * set notification banner position to default position - * - * @returns {void} - */ - notificationBannerPositionSetDefault() - { - if (this.#originals['bannerAlignmentX'] === undefined || - this.#originals['bannerAlignmentY'] === undefined || - this.#originals['hideNotification'] === undefined - ) { - return; - } - - let messageTray = this._main.messageTray; - let bannerBin = messageTray._bannerBin; - - messageTray.bannerAlignment = this.#originals['bannerAlignmentX']; - bannerBin.set_y_align(this.#originals['bannerAlignmentY']); - messageTray._hideNotification = this.#originals['hideNotification']; - } - - /** - * set the workspace switcher to always/never show - * - * @param {boolean} show true for always show, false for never show - * @param {boolean} fake true means set the current should show status - * - * @returns {void} - */ - workspaceSwitcherShouldShow(shouldShow = true, fake = false) - { - if (!fake) { - this._shouldShow = shouldShow; - } - - if (!this.isWorkspaceSwitcherVisible()) { - return; - } - - let ThumbnailsBoxProto = this._workspaceThumbnail.ThumbnailsBox.prototype; - - if (!this.#originals['updateShouldShow']) { - this.#originals['updateShouldShow'] = ThumbnailsBoxProto._updateShouldShow; - } - - ThumbnailsBoxProto._updateShouldShow = function () { - if (this._shouldShow === shouldShow) { - return; - } - this._shouldShow = shouldShow; - this.notify('should-show'); - }; - } - - /** - * set the always show workspace switcher status to last real status - * - * @returns {void} - */ - #workspaceSwitcherShouldShowSetToLast() - { - if (this._shouldShow === undefined) { - this.workspaceSwitcherShouldShowSetDefault(); - return; - } - - this.workspaceSwitcherShouldShow(this._shouldShow); - } - - /** - * set the always show workspace switcher status to default - * - * @returns {void} - */ - workspaceSwitcherShouldShowSetDefault() - { - if (!this.#originals['updateShouldShow'] || !this.isWorkspaceSwitcherVisible()) { - return; - } - - let ThumbnailsBoxProto = this._workspaceThumbnail.ThumbnailsBox.prototype; - ThumbnailsBoxProto._updateShouldShow = this.#originals['updateShouldShow']; - delete(this.#originals['updateShouldShow']); - delete(this._shouldShow); - } - - /** - * set panel button hpadding to default - * - * @returns {void} - */ - panelButtonHpaddingSetDefault() - { - if (this._panelButtonHpaddingSize === undefined) { - return; - } - - let classnameStarter = this.#getAPIClassname('panel-button-padding-size'); - this.UIStyleClassRemove(classnameStarter + this._panelButtonHpaddingSize); - this.#emitRefreshStyles(); - - delete this._panelButtonHpaddingSize; - } - - /** - * set panel button hpadding size - * - * @param {number} size in pixels (0 - 60) - * - * @returns {void} - */ - panelButtonHpaddingSizeSet(size) - { - this.panelButtonHpaddingSetDefault(); - - if (size < 0 || size > 60) { - return; - } - - this._panelButtonHpaddingSize = size; - - let classnameStarter = this.#getAPIClassname('panel-button-padding-size'); - this.UIStyleClassAdd(classnameStarter + size); - this.#emitRefreshStyles(); - } - - /** - * set panel indicator padding to default - * - * @returns {void} - */ - panelIndicatorPaddingSetDefault() - { - if (this._panelIndicatorPaddingSize === undefined) { - return; - } - - let classnameStarter = this.#getAPIClassname('panel-indicator-padding-size'); - this.UIStyleClassRemove(classnameStarter + this._panelIndicatorPaddingSize); - this.#emitRefreshStyles(); - - delete this._panelIndicatorPaddingSize; - } - - /** - * set panel indicator padding size - * - * @param {number} size in pixels (0 - 60) - * - * @returns {void} - */ - panelIndicatorPaddingSizeSet(size) - { - this.panelIndicatorPaddingSetDefault(); - - if (size < 0 || size > 60) { - return; - } - - this._panelIndicatorPaddingSize = size; - - let classnameStarter = this.#getAPIClassname('panel-indicator-padding-size'); - this.UIStyleClassAdd(classnameStarter + size); - this.#emitRefreshStyles(); - } - - /** - * get window preview prototype - * - * @returns {Object} - */ - #windowPreviewGetPrototype() - { - return this._windowPreview.WindowPreview.prototype; - } - - /** - * enable window preview caption - * - * @returns {void} - */ - windowPreviewCaptionEnable() - { - if (!this.#originals['windowPreviewGetCaption']) { - return; - } - - let windowPreviewProto = this.#windowPreviewGetPrototype(); - windowPreviewProto._getCaption = this.#originals['windowPreviewGetCaption']; - - this.UIStyleClassRemove(this.#getAPIClassname('no-window-caption')); - } - - /** - * disable window preview caption - * - * @returns {void} - */ - windowPreviewCaptionDisable() - { - let windowPreviewProto = this.#windowPreviewGetPrototype(); - - if (!this.#originals['windowPreviewGetCaption']) { - this.#originals['windowPreviewGetCaption'] = windowPreviewProto._getCaption; - } - - windowPreviewProto._getCaption = () => { - return ''; - }; - - this.UIStyleClassAdd(this.#getAPIClassname('no-window-caption')); - } - - /** - * set workspace background border radius to default size - * - * @returns {void} - */ - workspaceBackgroundRadiusSetDefault() - { - if (this._workspaceBackgroundRadiusSize === undefined) { - return; - } - - let workspaceBackgroundProto = this._workspace.WorkspaceBackground.prototype; - - workspaceBackgroundProto._updateBorderRadius - = this.#originals['workspaceBackgroundUpdateBorderRadius']; - - let classnameStarter = this.#getAPIClassname('workspace-background-radius-size'); - this.UIStyleClassRemove(classnameStarter + this._workspaceBackgroundRadiusSize); - - delete this._workspaceBackgroundRadiusSize; - } - - /** - * set workspace background border radius size - * - * @param {number} size in pixels (0 - 60) - * - * @returns {void} - */ - workspaceBackgroundRadiusSet(size) - { - if (size < 0 || size > 60) { - return; - } - - this.workspaceBackgroundRadiusSetDefault(); - - let workspaceBackgroundProto = this._workspace.WorkspaceBackground.prototype; - - if (!this.#originals['workspaceBackgroundUpdateBorderRadius']) { - this.#originals['workspaceBackgroundUpdateBorderRadius'] - = workspaceBackgroundProto._updateBorderRadius; - } - - const Util = this._util; - const St = this._st; - - workspaceBackgroundProto._updateBorderRadius = function () { - const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage); - const cornerRadius = scaleFactor * size; - - const backgroundContent = this._bgManager.backgroundActor.content; - backgroundContent.rounded_clip_radius = - Util.lerp(0, cornerRadius, this._stateAdjustment.value); - } - - this._workspaceBackgroundRadiusSize = size; - - let classnameStarter = this.#getAPIClassname('workspace-background-radius-size'); - this.UIStyleClassAdd(classnameStarter + size); - } - - /** - * enable workspace wraparound - * - * @returns {void} - */ - workspaceWraparoundEnable() - { - let metaWorkspaceProto = this._meta.Workspace.prototype; - - if (!this.#originals['metaWorkspaceGetNeighbor']) { - this.#originals['metaWorkspaceGetNeighbor'] - = metaWorkspaceProto.get_neighbor; - } - - const Meta = this._meta; - - metaWorkspaceProto.get_neighbor = function (dir) { - - let index = this.index(); - let lastIndex = global.workspace_manager.n_workspaces - 1; - let neighborIndex; - - if (dir === Meta.MotionDirection.UP || dir === Meta.MotionDirection.LEFT) { - // prev - neighborIndex = (index > 0) ? index - 1 : lastIndex; - } else { - // next - neighborIndex = (index < lastIndex) ? index + 1 : 0; - } - - return global.workspace_manager.get_workspace_by_index(neighborIndex); - }; - } - - /** - * disable workspace wraparound - * - * @returns {void} - */ - workspaceWraparoundDisable() - { - if (!this.#originals['metaWorkspaceGetNeighbor']) { - return; - } - - let metaWorkspaceProto = this._meta.Workspace.prototype; - metaWorkspaceProto.get_neighbor = this.#originals['metaWorkspaceGetNeighbor']; - } - - /** - * enable window preview close button - * - * @returns {void} - */ - windowPreviewCloseButtonEnable() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-window-close')); - } - - /** - * disable window preview close button - * - * @returns {void} - */ - windowPreviewCloseButtonDisable() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-window-close')); - } - - /** - * enable ripple box - * - * @returns {void} - */ - rippleBoxEnable() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-ripple-box')); - } - - /** - * disable ripple box - * - * @returns {void} - */ - rippleBoxDisable() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-ripple-box')); - } - - /** - * unblock overlay key - * - * @returns {void} - */ - unblockOverlayKey() - { - if (!this._overlayKeyOldSignalId) { - return; - } - - this._gobject.signal_handler_unblock( - global.display, - this._overlayKeyOldSignalId - ); - - delete(this._overlayKeyOldSignalId); - } - - /** - * block overlay key - * - * @returns {void} - */ - blockOverlayKey() - { - this._overlayKeyOldSignalId = this.#getSignalId(global.display, 'overlay-key'); - - if (!this._overlayKeyOldSignalId) { - return; - } - - this._gobject.signal_handler_block(global.display, this._overlayKeyOldSignalId); - } - - /** - * enable double super press to toggle app grid - * - * @returns {void} - */ - doubleSuperToAppGridEnable() - { - if (this._isDoubleSuperToAppGrid === true) { - return; - } - - if (!this._overlayKeyNewSignalId) { - return; - } - - global.display.disconnect(this._overlayKeyNewSignalId); - delete(this._overlayKeyNewSignalId); - this.unblockOverlayKey(); - - this._isDoubleSuperToAppGrid = true; - } - - /** - * disable double super press to toggle app grid - * - * @returns {void} - */ - doubleSuperToAppGridDisable() - { - if (this._isDoubleSuperToAppGrid === false) { - return; - } - - this.blockOverlayKey(); - - this._overlayKeyNewSignalId = global.display.connect('overlay-key', () => { - this._main.overview.toggle(); - }); - - this._isDoubleSuperToAppGrid = false; - } - - /** - * disable the removal of switcher popup delay - * - * @returns {void} - */ - switcherPopupDelaySetDefault() - { - let SwitcherPopupProto = this._switcherPopup.SwitcherPopup.prototype; - - if (!SwitcherPopupProto.showOld) { - return; - } - - SwitcherPopupProto.show = SwitcherPopupProto.showOld; - delete(SwitcherPopupProto.showOld); - } - - /** - * enable the removal of switcher popup delay - * - * @returns {void} - */ - removeSwitcherPopupDelay() - { - let SwitcherPopupProto = this._switcherPopup.SwitcherPopup.prototype; - - SwitcherPopupProto.showOld = SwitcherPopupProto.show; - - SwitcherPopupProto.show = function (...args) { - let res = this.showOld(...args); - if (res) { - this._showImmediately(); - } - return res; - }; - } - - /** - * set default OSD position - * - * @returns {void} - */ - osdPositionSetDefault() - { - if (!this.#originals['osdWindowShow']) { - return; - } - - let osdWindowProto = this._osdWindow.OsdWindow.prototype; - - osdWindowProto.show = this.#originals['osdWindowShow']; - - delete(osdWindowProto._oldShow); - delete(this.#originals['osdWindowShow']); - - if ( - this.#originals['osdWindowXAlign'] !== undefined && - this.#originals['osdWindowYAlign'] !== undefined - ) { - let osdWindows = this._main.osdWindowManager._osdWindows; - osdWindows.forEach(osdWindow => { - osdWindow.x_align = this.#originals['osdWindowXAlign']; - osdWindow.y_align = this.#originals['osdWindowYAlign']; - }); - delete(this.#originals['osdWindowXAlign']); - delete(this.#originals['osdWindowYAlign']); - } - - this.UIStyleClassRemove(this.#getAPIClassname('osd-position-top')); - this.UIStyleClassRemove(this.#getAPIClassname('osd-position-bottom')); - this.UIStyleClassRemove(this.#getAPIClassname('osd-position-center')); - } - - /** - * set OSD position - * - * @param int pos position XY_POSITION - * - * @returns {void} - */ - osdPositionSet(pos) - { - let osdWindowProto = this._osdWindow.OsdWindow.prototype; - - if (!this.#originals['osdWindowShow']) { - this.#originals['osdWindowShow'] = osdWindowProto.show; - } - - if ( - this.#originals['osdWindowXAlign'] === undefined || - this.#originals['osdWindowYAlign'] === undefined - ) { - let osdWindows = this._main.osdWindowManager._osdWindows; - this.#originals['osdWindowXAlign'] = osdWindows[0].x_align; - this.#originals['osdWindowYAlign'] = osdWindows[0].y_align; - } - - if (osdWindowProto._oldShow === undefined) { - osdWindowProto._oldShow = this.#originals['osdWindowShow']; - } - - let [xAlign, yAlign] = this.#xyAlignGet(pos); - osdWindowProto.show = function () { - this.x_align = xAlign; - this.y_align = yAlign; - this._oldShow(); - }; - - if ( - pos === XY_POSITION.TOP_START || - pos === XY_POSITION.TOP_CENTER || - pos === XY_POSITION.TOP_END - ) { - this.UIStyleClassAdd(this.#getAPIClassname('osd-position-top')); - } - - if ( - pos === XY_POSITION.BOTTOM_START || - pos === XY_POSITION.BOTTOM_CENTER || - pos === XY_POSITION.BOTTOM_END - ) { - this.UIStyleClassAdd(this.#getAPIClassname('osd-position-bottom')); - } - - if ( - pos === XY_POSITION.CENTER_START || - pos === XY_POSITION.CENTER_CENTER || - pos === XY_POSITION.CENTER_END - ) { - this.UIStyleClassAdd(this.#getAPIClassname('osd-position-center')); - } - } - - /** - * show weather in date menu - * - * @returns {void} - */ - weatherShow() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-weather')); - } - - /** - * hide weather in date menu - * - * @returns {void} - */ - weatherHide() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-weather')); - } - - /** - * show world clocks in date menu - * - * @returns {void} - */ - worldClocksShow() - { - if (!this.#originals['clocksItemSync']) { - return; - } - - let clocksItem = this._main.panel.statusArea.dateMenu._clocksItem; - - clocksItem._sync = this.#originals['clocksItemSync']; - delete(this.#originals['clocksItemSync']); - - if (this._clocksItemShowSignal) { - clocksItem.disconnect(this._clocksItemShowSignal); - delete(this._clocksItemShowSignal); - } - - clocksItem._sync(); - } - - /** - * hide world clocks in date menu - * - * @returns {void} - */ - worldClocksHide() - { - let clocksItem = this._main.panel.statusArea.dateMenu._clocksItem; - - if (!this.#originals['clocksItemSync']) { - this.#originals['clocksItemSync'] = clocksItem._sync; - } - - clocksItem._sync = function () { - this.visible = false; - }; - - if (!this._clocksItemShowSignal) { - this._clocksItemShowSignal = clocksItem.connect('show', () => { - clocksItem._sync(); - }); - } - - clocksItem._sync(); - } - - /** - * show events button in date menu - * - * @returns {void} - */ - eventsButtonShow() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-events-button')); - } - - /** - * hide events button in date menu - * - * @returns {void} - */ - eventsButtonHide() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-events-button')); - } - - /** - * show calendar in date menu - * - * @returns {void} - */ - calendarShow() - { - this._main.panel.statusArea.dateMenu._calendar.show(); - } - - /** - * hide calendar in date menu - * - * @returns {void} - */ - calendarHide() - { - this._main.panel.statusArea.dateMenu._calendar.hide(); - } - - /** - * set default panel icon size - * - * @returns {void} - */ - panelIconSetDefaultSize() - { - if (this._panelIconSize === undefined || !this.#originals['panelIconSize']) { - return; - } - - let classnameStarter = this.#getAPIClassname('panel-icon-size'); - this.UIStyleClassRemove(classnameStarter + this._panelIconSize); - this.#emitRefreshStyles(); - - let defaultSize = this.#originals['panelIconSize']; - this.#changeDateMenuIndicatorIconSize(defaultSize); - - delete(this._panelIconSize); - } - - /** - * set panel icon size - * - * @param {number} size 1-60 - * - * @returns {void} - */ - panelIconSetSize(size) - { - if (size < 1 || size > 60) { - return; - } - - if (!this.#originals['panelIconSize']) { - this.#originals['panelIconSize'] = this._panel.PANEL_ICON_SIZE; - } - - let classnameStarter = this.#getAPIClassname('panel-icon-size'); - this.UIStyleClassRemove(classnameStarter + this.panelIconGetSize()); - this.UIStyleClassAdd(classnameStarter + size); - this.#emitRefreshStyles(); - - this.#changeDateMenuIndicatorIconSize(size); - - this._panelIconSize = size; - } - - /** - * change date menu indicator icon size - * - * @param {number} size - * - * @returns {void} - */ - #changeDateMenuIndicatorIconSize(size) - { - let dateMenu = this._main.panel.statusArea.dateMenu; - - // we get set_icon_size is not a function in some setups - // in case the date menu has been removed or not created - if ( - dateMenu && - dateMenu._indicator && - dateMenu._indicator.set_icon_size - ) { - dateMenu._indicator.set_icon_size(size); - } - } - - /** - * get panel icon size - * - * @returns {void} - */ - panelIconGetSize() - { - if (this._panelIconSize !== undefined) { - return this._panelIconSize; - } - - return this._panel.PANEL_ICON_SIZE; - } - - /** - * show dash separator - * - * @returns {void} - */ - dashSeparatorShow() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-dash-separator')); - } - - /** - * hide dash separator - * - * @returns {void} - */ - dashSeparatorHide() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-dash-separator')); - } - - /** - * get looking glass size - * - * @returns {array} - * width: int - * height: int - */ - #lookingGlassGetSize() - { - let lookingGlass = this._main.createLookingGlass(); - - return [lookingGlass.width, lookingGlass.height]; - } - - /** - * set default looking glass size - * - * @returns {void} - */ - lookingGlassSetDefaultSize() - { - if (!this._lookingGlassShowSignal) { - return; - } - - this._main.lookingGlass.disconnect(this._lookingGlassShowSignal); - - delete(this._lookingGlassShowSignal); - delete(this._lookingGlassOriginalSize); - delete(this._monitorsChangedSignal); - } - - /** - * set looking glass size - * - * @param {number} width in float - * @param {number} height in float - * - * @returns {void} - */ - lookingGlassSetSize(width, height) - { - let lookingGlass = this._main.createLookingGlass(); - - if (!this._lookingGlassOriginalSize) { - this._lookingGlassOriginalSize = this.#lookingGlassGetSize(); - } - - if (this._lookingGlassShowSignal) { - lookingGlass.disconnect(this._lookingGlassShowSignal); - delete(this._lookingGlassShowSignal); - } - - this._lookingGlassShowSignal = lookingGlass.connect('show', () => { - let [, currentHeight] = this.#lookingGlassGetSize(); - let [originalWidth, originalHeight] = this._lookingGlassOriginalSize; - - let monitorInfo = this.monitorGetInfo(); - - let dialogWidth - = (width !== null) - ? monitorInfo.width * width - : originalWidth; - - let x = monitorInfo.x + (monitorInfo.width - dialogWidth) / 2; - lookingGlass.set_x(x); - - let keyboardHeight = this._main.layoutManager.keyboardBox.height; - let availableHeight = monitorInfo.height - keyboardHeight; - let dialogHeight - = (height !== null) - ? Math.min(monitorInfo.height * height, availableHeight * 0.9) - : originalHeight; - - let hiddenY = lookingGlass._hiddenY + currentHeight - dialogHeight; - lookingGlass.set_y(hiddenY); - lookingGlass._hiddenY = hiddenY; - - lookingGlass.set_size(dialogWidth, dialogHeight); - }); - - if (!this._monitorsChangedSignal) { - this._monitorsChangedSignal = this._main.layoutManager.connect('monitors-changed', - () => { - this.lookingGlassSetSize(width, height); - }); - } - } - - /** - * show screenshot in window menu - * - * @returns {void} - */ - screenshotInWindowMenuShow() - { - let windowMenuProto = this._windowMenu.WindowMenu.prototype; - - if (windowMenuProto._oldBuildMenu === undefined) { - return; - } - - windowMenuProto._buildMenu = this.#originals['WindowMenubuildMenu']; - - delete(windowMenuProto._oldBuildMenu); - } - - /** - * hide screenshot in window menu - * - * @returns {void} - */ - screenshotInWindowMenuHide() - { - let windowMenuProto = this._windowMenu.WindowMenu.prototype; - - if (!this.#originals['WindowMenubuildMenu']) { - this.#originals['WindowMenubuildMenu'] = windowMenuProto._buildMenu; - } - - if (windowMenuProto._oldBuildMenu === undefined) { - windowMenuProto._oldBuildMenu = this.#originals['WindowMenubuildMenu']; - } - - windowMenuProto._buildMenu = function (window) { - this._oldBuildMenu(window); - this.firstMenuItem.hide(); - }; - } - - /** - * set all alt tab sizes to default - * - * @returns {void} - */ - #altTabSizesSetDefault() - { - let WindowIconProto = this._altTab.WindowIcon.prototype; - if (WindowIconProto._initOld) { - WindowIconProto._init = WindowIconProto._initOld; - delete(WindowIconProto._initOld); - } - - delete(this._altTabAPP_ICON_SIZE); - delete(this._altTabAPP_ICON_SIZE_SMALL); - delete(this._altTabWINDOW_PREVIEW_SIZE); - } - - /** - * set alt tab sizes - * - * @param {number|null} appIconSize - * @param {number|null} appIconSizeSmall - * @param {number|null} windowPreviewSize - * - * @returns {void} - */ - #altTabSizesSet(appIconSize, appIconSizeSmall, windowPreviewSize) - { - let WindowIconProto = this._altTab.WindowIcon.prototype; - if (!WindowIconProto._initOld) { - WindowIconProto._initOld = WindowIconProto._init; - } - - this._altTabAPP_ICON_SIZE ||= this._altTab.APP_ICON_SIZE; - this._altTabAPP_ICON_SIZE_SMALL ||= this._altTab.APP_ICON_SIZE_SMALL; - this._altTabWINDOW_PREVIEW_SIZE ||= this._altTab.WINDOW_PREVIEW_SIZE; - - const APP_ICON_SIZE = appIconSize || this._altTabAPP_ICON_SIZE; - const APP_ICON_SIZE_SMALL = appIconSizeSmall || this._altTabAPP_ICON_SIZE_SMALL; - const WINDOW_PREVIEW_SIZE = windowPreviewSize || this._altTabWINDOW_PREVIEW_SIZE; - - WindowIconProto._init = function(window, mode) { - this._initOld(window, mode); - } - } - - /** - * set default alt tab window preview size - * - * @returns {void} - */ - altTabWindowPreviewSetDefaultSize() - { - if (!this.#originals['altTabWindowPreviewSize']) { - return; - } - - this.#altTabSizesSet(null, null, this.#originals['altTabWindowPreviewSize']); - } - - /** - * set alt tab window preview size - * - * @param {number} size 1-512 - * - * @returns {void} - */ - altTabWindowPreviewSetSize(size) - { - if (size < 1 || size > 512) { - return; - } - - if (!this.#originals['altTabWindowPreviewSize']) { - this.#originals['altTabWindowPreviewSize'] = this._altTab.WINDOW_PREVIEW_SIZE; - } - - this.#altTabSizesSet(null, null, size); - } - - /** - * set default alt tab small icon size - * - * @returns {void} - */ - altTabSmallIconSetDefaultSize() - { - if (!this.#originals['altTabAppIconSizeSmall']) { - return; - } - - this.#altTabSizesSet(null, this.#originals['altTabAppIconSizeSmall'], null); - } - - /** - * set alt tab small icon size - * - * @param {number} size 1-512 - * - * @returns {void} - */ - altTabSmallIconSetSize(size) - { - if (size < 1 || size > 512) { - return; - } - - if (!this.#originals['altTabAppIconSizeSmall']) { - this.#originals['altTabAppIconSizeSmall'] = this._altTab.APP_ICON_SIZE_SMALL; - } - - this.#altTabSizesSet(null, size, null); - } - - /** - * set default alt tab icon size - * - * @returns {void} - */ - altTabIconSetDefaultSize() - { - if (!this.#originals['altTabAppIconSize']) { - return; - } - - this.#altTabSizesSet(this.#originals['altTabAppIconSize'], null, null); - } - - /** - * set alt tab icon size - * - * @param {number} size 1-512 - * - * @returns {void} - */ - altTabIconSetSize(size) - { - if (size < 1 || size > 512) { - return; - } - - if (!this.#originals['altTabAppIconSize']) { - this.#originals['altTabAppIconSize'] = this._altTab.APP_ICON_SIZE; - } - - this.#altTabSizesSet(size, null, null); - } - - /** - * enable screen sharing indicator - * - * @returns {void} - */ - screenSharingIndicatorEnable() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-screen-sharing-indicator')); - } - - /** - * disable screen sharing indicator - * - * @returns {void} - */ - screenSharingIndicatorDisable() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-screen-sharing-indicator')); - } - - /** - * enable screen recording indicator - * - * @returns {void} - */ - screenRecordingIndicatorEnable() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-screen-recording-indicator')); - } - - /** - * disable screen recording indicator - * - * @returns {void} - */ - screenRecordingIndicatorDisable() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-screen-recording-indicator')); - } - - /** - * set controls manager spacing to default - * - * @returns {void} - */ - controlsManagerSpacingSetDefault() - { - if (this._controlsManagerSpacingSize === undefined) { - return; - } - - let classnameStarter = this.#getAPIClassname('controls-manager-spacing-size'); - this.UIStyleClassRemove(classnameStarter + this._controlsManagerSpacingSize); - - delete this._controlsManagerSpacingSize; - } - - /** - * set controls manager spacing size - * - * @param {number} size in pixels (0 - 150) - * - * @returns {void} - */ - controlsManagerSpacingSizeSet(size) - { - this.controlsManagerSpacingSetDefault(); - - if (size < 0 || size > 150) { - return; - } - - this._controlsManagerSpacingSize = size; - - let classnameStarter = this.#getAPIClassname('controls-manager-spacing-size'); - this.UIStyleClassAdd(classnameStarter + size); - } - - /** - * set workspaces view spacing to default - * - * @returns {void} - */ - workspacesViewSpacingSetDefault() - { - let wsvp = this._workspacesView.WorkspacesView.prototype; - - if (wsvp._getSpacingOld === undefined) { - return; - } - - wsvp._getSpacing = wsvp._getSpacingOld; - delete wsvp._getSpacingOld; - } - - /** - * set workspaces view spacing size - * - * @param {number} size in pixels (0 - 500) - * - * @returns {void} - */ - workspacesViewSpacingSizeSet(size) - { - if (size < 0 || size > 500) { - return; - } - - let wsvp = this._workspacesView.WorkspacesView.prototype; - - if (wsvp._getSpacingOld === undefined) { - wsvp._getSpacingOld = wsvp._getSpacing; - } - - wsvp._getSpacing = function (box, fitMode, vertical) { - if (fitMode === 0) { - return size; - } - return this._getSpacingOld(box, fitMode, vertical); - }; - } - - /** - * show dash app running dot - * - * @returns {void} - */ - dashAppRunningDotShow() - { - this.UIStyleClassRemove(this.#getAPIClassname('no-dash-app-running-dot')); - } - - /** - * hide dash app running dot - * - * @returns {void} - */ - dashAppRunningDotHide() - { - this.UIStyleClassAdd(this.#getAPIClassname('no-dash-app-running-dot')); - } - - /** - * show dark style toggle button in quick settings - * - * @returns {void} - */ - quickSettingsDarkStyleToggleShow() - { - this.#onQuickSettingsPropertyCall('_darkMode', (darkMode) => { - darkMode.quickSettingsItems[0].show(); - }); - } - - /** - * hide dark style toggle button in quick settings - * - * @returns {void} - */ - quickSettingsDarkStyleToggleHide() - { - this.#onQuickSettingsPropertyCall('_darkMode', (darkMode) => { - darkMode.quickSettingsItems[0].hide(); - }); - } - - /** - * set workspaces view spacing size - * - * @param {string} propertyName - * @param {Function} func function to call when the property is available - * - * @returns {void} - */ - #onQuickSettingsPropertyCall(propertyName, func) - { - const quickSettings = this._main.panel.statusArea.quickSettings; - - this._glib.idle_add(this._glib.PRIORITY_DEFAULT_IDLE, () => { - if (!quickSettings[propertyName]) { - return this._glib.SOURCE_CONTINUE; - } - func(quickSettings[propertyName]); - return this._glib.SOURCE_REMOVE; - }); - } -} diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/Manager.js b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/Manager.js deleted file mode 100755 index 95b14d1..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/Manager.js +++ /dev/null @@ -1,1481 +0,0 @@ -/** - * Manager Library - * - * @author Javad Rahmatzadeh - * @copyright 2020-2024 - * @license GPL-3.0-only - */ - -/** - * Apply settings to the GNOME Shell - */ -export class Manager -{ - /** - * Current shell version - * - * @type {number|null} - */ - #shellVersion = null; - - /** - * Instance of API - * - * @type {API|null} - */ - #api = null; - - /** - * Instance of Gio.Settings - * - * @type {Settings|null} - */ - #settings = null; - - /** - * Class Constructor - * - * @param {Object} dependencies - * 'API' instance of lib::API - * 'Settings' instance of Gio::Settings - * @param {number} shellVersion float in major.minor format - */ - constructor(dependencies, shellVersion) - { - this.#api = dependencies['API'] || null; - this.#settings = dependencies['Settings'] || null; - - this.#shellVersion = shellVersion; - } - - /** - * register all signals for settings - * - * @returns {void} - */ - registerSettingsSignals() - { - this.#settings.connect('changed::panel', () => { - this.#applyPanel(false); - }); - - this.#settings.connect('changed::panel-in-overview', () => { - this.#applyPanel(false); - }); - - this.#settings.connect('changed::search', () => { - this.#applySearch(false); - }); - - this.#settings.connect('changed::dash', () => { - this.#applyDash(false); - }); - - this.#settings.connect('changed::osd', () => { - this.#applyOSD(false); - }); - - this.#settings.connect('changed::workspace-popup', () => { - this.#applyWorkspacePopup(false); - }); - - this.#settings.connect('changed::workspace', () => { - this.#applyWorkspace(false); - }); - - this.#settings.connect('changed::background-menu', () => { - this.#applyBackgroundMenu(false); - }); - - this.#settings.connect('changed::theme', () => { - this.#applyTheme(false); - }); - - this.#settings.connect('changed::activities-button', () => { - this.#applyActivitiesButton(false); - }); - - this.#settings.connect('changed::clock-menu', () => { - this.#applyClockMenu(false); - }); - - this.#settings.connect('changed::keyboard-layout', () => { - this.#applyKeyboardLayout(false); - }); - - this.#settings.connect('changed::accessibility-menu', () => { - this.#applyAccessibilityMenu(false); - }); - - this.#settings.connect('changed::quick-settings', () => { - this.#applyQuickSettings(false); - }); - - this.#settings.connect('changed::quick-settings-dark-mode', () => { - this.#applyQuickSettingsDarkMode(false); - }); - - this.#settings.connect('changed::window-picker-icon', () => { - this.#applyWindowPickerIcon(false); - }); - - this.#settings.connect('changed::type-to-search', () => { - this.#applyTypeToSearch(false); - }); - - this.#settings.connect('changed::workspace-switcher-size', () => { - this.#applyWorkspaceSwitcherSize(false); - }); - - this.#settings.connect('changed::power-icon', () => { - this.#applyPowerIcon(false); - }); - - this.#settings.connect('changed::top-panel-position', () => { - this.#applyTopPanelPosition(false); - }); - - this.#settings.connect('changed::panel-notification-icon', () => { - this.#applyPanelNotificationIcon(false); - }); - - this.#settings.connect('changed::clock-menu-position', () => { - this.#applyClockMenuPosition(false); - }); - - this.#settings.connect('changed::clock-menu-position-offset', () => { - this.#applyClockMenuPosition(false); - }); - - this.#settings.connect('changed::show-apps-button', () => { - this.#applyShowAppsButton(false); - }); - - this.#settings.connect('changed::animation', () => { - this.#applyAnimation(false); - }); - - this.#settings.connect('changed::window-demands-attention-focus', () => { - this.#applyWindowDemandsAttentionFocus(false); - }); - - this.#settings.connect('changed::window-maximized-on-create', () => { - this.#applyWindowMaximizedOnCreate(false); - }); - - this.#settings.connect('changed::dash-icon-size', () => { - this.#applyDashIconSize(false); - }); - - this.#settings.connect('changed::startup-status', () => { - this.#applyStartupStatus(false); - }); - - this.#settings.connect('changed::workspaces-in-app-grid', () => { - this.#applyWorkspacesInAppGrid(false); - }); - - this.#settings.connect('changed::notification-banner-position', () => { - this.#applyNotificationBannerPosition(false); - }); - - this.#settings.connect('changed::workspace-switcher-should-show', () => { - this.#applyWorkspaceSwitcherShouldShow(false); - }); - - this.#settings.connect('changed::panel-size', () => { - this.#applyPanelSize(false); - }); - - this.#settings.connect('changed::panel-button-padding-size', () => { - this.#applyPanelButtonPaddingSize(false); - }); - - this.#settings.connect('changed::panel-indicator-padding-size', () => { - this.#applyPanelIndicatorPaddingSize(false); - }); - - this.#settings.connect('changed::window-preview-caption', () => { - this.#applyWindowPreviewCaption(false); - }); - - this.#settings.connect('changed::window-preview-close-button', () => { - this.#applyWindowPreviewCloseButton(false); - }); - - this.#settings.connect('changed::workspace-background-corner-size', () => { - this.#applyWorkspaceBackgroundCornerSize(false); - }); - - this.#settings.connect('changed::workspace-wrap-around', () => { - this.#applyWorkspaceWrapAround(false); - }); - - this.#settings.connect('changed::ripple-box', () => { - this.#applyRippleBox(false); - }); - - this.#settings.connect('changed::overlay-key', () => { - this.#applyOverlayKey(false); - }); - - this.#settings.connect('changed::double-super-to-appgrid', () => { - this.#applyOverlayKey(false); - }); - - this.#settings.connect('changed::switcher-popup-delay', () => { - this.#applySwitcherPopupDelay(false); - }); - - this.#settings.connect('changed::world-clock', () => { - this.#applyWorldClock(false); - }); - - this.#settings.connect('changed::weather', () => { - this.#applyWeather(false); - }); - - this.#settings.connect('changed::calendar', () => { - this.#applyCalendar(false); - }); - - this.#settings.connect('changed::events-button', () => { - this.#applyEventsButton(false); - }); - - this.#settings.connect('changed::panel-icon-size', () => { - this.#applyPanelIconSize(false); - }); - - this.#settings.connect('changed::dash-separator', () => { - this.#applyDashSeparator(false); - }); - - this.#settings.connect('changed::looking-glass-width', () => { - this.#applyLookingGlassSize(false); - }); - - this.#settings.connect('changed::looking-glass-height', () => { - this.#applyLookingGlassSize(false); - }); - - this.#settings.connect('changed::osd-position', () => { - this.#applyOSDPosition(false); - }); - - this.#settings.connect('changed::window-menu-take-screenshot-button', () => { - this.#applyWindowMenuTakeScreenshotButton(false); - }); - - this.#settings.connect('changed::alt-tab-window-preview-size', () => { - this.#applyAltTabWindowPreviewSize(false); - }); - - this.#settings.connect('changed::alt-tab-small-icon-size', () => { - this.#applyAltTabSmallIconSize(false); - }); - - this.#settings.connect('changed::alt-tab-icon-size', () => { - this.#applyAltTabIconSize(false); - }); - - this.#settings.connect('changed::screen-sharing-indicator', () => { - this.#applyScreenSharingIndicator(false); - }); - - this.#settings.connect('changed::screen-recording-indicator', () => { - this.#applyScreenRecordingIndicator(false); - }); - - this.#settings.connect('changed::controls-manager-spacing-size', () => { - this.#applyControlsManagerSpacingSize(false); - }); - - this.#settings.connect('changed::workspace-peek', () => { - this.#applyWorkspacePeek(false); - }); - - this.#settings.connect('changed::dash-app-running', () => { - this.#applyDashAppRunning(false); - }); - - this.#settings.connect('changed::max-displayed-search-results', () => { - this.#applyMaxDisplayedSearchResults(false); - }); - } - - /** - * apply everything to the GNOME Shell - * - * @returns {void} - */ - applyAll() - { - this.#applyTheme(false); - this.#applyPanel(false); - this.#applySearch(false); - this.#applyDash(false); - this.#applyOSD(false); - this.#applyWorkspacePopup(false); - this.#applyWorkspace(false); - this.#applyBackgroundMenu(false); - this.#applyActivitiesButton(false); - this.#applyClockMenu(false); - this.#applyKeyboardLayout(false); - this.#applyAccessibilityMenu(false); - this.#applyQuickSettings(false); - this.#applyQuickSettingsDarkMode(false); - this.#applyWindowPickerIcon(false); - this.#applyTypeToSearch(false); - this.#applyWorkspaceSwitcherSize(false); - this.#applyPowerIcon(false); - this.#applyTopPanelPosition(false); - this.#applyPanelNotificationIcon(false); - this.#applyClockMenuPosition(false); - this.#applyShowAppsButton(false); - this.#applyAnimation(false); - this.#applyWindowDemandsAttentionFocus(false); - this.#applyWindowMaximizedOnCreate(false); - this.#applyDashIconSize(false); - this.#applyStartupStatus(false); - this.#applyWorkspacesInAppGrid(false); - this.#applyNotificationBannerPosition(false); - this.#applyWorkspaceSwitcherShouldShow(false); - this.#applyPanelSize(false); - this.#applyPanelButtonPaddingSize(false); - this.#applyPanelIndicatorPaddingSize(false); - this.#applyWindowPreviewCaption(false); - this.#applyWindowPreviewCloseButton(false); - this.#applyWorkspaceBackgroundCornerSize(false); - this.#applyWorkspaceWrapAround(false); - this.#applyRippleBox(false); - this.#applyOverlayKey(false); - this.#applySwitcherPopupDelay(false); - this.#applyWorldClock(false); - this.#applyWeather(false); - this.#applyPanelIconSize(false); - this.#applyEventsButton(false); - this.#applyCalendar(false); - this.#applyDashSeparator(false); - this.#applyLookingGlassSize(false); - this.#applyOSDPosition(false); - this.#applyWindowMenuTakeScreenshotButton(false); - this.#applyAltTabWindowPreviewSize(false); - this.#applyAltTabSmallIconSize(false); - this.#applyAltTabIconSize(false); - this.#applyScreenSharingIndicator(false); - this.#applyScreenRecordingIndicator(false); - this.#applyControlsManagerSpacingSize(false); - this.#applyWorkspacePeek(false); - this.#applyDashAppRunning(false); - this.#applyMaxDisplayedSearchResults(false); - } - - /** - * revert everything done by this class to the GNOME Shell - * - * @returns {void} - */ - revertAll() - { - this.#applyTheme(true); - this.#applyPanel(true); - this.#applySearch(true); - this.#applyDash(true); - this.#applyOSD(true); - this.#applyWorkspace(true); - this.#applyWorkspacePopup(true); - this.#applyBackgroundMenu(true); - this.#applyActivitiesButton(true); - this.#applyClockMenu(true); - this.#applyKeyboardLayout(true); - this.#applyAccessibilityMenu(true); - this.#applyQuickSettings(true); - this.#applyQuickSettingsDarkMode(true); - this.#applyWindowPickerIcon(true); - this.#applyTypeToSearch(true); - this.#applyWorkspaceSwitcherSize(true); - this.#applyPowerIcon(true); - this.#applyTopPanelPosition(true); - this.#applyPanelNotificationIcon(true); - this.#applyClockMenuPosition(true); - this.#applyShowAppsButton(true); - this.#applyAnimation(true); - this.#applyWindowDemandsAttentionFocus(true); - this.#applyWindowMaximizedOnCreate(true); - this.#applyDashIconSize(true); - this.#applyStartupStatus(true); - this.#applyWorkspacesInAppGrid(true); - this.#applyNotificationBannerPosition(true); - this.#applyWorkspaceSwitcherShouldShow(true); - this.#applyPanelSize(true); - this.#applyPanelButtonPaddingSize(true); - this.#applyPanelIndicatorPaddingSize(true); - this.#applyWindowPreviewCaption(true); - this.#applyWindowPreviewCloseButton(true); - this.#applyWorkspaceBackgroundCornerSize(true); - this.#applyWorkspaceWrapAround(true); - this.#applyRippleBox(true); - this.#applyOverlayKey(true); - this.#applySwitcherPopupDelay(true); - this.#applyWorldClock(true); - this.#applyWeather(true); - this.#applyPanelIconSize(true); - this.#applyEventsButton(true); - this.#applyCalendar(true); - this.#applyDashSeparator(true); - this.#applyLookingGlassSize(true); - this.#applyOSDPosition(true); - this.#applyWindowMenuTakeScreenshotButton(true); - this.#applyAltTabWindowPreviewSize(true); - this.#applyAltTabSmallIconSize(true); - this.#applyAltTabIconSize(true); - this.#applyScreenSharingIndicator(true); - this.#applyScreenRecordingIndicator(true); - this.#applyControlsManagerSpacingSize(true); - this.#applyWorkspacePeek(true); - this.#applyDashAppRunning(true); - this.#applyMaxDisplayedSearchResults(true); - } - - /** - * apply panel settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyPanel(forceOriginal) - { - let panel = this.#settings.get_boolean('panel'); - let panelInOverview = this.#settings.get_boolean('panel-in-overview'); - - if (forceOriginal || panel) { - this.#api.panelShow(); - } else { - let mode = (panelInOverview) ? 1 : 0; - this.#api.panelHide(mode); - } - } - - /** - * apply search settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applySearch(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('search')) { - this.#api.searchEntryShow(false); - } else { - this.#api.searchEntryHide(false); - } - } - - /** - * apply type to search settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyTypeToSearch(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('type-to-search')) { - this.#api.startSearchEnable(); - } else { - this.#api.startSearchDisable(); - } - } - - /** - * apply dash settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyDash(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('dash')) { - this.#api.dashShow(); - } else { - this.#api.dashHide(); - } - } - - /** - * apply osd settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyOSD(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('osd')) { - this.#api.OSDEnable(); - } else { - this.#api.OSDDisable(); - } - } - - /** - * apply workspace popup settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWorkspacePopup(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('workspace-popup')) { - this.#api.workspacePopupEnable(); - } else { - this.#api.workspacePopupDisable(); - } - } - - /** - * apply workspace settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWorkspace(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('workspace')) { - this.#api.workspaceSwitcherShow(); - } else { - this.#api.workspaceSwitcherHide(); - } - } - - /** - * apply background menu settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyBackgroundMenu(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('background-menu')) { - this.#api.backgroundMenuEnable(); - } else { - this.#api.backgroundMenuDisable(); - } - } - - /** - * apply theme settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyTheme(forceOriginal) - { - let className = 'just-perfection'; - - if (forceOriginal || !this.#settings.get_boolean('theme')) { - this.#api.UIStyleClassRemove(className); - } else { - this.#api.UIStyleClassAdd(className); - } - } - - /** - * apply activities button settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyActivitiesButton(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('activities-button')) { - this.#api.activitiesButtonShow(); - } else { - this.#api.activitiesButtonHide(); - } - } - - /** - * apply clock menu (aka date menu) settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyClockMenu(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('clock-menu')) { - this.#api.dateMenuShow(); - } else { - this.#api.dateMenuHide(); - } - } - - /** - * apply keyboard layout settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyKeyboardLayout(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('keyboard-layout')) { - this.#api.keyboardLayoutShow(); - } else { - this.#api.keyboardLayoutHide(); - } - } - - /** - * apply accessibility menu settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyAccessibilityMenu(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('accessibility-menu')) { - this.#api.accessibilityMenuShow(); - } else { - this.#api.accessibilityMenuHide(); - } - } - - /** - * apply quick settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyQuickSettings(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('quick-settings')) { - this.#api.quickSettingsMenuShow(); - } else { - this.#api.quickSettingsMenuHide(); - } - } - - /** - * apply quick settings dark mode - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyQuickSettingsDarkMode(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('quick-settings-dark-mode')) { - this.#api.quickSettingsDarkStyleToggleShow(); - } else { - this.#api.quickSettingsDarkStyleToggleHide(); - } - } - - /** - * apply window picker icon settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWindowPickerIcon(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('window-picker-icon')) { - this.#api.windowPickerIconEnable(); - } else { - this.#api.windowPickerIconDisable(); - } - } - - /** - * apply workspace switcher size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWorkspaceSwitcherSize(forceOriginal) - { - let size = this.#settings.get_int('workspace-switcher-size'); - - if (forceOriginal || size === 0) { - this.#api.workspaceSwitcherSetDefaultSize(); - } else { - this.#api.workspaceSwitcherSetSize(size / 100); - } - } - - /** - * apply power icon settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyPowerIcon(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('power-icon')) { - this.#api.powerIconShow(); - } else { - this.#api.powerIconHide(); - } - } - - /** - * apply top panel position settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyTopPanelPosition(forceOriginal) - { - if (forceOriginal || this.#settings.get_int('top-panel-position') === 0) { - this.#api.panelSetPosition(0); - } else { - this.#api.panelSetPosition(1); - } - } - - /** - * apply panel notification icon settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyPanelNotificationIcon(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('panel-notification-icon')) { - this.#api.panelNotificationIconEnable(); - } else { - this.#api.panelNotificationIconDisable(); - } - } - - /** - * apply clock menu position settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyClockMenuPosition(forceOriginal) - { - if (forceOriginal) { - this.#api.clockMenuPositionSetDefault(); - } else { - let pos = this.#settings.get_int('clock-menu-position'); - let offset = this.#settings.get_int('clock-menu-position-offset'); - this.#api.clockMenuPositionSet(pos, offset); - } - } - - /** - * apply show apps button settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyShowAppsButton(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('show-apps-button')) { - this.#api.showAppsButtonEnable(); - } else { - this.#api.showAppsButtonDisable(); - } - } - - /** - * apply animation settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyAnimation(forceOriginal) - { - let animation = this.#settings.get_int('animation'); - - let factors = [ - 0.01, // almost none - 0.2, // fastest - 0.6, // faster - 0.8, // fast - 1.3, // slow - 1.6, // slower - 2.8, // slowest - ]; - - if (forceOriginal) { - this.#api.animationSpeedSetDefault(); - this.#api.enableAnimationsSetDefault(); - } else if (animation === 0) { - // disabled - this.#api.animationSpeedSetDefault(); - this.#api.enableAnimationsSet(false); - } else if (animation === 1) { - // default speed - this.#api.animationSpeedSetDefault(); - this.#api.enableAnimationsSet(true); - } else if (factors[animation - 2] !== undefined) { - // custom speed - this.#api.animationSpeedSet(factors[animation - 2]); - this.#api.enableAnimationsSet(true); - } - } - - /** - * apply window demands attention focus settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWindowDemandsAttentionFocus(forceOriginal) - { - let focus = this.#settings.get_boolean('window-demands-attention-focus'); - - if (forceOriginal || !focus) { - this.#api.windowDemandsAttentionFocusDisable(); - } else { - this.#api.windowDemandsAttentionFocusEnable(); - } - } - - /** - * apply window maximized on create settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWindowMaximizedOnCreate(forceOriginal) - { - let maximize = this.#settings.get_boolean('window-maximized-on-create'); - - if (forceOriginal || !maximize) { - this.#api.windowMaximizedOnCreateDisable(); - } else { - this.#api.windowMaximizedOnCreateEnable(); - } - } - - /** - * apply dash icon size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyDashIconSize(forceOriginal) - { - let size = this.#settings.get_int('dash-icon-size'); - - if (forceOriginal || size === 0) { - this.#api.dashIconSizeSetDefault(); - } else { - this.#api.dashIconSizeSet(size); - } - } - - /** - * apply startup status settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyStartupStatus(forceOriginal) - { - let status = this.#settings.get_int('startup-status'); - - if (forceOriginal) { - this.#api.startupStatusSetDefault(); - } else { - this.#api.startupStatusSet(status); - } - } - - /** - * apply workspaces in app grid status settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWorkspacesInAppGrid(forceOriginal) - { - let status = this.#settings.get_boolean('workspaces-in-app-grid'); - - if (forceOriginal || status) { - this.#api.workspacesInAppGridEnable(); - } else { - this.#api.workspacesInAppGridDisable(); - } - } - - /** - * apply notification banner position settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyNotificationBannerPosition(forceOriginal) - { - let pos = this.#settings.get_int('notification-banner-position'); - - if (forceOriginal) { - this.#api.notificationBannerPositionSetDefault(); - } else { - this.#api.notificationBannerPositionSet(pos); - } - } - - /** - * apply workspace switcher should show settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWorkspaceSwitcherShouldShow(forceOriginal) - { - let shouldShow = this.#settings.get_boolean('workspace-switcher-should-show'); - - if (forceOriginal || !shouldShow) { - this.#api.workspaceSwitcherShouldShowSetDefault(); - } else { - this.#api.workspaceSwitcherShouldShow(true); - } - } - - /** - * apply panel size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyPanelSize(forceOriginal) - { - let size = this.#settings.get_int('panel-size'); - - if (forceOriginal || size === 0) { - this.#api.panelSetDefaultSize(); - } else { - this.#api.panelSetSize(size, false); - } - } - - /** - * apply panel button padding size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyPanelButtonPaddingSize(forceOriginal) - { - let size = this.#settings.get_int('panel-button-padding-size'); - - if (forceOriginal || size === 0) { - this.#api.panelButtonHpaddingSetDefault(); - } else { - this.#api.panelButtonHpaddingSizeSet(size - 1); - } - } - - /** - * apply panel indicator padding size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyPanelIndicatorPaddingSize(forceOriginal) - { - let size = this.#settings.get_int('panel-indicator-padding-size'); - - if (forceOriginal || size === 0) { - this.#api.panelIndicatorPaddingSetDefault(); - } else { - this.#api.panelIndicatorPaddingSizeSet(size - 1); - } - } - - /** - * apply window preview caption settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWindowPreviewCaption(forceOriginal) - { - let status = this.#settings.get_boolean('window-preview-caption'); - - if (forceOriginal || status) { - this.#api.windowPreviewCaptionEnable(); - } else { - this.#api.windowPreviewCaptionDisable(); - } - } - - /** - * apply window preview close button settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWindowPreviewCloseButton(forceOriginal) - { - let status = this.#settings.get_boolean('window-preview-close-button'); - - if (forceOriginal || status) { - this.#api.windowPreviewCloseButtonEnable(); - } else { - this.#api.windowPreviewCloseButtonDisable(); - } - } - - /** - * apply workspace background corner size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWorkspaceBackgroundCornerSize(forceOriginal) - { - let size = this.#settings.get_int('workspace-background-corner-size'); - - if (forceOriginal || size === 0) { - this.#api.workspaceBackgroundRadiusSetDefault(); - } else { - this.#api.workspaceBackgroundRadiusSet(size - 1); - } - } - - /** - * apply workspace wrap around settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWorkspaceWrapAround(forceOriginal) - { - let status = this.#settings.get_boolean('workspace-wrap-around'); - - if (forceOriginal || !status) { - this.#api.workspaceWraparoundDisable(); - } else { - this.#api.workspaceWraparoundEnable(); - } - } - - /** - * apply ripple box settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyRippleBox(forceOriginal) - { - let status = this.#settings.get_boolean('ripple-box'); - - if (forceOriginal || status) { - this.#api.rippleBoxEnable(); - } else { - this.#api.rippleBoxDisable(); - } - } - - /** - * apply overlay key - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyOverlayKey(forceOriginal) - { - let overlayKey = this.#settings.get_boolean('overlay-key'); - let doubleSuper = this.#settings.get_boolean('double-super-to-appgrid'); - - if (forceOriginal) { - this.#api.doubleSuperToAppGridEnable(); - this.#api.unblockOverlayKey(); - } else if (!overlayKey) { - this.#api.doubleSuperToAppGridEnable(); - this.#api.blockOverlayKey(); - } else { - this.#api.unblockOverlayKey(); - if (doubleSuper) { - this.#api.doubleSuperToAppGridEnable(); - } else { - this.#api.doubleSuperToAppGridDisable(); - } - } - } - - /** - * apply switcher popup delay settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applySwitcherPopupDelay(forceOriginal) - { - let status = this.#settings.get_boolean('switcher-popup-delay'); - - if (forceOriginal || status) { - this.#api.switcherPopupDelaySetDefault(); - } else { - this.#api.removeSwitcherPopupDelay(); - } - } - - /** - * apply world clock settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWorldClock(forceOriginal) - { - let status = this.#settings.get_boolean('world-clock'); - - if (forceOriginal || status) { - this.#api.worldClocksShow(); - } else { - this.#api.worldClocksHide(); - } - } - - /** - * apply weather settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWeather(forceOriginal) - { - let status = this.#settings.get_boolean('weather'); - - if (forceOriginal || status) { - this.#api.weatherShow(); - } else { - this.#api.weatherHide(); - } - } - - /** - * apply calendar settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyCalendar(forceOriginal) - { - let status = this.#settings.get_boolean('calendar'); - - if (forceOriginal || status) { - this.#api.calendarShow(); - } else { - this.#api.calendarHide(); - } - } - - /** - * apply events button settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyEventsButton(forceOriginal) - { - let status = this.#settings.get_boolean('events-button'); - - if (forceOriginal || status) { - this.#api.eventsButtonShow(); - } else { - this.#api.eventsButtonHide(); - } - } - - /** - * apply panel icon size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyPanelIconSize(forceOriginal) - { - let size = this.#settings.get_int('panel-icon-size'); - - if (forceOriginal || size === 0) { - this.#api.panelIconSetDefaultSize(); - } else { - this.#api.panelIconSetSize(size); - } - } - - /** - * apply dash separator settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyDashSeparator(forceOriginal) - { - let status = this.#settings.get_boolean('dash-separator'); - - if (forceOriginal || status) { - this.#api.dashSeparatorShow(); - } else { - this.#api.dashSeparatorHide(); - } - } - - /** - * apply looking glass size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyLookingGlassSize(forceOriginal) - { - let widthSize = this.#settings.get_int('looking-glass-width'); - let heightSize = this.#settings.get_int('looking-glass-height'); - - if (forceOriginal) { - this.#api.lookingGlassSetDefaultSize(); - } else { - let width = (widthSize !== 0) ? widthSize / 10 : null; - let height = (heightSize !== 0) ? heightSize / 10 : null; - this.#api.lookingGlassSetSize(width, height); - } - } - - /** - * apply osd position settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyOSDPosition(forceOriginal) - { - let pos = this.#settings.get_int('osd-position'); - - if (forceOriginal || pos === 0) { - this.#api.osdPositionSetDefault(); - } else { - this.#api.osdPositionSet(pos - 1); - } - } - - /** - * apply window menu take screenshot button settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWindowMenuTakeScreenshotButton(forceOriginal) - { - let status = this.#settings.get_boolean('window-menu-take-screenshot-button'); - - if (forceOriginal || status) { - this.#api.screenshotInWindowMenuShow(); - } else { - this.#api.screenshotInWindowMenuHide(); - } - } - - /** - * apply alt tab window preview size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyAltTabWindowPreviewSize(forceOriginal) - { - let size = this.#settings.get_int('alt-tab-window-preview-size'); - - if (forceOriginal || size === 0) { - this.#api.altTabWindowPreviewSetDefaultSize(); - } else { - this.#api.altTabWindowPreviewSetSize(size); - } - } - - /** - * apply alt tab small icon size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyAltTabSmallIconSize(forceOriginal) - { - let size = this.#settings.get_int('alt-tab-small-icon-size'); - - if (forceOriginal || size === 0) { - this.#api.altTabSmallIconSetDefaultSize(); - } else { - this.#api.altTabSmallIconSetSize(size); - } - } - - /** - * apply alt tab icon size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyAltTabIconSize(forceOriginal) - { - let size = this.#settings.get_int('alt-tab-icon-size'); - - if (forceOriginal || size === 0) { - this.#api.altTabIconSetDefaultSize(); - } else { - this.#api.altTabIconSetSize(size); - } - } - - /** - * apply screen sharing indicator settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyScreenSharingIndicator(forceOriginal) - { - let status = this.#settings.get_boolean('screen-sharing-indicator'); - - if (forceOriginal || status) { - this.#api.screenSharingIndicatorEnable(); - } else { - this.#api.screenSharingIndicatorDisable(); - } - } - - /** - * apply screen recording indicator settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyScreenRecordingIndicator(forceOriginal) - { - let status = this.#settings.get_boolean('screen-recording-indicator'); - - if (forceOriginal || status) { - this.#api.screenRecordingIndicatorEnable(); - } else { - this.#api.screenRecordingIndicatorDisable(); - } - } - - /** - * apply controls manager spacing size settings - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyControlsManagerSpacingSize(forceOriginal) - { - let size = this.#settings.get_int('controls-manager-spacing-size'); - - if (forceOriginal || size === 0) { - this.#api.controlsManagerSpacingSetDefault(); - } else { - this.#api.controlsManagerSpacingSizeSet(size); - } - } - - /** - * apply workspace peek - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyWorkspacePeek(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('workspace-peek')) { - this.#api.workspacesViewSpacingSetDefault(); - } else { - this.#api.workspacesViewSpacingSizeSet(400); - } - } - - /** - * apply dash app running - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyDashAppRunning(forceOriginal) - { - if (forceOriginal || this.#settings.get_boolean('dash-app-running')) { - this.#api.dashAppRunningDotShow(); - } else { - this.#api.dashAppRunningDotHide(); - } - } - - /** - * apply max displayed search results - * - * @param {boolean} forceOriginal force original shell setting - * - * @returns {void} - */ - #applyMaxDisplayedSearchResults(forceOriginal) - { - let items = this.#settings.get_int('max-displayed-search-results'); - - if (forceOriginal || items === 0) { - this.#api.setMaxDisplayedSearchResultToDefault(); - } else { - this.#api.setMaxDisplayedSearchResult(items); - } - } -} - diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/Prefs/Prefs.js b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/Prefs/Prefs.js deleted file mode 100755 index 04b1e7e..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/Prefs/Prefs.js +++ /dev/null @@ -1,410 +0,0 @@ -/** - * Prefs Library - * - * @author Javad Rahmatzadeh - * @copyright 2020-2024 - * @license GPL-3.0-only - */ - -/** - * prefs widget for showing prefs window - */ -export class Prefs -{ - /** - * Current shell version - * - * @type {number|null} - */ - #shellVersion = null; - - /** - * Instance of PrefsKeys - * - * @type {PrefsKeys|null} - */ - #prefsKeys = null; - - /** - * Instance of Gtk.Builder - * - * @type {Gtk.Builder|null} - */ - #builder = null; - - /** - * Instance of Gio.Settings - * - * @type {Settings|null} - */ - #settings = null; - - /** - * Instance of Resource - * - * @type {Gio.Resource|null} - */ - #resource = null; - - /** - * Instance of Gdk - * - * @type {Gdk|null} - */ - #gdk = null; - - /** - * Instance of Gio - * - * @type {Gio|null} - */ - #gio = null; - - /** - * All available profile names - * - * @type {Array} - */ - #profiles = [ - 'default', - 'minimal', - 'superminimal', - ]; - - /** - * class constructor - * - * @param {Object} dependencies - * 'Builder' instance of Gtk::Builder - * 'Settings' instance of Gio::Settings - * 'Gdk' reference to Gdk - * 'Gio' reference to Gio - * @param {PrefsKeys.PrefsKeys} prefsKeys instance of PrefsKeys - * @param {number} shellVersion float in major.minor format - */ - constructor(dependencies, prefsKeys, shellVersion) - { - this.#settings = dependencies['Settings'] || null; - this.#builder = dependencies['Builder'] || null; - this.#gdk = dependencies['Gdk'] || null; - this.#gio = dependencies['Gio'] || null; - - this.#prefsKeys = prefsKeys; - this.#shellVersion = shellVersion; - } - - /** - * fill prefs window - * - * @param {Adw.PreferencesWindow} window prefs dialog - * @param {string} ResourcesFolderPath folder path to resources folder - * @param {string} gettextDomain gettext domain - * - * @returns {void} - */ - fillPrefsWindow(window, ResourcesFolderPath, gettextDomain) - { - // changing the order here can change the elements order in ui - let uiFilenames = [ - 'profile', - 'visibility', - 'icons', - 'behavior', - 'customize', - ]; - - this.#loadResource(ResourcesFolderPath); - - this.#builder.set_translation_domain(gettextDomain); - for (let uiFilename of uiFilenames) { - this.#builder.add_from_resource( - `/org/gnome/Shell/Extensions/justperfection/ui/${uiFilename}.ui` - ); - } - - for (let uiFilename of uiFilenames) { - let page = this.#builder.get_object(uiFilename); - window.add(page); - } - - this.#setValues(); - this.#guessProfile(); - this.#onlyShowSupportedRows(); - this.#registerAllSignals(window); - - this.#setWindowSize(window); - - window.search_enabled = true; - } - - /** - * load resource - * - * @param {string} folder path to the resources folder - * - * @returns {void} - */ - #loadResource(path) - { - this.#resource = this.#gio.Resource.load(`${path}/resources.gresource`); - this.#gio.resources_register(this.#resource); - } - - /** - * set window size - * - * @param {Adw.PreferencesWindow} window prefs window - * - * @returns {void} - */ - #setWindowSize(window) - { - let [pmWidth, pmHeight, pmScale] = this.#getPrimaryMonitorInfo(); - let sizeTolerance = 50; - let width = 600; - let height = 730; - - if ( - (pmWidth / pmScale) - sizeTolerance >= width && - (pmHeight / pmScale) - sizeTolerance >= height - ) { - window.set_default_size(width, height); - } - } - - /** - * get primary monitor info - * - * @returns {Array} [width, height, scale] - */ - #getPrimaryMonitorInfo() - { - let display = this.#gdk.Display.get_default(); - - let pm = display.get_monitors().get_item(0); - - if (!pm) { - return [700, 500, 1]; - } - - let geo = pm.get_geometry(); - let scale = pm.get_scale_factor(); - - return [geo.width, geo.height, scale]; - } - - /** - * register all signals - * - * @param {Adw.PreferencesWindow} window prefs dialog - * - * @returns {void} - */ - #registerAllSignals(window) - { - this.#registerKeySignals(); - this.#registerProfileSignals(); - this.#registerCloseSignal(window); - } - - /** - * register close signal - * - * @param {Adw.PreferencesWindow} window prefs dialog - * - * @returns {void} - */ - #registerCloseSignal(window) - { - window.connect('close-request', () => { - if (this.#resource) { - this.#gio.resources_unregister(this.#resource); - } - }); - } - - /** - * register signals of all prefs keys - * - * @returns {void} - */ - #registerKeySignals() - { - // all available keys - for (let [, key] of Object.entries(this.#prefsKeys.keys)) { - - switch (key.widgetType) { - - case 'GtkSwitch': - this.#builder.get_object(key.widgetId).connect('state-set', (w) => { - this.#settings.set_boolean(key.name, w.get_active()); - this.#guessProfile(); - }); - break; - - case 'AdwActionRow': - this.#builder.get_object(key.widgetId).connect('notify::selected-item', (w) => { - let index = w.get_selected(); - let value = (index in key.maps) ? key.maps[index] : index; - this.#settings.set_int(key.name, value); - this.#guessProfile(); - }); - break; - - case 'AdwSpinRow': - this.#builder.get_object(key.widgetId).connect('notify::value', (w) => { - let value = w.get_value(); - this.#settings.set_int(key.name, value); - this.#guessProfile(); - }); - break; - } - } - } - - /** - * register profile signals - * - * @returns {void} - */ - #registerProfileSignals() - { - for (let profile of this.#profiles) { - let widget = this.#builder.get_object(`profile_${profile}`); - if (!widget) { - break; - } - widget.connect('clicked', (w) => { - this.#setValues(profile); - }); - } - } - - /** - * can check all current values and guess the profile based on the values - * - * @returns {void} - */ - #guessProfile() - { - let totalCount = 0; - let matchCount = {}; - - for (let profile of this.#profiles) { - matchCount[profile] = 0; - } - - for (let [, key] of Object.entries(this.#prefsKeys.keys)) { - - if (!key.supported) { - continue; - } - - let value; - - switch (key.widgetType) { - case 'GtkSwitch': - value = this.#builder.get_object(key.widgetId).get_active(); - break; - case 'AdwActionRow': - value = this.#builder.get_object(key.widgetId).get_selected(); - break; - case 'AdwSpinRow': - value = this.#builder.get_object(key.widgetId).get_value(); - break; - default: - value = ''; - continue; - } - - for (let profile of this.#profiles) { - if (key.profiles[profile] === value) { - matchCount[profile]++; - } - } - - totalCount++; - } - - let currentProfile = 'custom'; - for (let profile of this.#profiles) { - if (matchCount[profile] === totalCount) { - currentProfile = profile; - break; - } - } - - let widget = this.#builder.get_object(`profile_${currentProfile}`); - if (widget) { - widget.set_active(true); - } - } - - /** - * set values for all elements - * - * @param {string} profile profile name or null for get it from gsettings - * - * @returns {void} - */ - #setValues(profile) - { - for (let [, key] of Object.entries(this.#prefsKeys.keys)) { - - let widget = this.#builder.get_object(key.widgetId); - - let value; - - switch (key.widgetType) { - - case 'GtkSwitch': - value - = (profile) - ? key.profiles[profile] - : this.#settings.get_boolean(key.name); - - widget.set_active(value); - break; - - case 'AdwActionRow': - let index - = (profile) - ? key.profiles[profile] - : this.#settings.get_int(key.name); - - for (let k in key.maps) { - if (key.maps[k] === index) { - index = k; - break; - } - } - widget.set_selected(index); - break; - - case 'AdwSpinRow': - value - = (profile) - ? key.profiles[profile] - : this.#settings.get_int(key.name); - - widget.set_value(value); - break; - } - } - } - - /** - * apply all supported keys to the elements - * - * @returns {void} - */ - #onlyShowSupportedRows() - { - for (let [, key] of Object.entries(this.#prefsKeys.keys)) { - let row = this.#builder.get_object(`${key.id}_row`); - let visible = key.supported; - row.visible = visible; - } - } -}; - diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/Prefs/PrefsKeys.js b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/Prefs/PrefsKeys.js deleted file mode 100755 index 398c09a..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/lib/Prefs/PrefsKeys.js +++ /dev/null @@ -1,880 +0,0 @@ -/** - * PrefsKeys Library - * - * @author Javad Rahmatzadeh - * @copyright 2020-2024 - * @license GPL-3.0-only - */ - -/** - * prefs keys - */ -export class PrefsKeys -{ - /** - * Current shell version - * - * @type {number|null} - */ - #shellVersion = null; - - /** - * class constructor - * - * @param {number} shellVersion float in major.minor format - */ - constructor(shellVersion) - { - this.#shellVersion = shellVersion; - - /** - * holds all keys generated by this.#setKey() - * - * @member {Object} - */ - this.keys = {}; - - this.#setDefaults(); - } - - /** - * set all default keys - * - * @returns {void} - */ - #setDefaults() - { - this.#setKey( - 'visibility', - 'panel', - 'GtkSwitch', - true, - { - default: true, - minimal: false, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'panel-in-overview', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'activities-button', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'clock-menu', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'keyboard-layout', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'accessibility-menu', - 'GtkSwitch', - true, - { - default: true, - minimal: false, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'quick-settings', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'quick-settings-dark-mode', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'screen-sharing-indicator', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'screen-recording-indicator', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'search', - 'GtkSwitch', - true, - { - default: true, - minimal: false, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'dash', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'dash-separator', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'dash-app-running', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'osd', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'workspace-popup', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'workspace', - 'GtkSwitch', - true, - { - default: true, - minimal: false, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'background-menu', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'show-apps-button', - 'GtkSwitch', - true, - { - default: true, - minimal: false, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'workspaces-in-app-grid', - 'GtkSwitch', - true, - { - default: true, - minimal: false, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'window-preview-caption', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'window-preview-close-button', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'ripple-box', - 'GtkSwitch', - true, - { - default: true, - minimal: false, - superminimal: false, - } - ); - - this.#setKey( - 'visibility', - 'world-clock', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'weather', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'calendar', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'events-button', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'visibility', - 'window-menu-take-screenshot-button', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'icons', - 'panel-notification-icon', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'icons', - 'power-icon', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'icons', - 'window-picker-icon', - 'GtkSwitch', - true, - { - default: true, - minimal: false, - superminimal: false, - } - ); - - this.#setKey( - 'behavior', - 'type-to-search', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'behavior', - 'window-demands-attention-focus', - 'GtkSwitch', - true, - { - default: false, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'behavior', - 'window-maximized-on-create', - 'GtkSwitch', - true, - { - default: false, - minimal: false, - superminimal: false, - } - ); - - this.#setKey( - 'behavior', - 'workspace-switcher-should-show', - 'GtkSwitch', - true, - { - default: false, - minimal: false, - superminimal: false, - } - ); - - this.#setKey( - 'behavior', - 'startup-status', - 'AdwActionRow', - true, - { - default: 1, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'behavior', - 'workspace-wrap-around', - 'GtkSwitch', - true, - { - default: false, - minimal: false, - superminimal: false, - } - ); - - this.#setKey( - 'behavior', - 'workspace-peek', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'behavior', - 'overlay-key', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'behavior', - 'double-super-to-appgrid', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: false, - } - ); - - this.#setKey( - 'behavior', - 'switcher-popup-delay', - 'GtkSwitch', - true, - { - default: true, - minimal: true, - superminimal: true, - } - ); - - this.#setKey( - 'customize', - 'controls-manager-spacing-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 22, - } - ); - - this.#setKey( - 'customize', - 'workspace-background-corner-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 15, - } - ); - - this.#setKey( - 'customize', - 'top-panel-position', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'clock-menu-position', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'clock-menu-position-offset', - 'AdwSpinRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'workspace-switcher-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'animation', - 'AdwActionRow', - true, - { - default: 1, - minimal: 1, - superminimal: 1, - } - ); - - this.#setKey( - 'customize', - 'dash-icon-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 1, - superminimal: 0, - }, - { - '1': 16, - '2': 22, - '3': 24, - '4': 32, - '5': 40, - '6': 48, - '7': 56, - '8': 64, - } - ); - - this.#setKey( - 'customize', - 'notification-banner-position', - 'AdwActionRow', - true, - { - default: 1, - minimal: 1, - superminimal: 1, - } - ); - - this.#setKey( - 'customize', - 'panel-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'panel-button-padding-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'panel-indicator-padding-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'panel-icon-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'osd-position', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'looking-glass-width', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'looking-glass-height', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'customize', - 'alt-tab-window-preview-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - }, - { - '0': 0, - '1': 32, - '2': 64, - '3': 128, - '4': 256, - '5': 512, - } - ); - - this.#setKey( - 'customize', - 'alt-tab-small-icon-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - }, - { - '0': 0, - '1': 32, - '2': 64, - '3': 128, - '4': 256, - '5': 512, - } - ); - - this.#setKey( - 'customize', - 'alt-tab-icon-size', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - }, - { - '0': 0, - '1': 32, - '2': 64, - '3': 128, - '4': 256, - '5': 512, - } - ); - - this.#setKey( - 'customize', - 'max-displayed-search-results', - 'AdwActionRow', - true, - { - default: 0, - minimal: 0, - superminimal: 0, - } - ); - - this.#setKey( - 'override', - 'theme', - 'GtkSwitch', - true, - { - default: false, - minimal: true, - superminimal: true, - } - ); - } - - /** - * set key - * - * @param {string} category possible values: - * - visibility - * - icons - * - behavior - * - customize - * - override - * @param {string} name should be the same as gsettings key name - * @param {string} widgetType gtk widget type like 'GtkSwitch'. - * @param {boolean} supported whether supported in the current shell - * @param {Object} profiles values for each profile. for example: - * {default: true, minimal: false} - * @param {Object} [maps] for example for combobox you can specify - * if the index is 1 use 32 as value: - * {1 : 32} - * - * @returns {void} - */ - #setKey(category, name, widgetType, supported, profiles, maps = {}) - { - let id = name.replace(/-/g, '_'); - let widgetName = widgetType.toLowerCase().replace('gtk', ''); - - let widgetId - = (widgetType === 'AdwActionRow' || widgetType === 'AdwSpinRow') - ? `${id}_row` - : `${id}_${widgetName}`; - - this.keys[id] = { - category, - widgetType, - name, - id, - widgetId, - supported, - profiles, - maps, - }; - } - - /** - * delete key - * - * @param {string} id key id - * - * @returns {void} - */ - deleteKey(id) - { - delete(this.keys[id]); - } -}; - diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/af/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/af/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 64b1c7b..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/af/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ar/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ar/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index fd2f0c5..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ar/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/be/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/be/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index e84ec4b..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/be/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/bg/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/bg/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 09909ff..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/bg/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ca/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ca/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 1ccd83b..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ca/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/de/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/de/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 9149e9e..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/de/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/es/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/es/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 1dce328..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/es/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/fr/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/fr/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 306a63b..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/fr/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/gl/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/gl/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 6a78ab4..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/gl/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/it/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/it/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index b3c07fb..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/it/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ja/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ja/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index d490014..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ja/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ne/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ne/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 662e547..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ne/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/nl/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/nl/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 60fb2fa..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/nl/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/pt_BR/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/pt_BR/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index b3682f0..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/pt_BR/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ru/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ru/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index a4fac19..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/ru/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/sv/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/sv/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 2aa3656..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/sv/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/zh_CN/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/zh_CN/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 72ac65e..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/zh_CN/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/zh_TW/LC_MESSAGES/just-perfection.mo b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/zh_TW/LC_MESSAGES/just-perfection.mo deleted file mode 100644 index 67192c2..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/locale/zh_TW/LC_MESSAGES/just-perfection.mo and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/metadata.json b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/metadata.json deleted file mode 100644 index bec7eee..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/metadata.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "_generated": "Generated by SweetTooth, do not edit", - "description": "Tweak Tool to Customize GNOME Shell, Change the Behavior and Disable UI Elements", - "donations": { - "buymeacoffee": "justperfection", - "patreon": "justperfection" - }, - "gettext-domain": "just-perfection", - "name": "Just Perfection", - "settings-schema": "org.gnome.shell.extensions.just-perfection", - "shell-version": [ - "45", - "46", - "47" - ], - "url": "https://gitlab.gnome.org/jrahmatzadeh/just-perfection", - "uuid": "just-perfection-desktop@just-perfection", - "version": 30 -} \ No newline at end of file diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/prefs.js b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/prefs.js deleted file mode 100755 index f7cea09..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/prefs.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Prefs Dialog - * - * @author Javad Rahmatzadeh - * @copyright 2020-2024 - * @license GPL-3.0-only - */ - -import Gtk from 'gi://Gtk'; -import Gdk from 'gi://Gdk'; -import Gio from 'gi://Gio'; - -import {ExtensionPreferences} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; -import * as Config from 'resource:///org/gnome/Shell/Extensions/js/misc/config.js'; - -import {Prefs} from './lib/Prefs/Prefs.js'; -import {PrefsKeys} from './lib/Prefs/PrefsKeys.js'; - -/** - * Preferences window entry point - */ -export default class JustPerfectionPrefs extends ExtensionPreferences -{ - /** - * fill preferences window - * - * @returns {void} - */ - fillPreferencesWindow(window) - { - const shellVersion = parseFloat(Config.PACKAGE_VERSION); - const gettextDomain = this.metadata['gettext-domain']; - - let ResourcesFolderPath = this.path; - let prefsKeys = new PrefsKeys(shellVersion); - - let prefs = new Prefs( - { - Builder: new Gtk.Builder(), - Settings: this.getSettings(), - Gdk, - Gio, - }, - prefsKeys, - shellVersion - ); - - prefs.fillPrefsWindow(window, ResourcesFolderPath, gettextDomain); - } -} - diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/resources.gresource b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/resources.gresource deleted file mode 100644 index a93f5c1..0000000 Binary files a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/resources.gresource and /dev/null differ diff --git a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/stylesheet.css b/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/stylesheet.css deleted file mode 100755 index 974d8e2..0000000 --- a/gnome-shell-extension-just-perfection/usr/share/gnome-shell/extensions/just-perfection-desktop@just-perfection/stylesheet.css +++ /dev/null @@ -1,714 +0,0 @@ -.just-perfection -{ - font-size: small; -} - -.just-perfection .switcher-popup -{ - padding: 8px; - spacing: 24px; - font-size: small; -} - -.just-perfection .switcher-list -{ - color: #eeeeec; - background-color: rgba(0, 0, 0, .99); - border: 0 solid rgba(255, 255, 255, .16); - border-radius: 15px; - padding: 12px; - box-shadow: none; -} - -.just-perfection .switcher-list .item-box -{ - padding: 8px; - border-radius: 14px; - border: 1px solid transparent; - background-color: transparent; -} - -.just-perfection .switcher-list .item-box:outlined -{ - border: 0; - background-color: rgba(255, 255, 255, .1); - box-shadow: inset 0 2px 2px 0 rgba(0, 0, 0, .4); -} - -.just-perfection .switcher-list .item-box:selected -{ - background-color: #404040; - color: #eeeeec; -} - -.just-perfection .switcher-list .thumbnail-box -{ - padding: 2px; - spacing: 6px; -} - -.just-perfection .switcher-list .thumbnail -{ - width: 256px; -} - -.just-perfection .switcher-list .separator -{ - width: 1px; - background: #272727; -} - -.just-perfection .switcher-list .switcher-list-item-container -{ - spacing: 12px; -} - -.just-perfection .workspace-switcher-container -{ - background-color: rgba(0, 0, 0, .7); - box-shadow: none; - padding: 3px; - border: 0; - border-radius: 3px; -} - -.just-perfection .workspace-switcher -{ - background: transparent; - border: none; - border-radius: 0; - padding: 0; - spacing: 4px; -} - -.just-perfection .workspace-switcher -{ - background: rgba(0, 0, 0, .7); - box-shadow: none; - border: none; - border-radius: 5px; - padding: 0; - spacing: 4px; -} - -.just-perfection .ws-switcher-indicator, -.just-perfection .ws-switcher-indicator:active -{ - background: transparent; - background-color: rgba(255, 255, 255, .02); - height: 8px; - width: 20px; - border: 0; - border-radius: 2px; - padding: 2px; - margin: 5px; -} - -.just-perfection .ws-switcher-indicator:active -{ - background-color: rgba(255, 255, 255, .8); -} - -.just-perfection .ws-switcher-box -{ - background: transparent; - background-color: rgba(255, 255, 255, .02); - height: 10px; - background-size: 0; - border: 0; - border-radius: 1px; -} - -.just-perfection .ws-switcher-active-up, -.just-perfection .ws-switcher-active-down, -.just-perfection .ws-switcher-active-left, -.just-perfection .ws-switcher-active-right -{ - height: 10px; - background-color: rgba(255, 255, 255, .8); - border: 0; - border-radius: 1px; -} - -.just-perfection .osd-window -{ - background-color: rgba(0, 0, 0, 1); - border: 0; - border-radius: 8px; - padding: 10px; - box-shadow: none; -} - -.just-perfection .osd-window StIcon -{ - width: 22px; - height: 22px; -} - -.just-perfection .tile-preview -{ - background-color: rgba(255, 255, 255, 0.15); - border: 1px solid rgba(255, 255, 255, 0.6); - border-radius: 0; -} - -.just-perfection-api-no-search #overview -{ - spacing: 0; -} - -.just-perfection-api-no-search .overview-controls -{ - padding-top: 36px; - padding-bottom: 36px; -} - -/* commented because the icon will be shown when the window is getting dragged */ -/* icon-dropshadow isn't specific to the window picker icon but that's a quick fix for now */ -/* .just-perfection-api-no-window-picker-icon .workspaces-view StIcon */ -.just-perfection-api-no-window-picker-icon .icon-dropshadow -{ - width: 0; - height: 0; - padding: 0; -} - -.just-perfection-api-bottom-panel #overview -{ - margin-bottom: 24px; -} - -.just-perfection-api-bottom-panel .popup-menu.panel-menu -{ - margin-bottom: 0; -} - -.just-perfection-api-bottom-panel.just-perfection-api-no-panel #overview -{ - margin-bottom: 0; -} - -.just-perfection-api-no-panel #panel -{ - background-color: transparent; -} - -.just-perfection-api-no-panel-arrow .panel-button .popup-menu-arrow -{ - width: 0; - height: 0; -} - -.just-perfection-api-no-panel-notification-icon .clock-display StIcon -{ - width: 0; - height: 0; - padding: 0; -} - -.just-perfection-api-no-panel-notification-icon .clock-display-box -{ - padding: 0; - spacing: 0; -} - -.just-perfection-api-no-show-apps-button .show-apps, -.just-perfection-api-no-show-apps-button .show-apps StIcon -{ - width: 0; - margin: 0; - spacing: 0; -} - -.just-perfection-api-dash-icon-size16 #dash StIcon { height: 16px; width: 16px; } -.just-perfection-api-dash-icon-size22 #dash StIcon { height: 22px; width: 22px; } -.just-perfection-api-dash-icon-size24 #dash StIcon { height: 24px; width: 24px; } -.just-perfection-api-dash-icon-size32 #dash StIcon { height: 32px; width: 32px; } -.just-perfection-api-dash-icon-size40 #dash StIcon { height: 40px; width: 40px; } -.just-perfection-api-dash-icon-size48 #dash StIcon { height: 48px; width: 48px; } -.just-perfection-api-dash-icon-size56 #dash StIcon { height: 56px; width: 56px; } -.just-perfection-api-dash-icon-size64 #dash StIcon { height: 64px; width: 64px; } - - -.just-perfection-api-panel-button-padding-size0 .panel-button { -natural-hpadding: 0px; -minimum-hpadding: 0px; } -.just-perfection-api-panel-button-padding-size1 .panel-button { -natural-hpadding: 1px; -minimum-hpadding: 1px; } -.just-perfection-api-panel-button-padding-size2 .panel-button { -natural-hpadding: 2px; -minimum-hpadding: 2px; } -.just-perfection-api-panel-button-padding-size3 .panel-button { -natural-hpadding: 3px; -minimum-hpadding: 3px; } -.just-perfection-api-panel-button-padding-size4 .panel-button { -natural-hpadding: 4px; -minimum-hpadding: 4px; } -.just-perfection-api-panel-button-padding-size5 .panel-button { -natural-hpadding: 5px; -minimum-hpadding: 5px; } -.just-perfection-api-panel-button-padding-size6 .panel-button { -natural-hpadding: 6px; -minimum-hpadding: 6px; } -.just-perfection-api-panel-button-padding-size7 .panel-button { -natural-hpadding: 7px; -minimum-hpadding: 7px; } -.just-perfection-api-panel-button-padding-size8 .panel-button { -natural-hpadding: 8px; -minimum-hpadding: 8px; } -.just-perfection-api-panel-button-padding-size9 .panel-button { -natural-hpadding: 9px; -minimum-hpadding: 9px; } -.just-perfection-api-panel-button-padding-size10 .panel-button { -natural-hpadding: 10px; -minimum-hpadding: 10px; } -.just-perfection-api-panel-button-padding-size11 .panel-button { -natural-hpadding: 11px; -minimum-hpadding: 11px; } -.just-perfection-api-panel-button-padding-size12 .panel-button { -natural-hpadding: 12px; -minimum-hpadding: 12px; } -.just-perfection-api-panel-button-padding-size13 .panel-button { -natural-hpadding: 13px; -minimum-hpadding: 13px; } -.just-perfection-api-panel-button-padding-size14 .panel-button { -natural-hpadding: 14px; -minimum-hpadding: 14px; } -.just-perfection-api-panel-button-padding-size15 .panel-button { -natural-hpadding: 15px; -minimum-hpadding: 15px; } -.just-perfection-api-panel-button-padding-size16 .panel-button { -natural-hpadding: 16px; -minimum-hpadding: 16px; } -.just-perfection-api-panel-button-padding-size17 .panel-button { -natural-hpadding: 17px; -minimum-hpadding: 17px; } -.just-perfection-api-panel-button-padding-size18 .panel-button { -natural-hpadding: 18px; -minimum-hpadding: 18px; } -.just-perfection-api-panel-button-padding-size19 .panel-button { -natural-hpadding: 19px; -minimum-hpadding: 19px; } -.just-perfection-api-panel-button-padding-size20 .panel-button { -natural-hpadding: 20px; -minimum-hpadding: 20px; } -.just-perfection-api-panel-button-padding-size21 .panel-button { -natural-hpadding: 21px; -minimum-hpadding: 21px; } -.just-perfection-api-panel-button-padding-size22 .panel-button { -natural-hpadding: 22px; -minimum-hpadding: 22px; } -.just-perfection-api-panel-button-padding-size23 .panel-button { -natural-hpadding: 23px; -minimum-hpadding: 23px; } -.just-perfection-api-panel-button-padding-size24 .panel-button { -natural-hpadding: 24px; -minimum-hpadding: 24px; } -.just-perfection-api-panel-button-padding-size25 .panel-button { -natural-hpadding: 25px; -minimum-hpadding: 25px; } -.just-perfection-api-panel-button-padding-size26 .panel-button { -natural-hpadding: 26px; -minimum-hpadding: 26px; } -.just-perfection-api-panel-button-padding-size27 .panel-button { -natural-hpadding: 27px; -minimum-hpadding: 27px; } -.just-perfection-api-panel-button-padding-size28 .panel-button { -natural-hpadding: 28px; -minimum-hpadding: 28px; } -.just-perfection-api-panel-button-padding-size29 .panel-button { -natural-hpadding: 29px; -minimum-hpadding: 29px; } -.just-perfection-api-panel-button-padding-size30 .panel-button { -natural-hpadding: 30px; -minimum-hpadding: 30px; } -.just-perfection-api-panel-button-padding-size31 .panel-button { -natural-hpadding: 31px; -minimum-hpadding: 31px; } -.just-perfection-api-panel-button-padding-size32 .panel-button { -natural-hpadding: 32px; -minimum-hpadding: 32px; } -.just-perfection-api-panel-button-padding-size33 .panel-button { -natural-hpadding: 33px; -minimum-hpadding: 33px; } -.just-perfection-api-panel-button-padding-size34 .panel-button { -natural-hpadding: 34px; -minimum-hpadding: 34px; } -.just-perfection-api-panel-button-padding-size35 .panel-button { -natural-hpadding: 35px; -minimum-hpadding: 35px; } -.just-perfection-api-panel-button-padding-size36 .panel-button { -natural-hpadding: 36px; -minimum-hpadding: 36px; } -.just-perfection-api-panel-button-padding-size37 .panel-button { -natural-hpadding: 37px; -minimum-hpadding: 37px; } -.just-perfection-api-panel-button-padding-size38 .panel-button { -natural-hpadding: 38px; -minimum-hpadding: 38px; } -.just-perfection-api-panel-button-padding-size39 .panel-button { -natural-hpadding: 39px; -minimum-hpadding: 39px; } -.just-perfection-api-panel-button-padding-size40 .panel-button { -natural-hpadding: 40px; -minimum-hpadding: 40px; } -.just-perfection-api-panel-button-padding-size41 .panel-button { -natural-hpadding: 41px; -minimum-hpadding: 41px; } -.just-perfection-api-panel-button-padding-size42 .panel-button { -natural-hpadding: 42px; -minimum-hpadding: 42px; } -.just-perfection-api-panel-button-padding-size43 .panel-button { -natural-hpadding: 43px; -minimum-hpadding: 43px; } -.just-perfection-api-panel-button-padding-size44 .panel-button { -natural-hpadding: 44px; -minimum-hpadding: 44px; } -.just-perfection-api-panel-button-padding-size45 .panel-button { -natural-hpadding: 45px; -minimum-hpadding: 45px; } -.just-perfection-api-panel-button-padding-size46 .panel-button { -natural-hpadding: 46px; -minimum-hpadding: 46px; } -.just-perfection-api-panel-button-padding-size47 .panel-button { -natural-hpadding: 47px; -minimum-hpadding: 47px; } -.just-perfection-api-panel-button-padding-size48 .panel-button { -natural-hpadding: 48px; -minimum-hpadding: 48px; } -.just-perfection-api-panel-button-padding-size49 .panel-button { -natural-hpadding: 49px; -minimum-hpadding: 49px; } -.just-perfection-api-panel-button-padding-size50 .panel-button { -natural-hpadding: 50px; -minimum-hpadding: 50px; } -.just-perfection-api-panel-button-padding-size51 .panel-button { -natural-hpadding: 51px; -minimum-hpadding: 51px; } -.just-perfection-api-panel-button-padding-size52 .panel-button { -natural-hpadding: 52px; -minimum-hpadding: 52px; } -.just-perfection-api-panel-button-padding-size53 .panel-button { -natural-hpadding: 53px; -minimum-hpadding: 53px; } -.just-perfection-api-panel-button-padding-size54 .panel-button { -natural-hpadding: 54px; -minimum-hpadding: 54px; } -.just-perfection-api-panel-button-padding-size55 .panel-button { -natural-hpadding: 55px; -minimum-hpadding: 55px; } -.just-perfection-api-panel-button-padding-size56 .panel-button { -natural-hpadding: 56px; -minimum-hpadding: 56px; } -.just-perfection-api-panel-button-padding-size57 .panel-button { -natural-hpadding: 57px; -minimum-hpadding: 57px; } -.just-perfection-api-panel-button-padding-size58 .panel-button { -natural-hpadding: 58px; -minimum-hpadding: 58px; } -.just-perfection-api-panel-button-padding-size59 .panel-button { -natural-hpadding: 59px; -minimum-hpadding: 59px; } -.just-perfection-api-panel-button-padding-size60 .panel-button { -natural-hpadding: 60px; -minimum-hpadding: 60px; } - -.just-perfection-api-no-window-caption .window-caption -{ - spacing: 0; - background-color: transparent; - border-radius: 0; - border: 0; - padding: 0; - font-size: 0; -} - -.just-perfection-api-workspace-background-radius-size0 .workspace-background { border-radius: 0; } -.just-perfection-api-workspace-background-radius-size1 .workspace-background { border-radius: 1px; } -.just-perfection-api-workspace-background-radius-size2 .workspace-background { border-radius: 2px; } -.just-perfection-api-workspace-background-radius-size3 .workspace-background { border-radius: 3px; } -.just-perfection-api-workspace-background-radius-size4 .workspace-background { border-radius: 4px; } -.just-perfection-api-workspace-background-radius-size5 .workspace-background { border-radius: 5px; } -.just-perfection-api-workspace-background-radius-size6 .workspace-background { border-radius: 6px; } -.just-perfection-api-workspace-background-radius-size7 .workspace-background { border-radius: 7px; } -.just-perfection-api-workspace-background-radius-size8 .workspace-background { border-radius: 8px; } -.just-perfection-api-workspace-background-radius-size9 .workspace-background { border-radius: 9px; } -.just-perfection-api-workspace-background-radius-size10 .workspace-background { border-radius: 10px; } -.just-perfection-api-workspace-background-radius-size11 .workspace-background { border-radius: 11px; } -.just-perfection-api-workspace-background-radius-size12 .workspace-background { border-radius: 12px; } -.just-perfection-api-workspace-background-radius-size13 .workspace-background { border-radius: 13px; } -.just-perfection-api-workspace-background-radius-size14 .workspace-background { border-radius: 14px; } -.just-perfection-api-workspace-background-radius-size15 .workspace-background { border-radius: 15px; } -.just-perfection-api-workspace-background-radius-size16 .workspace-background { border-radius: 16px; } -.just-perfection-api-workspace-background-radius-size17 .workspace-background { border-radius: 17px; } -.just-perfection-api-workspace-background-radius-size18 .workspace-background { border-radius: 18px; } -.just-perfection-api-workspace-background-radius-size19 .workspace-background { border-radius: 19px; } -.just-perfection-api-workspace-background-radius-size20 .workspace-background { border-radius: 20px; } -.just-perfection-api-workspace-background-radius-size21 .workspace-background { border-radius: 21px; } -.just-perfection-api-workspace-background-radius-size22 .workspace-background { border-radius: 22px; } -.just-perfection-api-workspace-background-radius-size23 .workspace-background { border-radius: 23px; } -.just-perfection-api-workspace-background-radius-size24 .workspace-background { border-radius: 24px; } -.just-perfection-api-workspace-background-radius-size25 .workspace-background { border-radius: 25px; } -.just-perfection-api-workspace-background-radius-size26 .workspace-background { border-radius: 26px; } -.just-perfection-api-workspace-background-radius-size27 .workspace-background { border-radius: 27px; } -.just-perfection-api-workspace-background-radius-size28 .workspace-background { border-radius: 28px; } -.just-perfection-api-workspace-background-radius-size29 .workspace-background { border-radius: 29px; } -.just-perfection-api-workspace-background-radius-size30 .workspace-background { border-radius: 30px; } -.just-perfection-api-workspace-background-radius-size31 .workspace-background { border-radius: 31px; } -.just-perfection-api-workspace-background-radius-size32 .workspace-background { border-radius: 32px; } -.just-perfection-api-workspace-background-radius-size33 .workspace-background { border-radius: 33px; } -.just-perfection-api-workspace-background-radius-size34 .workspace-background { border-radius: 34px; } -.just-perfection-api-workspace-background-radius-size35 .workspace-background { border-radius: 35px; } -.just-perfection-api-workspace-background-radius-size36 .workspace-background { border-radius: 36px; } -.just-perfection-api-workspace-background-radius-size37 .workspace-background { border-radius: 37px; } -.just-perfection-api-workspace-background-radius-size38 .workspace-background { border-radius: 38px; } -.just-perfection-api-workspace-background-radius-size39 .workspace-background { border-radius: 39px; } -.just-perfection-api-workspace-background-radius-size40 .workspace-background { border-radius: 40px; } -.just-perfection-api-workspace-background-radius-size41 .workspace-background { border-radius: 41px; } -.just-perfection-api-workspace-background-radius-size42 .workspace-background { border-radius: 42px; } -.just-perfection-api-workspace-background-radius-size43 .workspace-background { border-radius: 43px; } -.just-perfection-api-workspace-background-radius-size44 .workspace-background { border-radius: 44px; } -.just-perfection-api-workspace-background-radius-size45 .workspace-background { border-radius: 45px; } -.just-perfection-api-workspace-background-radius-size46 .workspace-background { border-radius: 46px; } -.just-perfection-api-workspace-background-radius-size47 .workspace-background { border-radius: 47px; } -.just-perfection-api-workspace-background-radius-size48 .workspace-background { border-radius: 48px; } -.just-perfection-api-workspace-background-radius-size49 .workspace-background { border-radius: 49px; } -.just-perfection-api-workspace-background-radius-size50 .workspace-background { border-radius: 50px; } -.just-perfection-api-workspace-background-radius-size51 .workspace-background { border-radius: 51px; } -.just-perfection-api-workspace-background-radius-size52 .workspace-background { border-radius: 52px; } -.just-perfection-api-workspace-background-radius-size53 .workspace-background { border-radius: 53px; } -.just-perfection-api-workspace-background-radius-size54 .workspace-background { border-radius: 54px; } -.just-perfection-api-workspace-background-radius-size55 .workspace-background { border-radius: 55px; } -.just-perfection-api-workspace-background-radius-size56 .workspace-background { border-radius: 56px; } -.just-perfection-api-workspace-background-radius-size57 .workspace-background { border-radius: 57px; } -.just-perfection-api-workspace-background-radius-size58 .workspace-background { border-radius: 58px; } -.just-perfection-api-workspace-background-radius-size59 .workspace-background { border-radius: 59px; } -.just-perfection-api-workspace-background-radius-size60 .workspace-background { border-radius: 60px; } - -.just-perfection-api-panel-indicator-padding-size0 .panel-status-indicators-box { spacing: 0; } -.just-perfection-api-panel-indicator-padding-size1 .panel-status-indicators-box { spacing: 1px; } -.just-perfection-api-panel-indicator-padding-size2 .panel-status-indicators-box { spacing: 2px; } -.just-perfection-api-panel-indicator-padding-size3 .panel-status-indicators-box { spacing: 3px; } -.just-perfection-api-panel-indicator-padding-size4 .panel-status-indicators-box { spacing: 4px; } -.just-perfection-api-panel-indicator-padding-size5 .panel-status-indicators-box { spacing: 5px; } -.just-perfection-api-panel-indicator-padding-size6 .panel-status-indicators-box { spacing: 6px; } -.just-perfection-api-panel-indicator-padding-size7 .panel-status-indicators-box { spacing: 7px; } -.just-perfection-api-panel-indicator-padding-size8 .panel-status-indicators-box { spacing: 8px; } -.just-perfection-api-panel-indicator-padding-size9 .panel-status-indicators-box { spacing: 9px; } -.just-perfection-api-panel-indicator-padding-size10 .panel-status-indicators-box { spacing: 10px; } -.just-perfection-api-panel-indicator-padding-size11 .panel-status-indicators-box { spacing: 11px; } -.just-perfection-api-panel-indicator-padding-size12 .panel-status-indicators-box { spacing: 12px; } -.just-perfection-api-panel-indicator-padding-size13 .panel-status-indicators-box { spacing: 13px; } -.just-perfection-api-panel-indicator-padding-size14 .panel-status-indicators-box { spacing: 14px; } -.just-perfection-api-panel-indicator-padding-size15 .panel-status-indicators-box { spacing: 15px; } -.just-perfection-api-panel-indicator-padding-size16 .panel-status-indicators-box { spacing: 16px; } -.just-perfection-api-panel-indicator-padding-size17 .panel-status-indicators-box { spacing: 17px; } -.just-perfection-api-panel-indicator-padding-size18 .panel-status-indicators-box { spacing: 18px; } -.just-perfection-api-panel-indicator-padding-size19 .panel-status-indicators-box { spacing: 19px; } -.just-perfection-api-panel-indicator-padding-size20 .panel-status-indicators-box { spacing: 20px; } -.just-perfection-api-panel-indicator-padding-size21 .panel-status-indicators-box { spacing: 21px; } -.just-perfection-api-panel-indicator-padding-size22 .panel-status-indicators-box { spacing: 22px; } -.just-perfection-api-panel-indicator-padding-size23 .panel-status-indicators-box { spacing: 23px; } -.just-perfection-api-panel-indicator-padding-size24 .panel-status-indicators-box { spacing: 24px; } -.just-perfection-api-panel-indicator-padding-size25 .panel-status-indicators-box { spacing: 25px; } -.just-perfection-api-panel-indicator-padding-size26 .panel-status-indicators-box { spacing: 26px; } -.just-perfection-api-panel-indicator-padding-size27 .panel-status-indicators-box { spacing: 27px; } -.just-perfection-api-panel-indicator-padding-size28 .panel-status-indicators-box { spacing: 28px; } -.just-perfection-api-panel-indicator-padding-size29 .panel-status-indicators-box { spacing: 29px; } -.just-perfection-api-panel-indicator-padding-size30 .panel-status-indicators-box { spacing: 30px; } -.just-perfection-api-panel-indicator-padding-size31 .panel-status-indicators-box { spacing: 31px; } -.just-perfection-api-panel-indicator-padding-size32 .panel-status-indicators-box { spacing: 32px; } -.just-perfection-api-panel-indicator-padding-size33 .panel-status-indicators-box { spacing: 33px; } -.just-perfection-api-panel-indicator-padding-size34 .panel-status-indicators-box { spacing: 34px; } -.just-perfection-api-panel-indicator-padding-size35 .panel-status-indicators-box { spacing: 35px; } -.just-perfection-api-panel-indicator-padding-size36 .panel-status-indicators-box { spacing: 36px; } -.just-perfection-api-panel-indicator-padding-size37 .panel-status-indicators-box { spacing: 37px; } -.just-perfection-api-panel-indicator-padding-size38 .panel-status-indicators-box { spacing: 38px; } -.just-perfection-api-panel-indicator-padding-size39 .panel-status-indicators-box { spacing: 39px; } -.just-perfection-api-panel-indicator-padding-size40 .panel-status-indicators-box { spacing: 40px; } -.just-perfection-api-panel-indicator-padding-size41 .panel-status-indicators-box { spacing: 41px; } -.just-perfection-api-panel-indicator-padding-size42 .panel-status-indicators-box { spacing: 42px; } -.just-perfection-api-panel-indicator-padding-size43 .panel-status-indicators-box { spacing: 43px; } -.just-perfection-api-panel-indicator-padding-size44 .panel-status-indicators-box { spacing: 44px; } -.just-perfection-api-panel-indicator-padding-size45 .panel-status-indicators-box { spacing: 45px; } -.just-perfection-api-panel-indicator-padding-size46 .panel-status-indicators-box { spacing: 46px; } -.just-perfection-api-panel-indicator-padding-size47 .panel-status-indicators-box { spacing: 47px; } -.just-perfection-api-panel-indicator-padding-size48 .panel-status-indicators-box { spacing: 48px; } -.just-perfection-api-panel-indicator-padding-size49 .panel-status-indicators-box { spacing: 49px; } -.just-perfection-api-panel-indicator-padding-size50 .panel-status-indicators-box { spacing: 50px; } -.just-perfection-api-panel-indicator-padding-size51 .panel-status-indicators-box { spacing: 51px; } -.just-perfection-api-panel-indicator-padding-size52 .panel-status-indicators-box { spacing: 52px; } -.just-perfection-api-panel-indicator-padding-size53 .panel-status-indicators-box { spacing: 53px; } -.just-perfection-api-panel-indicator-padding-size54 .panel-status-indicators-box { spacing: 54px; } -.just-perfection-api-panel-indicator-padding-size55 .panel-status-indicators-box { spacing: 55px; } -.just-perfection-api-panel-indicator-padding-size56 .panel-status-indicators-box { spacing: 56px; } -.just-perfection-api-panel-indicator-padding-size57 .panel-status-indicators-box { spacing: 57px; } -.just-perfection-api-panel-indicator-padding-size58 .panel-status-indicators-box { spacing: 58px; } -.just-perfection-api-panel-indicator-padding-size59 .panel-status-indicators-box { spacing: 59px; } -.just-perfection-api-panel-indicator-padding-size60 .panel-status-indicators-box { spacing: 60px; } - -.just-perfection-api-no-window-close .window-close, -.just-perfection-api-no-window-close.just-perfection .window-close -{ - height: 0; - width: 30px; - background: transparent; - border-radius: 0; - border: 0; - spacing: 0; - box-shadow: none; -} - -.just-perfection-api-no-ripple-box .ripple-box -{ - background-color: transparent; -} - -/** - * This can refresh other styles that is not inculding any standard css styles. - * values are not important here. Just do something that can change the look - */ -.just-perfection-api-refresh-styles .panel-button, -.just-perfection-api-refresh-styles .panel-status-indicators-box, -.just-perfection-api-refresh-styles .panel-button StIcon, -.just-perfection-api-refresh-styles .system-status-icon -{ - background-color: gold; - color: gold; - border-radius: 100px; -} - -.just-perfection-api-no-weather .weather-button, -.just-perfection-api-no-weather .weather-button *, -.just-perfection-api-no-events-button .events-button, -.just-perfection-api-no-events-button .events-button *, -.just-perfection-api-no-screen-sharing-indicator .screen-sharing-indicator, -.just-perfection-api-no-screen-sharing-indicator .screen-sharing-indicator *, -.just-perfection-api-no-screen-recording-indicator .screen-recording-indicator, -.just-perfection-api-no-screen-recording-indicator .screen-recording-indicator * -{ - padding: 0; - margin: 0; - border: 0; - height: 0; - width: 0; -} - -.just-perfection-api-panel-icon-size1 .system-status-icon, .just-perfection-api-panel-icon-size1 .panel-button StIcon { icon-size: 1px; } -.just-perfection-api-panel-icon-size2 .system-status-icon, .just-perfection-api-panel-icon-size2 .panel-button StIcon { icon-size: 2px; } -.just-perfection-api-panel-icon-size3 .system-status-icon, .just-perfection-api-panel-icon-size3 .panel-button StIcon { icon-size: 3px; } -.just-perfection-api-panel-icon-size4 .system-status-icon, .just-perfection-api-panel-icon-size4 .panel-button StIcon { icon-size: 4px; } -.just-perfection-api-panel-icon-size5 .system-status-icon, .just-perfection-api-panel-icon-size5 .panel-button StIcon { icon-size: 5px; } -.just-perfection-api-panel-icon-size6 .system-status-icon, .just-perfection-api-panel-icon-size6 .panel-button StIcon { icon-size: 6px; } -.just-perfection-api-panel-icon-size7 .system-status-icon, .just-perfection-api-panel-icon-size7 .panel-button StIcon { icon-size: 7px; } -.just-perfection-api-panel-icon-size8 .system-status-icon, .just-perfection-api-panel-icon-size8 .panel-button StIcon { icon-size: 8px; } -.just-perfection-api-panel-icon-size9 .system-status-icon, .just-perfection-api-panel-icon-size9 .panel-button StIcon { icon-size: 9px; } -.just-perfection-api-panel-icon-size10 .system-status-icon, .just-perfection-api-panel-icon-size10 .panel-button StIcon { icon-size: 10px; } -.just-perfection-api-panel-icon-size11 .system-status-icon, .just-perfection-api-panel-icon-size11 .panel-button StIcon { icon-size: 11px; } -.just-perfection-api-panel-icon-size12 .system-status-icon, .just-perfection-api-panel-icon-size12 .panel-button StIcon { icon-size: 12px; } -.just-perfection-api-panel-icon-size13 .system-status-icon, .just-perfection-api-panel-icon-size13 .panel-button StIcon { icon-size: 13px; } -.just-perfection-api-panel-icon-size14 .system-status-icon, .just-perfection-api-panel-icon-size14 .panel-button StIcon { icon-size: 14px; } -.just-perfection-api-panel-icon-size15 .system-status-icon, .just-perfection-api-panel-icon-size15 .panel-button StIcon { icon-size: 15px; } -.just-perfection-api-panel-icon-size16 .system-status-icon, .just-perfection-api-panel-icon-size16 .panel-button StIcon { icon-size: 16px; } -.just-perfection-api-panel-icon-size17 .system-status-icon, .just-perfection-api-panel-icon-size17 .panel-button StIcon { icon-size: 17px; } -.just-perfection-api-panel-icon-size18 .system-status-icon, .just-perfection-api-panel-icon-size18 .panel-button StIcon { icon-size: 18px; } -.just-perfection-api-panel-icon-size19 .system-status-icon, .just-perfection-api-panel-icon-size19 .panel-button StIcon { icon-size: 19px; } -.just-perfection-api-panel-icon-size20 .system-status-icon, .just-perfection-api-panel-icon-size20 .panel-button StIcon { icon-size: 20px; } -.just-perfection-api-panel-icon-size21 .system-status-icon, .just-perfection-api-panel-icon-size21 .panel-button StIcon { icon-size: 21px; } -.just-perfection-api-panel-icon-size22 .system-status-icon, .just-perfection-api-panel-icon-size22 .panel-button StIcon { icon-size: 22px; } -.just-perfection-api-panel-icon-size23 .system-status-icon, .just-perfection-api-panel-icon-size23 .panel-button StIcon { icon-size: 23px; } -.just-perfection-api-panel-icon-size24 .system-status-icon, .just-perfection-api-panel-icon-size24 .panel-button StIcon { icon-size: 24px; } -.just-perfection-api-panel-icon-size25 .system-status-icon, .just-perfection-api-panel-icon-size25 .panel-button StIcon { icon-size: 25px; } -.just-perfection-api-panel-icon-size26 .system-status-icon, .just-perfection-api-panel-icon-size26 .panel-button StIcon { icon-size: 26px; } -.just-perfection-api-panel-icon-size27 .system-status-icon, .just-perfection-api-panel-icon-size27 .panel-button StIcon { icon-size: 27px; } -.just-perfection-api-panel-icon-size28 .system-status-icon, .just-perfection-api-panel-icon-size28 .panel-button StIcon { icon-size: 28px; } -.just-perfection-api-panel-icon-size29 .system-status-icon, .just-perfection-api-panel-icon-size29 .panel-button StIcon { icon-size: 29px; } -.just-perfection-api-panel-icon-size30 .system-status-icon, .just-perfection-api-panel-icon-size30 .panel-button StIcon { icon-size: 30px; } -.just-perfection-api-panel-icon-size31 .system-status-icon, .just-perfection-api-panel-icon-size31 .panel-button StIcon { icon-size: 31px; } -.just-perfection-api-panel-icon-size32 .system-status-icon, .just-perfection-api-panel-icon-size32 .panel-button StIcon { icon-size: 32px; } -.just-perfection-api-panel-icon-size33 .system-status-icon, .just-perfection-api-panel-icon-size33 .panel-button StIcon { icon-size: 33px; } -.just-perfection-api-panel-icon-size34 .system-status-icon, .just-perfection-api-panel-icon-size34 .panel-button StIcon { icon-size: 34px; } -.just-perfection-api-panel-icon-size35 .system-status-icon, .just-perfection-api-panel-icon-size35 .panel-button StIcon { icon-size: 35px; } -.just-perfection-api-panel-icon-size36 .system-status-icon, .just-perfection-api-panel-icon-size36 .panel-button StIcon { icon-size: 36px; } -.just-perfection-api-panel-icon-size37 .system-status-icon, .just-perfection-api-panel-icon-size37 .panel-button StIcon { icon-size: 37px; } -.just-perfection-api-panel-icon-size38 .system-status-icon, .just-perfection-api-panel-icon-size38 .panel-button StIcon { icon-size: 38px; } -.just-perfection-api-panel-icon-size39 .system-status-icon, .just-perfection-api-panel-icon-size39 .panel-button StIcon { icon-size: 39px; } -.just-perfection-api-panel-icon-size40 .system-status-icon, .just-perfection-api-panel-icon-size40 .panel-button StIcon { icon-size: 40px; } -.just-perfection-api-panel-icon-size41 .system-status-icon, .just-perfection-api-panel-icon-size41 .panel-button StIcon { icon-size: 41px; } -.just-perfection-api-panel-icon-size42 .system-status-icon, .just-perfection-api-panel-icon-size42 .panel-button StIcon { icon-size: 42px; } -.just-perfection-api-panel-icon-size43 .system-status-icon, .just-perfection-api-panel-icon-size43 .panel-button StIcon { icon-size: 43px; } -.just-perfection-api-panel-icon-size44 .system-status-icon, .just-perfection-api-panel-icon-size44 .panel-button StIcon { icon-size: 44px; } -.just-perfection-api-panel-icon-size45 .system-status-icon, .just-perfection-api-panel-icon-size45 .panel-button StIcon { icon-size: 45px; } -.just-perfection-api-panel-icon-size46 .system-status-icon, .just-perfection-api-panel-icon-size46 .panel-button StIcon { icon-size: 46px; } -.just-perfection-api-panel-icon-size47 .system-status-icon, .just-perfection-api-panel-icon-size47 .panel-button StIcon { icon-size: 47px; } -.just-perfection-api-panel-icon-size48 .system-status-icon, .just-perfection-api-panel-icon-size48 .panel-button StIcon { icon-size: 48px; } -.just-perfection-api-panel-icon-size49 .system-status-icon, .just-perfection-api-panel-icon-size49 .panel-button StIcon { icon-size: 49px; } -.just-perfection-api-panel-icon-size50 .system-status-icon, .just-perfection-api-panel-icon-size50 .panel-button StIcon { icon-size: 50px; } -.just-perfection-api-panel-icon-size51 .system-status-icon, .just-perfection-api-panel-icon-size51 .panel-button StIcon { icon-size: 51px; } -.just-perfection-api-panel-icon-size52 .system-status-icon, .just-perfection-api-panel-icon-size52 .panel-button StIcon { icon-size: 52px; } -.just-perfection-api-panel-icon-size53 .system-status-icon, .just-perfection-api-panel-icon-size53 .panel-button StIcon { icon-size: 53px; } -.just-perfection-api-panel-icon-size54 .system-status-icon, .just-perfection-api-panel-icon-size54 .panel-button StIcon { icon-size: 54px; } -.just-perfection-api-panel-icon-size55 .system-status-icon, .just-perfection-api-panel-icon-size55 .panel-button StIcon { icon-size: 55px; } -.just-perfection-api-panel-icon-size56 .system-status-icon, .just-perfection-api-panel-icon-size56 .panel-button StIcon { icon-size: 56px; } -.just-perfection-api-panel-icon-size57 .system-status-icon, .just-perfection-api-panel-icon-size57 .panel-button StIcon { icon-size: 57px; } -.just-perfection-api-panel-icon-size58 .system-status-icon, .just-perfection-api-panel-icon-size58 .panel-button StIcon { icon-size: 58px; } -.just-perfection-api-panel-icon-size59 .system-status-icon, .just-perfection-api-panel-icon-size59 .panel-button StIcon { icon-size: 59px; } -.just-perfection-api-panel-icon-size60 .system-status-icon, .just-perfection-api-panel-icon-size60 .panel-button StIcon { icon-size: 60px; } - -.just-perfection-api-controls-manager-spacing-size0 .controls-manager, .just-perfection-api-controls-manager-spacing-size0 .secondary-monitor-workspaces { spacing: 0; } -.just-perfection-api-controls-manager-spacing-size1 .controls-manager, .just-perfection-api-controls-manager-spacing-size1 .secondary-monitor-workspaces { spacing: 1px; } -.just-perfection-api-controls-manager-spacing-size2 .controls-manager, .just-perfection-api-controls-manager-spacing-size2 .secondary-monitor-workspaces { spacing: 2px; } -.just-perfection-api-controls-manager-spacing-size3 .controls-manager, .just-perfection-api-controls-manager-spacing-size3 .secondary-monitor-workspaces { spacing: 3px; } -.just-perfection-api-controls-manager-spacing-size4 .controls-manager, .just-perfection-api-controls-manager-spacing-size4 .secondary-monitor-workspaces { spacing: 4px; } -.just-perfection-api-controls-manager-spacing-size5 .controls-manager, .just-perfection-api-controls-manager-spacing-size5 .secondary-monitor-workspaces { spacing: 5px; } -.just-perfection-api-controls-manager-spacing-size6 .controls-manager, .just-perfection-api-controls-manager-spacing-size6 .secondary-monitor-workspaces { spacing: 6px; } -.just-perfection-api-controls-manager-spacing-size7 .controls-manager, .just-perfection-api-controls-manager-spacing-size7 .secondary-monitor-workspaces { spacing: 7px; } -.just-perfection-api-controls-manager-spacing-size8 .controls-manager, .just-perfection-api-controls-manager-spacing-size8 .secondary-monitor-workspaces { spacing: 8px; } -.just-perfection-api-controls-manager-spacing-size9 .controls-manager, .just-perfection-api-controls-manager-spacing-size9 .secondary-monitor-workspaces { spacing: 9px; } -.just-perfection-api-controls-manager-spacing-size10 .controls-manager, .just-perfection-api-controls-manager-spacing-size10 .secondary-monitor-workspaces { spacing: 10px; } -.just-perfection-api-controls-manager-spacing-size11 .controls-manager, .just-perfection-api-controls-manager-spacing-size11 .secondary-monitor-workspaces { spacing: 11px; } -.just-perfection-api-controls-manager-spacing-size12 .controls-manager, .just-perfection-api-controls-manager-spacing-size12 .secondary-monitor-workspaces { spacing: 12px; } -.just-perfection-api-controls-manager-spacing-size13 .controls-manager, .just-perfection-api-controls-manager-spacing-size13 .secondary-monitor-workspaces { spacing: 13px; } -.just-perfection-api-controls-manager-spacing-size14 .controls-manager, .just-perfection-api-controls-manager-spacing-size14 .secondary-monitor-workspaces { spacing: 14px; } -.just-perfection-api-controls-manager-spacing-size15 .controls-manager, .just-perfection-api-controls-manager-spacing-size15 .secondary-monitor-workspaces { spacing: 15px; } -.just-perfection-api-controls-manager-spacing-size16 .controls-manager, .just-perfection-api-controls-manager-spacing-size16 .secondary-monitor-workspaces { spacing: 16px; } -.just-perfection-api-controls-manager-spacing-size17 .controls-manager, .just-perfection-api-controls-manager-spacing-size17 .secondary-monitor-workspaces { spacing: 17px; } -.just-perfection-api-controls-manager-spacing-size18 .controls-manager, .just-perfection-api-controls-manager-spacing-size18 .secondary-monitor-workspaces { spacing: 18px; } -.just-perfection-api-controls-manager-spacing-size19 .controls-manager, .just-perfection-api-controls-manager-spacing-size19 .secondary-monitor-workspaces { spacing: 19px; } -.just-perfection-api-controls-manager-spacing-size20 .controls-manager, .just-perfection-api-controls-manager-spacing-size20 .secondary-monitor-workspaces { spacing: 20px; } -.just-perfection-api-controls-manager-spacing-size21 .controls-manager, .just-perfection-api-controls-manager-spacing-size21 .secondary-monitor-workspaces { spacing: 21px; } -.just-perfection-api-controls-manager-spacing-size22 .controls-manager, .just-perfection-api-controls-manager-spacing-size22 .secondary-monitor-workspaces { spacing: 22px; } -.just-perfection-api-controls-manager-spacing-size23 .controls-manager, .just-perfection-api-controls-manager-spacing-size23 .secondary-monitor-workspaces { spacing: 23px; } -.just-perfection-api-controls-manager-spacing-size24 .controls-manager, .just-perfection-api-controls-manager-spacing-size24 .secondary-monitor-workspaces { spacing: 24px; } -.just-perfection-api-controls-manager-spacing-size25 .controls-manager, .just-perfection-api-controls-manager-spacing-size25 .secondary-monitor-workspaces { spacing: 25px; } -.just-perfection-api-controls-manager-spacing-size26 .controls-manager, .just-perfection-api-controls-manager-spacing-size26 .secondary-monitor-workspaces { spacing: 26px; } -.just-perfection-api-controls-manager-spacing-size27 .controls-manager, .just-perfection-api-controls-manager-spacing-size27 .secondary-monitor-workspaces { spacing: 27px; } -.just-perfection-api-controls-manager-spacing-size28 .controls-manager, .just-perfection-api-controls-manager-spacing-size28 .secondary-monitor-workspaces { spacing: 28px; } -.just-perfection-api-controls-manager-spacing-size29 .controls-manager, .just-perfection-api-controls-manager-spacing-size29 .secondary-monitor-workspaces { spacing: 29px; } -.just-perfection-api-controls-manager-spacing-size30 .controls-manager, .just-perfection-api-controls-manager-spacing-size30 .secondary-monitor-workspaces { spacing: 30px; } -.just-perfection-api-controls-manager-spacing-size31 .controls-manager, .just-perfection-api-controls-manager-spacing-size31 .secondary-monitor-workspaces { spacing: 31px; } -.just-perfection-api-controls-manager-spacing-size32 .controls-manager, .just-perfection-api-controls-manager-spacing-size32 .secondary-monitor-workspaces { spacing: 32px; } -.just-perfection-api-controls-manager-spacing-size33 .controls-manager, .just-perfection-api-controls-manager-spacing-size33 .secondary-monitor-workspaces { spacing: 33px; } -.just-perfection-api-controls-manager-spacing-size34 .controls-manager, .just-perfection-api-controls-manager-spacing-size34 .secondary-monitor-workspaces { spacing: 34px; } -.just-perfection-api-controls-manager-spacing-size35 .controls-manager, .just-perfection-api-controls-manager-spacing-size35 .secondary-monitor-workspaces { spacing: 35px; } -.just-perfection-api-controls-manager-spacing-size36 .controls-manager, .just-perfection-api-controls-manager-spacing-size36 .secondary-monitor-workspaces { spacing: 36px; } -.just-perfection-api-controls-manager-spacing-size37 .controls-manager, .just-perfection-api-controls-manager-spacing-size37 .secondary-monitor-workspaces { spacing: 37px; } -.just-perfection-api-controls-manager-spacing-size38 .controls-manager, .just-perfection-api-controls-manager-spacing-size38 .secondary-monitor-workspaces { spacing: 38px; } -.just-perfection-api-controls-manager-spacing-size39 .controls-manager, .just-perfection-api-controls-manager-spacing-size39 .secondary-monitor-workspaces { spacing: 39px; } -.just-perfection-api-controls-manager-spacing-size40 .controls-manager, .just-perfection-api-controls-manager-spacing-size40 .secondary-monitor-workspaces { spacing: 40px; } -.just-perfection-api-controls-manager-spacing-size41 .controls-manager, .just-perfection-api-controls-manager-spacing-size41 .secondary-monitor-workspaces { spacing: 41px; } -.just-perfection-api-controls-manager-spacing-size42 .controls-manager, .just-perfection-api-controls-manager-spacing-size42 .secondary-monitor-workspaces { spacing: 42px; } -.just-perfection-api-controls-manager-spacing-size43 .controls-manager, .just-perfection-api-controls-manager-spacing-size43 .secondary-monitor-workspaces { spacing: 43px; } -.just-perfection-api-controls-manager-spacing-size44 .controls-manager, .just-perfection-api-controls-manager-spacing-size44 .secondary-monitor-workspaces { spacing: 44px; } -.just-perfection-api-controls-manager-spacing-size45 .controls-manager, .just-perfection-api-controls-manager-spacing-size45 .secondary-monitor-workspaces { spacing: 45px; } -.just-perfection-api-controls-manager-spacing-size46 .controls-manager, .just-perfection-api-controls-manager-spacing-size46 .secondary-monitor-workspaces { spacing: 46px; } -.just-perfection-api-controls-manager-spacing-size47 .controls-manager, .just-perfection-api-controls-manager-spacing-size47 .secondary-monitor-workspaces { spacing: 47px; } -.just-perfection-api-controls-manager-spacing-size48 .controls-manager, .just-perfection-api-controls-manager-spacing-size48 .secondary-monitor-workspaces { spacing: 48px; } -.just-perfection-api-controls-manager-spacing-size49 .controls-manager, .just-perfection-api-controls-manager-spacing-size49 .secondary-monitor-workspaces { spacing: 49px; } -.just-perfection-api-controls-manager-spacing-size50 .controls-manager, .just-perfection-api-controls-manager-spacing-size50 .secondary-monitor-workspaces { spacing: 50px; } -.just-perfection-api-controls-manager-spacing-size51 .controls-manager, .just-perfection-api-controls-manager-spacing-size51 .secondary-monitor-workspaces { spacing: 51px; } -.just-perfection-api-controls-manager-spacing-size52 .controls-manager, .just-perfection-api-controls-manager-spacing-size52 .secondary-monitor-workspaces { spacing: 52px; } -.just-perfection-api-controls-manager-spacing-size53 .controls-manager, .just-perfection-api-controls-manager-spacing-size53 .secondary-monitor-workspaces { spacing: 53px; } -.just-perfection-api-controls-manager-spacing-size54 .controls-manager, .just-perfection-api-controls-manager-spacing-size54 .secondary-monitor-workspaces { spacing: 54px; } -.just-perfection-api-controls-manager-spacing-size55 .controls-manager, .just-perfection-api-controls-manager-spacing-size55 .secondary-monitor-workspaces { spacing: 55px; } -.just-perfection-api-controls-manager-spacing-size56 .controls-manager, .just-perfection-api-controls-manager-spacing-size56 .secondary-monitor-workspaces { spacing: 56px; } -.just-perfection-api-controls-manager-spacing-size57 .controls-manager, .just-perfection-api-controls-manager-spacing-size57 .secondary-monitor-workspaces { spacing: 57px; } -.just-perfection-api-controls-manager-spacing-size58 .controls-manager, .just-perfection-api-controls-manager-spacing-size58 .secondary-monitor-workspaces { spacing: 58px; } -.just-perfection-api-controls-manager-spacing-size59 .controls-manager, .just-perfection-api-controls-manager-spacing-size59 .secondary-monitor-workspaces { spacing: 59px; } -.just-perfection-api-controls-manager-spacing-size60 .controls-manager, .just-perfection-api-controls-manager-spacing-size60 .secondary-monitor-workspaces { spacing: 60px; } -.just-perfection-api-controls-manager-spacing-size61 .controls-manager, .just-perfection-api-controls-manager-spacing-size61 .secondary-monitor-workspaces { spacing: 61px; } -.just-perfection-api-controls-manager-spacing-size62 .controls-manager, .just-perfection-api-controls-manager-spacing-size62 .secondary-monitor-workspaces { spacing: 62px; } -.just-perfection-api-controls-manager-spacing-size63 .controls-manager, .just-perfection-api-controls-manager-spacing-size63 .secondary-monitor-workspaces { spacing: 63px; } -.just-perfection-api-controls-manager-spacing-size64 .controls-manager, .just-perfection-api-controls-manager-spacing-size64 .secondary-monitor-workspaces { spacing: 64px; } -.just-perfection-api-controls-manager-spacing-size65 .controls-manager, .just-perfection-api-controls-manager-spacing-size65 .secondary-monitor-workspaces { spacing: 65px; } -.just-perfection-api-controls-manager-spacing-size66 .controls-manager, .just-perfection-api-controls-manager-spacing-size66 .secondary-monitor-workspaces { spacing: 66px; } -.just-perfection-api-controls-manager-spacing-size67 .controls-manager, .just-perfection-api-controls-manager-spacing-size67 .secondary-monitor-workspaces { spacing: 67px; } -.just-perfection-api-controls-manager-spacing-size68 .controls-manager, .just-perfection-api-controls-manager-spacing-size68 .secondary-monitor-workspaces { spacing: 68px; } -.just-perfection-api-controls-manager-spacing-size69 .controls-manager, .just-perfection-api-controls-manager-spacing-size69 .secondary-monitor-workspaces { spacing: 69px; } -.just-perfection-api-controls-manager-spacing-size70 .controls-manager, .just-perfection-api-controls-manager-spacing-size70 .secondary-monitor-workspaces { spacing: 70px; } -.just-perfection-api-controls-manager-spacing-size71 .controls-manager, .just-perfection-api-controls-manager-spacing-size71 .secondary-monitor-workspaces { spacing: 71px; } -.just-perfection-api-controls-manager-spacing-size72 .controls-manager, .just-perfection-api-controls-manager-spacing-size72 .secondary-monitor-workspaces { spacing: 72px; } -.just-perfection-api-controls-manager-spacing-size73 .controls-manager, .just-perfection-api-controls-manager-spacing-size73 .secondary-monitor-workspaces { spacing: 73px; } -.just-perfection-api-controls-manager-spacing-size74 .controls-manager, .just-perfection-api-controls-manager-spacing-size74 .secondary-monitor-workspaces { spacing: 74px; } -.just-perfection-api-controls-manager-spacing-size75 .controls-manager, .just-perfection-api-controls-manager-spacing-size75 .secondary-monitor-workspaces { spacing: 75px; } -.just-perfection-api-controls-manager-spacing-size76 .controls-manager, .just-perfection-api-controls-manager-spacing-size76 .secondary-monitor-workspaces { spacing: 76px; } -.just-perfection-api-controls-manager-spacing-size77 .controls-manager, .just-perfection-api-controls-manager-spacing-size77 .secondary-monitor-workspaces { spacing: 77px; } -.just-perfection-api-controls-manager-spacing-size78 .controls-manager, .just-perfection-api-controls-manager-spacing-size78 .secondary-monitor-workspaces { spacing: 78px; } -.just-perfection-api-controls-manager-spacing-size79 .controls-manager, .just-perfection-api-controls-manager-spacing-size79 .secondary-monitor-workspaces { spacing: 79px; } -.just-perfection-api-controls-manager-spacing-size80 .controls-manager, .just-perfection-api-controls-manager-spacing-size80 .secondary-monitor-workspaces { spacing: 80px; } -.just-perfection-api-controls-manager-spacing-size81 .controls-manager, .just-perfection-api-controls-manager-spacing-size81 .secondary-monitor-workspaces { spacing: 81px; } -.just-perfection-api-controls-manager-spacing-size82 .controls-manager, .just-perfection-api-controls-manager-spacing-size82 .secondary-monitor-workspaces { spacing: 82px; } -.just-perfection-api-controls-manager-spacing-size83 .controls-manager, .just-perfection-api-controls-manager-spacing-size83 .secondary-monitor-workspaces { spacing: 83px; } -.just-perfection-api-controls-manager-spacing-size84 .controls-manager, .just-perfection-api-controls-manager-spacing-size84 .secondary-monitor-workspaces { spacing: 84px; } -.just-perfection-api-controls-manager-spacing-size85 .controls-manager, .just-perfection-api-controls-manager-spacing-size85 .secondary-monitor-workspaces { spacing: 85px; } -.just-perfection-api-controls-manager-spacing-size86 .controls-manager, .just-perfection-api-controls-manager-spacing-size86 .secondary-monitor-workspaces { spacing: 86px; } -.just-perfection-api-controls-manager-spacing-size87 .controls-manager, .just-perfection-api-controls-manager-spacing-size87 .secondary-monitor-workspaces { spacing: 87px; } -.just-perfection-api-controls-manager-spacing-size88 .controls-manager, .just-perfection-api-controls-manager-spacing-size88 .secondary-monitor-workspaces { spacing: 88px; } -.just-perfection-api-controls-manager-spacing-size89 .controls-manager, .just-perfection-api-controls-manager-spacing-size89 .secondary-monitor-workspaces { spacing: 89px; } -.just-perfection-api-controls-manager-spacing-size90 .controls-manager, .just-perfection-api-controls-manager-spacing-size90 .secondary-monitor-workspaces { spacing: 90px; } -.just-perfection-api-controls-manager-spacing-size91 .controls-manager, .just-perfection-api-controls-manager-spacing-size91 .secondary-monitor-workspaces { spacing: 91px; } -.just-perfection-api-controls-manager-spacing-size92 .controls-manager, .just-perfection-api-controls-manager-spacing-size92 .secondary-monitor-workspaces { spacing: 92px; } -.just-perfection-api-controls-manager-spacing-size93 .controls-manager, .just-perfection-api-controls-manager-spacing-size93 .secondary-monitor-workspaces { spacing: 93px; } -.just-perfection-api-controls-manager-spacing-size94 .controls-manager, .just-perfection-api-controls-manager-spacing-size94 .secondary-monitor-workspaces { spacing: 94px; } -.just-perfection-api-controls-manager-spacing-size95 .controls-manager, .just-perfection-api-controls-manager-spacing-size95 .secondary-monitor-workspaces { spacing: 95px; } -.just-perfection-api-controls-manager-spacing-size96 .controls-manager, .just-perfection-api-controls-manager-spacing-size96 .secondary-monitor-workspaces { spacing: 96px; } -.just-perfection-api-controls-manager-spacing-size97 .controls-manager, .just-perfection-api-controls-manager-spacing-size97 .secondary-monitor-workspaces { spacing: 97px; } -.just-perfection-api-controls-manager-spacing-size98 .controls-manager, .just-perfection-api-controls-manager-spacing-size98 .secondary-monitor-workspaces { spacing: 98px; } -.just-perfection-api-controls-manager-spacing-size99 .controls-manager, .just-perfection-api-controls-manager-spacing-size99 .secondary-monitor-workspaces { spacing: 99px; } -.just-perfection-api-controls-manager-spacing-size100 .controls-manager, .just-perfection-api-controls-manager-spacing-size100 .secondary-monitor-workspaces { spacing: 100px; } -.just-perfection-api-controls-manager-spacing-size101 .controls-manager, .just-perfection-api-controls-manager-spacing-size101 .secondary-monitor-workspaces { spacing: 101px; } -.just-perfection-api-controls-manager-spacing-size102 .controls-manager, .just-perfection-api-controls-manager-spacing-size102 .secondary-monitor-workspaces { spacing: 102px; } -.just-perfection-api-controls-manager-spacing-size103 .controls-manager, .just-perfection-api-controls-manager-spacing-size103 .secondary-monitor-workspaces { spacing: 103px; } -.just-perfection-api-controls-manager-spacing-size104 .controls-manager, .just-perfection-api-controls-manager-spacing-size104 .secondary-monitor-workspaces { spacing: 104px; } -.just-perfection-api-controls-manager-spacing-size105 .controls-manager, .just-perfection-api-controls-manager-spacing-size105 .secondary-monitor-workspaces { spacing: 105px; } -.just-perfection-api-controls-manager-spacing-size106 .controls-manager, .just-perfection-api-controls-manager-spacing-size106 .secondary-monitor-workspaces { spacing: 106px; } -.just-perfection-api-controls-manager-spacing-size107 .controls-manager, .just-perfection-api-controls-manager-spacing-size107 .secondary-monitor-workspaces { spacing: 107px; } -.just-perfection-api-controls-manager-spacing-size108 .controls-manager, .just-perfection-api-controls-manager-spacing-size108 .secondary-monitor-workspaces { spacing: 108px; } -.just-perfection-api-controls-manager-spacing-size109 .controls-manager, .just-perfection-api-controls-manager-spacing-size109 .secondary-monitor-workspaces { spacing: 109px; } -.just-perfection-api-controls-manager-spacing-size110 .controls-manager, .just-perfection-api-controls-manager-spacing-size110 .secondary-monitor-workspaces { spacing: 110px; } -.just-perfection-api-controls-manager-spacing-size111 .controls-manager, .just-perfection-api-controls-manager-spacing-size111 .secondary-monitor-workspaces { spacing: 111px; } -.just-perfection-api-controls-manager-spacing-size112 .controls-manager, .just-perfection-api-controls-manager-spacing-size112 .secondary-monitor-workspaces { spacing: 112px; } -.just-perfection-api-controls-manager-spacing-size113 .controls-manager, .just-perfection-api-controls-manager-spacing-size113 .secondary-monitor-workspaces { spacing: 113px; } -.just-perfection-api-controls-manager-spacing-size114 .controls-manager, .just-perfection-api-controls-manager-spacing-size114 .secondary-monitor-workspaces { spacing: 114px; } -.just-perfection-api-controls-manager-spacing-size115 .controls-manager, .just-perfection-api-controls-manager-spacing-size115 .secondary-monitor-workspaces { spacing: 115px; } -.just-perfection-api-controls-manager-spacing-size116 .controls-manager, .just-perfection-api-controls-manager-spacing-size116 .secondary-monitor-workspaces { spacing: 116px; } -.just-perfection-api-controls-manager-spacing-size117 .controls-manager, .just-perfection-api-controls-manager-spacing-size117 .secondary-monitor-workspaces { spacing: 117px; } -.just-perfection-api-controls-manager-spacing-size118 .controls-manager, .just-perfection-api-controls-manager-spacing-size118 .secondary-monitor-workspaces { spacing: 118px; } -.just-perfection-api-controls-manager-spacing-size119 .controls-manager, .just-perfection-api-controls-manager-spacing-size119 .secondary-monitor-workspaces { spacing: 119px; } -.just-perfection-api-controls-manager-spacing-size120 .controls-manager, .just-perfection-api-controls-manager-spacing-size120 .secondary-monitor-workspaces { spacing: 120px; } -.just-perfection-api-controls-manager-spacing-size121 .controls-manager, .just-perfection-api-controls-manager-spacing-size121 .secondary-monitor-workspaces { spacing: 121px; } -.just-perfection-api-controls-manager-spacing-size122 .controls-manager, .just-perfection-api-controls-manager-spacing-size122 .secondary-monitor-workspaces { spacing: 122px; } -.just-perfection-api-controls-manager-spacing-size123 .controls-manager, .just-perfection-api-controls-manager-spacing-size123 .secondary-monitor-workspaces { spacing: 123px; } -.just-perfection-api-controls-manager-spacing-size124 .controls-manager, .just-perfection-api-controls-manager-spacing-size124 .secondary-monitor-workspaces { spacing: 124px; } -.just-perfection-api-controls-manager-spacing-size125 .controls-manager, .just-perfection-api-controls-manager-spacing-size125 .secondary-monitor-workspaces { spacing: 125px; } -.just-perfection-api-controls-manager-spacing-size126 .controls-manager, .just-perfection-api-controls-manager-spacing-size126 .secondary-monitor-workspaces { spacing: 126px; } -.just-perfection-api-controls-manager-spacing-size127 .controls-manager, .just-perfection-api-controls-manager-spacing-size127 .secondary-monitor-workspaces { spacing: 127px; } -.just-perfection-api-controls-manager-spacing-size128 .controls-manager, .just-perfection-api-controls-manager-spacing-size128 .secondary-monitor-workspaces { spacing: 128px; } -.just-perfection-api-controls-manager-spacing-size129 .controls-manager, .just-perfection-api-controls-manager-spacing-size129 .secondary-monitor-workspaces { spacing: 129px; } -.just-perfection-api-controls-manager-spacing-size130 .controls-manager, .just-perfection-api-controls-manager-spacing-size130 .secondary-monitor-workspaces { spacing: 130px; } -.just-perfection-api-controls-manager-spacing-size131 .controls-manager, .just-perfection-api-controls-manager-spacing-size131 .secondary-monitor-workspaces { spacing: 131px; } -.just-perfection-api-controls-manager-spacing-size132 .controls-manager, .just-perfection-api-controls-manager-spacing-size132 .secondary-monitor-workspaces { spacing: 132px; } -.just-perfection-api-controls-manager-spacing-size133 .controls-manager, .just-perfection-api-controls-manager-spacing-size133 .secondary-monitor-workspaces { spacing: 133px; } -.just-perfection-api-controls-manager-spacing-size134 .controls-manager, .just-perfection-api-controls-manager-spacing-size134 .secondary-monitor-workspaces { spacing: 134px; } -.just-perfection-api-controls-manager-spacing-size135 .controls-manager, .just-perfection-api-controls-manager-spacing-size135 .secondary-monitor-workspaces { spacing: 135px; } -.just-perfection-api-controls-manager-spacing-size136 .controls-manager, .just-perfection-api-controls-manager-spacing-size136 .secondary-monitor-workspaces { spacing: 136px; } -.just-perfection-api-controls-manager-spacing-size137 .controls-manager, .just-perfection-api-controls-manager-spacing-size137 .secondary-monitor-workspaces { spacing: 137px; } -.just-perfection-api-controls-manager-spacing-size138 .controls-manager, .just-perfection-api-controls-manager-spacing-size138 .secondary-monitor-workspaces { spacing: 138px; } -.just-perfection-api-controls-manager-spacing-size139 .controls-manager, .just-perfection-api-controls-manager-spacing-size139 .secondary-monitor-workspaces { spacing: 139px; } -.just-perfection-api-controls-manager-spacing-size140 .controls-manager, .just-perfection-api-controls-manager-spacing-size140 .secondary-monitor-workspaces { spacing: 140px; } -.just-perfection-api-controls-manager-spacing-size141 .controls-manager, .just-perfection-api-controls-manager-spacing-size141 .secondary-monitor-workspaces { spacing: 141px; } -.just-perfection-api-controls-manager-spacing-size142 .controls-manager, .just-perfection-api-controls-manager-spacing-size142 .secondary-monitor-workspaces { spacing: 142px; } -.just-perfection-api-controls-manager-spacing-size143 .controls-manager, .just-perfection-api-controls-manager-spacing-size143 .secondary-monitor-workspaces { spacing: 143px; } -.just-perfection-api-controls-manager-spacing-size144 .controls-manager, .just-perfection-api-controls-manager-spacing-size144 .secondary-monitor-workspaces { spacing: 144px; } -.just-perfection-api-controls-manager-spacing-size145 .controls-manager, .just-perfection-api-controls-manager-spacing-size145 .secondary-monitor-workspaces { spacing: 145px; } -.just-perfection-api-controls-manager-spacing-size146 .controls-manager, .just-perfection-api-controls-manager-spacing-size146 .secondary-monitor-workspaces { spacing: 146px; } -.just-perfection-api-controls-manager-spacing-size147 .controls-manager, .just-perfection-api-controls-manager-spacing-size147 .secondary-monitor-workspaces { spacing: 147px; } -.just-perfection-api-controls-manager-spacing-size148 .controls-manager, .just-perfection-api-controls-manager-spacing-size148 .secondary-monitor-workspaces { spacing: 148px; } -.just-perfection-api-controls-manager-spacing-size149 .controls-manager, .just-perfection-api-controls-manager-spacing-size149 .secondary-monitor-workspaces { spacing: 149px; } -.just-perfection-api-controls-manager-spacing-size150 .controls-manager, .just-perfection-api-controls-manager-spacing-size150 .secondary-monitor-workspaces { spacing: 150px; } - -.just-perfection-api-osd-position-top .osd-window { margin: 4em 2.5em; } -.just-perfection-api-osd-position-center .osd-window { margin: 4em 3.5em; } -.just-perfection-api-osd-position-bottom .osd-window { margin: 4em 3em; } - -.just-perfection-api-no-dash-separator .dash-separator, -.just-perfection-api-no-dash-app-running-dot #dash .app-well-app-running-dot -{ - width: 0; - height: 0; - spacing: 0; - padding: 0; - margin: 0; -} - -.just-perfection-api-no-workspaces-in-app-grid .workspace-background -{ - background-color: transparent; - box-shadow: 0 4px 16px 4px transparent; -} - -.just-perfection-api-no-power-icon .power-status StIcon -{ - width: 0; - height: 0; - padding: 0; -} diff --git a/main.sh b/main.sh index d989810..2fb13a2 100755 --- a/main.sh +++ b/main.sh @@ -6,13 +6,13 @@ set -e echo "$PIKA_BUILD_ARCH" > pika-build-arch -VERSION="46.0" +VERSION="61" # Clone Upstream cd ./gnome-shell-extension-just-perfection/ # Get build deps -LOGNAME=root dh_make --createorig -y -l -p gnome-shell-extension-just-perfection_"$VERSION" || echo "dh-make: Ignoring Last Error" +LOGNAME=root dh_make --createorig -y -l -p gnome-shell-extension-arc-menu_"$VERSION" || echo "dh-make: Ignoring Last Error" apt-get build-dep ./ -y # Build package