Oreon-Lime-R2/arcmenu/arc-menu-27/menuWidgets.js

3693 lines
133 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* ArcMenu - A traditional application menu for GNOME 3
*
* ArcMenu Lead Developer and Maintainer
* Andrew Zaech https://gitlab.com/AndrewZaech
*
* ArcMenu Founder, Former Maintainer, and Former Graphic Designer
* LinxGem33 https://gitlab.com/LinxGem33 - (No Longer Active)
*
* 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, see <http://www.gnu.org/licenses/>.
*/
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const {Atk, Clutter, Gio, GLib, GMenu, GObject, Gtk, Shell, St} = imports.gi;
const AccountsService = imports.gi.AccountsService;
const AppFavorites = imports.ui.appFavorites;
const BoxPointer = imports.ui.boxpointer;
const Constants = Me.imports.constants;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const Signals = imports.signals;
const _SystemActions = imports.misc.systemActions;
const SystemActions = _SystemActions.getDefault();
const Util = imports.misc.util;
const Utils = Me.imports.utils;
const _ = Gettext.gettext;
const { loadInterfaceXML } = imports.misc.fileUtils;
const ClocksIntegrationIface = loadInterfaceXML('org.gnome.Shell.ClocksIntegration');
const ClocksProxy = Gio.DBusProxy.makeProxyWrapper(ClocksIntegrationIface);
Gio._promisify(Gio._LocalFilePrototype, 'query_info_async', 'query_info_finish');
Gio._promisify(Gio._LocalFilePrototype, 'set_attributes_async', 'set_attributes_finish');
const INDICATOR_ICON_SIZE = 18;
const USER_AVATAR_SIZE = 28;
function activatePowerOption(powerType, arcMenu){
arcMenu.itemActivated(BoxPointer.PopupAnimation.NONE);
if(powerType === Constants.PowerType.POWER_OFF)
SystemActions.activatePowerOff();
else if(powerType === Constants.PowerType.RESTART)
SystemActions.activateRestart ? SystemActions.activateRestart() : SystemActions.activatePowerOff();
else if(powerType === Constants.PowerType.LOCK)
SystemActions.activateLockScreen();
else if(powerType === Constants.PowerType.LOGOUT)
SystemActions.activateLogout();
else if(powerType === Constants.PowerType.SUSPEND)
SystemActions.activateSuspend();
else if(powerType === Constants.PowerType.HYBRID_SLEEP)
Utils.activateHybridSleep();
else if(powerType === Constants.PowerType.HIBERNATE)
Utils.activateHibernate();
}
var ApplicationContextItems = GObject.registerClass({
Signals: {
'close-context-menu': { },
},
}, class Arc_Menu_ApplicationContextItems extends St.BoxLayout{
_init(actor, app, menuLayout){
super._init({
vertical: true,
x_expand: true,
y_expand: true,
style_class: 'margin-box',
});
this._menuLayout = menuLayout;
this._settings = menuLayout._settings;
this._menuButton = menuLayout.menuButton;
this._app = app;
this.sourceActor = actor;
this.layout = this._settings.get_enum('menu-layout');
this.discreteGpuAvailable = false;
this._switcherooNotifyId = global.connect('notify::switcheroo-control',
() => this._updateDiscreteGpuAvailable());
this._updateDiscreteGpuAvailable();
}
set path(path){
this._path = path;
}
_updateDiscreteGpuAvailable() {
this._switcherooProxy = global.get_switcheroo_control();
if (this._switcherooProxy) {
let prop = this._switcherooProxy.get_cached_property('HasDualGpu');
this.discreteGpuAvailable = prop?.unpack() ?? false;
} else {
this.discreteGpuAvailable = false;
}
}
closeMenus(){
this.close();
this._menuLayout.arcMenu.toggle();
}
close(){
this.emit('close-context-menu');
}
rebuildItems(){
this.destroy_all_children();
if(this._app instanceof Shell.App){
this.appInfo = this._app.get_app_info();
let actions = this.appInfo.list_actions();
let windows = this._app.get_windows().filter(
w => !w.skip_taskbar
);
if (windows.length > 0){
let item = new PopupMenu.PopupMenuItem(_("Current Windows:"), {
reactive: false,
can_focus: false
});
item.actor.add_style_class_name('inactive');
this.add_child(item);
windows.forEach(window => {
let title = window.title ? window.title
: this._app.get_name();
let item = this._appendMenuItem(title);
item.connect('activate', () => {
this.closeMenus();
Main.activateWindow(window);
});
});
this._appendSeparator();
}
if (!this._app.is_window_backed()) {
if (this._app.can_open_new_window() && !actions.includes('new-window')) {
let newWindowItem = this._appendMenuItem(_("New Window"));
newWindowItem.connect('activate', () => {
this.closeMenus();
this._app.open_new_window(-1);
});
}
if (this.discreteGpuAvailable && this._app.state == Shell.AppState.STOPPED) {
const appPrefersNonDefaultGPU = this.appInfo.get_boolean('PrefersNonDefaultGPU');
const gpuPref = appPrefersNonDefaultGPU
? Shell.AppLaunchGpu.DEFAULT
: Shell.AppLaunchGpu.DISCRETE;
this._onGpuMenuItem = this._appendMenuItem(appPrefersNonDefaultGPU
? _('Launch using Integrated Graphics Card')
: _('Launch using Discrete Graphics Card'));
this._onGpuMenuItem.connect('activate', () => {
this.closeMenus();
this._app.launch(0, -1, gpuPref);
});
}
for (let i = 0; i < actions.length; i++) {
let action = actions[i];
let item;
if(action === "empty-trash-inactive"){
item = new PopupMenu.PopupMenuItem(this.appInfo.get_action_name(action), {reactive:false, can_focus:false});
item.actor.add_style_class_name('inactive');
this._appendSeparator();
this.add_child(item);
}
else if(action === "empty-trash"){
this._appendSeparator();
item = this._appendMenuItem(this.appInfo.get_action_name(action));
}
else{
item = this._appendMenuItem(this.appInfo.get_action_name(action));
}
item.connect('activate', (emitter, event) => {
this.closeMenus();
this._app.launch_action(action, event.get_time(), -1);
});
}
//If Trash Can, we don't want to add the rest of the entries below.
if(this.appInfo.get_string('Id') === "ArcMenu_Trash")
return false;
let desktopIcons = Main.extensionManager.lookup("desktop-icons@csoriano");
let desktopIconsNG = Main.extensionManager.lookup("ding@rastersoft.com");
if((desktopIcons && desktopIcons.stateObj) || (desktopIconsNG && desktopIconsNG.stateObj)){
this._appendSeparator();
let fileDestination = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
let src = Gio.File.new_for_path(this.appInfo.get_filename());
let dst = Gio.File.new_for_path(GLib.build_filenamev([fileDestination, src.get_basename()]));
let exists = dst.query_exists(null);
if(exists) {
let item = this._appendMenuItem(_("Delete Desktop Shortcut"));
item.connect('activate', () => {
if(src && dst){
try {
dst.delete(null);
} catch (e) {
log(`Failed to delete shortcut: ${e.message}`);
}
}
this.close();
});
}
else {
let item = this._appendMenuItem(_("Create Desktop Shortcut"));
item.connect('activate', () => {
if(src && dst){
try {
// copy_async() isn't introspectable :-(
src.copy(dst, Gio.FileCopyFlags.OVERWRITE, null, null);
this._markTrusted(dst);
} catch (e) {
log(`Failed to copy to desktop: ${e.message}`);
}
}
this.close();
});
}
}
let canFavorite = global.settings.is_writable('favorite-apps');
if (canFavorite) {
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._app.get_id());
if (isFavorite) {
let item = this._appendMenuItem(_("Remove from Favorites"));
item.connect('activate', () => {
let favs = AppFavorites.getAppFavorites();
favs.removeFavorite(this._app.get_id());
this.close();
});
} else {
let item = this._appendMenuItem(_("Add to Favorites"));
item.connect('activate', () => {
let favs = AppFavorites.getAppFavorites();
favs.addFavorite(this._app.get_id());
this.close();
});
}
}
let pinnedApps = this._settings.get_strv('pinned-app-list');
let pinnedAppID = [];
//filter pinnedApps list by every 3rd entry in list. 3rd entry contains an appID or command
for(let i = 2; i < pinnedApps.length; i += 3){
pinnedAppID.push(pinnedApps[i]);
}
let isAppPinned = pinnedAppID.find((element) => {
return element == this._app.get_id();
});
//if app is pinned and menulayout has PinnedApps category, show Unpin from ArcMenu entry
if(isAppPinned && this._menuLayout.hasPinnedApps) {
let item = this._appendMenuItem(_("Unpin from ArcMenu"));
item.connect('activate', ()=>{
this.close();
for(let i = 0; i < pinnedApps.length; i += 3){
if(pinnedApps[i + 2] === this._app.get_id()){
pinnedApps.splice(i, 3);
this._settings.set_strv('pinned-app-list', pinnedApps);
break;
}
}
});
}
else if(this._menuLayout.hasPinnedApps) {
let item = this._appendMenuItem(_("Pin to ArcMenu"));
item.connect('activate', ()=>{
this.close();
pinnedApps.push(this.appInfo.get_display_name());
pinnedApps.push('');
pinnedApps.push(this._app.get_id());
this._settings.set_strv('pinned-app-list',pinnedApps);
});
}
if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop')) {
this._appendSeparator();
let item = this._appendMenuItem(_("Show Details"));
item.connect('activate', () => {
let id = this._app.get_id();
let args = GLib.Variant.new('(ss)', [id, '']);
Gio.DBus.get(Gio.BusType.SESSION, null, (o, res) => {
let bus = Gio.DBus.get_finish(res);
bus.call('org.gnome.Software',
'/org/gnome/Software',
'org.gtk.Actions', 'Activate',
GLib.Variant.new('(sava{sv})',
['details', [args], null]),
null, 0, -1, null, null);
this.closeMenus();
});
});
}
}
}
else if(this._path){
let newWindowItem = this._appendMenuItem(_("Open Folder Location"));
newWindowItem.connect('activate', () => {
let file = Gio.File.new_for_path(this._path);
let 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();
});
}
else if(this._menuLayout.hasPinnedApps && this.sourceActor instanceof PinnedAppsMenuItem) {
let item = this._appendMenuItem(_("Unpin from ArcMenu"));
item.connect('activate', () => {
this.close();
let pinnedApps = this._settings.get_strv('pinned-app-list');
for(let i = 0; i < pinnedApps.length; i += 3){
if(pinnedApps[i + 2] === this._app){
pinnedApps.splice(i, 3);
this._settings.set_strv('pinned-app-list', pinnedApps);
break;
}
}
});
}
}
//_markTrusted function borrowed from
//https://gitlab.gnome.org/GNOME/gnome-shell-extensions/-/tree/master/extensions/apps-menu
async _markTrusted(file) {
let modeAttr = Gio.FILE_ATTRIBUTE_UNIX_MODE;
let trustedAttr = 'metadata::trusted';
let queryFlags = Gio.FileQueryInfoFlags.NONE;
let ioPriority = GLib.PRIORITY_DEFAULT;
try {
let info = await file.query_info_async(modeAttr, queryFlags, ioPriority, null);
let mode = info.get_attribute_uint32(modeAttr) | 0o100;
info.set_attribute_uint32(modeAttr, mode);
info.set_attribute_string(trustedAttr, 'yes');
await file.set_attributes_async(info, queryFlags, ioPriority, null);
// Hack: force nautilus to reload file info
info = new Gio.FileInfo();
info.set_attribute_uint64(
Gio.FILE_ATTRIBUTE_TIME_ACCESS, GLib.get_real_time());
try {
await file.set_attributes_async(info, queryFlags, ioPriority, null);
} catch (e) {
log(`Failed to update access time: ${e.message}`);
}
} catch (e) {
log(`Failed to mark file as trusted: ${e.message}`);
}
}
_appendSeparator() {
let separator = new ArcMenuSeparator(Constants.SeparatorStyle.SHORT, Constants.SeparatorAlignment.HORIZONTAL);
this.add_child(separator);
}
_appendMenuItem(labelText) {
let item = new ArcMenuPopupBaseMenuItem(this._menuLayout);
this.label = new St.Label({
text: _(labelText),
y_expand: true,
y_align: Clutter.ActorAlign.CENTER
});
item.add_child(this.label);
this.add_child(item);
return item;
}
});
var ApplicationContextMenu = class Arc_Menu_ApplicationContextMenu extends PopupMenu.PopupMenu {
constructor(actor, app, menuLayout){
super(actor, 0.0, St.Side.TOP);
this._menuLayout = menuLayout;
this._settings = menuLayout._settings;
this._menuButton = menuLayout.menuButton;
this._app = app;
this.layout = this._settings.get_enum('menu-layout');
this._boxPointer.setSourceAlignment(.20);
this._boxPointer._border.queue_repaint();
this.blockSourceEvents = true;
Main.uiGroup.add_child(this.actor);
this._menuLayout.contextMenuManager.addMenu(this);
this.contextMenuItems = new ApplicationContextItems(actor, app, menuLayout);
this.contextMenuItems.connect('close-context-menu', () => this.toggle());
this.contextMenuItems._delegate = this.contextMenuItems;
this.box.add_child(this.contextMenuItems);
this.sourceActor = actor;
this.sourceActor.connect("destroy", ()=> {
if(this.isOpen)
this.close();
Main.uiGroup.remove_child(this.actor);
this.contextMenuItems.destroy();
this.destroy();
});
}
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();
}
set path(path){
this.contextMenuItems.path = path;
}
open(animate){
if(this._menuLayout.searchResults && this.sourceActor !== this._menuLayout.searchResults.getTopResult())
this._menuLayout.searchResults.getTopResult()?.remove_style_pseudo_class('active');
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;
}
super.open(animate);
}
close(animate){
super.close(animate);
if(this.sourceActor instanceof ArcMenuButtonItem)
this.sourceActor.sync_hover();
else{
this.sourceActor.active = false;
}
this.sourceActor.sync_hover();
this.sourceActor.hovered = this.sourceActor.hover;
}
rebuildItems(){
let customStyle = this._settings.get_boolean('enable-custom-arc-menu');
if(customStyle){
this.actor.style_class = 'arc-menu-boxpointer';
this.actor.add_style_class_name('arc-menu');
}
else{
this.actor.style_class = 'popup-menu-boxpointer';
this.actor.add_style_class_name('popup-menu');
}
this.contextMenuItems.rebuildItems();
}
_onKeyPress(actor, event) {
return Clutter.EVENT_PROPAGATE;
}
};
var ArcMenuPopupBaseMenuItem = GObject.registerClass({
Properties: {
'active': GObject.ParamSpec.boolean('active', 'active', 'active',
GObject.ParamFlags.READWRITE,
false),
'hovered': GObject.ParamSpec.boolean('hovered', 'hovered', 'hovered',
GObject.ParamFlags.READWRITE,
false),
'sensitive': GObject.ParamSpec.boolean('sensitive', 'sensitive', 'sensitive',
GObject.ParamFlags.READWRITE,
true),
},
Signals: {
'activate': { param_types: [Clutter.Event.$gtype] },
},
}, class Arc_Menu_PopupBaseMenuItem extends St.BoxLayout{
_init(menuLayout, params){
params = imports.misc.params.parse(params, {
reactive: true,
activate: true,
hover: true,
style_class: null,
can_focus: true,
});
super._init({ style_class: 'popup-menu-item arcmenu-menu-item',
reactive: params.reactive,
track_hover: params.reactive,
can_focus: params.can_focus,
accessible_role: Atk.Role.MENU_ITEM
});
this.set_offscreen_redirect(Clutter.OffscreenRedirect.ON_IDLE);
this.hasContextMenu = false;
this._delegate = this;
this._menuLayout = menuLayout;
this.arcMenu = this._menuLayout.arcMenu;
this.shouldShow = true;
this._parent = null;
this._active = false;
this._activatable = params.reactive && params.activate;
this._sensitive = true;
this._ornamentLabel = new St.Label({ style_class: 'popup-menu-ornament' });
this.add_child(this._ornamentLabel);
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.reactive && params.hover)
this.bind_property('hover', this, 'hovered', GObject.BindingFlags.SYNC_CREATE);
if(params.hover)
this.actor.connect('notify::hover', this._onHover.bind(this));
this.arcMenuOpenStateChangeID = this.arcMenu.connect('open-state-changed', (menu, open) =>{
if(!open)
this.cancelPopupTimeout();
});
let textureCache = St.TextureCache.get_default();
let iconThemeChangedId = textureCache.connect('icon-theme-changed', this._updateIcon.bind(this));
this.connect('destroy', () => {
textureCache.disconnect(iconThemeChangedId);
this._onDestroy();
});
}
_updateIcon() {
if(!this._iconBin || !this.createIcon)
return;
let icon = this.createIcon();
this._iconBin.set_child(icon);
}
get actor() {
return this;
}
get active(){
return this._active;
}
set active(active) {
if(this.isDestroyed)
return;
let activeChanged = active != this.active;
if(activeChanged){
this._active = active;
if(active){
if(this._menuLayout.activeMenuItem !== this)
this._menuLayout.activeMenuItem = this;
this.remove_style_class_name('selected');
this.add_style_pseudo_class('active');
if(this.can_focus)
this.grab_key_focus();
}
else{
this.remove_style_pseudo_class('active');
this.remove_style_class_name('selected');
}
this.notify('active');
}
}
set hovered(hover) {
let hoverChanged = hover != this.hovered;
if(hoverChanged){
let isActiveStyle = this.get_style_pseudo_class()?.includes('active')
if(hover && !isActiveStyle){
this.add_style_class_name('selected');
}
else{
this.remove_style_class_name('selected');
}
}
}
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.tooltip === undefined && this.actor.hover && this.label){
let description = this.description;
if(this._app)
description = this._app.get_description();
Utils.createTooltip(this._menuLayout, this, this.label, description, this._displayType ? this._displayType : -1);
}
}
vfunc_button_press_event(){
let event = Clutter.get_current_event();
this.pressed = false;
if(event.get_button() == 1){
this._menuLayout._blockActivateEvent = false;
this.pressed = true;
if(this.hasContextMenu)
this.contextMenuTimeOut();
}
else if(event.get_button() == 3){
this.pressed = true;
}
this.active = true;
return Clutter.EVENT_PROPAGATE;
}
vfunc_button_release_event(){
let event = Clutter.get_current_event();
if(event.get_button() == 1 && !this._menuLayout._blockActivateEvent && this.pressed){
this.pressed = false;
this.activate(event);
if(!(this instanceof CategoryMenuItem) && !(this instanceof ArcMenuButtonItem))
this.active = false;
return Clutter.EVENT_STOP;
}
if(event.get_button() == 3 && this.pressed){
this.pressed = false;
if(this.hasContextMenu)
this.popupContextMenu();
else if(!(this instanceof CategoryMenuItem && !(this instanceof ArcMenuButtonItem))){
this.active = false;
this.hovered = true;
}
return Clutter.EVENT_STOP;
}
return Clutter.EVENT_PROPAGATE;
}
vfunc_key_focus_in() {
super.vfunc_key_focus_in();
if(!this.actor.hover)
this._menuLayout._keyFocusIn(this.actor);
this.active = true;
}
vfunc_key_focus_out() {
if(this.contextMenu && this.contextMenu.isOpen){
return;
}
super.vfunc_key_focus_out();
this.active = false;
}
activate(event) {
this.emit('activate', event);
}
vfunc_key_press_event(keyEvent) {
if (!this._activatable)
return super.vfunc_key_press_event(keyEvent);
let state = keyEvent.modifier_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;
let symbol = keyEvent.keyval;
if ( symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter) {
this.activate(Clutter.get_current_event());
return Clutter.EVENT_STOP;
}
return Clutter.EVENT_PROPAGATE;
}
vfunc_touch_event(event){
if(event.type == Clutter.EventType.TOUCH_END && !this._menuLayout._blockActivateEvent && this.pressed){
this.remove_style_pseudo_class('active');
this.activate(Clutter.get_current_event());
this.pressed = false;
return Clutter.EVENT_STOP;
}
else if(event.type == Clutter.EventType.TOUCH_BEGIN && !this._menuLayout.contextMenuManager.activeMenu){
this.pressed = true;
this._menuLayout._blockActivateEvent = false;
if(this.hasContextMenu)
this.contextMenuTimeOut();
this.add_style_pseudo_class('active');
}
else if(event.type == Clutter.EventType.TOUCH_BEGIN && this._menuLayout.contextMenuManager.activeMenu){
this.pressed = false;
this._menuLayout._blockActivateEvent = false;
this._menuLayout.contextMenuManager.activeMenu.toggle();
}
return Clutter.EVENT_PROPAGATE;
}
contextMenuTimeOut(){
this._popupTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 600, () => {
this.pressed = false;
this._popupTimeoutId = null;
if(this.hasContextMenu && this._menuLayout.arcMenu.isOpen && !this._menuLayout._blockActivateEvent) {
this.popupContextMenu();
this._menuLayout.contextMenuManager.ignoreRelease();
}
return GLib.SOURCE_REMOVE;
});
}
cancelPopupTimeout(){
if(this._popupTimeoutId){
GLib.source_remove(this._popupTimeoutId);
this._popupTimeoutId = null;
}
}
_onDestroy(){
this.isDestroyed = true;
if(this.arcMenuOpenStateChangeID){
this.arcMenu.disconnect(this.arcMenuOpenStateChangeID);
this.arcMenuOpenStateChangeID = null;
}
}
});
var ArcMenuSeparator = GObject.registerClass(
class Arc_Menu_Separator extends PopupMenu.PopupBaseMenuItem {
_init(separatorLength, separatorAlignment, text) {
super._init({
style_class: 'popup-separator-menu-item',
reactive: false,
can_focus: false,
});
this._settings = ExtensionUtils.getSettings(Me.metadata['settings-schema']);
this.remove_child(this._ornamentLabel);
this.reactive = true;
this.label = new St.Label({
text: text || '',
style: 'font-weight: bold'
});
this.add_child(this.label);
this.label_actor = this.label;
this.label.add_style_pseudo_class = () => { return false; };
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;"
if(separatorLength === Constants.SeparatorStyle.SHORT)
this._separator.style = "margin: 5px 45px;";
else if(separatorLength === Constants.SeparatorStyle.MEDIUM)
this._separator.style = "margin: 5px 15px;";
else if(separatorLength === Constants.SeparatorStyle.LONG)
this._separator.style = "margin: 0px 5px;";
else if(separatorLength === Constants.SeparatorStyle.MAX)
this._separator.style = "margin: 0px; padding: 0px;";
else if(separatorLength === Constants.SeparatorStyle.HEADER_LABEL){
this._separator.style = "margin: 0px 20px 0px 10px;";
this.style = "padding: 5px 15px;"
}
}
else if(separatorAlignment === Constants.SeparatorAlignment.VERTICAL){
if(separatorLength === Constants.SeparatorStyle.ALWAYS_SHOW){
this.style = "padding: 8px 4px;"
}
else{
this._syncVisibility();
this.vertSeparatorChangedID = this._settings.connect('changed::vert-separator', this._syncVisibility.bind(this));
this.style = "padding: 0px 4px;"
}
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;
}
this.connect('destroy', () => {
if(this.vertSeparatorChangedID){
this._settings.disconnect(this.vertSeparatorChangedID);
this.vertSeparatorChangedID = null;
}
});
}
_syncLabelVisibility() {
this.label.visible = this.label.text != '';
}
_syncVisibility() {
this._separator.visible = this._settings.get_boolean('vert-separator');
}
});
var ActivitiesMenuItem = GObject.registerClass(class Arc_Menu_ActivitiesMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout) {
super._init(menuLayout);
this._menuLayout = menuLayout;
this._settings = this._menuLayout._settings;
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 LayoutProps = this._menuLayout.layoutProperties;
let defaultIconSize = LayoutProps.DefaultQuickLinksIconSize;
let iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
return new St.Icon({
icon_name: 'view-fullscreen-symbolic',
style_class: 'popup-menu-icon',
icon_size: iconSize
});
}
activate(event) {
this._menuLayout.arcMenu.toggle();
Main.overview.show();
super.activate(event);
}
});
var Tooltip = class Arc_Menu_Tooltip{
constructor(menuLayout, sourceActor, title, description) {
this._menuButton = menuLayout.menuButton;
this._settings = this._menuButton._settings;
this.sourceActor = sourceActor;
if(this.sourceActor.tooltipLocation)
this.location = this.sourceActor.tooltipLocation;
else
this.location = Constants.TooltipLocation.BOTTOM;
let descriptionLabel;
this.actor = new St.BoxLayout({
vertical: true,
style_class: 'dash-label tooltip-menu-item',
opacity: 0
});
if(title){
this.titleLabel = new St.Label({
text: _(title),
style: description ? "font-weight: bold;" : null,
y_align: Clutter.ActorAlign.CENTER
});
this.actor.add_child(this.titleLabel);
}
if(description){
descriptionLabel = new St.Label({
text: description,
y_align: Clutter.ActorAlign.CENTER
});
this.actor.add_child(descriptionLabel);
}
global.stage.add_child(this.actor);
this.actor.connect('destroy',()=>{
if(this.destroyID){
this.sourceActor.disconnect(this.destroyID);
this.destroyID = null;
}
if(this.activeID){
this.sourceActor.disconnect(this.activeID);
this.activeID = null;
}
if(this.hoverID){
this.sourceActor.disconnect(this.hoverID);
this.hoverID = null;
}
if(this.toggleID){
this._settings.disconnect(this.toggleID);
this.toggleID = null;
}
})
this.activeID = this.sourceActor.connect('notify::active', ()=> this.setActive(this.sourceActor.active));
this.destroyID = this.sourceActor.connect('destroy',this.destroy.bind(this));
this.hoverID = this.sourceActor.connect('notify::hover', this._onHover.bind(this));
this._useTooltips = ! this._settings.get_boolean('disable-tooltips');
this.toggleID = this._settings.connect('changed::disable-tooltips', this.disableTooltips.bind(this));
}
setActive(active){
if(!active)
this.hide();
}
disableTooltips() {
this._useTooltips = ! this._settings.get_boolean('disable-tooltips');
}
_onHover() {
if(this._useTooltips){
if(this.sourceActor.hover){
if(this._menuButton.tooltipShowing){
this.show();
this._menuButton.activeTooltip = this.actor;
}
else{
this._menuButton.tooltipShowingID = GLib.timeout_add(0, 750, () => {
this.show();
this._menuButton.tooltipShowing = true;
this._menuButton.activeTooltip = this.actor;
this._menuButton.tooltipShowingID = null;
return GLib.SOURCE_REMOVE;
});
}
if(this._menuButton.tooltipHidingID){
GLib.source_remove(this._menuButton.tooltipHidingID);
this._menuButton.tooltipHidingID = null;
}
}
else {
this.hide();
if(this._menuButton.tooltipShowingID){
GLib.source_remove(this._menuButton.tooltipShowingID);
this._menuButton.tooltipShowingID = null;
}
this._menuButton.tooltipHidingID = GLib.timeout_add(0, 750, () => {
this._menuButton.tooltipShowing = false;
this._menuButton.activeTooltip = null;
this._menuButton.tooltipHidingID = null;
return GLib.SOURCE_REMOVE;
});
}
}
}
show() {
if(this._useTooltips){
this.actor.opacity = 0;
this.actor.show();
let [stageX, stageY] = this.sourceActor.get_transformed_position();
let itemWidth = this.sourceActor.allocation.x2 - this.sourceActor.allocation.x1;
let itemHeight = this.sourceActor.allocation.y2 - this.sourceActor.allocation.y1;
let labelWidth = this.actor.get_width();
let labelHeight = this.actor.get_height();
let x, y;
let 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:
y = stageY + itemHeight + gap;
x = stageX + gap;
break;
}
// keep the label inside the screen
let 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.actor.set_position(x, y);
this.actor.ease({
opacity: 255,
duration: Dash.DASH_ITEM_LABEL_SHOW_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
});
}
}
hide() {
if(this._useTooltips){
this.actor.ease({
opacity: 0,
duration: Dash.DASH_ITEM_LABEL_HIDE_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this.actor.hide()
});
}
}
destroy() {
if (this._menuButton.tooltipShowingID) {
GLib.source_remove(this._menuButton.tooltipShowingID);
this._menuButton.tooltipShowingID = null;
}
if (this._menuButton.tooltipHidingID) {
GLib.source_remove(this._menuButton.tooltipHidingID);
this._menuButton.tooltipHidingID = null;
}
if(this.toggleID>0){
this._settings.disconnect(this.toggleID);
this.toggleID = 0;
}
if(this.hoverID>0){
this.sourceActor.disconnect(this.hoverID);
this.hoverID = 0;
}
if(this._menuButton.activeTooltip = this.actor)
this._menuButton.activeTooltip = null;
global.stage.remove_child(this.actor);
this.actor.destroy();
}
};
var ArcMenuButtonItem = GObject.registerClass(
class Arc_Menu_ArcMenuButtonItem extends ArcMenuPopupBaseMenuItem {
_init(menuLayout, tooltipText, iconName, gicon) {
super._init(menuLayout);
this.style_class = 'popup-menu-item arc-menu-button';
this._settings = this._menuLayout._settings;
this._menuLayout = menuLayout;
this.remove_child(this._ornamentLabel);
this.x_expand = false;
this.x_align = Clutter.ActorAlign.CENTER;
this.y_expand = false;
this.y_align = Clutter.ActorAlign.CENTER;
this.iconName = iconName;
this.gicon = gicon;
this.toggleMenuOnClick = true;
if(tooltipText){
this.tooltip = new Tooltip(this._menuLayout, this.actor, tooltipText);
this.tooltip.location = Constants.TooltipLocation.TOP_CENTERED;
this.tooltip.hide();
}
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 LayoutProps = this._menuLayout.layoutProperties;
let defaultIconSize = LayoutProps.DefaultButtonsIconSize;
let iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
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);
}
});
// Settings Button
var SettingsButton = GObject.registerClass(class Arc_Menu_SettingsButton extends ArcMenuButtonItem {
_init(menuLayout) {
super._init(menuLayout, _("Settings"), 'emblem-system-symbolic');
}
activate(event) {
super.activate(event);
Util.spawnCommandLine('gnome-control-center');
}
});
// Runner Layout Tweaks Button
var RunnerTweaksButton = GObject.registerClass(class Arc_Menu_RunnerTweaksButton extends ArcMenuButtonItem {
_init(menuLayout) {
super._init(menuLayout, _("Configure Runner"), 'emblem-system-symbolic');
this.tooltip.location = Constants.TooltipLocation.BOTTOM_CENTERED;
}
activate(event) {
super.activate(event);
this._menuLayout._settings.set_int('prefs-visible-page', Constants.PrefsVisiblePage.RUNNER_TWEAKS);
Util.spawnCommandLine(Constants.ArcMenuSettingsCommand);
}
});
//'Insider' layout Pinned Apps hamburger button
var PinnedAppsButton = GObject.registerClass(class Arc_Menu_PinnedAppsButton extends ArcMenuButtonItem {
_init(menuLayout) {
super._init(menuLayout, _("Pinned Apps"), Me.path + Constants.HamburgerIcon.PATH);
this.toggleMenuOnClick = false;
}
activate(event) {
super.activate(event);
this._menuLayout.togglePinnedAppsMenu();
}
});
//'Windows' layout extras hamburger button
var ExtrasButton = GObject.registerClass(class Arc_Menu_ExtrasButton extends ArcMenuButtonItem {
_init(menuLayout) {
super._init(menuLayout, _("Extras"), Me.path + Constants.HamburgerIcon.PATH);
this.toggleMenuOnClick = false;
}
activate(event) {
super.activate(event);
this._menuLayout.toggleExtrasMenu();
}
});
//"Leave" Button with popupmenu that shows lock, power off, restart, etc
var LeaveButton = GObject.registerClass(class Arc_Menu_LeaveButton extends ArcMenuButtonItem {
_init(menuLayout) {
super._init(menuLayout, _("Leave"), 'system-shutdown-symbolic');
this.toggleMenuOnClick = false;
this._menuLayout = menuLayout;
this.menuButton = menuLayout.menuButton;
this._settings = menuLayout._settings;
this._createLeaveMenu();
}
_createLeaveMenu(){
this.leaveMenu = new PopupMenu.PopupMenu(this, 0.5 , St.Side.BOTTOM);
this.leaveMenu.blockSourceEvents = true;
let section = new PopupMenu.PopupMenuSection();
this.leaveMenu.addMenuItem(section);
let box = new St.BoxLayout({
vertical: true,
style_class: 'margin-box'
});
box._delegate = box;
section.actor.add_child(box);
box.add_child(this._menuLayout.createLabelRow(_("Session")));
this.lockItem = new PowerMenuItem(this._menuLayout, Constants.PowerType.LOCK);
box.add_child(this.lockItem);
let logOutItem = new PowerMenuItem(this._menuLayout, Constants.PowerType.LOGOUT);
box.add_child(logOutItem);
box.add_child(this._menuLayout.createLabelRow(_("System")));
Utils.canHybridSleep((canHybridSleep, needsAuth) => {
if(canHybridSleep){
let sleepItem = new PowerMenuItem(this._menuLayout, Constants.PowerType.HYBRID_SLEEP);
box.insert_child_at_index(sleepItem, 4);
}
});
Utils.canHibernate((canHibernate, needsAuth) => {
if(canHibernate){
let hibernateItem = new PowerMenuItem(this._menuLayout, Constants.PowerType.HIBERNATE);
box.insert_child_at_index(hibernateItem, 5);
}
});
let suspendItem = new PowerMenuItem(this._menuLayout, Constants.PowerType.SUSPEND);
box.add_child(suspendItem);
let restartItem = new PowerMenuItem(this._menuLayout, Constants.PowerType.RESTART);
box.add_child(restartItem);
let powerOffItem = new PowerMenuItem(this._menuLayout, Constants.PowerType.POWER_OFF);
box.add_child(powerOffItem);
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){
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.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);
let customStyle = this._settings.get_boolean('enable-custom-arc-menu');
this.leaveMenu.actor.style_class = customStyle ? 'arc-menu-boxpointer': 'popup-menu-boxpointer';
this.leaveMenu.actor.add_style_class_name( customStyle ? 'arc-menu' : 'popup-menu');
this.leaveMenu.toggle();
}
});
//'Unity' layout categories hamburger button
var CategoriesButton = GObject.registerClass(class Arc_Menu_CategoriesButton extends ArcMenuButtonItem {
_init(menuLayout) {
super._init(menuLayout, _("Categories"), Me.path + Constants.HamburgerIcon.PATH);
this.toggleMenuOnClick = false;
}
activate(event) {
super.activate(event);
this._menuLayout.toggleCategoriesMenu();
}
});
var PowerButton = GObject.registerClass(class Arc_Menu_PowerButton extends ArcMenuButtonItem {
_init(menuLayout, powerType) {
super._init(menuLayout, Constants.PowerOptions[powerType].TITLE, Constants.PowerOptions[powerType].IMAGE);
this.powerType = powerType;
}
activate(event) {
activatePowerOption(this.powerType, this._menuLayout.arcMenu);
}
});
var PowerMenuItem = GObject.registerClass(class Arc_Menu_PowerMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout, type) {
super._init(menuLayout);
this.powerType = type;
this._menuLayout = menuLayout;
this._layout = this._menuLayout.layout;
this._settings = this._menuLayout._settings;
this._iconBin = new St.Bin();
this.add_child(this._iconBin);
this._updateIcon();
this.label = new St.Label({
text: _(Constants.PowerOptions[this.powerType].TITLE),
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 LayoutProps = this._menuLayout.layoutProperties;
let defaultIconSize = LayoutProps.DefaultQuickLinksIconSize;
let iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
return new St.Icon({
gicon: Gio.icon_new_for_string(Constants.PowerOptions[this.powerType].IMAGE),
style_class: 'popup-menu-icon',
icon_size: iconSize,
});
}
activate(){
activatePowerOption(this.powerType, this._menuLayout.arcMenu);
}
});
var PlasmaMenuItem = GObject.registerClass(class Arc_Menu_PlasmaMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout, title, iconPath) {
super._init(menuLayout);
this.remove_child(this._ornamentLabel);
this._menuLayout = menuLayout;
this.tooltipLocation = Constants.TooltipLocation.BOTTOM_CENTERED;
this._layout = this._menuLayout.layout;
this._settings = this._menuLayout._settings;
this.vertical = true;
this.name = "arc-menu-plasma-button";
this.iconPath = iconPath;
this._iconBin = new St.Bin();
this.add_child(this._iconBin);
this._updateIcon();
this.label = new St.Label({
text: _(title),
x_expand: true,
y_expand: false,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER
});
this.label.x_align = this.label.y_align = Clutter.ActorAlign.CENTER;
this.label.y_expand = true;
this._iconBin.x_align = this._iconBin.y_align = Clutter.ActorAlign.CENTER;
this._iconBin.y_expand = true;
this.label.get_clutter_text().set_line_wrap(true);
this.add_child(this.label);
this.actor.connect('notify::hover', this._onHover.bind(this));
}
createIcon(){
return new St.Icon({
gicon: Gio.icon_new_for_string(this.iconPath),
style_class: 'popup-menu-icon',
icon_size: Constants.MEDIUM_ICON_SIZE
});
}
_onHover(){
if(this.tooltip === undefined && this.actor.hover && this.label){
let description = null;
Utils.createTooltip(this._menuLayout, this, this.label, description, Constants.DisplayType.LIST);
}
let shouldHover = this._settings.get_boolean('plasma-enable-hover');
if(shouldHover && this.actor.hover && !this.isActive){
this.activate(Clutter.get_current_event());
}
}
set active(active) {
let 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.searchBox.clearWithoutSearchChangeEvent();
this._menuLayout.clearActiveItem();
this.setActive(true);
super.activate(event);
}
});
var PlasmaCategoryHeader = GObject.registerClass(class Arc_Menu_PlasmaCategoryHeader extends St.BoxLayout{
_init(menuLayout) {
super._init({
style_class: "popup-menu-item",
style: 'padding: 0px;',
reactive: true,
track_hover:true,
can_focus: true,
accessible_role: Atk.Role.MENU_ITEM
});
this._menuLayout = menuLayout;
this._layout = this._menuLayout.layout;
this._settings = this._menuLayout._settings;
this.backButton = new ArcMenuPopupBaseMenuItem(this._menuLayout);
this.backButton.x_expand = false;
this.backButton.x_align = Clutter.ActorAlign.CENTER;
this.label = new St.Label({
text: _("Applications"),
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();
}
});
var AllAppsButton = GObject.registerClass(class Arc_Menu_AllAppsButton extends ArcMenuButtonItem{
_init(menuLayout) {
super._init(menuLayout, null, 'go-next-symbolic');
this.setIconSize(Constants.EXTRA_SMALL_ICON_SIZE);
this.toggleMenuOnClick = false;
this.style = 'min-height: 28px; padding: 0px 8px;'
this._menuLayout = menuLayout;
this._layout = this._menuLayout.layout;
this._settings = this._menuLayout._settings;
this.x_expand = true;
this.x_align = Clutter.ActorAlign.END;
this._label = new St.Label({
text: _("All Apps"),
x_expand: true,
x_align: Clutter.ActorAlign.FILL,
y_expand: false,
y_align: Clutter.ActorAlign.CENTER
});
this.insert_child_at_index(this._label, 0);
}
activate(event){
super.activate(event);
this._menuLayout.displayAllApps();
}
});
var BackButton = GObject.registerClass(class Arc_Menu_BackButton extends ArcMenuButtonItem{
_init(menuLayout) {
super._init(menuLayout, null, 'go-previous-symbolic');
this.setIconSize(Constants.EXTRA_SMALL_ICON_SIZE);
this.toggleMenuOnClick = false;
this.style = 'min-height: 28px; padding: 0px 8px;'
this._menuLayout = menuLayout;
this._layout = this._menuLayout.layout;
this._settings = this._menuLayout._settings;
this.x_expand = true;
this.x_align = Clutter.ActorAlign.END;
this._label = new St.Label({
text: _("Back"),
x_expand: true,
x_align: Clutter.ActorAlign.FILL,
y_expand: false,
y_align: Clutter.ActorAlign.CENTER
});
this.add_child(this._label);
}
activate(event){
super.activate(event);
this._menuLayout.setDefaultMenuView();
}
});
// Menu item to go back to category view
var BackMenuItem = GObject.registerClass(class Arc_Menu_BackMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout) {
super._init(menuLayout);
this._menuLayout = menuLayout;
this._layout = this._menuLayout.layout;
this._settings = this._menuLayout._settings;
this._iconBin = new St.Bin({
x_expand: false,
x_align: Clutter.ActorAlign.START,
});
this.add_child(this._iconBin);
this._updateIcon();
let backLabel = new St.Label({
text: _("Back"),
x_expand: false,
x_align: Clutter.ActorAlign.START,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER,
});
this.add_child(backLabel);
}
createIcon(){
const IconSizeEnum = this._settings.get_enum('misc-item-icon-size');
let iconSize = Utils.getIconSize(IconSizeEnum, Constants.MISC_ICON_SIZE);
return new St.Icon({
icon_name: 'go-previous-symbolic',
icon_size: iconSize,
});
}
activate(event) {
if(this._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(this._layout === Constants.MenuLayout.TOGNEE)
this._menuLayout.setDefaultMenuView();
super.activate(event);
}
});
// Menu item to view all apps
var ViewAllPrograms = GObject.registerClass(class Arc_Menu_ViewAllPrograms extends ArcMenuPopupBaseMenuItem{
_init(menuLayout) {
super._init(menuLayout);
this._menuLayout = menuLayout;
this._settings = this._menuLayout._settings;
let backLabel = new St.Label({
text: _("All Applications"),
x_expand: false,
x_align: Clutter.ActorAlign.START,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER,
});
this.add_child(backLabel);
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');
let 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
});
}
activate(event) {
let 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);
}
});
var ShortcutMenuItem = GObject.registerClass(class Arc_Menu_ShortcutMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout, name, icon, command, displayType, isContainedInCategory) {
super._init(menuLayout);
this._menuLayout = menuLayout;
this._settings = this._menuLayout._settings;
if(this._settings.get_enum('shortcut-icon-type') === Constants.CategoryIconType.FULL_COLOR)
this.add_style_class_name('regular-icons');
else
this.add_style_class_name('symbolic-icons');
this._command = command;
this._displayType = displayType;
this.isContainedInCategory = isContainedInCategory;
this.iconName = icon;
//Check for default commands--------
if(this._command == "ArcMenu_Software"){
let softwareManager = Utils.findSoftwareManager();
this._command = softwareManager ? softwareManager : 'ArcMenu_unfound.desktop';
}
else if(this._command === "ArcMenu_Trash"){
this.trash = new Me.imports.placeDisplay.Trash(this);
this._command = "ArcMenu_Trash";
this._app = this.trash.getApp();
}
if(!this._app)
this._app = Shell.AppSystem.get_default().lookup_app(this._command);
if(this._app && icon === ''){
let appIcon = this._app.create_icon_texture(Constants.MEDIUM_ICON_SIZE);
if(appIcon instanceof St.Icon){
this.iconName = appIcon.gicon.to_string();
}
}
//-------------------------------------
this.hasContextMenu = this._app ? true : false;
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
});
this.layout = this._settings.get_enum('menu-layout');
if(this.layout === Constants.MenuLayout.PLASMA && this._settings.get_boolean('apps-show-extra-details') && this._app){
let labelBox = new St.BoxLayout({
vertical: true
});
let 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){
this.style_class = 'popup-menu-item arc-menu-button';
this.remove_child(this._ornamentLabel);
this.remove_child(this.label);
this.x_expand = false;
this.x_align = Clutter.ActorAlign.CENTER;
this.y_expand = false;
this.y_align = Clutter.ActorAlign.CENTER;
}
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');
const LayoutProps = this._menuLayout.layoutProperties;
let defaultIconSize = this.isContainedInCategory ? LayoutProps.DefaultApplicationIconSize : LayoutProps.DefaultQuickLinksIconSize;
let iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize);
if(this._displayType === Constants.DisplayType.BUTTON){
iconSizeEnum = this._settings.get_enum('button-item-icon-size');
defaultIconSize = LayoutProps.DefaultButtonsIconSize;
iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize);
}
else if(this._displayType === Constants.DisplayType.GRID){
iconSizeEnum = this._settings.get_enum('menu-item-grid-icon-size');
let defaultIconStyle = LayoutProps.DefaultIconGridStyle;
iconSize = Utils.getGridIconSize(iconSizeEnum, defaultIconStyle);
}
return new St.Icon({
icon_name: this.iconName,
gicon: Gio.icon_new_for_string(this.iconName),
style_class: 'popup-menu-icon',
icon_size: iconSize
});
}
popupContextMenu(){
if(this._app && this.contextMenu == undefined){
this.contextMenu = new ApplicationContextMenu(this.actor, this._app, this._menuLayout);
if(this._displayType === Constants.DisplayType.GRID || this.layout === Constants.MenuLayout.UNITY || this.layout === Constants.MenuLayout.AZ)
this.contextMenu.centerBoxPointerPosition();
else if(this.layout === Constants.MenuLayout.MINT || this.layout === Constants.MenuLayout.TOGNEE)
this.contextMenu.rightBoxPointerPosition();
if(this._path)
this.contextMenu.path = this._path;
}
if(this.contextMenu !== undefined){
if(this.tooltip !== undefined)
this.tooltip.hide();
if(!this.contextMenu.isOpen){
this.contextMenu.rebuildItems();
}
this.contextMenu.toggle();
}
}
activate(event) {
if(this._command === "ArcMenu_LogOut")
activatePowerOption(Constants.PowerType.LOGOUT, this._menuLayout.arcMenu);
else if(this._command === "ArcMenu_Lock")
activatePowerOption(Constants.PowerType.LOCK, this._menuLayout.arcMenu);
else if(this._command === "ArcMenu_PowerOff")
activatePowerOption(Constants.PowerType.POWER_OFF, this._menuLayout.arcMenu);
else if(this._command === "ArcMenu_Restart")
activatePowerOption(Constants.PowerType.RESTART, this._menuLayout.arcMenu);
else if(this._command === "ArcMenu_Suspend")
activatePowerOption(Constants.PowerType.SUSPEND, this._menuLayout.arcMenu);
else if(this._command === "ArcMenu_HybridSleep")
activatePowerOption(Constants.PowerType.HYBRID_SLEEP, this._menuLayout.arcMenu);
else if(this._command === "ArcMenu_Hibernate")
activatePowerOption(Constants.PowerType.HIBERNATE, this._menuLayout.arcMenu);
else{
this._menuLayout.arcMenu.toggle();
if(this._command === "ArcMenu_ActivitiesOverview")
Main.overview.show();
else if(this._command === "ArcMenu_RunCommand")
Main.openRunDialog();
else if(this._command === "ArcMenu_ShowAllApplications")
Main.overview._overview._controls._toggleAppsPage();
else if(this._app)
this._app.open_new_window(-1);
else
Util.spawnCommandLine(this._command);
}
}
});
// Menu item which displays the current user
var UserMenuItem = GObject.registerClass(class Arc_Menu_UserMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout, displayType) {
super._init(menuLayout);
this._menuLayout = menuLayout;
this._displayType = displayType;
this._settings = this._menuLayout._settings;
if(this._displayType === Constants.DisplayType.BUTTON){
this.style_class = 'popup-menu-item arc-menu-button';
const IconSizeEnum = this._settings.get_enum('button-item-icon-size');
const LayoutProps = this._menuLayout.layoutProperties;
let defaultIconSize = LayoutProps.DefaultButtonsIconSize;
this.iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
this.remove_child(this._ornamentLabel);
this.x_expand = false;
this.x_align = Clutter.ActorAlign.CENTER;
this.y_expand = false;
this.y_align = Clutter.ActorAlign.CENTER;
}
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.actor);
this.label = this.userMenuIcon.label;
if(this._displayType !== Constants.DisplayType.BUTTON)
this.add_child(this.label);
}
activate(event) {
Util.spawnCommandLine("gnome-control-center user-accounts");
this._menuLayout.arcMenu.toggle();
super.activate(event);
}
});
var UserMenuIcon = class Arc_Menu_UserMenuIcon{
constructor(menuLayout, size, hasTooltip) {
this._menuLayout = menuLayout;
this.iconSize = size;
let username = GLib.get_user_name();
this._user = AccountsService.UserManager.get_default().get_user(username);
this.actor = new St.Bin({
style_class: 'menu-user-avatar user-icon',
track_hover: true,
reactive: true,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER
});
this.label = new St.Label({
text: GLib.get_real_name(),
y_align: Clutter.ActorAlign.CENTER
});
this.actor.style = "width: " + this.iconSize + "px; height: " + this.iconSize + "px;";
this._userLoadedId = this._user.connect('notify::is-loaded', this._onUserChanged.bind(this));
this._userChangedId = this._user.connect('changed', this._onUserChanged.bind(this));
this.actor.connect('destroy', this._onDestroy.bind(this));
if(hasTooltip)
this.actor.connect('notify::hover',this._onHover.bind(this));
this._onUserChanged();
}
_onHover() {
if(this.tooltip === undefined && this.actor.hover){
this.tooltip = new Tooltip(this._menuLayout, this.actor, GLib.get_real_name());
this.tooltip.location = Constants.TooltipLocation.BOTTOM_CENTERED;
this.tooltip._onHover();
}
}
_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.actor.child = null;
this.actor.add_style_class_name('user-avatar');
this.actor.style = 'background-image: url("%s");'.format(iconFile) + "width: " + this.iconSize + "px; height: " + this.iconSize + "px;";
}
else {
this.actor.style = "width: " + this.iconSize + "px; height: " + this.iconSize + "px;";
this.actor.child = new St.Icon({
icon_name: 'avatar-default-symbolic',
icon_size: this.iconSize,
style: "padding: 5px; width: " + this.iconSize + "px; height: " + this.iconSize + "px;"
});
}
}
}
_onDestroy() {
if (this._userLoadedId) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = null;
}
if (this._userChangedId) {
this._user.disconnect(this._userChangedId);
this._userChangedId = null;
}
}
};
// Menu pinned apps item class
var PinnedAppsMenuItem = GObject.registerClass({
Signals: { 'saveSettings': {}, },
}, class Arc_Menu_PinnedAppsMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout, name, icon, command, displayType, isContainedInCategory) {
super._init(menuLayout);
this._menuLayout = menuLayout;
this._menuButton = menuLayout.menuButton;
this._settings = this._menuLayout._settings;
this._command = command;
this._iconString = this._iconPath = icon;
this._name = name;
this._displayType = displayType;
this._app = Shell.AppSystem.get_default().lookup_app(this._command);
this.hasContextMenu = true;
this.gridLocation = [-1, -1];
this.isContainedInCategory = isContainedInCategory;
//Modifiy the Default Pinned Apps---------------------
if(this._name == "ArcMenu Settings"){
this._name = _("ArcMenu Settings");
}
else if(this._name == "Terminal"){
this._name = _("Terminal");
}
else if(this._name == "Files"){
this._name = _("Files");
}
if(this._iconPath === "ArcMenu_ArcMenuIcon" || this._iconPath === Me.path + '/media/icons/arc-menu-symbolic.svg'){
this._iconString = this._iconPath = Me.path + '/media/icons/menu_icons/arc-menu-symbolic.svg';
}
//-------------------------------------------------------
if(this._app && this._iconPath === ''){
let 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 = "";
}
}
this._iconBin = new St.Bin();
this.add_child(this._iconBin);
this._updateIcon();
this.label = new St.Label({
text: _(this._name),
y_expand: true,
y_align: Clutter.ActorAlign.CENTER
});
if(this._displayType === Constants.DisplayType.LIST && this._settings.get_boolean('apps-show-extra-details') && this._app){
let labelBox = new St.BoxLayout({
vertical: true
});
let 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);
}
this._draggable = DND.makeDraggable(this.actor);
this._draggable._animateDragEnd = (eventTime) => {
this._draggable._animationInProgress = true;
this._draggable._onAnimationComplete(this._draggable._dragActor, eventTime);
};
this.isDraggableApp = true;
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);
this.setShouldShow();
}
createIcon(){
let iconSize;
if(this._displayType === Constants.DisplayType.GRID){
const IconSizeEnum = this._settings.get_enum('menu-item-grid-icon-size');
const LayoutProps = this._menuLayout.layoutProperties;
let defaultIconStyle = LayoutProps.DefaultIconGridStyle;
iconSize = Utils.getGridIconSize(IconSizeEnum, defaultIconStyle);
}
else if(this._displayType === Constants.DisplayType.LIST){
const IconSizeEnum = this._settings.get_enum('menu-item-icon-size');
const LayoutProps = this._menuLayout.layoutProperties;
let defaultIconSize = this.isContainedInCategory ? LayoutProps.DefaultApplicationIconSize : LayoutProps.DefaultPinnedIconSize;
iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
}
return new St.Icon({
gicon: Gio.icon_new_for_string(this._iconString),
icon_size: iconSize
});
}
popupContextMenu(){
if(this.contextMenu == undefined){
let app = this._app ? this._app : this._command;
this.contextMenu = new ApplicationContextMenu(this.actor, app, this._menuLayout);
if(this._displayType === Constants.DisplayType.GRID)
this.contextMenu.centerBoxPointerPosition();
}
if(this.tooltip !== undefined)
this.tooltip.hide();
if(!this.contextMenu.isOpen)
this.contextMenu.rebuildItems();
this.contextMenu.toggle();
}
_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.cancelPopupTimeout();
this._dragMonitor = {
dragMotion: this._onDragMotion.bind(this)
};
DND.addDragMonitor(this._dragMonitor);
this._parentBox = this.actor.get_parent();
let p = this._parentBox.get_transformed_position();
this.posX = p[0];
this.posY = p[1];
this.actor.opacity = 55;
this.get_allocation_box();
this.rowHeight = this.height;
this.rowWidth = this.width;
}
_onDragMotion(dragEvent) {
let layoutManager = this._parentBox.layout_manager;
if(layoutManager instanceof Clutter.GridLayout){
this.xIndex = Math.floor((this._draggable._dragX - this.posX) / (this.rowWidth + layoutManager.column_spacing));
this.yIndex = Math.floor((this._draggable._dragY - this.posY) / (this.rowHeight + layoutManager.row_spacing));
if(this.xIndex === this.gridLocation[0] && this.yIndex === this.gridLocation[1]){
return DND.DragMotionResult.CONTINUE;
}
else{
this.gridLocation = [this.xIndex, this.yIndex];
}
this._parentBox.remove_child(this);
let children = this._parentBox.get_children();
let childrenCount = children.length;
let columns = layoutManager.gridColumns;
let rows = Math.floor(childrenCount / columns);
if(this.yIndex >= rows)
this.yIndex = rows;
if(this.yIndex < 0)
this.yIndex = 0;
if(this.xIndex >= columns - 1)
this.xIndex = columns - 1;
if(this.xIndex < 0)
this.xIndex = 0;
if(((this.xIndex + 1) + (this.yIndex * columns)) > childrenCount)
this.xIndex = Math.floor(childrenCount % columns);
this._parentBox.remove_all_children();
let x = 0, y = 0;
for(let i = 0; i < children.length; i++){
if(this.xIndex === x && this.yIndex === y)
[x, y] = this.gridLayoutIter(x, y, columns);
layoutManager.attach(children[i], x, y, 1, 1);
[x, y] = this.gridLayoutIter(x, y, columns);
}
layoutManager.attach(this, this.xIndex, this.yIndex, 1, 1);
}
return DND.DragMotionResult.CONTINUE;
}
_onDragEnd() {
if (this._dragMonitor) {
DND.removeDragMonitor(this._dragMonitor);
this._dragMonitor = null;
}
this.actor.opacity = 255;
let layoutManager = this._parentBox.layout_manager;
if(layoutManager instanceof Clutter.GridLayout){
let x = 0, y = 0;
let columns = layoutManager.gridColumns;
let orderedList = [];
let children = this._parentBox.get_children();
for(let i = 0; i < children.length; i++){
orderedList.push(this._parentBox.layout_manager.get_child_at(x, y));
[x, y] = this.gridLayoutIter(x, y, columns);
}
this._menuLayout.pinnedAppsArray = orderedList;
}
this.emit('saveSettings');
}
getDragActor() {
let customStyle = this._settings.get_boolean('enable-custom-arc-menu');
let 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
});
let iconColor = this._settings.get_string('menu-foreground-color');
if(customStyle)
icon.style = `color: ${iconColor};`;
return icon;
}
getDragActorSource() {
return this.actor;
}
gridLayoutIter(x, y, columns){
x++;
if(x === columns){
y++;
x = 0;
}
return [x, y];
}
activate(event) {
if(this._app)
this._app.open_new_window(-1);
else if(this._command === "ArcMenu_ShowAllApplications")
Main.overview._overview._controls._toggleAppsPage();
else
Util.spawnCommandLine(this._command);
this._menuLayout.arcMenu.toggle();
super.activate(event);
}
});
var ApplicationMenuItem = GObject.registerClass(class Arc_Menu_ApplicationMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout, app, displayType, metaInfo, isContainedInCategory) {
super._init(menuLayout);
this._app = app;
this._menuLayout = menuLayout;
this.metaInfo = metaInfo;
this._settings = this._menuLayout._settings;
this.searchType = this._menuLayout.layoutProperties.SearchDisplayType;
this._displayType = displayType;
this.hasContextMenu = true;
this.isSearchResult = this.metaInfo ? true : false;
this.isContainedInCategory = isContainedInCategory;
if(this._app){
let disableRecentAppsIndicator = this._settings.get_boolean("disable-recently-installed-apps")
if(!disableRecentAppsIndicator){
let 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.CENTER,
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'],
y_expand: true,
y_align: Clutter.ActorAlign.CENTER
});
this.description = this._app ? this._app.get_description() : this.metaInfo['description'];
let searchResultsDescriptionsSetting = this._settings.get_boolean("show-search-result-details");
let appsShowDescriptionsSetting = this._settings.get_boolean("apps-show-extra-details");
this.searchResultsDescriptions = searchResultsDescriptionsSetting && this.isSearchResult;
this.appsShowDescriptions = appsShowDescriptionsSetting && !this.isSearchResult;
if(this.description && (this.searchResultsDescriptions || this.appsShowDescriptions) && this._displayType === Constants.DisplayType.LIST){
let labelBox = new St.BoxLayout({
vertical: true
});
let descriptionText = this.description.split('\n')[0];
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: "arc-menu-menu-item-text-indicator",
style: "border-radius: 15px; margin: 0px; padding: 0px 10px;",
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.hoverID = this.connect("notify::hover", () => this.removeIndicator());
this.keyFocusInID = this.connect("key-focus-in", () => this.removeIndicator());
}
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 LayoutProps = this._menuLayout.layoutProperties;
let defaultIconStyle = LayoutProps.DefaultIconGridStyle;
iconSize = Utils.getGridIconSize(IconSizeEnum, defaultIconStyle);
}
else if(this._displayType === Constants.DisplayType.LIST){
const IconSizeEnum = this._settings.get_enum('menu-item-icon-size');
const LayoutProps = this._menuLayout.layoutProperties;
let defaultIconSize = this.isContainedInCategory || this.isSearchResult ? LayoutProps.DefaultApplicationIconSize : LayoutProps.DefaultPinnedIconSize;
iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
}
return this.metaInfo ? this.metaInfo['createIcon'](iconSize) : this._app.create_icon_texture(iconSize);
}
removeIndicator(){
if(this.isRecentlyInstalled){
this.isRecentlyInstalled = false;
let recentApps = this._settings.get_strv('recently-installed-apps');
let 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.setRecentlyInstalledIndicator();
}
}
popupContextMenu(){
this.removeIndicator();
if(this.tooltip)
this.tooltip.hide();
if(!this._app && !this._path)
return;
if(this.contextMenu === undefined){
this.contextMenu = new ApplicationContextMenu(this.actor, this._app, this._menuLayout);
if(this._path)
this.contextMenu.path = this._path;
if(this._displayType === Constants.DisplayType.GRID)
this.contextMenu.centerBoxPointerPosition();
}
if(!this.contextMenu.isOpen)
this.contextMenu.rebuildItems();
this.contextMenu.toggle();
}
activateSearchResult(provider, metaInfo, terms, event){
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')) {
let app = Shell.AppSystem.get_default().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);
SystemActions.activateAction(metaInfo.id);
}
}
}
activate(event) {
this.removeIndicator();
if(this.metaInfo){
this.activateSearchResult(this.provider, this.metaInfo, this.resultsView.terms, event);
return Clutter.EVENT_STOP;
}
else
this._app.open_new_window(-1);
this._menuLayout.arcMenu.toggle();
super.activate(event);
}
_onDestroy(){
if(this.hoverID){
this.disconnect(this.hoverID);
this.hoverID = null;
}
if(this.keyFocusInID){
this.disconnect(this.keyFocusInID);
this.keyFocusInID = null;
}
}
});
// Menu Category item class
var CategoryMenuItem = GObject.registerClass(class Arc_Menu_CategoryMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout, category, displayType) {
super._init(menuLayout);
this.appList = [];
this._menuLayout = menuLayout;
this._settings = this._menuLayout._settings;
this._layout = this._settings.get_enum('menu-layout');
this._category = category;
this._name = "";
this._horizontalFlip = this._settings.get_boolean('enable-horizontal-flip');
this._displayType = displayType;
this.layoutProps = this._menuLayout.layoutProperties;
this._iconBin = new St.Bin();
this.add_child(this._iconBin);
this._updateIcon();
this.label = new St.Label({
text: this._name,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER
});
this.add_child(this.label);
if(this.isRecentlyInstalled)
this.setRecentlyInstalledIndicator(true);
if(this._displayType === Constants.DisplayType.BUTTON){
this.style_class = 'popup-menu-item arc-menu-button';
this.remove_child(this._ornamentLabel);
this.x_expand = false;
this.x_align = Clutter.ActorAlign.CENTER;
this.y_expand = false;
this.y_align = Clutter.ActorAlign.CENTER;
this.remove_child(this.label);
}
this.label_actor = this.label;
this._menuLayout._oldX = -1;
this._menuLayout._oldY = -1;
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(){
const IconSizeEnum = this._settings.get_enum('menu-item-icon-size');
let defaultIconSize = this.layoutProps.DefaultCategoryIconSize;
let iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
if(this._displayType === Constants.DisplayType.BUTTON){
const IconSizeEnum = this._settings.get_enum('button-item-icon-size');
let defaultIconSize = this.layoutProps.DefaultButtonsIconSize;
iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
}
let icon = new St.Icon({
style_class: 'popup-menu-icon',
icon_size: iconSize
});
let categoryIconType = this._settings.get_enum('category-icon-type');
let [name, gicon, iconName, fallbackIconName] = Utils.getCategoryDetails(this._category, categoryIconType);
this._name = _(name);
if(gicon)
icon.gicon = gicon;
else if(iconName)
icon.icon_name = iconName;
else
icon.fallback_icon_name = fallbackIconName;
return icon;
}
setRecentlyInstalledIndicator(shouldShow){
if(this._displayType === Constants.DisplayType.BUTTON)
return;
this.isRecentlyInstalled = shouldShow;
if(shouldShow){
this._indicator = new St.Icon({
icon_name: 'message-indicator-symbolic',
style_class: 'arc-menu-menu-item-indicator',
icon_size: INDICATOR_ICON_SIZE,
x_expand: true,
y_expand: false,
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER
});
this.add_child(this._indicator);
}
else if(this._indicator && this.contains(this._indicator))
this.remove_child(this._indicator);
}
displayAppList(){
this._menuLayout.searchBox?.clearWithoutSearchChangeEvent();
this._menuLayout.activeCategory = this._name;
Utils.activateCategory(this._category, this._menuLayout, this, null);
}
activate(event) {
this.displayAppList();
if(this.layoutProps.SupportsCategoryOnHover)
this._menuLayout.setActiveCategory(this, true);
super.activate(event);
}
_onEnterEvent(actor, event) {
if(this._menuLayout.navigatingCategoryLeaveEventID){
GLib.source_remove(this._menuLayout.navigatingCategoryLeaveEventID);
this._menuLayout.navigatingCategoryLeaveEventID = null;
}
}
_onLeaveEvent(actor, event) {
if(!this._menuLayout.navigatingCategoryLeaveEventID){
this._menuLayout.navigatingCategoryLeaveEventID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => {
this._menuLayout.navigatingCategory = null;
this._menuLayout.navigatingCategoryLeaveEventID = null;
return GLib.SOURCE_REMOVE;
});
}
}
_onMotionEvent(actor, event) {
if(this.layoutProps.SupportsCategoryOnHover && this._settings.get_boolean('activate-on-hover')){
if (!this._menuLayout.navigatingCategory) {
this._menuLayout.navigatingCategory = this;
}
if (this._isInTriangle(event.get_coords())){
if(this._menuLayout.activeCategoryType !== this._category && this._menuLayout.navigatingCategory === this)
this.activate(Clutter.get_current_event());
return true;
}
this._menuLayout.navigatingCategory = this;
return true;
}
}
_isInTriangle([x, y]){
let [posX, posY] = this._menuLayout.navigatingCategory.get_transformed_position();
//the mouse is still in the active category
if (this._menuLayout.navigatingCategory === this){
this._menuLayout._oldX = x;
this._menuLayout._oldY = y;
return true;
}
if(!this._menuLayout.navigatingCategory)
return false;
let width = this._menuLayout.navigatingCategory.width;
let height = this._menuLayout.navigatingCategory.height;
let maxX = this._horizontalFlip ? posX : posX + width;
let maxY = posY + height;
let distance = Math.abs(maxX - this._menuLayout._oldX);
let point1 = [this._menuLayout._oldX, this._menuLayout._oldY]
let point2 = [maxX, posY - distance];
let point3 = [maxX, maxY + distance];
let area = Utils.areaOfTriangle(point1, point2, point3);
let a1 = Utils.areaOfTriangle([x, y], point2, point3);
let a2 = Utils.areaOfTriangle(point1, [x, y], point3);
let a3 = Utils.areaOfTriangle(point1, point2, [x, y]);
return area === a1 + a2 + a3;
}
});
var SimpleMenuItem = GObject.registerClass(class Arc_Menu_SimpleMenuItem extends CategoryMenuItem{
_init(menuLayout, category) {
super._init(menuLayout, category);
this.subMenu = new PopupMenu.PopupMenu(this.actor,.5,St.Side.LEFT);
this.subMenu.connect("open-state-changed", (menu, open) => {
if(!open){
let appsScrollBoxAdj = this.applicationsScrollBox.get_vscroll_bar().get_adjustment();
appsScrollBoxAdj.set_value(0);
}
});
Main.uiGroup.add_child(this.subMenu.actor);
this.section = new PopupMenu.PopupMenuSection();
this.subMenu.addMenuItem(this.section);
this.applicationsScrollBox = this._menuLayout._createScrollBox({
x_expand: true,
y_expand: true,
y_align: Clutter.ActorAlign.START,
style_class: 'left-panel ' + (this._menuLayout.disableFadeEffect ? '' : 'small-vfade'),
overlay_scrollbars: true
});
this._menuLayout.subMenuManager.addMenu(this.subMenu);
this.applicationsBox = new St.BoxLayout({
vertical: true,
style_class: 'margin-box'
});
this.applicationsScrollBox.style = 'max-height: 25em;';
this.applicationsBox._delegate = this.applicationsBox;
this.applicationsScrollBox.add_actor(this.applicationsBox);
this.section.actor.add_child(this.applicationsScrollBox);
if(this.subMenu._keyPressId)
this.actor.disconnect(this.subMenu._keyPressId);
this.applicationsScrollBox.connect("key-press-event",(actor, event)=>{
let symbol = event.get_key_symbol();
switch (symbol) {
case Clutter.KEY_Right:
case Clutter.KEY_Left:
this.subMenu.toggle();
this.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
case Clutter.KEY_Escape:
if(this.subMenu.isOpen){
this.subMenu.toggle();
this.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
}
return Clutter.EVENT_STOP;
default:
return Clutter.EVENT_PROPAGATE;
}
});
this.actor.connect("key-press-event",(actor, event)=>{
let symbol = event.get_key_symbol();
switch (symbol) {
case Clutter.KEY_Escape:
if(this.subMenu.isOpen){
this.subMenu.toggle();
}
return Clutter.EVENT_STOP;
case Clutter.KEY_Left:
case Clutter.KEY_Right:
case Clutter.KEY_Return:
if(!this.subMenu.isOpen){
let navigateFocus = true;
this.activate(event, navigateFocus);
this.subMenu.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
return Clutter.EVENT_STOP;
}
else{
return Clutter.EVENT_PROPAGATE;
}
default:
return Clutter.EVENT_PROPAGATE;
}
});
this.updateStyle();
}
updateStyle(){
let customStyle = this._settings.get_boolean('enable-custom-arc-menu');
this.subMenu.actor.hide();
if(customStyle){
this.subMenu.actor.style_class = 'arc-menu-boxpointer';
this.subMenu.actor.add_style_class_name('arc-menu');
}
else
{
this.subMenu.actor.style_class = 'popup-menu-boxpointer';
this.subMenu.actor.add_style_class_name('popup-menu');
}
}
displayAppList(){
this._menuLayout.activeCategory = this._name;
}
activate(event, navigateFocus = true) {
this._menuLayout.activeCategory = this._name;
Utils.activateCategory(this._category, this._menuLayout, this, true);
this.subMenu.toggle();
if(navigateFocus)
this.subMenu.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
this._menuLayout.setActiveCategory(this, true);
}
_onMotionEvent(actor, event) {
if (!this._menuLayout.navigatingCategory) {
this._menuLayout.navigatingCategory = this;
}
if (this._isInTriangle(event.get_coords())){
if(this._menuLayout.activeCategory !== this._name && this._menuLayout.navigatingCategory === this){
let navigateFocus = false;
this.activate(event, navigateFocus);
}
return true;
}
this._menuLayout.navigatingCategory = this;
return true;
}
});
// SubMenu Category item class
var CategorySubMenuItem = GObject.registerClass(class Arc_Menu_CategorySubMenuItem extends PopupMenu.PopupSubMenuMenuItem{
_init(menuLayout, category) {
super._init('', true);
this.add_style_class_name("arcmenu-menu-item");
this.add_style_class_name('margin-box');
this._category = category;
this._menuLayout = menuLayout;
this._settings = this._menuLayout._settings;
this._name = "";
this.isSimpleMenuItem = false;
this._active = false;
this.applicationsMap = new Map();
this.appList = [];
let categoryIconType = this._settings.get_enum('category-icon-type');
let [name, gicon, iconName, fallbackIconName] = Utils.getCategoryDetails(this._category, categoryIconType);
this._name = _(name);
if(gicon)
this.icon.gicon = gicon;
else if(iconName)
this.icon.icon_name = iconName;
else
this.icon.fallback_icon_name = fallbackIconName;
this.label.text = this._name;
const IconSizeEnum = this._settings.get_enum('menu-item-icon-size');
const LayoutProps = this._menuLayout.layoutProperties;
let defaultIconSize = LayoutProps.DefaultCategoryIconSize;
let iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
this.icon.icon_size = iconSize;
let panAction = new Clutter.PanAction({ interpolate: false });
panAction.connect('pan', (action) => {
this._menuLayout._blockActivateEvent = true;
this._menuLayout.onPan(action, this.menu.actor);
});
panAction.connect('gesture-cancel',(action) => this._menuLayout.onPanEnd(action, this.menu.actor));
panAction.connect('gesture-end', (action) => this._menuLayout.onPanEnd(action, this.menu.actor));
this.menu.actor.add_action(panAction);
this.menu.actor.style = 'max-height: 250px;';
this.menu.actor.overlay_scrollbars = true;
this.menu.actor.style_class = 'popup-sub-menu ' + (this._menuLayout.disableFadeEffect ? '' : 'small-vfade');
this.menu._needsScrollbar = () => this._needsScrollbar();
this.menu.connect('open-state-changed', () => {
if(!this.menu.isOpen){
let scrollbar= this.menu.actor.get_vscroll_bar().get_adjustment();
scrollbar.set_value(0);
}
});
this.menu.box.style_class = 'margin-box';
}
setRecentlyInstalledIndicator(shouldShow){
this.isRecentlyInstalled = shouldShow;
if(shouldShow){
this._indicator = new St.Icon({
icon_name: 'message-indicator-symbolic',
style_class: 'arc-menu-menu-item-indicator',
icon_size: INDICATOR_ICON_SIZE,
x_expand: true,
y_expand: false,
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER
});
this.actor.add_child(this._indicator);
}
else if(this._indicator && this.actor.contains(this._indicator))
this.actor.remove_child(this._indicator);
}
_needsScrollbar() {
let topMenu = this.menu;
let [, topNaturalHeight] = topMenu.actor.get_preferred_height(-1);
let topThemeNode = topMenu.actor.get_theme_node();
let topMaxHeight = topThemeNode.get_max_height();
let needsScrollbar = topMaxHeight >= 0 && topNaturalHeight >= topMaxHeight;
if(needsScrollbar)
this.menu.actor.style = 'min-height:150px; max-height: 250px;';
else
this.menu.actor.style = 'max-height: 250px;';
this.menu.actor.vscrollbar_policy = St.PolicyType.AUTOMATIC;
if (needsScrollbar)
this.menu.actor.add_style_pseudo_class('scrolled');
else
this.menu.actor.remove_style_pseudo_class('scrolled');
return needsScrollbar;
}
loadMenu(){
let children = this.menu.box.get_children();
for (let i = 0; i < children.length; i++) {
let item = children[i];
this.menu.box.remove_child(item);
}
let appList = [];
this.applicationsMap.forEach((value,key,map) => {
appList.push(key);
});
appList.sort((a, b) => {
return a.get_name().toLowerCase() > b.get_name().toLowerCase();
});
for (let i = 0; i < appList.length; i++) {
let app = appList[i];
let item = this.applicationsMap.get(app);
if(item.actor.get_parent()){
item.actor.get_parent().remove_child(item.actor);
}
if (!item.actor.get_parent()) {
this.menu.box.add_child(item.actor);
}
}
}
_setOpenState(open){
if(this.isSimpleMenuItem && open){
this._menuLayout.activeCategory = this._name;
Utils.activateCategory(this._category, this._menuLayout, this, true);
}
else if(open)
this.loadMenu();
this.setSubmenuShown(open);
}
});
// Place Info class
var PlaceInfo = class Arc_Menu_PlaceInfo {
constructor(file, name, icon) {
this.file = file;
this.name = name ? name : this._getFileName();
this.icon = icon ? icon : null;
this.gicon = icon ? null : this.getIcon();
}
launch(timestamp) {
let context = global.create_app_launch_context(timestamp, -1);
new Promise((resolve, reject) => {
Gio.AppInfo.launch_default_for_uri_async(this.file.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(this._getFileName()), e.message);
reject(e);
}
});
});
}
getIcon() {
try {
let info = this.file.query_info('standard::symbolic-icon', 0, null);
return info.get_symbolic_icon();
} catch (e) {
if (e instanceof Gio.IOErrorEnum) {
if (!this.file.is_native()) {
return new Gio.ThemedIcon({ name: 'folder-remote-symbolic' });
} else {
return new Gio.ThemedIcon({ name: 'folder-symbolic' });
}
}
}
}
_getFileName() {
try {
let 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();
}
}
}
destroy(){
}
};
Signals.addSignalMethods(PlaceInfo.prototype);
// Menu Place Shortcut item class
var PlaceMenuItem = GObject.registerClass(class Arc_Menu_PlaceMenuItem extends ArcMenuPopupBaseMenuItem{
_init(menuLayout, info, displayType, isContainedInCategory) {
super._init(menuLayout);
this._menuLayout = menuLayout;
this._displayType = displayType;
this._info = info;
this._settings = menuLayout._settings;
this.isContainedInCategory = isContainedInCategory;
this.hasContextMenu = true;
this.label = new St.Label({
text: _(info.name),
y_expand: true,
y_align: Clutter.ActorAlign.CENTER
});
this._iconBin = new St.Bin();
this.add_child(this._iconBin);
this._updateIcon();
if(this._displayType === Constants.DisplayType.BUTTON){
this.style_class = 'popup-menu-item arc-menu-button';
this.remove_child(this._ornamentLabel);
this.x_expand = this.y_expand = false;
this.x_align = this.y_align = Clutter.ActorAlign.CENTER;
}
else{
this.add_child(this.label);
}
this._changedId = this._info.connect('changed', this._propertiesChanged.bind(this));
}
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 LayoutProps = this._menuLayout.layoutProperties;
let defaultIconSize = this.isContainedInCategory ? LayoutProps.DefaultApplicationIconSize : LayoutProps.DefaultQuickLinksIconSize;
let iconSize = Utils.getIconSize(iconSizeEnum, defaultIconSize);
if(this._displayType === Constants.DisplayType.BUTTON){
let defaultIconSize = LayoutProps.DefaultButtonsIconSize;
const IconSizeEnum = this._settings.get_enum('button-item-icon-size');
iconSize = Utils.getIconSize(IconSizeEnum, defaultIconSize);
}
return new St.Icon({
gicon: this._info.gicon ? this._info.gicon : this._info.icon,
icon_size: iconSize,
});
}
_onDestroy() {
if (this._changedId) {
this._info.disconnect(this._changedId);
this._changedId = null;
}
if(this._info)
this._info.destroy();
super._onDestroy();
}
popupContextMenu(){
if(this.tooltip)
this.tooltip.hide();
if(!this._app && !this._path)
return;
if(this.contextMenu === undefined){
this.contextMenu = new ApplicationContextMenu(this.actor, this._app, this._menuLayout);
if(this._path)
this.contextMenu.path = this._path;
if(this._displayType === Constants.DisplayType.GRID)
this.contextMenu.centerBoxPointerPosition();
}
if(!this.contextMenu.isOpen)
this.contextMenu.rebuildItems();
this.contextMenu.toggle();
}
activate(event) {
this._info.launch(event.get_time());
this._menuLayout.arcMenu.toggle();
super.activate(event);
}
_propertiesChanged(info) {
this._info = info;
this.createIcon();
if(this.label)
this.label.text = info.name;
}
});
var SearchBox = GObject.registerClass({
Signals: {
'search-changed': { param_types: [GObject.TYPE_STRING] },
'entry-key-focus-in': { },
'entry-key-press': { param_types: [Clutter.Event.$gtype] },
},},
class Arc_Menu_SearchBox extends St.Entry {
_init(menuLayout) {
super._init({
hint_text: _("Search…"),
track_hover: true,
can_focus: true,
x_expand: true,
x_align: Clutter.ActorAlign.FILL,
y_align: Clutter.ActorAlign.START,
name: "ArcSearchEntry"
});
this.searchResults = menuLayout.searchResults;
this._settings = menuLayout._settings;
this.triggerSearchChangeEvent = true;
const IconSizeEnum = this._settings.get_enum('misc-item-icon-size');
let 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._textChangedId = this._text.connect('text-changed', this._onTextChanged.bind(this));
this._keyPressId = this._text.connect('key-press-event', this._onKeyPress.bind(this));
this._keyFocusInId = this._text.connect('key-focus-in', this._onKeyFocusIn.bind(this));
this._keyFocusInId = this._text.connect('key-focus-out', this._onKeyFocusOut.bind(this));
this._searchIconClickedId = this.connect('secondary-icon-clicked', () => this.clear());
this.connect('destroy', this._onDestroy.bind(this));
}
updateStyle(removeBorder){
let style = this.style;
this.style = style.replace("border-width: 0;", "");
if(removeBorder)
this.style += 'border-width: 0;';
}
get entryBox(){
return this;
}
get actor(){
return 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() == '';
}
_onKeyFocusOut(){
if(!this.isEmpty()){
this.add_style_pseudo_class('focus');
return Clutter.EVENT_STOP;
}
}
_onTextChanged() {
let searchString = this.get_text();
if(!this.isEmpty()){
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');
this.set_secondary_icon(this._clearIcon);
}
else{
if(!this.hasKeyFocus())
this.remove_style_pseudo_class('focus');
this.set_secondary_icon(null);
}
if(this.triggerSearchChangeEvent)
this.emit('search-changed', searchString);
}
_onKeyPress(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Return ||
symbol == Clutter.KEY_KP_Enter) {
if (!this.isEmpty()) {
if (this.searchResults.getTopResult()) {
this.searchResults.getTopResult().activate(event);
}
}
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._textChangedId) {
this._text.disconnect(this._textChangedId);
this._textChangedId = null;
}
if (this._keyPressId) {
this._text.disconnect(this._keyPressId);
this._keyPressId = null;
}
if (this._keyFocusInId) {
this._text.disconnect(this._keyFocusInId);
this._keyFocusInId = null;
}
if(this._searchIconClickedId){
this.disconnect(this._searchIconClickedId);
this._searchIconClickedId = null;
}
}
});
/**
* This class is responsible for the appearance of the menu button.
*/
var MenuButtonWidget = class Arc_Menu_MenuButtonWidget{
constructor() {
this.actor = new St.BoxLayout({
style_class: 'panel-status-menu-box',
pack_start: false
});
this._arrowIcon = PopupMenu.arrowIcon(St.Side.BOTTOM);
this._arrowIcon.add_style_class_name('arc-menu-arrow');
this._icon = new St.Icon({
icon_name: 'start-here-symbolic',
style_class: 'arc-menu-icon',
track_hover:true,
reactive: true,
});
this._label = new St.Label({
text: _("Applications"),
y_expand: true,
style_class: 'arc-menu-text',
y_align: Clutter.ActorAlign.CENTER,
});
this.actor.add_child(this._icon);
this.actor.add_child(this._label);
this.actor.add_child(this._arrowIcon);
}
setActiveStylePseudoClass(enable){
if(enable){
this._arrowIcon.add_style_pseudo_class('active');
this._icon.add_style_pseudo_class('active');
this._label.add_style_pseudo_class('active');
}
else{
this._arrowIcon.remove_style_pseudo_class('active');
this._icon.remove_style_pseudo_class('active');
this._label.remove_style_pseudo_class('active');
}
}
updateArrowIconSide(side){
let iconName;
switch (side) {
case St.Side.TOP:
iconName = 'pan-down-symbolic';
break;
case St.Side.RIGHT:
iconName = 'pan-start-symbolic';
break;
case St.Side.BOTTOM:
iconName = 'pan-up-symbolic';
break;
case St.Side.LEFT:
iconName = 'pan-end-symbolic';
break;
}
this._arrowIcon.icon_name = iconName;
}
getPanelLabel() {
return this._label;
}
getPanelIcon() {
return this._icon;
}
showArrowIcon() {
if (!this.actor.contains(this._arrowIcon)) {
this.actor.add_child(this._arrowIcon);
}
}
hideArrowIcon() {
if (this.actor.contains(this._arrowIcon)) {
this.actor.remove_child(this._arrowIcon);
}
}
showPanelIcon() {
if (!this.actor.contains(this._icon)) {
this.actor.add_child(this._icon);
}
}
hidePanelIcon() {
if (this.actor.contains(this._icon)) {
this.actor.remove_child(this._icon);
}
}
showPanelText() {
if (!this.actor.contains(this._label)) {
this.actor.add_child(this._label);
}
}
hidePanelText() {
this._label.style = '';
if (this.actor.contains(this._label)) {
this.actor.remove_child(this._label);
}
}
setPanelTextStyle(style){
this._label.style = style;
}
};
var DashMenuButtonWidget = class Arc_Menu_DashMenuButtonWidget{
constructor(menuButton, settings) {
this._menuButton = menuButton;
this._settings = settings;
this.actor = new St.Button({
style_class: 'show-apps',
track_hover: true,
can_focus: true,
toggle_mode: false,
reactive: false
});
this.actor._delegate = this;
this.icon = new imports.ui.iconGrid.BaseIcon(_("ArcMenu"),
{ setSizeManually: true,
showLabel: false,
createIcon: this._createIcon.bind(this) });
this._icon = new St.Icon({
icon_name: 'start-here-symbolic',
style_class: 'arc-menu-icon',
icon_size: 15,
track_hover:true,
reactive: true
});
this.icon._delegate = this.actor;
this._labelText = _("ArcMenu");
this.label = new St.Label({ style_class: 'dash-label' });
this.label.hide();
Main.layoutManager.addChrome(this.label);
this.label_actor = this.label;
this.actor.set_child(this.icon);
this.child = this.actor;
}
showLabel() {
if (!this._labelText)
return;
this.label.set_text(this._labelText);
this.label.opacity = 0;
this.label.show();
let [stageX, stageY] = this.actor.get_transformed_position();
let node = this.label.get_theme_node();
let itemWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
let labelWidth = this.label.get_width();
let labelHeight = this.label.get_height();
let x, y, xOffset, yOffset;
let position = this._menuButton._panel._settings.get_enum('dock-position');
this._isHorizontal = ((position == St.Side.TOP) || (position == St.Side.BOTTOM));
let labelOffset = node.get_length('-x-offset');
switch (position) {
case St.Side.LEFT:
yOffset = Math.floor((itemHeight - labelHeight) / 2);
y = stageY + yOffset;
xOffset = labelOffset;
x = stageX + this.actor.get_width() + xOffset;
break;
case St.Side.RIGHT:
yOffset = Math.floor((itemHeight - labelHeight) / 2);
y = stageY + yOffset;
xOffset = labelOffset;
x = Math.round(stageX) - labelWidth - xOffset;
break;
case St.Side.TOP:
y = stageY + labelOffset + itemHeight;
xOffset = Math.floor((itemWidth - labelWidth) / 2);
x = stageX + xOffset;
break;
case St.Side.BOTTOM:
yOffset = labelOffset;
y = stageY - labelHeight - yOffset;
xOffset = Math.floor((itemWidth - labelWidth) / 2);
x = stageX + xOffset;
break;
}
// keep the label inside the screen border
// Only needed fot the x coordinate.
// Leave a few pixel gap
let gap = 5;
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
if (x - monitor.x < gap)
x += monitor.x - x + labelOffset;
else if (x + labelWidth > monitor.x + monitor.width - gap)
x -= x + labelWidth - (monitor.x + monitor.width) + gap;
this.label.remove_all_transitions();
this.label.set_position(x, y);
this.label.ease({
opacity: 255,
duration: Dash.DASH_ITEM_LABEL_SHOW_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD
});
}
hideLabel() {
this.label.ease({
opacity: 0,
duration: Dash.DASH_ITEM_LABEL_HIDE_TIME,
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
onComplete: () => this.label.hide()
});
}
_createIcon(size) {
this._icon = new St.Icon({
icon_name: 'start-here-symbolic',
style_class: 'arc-menu-icon',
track_hover:true,
icon_size: size,
reactive: true
});
let path = this._settings.get_string('custom-menu-button-icon');
let iconString = Utils.getMenuButtonIcon(this._settings, path);
this._icon.set_gicon(Gio.icon_new_for_string(iconString));
return this._icon;
}
getPanelIcon() {
return this._icon;
}
};
var WorldClocksSection = GObject.registerClass(class Arc_Menu_WorldClocksSection extends ArcMenuButtonItem {
_init(menuLayout) {
super._init(menuLayout, null, null);
this.x_expand = true;
this._clock = new imports.gi.GnomeDesktop.WallClock();
this._clockNotifyId = 0;
this._locations = [];
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
this._grid = new St.Widget({ style_class: 'world-clocks-grid',
x_expand: true,
layout_manager: layout });
layout.hookup_style(this._grid);
this.add_child(this._grid);
this._clocksApp = null;
this._clocksProxy = new ClocksProxy(
Gio.DBus.session,
'org.gnome.clocks',
'/org/gnome/clocks',
this._onProxyReady.bind(this),
null /* cancellable */,
Gio.DBusProxyFlags.DO_NOT_AUTO_START | Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES);
this._clockSettings = new Gio.Settings({
schema_id: 'org.gnome.shell.world-clocks',
});
this.clocksChangedID = this._clockSettings.connect('changed', this._clocksChanged.bind(this));
this._clocksChanged();
this._appSystem = Shell.AppSystem.get_default();
this.syncID = this._appSystem.connect('installed-changed',
this._sync.bind(this));
this._sync();
}
_onDestroy(){
if(this.syncID){
this._appSystem.disconnect(this.syncID);
this.syncID = null;
}
if(this.clocksChangedID){
this._clockSettings.disconnect(this.clocksChangedID);
this.clocksChangedID = null;
}
if(this.clocksProxyID){
this._clocksProxy.disconnect(this.clocksProxyID);
this.clocksProxyID = null;
}
if (this._clockNotifyId){
this._clock.disconnect(this._clockNotifyId);
this._clockNotifyId = null;
}
}
activate(event) {
super.activate(event);
if (this._clocksApp){
this._clocksApp.activate();
}
}
_sync() {
this._clocksApp = this._appSystem.lookup_app('org.gnome.clocks.desktop');
this.visible = this._clocksApp != null;
}
_clocksChanged() {
this._grid.destroy_all_children();
this._locations = [];
let world = imports.gi.GWeather.Location.get_world();
let clocks = this._clockSettings.get_value('locations').deep_unpack();
for (let i = 0; i < clocks.length; i++) {
let l = world.deserialize(clocks[i]);
if (l && l.get_timezone() != null)
this._locations.push({ location: l });
}
this._locations.sort((a, b) => {
return a.location.get_timezone().get_offset() -
b.location.get_timezone().get_offset();
});
let layout = this._grid.layout_manager;
let title = this._locations.length == 0
? _("Add world clocks…")
: _("World Clocks");
let header = new St.Label({ x_align: Clutter.ActorAlign.START,
text: title });
header.style = "font-weight: bold;";
layout.attach(header, 0, 0, 2, 1);
this.label_actor = header;
let localOffset = GLib.DateTime.new_now_local().get_utc_offset();
for (let i = 0; i < this._locations.length; i++) {
let l = this._locations[i].location;
let name = l.get_city_name() || l.get_name();
let label = new St.Label({ text: name,
x_align: Clutter.ActorAlign.START,
y_align: Clutter.ActorAlign.CENTER,
x_expand: true });
label.style = "font-weight: normal; font-size: 0.9em;";
let time = new St.Label();
time.style = "font-feature-settings: \"tnum\"; font-size: 1.2em;";
let otherOffset = this._getTimeAtLocation(l).get_utc_offset();
let offset = (otherOffset - localOffset) / GLib.TIME_SPAN_HOUR;
let fmt = Math.trunc(offset) == offset ? '%s%.0f' : '%s%.1f';
let prefix = offset >= 0 ? '+' : '-';
let tz = new St.Label({ text: fmt.format(prefix, Math.abs(offset)),
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER });
tz.style = "font-feature-settings: \"tnum\"; font-size: 0.9em;";
if (this._grid.text_direction == Clutter.TextDirection.RTL) {
layout.attach(tz, 0, i + 1, 1, 1);
layout.attach(time, 1, i + 1, 1, 1);
layout.attach(label, 2, i + 1, 1, 1);
} else {
layout.attach(label, 0, i + 1, 1, 1);
layout.attach(time, 1, i + 1, 1, 1);
layout.attach(tz, 2, i + 1, 1, 1);
}
this._locations[i].actor = time;
}
if (this._grid.get_n_children() > 1) {
if (!this._clockNotifyId) {
this._clockNotifyId =
this._clock.connect('notify::clock', this._updateLabels.bind(this));
}
this._updateLabels();
} else {
if (this._clockNotifyId)
this._clock.disconnect(this._clockNotifyId);
this._clockNotifyId = 0;
}
}
_getTimeAtLocation(location) {
let tz = GLib.TimeZone.new(location.get_timezone().get_tzid());
return GLib.DateTime.new_now(tz);
}
_updateLabels() {
for (let i = 0; i < this._locations.length; i++) {
let l = this._locations[i];
let now = this._getTimeAtLocation(l.location);
l.actor.text = Util.formatTime(now, { timeOnly: true });
}
}
_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();
}
_onClocksPropertiesChanged() {
if (this._clocksProxy.g_name_owner == null)
return;
this._clockSettings.set_value('locations',
new GLib.Variant('av', this._clocksProxy.Locations));
}
});
var WeatherSection = GObject.registerClass(class Arc_Menu_WeatherSection extends ArcMenuButtonItem {
_init(menuLayout) {
super._init(menuLayout, null, null);
this.x_expand = true;
this.x_align = Clutter.ActorAlign.FILL;
this._weatherClient = new imports.misc.weather.WeatherClient();
let box = new St.BoxLayout({
vertical: true,
x_expand: true,
});
this.add_child(box);
let titleBox = new St.BoxLayout({ });
let label = new St.Label({
x_align: Clutter.ActorAlign.START,
x_expand: true,
y_align: Clutter.ActorAlign.END,
text: _('Weather'),
})
label.style = "font-weight: bold; padding-bottom: 5px;";
titleBox.add_child(label);
box.add_child(titleBox);
this._titleLocation = new St.Label({
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END,
});
this._titleLocation.style = "font-weight: bold; padding-bottom: 5px;";
titleBox.add_child(this._titleLocation);
let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
this._forecastGrid = new St.Widget({
style_class: 'weather-grid',
layout_manager: layout,
});
layout.hookup_style(this._forecastGrid);
box.add_child(this._forecastGrid);
this.syncID = this._weatherClient.connect('changed', this._sync.bind(this));
this._sync();
}
_onDestroy(){
if(this.syncID){
this._weatherClient.disconnect(this.syncID);
this.syncID = null;
}
this._weatherClient = null;
}
vfunc_map() {
this._weatherClient.update();
super.vfunc_map();
}
activate(event) {
super.activate(event);
this._weatherClient.activateApp();
}
_getInfos() {
let forecasts = this._weatherClient.info.get_forecast_list();
let now = GLib.DateTime.new_now_local();
let current = GLib.DateTime.new_from_unix_local(0);
let infos = [];
for (let i = 0; i < forecasts.length; i++) {
const [valid, timestamp] = forecasts[i].get_value_update();
if (!valid || timestamp === 0)
continue; // 0 means 'never updated'
const datetime = GLib.DateTime.new_from_unix_local(timestamp);
if (now.difference(datetime) > 0)
continue; // Ignore earlier forecasts
if (datetime.difference(current) < GLib.TIME_SPAN_HOUR)
continue; // Enforce a minimum interval of 1h
if (infos.push(forecasts[i]) == 5)
break; // Use a maximum of five forecasts
current = datetime;
}
return infos;
}
_addForecasts() {
let layout = this._forecastGrid.layout_manager;
let infos = this._getInfos();
if (this._forecastGrid.text_direction == Clutter.TextDirection.RTL)
infos.reverse();
let col = 0;
infos.forEach(fc => {
const [valid_, timestamp] = fc.get_value_update();
let timeStr = Util.formatTime(new Date(timestamp * 1000), {
timeOnly: true
});
const [, tempValue] = fc.get_value_temp(imports.gi.GWeather.TemperatureUnit.DEFAULT);
const tempPrefix = tempValue >= 0 ? ' ' : '';
let time = new St.Label({
text: timeStr,
x_align: Clutter.ActorAlign.CENTER,
});
time.style = "font-size: 0.8em;"
let icon = new St.Icon({
style_class: 'weather-forecast-icon',
icon_name: fc.get_symbolic_icon_name(),
x_align: Clutter.ActorAlign.CENTER,
x_expand: true,
});
let temp = new St.Label({
text: '%s%.0f°'.format(tempPrefix, tempValue),
x_align: Clutter.ActorAlign.CENTER,
});
temp.clutter_text.ellipsize = imports.gi.Pango.EllipsizeMode.NONE;
time.clutter_text.ellipsize = imports.gi.Pango.EllipsizeMode.NONE;
layout.attach(time, col, 0, 1, 1);
layout.attach(icon, col, 1, 1, 1);
layout.attach(temp, col, 2, 1, 1);
col++;
});
}
_setStatusLabel(text) {
let layout = this._forecastGrid.layout_manager;
let label = new St.Label({ text });
layout.attach(label, 0, 0, 1, 1);
}
_updateForecasts() {
this._forecastGrid.destroy_all_children();
if (!this._weatherClient.hasLocation) {
this._setStatusLabel(_("Select a location…"));
return;
}
let info = this._weatherClient.info;
let loc = info.get_location();
if (loc.get_level() !== imports.gi.GWeather.LocationLevel.CITY && loc.has_coords()) {
let world = imports.gi.GWeather.Location.get_world();
loc = world.find_nearest_city(...loc.get_coords());
}
this._titleLocation.text = loc.get_name();
if (this._weatherClient.loading) {
this._setStatusLabel(_("Loading…"));
return;
}
if (info.is_valid()) {
this._addForecasts();
return;
}
if (info.network_error())
this._setStatusLabel(_("Go online for weather information"));
else
this._setStatusLabel(_("Weather information is currently unavailable"));
}
_sync() {
this.visible = this._weatherClient.available;
if (!this.visible)
return;
this._titleLocation.visible = this._weatherClient.hasLocation;
this._updateForecasts();
}
});
function _isToday(date) {
let now = new Date();
return now.getYear() == date.getYear() &&
now.getMonth() == date.getMonth() &&
now.getDate() == date.getDate();
}