[gnome-shell/wip/new-notifications: 1/8] messageTray: Add a new message tray indicator
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/new-notifications: 1/8] messageTray: Add a new message tray indicator
- Date: Fri, 13 Dec 2013 18:26:49 +0000 (UTC)
commit f583fc2df630c342e353f6817f86f01a3bd62031
Author: Jasper St. Pierre <jstpierre mecheye net>
Date: Wed Dec 4 21:09:23 2013 -0500
messageTray: Add a new message tray indicator
data/theme/gnome-shell.css | 22 ++++++
js/ui/layout.js | 165 +++++++++++++++++++++-----------------------
js/ui/messageTray.js | 125 +++++++++++++++++++++++++++++++++
js/ui/overview.js | 2 -
js/ui/overviewControls.js | 84 ----------------------
5 files changed, 226 insertions(+), 172 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index d3e8a75..fa00cbb 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -1450,6 +1450,28 @@ StScrollBar StButton#vhandle:active {
color: #999999;
}
+.message-tray-indicator {
+ spacing: 4px;
+}
+
+.message-tray-indicator-count {
+ font-size: 1.2em;
+ font-weight: bold;
+
+ color: black;
+ background-color: rgba(255, 255, 255, 0.7);
+ border-radius: 1em;
+ padding: 1em;
+ text-align: center;
+}
+
+.message-tray-indicator-glow {
+ height: 4px;
+ background-gradient-start: rgba(255, 255, 255, 0);
+ background-gradient-end: rgba(255, 255, 255, 1);
+ background-gradient-direction: vertical;
+}
+
.notification {
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.9);
diff --git a/js/ui/layout.js b/js/ui/layout.js
index c74033b..9e7749d 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -23,11 +23,6 @@ const KEYBOARD_ANIMATION_TIME = 0.15;
const BACKGROUND_FADE_ANIMATION_TIME = 1.0;
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
-// The message tray takes this much pressure
-// in the pressure barrier at once to release it.
-const MESSAGE_TRAY_PRESSURE_THRESHOLD = 250; // pixels
-const MESSAGE_TRAY_PRESSURE_TIMEOUT = 1000; // ms
-
const HOT_CORNER_PRESSURE_THRESHOLD = 100; // pixels
const HOT_CORNER_PRESSURE_TIMEOUT = 1000; // ms
@@ -151,7 +146,6 @@ const LayoutManager = new Lang.Class({
this._keyboardIndex = -1;
this._rightPanelBarrier = null;
- this._trayBarrier = null;
this._inOverview = false;
this._updateRegionIdle = 0;
@@ -211,7 +205,6 @@ const LayoutManager = new Lang.Class({
this.trayBox = new St.Widget({ name: 'trayBox',
layout_manager: new Clutter.BinLayout() });
this.addChrome(this.trayBox);
- this._setupTrayPressure();
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true,
@@ -441,50 +434,9 @@ const LayoutManager = new Lang.Class({
}
},
- _setupTrayPressure: function() {
- this._trayPressure = new PressureBarrier(MESSAGE_TRAY_PRESSURE_THRESHOLD,
- MESSAGE_TRAY_PRESSURE_TIMEOUT,
- Shell.KeyBindingMode.NORMAL |
- Shell.KeyBindingMode.OVERVIEW);
- this._trayPressure.setEventFilter(this._trayBarrierEventFilter);
- this._trayPressure.connect('trigger', function(barrier) {
- if (Main.layoutManager.bottomMonitor.inFullscreen)
- return;
-
- Main.messageTray.openTray();
- });
- },
-
- _updateTrayBarrier: function() {
- let monitor = this.bottomMonitor;
-
- if (this._trayBarrier) {
- this._trayPressure.removeBarrier(this._trayBarrier);
- this._trayBarrier.destroy();
- this._trayBarrier = null;
- }
-
- this._trayBarrier = new Meta.Barrier({ display: global.display,
- x1: monitor.x, x2: monitor.x + monitor.width,
- y1: monitor.y + monitor.height, y2: monitor.y +
monitor.height,
- directions: Meta.BarrierDirection.NEGATIVE_Y });
- this._trayPressure.addBarrier(this._trayBarrier);
- },
-
- _trayBarrierEventFilter: function(event) {
- // Throw out all events where the pointer was grabbed by another
- // client, as the client that grabbed the pointer expects to have
- // complete control over it
- if (event.grabbed && Main.modalCount == 0)
- return true;
-
- return false;
- },
-
_monitorsChanged: function() {
this._updateMonitors();
this._updateBoxes();
- this._updateTrayBarrier();
this._updateHotCorners();
this._updateBackgrounds();
this._updateFullscreen();
@@ -1066,10 +1018,10 @@ const HotCorner = new Lang.Class({
this._setupFallbackCornerIfNeeded(layoutManager);
- this._pressureBarrier = new PressureBarrier(HOT_CORNER_PRESSURE_THRESHOLD,
- HOT_CORNER_PRESSURE_TIMEOUT,
- Shell.KeyBindingMode.NORMAL |
- Shell.KeyBindingMode.OVERVIEW);
+ this._pressureBarrier = new TriggerablePressureBarrier(HOT_CORNER_PRESSURE_THRESHOLD,
+ HOT_CORNER_PRESSURE_TIMEOUT,
+ Shell.KeyBindingMode.NORMAL |
+ Shell.KeyBindingMode.OVERVIEW);
this._pressureBarrier.connect('trigger', Lang.bind(this, this._toggleOverview));
// Cache the three ripples instead of dynamically creating and destroying them.
@@ -1247,14 +1199,12 @@ const PressureBarrier = new Lang.Class({
Name: 'PressureBarrier',
_init: function(threshold, timeout, keybindingMode) {
- this._threshold = threshold;
- this._timeout = timeout;
+ this.threshold = threshold;
+ this.timeout = timeout;
this._keybindingMode = keybindingMode;
this._barriers = [];
this._eventFilter = null;
-
- this._isTriggered = false;
- this._reset();
+ this.reset();
},
addBarrier: function(barrier) {
@@ -1283,10 +1233,10 @@ const PressureBarrier = new Lang.Class({
this._eventFilter = filter;
},
- _reset: function() {
+ reset: function() {
this._barrierEvents = [];
- this._currentPressure = 0;
this._lastTime = 0;
+ this.currentPressure = 0;
},
_isHorizontal: function(barrier) {
@@ -1307,12 +1257,21 @@ const PressureBarrier = new Lang.Class({
return Math.abs(event.dy);
},
+ get currentPressure() {
+ return this._currentPressure;
+ },
+
+ set currentPressure(value) {
+ this._currentPressure = value;
+ this.emit('pressure-changed');
+ },
+
_trimBarrierEvents: function() {
// Events are guaranteed to be sorted in time order from
// oldest to newest, so just look for the first old event,
// and then chop events after that off.
let i = 0;
- let threshold = this._lastTime - this._timeout;
+ let threshold = this._lastTime - this.timeout;
while (i < this._barrierEvents.length) {
let [time, distance] = this._barrierEvents[i];
@@ -1325,49 +1284,38 @@ const PressureBarrier = new Lang.Class({
for (i = 0; i < firstNewEvent; i++) {
let [time, distance] = this._barrierEvents[i];
- this._currentPressure -= distance;
+ this.currentPressure = distance;
}
this._barrierEvents = this._barrierEvents.slice(firstNewEvent);
},
_onBarrierLeft: function(barrier, event) {
- this._reset();
- this._isTriggered = false;
- },
-
- _trigger: function() {
- this._isTriggered = true;
- this.emit('trigger');
- this._reset();
+ this.reset();
},
- _onBarrierHit: function(barrier, event) {
- // If we've triggered the barrier, wait until the pointer has the
- // left the barrier hitbox until we trigger it again.
- if (this._isTriggered)
- return;
-
+ _shouldUseEvent: function(barrier, event) {
if (this._eventFilter && this._eventFilter(event))
- return;
+ return false;
// Throw out all events not in the proper keybinding mode
if (!(this._keybindingMode & Main.keybindingMode))
- return;
+ return false;
let slide = this._getDistanceAlongBarrier(barrier, event);
let distance = this._getDistanceAcrossBarrier(barrier, event);
- if (distance >= this._threshold) {
- this._trigger();
- return;
- }
-
// Throw out events where the cursor is move more
// along the axis of the barrier than moving with
// the barrier.
if (slide > distance)
- return;
+ return false;
+
+ return true;
+ },
+
+ _appendEvent: function(barrier, event) {
+ let distance = this._getDistanceAcrossBarrier(barrier, event);
this._lastTime = event.time;
@@ -1375,10 +1323,55 @@ const PressureBarrier = new Lang.Class({
distance = Math.min(15, distance);
this._barrierEvents.push([event.time, distance]);
- this._currentPressure += distance;
+ this.currentPressure += distance;
+ },
- if (this._currentPressure >= this._threshold)
- this._trigger();
+ _onBarrierHit: function(barrier, event) {
+ if (!this._shouldUseEvent(barrier, event))
+ return;
+
+ this._appendEvent(barrier, event);
}
});
Signals.addSignalMethods(PressureBarrier.prototype);
+
+const TriggerablePressureBarrier = new Lang.Class({
+ Name: 'TriggerablePressureBarrier',
+ Extends: PressureBarrier,
+
+ _init: function(threshold, timeout, keybindingMode) {
+ this.parent(threshold, timeout, keybindingMode);
+ this._isTriggered = false;
+ },
+
+ _trigger: function() {
+ this._isTriggered = true;
+ this.emit('trigger');
+ this.reset();
+ },
+
+ _onBarrierLeft: function() {
+ this.parent();
+ this._isTriggered = false;
+ },
+
+ _onBarrierHit: function(barrier, event) {
+ // If we've triggered the barrier, wait until the pointer has the
+ // left the barrier hitbox until we trigger it again.
+ if (this._isTriggered)
+ return;
+
+ if (!this._shouldUseEvent(barrier, event))
+ return;
+
+ if (distance >= this.threshold) {
+ this._trigger();
+ return;
+ }
+
+ this._appendEvent(barrier, event);
+
+ if (this.currentPressure >= this.threshold)
+ this._trigger();
+ },
+});
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index dc630b3..9f6eba3 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -20,6 +20,7 @@ const CtrlAltTab = imports.ui.ctrlAltTab;
const GnomeSession = imports.misc.gnomeSession;
const GrabHelper = imports.ui.grabHelper;
const Hash = imports.misc.hash;
+const Layout = imports.ui.layout;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const PointerWatcher = imports.ui.pointerWatcher;
@@ -49,6 +50,9 @@ const TRAY_DWELL_CHECK_INTERVAL = 100; // ms
const IDLE_TIME = 1000;
+const MESSAGE_TRAY_PRESSURE_THRESHOLD = 250; // pixels
+const MESSAGE_TRAY_PRESSURE_TIMEOUT = 1000; // ms
+
const State = {
HIDDEN: 0,
SHOWING: 1,
@@ -1723,6 +1727,118 @@ const MessageTrayMenuButton = new Lang.Class({
},
});
+const MessageTrayIndicator = new Lang.Class({
+ Name: 'MessageTrayIndicator',
+
+ _init: function(tray) {
+ this._tray = tray;
+
+ this.actor = new St.BoxLayout({ style_class: 'message-tray-indicator',
+ reactive: true,
+ track_hover: true,
+ vertical: true,
+ x_expand: true,
+ y_expand: true,
+ y_align: Clutter.ActorAlign.START });
+ this.actor.connect('notify::height', Lang.bind(this, function() {
+ this.actor.translation_y = -this.actor.height;
+ }));
+ this.actor.connect('button-press-event', Lang.bind(this, function() {
+ this._tray.openTray();
+ this._pressureBarrier.reset();
+ }));
+
+ this._count = new St.Label({ style_class: 'message-tray-indicator-count',
+ x_expand: true,
+ x_align: Clutter.ActorAlign.CENTER });
+ this.actor.add_child(this._count);
+
+ this._tray.connect('indicator-count-updated', Lang.bind(this, this._syncCount));
+ this._syncCount();
+
+ this._glow = new St.Widget({ style_class: 'message-tray-indicator-glow',
+ x_expand: true });
+ this.actor.add_child(this._glow);
+
+ this._pressureBarrier = new Layout.PressureBarrier(MESSAGE_TRAY_PRESSURE_THRESHOLD,
+ MESSAGE_TRAY_PRESSURE_TIMEOUT,
+ Shell.KeyBindingMode.NORMAL |
+ Shell.KeyBindingMode.OVERVIEW);
+ this._pressureBarrier.setEventFilter(this._barrierEventFilter);
+ Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateBarrier));
+ this._updateBarrier();
+
+ this._pressureBarrier.connect('pressure-changed', Lang.bind(this, this._updatePressure));
+ this._pressureValue = 0;
+ this._syncGlow();
+ },
+
+ _updateBarrier: function() {
+ let monitor = Main.layoutManager.bottomMonitor;
+
+ if (this._barrier) {
+ this._pressureBarrier.removeBarrier(this._trayBarrier);
+ this._barrier.destroy();
+ this._barrier = null;
+ }
+
+ this._barrier = new Meta.Barrier({ display: global.display,
+ x1: monitor.x, x2: monitor.x + monitor.width,
+ y1: monitor.y + monitor.height, y2: monitor.y + monitor.height,
+ directions: Meta.BarrierDirection.NEGATIVE_Y });
+ this._pressureBarrier.addBarrier(this._barrier);
+ },
+
+ _trayBarrierEventFilter: function(event) {
+ // Throw out all events where the pointer was grabbed by another
+ // client, as the client that grabbed the pointer expects to have
+ // complete control over it
+ if (event.grabbed && Main.modalCount == 0)
+ return true;
+
+ if (this._tray.hasVisibleNotification())
+ return true;
+
+ return false;
+ },
+
+ _syncCount: function() {
+ let count = this._tray.indicatorCount;
+ this._count.visible = (count > 0);
+ this._count.text = '' + count;
+ },
+
+ _syncGlow: function() {
+ let value = this._pressureValue;
+ let percent = value / this._pressureBarrier.threshold;
+ this.actor.opacity = Math.min(percent * 255, 255);
+ this.actor.visible = (value > 0);
+ },
+
+ get pressureValue() {
+ return this._pressureValue;
+ },
+
+ set pressureValue(value) {
+ this._pressureValue = value;
+ this._syncGlow();
+ },
+
+ _updatePressure: function() {
+ let value = this._pressureBarrier.currentPressure;
+ this.pressureValue = value;
+ if (value > 0) {
+ Tweener.removeTweens(this);
+ Tweener.addTween(this, { time: this._pressureBarrier.timeout / 1000,
+ pressureValue: 0 });
+ }
+ },
+
+ destroy: function() {
+ this.actor.destroy();
+ },
+});
+
const MessageTray = new Lang.Class({
Name: 'MessageTray',
@@ -1900,6 +2016,11 @@ const MessageTray = new Lang.Class({
this._messageTrayMenuButton = new MessageTrayMenuButton(this);
this.actor.add_actor(this._messageTrayMenuButton.actor);
+
+ this._indicator = new MessageTrayIndicator(this);
+ Main.layoutManager.trayBox.add_child(this._indicator.actor);
+ Main.layoutManager.trackChrome(this._indicator.actor);
+ this._grabHelper.addActor(this._indicator.actor);
},
close: function() {
@@ -2351,6 +2472,10 @@ const MessageTray = new Lang.Class({
this._updateState();
},
+ hasVisibleNotification: function() {
+ return this._notificationState != State.HIDDEN;
+ },
+
// All of the logic for what happens when occurs here; the various
// event handlers merely update variables such as
// 'this._pointerInNotification', 'this._traySummoned', etc, and
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 1809fe4..a8bcd48 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -269,8 +269,6 @@ const Overview = new Lang.Class({
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
- this._stack.add_actor(this._controls.indicatorActor);
-
// TODO - recalculate everything when desktop size changes
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
diff --git a/js/ui/overviewControls.js b/js/ui/overviewControls.js
index de6142d..0f12e8b 100644
--- a/js/ui/overviewControls.js
+++ b/js/ui/overviewControls.js
@@ -391,87 +391,6 @@ const DashSpacer = new Lang.Class({
}
});
-const MessagesIndicator = new Lang.Class({
- Name: 'MessagesIndicator',
-
- _init: function(viewSelector) {
- this._count = 0;
- this._sources = [];
- this._viewSelector = viewSelector;
-
- this._container = new St.BoxLayout({ style_class: 'messages-indicator-contents',
- reactive: true,
- track_hover: true,
- x_expand: true,
- y_expand: true,
- x_align: Clutter.ActorAlign.CENTER });
-
- this._icon = new St.Icon({ icon_name: 'user-idle-symbolic',
- icon_size: 16 });
- this._container.add_actor(this._icon);
-
- this._label = new St.Label();
- this._container.add_actor(this._label);
-
- this._highlight = new St.Widget({ style_class: 'messages-indicator-highlight',
- x_expand: true,
- y_expand: true,
- y_align: Clutter.ActorAlign.END,
- visible: false });
-
- this._container.connect('notify::hover', Lang.bind(this,
- function() {
- this._highlight.visible = this._container.hover;
- }));
-
- let clickAction = new Clutter.ClickAction();
- this._container.add_action(clickAction);
- clickAction.connect('clicked', Lang.bind(this,
- function() {
- Main.messageTray.openTray();
- }));
-
- Main.messageTray.connect('showing', Lang.bind(this,
- function() {
- this._highlight.visible = false;
- this._container.hover = false;
- }));
-
- let layout = new Clutter.BinLayout();
- this.actor = new St.Widget({ layout_manager: layout,
- style_class: 'messages-indicator',
- y_expand: true,
- y_align: Clutter.ActorAlign.END,
- visible: false });
- this.actor.add_actor(this._container);
- this.actor.add_actor(this._highlight);
-
- Main.messageTray.connect('indicator-count-updated', Lang.bind(this, this._sync));
- this._sync();
-
- this._viewSelector.connect('page-changed', Lang.bind(this, this._updateVisibility));
- Main.overview.connect('showing', Lang.bind(this, this._updateVisibility));
- },
-
- _sync: function() {
- let count = Main.messageTray.indicatorCount;
- this._count = count;
- this._label.text = ngettext("%d new message",
- "%d new messages",
- count).format(count);
-
- this._icon.visible = Main.messageTray.hasChatSources;
- this._updateVisibility();
- },
-
- _updateVisibility: function() {
- let activePage = this._viewSelector.getActivePage();
- let visible = ((this._count > 0) && (activePage == ViewSelector.ViewPage.WINDOWS));
-
- this.actor.visible = visible;
- }
-});
-
const ControlsLayout = new Lang.Class({
Name: 'ControlsLayout',
Extends: Clutter.BinLayout,
@@ -500,9 +419,6 @@ const ControlsManager = new Lang.Class({
this.viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
this.viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
- this._indicator = new MessagesIndicator(this.viewSelector);
- this.indicatorActor = this._indicator.actor;
-
let layout = new ControlsLayout();
this.actor = new St.Widget({ layout_manager: layout,
reactive: true,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]