Files
dash-to-panel/overview.js
Philipp a4224f4acc Update to Gnome 40 (#1303)
* 1

* renamed ui.viewSelector to ui.searchController

* add arrowIcon null check

* add object null check

* add _showAppsButton null check

* fixed _workspacesDisplay

* removed shadow-type properties

* removed packing from ui

* renamed margin_left/right to margin_start/end

* renamed GtkRadioButton to GtkToggleButton, removed draw_indicator and image_position

* removed xalign from buttons, stock -> icon_name, GtkFileChooserButton -> BtkButton (todo connect a GtkFileChooserNative), removed format-value (todo connect GtkScaleFormatValueFunc callback)

* removed events, relief

* comment arrowIcon.set_icon_name and _hbox.get_last_child out

* called gtk4-builder-tool simplify --3to4 on Settings.ui

* fix _workspaceDisplay

* revert Settings.ui back to old gtk3 version

* removed _builder.connect_signals_full and added a BuilderScope instead, use class specific add functions, removed show_all for widgets, updated margins* for GtkGrid

* add try catch aroung object.connect

* fixed _searchEntry path

* disabled _newUpdateWorkspacesViews temporarily, because it is very buggy right now

* fixed _searchEntry path (this time for real?)

* dialog.show_all() -> dialog.show(), widget.get_toplevel() -> widget.get_root(), fixed adjustScrollableHeight (no longer crashes, still does not work correctly)

* updated GtkGrid.attach calls

* added Gtk.FileChooserNative for show_applications_icon_file_filebutton

* added packing again to ui (commented) and also added layout row/column/width/height in exchange for top_attach/left_attach/colspan/rowspan

* renamed colspan/rowspan to column-span/row-span

* removed all packaing left_attach/top_attach

* updated adjustScrollableHeight so the dialog will have a reasonable size again

* limit shell-version to 40 since it is not backwards compatible right now

* called _updateBackgrounds in layoutManager ,fixed overview <-> _overview typo?

* pass Settings instead of Settings._settings to BuilderScope

* workaround for showAppsIcon (_showAppsButton seems to be no longer be available)

* replaced newlines in the ui file with the newline unicode symbol, so code block collapsing works peroperly

* moved the scrolled window directly into each notebook tab, so the tab headers are always visible

* fixed taskbarListBox children loops

* commented non working elements out (panel on left/right, import/export settings, update)

* renamed Settings to Preferences to avoid confusion with dconf settings object

* updated this.widget to this.notebook

* fixed dialogs size and expand

* fixed window preview options having too many columns

* removed all packing with expand/fill/position (already uncommented)

* removed menu arrows since they are no longer recommended for gnome40

* removed adjustScrollableHeight, default sizes are acceptable

* updated ui required version to gtk 4.0

* updated path to dash.showAppsButton

* fixes for showAppsButton

* fixed clickToExit

* fixed import/export settings

* added disable show overview on startup option

* removed old show apps animation

* fixed panel on left/right side

* fixed scroll on volume icon

no longer will scrolling on the volume icon switch workspaces as well

* commented some setFocusedMonitor support out, caused issues on Xorg

* removed oldDash

* updated hide overview on startup, no longer closes overview after suspend etc

* removed _newOverviewRelayout

* removed _newUpdateWorkspacesViews

* commented setFocusedMonitor out completly, because show overview on non primary monitor does not work properly for now

* fixed typo

* fixed merging error

* check if func is valid in hookVfunc

* updated ui to gtk4, fixed merging error

* fixed layout for apply changes to all monitors

* fix behaviour tab and app icon margin/padding grid

* fix animate launching new windows grid row

* fixed about tab

Co-authored-by: Cole Gerdemann <corvettecole@gmail.com>
2021-05-08 09:07:25 -04:00

470 lines
16 KiB
JavaScript

/*
* This file is part of the Dash-To-Panel extension for Gnome 3
*
* 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/>.
*
* Credits:
* This file is based on code from the Dash to Dock extension by micheleg
*
* Some code was also adapted from the upstream Gnome Shell source code.
*/
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Intellihide = Me.imports.intellihide;
const Utils = Me.imports.utils;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Main = imports.ui.main;
const Shell = imports.gi.Shell;
const Gtk = imports.gi.Gtk;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
const IconGrid = imports.ui.iconGrid;
const Meta = imports.gi.Meta;
const GS_HOTKEYS_KEY = 'switch-to-application-';
//timeout names
const T1 = 'swipeEndTimeout';
var dtpOverview = Utils.defineClass({
Name: 'DashToPanel.Overview',
_init: function() {
this._numHotkeys = 10;
this._timeoutsHandler = new Utils.TimeoutsHandler();
},
enable : function(panel) {
this._panel = panel;
this.taskbar = panel.taskbar;
this._injectionsHandler = new Utils.InjectionsHandler();
this._signalsHandler = new Utils.GlobalSignalsHandler();
this._optionalWorkspaceIsolation();
this._optionalHotKeys();
this._optionalNumberOverlay();
this._optionalClickToExit();
this._toggleDash();
this._signalsHandler.add([
Me.settings,
'changed::stockgs-keep-dash',
() => this._toggleDash()
]);
},
disable: function () {
this._signalsHandler.destroy();
this._injectionsHandler.destroy();
this._toggleDash(true);
// Remove key bindings
this._disableHotKeys();
this._disableExtraShortcut();
this._disableClickToExit();
},
_toggleDash: function(visible) {
// To hide the dash, set its width to 1, so it's almost not taken into account by code
// calculaing the reserved space in the overview. The reason to keep it at 1 is
// to allow its visibility change to trigger an allocaion of the appGrid which
// in turn is triggergin the appsIcon spring animation, required when no other
// actors has this effect, i.e in horizontal mode and without the workspaceThumnails
// 1 static workspace only)
if (visible === undefined) {
visible = Me.settings.get_boolean('stockgs-keep-dash');
}
let visibilityFunc = visible ? 'show' : 'hide';
let width = visible ? -1 : 1;
let overviewControls = Main.overview._overview._controls || Main.overview._controls;
overviewControls.dash.actor[visibilityFunc]();
overviewControls.dash.actor.set_width(width);
// This force the recalculation of the icon size
overviewControls.dash._maxHeight = -1;
},
/**
* Isolate overview to open new windows for inactive apps
*/
_optionalWorkspaceIsolation: function() {
let label = 'optionalWorkspaceIsolation';
this._signalsHandler.add([
Me.settings,
'changed::isolate-workspaces',
Lang.bind(this, function() {
this._panel.panelManager.allPanels.forEach(p => p.taskbar.resetAppIcons());
if (Me.settings.get_boolean('isolate-workspaces'))
Lang.bind(this, enable)();
else
Lang.bind(this, disable)();
})
]);
if (Me.settings.get_boolean('isolate-workspaces'))
Lang.bind(this, enable)();
function enable() {
this._injectionsHandler.removeWithLabel(label);
this._injectionsHandler.addWithLabel(label, [
Shell.App.prototype,
'activate',
IsolatedOverview
]);
this._signalsHandler.removeWithLabel(label);
this._signalsHandler.addWithLabel(label, [
global.window_manager,
'switch-workspace',
() => this._panel.panelManager.allPanels.forEach(p => p.taskbar.handleIsolatedWorkspaceSwitch())
]);
}
function disable() {
this._signalsHandler.removeWithLabel(label);
this._injectionsHandler.removeWithLabel(label);
}
function IsolatedOverview() {
// These lines take care of Nautilus for icons on Desktop
let activeWorkspace = Utils.DisplayWrapper.getWorkspaceManager().get_active_workspace();
let windows = this.get_windows().filter(w => w.get_workspace().index() == activeWorkspace.index());
if (windows.length > 0 &&
(!(windows.length == 1 && windows[0].skip_taskbar) ||
this.is_on_workspace(activeWorkspace)))
return Main.activateWindow(windows[0]);
return this.open_new_window(-1);
}
},
// Hotkeys
_activateApp: function(appIndex) {
let seenApps = {};
let apps = [];
this.taskbar._getAppIcons().forEach(function(appIcon) {
if (!seenApps[appIcon.app]) {
apps.push(appIcon);
}
seenApps[appIcon.app] = (seenApps[appIcon.app] || 0) + 1;
});
this._showOverlay();
if (appIndex < apps.length) {
let appIcon = apps[appIndex];
let seenAppCount = seenApps[appIcon.app];
let windowCount = appIcon.window || appIcon._hotkeysCycle ? seenAppCount : appIcon._nWindows;
if (Me.settings.get_boolean('shortcut-previews') && windowCount > 1 &&
!(Clutter.get_current_event().get_state() & ~(Clutter.ModifierType.MOD1_MASK | Clutter.ModifierType.MOD4_MASK))) { //ignore the alt (MOD1_MASK) and super key (MOD4_MASK)
if (this._hotkeyPreviewCycleInfo && this._hotkeyPreviewCycleInfo.appIcon != appIcon) {
this._endHotkeyPreviewCycle();
}
if (!this._hotkeyPreviewCycleInfo) {
this._hotkeyPreviewCycleInfo = {
appIcon: appIcon,
currentWindow: appIcon.window,
keyFocusOutId: appIcon.actor.connect('key-focus-out', () => appIcon.actor.grab_key_focus()),
capturedEventId: global.stage.connect('captured-event', (actor, e) => {
if (e.type() == Clutter.EventType.KEY_RELEASE && e.get_key_symbol() == (Clutter.KEY_Super_L || Clutter.Super_L)) {
this._endHotkeyPreviewCycle(true);
}
return Clutter.EVENT_PROPAGATE;
})
};
appIcon._hotkeysCycle = appIcon.window;
appIcon.window = null;
appIcon._previewMenu.open(appIcon);
appIcon.actor.grab_key_focus();
}
appIcon._previewMenu.focusNext();
} else {
// Activate with button = 1, i.e. same as left click
let button = 1;
this._endHotkeyPreviewCycle();
appIcon.activate(button, true);
}
}
},
_endHotkeyPreviewCycle: function(focusWindow) {
if (this._hotkeyPreviewCycleInfo) {
global.stage.disconnect(this._hotkeyPreviewCycleInfo.capturedEventId);
this._hotkeyPreviewCycleInfo.appIcon.actor.disconnect(this._hotkeyPreviewCycleInfo.keyFocusOutId);
if (focusWindow) {
this._hotkeyPreviewCycleInfo.appIcon._previewMenu.activateFocused();
}
this._hotkeyPreviewCycleInfo.appIcon.window = this._hotkeyPreviewCycleInfo.currentWindow;
delete this._hotkeyPreviewCycleInfo.appIcon._hotkeysCycle;
this._hotkeyPreviewCycleInfo = 0;
}
},
_optionalHotKeys: function() {
this._hotKeysEnabled = false;
if (Me.settings.get_boolean('hot-keys'))
this._enableHotKeys();
this._signalsHandler.add([
Me.settings,
'changed::hot-keys',
Lang.bind(this, function() {
if (Me.settings.get_boolean('hot-keys'))
Lang.bind(this, this._enableHotKeys)();
else
Lang.bind(this, this._disableHotKeys)();
})
]);
},
_resetHotkeys: function() {
this._disableHotKeys();
this._enableHotKeys();
},
_enableHotKeys: function() {
if (this._hotKeysEnabled)
return;
//3.32 introduced app hotkeys, disable them to prevent conflicts
if (Main.wm._switchToApplication) {
for (let i = 1; i < 10; ++i) {
Utils.removeKeybinding(GS_HOTKEYS_KEY + i);
}
}
// Setup keyboard bindings for taskbar elements
let shortcutNumKeys = Me.settings.get_string('shortcut-num-keys');
let bothNumKeys = shortcutNumKeys == 'BOTH';
let keys = [];
if (bothNumKeys || shortcutNumKeys == 'NUM_ROW') {
keys.push('app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'); // Regular numbers
}
if (bothNumKeys || shortcutNumKeys == 'NUM_KEYPAD') {
keys.push('app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-'); // Key-pad numbers
}
keys.forEach( function(key) {
for (let i = 0; i < this._numHotkeys; i++) {
let appNum = i;
Utils.addKeybinding(key + (i + 1), Me.settings, () => this._activateApp(appNum));
}
}, this);
this._hotKeysEnabled = true;
if (Me.settings.get_string('hotkeys-overlay-combo') === 'ALWAYS')
this.taskbar.toggleNumberOverlay(true);
},
_disableHotKeys: function() {
if (!this._hotKeysEnabled)
return;
let keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-', // Regular numbers
'app-hotkey-kp-', 'app-shift-hotkey-kp-', 'app-ctrl-hotkey-kp-']; // Key-pad numbers
keys.forEach( function(key) {
for (let i = 0; i < this._numHotkeys; i++) {
Utils.removeKeybinding(key + (i + 1));
}
}, this);
if (Main.wm._switchToApplication) {
let gsSettings = new Gio.Settings({ schema_id: imports.ui.windowManager.SHELL_KEYBINDINGS_SCHEMA });
for (let i = 1; i < 10; ++i) {
Utils.addKeybinding(GS_HOTKEYS_KEY + i, gsSettings, Main.wm._switchToApplication.bind(Main.wm));
}
}
this._hotKeysEnabled = false;
this.taskbar.toggleNumberOverlay(false);
},
_optionalNumberOverlay: function() {
// Enable extra shortcut
if (Me.settings.get_boolean('hot-keys'))
this._enableExtraShortcut();
this._signalsHandler.add([
Me.settings,
'changed::hot-keys',
Lang.bind(this, this._checkHotkeysOptions)
], [
Me.settings,
'changed::hotkeys-overlay-combo',
Lang.bind(this, function() {
if (Me.settings.get_boolean('hot-keys') && Me.settings.get_string('hotkeys-overlay-combo') === 'ALWAYS')
this.taskbar.toggleNumberOverlay(true);
else
this.taskbar.toggleNumberOverlay(false);
})
], [
Me.settings,
'changed::shortcut-num-keys',
() => this._resetHotkeys()
]);
},
_checkHotkeysOptions: function() {
if (Me.settings.get_boolean('hot-keys'))
this._enableExtraShortcut();
else
this._disableExtraShortcut();
},
_enableExtraShortcut: function() {
Utils.addKeybinding('shortcut', Me.settings, () => this._showOverlay(true));
},
_disableExtraShortcut: function() {
Utils.removeKeybinding('shortcut');
},
_showOverlay: function(overlayFromShortcut) {
//wait for intellihide timeout initialization
if (!this._panel.intellihide) {
return;
}
// Restart the counting if the shortcut is pressed again
if (this._numberOverlayTimeoutId) {
Mainloop.source_remove(this._numberOverlayTimeoutId);
this._numberOverlayTimeoutId = 0;
}
let hotkey_option = Me.settings.get_string('hotkeys-overlay-combo');
if (hotkey_option === 'NEVER')
return;
if (hotkey_option === 'TEMPORARILY' || overlayFromShortcut)
this.taskbar.toggleNumberOverlay(true);
this._panel.intellihide.revealAndHold(Intellihide.Hold.TEMPORARY);
let timeout = Me.settings.get_int('overlay-timeout');
if (overlayFromShortcut) {
timeout = Me.settings.get_int('shortcut-timeout');
}
// Hide the overlay/dock after the timeout
this._numberOverlayTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, function() {
this._numberOverlayTimeoutId = 0;
if (hotkey_option != 'ALWAYS') {
this.taskbar.toggleNumberOverlay(false);
}
this._panel.intellihide.release(Intellihide.Hold.TEMPORARY);
}));
},
_optionalClickToExit: function() {
this._clickToExitEnabled = false;
if (Me.settings.get_boolean('overview-click-to-exit'))
this._enableClickToExit();
this._signalsHandler.add([
Me.settings,
'changed::overview-click-to-exit',
Lang.bind(this, function() {
if (Me.settings.get_boolean('overview-click-to-exit'))
Lang.bind(this, this._enableClickToExit)();
else
Lang.bind(this, this._disableClickToExit)();
})
]);
},
_enableClickToExit: function() {
if (this._clickToExitEnabled)
return;
let view = imports.ui.appDisplay;
this._oldOverviewReactive = Main.overview._overview.reactive
Main.overview._overview.reactive = true;
this._clickAction = new Clutter.ClickAction();
this._clickAction.connect('clicked', () => {
if (this._swiping)
return Clutter.EVENT_PROPAGATE;
let [x, y] = global.get_pointer();
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
Main.overview.toggle();
});
Main.overview._overview.add_action(this._clickAction);
this._clickToExitEnabled = true;
},
_disableClickToExit: function () {
if (!this._clickToExitEnabled)
return;
Main.overview._overview.remove_action(this._clickAction);
Main.overview._overview.reactive = this._oldOverviewReactive;
this._signalsHandler.removeWithLabel('clickToExit');
this._clickToExitEnabled = false;
},
_onSwipeBegin: function() {
this._swiping = true;
return true;
},
_onSwipeEnd: function() {
this._timeoutsHandler.add([
T1,
0,
() => this._swiping = false
]);
return true;
}
});