diff --git a/js/ui/appdisplay.js b/js/ui/appdisplay.js index 5c38d4f82..176b6585c 100644 --- a/js/ui/appdisplay.js +++ b/js/ui/appdisplay.js @@ -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 Clutter = imports.gi.Clutter; @@ -10,31 +10,31 @@ const Tidy = imports.gi.Tidy; const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; -//TODO - move this into GConf once we're not a plugin anymore -//but have taken over metacity -//This list is taken from GNOME Online popular applications -//http://online.gnome.org/applications -//but with nautilus removed +// TODO - move this into GConf once we're not a plugin anymore +// but have taken over metacity +// This list is taken from GNOME Online popular applications +// http://online.gnome.org/applications +// but with nautilus removed const DEFAULT_APPLICATIONS = [ - 'mozilla-firefox.desktop', - 'gnome-terminal.desktop', - 'evolution.desktop', - 'evince.desktop', - 'gedit.desktop', - 'mozilla-thunderbird.desktop', - 'totem.desktop', - 'gnome-file-roller.desktop', - 'rhythmbox.desktop', - 'epiphany.desktop', - 'xchat.desktop', - 'openoffice.org-1.9-writer.desktop', - 'emacs.desktop', - 'gnome-system-monitor.desktop', - 'openoffice.org-1.9-calc.desktop', - 'eclipse.desktop', - 'openoffice.org-1.9-impress.desktop', - 'vncviewer.desktop' - ]; + 'mozilla-firefox.desktop', + 'gnome-terminal.desktop', + 'evolution.desktop', + 'evince.desktop', + 'gedit.desktop', + 'mozilla-thunderbird.desktop', + 'totem.desktop', + 'gnome-file-roller.desktop', + 'rhythmbox.desktop', + 'epiphany.desktop', + 'xchat.desktop', + 'openoffice.org-1.9-writer.desktop', + 'emacs.desktop', + 'gnome-system-monitor.desktop', + 'openoffice.org-1.9-calc.desktop', + 'eclipse.desktop', + 'openoffice.org-1.9-impress.desktop', + 'vncviewer.desktop' +]; const APPDISPLAY_NAME_COLOR = new Clutter.Color(); APPDISPLAY_NAME_COLOR.from_pixel(0xffffffff); @@ -49,304 +49,305 @@ const APPDISPLAY_HEIGHT = 50; const APPDISPLAY_PADDING = 4; function AppDisplayItem(node, width) { - this._init(node, width); + this._init(node, width); } AppDisplayItem.prototype = { -_init: function(appinfo, width) { - let me = this; - this._appinfo = appinfo; + _init: function(appinfo, width) { + let me = this; + 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, - width: width, - height: APPDISPLAY_HEIGHT}); - this._group.connect('button-press-event', function(group, e) { - me.emit('activate'); - return true; - }); - this._bg = new Clutter.Rectangle({ color: APPDISPLAY_BACKGROUND_COLOR, - x: 0, y: 0, width: width, height: APPDISPLAY_HEIGHT }); - this._group.add_actor(this._bg); + this._group = new Clutter.Group({reactive: true, + width: width, + height: APPDISPLAY_HEIGHT}); + this._group.connect('button-press-event', function(group, e) { + me.emit('activate'); + return true; + }); + this._bg = new Clutter.Rectangle({ color: APPDISPLAY_BACKGROUND_COLOR, + x: 0, y: 0, + width: width, height: APPDISPLAY_HEIGHT }); + this._group.add_actor(this._bg); - this._icon = new Clutter.Texture({ width: 48, height: 48, x: 0, y: 0 }); - let gicon = appinfo.get_icon(); - let path = null; - if (gicon != null) { - let iconinfo = icontheme.lookup_by_gicon(gicon, 48, Gtk.IconLookupFlags.NO_SVG); - if (iconinfo) - path = iconinfo.get_filename(); - } + this._icon = new Clutter.Texture({ width: 48, height: 48, x: 0, y: 0 }); + let gicon = appinfo.get_icon(); + let path = null; + if (gicon != null) { + let iconinfo = icontheme.lookup_by_gicon(gicon, 48, Gtk.IconLookupFlags.NO_SVG); + if (iconinfo) + path = iconinfo.get_filename(); + } - if (path) - this._icon.set_from_file(path); - this._group.add_actor(this._icon); + if (path) + this._icon.set_from_file(path); + 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); function AppDisplay(x, y, width, height) { - this._init(x, y, width, height); + this._init(x, y, width, height); } AppDisplay.prototype = { -_init : function(x, y, width, height) { - let me = this; - let global = Shell.Global.get(); - this._search = ''; - this._x = x; - this._y = y; - this._width = width; - this._height = height; - this._appmonitor = new Shell.AppMonitor(); - this._appsStale = true; - this._appmonitor.connect('changed', function(mon) { - me._appsStale = true; - }); - this._grid = new Tidy.Grid({x: x, y: y, width: width, height: height}); - global.stage.add_actor(this._grid); - this._appset = {}; // Map - this._displayed = {} // Map - this._selectedIndex = -1; - this._max_items = this._height / (APPDISPLAY_HEIGHT + APPDISPLAY_PADDING); -}, + _init : function(x, y, width, height) { + let me = this; + let global = Shell.Global.get(); + this._search = ''; + this._x = x; + this._y = y; + this._width = width; + this._height = height; + this._appmonitor = new Shell.AppMonitor(); + this._appsStale = true; + this._appmonitor.connect('changed', function(mon) { + me._appsStale = true; + }); + this._grid = new Tidy.Grid({x: x, y: y, width: width, height: height}); + global.stage.add_actor(this._grid); + this._appset = {}; // Map + this._displayed = {}; // Map + this._selectedIndex = -1; + this._max_items = this._height / (APPDISPLAY_HEIGHT + APPDISPLAY_PADDING); + }, -_refreshCache: function() { - let me = this; + _refreshCache: function() { + let me = this; - if (!this._appsStale) - return; - for (id in this._displayed) - this._displayed[id].destroy(); - this._appset = {}; - this._displayed = {}; - this._selectedIndex = -1; - let apps = Gio.app_info_get_all(); - for (let i = 0; i < apps.length; i++) { - let appinfo = apps[i]; - let appid = appinfo.get_id(); - 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; + if (!this._appsStale) + return; + for (id in this._displayed) + this._displayed[id].destroy(); + this._appset = {}; + this._displayed = {}; + this._selectedIndex = -1; + let apps = Gio.app_info_get_all(); + for (let i = 0; i < apps.length; i++) { + let appinfo = apps[i]; + let appid = appinfo.get_id(); + this._appset[appid] = appinfo; } - } - return null; -}, + this._appsStale = false; + }, -searchActivate: function() { - if (this._selectedIndex != -1) { - let selected = this._findDisplayedByIndex(this._selectedIndex); - selected.launch(); + _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; + } + } + 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'); - 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) { - if (this._selectedIndex != -1) { - let prev = this._findDisplayedByIndex(this._selectedIndex); - log("demarking " + prev); - prev.markSelected(false); + _selectIndex: function(index) { + if (this._selectedIndex != -1) { + let prev = this._findDisplayedByIndex(this._selectedIndex); + log("demarking " + prev); + 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); - diff --git a/js/ui/button.js b/js/ui/button.js index a56f0e2dc..7076ca024 100644 --- a/js/ui/button.js +++ b/js/ui/button.js @@ -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 Tweener = imports.tweener.tweener; @@ -9,7 +9,7 @@ DEFAULT_BUTTON_COLOR.from_pixel(0xeeddccff); const DEFAULT_PRESSED_BUTTON_COLOR = new Clutter.Color(); 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 NO_OPACITY = 0; @@ -23,95 +23,95 @@ function Button(text, buttonColor, pressedButtonColor, staysPressed, minWidth, m } Button.prototype = { -_init : function(text, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) { - let me = this; - this._buttonColor = buttonColor - if (buttonColor == null) - this._buttonColor = DEFAULT_BUTTON_COLOR; + _init : function(text, buttonColor, pressedButtonColor, staysPressed, minWidth, minHeight) { + let me = this; - this._pressedButtonColor = pressedButtonColor - if (pressedButtonColor == null) - this._pressedButtonColor = DEFAULT_PRESSED_BUTTON_COLOR; + this._buttonColor = buttonColor + if (buttonColor == null) + this._buttonColor = DEFAULT_BUTTON_COLOR; - if (staysPressed == null) - staysPressed = false; - if (minWidth == null) - minWidth = 0; - if (minHeight == null) - minHeight = 0; + this._pressedButtonColor = pressedButtonColor + if (pressedButtonColor == null) + this._pressedButtonColor = DEFAULT_PRESSED_BUTTON_COLOR; - // if staysPressed is true, this.active will be true past the first release of a button, untill a subsequent one (the button - // is unpressed) or untill release() is called explicitly - this._active = false; - this._isBetweenPressAndRelease = false; - this._mouseIsOverButton = false; + if (staysPressed == null) + staysPressed = false; + if (minWidth == null) + minWidth = 0; + if (minHeight == null) + minHeight = 0; - this.button = new Clutter.Group({reactive: true}); - 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) { + // if staysPressed is true, this.active will be true past the first release of a button, untill a subsequent one (the button + // is unpressed) or untill release() is called explicitly this._active = false; - Tweener.removeTweens(this._pressedBackground); - if (this._mouseIsOverButton) { - this._pressedBackground.set_opacity(PARTIAL_OPACITY); - } else { - this._pressedBackground.set_opacity(NO_OPACITY); + this._isBetweenPressAndRelease = false; + this._mouseIsOverButton = false; + + this.button = new Clutter.Group({reactive: true}); + 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); + } } } -} -} - +}; diff --git a/js/ui/main.js b/js/ui/main.js index dde716829..8928a1469 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -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 Shell = imports.gi.Shell; @@ -18,67 +18,67 @@ let overlay = null; let run_dialog = null; let wm = null; -//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 -//Tweener just uses a simple timeout at a fixed frame rate and has no idea -//of "catching up" by dropping frames. - -//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 -//sophisticated than a simple timeout at a fixed frame rate, but at least -//it knows how to drop frames. (See HippoAnimationManager for a more -//sophisticated view of continous time updates; even better is to pay -//attention to the vertical vblank and sync to that when possible.) - +// 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 +// Tweener just uses a simple timeout at a fixed frame rate and has no idea +// of "catching up" by dropping frames. +// +// 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 +// sophisticated than a simple timeout at a fixed frame rate, but at least +// it knows how to drop frames. (See HippoAnimationManager for a more +// sophisticated view of continous time updates; even better is to pay +// attention to the vertical vblank and sync to that when possible.) +// function ClutterFrameTicker() { this._init(); } ClutterFrameTicker.prototype = { -TARGET_FRAME_RATE : 60, + TARGET_FRAME_RATE : 60, -_init : function() { - // We don't have a finite duration; tweener will tell us to stop - // when we need to stop, so use 1000 seconds as "infinity" - this._timeline = new Clutter.Timeline({ fps: this.TARGET_FRAME_RATE, - duration: 1000*1000 }); - this._frame = 0; + _init : function() { + // We don't have a finite duration; tweener will tell us to stop + // when we need to stop, so use 1000 seconds as "infinity" + this._timeline = new Clutter.Timeline({ fps: this.TARGET_FRAME_RATE, + duration: 1000*1000 }); + this._frame = 0; - let me = this; - this._timeline.connect('new-frame', + let me = this; + this._timeline.connect('new-frame', function(timeline, frame) { - me._onNewFrame(frame); - }); -}, + me._onNewFrame(frame); + }); + }, -_onNewFrame : function(frame) { - // Unfortunately the interface to to send a new frame to tweener - // is a simple "next frame" and there is no provision for signaling - // that frames have been skipped or just telling it the new time. - // But what it actually does internally is just: - // - // _currentTime += 1000/_ticker.FRAME_RATE; - // - // So by dynamically adjusting the value of FRAME_RATE we can trick - // it into dealing with dropped frames. + _onNewFrame : function(frame) { + // Unfortunately the interface to to send a new frame to tweener + // is a simple "next frame" and there is no provision for signaling + // that frames have been skipped or just telling it the new time. + // But what it actually does internally is just: + // + // _currentTime += 1000/_ticker.FRAME_RATE; + // + // So by dynamically adjusting the value of FRAME_RATE we can trick + // it into dealing with dropped frames. - let delta = frame - this._frame; - if (delta == 0) - this.FRAME_RATE = this.TARGET_FRAME_RATE; - else - this.FRAME_RATE = this.TARGET_FRAME_RATE / delta; - this._frame = frame; - this.emit('prepare-frame'); -}, + let delta = frame - this._frame; + if (delta == 0) + this.FRAME_RATE = this.TARGET_FRAME_RATE; + else + this.FRAME_RATE = this.TARGET_FRAME_RATE / delta; + this._frame = frame; + this.emit('prepare-frame'); + }, -start : function() { - this._timeline.start(); -}, + start : function() { + this._timeline.start(); + }, -stop : function() { - this._timeline.stop(); - this._frame = 0; -} + stop : function() { + this._timeline.stop(); + this._frame = 0; + } }; Signals.addSignalMethods(ClutterFrameTicker.prototype); @@ -121,9 +121,9 @@ function start() { wm = new WindowManager.WindowManager(); } -//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 -//went modal, false otherwise +// 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 +// went modal, false otherwise function startModal() { let global = Shell.Global.get(); diff --git a/js/ui/overlay.js b/js/ui/overlay.js index ee300cc3c..488cc5783 100644 --- a/js/ui/overlay.js +++ b/js/ui/overlay.js @@ -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 Mainloop = imports.mainloop; @@ -23,27 +23,27 @@ SIDESHOW_SEARCH_BG_COLOR.from_pixel(0xffffffff); const SIDESHOW_TEXT_COLOR = new Clutter.Color(); 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; -//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; -//Windows are slightly translucent in the overlay mode +// Windows are slightly translucent in the overlay mode const WINDOW_OPACITY = 0.9 * 255; -//Define a layout scheme for small window counts. For larger -//counts we fall back to an algorithm. We need more schemes here -//unless we have a really good algorithm. - -//Each triplet is [xCenter, yCenter, scale] where the scale -//is relative to the width of the desktop. +// Define a layout scheme for small window counts. For larger +// counts we fall back to an algorithm. We need more schemes here +// unless we have a really good algorithm. +// +// Each triplet is [xCenter, yCenter, scale] where the scale +// is relative to the width of the desktop. const POSITIONS = { - 1: [[0.5, 0.5, 0.8]], - 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]], - 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]] + 1: [[0.5, 0.5, 0.8]], + 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]], + 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]] }; function Sideshow(width) { @@ -51,95 +51,95 @@ function Sideshow(width) { } Sideshow.prototype = { -_init : function(width) { - let me = this; + _init : function(width) { + let me = this; - let global = Shell.Global.get(); - this._group = new Clutter.Group(); - this._group.hide(); - global.stage.add_actor(this._group); - let icontheme = Gtk.IconTheme.get_default(); - let rect = new Clutter.Rectangle({ color: SIDESHOW_SEARCH_BG_COLOR, - x: SIDESHOW_PAD, - y: Panel.PANEL_HEIGHT + SIDESHOW_PAD, - width: width, - height: 24}); - this._group.add_actor(rect); + let global = Shell.Global.get(); + this._group = new Clutter.Group(); + this._group.hide(); + global.stage.add_actor(this._group); + let icontheme = Gtk.IconTheme.get_default(); + let rect = new Clutter.Rectangle({ color: SIDESHOW_SEARCH_BG_COLOR, + x: SIDESHOW_PAD, + y: Panel.PANEL_HEIGHT + SIDESHOW_PAD, + width: width, + height: 24}); + this._group.add_actor(rect); - let searchIconTexture = new Clutter.Texture({ x: SIDESHOW_PAD + 2, - y: rect.y + 2 }); - let searchIconPath = icontheme.lookup_icon('gtk-find', 16, 0).get_filename(); - searchIconTexture.set_from_file(searchIconPath); - this._group.add_actor(searchIconTexture); + let searchIconTexture = new Clutter.Texture({ x: SIDESHOW_PAD + 2, + y: rect.y + 2 }); + let searchIconPath = icontheme.lookup_icon('gtk-find', 16, 0).get_filename(); + searchIconTexture.set_from_file(searchIconPath); + this._group.add_actor(searchIconTexture); - this._searchEntry = new Clutter.Entry({ - font_name: "Sans 14px", - x: searchIconTexture.x - + searchIconTexture.width + 4, - y: searchIconTexture.y, - width: rect.width - (searchIconTexture.x), - height: searchIconTexture.height}); - this._group.add_actor(this._searchEntry); - global.stage.set_key_focus(this._searchEntry); - this._searchQueued = false; - this._searchActive = false; - this._searchEntry.connect('notify::text', function (se, prop) { - if (me._searchQueued) - return; - Mainloop.timeout_add(250, function() { - let text = me._searchEntry.text; - me._searchQueued = false; - me._searchActive = text != ''; - me._appdisplay.setSearch(text); + this._searchEntry = new Clutter.Entry({ + font_name: "Sans 14px", + x: searchIconTexture.x + + searchIconTexture.width + 4, + y: searchIconTexture.y, + width: rect.width - (searchIconTexture.x), + height: searchIconTexture.height}); + this._group.add_actor(this._searchEntry); + global.stage.set_key_focus(this._searchEntry); + this._searchQueued = false; + this._searchActive = false; + this._searchEntry.connect('notify::text', function (se, prop) { + if (me._searchQueued) + return; + Mainloop.timeout_add(250, function() { + let text = me._searchEntry.text; + me._searchQueued = false; + me._searchActive = 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; }); - }); - 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, - font_name: "Sans Bold 14px", - text: "Applications", - x: SIDESHOW_PAD, - y: this._searchEntry.y + this._searchEntry.height + 10, - height: 16}); - this._group.add_actor(appsText); + let appsText = new Clutter.Label({ color: SIDESHOW_TEXT_COLOR, + font_name: "Sans Bold 14px", + text: "Applications", + x: SIDESHOW_PAD, + y: this._searchEntry.y + this._searchEntry.height + 10, + height: 16}); + this._group.add_actor(appsText); - let menuY = appsText.y + appsText.height + 6; - this._appdisplay = new AppDisplay.AppDisplay(SIDESHOW_PAD, - menuY, width, global.screen_height - menuY); + let menuY = appsText.y + appsText.height + 6; + this._appdisplay = new AppDisplay.AppDisplay(SIDESHOW_PAD, + menuY, width, global.screen_height - menuY); - /* Proxy the activated signal */ - this._appdisplay.connect('activated', function(appdisplay) { - me.emit('activated'); - }); -}, + /* Proxy the activated signal */ + this._appdisplay.connect('activated', function(appdisplay) { + me.emit('activated'); + }); + }, -show: function() { - this._group.show(); - this._appdisplay.show(); -}, + show: function() { + this._group.show(); + this._appdisplay.show(); + }, -hide: function() { - this._group.hide(); - this._appdisplay.hide(); -} + hide: function() { + this._group.hide(); + this._appdisplay.hide(); + } }; Signals.addSignalMethods(Sideshow.prototype); @@ -148,252 +148,254 @@ function Overlay() { } Overlay.prototype = { -_init : function() { - let me = this; + _init : function() { + let me = this; - let global = Shell.Global.get(); + let global = Shell.Global.get(); - this._group = new Clutter.Group(); - this.visible = false; + this._group = new Clutter.Group(); + this.visible = false; - let background = new Clutter.Rectangle({ color: OVERLAY_BACKGROUND_COLOR, - reactive: true, - x: 0, - y: Panel.PANEL_HEIGHT, - width: global.screen_width, - height: global.screen_width - Panel.PANEL_HEIGHT }); - this._group.add_actor(background); + let background = new Clutter.Rectangle({ color: OVERLAY_BACKGROUND_COLOR, + reactive: true, + x: 0, + y: Panel.PANEL_HEIGHT, + width: global.screen_width, + height: global.screen_width - Panel.PANEL_HEIGHT }); + this._group.add_actor(background); - this._group.hide(); - global.overlay_group.add_actor(this._group); + this._group.hide(); + global.overlay_group.add_actor(this._group); this._windowClones = []; - // TODO - recalculate everything when desktop size changes - this._recalculateSize(); + // TODO - recalculate everything when desktop size changes + this._recalculateSize(); - this._sideshow = new Sideshow(this._desktopX - 10); - this._sideshow.connect('activated', function(sideshow) { - // TODO - have some sort of animation/effect while - // transitioning to the new app. We definitely need - // startup-notification integration at least. - me._deactivate(); - }); -}, + this._sideshow = new Sideshow(this._desktopX - 10); + this._sideshow.connect('activated', function(sideshow) { + // TODO - have some sort of animation/effect while + // transitioning to the new app. We definitely need + // startup-notification integration at least. + me._deactivate(); + }); + }, -_recalculateSize: function () { - let global = Shell.Global.get(); - let screenWidth = global.screen_width; - let screenHeight = global.screen_height; - // 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 - this._desktopWidth = screenWidth * DESKTOP_SCALE; - this._desktopHeight = screenHeight * DESKTOP_SCALE; - this._desktopX = screenWidth - this._desktopWidth - 10; - this._desktopY = Panel.PANEL_HEIGHT + (screenHeight - this._desktopHeight - Panel.PANEL_HEIGHT) / 2; -}, + _recalculateSize: function () { + let global = Shell.Global.get(); + let screenWidth = global.screen_width; + let screenHeight = global.screen_height; + // 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 + this._desktopWidth = screenWidth * DESKTOP_SCALE; + this._desktopHeight = screenHeight * DESKTOP_SCALE; + this._desktopX = screenWidth - this._desktopWidth - 10; + this._desktopY = Panel.PANEL_HEIGHT + (screenHeight - this._desktopHeight - Panel.PANEL_HEIGHT) / 2; + }, -show : function() { - if (this.visible) - return; - this.visible = true; + show : function() { + if (this.visible) + return; - let global = Shell.Global.get(); + this.visible = true; - let windows = global.get_windows(); - let desktopWindow = null; + let global = Shell.Global.get(); - this._recalculateSize(); + let windows = global.get_windows(); + let desktopWindow = null; - for (let i = 0; i < windows.length; i++) - if (windows[i].get_window_type() == Meta.WindowType.DESKTOP) - desktopWindow = windows[i]; + this._recalculateSize(); - // If a file manager is displaying desktop icons, there will be a desktop window. - // This window will have the size of the whole desktop. When such window is not present - // (e.g. when the preference for showing icons on the desktop is disabled by the user - // 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(); + for (let i = 0; i < windows.length; i++) + if (windows[i].get_window_type() == Meta.WindowType.DESKTOP) + desktopWindow = windows[i]; - // 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; + // If a file manager is displaying desktop icons, there will be a desktop window. + // This window will have the size of the whole desktop. When such window is not present + // (e.g. when the preference for showing icons on the desktop is disabled by the user + // 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(); - 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(); -} }; diff --git a/js/ui/panel.js b/js/ui/panel.js index 71801b0f5..2c37a7dd8 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -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 Mainloop = imports.mainloop; @@ -21,123 +21,123 @@ function Panel() { } Panel.prototype = { -_init : function() { - let global = Shell.Global.get(); + _init : function() { + let global = Shell.Global.get(); - this._group = new Clutter.Group(); + this._group = new Clutter.Group(); - let background = new Clutter.Rectangle({ color: PANEL_BACKGROUND_COLOR, - reactive: true, - width: global.screen_width+2, - height: PANEL_HEIGHT+1, - border_width: 1}); - background.set_position(-1, -1); - this._group.add_actor(background); + let background = new Clutter.Rectangle({ color: PANEL_BACKGROUND_COLOR, + reactive: true, + width: global.screen_width+2, + height: PANEL_HEIGHT+1, + border_width: 1}); + background.set_position(-1, -1); + 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, - valign: 0.5, - end_align: true, - column_gap: 2 }) - this._group.add_actor(this._grid); + this._grid = new Tidy.Grid({ height: TRAY_HEIGHT, + valign: 0.5, + end_align: true, + column_gap: 2 }) + this._group.add_actor(this._grid); - this._clock = new Clutter.Label({ font_name: "Sans Bold 16px", - text: "" }); - this._grid.add_actor(this._clock); + this._clock = new Clutter.Label({ font_name: "Sans Bold 16px", + text: "" }); + this._grid.add_actor(this._clock); - // Setting the anchor point at top right (north east) makes that portion of the - // grid positioned at the position specified below. - this._grid.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); - this._grid.set_position(global.screen_width - 2, (PANEL_HEIGHT - TRAY_HEIGHT) / 2); + // Setting the anchor point at top right (north east) makes that portion of the + // grid positioned at the position specified below. + 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._traymanager = new Shell.TrayManager({ bg_color: PANEL_BACKGROUND_COLOR }); - let me = this; - // 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 - this._traymanager.connect('tray-icon-added', + this._traymanager = new Shell.TrayManager({ bg_color: PANEL_BACKGROUND_COLOR }); + let me = this; + // 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 + this._traymanager.connect('tray-icon-added', function(o, icon) { - me._grid.add_actor(icon); - /* bump the clock back to the end */ - me._grid.remove_actor(me._clock); - me._grid.add_actor(me._clock); - me._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); - }); - this._traymanager.connect('tray-icon-removed', + me._grid.add_actor(icon); + /* bump the clock back to the end */ + me._grid.remove_actor(me._clock); + me._grid.add_actor(me._clock); + me._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); + }); + this._traymanager.connect('tray-icon-removed', function(o, icon) { - me._grid.remove_actor(icon); - me._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); - }); - this._traymanager.manage_stage(global.stage); + me._grid.remove_actor(icon); + me._grid.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); + }); + 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.) - // 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 - // to switch to. - this.button.button.connect('button-press-event', + // 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 + // have the overlay act like a menu that allows the user to release the mouse on the activity the user wants + // to switch to. + this.button.button.connect('button-press-event', function(o, event) { - if (Main.overlay.visible) - Main.hide_overlay(); - else - Main.show_overlay(); + if (Main.overlay.visible) + Main.hide_overlay(); + else + Main.show_overlay(); - return true; - }); + return true; + }); - this._setStruts(); - global.screen.connect('notify::n-workspaces', + this._setStruts(); + global.screen.connect('notify::n-workspaces', function() { - me._setStruts(); - }); + me._setStruts(); + }); - global.stage.add_actor(this._group); + global.stage.add_actor(this._group); - this._updateClock(); - this._startClock(); -}, + this._updateClock(); + this._startClock(); + }, -// Struts determine the area along each side of the screen that is reserved -// and not available to applications -_setStruts: function() { - let global = Shell.Global.get(); + // Struts determine the area along each side of the screen that is reserved + // and not available to applications + _setStruts: function() { + let global = Shell.Global.get(); - let struts = [ - new Meta.Strut({ - rect: { - x: 0, - y: 0, - width: global.screen_width, - height: PANEL_HEIGHT - }, - side: Meta.Direction.TOP - }) - ]; + let struts = [ + new Meta.Strut({ + rect: { + x: 0, + y: 0, + width: global.screen_width, + height: PANEL_HEIGHT + }, + side: Meta.Direction.TOP + }) + ]; - let screen = global.screen; - for (let i = 0; i < screen.n_workspaces; i++) { - let workspace = screen.get_workspace_by_index(i); - workspace.set_builtin_struts(struts); + let screen = global.screen; + for (let i = 0; i < screen.n_workspaces; i++) { + let workspace = screen.get_workspace_by_index(i); + 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(); -} }; diff --git a/js/ui/run_dialog.js b/js/ui/run_dialog.js index 25e0b91c4..fdfd36a8c 100644 --- a/js/ui/run_dialog.js +++ b/js/ui/run_dialog.js @@ -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 Shell = imports.gi.Shell; @@ -23,110 +23,111 @@ function RunDialog() { }; RunDialog.prototype = { - _init : function() { - let global = Shell.Global.get(); + _init : function() { + let global = Shell.Global.get(); - // All actors are inside _group. We create it initially - // hidden then show it in show() - this._group = new Clutter.Group({ visible: false }); - global.stage.add_actor(this._group); + // All actors are inside _group. We create it initially + // hidden then show it in show() + this._group = new Clutter.Group({ visible: false }); + global.stage.add_actor(this._group); - this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR, - width: global.screen_width, - height: global.screen_height, - border_width: 0, - reactive: true }); - this._group.add_actor(this._overlay); + this._overlay = new Clutter.Rectangle({ color: OVERLAY_COLOR, + width: global.screen_width, + height: global.screen_height, + border_width: 0, + reactive: true }); + this._group.add_actor(this._overlay); - let boxGroup = new Clutter.Group(); - boxGroup.set_position((global.screen_width - BOX_WIDTH) / 2, - (global.screen_height - BOX_HEIGHT) / 2); - this._group.add_actor(boxGroup); + let boxGroup = new Clutter.Group(); + boxGroup.set_position((global.screen_width - BOX_WIDTH) / 2, + (global.screen_height - BOX_HEIGHT) / 2); + this._group.add_actor(boxGroup); - let box = new Clutter.Rectangle({ color: BOX_BACKGROUND_COLOR, - reactive: false, - width: BOX_WIDTH, - height: BOX_HEIGHT, - border_width: 0 }); - boxGroup.add_actor(box); + let box = new Clutter.Rectangle({ color: BOX_BACKGROUND_COLOR, + reactive: false, + width: BOX_WIDTH, + height: BOX_HEIGHT, + border_width: 0 }); + boxGroup.add_actor(box); - let label = new Clutter.Label({ color: BOX_TEXT_COLOR, - font_name: '18px Sans', - text: 'Please enter a command:' }); - label.set_position(6, 6); - boxGroup.add_actor(label); + let label = new Clutter.Label({ color: BOX_TEXT_COLOR, + font_name: '18px Sans', + text: 'Please enter a command:' }); + label.set_position(6, 6); + boxGroup.add_actor(label); - this._entry = new Clutter.Entry({ color: BOX_TEXT_COLOR, - font_name: '20px Sans Bold', - reactive: true, - text: '', - entry_padding: 0, - width: BOX_WIDTH - 12, - height: BOX_HEIGHT - 12 }); - // TODO: Implement relative positioning using Tidy. - this._entry.set_position(6, 30); - boxGroup.add_actor(this._entry); + this._entry = new Clutter.Entry({ color: BOX_TEXT_COLOR, + font_name: '20px Sans Bold', + reactive: true, + text: '', + entry_padding: 0, + width: BOX_WIDTH - 12, + height: BOX_HEIGHT - 12 }); + // TODO: Implement relative positioning using Tidy. + this._entry.set_position(6, 30); + boxGroup.add_actor(this._entry); - let me = this; + let me = this; - 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) { + this._entry.connect('activate', function (o, e) { me.hide(); - me.emit('cancel'); - return true; - } else + me._run(o.get_text()); 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() { - if (!this._group.visible) - return; + this.emit('run'); + }, - this._group.hide(); - Main.endModal(); -}, + show : function() { + let me = this; -destroy : function(){ - this.hide(); - this._group.destroy(); -} + 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.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); diff --git a/js/ui/windowmanager.js b/js/ui/windowmanager.js index 90dd579fc..3cb1bf5fa 100644 --- a/js/ui/windowmanager.js +++ b/js/ui/windowmanager.js @@ -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 Mainloop = imports.mainloop; @@ -15,111 +15,111 @@ function WindowManager() { } WindowManager.prototype = { -_init : function() { - let me = this; + _init : function() { + let me = this; - this._global = Shell.Global.get(); - this._shellwm = this._global.window_manager; + this._global = Shell.Global.get(); + this._shellwm = this._global.window_manager; - this._switchData = null; - this._shellwm.connect('switch-workspace', + this._switchData = null; + this._shellwm.connect('switch-workspace', function(o, from, to, direction) { - let actors = me._shellwm.get_switch_workspace_actors(); - me.switchWorkspace(actors, from, to, direction); - }); - this._shellwm.connect('kill-switch-workspace', + let actors = me._shellwm.get_switch_workspace_actors(); + me.switchWorkspace(actors, from, to, direction); + }); + this._shellwm.connect('kill-switch-workspace', function(o) { - me.switchWorkspaceDone(); - }); -}, + me.switchWorkspaceDone(); + }); + }, -switchWorkspace : function(windows, from, to, direction) { - /* @direction is the direction that the "camera" moves, so the - * screen contents have to move one screen's worth in the - * opposite direction. - */ - let xDest = 0, yDest = 0; + switchWorkspace : function(windows, from, to, direction) { + /* @direction is the direction that the "camera" moves, so the + * screen contents have to move one screen's worth in the + * opposite direction. + */ + let xDest = 0, yDest = 0; - if (direction == Meta.MotionDirection.UP || + if (direction == Meta.MotionDirection.UP || direction == Meta.MotionDirection.UP_LEFT || direction == Meta.MotionDirection.UP_RIGHT) - yDest = this._global.screen_height; - else if (direction == Meta.MotionDirection.DOWN || + yDest = this._global.screen_height; + else if (direction == Meta.MotionDirection.DOWN || direction == Meta.MotionDirection.DOWN_LEFT || 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.DOWN_LEFT) - xDest = this._global.screen_width; - else if (direction == Meta.MotionDirection.RIGHT || - direction == Meta.MotionDirection.UP_RIGHT || - direction == Meta.MotionDirection.DOWN_RIGHT) - xDest = -this._global.screen_width; + xDest = this._global.screen_width; + else if (direction == Meta.MotionDirection.RIGHT || + direction == Meta.MotionDirection.UP_RIGHT || + direction == Meta.MotionDirection.DOWN_RIGHT) + xDest = -this._global.screen_width; - let switchData = {}; - this._switchData = switchData; - switchData.inGroup = new Clutter.Group(); - switchData.outGroup = new Clutter.Group(); - switchData.windows = []; + let switchData = {}; + this._switchData = switchData; + switchData.inGroup = new Clutter.Group(); + switchData.outGroup = new Clutter.Group(); + switchData.windows = []; - let wgroup = this._global.window_group; - wgroup.add_actor(switchData.inGroup); - wgroup.add_actor(switchData.outGroup); + let wgroup = this._global.window_group; + wgroup.add_actor(switchData.inGroup); + wgroup.add_actor(switchData.outGroup); - for (let i = 0; i < windows.length; i++) { - let window = windows[i]; - if (window.get_workspace() == from) { - switchData.windows.push({ window: window, - parent: window.get_parent() }); - window.reparent(switchData.outGroup); - } else if (window.get_workspace() == to) { - switchData.windows.push({ window: window, - parent: window.get_parent() }); - window.reparent(switchData.inGroup); - window.show_all(); + for (let i = 0; i < windows.length; i++) { + let window = windows[i]; + if (window.get_workspace() == from) { + switchData.windows.push({ window: window, + parent: window.get_parent() }); + window.reparent(switchData.outGroup); + } else if (window.get_workspace() == to) { + switchData.windows.push({ window: window, + parent: window.get_parent() }); + window.reparent(switchData.inGroup); + 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(); -} - };