window-list: New extension

https://bugzilla.gnome.org/show_bug.cgi?id=693171
This commit is contained in:
Florian Müllner
2012-11-09 22:43:30 +01:00
parent 9b05c80f81
commit b843058c18
5 changed files with 493 additions and 2 deletions
+3 -2
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
EXTENSION_ID = window-list
include ../../extension.mk
+410
View File
@@ -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);
}
+10
View File
@@ -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@"
}
+67
View File
@@ -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;
}