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

3694 lines
133 KiB
JavaScript
Raw Normal View History

/*
* 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();
}