From bfc850a94dcc89d105654b80d570c31b9e6c2424 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Fri, 19 Nov 2010 13:38:06 +0000 Subject: [PATCH 01/61] statusMenu: Make "My Account" menu item work again For GNOME 3.x. gnome-about-me is dead, so we should be using the user-accounts panel of gnome-control-center instead. https://bugzilla.gnome.org/show_bug.cgi?id=635264 --- js/ui/statusMenu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ui/statusMenu.js b/js/ui/statusMenu.js index 67fcd71f9..c0cd86679 100644 --- a/js/ui/statusMenu.js +++ b/js/ui/statusMenu.js @@ -153,7 +153,7 @@ StatusMenuButton.prototype = { _onMyAccountActivate: function() { Main.overview.hide(); - this._spawn(['gnome-about-me']); + this._spawn(['gnome-control-center user-accounts']); }, _onPreferencesActivate: function() { From b956c6f093493b1b6bde1be0dcb28072694bbcf4 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Fri, 19 Nov 2010 10:18:14 -0500 Subject: [PATCH 02/61] dnd: when snapping back, deal with moved/rescaled parents Previously, when snapping back a drag actor, we moved it back to its original stage-relative position and scale. This worked fine if its parent was still in the same place it was when the drag started, but failed in cases like the linear workspace layout window drag-and-drop, where dragging a window would "zoom out" its parent workspace, causing the snapback to send it to the wrong place. Fix this by instead snapping the actor back to "where the actor would have been right now if it were still at its original scale and position within its original parent actor" rather than "where it was before the drag started" https://bugzilla.gnome.org/show_bug.cgi?id=635272 --- js/ui/dnd.js | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/js/ui/dnd.js b/js/ui/dnd.js index 04e540194..c4ba7fde1 100644 --- a/js/ui/dnd.js +++ b/js/ui/dnd.js @@ -440,21 +440,39 @@ _Draggable.prototype = { return true; }, - // Get position of the drag actor's source if the source is still around, - // or return the original location if the actor itself was being dragged - // or the source is no longer around. _getRestoreLocation: function() { - let locX = this._snapBackX; - let locY = this._snapBackY; + let x, y, scale; - if (this._dragActorSource && this._dragActorSource.visible) - [locX, locY] = this._dragActorSource.get_transformed_position(); - return [locX, locY]; + if (this._dragActorSource && this._dragActorSource.visible) { + // Snap the clone back to its source + [x, y] = this._dragActorSource.get_transformed_position(); + let [sourceScaledWidth, sourceScaledHeight] = this._dragActorSource.get_transformed_size(); + scale = this._dragActor.width / sourceScaledWidth; + } else if (this._dragOrigParent) { + // Snap the actor back to its original position within + // its parent, adjusting for the fact that the parent + // may have been moved or scaled + let [parentX, parentY] = this._dragOrigParent.get_transformed_position(); + x = parentX + this._dragOrigParent.scale_x * this._dragOrigX; + y = parentY + this._dragOrigParent.scale_y * this._dragOrigY; + + let [parentWidth, parentHeight] = this._dragOrigParent.get_size(); + let [parentScaledWidth, parentScaledHeight] = this._dragOrigParent.get_transformed_size(); + let parentScale = parentScaledWidth / parentWidth; + scale = this._dragOrigScale * parentScale; + } else { + // Snap back actor to its original stage position + x = this._snapBackX; + y = this._snapBackY; + scale = this._snapBackScale; + } + + return [x, y, scale]; }, _cancelDrag: function(eventTime) { this._dragInProgress = false; - let [snapBackX, snapBackY] = this._getRestoreLocation(); + let [snapBackX, snapBackY, snapBackScale] = this._getRestoreLocation(); if (this._actorDestroyed) { global.unset_cursor(); @@ -467,8 +485,8 @@ _Draggable.prototype = { Tweener.addTween(this._dragActor, { x: snapBackX, y: snapBackY, - scale_x: this._snapBackScale, - scale_y: this._snapBackScale, + scale_x: snapBackScale, + scale_y: snapBackScale, opacity: this._dragOrigOpacity, time: SNAP_BACK_ANIMATION_TIME, transition: 'easeOutQuad', @@ -480,11 +498,11 @@ _Draggable.prototype = { _restoreDragActor: function(eventTime) { this._dragInProgress = false; - [restoreX, restoreY] = this._getRestoreLocation(); + [restoreX, restoreY, restoreScale] = this._getRestoreLocation(); // fade the actor back in at its original location this._dragActor.set_position(restoreX, restoreY); - this._dragActor.set_scale(this._snapBackScale, this._snapBackScale); + this._dragActor.set_scale(restoreScale, restoreScale); this._dragActor.opacity = 0; this._animationInProgress = true; From b7c1400eb374d3f0cd3adff7f98996af8ccf2a0d Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Fri, 19 Nov 2010 11:09:48 -0500 Subject: [PATCH 03/61] dnd: fix a case where ungrabEvents wasn't being called If the drag actor is destroyed as part of a drag target accepting it, we were not calling ungrabEvents, meaning the mouse/keyboard remained grabbed until you clicked somewhere to cancel it. This fixes that without trying to improve the extremely confusing control flow... https://bugzilla.gnome.org/show_bug.cgi?id=635278 --- js/ui/dnd.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/js/ui/dnd.js b/js/ui/dnd.js index c4ba7fde1..66f468083 100644 --- a/js/ui/dnd.js +++ b/js/ui/dnd.js @@ -100,6 +100,8 @@ _Draggable.prototype = { this._buttonDown = false; // The mouse button has been pressed and has not yet been released. this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet. this._animationInProgress = false; // The drag is over and the item is in the process of animating to its original position (snapping back or reverting). + + this._eventsGrabbed = false; }, _onButtonPress : function (actor, event) { @@ -147,13 +149,19 @@ _Draggable.prototype = { }, _grabEvents: function() { - Clutter.grab_pointer(_getEventHandlerActor()); - Clutter.grab_keyboard(_getEventHandlerActor()); + if (!this._eventsGrabbed) { + Clutter.grab_pointer(_getEventHandlerActor()); + Clutter.grab_keyboard(_getEventHandlerActor()); + this._eventsGrabbed = true; + } }, _ungrabEvents: function() { - Clutter.ungrab_pointer(); - Clutter.ungrab_keyboard(); + if (this._eventsGrabbed) { + Clutter.ungrab_pointer(); + Clutter.ungrab_keyboard(); + this._eventsGrabbed = false; + } }, _onEvent: function(actor, event) { @@ -476,6 +484,8 @@ _Draggable.prototype = { if (this._actorDestroyed) { global.unset_cursor(); + if (!this._buttonDown) + this._ungrabEvents(); this.emit('drag-end', eventTime, false); return; } From f67ad2303369a4f89daa89a2d7c69288c735e878 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Fri, 19 Nov 2010 20:33:43 +0000 Subject: [PATCH 04/61] statusMenu: Fix launching gnome-control-center with an option This isn't how you launch an app with an option... --- js/ui/statusMenu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/ui/statusMenu.js b/js/ui/statusMenu.js index c0cd86679..0e91d5d66 100644 --- a/js/ui/statusMenu.js +++ b/js/ui/statusMenu.js @@ -153,7 +153,7 @@ StatusMenuButton.prototype = { _onMyAccountActivate: function() { Main.overview.hide(); - this._spawn(['gnome-control-center user-accounts']); + this._spawn(['gnome-control-center', 'user-accounts']); }, _onPreferencesActivate: function() { From 5086bfedacb1dce184ca566e9f154b411b8bfb40 Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Fri, 19 Nov 2010 18:42:37 +0100 Subject: [PATCH 05/61] Fix dependencies for gnome-power-manager Merging the g-p-m branch with the one adding gnome-settings-daemon for A11y, a lot of modules were duplicated. Also, gnome-keyring is not needed, the distro provided one is enough. https://bugzilla.gnome.org/show_bug.cgi?id=635199 --- tools/build/gnome-shell-build-setup.sh | 3 ++- tools/build/gnome-shell.modules | 30 -------------------------- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/tools/build/gnome-shell-build-setup.sh b/tools/build/gnome-shell-build-setup.sh index 2cc10138f..8e3e53c9d 100755 --- a/tools/build/gnome-shell-build-setup.sh +++ b/tools/build/gnome-shell-build-setup.sh @@ -62,7 +62,7 @@ fi # libxklavier, libxml2, ORBit2, pam, python, readline, # spidermonkey ({mozilla,firefox,xulrunner}-js), startup-notification, # xdamage, icon-naming-utils, upower, libtool-ltdl, libvorbis, -# libgcrypt, libtasn1 +# libgcrypt, libtasn1, libgnome-keyring # # Non-devel packages needed by gnome-shell and its deps: # glxinfo, gstreamer-plugins-base, gstreamer-plugins-good, @@ -120,6 +120,7 @@ if test "x$system" = xFedora ; then startup-notification-devel xorg-x11-server-Xephyr gnome-terminal zenity icon-naming-utils upower-devel libtool-ltdl-devel libvorbis-devel libxklavier-devel libgcrypt-devel libtasn1-devel libtasn1-tools + libgnome-keyring-devel " if expr $version \>= 14 > /dev/null ; then diff --git a/tools/build/gnome-shell.modules b/tools/build/gnome-shell.modules index 044c99787..4438f0060 100644 --- a/tools/build/gnome-shell.modules +++ b/tools/build/gnome-shell.modules @@ -191,35 +191,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -238,7 +209,6 @@ - From de50cf80a80b09d8385abfcdb3b0dd93b84d1b8a Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Fri, 19 Nov 2010 18:57:35 +0100 Subject: [PATCH 06/61] PowerStatus: Update for gnome-power-manager API changes g-p-m no longer exports a summary, and instead exports a GIcon to show. Update for that. https://bugzilla.gnome.org/show_bug.cgi?id=635288 --- js/ui/status/power.js | 60 ++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/js/ui/status/power.js b/js/ui/status/power.js index 6f8ba321d..52de29ed9 100644 --- a/js/ui/status/power.js +++ b/js/ui/status/power.js @@ -26,7 +26,10 @@ const UPDeviceType = { MOUSE: 5, KEYBOARD: 6, PDA: 7, - PHONE: 8 + PHONE: 8, + MEDIA_PLAYER: 9, + TABLET: 10, + COMPUTER: 11 }; const UPDeviceState = { @@ -181,24 +184,12 @@ DeviceItem.prototype = { _init: function(device) { PopupMenu.PopupBaseMenuItem.prototype._init.call(this); - let [device_id, device_type, summary, percentage, state, time] = device; + let [device_id, device_type, icon, percentage, state, time] = device; this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' }); - this._label = new St.Label({ text: summary }); + this._label = new St.Label({ text: this._deviceTypeToString(device_type) }); - let icon; - switch (state) { - case UPDeviceState.FULLY_CHARGED: - icon = 'battery-full-charged'; - break; - case UPDeviceState.UNKNOWN: - icon = 'battery-missing'; - break; - default: - icon = this._percentageToIcon(percentage) + (state == UPDeviceState.CHARGING ? '-charging' : ''); - } - - this._icon = new St.Icon({ icon_name: icon, + this._icon = new St.Icon({ gicon: Shell.util_icon_from_string(icon), icon_type: St.IconType.SYMBOLIC, style_class: 'popup-menu-icon' }); @@ -212,15 +203,32 @@ DeviceItem.prototype = { this.addActor(percentBin); }, - _percentageToIcon: function(p) { - if (p > 60) - return 'battery-full'; - if (p > 30) - return 'battery-good'; - if (p > 10) - return 'battery-low'; - if (p > 0) - return 'battery-caution'; - return 'battery-empty'; + _deviceTypeToString: function(type) { + switch (type) { + case UPDeviceType.AC_POWER: + return _("AC adapter"); + case UPDeviceType.BATTERY: + return _("Laptop battery"); + case UPDeviceType.UPS: + return _("UPS"); + case UPDeviceType.MONITOR: + return _("Monitor"); + case UPDeviceType.MOUSE: + return _("Mouse"); + case UPDeviceType.KEYBOARD: + return _("Keyboard"); + case UPDeviceType.PDA: + return _("PDA"); + case UPDeviceType.PHONE: + return _("Cell phone"); + case UPDeviceType.MEDIA_PLAYER: + return _("Media player"); + case UPDeviceType.TABLET: + return _("Tablet"); + case UPDeviceType.COMPUTER: + return _("Computer"); + default: + return _("Unknown"); + } } } From d5bfc503fe4e0d4e4462206caec7f9ebec402346 Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Tue, 16 Nov 2010 15:22:38 +0100 Subject: [PATCH 07/61] Sound Menu: only show the slider Reimplement UI without any indication of percentage or mutedness, and whitout switches. The only interaction point is slider, but it still supports mute changing for applications that track it, and will react appropriately to external changes. https://bugzilla.gnome.org/show_bug.cgi?id=634329 --- js/ui/popupMenu.js | 8 +++--- js/ui/status/volume.js | 62 +++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js index 8f84805ca..23c18c2ae 100644 --- a/js/ui/popupMenu.js +++ b/js/ui/popupMenu.js @@ -296,15 +296,15 @@ PopupBaseMenuItem.prototype = { }; Signals.addSignalMethods(PopupBaseMenuItem.prototype); -function PopupMenuItem(text) { - this._init(text); +function PopupMenuItem() { + this._init.apply(this, arguments); } PopupMenuItem.prototype = { __proto__: PopupBaseMenuItem.prototype, - _init: function (text) { - PopupBaseMenuItem.prototype._init.call(this); + _init: function (text, params) { + PopupBaseMenuItem.prototype._init.call(this, params); this.label = new St.Label({ text: text }); this.addActor(this.label); diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js index 09684e606..63f76fe0d 100644 --- a/js/ui/status/volume.js +++ b/js/ui/status/volume.js @@ -38,12 +38,11 @@ Indicator.prototype = { this._output = null; this._outputVolumeId = 0; this._outputMutedId = 0; - this._outputSwitch = new PopupMenu.PopupSwitchMenuItem(_("Volume: Muted"), false); - this._outputSwitch.connect('toggled', Lang.bind(this, this._switchToggled, '_output')); + this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false }); this._outputSlider = new PopupMenu.PopupSliderMenuItem(0); this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output')); this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange)); - this.menu.addMenuItem(this._outputSwitch); + this.menu.addMenuItem(this._outputTitle); this.menu.addMenuItem(this._outputSlider); this._separator = new PopupMenu.PopupSeparatorMenuItem(); @@ -52,12 +51,11 @@ Indicator.prototype = { this._input = null; this._inputVolumeId = 0; this._inputMutedId = 0; - this._inputSwitch = new PopupMenu.PopupSwitchMenuItem(_("Microphone: Muted"), false); - this._inputSwitch.connect('toggled', Lang.bind(this, this._switchToggled, '_input')); + this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false }); this._inputSlider = new PopupMenu.PopupSliderMenuItem(0); this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input')); this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange)); - this.menu.addMenuItem(this._inputSwitch); + this.menu.addMenuItem(this._inputTitle); this.menu.addMenuItem(this._inputSlider); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); @@ -75,11 +73,18 @@ Indicator.prototype = { let currentVolume = this._output.volume; if (direction == Clutter.ScrollDirection.DOWN) { + let prev_muted = this._output.is_muted; this._output.volume = Math.max(0, currentVolume - VOLUME_MAX * VOLUME_ADJUSTMENT_STEP); + if (this._output.volume < 1) { + this._output.volume = 0; + if (!prev_muted) + this._output.change_is_muted(true); + } this._output.push_volume(); } else if (direction == Clutter.ScrollDirection.UP) { this._output.volume = Math.min(VOLUME_MAX, currentVolume + VOLUME_MAX * VOLUME_ADJUSTMENT_STEP); + this._output.change_is_muted(false); this._output.push_volume(); } }, @@ -103,8 +108,7 @@ Indicator.prototype = { this._mutedChanged (null, null, '_output'); this._volumeChanged (null, null, '_output'); } else { - this._outputSwitch.label.text = _("Volume: Muted"); - this._outputSwitch.setToggleState(false); + this._outputSlider.setValue(0); this.setIcon('audio-volume-muted-symbolic'); } }, @@ -124,7 +128,7 @@ Indicator.prototype = { this._volumeChanged (null, null, '_input'); } else { this._separator.actor.hide(); - this._inputSwitch.actor.hide(); + this._inputTitle.actor.hide(); this._inputSlider.actor.hide(); } }, @@ -147,11 +151,11 @@ Indicator.prototype = { } if (showInput) { this._separator.actor.show(); - this._inputSwitch.actor.show(); + this._inputTitle.actor.show(); this._inputSlider.actor.show(); } else { this._separator.actor.hide(); - this._inputSwitch.actor.hide(); + this._inputTitle.actor.hide(); this._inputSlider.actor.hide(); } }, @@ -174,7 +178,17 @@ Indicator.prototype = { log ('Volume slider changed for %s, but %s does not exist'.format(property, property)); return; } - this[property].volume = value * VOLUME_MAX; + let volume = value * VOLUME_MAX; + let prev_muted = this[property].is_muted; + if (volume < 1) { + this[property].volume = 0; + if (!prev_muted) + this[property].change_is_muted(true); + } else { + this[property].volume = volume; + if (prev_muted) + this[property].change_is_muted(false); + } this[property].push_volume(); }, @@ -182,20 +196,10 @@ Indicator.prototype = { global.play_theme_sound('audio-volume-change'); }, - _switchToggled: function(switchItem, state, property) { - if (this[property] == null) { - log ('Volume mute switch toggled for %s, but %s does not exist'.format(property, property)); - return; - } - this[property].change_is_muted(!state); - this._notifyVolumeChange(); - }, - _mutedChanged: function(object, param_spec, property) { let muted = this[property].is_muted; - let toggleSwitch = this[property+'Switch']; - toggleSwitch.setToggleState(!muted); - this._updateLabel(property); + let slider = this[property+'Slider']; + slider.setValue(muted ? 0 : (this[property].volume / VOLUME_MAX)); if (property == '_output') { if (muted) this.setIcon('audio-volume-muted'); @@ -206,17 +210,7 @@ Indicator.prototype = { _volumeChanged: function(object, param_spec, property) { this[property+'Slider'].setValue(this[property].volume / VOLUME_MAX); - this._updateLabel(property); if (property == '_output' && !this._output.is_muted) this.setIcon(this._volumeToIcon(this._output.volume)); - }, - - _updateLabel: function(property) { - let label; - if (this[property].is_muted) - label = (property == '_output' ? _("Volume: Muted") : _("Microphone: Muted")); - else - label = (property == '_output' ? _("Volume: %3.0f%%") : _("Microphone: %3.0f%%")).format(this[property].volume / VOLUME_MAX * 100); - this[property+'Switch'].label.text = label; } }; From d1407d0026c9607b936c4137e075121e17a83fed Mon Sep 17 00:00:00 2001 From: "Gheyret T.Kenji" Date: Sat, 20 Nov 2010 11:47:22 +0100 Subject: [PATCH 08/61] Added UG translation --- po/ug.po | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/po/ug.po b/po/ug.po index dc8bb1eac..24b4d23b5 100644 --- a/po/ug.po +++ b/po/ug.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n" -"POT-Creation-Date: 2010-11-11 00:43+0000\n" +"POT-Creation-Date: 2010-11-13 23:05+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Gheyret Kenji\n" "Language-Team: Uyghur Computer Science Association \n" @@ -384,7 +384,7 @@ msgstr "ماس كېلىدىغان نەتىجە يوق." #. **** Places **** #. Translators: This is in the sense of locations for documents, #. network locations, etc. -#: ../js/ui/dash.js:797 ../js/ui/placeDisplay.js:554 +#: ../js/ui/dash.js:797 ../js/ui/placeDisplay.js:558 msgid "PLACES & DEVICES" msgstr "ئورۇن ۋە ئۈسكۈنىلەر" @@ -522,39 +522,31 @@ msgstr "ئىشلىتىلىشچان" msgid "Busy" msgstr "ئالدىراش" -#: ../js/ui/statusMenu.js:111 -msgid "Invisible" -msgstr "يوشۇرۇن" - -#: ../js/ui/statusMenu.js:119 +#: ../js/ui/statusMenu.js:114 msgid "My Account..." msgstr "" -#: ../js/ui/statusMenu.js:123 +#: ../js/ui/statusMenu.js:118 msgid "System Settings..." msgstr "سىستېما تەڭشەكلىرى" -#: ../js/ui/statusMenu.js:130 +#: ../js/ui/statusMenu.js:125 msgid "Lock Screen" msgstr "ئېكراننى قۇلۇپلا" -#: ../js/ui/statusMenu.js:134 +#: ../js/ui/statusMenu.js:129 msgid "Switch User" msgstr "ئىشلەتكۈچى ئالماشتۇر" -#: ../js/ui/statusMenu.js:139 +#: ../js/ui/statusMenu.js:134 msgid "Log Out..." msgstr "تىزىمدىن چىق…" -#: ../js/ui/statusMenu.js:146 +#: ../js/ui/statusMenu.js:141 msgid "Suspend" msgstr "ۋاقىتلىق توختىتىش" -#: ../js/ui/statusMenu.js:150 -msgid "Restart..." -msgstr "" - -#: ../js/ui/statusMenu.js:154 +#: ../js/ui/statusMenu.js:145 msgid "Shut Down..." msgstr "تاقا…" @@ -594,11 +586,11 @@ msgstr "ھەممىباب زىيارەت تەڭشىكى" msgid "High Contrast" msgstr "يۇقىرى ئاق-قارىلىقى" -#: ../js/ui/status/accessibility.js:202 +#: ../js/ui/status/accessibility.js:205 msgid "Large Text" msgstr "چوڭ تېكىست" -#: ../js/ui/status/accessibility.js:223 +#: ../js/ui/status/accessibility.js:224 msgid "Zoom" msgstr "كېڭەيت تارايت" @@ -692,3 +684,6 @@ msgstr "ئىزدە" #, c-format msgid "%1$s: %2$s" msgstr "%1$s: %2$s" + +#~ msgid "Invisible" +#~ msgstr "يوشۇرۇن" From c0b9ce16a7dba14c746b7d033b2823d5e4555d6f Mon Sep 17 00:00:00 2001 From: Kjartan Maraas Date: Sat, 20 Nov 2010 14:40:33 +0100 Subject: [PATCH 09/61] =?UTF-8?q?Updated=20Norwegian=20bokm=C3=A5l=20trans?= =?UTF-8?q?lation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- po/nb.po | 90 +++++++++++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/po/nb.po b/po/nb.po index b75f5b4a2..440cb6c92 100644 --- a/po/nb.po +++ b/po/nb.po @@ -5,10 +5,10 @@ # msgid "" msgstr "" -"Project-Id-Version: gnome-shell 2.31.x\n" +"Project-Id-Version: gnome-shell 2.91.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-10-28 17:35+0200\n" -"PO-Revision-Date: 2010-10-28 17:38+0200\n" +"POT-Creation-Date: 2010-11-20 14:39+0100\n" +"PO-Revision-Date: 2010-11-20 14:40+0100\n" "Last-Translator: Kjartan Maraas \n" "Language-Team: Norwegian bokmål \n" "Language: \n" @@ -344,19 +344,19 @@ msgstr "PROGRAMMER" msgid "PREFERENCES" msgstr "BRUKERVALG" -#: ../js/ui/appDisplay.js:648 +#: ../js/ui/appDisplay.js:647 msgid "New Window" msgstr "Nytt vindu" -#: ../js/ui/appDisplay.js:652 +#: ../js/ui/appDisplay.js:651 msgid "Remove from Favorites" msgstr "Fjern fra favoritter" -#: ../js/ui/appDisplay.js:653 +#: ../js/ui/appDisplay.js:652 msgid "Add to Favorites" msgstr "Legg til i favoritter" -#: ../js/ui/appDisplay.js:830 +#: ../js/ui/appDisplay.js:829 msgid "Drag here to add favorites" msgstr "Dra hit for å legge til favoritter" @@ -385,7 +385,7 @@ msgstr "Ingen treff." #. **** Places **** #. Translators: This is in the sense of locations for documents, #. network locations, etc. -#: ../js/ui/dash.js:797 ../js/ui/placeDisplay.js:554 +#: ../js/ui/dash.js:797 ../js/ui/placeDisplay.js:558 msgid "PLACES & DEVICES" msgstr "STEDER & ENHETER" @@ -429,58 +429,58 @@ msgid "Undo" msgstr "Angre" #. TODO - _quit() doesn't really work on apps in state STARTING yet -#: ../js/ui/panel.js:469 +#: ../js/ui/panel.js:470 #, c-format msgid "Quit %s" msgstr "Avslutt %s" -#: ../js/ui/panel.js:494 +#: ../js/ui/panel.js:495 msgid "Preferences" msgstr "Brukervalg" #. Translators: This is the time format with date used #. in 24-hour mode. -#: ../js/ui/panel.js:580 +#: ../js/ui/panel.js:581 msgid "%a %b %e, %R:%S" msgstr "%a %e %b, %R.%S" -#: ../js/ui/panel.js:581 +#: ../js/ui/panel.js:582 msgid "%a %b %e, %R" msgstr "%a %e %b, %R" #. Translators: This is the time format without date used #. in 24-hour mode. -#: ../js/ui/panel.js:585 +#: ../js/ui/panel.js:586 msgid "%a %R:%S" msgstr "%a %R.%S" -#: ../js/ui/panel.js:586 +#: ../js/ui/panel.js:587 msgid "%a %R" msgstr "%a %R" #. Translators: This is a time format with date used #. for AM/PM. -#: ../js/ui/panel.js:593 +#: ../js/ui/panel.js:594 msgid "%a %b %e, %l:%M:%S %p" msgstr "%a %e %b, %l.%M.%S %p" -#: ../js/ui/panel.js:594 +#: ../js/ui/panel.js:595 msgid "%a %b %e, %l:%M %p" msgstr "%a %e %b, %l.%M %p" #. Translators: This is a time format without date used #. for AM/PM. -#: ../js/ui/panel.js:598 +#: ../js/ui/panel.js:599 msgid "%a %l:%M:%S %p" msgstr "%a %l.%M.%S %p" -#: ../js/ui/panel.js:599 +#: ../js/ui/panel.js:600 msgid "%a %l:%M %p" msgstr "%a %l.%M %p" #. Button on the left side of the panel. #. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". -#: ../js/ui/panel.js:744 +#: ../js/ui/panel.js:745 msgid "Activities" msgstr "Aktiviteter" @@ -523,39 +523,31 @@ msgstr "Tilgjengelig" msgid "Busy" msgstr "Opptatt" -#: ../js/ui/statusMenu.js:111 -msgid "Invisible" -msgstr "Usynlig" - -#: ../js/ui/statusMenu.js:119 +#: ../js/ui/statusMenu.js:114 msgid "My Account..." msgstr "Min konto..." -#: ../js/ui/statusMenu.js:123 -msgid "System Preferences..." -msgstr "Brukervalg for systemet..." +#: ../js/ui/statusMenu.js:118 +msgid "System Settings..." +msgstr "Systeminnstillinger..." -#: ../js/ui/statusMenu.js:130 +#: ../js/ui/statusMenu.js:125 msgid "Lock Screen" msgstr "Lås skjerm" -#: ../js/ui/statusMenu.js:134 +#: ../js/ui/statusMenu.js:129 msgid "Switch User" msgstr "Bytt bruker" -#: ../js/ui/statusMenu.js:139 +#: ../js/ui/statusMenu.js:134 msgid "Log Out..." msgstr "Logg ut..." -#: ../js/ui/statusMenu.js:146 +#: ../js/ui/statusMenu.js:141 msgid "Suspend" msgstr "Hvilemodus" -#: ../js/ui/statusMenu.js:150 -msgid "Restart..." -msgstr "Start på nytt..." - -#: ../js/ui/statusMenu.js:154 +#: ../js/ui/statusMenu.js:145 msgid "Shut Down..." msgstr "Avslutt..." @@ -595,11 +587,11 @@ msgstr "Innstillinger for tilgjengelighet" msgid "High Contrast" msgstr "Høy kontrast" -#: ../js/ui/status/accessibility.js:202 +#: ../js/ui/status/accessibility.js:205 msgid "Large Text" msgstr "Stor tekst" -#: ../js/ui/status/accessibility.js:223 +#: ../js/ui/status/accessibility.js:224 msgid "Zoom" msgstr "Zoom" @@ -646,49 +638,53 @@ msgstr[1] "%u innganger" msgid "System Sounds" msgstr "Systemlyder" -#: ../src/shell-global.c:1204 +#: ../src/shell-app-system.c:1012 +msgid "Unknown" +msgstr "Ukjent" + +#: ../src/shell-global.c:1158 msgid "Less than a minute ago" msgstr "Mindre enn ett minutt siden" -#: ../src/shell-global.c:1208 +#: ../src/shell-global.c:1162 #, c-format msgid "%d minute ago" msgid_plural "%d minutes ago" msgstr[0] "%d minutt siden" msgstr[1] "%d minutter siden" -#: ../src/shell-global.c:1213 +#: ../src/shell-global.c:1167 #, c-format msgid "%d hour ago" msgid_plural "%d hours ago" msgstr[0] "%d time siden" msgstr[1] "%d timer siden" -#: ../src/shell-global.c:1218 +#: ../src/shell-global.c:1172 #, c-format msgid "%d day ago" msgid_plural "%d days ago" msgstr[0] "%d dag siden" msgstr[1] "%d dager siden" -#: ../src/shell-global.c:1223 +#: ../src/shell-global.c:1177 #, c-format msgid "%d week ago" msgid_plural "%d weeks ago" msgstr[0] "%d uke siden" msgstr[1] "%d uker siden" -#: ../src/shell-uri-util.c:89 +#: ../src/shell-util.c:89 msgid "Home Folder" msgstr "Hjemmemappe" #. Translators: this is the same string as the one found in #. * nautilus -#: ../src/shell-uri-util.c:104 +#: ../src/shell-util.c:104 msgid "File System" msgstr "Filsystem" -#: ../src/shell-uri-util.c:250 +#: ../src/shell-util.c:250 msgid "Search" msgstr "Søk" @@ -697,7 +693,7 @@ msgstr "Søk" #. * example, "Trash: some-directory". It means that the #. * directory called "some-directory" is in the trash. #. -#: ../src/shell-uri-util.c:300 +#: ../src/shell-util.c:300 #, c-format msgid "%1$s: %2$s" msgstr "%1$s: %2$s" From 71685a3b483f3db8daaaa0375126786a5c65854c Mon Sep 17 00:00:00 2001 From: Kjartan Maraas Date: Sat, 20 Nov 2010 14:40:40 +0100 Subject: [PATCH 10/61] Fix this up --- po/POTFILES.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index a23f9bf51..c9fd4435d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -19,5 +19,7 @@ js/ui/windowAttentionHandler.js js/ui/workspacesView.js src/gvc/gvc-mixer-control.c src/gdmuser/gdm-user.c +src/shell-app-system.c src/shell-global.c -src/shell-uri-util.c +src/shell-util.c + From 1fce2375383943559acd767285b96aa2e2fb078a Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Sun, 14 Nov 2010 13:33:48 -0500 Subject: [PATCH 11/61] StThemeNode: suppress compiler warnings and fix minor bugs Aggressive compiler flags can cause the compiler to be smart enough to inline functions and detect variables not being set on certain code paths but not smart enough to understand the overall logic; add some extra initializations to suppress the warnings. Fix several minor bugs in the logic found when double checking the logic before adding the initializations. Based on a patch by Marc-Antoine Perennou . https://bugzilla.gnome.org/show_bug.cgi?id=634225 --- src/st/st-theme-node.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c index a9d1c2f6d..ce4cdbe9e 100644 --- a/src/st/st-theme-node.c +++ b/src/st/st-theme-node.c @@ -718,7 +718,7 @@ get_length_from_term (StThemeNode *node, if (term->type != TERM_NUMBER) { g_warning ("Ignoring length property that isn't a number"); - return FALSE; + return VALUE_NOT_FOUND; } num = term->content.num; @@ -859,7 +859,7 @@ get_length_from_term_int (StThemeNode *node, GetFromTermResult result; result = get_length_from_term (node, term, use_parent_font, &value); - if (result != VALUE_NOT_FOUND) + if (result == VALUE_FOUND) *length = (int) (0.5 + value); return result; } @@ -1053,7 +1053,7 @@ do_border_property (StThemeNode *node, StSide side = (StSide)-1; ClutterColor color; gboolean color_set = FALSE; - int width; + int width = 0; /* suppress warning */ gboolean width_set = FALSE; int j; @@ -1098,7 +1098,8 @@ do_border_property (StThemeNode *node, const char *ident = term->content.str->stryng->str; if (strcmp (ident, "none") == 0 || strcmp (ident, "hidden") == 0) { - width = 0.; + width = 0; + width_set = TRUE; continue; } else if (strcmp (ident, "solid") == 0) @@ -1185,7 +1186,7 @@ do_outline_property (StThemeNode *node, const char *property_name = decl->property->stryng->str + 7; /* Skip 'outline' */ ClutterColor color; gboolean color_set = FALSE; - int width; + int width = 0; /* suppress warning */ gboolean width_set = FALSE; if (strcmp (property_name, "") == 0) @@ -1202,7 +1203,8 @@ do_outline_property (StThemeNode *node, const char *ident = term->content.str->stryng->str; if (strcmp (ident, "none") == 0 || strcmp (ident, "hidden") == 0) { - width = 0.; + width = 0; + width_set = TRUE; continue; } else if (strcmp (ident, "solid") == 0) @@ -2234,17 +2236,19 @@ font_variant_from_term (CRTerm *term, const PangoFontDescription * st_theme_node_get_font (StThemeNode *node) { - PangoStyle font_style; + /* Initialized despite _set flags to suppress compiler warnings */ + PangoStyle font_style = PANGO_STYLE_NORMAL; gboolean font_style_set = FALSE; - PangoVariant variant; + PangoVariant variant = PANGO_VARIANT_NORMAL; gboolean variant_set = FALSE; - PangoWeight weight; - gboolean weight_absolute; + PangoWeight weight = PANGO_WEIGHT_NORMAL; + gboolean weight_absolute = TRUE; gboolean weight_set = FALSE; - double parent_size; - double size = 0.; /* Suppress warning */ + double size = 0.; gboolean size_set = FALSE; + char *family = NULL; + double parent_size; int i; if (node->font_desc) From 4800f63c3a622c2f7c67bb2d166641d4c604cc5e Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Sat, 20 Nov 2010 13:01:43 -0500 Subject: [PATCH 12/61] Correct and simplify setting the GJS module path We were going to great effort to include the normal directories in the GJS search path and the code to to do this broke recently when jsdir and jsnativedir were moved to gjs-internals-1.0.pc. However, it was actually unnecessary since the standard directories are appended to the default path. (We continue to use a GNOME_SHELL_JS envvar separate from GJS_PATH for the Shell to enable the somewhat unlikely case where someone wants to invoke the shell specifying a GJS_PATH.) https://bugzilla.gnome.org/show_bug.cgi?id=635367 --- configure.ac | 4 ---- src/Makefile.am | 2 -- src/gnome-shell.in | 2 +- tests/Makefile.am | 2 -- tests/run-test.sh.in | 10 +++------- 5 files changed, 4 insertions(+), 16 deletions(-) diff --git a/configure.ac b/configure.ac index a02faf584..519d02825 100644 --- a/configure.ac +++ b/configure.ac @@ -103,11 +103,7 @@ AC_SUBST(MUTTER_BIN_DIR) AC_SUBST(MUTTER_LIB_DIR) AC_SUBST(MUTTER_PLUGIN_DIR) -GJS_JS_DIR=`$PKG_CONFIG --variable=jsdir gjs-1.0` -GJS_JS_NATIVE_DIR=`$PKG_CONFIG --variable=jsnativedir gjs-1.0` GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0` -AC_SUBST(GJS_JS_DIR) -AC_SUBST(GJS_JS_NATIVE_DIR) AC_SUBST(GJS_CONSOLE) AC_CHECK_FUNCS(fdwalk) diff --git a/src/Makefile.am b/src/Makefile.am index 05b1dedd1..ee42b33b9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -13,8 +13,6 @@ bin_SCRIPTS = gnome-shell gnome-shell-clock-preferences gnome-shell: gnome-shell.in $(AM_V_GEN) sed -e "s|@MUTTER_BIN_DIR[@]|$(MUTTER_BIN_DIR)|" \ -e "s|@datadir[@]|$(datadir)|" \ - -e "s|@GJS_JS_DIR[@]|$(GJS_JS_DIR)|" \ - -e "s|@GJS_JS_NATIVE_DIR[@]|$(GJS_JS_NATIVE_DIR)|" \ -e "s|@libexecdir[@]|$(libexecdir)|" \ -e "s|@libdir[@]|$(libdir)|" \ -e "s|@pkgdatadir[@]|$(pkgdatadir)|" \ diff --git a/src/gnome-shell.in b/src/gnome-shell.in index c855c0180..2abd7d5cc 100755 --- a/src/gnome-shell.in +++ b/src/gnome-shell.in @@ -220,7 +220,7 @@ def start_shell(perf_output=None): # Set up environment env = dict(os.environ) - env.update({'GNOME_SHELL_JS' : '@GJS_JS_DIR@:@GJS_JS_NATIVE_DIR@:' + js_dir, + env.update({'GNOME_SHELL_JS' : js_dir, 'PATH' : '@MUTTER_BIN_DIR@:' + os.environ.get('PATH', ''), 'XDG_CONFIG_DIRS' : '@sysconfdir@/xdg:' + (os.environ.get('XDG_CONFIG_DIRS') or '/etc/xdg'), 'XDG_DATA_DIRS' : '@datadir@:' + (os.environ.get('XDG_DATA_DIRS') or '/usr/local/share:/usr/share'), diff --git a/tests/Makefile.am b/tests/Makefile.am index 42a92a3a1..4c6b92188 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -24,8 +24,6 @@ EXTRA_DIST += $(TEST_MISC) run-test.sh: run-test.sh.in $(AM_V_GEN) sed \ - -e "s|@GJS_JS_DIR[@]|$(GJS_JS_DIR)|" \ - -e "s|@GJS_JS_NATIVE_DIR[@]|$(GJS_JS_NATIVE_DIR)|" \ -e "s|@MUTTER_LIB_DIR[@]|$(MUTTER_LIB_DIR)|" \ -e "s|@srcdir[@]|$(srcdir)|" \ $< > $@ && chmod a+x $@ diff --git a/tests/run-test.sh.in b/tests/run-test.sh.in index af905cbc8..300ca2baf 100644 --- a/tests/run-test.sh.in +++ b/tests/run-test.sh.in @@ -31,17 +31,13 @@ srcdir=$builddir/@srcdir@ srcdir=`cd $srcdir && pwd` GI_TYPELIB_PATH="@MUTTER_LIB_DIR@/mutter:$builddir/../src" +GJS_PATH="$srcdir:$srcdir/../js" GJS_DEBUG_OUTPUT=stderr $verbose || GJS_DEBUG_TOPICS="JS ERROR;JS LOG" GNOME_SHELL_TESTSDIR="$srcdir/" -export GI_TYPELIB_PATH GJS_DEBUG_OUTPUT GJS_DEBUG_TOPICS GNOME_SHELL_JS GNOME_SHELL_TESTSDIR LD_PRELOAD - -run_js_test_args= -for i in $srcdir $srcdir/../js @GJS_JS_DIR@ @GJS_JS_NATIVE_DIR@ ; do - run_js_test_args="$run_js_test_args -I $i" -done +export GI_TYPELIB_PATH GJS_PATH GJS_DEBUG_OUTPUT GJS_DEBUG_TOPICS GNOME_SHELL_JS GNOME_SHELL_TESTSDIR LD_PRELOAD for test in $tests ; do - $debug $builddir/../src/run-js-test $run_js_test_args $test || exit $? + $debug $builddir/../src/run-js-test $test || exit $? done From 59ba112959ebba8147138a7ad94c542e47f3e420 Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Sat, 20 Nov 2010 18:19:32 +0100 Subject: [PATCH 13/61] Complete porting to new gnome-power-manager API We updated the normal devices part, but we forgot the part about the primary device (possibly because it is not reported as such in case it is fully charged). Update that as well, to avoid showing weird GIcon serializations. https://bugzilla.gnome.org/show_bug.cgi?id=635288 --- js/ui/status/power.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/js/ui/status/power.js b/js/ui/status/power.js index 52de29ed9..794a090ec 100644 --- a/js/ui/status/power.js +++ b/js/ui/status/power.js @@ -71,12 +71,19 @@ Indicator.prototype = { this._deviceItems = [ ]; this._hasPrimary = false; this._primaryDeviceId = null; + this._batteryItem = new PopupMenu.PopupMenuItem(''); + this._primaryPercentage = new St.Label(); + let percentBin = new St.Bin(); + percentBin.set_child(this._primaryPercentage, { x_align: St.Align.END }); + this._batteryItem.addActor(percentBin); this.menu.addMenuItem(this._batteryItem); + this._deviceSep = new PopupMenu.PopupSeparatorMenuItem(); this.menu.addMenuItem(this._deviceSep); this._otherDevicePosition = 2; this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + this.menu.addAction(_("What's using power..."),function() { GLib.spawn_command_line_async('gnome-power-statistics --device wakeups'); }); @@ -98,10 +105,12 @@ Indicator.prototype = { this._deviceSep.actor.hide(); return; } - let [device_id, device_type, summary, percentage, state, time] = device; + let [device_id, device_type, icon, percentage, state, time] = device; if (device_type == UPDeviceType.BATTERY) { this._hasPrimary = true; - this._batteryItem.label.text = summary; + let minutes = Math.floor(time / 60); + this._batteryItem.label.text = Gettext.ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes); + this._primaryPercentage.text = '%d%%'.format(Math.round(percentage)); this._batteryItem.actor.show(); if (this._deviceItems.length > 0) this._deviceSep.actor.show(); From a5e61e27c73a30629c06a2a6b741de81245c67f7 Mon Sep 17 00:00:00 2001 From: Adel Gadllah Date: Mon, 22 Nov 2010 21:19:37 +0100 Subject: [PATCH 14/61] [statusMenu] Fix ellipsis in menu entries According to the HIG we should use ellipsis when: "Label the menu item with a trailing ellipsis ("...") only if the command requires further input from the user before it can be performed. Do not add an ellipsis to items that only present a confirmation dialog (such as Delete), or that do not require further input (such as Properties, Preferences or About)" So adjust the use of ellipsis to match that. Pointed out by Michael Monreal. --- js/ui/statusMenu.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/ui/statusMenu.js b/js/ui/statusMenu.js index 0e91d5d66..14d45d02f 100644 --- a/js/ui/statusMenu.js +++ b/js/ui/statusMenu.js @@ -111,11 +111,11 @@ StatusMenuButton.prototype = { item = new PopupMenu.PopupSeparatorMenuItem(); this.menu.addMenuItem(item); - item = new PopupMenu.PopupMenuItem(_("My Account...")); + item = new PopupMenu.PopupMenuItem(_("My Account")); item.connect('activate', Lang.bind(this, this._onMyAccountActivate)); this.menu.addMenuItem(item); - item = new PopupMenu.PopupMenuItem(_("System Settings...")); + item = new PopupMenu.PopupMenuItem(_("System Settings")); item.connect('activate', Lang.bind(this, this._onPreferencesActivate)); this.menu.addMenuItem(item); @@ -138,7 +138,7 @@ StatusMenuButton.prototype = { item = new PopupMenu.PopupSeparatorMenuItem(); this.menu.addMenuItem(item); - item = new PopupMenu.PopupMenuItem(_("Suspend")); + item = new PopupMenu.PopupMenuItem(_("Suspend...")); item.connect('activate', Lang.bind(this, this._onShutDownActivate)); this.menu.addMenuItem(item); From 3d468c26b0f20eaa4265962a9b6660c98a25fed7 Mon Sep 17 00:00:00 2001 From: Sira Nokyoongtong Date: Tue, 23 Nov 2010 10:26:22 +0700 Subject: [PATCH 15/61] Updated Thai translation. --- po/th.po | 549 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 460 insertions(+), 89 deletions(-) diff --git a/po/th.po b/po/th.po index 966c05f96..56dec85b6 100644 --- a/po/th.po +++ b/po/th.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: gnome-shell master\n" "Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" "shell&component=general\n" -"POT-Creation-Date: 2010-06-13 23:04+0000\n" -"PO-Revision-Date: 2010-06-14 20:22+0700\n" +"POT-Creation-Date: 2010-11-20 13:40+0000\n" +"PO-Revision-Date: 2010-11-23 10:04+0700\n" "Last-Translator: Sira Nokyoongtong \n" "Language-Team: Thai \n" "MIME-Version: 1.0\n" @@ -19,11 +19,11 @@ msgstr "" #: ../data/gnome-shell.desktop.in.in.h:1 msgid "GNOME Shell" -msgstr "" +msgstr "เชลล์ GNOME" #: ../data/gnome-shell.desktop.in.in.h:2 msgid "Window management and application launching" -msgstr "" +msgstr "การจัดการหน้าต่างและการเรียกใช้โปรแกรม" #: ../data/gnome-shell-clock-preferences.desktop.in.in.h:1 msgid "Clock" @@ -33,6 +33,294 @@ msgstr "นาฬิกา" msgid "Customize the panel clock" msgstr "ปรับแต่งนาฬิกาบนพาเนล" +#: ../data/org.gnome.shell.gschema.xml.in.h:1 +msgid "" +"Allows access to internal debugging and monitoring tools using the Alt-F2 " +"dialog." +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:2 +msgid "Custom format of the clock" +msgstr "รูปแบบนาฬิกากำหนดเอง" + +#: ../data/org.gnome.shell.gschema.xml.in.h:3 +msgid "Enable internal tools useful for developers and testers from Alt-F2" +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:4 +msgid "File extension used for storing the screencast" +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:5 +msgid "Framerate used for recording screencasts." +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:6 +msgid "" +"GNOME Shell extensions have a uuid property; this key lists extensions which " +"should not be loaded." +msgstr "" +"ส่วนขยายต่างๆ ของเชลล์ GNOME จะมี uuid ประจำตัว ค่านี้จะเก็บรายชื่อของส่วนขยายที่จะไม่เรียกใช้" + +#: ../data/org.gnome.shell.gschema.xml.in.h:7 +msgid "History for command (Alt-F2) dialog" +msgstr "ประวัติคำสั่งของกล่องโต้ตอบเรียกโปรแกรม (Alt-F2)" + +#: ../data/org.gnome.shell.gschema.xml.in.h:8 +msgid "Hour format" +msgstr "รูปแบบชั่วโมง" + +#: ../data/org.gnome.shell.gschema.xml.in.h:9 +msgid "" +"If true and format is either \"12-hour\" or \"24-hour\", display date in the " +"clock, in addition to time." +msgstr "" +"ถ้าเลือก และรูปแบบเวลาเป็น \"12-hour\" หรือ \"24-hour\" " +"ก็จะแสดงวันที่ในนาฬิกาควบคู่กับเวลาด้วย" + +#: ../data/org.gnome.shell.gschema.xml.in.h:10 +msgid "" +"If true and format is either \"12-hour\" or \"24-hour\", display seconds in " +"time." +msgstr "" +"ถ้าเลือก และรูปแบบเวลาเป็น \"12-hour\" หรือ \"24-hour\" ก็จะแสดงวินาทีในเวลาด้วย" + +# See http://en.wikipedia.org/wiki/ISO_week_date +#: ../data/org.gnome.shell.gschema.xml.in.h:11 +msgid "If true, display the ISO week date in the calendar." +msgstr "ถ้าเลือก จะแสดงวันที่แบบสัปดาห์ของ ISO ในปฏิทิน" + +#: ../data/org.gnome.shell.gschema.xml.in.h:12 +msgid "List of desktop file IDs for favorite applications" +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:13 +msgid "Overview workspace view mode" +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:14 +msgid "" +"Sets the GStreamer pipeline used to encode recordings. It follows the syntax " +"used for gst-launch. The pipeline should have an unconnected sink pad where " +"the recorded video is recorded. It will normally have a unconnected source " +"pad; output from that pad will be written into the output file. However the " +"pipeline can also take care of its own output - this might be used to send " +"the output to an icecast server via shout2send or similar. When unset or set " +"to an empty value, the default pipeline will be used. This is currently " +"'videorate ! theoraenc ! oggmux' and records to Ogg Theora." +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:15 +msgid "Show date in clock" +msgstr "แสดงวันที่ในนาฬิกา" + +# See http://en.wikipedia.org/wiki/ISO_week_date +#: ../data/org.gnome.shell.gschema.xml.in.h:16 +msgid "Show the week date in the calendar" +msgstr "แสดงวันที่แบบสัปดาห์ในปฏิทิน" + +#: ../data/org.gnome.shell.gschema.xml.in.h:17 +msgid "Show time with seconds" +msgstr "แสดงเวลาพร้อมวินาที" + +#: ../data/org.gnome.shell.gschema.xml.in.h:18 +msgid "" +"The applications corresponding to these identifiers will be displayed in the " +"favorites area." +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:19 +msgid "" +"The filename for recorded screencasts will be a unique filename based on the " +"current date, and use this extension. It should be changed when recording to " +"a different container format." +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:20 +msgid "" +"The framerate of the resulting screencast recordered by GNOME Shell's " +"screencast recorder in frames-per-second." +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:21 +msgid "The gstreamer pipeline used to encode the screencast" +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:22 +msgid "" +"The selected workspace view mode in the overview. Supported values are " +"\"single\" and \"grid\"." +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:23 +msgid "" +"The shell normally monitors active applications in order to present the most " +"used ones (e.g. in launchers). While this data will be kept private, you may " +"want to disable this for privacy reasons. Please note that doing so won't " +"remove already saved data." +msgstr "" + +#: ../data/org.gnome.shell.gschema.xml.in.h:24 +msgid "" +"This key specifies the format used by the panel clock when the format key is " +"set to \"custom\". You can use conversion specifiers understood by strftime" +"() to obtain a specific format. See the strftime() manual for more " +"information." +msgstr "" +"ค่านี้กำหนดรูปแบบของเวลาในแอพเพล็ตนาฬิกาเมื่อกำหนดรูปแบบเป็น \"custom\" " +"คุณสามารถใช้รหัสการแปลงของ strftime() เพื่อระบุรูปแบบที่ต้องการได้ " +"ดูข้อมูลเพิ่มเติมได้จากคู่มือของ strftime()" + +#: ../data/org.gnome.shell.gschema.xml.in.h:25 +msgid "" +"This key specifies the hour format used by the panel clock. Possible values " +"are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to \"unix\", " +"the clock will display time in seconds since Epoch, i.e. 1970-01-01. If set " +"to \"custom\", the clock will display time according to the format specified " +"in the custom_format key. Note that if set to either \"unix\" or \"custom\", " +"the show_date and show_seconds keys are ignored." +msgstr "" +"ค่านี้กำหนดรูปแบบชั่วโมงที่จะใช้ในแอพเพล็ตนาฬิกา ค่าที่เป็นไปได้คือ\"12-hour\", \"24-hour\", " +"\"unix\" และ \"custom\" ถ้ากำหนดเป็น \"unix\" นาฬิกาจะแสดงเวลาเป็นวินาทีนับจาก 1970-" +"01-01 ถ้ากำหนดเป็น \"custom\" นาฬิกาจะแสดงตามรูปแบบที่กำหนดใน custom_format " +"สังเกตว่าถ้ากำหนดเป็น \"unix\" หรือ \"custom\" ค่า show_date และ show_seconds " +"ก็จะไม่มีผล" + +#: ../data/org.gnome.shell.gschema.xml.in.h:26 +msgid "Uuids of extensions to disable" +msgstr "uuid ของส่วนขยายที่จะปิดใช้" + +#: ../data/org.gnome.shell.gschema.xml.in.h:27 +msgid "Whether to collect stats about applications usage" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:1 +msgid "Clip the crosshairs at the center" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:2 +msgid "Color of the crosshairs" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:3 +msgid "" +"Determines the length of the vertical and horizontal lines that make up the " +"crosshairs." +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:4 +msgid "" +"Determines the position of the magnified mouse image within the magnified " +"view and how it reacts to system mouse movement. The values are - none: no " +"mouse tracking; - centered: the mouse image is displayed at the center of " +"the zoom region (which also represents the point under the system mouse) and " +"the magnified contents are scrolled as the system mouse moves; - " +"proportional: the position of the magnified mouse in the zoom region is " +"proportionally the same as the position of the system mouse on screen; - " +"push: when the magnified mouse intersects a boundary of the zoom region, the " +"contents are scrolled into view." +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:5 +msgid "" +"Determines the transparency of the crosshairs, from fully opaque to fully " +"transparent." +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:6 +msgid "" +"Determines whether the crosshairs intersect the magnified mouse sprite, or " +"are clipped such that the ends of the horizontal and vertical lines surround " +"the mouse image." +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:7 +msgid "Enable lens mode" +msgstr "เปิดใช้โหมดแว่นขยาย" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:8 +msgid "" +"Enables/disables display of crosshairs centered on the magnified mouse " +"sprite." +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:9 +msgid "" +"For centered mouse tracking, when the system pointer is at or near the edge " +"of the screen, the magnified contents continue to scroll such that the " +"screen edge moves into the magnified view." +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:10 +msgid "Length of the crosshairs" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:11 +msgid "Magnification factor" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:12 +msgid "Mouse Tracking Mode" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:13 +msgid "Opacity of the crosshairs" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:14 +msgid "Screen position" +msgstr "ตำแหน่งของหน้าจอ" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:15 +msgid "Scroll magnified contents beyond the edges of the desktop" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:16 +msgid "Show or hide crosshairs" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:17 +msgid "Show or hide the magnifier" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:18 +msgid "Show or hide the magnifier and all of its zoom regions." +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:19 +msgid "" +"The color of the the vertical and horizontal lines that make up the " +"crosshairs." +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:20 +msgid "" +"The magnified view either fills the entire screen, or occupies the top-half, " +"bottom-half, left-half, or right-half of the screen." +msgstr "" +"ช่องแสดงภาพขยายอาจจะใหญ่เต็มจอภาพ (full-screen) หรืออาจจะกินที่แค่ครึ่งบน (top-half) " +"ครึ่งล่าง (bottom-half) ครึ่งซ้าย (left-half) หรือครึ่งขวา (right-half)" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:21 +msgid "" +"The power of the magnification. A value of 1.0 means no magnification. A " +"value of 2.0 doubles the size." +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:22 +msgid "Thickness of the crosshairs" +msgstr "" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:23 +msgid "" +"Whether the magnified view should be centered over the location of the " +"system mouse and move with it." +msgstr "กำหนดว่าจะให้ภาพขยายหน้าจอวางตำแหน่งกึ่งกลางที่เมาส์และเคลื่อนย้ายไปตามเมาส์หรือไม่" + +#: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:24 +msgid "Width of the vertical and horizontal lines that make up the crosshairs." +msgstr "" + #: ../data/clock-preferences.ui.h:1 msgid "Clock Format" msgstr "รูปแบบนาฬิกา" @@ -62,31 +350,31 @@ msgid "_24 hour format" msgstr "แบบ _24 ขั่วโมง" #. **** Applications **** -#: ../js/ui/appDisplay.js:388 ../js/ui/dash.js:872 +#: ../js/ui/appDisplay.js:316 ../js/ui/dash.js:778 msgid "APPLICATIONS" msgstr "โปรแกรม" -#: ../js/ui/appDisplay.js:420 +#: ../js/ui/appDisplay.js:348 msgid "PREFERENCES" msgstr "ปรับแต่ง" -#: ../js/ui/appDisplay.js:726 +#: ../js/ui/appDisplay.js:647 msgid "New Window" msgstr "หน้าต่างใหม่" -#: ../js/ui/appDisplay.js:730 +#: ../js/ui/appDisplay.js:651 msgid "Remove from Favorites" msgstr "ลบออกจากรายการโปรด" -#: ../js/ui/appDisplay.js:731 +#: ../js/ui/appDisplay.js:652 msgid "Add to Favorites" msgstr "เพิ่มเข้าในรายการโปรด" -#: ../js/ui/appDisplay.js:1038 +#: ../js/ui/appDisplay.js:829 msgid "Drag here to add favorites" msgstr "ลากมาที่นี่เพื่อเพิ่มเป็นรายการโปรด" -#: ../js/ui/appFavorites.js:89 +#: ../js/ui/appFavorites.js:88 #, c-format msgid "%s has been added to your favorites." msgstr "%s ถูกเพิ่มเข้าในรายการโปรดของคุณแล้ว" @@ -96,233 +384,313 @@ msgstr "%s ถูกเพิ่มเข้าในรายการโปร msgid "%s has been removed from your favorites." msgstr "%s ถูกลบออกจากรายการโปรดของคุณแล้ว" -#: ../js/ui/dash.js:204 +#: ../js/ui/dash.js:142 msgid "Find" msgstr "หา" -#: ../js/ui/dash.js:527 +#: ../js/ui/dash.js:473 msgid "Searching..." msgstr "กำลังค้นหา..." -#: ../js/ui/dash.js:541 +#: ../js/ui/dash.js:487 msgid "No matching results." msgstr "ไม่มีผลลัพธ์ที่ตรงกัน" #. **** Places **** #. Translators: This is in the sense of locations for documents, #. network locations, etc. -#: ../js/ui/dash.js:891 ../js/ui/placeDisplay.js:551 +#: ../js/ui/dash.js:797 ../js/ui/placeDisplay.js:558 msgid "PLACES & DEVICES" -msgstr "" +msgstr "ที่หลักๆ และอุปกรณ์" #. **** Documents **** -#: ../js/ui/dash.js:898 ../js/ui/docDisplay.js:497 +#: ../js/ui/dash.js:804 ../js/ui/docDisplay.js:494 msgid "RECENT ITEMS" -msgstr "" +msgstr "รายการล่าสุด" -#: ../js/ui/lookingGlass.js:475 +#: ../js/ui/lookingGlass.js:552 msgid "No extensions installed" msgstr "ไม่มีส่วนขยายติดตั้งอยู่" -#: ../js/ui/lookingGlass.js:512 +#: ../js/ui/lookingGlass.js:589 msgid "Enabled" -msgstr "" +msgstr "เปิดใช้งาน" -#: ../js/ui/lookingGlass.js:514 +#. translators: +#. * The device has been disabled +#: ../js/ui/lookingGlass.js:591 ../src/gvc/gvc-mixer-control.c:1087 msgid "Disabled" -msgstr "" +msgstr "ปิดใช้" -#: ../js/ui/lookingGlass.js:516 +#: ../js/ui/lookingGlass.js:593 msgid "Error" msgstr "ผิดพลาด" -#: ../js/ui/lookingGlass.js:518 +#: ../js/ui/lookingGlass.js:595 msgid "Out of date" -msgstr "" +msgstr "ตกรุ่น" -#: ../js/ui/lookingGlass.js:543 +#: ../js/ui/lookingGlass.js:620 msgid "View Source" -msgstr "" +msgstr "ดูซอร์ส" -#: ../js/ui/lookingGlass.js:549 +#: ../js/ui/lookingGlass.js:626 msgid "Web Page" msgstr "หน้าเว็บ" -#: ../js/ui/overview.js:165 +#: ../js/ui/overview.js:160 msgid "Undo" msgstr "เรียกคืน" -#: ../js/ui/panel.js:334 +#. TODO - _quit() doesn't really work on apps in state STARTING yet +#: ../js/ui/panel.js:470 #, c-format msgid "Quit %s" msgstr "ออกจาก %s" -#: ../js/ui/panel.js:354 +#: ../js/ui/panel.js:495 msgid "Preferences" msgstr "ปรับแต่ง" #. Translators: This is the time format with date used #. in 24-hour mode. -#: ../js/ui/panel.js:441 +#: ../js/ui/panel.js:581 msgid "%a %b %e, %R:%S" -msgstr "" +msgstr "%a %d %b, %R:%S" -#: ../js/ui/panel.js:442 +#: ../js/ui/panel.js:582 msgid "%a %b %e, %R" -msgstr "" +msgstr "%a %d %b, %R" #. Translators: This is the time format without date used #. in 24-hour mode. -#: ../js/ui/panel.js:446 +#: ../js/ui/panel.js:586 msgid "%a %R:%S" -msgstr "" +msgstr "%a %R:%S" -#: ../js/ui/panel.js:447 +#: ../js/ui/panel.js:587 msgid "%a %R" -msgstr "" +msgstr "%a %R" #. Translators: This is a time format with date used #. for AM/PM. -#: ../js/ui/panel.js:454 +#: ../js/ui/panel.js:594 msgid "%a %b %e, %l:%M:%S %p" -msgstr "" +msgstr "%a %d %b, %l:%M:%S %p" -#: ../js/ui/panel.js:455 +#: ../js/ui/panel.js:595 msgid "%a %b %e, %l:%M %p" -msgstr "" +msgstr "%a %d %b, %l:%M %p" #. Translators: This is a time format without date used #. for AM/PM. -#: ../js/ui/panel.js:459 +#: ../js/ui/panel.js:599 msgid "%a %l:%M:%S %p" -msgstr "" +msgstr "%a %l:%M:%S %p" -#: ../js/ui/panel.js:460 +#: ../js/ui/panel.js:600 msgid "%a %l:%M %p" -msgstr "" +msgstr "%a %l:%M %p" #. Button on the left side of the panel. #. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". -#: ../js/ui/panel.js:562 +#: ../js/ui/panel.js:745 msgid "Activities" msgstr "กิจกรรม" -#: ../js/ui/placeDisplay.js:108 +#: ../js/ui/placeDisplay.js:111 #, c-format msgid "Failed to unmount '%s'" msgstr "เลิกเมานท์ '%s' ไม่สำเร็จ" -#: ../js/ui/placeDisplay.js:111 +#: ../js/ui/placeDisplay.js:114 msgid "Retry" msgstr "ลองใหม่" -#: ../js/ui/placeDisplay.js:156 +#: ../js/ui/placeDisplay.js:159 msgid "Connect to..." msgstr "เชื่อมต่อไปยัง..." -#: ../js/ui/runDialog.js:235 +#. Translators: this MUST be either "toggle-switch-us" +#. (for toggle switches containing the English words +#. "ON" and "OFF") or "toggle-switch-intl" (for toggle +#. switches containing "◯" and "|"). Other values will +#. simply result in invisible toggle switches. +#: ../js/ui/popupMenu.js:33 +msgid "toggle-switch-us" +msgstr "" + +#: ../js/ui/runDialog.js:233 msgid "Please enter a command:" msgstr "โปรดป้อนคำสั่ง:" -#: ../js/ui/runDialog.js:380 +#: ../js/ui/runDialog.js:378 #, c-format msgid "Execution of '%s' failed:" -msgstr "" +msgstr "คำสั่ง '%s' ทำงานล้มเหลว:" -#: ../js/ui/statusMenu.js:91 +#: ../js/ui/statusMenu.js:101 msgid "Available" -msgstr "" - -#: ../js/ui/statusMenu.js:95 -msgid "Busy" -msgstr "" - -#: ../js/ui/statusMenu.js:99 -msgid "Invisible" -msgstr "" +msgstr "อยู่" #: ../js/ui/statusMenu.js:106 -msgid "Account Information..." -msgstr "ข้อมูลบัญชี..." +msgid "Busy" +msgstr "ไม่ว่าง" -#: ../js/ui/statusMenu.js:110 -msgid "System Preferences..." -msgstr "ปรับแต่งระบบ..." +#: ../js/ui/statusMenu.js:114 +msgid "My Account..." +msgstr "บัญชีของฉัน..." -#: ../js/ui/statusMenu.js:117 +#: ../js/ui/statusMenu.js:118 +msgid "System Settings..." +msgstr "ตั้งค่าระบบ..." + +#: ../js/ui/statusMenu.js:125 msgid "Lock Screen" msgstr "ล็อคหน้าจอ" -#: ../js/ui/statusMenu.js:121 +#: ../js/ui/statusMenu.js:129 msgid "Switch User" msgstr "สลับผู้ใช้" -#: ../js/ui/statusMenu.js:126 +#: ../js/ui/statusMenu.js:134 msgid "Log Out..." msgstr "ออกจากระบบ..." -#: ../js/ui/statusMenu.js:130 +#: ../js/ui/statusMenu.js:141 +msgid "Suspend" +msgstr "พักเครื่อง" + +#: ../js/ui/statusMenu.js:145 msgid "Shut Down..." msgstr "ปิดเครื่อง..." -#: ../js/ui/windowAttentionHandler.js:47 +#: ../js/ui/status/accessibility.js:88 +msgid "Screen Reader" +msgstr "โปรแกรมอ่านหน้าจอ" + +#: ../js/ui/status/accessibility.js:91 +msgid "Screen Keyboard" +msgstr "แป้นพิมพ์บนหน้าจอ" + +#: ../js/ui/status/accessibility.js:94 +msgid "Visual Alerts" +msgstr "แจ้งเหตุด้วยภาพ" + +#: ../js/ui/status/accessibility.js:97 +msgid "Sticky Keys" +msgstr "ค้างปุ่มกด" + +#: ../js/ui/status/accessibility.js:100 +msgid "Slow Keys" +msgstr "พิมพ์แบบช้า" + +#: ../js/ui/status/accessibility.js:103 +msgid "Bounce Keys" +msgstr "ป้องกันการกดแป้นรัว" + +#: ../js/ui/status/accessibility.js:106 +msgid "Mouse Keys" +msgstr "บังคับเมาส์ด้วยแป้น" + +#: ../js/ui/status/accessibility.js:110 +msgid "Universal Access Settings" +msgstr "ตั้งค่าสิ่งอำนวยความสะดวก" + +#: ../js/ui/status/accessibility.js:163 +msgid "High Contrast" +msgstr "สีตัดกัน" + +#: ../js/ui/status/accessibility.js:205 +msgid "Large Text" +msgstr "อักษรขนาดใหญ่" + +#: ../js/ui/status/accessibility.js:224 +msgid "Zoom" +msgstr "ซูม" + +#: ../js/ui/windowAttentionHandler.js:43 #, c-format msgid "%s has finished starting" -msgstr "" +msgstr "%s เปิดเสร็จแล้ว" -#: ../js/ui/windowAttentionHandler.js:49 +#: ../js/ui/windowAttentionHandler.js:45 #, c-format msgid "'%s' is ready" msgstr "'%s' พร้อมแล้ว" -#: ../js/ui/workspacesView.js:237 +#: ../js/ui/workspacesView.js:229 msgid "" "Can't add a new workspace because maximum workspaces limit has been reached." msgstr "" -#: ../js/ui/workspacesView.js:254 +#: ../js/ui/workspacesView.js:246 msgid "Can't remove the first workspace." msgstr "ไม่สามารถลบพื้นที่ทำงานแรกได้" -#: ../src/shell-global.c:1025 +#. translators: +#. * The number of sound outputs on a particular device +#: ../src/gvc/gvc-mixer-control.c:1094 +#, c-format +msgid "%u Output" +msgid_plural "%u Outputs" +msgstr[0] "" + +#. translators: +#. * The number of sound inputs on a particular device +#: ../src/gvc/gvc-mixer-control.c:1104 +#, c-format +msgid "%u Input" +msgid_plural "%u Inputs" +msgstr[0] "" + +#: ../src/gvc/gvc-mixer-control.c:1402 +msgid "System Sounds" +msgstr "เสียงของระบบ" + +#: ../src/shell-app-system.c:1012 +msgid "Unknown" +msgstr "" + +#: ../src/shell-global.c:1158 msgid "Less than a minute ago" msgstr "ไม่ถึงหนึ่งนาทีก่อน" -#: ../src/shell-global.c:1029 +#: ../src/shell-global.c:1162 #, c-format msgid "%d minute ago" msgid_plural "%d minutes ago" msgstr[0] "%d นาทีก่อน" -#: ../src/shell-global.c:1034 +#: ../src/shell-global.c:1167 #, c-format msgid "%d hour ago" msgid_plural "%d hours ago" msgstr[0] "%d ชั่วโมงก่อน" -#: ../src/shell-global.c:1039 +#: ../src/shell-global.c:1172 #, c-format msgid "%d day ago" msgid_plural "%d days ago" msgstr[0] "%d วันก่อน" -#: ../src/shell-global.c:1044 +#: ../src/shell-global.c:1177 #, c-format msgid "%d week ago" msgid_plural "%d weeks ago" msgstr[0] "%d สัปดาห์ก่อน" -#: ../src/shell-uri-util.c:89 +#: ../src/shell-util.c:89 msgid "Home Folder" msgstr "โฟลเดอร์บ้าน" #. Translators: this is the same string as the one found in #. * nautilus -#: ../src/shell-uri-util.c:104 +#: ../src/shell-util.c:104 msgid "File System" msgstr "ระบบแฟ้ม" -#: ../src/shell-uri-util.c:250 +#: ../src/shell-util.c:250 msgid "Search" msgstr "ค้นหา" @@ -331,7 +699,10 @@ msgstr "ค้นหา" #. * example, "Trash: some-directory". It means that the #. * directory called "some-directory" is in the trash. #. -#: ../src/shell-uri-util.c:300 +#: ../src/shell-util.c:300 #, c-format msgid "%1$s: %2$s" -msgstr "" +msgstr "%1$s: %2$s" + +#~ msgid "Account Information..." +#~ msgstr "ข้อมูลบัญชี..." From 6a52deec7df0e9e9b8de0cee869bc2047c7c42a8 Mon Sep 17 00:00:00 2001 From: Maxim Ermilov Date: Sun, 3 Oct 2010 23:48:56 +0400 Subject: [PATCH 16/61] Add function for finding urls in string https://bugzilla.gnome.org/show_bug.cgi?id=610219 --- js/Makefile.am | 1 + js/misc/utils.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 js/misc/utils.js diff --git a/js/Makefile.am b/js/Makefile.am index b7632b472..47fac8237 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -8,6 +8,7 @@ nobase_dist_js_DATA = \ misc/gnomeSession.js \ misc/params.js \ misc/telepathy.js \ + misc/utils.js \ perf/core.js \ prefs/clockPreferences.js \ ui/altTab.js \ diff --git a/js/misc/utils.js b/js/misc/utils.js new file mode 100644 index 000000000..0b7d6c02b --- /dev/null +++ b/js/misc/utils.js @@ -0,0 +1,19 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +/* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */ +const _urlRegexp = /\b(([a-z][\w-]+:(\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)([^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))/gi; + +// findUrls: +// @str: string to find URLs in +// +// Searches @str for URLs and returns an array of objects with %url +// properties showing the matched URL string, and %pos properties indicating +// the position within @str where the URL was found. +// +// Return value: the list of match objects, as described above +function findUrls(str) { + let res = [], match; + while ((match = _urlRegexp.exec(str))) + res.push({ url: match[0], pos: match.index }); + return res; +} From 65f0b483f801378cdb3c5f808badc9822ca0e784 Mon Sep 17 00:00:00 2001 From: Maxim Ermilov Date: Wed, 24 Nov 2010 02:27:47 +0300 Subject: [PATCH 17/61] messageTray: make links in message banners clickable https://bugzilla.gnome.org/show_bug.cgi?id=610219 --- data/theme/gnome-shell.css | 4 ++ js/ui/dnd.js | 10 +-- js/ui/messageTray.js | 134 +++++++++++++++++++++++++++++++++---- src/shell-global.c | 5 ++ src/shell-global.h | 3 +- 5 files changed, 138 insertions(+), 18 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 82b8440f9..c5b6b54c0 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -847,6 +847,10 @@ StTooltip StLabel { color: #cccccc; } +.url-highlighter { + link-color: #ccccff; +} + /* Message Tray */ #message-tray { background-gradient-direction: vertical; diff --git a/js/ui/dnd.js b/js/ui/dnd.js index 66f468083..71d8c04bc 100644 --- a/js/ui/dnd.js +++ b/js/ui/dnd.js @@ -26,9 +26,9 @@ const DragMotionResult = { }; const DRAG_CURSOR_MAP = { - 0: Shell.Cursor.UNSUPPORTED_TARGET, - 1: Shell.Cursor.COPY, - 2: Shell.Cursor.MOVE + 0: Shell.Cursor.DND_UNSUPPORTED_TARGET, + 1: Shell.Cursor.DND_COPY, + 2: Shell.Cursor.DND_MOVE }; const DragDropResult = { @@ -221,7 +221,7 @@ _Draggable.prototype = { if (this._onEventId) this._ungrabActor(); this._grabEvents(); - global.set_cursor(Shell.Cursor.IN_DRAG); + global.set_cursor(Shell.Cursor.DND_IN_DRAG); this._dragX = this._dragStartX = stageX; this._dragY = this._dragStartY = stageY; @@ -382,7 +382,7 @@ _Draggable.prototype = { } target = target.get_parent(); } - global.set_cursor(Shell.Cursor.IN_DRAG); + global.set_cursor(Shell.Cursor.DND_IN_DRAG); } return true; diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js index e0c4ecc86..2b6226a78 100644 --- a/js/ui/messageTray.js +++ b/js/ui/messageTray.js @@ -1,6 +1,8 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ const Clutter = imports.gi.Clutter; +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Mainloop = imports.mainloop; @@ -14,6 +16,7 @@ const Tweener = imports.ui.tweener; const Main = imports.ui.main; const BoxPointer = imports.ui.boxpointer; const Params = imports.misc.params; +const Utils = imports.misc.utils; const ANIMATION_TIME = 0.2; const NOTIFICATION_TIMEOUT = 4; @@ -44,6 +47,117 @@ function _cleanMarkup(text) { return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '<$1'); } +function URLHighlighter(text, lineWrap) { + this._init(text, lineWrap); +} + +URLHighlighter.prototype = { + _init: function(text, lineWrap) { + if (!text) + text = ''; + this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' }); + this._linkColor = '#ccccff'; + this.actor.connect('style-changed', Lang.bind(this, function() { + let color = new Clutter.Color(); + let hasColor = this.actor.get_theme_node().get_color('link-color', color); + if (hasColor) { + let linkColor = color.to_string().substr(0, 7); + if (linkColor != this._linkColor) { + this._linkColor = linkColor; + this._highlightUrls(); + } + } + })); + if (lineWrap) { + this.actor.clutter_text.line_wrap = true; + this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; + this.actor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; + } + + this.setMarkup(text); + this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) { + let urlId = this._findUrlAtPos(event); + if (urlId != -1) { + let url = this._urls[urlId].url; + if (url.indexOf(':') == -1) + url = 'http://' + url; + try { + Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context()); + return true; + } catch (e) { + // TODO: remove this after gnome 3 release + let p = new Shell.Process({ 'args' : ['gvfs-open', url] }); + p.run(); + return true; + } + } + return false; + })); + this.actor.connect('motion-event', Lang.bind(this, function(actor, event) { + let urlId = this._findUrlAtPos(event); + if (urlId != -1 && !this._cursorChanged) { + global.set_cursor(Shell.Cursor.POINTING_HAND); + this._cursorChanged = true; + } else if (urlId == -1) { + global.unset_cursor(); + this._cursorChanged = false; + } + return false; + })); + this.actor.connect('leave-event', Lang.bind(this, function() { + if (this._cursorChanged) { + this._cursorChanged = false; + global.unset_cursor(); + } + })); + }, + + setMarkup: function(text) { + text = text ? _cleanMarkup(text) : ''; + this._text = text; + + this.actor.clutter_text.set_markup(text); + /* clutter_text.text contain text without markup */ + this._urls = Utils.findUrls(this.actor.clutter_text.text); + this._highlightUrls(); + }, + + _highlightUrls: function() { + // text here contain markup + let urls = Utils.findUrls(this._text); + let markup = ''; + let pos = 0; + for (let i = 0; i < urls.length; i++) { + let url = urls[i]; + let str = this._text.substr(pos, url.pos - pos); + markup += str + '' + url.url + ''; + pos = url.pos + url.url.length; + } + markup += this._text.substr(pos); + this.actor.clutter_text.set_markup(markup); + }, + + _findUrlAtPos: function(event) { + let success; + let [x, y] = event.get_coords(); + [success, x, y] = this.actor.transform_stage_point(x, y); + let find_pos = -1; + for (let i = 0; i < this.actor.clutter_text.text.length; i++) { + let [success, px, py, line_height] = this.actor.clutter_text.position_to_coords(i); + if (py > y || py + line_height < y || x < px) + continue; + find_pos = i; + } + if (find_pos != -1) { + for (let i = 0; i < this._urls.length; i++) + if (find_pos >= this._urls[i].pos && + this._urls[i].pos + this._urls[i].url.length > find_pos) + return i; + } + return -1; + } +}; + // Notification: // @source: the notification's Source // @title: the title @@ -148,7 +262,8 @@ Notification.prototype = { this._titleLabel = new St.Label(); this._bannerBox.add_actor(this._titleLabel); - this._bannerLabel = new St.Label(); + this._bannerUrlHighlighter = new URLHighlighter(); + this._bannerLabel = this._bannerUrlHighlighter.actor; this._bannerBox.add_actor(this._bannerLabel); this.update(title, banner, params); @@ -214,8 +329,9 @@ Notification.prototype = { // not fitting fully in the single-line mode. this._bannerBodyText = this._customContent ? null : banner; - banner = banner ? _cleanMarkup(banner.replace(/\n/g, ' ')) : ''; - this._bannerLabel.clutter_text.set_markup(banner); + banner = banner ? banner.replace(/\n/g, ' ') : ''; + + this._bannerUrlHighlighter.setMarkup(banner); this._bannerLabel.queue_relayout(); // Add the bannerBody now if we know for sure we'll need it @@ -259,16 +375,10 @@ Notification.prototype = { // // Return value: the newly-added label addBody: function(text) { - let body = new St.Label(); - body.clutter_text.line_wrap = true; - body.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; - body.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; + let label = new URLHighlighter(text, true); - text = text ? _cleanMarkup(text) : ''; - body.clutter_text.set_markup(text); - - this.addActor(body); - return body; + this.addActor(label.actor); + return label.actor; }, _addBannerBody: function() { diff --git a/src/shell-global.c b/src/shell-global.c index dad0b2068..77e2d851a 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -497,6 +497,9 @@ shell_global_set_cursor (ShellGlobal *global, case SHELL_CURSOR_DND_UNSUPPORTED_TARGET: name = "dnd-none"; break; + case SHELL_CURSOR_POINTING_HAND: + name = "hand"; + break; default: g_return_if_reached (); } @@ -516,6 +519,8 @@ shell_global_set_cursor (ShellGlobal *global, case SHELL_CURSOR_DND_COPY: cursor_type = GDK_PLUS; break; + case SHELL_CURSOR_POINTING_HAND: + cursor_type = GDK_HAND2; case SHELL_CURSOR_DND_UNSUPPORTED_TARGET: cursor_type = GDK_X_CURSOR; break; diff --git a/src/shell-global.h b/src/shell-global.h index b8c5af7c6..91a82c454 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -38,7 +38,8 @@ typedef enum { SHELL_CURSOR_DND_IN_DRAG, SHELL_CURSOR_DND_UNSUPPORTED_TARGET, SHELL_CURSOR_DND_MOVE, - SHELL_CURSOR_DND_COPY + SHELL_CURSOR_DND_COPY, + SHELL_CURSOR_POINTING_HAND } ShellCursor; void shell_global_set_cursor (ShellGlobal *global, From d6f1c10b1bae6b0059bbe5827e3b7424fd692d6b Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Wed, 24 Nov 2010 02:31:55 +0300 Subject: [PATCH 18/61] messageTray: fix handling of markup vs non-markup notifications NotificationDaemon-based notifications have markup in the banner/body, but Telepathy-based notifications don't. (Eg, an XMPP message containing "foo" should show up angle brackets and all, not as bold.) Fix MessageTray.Notification to allow explicitly specifying where there should and shouldn't be markup, and use that appropriately. https://bugzilla.gnome.org/show_bug.cgi?id=610219 --- js/ui/messageTray.js | 59 ++++++++++++++++++++++++------------- js/ui/notificationDaemon.js | 7 +++-- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js index 2b6226a78..05335b550 100644 --- a/js/ui/messageTray.js +++ b/js/ui/messageTray.js @@ -38,21 +38,27 @@ const State = { HIDING: 3 }; -function _cleanMarkup(text) { - // Support &, ", ', < and >, escape all other - // occurrences of '&'. - let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&'); - // Support , , and , escape anything else - // so it displays as raw markup. - return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '<$1'); +function _fixMarkup(text, allowMarkup) { + if (allowMarkup) { + // Support &, ", ', < and >, escape all other + // occurrences of '&'. + let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&'); + // Support , , and , escape anything else + // so it displays as raw markup. + return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '<$1'); + } else { + // Escape everything + let _text = text.replace(/&/g, '&'); + return _text.replace(/' + title + ''); // Unless the notification has custom content, we save this._bannerBodyText @@ -328,10 +345,11 @@ Notification.prototype = { // expandable due to other elements in its content area or due to the banner // not fitting fully in the single-line mode. this._bannerBodyText = this._customContent ? null : banner; + this._bannerBodyMarkup = params.bannerMarkup; banner = banner ? banner.replace(/\n/g, ' ') : ''; - this._bannerUrlHighlighter.setMarkup(banner); + this._bannerUrlHighlighter.setMarkup(banner, params.bannerMarkup); this._bannerLabel.queue_relayout(); // Add the bannerBody now if we know for sure we'll need it @@ -339,7 +357,7 @@ Notification.prototype = { this._addBannerBody(); if (params.body) - this.addBody(params.body); + this.addBody(params.body, params.bodyMarkup); this._updated(); }, @@ -370,12 +388,13 @@ Notification.prototype = { // addBody: // @text: the text + // @markup: %true if @text contains pango markup // // Adds a multi-line label containing @text to the notification. // // Return value: the newly-added label - addBody: function(text) { - let label = new URLHighlighter(text, true); + addBody: function(text, markup) { + let label = new URLHighlighter(text, true, markup); this.addActor(label.actor); return label.actor; @@ -385,7 +404,7 @@ Notification.prototype = { if (this._bannerBodyText) { let text = this._bannerBodyText; this._bannerBodyText = null; - this.addBody(text); + this.addBody(text, this._bannerBodyMarkup); } }, diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js index c62cb4c20..dc5053738 100644 --- a/js/ui/notificationDaemon.js +++ b/js/ui/notificationDaemon.js @@ -205,8 +205,6 @@ NotificationDaemon.prototype = { return id; } - summary = GLib.markup_escape_text(summary, -1); - let rewrites = rewriteRules[appName]; if (rewrites) { for (let i = 0; i < rewrites.length; i++) { @@ -281,7 +279,9 @@ NotificationDaemon.prototype = { let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE); if (notification == null) { - notification = new MessageTray.Notification(source, summary, body, { icon: iconActor }); + notification = new MessageTray.Notification(source, summary, body, + { icon: iconActor, + bannerMarkup: true }); ndata.notification = notification; notification.connect('clicked', Lang.bind(this, function(n) { @@ -294,6 +294,7 @@ NotificationDaemon.prototype = { notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id)); } else { notification.update(summary, body, { icon: iconActor, + bannerMarkup: true, clear: true }); } From 01e7d6f30e288dad056b6496170607b0751eb0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Wed, 24 Nov 2010 18:58:43 +0100 Subject: [PATCH 19/61] workspace: Simplify the close button's timeout handler Clutter actor gained a :has_pointer property after the original code was written, so use this instead of picking. --- js/ui/workspace.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 349c87b5e..3e80d5ab5 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -534,12 +534,10 @@ WindowOverlay.prototype = { _idleToggleCloseButton: function() { this._idleToggleCloseId = 0; - let [x, y, mask] = global.get_pointer(); - let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, - x, y); - if (actor != this._windowClone.actor && actor != this.closeButton) { + if (!this._windowClone.actor.has_pointer && + !this.closeButton.has_pointer) this.closeButton.hide(); - } + return false; }, From 6e902f5fece57a5f87e588cbb99e5d4b77849003 Mon Sep 17 00:00:00 2001 From: Nick Glynn Date: Thu, 25 Nov 2010 00:14:42 +0000 Subject: [PATCH 20/61] Add development packages needed to build on Ubuntu --- tools/build/gnome-shell-build-setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build/gnome-shell-build-setup.sh b/tools/build/gnome-shell-build-setup.sh index 8e3e53c9d..b6c48734f 100755 --- a/tools/build/gnome-shell-build-setup.sh +++ b/tools/build/gnome-shell-build-setup.sh @@ -82,7 +82,7 @@ if test "x$system" = xUbuntu -o "x$system" = xDebian -o "x$system" = xLinuxMint mesa-common-dev mesa-utils libpam-dev python-dev python-gconf python-gobject xulrunner-dev xserver-xephyr gnome-terminal libcroco3-dev libgstreamer0.10-dev gstreamer0.10-plugins-base gstreamer0.10-plugins-good - libltdl-dev libvorbis-dev libxklavier-dev + libltdl-dev libvorbis-dev libxklavier-dev libgnome-keyring-dev libupower-glib-dev " if apt-cache show autopoint > /dev/null 2> /dev/null; then From 4f7a28863c9471787a6e2685de46894af2e4825b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Tue, 23 Nov 2010 13:48:23 +0100 Subject: [PATCH 21/61] st-icon: Add support for -st-shadow property Add a drop shadow to the icon texture if the -st-shadow property is specified. https://bugzilla.gnome.org/show_bug.cgi?id=635608 --- src/st/st-icon.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/src/st/st-icon.c b/src/st/st-icon.c index 4b6b14b76..4dc0864d9 100644 --- a/src/st/st-icon.c +++ b/src/st/st-icon.c @@ -56,6 +56,10 @@ struct _StIconPrivate gint prop_icon_size; /* icon size set as property */ gint theme_icon_size; /* icon size from theme node */ gint icon_size; /* icon size we are using */ + + CoglHandle shadow_material; + float shadow_width; + float shadow_height; }; static void st_icon_update (StIcon *icon); @@ -145,6 +149,12 @@ st_icon_dispose (GObject *gobject) priv->gicon = NULL; } + if (priv->shadow_material) + { + cogl_handle_unref (priv->shadow_material); + priv->shadow_material = COGL_INVALID_HANDLE; + } + G_OBJECT_CLASS (st_icon_parent_class)->dispose (gobject); } @@ -227,7 +237,30 @@ st_icon_paint (ClutterActor *actor) CLUTTER_ACTOR_CLASS (st_icon_parent_class)->paint (actor); if (priv->icon_texture) - clutter_actor_paint (priv->icon_texture); + { + if (priv->shadow_material) + { + StThemeNode *node = st_widget_get_theme_node (ST_WIDGET (actor)); + StShadow *shadow_spec = st_theme_node_get_shadow (node); + ClutterActorBox allocation; + float width, height; + + clutter_actor_get_allocation_box (priv->icon_texture, &allocation); + clutter_actor_box_get_size (&allocation, &width, &height); + + allocation.x1 = (width - priv->shadow_width) / 2; + allocation.y1 = (height - priv->shadow_height) / 2; + allocation.x2 = allocation.x1 + priv->shadow_width; + allocation.y2 = allocation.y1 + priv->shadow_height; + + _st_paint_shadow_with_opacity (shadow_spec, + priv->shadow_material, + &allocation, + clutter_actor_get_paint_opacity (priv->icon_texture)); + } + + clutter_actor_paint (priv->icon_texture); + } } static void @@ -326,6 +359,45 @@ st_icon_init (StIcon *self) self->priv->icon_size = DEFAULT_ICON_SIZE; self->priv->prop_icon_size = -1; self->priv->icon_type = DEFAULT_ICON_TYPE; + + self->priv->icon_texture = COGL_INVALID_HANDLE; + self->priv->shadow_width = -1; + self->priv->shadow_height = -1; +} + +static void +st_icon_update_shadow_material (StIcon *icon) +{ + StIconPrivate *priv = icon->priv; + StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (icon)); + StShadow *shadow_spec = st_theme_node_get_shadow (theme_node); + + if (priv->shadow_material) + { + cogl_handle_unref (priv->shadow_material); + priv->shadow_material = COGL_INVALID_HANDLE; + } + + if (shadow_spec) + { + CoglHandle material; + gint width, height; + + clutter_texture_get_base_size (CLUTTER_TEXTURE (priv->icon_texture), + &width, &height); + material = _st_create_shadow_material_from_actor (shadow_spec, + priv->icon_texture); + priv->shadow_material = material; + priv->shadow_width = width; + priv->shadow_height = height; + } +} + +static void +on_pixbuf_changed (ClutterTexture *texture, + StIcon *icon) +{ + st_icon_update_shadow_material (icon); } static void @@ -366,7 +438,14 @@ st_icon_update (StIcon *icon) priv->icon_size); } if (priv->icon_texture) - clutter_actor_set_parent (priv->icon_texture, CLUTTER_ACTOR (icon)); + { + st_icon_update_shadow_material (icon); + clutter_actor_set_parent (priv->icon_texture, CLUTTER_ACTOR (icon)); + + /* "pixbuf-change" is actually a misnomer for "texture-changed" */ + g_signal_connect (priv->icon_texture, "pixbuf-change", + G_CALLBACK (on_pixbuf_changed), icon); + } } static gboolean From 926ddc2bdf7de460adaef23fdb7b4a5afedb875c Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Thu, 25 Nov 2010 15:29:41 +0100 Subject: [PATCH 22/61] Show timestamp in expanded chat When the last message is older than SCROLLBACK_IMMEDIATE_TIME (1 minutes), show a timestamp in the middle, indicating the time it was sent. Use the same style for presence changes, but show them on the left. https://bugzilla.gnome.org/show_bug.cgi?id=617228 --- data/theme/gnome-shell.css | 7 ++++ js/ui/messageTray.js | 9 +++-- js/ui/telepathyClient.js | 75 ++++++++++++++++++++++++++++++-------- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index c5b6b54c0..58aa1febb 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -957,6 +957,13 @@ StTooltip StLabel { border-radius: 4px; } +.chat-meta-message { + padding-left: 4px; + border-radius: 4px; + font-size: 14px; + color: #bbbbbb; +} + .chat-response { padding: 4px; border-radius: 4px; diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js index 05335b550..e2a4c182d 100644 --- a/js/ui/messageTray.js +++ b/js/ui/messageTray.js @@ -365,7 +365,7 @@ Notification.prototype = { // @actor: actor to add to the body of the notification // // Appends @actor to the notification's body - addActor: function(actor) { + addActor: function(actor, style) { if (!this._scrollArea) { this.actor.add_style_class_name('multi-line-notification'); this._scrollArea = new St.ScrollView({ name: 'notification-scrollview', @@ -382,21 +382,22 @@ Notification.prototype = { this._addBannerBody(); } - this._contentArea.add(actor); + this._contentArea.add(actor, style ? style : {}); this._updated(); }, // addBody: // @text: the text // @markup: %true if @text contains pango markup + // @style: style to use when adding the actor containing the text // // Adds a multi-line label containing @text to the notification. // // Return value: the newly-added label - addBody: function(text, markup) { + addBody: function(text, markup, style) { let label = new URLHighlighter(text, true, markup); - this.addActor(label.actor); + this.addActor(label.actor, style); return label.actor; }, diff --git a/js/ui/telepathyClient.js b/js/ui/telepathyClient.js index 867c63aff..c11d0ba58 100644 --- a/js/ui/telepathyClient.js +++ b/js/ui/telepathyClient.js @@ -3,6 +3,7 @@ const DBus = imports.dbus; const GLib = imports.gi.GLib; const Lang = imports.lang; +const Mainloop = imports.mainloop; const Signals = imports.signals; const St = imports.gi.St; const Gettext = imports.gettext.domain('gnome-shell'); @@ -16,6 +17,7 @@ let contactManager; let channelDispatcher; // See Notification.appendMessage +const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes const SCROLLBACK_RECENT_LENGTH = 20; const SCROLLBACK_IDLE_LENGTH = 5; @@ -531,7 +533,7 @@ Source.prototype = { _messageReceived: function(channel, id, timestamp, sender, type, flags, text) { this._ensureNotification(); - this._notification.appendMessage(text); + this._notification.appendMessage(text, timestamp); this.notify(this._notification); }, @@ -565,7 +567,7 @@ Source.prototype = { msg += ' (' + GLib.markup_escape_text(message, -1) + ')'; this._ensureNotification(); - this._notification.appendMessage(msg, true); + this._notification.appendPresence(msg, notify); if (notify) this.notify(this._notification); } @@ -586,23 +588,40 @@ Notification.prototype = { this.setActionArea(this._responseEntry); this._history = []; + this._timestampTimeoutId = 0; }, - appendMessage: function(text, asTitle) { - if (asTitle) - this.update(text, null, { customContent: true }); - else - this.update(this.source.title, text, { customContent: true }); - this._append(text, 'chat-received'); + appendMessage: function(text, timestamp) { + this.update(this.source.title, text, { customContent: true }); + this._append(text, 'chat-received', timestamp); }, - _append: function(text, style) { + _append: function(text, style, timestamp) { + let currentTime = (Date.now() / 1000); + if (!timestamp) + timestamp = currentTime; + let lastMessageTime = -1; + if (this._history.length > 0) + lastMessageTime = this._history[0].time; + + // Reset the old message timeout + if (this._timestampTimeoutId) + Mainloop.source_remove(this._timestampTimeoutId); + let body = this.addBody(text); body.add_style_class_name(style); this.scrollTo(St.Side.BOTTOM); - let now = new Date().getTime() / 1000; - this._history.unshift({ actor: body, time: now }); + this._history.unshift({ actor: body, time: timestamp, realMessage: true }); + + if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) + this._appendTimestamp(); + else + // Schedule a new timestamp in SCROLLBACK_IMMEDIATE_TIME + // from the timestamp of the message. + this._timestampTimeoutId = Mainloop.timeout_add_seconds( + SCROLLBACK_IMMEDIATE_TIME - (currentTime - timestamp), + Lang.bind(this, this._appendTimestamp)); if (this._history.length > 1) { // Keep the scrollback from growing too long. If the most @@ -611,17 +630,43 @@ Notification.prototype = { // SCROLLBACK_RECENT_LENGTH previous messages. Otherwise // we'll keep SCROLLBACK_IDLE_LENGTH messages. - let lastMessageTime = this._history[1].time; - let maxLength = (lastMessageTime < now - SCROLLBACK_RECENT_TIME) ? + let maxLength = (lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME) ? SCROLLBACK_IDLE_LENGTH : SCROLLBACK_RECENT_LENGTH; - if (this._history.length > maxLength) { - let expired = this._history.splice(maxLength); + let filteredHistory = this._history.filter(function(item) { return item.realMessage }); + if (filteredHistory.length > maxLength) { + let lastMessageToKeep = filteredHistory[maxLength]; + let expired = this._history.splice(this._history.indexOf(lastMessageToKeep)); for (let i = 0; i < expired.length; i++) expired[i].actor.destroy(); } } }, + _appendTimestamp: function() { + let lastMessageTime = this._history[0].time; + let lastMessageDate = new Date(lastMessageTime * 1000); + + /* Translators: this is a time format string followed by a date. + If applicable, replace %X with a strftime format valid for your + locale, without seconds. */ + let timeLabel = this.addBody(lastMessageDate.toLocaleFormat(_("Sent at %X on %A")), false, { expand: true, x_fill: false, x_align: St.Align.END }); + timeLabel.add_style_class_name('chat-meta-message'); + this._history.unshift({ actor: timeLabel, time: lastMessageTime, realMessage: false }); + + this._timestampTimeoutId = 0; + return false; + }, + + appendPresence: function(text, asTitle) { + if (asTitle) + this.update(text, null, { customContent: true }); + else + this.update(this.source.title, null, { customContent: true }); + let label = this.addBody(text); + label.add_style_class_name('chat-meta-message'); + this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false}); + }, + grabFocus: function(lockTray) { // Need to call the base class function first so that // it saves where the key focus was before. From 2167be053da595be0b9a65168e1a4c91c5bbb398 Mon Sep 17 00:00:00 2001 From: "Gheyret T.Kenji" Date: Fri, 26 Nov 2010 14:28:16 +0100 Subject: [PATCH 23/61] Added UG translation --- po/ug.po | 257 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 156 insertions(+), 101 deletions(-) diff --git a/po/ug.po b/po/ug.po index 24b4d23b5..1e58f50de 100644 --- a/po/ug.po +++ b/po/ug.po @@ -1,17 +1,18 @@ -# Uyghur translation for gnome-shell. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# Gheyret Kenji,2010. -# Sahran , 2010. -# Zeper , 2010. -# +# Uyghur translation for gnome-shell. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Gheyret Kenji,2010. +# Sahran , 2010. +# Zeper , 2010. +# msgid "" msgstr "" "Project-Id-Version: gnome-shell\n" -"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell&component=general\n" -"POT-Creation-Date: 2010-11-13 23:05+0000\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Gheyret Kenji\n" +"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-" +"shell&component=general\n" +"POT-Creation-Date: 2010-11-25 14:32+0000\n" +"PO-Revision-Date: 2010-11-25 14:28+0600\n" +"Last-Translator: Sahran \n" "Language-Team: Uyghur Computer Science Association \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -20,7 +21,7 @@ msgstr "" #: ../data/gnome-shell.desktop.in.in.h:1 msgid "GNOME Shell" -msgstr "GNOME چاپان" +msgstr "GNOME Shell" #: ../data/gnome-shell.desktop.in.in.h:2 msgid "Window management and application launching" @@ -32,7 +33,7 @@ msgstr "سائەت" #: ../data/gnome-shell-clock-preferences.desktop.in.in.h:2 msgid "Customize the panel clock" -msgstr "ئىختىيارىچە تاختا سائەت" +msgstr "تاختا سائەتنى ئۆزلەشتۈرىدۇ" #: ../data/org.gnome.shell.gschema.xml.in.h:1 msgid "" @@ -42,25 +43,32 @@ msgstr "ئىچكى سازلاش ۋە كۆزىتىش قورالىنى زىيار #: ../data/org.gnome.shell.gschema.xml.in.h:2 msgid "Custom format of the clock" -msgstr "ئىختىيارىي سائەت فورماتى" +msgstr "سائەتنىڭ ئىختىيارىي فورماتى" #: ../data/org.gnome.shell.gschema.xml.in.h:3 msgid "Enable internal tools useful for developers and testers from Alt-F2" -msgstr "ئىچكى قورال قوزغىتىلسا ئىجادكارلار ۋە سىنىغۇچىلارنىڭ Alt-F2 ئارقىلىق كىرىشىگە قۇلايلىق" +msgstr "" +"ئىچكى قورال قوزغىتىلسا ئىجادكارلار ۋە سىنىغۇچىلارنىڭ Alt-F2 ئارقىلىق " +"كىرىشىگە قۇلايلىق" #: ../data/org.gnome.shell.gschema.xml.in.h:4 msgid "File extension used for storing the screencast" -msgstr "ھۆججەت كېڭەيتىلگەن ئاتى ئېكران كەسمىسى (screencasts) ساقلاشقا ئىشلىتىلىدۇ" +msgstr "" +"ئېكران كەسمىسى (screencasts) ساقلاشتا ئىشلىتىلىدىغان ھۆججەتنىڭ كېڭەيتىلگەن " +"ئاتى " #: ../data/org.gnome.shell.gschema.xml.in.h:5 msgid "Framerate used for recording screencasts." -msgstr "كاندۇك نىسبىتى ئېكران كەسمىسى (screencasts) خاتىرىلەشكە ئىشلىتىلىدۇ" +msgstr "" +"ئېكران كەسمىسى (screencasts) خاتىرىلەشتە ئىشلىتىلىدىغان كاندۇك تېزلىكى." #: ../data/org.gnome.shell.gschema.xml.in.h:6 msgid "" "GNOME Shell extensions have a uuid property; this key lists extensions which " "should not be loaded." -msgstr "GNOME چاپان (Shell)كېڭەيتىلمىسىنىڭ uuid خاسلىقى بار؛ بۇ كۇنۇپكا يۈكلەنمەيدىغان كېڭەيتىلمىلەر تىزىملىكىنى كۆرسىتىدۇ." +msgstr "" +"GNOME چاپان (Shell)كېڭەيتىلمىسىنىڭ uuid خاسلىقى بار؛ بۇ كۇنۇپكا " +"يۈكلەنمەيدىغان كېڭەيتىلمىلەر تىزىملىكىنى كۆرسىتىدۇ." #: ../data/org.gnome.shell.gschema.xml.in.h:7 msgid "History for command (Alt-F2) dialog" @@ -74,13 +82,17 @@ msgstr "سائەت فورماتى" msgid "" "If true and format is either \"12-hour\" or \"24-hour\", display date in the " "clock, in addition to time." -msgstr "ئەگەر راست (true) بولسا سائەتتە ۋاقىت فورماتىنى «12 سائەت» ياكى «24 سائەت» كۆرسەتكەندىن سىرت چېسلانىمۇ كۆرسىتىدۇ." +msgstr "" +"ئەگەر راست (true) بولسا سائەتتە ۋاقىت فورماتىنى «12 سائەت» ياكى «24 سائەت» " +"كۆرسەتكەندىن سىرت چېسلانىمۇ كۆرسىتىدۇ." #: ../data/org.gnome.shell.gschema.xml.in.h:10 msgid "" "If true and format is either \"12-hour\" or \"24-hour\", display seconds in " "time." -msgstr "ئەگەر راست (true) بولسا سائەتتە ۋاقىت فورماتىنى «12 سائەت» ياكى «24 سائەت» كۆرسەتكەندىن سىرت سېكۇنتنىمۇ كۆرسىتىدۇ." +msgstr "" +"ئەگەر راست (true) بولسا سائەتتە ۋاقىت فورماتىنى «12 سائەت» ياكى «24 سائەت» " +"كۆرسەتكەندىن سىرت سېكۇنتنىمۇ كۆرسىتىدۇ. " #: ../data/org.gnome.shell.gschema.xml.in.h:11 msgid "If true, display the ISO week date in the calendar." @@ -88,11 +100,11 @@ msgstr "ئەگەر راست(true) بولسا يىلنامىدىكى ISO ھەپت #: ../data/org.gnome.shell.gschema.xml.in.h:12 msgid "List of desktop file IDs for favorite applications" -msgstr "ئامراق پروگراممىلارنىڭ ئۈستەل ئۈستى ھۆججەت ID تىزىملىكى" +msgstr "ئامراق قوللىنىشچان پروگراممىلارنىڭ ئۈستەل ئۈستى ھۆججەت ID تىزىملىكى" #: ../data/org.gnome.shell.gschema.xml.in.h:13 msgid "Overview workspace view mode" -msgstr "قىسقىچە بايان خىزمەت رايون كۆرۈنۈش ھالىتى" +msgstr "خىزمەت رايون كۆرۈنۈش ھالىتى ھەققىدە قىسقىچە بايان" #: ../data/org.gnome.shell.gschema.xml.in.h:14 msgid "" @@ -105,6 +117,16 @@ msgid "" "to an empty value, the default pipeline will be used. This is currently " "'videorate ! theoraenc ! oggmux' and records to Ogg Theora." msgstr "" +"ئۈن ئېلىشنى كودلاشتا ئىشلىتىلىدىغان GStreamer ئاقما لىنىيىنى تەڭشەيدۇ. ئۇ " +"gst-launch گرامماتىكىسىغا بوي سۇنىدۇ. بۇ ئاقما لىنىيىدە ئۇلانمىغان sink pad " +"بولۇشى لازىم، خاتىرىلىنىدىغان سىن مۇشۇ جايدا خاتىرىلىنىدۇ. بۇ لىنىيىدە " +"ئادەتتە يەنە بىر ئۇلانمىغان مەنبە pad بولىدۇ؛ بۇ pad چىقارغان ئۇچۇرلار " +"ھۆججەتكە يېزىلىدۇ. ئەمما ئاقما لىنىيە ئۆزىنىڭ چىقىرىشىنى بىر تەرەپ " +"قىلالايدۇ، بۇنداق بولغاندا shout2send ئارقىلىق ياكى شۇنىڭغا ئوخشاش ئۇسۇلدا " +"چىقىرىشنى icecast مۇلازىمېتىرىغا يوللايدۇ. ئاقما لىنىيە تەڭشەلمىگەن ياكى بوش " +"قىممەتكە تەڭشەلگەندە كۆڭۈلدىكى ئاقما لىنىيە قوزغىتىلىدۇ. ئۇنىڭ نۆۋەتتىكى " +"قىممىتى 'videorate ! theoraenc ! oggmux' بولۇپ، فورماتى Ogg شەكلىدە " +"خاتىرىلىنىدۇ." #: ../data/org.gnome.shell.gschema.xml.in.h:15 msgid "Show date in clock" @@ -112,7 +134,7 @@ msgstr "سائەت ئىچىدە چېسلا كۆرسەت" #: ../data/org.gnome.shell.gschema.xml.in.h:16 msgid "Show the week date in the calendar" -msgstr "يىلنامىدە ھەپتە كۆرسەت" +msgstr "يىلنامىدە ھەپتىنى كۆرسىتىدۇ" #: ../data/org.gnome.shell.gschema.xml.in.h:17 msgid "Show time with seconds" @@ -129,23 +151,30 @@ msgid "" "The filename for recorded screencasts will be a unique filename based on the " "current date, and use this extension. It should be changed when recording to " "a different container format." -msgstr "خاتىرىلەنگەن ئېكراننىڭ ھۆججەت ئاتى نۆۋەتتىكى چېسلا ئاساسىدا بىردىنبىر بولۇپ بۇ كېڭەيتىلگەن ئاتىنى ئىشلىتىدۇ. ئۇ ئۆزگەرسە ئوخشاش بولمىغان قاچا فورماتىدا خاتىرىلەيدۇ." +msgstr "" +"خاتىرىلەنگەن ئېكراننىڭ ھۆججەت ئاتى نۆۋەتتىكى چېسلا ئاساسىدا بىردىنبىر بولۇپ " +"بۇ كېڭەيتىلگەن ئاتىنى ئىشلىتىدۇ. ئۇ ئۆزگەرسە ئوخشاش بولمىغان قاچا فورماتىدا " +"خاتىرىلەيدۇ." #: ../data/org.gnome.shell.gschema.xml.in.h:20 msgid "" "The framerate of the resulting screencast recordered by GNOME Shell's " "screencast recorder in frames-per-second." -msgstr "GNOME Shell ئېكران خاتىرىلىگۈچ ھەر سېكۇنتتا خاتىرىلەيدىغان ئېكران كەسمىسى كاندۇك نىسبىتى" +msgstr "" +"GNOME Shell ئېكران خاتىرىلىگۈچ ھەر سېكۇنتتا خاتىرىلەيدىغان ئېكران كەسمىسى " +"كاندۇك سۈرىتى(ھەر سېكۇنتتىكى كاندۇك سانى)." #: ../data/org.gnome.shell.gschema.xml.in.h:21 msgid "The gstreamer pipeline used to encode the screencast" -msgstr "" +msgstr "ئېكران كەسمىسىنى كودلاشتا ئىشلىتىلىدىغان gstreamer ئاقما لىنىيىسى" #: ../data/org.gnome.shell.gschema.xml.in.h:22 msgid "" "The selected workspace view mode in the overview. Supported values are " "\"single\" and \"grid\"." -msgstr "تاللانغان قىسقىچە مەزمۇن خىزمەت رايونى كۆرۈنۈشىدە قوللايدىغان قىممەت «يەككە» ۋە «سېتكا»" +msgstr "" +"قىسقىچە باياندىكى تاللانغان خىزمەت رايونىنىڭ كۆرۈنۈش ھالىتى. ئىشلىتىشكە " +"بولىدىغان قىممەتلەر «يەككە» ۋە «سېتكا»" #: ../data/org.gnome.shell.gschema.xml.in.h:23 msgid "" @@ -153,7 +182,11 @@ msgid "" "used ones (e.g. in launchers). While this data will be kept private, you may " "want to disable this for privacy reasons. Please note that doing so won't " "remove already saved data." -msgstr "چاپان (shell) ئادەتتىكى ئەھۋالدا كۆپ ئىشلىتىلىدىغان ئاكتىپ پروگراممىلار(مەسىلەن، ئىجرا قىلىنىۋاتقان)نى كۆزىتىدۇ. گەرچە بۇ سانلىق مەلۇماتلار مەخپىي ساقلانسىمۇ، شەخسىي سىر سەۋەبىدىن بۇنى چەكلىشىڭىز مۇمكىن. دىققەت بۇنداق قىلغاندا ئاللىبۇرۇن ساقلانغان سانلىق مەلۇماتلار چىقىرىۋېتىلمەيدۇ." +msgstr "" +"چاپان (shell) ئادەتتىكى ئەھۋالدا كۆپ ئىشلىتىلىدىغان ئاكتىپ پروگراممىلار" +"(مەسىلەن، ئىجرا قىلىنىۋاتقان)نى كۆزىتىدۇ. گەرچە بۇ سانلىق مەلۇماتلار مەخپىي " +"ساقلانسىمۇ، شەخسىي سىر سەۋەبىدىن بۇنى چەكلىشىڭىز مۇمكىن. دىققەت بۇنداق " +"قىلغاندا ئاللىبۇرۇن ساقلانغان سانلىق مەلۇماتلار چىقىرىۋېتىلمەيدۇ." #: ../data/org.gnome.shell.gschema.xml.in.h:24 msgid "" @@ -161,7 +194,11 @@ msgid "" "set to \"custom\". You can use conversion specifiers understood by strftime" "() to obtain a specific format. See the strftime() manual for more " "information." -msgstr "format (فورمات) كۇنۇپكىسى \"custom\" (ئىختىيارى) قىلىپ تەڭشەلسە بۇ كۇنۇپكا تاختا سائەت ئىشلىتىدىغان فورماتنى بەلگىلەيدۇ. سىز strftime()نىڭ فورمات بەلگىسىنى ئىشلىتىپ بەلگىلەنگەن فورماتقا ئېرىشەلەيسىز. تەپسىلاتىنى strftime() نىڭ قوللانمىسىدىن كۆرۈڭ." +msgstr "" +"format (فورمات) كۇنۇپكىسى \"custom\" (ئىختىيارى) قىلىپ تەڭشەلسە بۇ كۇنۇپكا " +"تاختا سائەت ئىشلىتىدىغان فورماتنى بەلگىلەيدۇ. سىز strftime()نىڭ فورمات " +"بەلگىسىنى ئىشلىتىپ بەلگىلەنگەن فورماتقا ئېرىشەلەيسىز. تەپسىلاتىنى strftime() " +"نىڭ قوللانمىسىدىن كۆرۈڭ." #: ../data/org.gnome.shell.gschema.xml.in.h:25 msgid "" @@ -171,11 +208,18 @@ msgid "" "to \"custom\", the clock will display time according to the format specified " "in the custom_format key. Note that if set to either \"unix\" or \"custom\", " "the show_date and show_seconds keys are ignored." -msgstr "بۇ كۇنۇپكا تاختا سائەت ئىشلەتكەن سائەت فورماتىنى بەلگىلىگەن. ئىشلەتكىلى بولىدىغان قىممىتى \"12-hour\" يەنى (12 سائەت)، \"24-hour\" يەنى (24 سائەت)، \"unix\" ۋە \"custom\" (ئىختىيارى). ئەگەر \"unix\" قىلىپ تەڭشەلسە سائەت ئۆزلۈكىدىن يېڭى ئېرا (يەنى، 1970-01-01) دىن ئۆتكەن سېكۇنتنى ئاساس قىلىدۇ. ئەگەر \"custom\" قىلىپ تەڭشەلسە سائەت custom_format كۇنۇپكا قىممىتىگە ئاساسەن ۋاقىتنى كۆرسىتىدۇ. ئەگەر \"unix\" ياكى \"custom\" قىلىپ تەڭشەلسە show_date ۋە show_seconds قىممىتىگە پەرۋا قىلمايدۇ" +msgstr "" +"بۇ كۇنۇپكا تاختا سائەت ئىشلەتكەن سائەت فورماتىنى بەلگىلىگەن. ئىشلەتكىلى " +"بولىدىغان قىممىتى \"12-hour\" يەنى (12 سائەت)، \"24-hour\" يەنى (24 سائەت)، " +"\"unix\" ۋە \"custom\" (ئىختىيارى). ئەگەر \"unix\" قىلىپ تەڭشەلسە سائەت " +"ئۆزلۈكىدىن يېڭى ئېرا (يەنى، 1970-01-01) دىن ئۆتكەن سېكۇنتنى ئاساس قىلىدۇ. " +"ئەگەر \"custom\" قىلىپ تەڭشەلسە سائەت custom_format كۇنۇپكا قىممىتىگە " +"ئاساسەن ۋاقىتنى كۆرسىتىدۇ. ئەگەر \"unix\" ياكى \"custom\" قىلىپ تەڭشەلسە " +"show_date ۋە show_seconds قىممىتىگە پەرۋا قىلمايدۇ" #: ../data/org.gnome.shell.gschema.xml.in.h:26 msgid "Uuids of extensions to disable" -msgstr "كېڭەيتىلمىنىڭ Uuid چەكلەندى" +msgstr "چەكلىنىدىغان كېڭەيتىلمىنىڭ Uuid سى" #: ../data/org.gnome.shell.gschema.xml.in.h:27 msgid "Whether to collect stats about applications usage" @@ -183,17 +227,18 @@ msgstr "پروگراممىنىڭ ئىشلىتىلىشى ھەققىدىكى ست #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:1 msgid "Clip the crosshairs at the center" -msgstr "" +msgstr "نىشانلىغۇچنى ئوتتۇرىغا توغرىلا" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:2 msgid "Color of the crosshairs" -msgstr "" +msgstr "نىشانلىغۇچنىڭ رەڭگى" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:3 msgid "" "Determines the length of the vertical and horizontal lines that make up the " "crosshairs." msgstr "" +"نىشانلىغۇچنى ھاسىل قىلىدىغان توغرا ۋە بوي سىزىقنىڭ ئۇزۇنلۇقى بەلگىلىنىدۇ." #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:4 msgid "" @@ -212,7 +257,7 @@ msgstr "" msgid "" "Determines the transparency of the crosshairs, from fully opaque to fully " "transparent." -msgstr "" +msgstr "نىشانلىغۇچنىڭ سۈزۈكلۈكى تولۇق سۈزۈكتىن تولۇق تۇتۇققىچە بەلگىلىنىدۇ." #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:6 msgid "" @@ -223,7 +268,7 @@ msgstr "" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:7 msgid "Enable lens mode" -msgstr "" +msgstr "لېنزا ھالىتىنى قوزغات" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:8 msgid "" @@ -240,19 +285,19 @@ msgstr "" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:10 msgid "Length of the crosshairs" -msgstr "" +msgstr "نىشانلىغۇچنىڭ ئېگىزلىكى" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:11 msgid "Magnification factor" -msgstr "" +msgstr "چوڭايتىش-كىچىكلىتىش نىسبىتى" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:12 msgid "Mouse Tracking Mode" -msgstr "" +msgstr "چاشقىنەك ئىزلاش ھالىتى" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:13 msgid "Opacity of the crosshairs" -msgstr "" +msgstr "نىشانلىغۇچنىڭ سۈزۈكلۈكى" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:14 msgid "Screen position" @@ -264,21 +309,21 @@ msgstr "دومىلىما چوڭايتقۇچ ئۈستەل ئۈستى گىرۋەك #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:16 msgid "Show or hide crosshairs" -msgstr "" +msgstr "نىشانلىغۇچنى كۆرسەت ياكى يوشۇر" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:17 msgid "Show or hide the magnifier" -msgstr "" +msgstr "لوپا ئەينەكنى كۆرسەت ياكى يوشۇر" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:18 msgid "Show or hide the magnifier and all of its zoom regions." -msgstr "" +msgstr "لوپا ئەينەك ۋە ئۇنىڭ ھەممە چوڭايتىش دائىرىسىنى كۆرسەت ياكى يوشۇرىدۇ." #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:19 msgid "" "The color of the the vertical and horizontal lines that make up the " "crosshairs." -msgstr "" +msgstr "نىشانلىغۇچنى ھاسىل قىلىدىغان توغرا ۋە بوي سىزىقنىڭ رەڭگى." #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:20 msgid "" @@ -290,11 +335,11 @@ msgstr "" msgid "" "The power of the magnification. A value of 1.0 means no magnification. A " "value of 2.0 doubles the size." -msgstr "" +msgstr "چوڭايتىش كۈچى. 1.0 چوڭايتمايدۇ، 2.0 چوڭلۇقىنى ھەسسىلەيدۇ دېگەن مەنىدە." #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:22 msgid "Thickness of the crosshairs" -msgstr "" +msgstr "نىشانلىغۇچنىڭ قېلىنلىقى" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:23 msgid "" @@ -304,7 +349,7 @@ msgstr "" #: ../data/org.gnome.accessibility.magnifier.gschema.xml.in.h:24 msgid "Width of the vertical and horizontal lines that make up the crosshairs." -msgstr "" +msgstr "نىشانلىغۇچنى ھاسىل قىلىدىغان توغرا ۋە بوي سىزىقنىڭ كەڭلىكى." #: ../data/clock-preferences.ui.h:1 msgid "Clock Format" @@ -316,11 +361,11 @@ msgstr "سائەت مايىللىقى" #: ../data/clock-preferences.ui.h:3 msgid "Panel Display" -msgstr "تاختا كۆرسەت" +msgstr "تاختا كۆرسىتىش" #: ../data/clock-preferences.ui.h:4 msgid "Show seco_nds" -msgstr "سېكۇنت كۆرسەت(_N)" +msgstr "سېكۇنتنى كۆرسەت(_N)" #: ../data/clock-preferences.ui.h:5 msgid "Show the _date" @@ -357,12 +402,12 @@ msgstr "يىغقۇچقا قوش" #: ../js/ui/appDisplay.js:829 msgid "Drag here to add favorites" -msgstr "بۇ جايغا سۆرەپ قىسقۇچقا قوش" +msgstr "بۇ جايغا سۆرەپ يىغقۇچقا قوش" #: ../js/ui/appFavorites.js:88 #, c-format msgid "%s has been added to your favorites." -msgstr "%s يىغقۇچىڭىزغا قوشۇلىدۇ." +msgstr "%s يىغقۇچىڭىزغا قوشۇلدى." #: ../js/ui/appFavorites.js:107 #, c-format @@ -375,7 +420,7 @@ msgstr "ئىزدە" #: ../js/ui/dash.js:473 msgid "Searching..." -msgstr "ئىزدەۋاتىدۇ…" +msgstr "ئىزدەۋاتىدۇ..." #: ../js/ui/dash.js:487 msgid "No matching results." @@ -421,72 +466,72 @@ msgstr "مەنبەنى كۆرسەت" #: ../js/ui/lookingGlass.js:626 msgid "Web Page" -msgstr "تور بەت" +msgstr "توربەت" #: ../js/ui/overview.js:160 msgid "Undo" msgstr "يېنىۋال" #. TODO - _quit() doesn't really work on apps in state STARTING yet -#: ../js/ui/panel.js:469 +#: ../js/ui/panel.js:470 #, c-format msgid "Quit %s" msgstr "%s چېكىن" -#: ../js/ui/panel.js:494 +#: ../js/ui/panel.js:495 msgid "Preferences" msgstr "مايىللىق" #. Translators: This is the time format with date used #. in 24-hour mode. -#: ../js/ui/panel.js:580 +#: ../js/ui/panel.js:581 msgid "%a %b %e, %R:%S" msgstr "%a %b %e، %R:%S" -#: ../js/ui/panel.js:581 +#: ../js/ui/panel.js:582 msgid "%a %b %e, %R" msgstr "%a %b %e، %R" #. Translators: This is the time format without date used #. in 24-hour mode. -#: ../js/ui/panel.js:585 +#: ../js/ui/panel.js:586 msgid "%a %R:%S" msgstr "%a %R:%S" -#: ../js/ui/panel.js:586 +#: ../js/ui/panel.js:587 msgid "%a %R" msgstr "%a %R" #. Translators: This is a time format with date used #. for AM/PM. -#: ../js/ui/panel.js:593 -msgid "%a %b %e, %l:%M:%S %p" -msgstr "%a %b %e, %l:%M:%S %p" - #: ../js/ui/panel.js:594 +msgid "%a %b %e, %l:%M:%S %p" +msgstr "%a %b %e، %l:%M:%S %p" + +#: ../js/ui/panel.js:595 msgid "%a %b %e, %l:%M %p" -msgstr "%a %b %e, %l:%M %p" +msgstr "%a %b %e، %l:%M %p" #. Translators: This is a time format without date used #. for AM/PM. -#: ../js/ui/panel.js:598 +#: ../js/ui/panel.js:599 msgid "%a %l:%M:%S %p" msgstr "%a %l:%M:%S %p" -#: ../js/ui/panel.js:599 +#: ../js/ui/panel.js:600 msgid "%a %l:%M %p" msgstr "%p%l:%M (%a)" #. Button on the left side of the panel. #. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". -#: ../js/ui/panel.js:744 +#: ../js/ui/panel.js:745 msgid "Activities" -msgstr "ھەرىكەتچان" +msgstr "پائالىيەتلەر" #: ../js/ui/placeDisplay.js:111 #, c-format msgid "Failed to unmount '%s'" -msgstr "%s نىڭ ئېگەرنى يېشىش مەغلۇپ بولدى." +msgstr "«%s» نى ئېگەرسىزلەش مەغلۇپ بولدى" #: ../js/ui/placeDisplay.js:114 msgid "Retry" @@ -494,7 +539,7 @@ msgstr "قايتا سىنا" #: ../js/ui/placeDisplay.js:159 msgid "Connect to..." -msgstr "باغلانماق..." +msgstr "باغلىنىش…" #. Translators: this MUST be either "toggle-switch-us" #. (for toggle switches containing the English words @@ -503,7 +548,7 @@ msgstr "باغلانماق..." #. simply result in invisible toggle switches. #: ../js/ui/popupMenu.js:33 msgid "toggle-switch-us" -msgstr "" +msgstr "toggle-switch-us" #: ../js/ui/runDialog.js:233 msgid "Please enter a command:" @@ -512,39 +557,45 @@ msgstr "بۇيرۇق كىرگۈزۈڭ:" #: ../js/ui/runDialog.js:378 #, c-format msgid "Execution of '%s' failed:" -msgstr "'%s' ئىجرا قىلالمىدى:" +msgstr "«%s» ئىجرا قىلىش مەغلۇپ بولدى:" #: ../js/ui/statusMenu.js:101 msgid "Available" -msgstr "ئىشلىتىلىشچان" +msgstr "بار" #: ../js/ui/statusMenu.js:106 msgid "Busy" msgstr "ئالدىراش" #: ../js/ui/statusMenu.js:114 -msgid "My Account..." -msgstr "" +#, fuzzy +#| msgid "My Account..." +msgid "My Account" +msgstr "ھېساباتىم…" #: ../js/ui/statusMenu.js:118 -msgid "System Settings..." -msgstr "سىستېما تەڭشەكلىرى" +#, fuzzy +#| msgid "System Settings..." +msgid "System Settings" +msgstr "سىستېما تەڭشەكلىرى..." #: ../js/ui/statusMenu.js:125 msgid "Lock Screen" -msgstr "ئېكراننى قۇلۇپلا" +msgstr "ئېكراننى قۇلۇپلاش" #: ../js/ui/statusMenu.js:129 msgid "Switch User" -msgstr "ئىشلەتكۈچى ئالماشتۇر" +msgstr "ئىشلەتكۈچى ئالماشتۇرۇش" #: ../js/ui/statusMenu.js:134 msgid "Log Out..." -msgstr "تىزىمدىن چىق…" +msgstr "تىزىمدىن چىقىش…" #: ../js/ui/statusMenu.js:141 -msgid "Suspend" -msgstr "ۋاقىتلىق توختىتىش" +#, fuzzy +#| msgid "Suspend" +msgid "Suspend..." +msgstr "توڭلات" #: ../js/ui/statusMenu.js:145 msgid "Shut Down..." @@ -564,23 +615,23 @@ msgstr "كۆرۈنمە ئاگاھلاندۇرۇش" #: ../js/ui/status/accessibility.js:97 msgid "Sticky Keys" -msgstr "چاپلاش كۇنۇپكىسى" +msgstr "Sticky Keys" #: ../js/ui/status/accessibility.js:100 msgid "Slow Keys" -msgstr "ئاستا كۇنۇپكا" +msgstr "Slow Keys" #: ../js/ui/status/accessibility.js:103 msgid "Bounce Keys" -msgstr "قاڭقىش كۇنۇپكىسى" +msgstr "Bounce Keys" #: ../js/ui/status/accessibility.js:106 msgid "Mouse Keys" -msgstr "چاشقىنەك كۇنۇپكا" +msgstr "Mouse Keys" #: ../js/ui/status/accessibility.js:110 msgid "Universal Access Settings" -msgstr "ھەممىباب زىيارەت تەڭشىكى" +msgstr "ئۇنىۋېرسال زىيارەت تەڭشىكى" #: ../js/ui/status/accessibility.js:163 msgid "High Contrast" @@ -602,7 +653,7 @@ msgstr "%s باشلاشنى تاماملىدى" #: ../js/ui/windowAttentionHandler.js:45 #, c-format msgid "'%s' is ready" -msgstr "'%s' تەييار بولدى" +msgstr "«%s» تەييار" #: ../js/ui/workspacesView.js:229 msgid "" @@ -611,7 +662,7 @@ msgstr "يېڭى خىزمەت رايونى قوشالمايدۇ چۈنكى ئە #: ../js/ui/workspacesView.js:246 msgid "Can't remove the first workspace." -msgstr "بىرىنچى خىزمەت رايونىنى چىقىرىۋېتەلمەيدۇ." +msgstr "بىرىنچى خىزمەت رايونىنى چىقىرىۋەتكىلى بولمايدۇ." #. translators: #. * The number of sound outputs on a particular device @@ -619,7 +670,7 @@ msgstr "بىرىنچى خىزمەت رايونىنى چىقىرىۋېتەلمە #, c-format msgid "%u Output" msgid_plural "%u Outputs" -msgstr[0] "%u چىقار" +msgstr[0] "%u چىقىرىلما" #. translators: #. * The number of sound inputs on a particular device @@ -627,51 +678,55 @@ msgstr[0] "%u چىقار" #, c-format msgid "%u Input" msgid_plural "%u Inputs" -msgstr[0] "%u كىرگۈز" +msgstr[0] "%u كىرگۈزۈلمە" #: ../src/gvc/gvc-mixer-control.c:1402 msgid "System Sounds" msgstr "سىستېما ئاۋازى" -#: ../src/shell-global.c:1219 +#: ../src/shell-app-system.c:1012 +msgid "Unknown" +msgstr "نامەلۇم" + +#: ../src/shell-global.c:1163 msgid "Less than a minute ago" msgstr "بىر مىنۇتتىنمۇ ئىلگىرى" -#: ../src/shell-global.c:1223 +#: ../src/shell-global.c:1167 #, c-format msgid "%d minute ago" msgid_plural "%d minutes ago" msgstr[0] "%d مىنۇت ئىلگىرى" -#: ../src/shell-global.c:1228 +#: ../src/shell-global.c:1172 #, c-format msgid "%d hour ago" msgid_plural "%d hours ago" msgstr[0] "%d سائەت ئىلگىرى" -#: ../src/shell-global.c:1233 +#: ../src/shell-global.c:1177 #, c-format msgid "%d day ago" msgid_plural "%d days ago" msgstr[0] "%d كۈن ئىلگىرى" -#: ../src/shell-global.c:1238 +#: ../src/shell-global.c:1182 #, c-format msgid "%d week ago" msgid_plural "%d weeks ago" msgstr[0] "%d ھەپتە ئىلگىرى" -#: ../src/shell-uri-util.c:89 +#: ../src/shell-util.c:89 msgid "Home Folder" -msgstr "باش مۇندەرىجە" +msgstr "ماكان مۇندەرىجە" #. Translators: this is the same string as the one found in #. * nautilus -#: ../src/shell-uri-util.c:104 +#: ../src/shell-util.c:104 msgid "File System" msgstr "ھۆججەت سىستېمىسى" -#: ../src/shell-uri-util.c:250 +#: ../src/shell-util.c:250 msgid "Search" msgstr "ئىزدە" @@ -680,10 +735,10 @@ msgstr "ئىزدە" #. * example, "Trash: some-directory". It means that the #. * directory called "some-directory" is in the trash. #. -#: ../src/shell-uri-util.c:300 +#: ../src/shell-util.c:300 #, c-format msgid "%1$s: %2$s" -msgstr "%1$s: %2$s" +msgstr "%1$s: %2$s " #~ msgid "Invisible" #~ msgstr "يوشۇرۇن" From dc24252e824af1721a938b05628470b77849ae75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Di=C3=A9guez?= Date: Sat, 27 Nov 2010 01:43:13 +0100 Subject: [PATCH 24/61] Updated Galician translations --- po/gl.po | 94 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/po/gl.po b/po/gl.po index e0c8bf60a..1a9e3be5c 100644 --- a/po/gl.po +++ b/po/gl.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell master\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-11-03 00:26+0100\n" -"PO-Revision-Date: 2010-11-03 00:28+0100\n" +"POT-Creation-Date: 2010-11-27 01:42+0100\n" +"PO-Revision-Date: 2010-11-27 01:43+0100\n" "Last-Translator: Fran Diéguez \n" "Language-Team: Galician \n" "Language: gl\n" @@ -459,7 +459,7 @@ msgstr "Non hai resultados que coincidan." #. **** Places **** #. Translators: This is in the sense of locations for documents, #. network locations, etc. -#: ../js/ui/dash.js:797 ../js/ui/placeDisplay.js:554 +#: ../js/ui/dash.js:797 ../js/ui/placeDisplay.js:558 msgid "PLACES & DEVICES" msgstr "LUGARES E DISPOSITIVOS" @@ -503,58 +503,58 @@ msgid "Undo" msgstr "Desfacer" #. TODO - _quit() doesn't really work on apps in state STARTING yet -#: ../js/ui/panel.js:469 +#: ../js/ui/panel.js:470 #, c-format msgid "Quit %s" msgstr "Saír de %s" -#: ../js/ui/panel.js:494 +#: ../js/ui/panel.js:495 msgid "Preferences" msgstr "Preferencias" #. Translators: This is the time format with date used #. in 24-hour mode. -#: ../js/ui/panel.js:580 +#: ../js/ui/panel.js:581 msgid "%a %b %e, %R:%S" msgstr "%a %e de %b, %R:%S" -#: ../js/ui/panel.js:581 +#: ../js/ui/panel.js:582 msgid "%a %b %e, %R" msgstr "%a %e de %b, %R" #. Translators: This is the time format without date used #. in 24-hour mode. -#: ../js/ui/panel.js:585 +#: ../js/ui/panel.js:586 msgid "%a %R:%S" msgstr "%a %R:%S" -#: ../js/ui/panel.js:586 +#: ../js/ui/panel.js:587 msgid "%a %R" msgstr "%a %R" #. Translators: This is a time format with date used #. for AM/PM. -#: ../js/ui/panel.js:593 +#: ../js/ui/panel.js:594 msgid "%a %b %e, %l:%M:%S %p" msgstr "%a %e de %b, %H:%M:%S" -#: ../js/ui/panel.js:594 +#: ../js/ui/panel.js:595 msgid "%a %b %e, %l:%M %p" msgstr "%a %e de %b, %H:%M" #. Translators: This is a time format without date used #. for AM/PM. -#: ../js/ui/panel.js:598 +#: ../js/ui/panel.js:599 msgid "%a %l:%M:%S %p" msgstr "%a %H:%M:%S" -#: ../js/ui/panel.js:599 +#: ../js/ui/panel.js:600 msgid "%a %l:%M %p" msgstr "%a %l:%M %p" #. Button on the left side of the panel. #. Translators: If there is no suitable word for "Activities" in your language, you can use the word for "Overview". -#: ../js/ui/panel.js:744 +#: ../js/ui/panel.js:745 msgid "Activities" msgstr "Actividades" @@ -597,39 +597,31 @@ msgstr "Dispoñíbel" msgid "Busy" msgstr "Ocupado" -#: ../js/ui/statusMenu.js:111 -msgid "Invisible" -msgstr "Invisíbel" +#: ../js/ui/statusMenu.js:114 +msgid "My Account" +msgstr "A miña conta" -#: ../js/ui/statusMenu.js:119 -msgid "My Account..." -msgstr "A miña conta..." +#: ../js/ui/statusMenu.js:118 +msgid "System Settings" +msgstr "Configuracións do sistema" -#: ../js/ui/statusMenu.js:123 -msgid "System Settings..." -msgstr "Configuracións do sistema..." - -#: ../js/ui/statusMenu.js:130 +#: ../js/ui/statusMenu.js:125 msgid "Lock Screen" msgstr "Bloquear pantalla" -#: ../js/ui/statusMenu.js:134 +#: ../js/ui/statusMenu.js:129 msgid "Switch User" msgstr "Cambiar de usuario" -#: ../js/ui/statusMenu.js:139 +#: ../js/ui/statusMenu.js:134 msgid "Log Out..." msgstr "Saír da sesión..." -#: ../js/ui/statusMenu.js:146 -msgid "Suspend" -msgstr "Suspender" +#: ../js/ui/statusMenu.js:141 +msgid "Suspend..." +msgstr "Suspender..." -#: ../js/ui/statusMenu.js:150 -msgid "Restart..." -msgstr "Reiniciar..." - -#: ../js/ui/statusMenu.js:154 +#: ../js/ui/statusMenu.js:145 msgid "Shut Down..." msgstr "Apagar..." @@ -669,11 +661,11 @@ msgstr "Configuracións de acceso universal" msgid "High Contrast" msgstr "Alto contraste" -#: ../js/ui/status/accessibility.js:202 +#: ../js/ui/status/accessibility.js:205 msgid "Large Text" msgstr "Texto máis grande" -#: ../js/ui/status/accessibility.js:223 +#: ../js/ui/status/accessibility.js:224 msgid "Zoom" msgstr "Ampliación" @@ -720,49 +712,53 @@ msgstr[1] "%u entradas" msgid "System Sounds" msgstr "Sons do sistema" -#: ../src/shell-global.c:1219 +#: ../src/shell-app-system.c:1012 +msgid "Unknown" +msgstr "Descoñecido" + +#: ../src/shell-global.c:1163 msgid "Less than a minute ago" msgstr "Hai menos dun minuto" -#: ../src/shell-global.c:1223 +#: ../src/shell-global.c:1167 #, c-format msgid "%d minute ago" msgid_plural "%d minutes ago" msgstr[0] "hai %d minuto" msgstr[1] "hai %d minutos" -#: ../src/shell-global.c:1228 +#: ../src/shell-global.c:1172 #, c-format msgid "%d hour ago" msgid_plural "%d hours ago" msgstr[0] "hai %d hora" msgstr[1] "hai %d horas" -#: ../src/shell-global.c:1233 +#: ../src/shell-global.c:1177 #, c-format msgid "%d day ago" msgid_plural "%d days ago" msgstr[0] "hai %d día" msgstr[1] "hai %d días" -#: ../src/shell-global.c:1238 +#: ../src/shell-global.c:1182 #, c-format msgid "%d week ago" msgid_plural "%d weeks ago" msgstr[0] "hai %d semana" msgstr[1] "hai %d semanas" -#: ../src/shell-uri-util.c:89 +#: ../src/shell-util.c:89 msgid "Home Folder" msgstr "Cartafol persoal" #. Translators: this is the same string as the one found in #. * nautilus -#: ../src/shell-uri-util.c:104 +#: ../src/shell-util.c:104 msgid "File System" msgstr "Sistema de ficheiros" -#: ../src/shell-uri-util.c:250 +#: ../src/shell-util.c:250 msgid "Search" msgstr "Buscar" @@ -771,11 +767,17 @@ msgstr "Buscar" #. * example, "Trash: some-directory". It means that the #. * directory called "some-directory" is in the trash. #. -#: ../src/shell-uri-util.c:300 +#: ../src/shell-util.c:300 #, c-format msgid "%1$s: %2$s" msgstr "%1$s: %2$s" +#~ msgid "Invisible" +#~ msgstr "Invisíbel" + +#~ msgid "Restart..." +#~ msgstr "Reiniciar..." + #~ msgid "System Preferences..." #~ msgstr "Preferencias do sistema..." From 8d47a150dfe473ddce68a2f7af1d531d322a9ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Sun, 11 Jul 2010 14:41:17 +0200 Subject: [PATCH 25/61] linear-view: Remove the scrollbar The scrollbar is the main culprit for cluttered controls in the linear view - all its functionality is already provided by the workspace indicators, so it is save to remove the scrollbar in order to clean up the interface. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 12 ---- js/ui/workspacesView.js | 119 +++++++++---------------------------- 2 files changed, 27 insertions(+), 104 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 58aa1febb..716050775 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -322,10 +322,6 @@ StTooltip StLabel { spacing: 15px; } -.workspaces-bar { - height: 48px; -} - .workspaces-bar { spacing: 5px; } @@ -391,14 +387,6 @@ StTooltip StLabel { background-image: url("mosaic-view-active.svg"); } -#SwitchScroll { - height: 14px; -} - -#SwitchScroll #hhandle { - border-radius: 7px; -} - /* Dash */ #dash { diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 91a15c9ca..5c958829b 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -691,14 +691,23 @@ SingleView.prototype = { this.actor.set_clip(x, y, width, height); this._activeWorkspaceX = 0; // x offset of active ws while dragging this._activeWorkspaceY = 0; // y offset of active ws while dragging - this._scroll = null; this._lostWorkspaces = []; this._animating = false; // tweening - this._scrolling = false; // dragging scroll bar or desktop - this._animatingScroll = false; // programatically move the scroll bar + this._scrolling = false; // dragging desktop + this._animatingScroll = false; // programatically updating the adjustment this._inDrag = false; // dragging a window this._lastMotionTime = -1; // used to track "stopping" while dragging workspaces + let active = global.screen.get_active_workspace_index(); + this._scrollAdjustment = new St.Adjustment({ value: active, + lower: 0, + page_increment: 1, + page_size: 1, + step_increment: 0, + upper: this._workspaces.length }); + this._scrollAdjustment.connect('notify::value', + Lang.bind(this, this._onScroll)); + this._dragIndex = -1; this._buttonPressId = 0; @@ -800,7 +809,7 @@ SingleView.prototype = { this._computeWorkspacePositions(); this._updateWorkspaceActors(showAnimation); - this._scrollScrollBarToIndex(active, showAnimation); + this._updateScrollAdjustment(active, showAnimation); }, // _setWorkspaceDraggable: @@ -875,7 +884,7 @@ SingleView.prototype = { // If the user has moved more than half a workspace, we want to "settle" // to the new workspace even if the user stops dragging rather "throws" // by releasing during the drag. - let noStop = Math.abs(activate - this._scroll.adjustment.value) > 0.5; + let noStop = Math.abs(activate - this._scrollAdjustment.value) > 0.5; // We detect if the user is stopped by comparing the timestamp of the button // release with the timestamp of the last motion. Experimentally, a difference @@ -906,7 +915,7 @@ SingleView.prototype = { let dx = this._dragX - stageX; let primary = global.get_primary_monitor(); - this._scroll.adjustment.value += (dx / primary.width); + this._scrollAdjustment.value += (dx / primary.width); this._dragX = stageX; this._lastMotionTime = event.get_time(); @@ -1078,14 +1087,14 @@ SingleView.prototype = { this._updateWorkspaceActors(false); }, - _scrollScrollBarToIndex: function(index, showAnimation) { - if (!this._scroll || this._scrolling) + _updateScrollAdjustment: function(index, showAnimation) { + if (this._scrolling) return; this._animatingScroll = true; if (showAnimation) { - Tweener.addTween(this._scroll.adjustment, { + Tweener.addTween(this._scrollAdjustment, { value: index, time: WORKSPACE_SWITCH_TIME, transition: 'easeOutQuad', @@ -1095,7 +1104,7 @@ SingleView.prototype = { }) }); } else { - this._scroll.adjustment.value = index; + this._scrollAdjustment.value = index; this._animatingScroll = false; } }, @@ -1106,12 +1115,11 @@ SingleView.prototype = { for (let l = 0; l < lostWorkspaces.length; l++) lostWorkspaces[l].disconnectAll(); - if (this._scroll != null) - Tweener.addTween(this._scroll.adjustment, - { upper: newNumWorkspaces, - time: WORKSPACE_SWITCH_TIME, - transition: 'easeOutQuad' - }); + Tweener.addTween(this._scrollAdjustment, + { upper: newNumWorkspaces, + time: WORKSPACE_SWITCH_TIME, + transition: 'easeOutQuad' + }); if (newNumWorkspaces > oldNumWorkspaces) { for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) { @@ -1129,12 +1137,9 @@ SingleView.prototype = { } this._scrollToActive(true); - this._updatePanelVisibility(); }, _activeWorkspaceChanged: function(wm, from, to, direction) { - this._updatePanelVisibility(); - if (this._scrolling) return; @@ -1169,7 +1174,7 @@ SingleView.prototype = { }, _dragBegin: function() { - if (!this._scroll || this._scrolling) + if (this._scrolling) return; this._inDrag = true; @@ -1273,8 +1278,7 @@ SingleView.prototype = { this._workspaces[i].setReservedSlot(null); }, - // handle changes to the scroll bar's adjustment: - // sync the workspaces' positions to the position of the scroll bar handle + // sync the workspaces' positions to the value of the scroll adjustment // and change the active workspace if appropriate _onScroll: function(adj) { if (this._animatingScroll) @@ -1285,20 +1289,7 @@ SingleView.prototype = { if (active != current) { let metaWorkspace = this._workspaces[current].metaWorkspace; - - if (!this._scrolling) { - // This here is a little tricky - we get here when StScrollBar - // animates paging; we switch the active workspace, but - // leave out any extra animation (just like we would do when - // the handle was dragged) - // If StScrollBar emitted scroll-start before and scroll-stop - // after the animation, this would not be necessary - this._scrolling = true; - metaWorkspace.activate(global.get_current_time()); - this._scrolling = false; - } else { - metaWorkspace.activate(global.get_current_time()); - } + metaWorkspace.activate(global.get_current_time()); } let last = this._workspaces.length - 1; @@ -1306,8 +1297,6 @@ SingleView.prototype = { let lastWorkspaceX = this._workspaces[last].actor.x; let workspacesWidth = lastWorkspaceX - firstWorkspaceX; - // The scrollbar is hidden when there is only one workspace, so - // adj.upper should at least be 2 - but better be safe than sorry if (adj.upper == 1) return; @@ -1321,12 +1310,6 @@ SingleView.prototype = { this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1; this._workspaces[i].actor.x += dx; } - - if (!this._scrolling && active == adj.value) { - // Again, work around the paging in StScrollBar: simulate - // the effect of scroll-stop - this._updateWorkspaceActors(false); - } }, // handle scroll wheel events: @@ -1353,37 +1336,6 @@ SingleView.prototype = { pack_start: true, vertical: true }); - let active = global.screen.get_active_workspace_index(); - let adj = new St.Adjustment({ value: active, - lower: 0, - page_increment: 1, - page_size: 1, - step_increment: 0, - upper: this._workspaces.length }); - this._scroll = new St.ScrollBar({ adjustment: adj, - vertical: false, - name: 'SwitchScroll' }); - - // we have set adj.step_increment to 0, so all scroll wheel events - // are processed with this handler - this allows us to animate the - // workspace switch - this._scroll.connect('scroll-event', - Lang.bind(this, this._onScrollEvent)); - - this._scroll.adjustment.connect('notify::value', - Lang.bind(this, this._onScroll)); - - - this._scroll.connect('scroll-start', Lang.bind(this, - function() { - this._scrolling = true; - })); - this._scroll.connect('scroll-stop', Lang.bind(this, - function() { - this._scrolling = false; - this._scrollToActive(true); - })); - let indicator = new WorkspaceIndicator(Lang.bind(this, function(i) { if (this._workspaces[i] != undefined) this._workspaces[i].metaWorkspace.activate(global.get_current_time()); @@ -1398,26 +1350,9 @@ SingleView.prototype = { }), Lang.bind(this, this._onScrollEvent)); actor.add(indicator.actor, { expand: true, x_fill: true, y_fill: true }); - actor.add(this._scroll, { expand: true, - x_fill: true, - y_fill: false, - y_align: St.Align.START }); - - this._updatePanelVisibility(); - return actor; }, - _updatePanelVisibility: function() { - let showSwitches = (global.screen.n_workspaces > 1); - if (this._scroll != null) { - Tweener.addTween(this._scroll, - { opacity: showSwitches ? 255 : 0, - time: WORKSPACE_SWITCH_TIME, - transition: 'easeOutQuad' }); - } - }, - addWorkspace: function() { let ws = GenericWorkspacesView.prototype.addWorkspace.call(this); if (ws != null) From 1ea488bb3db9fe4eb030025676c0b2da7bbeb917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 22 Jul 2010 17:49:43 +0200 Subject: [PATCH 26/61] overview: Replace InfoBar with message tray notifications The layout of recent mockups occupies the space previously reserved for the info bar with the view selector. As the bar's purpose is mainly to provide the user with feedback, it makes sense to use the existing message tray facility instead of moving the bar elsewhere. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 17 ------ js/ui/appFavorites.js | 4 +- js/ui/overview.js | 114 ++++++++++++++++++------------------- js/ui/placeDisplay.js | 2 +- js/ui/workspacesView.js | 4 +- 5 files changed, 59 insertions(+), 82 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 716050775..f5cf3d2f8 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -261,23 +261,6 @@ StTooltip StLabel { background-color: #111; } -.info-bar { - color: #fff; - font-size: 14px; - spacing: 20px; -} - -.info-bar-link-button { - background-color: #2d2d2d; - padding: 2px 14px; - border-radius: 10px; - border: 1px solid #181818; -} - -.info-bar-link-button:hover { - border: 1px solid #666666; -} - .new-workspace-area { border: 2px solid rgba(255, 255, 255, 0.8); border-radius: 10px; diff --git a/js/ui/appFavorites.js b/js/ui/appFavorites.js index 2a20123da..9c050ee4a 100644 --- a/js/ui/appFavorites.js +++ b/js/ui/appFavorites.js @@ -85,7 +85,7 @@ AppFavorites.prototype = { let app = Shell.AppSystem.get_default().get_app(appId); - Main.overview.infoBar.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () { + Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () { this._removeFavorite(appId); })); }, @@ -104,7 +104,7 @@ AppFavorites.prototype = { if (!this._removeFavorite(appId)) return; - Main.overview.infoBar.setMessage(_("%s has been removed from your favorites.").format(app.get_name()), + Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()), Lang.bind(this, function () { this._addFavorite(appId); })); diff --git a/js/ui/overview.js b/js/ui/overview.js index abf595352..bec99c707 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -11,6 +11,7 @@ const _ = Gettext.gettext; const GenericDisplay = imports.ui.genericDisplay; const Lightbox = imports.ui.lightbox; const Main = imports.ui.main; +const MessageTray = imports.ui.messageTray; const Panel = imports.ui.panel; const Dash = imports.ui.dash; const Tweener = imports.ui.tweener; @@ -71,41 +72,45 @@ const SHADOW_WIDTH = 6; const NUMBER_OF_SECTIONS_IN_SEARCH = 2; -const INFO_BAR_HIDE_TIMEOUT = 10; +const SHELL_INFO_HIDE_TIMEOUT = 10; let wideScreen = false; let displayGridColumnWidth = null; let displayGridRowHeight = null; -function InfoBar() { +function Source() { this._init(); } -InfoBar.prototype = { +Source.prototype = { + __proto__: MessageTray.Source.prototype, + _init: function() { - this.actor = new St.Bin({ style_class: 'info-bar-panel', - x_fill: true, - y_fill: false }); - this._label = new St.Label(); - this._undo = new St.Button({ style_class: 'info-bar-link-button' }); + MessageTray.Source.prototype._init.call(this, + "System Information"); + this._setSummaryIcon(this.createNotificationIcon()); + }, - let bin = new St.Bin({ x_fill: false, - y_fill: false, - x_align: St.Align.MIDDLE, - y_align: St.Align.MIDDLE }); - this.actor.set_child(bin); + createNotificationIcon: function() { + return new St.Icon({ icon_name: 'info', + icon_type: St.IconType.FULLCOLOR, + icon_size: this.ICON_SIZE }); + }, - let box = new St.BoxLayout({ style_class: 'info-bar' }); - bin.set_child(box); + _notificationClicked: function() { + this.destroy(); + } +} + +function ShellInfo() { + this._init(); +} + +ShellInfo.prototype = { + _init: function() { + this._source = null; this._timeoutId = 0; - - box.add(this._label, {'y-fill' : false, 'y-align' : St.Align.MIDDLE}); - box.add(this._undo); - - this.actor.set_opacity(0); - this._undoCallback = null; - this._undo.connect('clicked', Lang.bind(this, this._onUndoClicked)); }, _onUndoClicked: function() { @@ -114,27 +119,16 @@ InfoBar.prototype = { if (this._undoCallback) this._undoCallback(); - this.actor.set_opacity(0); this._undoCallback = null; - }, - _hideDone: function() { - this._undoCallback = null; - }, - - _hide: function() { - Tweener.addTween(this.actor, - { opacity: 0, - transition: 'easeOutQuad', - time: ANIMATION_TIME, - onComplete: this._hideDone, - onCompleteScope: this - }); + if (this._source) + this._source.destroy(); }, _onTimeout: function() { this._timeoutId = 0; - this._hide(); + if (this._source) + this._source.destroy(); return false; }, @@ -142,28 +136,33 @@ InfoBar.prototype = { if (this._timeoutId) Mainloop.source_remove(this._timeoutId); - this._timeout = false; + this._timeoutId = Mainloop.timeout_add_seconds(SHELL_INFO_HIDE_TIMEOUT, + Lang.bind(this, this._onTimeout)); - this._label.text = text; + if (this._source == null) { + this._source = new Source(); + this._source.connect('destroy', Lang.bind(this, + function() { + this._source = null; + })); + Main.messageTray.add(this._source); + } - Tweener.addTween(this.actor, - { opacity: 255, - transition: 'easeOutQuad', - time: ANIMATION_TIME - }); - - this._timeoutId = Mainloop.timeout_add_seconds(INFO_BAR_HIDE_TIMEOUT, Lang.bind(this, this._onTimeout)); - - if (undoLabel) - this._undo.label = undoLabel; + let notification = this._source.notification; + if (notification == null) + notification = new MessageTray.Notification(this._source, text, null); else - this._undo.label = _("Undo"); + notification.update(text, null, { clear: true }); this._undoCallback = undoCallback; - if (undoCallback) - this._undo.show(); - else - this._undo.hide(); + if (undoCallback) { + notification.addButton('system-undo', + undoLabel ? undoLabel : _("Undo")); + notification.connect('action-invoked', + Lang.bind(this, this._onUndoClicked)); + } + + this._source.notify(notification); } }; @@ -183,8 +182,7 @@ Overview.prototype = { } })); - this.infoBar = new InfoBar(); - this._group.add_actor(this.infoBar.actor); + this.shellInfo = new ShellInfo(); this._workspacesManager = null; this._lightbox = null; @@ -311,10 +309,6 @@ Overview.prototype = { this._dash.sectionArea.height = this._workspacesHeight; this._dash.searchResults.actor.height = this._workspacesHeight; - this.infoBar.actor.set_position(displayGridColumnWidth, Panel.PANEL_HEIGHT); - this.infoBar.actor.set_size(primary.width - displayGridColumnWidth, this._workspacesY - Panel.PANEL_HEIGHT); - this.infoBar.actor.raise_top(); - // place the 'Add Workspace' button in the bottom row of the grid this._workspacesBarX = this._workspacesX; this._workspacesBarWidth = this._workspacesWidth; diff --git a/js/ui/placeDisplay.js b/js/ui/placeDisplay.js index 88d25dabb..fb96149c9 100644 --- a/js/ui/placeDisplay.js +++ b/js/ui/placeDisplay.js @@ -109,7 +109,7 @@ PlaceDeviceInfo.prototype = { this._mount.unmount_finish(res); } catch (e) { let message = _("Failed to unmount '%s'").format(o.get_name()); - Main.overview.infoBar.setMessage(message, + Main.overview.shellInfo.setMessage(message, Lang.bind(this, this.remove), _("Retry")); } diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 5c958829b..34945d776 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -226,7 +226,7 @@ GenericWorkspacesView.prototype = { addWorkspace: function() { if (!this.canAddWorkspace()) { - Main.overview.infoBar.setMessage(_("Can't add a new workspace because maximum workspaces limit has been reached.")); + Main.overview.shellInfo.setMessage(_("Can't add a new workspace because maximum workspaces limit has been reached.")); return null; } @@ -243,7 +243,7 @@ GenericWorkspacesView.prototype = { removeWorkspace: function() { if (!this.canRemoveWorkspace()) { - Main.overview.infoBar.setMessage(_("Can't remove the first workspace.")); + Main.overview.shellInfo.setMessage(_("Can't remove the first workspace.")); return; } let index = this._getWorkspaceIndexToRemove(); From f24e567dc43bd02c897937a0a32a27654d465650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 15 Jul 2010 16:21:32 +0200 Subject: [PATCH 27/61] overview: Do not zoom the desktop background While scaling the desktop background with the window previews represents workspaces quite intuitively, the approach is not without problems. As window previews in the overview behave quite differently to "real" windows, the representation of workspaces as miniature versions of "real" workspaces is flawed. The scaling also makes the transitions to and from the overview much more visually expensive, without adding much benefit. Leaving the background in place provides more visual stability to the transitions and emphasizes the distinctive behavior of elements in the overview. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 4 - js/ui/overview.js | 62 ++++++++++-- js/ui/workspace.js | 188 ++++++++++--------------------------- js/ui/workspacesView.js | 10 +- 4 files changed, 112 insertions(+), 152 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index f5cf3d2f8..fa8c95936 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -257,10 +257,6 @@ StTooltip StLabel { /* Overview */ -.overview { - background-color: #111; -} - .new-workspace-area { border: 2px solid rgba(255, 255, 255, 0.8); border-radius: 10px; diff --git a/js/ui/overview.js b/js/ui/overview.js index bec99c707..5e2385f38 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -1,6 +1,7 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ const Clutter = imports.gi.Clutter; +const Meta = imports.gi.Meta; const Mainloop = imports.mainloop; const Signals = imports.signals; const Lang = imports.lang; @@ -172,7 +173,16 @@ function Overview() { Overview.prototype = { _init : function() { - this._group = new St.Group({ style_class: 'overview' }); + // The actual global.background_actor is inside global.window_group, + // which is hidden when displaying the overview, so we display a clone. + this._background = new Clutter.Clone({ source: global.background_actor }); + this._background.hide(); + global.overlay_group.add_actor(this._background); + + this._desktopFade = new St.Bin(); + global.overlay_group.add_actor(this._desktopFade); + + this._group = new St.Group({ name: 'overview' }); this._group._delegate = this; this._group.connect('destroy', Lang.bind(this, function() { @@ -209,10 +219,6 @@ Overview.prototype = { reactive: true }); this._group.add_actor(this._transparentBackground); - // Background color for the Overview - this._backOver = new St.Label(); - this._group.add_actor(this._backOver); - this._group.hide(); global.overlay_group.add_actor(this._group); @@ -238,6 +244,20 @@ Overview.prototype = { this.workspaces = null; }, + _getDesktopClone: function() { + let windows = global.get_window_actors().filter(function(w) { + return w.meta_window.get_window_type() == Meta.WindowType.DESKTOP; + }); + if (windows.length == 0) + return null; + + let clone = new Clutter.Clone({ source: windows[0].get_texture() }); + clone.source.connect('destroy', Lang.bind(this, function() { + clone.destroy(); + })); + return clone; + }, + _onViewChanged: function() { if (!this.visible) return; @@ -314,11 +334,6 @@ Overview.prototype = { this._workspacesBarWidth = this._workspacesWidth; this._workspacesBarY = primary.height - displayGridRowHeight; - // The parent (this._group) is positioned at the top left of the primary monitor - // while this._backOver occupies the entire screen. - this._backOver.set_position(- primary.x, - primary.y); - this._backOver.set_size(global.screen_width, global.screen_height); - this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING, this._workspacesY); // Dynamic width @@ -456,6 +471,19 @@ Overview.prototype = { this._group.add_actor(this._workspacesBar); this._workspacesBar.raise(this.workspaces.actor); + if (!this._desktopFade.child) + this._desktopFade.child = this._getDesktopClone(); + + if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) { + this._desktopFade.opacity = 255; + this._desktopFade.show(); + Tweener.addTween(this._desktopFade, + { opacity: 0, + time: ANIMATION_TIME, + transition: 'easeOutQuad' + }); + } + // All the the actors in the window group are completely obscured, // hiding the group holding them while the Overview is displayed greatly // increases performance of the Overview especially when there are many @@ -465,6 +493,7 @@ Overview.prototype = { // clones of them, this would obviously no longer be necessary. global.window_group.hide(); this._group.show(); + this._background.show(); // Create a zoom out effect. First scale the Overview group up and // position it so that the active workspace fills up the whole screen, @@ -502,6 +531,16 @@ Overview.prototype = { this.animationInProgress = true; this._hideInProgress = true; + + if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) { + this._desktopFade.opacity = 0; + this._desktopFade.show(); + Tweener.addTween(this._desktopFade, + { opacity: 255, + time: ANIMATION_TIME, + transition: 'easeOutQuad' }); + } + if (this._activeDisplayPane != null) this._activeDisplayPane.close(); this.workspaces.hide(); @@ -559,6 +598,7 @@ Overview.prototype = { return; this.animationInProgress = false; + this._desktopFade.hide(); this._coverPane.lower_bottom(); this.emit('shown'); @@ -576,6 +616,8 @@ Overview.prototype = { this._workspacesManager = null; this._dash.hide(); + this._desktopFade.hide(); + this._background.hide(); this._group.hide(); this.visible = false; diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 3e80d5ab5..ec7e24a23 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -140,7 +140,10 @@ WindowClone.prototype = { if (this.inDrag || this._zooming) // We'll fix up the stack after the drag/zooming return; - this.actor.raise(this._stackAbove); + if (this._stackAbove == null) + this.actor.lower_bottom(); + else + this.actor.raise(this._stackAbove); }, destroy: function () { @@ -247,11 +250,13 @@ WindowClone.prototype = { this.emit('zoom-end'); this.actor.reparent(this._origParent); + if (this._stackAbove == null) + this.actor.lower_bottom(); // If the workspace has been destroyed while we were reparented to // the stage, _stackAbove will be unparented and we can't raise our // actor above it - as we are bound to be destroyed anyway in that // case, we can skip that step - if (this._stackAbove && this._stackAbove.get_parent()) + else if (this._stackAbove.get_parent()) this.actor.raise(this._stackAbove); [this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition(); @@ -283,82 +288,20 @@ WindowClone.prototype = { // We may not have a parent if DnD completed successfully, in // which case our clone will shortly be destroyed and replaced // with a new one on the target workspace. - if (this.actor.get_parent() != null) - this.actor.raise(this._stackAbove); + if (this.actor.get_parent() != null) { + if (this._stackAbove == null) + this.actor.lower_bottom(); + else + this.actor.raise(this._stackAbove); + } + this.emit('drag-end'); } }; - Signals.addSignalMethods(WindowClone.prototype); -function DesktopClone(window) { - this._init(window); -} - -DesktopClone.prototype = { - _init : function(window) { - this.actor = new Clutter.Group({ reactive: true }); - - let background = new Clutter.Clone({ source: global.background_actor }); - this.actor.add_actor(background); - - if (window) { - this._desktop = new Clutter.Clone({ source: window.get_texture() }); - this.actor.add_actor(this._desktop); - this._desktop.hide(); - } else { - this._desktop = null; - } - - this.actor.connect('button-release-event', - Lang.bind(this, this._onButtonRelease)); - }, - - zoomFromOverview: function(fadeInIcons) { - if (this._desktop == null) - return; - - if (fadeInIcons) { - this._desktop.opacity = 0; - this._desktop.show(); - Tweener.addTween(this._desktop, - { opacity: 255, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad' }); - } - }, - - zoomToOverview: function(fadeOutIcons) { - if (this._desktop == null) - return; - - if (fadeOutIcons) { - this._desktop.opacity = 255; - this._desktop.show(); - Tweener.addTween(this._desktop, - { opacity: 0, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: Lang.bind(this, - function() { - this._desktop.hide(); - }) - }); - } else { - this._desktop.hide(); - } - }, - - _onButtonRelease : function (actor, event) { - this.emit('selected', event.get_time()); - } -}; - -Signals.addSignalMethods(DesktopClone.prototype); - - /** * @windowClone: Corresponding window clone * @parentActor: The actor which will be the parent of all overlay items @@ -559,7 +502,6 @@ WindowOverlay.prototype = { this._parentActor.queue_relayout(); } }; - Signals.addSignalMethods(WindowOverlay.prototype); const WindowPositionFlags = { @@ -583,10 +525,20 @@ Workspace.prototype = { // Without this the drop area will be overlapped. this._windowOverlaysGroup.set_size(0, 0); - this.actor = new Clutter.Group(); + this.actor = new Clutter.Group({ reactive: true }); this.actor._delegate = this; this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); + this.actor.connect('button-release-event', Lang.bind(this, + function(actor, event) { + // Only switch to the workspace when there's no application + // windows open. The problem is that it's too easy to miss + // an app window and get the wrong one focused. + if (this._windows.length == 0) { + this.metaWorkspace.activate(event.get_time()); + Main.overview.hide(); + } + })); // Items in _windowOverlaysGroup should not be scaled, so we don't // add them to this.actor, but to its parent whenever it changes @@ -602,35 +554,10 @@ Workspace.prototype = { let windows = global.get_window_actors().filter(this._isMyWindow, this); - // Find the desktop window - for (let i = 0; i < windows.length; i++) { - if (windows[i].meta_window.get_window_type() == Meta.WindowType.DESKTOP) { - this._desktop = new DesktopClone(windows[i]); - break; - } - } - // If there wasn't one, fake it - if (!this._desktop) - this._desktop = new DesktopClone(); - - this._desktop.connect('selected', - Lang.bind(this, - function(clone, time) { - // Only switch to the workspace when there's no application windows - // open (we always have one window for the desktop). The problem - // is that it's too easy to miss an app window and get the wrong - // one focused. - if (this._windows.length == 1) { - this.metaWorkspace.activate(time); - Main.overview.hide(); - } - })); - this.actor.add_actor(this._desktop.actor); - // Create clones for remaining windows that should be // visible in the Overview - this._windows = [this._desktop]; - this._windowOverlays = [ null ]; + this._windows = []; + this._windowOverlays = []; for (let i = 0; i < windows.length; i++) { if (this._isOverviewWindow(windows[i])) { this._addWindowClone(windows[i]); @@ -745,10 +672,10 @@ Workspace.prototype = { // FIXME: do something cooler-looking using clutter-cairo this._frame = new Clutter.Rectangle({ color: FRAME_COLOR }); this.actor.add_actor(this._frame); - this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x, - this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y); - this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, - this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); + this._frame.set_position(- FRAME_SIZE / this.actor.scale_x, + - FRAME_SIZE / this.actor.scale_y); + this._frame.set_size(this.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, + this.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); this._frame.lower_bottom(); this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition)); @@ -768,14 +695,14 @@ Workspace.prototype = { * Set the workspace (desktop) reactive **/ setReactive: function(reactive) { - this._desktop.actor.reactive = reactive; + this.actor.reactive = reactive; }, _updateFramePosition : function() { - this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x, - this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y); - this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, - this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); + this._frame.set_position(- FRAME_SIZE / this.actor.scale_x, + - FRAME_SIZE / this.actor.scale_y); + this._frame.set_size(this.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, + this.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); }, _isCloneVisible: function(clone) { @@ -786,7 +713,7 @@ Workspace.prototype = { * _getVisibleClones: * * Returns a list WindowClone objects where the clone isn't filtered - * out by any application filter. The clone for the desktop is excluded. + * out by any application filter. * The returned array will always be newly allocated; it is not in any * defined order, and thus it's convenient to call .sort() with your * choice of sorting function. @@ -794,7 +721,7 @@ Workspace.prototype = { _getVisibleClones: function() { let visible = []; - for (let i = 1; i < this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; if (!this._isCloneVisible(clone)) @@ -806,7 +733,7 @@ Workspace.prototype = { }, _resetCloneVisibility: function () { - for (let i = 1; i < this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; let overlay = this._windowOverlays[i]; @@ -1005,9 +932,9 @@ Workspace.prototype = { let buttonOuterHeight, captionHeight; let buttonOuterWidth = 0; - if (this._windowOverlays[1]) { - [buttonOuterHeight, captionHeight] = this._windowOverlays[1].chromeHeights(); - buttonOuterWidth = this._windowOverlays[1].chromeWidth() / this.scale; + if (this._windowOverlays[0]) { + [buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights(); + buttonOuterWidth = this._windowOverlays[0].chromeWidth() / this.scale; } else [buttonOuterHeight, captionHeight] = [0, 0]; buttonOuterHeight /= this.scale; @@ -1126,8 +1053,6 @@ Workspace.prototype = { }, syncStacking: function(stackIndices) { - let desktopClone = this._windows[0]; - let visibleClones = this._getVisibleClones(); visibleClones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; }); @@ -1135,7 +1060,7 @@ Workspace.prototype = { let clone = visibleClones[i]; let metaWindow = clone.metaWindow; if (i == 0) { - clone.setStackAbove(desktopClone.actor); + clone.setStackAbove(null); } else { let previousClone = visibleClones[i - 1]; clone.setStackAbove(previousClone.actor); @@ -1170,7 +1095,7 @@ Workspace.prototype = { }, _fadeInAllOverlays: function() { - for (let i = 1; i < this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; let overlay = this._windowOverlays[i]; if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows)) @@ -1180,7 +1105,7 @@ Workspace.prototype = { }, _hideAllOverlays: function() { - for (let i = 1; i< this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let overlay = this._windowOverlays[i]; overlay.hide(); } @@ -1307,8 +1232,8 @@ Workspace.prototype = { }, // check for maximized windows on the workspace - _haveMaximizedWindows: function() { - for (let i = 1; i < this._windows.length; i++) { + hasMaximizedWindows: function() { + for (let i = 0; i < this._windows.length; i++) { let metaWindow = this._windows[i].metaWindow; if (metaWindow.showing_on_its_workspace() && metaWindow.maximized_horizontally && @@ -1329,12 +1254,6 @@ Workspace.prototype = { else this.positionWindows(WindowPositionFlags.ZOOM); - let active = global.screen.get_active_workspace(); - let fadeInIcons = (Main.overview.animationInProgress && - active == this.metaWorkspace && - !this._haveMaximizedWindows()); - this._desktop.zoomToOverview(fadeInIcons); - this._visible = true; }, @@ -1352,7 +1271,7 @@ Workspace.prototype = { this._doneLeavingOverview)); // Position and scale the windows. - for (let i = 1; i < this._windows.length; i++) { + for (let i = 0; i < this._windows.length; i++) { let clone = this._windows[i]; clone.zoomFromOverview(); @@ -1381,11 +1300,6 @@ Workspace.prototype = { } } - let active = global.screen.get_active_workspace(); - let fadeOutIcons = (active == this.metaWorkspace && - !this._haveMaximizedWindows()); - this._desktop.zoomFromOverview(fadeOutIcons); - this._visible = false; }, @@ -1449,7 +1363,7 @@ Workspace.prototype = { // Don't let the user try to select this workspace as it's // making its exit. - this._desktop.reactive = false; + this.actor.reactive = false; }, destroy : function() { @@ -1473,7 +1387,7 @@ Workspace.prototype = { // their parent (this.actor), but we might have a zoomed window // which has been reparented to the stage - _windows[0] holds // the desktop window, which is never reparented - for (let w = 1; w < this._windows.length; w++) + for (let w = 0; w < this._windows.length; w++) this._windows[w].destroy(); this._windows = []; }, @@ -1532,7 +1446,7 @@ Workspace.prototype = { }, _onShowOverlayClose: function (windowOverlay) { - for (let i = 1; i < this._windowOverlays.length; i++) { + for (let i = 0; i < this._windowOverlays.length; i++) { let overlay = this._windowOverlays[i]; if (overlay == windowOverlay) continue; diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 34945d776..4a7edc9c3 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -117,6 +117,11 @@ GenericWorkspacesView.prototype = { } }, + getActiveWorkspace: function() { + let active = global.screen.get_active_workspace_index(); + return this._workspaces[active]; + }, + _clearApplicationWindowSelection: function(reposition) { if (this._windowSelectionAppId == null) return; @@ -826,7 +831,7 @@ SingleView.prototype = { if (index < 0 || index >= global.n_workspaces) return; - let dragActor = this._workspaces[index]._desktop.actor; + let dragActor = this._workspaces[index].actor; if (draggable) { this._workspaces[index].actor.reactive = true; @@ -856,6 +861,9 @@ SingleView.prototype = { // start dragging the active workspace _onButtonPress: function(actor, event) { + if (actor != event.get_source()) + return; + if (this._dragIndex == -1) return; From e06b608b107f7f087230b0bc27f01ee456bd63dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Fri, 16 Jul 2010 19:15:29 +0200 Subject: [PATCH 28/61] linear-view: Remove shadows when zoomed out Overlaying inactive workspaces with a gradient to fade out the actors does no longer work when re-using the normal desktop background. If we keep the current DND behavior, we probably want to implement a real fade effect - for now, just remove the visually disruptive shadows. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 12 ----- js/ui/workspacesView.js | 93 +------------------------------------- 2 files changed, 2 insertions(+), 103 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index fa8c95936..d0625b516 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -277,18 +277,6 @@ StTooltip StLabel { background-gradient-end: rgba(16, 16, 16, 0.9); } -.left-workspaces-shadow { - background-gradient-direction: horizontal; - background-gradient-start: rgba(16, 16, 16, 1.0); - background-gradient-end: rgba(16, 16, 16, 0.0); -} - -.right-workspaces-shadow { - background-gradient-direction: horizontal; - background-gradient-end: rgba(16, 16, 16, 1.0); - background-gradient-start: rgba(16, 16, 16, 0); -} - .workspaces { color: white; } diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 4a7edc9c3..c8e911a1e 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -636,44 +636,11 @@ SingleView.prototype = { __proto__: GenericWorkspacesView.prototype, _init: function(width, height, x, y, workspaces) { - let shadowWidth = Math.ceil(global.screen_width * WORKSPACE_SHADOW_SCALE); this._newWorkspaceArea = new NewWorkspaceArea(); this._newWorkspaceArea.actor._delegate = { acceptDrop: Lang.bind(this, this._acceptNewWorkspaceDrop) }; - this._leftShadow = new St.Bin({ style_class: 'left-workspaces-shadow', - width: shadowWidth, - height: global.screen_height, - x: global.screen_width }); - this._leftShadow._delegate = { - acceptDrop: Lang.bind(this, function(source, actor, x, y, time) { - let active = global.screen.get_active_workspace_index(); - let leftWorkspace = this._workspaces[active - 1]; - if (leftWorkspace && - leftWorkspace.acceptDrop(source, actor, x, y, time)) { - leftWorkspace.metaWorkspace.activate(time); - return true; - } - return false; - }) - }; - this._rightShadow = new St.Bin({ style_class: 'right-workspaces-shadow', - width: shadowWidth, - height: global.screen_height, - x: global.screen_width }); - this._rightShadow._delegate = { - acceptDrop: Lang.bind(this, function(source, actor, x, y, time) { - let active = global.screen.get_active_workspace_index(); - let rightWorkspace = this._workspaces[active + 1]; - if (rightWorkspace && - rightWorkspace.acceptDrop(source, actor, x, y, time)) { - rightWorkspace.metaWorkspace.activate(time); - return true; - } - return false; - }) - }; GenericWorkspacesView.prototype._init.call(this, width, height, x, y, workspaces); @@ -689,8 +656,6 @@ SingleView.prototype = { } this.actor.add_actor(this._newWorkspaceArea.actor); - this.actor.add_actor(this._leftShadow); - this.actor.add_actor(this._rightShadow); this.actor.add_style_class_name('single'); this.actor.set_clip(x, y, width, height); @@ -757,16 +722,6 @@ SingleView.prototype = { this._newWorkspaceArea.gridX = this._x + this._activeWorkspaceX + (this._workspaces.length - active) * (_width + this._spacing); this._newWorkspaceArea.gridY = this._y + this._activeWorkspaceY; - - this._leftShadow.scale = scale; - this._leftShadow.gridX = this._x + this._activeWorkspaceX - - (this._leftShadow.width * scale + this._spacing); - this._leftShadow.gridY = this._y + this._activeWorkspaceY; - - this._rightShadow.scale = scale; - this._rightShadow.gridX = this._x + this._activeWorkspaceX - + (_width + this._spacing); - this._rightShadow.gridY = this._y + this._activeWorkspaceY; }, _transitionWorkspaces: function() { @@ -992,8 +947,6 @@ SingleView.prototype = { } Tweener.removeTweens(this._newWorkspaceArea.actor); - Tweener.removeTweens(this._leftShadow); - Tweener.removeTweens(this._rightShadow); this._newWorkspaceArea.gridX += dx; if (showAnimation) { @@ -1014,35 +967,11 @@ SingleView.prototype = { this._updateVisibility(); }) }); - this._leftShadow.x = this._leftShadow.gridX; - Tweener.addTween(this._leftShadow, - { y: this._leftShadow.gridY, - scale_x: this._leftShadow.scale, - scale_y: this._leftShadow.scale, - time: WORKSPACE_SWITCH_TIME, - transition: 'easeOutQuad' - }); - this._rightShadow.x = this._rightShadow.gridX; - Tweener.addTween(this._rightShadow, - { y: this._rightShadow.gridY, - scale_x: this._rightShadow.scale, - scale_y: this._rightShadow.scale, - time: WORKSPACE_SWITCH_TIME, - transition: 'easeOutQuad' - }); } else { this._newWorkspaceArea.actor.set_scale(this._newWorkspaceArea.scale, this._newWorkspaceArea.scale); this._newWorkspaceArea.actor.set_position(this._newWorkspaceArea.gridX, this._newWorkspaceArea.gridY); - this._leftShadow.set_scale(this._leftShadow.scale, - this._leftShadow.scale); - this._leftShadow.set_position(this._leftShadow.gridX, - this._leftShadow.gridY); - this._rightShadow.set_scale(this._rightShadow.scale, - this._rightShadow.scale); - this._rightShadow.set_position(this._rightShadow.gridX, - this._rightShadow.gridY); this._updateVisibility(); } }, @@ -1063,24 +992,6 @@ SingleView.prototype = { workspace.actor.visible = (w == active); } } - - if (this._inDrag) { - this._leftShadow.raise_top(); - this._rightShadow.raise_top(); - - if (active > 0) - this._leftShadow.show(); - else - this._leftShadow.hide(); - - if (active < this._workspaces.length - 1) - this._rightShadow.show(); - else - this._rightShadow.hide(); - } else { - this._leftShadow.hide(); - this._rightShadow.hide(); - } }, _cleanWorkspaces: function() { @@ -1227,7 +1138,7 @@ SingleView.prototype = { // check hover state of new workspace area / inactive workspaces if (leftWorkspace) { - if (dragEvent.targetActor == this._leftShadow) { + if (leftWorkspace.actor.contains(dragEvent.targetActor)) { hoverWorkspace = leftWorkspace; leftWorkspace.opacity = leftWorkspace.actor.opacity = 255; result = leftWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor); @@ -1237,7 +1148,7 @@ SingleView.prototype = { } if (rightWorkspace) { - if (dragEvent.targetActor == this._rightShadow) { + if (rightWorkspace.actor.contains(dragEvent.targetActor)) { hoverWorkspace = rightWorkspace; rightWorkspace.opacity = rightWorkspace.actor.opacity = 255; result = rightWorkspace.handleDragOver(dragEvent.source, dragEvent.dragActor); From 2d2ac5b3f62596a3296345fa92eb07c69eac47d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Mon, 4 Oct 2010 20:04:23 +0200 Subject: [PATCH 29/61] linear-view: Remove NewWorkspaceArea As the button to add workspaces will move to the same position as the new workspace drop area in drag mode, the latter is redundant and can be removed. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 20 ------- js/ui/workspacesView.js | 108 ++++++++----------------------------- 2 files changed, 23 insertions(+), 105 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index d0625b516..93b36d367 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -257,26 +257,6 @@ StTooltip StLabel { /* Overview */ -.new-workspace-area { - border: 2px solid rgba(255, 255, 255, 0.8); - border-radius: 10px; - background-color: #111; -} - -.new-workspace-area-internal { - background-gradient-direction: horizontal; - background-gradient-start: rgba(16, 16, 16, 0); - background-gradient-end: rgba(16, 16, 16, 1.0); - background-image: url("move-window-on-new.svg"); -} - -.new-workspace-area:hover { - border: 2px solid rgba(255, 255, 255, 1.0); - background-gradient-direction: horizontal; - background-gradient-start: rgba(130, 130, 130, 0.9); - background-gradient-end: rgba(16, 16, 16, 0.9); -} - .workspaces { color: white; } diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index c8e911a1e..82d486810 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -28,7 +28,7 @@ const WorkspacesViewType = { const WORKSPACES_VIEW_KEY = 'workspaces-view'; const WORKSPACE_DRAGGING_SCALE = 0.85; -const WORKSPACE_SHADOW_SCALE = (1 - WORKSPACE_DRAGGING_SCALE) / 2; + function GenericWorkspacesView(width, height, x, y, workspaces) { this._init(width, height, x, y, workspaces); @@ -472,32 +472,6 @@ MosaicView.prototype = { } }; -function NewWorkspaceArea() { - this._init(); -} - -NewWorkspaceArea.prototype = { - _init: function() { - let width = Math.ceil(global.screen_width * WORKSPACE_SHADOW_SCALE); - this.actor = new Clutter.Group({ width: width, - height: global.screen_height, - x: global.screen_width }); - - this._child1 = new St.Bin({ style_class: 'new-workspace-area', - width: width, - height: global.screen_height }); - this._child2 = new St.Bin({ style_class: 'new-workspace-area-internal', - width: width, - height: global.screen_height, - reactive: true }); - this.actor.add_actor(this._child1); - this.actor.add_actor(this._child2); - }, - - setStyle: function(isHover) { - this._child1.set_hover(isHover); - } -}; function WorkspaceIndicator(activateWorkspace, workspaceAcceptDrop, workspaceHandleDragOver, scrollEventCb) { this._init(activateWorkspace, workspaceAcceptDrop, workspaceHandleDragOver, scrollEventCb); @@ -636,12 +610,6 @@ SingleView.prototype = { __proto__: GenericWorkspacesView.prototype, _init: function(width, height, x, y, workspaces) { - - this._newWorkspaceArea = new NewWorkspaceArea(); - this._newWorkspaceArea.actor._delegate = { - acceptDrop: Lang.bind(this, this._acceptNewWorkspaceDrop) - }; - GenericWorkspacesView.prototype._init.call(this, width, height, x, y, workspaces); this._itemDragBeginId = Main.overview.connect('item-drag-begin', @@ -655,8 +623,6 @@ SingleView.prototype = { Lang.bind(this, this._dragEnd)); } - this.actor.add_actor(this._newWorkspaceArea.actor); - this.actor.add_style_class_name('single'); this.actor.set_clip(x, y, width, height); this._activeWorkspaceX = 0; // x offset of active ws while dragging @@ -717,11 +683,6 @@ SingleView.prototype = { workspace.setSelected(false); } - - this._newWorkspaceArea.scale = scale; - this._newWorkspaceArea.gridX = this._x + this._activeWorkspaceX - + (this._workspaces.length - active) * (_width + this._spacing); - this._newWorkspaceArea.gridY = this._y + this._activeWorkspaceY; }, _transitionWorkspaces: function() { @@ -908,19 +869,32 @@ SingleView.prototype = { workspace.gridX += dx; if (showAnimation) { - Tweener.addTween(workspace.actor, - { x: workspace.gridX, - y: workspace.gridY, - scale_x: workspace.scale, - scale_y: workspace.scale, - opacity: workspace.opacity, - time: WORKSPACE_SWITCH_TIME, - transition: 'easeOutQuad' - }); + let params = { x: workspace.gridX, + y: workspace.gridY, + scale_x: workspace.scale, + scale_y: workspace.scale, + opacity: workspace.opacity, + time: WORKSPACE_SWITCH_TIME, + transition: 'easeOutQuad' + }; + // we have to call _updateVisibility() once before the + // animation and once afterwards - it does not really + // matter which tween we use, so we pick the first one ... + if (w == 0) { + this._updateVisibility(); + params.onComplete = Lang.bind(this, + function() { + this._animating = false; + this._updateVisibility(); + }); + } + Tweener.addTween(workspace.actor, params); } else { workspace.actor.set_scale(workspace.scale, workspace.scale); workspace.actor.set_position(workspace.gridX, workspace.gridY); workspace.actor.opacity = workspace.opacity; + if (w == 0) + this._updateVisibility(); } } @@ -945,35 +919,6 @@ SingleView.prototype = { this._cleanWorkspaces(); } } - - Tweener.removeTweens(this._newWorkspaceArea.actor); - - this._newWorkspaceArea.gridX += dx; - if (showAnimation) { - // we have to call _updateVisibility() once before the - // animation and once afterwards - it does not really - // matter which tween we use, as long as it's not inside - // a loop ... - this._updateVisibility(); - Tweener.addTween(this._newWorkspaceArea.actor, - { x: this._newWorkspaceArea.gridX, - y: this._newWorkspaceArea.gridY, - scale_x: this._newWorkspaceArea.scale, - scale_y: this._newWorkspaceArea.scale, - time: WORKSPACE_SWITCH_TIME, - transition: 'easeOutQuad', - onComplete: Lang.bind(this, function() { - this._animating = false; - this._updateVisibility(); - }) - }); - } else { - this._newWorkspaceArea.actor.set_scale(this._newWorkspaceArea.scale, - this._newWorkspaceArea.scale); - this._newWorkspaceArea.actor.set_position(this._newWorkspaceArea.gridX, - this._newWorkspaceArea.gridY); - this._updateVisibility(); - } }, _updateVisibility: function() { @@ -1155,13 +1100,6 @@ SingleView.prototype = { } else { rightWorkspace.opacity = rightWorkspace.actor.opacity = 200; } - } else { - let targetParent = dragEvent.targetActor.get_parent(); - if (targetParent == this._newWorkspaceArea.actor) { - this._newWorkspaceArea.setStyle(true); - result = this._handleDragOverNewWorkspace(dragEvent.source, dragEvent.dragActor); - } else - this._newWorkspaceArea.setStyle(false); } // handle delayed workspace switches From 3e4f744e560b08106de5dbf377538eba91069b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 22 Jul 2010 01:29:02 +0200 Subject: [PATCH 30/61] dash: Reimplement the dash based on AppWell code The new dash implementation is a single-column vertical sidebar, whose items are scaled dynamically to fit the available height. If the height is still exceeded after scaling down to a minimum item size, excess items are cut off. The now unused old dash implementation is renamed to OldDash, as its code will be used as a base for the new view selector element. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/Makefile.am | 1 + data/theme/gnome-shell.css | 29 +++--- data/theme/running-indicator.svg | 89 +++++++++++++++++ js/ui/appDisplay.js | 144 ++------------------------- js/ui/dash.js | 163 ++++++++++++++++++++++++++++++- js/ui/overview.js | 14 +-- 6 files changed, 279 insertions(+), 161 deletions(-) create mode 100644 data/theme/running-indicator.svg diff --git a/data/Makefile.am b/data/Makefile.am index 68dcc1d04..e17b0e7f7 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -32,6 +32,7 @@ dist_theme_DATA = \ theme/move-window-on-new.svg \ theme/process-working.png \ theme/remove-workspace.svg \ + theme/running-indicator.svg \ theme/scroll-button-down-hover.png \ theme/scroll-button-down.png \ theme/scroll-button-up-hover.png \ diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 93b36d367..849b7d3fc 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -339,7 +339,17 @@ StTooltip StLabel { #dash { color: #5f5f5f; font-size: 12px; - padding: 0px 14px; + padding: 12px; + spacing: 6px; + background-color: rgba(0, 0, 0, 0.5); + border: 2px solid rgba(128, 128, 128, 0.4); + border-left: 0px; + border-radius: 0px 9px 9px 0px; +} + +#dash:empty { + height: 100px; + width: 60px; } #dashSections { @@ -526,31 +536,26 @@ StTooltip StLabel { } .app-well-app { - border: 1px solid #181818; border-radius: 4px; padding: 4px; - width: 70px; - height: 70px; font-size: 10px; + color: white; transition-duration: 100; text-align: center; } .app-well-app.running { - background-gradient-direction: vertical; - background-gradient-start: #3d3d3d; - background-gradient-end: #181818; + text-shadow: black 0px 2px 2px; + background-image: url("running-indicator.svg"); } .app-well-app.selected { - border: 1px solid #666666; + background: rgba(255,255,255,0.33); } .app-well-app:hover { - border: 1px solid #666666; - background-gradient-direction: vertical; - background-gradient-start: rgba(61,61,61,0.8); - background-gradient-end: rgba(24,24,24,0.2); + background: rgba(255,255,255,0.33); + text-shadow: black 0px 2px 2px; transition-duration: 100; } diff --git a/data/theme/running-indicator.svg b/data/theme/running-indicator.svg new file mode 100644 index 000000000..d721e6444 --- /dev/null +++ b/data/theme/running-indicator.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 8c3330e19..b9a339992 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -375,7 +375,9 @@ AppIcon.prototype = { let label = this.app.get_name(); - IconGrid.BaseIcon.prototype._init.call(this, label); + IconGrid.BaseIcon.prototype._init.call(this, + label, + { setSizeManually: true }); }, createIcon: function(iconSize) { @@ -396,8 +398,8 @@ AppWellIcon.prototype = { y_fill: true }); this.actor._delegate = this; - this._icon = new AppIcon(app); - this.actor.set_child(this._icon.actor); + this.icon = new AppIcon(app); + this.actor.set_child(this.icon.actor); this.actor.connect('clicked', Lang.bind(this, this._onClicked)); @@ -581,13 +583,13 @@ AppWellIcon.prototype = { }, getDragActor: function() { - return this.app.create_icon_texture(this._icon.iconSize); + return this.app.create_icon_texture(this.icon.iconSize); }, // Returns the original actor that should align with the actor // we show as the item is being dragged. getDragActorSource: function() { - return this._icon.icon; + return this.icon.icon; } }; Signals.addSignalMethods(AppWellIcon.prototype); @@ -756,135 +758,3 @@ AppIconMenu.prototype = { } }; Signals.addSignalMethods(AppIconMenu.prototype); - -function AppWell() { - this._init(); -} - -AppWell.prototype = { - _init : function() { - this._placeholderText = null; - this._menus = []; - this._menuDisplays = []; - - this._favorites = []; - - this._grid = new IconGrid.IconGrid(); - this.actor = this._grid.actor; - this.actor._delegate = this; - - this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay)); - - this._tracker = Shell.WindowTracker.get_default(); - this._appSystem = Shell.AppSystem.get_default(); - - this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay)); - AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay)); - this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay)); - }, - - _appIdListToHash: function(apps) { - let ids = {}; - for (let i = 0; i < apps.length; i++) - ids[apps[i].get_id()] = apps[i]; - return ids; - }, - - _queueRedisplay: function () { - Main.queueDeferredWork(this._workId); - }, - - _redisplay: function () { - this._grid.removeAll(); - - let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); - - /* hardcode here pending some design about how exactly desktop contexts behave */ - let contextId = ''; - - let running = this._tracker.get_running_apps(contextId); - let runningIds = this._appIdListToHash(running); - - let nFavorites = 0; - for (let id in favorites) { - let app = favorites[id]; - let display = new AppWellIcon(app); - this._grid.addItem(display.actor); - nFavorites++; - } - - for (let i = 0; i < running.length; i++) { - let app = running[i]; - if (app.get_id() in favorites) - continue; - let display = new AppWellIcon(app); - this._grid.addItem(display.actor); - } - if (this._placeholderText) { - this._placeholderText.destroy(); - this._placeholderText = null; - } - - if (running.length == 0 && nFavorites == 0) { - this._placeholderText = new St.Label({ text: _("Drag here to add favorites") }); - this.actor.add_actor(this._placeholderText); - } - }, - - handleDragOver : function(source, actor, x, y, time) { - let app = null; - if (source instanceof AppWellIcon) - app = this._appSystem.get_app(source.getId()); - else if (source instanceof Workspace.WindowClone) - app = this._tracker.get_window_app(source.metaWindow); - - // Don't allow favoriting of transient apps - if (app == null || app.is_transient()) - return DND.DragMotionResult.NO_DROP; - - let id = app.get_id(); - - let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); - - let srcIsFavorite = (id in favorites); - - if (srcIsFavorite) - return DND.DragMotionResult.NO_DROP; - - return DND.DragMotionResult.COPY_DROP; - }, - - // Draggable target interface - acceptDrop : function(source, actor, x, y, time) { - let app = null; - if (source instanceof AppWellIcon) { - app = this._appSystem.get_app(source.getId()); - } else if (source instanceof Workspace.WindowClone) { - app = this._tracker.get_window_app(source.metaWindow); - } - - // Don't allow favoriting of transient apps - if (app == null || app.is_transient()) { - return false; - } - - let id = app.get_id(); - - let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); - - let srcIsFavorite = (id in favorites); - - if (srcIsFavorite) { - return false; - } else { - Mainloop.idle_add(Lang.bind(this, function () { - AppFavorites.getAppFavorites().addFavorite(id); - return false; - })); - } - - return true; - } -}; - -Signals.addSignalMethods(AppWell.prototype); diff --git a/js/ui/dash.js b/js/ui/dash.js index 530962bba..3347d2772 100644 --- a/js/ui/dash.js +++ b/js/ui/dash.js @@ -4,11 +4,13 @@ const Clutter = imports.gi.Clutter; const Mainloop = imports.mainloop; const Signals = imports.signals; const Lang = imports.lang; +const Shell = imports.gi.Shell; const St = imports.gi.St; const Gettext = imports.gettext.domain('gnome-shell'); const _ = Gettext.gettext; const AppDisplay = imports.ui.appDisplay; +const AppFavorites = imports.ui.appFavorites; const DND = imports.ui.dnd; const DocDisplay = imports.ui.docDisplay; const PlaceDisplay = imports.ui.placeDisplay; @@ -16,6 +18,7 @@ const Main = imports.ui.main; const Overview = imports.ui.overview; const Search = imports.ui.search; const Tweener = imports.ui.tweener; +const Workspace = imports.ui.workspace; // 25 search results (per result type) should be enough for everyone const MAX_RENDERED_SEARCH_RESULTS = 25; @@ -689,11 +692,11 @@ Section.prototype = { } }; -function Dash() { +function OldDash() { this._init(); } -Dash.prototype = { +OldDash.prototype = { _init : function() { // dash and the popup panes need to be reactive so that the clicks in unoccupied places on them // are not passed to the transparent background underneath them. This background is used for the workspaces area when @@ -911,3 +914,159 @@ Dash.prototype = { } }; Signals.addSignalMethods(Dash.prototype); + + +function Dash() { + this._init(); +} + +Dash.prototype = { + _init : function() { + this._menus = []; + this._menuDisplays = []; + this._maxHeight = -1; + + this._favorites = []; + + this._box = new St.BoxLayout({ name: 'dash', + vertical: true, + clip_to_allocation: true }); + this._box._delegate = this; + + this.actor = new St.Bin({ y_align: St.Align.START, child: this._box }); + this.actor.connect('notify::height', Lang.bind(this, + function() { + if (this._maxHeight != this.actor.height) + this._queueRedisplay(); + this._maxHeight = this.actor.height; + })); + + this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay)); + + this._tracker = Shell.WindowTracker.get_default(); + this._appSystem = Shell.AppSystem.get_default(); + + this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay)); + AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay)); + this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay)); + }, + + _appIdListToHash: function(apps) { + let ids = {}; + for (let i = 0; i < apps.length; i++) + ids[apps[i].get_id()] = apps[i]; + return ids; + }, + + _queueRedisplay: function () { + Main.queueDeferredWork(this._workId); + }, + + _redisplay: function () { + this._box.hide(); + this._box.remove_all(); + + let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); + + /* hardcode here pending some design about how exactly desktop contexts behave */ + let contextId = ''; + + let running = this._tracker.get_running_apps(contextId); + + for (let id in favorites) { + let app = favorites[id]; + let display = new AppDisplay.AppWellIcon(app); + this._box.add(display.actor); + } + + for (let i = 0; i < running.length; i++) { + let app = running[i]; + if (app.get_id() in favorites) + continue; + let display = new AppDisplay.AppWellIcon(app); + this._box.add(display.actor); + } + + let children = this._box.get_children(); + if (children.length == 0) { + this._box.add_style_pseudo_class('empty'); + } else { + this._box.remove_style_pseudo_class('empty'); + + if (this._maxHeight > -1) { + let iconSizes = [ 48, 32, 24, 22, 16 ]; + + for (let i = 0; i < iconSizes.length; i++) { + let minHeight, natHeight; + + this._iconSize = iconSizes[i]; + for (let j = 0; j < children.length; j++) + children[j]._delegate.icon.setIconSize(this._iconSize); + + [minHeight, natHeight] = this.actor.get_preferred_height(-1); + + if (natHeight <= this._maxHeight) + break; + } + } + } + this._box.show(); + }, + + handleDragOver : function(source, actor, x, y, time) { + let app = null; + if (source instanceof AppDisplay.AppWellIcon) + app = this._appSystem.get_app(source.getId()); + else if (source instanceof Workspace.WindowClone) + app = this._tracker.get_window_app(source.metaWindow); + + // Don't allow favoriting of transient apps + if (app == null || app.is_transient()) + return DND.DragMotionResult.NO_DROP; + + let id = app.get_id(); + + let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); + + let srcIsFavorite = (id in favorites); + + if (srcIsFavorite) + return DND.DragMotionResult.NO_DROP; + + return DND.DragMotionResult.COPY_DROP; + }, + + // Draggable target interface + acceptDrop : function(source, actor, x, y, time) { + let app = null; + if (source instanceof AppDisplay.AppWellIcon) { + app = this._appSystem.get_app(source.getId()); + } else if (source instanceof Workspace.WindowClone) { + app = this._tracker.get_window_app(source.metaWindow); + } + + // Don't allow favoriting of transient apps + if (app == null || app.is_transient()) { + return false; + } + + let id = app.get_id(); + + let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); + + let srcIsFavorite = (id in favorites); + + if (srcIsFavorite) { + return false; + } else { + Mainloop.idle_add(Lang.bind(this, function () { + AppFavorites.getAppFavorites().addFavorite(id); + return false; + })); + } + + return true; + } +}; + +Signals.addSignalMethods(Dash.prototype); diff --git a/js/ui/overview.js b/js/ui/overview.js index 5e2385f38..a27b25bd1 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -319,15 +319,12 @@ Overview.prototype = { this._workspacesY = Math.floor(displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width)); if (rtl) { - this._dash.actor.set_position(primary.width - displayGridColumnWidth, contentY); + this._dash.actor.set_position(primary.width - displayGridColumnWidth - WORKSPACE_GRID_PADDING / 2, + this._workspacesY); } else { - this._dash.actor.set_position(0, contentY); + this._dash.actor.set_position(0, this._workspacesY); } - - this._dash.actor.set_size(displayGridColumnWidth, contentHeight); - this._dash.searchArea.height = this._workspacesY - contentY; - this._dash.sectionArea.height = this._workspacesHeight; - this._dash.searchResults.actor.height = this._workspacesHeight; + this._dash.actor.height = this._workspacesHeight; // place the 'Add Workspace' button in the bottom row of the grid this._workspacesBarX = this._workspacesX; @@ -445,8 +442,6 @@ Overview.prototype = { this.visible = true; this.animationInProgress = true; - this._dash.show(); - /* TODO: make this stuff dynamic */ this._workspacesManager = new WorkspacesView.WorkspacesManager(this._workspacesWidth, @@ -615,7 +610,6 @@ Overview.prototype = { this._workspacesManager = null; - this._dash.hide(); this._desktopFade.hide(); this._background.hide(); this._group.hide(); From 26225f0bfbaa58eb751d2c90ad0f351acfb4a77f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 30 Sep 2010 23:49:07 +0200 Subject: [PATCH 31/61] dash: Move padding into the icon for Fittsability With this change, the icons' reactive area extends to the screen edge, making them good targets according to Fitts' law. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 849b7d3fc..f76bb690c 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -339,8 +339,7 @@ StTooltip StLabel { #dash { color: #5f5f5f; font-size: 12px; - padding: 12px; - spacing: 6px; + padding: 6px 0px; background-color: rgba(0, 0, 0, 0.5); border: 2px solid rgba(128, 128, 128, 0.4); border-left: 0px; @@ -535,7 +534,11 @@ StTooltip StLabel { padding-bottom: 10px; } -.app-well-app { +#dash > .app-well-app { + padding: 6px 12px; +} + +.app-well-app > .overview-icon { border-radius: 4px; padding: 4px; font-size: 10px; @@ -544,22 +547,22 @@ StTooltip StLabel { text-align: center; } -.app-well-app.running { +.app-well-app.running > .overview-icon { text-shadow: black 0px 2px 2px; background-image: url("running-indicator.svg"); } -.app-well-app.selected { +.app-well-app.selected > .overview-icon { background: rgba(255,255,255,0.33); } -.app-well-app:hover { +.app-well-app:hover > .overview-icon { background: rgba(255,255,255,0.33); text-shadow: black 0px 2px 2px; transition-duration: 100; } -.app-well-app:active { +.app-well-app:active > .overview-icon { background-color: #1e1e1e; border: 1px solid #5f5f5f; } From e6bb06a7cc4fbd90d9d193d9516955ea2a62f46b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 18 Nov 2010 10:23:44 +0100 Subject: [PATCH 32/61] search-display: Move SearchResults to a separate file With the new layout, search results will be displayed in an independent view like window previews, applications and possible future additions; it does not make much sense keeping it with the switching logic, so move the code to its own file. Also remove the dash-prefix from the relevant style classes. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 12 +- js/Makefile.am | 1 + js/ui/dash.js | 316 +------------------------------------ js/ui/search.js | 2 +- js/ui/searchDisplay.js | 304 +++++++++++++++++++++++++++++++++++ 5 files changed, 314 insertions(+), 321 deletions(-) create mode 100644 js/ui/searchDisplay.js diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index f76bb690c..48b527846 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -431,25 +431,25 @@ StTooltip StLabel { padding: 8px 0px; } -.dash-search-statustext, -.dash-search-section-header { +.search-statustext, +.search-section-header { padding: 4px 0px; spacing: 4px; } -.dash-search-section-results { +.search-section-results { color: #ffffff; } -.dash-search-section-list-results { +.search-section-list-results { spacing: 4px; } -.dash-search-result-content { +.search-result-content { padding: 3px; } -.dash-search-result-content:selected { +.search-result-content:selected { padding: 2px; border: 1px solid #5c5c5c; border-radius: 2px; diff --git a/js/Makefile.am b/js/Makefile.am index 47fac8237..b03cd07da 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -40,6 +40,7 @@ nobase_dist_js_DATA = \ ui/runDialog.js \ ui/scripting.js \ ui/search.js \ + ui/searchDisplay.js \ ui/shellDBus.js \ ui/statusIconDispatcher.js \ ui/statusMenu.js \ diff --git a/js/ui/dash.js b/js/ui/dash.js index 3347d2772..53767052b 100644 --- a/js/ui/dash.js +++ b/js/ui/dash.js @@ -17,12 +17,10 @@ const PlaceDisplay = imports.ui.placeDisplay; const Main = imports.ui.main; const Overview = imports.ui.overview; const Search = imports.ui.search; +const SearchDisplay = imports.ui.searchDisplay; const Tweener = imports.ui.tweener; const Workspace = imports.ui.workspace; -// 25 search results (per result type) should be enough for everyone -const MAX_RENDERED_SEARCH_RESULTS = 25; - /* * Returns the index in an array of a given length that is obtained * if the provided index is incremented by an increment and the array @@ -283,295 +281,6 @@ SearchEntry.prototype = { }; Signals.addSignalMethods(SearchEntry.prototype); -function SearchResult(provider, metaInfo, terms) { - this._init(provider, metaInfo, terms); -} - -SearchResult.prototype = { - _init: function(provider, metaInfo, terms) { - this.provider = provider; - this.metaInfo = metaInfo; - this.actor = new St.Clickable({ style_class: 'dash-search-result', - reactive: true, - x_align: St.Align.START, - x_fill: true, - y_fill: true }); - this.actor._delegate = this; - - let content = provider.createResultActor(metaInfo, terms); - if (content == null) { - content = new St.BoxLayout({ style_class: 'dash-search-result-content' }); - let title = new St.Label({ text: this.metaInfo['name'] }); - let icon = this.metaInfo['icon']; - content.add(icon, { y_fill: false }); - content.add(title, { expand: true, y_fill: false }); - } - this._content = content; - this.actor.set_child(content); - - this.actor.connect('clicked', Lang.bind(this, this._onResultClicked)); - - let draggable = DND.makeDraggable(this.actor); - draggable.connect('drag-begin', - Lang.bind(this, function() { - Main.overview.beginItemDrag(this); - })); - draggable.connect('drag-end', - Lang.bind(this, function() { - Main.overview.endItemDrag(this); - })); - }, - - setSelected: function(selected) { - if (selected) - this._content.add_style_pseudo_class('selected'); - else - this._content.remove_style_pseudo_class('selected'); - }, - - activate: function() { - this.provider.activateResult(this.metaInfo.id); - Main.overview.toggle(); - }, - - _onResultClicked: function(actor, event) { - this.activate(); - }, - - getDragActorSource: function() { - return this.metaInfo['icon']; - }, - - getDragActor: function(stageX, stageY) { - return new Clutter.Clone({ source: this.metaInfo['icon'] }); - }, - - shellWorkspaceLaunch: function() { - if (this.provider.dragActivateResult) - this.provider.dragActivateResult(this.metaInfo.id); - else - this.provider.activateResult(this.metaInfo.id); - } -}; - -function OverflowSearchResults(provider) { - this._init(provider); -} - -OverflowSearchResults.prototype = { - __proto__: Search.SearchResultDisplay.prototype, - - _init: function(provider) { - Search.SearchResultDisplay.prototype._init.call(this, provider); - this.actor = new St.OverflowBox({ style_class: 'dash-search-section-list-results' }); - }, - - getVisibleResultCount: function() { - return this.actor.get_n_visible(); - }, - - renderResults: function(results, terms) { - for (let i = 0; i < results.length && i < MAX_RENDERED_SEARCH_RESULTS; i++) { - let result = results[i]; - let meta = this.provider.getResultMeta(result); - let display = new SearchResult(this.provider, meta, terms); - this.actor.add_actor(display.actor); - } - }, - - selectIndex: function(index) { - let nVisible = this.actor.get_n_visible(); - let children = this.actor.get_children(); - if (this.selectionIndex >= 0) { - let prevActor = children[this.selectionIndex]; - prevActor._delegate.setSelected(false); - } - this.selectionIndex = -1; - if (index >= nVisible) - return false; - else if (index < 0) - return false; - let targetActor = children[index]; - targetActor._delegate.setSelected(true); - this.selectionIndex = index; - return true; - }, - - activateSelected: function() { - let children = this.actor.get_children(); - let targetActor = children[this.selectionIndex]; - targetActor._delegate.activate(); - } -}; - -function SearchResults(searchSystem) { - this._init(searchSystem); -} - -SearchResults.prototype = { - _init: function(searchSystem) { - this._searchSystem = searchSystem; - - this.actor = new St.BoxLayout({ name: 'dashSearchResults', - vertical: true }); - this._statusText = new St.Label({ style_class: 'dash-search-statustext' }); - this.actor.add(this._statusText); - this._selectedProvider = -1; - this._providers = this._searchSystem.getProviders(); - this._providerMeta = []; - for (let i = 0; i < this._providers.length; i++) - this.createProviderMeta(this._providers[i]); - }, - - createProviderMeta: function(provider) { - let providerBox = new St.BoxLayout({ style_class: 'dash-search-section', - vertical: true }); - let titleButton = new St.Button({ style_class: 'dash-search-section-header', - reactive: true, - x_fill: true, - y_fill: true }); - titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); })); - providerBox.add(titleButton); - let titleBox = new St.BoxLayout(); - titleButton.set_child(titleBox); - let title = new St.Label({ text: provider.title }); - let count = new St.Label(); - titleBox.add(title, { expand: true }); - titleBox.add(count); - - let resultDisplayBin = new St.Bin({ style_class: 'dash-search-section-results', - x_fill: true, - y_fill: true }); - providerBox.add(resultDisplayBin, { expand: true }); - let resultDisplay = provider.createResultContainerActor(); - if (resultDisplay == null) { - resultDisplay = new OverflowSearchResults(provider); - } - resultDisplayBin.set_child(resultDisplay.actor); - - this._providerMeta.push({ actor: providerBox, - resultDisplay: resultDisplay, - count: count }); - this.actor.add(providerBox); - }, - - _clearDisplay: function() { - this._selectedProvider = -1; - this._visibleResultsCount = 0; - for (let i = 0; i < this._providerMeta.length; i++) { - let meta = this._providerMeta[i]; - meta.resultDisplay.clear(); - meta.actor.hide(); - } - }, - - reset: function() { - this._searchSystem.reset(); - this._statusText.hide(); - this._clearDisplay(); - }, - - startingSearch: function() { - this.reset(); - this._statusText.set_text(_("Searching...")); - this._statusText.show(); - }, - - _metaForProvider: function(provider) { - return this._providerMeta[this._providers.indexOf(provider)]; - }, - - updateSearch: function (searchString) { - let results = this._searchSystem.updateSearch(searchString); - - this._clearDisplay(); - - if (results.length == 0) { - this._statusText.set_text(_("No matching results.")); - this._statusText.show(); - return true; - } else { - this._statusText.hide(); - } - - let terms = this._searchSystem.getTerms(); - - for (let i = 0; i < results.length; i++) { - let [provider, providerResults] = results[i]; - let meta = this._metaForProvider(provider); - meta.actor.show(); - meta.resultDisplay.renderResults(providerResults, terms); - meta.count.set_text('' + providerResults.length); - } - - this.selectDown(false); - - return true; - }, - - _onHeaderClicked: function(provider) { - provider.expandSearch(this._searchSystem.getTerms()); - }, - - _modifyActorSelection: function(resultDisplay, up) { - let success; - let index = resultDisplay.getSelectionIndex(); - if (up && index == -1) - index = resultDisplay.getVisibleResultCount() - 1; - else if (up) - index = index - 1; - else - index = index + 1; - return resultDisplay.selectIndex(index); - }, - - selectUp: function(recursing) { - for (let i = this._selectedProvider; i >= 0; i--) { - let meta = this._providerMeta[i]; - if (!meta.actor.visible) - continue; - let success = this._modifyActorSelection(meta.resultDisplay, true); - if (success) { - this._selectedProvider = i; - return; - } - } - if (this._providerMeta.length > 0 && !recursing) { - this._selectedProvider = this._providerMeta.length - 1; - this.selectUp(true); - } - }, - - selectDown: function(recursing) { - let current = this._selectedProvider; - if (current == -1) - current = 0; - for (let i = current; i < this._providerMeta.length; i++) { - let meta = this._providerMeta[i]; - if (!meta.actor.visible) - continue; - let success = this._modifyActorSelection(meta.resultDisplay, false); - if (success) { - this._selectedProvider = i; - return; - } - } - if (this._providerMeta.length > 0 && !recursing) { - this._selectedProvider = 0; - this.selectDown(true); - } - }, - - activateSelected: function() { - let current = this._selectedProvider; - if (current < 0) - return; - let meta = this._providerMeta[current]; - let resultDisplay = meta.resultDisplay; - resultDisplay.activateSelected(); - Main.overview.hide(); - } -}; function MoreLink() { this._init(); @@ -654,27 +363,6 @@ SectionHeader.prototype = { Signals.addSignalMethods(SectionHeader.prototype); -function SearchSectionHeader(title, onClick) { - this._init(title, onClick); -} - -SearchSectionHeader.prototype = { - _init : function(title, onClick) { - this.actor = new St.Button({ style_class: 'dash-search-section-header', - x_fill: true, - y_fill: true }); - let box = new St.BoxLayout(); - this.actor.set_child(box); - let titleText = new St.Label({ style_class: 'dash-search-section-title', - text: title }); - this.countText = new St.Label({ style_class: 'dash-search-section-count' }); - - box.add(titleText); - box.add(this.countText, { expand: true, x_fill: false, x_align: St.Align.END }); - - this.actor.connect('clicked', onClick); - } -}; function Section(titleString, suppressBrowse) { this._init(titleString, suppressBrowse); @@ -736,7 +424,7 @@ OldDash.prototype = { this._searchSystem.registerProvider(new PlaceDisplay.PlaceSearchProvider()); this._searchSystem.registerProvider(new DocDisplay.DocSearchProvider()); - this.searchResults = new SearchResults(this._searchSystem); + this.searchResults = new SearchDisplay.SearchResults(this._searchSystem); this.actor.add(this.searchResults.actor); this.searchResults.actor.hide(); diff --git a/js/ui/search.js b/js/ui/search.js index f2a991524..33ecce8a8 100644 --- a/js/ui/search.js +++ b/js/ui/search.js @@ -182,7 +182,7 @@ SearchProvider.prototype = { * implementation will show the icon next to the name. * * The actor should be an instance of St.Widget, with the style class - * 'dash-search-result-content'. + * 'search-result-content'. */ createResultActor: function(resultMeta, terms) { return null; diff --git a/js/ui/searchDisplay.js b/js/ui/searchDisplay.js new file mode 100644 index 000000000..10d1739cc --- /dev/null +++ b/js/ui/searchDisplay.js @@ -0,0 +1,304 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +const Clutter = imports.gi.Clutter; +const Lang = imports.lang; +const Gettext = imports.gettext.domain('gnome-shell'); +const _ = Gettext.gettext; + +const DND = imports.ui.dnd; +const Main = imports.ui.main; +const Search = imports.ui.search; + + +// 25 search results (per result type) should be enough for everyone +const MAX_RENDERED_SEARCH_RESULTS = 25; + +function SearchResult(provider, metaInfo, terms) { + this._init(provider, metaInfo, terms); +} + +SearchResult.prototype = { + _init: function(provider, metaInfo, terms) { + this.provider = provider; + this.metaInfo = metaInfo; + this.actor = new St.Clickable({ style_class: 'search-result', + reactive: true, + x_align: St.Align.START, + x_fill: true, + y_fill: true }); + this.actor._delegate = this; + + let content = provider.createResultActor(metaInfo, terms); + if (content == null) { + content = new St.BoxLayout({ style_class: 'search-result-content' }); + let title = new St.Label({ text: this.metaInfo['name'] }); + let icon = this.metaInfo['icon']; + content.add(icon, { y_fill: false }); + content.add(title, { expand: true, y_fill: false }); + } + this._content = content; + this.actor.set_child(content); + + this.actor.connect('clicked', Lang.bind(this, this._onResultClicked)); + + let draggable = DND.makeDraggable(this.actor); + draggable.connect('drag-begin', + Lang.bind(this, function() { + Main.overview.beginItemDrag(this); + })); + draggable.connect('drag-end', + Lang.bind(this, function() { + Main.overview.endItemDrag(this); + })); + }, + + setSelected: function(selected) { + if (selected) + this._content.add_style_pseudo_class('selected'); + else + this._content.remove_style_pseudo_class('selected'); + }, + + activate: function() { + this.provider.activateResult(this.metaInfo.id); + Main.overview.toggle(); + }, + + _onResultClicked: function(actor, event) { + this.activate(); + }, + + getDragActorSource: function() { + return this.metaInfo['icon']; + }, + + getDragActor: function(stageX, stageY) { + return new Clutter.Clone({ source: this.metaInfo['icon'] }); + }, + + shellWorkspaceLaunch: function() { + if (this.provider.dragActivateResult) + this.provider.dragActivateResult(this.metaInfo.id); + else + this.provider.activateResult(this.metaInfo.id); + } +}; + +function OverflowSearchResults(provider) { + this._init(provider); +} + +OverflowSearchResults.prototype = { + __proto__: Search.SearchResultDisplay.prototype, + + _init: function(provider) { + Search.SearchResultDisplay.prototype._init.call(this, provider); + this.actor = new St.OverflowBox({ style_class: 'search-section-list-results' }); + }, + + getVisibleResultCount: function() { + return this.actor.get_n_visible(); + }, + + renderResults: function(results, terms) { + for (let i = 0; i < results.length && i < MAX_RENDERED_SEARCH_RESULTS; i++) { + let result = results[i]; + let meta = this.provider.getResultMeta(result); + let display = new SearchResult(this.provider, meta, terms); + this.actor.add_actor(display.actor); + } + }, + + selectIndex: function(index) { + let nVisible = this.actor.get_n_visible(); + let children = this.actor.get_children(); + if (this.selectionIndex >= 0) { + let prevActor = children[this.selectionIndex]; + prevActor._delegate.setSelected(false); + } + this.selectionIndex = -1; + if (index >= nVisible) + return false; + else if (index < 0) + return false; + let targetActor = children[index]; + targetActor._delegate.setSelected(true); + this.selectionIndex = index; + return true; + }, + + activateSelected: function() { + let children = this.actor.get_children(); + let targetActor = children[this.selectionIndex]; + targetActor._delegate.activate(); + } +}; + +function SearchResults(searchSystem) { + this._init(searchSystem); +} + +SearchResults.prototype = { + _init: function(searchSystem) { + this._searchSystem = searchSystem; + + this.actor = new St.BoxLayout({ name: 'searchResults', + vertical: true }); + this._statusText = new St.Label({ style_class: 'search-statustext' }); + this.actor.add(this._statusText); + this._selectedProvider = -1; + this._providers = this._searchSystem.getProviders(); + this._providerMeta = []; + for (let i = 0; i < this._providers.length; i++) + this.createProviderMeta(this._providers[i]); + }, + + createProviderMeta: function(provider) { + let providerBox = new St.BoxLayout({ style_class: 'search-section', + vertical: true }); + let titleButton = new St.Button({ style_class: 'search-section-header', + reactive: true, + x_fill: true, + y_fill: true }); + titleButton.connect('clicked', Lang.bind(this, function () { this._onHeaderClicked(provider); })); + providerBox.add(titleButton); + let titleBox = new St.BoxLayout(); + titleButton.set_child(titleBox); + let title = new St.Label({ text: provider.title }); + let count = new St.Label(); + titleBox.add(title, { expand: true }); + titleBox.add(count); + + let resultDisplayBin = new St.Bin({ style_class: 'search-section-results', + x_fill: true, + y_fill: true }); + providerBox.add(resultDisplayBin, { expand: true }); + let resultDisplay = provider.createResultContainerActor(); + if (resultDisplay == null) { + resultDisplay = new OverflowSearchResults(provider); + } + resultDisplayBin.set_child(resultDisplay.actor); + + this._providerMeta.push({ actor: providerBox, + resultDisplay: resultDisplay, + count: count }); + this.actor.add(providerBox); + }, + + _clearDisplay: function() { + this._selectedProvider = -1; + this._visibleResultsCount = 0; + for (let i = 0; i < this._providerMeta.length; i++) { + let meta = this._providerMeta[i]; + meta.resultDisplay.clear(); + meta.actor.hide(); + } + }, + + reset: function() { + this._searchSystem.reset(); + this._statusText.hide(); + this._clearDisplay(); + }, + + startingSearch: function() { + this.reset(); + this._statusText.set_text(_("Searching...")); + this._statusText.show(); + }, + + _metaForProvider: function(provider) { + return this._providerMeta[this._providers.indexOf(provider)]; + }, + + updateSearch: function (searchString) { + let results = this._searchSystem.updateSearch(searchString); + + this._clearDisplay(); + + if (results.length == 0) { + this._statusText.set_text(_("No matching results.")); + this._statusText.show(); + return true; + } else { + this._statusText.hide(); + } + + let terms = this._searchSystem.getTerms(); + + for (let i = 0; i < results.length; i++) { + let [provider, providerResults] = results[i]; + let meta = this._metaForProvider(provider); + meta.actor.show(); + meta.resultDisplay.renderResults(providerResults, terms); + meta.count.set_text('' + providerResults.length); + } + + this.selectDown(false); + + return true; + }, + + _onHeaderClicked: function(provider) { + provider.expandSearch(this._searchSystem.getTerms()); + }, + + _modifyActorSelection: function(resultDisplay, up) { + let success; + let index = resultDisplay.getSelectionIndex(); + if (up && index == -1) + index = resultDisplay.getVisibleResultCount() - 1; + else if (up) + index = index - 1; + else + index = index + 1; + return resultDisplay.selectIndex(index); + }, + + selectUp: function(recursing) { + for (let i = this._selectedProvider; i >= 0; i--) { + let meta = this._providerMeta[i]; + if (!meta.actor.visible) + continue; + let success = this._modifyActorSelection(meta.resultDisplay, true); + if (success) { + this._selectedProvider = i; + return; + } + } + if (this._providerMeta.length > 0 && !recursing) { + this._selectedProvider = this._providerMeta.length - 1; + this.selectUp(true); + } + }, + + selectDown: function(recursing) { + let current = this._selectedProvider; + if (current == -1) + current = 0; + for (let i = current; i < this._providerMeta.length; i++) { + let meta = this._providerMeta[i]; + if (!meta.actor.visible) + continue; + let success = this._modifyActorSelection(meta.resultDisplay, false); + if (success) { + this._selectedProvider = i; + return; + } + } + if (this._providerMeta.length > 0 && !recursing) { + this._selectedProvider = 0; + this.selectDown(true); + } + }, + + activateSelected: function() { + let current = this._selectedProvider; + if (current < 0) + return; + let meta = this._providerMeta[current]; + let resultDisplay = meta.resultDisplay; + resultDisplay.activateSelected(); + Main.overview.hide(); + } +}; From 48fff0e96bf4eabcc6b10d9fb2d24e3dc1696f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Fri, 12 Nov 2010 18:45:29 +0100 Subject: [PATCH 33/61] Use the old dash code to implement the view selector The view selector is a tabbed interface with a search entry. Starting a search switches focus to the results' tab, ending a search moves the focus back to the previously selected tab. Activating a normal tab while a search is active cancels the search. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 128 ++++---- js/Makefile.am | 1 + js/ui/dash.js | 589 ------------------------------------- js/ui/viewSelector.js | 517 ++++++++++++++++++++++++++++++++ po/POTFILES.in | 1 + 5 files changed, 583 insertions(+), 653 deletions(-) create mode 100644 js/ui/viewSelector.js diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 48b527846..a059ef358 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -355,90 +355,92 @@ StTooltip StLabel { spacing: 12px; } +#viewSelector { + spacing: 16px; +} + +#searchArea { + padding: 0px 12px; +} + #searchEntry { - padding: 4px; - border-radius: 4px; - color: #a8a8a8; - border: 1px solid #565656; - background-color: #404040; - caret-color: #fff; + padding: 4px 8px; + border-radius: 12px; + color: rgb(128, 128, 128); + border: 2px solid rgba(128, 128, 128, 0.4); + background-gradient-start: rgba(0, 0, 0, 0.2); + background-gradient-end: rgba(128, 128, 128, 0.2); + background-gradient-direction: vertical; + caret-color: rgb(128, 128, 128); caret-size: 1px; height: 16px; + width: 250px; transition-duration: 300; } #searchEntry:focus { - color: #545454; - border: 1px solid #3a3a3a; - background-color: #e8e8e8; - caret-color: #545454; + border: 2px solid #ffffff; + background-gradient-start: rgba(0, 0, 0, 0.2); + background-gradient-end: #ffffff; + background-gradient-direction: vertical; + color: rgb(64, 64, 64); + font-weight: bold; -st-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9); transition-duration: 0; } #searchEntry:hover { - color: #a8a8a8; - border: 1px solid #4d4d4d; - background-color: #e8e8e8; + border: 2px solid #e8e8e8; caret-color: #545454; transition-duration: 500; } -.dash-section { +.view-tab-title { + color: #888a85; + font-weight: bold; + padding: 0px 12px; +} + +.view-tab-title:selected { + color: white; +} + +.view-tab-boxpointer { + -arrow-border-radius: 9px; + -arrow-background-color: rgba(0,0,0,0.5); + -arrow-border-width: 2px; + -arrow-border-color: rgba(255,255,255,0.5); + -arrow-base: 30px; + -arrow-rise: 15px; +} + +#searchResults { + padding: 20px 10px 10px 10px; +} + +#searchResultsContent { + padding: 0 10px; spacing: 8px; } -.section-header { -} - -.section-header-inner { - spacing: 4px; -} - -.section-text-content { - padding: 4px 0px; -} - -.dash-section-content { - color: #ffffff; - spacing: 8px; -} - -.more-link { -} - -.more-link-expander { - background-image: url("section-more.svg"); - width: 9px; - height: 9px; -} - -.more-link-expander.open { - background-image: url("section-more-open.svg"); - width: 9px; - height: 9px; -} - -.dash-pane { - border-radius: 10px; - background-color: #111111; - border: 2px solid #868686; - color: #ffffff; - padding: 30px 10px 10px 20px; -} - -#dashAppSearchResults { - padding: 8px 0px; -} - .search-statustext, .search-section-header { - padding: 4px 0px; + padding: 4px 12px; spacing: 4px; + color: #6f6f6f; +} + +.search-section { + background-color: rgba(128, 128, 128, .1); + border: 1px solid rgba(50, 50, 50, .4); + border-radius: 10px; } .search-section-results { color: #ffffff; + border-radius: 10px; + border: 1px solid rgba(50, 50, 50, .4); + padding: 6px; } .search-section-list-results { @@ -446,17 +448,15 @@ StTooltip StLabel { } .search-result-content { - padding: 3px; + padding: 4px; } .search-result-content:selected { - padding: 2px; - border: 1px solid #5c5c5c; - border-radius: 2px; - background-color: #1e1e1e; + border-radius: 4px; + background: rgba(255,255,255,0.33); } -.dash-results-container { +.results-container { spacing: 4px; } diff --git a/js/Makefile.am b/js/Makefile.am index b03cd07da..98ec73751 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -49,6 +49,7 @@ nobase_dist_js_DATA = \ ui/status/volume.js \ ui/telepathyClient.js \ ui/tweener.js \ + ui/viewSelector.js \ ui/windowAttentionHandler.js \ ui/windowManager.js \ ui/workspace.js \ diff --git a/js/ui/dash.js b/js/ui/dash.js index 53767052b..1f0a534d4 100644 --- a/js/ui/dash.js +++ b/js/ui/dash.js @@ -1,6 +1,5 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ -const Clutter = imports.gi.Clutter; const Mainloop = imports.mainloop; const Signals = imports.signals; const Lang = imports.lang; @@ -12,597 +11,9 @@ const _ = Gettext.gettext; const AppDisplay = imports.ui.appDisplay; const AppFavorites = imports.ui.appFavorites; const DND = imports.ui.dnd; -const DocDisplay = imports.ui.docDisplay; -const PlaceDisplay = imports.ui.placeDisplay; const Main = imports.ui.main; -const Overview = imports.ui.overview; -const Search = imports.ui.search; -const SearchDisplay = imports.ui.searchDisplay; -const Tweener = imports.ui.tweener; const Workspace = imports.ui.workspace; -/* - * Returns the index in an array of a given length that is obtained - * if the provided index is incremented by an increment and the array - * is wrapped in if necessary. - * - * index: prior index, expects 0 <= index < length - * increment: the change in index, expects abs(increment) <= length - * length: the length of the array - */ -function _getIndexWrapped(index, increment, length) { - return (index + increment + length) % length; -} - -function Pane() { - this._init(); -} - -Pane.prototype = { - _init: function () { - this._open = false; - - this.actor = new St.BoxLayout({ style_class: 'dash-pane', - vertical: true, - reactive: true }); - this.actor.connect('button-press-event', Lang.bind(this, function (a, e) { - // Eat button press events so they don't go through and close the pane - return true; - })); - - // Hidden by default - this.actor.hide(); - }, - - open: function () { - if (this._open) - return; - this._open = true; - this.emit('open-state-changed', this._open); - this.actor.opacity = 0; - this.actor.show(); - Tweener.addTween(this.actor, - { opacity: 255, - time: Overview.PANE_FADE_TIME, - transition: 'easeOutQuad' - }); - }, - - close: function () { - if (!this._open) - return; - this._open = false; - Tweener.addTween(this.actor, - { opacity: 0, - time: Overview.PANE_FADE_TIME, - transition: 'easeOutQuad', - onComplete: Lang.bind(this, function() { - this.actor.hide(); - this.emit('open-state-changed', this._open); - }) - }); - }, - - destroyContent: function() { - let children = this.actor.get_children(); - for (let i = 0; i < children.length; i++) { - children[i].destroy(); - } - }, - - toggle: function () { - if (this._open) - this.close(); - else - this.open(); - } -}; -Signals.addSignalMethods(Pane.prototype); - -function ResultArea() { - this._init(); -} - -ResultArea.prototype = { - _init : function() { - this.actor = new St.BoxLayout({ vertical: true }); - this.resultsContainer = new St.BoxLayout({ style_class: 'dash-results-container' }); - this.actor.add(this.resultsContainer, { expand: true }); - - this.display = new DocDisplay.DocDisplay(); - this.resultsContainer.add(this.display.actor, { expand: true }); - this.display.load(); - } -}; - -function ResultPane(dash) { - this._init(dash); -} - -ResultPane.prototype = { - __proto__: Pane.prototype, - - _init: function(dash) { - Pane.prototype._init.call(this); - - let resultArea = new ResultArea(); - this.actor.add(resultArea.actor, { expand: true }); - this.connect('open-state-changed', Lang.bind(this, function(pane, isOpen) { - resultArea.display.resetState(); - })); - } -}; - -function SearchEntry() { - this._init(); -} - -SearchEntry.prototype = { - _init : function() { - this.actor = new St.Entry({ name: 'searchEntry', - hint_text: _("Find") }); - this.entry = this.actor.clutter_text; - - this.actor.clutter_text.connect('text-changed', Lang.bind(this, - function() { - if (this.isActive()) - this.actor.set_secondary_icon_from_file(global.imagedir + - 'close-black.svg'); - else - this.actor.set_secondary_icon_from_file(null); - })); - this.actor.connect('secondary-icon-clicked', Lang.bind(this, - function() { - this.reset(); - })); - this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); - - global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility)); - - this.pane = null; - - this._capturedEventId = 0; - }, - - _updateCursorVisibility: function() { - let focus = global.stage.get_key_focus(); - if (focus == global.stage || focus == this.entry) - this.entry.set_cursor_visible(true); - else - this.entry.set_cursor_visible(false); - }, - - show: function() { - if (this._capturedEventId == 0) - this._capturedEventId = global.stage.connect('captured-event', - Lang.bind(this, this._onCapturedEvent)); - this.entry.set_cursor_visible(true); - this.entry.set_selection(0, 0); - }, - - hide: function() { - if (this.isActive()) - this.reset(); - if (this._capturedEventId > 0) { - global.stage.disconnect(this._capturedEventId); - this._capturedEventId = 0; - } - }, - - reset: function () { - let [x, y, mask] = global.get_pointer(); - let actor = global.stage.get_actor_at_pos (Clutter.PickMode.REACTIVE, - x, y); - // this.actor is never hovered directly, only its clutter_text and icon - let hovered = this.actor == actor.get_parent(); - - this.actor.set_hover(hovered); - - this.entry.text = ''; - global.stage.set_key_focus(null); - this.entry.set_cursor_visible(true); - this.entry.set_selection(0, 0); - }, - - getText: function () { - return this.entry.get_text().replace(/^\s+/g, '').replace(/\s+$/g, ''); - }, - - // some search term has been entered - isActive: function() { - return this.actor.get_text() != ''; - }, - - // the entry does not show the hint - _isActivated: function() { - return this.entry.text == this.actor.get_text(); - }, - - _onCapturedEvent: function(actor, event) { - let source = event.get_source(); - let panelEvent = source && Main.panel.actor.contains(source); - - switch (event.type()) { - case Clutter.EventType.BUTTON_PRESS: - // the user clicked outside after activating the entry, but - // with no search term entered - cancel the search - if (source != this.entry && this.entry.text == '') { - this.reset(); - // allow only panel events to continue - return !panelEvent; - } - return false; - case Clutter.EventType.KEY_PRESS: - // If neither the stage nor our entry have key focus, some - // "special" actor grabbed the focus (run dialog, looking - // glass); we don't want to interfere with that - let focus = global.stage.get_key_focus(); - if (focus != global.stage && focus != this.entry) - return false; - - let sym = event.get_key_symbol(); - - // If we have an active search, Escape cancels it - if we - // haven't, the key is ignored - if (sym == Clutter.Escape) - if (this._isActivated()) { - this.reset(); - return true; - } else { - return false; - } - - // Ignore non-printable keys - if (!Clutter.keysym_to_unicode(sym)) - return false; - - // Search started - move the key focus to the entry and - // "repeat" the event - if (!this._isActivated()) { - global.stage.set_key_focus(this.entry); - this.entry.event(event, false); - } - - return false; - default: - // Suppress all other events outside the panel while the entry - // is activated and no search has been entered - any click - // outside the entry will cancel the search - return (this.entry.text == '' && !panelEvent); - } - }, - - _onDestroy: function() { - if (this._capturedEventId > 0) { - global.stage.disconnect(this._capturedEventId); - this._capturedEventId = 0; - } - } -}; -Signals.addSignalMethods(SearchEntry.prototype); - - -function MoreLink() { - this._init(); -} - -MoreLink.prototype = { - _init : function () { - this.actor = new St.BoxLayout({ style_class: 'more-link', - reactive: true }); - this.pane = null; - - this._expander = new St.Bin({ style_class: 'more-link-expander' }); - this.actor.add(this._expander, { expand: true, y_fill: false }); - }, - - activate: function() { - if (!this.actor.visible) - return true; // If the link isn't visible we don't want the header to react - // to clicks - if (this.pane == null) { - // Ensure the pane is created; the activated handler will call setPane - this.emit('activated'); - } - this._pane.toggle(); - return true; - }, - - setPane: function (pane) { - this._pane = pane; - this._pane.connect('open-state-changed', Lang.bind(this, function(pane, isOpen) { - if (isOpen) - this._expander.add_style_class_name('open'); - else - this._expander.remove_style_class_name('open'); - })); - } -}; - -Signals.addSignalMethods(MoreLink.prototype); - -function SectionHeader(title, suppressBrowse) { - this._init(title, suppressBrowse); -} - -SectionHeader.prototype = { - _init : function (title, suppressBrowse) { - this.actor = new St.Bin({ style_class: 'section-header', - x_align: St.Align.START, - x_fill: true, - y_fill: true, - reactive: !suppressBrowse }); - this._innerBox = new St.BoxLayout({ style_class: 'section-header-inner' }); - this.actor.set_child(this._innerBox); - - let textBox = new St.BoxLayout({ style_class: 'section-text-content' }); - this.text = new St.Label({ style_class: 'section-title', - text: title }); - textBox.add(this.text, { x_align: St.Align.START }); - - this._innerBox.add(textBox, { expand: true }); - - if (!suppressBrowse) { - this.moreLink = new MoreLink(); - this._innerBox.add(this.moreLink.actor, { x_align: St.Align.END }); - this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); - } - }, - - _onButtonPress: function() { - this.moreLink.activate(); - }, - - setMoreLinkVisible : function(visible) { - if (visible) - this.moreLink.actor.show(); - else - this.moreLink.actor.hide(); - } -}; - -Signals.addSignalMethods(SectionHeader.prototype); - - -function Section(titleString, suppressBrowse) { - this._init(titleString, suppressBrowse); -} - -Section.prototype = { - _init: function(titleString, suppressBrowse) { - this.actor = new St.BoxLayout({ style_class: 'dash-section', - vertical: true }); - this.header = new SectionHeader(titleString, suppressBrowse); - this.actor.add(this.header.actor); - this.content = new St.BoxLayout({ style_class: 'dash-section-content', - vertical: true }); - this.actor.add(this.content); - } -}; - -function OldDash() { - this._init(); -} - -OldDash.prototype = { - _init : function() { - // dash and the popup panes need to be reactive so that the clicks in unoccupied places on them - // are not passed to the transparent background underneath them. This background is used for the workspaces area when - // the additional dash panes are being shown and it handles clicks by closing the additional panes, so that the user - // can interact with the workspaces. However, this behavior is not desirable when the click is actually over a pane. - // - // We have to make the individual panes reactive instead of making the whole dash actor reactive because the width - // of the Group actor ends up including the width of its hidden children, so we were getting a reactive object as - // wide as the details pane that was blocking the clicks to the workspaces underneath it even when the details pane - // was actually hidden. - this.actor = new St.BoxLayout({ name: 'dash', - vertical: true, - reactive: true }); - - // The searchArea just holds the entry - this.searchArea = new St.BoxLayout({ name: 'dashSearchArea', - vertical: true }); - this.sectionArea = new St.BoxLayout({ name: 'dashSections', - vertical: true }); - - this.actor.add(this.searchArea); - this.actor.add(this.sectionArea); - - // The currently active popup display - this._activePane = null; - - /***** Search *****/ - - this._searchActive = false; - this._searchPending = false; - this._searchEntry = new SearchEntry(); - this.searchArea.add(this._searchEntry.actor, { y_fill: false, expand: true }); - - this._searchSystem = new Search.SearchSystem(); - this._searchSystem.registerProvider(new AppDisplay.AppSearchProvider()); - this._searchSystem.registerProvider(new AppDisplay.PrefsSearchProvider()); - this._searchSystem.registerProvider(new PlaceDisplay.PlaceSearchProvider()); - this._searchSystem.registerProvider(new DocDisplay.DocSearchProvider()); - - this.searchResults = new SearchDisplay.SearchResults(this._searchSystem); - this.actor.add(this.searchResults.actor); - this.searchResults.actor.hide(); - - this._keyPressId = 0; - this._searchTimeoutId = 0; - this._searchEntry.entry.connect('text-changed', Lang.bind(this, function (se, prop) { - let searchPreviouslyActive = this._searchActive; - this._searchActive = this._searchEntry.isActive(); - this._searchPending = this._searchActive && !searchPreviouslyActive; - if (this._searchPending) { - this.searchResults.startingSearch(); - } - if (this._searchActive) { - this.searchResults.actor.show(); - this.sectionArea.hide(); - } else { - this.searchResults.actor.hide(); - this.sectionArea.show(); - } - if (!this._searchActive) { - if (this._searchTimeoutId > 0) { - Mainloop.source_remove(this._searchTimeoutId); - this._searchTimeoutId = 0; - } - return; - } - if (this._searchTimeoutId > 0) - return; - this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch)); - })); - this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) { - if (this._searchTimeoutId > 0) { - Mainloop.source_remove(this._searchTimeoutId); - this._doSearch(); - } - this.searchResults.activateSelected(); - return true; - })); - - /***** Applications *****/ - - this._appsSection = new Section(_("APPLICATIONS")); - let appWell = new AppDisplay.AppWell(); - this._appsSection.content.add(appWell.actor, { expand: true }); - - this._allApps = null; - this._appsSection.header.moreLink.connect('activated', Lang.bind(this, function (link) { - if (this._allApps == null) { - this._allApps = new AppDisplay.AllAppDisplay(); - this._addPane(this._allApps, St.Align.START); - link.setPane(this._allApps); - } - })); - - this.sectionArea.add(this._appsSection.actor); - - /***** Places *****/ - - /* Translators: This is in the sense of locations for documents, - network locations, etc. */ - this._placesSection = new Section(_("PLACES & DEVICES"), true); - let placesDisplay = new PlaceDisplay.DashPlaceDisplay(); - this._placesSection.content.add(placesDisplay.actor, { expand: true }); - this.sectionArea.add(this._placesSection.actor); - - /***** Documents *****/ - - this._docsSection = new Section(_("RECENT ITEMS")); - - this._docDisplay = new DocDisplay.DashDocDisplay(); - this._docsSection.content.add(this._docDisplay.actor, { expand: true }); - - this._moreDocsPane = null; - this._docsSection.header.moreLink.connect('activated', Lang.bind(this, function (link) { - if (this._moreDocsPane == null) { - this._moreDocsPane = new ResultPane(this); - this._addPane(this._moreDocsPane, St.Align.END); - link.setPane(this._moreDocsPane); - } - })); - - this._docDisplay.connect('changed', Lang.bind(this, function () { - this._docsSection.header.setMoreLinkVisible( - this._docDisplay.actor.get_children().length > 0); - })); - this._docDisplay.emit('changed'); - - this.sectionArea.add(this._docsSection.actor, { expand: true }); - }, - - _onKeyPress: function(stage, event) { - // If neither the stage nor the search entry have key focus, some - // "special" actor grabbed the focus (run dialog, looking glass); - // we don't want to interfere with that - let focus = stage.get_key_focus(); - if (focus != stage && focus != this._searchEntry.entry) - return false; - - let symbol = event.get_key_symbol(); - if (symbol == Clutter.Escape) { - // If we're in one of the "more" modes or showing the - // details pane, close them - if (this._activePane != null) - this._activePane.close(); - // Otherwise, just close the Overview entirely - else - Main.overview.hide(); - return true; - } else if (symbol == Clutter.Up) { - if (!this._searchActive) - return true; - this.searchResults.selectUp(false); - - return true; - } else if (symbol == Clutter.Down) { - if (!this._searchActive) - return true; - - this.searchResults.selectDown(false); - return true; - } - return false; - }, - - _doSearch: function () { - this._searchTimeoutId = 0; - let text = this._searchEntry.getText(); - this.searchResults.updateSearch(text); - - return false; - }, - - addSearchProvider: function(provider) { - //Add a new search provider to the dash. - - this._searchSystem.registerProvider(provider); - this.searchResults.createProviderMeta(provider); - }, - - show: function() { - this._searchEntry.show(); - if (this._keyPressId == 0) - this._keyPressId = global.stage.connect('key-press-event', - Lang.bind(this, this._onKeyPress)); - }, - - hide: function() { - this._firstSelectAfterOverlayShow = true; - this._searchEntry.hide(); - if (this._activePane != null) - this._activePane.close(); - if (this._keyPressId > 0) { - global.stage.disconnect(this._keyPressId); - this._keyPressId = 0; - } - }, - - closePanes: function () { - if (this._activePane != null) - this._activePane.close(); - }, - - _addPane: function(pane, align) { - pane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) { - if (isOpen) { - if (pane != this._activePane && this._activePane != null) { - this._activePane.close(); - } - this._activePane = pane; - } else if (pane == this._activePane) { - this._activePane = null; - } - })); - Main.overview.addPane(pane, align); - } -}; -Signals.addSignalMethods(Dash.prototype); - function Dash() { this._init(); diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js new file mode 100644 index 000000000..20c640ee0 --- /dev/null +++ b/js/ui/viewSelector.js @@ -0,0 +1,517 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +const Clutter = imports.gi.Clutter; +const Mainloop = imports.mainloop; +const Meta = imports.gi.Meta; +const Signals = imports.signals; +const Lang = imports.lang; +const Shell = imports.gi.Shell; +const St = imports.gi.St; +const Gettext = imports.gettext.domain('gnome-shell'); +const _ = Gettext.gettext; + +const Main = imports.ui.main; +const Search = imports.ui.search; +const SearchDisplay = imports.ui.searchDisplay; +const Tweener = imports.ui.tweener; + + +function SearchEntry() { + this._init(); +} + +SearchEntry.prototype = { + _init : function() { + this.actor = new St.Entry({ name: 'searchEntry', + hint_text: _("Search your computer") }); + this.entry = this.actor.clutter_text; + + this.actor.clutter_text.connect('text-changed', Lang.bind(this, + function() { + if (this.isActive()) + this.actor.set_secondary_icon_from_file(global.imagedir + + 'close-black.svg'); + else + this.actor.set_secondary_icon_from_file(null); + })); + this.actor.connect('secondary-icon-clicked', Lang.bind(this, + function() { + this.reset(); + })); + this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); + + global.stage.connect('notify::key-focus', Lang.bind(this, this._updateCursorVisibility)); + + this.pane = null; + + this._capturedEventId = 0; + }, + + _updateCursorVisibility: function() { + let focus = global.stage.get_key_focus(); + if (focus == global.stage || focus == this.entry) + this.entry.set_cursor_visible(true); + else + this.entry.set_cursor_visible(false); + }, + + show: function() { + if (this._capturedEventId == 0) + this._capturedEventId = global.stage.connect('captured-event', + Lang.bind(this, this._onCapturedEvent)); + this.entry.set_cursor_visible(true); + this.entry.set_selection(0, 0); + }, + + hide: function() { + if (this.isActive()) + this.reset(); + if (this._capturedEventId > 0) { + global.stage.disconnect(this._capturedEventId); + this._capturedEventId = 0; + } + }, + + reset: function () { + let [x, y, mask] = global.get_pointer(); + let actor = global.stage.get_actor_at_pos (Clutter.PickMode.REACTIVE, + x, y); + // this.actor is never hovered directly, only its clutter_text and icon + let hovered = this.actor == actor.get_parent(); + + this.actor.set_hover(hovered); + + this.entry.text = ''; + this.entry.set_cursor_visible(true); + this.entry.set_selection(0, 0); + }, + + getText: function () { + return this.entry.get_text().replace(/^\s+/g, '').replace(/\s+$/g, ''); + }, + + // some search term has been entered + isActive: function() { + return this.actor.get_text() != ''; + }, + + // the entry does not show the hint + _isActivated: function() { + return this.entry.text == this.actor.get_text(); + }, + + _onCapturedEvent: function(actor, event) { + let source = event.get_source(); + let panelEvent = source && Main.panel.actor.contains(source); + + switch (event.type()) { + case Clutter.EventType.BUTTON_PRESS: + // the user clicked outside after activating the entry, but + // with no search term entered - cancel the search + if (source != this.entry && this.entry.text == '') { + this.reset(); + // allow only panel events to continue + return !panelEvent; + } + return false; + case Clutter.EventType.KEY_PRESS: + // If neither the stage nor our entry have key focus, some + // "special" actor grabbed the focus (run dialog, looking + // glass); we don't want to interfere with that + let focus = global.stage.get_key_focus(); + if (focus != global.stage && focus != this.entry) + return false; + + let sym = event.get_key_symbol(); + + // If we have an active search, Escape cancels it - if we + // haven't, the key is ignored + if (sym == Clutter.Escape) + if (this._isActivated()) { + this.reset(); + return true; + } else { + return false; + } + + // Ignore non-printable keys + if (!Clutter.keysym_to_unicode(sym)) + return false; + + // Search started - move the key focus to the entry and + // "repeat" the event + if (!this._isActivated()) { + global.stage.set_key_focus(this.entry); + this.entry.event(event, false); + } + + return false; + default: + // Suppress all other events outside the panel while the entry + // is activated and no search has been entered - any click + // outside the entry will cancel the search + return (this.entry.text == '' && !panelEvent); + } + }, + + _onDestroy: function() { + if (this._capturedEventId > 0) { + global.stage.disconnect(this._capturedEventId); + this._capturedEventId = 0; + } + } +}; +Signals.addSignalMethods(SearchEntry.prototype); + + +function BaseTab(titleActor, pageActor) { + this._init(titleActor, pageActor); +} + +BaseTab.prototype = { + _init: function(titleActor, pageActor) { + this.title = titleActor; + this.page = new St.Bin({ child: pageActor, + x_align: St.Align.START, + y_align: St.Align.START, + x_fill: true, + y_fill: true, + style_class: 'view-tab-page' }); + + this.visible = false; + }, + + show: function() { + this.visible = true; + this.page.opacity = 0; + this.page.show(); + + Tweener.addTween(this.page, + { opacity: 255, + time: 0.1, + transition: 'easeOutQuad' }); + }, + + hide: function() { + this.visible = false; + Tweener.addTween(this.page, + { opacity: 0, + time: 0.1, + transition: 'easeOutQuad', + onComplete: Lang.bind(this, + function() { + this.page.hide(); + }) + }); + }, + + _activate: function() { + this.emit('activated'); + } +}; +Signals.addSignalMethods(BaseTab.prototype); + + +function ViewTab(label, pageActor) { + this._init(label, pageActor); +} + +ViewTab.prototype = { + __proto__: BaseTab.prototype; + + _init: function(label, pageActor) { + let titleActor = new St.Button({ label: label, + style_class: 'view-tab-title' }); + titleActor.connect('clicked', Lang.bind(this, this._activate)); + + BaseTab.prototype._init.call(this, titleActor, pageActor); + } +}; + + +function SearchTab(entryActor, pageActor) { + this._init(entryActor, pageActor); +} + +SearchTab.prototype = { + __proto__: BaseTab.prototype +}; + + +function ViewSelector() { + this._init(); +} + +ViewSelector.prototype = { + _init : function() { + this.actor = new St.BoxLayout({ name: 'viewSelector', + vertical: true }); + + // The tab bar is located at the top of the view selector and + // holds both "normal" tab labels and the search entry. The former + // is left aligned, the latter right aligned - unless the text + // direction is RTL, in which case the order is reversed. + this._tabBar = new Shell.GenericContainer(); + this._tabBar.connect('get-preferred-width', + Lang.bind(this, this._getPreferredTabBarWidth)); + this._tabBar.connect('get-preferred-height', + Lang.bind(this, this._getPreferredTabBarHeight)); + this._tabBar.connect('allocate', + Lang.bind(this, this._allocateTabBar)); + this.actor.add(this._tabBar); + + // Box to hold "normal" tab labels + this._tabBox = new St.BoxLayout({ name: 'viewSelectorTabBar' }); + this._tabBar.add_actor(this._tabBox); + + // The searchArea just holds the entry + this._searchArea = new St.Bin({ name: 'searchArea' }); + this._tabBar.add_actor(this._searchArea); + + // The page area holds the tab pages. Every page is given the + // area's full allocation, so that the pages would appear on top + // of each other if the inactive ones weren't hidden. + this._pageArea = new Shell.Stack(); + this.actor.add(this._pageArea, { x_fill: true, + y_fill: true, + expand: true }); + + this._tabs = []; + this._activeTab = null; + + this._itemDragBeginId = 0; + this._overviewHidingId = 0; + + // Public constraints which may be used to tie actors' height or + // vertical position to the current tab's content; as the content's + // height and position depend on the view selector's style properties + // (e.g. font size, padding, spacing, ...) it would be extremely hard + // and ugly to get these from the outside. While it would be possible + // to use position and height properties directly, outside code would + // need to ensure that the content is properly allocated before + // accessing the properties. + this.constrainY = new Clutter.BindConstraint({ source: this._pageArea, + coordinate: Clutter.BindCoordinate.Y }); + this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea, + coordinate: Clutter.BindCoordinate.HEIGHT }); + + /***** Search *****/ + this._searchActive = false; + this._searchPending = false; + this._searchEntry = new SearchEntry(); + this._searchArea.set_child(this._searchEntry.actor); + + this._searchSystem = new Search.SearchSystem(); + + this.searchResults = new SearchDisplay.SearchResults(this._searchSystem); + this._searchTab = new SearchTab(this._searchArea, + this.searchResults.actor); + this._pageArea.add_actor(this._searchTab.page); + this._searchTab.hide(); + + this._keyPressId = 0; + this._searchTimeoutId = 0; + this._searchEntry.entry.connect('text-changed', Lang.bind(this, function (se, prop) { + let searchPreviouslyActive = this._searchActive; + this._searchActive = this._searchEntry.isActive(); + this._searchPending = this._searchActive && !searchPreviouslyActive; + if (this._searchPending) { + this.searchResults.startingSearch(); + } + if (this._searchActive) { + this._switchTab(this._searchTab); + } else { + this._switchTab(this._activeTab); + } + if (!this._searchActive) { + if (this._searchTimeoutId > 0) { + Mainloop.source_remove(this._searchTimeoutId); + this._searchTimeoutId = 0; + } + return; + } + if (this._searchTimeoutId > 0) + return; + this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch)); + })); + this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) { + if (this._searchTimeoutId > 0) { + Mainloop.source_remove(this._searchTimeoutId); + this._doSearch(); + } + this.searchResults.activateSelected(); + return true; + })); + }, + + addViewTab: function(title, pageActor) { + let viewTab = new ViewTab(title, pageActor); + this._tabs.push(viewTab); + this._tabBox.add(viewTab.title); + this._pageArea.add_actor(viewTab.page); + viewTab.page.hide(); + + viewTab.connect('activated', Lang.bind(this, + function(tab) { + this._switchTab(tab); + })); + }, + + _switchTab: function(tab) { + if (this._activeTab && this._activeTab.visible) { + if (this._activeTab == tab) + return; + this._activeTab.title.remove_style_pseudo_class('selected'); + this._activeTab.hide(); + } + + if (tab != this._searchTab) { + tab.title.add_style_pseudo_class('selected'); + this._activeTab = tab; + if (this._searchTab.visible) { + this._searchTab.hide(); + this._searchEntry.reset(); + } + } + + if (!tab.visible) + tab.show(); + }, + + _switchDefaultTab: function() { + if (this._tabs.length > 0) + this._switchTab(this._tabs[0]); + }, + + _getPreferredTabBarWidth: function(box, forHeight, alloc) { + let children = box.get_children(); + for (let i = 0; i < children.length; i++) { + let [childMin, childNat] = children[i].get_preferred_width(forHeight); + alloc.min_size += childMin; + alloc.natural_size += childNat; + } + }, + + _getPreferredTabBarHeight: function(box, forWidth, alloc) { + let children = box.get_children(); + for (let i = 0; i < children.length; i++) { + let [childMin, childNatural] = children[i].get_preferred_height(forWidth); + if (childMin > alloc.min_size) + alloc.min_size = childMin; + if (childNatural > alloc.natural_size) + alloc.natural_size = childNatural; + } + }, + + _allocateTabBar: function(container, box, flags) { + let allocWidth = box.x2 - box.x1; + let allocHeight = box.y2 - box.y1; + + let [searchMinWidth, searchNatWidth] = this._searchArea.get_preferred_width(-1); + let [barMinWidth, barNatWidth] = this._tabBox.get_preferred_width(-1); + let childBox = new Clutter.ActorBox(); + childBox.y1 = 0; + childBox.y2 = allocHeight; + if (this.actor.get_direction() == St.TextDirection.RTL) { + childBox.x1 = allocWidth - barNatWidth; + childBox.x2 = allocWidth; + } else { + childBox.x1 = 0; + childBox.x2 = barNatWidth; + } + this._tabBox.allocate(childBox, flags); + + if (this.actor.get_direction() == St.TextDirection.RTL) { + childBox.x1 = 0; + childBox.x2 = searchNatWidth; + } else { + childBox.x1 = allocWidth - searchNatWidth; + childBox.x2 = allocWidth; + } + this._searchArea.allocate(childBox, flags); + + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, + function() { + this.constrainY.offset = this.actor.y; + })); + }, + + _onKeyPress: function(stage, event) { + // If neither the stage nor the search entry have key focus, some + // "special" actor grabbed the focus (run dialog, looking glass); + // we don't want to interfere with that + let focus = stage.get_key_focus(); + if (focus != stage && focus != this._searchEntry.entry) + return false; + + let symbol = event.get_key_symbol(); + if (symbol == Clutter.Escape) { + Main.overview.hide(); + return true; + } else if (symbol == Clutter.Up) { + if (!this._searchActive) + return true; + this.searchResults.selectUp(false); + + return true; + } else if (symbol == Clutter.Down) { + if (!this._searchActive) + return true; + + this.searchResults.selectDown(false); + return true; + } + return false; + }, + + _doSearch: function () { + this._searchTimeoutId = 0; + let text = this._searchEntry.getText(); + this.searchResults.updateSearch(text); + + return false; + }, + + addSearchProvider: function(provider) { + this._searchSystem.registerProvider(provider); + this.searchResults.createProviderMeta(provider); + }, + + show: function() { + // Connect type-as-you-find listener + this._searchEntry.show(); + + if (this._itemDragBeginId == 0) + this._itemDragBeginId = Main.overview.connect('item-drag-begin', + Lang.bind(this, this._switchDefaultTab)); + if (this._overviewHidingId == 0) + this._overviewHidingId = Main.overview.connect('hiding', + Lang.bind(this, this._switchDefaultTab)); + if (this._keyPressId == 0) + this._keyPressId = global.stage.connect('key-press-event', + Lang.bind(this, this._onKeyPress)); + + this._switchDefaultTab(); + }, + + hide: function() { + // Disconnect type-as-you-find listener + this._searchEntry.hide(); + + if (this._keyPressId > 0) { + global.stage.disconnect(this._keyPressId); + this._keyPressId = 0; + } + + if (this._itemDragBeginId > 0) { + Main.overview.disconnect(this._itemDragBeginId); + this._itemDragBeginId = 0; + } + + if (this._overviewHidingId > 0) { + Main.overview.disconnect(this._overviewHidingId); + this._overviewHidingId = 0; + } + } +}; +Signals.addSignalMethods(ViewSelector.prototype); diff --git a/po/POTFILES.in b/po/POTFILES.in index c9fd4435d..fda5f6a7f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -15,6 +15,7 @@ js/ui/popupMenu.js js/ui/runDialog.js js/ui/statusMenu.js js/ui/status/accessibility.js +js/ui/viewSelector.js js/ui/windowAttentionHandler.js js/ui/workspacesView.js src/gvc/gvc-mixer-control.c From 688a315cbf744895a18134d797c700accfc6874e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 18 Nov 2010 13:51:47 +0100 Subject: [PATCH 34/61] view-selector: Move search logic into SearchTab The view selector should only deal with view switching, so move the logic to deal with search (find-as-you-type, cancelling a search, navigating/activating results) into the SearchTab. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- js/ui/viewSelector.js | 236 ++++++++++++++++++++++++++---------------- 1 file changed, 145 insertions(+), 91 deletions(-) diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js index 20c640ee0..d0d0676ef 100644 --- a/js/ui/viewSelector.js +++ b/js/ui/viewSelector.js @@ -64,8 +64,6 @@ SearchEntry.prototype = { }, hide: function() { - if (this.isActive()) - this.reset(); if (this._capturedEventId > 0) { global.stage.disconnect(this._capturedEventId); this._capturedEventId = 0; @@ -82,6 +80,10 @@ SearchEntry.prototype = { this.actor.set_hover(hovered); this.entry.text = ''; + + // Return focus to the stage + global.stage.set_key_focus(null); + this.entry.set_cursor_visible(true); this.entry.set_selection(0, 0); }, @@ -217,7 +219,7 @@ function ViewTab(label, pageActor) { } ViewTab.prototype = { - __proto__: BaseTab.prototype; + __proto__: BaseTab.prototype, _init: function(label, pageActor) { let titleActor = new St.Button({ label: label, @@ -229,12 +231,124 @@ ViewTab.prototype = { }; -function SearchTab(entryActor, pageActor) { - this._init(entryActor, pageActor); +function SearchTab() { + this._init(); } SearchTab.prototype = { - __proto__: BaseTab.prototype + __proto__: BaseTab.prototype, + + _init: function() { + this._searchActive = false; + this._searchPending = false; + this._keyPressId = 0; + this._searchTimeoutId = 0; + + this._searchSystem = new Search.SearchSystem(); + + this._searchEntry = new SearchEntry(); + this._searchResults = new SearchDisplay.SearchResults(this._searchSystem); + BaseTab.prototype._init.call(this, + this._searchEntry.actor, + this._searchResults.actor); + this._searchEntry.entry.connect('text-changed', + Lang.bind(this, this._onTextChanged)); + this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) { + if (this._searchTimeoutId > 0) { + Mainloop.source_remove(this._searchTimeoutId); + this._doSearch(); + } + this._searchResults.activateSelected(); + return true; + })); + }, + + setFindAsYouType: function(enabled) { + if (enabled) + this._searchEntry.show(); + else + this._searchEntry.hide(); + }, + + show: function() { + BaseTab.prototype.show.call(this); + + if (this._keyPressId == 0) + this._keyPressId = global.stage.connect('key-press-event', + Lang.bind(this, this._onKeyPress)); + }, + + hide: function() { + BaseTab.prototype.hide.call(this); + + if (this._keyPressId > 0) { + global.stage.disconnect(this._keyPressId); + this._keyPressId = 0; + } + this._searchEntry.reset(); + }, + + addSearchProvider: function(provider) { + this._searchSystem.registerProvider(provider); + this._searchResults.createProviderMeta(provider); + }, + + _onTextChanged: function (se, prop) { + let searchPreviouslyActive = this._searchActive; + this._searchActive = this._searchEntry.isActive(); + this._searchPending = this._searchActive && !searchPreviouslyActive; + if (this._searchPending) { + this._searchResults.startingSearch(); + } + if (this._searchActive) { + this._activate(); + } else { + this.emit('search-cancelled'); + } + if (!this._searchActive) { + if (this._searchTimeoutId > 0) { + Mainloop.source_remove(this._searchTimeoutId); + this._searchTimeoutId = 0; + } + return; + } + if (this._searchTimeoutId > 0) + return; + this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch)); + }, + + _onKeyPress: function(stage, event) { + // If neither the stage nor the search entry have key focus, some + // "special" actor grabbed the focus (run dialog, looking glass); + // we don't want to interfere with that + let focus = stage.get_key_focus(); + if (focus != stage && focus != this._searchEntry.entry) + return false; + + let symbol = event.get_key_symbol(); + if (symbol == Clutter.Up) { + if (!this._searchActive) + return true; + this._searchResults.selectUp(false); + + return true; + } else if (symbol == Clutter.Down) { + if (!this._searchActive) + return true; + + this._searchResults.selectDown(false); + return true; + } + return false; + }, + + _doSearch: function () { + this._searchTimeoutId = 0; + let text = this._searchEntry.getText(); + this._searchResults.updateSearch(text); + + return false; + } }; @@ -279,6 +393,16 @@ ViewSelector.prototype = { this._tabs = []; this._activeTab = null; + this._searchTab = new SearchTab(); + this._searchArea.set_child(this._searchTab.title); + this._addTab(this._searchTab); + + this._searchTab.connect('search-cancelled', Lang.bind(this, + function() { + this._switchTab(this._activeTab); + })); + + this._keyPressId = 0; this._itemDragBeginId = 0; this._overviewHidingId = 0; @@ -294,53 +418,13 @@ ViewSelector.prototype = { coordinate: Clutter.BindCoordinate.Y }); this.constrainHeight = new Clutter.BindConstraint({ source: this._pageArea, coordinate: Clutter.BindCoordinate.HEIGHT }); + }, - /***** Search *****/ - this._searchActive = false; - this._searchPending = false; - this._searchEntry = new SearchEntry(); - this._searchArea.set_child(this._searchEntry.actor); - - this._searchSystem = new Search.SearchSystem(); - - this.searchResults = new SearchDisplay.SearchResults(this._searchSystem); - this._searchTab = new SearchTab(this._searchArea, - this.searchResults.actor); - this._pageArea.add_actor(this._searchTab.page); - this._searchTab.hide(); - - this._keyPressId = 0; - this._searchTimeoutId = 0; - this._searchEntry.entry.connect('text-changed', Lang.bind(this, function (se, prop) { - let searchPreviouslyActive = this._searchActive; - this._searchActive = this._searchEntry.isActive(); - this._searchPending = this._searchActive && !searchPreviouslyActive; - if (this._searchPending) { - this.searchResults.startingSearch(); - } - if (this._searchActive) { - this._switchTab(this._searchTab); - } else { - this._switchTab(this._activeTab); - } - if (!this._searchActive) { - if (this._searchTimeoutId > 0) { - Mainloop.source_remove(this._searchTimeoutId); - this._searchTimeoutId = 0; - } - return; - } - if (this._searchTimeoutId > 0) - return; - this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch)); - })); - this._searchEntry.entry.connect('activate', Lang.bind(this, function (se) { - if (this._searchTimeoutId > 0) { - Mainloop.source_remove(this._searchTimeoutId); - this._doSearch(); - } - this.searchResults.activateSelected(); - return true; + _addTab: function(tab) { + tab.page.hide(); + this._pageArea.add_actor(tab.page); + tab.connect('activated', Lang.bind(this, function(tab) { + this._switchTab(tab); })); }, @@ -348,13 +432,7 @@ ViewSelector.prototype = { let viewTab = new ViewTab(title, pageActor); this._tabs.push(viewTab); this._tabBox.add(viewTab.title); - this._pageArea.add_actor(viewTab.page); - viewTab.page.hide(); - - viewTab.connect('activated', Lang.bind(this, - function(tab) { - this._switchTab(tab); - })); + this._addTab(viewTab); }, _switchTab: function(tab) { @@ -370,7 +448,6 @@ ViewSelector.prototype = { this._activeTab = tab; if (this._searchTab.visible) { this._searchTab.hide(); - this._searchEntry.reset(); } } @@ -437,49 +514,27 @@ ViewSelector.prototype = { }, _onKeyPress: function(stage, event) { - // If neither the stage nor the search entry have key focus, some - // "special" actor grabbed the focus (run dialog, looking glass); - // we don't want to interfere with that + // Only process events if the stage has key focus - search is handled + // by the search tab, and we do not want to interfere with "special" + // actors grabbing focus (run dialog, looking glass, notifications). let focus = stage.get_key_focus(); - if (focus != stage && focus != this._searchEntry.entry) + if (focus != stage) return false; let symbol = event.get_key_symbol(); if (symbol == Clutter.Escape) { - Main.overview.hide(); - return true; - } else if (symbol == Clutter.Up) { - if (!this._searchActive) - return true; - this.searchResults.selectUp(false); - - return true; - } else if (symbol == Clutter.Down) { - if (!this._searchActive) - return true; - - this.searchResults.selectDown(false); + Main.overview.hide(); return true; } return false; }, - _doSearch: function () { - this._searchTimeoutId = 0; - let text = this._searchEntry.getText(); - this.searchResults.updateSearch(text); - - return false; - }, - addSearchProvider: function(provider) { - this._searchSystem.registerProvider(provider); - this.searchResults.createProviderMeta(provider); + this._searchTab.addSearchProvider(provider); }, show: function() { - // Connect type-as-you-find listener - this._searchEntry.show(); + this._searchTab.setFindAsYouType(true); if (this._itemDragBeginId == 0) this._itemDragBeginId = Main.overview.connect('item-drag-begin', @@ -495,8 +550,7 @@ ViewSelector.prototype = { }, hide: function() { - // Disconnect type-as-you-find listener - this._searchEntry.hide(); + this._searchTab.setFindAsYouType(false); if (this._keyPressId > 0) { global.stage.disconnect(this._keyPressId); From ffd7eaede5cc76efdbca487209c6b01199d23c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Mon, 4 Oct 2010 16:05:32 +0200 Subject: [PATCH 35/61] view-selector: Add keyboard shortcut for view switching As the view selector is a tabbed interface, use the default keyboard shortcut of Ctrl-PageUp/PageDown of GtkNotebook for switching between views. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- js/ui/viewSelector.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js index d0d0676ef..368fc3035 100644 --- a/js/ui/viewSelector.js +++ b/js/ui/viewSelector.js @@ -460,6 +460,29 @@ ViewSelector.prototype = { this._switchTab(this._tabs[0]); }, + _nextTab: function() { + if (this._tabs.length == 0 || + this._tabs[this._tabs.length - 1] == this._activeTab) + return; + + for (let i = 0; i < this._tabs.length; i++) + if (this._tabs[i] == this._activeTab) { + this._switchTab(this._tabs[i + 1]); + return; + } + }, + + _prevTab: function() { + if (this._tabs.length == 0 || this._tabs[0] == this._activeTab) + return; + + for (let i = 0; i < this._tabs.length; i++) + if (this._tabs[i] == this._activeTab) { + this._switchTab(this._tabs[i - 1]); + return; + } + }, + _getPreferredTabBarWidth: function(box, forHeight, alloc) { let children = box.get_children(); for (let i = 0; i < children.length; i++) { @@ -521,10 +544,21 @@ ViewSelector.prototype = { if (focus != stage) return false; + let modifiers = Shell.get_event_state(event); let symbol = event.get_key_symbol(); if (symbol == Clutter.Escape) { Main.overview.hide(); return true; + } else if (modifiers & Clutter.ModifierType.CONTROL_MASK) { + if (symbol == Clutter.Page_Up) { + if (!this._searchActive) + this._prevTab(); + return true; + } else if (symbol == Clutter.Page_Down) { + if (!this._searchActive) + this._nextTab(); + return true; + } } return false; }, From 1a77acfda6a0f967534ddae6e7b43792443e6bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 29 Jul 2010 09:54:16 +0200 Subject: [PATCH 36/61] Fake workspaces tab https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- js/ui/viewSelector.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js index 368fc3035..c2c5b691e 100644 --- a/js/ui/viewSelector.js +++ b/js/ui/viewSelector.js @@ -453,6 +453,29 @@ ViewSelector.prototype = { if (!tab.visible) tab.show(); + + // Pull a Meg Ryan: + if (Main.overview && Main.overview.workspaces) { + if (tab != this._tabs[0]) { + Tweener.addTween(Main.overview.workspaces.actor, + { opacity: 0, + time: 0.1, + transition: 'easeOutQuad', + onComplete: Lang.bind(this, + function() { + Main.overview.workspaces.actor.hide(); + Main.overview.workspaces.actor.opacity = 255; + }) + }); + } else { + Main.overview.workspaces.actor.opacity = 0; + Main.overview.workspaces.actor.show(); + Tweener.addTween(Main.overview.workspaces.actor, + { opacity: 255, + time: 0.1, + transition: 'easeOutQuad' }); + } + } }, _switchDefaultTab: function() { From 1eb6dfe1b837103fcf9042cbade809f35b8cd559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 30 Sep 2010 17:43:07 +0200 Subject: [PATCH 37/61] app-display: Slight cleanup and style update Being no longer an independent menu pane, both the toggle() and close() functions are no longer needed, and the view's structure can be simplified a bit. Also update the style to fit into the view selector. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 15 +---------- js/ui/appDisplay.js | 51 +++++--------------------------------- 2 files changed, 7 insertions(+), 59 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index a059ef358..63678dc5d 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -508,10 +508,7 @@ StTooltip StLabel { } .all-app { - border-radius: 10px; - background-color: #111111; - border: 2px solid #868686; - color: #ffffff; + padding: 10px; } .app-section-divider-container { @@ -524,16 +521,6 @@ StTooltip StLabel { background-image: url("separator-white.png"); } -.all-app-controls-panel { - height: 30px; -} - -.all-app-scroll-view { - padding-right: 10px; - padding-left: 10px; - padding-bottom: 10px; -} - #dash > .app-well-app { padding: 6px 12px; } diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index b9a339992..632f89c8e 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -148,22 +148,14 @@ AllAppDisplay.prototype = { Main.queueDeferredWork(this._workId); })); - let bin = new St.BoxLayout({ style_class: 'all-app-controls-panel', - reactive: true }); - this.actor = new St.BoxLayout({ style_class: 'all-app', vertical: true }); - this.actor.hide(); - - let view = new St.ScrollView({ x_fill: true, - y_fill: false, - style_class: 'all-app-scroll-view', - vshadows: true }); - this._scrollView = view; - this.actor.add(bin); - this.actor.add(view, { expand: true, y_fill: false, y_align: St.Align.START }); + this._scrollView = new St.ScrollView({ x_fill: true, + y_fill: false, + vshadows: true }); + this.actor = new St.Bin({ style_class: 'all-app', + y_align: St.Align.START, + child: this._scrollView }); this._appView = new ViewByCategories(); - this._appView.connect('launching', Lang.bind(this, this.close)); - this._appView.connect('drag-begin', Lang.bind(this, this.close)); this._scrollView.add_actor(this._appView.actor); this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); @@ -177,37 +169,6 @@ AllAppDisplay.prototype = { }); this._appView.refresh(apps); - }, - - toggle: function() { - if (this.actor.visible) { - Tweener.addTween(this.actor, - { opacity: 0, - time: Overview.PANE_FADE_TIME, - transition: 'easeOutQuad', - onComplete: Lang.bind(this, - function() { - this.actor.hide(); - this.emit('open-state-changed', - this.actor.visible); - }) - }); - } else { - this.actor.show(); - this.emit('open-state-changed', this.actor.visible); - this.actor.opacity = 0; - Tweener.addTween(this.actor, - { opacity: 255, - time: Overview.PANE_FADE_TIME, - transition: 'easeOutQuad' - }); - } - }, - - close: function() { - if (!this.actor.visible) - return; - this.toggle(); } }; From 0942f50781d3652cdac42f51d741d4d330b51a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Fri, 12 Nov 2010 10:28:28 +0100 Subject: [PATCH 38/61] workspaces-view: Remove MosaicView The new layout does no longer support view switching, so merge GenericWorkspacesView and SingleView, and remove MosaicView. Also rename or remove workspace properties and functions which are now unused. The grid will have a comeback with the new DND behavior. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/org.gnome.shell.gschema.xml.in | 12 - data/theme/gnome-shell.css | 9 +- js/ui/workspace.js | 123 +--- js/ui/workspacesView.js | 853 ++++++++-------------------- 4 files changed, 253 insertions(+), 744 deletions(-) diff --git a/data/org.gnome.shell.gschema.xml.in b/data/org.gnome.shell.gschema.xml.in index 085db60c4..031930a4b 100644 --- a/data/org.gnome.shell.gschema.xml.in +++ b/data/org.gnome.shell.gschema.xml.in @@ -41,18 +41,6 @@ [] <_summary>History for command (Alt-F2) dialog - - 'single' - <_summary>Overview workspace view mode - <_description> - The selected workspace view mode in the overview. - Supported values are "single" and "grid". - - - - - - diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 63678dc5d..9ce36261f 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -257,18 +257,11 @@ StTooltip StLabel { /* Overview */ -.workspaces { +.workspaces-view { color: white; -} - -.workspaces.single { spacing: 25px; } -.workspaces.mosaic { - spacing: 15px; -} - .workspaces-bar { spacing: 5px; } diff --git a/js/ui/workspace.js b/js/ui/workspace.js index ec7e24a23..154f7e806 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -20,9 +20,6 @@ const FOCUS_ANIMATION_TIME = 0.15; const WINDOW_DND_SIZE = 256; -const FRAME_COLOR = new Clutter.Color(); -FRAME_COLOR.from_pixel(0xffffffff); - const SCROLL_SCALE_AMOUNT = 100 / 5; const LIGHTBOX_FADE_TIME = 0.1; @@ -54,11 +51,6 @@ function _clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } -// Spacing between workspaces. At the moment, the same spacing is used -// in both zoomed-in and zoomed-out views; this is slightly -// metaphor-breaking, but the alternatives are also weird. -const GRID_SPACING = 15; -const FRAME_SIZE = GRID_SPACING / 3; function ScaledPoint(x, y, scaleX, scaleY) { [this.x, this.y, this.scaleX, this.scaleY] = arguments; @@ -576,8 +568,6 @@ Workspace.prototype = { this._visible = false; - this._frame = null; - this.leavingOverview = false; }, @@ -639,9 +629,6 @@ Workspace.prototype = { this._lightbox.show(); else this._lightbox.hide(); - - if (this._frame) - this._frame.set_opacity(showLightbox ? 150 : 255); }, /** @@ -662,32 +649,6 @@ Workspace.prototype = { this._lightbox.highlight(actor); }, - // Mark the workspace selected/not-selected - setSelected : function(selected) { - // Don't draw a frame if we only have one workspace - if (selected && global.screen.n_workspaces > 1) { - if (this._frame) - return; - - // FIXME: do something cooler-looking using clutter-cairo - this._frame = new Clutter.Rectangle({ color: FRAME_COLOR }); - this.actor.add_actor(this._frame); - this._frame.set_position(- FRAME_SIZE / this.actor.scale_x, - - FRAME_SIZE / this.actor.scale_y); - this._frame.set_size(this.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, - this.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); - this._frame.lower_bottom(); - - this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition)); - } else { - if (!this._frame) - return; - this.actor.disconnect(this._framePosHandler); - this._frame.destroy(); - this._frame = null; - } - }, - /** * setReactive: * @reactive: %true iff the workspace should be reactive @@ -698,13 +659,6 @@ Workspace.prototype = { this.actor.reactive = reactive; }, - _updateFramePosition : function() { - this._frame.set_position(- FRAME_SIZE / this.actor.scale_x, - - FRAME_SIZE / this.actor.scale_y); - this._frame.set_size(this.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, - this.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); - }, - _isCloneVisible: function(clone) { return this._showOnlyWindows == null || (clone.metaWindow in this._showOnlyWindows); }, @@ -1080,8 +1034,8 @@ Workspace.prototype = { // be after the workspace animation finishes. let [cloneX, cloneY] = clone.actor.get_position(); let [cloneWidth, cloneHeight] = clone.actor.get_size(); - cloneX = this.gridX + this.scale * cloneX; - cloneY = this.gridY + this.scale * cloneY; + cloneX = this.x + this.scale * cloneX; + cloneY = this.y + this.scale * cloneY; cloneWidth = this.scale * clone.actor.scale_x * cloneWidth; cloneHeight = this.scale * clone.actor.scale_y * cloneHeight; @@ -1120,8 +1074,8 @@ Workspace.prototype = { let wsHeight = this.actor.height * this.scale; let pointerHasMoved = (this._cursorX != x && this._cursorY != y); - let inWorkspace = (this.gridX < x && x < this.gridX + wsWidth && - this.gridY < y && y < this.gridY + wsHeight); + let inWorkspace = (this.x < x && x < this.x + wsWidth && + this.y < y && y < this.y + wsHeight); if (pointerHasMoved && inWorkspace) { // store current cursor position @@ -1245,7 +1199,7 @@ Workspace.prototype = { // Animate the full-screen to Overview transition. zoomToOverview : function() { - this.actor.set_position(this.gridX, this.gridY); + this.actor.set_position(this.x, this.y); this.actor.set_scale(this.scale, this.scale); // Position and scale the windows. @@ -1303,69 +1257,6 @@ Workspace.prototype = { this._visible = false; }, - // Animates grid shrinking/expanding when a row or column - // of workspaces is added or removed - resizeToGrid : function (oldScale) { - this._hideAllOverlays(); - Tweener.addTween(this.actor, - { x: this.gridX, - y: this.gridY, - scale_x: this.scale, - scale_y: this.scale, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: Lang.bind(this, this._fadeInAllOverlays) - }); - }, - - // Animates the addition of a new (empty) workspace - slideIn : function(oldScale) { - if (this.gridCol > this.gridRow) { - this.actor.set_position(global.screen_width, this.gridY); - this.actor.set_scale(oldScale, oldScale); - } else { - this.actor.set_position(this.gridX, global.screen_height); - this.actor.set_scale(this.scale, this.scale); - } - Tweener.addTween(this.actor, - { x: this.gridX, - y: this.gridY, - scale_x: this.scale, - scale_y: this.scale, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad' - }); - - this._visible = true; - }, - - // Animates the removal of a workspace - slideOut : function(onComplete) { - let destX = this.actor.x, destY = this.actor.y; - - this._hideAllOverlays(); - - if (this.gridCol > this.gridRow) - destX = global.screen_width; - else - destY = global.screen_height; - Tweener.addTween(this.actor, - { x: destX, - y: destY, - scale_x: this.scale, - scale_y: this.scale, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: onComplete - }); - - this._visible = false; - - // Don't let the user try to select this workspace as it's - // making its exit. - this.actor.reactive = false; - }, - destroy : function() { this.actor.destroy(); }, @@ -1555,11 +1446,11 @@ function _workspaceRelativeModifier(workspace) { } return [ { name: 'x', - parameters: { workspacePos: workspace.gridX, + parameters: { workspacePos: workspace.x, overviewPos: overviewPosX, overviewScale: overviewScale } }, { name: 'y', - parameters: { workspacePos: workspace.gridY, + parameters: { workspacePos: workspace.y, overviewPos: overviewPosY, overviewScale: overviewScale } } ]; diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 82d486810..2bb2a06aa 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -19,459 +19,9 @@ const WORKSPACE_SWITCH_TIME = 0.25; // Note that mutter has a compile-time limit of 36 const MAX_WORKSPACES = 16; -// The values here are also used for GSettings, and the key and value -// names must match -const WorkspacesViewType = { - SINGLE: 'single', - GRID: 'grid' -}; -const WORKSPACES_VIEW_KEY = 'workspaces-view'; - const WORKSPACE_DRAGGING_SCALE = 0.85; -function GenericWorkspacesView(width, height, x, y, workspaces) { - this._init(width, height, x, y, workspaces); -} - -GenericWorkspacesView.prototype = { - _init: function(width, height, x, y, workspaces) { - this.actor = new St.Group({ style_class: 'workspaces' }); - - this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); - - this.actor.connect('style-changed', Lang.bind(this, - function() { - let node = this.actor.get_theme_node(); - this._spacing = node.get_length('spacing'); - if (Main.overview.animationInProgress) - this._computeWorkspacePositions(); - else - this._transitionWorkspaces(); - })); - - this._width = width; - this._height = height; - this._x = x; - this._y = y; - this._spacing = 0; - - this._windowSelectionAppId = null; - this._highlightWindow = null; - - let activeWorkspaceIndex = global.screen.get_active_workspace_index(); - this._workspaces = workspaces; - - // Add workspace actors - for (let w = 0; w < global.screen.n_workspaces; w++) - this._workspaces[w].actor.reparent(this.actor); - this._workspaces[activeWorkspaceIndex].actor.raise_top(); - - // Position/scale the desktop windows and their children after the - // workspaces have been created. This cannot be done first because - // window movement depends on the Workspaces object being accessible - // as an Overview member. - this._overviewShowingId = - Main.overview.connect('showing', - Lang.bind(this, function() { - this._onRestacked(); - for (let w = 0; w < this._workspaces.length; w++) - this._workspaces[w].zoomToOverview(); - })); - - this._switchWorkspaceNotifyId = - global.window_manager.connect('switch-workspace', - Lang.bind(this, this._activeWorkspaceChanged)); - this._restackedNotifyId = - global.screen.connect('restacked', - Lang.bind(this, this._onRestacked)); - }, - - _lookupWorkspaceForMetaWindow: function (metaWindow) { - for (let i = 0; i < this._workspaces.length; i++) { - if (this._workspaces[i].containsMetaWindow(metaWindow)) - return this._workspaces[i]; - } - return null; - }, - - _lookupCloneForMetaWindow: function (metaWindow) { - for (let i = 0; i < this._workspaces.length; i++) { - let clone = this._workspaces[i].lookupCloneForMetaWindow(metaWindow); - if (clone) - return clone; - } - return null; - }, - - setHighlightWindow: function (metaWindow) { - // Looping over all workspaces is easier than keeping track of the last - // highlighted window while trying to handle the window or workspace possibly - // going away. - for (let i = 0; i < this._workspaces.length; i++) { - this._workspaces[i].setHighlightWindow(null); - } - if (metaWindow != null) { - let workspace = this._lookupWorkspaceForMetaWindow(metaWindow); - workspace.setHighlightWindow(metaWindow); - } - }, - - getActiveWorkspace: function() { - let active = global.screen.get_active_workspace_index(); - return this._workspaces[active]; - }, - - _clearApplicationWindowSelection: function(reposition) { - if (this._windowSelectionAppId == null) - return; - this._windowSelectionAppId = null; - - for (let i = 0; i < this._workspaces.length; i++) { - this._workspaces[i].setLightboxMode(false); - this._workspaces[i].setShowOnlyWindows(null, reposition); - } - }, - - /** - * setApplicationWindowSelection: - * @appid: Application identifier string - * - * Enter a mode which shows only the windows owned by the - * given application, and allow highlighting of a specific - * window with setHighlightWindow(). - */ - setApplicationWindowSelection: function (appId) { - if (appId == null) { - this._clearApplicationWindowSelection(true); - return; - } - - if (appId == this._windowSelectionAppId) - return; - - this._windowSelectionAppId = appId; - - let appSys = Shell.AppSystem.get_default(); - - let showOnlyWindows = {}; - let app = appSys.get_app(appId); - let windows = app.get_windows(); - for (let i = 0; i < windows.length; i++) { - showOnlyWindows[windows[i]] = 1; - } - - for (let i = 0; i < this._workspaces.length; i++) { - this._workspaces[i].setLightboxMode(true); - this._workspaces[i].setShowOnlyWindows(showOnlyWindows, true); - } - }, - - hide: function() { - let activeWorkspaceIndex = global.screen.get_active_workspace_index(); - let activeWorkspace = this._workspaces[activeWorkspaceIndex]; - - if (this._windowSelectionAppId != null) - this._clearApplicationWindowSelection(false); - - activeWorkspace.actor.raise_top(); - - for (let w = 0; w < this._workspaces.length; w++) - this._workspaces[w].zoomFromOverview(); - }, - - destroy: function() { - this.actor.destroy(); - }, - - _onDestroy: function() { - Main.overview.disconnect(this._overviewShowingId); - global.window_manager.disconnect(this._switchWorkspaceNotifyId); - global.screen.disconnect(this._restackedNotifyId); - }, - - getScale: function() { - return this._workspaces[0].scale; - }, - - _onRestacked: function() { - let stack = global.get_window_actors(); - let stackIndices = {}; - - for (let i = 0; i < stack.length; i++) { - // Use the stable sequence for an integer to use as a hash key - stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i; - } - - for (let i = 0; i < this._workspaces.length; i++) - this._workspaces[i].syncStacking(stackIndices); - }, - - // Handles a drop onto the (+) button; assumes the new workspace - // has already been added - acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) { - return this._workspaces[this._workspaces.length - 1].acceptDrop(source, dropActor, x, y, time); - }, - - // Get the grid position of the active workspace. - getActiveWorkspacePosition: function() { - let activeWorkspaceIndex = global.screen.get_active_workspace_index(); - let activeWorkspace = this._workspaces[activeWorkspaceIndex]; - - return [activeWorkspace.gridX, activeWorkspace.gridY]; - }, - - createControllerBar: function() { - throw new Error('Not implemented'); - }, - - canAddWorkspace: function() { - return global.screen.n_workspaces < MAX_WORKSPACES; - }, - - addWorkspace: function() { - if (!this.canAddWorkspace()) { - Main.overview.shellInfo.setMessage(_("Can't add a new workspace because maximum workspaces limit has been reached.")); - return null; - } - - return global.screen.append_new_workspace(false, global.get_current_time()); - }, - - _getWorkspaceIndexToRemove: function() { - throw new Error('Not implemented'); - }, - - canRemoveWorkspace: function() { - return this._getWorkspaceIndexToRemove() > 0; - }, - - removeWorkspace: function() { - if (!this.canRemoveWorkspace()) { - Main.overview.shellInfo.setMessage(_("Can't remove the first workspace.")); - return; - } - let index = this._getWorkspaceIndexToRemove(); - let metaWorkspace = this._workspaces[index].metaWorkspace; - global.screen.remove_workspace(metaWorkspace, - global.get_current_time()); - }, - - updateWorkspaces: function() { - throw new Error('Not implemented'); - }, - - _transitionWorkspaces: function() { - throw new Error('Not implemented'); - }, - - _computeWorkspacePositions: function() { - throw new Error('Not implemented'); - }, - - _activeWorkspaceChanged: function() { - throw new Error('Not implemented'); - }, - - _handleDragOverNewWorkspace: function(source, dropActor, x, y, time) { - if (source instanceof Workspace.WindowClone) - return DND.DragMotionResult.MOVE_DROP; - if (source.shellWorkspaceLaunch) - return DND.DragMotionResult.COPY_DROP; - return DND.DragMotionResult.CONTINUE; - }, - - _acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) { - let ws = this.addWorkspace(); - if (ws == null) - return false; - return this.acceptNewWorkspaceDrop(source, dropActor, x, y, time); - } -}; - -function MosaicView(width, height, x, y, workspaces) { - this._init(width, height, x, y, workspaces); -} - -MosaicView.prototype = { - __proto__: GenericWorkspacesView.prototype, - - _init: function(width, height, x, y, workspaces) { - GenericWorkspacesView.prototype._init.call(this, width, height, x, y, workspaces); - - this.actor.add_style_class_name('mosaic'); - this.actor.set_clip(x - Workspace.FRAME_SIZE, - y - Workspace.FRAME_SIZE, - width + 2 * Workspace.FRAME_SIZE, - height + 2 * Workspace.FRAME_SIZE); - this._workspaces[global.screen.get_active_workspace_index()].setSelected(true); - }, - - // Assign grid positions to workspaces. We can't just do a simple - // row-major or column-major numbering, because we don't want the - // existing workspaces to get rearranged when we add a row or - // column. So we alternate between adding to rows and adding to - // columns. (So, eg, when going from a 2x2 grid of 4 workspaces to - // a 3x2 grid of 5 workspaces, the 4 existing workspaces stay - // where they are, and the 5th one is added to the end of the - // first row.) - // - // FIXME: need to make the metacity internal layout agree with this! - _computeWorkspacePositions: function() { - let gridWidth = Math.ceil(Math.sqrt(this._workspaces.length)); - let gridHeight = Math.ceil(this._workspaces.length / gridWidth); - - // adjust vertical spacing so workspaces can preserve their aspect - // ratio without exceeding this._height - let verticalSpacing = this._spacing * this._height / this._width; - - let wsWidth = (this._width - (gridWidth - 1) * this._spacing) / gridWidth; - let wsHeight = (this._height - (gridHeight - 1) * verticalSpacing) / gridHeight; - let scale = wsWidth / global.screen_width; - - let span = 1, n = 0, row = 0, col = 0, horiz = true; - - for (let w = 0; w < this._workspaces.length; w++) { - let workspace = this._workspaces[w]; - - workspace.gridRow = row; - workspace.gridCol = col; - - workspace.gridX = this._x + workspace.gridCol * (wsWidth + this._spacing); - workspace.gridY = this._y + workspace.gridRow * (wsHeight + verticalSpacing); - workspace.scale = scale; - - if (horiz) { - col++; - if (col == span) { - row = 0; - horiz = false; - } - } else { - row++; - if (row == span) { - col = 0; - horiz = true; - span++; - } - } - } - }, - - _transitionWorkspaces: function() { - // update workspace parameters - this._computeWorkspacePositions(); - - let active = global.screen.get_active_workspace_index(); - let activeWorkspace = this._workspaces[active]; - // scale is the factor needed to translate from the new scale - // (this view) to the currently active scale (previous view) - let scale = this._workspaces[0].actor.scale_x / activeWorkspace.scale; - - for (let w = 0; w < this._workspaces.length; w++) { - let workspace = this._workspaces[w]; - let originX, originY; - let dx, dy; - - // The correct transition would be a straightforward animation - // of each workspace's old position/scale to the new one; - // however, this looks overly busy, so we only use a zoom effect. - // Unfortunately this implies that we cannot pretend to not knowing - // the other view's layout at this point: - // We position the workspaces in the grid, which we scale up so - // that the active workspace fills the viewport. - dx = workspace.gridX - activeWorkspace.gridX; - dy = workspace.gridY - activeWorkspace.gridY; - originX = this._x + scale * dx; - originY = this._y + scale * dy; - - workspace.actor.set_position(originX, originY); - - workspace.positionWindows(Workspace.WindowPositionFlags.ANIMATE); - workspace.setSelected(false); - workspace.hideWindowsOverlays(); - - Tweener.addTween(workspace.actor, - { x: workspace.gridX, - y: workspace.gridY, - scale_x: workspace.scale, - scale_y: workspace.scale, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: function() { - workspace.zoomToOverview(false); - if (workspace.metaWorkspace.index() == active) - workspace.setSelected(true); - }}); - } - }, - - updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces, lostWorkspaces) { - let oldScale = this._workspaces[0].scale; - let oldGridWidth = Math.ceil(Math.sqrt(oldNumWorkspaces)); - let oldGridHeight = Math.ceil(oldNumWorkspaces / oldGridWidth); - - // Add actors - if (newNumWorkspaces > oldNumWorkspaces) - for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) - this.actor.add_actor(this._workspaces[w].actor); - - // Figure out the new layout - this._computeWorkspacePositions(); - let newScale = this._workspaces[0].scale; - let newGridWidth = Math.ceil(Math.sqrt(newNumWorkspaces)); - let newGridHeight = Math.ceil(newNumWorkspaces / newGridWidth); - - if (newGridWidth != oldGridWidth || newGridHeight != oldGridHeight) { - // We need to resize/move the existing workspaces/windows - let existingWorkspaces = Math.min(oldNumWorkspaces, newNumWorkspaces); - for (let w = 0; w < existingWorkspaces; w++) - this._workspaces[w].resizeToGrid(oldScale); - } - - if (newScale != oldScale) { - // The workspace scale affects window size/positioning because we clamp - // window size to a 1:1 ratio and never scale them up - let existingWorkspaces = Math.min(oldNumWorkspaces, newNumWorkspaces); - for (let w = 0; w < existingWorkspaces; w++) - this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE); - } - - if (newNumWorkspaces > oldNumWorkspaces) { - // Slide new workspaces in from offscreen - // New workspaces can contain windows. - for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) { - this._workspaces[w].positionWindows(0); - this._workspaces[w].slideIn(oldScale); - } - } else { - // Slide old workspaces out - for (let w = 0; w < lostWorkspaces.length; w++) { - let workspace = lostWorkspaces[w]; - workspace.slideOut(function () { workspace.destroy(); }); - } - } - - // Reset the selection state; if we went from > 1 workspace to 1, - // this has the side effect of removing the frame border - let activeIndex = global.screen.get_active_workspace_index(); - this._workspaces[activeIndex].setSelected(true); - }, - - _activeWorkspaceChanged: function(wm, from, to, direction) { - this._workspaces[from].setSelected(false); - this._workspaces[to].setSelected(true); - }, - - createControllerBar: function() { - return null; - }, - - _getWorkspaceIndexToRemove: function() { - return this._workspaces.length - 1; - } -}; - function WorkspaceIndicator(activateWorkspace, workspaceAcceptDrop, workspaceHandleDragOver, scrollEventCb) { this._init(activateWorkspace, workspaceAcceptDrop, workspaceHandleDragOver, scrollEventCb); @@ -602,29 +152,29 @@ WorkspaceIndicator.prototype = { } }; -function SingleView(width, height, x, y, workspaces) { +function WorkspacesView(width, height, x, y, workspaces) { this._init(width, height, x, y, workspaces); } -SingleView.prototype = { - __proto__: GenericWorkspacesView.prototype, - +WorkspacesView.prototype = { _init: function(width, height, x, y, workspaces) { - GenericWorkspacesView.prototype._init.call(this, width, height, x, y, workspaces); - - this._itemDragBeginId = Main.overview.connect('item-drag-begin', - Lang.bind(this, this._dragBegin)); - this._itemDragEndId = Main.overview.connect('item-drag-end', - Lang.bind(this, this._dragEnd)); - for (let i = 0; i < this._workspaces.length; i++) { - this._workspaces[i]._windowDragBeginId = this._workspaces[i].connect('window-drag-begin', - Lang.bind(this, this._dragBegin)); - this._workspaces[i]._windowDragEndId = this._workspaces[i].connect('window-drag-end', - Lang.bind(this, this._dragEnd)); - } - - this.actor.add_style_class_name('single'); + this.actor = new St.Group({ style_class: 'workspaces-view' }); this.actor.set_clip(x, y, width, height); + + this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); + + this.actor.connect('style-changed', Lang.bind(this, + function() { + let node = this.actor.get_theme_node(); + this._spacing = node.get_length('spacing'); + this._computeWorkspacePositions(); + })); + + this._width = width; + this._height = height; + this._x = x; + this._y = y; + this._spacing = 0; this._activeWorkspaceX = 0; // x offset of active ws while dragging this._activeWorkspaceY = 0; // y offset of active ws while dragging this._lostWorkspaces = []; @@ -634,8 +184,34 @@ SingleView.prototype = { this._inDrag = false; // dragging a window this._lastMotionTime = -1; // used to track "stopping" while dragging workspaces - let active = global.screen.get_active_workspace_index(); - this._scrollAdjustment = new St.Adjustment({ value: active, + let activeWorkspaceIndex = global.screen.get_active_workspace_index(); + this._workspaces = workspaces; + + // Add workspace actors + for (let w = 0; w < global.screen.n_workspaces; w++) { + this._workspaces[w].actor.reparent(this.actor); + this._workspaces[w]._windowDragBeginId = + this._workspaces[w].connect('window-drag-begin', + Lang.bind(this, this._dragBegin)); + this._workspaces[w]._windowDragEndId = + this._workspaces[w].connect('window-drag-end', + Lang.bind(this, this._dragEnd)); + } + this._workspaces[activeWorkspaceIndex].actor.raise_top(); + + // Position/scale the desktop windows and their children after the + // workspaces have been created. This cannot be done first because + // window movement depends on the Workspaces object being accessible + // as an Overview member. + this._overviewShowingId = + Main.overview.connect('showing', + Lang.bind(this, function() { + this._onRestacked(); + for (let w = 0; w < this._workspaces.length; w++) + this._workspaces[w].zoomToOverview(); + })); + + this._scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex, lower: 0, page_increment: 1, page_size: 1, @@ -649,6 +225,187 @@ SingleView.prototype = { this._buttonPressId = 0; this._capturedEventId = 0; this._timeoutId = 0; + + this._windowSelectionAppId = null; + this._highlightWindow = null; + + this._switchWorkspaceNotifyId = + global.window_manager.connect('switch-workspace', + Lang.bind(this, this._activeWorkspaceChanged)); + this._restackedNotifyId = + global.screen.connect('restacked', + Lang.bind(this, this._onRestacked)); + + this._itemDragBeginId = Main.overview.connect('item-drag-begin', + Lang.bind(this, this._dragBegin)); + this._itemDragEndId = Main.overview.connect('item-drag-end', + Lang.bind(this, this._dragEnd)); + }, + + _lookupWorkspaceForMetaWindow: function (metaWindow) { + for (let i = 0; i < this._workspaces.length; i++) { + if (this._workspaces[i].containsMetaWindow(metaWindow)) + return this._workspaces[i]; + } + return null; + }, + + setHighlightWindow: function (metaWindow) { + // Looping over all workspaces is easier than keeping track of the last + // highlighted window while trying to handle the window or workspace possibly + // going away. + for (let i = 0; i < this._workspaces.length; i++) { + this._workspaces[i].setHighlightWindow(null); + } + if (metaWindow != null) { + let workspace = this._lookupWorkspaceForMetaWindow(metaWindow); + workspace.setHighlightWindow(metaWindow); + } + }, + + getActiveWorkspace: function() { + let active = global.screen.get_active_workspace_index(); + return this._workspaces[active]; + }, + + _clearApplicationWindowSelection: function(reposition) { + if (this._windowSelectionAppId == null) + return; + this._windowSelectionAppId = null; + + for (let i = 0; i < this._workspaces.length; i++) { + this._workspaces[i].setLightboxMode(false); + this._workspaces[i].setShowOnlyWindows(null, reposition); + } + }, + + /** + * setApplicationWindowSelection: + * @appid: Application identifier string + * + * Enter a mode which shows only the windows owned by the + * given application, and allow highlighting of a specific + * window with setHighlightWindow(). + */ + setApplicationWindowSelection: function (appId) { + if (appId == null) { + this._clearApplicationWindowSelection(true); + return; + } + + if (appId == this._windowSelectionAppId) + return; + + this._windowSelectionAppId = appId; + + let appSys = Shell.AppSystem.get_default(); + + let showOnlyWindows = {}; + let app = appSys.get_app(appId); + let windows = app.get_windows(); + for (let i = 0; i < windows.length; i++) { + showOnlyWindows[windows[i]] = 1; + } + + for (let i = 0; i < this._workspaces.length; i++) { + this._workspaces[i].setLightboxMode(true); + this._workspaces[i].setShowOnlyWindows(showOnlyWindows, true); + } + }, + + hide: function() { + let activeWorkspaceIndex = global.screen.get_active_workspace_index(); + let activeWorkspace = this._workspaces[activeWorkspaceIndex]; + + if (this._windowSelectionAppId != null) + this._clearApplicationWindowSelection(false); + + activeWorkspace.actor.raise_top(); + + for (let w = 0; w < this._workspaces.length; w++) + this._workspaces[w].zoomFromOverview(); + }, + + destroy: function() { + this.actor.destroy(); + }, + + getScale: function() { + return this._workspaces[0].scale; + }, + + _onRestacked: function() { + let stack = global.get_window_actors(); + let stackIndices = {}; + + for (let i = 0; i < stack.length; i++) { + // Use the stable sequence for an integer to use as a hash key + stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i; + } + + for (let i = 0; i < this._workspaces.length; i++) + this._workspaces[i].syncStacking(stackIndices); + }, + + // Handles a drop onto the (+) button; assumes the new workspace + // has already been added + acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) { + return this._workspaces[this._workspaces.length - 1].acceptDrop(source, dropActor, x, y, time); + }, + + // Get the grid position of the active workspace. + getActiveWorkspacePosition: function() { + let activeWorkspaceIndex = global.screen.get_active_workspace_index(); + let activeWorkspace = this._workspaces[activeWorkspaceIndex]; + + return [activeWorkspace.x, activeWorkspace.y]; + }, + + canAddWorkspace: function() { + return global.screen.n_workspaces < MAX_WORKSPACES; + }, + + addWorkspace: function() { + let ws = null; + if (!this.canAddWorkspace()) { + Main.overview.shellInfo.setMessage(_("Can't add a new workspace because maximum workspaces limit has been reached.")); + } else { + let currentTime = global.get_current_time(); + ws = global.screen.append_new_workspace(false, currentTime); + ws.activate(currentTime); + } + + return ws; + }, + + canRemoveWorkspace: function() { + return this._getWorkspaceIndexToRemove() > 0; + }, + + removeWorkspace: function() { + if (!this.canRemoveWorkspace()) { + Main.overview.shellInfo.setMessage(_("Can't remove the first workspace.")); + return; + } + let index = this._getWorkspaceIndexToRemove(); + let metaWorkspace = this._workspaces[index].metaWorkspace; + global.screen.remove_workspace(metaWorkspace, + global.get_current_time()); + }, + + _handleDragOverNewWorkspace: function(source, dropActor, x, y, time) { + if (source instanceof Workspace.WindowClone) + return DND.DragMotionResult.MOVE_DROP; + if (source.shellWorkspaceLaunch) + return DND.DragMotionResult.COPY_DROP; + return DND.DragMotionResult.CONTINUE; + }, + + _acceptNewWorkspaceDrop: function(source, dropActor, x, y, time) { + let ws = this.addWorkspace(); + if (ws == null) + return false; + return this.acceptNewWorkspaceDrop(source, dropActor, x, y, time); }, // Compute the position, scale and opacity of the workspaces, but don't @@ -673,55 +430,10 @@ SingleView.prototype = { workspace.opacity = (this._inDrag && w != active) ? 200 : 255; - workspace.gridRow = 0; - workspace.gridCol = 0; - workspace.scale = scale; - workspace.gridX = this._x + this._activeWorkspaceX - + (w - active) * (_width + this._spacing); - workspace.gridY = this._y + this._activeWorkspaceY; - - workspace.setSelected(false); - } - }, - - _transitionWorkspaces: function() { - // update workspace parameters - this._computeWorkspacePositions(); - - let active = global.screen.get_active_workspace_index(); - let activeActor = this._workspaces[active].actor; - // scale is the factor needed to translate from the currently - // active scale (previous view) to the new scale (this view) - let scale = this._workspaces[active].scale / activeActor.scale_x; - - for (let w = 0; w < this._workspaces.length; w++) { - let workspace = this._workspaces[w]; - let targetX, targetY; - - // The correct transition would be a straightforward animation - // of each workspace's old position/scale to the new one; - // however, this looks overly busy, so we only use a zoom effect. - // Therefore we scale up each workspace's distance to the active - // workspace, so the latter fills the viewport while the other - // workspaces maintain their relative position - targetX = this._x + scale * (workspace.actor.x - activeActor.x); - targetY = this._y + scale * (workspace.actor.y - activeActor.y); - - workspace.positionWindows(Workspace.WindowPositionFlags.ANIMATE); - workspace.setSelected(false); - workspace._hideAllOverlays(); - - Tweener.addTween(workspace.actor, - { x: targetX, - y: targetY, - scale_x: workspace.scale, - scale_y: workspace.scale, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: function() { - workspace.zoomToOverview(false); - }}); + workspace.x = this._x + this._activeWorkspaceX + + (w - active) * (_width + this._spacing); + workspace.y = this._y + this._activeWorkspaceY; } }, @@ -855,7 +567,7 @@ SingleView.prototype = { _updateWorkspaceActors: function(showAnimation) { let active = global.screen.get_active_workspace_index(); let targetWorkspaceNewX = this._x + this._activeWorkspaceX; - let targetWorkspaceCurrentX = this._workspaces[active].gridX; + let targetWorkspaceCurrentX = this._workspaces[active].x; let dx = targetWorkspaceNewX - targetWorkspaceCurrentX; this._setWorkspaceDraggable(active, true); @@ -866,11 +578,11 @@ SingleView.prototype = { Tweener.removeTweens(workspace.actor); - workspace.gridX += dx; + workspace.x += dx; if (showAnimation) { - let params = { x: workspace.gridX, - y: workspace.gridY, + let params = { x: workspace.x, + y: workspace.y, scale_x: workspace.scale, scale_y: workspace.scale, opacity: workspace.opacity, @@ -891,7 +603,7 @@ SingleView.prototype = { Tweener.addTween(workspace.actor, params); } else { workspace.actor.set_scale(workspace.scale, workspace.scale); - workspace.actor.set_position(workspace.gridX, workspace.gridY); + workspace.actor.set_position(workspace.x, workspace.y); workspace.actor.opacity = workspace.opacity; if (w == 0) this._updateVisibility(); @@ -903,13 +615,13 @@ SingleView.prototype = { Tweener.removeTweens(workspace.actor); - workspace.gridX += dx; + workspace.x += dx; workspace.actor.show(); workspace.hideWindowsOverlays(); if (showAnimation) { Tweener.addTween(workspace.actor, - { x: workspace.gridX, + { x: workspace.x, time: WORKSPACE_SWITCH_TIME, transition: 'easeOutQuad', onComplete: Lang.bind(this, @@ -1011,7 +723,10 @@ SingleView.prototype = { }, _onDestroy: function() { - GenericWorkspacesView.prototype._onDestroy.call(this); + Main.overview.disconnect(this._overviewShowingId); + global.window_manager.disconnect(this._switchWorkspaceNotifyId); + global.screen.disconnect(this._restackedNotifyId); + this._setWorkspaceDraggable(this._dragIndex, false); if (this._timeoutId) { Mainloop.source_remove(this._timeoutId); @@ -1210,14 +925,6 @@ SingleView.prototype = { return actor; }, - addWorkspace: function() { - let ws = GenericWorkspacesView.prototype.addWorkspace.call(this); - if (ws != null) - ws.activate(global.get_current_time()); - - return ws; - }, - _getWorkspaceIndexToRemove: function() { return global.screen.get_active_workspace_index(); } @@ -1232,27 +939,8 @@ WorkspacesControls.prototype = { this.actor = new St.BoxLayout({ style_class: 'workspaces-bar' }); this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); - let view = global.settings.get_string(WORKSPACES_VIEW_KEY).toUpperCase(); - if (view in WorkspacesViewType) - this._currentViewType = WorkspacesViewType[view]; - else - this._currentViewType = WorkspacesViewType.SINGLE; - this._currentView = null; - // View switcher button - this._toggleViewButton = new St.Button(); - this._updateToggleButtonStyle(); - - this._toggleViewButton.connect('clicked', Lang.bind(this, function() { - if (this._currentViewType == WorkspacesViewType.SINGLE) - this._setView(WorkspacesViewType.GRID); - else - this._setView(WorkspacesViewType.SINGLE); - })); - - this.actor.add(this._toggleViewButton, { y_fill: false, y_align: St.Align.START }); - // View specific controls this._viewControls = new St.Bin({ x_fill: true, y_fill: true }); this.actor.add(this._viewControls, { expand: true, x_fill: true }); @@ -1283,12 +971,12 @@ WorkspacesControls.prototype = { this._nWorkspacesNotifyId = global.screen.connect('notify::n-workspaces', - Lang.bind(this, this._workspacesChanged)); + Lang.bind(this, this.updateControlsSensitivity)); this._switchWorkspaceNotifyId = global.window_manager.connect('switch-workspace', Lang.bind(this, this.updateControlsSensitivity)); - this._workspacesChanged(); + this.updateControlsSensitivity(); }, updateControls: function(view) { @@ -1316,26 +1004,6 @@ WorkspacesControls.prototype = { } }, - _updateToggleButtonStyle: function() { - if (this._currentViewType == WorkspacesViewType.SINGLE) - this._toggleViewButton.set_style_class_name('workspace-controls switch-mosaic'); - else - this._toggleViewButton.set_style_class_name('workspace-controls switch-single'); - }, - - _setView: function(view) { - if (this._currentViewType == view) - return; - - if (WorkspacesViewType.SINGLE == view) - this._toggleViewButton.set_style_class_name('workspace-controls switch-mosaic'); - else - this._toggleViewButton.set_style_class_name('workspace-controls switch-single'); - - this._currentViewType = view; - global.settings.set_string(WORKSPACES_VIEW_KEY, view); - }, - _onDestroy: function() { if (this._nWorkspacesNotifyId > 0) { global.screen.disconnect(this._nWorkspacesNotifyId); @@ -1358,15 +1026,6 @@ WorkspacesControls.prototype = { this._setButtonSensitivity(this._removeButton, this._currentView.canRemoveWorkspace()); this._setButtonSensitivity(this._addButton, this._currentView.canAddWorkspace()); } - }, - - _workspacesChanged: function() { - let showToggleButton = (global.screen.n_workspaces > 1); - Tweener.addTween(this._toggleViewButton, - { opacity: showToggleButton ? 255 : 0, - time: WORKSPACE_SWITCH_TIME, - transition: 'easeOutQuad' }); - this.updateControlsSensitivity(); } }; Signals.addSignalMethods(WorkspacesControls.prototype); @@ -1394,40 +1053,18 @@ WorkspacesManager.prototype = { this.controlsBar.actor.connect('destroy', Lang.bind(this, this._onDestroy)); - this._viewChangedId = - global.settings.connect('changed::' + WORKSPACES_VIEW_KEY, - Lang.bind(this, this._updateView)); this._nWorkspacesNotifyId = global.screen.connect('notify::n-workspaces', Lang.bind(this, this._workspacesChanged)); }, _updateView: function() { - let viewType, newView; - - let view = global.settings.get_string(WORKSPACES_VIEW_KEY).toUpperCase(); - if (view in WorkspacesViewType) - viewType = WorkspacesViewType[view]; - else - viewType = WorkspacesViewType.SINGLE; - - switch (viewType) { - case WorkspacesViewType.SINGLE: - newView = new SingleView(this._workspacesWidth, + let newView = new WorkspacesView(this._workspacesWidth, this._workspacesHeight, this._workspacesX, this._workspacesY, this._workspaces); - break; - case WorkspacesViewType.GRID: - default: - newView = new MosaicView(this._workspacesWidth, - this._workspacesHeight, - this._workspacesX, - this._workspacesY, - this._workspaces); - break; - } + if (this.workspacesView) this.workspacesView.destroy(); this.workspacesView = newView; From 6c5c3bedbed2840acc269683b6a61e22cae9032b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Fri, 12 Nov 2010 13:32:06 +0100 Subject: [PATCH 39/61] workspaces-view: Swap workspace ordering for RTL locales Make the first workspace the right-most one in RTL locales, as one would expect. Update all dragging/scrolling functions to behave correctly. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- js/ui/workspacesView.js | 46 +++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 2bb2a06aa..a1e5d1d44 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -431,8 +431,13 @@ WorkspacesView.prototype = { workspace.opacity = (this._inDrag && w != active) ? 200 : 255; workspace.scale = scale; - workspace.x = this._x + this._activeWorkspaceX - + (w - active) * (_width + this._spacing); + if (St.Widget.get_default_direction() == St.TextDirection.RTL) { + workspace.x = this._x + this._activeWorkspaceX + - (w - active) * (_width + this._spacing); + } else { + workspace.x = this._x + this._activeWorkspaceX + + (w - active) * (_width + this._spacing); + } workspace.y = this._y + this._activeWorkspaceY; } }, @@ -522,15 +527,18 @@ WorkspacesView.prototype = { // by releasing during the drag. let noStop = Math.abs(activate - this._scrollAdjustment.value) > 0.5; + let difference = stageX > this._dragStartX ? -1 : 1; + if (St.Widget.get_default_direction() == St.TextDirection.RTL) + difference *= -1; + // We detect if the user is stopped by comparing the timestamp of the button // release with the timestamp of the last motion. Experimentally, a difference // of 0 or 1 millisecond indicates that the mouse is in motion, a larger // difference indicates that the mouse is stopped. if ((this._lastMotionTime > 0 && this._lastMotionTime > event.get_time() - 2) || noStop) { - if (stageX > this._dragStartX && activate > 0) - activate--; - else if (stageX < this._dragStartX && activate < last) - activate++; + if (activate + difference >= 0 && + activate + difference <= last) + activate += difference; } if (activate != active) { @@ -551,7 +559,10 @@ WorkspacesView.prototype = { let dx = this._dragX - stageX; let primary = global.get_primary_monitor(); - this._scrollAdjustment.value += (dx / primary.width); + if (St.Widget.get_default_direction() == St.TextDirection.RTL) + this._scrollAdjustment.value -= (dx / primary.width); + else + this._scrollAdjustment.value += (dx / primary.width); this._dragX = stageX; this._lastMotionTime = event.get_time(); @@ -770,8 +781,14 @@ WorkspacesView.prototype = { let primary = global.get_primary_monitor(); let activeWorkspaceIndex = global.screen.get_active_workspace_index(); - let leftWorkspace = this._workspaces[activeWorkspaceIndex - 1]; - let rightWorkspace = this._workspaces[activeWorkspaceIndex + 1]; + let leftWorkspace, rightWorkspace; + if (St.Widget.get_default_direction() == St.TextDirection.RTL) { + leftWorkspace = this._workspaces[activeWorkspaceIndex + 1]; + rightWorkspace = this._workspaces[activeWorkspaceIndex - 1]; + } else { + leftWorkspace = this._workspaces[activeWorkspaceIndex - 1]; + rightWorkspace = this._workspaces[activeWorkspaceIndex + 1]; + } let hoverWorkspace = null; // reactive monitor edges @@ -892,10 +909,13 @@ WorkspacesView.prototype = { let current = global.screen.get_active_workspace_index(); let last = global.screen.n_workspaces - 1; let activate = current; - if (direction == Clutter.ScrollDirection.DOWN && current < last) - activate++; - else if (direction == Clutter.ScrollDirection.UP && current > 0) - activate--; + + let difference = direction == Clutter.ScrollDirection.UP ? -1 : 1; + if (St.Widget.get_default_direction() == St.TextDirection.RTL) + difference *= -1; + + if (activate + difference >= 0 && activate + difference <= last) + activate += difference; if (activate != current) { let metaWorkspace = this._workspaces[activate].metaWorkspace; From 7811632e6fddd0dd13a180d589866398ae8f42f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Mon, 4 Oct 2010 16:42:11 +0200 Subject: [PATCH 40/61] workspaces: Rework workspace controls for the view selector As workspaces will appear as a particular view in the view selector, merge WorkspacesControls and WorkspacesManager to control workspaces and related controls, so that a single actor can be added to the selector instead of positioning the elements from the overview. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 66 ++--- js/ui/overview.js | 68 +---- js/ui/workspacesView.js | 548 +++++++++++++++++++------------------ 3 files changed, 331 insertions(+), 351 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 9ce36261f..ed04a4663 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -262,21 +262,46 @@ StTooltip StLabel { spacing: 25px; } -.workspaces-bar { - spacing: 5px; +.workspace-indicator-panel { + spacing: 8px; } .workspace-indicator { width: 24px; height: 16px; - background: rgba(155,155,155,0.8); - border-spacing: 16px; + background: rgba(255,255,255,0.2); } .workspace-indicator.active { background: rgba(255,255,255,0.8); } +.workspace-controls { + width: 48px; + font-size: 32px; + font-weight: bold; + color: #ffffff; + border: 2px solid rgba(128, 128, 128, 0.4); + border-right: 0px; + border-radius: 9px 0px 0px 9px; +} + +.add-workspace { + background-color: rgba(128, 128, 128, 0.4); +} + +.add-workspace:hover { + background-color: rgba(128, 128, 128, 0.6); +} + +.remove-workspace { + height: 48px; +} + +.remove-workspace:hover { + background-color: rgba(128, 128, 128, 0.2); +} + .window-caption { background: rgba(0,0,0,0.8); border: 1px solid rgba(128,128,128,0.40); @@ -294,39 +319,6 @@ StTooltip StLabel { -shell-close-overlap: 16px; } -.single-view-controls { - padding: 0px 15px; -} - -.workspace-controls { - width: 24px; - height: 16px; -} - -.workspace-controls.add { - background-image: url("add-workspace.svg"); -} - -.workspace-controls.remove { - background-image: url("remove-workspace.svg"); -} - -.workspace-controls.switch-single { - background-image: url("single-view.svg"); -} - -.workspace-controls.switch-mosaic { - background-image: url("mosaic-view.svg"); -} - -.workspace-controls.switch-single:checked { - background-image: url("single-view-active.svg"); -} - -.workspace-controls.switch-mosaic:checked { - background-image: url("mosaic-view-active.svg"); -} - /* Dash */ #dash { diff --git a/js/ui/overview.js b/js/ui/overview.js index a27b25bd1..6c351fd52 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -184,18 +184,10 @@ Overview.prototype = { this._group = new St.Group({ name: 'overview' }); this._group._delegate = this; - this._group.connect('destroy', Lang.bind(this, - function() { - if (this._lightbox) { - this._lightbox.destroy(); - this._lightbox = null; - } - })); this.shellInfo = new ShellInfo(); - this._workspacesManager = null; - this._lightbox = null; + this._workspacesDisplay = null; this.visible = false; this.animationInProgress = false; @@ -258,18 +250,6 @@ Overview.prototype = { return clone; }, - _onViewChanged: function() { - if (!this.visible) - return; - - this.workspaces = this._workspacesManager.workspacesView; - - // Show new workspacesView - this._group.add_actor(this.workspaces.actor); - this._workspacesBar.raise(this.workspaces.actor); - this._dash.actor.raise(this.workspaces.actor); - }, - _recalculateGridSizes: function () { let primary = global.get_primary_monitor(); wideScreen = (primary.width/primary.height > WIDE_SCREEN_CUT_OFF_RATIO) && @@ -326,10 +306,6 @@ Overview.prototype = { } this._dash.actor.height = this._workspacesHeight; - // place the 'Add Workspace' button in the bottom row of the grid - this._workspacesBarX = this._workspacesX; - this._workspacesBarWidth = this._workspacesWidth; - this._workspacesBarY = primary.height - displayGridRowHeight; this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING, this._workspacesY); @@ -375,11 +351,6 @@ Overview.prototype = { this._activeDisplayPane.close(); return true; })); - if (!this._lightbox) - this._lightbox = new Lightbox.Lightbox(this._group, - { fadeTime: PANE_FADE_TIME }); - this._lightbox.show(); - this._lightbox.highlight(this._paneContainer); } else if (pane == this._activeDisplayPane) { this._activeDisplayPane = null; if (backgroundEventId != null) { @@ -388,7 +359,6 @@ Overview.prototype = { } this._transparentBackground.lower_bottom(); this._paneContainer.hide(); - this._lightbox.hide(); } })); }, @@ -443,29 +413,23 @@ Overview.prototype = { this.animationInProgress = true; /* TODO: make this stuff dynamic */ - this._workspacesManager = - new WorkspacesView.WorkspacesManager(this._workspacesWidth, + this._workspacesDisplay = + new WorkspacesView.WorkspacesDisplay(this._workspacesWidth, this._workspacesHeight, this._workspacesX, this._workspacesY); - this._workspacesManager.connect('view-changed', - Lang.bind(this, this._onViewChanged)); - this.workspaces = this._workspacesManager.workspacesView; + this._workspacesDisplay.actor.set_size(this._workspacesWidth, + this._workspacesHeight); + this._workspacesDisplay.actor.set_position(this._workspacesX, + this._workspacesY); + this._group.add_actor(this._workspacesDisplay.actor); + + this._workspacesDisplay.show(); + this.workspaces = this._workspacesDisplay.workspacesView; + + // Show new workspacesView this._group.add_actor(this.workspaces.actor); - // The workspaces actor is as big as the screen, so we have to raise the dash above it - // for drag and drop to work. In the future we should fix the workspaces to not - // be as big as the screen. - this._dash.actor.raise(this.workspaces.actor); - - this._workspacesBar = this._workspacesManager.controlsBar.actor; - this._workspacesBar.set_position(this._workspacesBarX, - this._workspacesBarY); - this._workspacesBar.width = this._workspacesBarWidth; - - this._group.add_actor(this._workspacesBar); - this._workspacesBar.raise(this.workspaces.actor); - if (!this._desktopFade.child) this._desktopFade.child = this._getDesktopClone(); @@ -605,10 +569,8 @@ Overview.prototype = { this.workspaces.destroy(); this.workspaces = null; - this._workspacesBar.destroy(); - this._workspacesBar = null; - - this._workspacesManager = null; + this._workspacesDisplay.actor.destroy(); + this._workspacesDisplay = null; this._desktopFade.hide(); this._background.hide(); diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index a1e5d1d44..f05b7bea3 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -22,135 +22,9 @@ const MAX_WORKSPACES = 16; const WORKSPACE_DRAGGING_SCALE = 0.85; +const CONTROLS_POP_IN_FRACTION = 0.8; +const CONTROLS_POP_IN_TIME = 0.1; -function WorkspaceIndicator(activateWorkspace, workspaceAcceptDrop, workspaceHandleDragOver, scrollEventCb) { - this._init(activateWorkspace, workspaceAcceptDrop, workspaceHandleDragOver, scrollEventCb); -} - -WorkspaceIndicator.prototype = { - _init: function(activateWorkspace, workspaceAcceptDrop, workspaceHandleDragOver, scrollEventCb) { - this._activateWorkspace = activateWorkspace; - this._workspaceAcceptDrop = workspaceAcceptDrop; - this._workspaceHandleDragOver = workspaceHandleDragOver; - this._scrollEventCb = scrollEventCb; - let actor = new St.Bin({ style_class: 'panel-button' }); - - this._indicatorsPanel = new Shell.GenericContainer(); - this._indicatorsPanel.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); - this._indicatorsPanel.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); - this._indicatorsPanel.connect('allocate', Lang.bind(this, this._allocate)); - this._indicatorsPanel.clip_to_allocation = true; - - actor.set_child(this._indicatorsPanel); - actor.set_alignment(St.Align.MIDDLE, St.Align.MIDDLE); - this._indicatorsPanel.hide(); - actor.connect('destroy', Lang.bind(this, this._onDestroy)); - - let workId = Main.initializeDeferredWork(actor, Lang.bind(this, this._workspacesChanged)); - this._nWorkspacesNotifyId = - global.screen.connect('notify::n-workspaces', function() { - Main.queueDeferredWork(workId); - }); - this._switchWorkspaceNotifyId = - global.window_manager.connect('switch-workspace', function() { - Main.queueDeferredWork(workId); - }); - - this.actor = actor; - }, - - _workspacesChanged: function() { - let active = global.screen.get_active_workspace_index(); - let n = global.screen.n_workspaces; - if (n > 1) - this._indicatorsPanel.show(); - else - this._indicatorsPanel.hide(); - this._fillPositionalIndicator(); - }, - - _onDestroy: function() { - if (this._nWorkspacesNotifyId > 0) - global.screen.disconnect(this._nWorkspacesNotifyId); - this._nWorkspacesNotifyId = 0; - if (this._switchWorkspaceNotifyId > 0) - global.window_manager.disconnect(this._switchWorkspaceNotifyId); - this._switchWorkspaceNotifyId = 0; - }, - - _allocate: function(actor, box, flags) { - let children = actor.get_children(); - let childBox = new Clutter.ActorBox(); - for (let i = 0; i < children.length; i++) { - childBox.x1 = children[i].x; - childBox.y1 = 0; - childBox.x2 = children[i].x + children[i].width; - childBox.y2 = children[i].height; - children[i].allocate(childBox, flags); - } - }, - - _getPreferredWidth: function(actor, fh, alloc) { - let children = actor.get_children(); - let width = 0; - for (let i = 0; i < children.length; i++) { - if (children[i].x + children[i].width <= width) - continue; - width = children[i].x + children[i].width; - } - alloc.min_size = 0; - alloc.natural_size = width; - }, - - _getPreferredHeight: function(actor, fw, alloc) { - let children = actor.get_children(); - let height = 0; - if (children.length) - height = children[0].height; - alloc.min_size = height; - alloc.natural_size = height; - }, - - _addIndicatorClone: function(i, active) { - let actor = new St.Button({ style_class: 'workspace-indicator' }); - if (active) { - actor.style_class = 'workspace-indicator active'; - } - actor.connect('clicked', Lang.bind(this, function() { - this._activateWorkspace(i); - })); - - actor._delegate = {}; - actor._delegate.acceptDrop = Lang.bind(this, function(source, actor, x, y, time) { - if (this._workspaceAcceptDrop(i, source, actor, x, y, time)) { - this._activateWorkspace(i); - return true; - } - else - return false; - }); - actor._delegate.handleDragOver = Lang.bind(this, function(source, actor, x, y, time) { - return this._workspaceHandleDragOver(i, source, actor, x, y, time); - }); - - actor.connect('scroll-event', this._scrollEventCb); - - this._indicatorsPanel.add_actor(actor); - - let spacing = actor.get_theme_node().get_length('border-spacing'); - actor.x = spacing * i + actor.width * i; - }, - - _fillPositionalIndicator: function() { - this._indicatorsPanel.remove_all(); - - let activeWorkspaceIndex = global.screen.get_active_workspace_index(); - let n = global.screen.n_workspaces; - for (let i = 0; i < n; i++) { - this._addIndicatorClone(i, i == activeWorkspaceIndex); - } - } -}; function WorkspacesView(width, height, x, y, workspaces) { this._init(width, height, x, y, workspaces); @@ -775,6 +649,8 @@ WorkspacesView.prototype = { dragMotion: Lang.bind(this, this._onDragMotion) }; DND.addDragMonitor(this._dragMonitor); + + this.emit('window-drag-begin'); }, _onDragMotion: function(dragEvent) { @@ -865,6 +741,8 @@ WorkspacesView.prototype = { for (let i = 0; i < this._workspaces.length; i++) this._workspaces[i].setReservedSlot(null); + + this.emit('window-drag-end'); }, // sync the workspaces' positions to the value of the scroll adjustment @@ -901,6 +779,128 @@ WorkspacesView.prototype = { } }, + _getWorkspaceIndexToRemove: function() { + return global.screen.get_active_workspace_index(); + } +}; +Signals.addSignalMethods(WorkspacesView.prototype); + + +function WorkspaceIndicatorPanel() { + this._init(); +} + +WorkspaceIndicatorPanel.prototype = { + _init: function() { + this.actor = new Shell.GenericContainer({ clip_to_allocation: true }); + this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); + this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); + this.actor.connect('allocate', Lang.bind(this, this._allocate)); + + this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); + + this._box = new St.BoxLayout({ style_class: 'workspace-indicator-panel' }); + this.actor.add_actor(this._box); + + this._switchWorkspaceNotifyId = + global.window_manager.connect('switch-workspace', + Lang.bind(this, this._updateActive)); + }, + + _onDestroy: function() { + if (this._switchWorkspaceNotifyId > 0) + global.window_manager.disconnect(this._switchWorkspaceNotifyId); + this._switchWorkspaceNotifyId = 0; + this._workspaces = null; + }, + + // Allocate the box centered to the available area like StBin would do, + // except that the full height is used even if the box is not actually + // shown. This is a workaround, as the size of the workspace area is + // determined once when entering the overview, so if it would take up + // the indicator space in that case, it would overlap it later when + // additional workspaces were added. + _allocate: function(actor, box, flags) { + let children = this._box.get_children(); + + let availWidth = box.x2 - box.x1; + let availHeight = box.y2 - box.y1; + let [minWidth, natWidth] = this._box.get_preferred_width(-1); + + let childBox = new Clutter.ActorBox(); + childBox.x1 = Math.floor((availWidth - natWidth) / 2); + childBox.x2 = childBox.x1 + natWidth; + childBox.y1 = 0; + childBox.y2 = availHeight; + + this._box.allocate(childBox, flags); + }, + + _getPreferredWidth: function(actor, forHeight, alloc) { + let [minWidth, natWidth] = this._box.get_preferred_width(-1); + alloc.min_size = 0; + alloc.natural_size = natWidth; + }, + + _getPreferredHeight: function(actor, forWidth, alloc) { + let [minHeight, natHeight] = this._box.get_preferred_height(-1); + alloc.min_size = minHeight; + alloc.natural_size = natHeight; + }, + + updateWorkspaces: function(workspaces) { + this._workspaces = workspaces; + + // Do not display a single indicator + if (this._workspaces.length == 1) + this.actor.set_skip_paint(this._box, true); + else + this.actor.set_skip_paint(this._box, false); + + this._box.remove_all(); + for (let i = 0; i < this._workspaces.length; i++) { + let actor = new St.Button({ style_class: 'workspace-indicator' }); + let workspace = this._workspaces[i]; + let metaWorkspace = this._workspaces[i].metaWorkspace; + + actor.connect('clicked', Lang.bind(this, function() { + metaWorkspace.activate(global.get_current_time()); + })); + + actor._delegate = { + acceptDrop: Lang.bind(this, + function(source, actor, x, y, time) { + if (workspace.acceptDrop(source, actor, x, y, time)) { + metaWorkspace.activate(time); + return true; + } + return false; + }), + handleDragOver: Lang.bind(this, + function(source, actor, x, y, time) { + return workspace.handleDragOver(source, actor, x, y, time); + }) + }; + + actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); + + this._box.add(actor); + } + + this._updateActive(); + }, + + _updateActive: function() { + let children = this._box.get_children(); + let activeIndex = global.screen.get_active_workspace_index(); + for (let i = 0; i < children.length; i++) { + if (i == activeIndex) + children[i].add_style_class_name('active'); + else + children[i].remove_style_class_name('active'); + } + }, + // handle scroll wheel events: // activate the next or previous workspace and let the signal handler // manage the animation @@ -921,140 +921,118 @@ WorkspacesView.prototype = { let metaWorkspace = this._workspaces[activate].metaWorkspace; metaWorkspace.activate(global.get_current_time()); } - }, - - createControllerBar: function() { - let actor = new St.BoxLayout({ style_class: 'single-view-controls', - pack_start: true, - vertical: true }); - - let indicator = new WorkspaceIndicator(Lang.bind(this, function(i) { - if (this._workspaces[i] != undefined) - this._workspaces[i].metaWorkspace.activate(global.get_current_time()); - }), Lang.bind(this, function(i, source, actor, x, y, time) { - if (this._workspaces[i] != undefined) - return this._workspaces[i].acceptDrop(source, actor, x, y, time); - return false; - }), Lang.bind(this, function(i, source, actor, x, y, time) { - if (this._workspaces[i] != undefined) - return this._workspaces[i].handleDragOver(source, actor, x, y, time); - return DND.DragMotionResult.CONTINUE; - }), Lang.bind(this, this._onScrollEvent)); - - actor.add(indicator.actor, { expand: true, x_fill: true, y_fill: true }); - return actor; - }, - - _getWorkspaceIndexToRemove: function() { - return global.screen.get_active_workspace_index(); } }; -function WorkspacesControls() { - this._init(); + +function WorkspaceControlsContainer(controls) { + this._init(controls); } -WorkspacesControls.prototype = { - _init: function() { - this.actor = new St.BoxLayout({ style_class: 'workspaces-bar' }); - this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); +WorkspaceControlsContainer.prototype = { + _init: function(controls) { + this.actor = new Shell.GenericContainer({ clip_to_allocation: true }); + this.actor.connect('get-preferred-width', + Lang.bind(this, this._getPreferredWidth)); + this.actor.connect('get-preferred-height', + Lang.bind(this, this._getPreferredHeight)); + this.actor.connect('allocate', Lang.bind(this, this._allocate)); - this._currentView = null; + this.actor.add_actor(controls); - // View specific controls - this._viewControls = new St.Bin({ x_fill: true, y_fill: true }); - this.actor.add(this._viewControls, { expand: true, x_fill: true }); + this._controls = controls; + this._controls.reactive = true; + this._controls.track_hover = true; + this._controls.connect('notify::hover', + Lang.bind(this, this._onHoverChanged)); - // Add/remove workspace buttons - this._removeButton = new St.Button({ style_class: 'workspace-controls remove' }); - this._removeButton.connect('clicked', Lang.bind(this, function() { - this._currentView.removeWorkspace(); - })); - this.actor.add(this._removeButton, { y_fill: false, - y_align: St.Align.START }); - - this._addButton = new St.Button({ style_class: 'workspace-controls add' }); - this._addButton.connect('clicked', Lang.bind(this, function() { - this._currentView.addWorkspace(); - })); - this._addButton._delegate = this._addButton; - this._addButton._delegate.acceptDrop = Lang.bind(this, - function(source, actor, x, y, time) { - return this._currentView._acceptNewWorkspaceDrop(source, actor, x, y, time); - }); - this._addButton._delegate.handleDragOver = Lang.bind(this, - function(source, actor, x, y, time) { - return this._currentView._handleDragOverNewWorkspace(source, actor, x, y, time); - }); - this.actor.add(this._addButton, { y_fill: false, - y_align: St.Align.START }); - - this._nWorkspacesNotifyId = - global.screen.connect('notify::n-workspaces', - Lang.bind(this, this.updateControlsSensitivity)); - this._switchWorkspaceNotifyId = - global.window_manager.connect('switch-workspace', - Lang.bind(this, this.updateControlsSensitivity)); - - this.updateControlsSensitivity(); + this._itemDragBeginId = 0; + this._itemDragEndId = 0; }, - updateControls: function(view) { - this._currentView = view; + show: function() { + if (this._itemDragBeginId == 0) + this._itemDragBeginId = Main.overview.connect('item-drag-begin', + Lang.bind(this, this.popOut)); + if (this._itemDragEndId == 0) + this._itemDragEndId = Main.overview.connect('item-drag-end', + Lang.bind(this, this.popIn)); + this._controls.x = this._poppedInX(); + }, - this.updateControlsSensitivity(); - - let newControls = this._currentView.createControllerBar(); - if (newControls) { - this._viewControls.child = newControls; - this._viewControls.child.opacity = 0; - Tweener.addTween(this._viewControls.child, - { opacity: 255, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad' }); - } else { - if (this._viewControls.child) - Tweener.addTween(this._viewControls.child, - { opacity: 0, - time: Overview.ANIMATION_TIME, - transition: 'easeOutQuad', - onComplete: Lang.bind(this, function() { - this._viewControls.child.destroy(); - })}); + hide: function() { + if (this._itemDragBeginId > 0) { + Main.overview.disconnect(this._itemDragBeginId); + this._itemDragBeginId = 0; + } + if (this._itemEndBeginId > 0) { + Main.overview.disconnect(this._itemDragEndId); + this._itemDragEndId = 0; } }, - _onDestroy: function() { - if (this._nWorkspacesNotifyId > 0) { - global.screen.disconnect(this._nWorkspacesNotifyId); - this._nWorkspacesNotifyId = 0; - } - if (this._switchWorkspaceNotifyId > 0) { - global.window_manager.disconnect(this._switchWorkspaceNotifyId); - this._switchWorkspaceNotifyId = 0; - } + _getPreferredWidth: function(actor, forHeight, alloc) { + let [minWidth, natWidth] = this._controls.get_preferred_width(-1); + alloc.min_size = minWidth; + alloc.natural_size = natWidth; }, - _setButtonSensitivity: function(button, sensitive) { - if (button == null) - return; - button.opacity = sensitive ? 255 : 85; + // Always request the full width ... + _getPreferredHeight: function(actor, forWidth, alloc) { + let [minHeight, natHeight] = this._controls.get_preferred_height(-1); + alloc.min_size = minHeight; + alloc.natural_size = natHeight; }, - updateControlsSensitivity: function() { - if (this._currentView) { - this._setButtonSensitivity(this._removeButton, this._currentView.canRemoveWorkspace()); - this._setButtonSensitivity(this._addButton, this._currentView.canAddWorkspace()); - } + // ... even when the controls are popped in, to keep the width constant. + // This is necessary as the workspace size is determined once before + // entering the overview, when the controls are popped in - if the width + // varied, the workspaces would be given too much width, and the controls + // would be overlapped by the workspaces when popped out, rendering them + // useless. + _allocate: function(actor, box, flags) { + let childBox = new Clutter.ActorBox(); + childBox.x1 = this._controls.x; + childBox.x2 = this._controls.x + this._controls.width; + childBox.y1 = 0; + childBox.y2 = box.y2 - box.y1; + this._controls.allocate(childBox, flags); + }, + + _onHoverChanged: function() { + if (this._controls.hover) + this.popOut(); + else + this.popIn(); + }, + + _poppedInX: function() { + let x = CONTROLS_POP_IN_FRACTION * this._controls.width; + if (St.Widget.get_default_direction() == St.TextDirection.RTL) + return -x; + return x; + }, + + popOut: function() { + Tweener.addTween(this._controls, + { x: 0, + time: CONTROLS_POP_IN_TIME, + transition: 'easeOutQuad' }); + }, + + popIn: function() { + Tweener.addTween(this._controls, + { x: this._poppedInX(), + time: CONTROLS_POP_IN_TIME, + transition: 'easeOutQuad' }); } }; -Signals.addSignalMethods(WorkspacesControls.prototype); -function WorkspacesManager(width, height, x, y) { +function WorkspacesDisplay(width, height, x, y) { this._init(width, height, x, y); } -WorkspacesManager.prototype = { +WorkspacesDisplay.prototype = { _init: function(width, height, x, y) { this._workspacesWidth = width; this._workspacesHeight = height; @@ -1067,18 +1045,52 @@ WorkspacesManager.prototype = { this._workspaces[w] = new Workspace.Workspace(metaWorkspace); } - this.workspacesView = null; - this.controlsBar = new WorkspacesControls(); - this._updateView(); + this.actor = new St.BoxLayout(); - this.controlsBar.actor.connect('destroy', - Lang.bind(this, this._onDestroy)); - this._nWorkspacesNotifyId = - global.screen.connect('notify::n-workspaces', - Lang.bind(this, this._workspacesChanged)); + let workspacesBox = new St.BoxLayout({ vertical: true }); + this.actor.add(workspacesBox, { expand: true }); + + // placeholder for window previews + workspacesBox.add(new St.Bin(), { expand: true }); + + this._workspaceIndicatorPanel = new WorkspaceIndicatorPanel(); + workspacesBox.add(this._workspaceIndicatorPanel.actor); + + let controls = new St.BoxLayout({ vertical: true, + style_class: 'workspace-controls' }); + this._controlsContainer = new WorkspaceControlsContainer(controls); + this.actor.add(this._controlsContainer.actor); + + // Add/remove workspace buttons + this._removeButton = new St.Button({ label: '–', // n-dash + style_class: 'remove-workspace' }); + this._removeButton.connect('clicked', Lang.bind(this, function() { + this.workspacesView.removeWorkspace(); + })); + controls.add(this._removeButton); + + this._addButton = new St.Button({ label: '+', + style_class: 'add-workspace' }); + this._addButton.connect('clicked', Lang.bind(this, function() { + this.workspacesView.addWorkspace(); + })); + this._addButton._delegate = this._addButton; + this._addButton._delegate.acceptDrop = Lang.bind(this, + function(source, actor, x, y, time) { + return this.workspacesView._acceptNewWorkspaceDrop(source, actor, x, y, time); + }); + this._addButton._delegate.handleDragOver = Lang.bind(this, + function(source, actor, x, y, time) { + return this.workspacesView._handleDragOverNewWorkspace(source, actor, x, y, time); + }); + controls.add(this._addButton, { expand: true }); + + this.workspacesView = null; }, - _updateView: function() { + show: function() { + this._controlsContainer.show(); + let newView = new WorkspacesView(this._workspacesWidth, this._workspacesHeight, this._workspacesX, @@ -1089,9 +1101,33 @@ WorkspacesManager.prototype = { this.workspacesView.destroy(); this.workspacesView = newView; - this.controlsBar.updateControls(this.workspacesView); + this.workspacesView.connect('window-drag-begin', Lang.bind(this, + function() { + this._controlsContainer.popOut(); + })); + this.workspacesView.connect('window-drag-end', Lang.bind(this, + function() { + this._controlsContainer.popIn(); + })); - this.emit('view-changed'); + this._workspaceIndicatorPanel.updateWorkspaces(this._workspaces); + + this._nWorkspacesNotifyId = + global.screen.connect('notify::n-workspaces', + Lang.bind(this, this._workspacesChanged)); + }, + + hide: function() { + this._controlsContainer.hide(); + + if (this._nWorkspacesNotifyId > 0) + global.screen.disconnect(this._nWorkspacesNotifyId); + this.workspacesView.destroy(); + this.workspacesView = null; + for (let w = 0; w < this._workspaces.length; w++) { + this._workspaces[w].disconnectAll(); + this._workspaces[w].destroy(); + } }, _workspacesChanged: function() { @@ -1134,17 +1170,7 @@ WorkspacesManager.prototype = { this.workspacesView.updateWorkspaces(oldNumWorkspaces, newNumWorkspaces, lostWorkspaces); - }, - - _onDestroy: function() { - if (this._nWorkspacesNotifyId > 0) - global.screen.disconnect(this._nWorkspacesNotifyId); - if (this._viewChangedId > 0) - global.settings.disconnect(this._viewChangedId); - for (let w = 0; w < this._workspaces.length; w++) { - this._workspaces[w].disconnectAll(); - this._workspaces[w].destroy(); - } + this._workspaceIndicatorPanel.updateWorkspaces(this._workspaces); } }; -Signals.addSignalMethods(WorkspacesManager.prototype); +Signals.addSignalMethods(WorkspacesDisplay.prototype); From d5d7d8a3913a2ae5d23acd96d8ad14df203b7ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 29 Jul 2010 09:55:08 +0200 Subject: [PATCH 41/61] overview: Add ViewSelector to the overview Add the view selector and adjust the positioning of elements in the overview. Unlike the old dash, the view selector is made public to indicate that extensions may add additional views or search providers. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 9 +- js/ui/overview.js | 283 ++++++++++--------------------------- js/ui/workspacesView.js | 59 +++++--- 3 files changed, 117 insertions(+), 234 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index ed04a4663..5e2b00085 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -257,6 +257,11 @@ StTooltip StLabel { /* Overview */ +#overview { + spacing: 12px; + background-color: rgba(0,0,0,0.6); +} + .workspaces-view { color: white; spacing: 25px; @@ -336,10 +341,6 @@ StTooltip StLabel { width: 60px; } -#dashSections { - spacing: 12px; -} - #viewSelector { spacing: 16px; } diff --git a/js/ui/overview.js b/js/ui/overview.js index 6c351fd52..19a24ed51 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -9,76 +9,27 @@ const St = imports.gi.St; const Gettext = imports.gettext.domain('gnome-shell'); const _ = Gettext.gettext; +const AppDisplay = imports.ui.appDisplay; +const Dash = imports.ui.dash; +const DocDisplay = imports.ui.docDisplay; const GenericDisplay = imports.ui.genericDisplay; const Lightbox = imports.ui.lightbox; const Main = imports.ui.main; const MessageTray = imports.ui.messageTray; const Panel = imports.ui.panel; -const Dash = imports.ui.dash; +const PlaceDisplay = imports.ui.placeDisplay; const Tweener = imports.ui.tweener; +const ViewSelector = imports.ui.viewSelector; const WorkspacesView = imports.ui.workspacesView; // Time for initial animation going into Overview mode const ANIMATION_TIME = 0.25; -// Time for pane menus to fade in/out -const PANE_FADE_TIME = 0.1; - -// We divide the screen into a grid of rows and columns, which we use -// to help us position the Overview components, such as the side panel -// that lists applications and documents, the workspaces display, and -// the button for adding additional workspaces. -// In the regular mode, the side panel takes up one column on the left, -// and the workspaces display takes up the remaining columns. -// In the expanded side panel display mode, the side panel takes up two -// columns, and the workspaces display slides all the way to the right, -// being visible only in the last quarter of the right-most column. -// In the future, this mode will have more components, such as a display -// of documents which were recently opened with a given application, which -// will take up the remaining sections of the display. - -const WIDE_SCREEN_CUT_OFF_RATIO = 1.4; -// A common netbook resolution is 1024x600, which trips the widescreen -// ratio. However that leaves way too few pixels for the dash. So -// just treat this as a regular screen. -const WIDE_SCREEN_MINIMUM_HEIGHT = 768; - -const COLUMNS_REGULAR_SCREEN = 4; -const ROWS_REGULAR_SCREEN = 8; -const COLUMNS_WIDE_SCREEN = 5; -const ROWS_WIDE_SCREEN = 10; - -const DEFAULT_PADDING = 4; - -// Padding around workspace grid / Spacing between Dash and Workspaces -const WORKSPACE_GRID_PADDING = 12; - -const COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN = 3; -const ROWS_FOR_WORKSPACES_REGULAR_SCREEN = 6; - -const COLUMNS_FOR_WORKSPACES_WIDE_SCREEN = 4; -const ROWS_FOR_WORKSPACES_WIDE_SCREEN = 8; - -// A multi-state; PENDING is used during animations -const STATE_ACTIVE = true; -const STATE_PENDING_INACTIVE = false; -const STATE_INACTIVE = false; - -const SHADOW_COLOR = new Clutter.Color(); -SHADOW_COLOR.from_pixel(0x00000033); -const TRANSPARENT_COLOR = new Clutter.Color(); -TRANSPARENT_COLOR.from_pixel(0x00000000); - -const SHADOW_WIDTH = 6; - -const NUMBER_OF_SECTIONS_IN_SEARCH = 2; +// We split the screen vertically between the dash and the view selector. +const DASH_SPLIT_FRACTION = 0.1; const SHELL_INFO_HIDE_TIMEOUT = 10; -let wideScreen = false; -let displayGridColumnWidth = null; -let displayGridRowHeight = null; - function Source() { this._init(); } @@ -182,8 +133,19 @@ Overview.prototype = { this._desktopFade = new St.Bin(); global.overlay_group.add_actor(this._desktopFade); + this._spacing = 0; + this._group = new St.Group({ name: 'overview' }); this._group._delegate = this; + this._group.connect('style-changed', + Lang.bind(this, function() { + let node = this._group.get_theme_node(); + let spacing = node.get_length('spacing'); + if (spacing != this._spacing) { + this._spacing = spacing; + this.relayout(); + } + })); this.shellInfo = new ShellInfo(); @@ -193,10 +155,6 @@ Overview.prototype = { this.animationInProgress = false; this._hideInProgress = false; - this._recalculateGridSizes(); - - this._activeDisplayPane = null; - // During transitions, we raise this to the top to avoid having the overview // area be reactive; it causes too many issues such as double clicks on // Dash elements, or mouseover handlers in the workspaces. @@ -205,31 +163,30 @@ Overview.prototype = { this._group.add_actor(this._coverPane); this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; })); - // Similar to the cover pane but used for dialogs ("panes"); see the comments - // in addPane below. - this._transparentBackground = new Clutter.Rectangle({ opacity: 0, - reactive: true }); - this._group.add_actor(this._transparentBackground); this._group.hide(); global.overlay_group.add_actor(this._group); + this.viewSelector = new ViewSelector.ViewSelector(); + this._group.add_actor(this.viewSelector.actor); + + this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); + this.viewSelector.addViewTab("Windows", this._workspacesDisplay.actor); + + let appView = new AppDisplay.AllAppDisplay(); + this.viewSelector.addViewTab("Applications", appView.actor); + + // Default search providers + this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider()); + this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider()); + this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider()); + this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider()); + // TODO - recalculate everything when desktop size changes this._dash = new Dash.Dash(); this._group.add_actor(this._dash.actor); - - // Container to hold popup pane chrome. - this._paneContainer = new St.BoxLayout({ style_class: 'overview-pane' }); - // Note here we explicitly don't set the paneContainer to be reactive yet; that's done - // inside the notify::visible handler on panes. - this._paneContainer.connect('button-release-event', Lang.bind(this, function(background) { - this._activeDisplayPane.close(); - return true; - })); - this._group.add_actor(this._paneContainer); - - this._transparentBackground.lower_bottom(); - this._paneContainer.hide(); + this._dash.actor.add_constraint(this.viewSelector.constrainY); + this._dash.actor.add_constraint(this.viewSelector.constrainHeight); this._coverPane.lower_bottom(); @@ -250,127 +207,39 @@ Overview.prototype = { return clone; }, - _recalculateGridSizes: function () { - let primary = global.get_primary_monitor(); - wideScreen = (primary.width/primary.height > WIDE_SCREEN_CUT_OFF_RATIO) && - (primary.height >= WIDE_SCREEN_MINIMUM_HEIGHT); - - // We divide the screen into an imaginary grid which helps us determine the layout of - // different visual components. - if (wideScreen) { - displayGridColumnWidth = Math.floor(primary.width / COLUMNS_WIDE_SCREEN); - displayGridRowHeight = Math.floor(primary.height / ROWS_WIDE_SCREEN); - } else { - displayGridColumnWidth = Math.floor(primary.width / COLUMNS_REGULAR_SCREEN); - displayGridRowHeight = Math.floor(primary.height / ROWS_REGULAR_SCREEN); - } - }, - relayout: function () { let primary = global.get_primary_monitor(); let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL); - this._recalculateGridSizes(); - - this._group.set_position(primary.x, primary.y); - this._group.set_size(primary.width, primary.height); - let contentY = Panel.PANEL_HEIGHT; - let contentHeight = primary.height - contentY; + let contentHeight = primary.height - contentY - Main.messageTray.actor.height; this._coverPane.set_position(0, contentY); this._coverPane.set_size(primary.width, contentHeight); - let workspaceColumnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN; - let workspaceRowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN; - - this._workspacesWidth = displayGridColumnWidth * workspaceColumnsUsed - - WORKSPACE_GRID_PADDING * 2; - // We scale the vertical padding by (primary.height / primary.width) - // so that the workspace preserves its aspect ratio. - this._workspacesHeight = Math.floor(displayGridRowHeight * workspaceRowsUsed - - WORKSPACE_GRID_PADDING * (primary.height / primary.width) * 2); + let viewWidth = (1.0 - DASH_SPLIT_FRACTION) * primary.width - this._spacing; + let viewHeight = contentHeight - 2 * this._spacing; + let viewY = contentY + this._spacing; + let viewX = rtl ? 0 + : Math.floor(DASH_SPLIT_FRACTION * primary.width) + this._spacing; + // Set the dash's x position - y is handled by a constraint + let dashX; if (rtl) { - this._workspacesX = WORKSPACE_GRID_PADDING; + this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); + dashX = primary.width; } else { - this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING; + dashX = 0; } - this._workspacesY = Math.floor(displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width)); + this._dash.actor.set_x(dashX); - if (rtl) { - this._dash.actor.set_position(primary.width - displayGridColumnWidth - WORKSPACE_GRID_PADDING / 2, - this._workspacesY); - } else { - this._dash.actor.set_position(0, this._workspacesY); - } - this._dash.actor.height = this._workspacesHeight; - - - this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING, - this._workspacesY); - // Dynamic width - this._paneContainer.height = this._workspacesHeight; - if (rtl) { - this._paneContainer.connect('notify::width', Lang.bind(this, function (paneContainer) { - paneContainer.x = this._dash.actor.x - (DEFAULT_PADDING + paneContainer.width); - })); - } - - this._transparentBackground.set_position(primary.x, primary.y); - this._transparentBackground.set_size(primary.width, primary.height); - - }, - - addPane: function (pane, align) { - pane.actor.height = .9 * this._workspacesHeight; - this._paneContainer.add(pane.actor, { expand: true, - y_fill: false, - y_align: align }); - // When a pane is displayed, we raise the transparent background to the top - // and connect to button-release-event on it, then raise the pane above that. - // The idea here is that clicking anywhere outside the pane should close it. - // When the active pane is closed, undo the effect. - let backgroundEventId = null; - pane.connect('open-state-changed', Lang.bind(this, function (pane, isOpen) { - if (isOpen) { - this._activeDisplayPane = pane; - this._transparentBackground.raise_top(); - this._paneContainer.raise_top(); - this._paneContainer.show(); - this._paneReady = false; - if (backgroundEventId != null) - this._transparentBackground.disconnect(backgroundEventId); - backgroundEventId = this._transparentBackground.connect('captured-event', Lang.bind(this, function (actor, event) { - if (event.get_source() != this._transparentBackground) - return false; - if (event.type() == Clutter.EventType.BUTTON_PRESS) - this._paneReady = true; - if (event.type() == Clutter.EventType.BUTTON_RELEASE - && this._paneReady) - this._activeDisplayPane.close(); - return true; - })); - } else if (pane == this._activeDisplayPane) { - this._activeDisplayPane = null; - if (backgroundEventId != null) { - this._transparentBackground.disconnect(backgroundEventId); - backgroundEventId = null; - } - this._transparentBackground.lower_bottom(); - this._paneContainer.hide(); - } - })); + this.viewSelector.actor.set_position(viewX, viewY); + this.viewSelector.actor.set_size(viewWidth, viewHeight); }, //// Public methods //// beginItemDrag: function(source) { - // Close any active panes if @source is a GenericDisplayItem. - // This allows the user to place the item on any workspace. - if (source instanceof GenericDisplay.GenericDisplayItem) - if (this._activeDisplayPane != null) - this._activeDisplayPane.close(); this.emit('item-drag-begin'); }, @@ -406,28 +275,27 @@ Overview.prototype = { show : function() { if (this.visible) return; - if (!Main.pushModal(this._dash.actor)) + if (!Main.pushModal(this.viewSelector.actor)) return; this.visible = true; this.animationInProgress = true; - /* TODO: make this stuff dynamic */ - this._workspacesDisplay = - new WorkspacesView.WorkspacesDisplay(this._workspacesWidth, - this._workspacesHeight, - this._workspacesX, - this._workspacesY); - this._workspacesDisplay.actor.set_size(this._workspacesWidth, - this._workspacesHeight); - this._workspacesDisplay.actor.set_position(this._workspacesX, - this._workspacesY); - this._group.add_actor(this._workspacesDisplay.actor); + // All the the actors in the window group are completely obscured, + // hiding the group holding them while the Overview is displayed greatly + // increases performance of the Overview especially when there are many + // windows visible. + // + // If we switched to displaying the actors in the Overview rather than + // clones of them, this would obviously no longer be necessary. + global.window_group.hide(); + this._group.show(); + this._background.show(); + this.viewSelector.show(); this._workspacesDisplay.show(); - this.workspaces = this._workspacesDisplay.workspacesView; - // Show new workspacesView + this.workspaces = this._workspacesDisplay.workspacesView; this._group.add_actor(this.workspaces.actor); if (!this._desktopFade.child) @@ -443,17 +311,6 @@ Overview.prototype = { }); } - // All the the actors in the window group are completely obscured, - // hiding the group holding them while the Overview is displayed greatly - // increases performance of the Overview especially when there are many - // windows visible. - // - // If we switched to displaying the actors in the Overview rather than - // clones of them, this would obviously no longer be necessary. - global.window_group.hide(); - this._group.show(); - this._background.show(); - // Create a zoom out effect. First scale the Overview group up and // position it so that the active workspace fills up the whole screen, // then transform the group to its normal dimensions and position. @@ -500,8 +357,6 @@ Overview.prototype = { transition: 'easeOutQuad' }); } - if (this._activeDisplayPane != null) - this._activeDisplayPane.close(); this.workspaces.hide(); // Create a zoom in effect by transforming the Overview group so that @@ -569,20 +424,28 @@ Overview.prototype = { this.workspaces.destroy(); this.workspaces = null; - this._workspacesDisplay.actor.destroy(); - this._workspacesDisplay = null; + this._workspacesDisplay.hide(); + this.viewSelector.hide(); this._desktopFade.hide(); this._background.hide(); this._group.hide(); + // Reset the overview actor's scale/position, so that other elements + // can calculate their position correctly the next time the overview + // is shown + let primary = global.get_primary_monitor(); + this._group.set_scale(1, 1); + this._group.set_position(primary.x, primary.y); + this._group.set_size(primary.width, primary.height); + this.visible = false; this.animationInProgress = false; this._hideInProgress = false; this._coverPane.lower_bottom(); - Main.popModal(this._dash.actor); + Main.popModal(this.viewSelector.actor); this.emit('hidden'); } }; diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index f05b7bea3..ec0dfd26d 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -1028,30 +1028,20 @@ WorkspaceControlsContainer.prototype = { } }; -function WorkspacesDisplay(width, height, x, y) { - this._init(width, height, x, y); +function WorkspacesDisplay() { + this._init(); } WorkspacesDisplay.prototype = { - _init: function(width, height, x, y) { - this._workspacesWidth = width; - this._workspacesHeight = height; - this._workspacesX = x; - this._workspacesY = y; - - this._workspaces = []; - for (let w = 0; w < global.screen.n_workspaces; w++) { - let metaWorkspace = global.screen.get_workspace_by_index(w); - this._workspaces[w] = new Workspace.Workspace(metaWorkspace); - } - + _init: function() { this.actor = new St.BoxLayout(); let workspacesBox = new St.BoxLayout({ vertical: true }); this.actor.add(workspacesBox, { expand: true }); // placeholder for window previews - workspacesBox.add(new St.Bin(), { expand: true }); + this._workspacesBin = new St.Bin(); + workspacesBox.add(this._workspacesBin, { expand: true }); this._workspaceIndicatorPanel = new WorkspaceIndicatorPanel(); workspacesBox.add(this._workspaceIndicatorPanel.actor); @@ -1086,16 +1076,45 @@ WorkspacesDisplay.prototype = { controls.add(this._addButton, { expand: true }); this.workspacesView = null; + this._nWorkspacesNotifyId = 0; }, show: function() { this._controlsContainer.show(); - let newView = new WorkspacesView(this._workspacesWidth, - this._workspacesHeight, - this._workspacesX, - this._workspacesY, - this._workspaces); + this._workspaces = []; + for (let i = 0; i < global.screen.n_workspaces; i++) { + let metaWorkspace = global.screen.get_workspace_by_index(i); + this._workspaces[i] = new Workspace.Workspace(metaWorkspace); + } + + this._nWorkspacesNotifyId = + global.screen.connect('notify::n-workspaces', + Lang.bind(this, this._workspacesChanged)); + + let binAllocation = this._workspacesBin.allocation; + let binWidth = binAllocation.x2 - binAllocation.x1; + let binHeight = binAllocation.y2 - binAllocation.y1; + + // Workspaces expect to have the same ratio as the screen, so take + // this into account when fitting the workspace into the bin + let width, height; + let binRatio = binWidth / binHeight; + let wsRatio = global.screen_width / global.screen_height; + if (wsRatio > binRatio) { + width = binWidth; + height = Math.floor(binWidth / wsRatio); + } else { + width = Math.floor(binHeight * wsRatio); + height = binHeight; + } + + // Position workspaces as if they were parented to this._workspacesBin + let [x, y] = this._workspacesBin.get_transformed_position(); + x = Math.floor(x + Math.abs(binWidth - width) / 2); + y = Math.floor(y + Math.abs(binHeight - height) / 2); + + let newView = new WorkspacesView(width, height, x, y, this._workspaces); if (this.workspacesView) this.workspacesView.destroy(); From 2c5d825c8769c04b1c150771978427b568a31dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 14 Oct 2010 14:27:28 +0200 Subject: [PATCH 42/61] search-display: Change the default display to use iconGrid Current mockups display all search results as icons as used by application results, so change the default result display to use iconGrid/BaseIcon. Remove the custom application results display, as it is no longer needed. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/theme/gnome-shell.css | 18 +++------ js/ui/appDisplay.js | 78 ++----------------------------------- js/ui/search.js | 2 +- js/ui/searchDisplay.js | 80 +++++++++++++++++++++++++------------- 4 files changed, 64 insertions(+), 114 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 5e2b00085..785b290dc 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -433,15 +433,6 @@ StTooltip StLabel { spacing: 4px; } -.search-result-content { - padding: 4px; -} - -.search-result-content:selected { - border-radius: 4px; - background: rgba(255,255,255,0.33); -} - .results-container { spacing: 4px; } @@ -511,7 +502,8 @@ StTooltip StLabel { padding: 6px 12px; } -.app-well-app > .overview-icon { +.app-well-app > .overview-icon, +.search-result-content > .overview-icon { border-radius: 4px; padding: 4px; font-size: 10px; @@ -525,11 +517,13 @@ StTooltip StLabel { background-image: url("running-indicator.svg"); } -.app-well-app.selected > .overview-icon { +.app-well-app:selected > .overview-icon, +.search-result-content:selected > .overview-icon { background: rgba(255,255,255,0.33); } -.app-well-app:hover > .overview-icon { +.app-well-app:hover > .overview-icon, +.search-result-content:hover > .overview-icon { background: rgba(255,255,255,0.33); text-shadow: black 0px 2px 2px; transition-duration: 100; diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 632f89c8e..09c7c8827 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -21,8 +21,6 @@ const Tweener = imports.ui.tweener; const Workspace = imports.ui.workspace; const Params = imports.misc.params; -const WELL_MAX_COLUMNS = 16; -const WELL_MAX_SEARCH_ROWS = 1; const MENU_POPUP_TIMEOUT = 600; function AlphabeticalView() { @@ -171,68 +169,8 @@ AllAppDisplay.prototype = { this._appView.refresh(apps); } }; - Signals.addSignalMethods(AllAppDisplay.prototype); -function AppSearchResultDisplay(provider) { - this._init(provider); -} - -AppSearchResultDisplay.prototype = { - __proto__: Search.SearchResultDisplay.prototype, - - _init: function (provider) { - Search.SearchResultDisplay.prototype._init.call(this, provider); - this._grid = new IconGrid.IconGrid({ rowLimit: WELL_MAX_SEARCH_ROWS }); - this.actor = new St.Bin({ name: 'dashAppSearchResults', - x_align: St.Align.START }); - this.actor.set_child(this._grid.actor); - }, - - renderResults: function(results, terms) { - let appSys = Shell.AppSystem.get_default(); - let maxItems = WELL_MAX_SEARCH_ROWS * WELL_MAX_COLUMNS; - for (let i = 0; i < results.length && i < maxItems; i++) { - let result = results[i]; - let app = appSys.get_app(result); - let display = new AppWellIcon(app); - this._grid.addItem(display.actor); - } - }, - - clear: function () { - this._grid.removeAll(); - this.selectionIndex = -1; - }, - - getVisibleResultCount: function() { - return this._grid.visibleItemsCount(); - }, - - selectIndex: function (index) { - let nVisible = this.getVisibleResultCount(); - if (this.selectionIndex >= 0) { - let prevActor = this._grid.getItemAtIndex(this.selectionIndex); - prevActor._delegate.setSelected(false); - } - this.selectionIndex = -1; - if (index >= nVisible) - return false; - else if (index < 0) - return false; - let targetActor = this._grid.getItemAtIndex(index); - targetActor._delegate.setSelected(true); - this.selectionIndex = index; - return true; - }, - - activateSelected: function() { - if (this.selectionIndex < 0) - return; - let targetActor = this._grid.getItemAtIndex(this.selectionIndex); - this.provider.activateResult(targetActor._delegate.app.get_id()); - } -}; function BaseAppSearchProvider() { this._init(); @@ -285,12 +223,10 @@ AppSearchProvider.prototype = { return this._appSys.subsearch(false, previousResults, terms); }, - createResultContainerActor: function () { - return new AppSearchResultDisplay(this); - }, - createResultActor: function (resultMeta, terms) { - return new AppIcon(resultMeta.id); + let app = this._appSys.get_app(resultMeta['id']); + let icon = new AppWellIcon(app); + return icon.actor; }, expandSearch: function(terms) { @@ -489,14 +425,6 @@ AppWellIcon.prototype = { } }, - setSelected: function (isSelected) { - this._selected = isSelected; - if (this._selected) - this.actor.add_style_class_name('selected'); - else - this.actor.remove_style_class_name('selected'); - }, - _onMenuPoppedUp: function() { if (this._getRunning()) { Main.overview.getWorkspacesForWindow(null).setApplicationWindowSelection(this.app.get_id()); diff --git a/js/ui/search.js b/js/ui/search.js index 33ecce8a8..adc561198 100644 --- a/js/ui/search.js +++ b/js/ui/search.js @@ -2,7 +2,7 @@ const Signals = imports.signals; -const RESULT_ICON_SIZE = 24; +const RESULT_ICON_SIZE = 48; // Not currently referenced by the search API, but // this enumeration can be useful for provider diff --git a/js/ui/searchDisplay.js b/js/ui/searchDisplay.js index 10d1739cc..3f094546f 100644 --- a/js/ui/searchDisplay.js +++ b/js/ui/searchDisplay.js @@ -4,14 +4,16 @@ const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Gettext = imports.gettext.domain('gnome-shell'); const _ = Gettext.gettext; +const Gtk = imports.gi.Gtk; +const St = imports.gi.St; const DND = imports.ui.dnd; +const IconGrid = imports.ui.iconGrid; const Main = imports.ui.main; const Search = imports.ui.search; +const MAX_SEARCH_RESULTS_ROWS = 2; -// 25 search results (per result type) should be enough for everyone -const MAX_RENDERED_SEARCH_RESULTS = 25; function SearchResult(provider, metaInfo, terms) { this._init(provider, metaInfo, terms); @@ -24,17 +26,19 @@ SearchResult.prototype = { this.actor = new St.Clickable({ style_class: 'search-result', reactive: true, x_align: St.Align.START, - x_fill: true, y_fill: true }); this.actor._delegate = this; let content = provider.createResultActor(metaInfo, terms); if (content == null) { - content = new St.BoxLayout({ style_class: 'search-result-content' }); - let title = new St.Label({ text: this.metaInfo['name'] }); - let icon = this.metaInfo['icon']; - content.add(icon, { y_fill: false }); - content.add(title, { expand: true, y_fill: false }); + content = new St.Bin({ style_class: 'search-result-content', + reactive: true, + track_hover: true }); + let icon = new IconGrid.BaseIcon(this.metaInfo['name'], + { createIcon: Lang.bind(this, function(size) { + return this.metaInfo['icon']; + })}); + content.set_child(icon.actor); } this._content = content; this.actor.set_child(content); @@ -84,36 +88,45 @@ SearchResult.prototype = { } }; -function OverflowSearchResults(provider) { + +function GridSearchResults(provider) { this._init(provider); } -OverflowSearchResults.prototype = { +GridSearchResults.prototype = { __proto__: Search.SearchResultDisplay.prototype, _init: function(provider) { Search.SearchResultDisplay.prototype._init.call(this, provider); - this.actor = new St.OverflowBox({ style_class: 'search-section-list-results' }); + this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS, + xAlign: St.Align.START }); + this.actor = new St.Bin({ x_align: St.Align.START }); + this.actor.set_child(this._grid.actor); + this.selectionIndex = -1; }, getVisibleResultCount: function() { - return this.actor.get_n_visible(); + return this._grid.visibleItemsCount(); }, renderResults: function(results, terms) { - for (let i = 0; i < results.length && i < MAX_RENDERED_SEARCH_RESULTS; i++) { + for (let i = 0; i < results.length; i++) { let result = results[i]; let meta = this.provider.getResultMeta(result); let display = new SearchResult(this.provider, meta, terms); - this.actor.add_actor(display.actor); + this._grid.addItem(display.actor); } }, - selectIndex: function(index) { - let nVisible = this.actor.get_n_visible(); - let children = this.actor.get_children(); + clear: function () { + this._grid.removeAll(); + this.selectionIndex = -1; + }, + + selectIndex: function (index) { + let nVisible = this.getVisibleResultCount(); if (this.selectionIndex >= 0) { - let prevActor = children[this.selectionIndex]; + let prevActor = this._grid.getItemAtIndex(this.selectionIndex); prevActor._delegate.setSelected(false); } this.selectionIndex = -1; @@ -121,19 +134,21 @@ OverflowSearchResults.prototype = { return false; else if (index < 0) return false; - let targetActor = children[index]; + let targetActor = this._grid.getItemAtIndex(index); targetActor._delegate.setSelected(true); this.selectionIndex = index; return true; }, activateSelected: function() { - let children = this.actor.get_children(); - let targetActor = children[this.selectionIndex]; + if (this.selectionIndex < 0) + return; + let targetActor = this._grid.getItemAtIndex(this.selectionIndex); targetActor._delegate.activate(); } }; + function SearchResults(searchSystem) { this._init(searchSystem); } @@ -142,10 +157,23 @@ SearchResults.prototype = { _init: function(searchSystem) { this._searchSystem = searchSystem; - this.actor = new St.BoxLayout({ name: 'searchResults', - vertical: true }); + this.actor = new St.Bin({ name: 'searchResults', + y_align: St.Align.START, + x_align: St.Align.START, + x_fill: true }); + this._content = new St.BoxLayout({ name: 'searchResultsContent', + vertical: true }); + + let scrollView = new St.ScrollView({ x_fill: true, + y_fill: false, + vshadows: true }); + scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); + scrollView.add_actor(this._content); + + this.actor.set_child(scrollView); + this._statusText = new St.Label({ style_class: 'search-statustext' }); - this.actor.add(this._statusText); + this._content.add(this._statusText); this._selectedProvider = -1; this._providers = this._searchSystem.getProviders(); this._providerMeta = []; @@ -175,14 +203,14 @@ SearchResults.prototype = { providerBox.add(resultDisplayBin, { expand: true }); let resultDisplay = provider.createResultContainerActor(); if (resultDisplay == null) { - resultDisplay = new OverflowSearchResults(provider); + resultDisplay = new GridSearchResults(provider); } resultDisplayBin.set_child(resultDisplay.actor); this._providerMeta.push({ actor: providerBox, resultDisplay: resultDisplay, count: count }); - this.actor.add(providerBox); + this._content.add(providerBox); }, _clearDisplay: function() { From 5fef9188c9c9638c684a26017e8c26e954d04537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Mon, 15 Nov 2010 21:58:27 +0100 Subject: [PATCH 43/61] workspaces: Change handling of window-drag signals Delegate the emission of the window-drag-begin/window-drag-end signals to overview functions, as done already for other items. This will enable objects to react to those signals without having access to the workspace objects / the workspaces view. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- js/ui/overview.js | 8 +++++ js/ui/workspace.js | 4 +-- js/ui/workspacesView.js | 65 ++++++++++++++++++----------------------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/js/ui/overview.js b/js/ui/overview.js index 19a24ed51..61c29c940 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -247,6 +247,14 @@ Overview.prototype = { this.emit('item-drag-end'); }, + beginWindowDrag: function(source) { + this.emit('window-drag-begin'); + }, + + endWindowDrag: function(source) { + this.emit('window-drag-end'); + }, + // Returns the scale the Overview has when we just start zooming out // to overview mode. That is, when just the active workspace is showing. getZoomedInScale : function() { diff --git a/js/ui/workspace.js b/js/ui/workspace.js index 154f7e806..4194897ea 100644 --- a/js/ui/workspace.js +++ b/js/ui/workspace.js @@ -1309,12 +1309,12 @@ Workspace.prototype = { Lang.bind(this, this._onCloneSelected)); clone.connect('drag-begin', Lang.bind(this, function(clone) { - this.emit('window-drag-begin', clone.actor); + Main.overview.beginWindowDrag(); overlay.hide(); })); clone.connect('drag-end', Lang.bind(this, function(clone) { - this.emit('window-drag-end', clone.actor); + Main.overview.endWindowDrag(); overlay.show(); })); clone.connect('zoom-start', diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index ec0dfd26d..2a4edc2c8 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -62,15 +62,8 @@ WorkspacesView.prototype = { this._workspaces = workspaces; // Add workspace actors - for (let w = 0; w < global.screen.n_workspaces; w++) { + for (let w = 0; w < global.screen.n_workspaces; w++) this._workspaces[w].actor.reparent(this.actor); - this._workspaces[w]._windowDragBeginId = - this._workspaces[w].connect('window-drag-begin', - Lang.bind(this, this._dragBegin)); - this._workspaces[w]._windowDragEndId = - this._workspaces[w].connect('window-drag-end', - Lang.bind(this, this._dragEnd)); - } this._workspaces[activeWorkspaceIndex].actor.raise_top(); // Position/scale the desktop windows and their children after the @@ -114,6 +107,10 @@ WorkspacesView.prototype = { Lang.bind(this, this._dragBegin)); this._itemDragEndId = Main.overview.connect('item-drag-end', Lang.bind(this, this._dragEnd)); + this._windowDragBeginId = Main.overview.connect('window-drag-begin', + Lang.bind(this, this._dragBegin)); + this._windowDragEndId = Main.overview.connect('window-drag-end', + Lang.bind(this, this._dragEnd)); }, _lookupWorkspaceForMetaWindow: function (metaWindow) { @@ -583,13 +580,8 @@ WorkspacesView.prototype = { }); if (newNumWorkspaces > oldNumWorkspaces) { - for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) { + for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) this.actor.add_actor(this._workspaces[w].actor); - this._workspaces[w]._windowDragBeginId = this._workspaces[w].connect('window-drag-begin', - Lang.bind(this, this._dragBegin)); - this._workspaces[w]._windowDragEndId = this._workspaces[w].connect('window-drag-end', - Lang.bind(this, this._dragEnd)); - } this._computeWorkspacePositions(); this._updateWorkspaceActors(false); @@ -625,15 +617,13 @@ WorkspacesView.prototype = { Main.overview.disconnect(this._itemDragEndId); this._itemDragEndId = 0; } - for (let w = 0; w < this._workspaces.length; w++) { - if (this._workspaces[w]._windowDragBeginId) { - this._workspaces[w].disconnect(this._workspaces[w]._windowDragBeginId); - this._workspaces[w]._windowDragBeginId = 0; - } - if (this._workspaces[w]._windowDragEndId) { - this._workspaces[w].disconnect(this._workspaces[w]._windowDragEndId); - this._workspaces[w]._windowDragEndId = 0; - } + if (this._windowDragBeginId > 0) { + Main.overview.disconnect(this._windowDragBeginId); + this._windowDragBeginId = 0; + } + if (this._windowDragEndId > 0) { + Main.overview.disconnect(this._windowDragEndId); + this._windowDragEndId = 0; } }, @@ -649,8 +639,6 @@ WorkspacesView.prototype = { dragMotion: Lang.bind(this, this._onDragMotion) }; DND.addDragMonitor(this._dragMonitor); - - this.emit('window-drag-begin'); }, _onDragMotion: function(dragEvent) { @@ -741,8 +729,6 @@ WorkspacesView.prototype = { for (let i = 0; i < this._workspaces.length; i++) this._workspaces[i].setReservedSlot(null); - - this.emit('window-drag-end'); }, // sync the workspaces' positions to the value of the scroll adjustment @@ -948,6 +934,8 @@ WorkspaceControlsContainer.prototype = { this._itemDragBeginId = 0; this._itemDragEndId = 0; + this._windowDragBeginId = 0; + this._windowDragEndId = 0; }, show: function() { @@ -957,6 +945,12 @@ WorkspaceControlsContainer.prototype = { if (this._itemDragEndId == 0) this._itemDragEndId = Main.overview.connect('item-drag-end', Lang.bind(this, this.popIn)); + if (this._windowDragBeginId == 0) + this._windowDragBeginId = Main.overview.connect('window-drag-begin', + Lang.bind(this, this.popOut)); + if (this._windowDragEndId == 0) + this._windowDragEndId = Main.overview.connect('window-drag-end', + Lang.bind(this, this.popIn)); this._controls.x = this._poppedInX(); }, @@ -969,6 +963,14 @@ WorkspaceControlsContainer.prototype = { Main.overview.disconnect(this._itemDragEndId); this._itemDragEndId = 0; } + if (this._windowDragBeginId > 0) { + Main.overview.disconnect(this._windowDragBeginId); + this._windowDragBeginId = 0; + } + if (this._windowDragEndId > 0) { + Main.overview.disconnect(this._windowDragEndId); + this._windowDragEndId = 0; + } }, _getPreferredWidth: function(actor, forHeight, alloc) { @@ -1120,15 +1122,6 @@ WorkspacesDisplay.prototype = { this.workspacesView.destroy(); this.workspacesView = newView; - this.workspacesView.connect('window-drag-begin', Lang.bind(this, - function() { - this._controlsContainer.popOut(); - })); - this.workspacesView.connect('window-drag-end', Lang.bind(this, - function() { - this._controlsContainer.popIn(); - })); - this._workspaceIndicatorPanel.updateWorkspaces(this._workspaces); this._nWorkspacesNotifyId = From b59daac6f307360cecf2a1f52428cd74b363e80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Mon, 8 Nov 2010 02:51:02 +0100 Subject: [PATCH 44/61] dash: Improve DND to dash and allow reordering Show a positional indicator where a new favorite will be added and make the favorites re-orderable. Also allow the removal of favorites using drag-and-drop according to the mockups. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- data/Makefile.am | 1 + data/theme/dash-placeholder.svg | 84 +++++++++++++ data/theme/gnome-shell.css | 16 +++ js/ui/appFavorites.js | 25 +++- js/ui/dash.js | 207 ++++++++++++++++++++++++++++++-- js/ui/overview.js | 2 + 6 files changed, 318 insertions(+), 17 deletions(-) create mode 100644 data/theme/dash-placeholder.svg diff --git a/data/Makefile.am b/data/Makefile.am index e17b0e7f7..8c0419811 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -25,6 +25,7 @@ dist_theme_DATA = \ theme/close-window.svg \ theme/close.svg \ theme/corner-ripple.png \ + theme/dash-placeholder.svg \ theme/dialog-error.svg \ theme/gnome-shell.css \ theme/mosaic-view-active.svg \ diff --git a/data/theme/dash-placeholder.svg b/data/theme/dash-placeholder.svg new file mode 100644 index 000000000..cbae148a2 --- /dev/null +++ b/data/theme/dash-placeholder.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 785b290dc..e5ab750b4 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -341,6 +341,11 @@ StTooltip StLabel { width: 60px; } +.dash-placeholder { + background-image: url("dash-placeholder.svg"); + height: 27px; +} + #viewSelector { spacing: 16px; } @@ -502,7 +507,17 @@ StTooltip StLabel { padding: 6px 12px; } +.remove-favorite-icon { + color: #a0a0a0; +} + +.remove-favorite-icon:hover { + color: white; + -st-shadow: black 0px 2px 2px; +} + .app-well-app > .overview-icon, +.remove-favorite > .overview-icon, .search-result-content > .overview-icon { border-radius: 4px; padding: 4px; @@ -523,6 +538,7 @@ StTooltip StLabel { } .app-well-app:hover > .overview-icon, +.remove-favorite:hover > .overview-icon, .search-result-content:hover > .overview-icon { background: rgba(255,255,255,0.33); text-shadow: black 0px 2px 2px; diff --git a/js/ui/appFavorites.js b/js/ui/appFavorites.js index 9c050ee4a..a3e83c4fd 100644 --- a/js/ui/appFavorites.js +++ b/js/ui/appFavorites.js @@ -63,7 +63,7 @@ AppFavorites.prototype = { return appId in this._favorites; }, - _addFavorite: function(appId) { + _addFavorite: function(appId, pos) { if (appId in this._favorites) return false; @@ -73,14 +73,17 @@ AppFavorites.prototype = { return false; let ids = this._getIds(); - ids.push(appId); + if (pos == -1) + ids.push(appId); + else + ids.splice(pos, 0, appId); global.settings.set_strv(this.FAVORITE_APPS_KEY, ids); this._favorites[appId] = app; return true; }, - addFavorite: function(appId) { - if (!this._addFavorite(appId)) + addFavoriteAtPos: function(appId, pos) { + if (!this._addFavorite(appId, pos)) return; let app = Shell.AppSystem.get_default().get_app(appId); @@ -90,6 +93,15 @@ AppFavorites.prototype = { })); }, + addFavorite: function(appId) { + this.addFavoriteAtPos(appId, -1); + }, + + moveFavoriteToPos: function(appId, pos) { + this._removeFavorite(appId); + this._addFavorite(appId, pos); + }, + _removeFavorite: function(appId) { if (!appId in this._favorites) return false; @@ -100,13 +112,16 @@ AppFavorites.prototype = { }, removeFavorite: function(appId) { + let ids = this._getIds(); + let pos = ids.indexOf(appId); + let app = this._favorites[appId]; if (!this._removeFavorite(appId)) return; Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()), Lang.bind(this, function () { - this._addFavorite(appId); + this._addFavorite(appId, pos); })); } }; diff --git a/js/ui/dash.js b/js/ui/dash.js index 1f0a534d4..39440cf73 100644 --- a/js/ui/dash.js +++ b/js/ui/dash.js @@ -1,8 +1,8 @@ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ -const Mainloop = imports.mainloop; const Signals = imports.signals; const Lang = imports.lang; +const Meta = imports.gi.Meta; const Shell = imports.gi.Shell; const St = imports.gi.St; const Gettext = imports.gettext.domain('gnome-shell'); @@ -11,10 +11,67 @@ const _ = Gettext.gettext; const AppDisplay = imports.ui.appDisplay; const AppFavorites = imports.ui.appFavorites; const DND = imports.ui.dnd; +const IconGrid = imports.ui.iconGrid; const Main = imports.ui.main; const Workspace = imports.ui.workspace; +function RemoveFavoriteIcon() { + this._init(); +} + +RemoveFavoriteIcon.prototype = { + _init: function() { + this.actor = new St.Bin({ style_class: 'remove-favorite' }); + this._iconActor = null; + this.icon = new IconGrid.BaseIcon(_("Remove"), + { setSizeManually: true, + createIcon: Lang.bind(this, this._createIcon) }); + this.actor.set_child(this.icon.actor); + this.actor._delegate = this; + }, + + _createIcon: function(size) { + this._iconActor = new St.Icon({ icon_name: 'user-trash', + style_class: 'remove-favorite-icon', + icon_size: size }); + return this._iconActor; + }, + + setHover: function(hovered) { + this.actor.set_hover(hovered); + if (this._iconActor) + this._iconActor.set_hover(hovered); + }, + + // Rely on the dragged item being a favorite + handleDragOver: function(source, actor, x, y, time) { + return DND.DragMotionResult.MOVE_DROP; + }, + + acceptDrop: function(source, actor, x, y, time) { + let app = null; + if (source instanceof AppDisplay.AppWellIcon) { + let appSystem = Shell.AppSystem.get_default(); + app = appSystem.get_app(source.getId()); + } else if (source instanceof Workspace.WindowClone) { + let tracker = Shell.WindowTracker.get_default(); + app = tracker.get_window_app(source.metaWindow); + } + + let id = app.get_id(); + + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, + function () { + AppFavorites.getAppFavorites().removeFavorite(id); + return false; + })); + + return true; + } +}; + + function Dash() { this._init(); } @@ -24,6 +81,11 @@ Dash.prototype = { this._menus = []; this._menuDisplays = []; this._maxHeight = -1; + this._iconSize = 48; + + this._dragPlaceholder = null; + this._dragPlaceholderPos = -1; + this._favRemoveTarget = null; this._favorites = []; @@ -50,6 +112,75 @@ Dash.prototype = { this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay)); }, + show: function() { + this._itemDragBeginId = Main.overview.connect('item-drag-begin', + Lang.bind(this, this._onDragBegin)); + this._itemDragEndId = Main.overview.connect('item-drag-end', + Lang.bind(this, this._onDragEnd)); + this._windowDragBeginId = Main.overview.connect('window-drag-begin', + Lang.bind(this, this._onDragBegin)); + this._windowDragEndId = Main.overview.connect('window-drag-end', + Lang.bind(this, this._onDragEnd)); + }, + + hide: function() { + Main.overview.disconnect(this._itemDragBeginId); + Main.overview.disconnect(this._itemDragEndId); + Main.overview.disconnect(this._windowDragBeginId); + Main.overview.disconnect(this._windowDragEndId); + }, + + _onDragBegin: function() { + this._dragMonitor = { + dragMotion: Lang.bind(this, this._onDragMotion) + }; + DND.addDragMonitor(this._dragMonitor); + }, + + _onDragEnd: function() { + this._clearDragPlaceholder(); + if (this._favRemoveTarget) { + this._favRemoveTarget.actor.destroy(); + this._favRemoveTarget = null; + } + DND.removeMonitor(this._dragMonitor); + }, + + _onDragMotion: function(dragEvent) { + let app = null; + if (dragEvent.source instanceof AppDisplay.AppWellIcon) + app = this._appSystem.get_app(dragEvent.source.getId()); + else if (dragEvent.source instanceof Workspace.WindowClone) + app = this._tracker.get_window_app(dragEvent.source.metaWindow); + else + return DND.DragMotionResult.CONTINUE; + + let id = app.get_id(); + + let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); + + let srcIsFavorite = (id in favorites); + + if (srcIsFavorite && this._favRemoveTarget == null) { + this._favRemoveTarget = new RemoveFavoriteIcon(); + this._favRemoveTarget.icon.setIconSize(this._iconSize); + this._box.add(this._favRemoveTarget.actor); + } + + let favRemoveHovered = false; + if (this._favRemoveTarget) + favRemoveHovered = + this._favRemoveTarget.actor.contains(dragEvent.targetActor); + + if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered) + this._clearDragPlaceholder(); + + if (this._favRemoveTarget) + this._favRemoveTarget.setHover(favRemoveHovered); + + return DND.DragMotionResult.CONTINUE; + }, + _appIdListToHash: function(apps) { let ids = {}; for (let i = 0; i < apps.length; i++) @@ -61,6 +192,19 @@ Dash.prototype = { Main.queueDeferredWork(this._workId); }, + _addApp: function(app) { + let display = new AppDisplay.AppWellIcon(app); + display._draggable.connect('drag-begin', + Lang.bind(this, function() { + display.actor.opacity = 50; + })); + display._draggable.connect('drag-end', + Lang.bind(this, function() { + display.actor.opacity = 255; + })); + this._box.add(display.actor); + }, + _redisplay: function () { this._box.hide(); this._box.remove_all(); @@ -74,16 +218,14 @@ Dash.prototype = { for (let id in favorites) { let app = favorites[id]; - let display = new AppDisplay.AppWellIcon(app); - this._box.add(display.actor); + this._addApp(app); } for (let i = 0; i < running.length; i++) { let app = running[i]; if (app.get_id() in favorites) continue; - let display = new AppDisplay.AppWellIcon(app); - this._box.add(display.actor); + this._addApp(app); } let children = this._box.get_children(); @@ -112,6 +254,14 @@ Dash.prototype = { this._box.show(); }, + _clearDragPlaceholder: function() { + if (this._dragPlaceholder) { + this._dragPlaceholder.destroy(); + this._dragPlaceholder = null; + this._dragPlaceholderPos = -1; + } + }, + handleDragOver : function(source, actor, x, y, time) { let app = null; if (source instanceof AppDisplay.AppWellIcon) @@ -123,6 +273,28 @@ Dash.prototype = { if (app == null || app.is_transient()) return DND.DragMotionResult.NO_DROP; + let numFavorites = AppFavorites.getAppFavorites().getFavorites().length; + let numChildren = this._box.get_children().length; + let boxHeight = this._box.height; + + // Keep the placeholder out of the index calculation; assuming that + // the remove target has the same size as "normal" items, we don't + // need to do the same adjustment there. + if (this._dragPlaceholder) { + boxHeight -= this._dragPlaceholder.height; + numChildren--; + } + + let pos = Math.round(y * numChildren / boxHeight); + + if (pos != this._dragPlaceholderPos && pos <= numFavorites) { + this._dragPlaceholderPos = pos; + if (this._dragPlaceholder) + this._dragPlaceholder.destroy(); + this._dragPlaceholder = new St.Bin({ style_class: 'dash-placeholder' }); + this._box.insert_actor(this._dragPlaceholder, pos); + } + let id = app.get_id(); let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); @@ -130,7 +302,7 @@ Dash.prototype = { let srcIsFavorite = (id in favorites); if (srcIsFavorite) - return DND.DragMotionResult.NO_DROP; + return DND.DragMotionResult.MOVE_DROP; return DND.DragMotionResult.COPY_DROP; }, @@ -155,14 +327,25 @@ Dash.prototype = { let srcIsFavorite = (id in favorites); - if (srcIsFavorite) { - return false; - } else { - Mainloop.idle_add(Lang.bind(this, function () { - AppFavorites.getAppFavorites().addFavorite(id); + let favPos = 0; + let children = this._box.get_children(); + for (let i = 0; i < this._dragPlaceholderPos; i++) { + let childId = children[i]._delegate.app.get_id(); + if (childId == id) + continue; + if (childId in favorites) + favPos++; + } + + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, + function () { + let appFavorites = AppFavorites.getAppFavorites(); + if (srcIsFavorite) + appFavorites.moveFavoriteToPos(id, favPos); + else + appFavorites.addFavoriteAtPos(id, favPos); return false; })); - } return true; } diff --git a/js/ui/overview.js b/js/ui/overview.js index 61c29c940..64c3035b4 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -302,6 +302,7 @@ Overview.prototype = { this.viewSelector.show(); this._workspacesDisplay.show(); + this._dash.show(); this.workspaces = this._workspacesDisplay.workspacesView; this._group.add_actor(this.workspaces.actor); @@ -434,6 +435,7 @@ Overview.prototype = { this._workspacesDisplay.hide(); this.viewSelector.hide(); + this._dash.hide(); this._desktopFade.hide(); this._background.hide(); From e2e11b1a29ed120360b66cc4cde20d788b5ba7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 11 Nov 2010 18:50:19 +0100 Subject: [PATCH 45/61] overview: Update animation Update the animation on entering/leaving the overview to only zoom the window previews and fade other elements. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- js/ui/overview.js | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/js/ui/overview.js b/js/ui/overview.js index 64c3035b4..7bdb8e664 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -214,6 +214,9 @@ Overview.prototype = { let contentY = Panel.PANEL_HEIGHT; let contentHeight = primary.height - contentY - Main.messageTray.actor.height; + this._group.set_position(primary.x, primary.y); + this._group.set_size(primary.width, primary.height); + this._coverPane.set_position(0, contentY); this._coverPane.set_size(primary.width, contentHeight); @@ -272,12 +275,12 @@ Overview.prototype = { // Returns the current scale of the Overview. getScale : function() { - return this._group.scaleX; + return this.workspaces.actor.scaleX; }, // Returns the current position of the Overview. getPosition : function() { - return [this._group.x, this._group.y]; + return [this.workspaces.actor.x, this.workspaces.actor.y]; }, show : function() { @@ -305,7 +308,7 @@ Overview.prototype = { this._dash.show(); this.workspaces = this._workspacesDisplay.workspacesView; - this._group.add_actor(this.workspaces.actor); + global.overlay_group.add_actor(this.workspaces.actor); if (!this._desktopFade.child) this._desktopFade.child = this._getDesktopClone(); @@ -320,16 +323,16 @@ Overview.prototype = { }); } - // Create a zoom out effect. First scale the Overview group up and + // Create a zoom out effect. First scale the workspaces view up and // position it so that the active workspace fills up the whole screen, - // then transform the group to its normal dimensions and position. + // then transform it to its normal dimensions and position. // The opposite transition is used in hide(). - this._group.scaleX = this._group.scaleY = this.getZoomedInScale(); - [this._group.x, this._group.y] = this.getZoomedInPosition(); + this.workspaces.actor.scaleX = this.workspaces.actor.scaleY = this.getZoomedInScale(); + [this.workspaces.actor.x, this.workspaces.actor.y] = this.getZoomedInPosition(); let primary = global.get_primary_monitor(); - Tweener.addTween(this._group, - { x: primary.x, - y: primary.y, + Tweener.addTween(this.workspaces.actor, + { x: primary.x - this._group.x, + y: primary.y - this._group.y, scaleX: 1, scaleY: 1, transition: 'easeOutQuad', @@ -338,9 +341,9 @@ Overview.prototype = { onCompleteScope: this }); - // Make Dash fade in so that it doesn't appear too big. - this._dash.actor.opacity = 0; - Tweener.addTween(this._dash.actor, + // Make the other elements fade in. + this._group.opacity = 0; + Tweener.addTween(this._group, { opacity: 255, transition: 'easeOutQuad', time: ANIMATION_TIME @@ -368,12 +371,12 @@ Overview.prototype = { this.workspaces.hide(); - // Create a zoom in effect by transforming the Overview group so that + // Create a zoom in effect by transforming the workspaces view so that // the active workspace fills up the whole screen. The opposite // transition is used in show(). let scale = this.getZoomedInScale(); let [posX, posY] = this.getZoomedInPosition(); - Tweener.addTween(this._group, + Tweener.addTween(this.workspaces.actor, { x: posX, y: posY, scaleX: scale, @@ -384,8 +387,8 @@ Overview.prototype = { onCompleteScope: this }); - // Make Dash fade out so that it doesn't appear to big. - Tweener.addTween(this._dash.actor, + // Make other elements fade out. + Tweener.addTween(this._group, { opacity: 0, transition: 'easeOutQuad', time: ANIMATION_TIME @@ -441,14 +444,6 @@ Overview.prototype = { this._background.hide(); this._group.hide(); - // Reset the overview actor's scale/position, so that other elements - // can calculate their position correctly the next time the overview - // is shown - let primary = global.get_primary_monitor(); - this._group.set_scale(1, 1); - this._group.set_position(primary.x, primary.y); - this._group.set_size(primary.width, primary.height); - this.visible = false; this.animationInProgress = false; this._hideInProgress = false; From 6f9ede569e1db4e5d7a4706ace3330f1e5407ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Fri, 12 Nov 2010 22:52:44 +0100 Subject: [PATCH 46/61] workspace-indicators: Add hover indication Scale up indicators on hover to hint at their clickability. https://bugzilla.gnome.org/show_bug.cgi?id=634948 --- js/ui/workspacesView.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index 2a4edc2c8..678a9e087 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -25,6 +25,8 @@ const WORKSPACE_DRAGGING_SCALE = 0.85; const CONTROLS_POP_IN_FRACTION = 0.8; const CONTROLS_POP_IN_TIME = 0.1; +const INDICATOR_HOVER_SCALE = 1.1; + function WorkspacesView(width, height, x, y, workspaces) { this._init(width, height, x, y, workspaces); @@ -812,12 +814,13 @@ WorkspaceIndicatorPanel.prototype = { let availWidth = box.x2 - box.x1; let availHeight = box.y2 - box.y1; let [minWidth, natWidth] = this._box.get_preferred_width(-1); + let [minHeight, natHeight] = this._box.get_preferred_height(-1); let childBox = new Clutter.ActorBox(); childBox.x1 = Math.floor((availWidth - natWidth) / 2); childBox.x2 = childBox.x1 + natWidth; - childBox.y1 = 0; - childBox.y2 = availHeight; + childBox.y1 = Math.floor((availHeight - natHeight) / 2); + childBox.y2 = childBox.y2 + natHeight; this._box.allocate(childBox, flags); }, @@ -830,8 +833,8 @@ WorkspaceIndicatorPanel.prototype = { _getPreferredHeight: function(actor, forWidth, alloc) { let [minHeight, natHeight] = this._box.get_preferred_height(-1); - alloc.min_size = minHeight; - alloc.natural_size = natHeight; + alloc.min_size = minHeight * INDICATOR_HOVER_SCALE; + alloc.natural_size = natHeight * INDICATOR_HOVER_SCALE; }, updateWorkspaces: function(workspaces) { @@ -845,13 +848,22 @@ WorkspaceIndicatorPanel.prototype = { this._box.remove_all(); for (let i = 0; i < this._workspaces.length; i++) { - let actor = new St.Button({ style_class: 'workspace-indicator' }); + let actor = new St.Button({ style_class: 'workspace-indicator', + track_hover: true }); let workspace = this._workspaces[i]; let metaWorkspace = this._workspaces[i].metaWorkspace; actor.connect('clicked', Lang.bind(this, function() { metaWorkspace.activate(global.get_current_time()); })); + actor.connect('notify::hover', Lang.bind(this, function() { + if (actor.hover) + actor.set_scale_with_gravity(INDICATOR_HOVER_SCALE, + INDICATOR_HOVER_SCALE, + Clutter.Gravity.CENTER); + else + actor.set_scale(1.0, 1.0); + })); actor._delegate = { acceptDrop: Lang.bind(this, From 41a5282b7e182c53c68203422841ecc4e7046c0e Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 22 Nov 2010 09:58:28 -0500 Subject: [PATCH 47/61] gnome-shell.css: don't override the chat-response entry's height We were forcing the chat-response entry to have too small a height, making underscores not show up. We're already setting the font size, so we should just let the entry request the height it needs based on that. https://bugzilla.gnome.org/show_bug.cgi?id=635471 --- data/theme/gnome-shell.css | 1 - 1 file changed, 1 deletion(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index e5ab750b4..49aa4978f 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -898,7 +898,6 @@ StTooltip StLabel { background-color: #404040; caret-color: #ffffff; caret-size: 1px; - height: 18px; } .chat-response:focus { From 56fb7e2c58ff53f7e2b3db3270017fc3fd7a4fe4 Mon Sep 17 00:00:00 2001 From: Adel Gadllah Date: Sat, 27 Nov 2010 20:46:05 +0100 Subject: [PATCH 48/61] St: Take advantage of clipped redraws In order to take advantage of clipped redraws (only redraw the parts that actually changed), we have to inform clutter about our paint_volume by implementing the get_paint_volume virtual method. As this feature had been added in in clutter 1.5.x we now require that. https://bugzilla.gnome.org/show_bug.cgi?id=630932 --- configure.ac | 2 +- src/shell-generic-container.c | 29 +++++++++++++++++++++++++++++ src/st/st-group.c | 28 ++++++++++++++++++++++++++++ src/st/st-widget.c | 27 +++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 519d02825..c5d5a8920 100644 --- a/configure.ac +++ b/configure.ac @@ -57,7 +57,7 @@ fi AM_CONDITIONAL(BUILD_RECORDER, $build_recorder) -CLUTTER_MIN_VERSION=1.3.14 +CLUTTER_MIN_VERSION=1.5.8 GOBJECT_INTROSPECTION_MIN_VERSION=0.6.11 GJS_MIN_VERSION=0.7 MUTTER_MIN_VERSION=2.91.0 diff --git a/src/shell-generic-container.c b/src/shell-generic-container.c index e378d7211..8be3a96c5 100644 --- a/src/shell-generic-container.c +++ b/src/shell-generic-container.c @@ -245,6 +245,34 @@ shell_generic_container_finalize (GObject *object) G_OBJECT_CLASS (shell_generic_container_parent_class)->finalize (object); } +/* Based on implementation from clutter-group.c */ +static gboolean +shell_generic_container_get_paint_volume (ClutterActor *actor, + ClutterPaintVolume *volume) +{ + GList *l, *children; + + children = st_container_get_children_list (ST_CONTAINER (actor)); + + CLUTTER_ACTOR_CLASS (shell_generic_container_parent_class)->get_paint_volume (actor, volume); + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + const ClutterPaintVolume *child_volume; + + /* This gets the paint volume of the child transformed into the + * group's coordinate space... */ + child_volume = clutter_actor_get_transformed_paint_volume (child, actor); + if (!child_volume) + return FALSE; + + clutter_paint_volume_union (volume, child_volume); + } + + return TRUE; +} + static void shell_generic_container_class_init (ShellGenericContainerClass *klass) { @@ -257,6 +285,7 @@ shell_generic_container_class_init (ShellGenericContainerClass *klass) actor_class->get_preferred_width = shell_generic_container_get_preferred_width; actor_class->get_preferred_height = shell_generic_container_get_preferred_height; actor_class->allocate = shell_generic_container_allocate; + actor_class->get_paint_volume = shell_generic_container_get_paint_volume; actor_class->paint = shell_generic_container_paint; actor_class->pick = shell_generic_container_pick; diff --git a/src/st/st-group.c b/src/st/st-group.c index b981de056..502957b03 100644 --- a/src/st/st-group.c +++ b/src/st/st-group.c @@ -3,6 +3,7 @@ * st-group.c: A fixed layout container based on ClutterGroup * * Copyright 2010 Florian Müllner + * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, @@ -230,7 +231,33 @@ st_group_hide_all (ClutterActor *actor) NULL); } +/* Based on implementation from clutter-group.c */ +static gboolean +st_group_get_paint_volume (ClutterActor *actor, + ClutterPaintVolume *volume) +{ + GList *l, *children; + children = st_container_get_children_list (ST_CONTAINER (actor)); + + CLUTTER_ACTOR_CLASS (st_group_parent_class)->get_paint_volume (actor, volume); + + for (l = children; l != NULL; l = l->next) + { + ClutterActor *child = l->data; + const ClutterPaintVolume *child_volume; + + /* This gets the paint volume of the child transformed into the + * group's coordinate space... */ + child_volume = clutter_actor_get_transformed_paint_volume (child, actor); + if (!child_volume) + return FALSE; + + clutter_paint_volume_union (volume, child_volume); + } + + return TRUE; +} static void @@ -242,6 +269,7 @@ st_group_class_init (StGroupClass *klass) actor_class->get_preferred_height = st_group_get_preferred_height; actor_class->allocate = st_group_allocate; actor_class->paint = st_group_paint; + actor_class->get_paint_volume = st_group_get_paint_volume; actor_class->pick = st_group_pick; actor_class->show_all = st_group_show_all; actor_class->hide_all = st_group_hide_all; diff --git a/src/st/st-widget.c b/src/st/st-widget.c index fae5f5658..23b591d2f 100644 --- a/src/st/st-widget.c +++ b/src/st/st-widget.c @@ -7,6 +7,7 @@ * Copyright 2009, 2010 Red Hat, Inc. * Copyright 2009 Abderrahim Kitouni * Copyright 2009, 2010 Florian Müllner + * Copyright 2010 Adel Gadllah * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, @@ -668,6 +669,31 @@ st_widget_hide (ClutterActor *actor) CLUTTER_ACTOR_CLASS (st_widget_parent_class)->hide (actor); } +static gboolean +st_widget_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) +{ + ClutterActorBox paint_box, alloc_box; + StThemeNode *theme_node; + ClutterVertex origin; + + /* Setting the paint volume does not make sense when we don't have any allocation */ + if (!clutter_actor_has_allocation (self)) + return FALSE; + + theme_node = st_widget_get_theme_node (ST_WIDGET(self)); + clutter_actor_get_allocation_box (self, &alloc_box); + st_theme_node_get_paint_box (theme_node, &alloc_box, &paint_box); + + origin.x = paint_box.x1 - alloc_box.x1; + origin.y = paint_box.y1 - alloc_box.y1; + origin.z = 0.0f; + + clutter_paint_volume_set_origin (volume, &origin); + clutter_paint_volume_set_width (volume, paint_box.x2 - paint_box.x1); + clutter_paint_volume_set_height (volume, paint_box.y2 - paint_box.y1); + + return TRUE; +} static void @@ -688,6 +714,7 @@ st_widget_class_init (StWidgetClass *klass) actor_class->get_preferred_height = st_widget_get_preferred_height; actor_class->allocate = st_widget_allocate; actor_class->paint = st_widget_paint; + actor_class->get_paint_volume = st_widget_get_paint_volume; actor_class->parent_set = st_widget_parent_set; actor_class->map = st_widget_map; actor_class->unmap = st_widget_unmap; From 6b723ed72af31b80364b319fe34f545f25f911b1 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Sat, 13 Nov 2010 07:59:41 -0500 Subject: [PATCH 49/61] StImText: add get_paint_volume() Since StImText isn't a StWidget, it needs it's own implementation of get_paint_volume() to enable clipped redraws. https://bugzilla.gnome.org/show_bug.cgi?id=630932 --- src/st/st-im-text.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/st/st-im-text.c b/src/st/st-im-text.c index 5f00abfca..2b3f0fd21 100644 --- a/src/st/st-im-text.c +++ b/src/st/st-im-text.c @@ -181,6 +181,13 @@ st_im_text_paint (ClutterActor *actor) update_im_cursor_location (self); } +static gboolean +st_im_text_get_paint_volume (ClutterActor *self, + ClutterPaintVolume *volume) +{ + return clutter_paint_volume_set_from_allocation (volume, self); +} + /* Returns a new reference to window */ static GdkWindow * window_for_actor (ClutterActor *actor) @@ -440,6 +447,7 @@ st_im_text_class_init (StIMTextClass *klass) object_class->dispose = st_im_text_dispose; actor_class->paint = st_im_text_paint; + actor_class->get_paint_volume = st_im_text_get_paint_volume; actor_class->realize = st_im_text_realize; actor_class->unrealize = st_im_text_unrealize; From d870fef12284d3d2b988c18a4115d8f5ba3b1458 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Sat, 13 Nov 2010 09:08:03 -0500 Subject: [PATCH 50/61] StBoxLayout: report correct paint volume when scrolled When scrolled, st_box_layout_apply_transform() includes the scroll offset and affects paint volumes. This is right for our children, but our paint volume is determined by our allocation and borders and doesn't scroll, so we need to reverse-compensate, the same as we do when painting. https://bugzilla.gnome.org/show_bug.cgi?id=630932 --- src/st/st-box-layout.c | 79 +++++++++++++++++++++++++++++------------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/src/st/st-box-layout.c b/src/st/st-box-layout.c index 3a912fa97..ae26cd76d 100644 --- a/src/st/st-box-layout.c +++ b/src/st/st-box-layout.c @@ -876,29 +876,39 @@ st_box_layout_apply_transform (ClutterActor *a, cogl_matrix_translate (m, (int) -x, (int) -y, 0); } +/* If we are translated, then we need to translate back before chaining + * up or the background and borders will be drawn in the wrong place */ +static void +get_border_paint_offsets (StBoxLayout *self, + double *x, + double *y) +{ + StBoxLayoutPrivate *priv = self->priv; + + if (priv->hadjustment) + *x = st_adjustment_get_value (priv->hadjustment); + else + *x = 0; + + if (priv->vadjustment) + *y = st_adjustment_get_value (priv->vadjustment); + else + *y = 0; +} + static void st_box_layout_paint (ClutterActor *actor) { - StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; + StBoxLayout *self = ST_BOX_LAYOUT (actor); + StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); GList *l, *children; gdouble x, y; ClutterActorBox allocation_box; ClutterActorBox content_box; - if (priv->hadjustment) - x = st_adjustment_get_value (priv->hadjustment); - else - x = 0; - - if (priv->vadjustment) - y = st_adjustment_get_value (priv->vadjustment); - else - y = 0; - - /* If we are translated, then we need to translate back before chaining - * up or the background and borders will be drawn in the wrong place */ + get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { cogl_push_matrix (); @@ -950,23 +960,15 @@ static void st_box_layout_pick (ClutterActor *actor, const ClutterColor *color) { - StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv; + StBoxLayout *self = ST_BOX_LAYOUT (actor); + StBoxLayoutPrivate *priv = self->priv; StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor)); GList *l, *children; gdouble x, y; ClutterActorBox allocation_box; ClutterActorBox content_box; - if (priv->hadjustment) - x = st_adjustment_get_value (priv->hadjustment); - else - x = 0; - - if (priv->vadjustment) - y = st_adjustment_get_value (priv->vadjustment); - else - y = 0; - + get_border_paint_offsets (self, &x, &y); if (x != 0 || y != 0) { cogl_push_matrix (); @@ -1011,6 +1013,34 @@ st_box_layout_pick (ClutterActor *actor, cogl_clip_pop (); } +static gboolean +st_box_layout_get_paint_volume (ClutterActor *actor, + ClutterPaintVolume *volume) +{ + StBoxLayout *self = ST_BOX_LAYOUT (actor); + gdouble x, y; + + CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->get_paint_volume (actor, volume); + + /* When scrolled, st_box_layout_apply_transform() includes the scroll offset + * and affects paint volumes. This is right for our children, but our paint volume + * is determined by our allocation and borders and doesn't scroll, so we need + * to reverse-compensate here, the same as we do when painting. + */ + get_border_paint_offsets (self, &x, &y); + if (x != 0 || y != 0) + { + ClutterVertex origin; + + clutter_paint_volume_get_origin (volume, &origin); + origin.x += x; + origin.y += y; + clutter_paint_volume_set_origin (volume, &origin); + } + + return TRUE; +} + static void st_box_layout_style_changed (StWidget *self) { @@ -1047,6 +1077,7 @@ st_box_layout_class_init (StBoxLayoutClass *klass) actor_class->apply_transform = st_box_layout_apply_transform; actor_class->paint = st_box_layout_paint; + actor_class->get_paint_volume = st_box_layout_get_paint_volume; actor_class->pick = st_box_layout_pick; widget_class->style_changed = st_box_layout_style_changed; From 063fc8e29c5e4adbb2cb8c6c8006fcaf0c4ac24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Di=C3=A9guez?= Date: Mon, 29 Nov 2010 17:33:20 +0100 Subject: [PATCH 51/61] Updated Galician translations --- po/gl.po | 119 +++++++++++++++++++++++++++---------------------------- 1 file changed, 58 insertions(+), 61 deletions(-) diff --git a/po/gl.po b/po/gl.po index 1a9e3be5c..1e7e309b7 100644 --- a/po/gl.po +++ b/po/gl.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: gnome-shell master\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-11-27 01:42+0100\n" -"PO-Revision-Date: 2010-11-27 01:43+0100\n" +"POT-Creation-Date: 2010-11-29 17:32+0100\n" +"PO-Revision-Date: 2010-11-29 17:33+0100\n" "Last-Translator: Fran Diéguez \n" "Language-Team: Galician \n" "Language: gl\n" @@ -104,10 +104,6 @@ msgid "List of desktop file IDs for favorite applications" msgstr "Mostra os ID de ficheiros desktop para os aplicativos preferidos" #: ../data/org.gnome.shell.gschema.xml.in.h:13 -msgid "Overview workspace view mode" -msgstr "Modo de visualización do espazo de traballo de vista previa" - -#: ../data/org.gnome.shell.gschema.xml.in.h:14 msgid "" "Sets the GStreamer pipeline used to encode recordings. It follows the syntax " "used for gst-launch. The pipeline should have an unconnected sink pad where " @@ -128,19 +124,19 @@ msgstr "" "está a un valor baleiro, usarase a tubería predeterminada. Actualmente é " "«videorate ! theoraenc ! oggmux» e grava en Ogg Theora." -#: ../data/org.gnome.shell.gschema.xml.in.h:15 +#: ../data/org.gnome.shell.gschema.xml.in.h:14 msgid "Show date in clock" msgstr "Mostrar a data no reloxo" -#: ../data/org.gnome.shell.gschema.xml.in.h:16 +#: ../data/org.gnome.shell.gschema.xml.in.h:15 msgid "Show the week date in the calendar" msgstr "Mostrar a data da semana no calendario" -#: ../data/org.gnome.shell.gschema.xml.in.h:17 +#: ../data/org.gnome.shell.gschema.xml.in.h:16 msgid "Show time with seconds" msgstr "Mostrar a hora con segundos" -#: ../data/org.gnome.shell.gschema.xml.in.h:18 +#: ../data/org.gnome.shell.gschema.xml.in.h:17 msgid "" "The applications corresponding to these identifiers will be displayed in the " "favorites area." @@ -148,7 +144,7 @@ msgstr "" "Os aplicativos que corresponden a estes identificadores mostraranse na área " "de preferidos." -#: ../data/org.gnome.shell.gschema.xml.in.h:19 +#: ../data/org.gnome.shell.gschema.xml.in.h:18 msgid "" "The filename for recorded screencasts will be a unique filename based on the " "current date, and use this extension. It should be changed when recording to " @@ -158,7 +154,7 @@ msgstr "" "baseado na data actual e usa esta extensión. Debería cambiar ao grabar nun " "formato de contedor diferente." -#: ../data/org.gnome.shell.gschema.xml.in.h:20 +#: ../data/org.gnome.shell.gschema.xml.in.h:19 msgid "" "The framerate of the resulting screencast recordered by GNOME Shell's " "screencast recorder in frames-per-second." @@ -166,19 +162,11 @@ msgstr "" "A taxa de marcos do screencast resultante grabado polo grabador de " "screencasts de GNOME Shell en marcos-por-segundo." -#: ../data/org.gnome.shell.gschema.xml.in.h:21 +#: ../data/org.gnome.shell.gschema.xml.in.h:20 msgid "The gstreamer pipeline used to encode the screencast" msgstr "A tubería de gstreamer usada para codificar o screencast" -#: ../data/org.gnome.shell.gschema.xml.in.h:22 -msgid "" -"The selected workspace view mode in the overview. Supported values are " -"\"single\" and \"grid\"." -msgstr "" -"O modo de visualización do espazo de traballo seleccionado na vista previa. " -"Os valores admitidos son \"single\" e \"grid\"." - -#: ../data/org.gnome.shell.gschema.xml.in.h:23 +#: ../data/org.gnome.shell.gschema.xml.in.h:21 msgid "" "The shell normally monitors active applications in order to present the most " "used ones (e.g. in launchers). While this data will be kept private, you may " @@ -190,7 +178,7 @@ msgstr "" "privados, vostede pode desactivar isto por motivos de privacidade. Teña en " "conta que facendo isto non eliminará os datos gardados." -#: ../data/org.gnome.shell.gschema.xml.in.h:24 +#: ../data/org.gnome.shell.gschema.xml.in.h:22 msgid "" "This key specifies the format used by the panel clock when the format key is " "set to \"custom\". You can use conversion specifiers understood by strftime" @@ -202,7 +190,7 @@ msgstr "" "de conversión que entende strftime() para obter un formato de hora " "especificado. Vexa o manual de strftime() para obter máis información." -#: ../data/org.gnome.shell.gschema.xml.in.h:25 +#: ../data/org.gnome.shell.gschema.xml.in.h:23 msgid "" "This key specifies the hour format used by the panel clock. Possible values " "are \"12-hour\", \"24-hour\", \"unix\" and \"custom\". If set to \"unix\", " @@ -219,11 +207,11 @@ msgstr "" "está estabelecido a «unix» ou «custom», as chaves show_date e show_seconds " "ignoraranse." -#: ../data/org.gnome.shell.gschema.xml.in.h:26 +#: ../data/org.gnome.shell.gschema.xml.in.h:24 msgid "Uuids of extensions to disable" msgstr "Os Uuid das extensións a desactivar" -#: ../data/org.gnome.shell.gschema.xml.in.h:27 +#: ../data/org.gnome.shell.gschema.xml.in.h:25 msgid "Whether to collect stats about applications usage" msgstr "Indica se recoller estatísticas sobre o uso dos aplicativos" @@ -409,62 +397,41 @@ msgstr "Formato _12 horas" msgid "_24 hour format" msgstr "Formato _24 horas" -#. **** Applications **** -#: ../js/ui/appDisplay.js:316 ../js/ui/dash.js:778 +#: ../js/ui/appDisplay.js:215 msgid "APPLICATIONS" msgstr "APLICATIVOS" -#: ../js/ui/appDisplay.js:348 +#: ../js/ui/appDisplay.js:245 msgid "PREFERENCES" msgstr "PREFERENCIAS" -#: ../js/ui/appDisplay.js:647 +#: ../js/ui/appDisplay.js:538 msgid "New Window" msgstr "Xanela nova" -#: ../js/ui/appDisplay.js:651 +#: ../js/ui/appDisplay.js:542 msgid "Remove from Favorites" msgstr "Eliminar dos favoritos" -#: ../js/ui/appDisplay.js:652 +#: ../js/ui/appDisplay.js:543 msgid "Add to Favorites" msgstr "Engadir aos favoritos" -#: ../js/ui/appDisplay.js:829 -msgid "Drag here to add favorites" -msgstr "Arrastre aquí para engadir aos favoritos" - -#: ../js/ui/appFavorites.js:88 +#: ../js/ui/appFavorites.js:91 #, c-format msgid "%s has been added to your favorites." msgstr "%s foi engadido aos seus favoritos." -#: ../js/ui/appFavorites.js:107 +#: ../js/ui/appFavorites.js:122 #, c-format msgid "%s has been removed from your favorites." msgstr "%s foi eliminado dos seus favoritos." -#: ../js/ui/dash.js:142 -msgid "Find" -msgstr "Buscar" +#: ../js/ui/dash.js:27 +msgid "Remove" +msgstr "Eliminar" -#: ../js/ui/dash.js:473 -msgid "Searching..." -msgstr "Buscando..." - -#: ../js/ui/dash.js:487 -msgid "No matching results." -msgstr "Non hai resultados que coincidan." - -#. **** Places **** -#. Translators: This is in the sense of locations for documents, -#. network locations, etc. -#: ../js/ui/dash.js:797 ../js/ui/placeDisplay.js:558 -msgid "PLACES & DEVICES" -msgstr "LUGARES E DISPOSITIVOS" - -#. **** Documents **** -#: ../js/ui/dash.js:804 ../js/ui/docDisplay.js:494 +#: ../js/ui/docDisplay.js:494 msgid "RECENT ITEMS" msgstr "ELEMENTOS RECENTES" @@ -498,7 +465,7 @@ msgstr "Ver fonte" msgid "Web Page" msgstr "Páxina web" -#: ../js/ui/overview.js:160 +#: ../js/ui/overview.js:112 msgid "Undo" msgstr "Desfacer" @@ -571,6 +538,10 @@ msgstr "Reintentar" msgid "Connect to..." msgstr "Conectar con..." +#: ../js/ui/placeDisplay.js:558 +msgid "PLACES & DEVICES" +msgstr "LUGARES E DISPOSITIVOS" + #. Translators: this MUST be either "toggle-switch-us" #. (for toggle switches containing the English words #. "ON" and "OFF") or "toggle-switch-intl" (for toggle @@ -669,6 +640,10 @@ msgstr "Texto máis grande" msgid "Zoom" msgstr "Ampliación" +#: ../js/ui/viewSelector.js:26 +msgid "Search your computer" +msgstr "Buscar no seu computador" + #: ../js/ui/windowAttentionHandler.js:43 #, c-format msgid "%s has finished starting" @@ -679,14 +654,14 @@ msgstr "%s rematou de iniarse" msgid "'%s' is ready" msgstr "«%s» está preparado" -#: ../js/ui/workspacesView.js:229 +#: ../js/ui/workspacesView.js:244 msgid "" "Can't add a new workspace because maximum workspaces limit has been reached." msgstr "" "Non é posíbel engadir unha área de traballo nova porque chegouse ao límite " "de áreas de traballo." -#: ../js/ui/workspacesView.js:246 +#: ../js/ui/workspacesView.js:260 msgid "Can't remove the first workspace." msgstr "Non é posíbel quitar a primeira área de traballo." @@ -772,6 +747,28 @@ msgstr "Buscar" msgid "%1$s: %2$s" msgstr "%1$s: %2$s" +#~ msgid "Overview workspace view mode" +#~ msgstr "Modo de visualización do espazo de traballo de vista previa" + +#~ msgid "" +#~ "The selected workspace view mode in the overview. Supported values are " +#~ "\"single\" and \"grid\"." +#~ msgstr "" +#~ "O modo de visualización do espazo de traballo seleccionado na vista " +#~ "previa. Os valores admitidos son \"single\" e \"grid\"." + +#~ msgid "Drag here to add favorites" +#~ msgstr "Arrastre aquí para engadir aos favoritos" + +#~ msgid "Find" +#~ msgstr "Buscar" + +#~ msgid "Searching..." +#~ msgstr "Buscando..." + +#~ msgid "No matching results." +#~ msgstr "Non hai resultados que coincidan." + #~ msgid "Invisible" #~ msgstr "Invisíbel" From c908a060b839b5e80a55dcbcd5e5f08246d1ba7c Mon Sep 17 00:00:00 2001 From: William Jon McCann Date: Mon, 29 Nov 2010 13:16:20 -0500 Subject: [PATCH 52/61] Touch up the spacing and style of the app view Adds more padding between app icons, left aligns the view, removes the ugly separator, adds some margin on the right. --- data/theme/gnome-shell.css | 10 +++++----- js/ui/appDisplay.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 49aa4978f..d9fa4a8fa 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -347,6 +347,7 @@ StTooltip StLabel { } #viewSelector { + padding: 16px; spacing: 16px; } @@ -485,22 +486,21 @@ StTooltip StLabel { } .icon-grid { - spacing: 6px; + spacing: 36px; -shell-grid-item-size: 70px; } .all-app { - padding: 10px; + padding: 36px 150px 10px 0px; } .app-section-divider-container { - padding-top: 10px; - padding-bottom: 10px; + padding-top: 36px; + padding-bottom: 36px; } .app-section-divider { height: 2px; - background-image: url("separator-white.png"); } #dash > .app-well-app { diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index 09c7c8827..a314098e8 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -30,7 +30,7 @@ function AlphabeticalView() { AlphabeticalView.prototype = { _init: function() { this.actor = new St.BoxLayout({ vertical: true }); - this._grid = new IconGrid.IconGrid(); + this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START }); this._appSystem = Shell.AppSystem.get_default(); this.actor.add(this._grid.actor, { y_align: St.Align.START, expand: true }); }, From 6b0fe1b0b074072c99d26729c20b06936d77900a Mon Sep 17 00:00:00 2001 From: Marina Zhurakhinskaya Date: Thu, 25 Nov 2010 01:40:56 -0500 Subject: [PATCH 53/61] Add padding on the left of the notification scrollbar It looks better that way. https://bugzilla.gnome.org/show_bug.cgi?id=630752 --- data/theme/gnome-shell.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index d9fa4a8fa..b27018209 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -823,6 +823,10 @@ StTooltip StLabel { height: 1em; } +#notification-scrollview > StScrollBar { + padding-left: 6px; +} + #notification-body { spacing: 5px; } From c9b178b193c788458d4ee15b21a83cad7ebf4126 Mon Sep 17 00:00:00 2001 From: William Jon McCann Date: Mon, 29 Nov 2010 14:03:07 -0500 Subject: [PATCH 54/61] Touch up padding again this time with the correct box --- data/theme/gnome-shell.css | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index b27018209..40dc13c79 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -347,12 +347,15 @@ StTooltip StLabel { } #viewSelector { - padding: 16px; spacing: 16px; } +#viewSelectorTabBar { + padding: 16px; +} + #searchArea { - padding: 0px 12px; + padding: 0px 24px; } #searchEntry { @@ -491,7 +494,7 @@ StTooltip StLabel { } .all-app { - padding: 36px 150px 10px 0px; + padding: 16px 250px 10px 16px; } .app-section-divider-container { From b25bad99951cde2066ba79183ef0da2d985c0ea4 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 29 Nov 2010 11:12:21 -0500 Subject: [PATCH 55/61] notificationDaemon: fix escaping in xchat rewrite rules https://bugzilla.gnome.org/show_bug.cgi?id=635712 --- js/ui/notificationDaemon.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/ui/notificationDaemon.js b/js/ui/notificationDaemon.js index dc5053738..0f2a6c9cd 100644 --- a/js/ui/notificationDaemon.js +++ b/js/ui/notificationDaemon.js @@ -75,11 +75,11 @@ const Urgency = { const rewriteRules = { 'XChat': [ { pattern: /^XChat: Private message from: (\S*) \(.*\)$/, - replacement: '<$1>' }, + replacement: '<$1>' }, { pattern: /^XChat: New public message from: (\S*) \((.*)\)$/, - replacement: '$2 <$1>' }, + replacement: '$2 <$1>' }, { pattern: /^XChat: Highlighted message from: (\S*) \((.*)\)$/, - replacement: '$2 <$1>' } + replacement: '$2 <$1>' } ] }; From 4517f60630a58bd9c1b7a9f8048a4f70fa150769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Mon, 29 Nov 2010 21:47:54 +0100 Subject: [PATCH 56/61] environment: Get text direction from GTK+-3 Despite of switching to GTK+-3 unconditionally, the default text direction was still taken from GTK+-2. --- js/ui/environment.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/ui/environment.js b/js/ui/environment.js index 82ecea148..0ab354c19 100644 --- a/js/ui/environment.js +++ b/js/ui/environment.js @@ -4,7 +4,7 @@ const Clutter = imports.gi.Clutter;; const GLib = imports.gi.GLib; const Shell = imports.gi.Shell; const St = imports.gi.St; -const Gettext_gtk20 = imports.gettext.domain('gtk20'); +const Gettext_gtk30 = imports.gettext.domain('gtk30'); const Tweener = imports.ui.tweener; @@ -65,7 +65,7 @@ function init() { String.prototype.format = Format.format; // Set the default direction for St widgets (this needs to be done before any use of St) - if (Gettext_gtk20.gettext('default:LTR') == 'default:RTL') { + if (Gettext_gtk30.gettext('default:LTR') == 'default:RTL') { St.Widget.set_default_direction(St.TextDirection.RTL); } From 8b5cd4ef0fc72527b88740220c2d4cb3bc061e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Mon, 29 Nov 2010 22:41:05 +0100 Subject: [PATCH 57/61] calendar: Take week start and month/year ordering from GTK+-3 Despite of switching to GTK+-3 unconditionally, those settings were still taken from GTK+-2. --- js/ui/calendar.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/ui/calendar.js b/js/ui/calendar.js index 078c69d82..8f58f69f7 100644 --- a/js/ui/calendar.js +++ b/js/ui/calendar.js @@ -5,7 +5,7 @@ const Gio = imports.gi.Gio; const Lang = imports.lang; const St = imports.gi.St; const Pango = imports.gi.Pango; -const Gettext_gtk20 = imports.gettext.domain('gtk20'); +const Gettext_gtk30 = imports.gettext.domain('gtk30'); const MSECS_IN_DAY = 24 * 60 * 60 * 1000; const WEEKDATE_HEADER_WIDTH_DIGITS = 3; @@ -60,7 +60,7 @@ Calendar.prototype = { this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange)); this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY); - let weekStartString = Gettext_gtk20.gettext('calendar:week_start:0'); + let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0'); if (weekStartString.indexOf('calendar:week_start:') == 0) { this._weekStart = parseInt(weekStartString.substring(20)); } @@ -71,7 +71,7 @@ Calendar.prototype = { } // Find the ordering for month/year in the calendar heading - switch (Gettext_gtk20.gettext('calendar:MY')) { + switch (Gettext_gtk30.gettext('calendar:MY')) { case 'calendar:MY': this._headerFormat = '%B %Y'; break; From eb2ee3f25938abb4d31e441ce6c34124a5edf2b7 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Mon, 29 Nov 2010 17:58:04 -0500 Subject: [PATCH 58/61] Bump version to 2.91.3 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c5d5a8920..daf833cfb 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.63) -AC_INIT([gnome-shell],[2.91.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) +AC_INIT([gnome-shell],[2.91.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_SRCDIR([src/shell-global.c]) From 6adc12368cf09c2056606c10314de2d9674a2557 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 22 Nov 2010 14:10:42 -0500 Subject: [PATCH 59/61] boxpointer: don't draw the arrow overlapping the corners In some circumstances, a boxpointer would draw itself with the arrow partially overlapping the rounded corner, causing things to not line up correctly. Don't do that. And while we're at it, don't draw the pointer very very close to the corner either, since it looks odd if the corner flows directly into the arrow. https://bugzilla.gnome.org/show_bug.cgi?id=635393 --- js/ui/boxpointer.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/js/ui/boxpointer.js b/js/ui/boxpointer.js index 06977e8c1..450bed3ef 100644 --- a/js/ui/boxpointer.js +++ b/js/ui/boxpointer.js @@ -11,7 +11,7 @@ const POPUP_ANIMATION_TIME = 0.15; /** * BoxPointer: - * @side: A St.Side type; currently only St.Side.TOP is implemented + * @side: side to draw the arrow on * @binProperties: Properties to set on contained bin * * An actor which displays a triangle "arrow" pointing to a given @@ -272,7 +272,9 @@ BoxPointer.prototype = { // edge by the same distance as the main part of the box is // separated from its sourceActor let primary = global.get_primary_monitor(); - let arrowRise = this.actor.get_theme_node().get_length('-arrow-rise'); + let themeNode = this.actor.get_theme_node(); + let arrowRise = themeNode.get_length('-arrow-rise'); + let borderRadius = themeNode.get_length('-arrow-border-radius'); let resX, resY; @@ -298,13 +300,13 @@ BoxPointer.prototype = { case St.Side.BOTTOM: switch (alignment) { case St.Align.START: - resX = sourceX; + resX = sourceX - 2 * borderRadius; break; case St.Align.MIDDLE: resX = sourceX - Math.floor((natWidth - sourceWidth) / 2); break; case St.Align.END: - resX = sourceX - (natWidth - sourceWidth); + resX = sourceX - (natWidth - sourceWidth) + 2 * borderRadius; break; } @@ -318,13 +320,13 @@ BoxPointer.prototype = { case St.Side.RIGHT: switch (alignment) { case St.Align.START: - resY = sourceY; + resY = sourceY - 2 * borderRadius; break; case St.Align.MIDDLE: resY = sourceY - Math.floor((natHeight - sourceHeight) / 2); break; case St.Align.END: - resY = sourceY - (natHeight - sourceHeight); + resY = sourceY - (natHeight - sourceHeight) + 2 * borderRadius; break; } From 6aac51a2a79e0ff93849adf83008bb3df3414d5c Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Mon, 22 Nov 2010 14:11:43 -0500 Subject: [PATCH 60/61] popupMenu: give sliders a minimum width The simpler volume menu resulted in a slider that was too short. Fix that. https://bugzilla.gnome.org/show_bug.cgi?id=635393 --- data/theme/gnome-shell.css | 1 + 1 file changed, 1 insertion(+) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 40dc13c79..77ee7f871 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -149,6 +149,7 @@ StTooltip StLabel { .popup-slider-menu-item { height: 1em; + min-width: 15em; -slider-height: 0.3em; -slider-background-color: #333333; -slider-border-color: #5f5f5f; From 991a0c642bf3b628cb3cb2c00e5c85f40d745a82 Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Fri, 26 Nov 2010 22:23:22 +0100 Subject: [PATCH 61/61] PowerStatus: make the primary device activatable If the primary device is not virtual, make it open gnome-power-statistics on activate. https://bugzilla.gnome.org/show_bug.cgi?id=635880 --- js/ui/status/power.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/js/ui/status/power.js b/js/ui/status/power.js index 794a090ec..e8d94be99 100644 --- a/js/ui/status/power.js +++ b/js/ui/status/power.js @@ -121,6 +121,18 @@ Indicator.prototype = { } this._primaryDeviceId = device_id; + if (this._primaryDeviceId) { + this._batteryItem.actor.reactive = true; + this._batteryItem.actor.can_focus = true; + this._batteryItem.connect('activate', function(item) { + let p = new Shell.Process({ args: ['gnome-power-statistics', '--device', device_id] }); + p.run(); + }); + } else { + // virtual device + this._batteryItem.actor.reactive = false; + this._batteryItem.actor.can_focus = false; + } })); },