Redo tabs => spaces indentation change to not lose manual indentation

Revert most JS changes in commit:

    Fri Nov 28 20:12:20 2008 +0000
    Convert all JS style to be uniform, add Eclipse settings bits

Instead, just add 'indent-tabs-mode: nil' to the mode lines and convert
tabs to spaces. The indentation no longer exactly matches the Eclipse
settings, since they differ in some ways from the style we are trying
to achieve.

svn path=/trunk/; revision=97
This commit is contained in:
Owen Taylor 2008-12-01 19:51:43 +00:00
parent 12720e2c90
commit 7b471645f4
7 changed files with 1049 additions and 1045 deletions

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Signals = imports.signals; const Signals = imports.signals;
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
@ -10,31 +10,31 @@ const Tidy = imports.gi.Tidy;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
//TODO - move this into GConf once we're not a plugin anymore // TODO - move this into GConf once we're not a plugin anymore
//but have taken over metacity // but have taken over metacity
//This list is taken from GNOME Online popular applications // This list is taken from GNOME Online popular applications
//http://online.gnome.org/applications // http://online.gnome.org/applications
//but with nautilus removed // but with nautilus removed
const DEFAULT_APPLICATIONS = [ const DEFAULT_APPLICATIONS = [
'mozilla-firefox.desktop', 'mozilla-firefox.desktop',
'gnome-terminal.desktop', 'gnome-terminal.desktop',
'evolution.desktop', 'evolution.desktop',
'evince.desktop', 'evince.desktop',
'gedit.desktop', 'gedit.desktop',
'mozilla-thunderbird.desktop', 'mozilla-thunderbird.desktop',
'totem.desktop', 'totem.desktop',
'gnome-file-roller.desktop', 'gnome-file-roller.desktop',
'rhythmbox.desktop', 'rhythmbox.desktop',
'epiphany.desktop', 'epiphany.desktop',
'xchat.desktop', 'xchat.desktop',
'openoffice.org-1.9-writer.desktop', 'openoffice.org-1.9-writer.desktop',
'emacs.desktop', 'emacs.desktop',
'gnome-system-monitor.desktop', 'gnome-system-monitor.desktop',
'openoffice.org-1.9-calc.desktop', 'openoffice.org-1.9-calc.desktop',
'eclipse.desktop', 'eclipse.desktop',
'openoffice.org-1.9-impress.desktop', 'openoffice.org-1.9-impress.desktop',
'vncviewer.desktop' 'vncviewer.desktop'
]; ];
const APPDISPLAY_NAME_COLOR = new Clutter.Color(); const APPDISPLAY_NAME_COLOR = new Clutter.Color();
APPDISPLAY_NAME_COLOR.from_pixel(0xffffffff); APPDISPLAY_NAME_COLOR.from_pixel(0xffffffff);
@ -49,304 +49,305 @@ const APPDISPLAY_HEIGHT = 50;
const APPDISPLAY_PADDING = 4; const APPDISPLAY_PADDING = 4;
function AppDisplayItem(node, width) { function AppDisplayItem(node, width) {
this._init(node, width); this._init(node, width);
} }
AppDisplayItem.prototype = { AppDisplayItem.prototype = {
_init: function(appinfo, width) { _init: function(appinfo, width) {
let me = this; let me = this;
this._appinfo = appinfo; this._appinfo = appinfo;
let name = appinfo.get_name(); let name = appinfo.get_name();
let icontheme = Gtk.IconTheme.get_default(); let icontheme = Gtk.IconTheme.get_default();
this._group = new Clutter.Group({reactive: true, this._group = new Clutter.Group({reactive: true,
width: width, width: width,
height: APPDISPLAY_HEIGHT}); height: APPDISPLAY_HEIGHT});
this._group.connect('button-press-event', function(group, e) { this._group.connect('button-press-event', function(group, e) {
me.emit('activate'); me.emit('activate');
return true; return true;
}); });
this._bg = new Clutter.Rectangle({ color: APPDISPLAY_BACKGROUND_COLOR, this._bg = new Clutter.Rectangle({ color: APPDISPLAY_BACKGROUND_COLOR,
x: 0, y: 0, width: width, height: APPDISPLAY_HEIGHT }); x: 0, y: 0,
this._group.add_actor(this._bg); width: width, height: APPDISPLAY_HEIGHT });
this._group.add_actor(this._bg);
this._icon = new Clutter.Texture({ width: 48, height: 48, x: 0, y: 0 }); this._icon = new Clutter.Texture({ width: 48, height: 48, x: 0, y: 0 });
let gicon = appinfo.get_icon(); let gicon = appinfo.get_icon();
let path = null; let path = null;
if (gicon != null) { if (gicon != null) {
let iconinfo = icontheme.lookup_by_gicon(gicon, 48, Gtk.IconLookupFlags.NO_SVG); let iconinfo = icontheme.lookup_by_gicon(gicon, 48, Gtk.IconLookupFlags.NO_SVG);
if (iconinfo) if (iconinfo)
path = iconinfo.get_filename(); path = iconinfo.get_filename();
} }
if (path) if (path)
this._icon.set_from_file(path); this._icon.set_from_file(path);
this._group.add_actor(this._icon); this._group.add_actor(this._icon);
let comment = appinfo.get_description();
let text_width = width - me._icon.width + 4;
this._name = new Clutter.Label({ color: APPDISPLAY_NAME_COLOR,
font_name: "Sans 14px",
width: text_width,
ellipsize: Pango.EllipsizeMode.END,
text: name,
x: this._icon.width + 4,
y: 0});
this._group.add_actor(this._name);
this._comment = new Clutter.Label({ color: APPDISPLAY_COMMENT_COLOR,
font_name: "Sans 12px",
width: text_width,
ellipsize: Pango.EllipsizeMode.END,
text: comment,
x: this._name.x,
y: this._name.height + 4})
this._group.add_actor(this._comment);
this.actor = this._group;
},
launch: function() {
this._appinfo.launch([], null);
},
appinfo: function () {
return this._appinfo;
},
markSelected: function(isSelected) {
let color;
if (isSelected)
color = APPDISPLAY_SELECTED_BACKGROUND_COLOR;
else
color = APPDISPLAY_BACKGROUND_COLOR;
this._bg.color = color;
}
};
let comment = appinfo.get_description();
let text_width = width - me._icon.width + 4;
this._name = new Clutter.Label({ color: APPDISPLAY_NAME_COLOR,
font_name: "Sans 14px",
width: text_width,
ellipsize: Pango.EllipsizeMode.END,
text: name,
x: this._icon.width + 4,
y: 0});
this._group.add_actor(this._name);
this._comment = new Clutter.Label({ color: APPDISPLAY_COMMENT_COLOR,
font_name: "Sans 12px",
width: text_width,
ellipsize: Pango.EllipsizeMode.END,
text: comment,
x: this._name.x,
y: this._name.height + 4})
this._group.add_actor(this._comment);
this.actor = this._group;
},
launch: function() {
this._appinfo.launch([], null);
},
appinfo: function () {
return this._appinfo;
},
markSelected: function(isSelected) {
let color;
if (isSelected)
color = APPDISPLAY_SELECTED_BACKGROUND_COLOR;
else
color = APPDISPLAY_BACKGROUND_COLOR;
this._bg.color = color;
}
}
Signals.addSignalMethods(AppDisplayItem.prototype); Signals.addSignalMethods(AppDisplayItem.prototype);
function AppDisplay(x, y, width, height) { function AppDisplay(x, y, width, height) {
this._init(x, y, width, height); this._init(x, y, width, height);
} }
AppDisplay.prototype = { AppDisplay.prototype = {
_init : function(x, y, width, height) { _init : function(x, y, width, height) {
let me = this; let me = this;
let global = Shell.Global.get(); let global = Shell.Global.get();
this._search = ''; this._search = '';
this._x = x; this._x = x;
this._y = y; this._y = y;
this._width = width; this._width = width;
this._height = height; this._height = height;
this._appmonitor = new Shell.AppMonitor(); this._appmonitor = new Shell.AppMonitor();
this._appsStale = true; this._appsStale = true;
this._appmonitor.connect('changed', function(mon) { this._appmonitor.connect('changed', function(mon) {
me._appsStale = true; me._appsStale = true;
}); });
this._grid = new Tidy.Grid({x: x, y: y, width: width, height: height}); this._grid = new Tidy.Grid({x: x, y: y, width: width, height: height});
global.stage.add_actor(this._grid); global.stage.add_actor(this._grid);
this._appset = {}; // Map<appid, appinfo> this._appset = {}; // Map<appid, appinfo>
this._displayed = {} // Map<appid, AppDisplay> this._displayed = {}; // Map<appid, AppDisplay>
this._selectedIndex = -1; this._selectedIndex = -1;
this._max_items = this._height / (APPDISPLAY_HEIGHT + APPDISPLAY_PADDING); this._max_items = this._height / (APPDISPLAY_HEIGHT + APPDISPLAY_PADDING);
}, },
_refreshCache: function() { _refreshCache: function() {
let me = this; let me = this;
if (!this._appsStale) if (!this._appsStale)
return; return;
for (id in this._displayed) for (id in this._displayed)
this._displayed[id].destroy(); this._displayed[id].destroy();
this._appset = {}; this._appset = {};
this._displayed = {}; this._displayed = {};
this._selectedIndex = -1; this._selectedIndex = -1;
let apps = Gio.app_info_get_all(); let apps = Gio.app_info_get_all();
for (let i = 0; i < apps.length; i++) { for (let i = 0; i < apps.length; i++) {
let appinfo = apps[i]; let appinfo = apps[i];
let appid = appinfo.get_id(); let appid = appinfo.get_id();
this._appset[appid] = appinfo; this._appset[appid] = appinfo;
}
this._appsStale = false;
},
_removeItem: function(appid) {
let item = this._displayed[appid];
let group = item.actor;
group.destroy();
delete this._displayed[appid];
},
_removeAll: function() {
for (appid in this._displayed)
this._removeItem(appid);
},
_setDefaultList: function() {
this._removeAll();
let added = 0;
for (let i = 0; i < DEFAULT_APPLICATIONS.length && added < this._max_items; i++) {
let appid = DEFAULT_APPLICATIONS[i];
let appinfo = this._appset[appid];
if (appinfo) {
this._filterAdd(appid);
added += 1;
}
}
},
_getNDisplayed: function() {
// Is there a better way to do .size() ?
let c = 0; for (i in this._displayed) { c += 1; };
return c;
},
_filterAdd: function(appid) {
let me = this;
let appinfo = this._appset[appid];
let name = appinfo.get_name();
let index = this._getNDisplayed();
let appdisplay = new AppDisplayItem(appinfo, this._width);
appdisplay.connect('activate', function() {
appdisplay.launch();
me.emit('activated');
});
let group = appdisplay.actor;
this._grid.add_actor(group);
this._displayed[appid] = appdisplay;
},
_filterRemove: function(appid) {
// In the future, do some sort of fade out or other effect here
let item = this._displayed[appid];
this._removeItem(item);
},
_appinfoMatches: function(appinfo, search) {
if (search == null || search == '')
return true;
let name = appinfo.get_name().toLowerCase();
if (name.indexOf(search) >= 0)
return true;
let description = appinfo.get_description();
if (description) {
description = description.toLowerCase();
if (description.indexOf(search) >= 0)
return true;
}
let exec = appinfo.get_executable().toLowerCase();
if (exec.indexOf(search) >= 0)
return true;
return false;
},
_sortApps: function(appids) {
let me = this;
return appids.sort(function (a,b) {
let appA = me._appset[a];
let appB = me._appset[b];
return appA.get_name().localeCompare(appB.get_name());
});
},
_doSearchFilter: function() {
this._removeAll();
let matchedApps = [];
for (appid in this._appset) {
if (matchedApps.length >= this._max_items)
break;
if (this._displayed[appid])
continue;
let app = this._appset[appid];
if (this._appinfoMatches(app, this._search))
matchedApps.push(appid);
}
this._sortApps(matchedApps);
for (let i = 0; i < matchedApps.length; i++) {
this._filterAdd(matchedApps[i]);
}
},
_redisplay: function() {
this._refreshCache();
if (!this._search)
this._setDefaultList();
else
this._doSearchFilter();
},
setSearch: function(text) {
this._search = text.toLowerCase();
this._redisplay();
},
_findDisplayedByIndex: function(index) {
let displayedActors = this._grid.get_children();
let actor = displayedActors[index];
return this._findDisplayedByActor(actor);
},
_findDisplayedByActor: function(actor) {
for (appid in this._displayed) {
let item = this._displayed[appid];
if (item.actor == actor) {
return item;
} }
} this._appsStale = false;
return null; },
},
searchActivate: function() { _removeItem: function(appid) {
if (this._selectedIndex != -1) { let item = this._displayed[appid];
let selected = this._findDisplayedByIndex(this._selectedIndex); let group = item.actor;
selected.launch(); group.destroy();
delete this._displayed[appid];
},
_removeAll: function() {
for (appid in this._displayed)
this._removeItem(appid);
},
_setDefaultList: function() {
this._removeAll();
let added = 0;
for (let i = 0; i < DEFAULT_APPLICATIONS.length && added < this._max_items; i++) {
let appid = DEFAULT_APPLICATIONS[i];
let appinfo = this._appset[appid];
if (appinfo) {
this._filterAdd(appid);
added += 1;
}
}
},
_getNDisplayed: function() {
// Is there a better way to do .size() ?
let c = 0; for (i in this._displayed) { c += 1; };
return c;
},
_filterAdd: function(appid) {
let me = this;
let appinfo = this._appset[appid];
let name = appinfo.get_name();
let index = this._getNDisplayed();
let appdisplay = new AppDisplayItem(appinfo, this._width);
appdisplay.connect('activate', function() {
appdisplay.launch();
me.emit('activated');
});
let group = appdisplay.actor;
this._grid.add_actor(group);
this._displayed[appid] = appdisplay;
},
_filterRemove: function(appid) {
// In the future, do some sort of fade out or other effect here
let item = this._displayed[appid];
this._removeItem(item);
},
_appinfoMatches: function(appinfo, search) {
if (search == null || search == '')
return true;
let name = appinfo.get_name().toLowerCase();
if (name.indexOf(search) >= 0)
return true;
let description = appinfo.get_description();
if (description) {
description = description.toLowerCase();
if (description.indexOf(search) >= 0)
return true;
}
let exec = appinfo.get_executable().toLowerCase();
if (exec.indexOf(search) >= 0)
return true;
return false;
},
_sortApps: function(appids) {
let me = this;
return appids.sort(function (a,b) {
let appA = me._appset[a];
let appB = me._appset[b];
return appA.get_name().localeCompare(appB.get_name());
});
},
_doSearchFilter: function() {
this._removeAll();
let matchedApps = [];
for (appid in this._appset) {
if (matchedApps.length >= this._max_items)
break;
if (this._displayed[appid])
continue;
let app = this._appset[appid];
if (this._appinfoMatches(app, this._search))
matchedApps.push(appid);
}
this._sortApps(matchedApps);
for (let i = 0; i < matchedApps.length; i++) {
this._filterAdd(matchedApps[i]);
}
},
_redisplay: function() {
this._refreshCache();
if (!this._search)
this._setDefaultList();
else
this._doSearchFilter();
},
setSearch: function(text) {
this._search = text.toLowerCase();
this._redisplay();
},
_findDisplayedByIndex: function(index) {
let displayedActors = this._grid.get_children();
let actor = displayedActors[index];
return this._findDisplayedByActor(actor);
},
_findDisplayedByActor: function(actor) {
for (appid in this._displayed) {
let item = this._displayed[appid];
if (item.actor == actor) {
return item;
}
}
return null;
},
searchActivate: function() {
if (this._selectedIndex != -1) {
let selected = this._findDisplayedByIndex(this._selectedIndex);
selected.launch();
this.emit('activated');
return;
}
let displayedActors = this._grid.get_children();
if (displayedActors.length != 1)
return;
let selectedActor = displayedActors[0];
let selectedMenuItem = this._findDisplayedByActor(selectedActor);
selectedMenuItem.launch();
this.emit('activated'); this.emit('activated');
return; },
}
let displayedActors = this._grid.get_children();
if (displayedActors.length != 1)
return;
let selectedActor = displayedActors[0];
let selectedMenuItem = this._findDisplayedByActor(selectedActor);
selectedMenuItem.launch();
this.emit('activated');
},
_selectIndex: function(index) { _selectIndex: function(index) {
if (this._selectedIndex != -1) { if (this._selectedIndex != -1) {
let prev = this._findDisplayedByIndex(this._selectedIndex); let prev = this._findDisplayedByIndex(this._selectedIndex);
log("demarking " + prev); log("demarking " + prev);
prev.markSelected(false); prev.markSelected(false);
}
this._selectedIndex = index;
let item = this._findDisplayedByIndex(index);
log("marking " + item);
item.markSelected(true);
},
selectUp: function() {
let prev = this._selectedIndex-1;
if (prev < 0)
return;
this._selectIndex(prev);
},
selectDown: function() {
let next = this._selectedIndex+1;
let nDisplayed = this._getNDisplayed();
if (next >= nDisplayed)
return;
this._selectIndex(next);
},
show: function() {
this._redisplay();
this._grid.show();
},
hide: function() {
this._grid.hide();
} }
this._selectedIndex = index; };
let item = this._findDisplayedByIndex(index);
log("marking " + item);
item.markSelected(true);
},
selectUp: function() {
let prev = this._selectedIndex-1;
if (prev < 0)
return;
this._selectIndex(prev);
},
selectDown: function() {
let next = this._selectedIndex+1;
let nDisplayed = this._getNDisplayed();
if (next >= nDisplayed)
return;
this._selectIndex(next);
},
show: function() {
this._redisplay();
this._grid.show();
},
hide: function() {
this._grid.hide();
}
}
Signals.addSignalMethods(AppDisplay.prototype); Signals.addSignalMethods(AppDisplay.prototype);

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Tweener = imports.tweener.tweener; const Tweener = imports.tweener.tweener;
@ -9,7 +9,7 @@ DEFAULT_BUTTON_COLOR.from_pixel(0xeeddccff);
const DEFAULT_PRESSED_BUTTON_COLOR = new Clutter.Color(); const DEFAULT_PRESSED_BUTTON_COLOR = new Clutter.Color();
DEFAULT_PRESSED_BUTTON_COLOR.from_pixel(0xccbbaaff); DEFAULT_PRESSED_BUTTON_COLOR.from_pixel(0xccbbaaff);
//Time for animation making the button darker // Time for animation making the button darker
const ANIMATION_TIME = 0.3; const ANIMATION_TIME = 0.3;
const NO_OPACITY = 0; const NO_OPACITY = 0;
@ -23,95 +23,95 @@ function Button(text, buttonColor, pressedButtonColor, staysPressed, minWidth, m
} }
Button.prototype = { Button.prototype = {
_init : function(text, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) { _init : function(text, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) {
let me = this; let me = this;
this._buttonColor = buttonColor
if (buttonColor == null)
this._buttonColor = DEFAULT_BUTTON_COLOR;
this._pressedButtonColor = pressedButtonColor this._buttonColor = buttonColor
if (pressedButtonColor == null) if (buttonColor == null)
this._pressedButtonColor = DEFAULT_PRESSED_BUTTON_COLOR; this._buttonColor = DEFAULT_BUTTON_COLOR;
if (staysPressed == null) this._pressedButtonColor = pressedButtonColor
staysPressed = false; if (pressedButtonColor == null)
if (minWidth == null) this._pressedButtonColor = DEFAULT_PRESSED_BUTTON_COLOR;
minWidth = 0;
if (minHeight == null)
minHeight = 0;
// if staysPressed is true, this.active will be true past the first release of a button, untill a subsequent one (the button if (staysPressed == null)
// is unpressed) or untill release() is called explicitly staysPressed = false;
this._active = false; if (minWidth == null)
this._isBetweenPressAndRelease = false; minWidth = 0;
this._mouseIsOverButton = false; if (minHeight == null)
minHeight = 0;
this.button = new Clutter.Group({reactive: true}); // if staysPressed is true, this.active will be true past the first release of a button, untill a subsequent one (the button
this._background = new Clutter.Rectangle({ color: this._buttonColor}); // is unpressed) or untill release() is called explicitly
this._pressedBackground = new Clutter.Rectangle({ color: this._pressedButtonColor, opacity: NO_OPACITY});
this._label = new Clutter.Label({ font_name: "Sans Bold 16px",
text: text});
this._label.set_position(5, 5);
let backgroundWidth = Math.max(this._label.get_width()+10, minWidth);
let backgroundHeight = Math.max(this._label.get_height()+10, minHeight);
this._background.set_width(backgroundWidth)
this._background.set_height(backgroundHeight)
this._pressedBackground.set_width(backgroundWidth)
this._pressedBackground.set_height(backgroundHeight)
this.button.add_actor(this._background);
this.button.add_actor(this._pressedBackground);
this.button.add_actor(this._label);
this.button.connect('button-press-event',
function(o, event) {
me._isBetweenPressAndRelease = true;
Tweener.addTween(me._pressedBackground,
{ time: ANIMATION_TIME,
opacity: FULL_OPACITY,
transition: "linear"
});
return false;
});
this.button.connect('button-release-event',
function(o, event) {
me._isBetweenPressAndRelease = false;
if (!staysPressed || me._active) {
me.release();
} else {
me._active = true;
}
return false;
});
this.button.connect('enter-event',
function(o, event) {
me._mouseIsOverButton = true;
if (!me._active) {
Tweener.removeTweens(me._pressedBackground);
me._pressedBackground.set_opacity(PARTIAL_OPACITY);
}
return false;
});
this.button.connect('leave-event',
function(o, event) {
me._isBetweenPressAndRelease = false;
me._mouseIsOverButton = false;
if (!me._active) {
Tweener.removeTweens(me._pressedBackground);
me._pressedBackground.set_opacity(NO_OPACITY);
}
return false;
});
},
release : function() {
if (!this._isBetweenPressAndRelease) {
this._active = false; this._active = false;
Tweener.removeTweens(this._pressedBackground); this._isBetweenPressAndRelease = false;
if (this._mouseIsOverButton) { this._mouseIsOverButton = false;
this._pressedBackground.set_opacity(PARTIAL_OPACITY);
} else { this.button = new Clutter.Group({reactive: true});
this._pressedBackground.set_opacity(NO_OPACITY); this._background = new Clutter.Rectangle({ color: this._buttonColor});
this._pressedBackground = new Clutter.Rectangle({ color: this._pressedButtonColor, opacity: NO_OPACITY});
this._label = new Clutter.Label({ font_name: "Sans Bold 16px",
text: text});
this._label.set_position(5, 5);
let backgroundWidth = Math.max(this._label.get_width()+10, minWidth);
let backgroundHeight = Math.max(this._label.get_height()+10, minHeight);
this._background.set_width(backgroundWidth)
this._background.set_height(backgroundHeight)
this._pressedBackground.set_width(backgroundWidth)
this._pressedBackground.set_height(backgroundHeight)
this.button.add_actor(this._background);
this.button.add_actor(this._pressedBackground);
this.button.add_actor(this._label);
this.button.connect('button-press-event',
function(o, event) {
me._isBetweenPressAndRelease = true;
Tweener.addTween(me._pressedBackground,
{ time: ANIMATION_TIME,
opacity: FULL_OPACITY,
transition: "linear"
});
return false;
});
this.button.connect('button-release-event',
function(o, event) {
me._isBetweenPressAndRelease = false;
if (!staysPressed || me._active) {
me.release();
} else {
me._active = true;
}
return false;
});
this.button.connect('enter-event',
function(o, event) {
me._mouseIsOverButton = true;
if (!me._active) {
Tweener.removeTweens(me._pressedBackground);
me._pressedBackground.set_opacity(PARTIAL_OPACITY);
}
return false;
});
this.button.connect('leave-event',
function(o, event) {
me._isBetweenPressAndRelease = false;
me._mouseIsOverButton = false;
if (!me._active) {
Tweener.removeTweens(me._pressedBackground);
me._pressedBackground.set_opacity(NO_OPACITY);
}
return false;
});
},
release : function() {
if (!this._isBetweenPressAndRelease) {
this._active = false;
Tweener.removeTweens(this._pressedBackground);
if (this._mouseIsOverButton) {
this._pressedBackground.set_opacity(PARTIAL_OPACITY);
} else {
this._pressedBackground.set_opacity(NO_OPACITY);
}
} }
} }
} };
}

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
@ -18,67 +18,67 @@ let overlay = null;
let run_dialog = null; let run_dialog = null;
let wm = null; let wm = null;
//The "FrameTicker" object is an object used to feed new frames to Tweener // The "FrameTicker" object is an object used to feed new frames to Tweener
//so it can update values and redraw. The default frame ticker for // so it can update values and redraw. The default frame ticker for
//Tweener just uses a simple timeout at a fixed frame rate and has no idea // Tweener just uses a simple timeout at a fixed frame rate and has no idea
//of "catching up" by dropping frames. // of "catching up" by dropping frames.
//
//We substitute it with custom frame ticker here that connects Tweener to // We substitute it with custom frame ticker here that connects Tweener to
//a Clutter.TimeLine. Now, Clutter.Timeline itself isn't a whole lot more // a Clutter.TimeLine. Now, Clutter.Timeline itself isn't a whole lot more
//sophisticated than a simple timeout at a fixed frame rate, but at least // sophisticated than a simple timeout at a fixed frame rate, but at least
//it knows how to drop frames. (See HippoAnimationManager for a more // it knows how to drop frames. (See HippoAnimationManager for a more
//sophisticated view of continous time updates; even better is to pay // sophisticated view of continous time updates; even better is to pay
//attention to the vertical vblank and sync to that when possible.) // attention to the vertical vblank and sync to that when possible.)
//
function ClutterFrameTicker() { function ClutterFrameTicker() {
this._init(); this._init();
} }
ClutterFrameTicker.prototype = { ClutterFrameTicker.prototype = {
TARGET_FRAME_RATE : 60, TARGET_FRAME_RATE : 60,
_init : function() { _init : function() {
// We don't have a finite duration; tweener will tell us to stop // We don't have a finite duration; tweener will tell us to stop
// when we need to stop, so use 1000 seconds as "infinity" // when we need to stop, so use 1000 seconds as "infinity"
this._timeline = new Clutter.Timeline({ fps: this.TARGET_FRAME_RATE, this._timeline = new Clutter.Timeline({ fps: this.TARGET_FRAME_RATE,
duration: 1000*1000 }); duration: 1000*1000 });
this._frame = 0; this._frame = 0;
let me = this; let me = this;
this._timeline.connect('new-frame', this._timeline.connect('new-frame',
function(timeline, frame) { function(timeline, frame) {
me._onNewFrame(frame); me._onNewFrame(frame);
}); });
}, },
_onNewFrame : function(frame) { _onNewFrame : function(frame) {
// Unfortunately the interface to to send a new frame to tweener // Unfortunately the interface to to send a new frame to tweener
// is a simple "next frame" and there is no provision for signaling // is a simple "next frame" and there is no provision for signaling
// that frames have been skipped or just telling it the new time. // that frames have been skipped or just telling it the new time.
// But what it actually does internally is just: // But what it actually does internally is just:
// //
// _currentTime += 1000/_ticker.FRAME_RATE; // _currentTime += 1000/_ticker.FRAME_RATE;
// //
// So by dynamically adjusting the value of FRAME_RATE we can trick // So by dynamically adjusting the value of FRAME_RATE we can trick
// it into dealing with dropped frames. // it into dealing with dropped frames.
let delta = frame - this._frame; let delta = frame - this._frame;
if (delta == 0) if (delta == 0)
this.FRAME_RATE = this.TARGET_FRAME_RATE; this.FRAME_RATE = this.TARGET_FRAME_RATE;
else else
this.FRAME_RATE = this.TARGET_FRAME_RATE / delta; this.FRAME_RATE = this.TARGET_FRAME_RATE / delta;
this._frame = frame; this._frame = frame;
this.emit('prepare-frame'); this.emit('prepare-frame');
}, },
start : function() { start : function() {
this._timeline.start(); this._timeline.start();
}, },
stop : function() { stop : function() {
this._timeline.stop(); this._timeline.stop();
this._frame = 0; this._frame = 0;
} }
}; };
Signals.addSignalMethods(ClutterFrameTicker.prototype); Signals.addSignalMethods(ClutterFrameTicker.prototype);
@ -121,9 +121,9 @@ function start() {
wm = new WindowManager.WindowManager(); wm = new WindowManager.WindowManager();
} }
//Used to go into a mode where all keyboard and mouse input goes to // Used to go into a mode where all keyboard and mouse input goes to
//the stage. Returns true if we successfully grabbed the keyboard and // the stage. Returns true if we successfully grabbed the keyboard and
//went modal, false otherwise // went modal, false otherwise
function startModal() { function startModal() {
let global = Shell.Global.get(); let global = Shell.Global.get();

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Signals = imports.signals; const Signals = imports.signals;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -23,27 +23,27 @@ SIDESHOW_SEARCH_BG_COLOR.from_pixel(0xffffffff);
const SIDESHOW_TEXT_COLOR = new Clutter.Color(); const SIDESHOW_TEXT_COLOR = new Clutter.Color();
SIDESHOW_TEXT_COLOR.from_pixel(0xffffffff); SIDESHOW_TEXT_COLOR.from_pixel(0xffffffff);
//Time for initial animation going into overlay mode // Time for initial animation going into overlay mode
const ANIMATION_TIME = 0.5; const ANIMATION_TIME = 0.5;
//How much to scale the desktop down by in overlay mode // How much to scale the desktop down by in overlay mode
const DESKTOP_SCALE = 0.75; const DESKTOP_SCALE = 0.75;
//Windows are slightly translucent in the overlay mode // Windows are slightly translucent in the overlay mode
const WINDOW_OPACITY = 0.9 * 255; const WINDOW_OPACITY = 0.9 * 255;
//Define a layout scheme for small window counts. For larger // Define a layout scheme for small window counts. For larger
//counts we fall back to an algorithm. We need more schemes here // counts we fall back to an algorithm. We need more schemes here
//unless we have a really good algorithm. // unless we have a really good algorithm.
//
//Each triplet is [xCenter, yCenter, scale] where the scale // Each triplet is [xCenter, yCenter, scale] where the scale
//is relative to the width of the desktop. // is relative to the width of the desktop.
const POSITIONS = { const POSITIONS = {
1: [[0.5, 0.5, 0.8]], 1: [[0.5, 0.5, 0.8]],
2: [[0.25, 0.5, 0.4], [0.75, 0.5, 0.4]], 2: [[0.25, 0.5, 0.4], [0.75, 0.5, 0.4]],
3: [[0.25, 0.25, 0.33], [0.75, 0.25, 0.33], [0.5, 0.75, 0.33]], 3: [[0.25, 0.25, 0.33], [0.75, 0.25, 0.33], [0.5, 0.75, 0.33]],
4: [[0.25, 0.25, 0.33], [0.75, 0.25, 0.33], [0.75, 0.75, 0.33], [0.25, 0.75, 0.33]], 4: [[0.25, 0.25, 0.33], [0.75, 0.25, 0.33], [0.75, 0.75, 0.33], [0.25, 0.75, 0.33]],
5: [[0.165, 0.25, 0.28], [0.495, 0.25, 0.28], [0.825, 0.25, 0.28], [0.25, 0.75, 0.4], [0.75, 0.75, 0.4]] 5: [[0.165, 0.25, 0.28], [0.495, 0.25, 0.28], [0.825, 0.25, 0.28], [0.25, 0.75, 0.4], [0.75, 0.75, 0.4]]
}; };
function Sideshow(width) { function Sideshow(width) {
@ -51,95 +51,95 @@ function Sideshow(width) {
} }
Sideshow.prototype = { Sideshow.prototype = {
_init : function(width) { _init : function(width) {
let me = this; let me = this;
let global = Shell.Global.get(); let global = Shell.Global.get();
this._group = new Clutter.Group(); this._group = new Clutter.Group();
this._group.hide(); this._group.hide();
global.stage.add_actor(this._group); global.stage.add_actor(this._group);
let icontheme = Gtk.IconTheme.get_default(); let icontheme = Gtk.IconTheme.get_default();
let rect = new Clutter.Rectangle({ color: SIDESHOW_SEARCH_BG_COLOR, let rect = new Clutter.Rectangle({ color: SIDESHOW_SEARCH_BG_COLOR,
x: SIDESHOW_PAD, x: SIDESHOW_PAD,
y: Panel.PANEL_HEIGHT + SIDESHOW_PAD, y: Panel.PANEL_HEIGHT + SIDESHOW_PAD,
width: width, width: width,
height: 24}); height: 24});
this._group.add_actor(rect); this._group.add_actor(rect);
let searchIconTexture = new Clutter.Texture({ x: SIDESHOW_PAD + 2, let searchIconTexture = new Clutter.Texture({ x: SIDESHOW_PAD + 2,
y: rect.y + 2 }); y: rect.y + 2 });
let searchIconPath = icontheme.lookup_icon('gtk-find', 16, 0).get_filename(); let searchIconPath = icontheme.lookup_icon('gtk-find', 16, 0).get_filename();
searchIconTexture.set_from_file(searchIconPath); searchIconTexture.set_from_file(searchIconPath);
this._group.add_actor(searchIconTexture); this._group.add_actor(searchIconTexture);
this._searchEntry = new Clutter.Entry({ this._searchEntry = new Clutter.Entry({
font_name: "Sans 14px", font_name: "Sans 14px",
x: searchIconTexture.x x: searchIconTexture.x
+ searchIconTexture.width + 4, + searchIconTexture.width + 4,
y: searchIconTexture.y, y: searchIconTexture.y,
width: rect.width - (searchIconTexture.x), width: rect.width - (searchIconTexture.x),
height: searchIconTexture.height}); height: searchIconTexture.height});
this._group.add_actor(this._searchEntry); this._group.add_actor(this._searchEntry);
global.stage.set_key_focus(this._searchEntry); global.stage.set_key_focus(this._searchEntry);
this._searchQueued = false; this._searchQueued = false;
this._searchActive = false; this._searchActive = false;
this._searchEntry.connect('notify::text', function (se, prop) { this._searchEntry.connect('notify::text', function (se, prop) {
if (me._searchQueued) if (me._searchQueued)
return; return;
Mainloop.timeout_add(250, function() { Mainloop.timeout_add(250, function() {
let text = me._searchEntry.text; let text = me._searchEntry.text;
me._searchQueued = false; me._searchQueued = false;
me._searchActive = text != ''; me._searchActive = text != '';
me._appdisplay.setSearch(text); me._appdisplay.setSearch(text);
return false;
});
});
this._searchEntry.connect('activate', function (se) {
if (!me._searchActive)
return false;
me._appdisplay.searchActivate();
return true;
});
this._searchEntry.connect('key-press-event', function (se, e) {
let code = e.get_code();
log("code: " + code);
if (code == 111) {
me._appdisplay.selectUp();
return true;
} else if (code == 116) {
me._appdisplay.selectDown();
return true;
}
return false; return false;
}); });
});
this._searchEntry.connect('activate', function (se) {
if (!me._searchActive)
return false;
me._appdisplay.searchActivate();
return true;
});
this._searchEntry.connect('key-press-event', function (se, e) {
let code = e.get_code();
log("code: " + code);
if (code == 111) {
me._appdisplay.selectUp();
return true;
} else if (code == 116) {
me._appdisplay.selectDown();
return true;
}
return false;
});
let appsText = new Clutter.Label({ color: SIDESHOW_TEXT_COLOR, let appsText = new Clutter.Label({ color: SIDESHOW_TEXT_COLOR,
font_name: "Sans Bold 14px", font_name: "Sans Bold 14px",
text: "Applications", text: "Applications",
x: SIDESHOW_PAD, x: SIDESHOW_PAD,
y: this._searchEntry.y + this._searchEntry.height + 10, y: this._searchEntry.y + this._searchEntry.height + 10,
height: 16}); height: 16});
this._group.add_actor(appsText); this._group.add_actor(appsText);
let menuY = appsText.y + appsText.height + 6; let menuY = appsText.y + appsText.height + 6;
this._appdisplay = new AppDisplay.AppDisplay(SIDESHOW_PAD, this._appdisplay = new AppDisplay.AppDisplay(SIDESHOW_PAD,
menuY, width, global.screen_height - menuY); menuY, width, global.screen_height - menuY);
/* Proxy the activated signal */ /* Proxy the activated signal */
this._appdisplay.connect('activated', function(appdisplay) { this._appdisplay.connect('activated', function(appdisplay) {
me.emit('activated'); me.emit('activated');
}); });
}, },
show: function() { show: function() {
this._group.show(); this._group.show();
this._appdisplay.show(); this._appdisplay.show();
}, },
hide: function() { hide: function() {
this._group.hide(); this._group.hide();
this._appdisplay.hide(); this._appdisplay.hide();
} }
}; };
Signals.addSignalMethods(Sideshow.prototype); Signals.addSignalMethods(Sideshow.prototype);
@ -148,252 +148,254 @@ function Overlay() {
} }
Overlay.prototype = { Overlay.prototype = {
_init : function() { _init : function() {
let me = this; let me = this;
let global = Shell.Global.get(); let global = Shell.Global.get();
this._group = new Clutter.Group(); this._group = new Clutter.Group();
this.visible = false; this.visible = false;
let background = new Clutter.Rectangle({ color: OVERLAY_BACKGROUND_COLOR, let background = new Clutter.Rectangle({ color: OVERLAY_BACKGROUND_COLOR,
reactive: true, reactive: true,
x: 0, x: 0,
y: Panel.PANEL_HEIGHT, y: Panel.PANEL_HEIGHT,
width: global.screen_width, width: global.screen_width,
height: global.screen_width - Panel.PANEL_HEIGHT }); height: global.screen_width - Panel.PANEL_HEIGHT });
this._group.add_actor(background); this._group.add_actor(background);
this._group.hide(); this._group.hide();
global.overlay_group.add_actor(this._group); global.overlay_group.add_actor(this._group);
this._windowClones = []; this._windowClones = [];
// TODO - recalculate everything when desktop size changes // TODO - recalculate everything when desktop size changes
this._recalculateSize(); this._recalculateSize();
this._sideshow = new Sideshow(this._desktopX - 10); this._sideshow = new Sideshow(this._desktopX - 10);
this._sideshow.connect('activated', function(sideshow) { this._sideshow.connect('activated', function(sideshow) {
// TODO - have some sort of animation/effect while // TODO - have some sort of animation/effect while
// transitioning to the new app. We definitely need // transitioning to the new app. We definitely need
// startup-notification integration at least. // startup-notification integration at least.
me._deactivate(); me._deactivate();
}); });
}, },
_recalculateSize: function () { _recalculateSize: function () {
let global = Shell.Global.get(); let global = Shell.Global.get();
let screenWidth = global.screen_width; let screenWidth = global.screen_width;
let screenHeight = global.screen_height; let screenHeight = global.screen_height;
// The desktop windows are shown on top of a scaled down version of the // The desktop windows are shown on top of a scaled down version of the
// desktop. This is positioned at the right side of the screen // desktop. This is positioned at the right side of the screen
this._desktopWidth = screenWidth * DESKTOP_SCALE; this._desktopWidth = screenWidth * DESKTOP_SCALE;
this._desktopHeight = screenHeight * DESKTOP_SCALE; this._desktopHeight = screenHeight * DESKTOP_SCALE;
this._desktopX = screenWidth - this._desktopWidth - 10; this._desktopX = screenWidth - this._desktopWidth - 10;
this._desktopY = Panel.PANEL_HEIGHT + (screenHeight - this._desktopHeight - Panel.PANEL_HEIGHT) / 2; this._desktopY = Panel.PANEL_HEIGHT + (screenHeight - this._desktopHeight - Panel.PANEL_HEIGHT) / 2;
}, },
show : function() { show : function() {
if (this.visible) if (this.visible)
return; return;
this.visible = true;
let global = Shell.Global.get(); this.visible = true;
let windows = global.get_windows(); let global = Shell.Global.get();
let desktopWindow = null;
this._recalculateSize(); let windows = global.get_windows();
let desktopWindow = null;
for (let i = 0; i < windows.length; i++) this._recalculateSize();
if (windows[i].get_window_type() == Meta.WindowType.DESKTOP)
desktopWindow = windows[i];
// If a file manager is displaying desktop icons, there will be a desktop window. for (let i = 0; i < windows.length; i++)
// This window will have the size of the whole desktop. When such window is not present if (windows[i].get_window_type() == Meta.WindowType.DESKTOP)
// (e.g. when the preference for showing icons on the desktop is disabled by the user desktopWindow = windows[i];
// or we are running inside a Xephyr window), we should create a desktop rectangle
// to serve as the background.
if (desktopWindow)
this._createDesktopClone(desktopWindow);
else
this._createDesktopRectangle();
// Count the total number of windows so we know what layout scheme to use // If a file manager is displaying desktop icons, there will be a desktop window.
let numberOfWindows = 0; // This window will have the size of the whole desktop. When such window is not present
for (let i = 0; i < windows.length; i++) { // (e.g. when the preference for showing icons on the desktop is disabled by the user
let w = windows[i]; // or we are running inside a Xephyr window), we should create a desktop rectangle
if (w == desktopWindow || w.is_override_redirect()) // to serve as the background.
continue; if (desktopWindow)
this._createDesktopClone(desktopWindow);
else
this._createDesktopRectangle();
numberOfWindows++; // Count the total number of windows so we know what layout scheme to use
let numberOfWindows = 0;
for (let i = 0; i < windows.length; i++) {
let w = windows[i];
if (w == desktopWindow || w.is_override_redirect())
continue;
numberOfWindows++;
}
// Now create actors for all the desktop windows. Do it in
// reverse order so that the active actor ends up on top
let windowIndex = 0;
for (let i = windows.length - 1; i >= 0; i--) {
let w = windows[i];
if (w == desktopWindow || w.is_override_redirect())
continue;
this._createWindowClone(w, numberOfWindows - windowIndex - 1, numberOfWindows);
windowIndex++;
}
this._sideshow.show();
// All the the actors in the window group are completely obscured,
// hiding the group holding them while the overlay is displayed greatly
// increases performance of the overlay especially when there are many
// windows visible.
//
// If we switched to displaying the actors in the overlay rather than
// clones of them, this would obviously no longer be necessary.
global.window_group.hide()
this._group.show();
},
hide : function() {
if (!this.visible)
return;
let global = Shell.Global.get();
this.visible = false;
global.window_group.show()
this._group.hide();
for (let i = 0; i < this._windowClones.length; i++) {
this._windowClones[i].destroy();
}
this._sideshow.hide();
this._windowClones = [];
},
_createDesktopClone : function(w) {
let clone = new Clutter.CloneTexture({ parent_texture: w.get_texture(),
reactive: true,
x: 0,
y: 0 });
this._addDesktop(clone);
},
_createDesktopRectangle : function() {
let global = Shell.Global.get();
// In the case when we have a desktop window from the file manager, its height is
// full-screen, i.e. it includes the height of the panel, so we should not subtract
// the height of the panel from global.screen_height here either to have them show
// up identically.
// We are also using (0,0) coordinates in both cases which makes the background
// window animate out from behind the panel.
let desktopRectangle = new Clutter.Rectangle({ color: global.stage.color,
reactive: true,
x: 0,
y: 0,
width: global.screen_width,
height: global.screen_height });
this._addDesktop(desktopRectangle);
},
_addDesktop : function(desktop) {
let me = this;
this._windowClones.push(desktop);
this._group.add_actor(desktop);
// Since the right side only moves a little bit (the width of padding
// we add) it looks less jittery to put the anchor there.
desktop.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
Tweener.addTween(desktop,
{ x: this._desktopX + this._desktopWidth,
y: this._desktopY,
scale_x: DESKTOP_SCALE,
scale_y: DESKTOP_SCALE,
time: ANIMATION_TIME,
transition: "easeOutQuad"
});
desktop.connect("button-press-event",
function() {
me._deactivate();
});
},
// windowIndex == 0 => top in stacking order
_computeWindowPosition : function(windowIndex, numberOfWindows) {
if (numberOfWindows in POSITIONS)
return POSITIONS[numberOfWindows][windowIndex];
// If we don't have a predefined scheme for this window count, overlap the windows
// along the diagonal of the desktop (improve this!)
let fraction = Math.sqrt(1/numberOfWindows);
// The top window goes at the lower right - this is different from the
// fixed position schemes where the windows are in "reading order"
// and the top window goes at the upper left.
let pos = (numberOfWindows - windowIndex - 1) / (numberOfWindows - 1);
let xCenter = (fraction / 2) + (1 - fraction) * pos;
let yCenter = xCenter;
return [xCenter, yCenter, fraction];
},
_createWindowClone : function(w, windowIndex, numberOfWindows) {
let me = this;
// We show the window using "clones" of the texture .. separate
// actors that mirror the original actors for the window. For
// animation purposes, it may be better to actually move the
// original actors about instead.
let clone = new Clutter.CloneTexture({ parent_texture: w.get_texture(),
reactive: true,
x: w.x,
y: w.y });
let [xCenter, yCenter, fraction] = this._computeWindowPosition(windowIndex, numberOfWindows);
let desiredSize = this._desktopWidth * fraction;
xCenter = this._desktopX + xCenter * this._desktopWidth;
yCenter = this._desktopY + yCenter * this._desktopHeight;
let size = clone.width;
if (clone.height > size)
size = clone.height;
// Never scale up
let scale = desiredSize / size;
if (scale > 1)
scale = 1;
this._group.add_actor(clone);
this._windowClones.push(clone);
Tweener.addTween(clone,
{ x: xCenter - 0.5 * scale * w.width,
y: yCenter - 0.5 * scale * w.height,
scale_x: scale,
scale_y: scale,
time: ANIMATION_TIME,
opacity: WINDOW_OPACITY,
transition: "easeOutQuad"
});
clone.connect("button-press-event",
function(clone, event) {
me._activateWindow(w, event.get_time());
});
},
_activateWindow : function(w, time) {
this._deactivate();
w.get_meta_window().activate(time);
},
_deactivate : function() {
Main.hide_overlay();
} }
// Now create actors for all the desktop windows. Do it in
// reverse order so that the active actor ends up on top
let windowIndex = 0;
for (let i = windows.length - 1; i >= 0; i--) {
let w = windows[i];
if (w == desktopWindow || w.is_override_redirect())
continue;
this._createWindowClone(w, numberOfWindows - windowIndex - 1, numberOfWindows);
windowIndex++;
}
this._sideshow.show();
// All the the actors in the window group are completely obscured,
// hiding the group holding them while the overlay is displayed greatly
// increases performance of the overlay especially when there are many
// windows visible.
//
// If we switched to displaying the actors in the overlay rather than
// clones of them, this would obviously no longer be necessary.
global.window_group.hide()
this._group.show();
},
hide : function() {
if (!this.visible)
return;
let global = Shell.Global.get();
this.visible = false;
global.window_group.show()
this._group.hide();
for (let i = 0; i < this._windowClones.length; i++) {
this._windowClones[i].destroy();
}
this._sideshow.hide();
this._windowClones = [];
},
_createDesktopClone : function(w) {
let clone = new Clutter.CloneTexture({ parent_texture: w.get_texture(),
reactive: true,
x: 0,
y: 0 });
this._addDesktop(clone);
},
_createDesktopRectangle : function() {
let global = Shell.Global.get();
// In the case when we have a desktop window from the file manager, its height is
// full-screen, i.e. it includes the height of the panel, so we should not subtract
// the height of the panel from global.screen_height here either to have them show
// up identically.
// We are also using (0,0) coordinates in both cases which makes the background
// window animate out from behind the panel.
let desktopRectangle = new Clutter.Rectangle({ color: global.stage.color,
reactive: true,
x: 0,
y: 0,
width: global.screen_width,
height: global.screen_height });
this._addDesktop(desktopRectangle);
},
_addDesktop : function(desktop) {
let me = this;
this._windowClones.push(desktop);
this._group.add_actor(desktop);
// Since the right side only moves a little bit (the width of padding
// we add) it looks less jittery to put the anchor there.
desktop.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
Tweener.addTween(desktop,
{ x: this._desktopX + this._desktopWidth,
y: this._desktopY,
scale_x: DESKTOP_SCALE,
scale_y: DESKTOP_SCALE,
time: ANIMATION_TIME,
transition: "easeOutQuad"
});
desktop.connect("button-press-event",
function() {
me._deactivate();
});
},
//windowIndex == 0 => top in stacking order
_computeWindowPosition : function(windowIndex, numberOfWindows) {
if (numberOfWindows in POSITIONS)
return POSITIONS[numberOfWindows][windowIndex];
// If we don't have a predefined scheme for this window count, overlap the windows
// along the diagonal of the desktop (improve this!)
let fraction = Math.sqrt(1/numberOfWindows);
// The top window goes at the lower right - this is different from the
// fixed position schemes where the windows are in "reading order"
// and the top window goes at the upper left.
let pos = (numberOfWindows - windowIndex - 1) / (numberOfWindows - 1);
let xCenter = (fraction / 2) + (1 - fraction) * pos;
let yCenter = xCenter;
return [xCenter, yCenter, fraction];
},
_createWindowClone : function(w, windowIndex, numberOfWindows) {
let me = this;
// We show the window using "clones" of the texture .. separate
// actors that mirror the original actors for the window. For
// animation purposes, it may be better to actually move the
// original actors about instead.
let clone = new Clutter.CloneTexture({ parent_texture: w.get_texture(),
reactive: true,
x: w.x,
y: w.y });
let [xCenter, yCenter, fraction] = this._computeWindowPosition(windowIndex, numberOfWindows);
let desiredSize = this._desktopWidth * fraction;
xCenter = this._desktopX + xCenter * this._desktopWidth;
yCenter = this._desktopY + yCenter * this._desktopHeight;
let size = clone.width;
if (clone.height > size)
size = clone.height;
// Never scale up
let scale = desiredSize / size;
if (scale > 1)
scale = 1;
this._group.add_actor(clone);
this._windowClones.push(clone);
Tweener.addTween(clone,
{ x: xCenter - 0.5 * scale * w.width,
y: yCenter - 0.5 * scale * w.height,
scale_x: scale,
scale_y: scale,
time: ANIMATION_TIME,
opacity: WINDOW_OPACITY,
transition: "easeOutQuad"
});
clone.connect("button-press-event",
function(clone, event) {
me._activateWindow(w, event.get_time());
});
},
_activateWindow : function(w, time) {
this._deactivate();
w.get_meta_window().activate(time);
},
_deactivate : function() {
Main.hide_overlay();
}
}; };

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -21,123 +21,123 @@ function Panel() {
} }
Panel.prototype = { Panel.prototype = {
_init : function() { _init : function() {
let global = Shell.Global.get(); let global = Shell.Global.get();
this._group = new Clutter.Group(); this._group = new Clutter.Group();
let background = new Clutter.Rectangle({ color: PANEL_BACKGROUND_COLOR, let background = new Clutter.Rectangle({ color: PANEL_BACKGROUND_COLOR,
reactive: true, reactive: true,
width: global.screen_width+2, width: global.screen_width+2,
height: PANEL_HEIGHT+1, height: PANEL_HEIGHT+1,
border_width: 1}); border_width: 1});
background.set_position(-1, -1); background.set_position(-1, -1);
this._group.add_actor(background); this._group.add_actor(background);
this.button = new Button.Button("Activities", PANEL_BACKGROUND_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, true, null, PANEL_HEIGHT-1); this.button = new Button.Button("Activities", PANEL_BACKGROUND_COLOR, PRESSED_BUTTON_BACKGROUND_COLOR, true, null, PANEL_HEIGHT-1);
this._group.add_actor(this.button.button); this._group.add_actor(this.button.button);
this._grid = new Tidy.Grid({ height: TRAY_HEIGHT, this._grid = new Tidy.Grid({ height: TRAY_HEIGHT,
valign: 0.5, valign: 0.5,
end_align: true, end_align: true,
column_gap: 2 }) column_gap: 2 })
this._group.add_actor(this._grid); this._group.add_actor(this._grid);
this._clock = new Clutter.Label({ font_name: "Sans Bold 16px", this._clock = new Clutter.Label({ font_name: "Sans Bold 16px",
text: "" }); text: "" });
this._grid.add_actor(this._clock); this._grid.add_actor(this._clock);
// Setting the anchor point at top right (north east) makes that portion of the // Setting the anchor point at top right (north east) makes that portion of the
// grid positioned at the position specified below. // grid positioned at the position specified below.
this._grid.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); this._grid.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
this._grid.set_position(global.screen_width - 2, (PANEL_HEIGHT - TRAY_HEIGHT) / 2); this._grid.set_position(global.screen_width - 2, (PANEL_HEIGHT - TRAY_HEIGHT) / 2);
this._traymanager = new Shell.TrayManager({ bg_color: PANEL_BACKGROUND_COLOR }); this._traymanager = new Shell.TrayManager({ bg_color: PANEL_BACKGROUND_COLOR });
let me = this; let me = this;
// the anchor point needs to be updated each time the height/width of the content might have changed, because // the anchor point needs to be updated each time the height/width of the content might have changed, because
// it doesn't get updated on its own // it doesn't get updated on its own
this._traymanager.connect('tray-icon-added', this._traymanager.connect('tray-icon-added',
function(o, icon) { function(o, icon) {
me._grid.add_actor(icon); me._grid.add_actor(icon);
/* bump the clock back to the end */ /* bump the clock back to the end */
me._grid.remove_actor(me._clock); me._grid.remove_actor(me._clock);
me._grid.add_actor(me._clock); me._grid.add_actor(me._clock);
me._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); me._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
}); });
this._traymanager.connect('tray-icon-removed', this._traymanager.connect('tray-icon-removed',
function(o, icon) { function(o, icon) {
me._grid.remove_actor(icon); me._grid.remove_actor(icon);
me._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); me._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
}); });
this._traymanager.manage_stage(global.stage); this._traymanager.manage_stage(global.stage);
// TODO: decide what to do with the rest of the panel in the overlay mode (make it fade-out, become non-reactive, etc.) // TODO: decide what to do with the rest of the panel in the overlay mode (make it fade-out, become non-reactive, etc.)
// We get into the overlay mode on button-press-event as opposed to button-release-event because eventually we'll probably // We get into the overlay mode on button-press-event as opposed to button-release-event because eventually we'll probably
// have the overlay act like a menu that allows the user to release the mouse on the activity the user wants // have the overlay act like a menu that allows the user to release the mouse on the activity the user wants
// to switch to. // to switch to.
this.button.button.connect('button-press-event', this.button.button.connect('button-press-event',
function(o, event) { function(o, event) {
if (Main.overlay.visible) if (Main.overlay.visible)
Main.hide_overlay(); Main.hide_overlay();
else else
Main.show_overlay(); Main.show_overlay();
return true; return true;
}); });
this._setStruts(); this._setStruts();
global.screen.connect('notify::n-workspaces', global.screen.connect('notify::n-workspaces',
function() { function() {
me._setStruts(); me._setStruts();
}); });
global.stage.add_actor(this._group); global.stage.add_actor(this._group);
this._updateClock(); this._updateClock();
this._startClock(); this._startClock();
}, },
// Struts determine the area along each side of the screen that is reserved // Struts determine the area along each side of the screen that is reserved
// and not available to applications // and not available to applications
_setStruts: function() { _setStruts: function() {
let global = Shell.Global.get(); let global = Shell.Global.get();
let struts = [ let struts = [
new Meta.Strut({ new Meta.Strut({
rect: { rect: {
x: 0, x: 0,
y: 0, y: 0,
width: global.screen_width, width: global.screen_width,
height: PANEL_HEIGHT height: PANEL_HEIGHT
}, },
side: Meta.Direction.TOP side: Meta.Direction.TOP
}) })
]; ];
let screen = global.screen; let screen = global.screen;
for (let i = 0; i < screen.n_workspaces; i++) { for (let i = 0; i < screen.n_workspaces; i++) {
let workspace = screen.get_workspace_by_index(i); let workspace = screen.get_workspace_by_index(i);
workspace.set_builtin_struts(struts); workspace.set_builtin_struts(struts);
}
},
_startClock: function() {
let me = this;
// TODO: this makes the clock updated every 60 seconds, but not necessarily on the minute, so it is inaccurate
Mainloop.timeout_add_seconds(60,
function() {
me._updateClock();
return true;
});
},
_updateClock: function() {
this._clock.set_text(new Date().toLocaleFormat("%H:%M"));
return true;
},
overlayHidden: function() {
this.button.release();
} }
},
_startClock: function() {
let me = this;
// TODO: this makes the clock updated every 60 seconds, but not necessarily on the minute, so it is inaccurate
Mainloop.timeout_add_seconds(60,
function() {
me._updateClock();
return true;
});
},
_updateClock: function() {
this._clock.set_text(new Date().toLocaleFormat("%H:%M"));
return true;
},
overlayHidden: function() {
this.button.release();
}
}; };

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Signals = imports.signals; const Signals = imports.signals;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
@ -23,110 +23,111 @@ function RunDialog() {
}; };
RunDialog.prototype = { RunDialog.prototype = {
_init : function() { _init : function() {
let global = Shell.Global.get(); let global = Shell.Global.get();
// All actors are inside _group. We create it initially // All actors are inside _group. We create it initially
// hidden then show it in show() // hidden then show it in show()
this._group = new Clutter.Group({ visible: false }); this._group = new Clutter.Group({ visible: false });
global.stage.add_actor(this._group); global.stage.add_actor(this._group);
this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR, this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR,
width: global.screen_width, width: global.screen_width,
height: global.screen_height, height: global.screen_height,
border_width: 0, border_width: 0,
reactive: true }); reactive: true });
this._group.add_actor(this._overlay); this._group.add_actor(this._overlay);
let boxGroup = new Clutter.Group(); let boxGroup = new Clutter.Group();
boxGroup.set_position((global.screen_width - BOX_WIDTH) / 2, boxGroup.set_position((global.screen_width - BOX_WIDTH) / 2,
(global.screen_height - BOX_HEIGHT) / 2); (global.screen_height - BOX_HEIGHT) / 2);
this._group.add_actor(boxGroup); this._group.add_actor(boxGroup);
let box = new Clutter.Rectangle({ color: BOX_BACKGROUND_COLOR, let box = new Clutter.Rectangle({ color: BOX_BACKGROUND_COLOR,
reactive: false, reactive: false,
width: BOX_WIDTH, width: BOX_WIDTH,
height: BOX_HEIGHT, height: BOX_HEIGHT,
border_width: 0 }); border_width: 0 });
boxGroup.add_actor(box); boxGroup.add_actor(box);
let label = new Clutter.Label({ color: BOX_TEXT_COLOR, let label = new Clutter.Label({ color: BOX_TEXT_COLOR,
font_name: '18px Sans', font_name: '18px Sans',
text: 'Please enter a command:' }); text: 'Please enter a command:' });
label.set_position(6, 6); label.set_position(6, 6);
boxGroup.add_actor(label); boxGroup.add_actor(label);
this._entry = new Clutter.Entry({ color: BOX_TEXT_COLOR, this._entry = new Clutter.Entry({ color: BOX_TEXT_COLOR,
font_name: '20px Sans Bold', font_name: '20px Sans Bold',
reactive: true, reactive: true,
text: '', text: '',
entry_padding: 0, entry_padding: 0,
width: BOX_WIDTH - 12, width: BOX_WIDTH - 12,
height: BOX_HEIGHT - 12 }); height: BOX_HEIGHT - 12 });
// TODO: Implement relative positioning using Tidy. // TODO: Implement relative positioning using Tidy.
this._entry.set_position(6, 30); this._entry.set_position(6, 30);
boxGroup.add_actor(this._entry); boxGroup.add_actor(this._entry);
let me = this; let me = this;
this._entry.connect('activate', function (o, e) { this._entry.connect('activate', function (o, e) {
me.hide();
me._run(o.get_text());
return false;
});
},
_run : function(command) {
if (command) {
var p = new Shell.Process({'args' : [command]});
try {
p.run();
} catch (e) {
// TODO: Give the user direct feedback.
log('Could not run command ' + command + '.');
}
}
this.emit('run');
},
show : function() {
let me = this;
if (this._group.visible) // Already shown
return false;
if (!Main.startModal())
return false;
this._group.show_all();
this._entry.connect('key-press-event', function(o, e) {
if (e.get_code() == 9) {
me.hide(); me.hide();
me.emit('cancel'); me._run(o.get_text());
return true;
} else
return false; return false;
}); });
let global = Shell.Global.get(); },
global.stage.set_key_focus(this._entry);
return true; _run : function(command) {
}, if (command) {
var p = new Shell.Process({'args' : [command]});
try {
p.run();
} catch (e) {
// TODO: Give the user direct feedback.
log('Could not run command ' + command + '.');
}
}
hide : function() { this.emit('run');
if (!this._group.visible) },
return;
this._group.hide(); show : function() {
Main.endModal(); let me = this;
},
destroy : function(){ if (this._group.visible) // Already shown
this.hide(); return false;
this._group.destroy();
} if (!Main.startModal())
return false;
this._group.show_all();
this._entry.connect('key-press-event', function(o, e) {
if (e.get_code() == 9) {
me.hide();
me.emit('cancel');
return true;
} else
return false;
});
let global = Shell.Global.get();
global.stage.set_key_focus(this._entry);
return true;
},
hide : function() {
if (!this._group.visible)
return;
this._group.hide();
Main.endModal();
},
destroy : function(){
this.hide();
this._group.destroy();
}
}; };
Signals.addSignalMethods(RunDialog.prototype); Signals.addSignalMethods(RunDialog.prototype);

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -15,111 +15,111 @@ function WindowManager() {
} }
WindowManager.prototype = { WindowManager.prototype = {
_init : function() { _init : function() {
let me = this; let me = this;
this._global = Shell.Global.get(); this._global = Shell.Global.get();
this._shellwm = this._global.window_manager; this._shellwm = this._global.window_manager;
this._switchData = null; this._switchData = null;
this._shellwm.connect('switch-workspace', this._shellwm.connect('switch-workspace',
function(o, from, to, direction) { function(o, from, to, direction) {
let actors = me._shellwm.get_switch_workspace_actors(); let actors = me._shellwm.get_switch_workspace_actors();
me.switchWorkspace(actors, from, to, direction); me.switchWorkspace(actors, from, to, direction);
}); });
this._shellwm.connect('kill-switch-workspace', this._shellwm.connect('kill-switch-workspace',
function(o) { function(o) {
me.switchWorkspaceDone(); me.switchWorkspaceDone();
}); });
}, },
switchWorkspace : function(windows, from, to, direction) { switchWorkspace : function(windows, from, to, direction) {
/* @direction is the direction that the "camera" moves, so the /* @direction is the direction that the "camera" moves, so the
* screen contents have to move one screen's worth in the * screen contents have to move one screen's worth in the
* opposite direction. * opposite direction.
*/ */
let xDest = 0, yDest = 0; let xDest = 0, yDest = 0;
if (direction == Meta.MotionDirection.UP || if (direction == Meta.MotionDirection.UP ||
direction == Meta.MotionDirection.UP_LEFT || direction == Meta.MotionDirection.UP_LEFT ||
direction == Meta.MotionDirection.UP_RIGHT) direction == Meta.MotionDirection.UP_RIGHT)
yDest = this._global.screen_height; yDest = this._global.screen_height;
else if (direction == Meta.MotionDirection.DOWN || else if (direction == Meta.MotionDirection.DOWN ||
direction == Meta.MotionDirection.DOWN_LEFT || direction == Meta.MotionDirection.DOWN_LEFT ||
direction == Meta.MotionDirection.DOWN_RIGHT) direction == Meta.MotionDirection.DOWN_RIGHT)
yDest = -this._global.screen_height; yDest = -this._global.screen_height;
if (direction == Meta.MotionDirection.LEFT || if (direction == Meta.MotionDirection.LEFT ||
direction == Meta.MotionDirection.UP_LEFT || direction == Meta.MotionDirection.UP_LEFT ||
direction == Meta.MotionDirection.DOWN_LEFT) direction == Meta.MotionDirection.DOWN_LEFT)
xDest = this._global.screen_width; xDest = this._global.screen_width;
else if (direction == Meta.MotionDirection.RIGHT || else if (direction == Meta.MotionDirection.RIGHT ||
direction == Meta.MotionDirection.UP_RIGHT || direction == Meta.MotionDirection.UP_RIGHT ||
direction == Meta.MotionDirection.DOWN_RIGHT) direction == Meta.MotionDirection.DOWN_RIGHT)
xDest = -this._global.screen_width; xDest = -this._global.screen_width;
let switchData = {}; let switchData = {};
this._switchData = switchData; this._switchData = switchData;
switchData.inGroup = new Clutter.Group(); switchData.inGroup = new Clutter.Group();
switchData.outGroup = new Clutter.Group(); switchData.outGroup = new Clutter.Group();
switchData.windows = []; switchData.windows = [];
let wgroup = this._global.window_group; let wgroup = this._global.window_group;
wgroup.add_actor(switchData.inGroup); wgroup.add_actor(switchData.inGroup);
wgroup.add_actor(switchData.outGroup); wgroup.add_actor(switchData.outGroup);
for (let i = 0; i < windows.length; i++) { for (let i = 0; i < windows.length; i++) {
let window = windows[i]; let window = windows[i];
if (window.get_workspace() == from) { if (window.get_workspace() == from) {
switchData.windows.push({ window: window, switchData.windows.push({ window: window,
parent: window.get_parent() }); parent: window.get_parent() });
window.reparent(switchData.outGroup); window.reparent(switchData.outGroup);
} else if (window.get_workspace() == to) { } else if (window.get_workspace() == to) {
switchData.windows.push({ window: window, switchData.windows.push({ window: window,
parent: window.get_parent() }); parent: window.get_parent() });
window.reparent(switchData.inGroup); window.reparent(switchData.inGroup);
window.show_all(); window.show_all();
}
} }
switchData.inGroup.set_position(-xDest, -yDest);
switchData.inGroup.raise_top();
Tweener.addTween(switchData.outGroup,
{ x: xDest,
y: yDest,
time: SWITCH_ANIMATION_TIME,
transition: "easeOutBack",
onComplete: this.switchWorkspaceDone
});
Tweener.addTween(switchData.inGroup,
{ x: 0,
y: 0,
time: SWITCH_ANIMATION_TIME,
transition: "easeOutBack"
});
},
switchWorkspaceDone : function() {
let switchData = this._switchData;
if (!switchData)
return;
this._switchData = null;
for (let i = 0; i < switchData.windows.length; i++) {
let w = switchData.windows[i];
if (w.window.get_parent() == switchData.outGroup) {
w.window.reparent(w.parent);
w.window.hide();
} else
w.window.reparent(w.parent);
}
Tweener.removeTweens(switchData.inGroup);
Tweener.removeTweens(switchData.outGroup);
switchData.inGroup.destroy();
switchData.outGroup.destroy();
this._shellwm.completed_switch_workspace();
} }
switchData.inGroup.set_position(-xDest, -yDest);
switchData.inGroup.raise_top();
Tweener.addTween(switchData.outGroup,
{ x: xDest,
y: yDest,
time: SWITCH_ANIMATION_TIME,
transition: "easeOutBack",
onComplete: this.switchWorkspaceDone
});
Tweener.addTween(switchData.inGroup,
{ x: 0,
y: 0,
time: SWITCH_ANIMATION_TIME,
transition: "easeOutBack"
});
},
switchWorkspaceDone : function() {
let switchData = this._switchData;
if (!switchData)
return;
this._switchData = null;
for (let i = 0; i < switchData.windows.length; i++) {
let w = switchData.windows[i];
if (w.window.get_parent() == switchData.outGroup) {
w.window.reparent(w.parent);
w.window.hide();
} else
w.window.reparent(w.parent);
}
Tweener.removeTweens(switchData.inGroup);
Tweener.removeTweens(switchData.outGroup);
switchData.inGroup.destroy();
switchData.outGroup.destroy();
this._shellwm.completed_switch_workspace();
}
}; };