window-list: New extension
https://bugzilla.gnome.org/show_bug.cgi?id=693171
This commit is contained in:
+3
-2
@@ -27,7 +27,7 @@ fi
|
||||
AC_SUBST([SHELL_VERSION])
|
||||
|
||||
dnl keep this in alphabetic order
|
||||
CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab default-min-max launch-new-instance static-workspaces"
|
||||
CLASSIC_EXTENSIONS="apps-menu places-menu alternate-tab default-min-max launch-new-instance static-workspaces window-list"
|
||||
DEFAULT_EXTENSIONS="$CLASSIC_EXTENSIONS alternative-status-menu drive-menu windowsNavigator workspace-indicator"
|
||||
ALL_EXTENSIONS="$DEFAULT_EXTENSIONS auto-move-windows example native-window-placement systemMonitor user-theme xrandr-indicator"
|
||||
AC_SUBST(CLASSIC_EXTENSIONS, [$CLASSIC_EXTENSIONS])
|
||||
@@ -71,7 +71,7 @@ for e in $enable_extensions; do
|
||||
[AC_MSG_WARN([gnome-desktop-3.0 not found, disabling xrandr-indicator])])
|
||||
;;
|
||||
dnl keep this in alphabetic order
|
||||
alternate-tab|alternative-status-menu|apps-menu|auto-move-windows|default-min-max|drive-menu|example|launch-new-instance|native-window-placement|places-menu|static-workspaces|user-theme|windowsNavigator|workspace-indicator)
|
||||
alternate-tab|alternative-status-menu|apps-menu|auto-move-windows|default-min-max|drive-menu|example|launch-new-instance|native-window-placement|places-menu|static-workspaces|user-theme|window-list|windowsNavigator|workspace-indicator)
|
||||
ENABLED_EXTENSIONS="$ENABLED_EXTENSIONS $e"
|
||||
;;
|
||||
*)
|
||||
@@ -97,6 +97,7 @@ AC_CONFIG_FILES([
|
||||
extensions/static-workspaces/Makefile
|
||||
extensions/systemMonitor/Makefile
|
||||
extensions/user-theme/Makefile
|
||||
extensions/window-list/Makefile
|
||||
extensions/windowsNavigator/Makefile
|
||||
extensions/workspace-indicator/Makefile
|
||||
extensions/xrandr-indicator/Makefile
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
EXTENSION_ID = window-list
|
||||
|
||||
include ../../extension.mk
|
||||
@@ -0,0 +1,410 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
|
||||
const Hash = imports.misc.hash;
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
|
||||
function _minimizeOrActivateWindow(window) {
|
||||
let focusWindow = global.display.focus_window;
|
||||
if (focusWindow == window ||
|
||||
focusWindow && focusWindow.get_transient_for() == window)
|
||||
window.minimize();
|
||||
else
|
||||
window.activate(global.get_current_time());
|
||||
}
|
||||
|
||||
|
||||
const WindowButton = new Lang.Class({
|
||||
Name: 'WindowButton',
|
||||
|
||||
_init: function(metaWindow) {
|
||||
this.metaWindow = metaWindow;
|
||||
|
||||
let box = new St.BoxLayout();
|
||||
this.actor = new St.Button({ style_class: 'window-button',
|
||||
x_fill: true,
|
||||
can_focus: true,
|
||||
child: box });
|
||||
this.actor._delegate = this;
|
||||
|
||||
this.actor.connect('allocation-changed',
|
||||
Lang.bind(this, this._updateIconGeometry));
|
||||
|
||||
let textureCache = St.TextureCache.get_default();
|
||||
let icon = textureCache.bind_pixbuf_property(this.metaWindow, "icon");
|
||||
this._icon = new St.Bin({ style_class: 'window-button-icon',
|
||||
child: icon });
|
||||
box.add(this._icon);
|
||||
this._label = new St.Label();
|
||||
box.add(this._label);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._switchWorkspaceId =
|
||||
global.window_manager.connect('switch-workspace',
|
||||
Lang.bind(this, this._updateVisibility));
|
||||
this._updateVisibility();
|
||||
|
||||
this._notifyTitleId =
|
||||
this.metaWindow.connect('notify::title',
|
||||
Lang.bind(this, this._updateTitle));
|
||||
this._notifyMinimizedId =
|
||||
this.metaWindow.connect('notify::minimized',
|
||||
Lang.bind(this, this._minimizedChanged));
|
||||
this._notifyFocusId =
|
||||
global.display.connect('notify::focus-window',
|
||||
Lang.bind(this, this._updateStyle));
|
||||
this._minimizedChanged();
|
||||
},
|
||||
|
||||
_onClicked: function() {
|
||||
_minimizeOrActivateWindow(this.metaWindow);
|
||||
},
|
||||
|
||||
_minimizedChanged: function() {
|
||||
this._icon.opacity = this.metaWindow.minimized ? 128 : 255;
|
||||
this._updateTitle();
|
||||
this._updateStyle();
|
||||
},
|
||||
|
||||
_updateTitle: function() {
|
||||
if (this.metaWindow.minimized)
|
||||
this._label.text = '[%s]'.format(this.metaWindow.title);
|
||||
else
|
||||
this._label.text = this.metaWindow.title;
|
||||
},
|
||||
|
||||
_updateStyle: function() {
|
||||
if (this.metaWindow.minimized)
|
||||
this.actor.add_style_class_name('minimized');
|
||||
else
|
||||
this.actor.remove_style_class_name('minimized');
|
||||
|
||||
if (global.display.focus_window == this.metaWindow)
|
||||
this.actor.add_style_class_name('focused');
|
||||
else
|
||||
this.actor.remove_style_class_name('focused');
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
let workspace = global.screen.get_active_workspace();
|
||||
this.actor.visible = this.metaWindow.located_on_workspace(workspace);
|
||||
},
|
||||
|
||||
_updateIconGeometry: function() {
|
||||
let rect = new Meta.Rectangle();
|
||||
|
||||
[rect.x, rect.y] = this.actor.get_transformed_position();
|
||||
[rect.width, rect.height] = this.actor.get_transformed_size();
|
||||
|
||||
this.metaWindow.set_icon_geometry(rect);
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
global.window_manager.disconnect(this._switchWorkspaceId);
|
||||
this.metaWindow.disconnect(this._notifyTitleId);
|
||||
this.metaWindow.disconnect(this._notifyMinimizedId);
|
||||
global.display.disconnect(this._notifyFocusId);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const TrayButton = new Lang.Class({
|
||||
Name: 'TrayButton',
|
||||
|
||||
_init: function() {
|
||||
this._counterLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
y_expand: true });
|
||||
this.actor = new St.Button({ style_class: 'summary-source-counter',
|
||||
child: this._counterLabel,
|
||||
layoutManager: new Clutter.BinLayout() });
|
||||
this.actor.set_x_align(Clutter.ActorAlign.END);
|
||||
this.actor.set_x_expand(true);
|
||||
this.actor.set_y_expand(true);
|
||||
|
||||
this.actor.connect('clicked', Lang.bind(this,
|
||||
function() {
|
||||
if (Main.messageTray._trayState == MessageTray.State.HIDDEN)
|
||||
Main.messageTray.toggle();
|
||||
}));
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
this._trayItemCount = 0;
|
||||
Main.messageTray.getSources().forEach(Lang.bind(this,
|
||||
function(source) {
|
||||
this._sourceAdded(Main.messageTray, source);
|
||||
}));
|
||||
this._sourceAddedId =
|
||||
Main.messageTray.connect('source-added',
|
||||
Lang.bind(this, this._sourceAdded));
|
||||
this._sourceRemovedId =
|
||||
Main.messageTray.connect('source-removed',
|
||||
Lang.bind(this, this._sourceRemoved));
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_sourceAdded: function(tray, source) {
|
||||
this._trayItemCount++;
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_sourceRemoved: function(source) {
|
||||
this._trayItemCount--;
|
||||
this.actor.checked = false;
|
||||
this._updateVisibility();
|
||||
},
|
||||
|
||||
_updateVisibility: function() {
|
||||
this._counterLabel.text = this._trayItemCount.toString();
|
||||
this.actor.visible = this._trayItemCount > 0;
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
Main.messageTray.getSources().forEach(Lang.bind(this,
|
||||
function(source) {
|
||||
if (!source._windowListDestroyId)
|
||||
return;
|
||||
source.disconnect(source._windowListDestroyId)
|
||||
delete source._windowListDestroyId;
|
||||
}));
|
||||
Main.messageTray.disconnect(this._sourceAddedId);
|
||||
Main.messageTray.disconnect(this._sourceRemovedId);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const WindowList = new Lang.Class({
|
||||
Name: 'WindowList',
|
||||
|
||||
_init: function() {
|
||||
this.actor = new St.Widget({ name: 'panel',
|
||||
style_class: 'bottom-panel',
|
||||
reactive: true,
|
||||
track_hover: true,
|
||||
layout_manager: new Clutter.BinLayout()});
|
||||
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
|
||||
|
||||
let box = new St.BoxLayout({ x_expand: true, y_expand: true });
|
||||
this.actor.add_actor(box);
|
||||
|
||||
let layout = new Clutter.BoxLayout({ homogeneous: true });
|
||||
this._windowList = new St.Widget({ style_class: 'window-list',
|
||||
layout_manager: layout,
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
x_expand: true,
|
||||
y_expand: true });
|
||||
box.add(this._windowList, { expand: true });
|
||||
|
||||
this._windowList.connect('style-changed', Lang.bind(this,
|
||||
function() {
|
||||
let node = this._windowList.get_theme_node();
|
||||
let spacing = node.get_length('spacing');
|
||||
this._windowList.layout_manager.spacing = spacing;
|
||||
}));
|
||||
|
||||
this._trayButton = new TrayButton();
|
||||
box.add(this._trayButton.actor);
|
||||
|
||||
Main.layoutManager.addChrome(this.actor, { affectsStruts: true,
|
||||
trackFullscreen: true });
|
||||
Main.ctrlAltTabManager.addGroup(this.actor, _('Window List'), 'start-here-symbolic');
|
||||
|
||||
this._monitorsChangedId =
|
||||
Main.layoutManager.connect('monitors-changed',
|
||||
Lang.bind(this, this._updatePosition));
|
||||
this._updatePosition();
|
||||
|
||||
this._keyboardVisiblechangedId =
|
||||
Main.layoutManager.connect('keyboard-visible-changed',
|
||||
Lang.bind(this, function(o, state) {
|
||||
Main.layoutManager.keyboardBox.visible = state;
|
||||
Main.uiGroup.set_child_above_sibling(windowList.actor,
|
||||
Main.layoutManager.keyboardBox);
|
||||
this._updateKeyboardAnchor();
|
||||
}));
|
||||
|
||||
this._workspaceSignals = new Hash.Map();
|
||||
this._nWorkspacesChangedId =
|
||||
global.screen.connect('notify::n-workspaces',
|
||||
Lang.bind(this, this._onWorkspacesChanged));
|
||||
this._onWorkspacesChanged();
|
||||
|
||||
this._overviewShowingId =
|
||||
Main.overview.connect('showing', Lang.bind(this, function() {
|
||||
this.actor.hide();
|
||||
this._updateKeyboardAnchor();
|
||||
}));
|
||||
|
||||
this._overviewHidingId =
|
||||
Main.overview.connect('hiding', Lang.bind(this, function() {
|
||||
this.actor.show();
|
||||
this._updateKeyboardAnchor();
|
||||
}));
|
||||
|
||||
let windows = Meta.get_window_actors(global.screen);
|
||||
for (let i = 0; i < windows.length; i++)
|
||||
this._onWindowAdded(null, windows[i].metaWindow);
|
||||
},
|
||||
|
||||
_updatePosition: function() {
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
this.actor.width = monitor.width;
|
||||
this.actor.set_position(monitor.x, monitor.y + monitor.height - this.actor.height);
|
||||
},
|
||||
|
||||
_updateKeyboardAnchor: function() {
|
||||
if (!Main.keyboard.actor)
|
||||
return;
|
||||
|
||||
let anchorY = Main.overview.visible ? 0 : this.actor.height;
|
||||
Main.keyboard.actor.anchor_y = anchorY;
|
||||
},
|
||||
|
||||
_onWindowAdded: function(ws, win) {
|
||||
if (!Shell.WindowTracker.get_default().is_window_interesting(win))
|
||||
return;
|
||||
|
||||
let button = new WindowButton(win);
|
||||
this._windowList.layout_manager.pack(button.actor,
|
||||
true, true, true,
|
||||
Clutter.BoxAlignment.START,
|
||||
Clutter.BoxAlignment.START);
|
||||
},
|
||||
|
||||
_onWindowRemoved: function(ws, win) {
|
||||
let children = this._windowList.get_children();
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (children[i]._delegate.metaWindow == win) {
|
||||
children[i].destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_onWorkspacesChanged: function() {
|
||||
let numWorkspaces = global.screen.n_workspaces;
|
||||
for (let i = 0; i < numWorkspaces; i++) {
|
||||
let workspace = global.screen.get_workspace_by_index(i);
|
||||
if (this._workspaceSignals.has(workspace))
|
||||
continue;
|
||||
|
||||
let signals = { windowAddedId: 0, windowRemovedId: 0 };
|
||||
signals._windowAddedId =
|
||||
workspace.connect('window-added',
|
||||
Lang.bind(this, this._onWindowAdded));
|
||||
signals._windowRemovedId =
|
||||
workspace.connect('window-removed',
|
||||
Lang.bind(this, this._onWindowRemoved));
|
||||
this._workspaceSignals.set(workspace, signals);
|
||||
}
|
||||
},
|
||||
|
||||
_disconnectWorkspaceSignals: function() {
|
||||
let numWorkspaces = global.screen.n_workspaces;
|
||||
for (let i = 0; i < numWorkspaces; i++) {
|
||||
let workspace = global.screen.get_workspace_by_index(i);
|
||||
let signals = this._workspaceSignals.delete(workspace)[1];
|
||||
workspace.disconnect(signals._windowAddedId);
|
||||
workspace.disconnect(signals._windowRemovedId);
|
||||
}
|
||||
},
|
||||
|
||||
_onDestroy: function() {
|
||||
Main.ctrlAltTabManager.removeGroup(this.actor);
|
||||
|
||||
Main.layoutManager.disconnect(this._monitorsChangedId);
|
||||
this._monitorsChangedId = 0;
|
||||
|
||||
Main.layoutManager.disconnect(this._keyboardVisiblechangedId);
|
||||
this._keyboardVisiblechangedId = 0;
|
||||
|
||||
Main.layoutManager.hideKeyboard();
|
||||
|
||||
this._disconnectWorkspaceSignals();
|
||||
global.screen.disconnect(this._nWorkspacesChangedId);
|
||||
this._nWorkspacesChangedId = 0;
|
||||
|
||||
Main.overview.disconnect(this._overviewShowingId);
|
||||
Main.overview.disconnect(this._overviewHidingId);
|
||||
|
||||
let windows = Meta.get_window_actors(global.screen);
|
||||
for (let i = 0; i < windows.length; i++)
|
||||
windows[i].metaWindow.set_icon_geometry(null);
|
||||
}
|
||||
});
|
||||
|
||||
let windowList;
|
||||
let injections = {};
|
||||
let notificationParent;
|
||||
|
||||
function init() {
|
||||
}
|
||||
|
||||
function enable() {
|
||||
windowList = new WindowList();
|
||||
|
||||
windowList.actor.connect('notify::hover', Lang.bind(Main.messageTray,
|
||||
function() {
|
||||
this._pointerInTray = windowList.actor.hover;
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
injections['_trayDwellTimeout'] = MessageTray.MessageTray.prototype._trayDwellTimeout;
|
||||
MessageTray.MessageTray.prototype._trayDwellTimeout = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
injections['_tween'] = MessageTray.MessageTray.prototype._tween;
|
||||
MessageTray.MessageTray.prototype._tween = function(actor, statevar, value, params) {
|
||||
if (!Main.overview.visible) {
|
||||
let anchorY;
|
||||
if (statevar == '_trayState')
|
||||
anchorY = windowList.actor.height;
|
||||
else if (statevar == '_notificationState')
|
||||
anchorY = -windowList.actor.height;
|
||||
else
|
||||
anchorY = 0;
|
||||
actor.anchor_y = anchorY;
|
||||
}
|
||||
injections['_tween'].call(Main.messageTray, actor, statevar, value, params);
|
||||
};
|
||||
injections['_onTrayHidden'] = MessageTray.MessageTray.prototype._onTrayHidden;
|
||||
MessageTray.MessageTray.prototype._onTrayHidden = function() {
|
||||
this.actor.anchor_y = 0;
|
||||
injections['_onTrayHidden'].call(Main.messageTray);
|
||||
};
|
||||
|
||||
notificationParent = Main.messageTray._notificationWidget.get_parent();
|
||||
Main.messageTray._notificationWidget.hide();
|
||||
Main.messageTray._notificationWidget.reparent(windowList.actor);
|
||||
Main.messageTray._notificationWidget.show();
|
||||
}
|
||||
|
||||
function disable() {
|
||||
if (!windowList)
|
||||
return;
|
||||
|
||||
windowList.actor.hide();
|
||||
|
||||
if (notificationParent) {
|
||||
Main.messageTray._notificationWidget.reparent(notificationParent);
|
||||
notificationParent = null;
|
||||
}
|
||||
|
||||
windowList.actor.destroy();
|
||||
windowList = null;
|
||||
|
||||
for (prop in injections)
|
||||
MessageTray.MessageTray.prototype[prop] = injections[prop];
|
||||
|
||||
Main.messageTray._notificationWidget.set_anchor_point(0, 0);
|
||||
Main.messageTray.actor.set_anchor_point(0, 0);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extension-id": "@extension_id@",
|
||||
"uuid": "@uuid@",
|
||||
"settings-schema": "@gschemaname@",
|
||||
"gettext-domain": "@gettext_domain@",
|
||||
"name": "Window List",
|
||||
"description": "Display a window list at the bottom of the screen",
|
||||
"shell-version": [ "@shell_current@" ],
|
||||
"url": "@url@"
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
.bottom-panel {
|
||||
/* .window-button-icon height +
|
||||
.window-button vertical padding +
|
||||
.window-button > StWidget vertical padding) */
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.window-list {
|
||||
spacing: 2px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.window-button {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
.window-button:first-child:ltr {
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.window-button:last-child:rtl {
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.window-button > StWidget {
|
||||
max-width: 250px;
|
||||
color: #bbb;
|
||||
background-color: black;
|
||||
border-radius: 4px;
|
||||
padding: 3px 6px 1px;
|
||||
box-shadow: inset 1px 1px 4px rgba(255,255,255,0.5);
|
||||
text-shadow: 1px 1px 4px rgba(0,0,0,0.8);
|
||||
spacing: 4px;
|
||||
}
|
||||
|
||||
.window-button:hover > StWidget {
|
||||
color: white;
|
||||
background-color: #1f1f1f;
|
||||
}
|
||||
|
||||
.window-button:active > StWidget,
|
||||
.window-button:focus > StWidget {
|
||||
box-shadow: inset 2px 2px 4px rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.window-button.focused > StWidget {
|
||||
color: white;
|
||||
box-shadow: inset 1px 1px 4px rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.window-button.focused:active > StWidget {
|
||||
box-shadow: inset 2px 2px 4px rgba(255,255,255,0.7);
|
||||
}
|
||||
|
||||
.window-button.minimized > StWidget {
|
||||
color: #666;
|
||||
box-shadow: inset -1px -1px 4px rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.window-button.minimized:active > StWidget {
|
||||
box-shadow: inset -2px -2px 4px rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.window-button-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
Reference in New Issue
Block a user