Compare commits
	
		
			20 Commits
		
	
	
		
			3.35.91
			...
			wip/grab-h
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					64bacc6286 | ||
| 
						 | 
					50fec9e1f8 | ||
| 
						 | 
					b03f7564e3 | ||
| 
						 | 
					ef9006fa65 | ||
| 
						 | 
					485d16ca4e | ||
| 
						 | 
					1e40264ee4 | ||
| 
						 | 
					ae22bde368 | ||
| 
						 | 
					32502af652 | ||
| 
						 | 
					7d693cbd17 | ||
| 
						 | 
					d86e57a8d9 | ||
| 
						 | 
					75985ae0ce | ||
| 
						 | 
					7ee897ead5 | ||
| 
						 | 
					7c371392da | ||
| 
						 | 
					ab549f763d | ||
| 
						 | 
					ea25331a55 | ||
| 
						 | 
					c6bc1526fa | ||
| 
						 | 
					871ae3f9b2 | ||
| 
						 | 
					8ebbf442cd | ||
| 
						 | 
					b936f094d1 | ||
| 
						 | 
					165265f4fb | 
@@ -114,14 +114,6 @@ AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
 | 
			
		||||
 | 
			
		||||
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
 | 
			
		||||
 | 
			
		||||
saved_CFLAGS=$CFLAGS
 | 
			
		||||
saved_LIBS=$LIBS
 | 
			
		||||
CFLAGS=$GNOME_SHELL_CFLAGS
 | 
			
		||||
LIBS=$GNOME_SHELL_LIBS
 | 
			
		||||
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
 | 
			
		||||
CFLAGS=$saved_CFLAGS
 | 
			
		||||
LIBS=$saved_LIBS
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
 | 
			
		||||
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
 | 
			
		||||
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
 | 
			
		||||
 
 | 
			
		||||
@@ -48,18 +48,16 @@ const AlphabeticalView = new Lang.Class({
 | 
			
		||||
                                         style_class: 'vfade' });
 | 
			
		||||
        this.actor.add_actor(box);
 | 
			
		||||
        this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (!this.actor.mapped)
 | 
			
		||||
                    return;
 | 
			
		||||
        let action = new Clutter.PanAction({ interpolate: true });
 | 
			
		||||
        action.connect('pan', Lang.bind(this, this._onPan));
 | 
			
		||||
        this.actor.add_action(action);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
                let adjustment = this.actor.vscroll.adjustment;
 | 
			
		||||
                let direction = Overview.SwipeScrollDirection.VERTICAL;
 | 
			
		||||
                Main.overview.setScrollAdjustment(adjustment, direction);
 | 
			
		||||
 | 
			
		||||
                // Reset scroll on mapping
 | 
			
		||||
                adjustment.value = 0;
 | 
			
		||||
            }));
 | 
			
		||||
    _onPan: function(action) {
 | 
			
		||||
        let [dist, dx, dy] = action.get_motion_delta(0);
 | 
			
		||||
        let adjustment = this.actor.vscroll.adjustment;
 | 
			
		||||
        adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,6 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._actors = [];
 | 
			
		||||
        this._capturedEventId = 0;
 | 
			
		||||
        this._eventId = 0;
 | 
			
		||||
        this._keyFocusNotifyId = 0;
 | 
			
		||||
        this._focusWindowChangedId = 0;
 | 
			
		||||
        this._ignoreRelease = false;
 | 
			
		||||
@@ -77,10 +76,25 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _actorInGrabStack: function(actor) {
 | 
			
		||||
        while (actor) {
 | 
			
		||||
            for (let i = 0; i < this._grabStack.length; i++) {
 | 
			
		||||
                let grab = this._grabStack[i];
 | 
			
		||||
                if (grab.actor == actor)
 | 
			
		||||
                    return i;
 | 
			
		||||
            }
 | 
			
		||||
            actor = actor.get_parent();
 | 
			
		||||
        }
 | 
			
		||||
        return -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _isWithinGrabbedActor: function(actor) {
 | 
			
		||||
       let currentActor = this.currentGrab.actor;
 | 
			
		||||
        while (actor) {
 | 
			
		||||
            if (this._actors.indexOf(actor) != -1)
 | 
			
		||||
                return true;
 | 
			
		||||
            if (actor == currentActor)
 | 
			
		||||
                return true;
 | 
			
		||||
            actor = actor.get_parent();
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
@@ -90,6 +104,14 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        return this._grabStack[this._grabStack.length - 1] || {};
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get grabbed() {
 | 
			
		||||
        return this._grabStack.length > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get grabStack() {
 | 
			
		||||
        return this._grabStack;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findStackIndex: function(actor) {
 | 
			
		||||
        if (!actor)
 | 
			
		||||
            return -1;
 | 
			
		||||
@@ -171,7 +193,6 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
            this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._modalCount++;
 | 
			
		||||
@@ -188,11 +209,6 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
            this._capturedEventId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._eventId > 0) {
 | 
			
		||||
            global.stage.disconnect(this._eventId);
 | 
			
		||||
            this._eventId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Main.popModal(this._owner);
 | 
			
		||||
        global.sync_pointer();
 | 
			
		||||
    },
 | 
			
		||||
@@ -323,27 +339,22 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        if (Main.keyboard.shouldTakeEvent(event))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (button) {
 | 
			
		||||
            // If we have a press event, ignore the next event,
 | 
			
		||||
            // which should be a release event.
 | 
			
		||||
            if (press)
 | 
			
		||||
                this._ignoreRelease = true;
 | 
			
		||||
            this.ungrab({ actor: this._grabStack[0].actor });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._modalCount > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // We catch 'event' rather than 'key-press-event' so that we get
 | 
			
		||||
    // a chance to run before the overview's own Escape check
 | 
			
		||||
    _onEvent: function(actor, event) {
 | 
			
		||||
        if (event.type() == Clutter.EventType.KEY_PRESS &&
 | 
			
		||||
        if (type == Clutter.EventType.KEY_PRESS &&
 | 
			
		||||
            event.get_key_symbol() == Clutter.KEY_Escape) {
 | 
			
		||||
            this.ungrab();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        if (button) {
 | 
			
		||||
            // If we have a press event, ignore the next event,
 | 
			
		||||
            // which should be a release event.
 | 
			
		||||
            if (press)
 | 
			
		||||
                this._ignoreRelease = true;
 | 
			
		||||
            let i = this._actorInGrabStack(event.get_source()) + 1;
 | 
			
		||||
            this.ungrab({ actor: this._grabStack[i].actor });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._modalCount > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyFocusChanged: function() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										136
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								js/ui/layout.js
									
									
									
									
									
								
							@@ -19,6 +19,12 @@ const STARTUP_ANIMATION_TIME = 0.2;
 | 
			
		||||
const KEYBOARD_ANIMATION_TIME = 0.5;
 | 
			
		||||
const PLYMOUTH_TRANSITION_TIME = 1;
 | 
			
		||||
 | 
			
		||||
const MESSAGE_TRAY_PRESSURE_THRESHOLD = 200;
 | 
			
		||||
// The maximium amount that the user is allowed to travel
 | 
			
		||||
// perpendicular to the barrier before we release the accumulated
 | 
			
		||||
// pressure.
 | 
			
		||||
const MESSAGE_TRAY_MAX_SKIRT = 100;
 | 
			
		||||
 | 
			
		||||
const MonitorConstraint = new Lang.Class({
 | 
			
		||||
    Name: 'MonitorConstraint',
 | 
			
		||||
    Extends: Clutter.Constraint,
 | 
			
		||||
@@ -106,9 +112,9 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this._keyboardIndex = -1;
 | 
			
		||||
        this._hotCorners = [];
 | 
			
		||||
        this._background = null;
 | 
			
		||||
        this._leftPanelBarrier = 0;
 | 
			
		||||
        this._rightPanelBarrier = 0;
 | 
			
		||||
        this._trayBarrier = 0;
 | 
			
		||||
        this._leftPanelBarrier = null;
 | 
			
		||||
        this._rightPanelBarrier = null;
 | 
			
		||||
        this._trayBarrier = null;
 | 
			
		||||
 | 
			
		||||
        this._chrome = new Chrome(this);
 | 
			
		||||
 | 
			
		||||
@@ -129,6 +135,8 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this.trayBox = new St.Widget({ name: 'trayBox',
 | 
			
		||||
                                       layout_manager: new Clutter.BinLayout() }); 
 | 
			
		||||
        this.addChrome(this.trayBox);
 | 
			
		||||
        this.trayBox.connect('allocation-changed',
 | 
			
		||||
                             Lang.bind(this, this._updateTrayBarrier));
 | 
			
		||||
 | 
			
		||||
        this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
 | 
			
		||||
                                              reactive: true,
 | 
			
		||||
@@ -257,24 +265,50 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updatePanelBarriers: function() {
 | 
			
		||||
        if (this._leftPanelBarrier)
 | 
			
		||||
            global.destroy_pointer_barrier(this._leftPanelBarrier);
 | 
			
		||||
        if (this._rightPanelBarrier)
 | 
			
		||||
            global.destroy_pointer_barrier(this._rightPanelBarrier);
 | 
			
		||||
        if (this._leftPanelBarrier) {
 | 
			
		||||
            this._leftPanelBarrier.destroy();
 | 
			
		||||
            this._leftPanelBarrier = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._rightPanelBarrier) {
 | 
			
		||||
            this._rightPanelBarrier.destroy();
 | 
			
		||||
            this._rightPanelBarrier = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this.panelBox.height) {
 | 
			
		||||
            let primary = this.primaryMonitor;
 | 
			
		||||
            this._leftPanelBarrier =
 | 
			
		||||
                global.create_pointer_barrier(primary.x, primary.y,
 | 
			
		||||
                                              primary.x, primary.y + this.panelBox.height,
 | 
			
		||||
                                              1 /* BarrierPositiveX */);
 | 
			
		||||
            this._rightPanelBarrier =
 | 
			
		||||
                global.create_pointer_barrier(primary.x + primary.width, primary.y,
 | 
			
		||||
                                              primary.x + primary.width, primary.y + this.panelBox.height,
 | 
			
		||||
                                              4 /* BarrierNegativeX */);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._leftPanelBarrier = 0;
 | 
			
		||||
            this._rightPanelBarrier = 0;
 | 
			
		||||
 | 
			
		||||
            this._leftPanelBarrier  = new Meta.Barrier({ display: global.display,
 | 
			
		||||
                                                         x1: primary.x, y1: primary.y,
 | 
			
		||||
                                                         x2: primary.x, y2: primary.y + this.panelBox.height,
 | 
			
		||||
                                                         directions: Meta.BarrierDirection.POSITIVE_X });
 | 
			
		||||
            this._rightPanelBarrier = new Meta.Barrier({ display: global.display,
 | 
			
		||||
                                                         x1: primary.x + primary.width, y1: primary.y,
 | 
			
		||||
                                                         x2: primary.x + primary.width, y2: primary.y + this.panelBox.height,
 | 
			
		||||
                                                         directions: Meta.BarrierDirection.NEGATIVE_X });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateTrayBarrier: function() {
 | 
			
		||||
        let monitor = this.bottomMonitor;
 | 
			
		||||
 | 
			
		||||
        if (this._trayBarrier) {
 | 
			
		||||
            this._trayBarrier.destroy();
 | 
			
		||||
            this._trayBarrier = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (Main.messageTray) {
 | 
			
		||||
            this._trayBarrier = new Meta.Barrier({ display: global.display,
 | 
			
		||||
                                                   x1: monitor.x, x2: monitor.x + monitor.width,
 | 
			
		||||
                                                   y1: monitor.y + monitor.height - 1, y2: monitor.y + monitor.height - 1,
 | 
			
		||||
                                                   directions: Meta.BarrierDirection.NEGATIVE_Y });
 | 
			
		||||
 | 
			
		||||
            this._trayPressure = new PressureBarrier(this._trayBarrier,
 | 
			
		||||
                                                     MESSAGE_TRAY_PRESSURE_THRESHOLD,
 | 
			
		||||
                                                     MESSAGE_TRAY_MAX_SKIRT);
 | 
			
		||||
            this._trayPressure.connect('trigger', function() {
 | 
			
		||||
                Main.messageTray.openTray();
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1127,5 +1161,69 @@ const Chrome = new Lang.Class({
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
Signals.addSignalMethods(Chrome.prototype);
 | 
			
		||||
 | 
			
		||||
const PressureBarrier = new Lang.Class({
 | 
			
		||||
    Name: 'TrayPressure',
 | 
			
		||||
 | 
			
		||||
    _init: function(barrier, pressureThreshold, perpThreshold) {
 | 
			
		||||
        this._barrier = barrier;
 | 
			
		||||
        this._pressureThreshold = pressureThreshold;
 | 
			
		||||
        this._perpThreshold = perpThreshold;
 | 
			
		||||
        this._getVelocityAndPerp = this._makeGetVelocityAndPerp(barrier);
 | 
			
		||||
 | 
			
		||||
        this._reset(0);
 | 
			
		||||
 | 
			
		||||
        this._barrierHitId = this._barrier.connect('hit', Lang.bind(this, this._onBarrierHit));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this._barrier.disconnect(this._barrierHitId);
 | 
			
		||||
        this._barrier = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reset: function(eventId) {
 | 
			
		||||
        this._currentEventId = eventId;
 | 
			
		||||
        this._currentPressure = 0;
 | 
			
		||||
        this._perpAccumulator = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _makeGetVelocityAndPerp: function(barrier) {
 | 
			
		||||
        if (barrier.y1 === barrier.y2) {
 | 
			
		||||
            return function(event) {
 | 
			
		||||
                return [Math.abs(event.dy), event.dx];
 | 
			
		||||
            };
 | 
			
		||||
        } else {
 | 
			
		||||
            return function(event) {
 | 
			
		||||
                return [Math.abs(event.dx), event.dy];
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onBarrierHit: function(barrier, event) {
 | 
			
		||||
        // Event IDs are incremented every time the user stops
 | 
			
		||||
        // hitting the barrier. So, if the event ID switches,
 | 
			
		||||
        // reset the current state, and start over.
 | 
			
		||||
        if (this._currentEventId != event.event_id) {
 | 
			
		||||
            this._reset(event.event_id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let [velocity, perp] = this._getVelocityAndPerp(event);
 | 
			
		||||
        this._perpAccumulator += perp;
 | 
			
		||||
 | 
			
		||||
        // If the user travels too far in the direction perpendicular
 | 
			
		||||
        // to the barrier, start over from scratch -- the user is simply
 | 
			
		||||
        // trying to skirt along the barrier.
 | 
			
		||||
        if (Math.abs(this._perpAccumulator) >= this._perpThreshold) {
 | 
			
		||||
            this._reset(0);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._currentPressure += velocity;
 | 
			
		||||
        if (this._currentPressure >= this._pressureThreshold) {
 | 
			
		||||
            this.emit('trigger');
 | 
			
		||||
            this._reset(0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(PressureBarrier.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -40,11 +40,6 @@ const LONGER_HIDE_TIMEOUT = 0.6;
 | 
			
		||||
// range from the point where it left the tray.
 | 
			
		||||
const MOUSE_LEFT_ACTOR_THRESHOLD = 20;
 | 
			
		||||
 | 
			
		||||
// Time the user needs to leave the mouse on the bottom pixel row to open the tray
 | 
			
		||||
const TRAY_DWELL_TIME = 1000; // ms
 | 
			
		||||
// Time resolution when tracking the mouse to catch the open tray dwell
 | 
			
		||||
const TRAY_DWELL_CHECK_INTERVAL = 100; // ms
 | 
			
		||||
 | 
			
		||||
const IDLE_TIME = 1000;
 | 
			
		||||
 | 
			
		||||
const State = {
 | 
			
		||||
@@ -1441,7 +1436,6 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
        this._clickedSummaryItem = null;
 | 
			
		||||
        this._clickedSummaryItemMouseButton = -1;
 | 
			
		||||
        this._clickedSummaryItemAllocationChangedId = 0;
 | 
			
		||||
        this._pointerBarrier = 0;
 | 
			
		||||
 | 
			
		||||
        this._closeButton = makeCloseButton();
 | 
			
		||||
        this._closeButton.hide();
 | 
			
		||||
@@ -1537,12 +1531,6 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
        this._summaryItems = [];
 | 
			
		||||
        this._chatSummaryItemsCount = 0;
 | 
			
		||||
 | 
			
		||||
        let pointerWatcher = PointerWatcher.getPointerWatcher();
 | 
			
		||||
        pointerWatcher.addWatch(TRAY_DWELL_CHECK_INTERVAL, Lang.bind(this, this._checkTrayDwell));
 | 
			
		||||
        this._trayDwellTimeoutId = 0;
 | 
			
		||||
        this._trayDwelling = false;
 | 
			
		||||
        this._trayDwellUserTime = 0;
 | 
			
		||||
 | 
			
		||||
        this._sessionUpdated();
 | 
			
		||||
        this._updateNoMessagesLabel();
 | 
			
		||||
    },
 | 
			
		||||
@@ -1572,52 +1560,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
        this._updateState();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _checkTrayDwell: function(x, y) {
 | 
			
		||||
        let monitor = Main.layoutManager.bottomMonitor;
 | 
			
		||||
        let shouldDwell = (x >= monitor.x && x <= monitor.x + monitor.width &&
 | 
			
		||||
                           y == monitor.y + monitor.height - 1);
 | 
			
		||||
        if (shouldDwell) {
 | 
			
		||||
            // We only set up dwell timeout when the user is not hovering over the tray
 | 
			
		||||
            // (!this.actor.hover). This avoids bringing up the message tray after the
 | 
			
		||||
            // user clicks on a notification with the pointer on the bottom pixel
 | 
			
		||||
            // of the monitor. The _trayDwelling variable is used so that we only try to
 | 
			
		||||
            // fire off one tray dwell - if it fails (because, say, the user has the mouse down),
 | 
			
		||||
            // we don't try again until the user moves the mouse up and down again.
 | 
			
		||||
            if (!this._trayDwelling && !this.actor.hover && this._trayDwellTimeoutId == 0) {
 | 
			
		||||
                // Save the interaction timestamp so we can detect user input
 | 
			
		||||
                let focusWindow = global.display.focus_window;
 | 
			
		||||
                this._trayDwellUserTime = focusWindow ? focusWindow.user_time : 0;
 | 
			
		||||
 | 
			
		||||
                this._trayDwellTimeoutId = Mainloop.timeout_add(TRAY_DWELL_TIME,
 | 
			
		||||
                                                                Lang.bind(this, this._trayDwellTimeout));
 | 
			
		||||
            }
 | 
			
		||||
            this._trayDwelling = true;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._cancelTrayDwell();
 | 
			
		||||
            this._trayDwelling = false;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _cancelTrayDwell: function() {
 | 
			
		||||
        if (this._trayDwellTimeoutId != 0) {
 | 
			
		||||
            Mainloop.source_remove(this._trayDwellTimeoutId);
 | 
			
		||||
            this._trayDwellTimeoutId = 0;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _trayDwellTimeout: function() {
 | 
			
		||||
        this._trayDwellTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        if (Main.modalCount > 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        // If the user interacted with the focus window since we started the tray
 | 
			
		||||
        // dwell (by clicking or typing), don't activate the message tray
 | 
			
		||||
        let focusWindow = global.display.focus_window;
 | 
			
		||||
        let currentUserTime = focusWindow ? focusWindow.user_time : 0;
 | 
			
		||||
        if (currentUserTime != this._trayDwellUserTime)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
    openTray: function() {
 | 
			
		||||
        this._traySummoned = true;
 | 
			
		||||
        this._updateState();
 | 
			
		||||
 | 
			
		||||
@@ -1849,9 +1792,6 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onTrayHoverChanged: function() {
 | 
			
		||||
        if (this.actor.hover) {
 | 
			
		||||
            // No dwell inside notifications at the bottom of the screen
 | 
			
		||||
            this._cancelTrayDwell();
 | 
			
		||||
 | 
			
		||||
            // Don't do anything if the one pixel area at the bottom is hovered over while the tray is hidden.
 | 
			
		||||
            if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN)
 | 
			
		||||
                return;
 | 
			
		||||
 
 | 
			
		||||
@@ -46,18 +46,6 @@ const GLSL_DIM_EFFECT_CODE = '\
 | 
			
		||||
   cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \
 | 
			
		||||
   cogl_color_out.a = 1.0;';
 | 
			
		||||
 | 
			
		||||
const SwipeScrollDirection = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
    HORIZONTAL: 1,
 | 
			
		||||
    VERTICAL: 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SwipeScrollResult = {
 | 
			
		||||
    CANCEL: 0,
 | 
			
		||||
    SWIPE: 1,
 | 
			
		||||
    CLICK: 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ShellInfo = new Lang.Class({
 | 
			
		||||
    Name: 'ShellInfo',
 | 
			
		||||
 | 
			
		||||
@@ -165,8 +153,6 @@ const Overview = new Lang.Class({
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._scrollDirection = SwipeScrollDirection.NONE;
 | 
			
		||||
        this._scrollAdjustment = null;
 | 
			
		||||
        this._capturedEventId = 0;
 | 
			
		||||
        this._buttonPressId = 0;
 | 
			
		||||
 | 
			
		||||
@@ -341,159 +327,11 @@ const Overview = new Lang.Class({
 | 
			
		||||
        return DND.DragMotionResult.CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setScrollAdjustment: function(adjustment, direction) {
 | 
			
		||||
    addAction: function(action) {
 | 
			
		||||
        if (this.isDummy)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._scrollAdjustment = adjustment;
 | 
			
		||||
        if (this._scrollAdjustment == null)
 | 
			
		||||
            this._scrollDirection = SwipeScrollDirection.NONE;
 | 
			
		||||
        else
 | 
			
		||||
            this._scrollDirection = direction;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onButtonPress: function(actor, event) {
 | 
			
		||||
        if (this._scrollDirection == SwipeScrollDirection.NONE
 | 
			
		||||
            || event.get_button() != 1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let [stageX, stageY] = event.get_coords();
 | 
			
		||||
        this._dragStartX = this._dragX = stageX;
 | 
			
		||||
        this._dragStartY = this._dragY = stageY;
 | 
			
		||||
        this._dragStartValue = this._scrollAdjustment.value;
 | 
			
		||||
        this._lastMotionTime = -1; // used to track "stopping" while swipe-scrolling
 | 
			
		||||
        this._capturedEventId = global.stage.connect('captured-event',
 | 
			
		||||
            Lang.bind(this, this._onCapturedEvent));
 | 
			
		||||
        this.emit('swipe-scroll-begin');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onCapturedEvent: function(actor, event) {
 | 
			
		||||
        let stageX, stageY;
 | 
			
		||||
        let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
 | 
			
		||||
 | 
			
		||||
        switch(event.type()) {
 | 
			
		||||
            case Clutter.EventType.BUTTON_RELEASE:
 | 
			
		||||
                [stageX, stageY] = event.get_coords();
 | 
			
		||||
 | 
			
		||||
                // default to snapping back to the original value
 | 
			
		||||
                let newValue = this._dragStartValue;
 | 
			
		||||
 | 
			
		||||
                let minValue = this._scrollAdjustment.lower;
 | 
			
		||||
                let maxValue = this._scrollAdjustment.upper - this._scrollAdjustment.page_size;
 | 
			
		||||
 | 
			
		||||
                let direction;
 | 
			
		||||
                if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
 | 
			
		||||
                    direction = stageX > this._dragStartX ? -1 : 1;
 | 
			
		||||
                    if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
                        direction *= -1;
 | 
			
		||||
                } else {
 | 
			
		||||
                    direction = stageY > this._dragStartY ? -1 : 1;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // We default to scroll a full page size; both the first
 | 
			
		||||
                // and the last page may be smaller though, so we need to
 | 
			
		||||
                // adjust difference in those cases.
 | 
			
		||||
                let difference = direction * this._scrollAdjustment.page_size;
 | 
			
		||||
                if (this._dragStartValue + difference > maxValue)
 | 
			
		||||
                    difference = maxValue - this._dragStartValue;
 | 
			
		||||
                else if (this._dragStartValue + difference < minValue)
 | 
			
		||||
                    difference = minValue - this._dragStartValue;
 | 
			
		||||
 | 
			
		||||
                // If the user has moved more than half the scroll
 | 
			
		||||
                // difference, we want to "settle" to the new value
 | 
			
		||||
                // even if the user stops dragging rather "throws" by
 | 
			
		||||
                // releasing during the drag.
 | 
			
		||||
                let distance = this._dragStartValue - this._scrollAdjustment.value;
 | 
			
		||||
                let noStop = Math.abs(distance / difference) > 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 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 (this._dragStartValue + difference >= minValue &&
 | 
			
		||||
                        this._dragStartValue + difference <= maxValue)
 | 
			
		||||
                        newValue += difference;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let result;
 | 
			
		||||
 | 
			
		||||
                // See if the user has moved the mouse enough to trigger
 | 
			
		||||
                // a drag
 | 
			
		||||
                if (Math.abs(stageX - this._dragStartX) < threshold &&
 | 
			
		||||
                    Math.abs(stageY - this._dragStartY) < threshold) {
 | 
			
		||||
                    // no motion? It's a click!
 | 
			
		||||
                    result = SwipeScrollResult.CLICK;
 | 
			
		||||
                    this.emit('swipe-scroll-end', result);
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (newValue == this._dragStartValue)
 | 
			
		||||
                        result = SwipeScrollResult.CANCEL;
 | 
			
		||||
                    else
 | 
			
		||||
                        result = SwipeScrollResult.SWIPE;
 | 
			
		||||
 | 
			
		||||
                    // The event capture handler is disconnected
 | 
			
		||||
                    // while scrolling to the final position, so
 | 
			
		||||
                    // to avoid undesired prelights we raise
 | 
			
		||||
                    // the cover pane.
 | 
			
		||||
                    this._coverPane.raise_top();
 | 
			
		||||
                    this._coverPane.show();
 | 
			
		||||
 | 
			
		||||
                    Tweener.addTween(this._scrollAdjustment,
 | 
			
		||||
                                     { value: newValue,
 | 
			
		||||
                                       time: ANIMATION_TIME,
 | 
			
		||||
                                       transition: 'easeOutQuad',
 | 
			
		||||
                                       onCompleteScope: this,
 | 
			
		||||
                                       onComplete: function() {
 | 
			
		||||
                                          this._coverPane.hide();
 | 
			
		||||
                                          this.emit('swipe-scroll-end',
 | 
			
		||||
                                                    result);
 | 
			
		||||
                                       }
 | 
			
		||||
                                     });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                global.stage.disconnect(this._capturedEventId);
 | 
			
		||||
                this._capturedEventId = 0;
 | 
			
		||||
 | 
			
		||||
                return result != SwipeScrollResult.CLICK;
 | 
			
		||||
 | 
			
		||||
            case Clutter.EventType.MOTION:
 | 
			
		||||
                [stageX, stageY] = event.get_coords();
 | 
			
		||||
                let dx = this._dragX - stageX;
 | 
			
		||||
                let dy = this._dragY - stageY;
 | 
			
		||||
                let primary = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
                this._dragX = stageX;
 | 
			
		||||
                this._dragY = stageY;
 | 
			
		||||
                this._lastMotionTime = event.get_time();
 | 
			
		||||
 | 
			
		||||
                // See if the user has moved the mouse enough to trigger
 | 
			
		||||
                // a drag
 | 
			
		||||
                if (Math.abs(stageX - this._dragStartX) < threshold &&
 | 
			
		||||
                    Math.abs(stageY - this._dragStartY) < threshold)
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
 | 
			
		||||
                    if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
 | 
			
		||||
                        this._scrollAdjustment.value -= (dx / primary.width) * this._scrollAdjustment.page_size;
 | 
			
		||||
                    else
 | 
			
		||||
                        this._scrollAdjustment.value += (dx / primary.width) * this._scrollAdjustment.page_size;
 | 
			
		||||
                } else {
 | 
			
		||||
                    this._scrollAdjustment.value += (dy / primary.height) * this._scrollAdjustment.page_size;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
 | 
			
		||||
            // Block enter/leave events to avoid prelights
 | 
			
		||||
            // during swipe-scroll
 | 
			
		||||
            case Clutter.EventType.ENTER:
 | 
			
		||||
            case Clutter.EventType.LEAVE:
 | 
			
		||||
                return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        this._group.add_action(action);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getDesktopClone: function() {
 | 
			
		||||
@@ -596,9 +434,6 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this._modal = true;
 | 
			
		||||
        this._animateVisible();
 | 
			
		||||
        this._shown = true;
 | 
			
		||||
 | 
			
		||||
        this._buttonPressId = this._group.connect('button-press-event',
 | 
			
		||||
            Lang.bind(this, this._onButtonPress));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    fadeInDesktop: function() {
 | 
			
		||||
@@ -698,10 +533,6 @@ const Overview = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._shown = false;
 | 
			
		||||
        this._syncInputMode();
 | 
			
		||||
 | 
			
		||||
        if (this._buttonPressId > 0)
 | 
			
		||||
            this._group.disconnect(this._buttonPressId);
 | 
			
		||||
        this._buttonPressId = 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // hideTemporarily:
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ const St = imports.gi.St;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const GrabHelper = imports.ui.grabHelper;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
const Tweener = imports.ui.tweener;
 | 
			
		||||
@@ -658,14 +659,23 @@ const PopupSliderMenuItem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function (actor, event) {
 | 
			
		||||
        let direction = event.get_scroll_direction();
 | 
			
		||||
        let delta;
 | 
			
		||||
 | 
			
		||||
        if (event.is_pointer_emulated())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.ScrollDirection.DOWN) {
 | 
			
		||||
            this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP);
 | 
			
		||||
        }
 | 
			
		||||
        else if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
            this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP);
 | 
			
		||||
            delta = -SLIDER_SCROLL_STEP;
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
            delta = +SLIDER_SCROLL_STEP;
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.SMOOTH) {
 | 
			
		||||
            let [dx, dy] = event.get_scroll_delta();
 | 
			
		||||
            // Even though the slider is horizontal, use dy to match
 | 
			
		||||
            // the UP/DOWN above.
 | 
			
		||||
            delta = -dy / 10;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._value = Math.min(Math.max(0, this._value + delta), 1);
 | 
			
		||||
        this._slider.queue_repaint();
 | 
			
		||||
        this.emit('value-changed', this._value);
 | 
			
		||||
    },
 | 
			
		||||
@@ -1204,7 +1214,6 @@ const PopupMenu = new Lang.Class({
 | 
			
		||||
        this.actor = this._boxPointer.actor;
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this.actor.style_class = 'popup-menu-boxpointer';
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
 | 
			
		||||
 | 
			
		||||
        this._boxWrapper = new Shell.GenericContainer();
 | 
			
		||||
        this._boxWrapper.connect('get-preferred-width', Lang.bind(this, this._boxGetPreferredWidth));
 | 
			
		||||
@@ -1234,15 +1243,6 @@ const PopupMenu = new Lang.Class({
 | 
			
		||||
        this.box.allocate(box, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPressEvent: function(actor, event) {
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.Escape) {
 | 
			
		||||
            this.close(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setArrowOrigin: function(origin) {
 | 
			
		||||
        this._boxPointer.setArrowOrigin(origin);
 | 
			
		||||
    },
 | 
			
		||||
@@ -1537,7 +1537,6 @@ const PopupComboMenu = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor = this.box;
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
 | 
			
		||||
        this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
 | 
			
		||||
        sourceActor.connect('style-changed',
 | 
			
		||||
                            Lang.bind(this, this._onSourceActorStyleChanged));
 | 
			
		||||
@@ -1545,15 +1544,6 @@ const PopupComboMenu = new Lang.Class({
 | 
			
		||||
        global.focus_manager.add_group(this.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPressEvent: function(actor, event) {
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.Escape) {
 | 
			
		||||
            this.close(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyFocusIn: function(actor) {
 | 
			
		||||
        let items = this._getMenuItems();
 | 
			
		||||
        let activeItem = items[this._activeItemPos];
 | 
			
		||||
@@ -2044,17 +2034,8 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function(owner) {
 | 
			
		||||
        this._owner = owner;
 | 
			
		||||
        this.grabbed = false;
 | 
			
		||||
 | 
			
		||||
        this._eventCaptureId = 0;
 | 
			
		||||
        this._enterEventId = 0;
 | 
			
		||||
        this._leaveEventId = 0;
 | 
			
		||||
        this._keyFocusNotifyId = 0;
 | 
			
		||||
        this._activeMenu = null;
 | 
			
		||||
        this._grabHelper = new GrabHelper.GrabHelper(owner.actor);
 | 
			
		||||
        this._menus = [];
 | 
			
		||||
        this._menuStack = [];
 | 
			
		||||
        this._preGrabInputMode = null;
 | 
			
		||||
        this._grabbedFromKeynav = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addMenu: function(menu, position) {
 | 
			
		||||
@@ -2073,6 +2054,8 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let source = menu.sourceActor;
 | 
			
		||||
        if (source) {
 | 
			
		||||
            if (!menu.blockSourceEvents)
 | 
			
		||||
                this._grabHelper.addActor(source);
 | 
			
		||||
            menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
 | 
			
		||||
            menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
 | 
			
		||||
        }
 | 
			
		||||
@@ -2085,7 +2068,7 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    removeMenu: function(menu) {
 | 
			
		||||
        if (menu == this._activeMenu)
 | 
			
		||||
            this._closeMenu();
 | 
			
		||||
            this._closeMenu(menu);
 | 
			
		||||
 | 
			
		||||
        let position = this._findMenu(menu);
 | 
			
		||||
        if (position == -1) // not a menu we manage
 | 
			
		||||
@@ -2102,79 +2085,25 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
        if (menudata.focusInId)
 | 
			
		||||
            menu.sourceActor.disconnect(menudata.focusInId);
 | 
			
		||||
 | 
			
		||||
        if (menu.sourceActor)
 | 
			
		||||
            this._grabHelper.removeActor(menu.sourceActor);
 | 
			
		||||
        this._menus.splice(position, 1);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _grab: function() {
 | 
			
		||||
        Main.pushModal(this._owner.actor);
 | 
			
		||||
 | 
			
		||||
        this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
 | 
			
		||||
        // captured-event doesn't see enter/leave events
 | 
			
		||||
        this._enterEventId = global.stage.connect('enter-event', Lang.bind(this, this._onEventCapture));
 | 
			
		||||
        this._leaveEventId = global.stage.connect('leave-event', Lang.bind(this, this._onEventCapture));
 | 
			
		||||
        this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
 | 
			
		||||
 | 
			
		||||
        this.grabbed = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ungrab: function() {
 | 
			
		||||
        global.stage.disconnect(this._eventCaptureId);
 | 
			
		||||
        this._eventCaptureId = 0;
 | 
			
		||||
        global.stage.disconnect(this._enterEventId);
 | 
			
		||||
        this._enterEventId = 0;
 | 
			
		||||
        global.stage.disconnect(this._leaveEventId);
 | 
			
		||||
        this._leaveEventId = 0;
 | 
			
		||||
        global.stage.disconnect(this._keyFocusNotifyId);
 | 
			
		||||
        this._keyFocusNotifyId = 0;
 | 
			
		||||
 | 
			
		||||
        this.grabbed = false;
 | 
			
		||||
        Main.popModal(this._owner.actor);
 | 
			
		||||
    get activeMenu() {
 | 
			
		||||
        let actor = this._grabHelper.currentGrab.actor;
 | 
			
		||||
        if (actor)
 | 
			
		||||
            return actor._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuOpenState: function(menu, open) {
 | 
			
		||||
        if (open) {
 | 
			
		||||
            if (this._activeMenu && this._activeMenu.isChildMenu(menu)) {
 | 
			
		||||
                this._menuStack.push(this._activeMenu);
 | 
			
		||||
                menu.actor.grab_key_focus();
 | 
			
		||||
            }
 | 
			
		||||
            this._activeMenu = menu;
 | 
			
		||||
            this._grabHelper.grab({ actor: menu.actor, modal: true,
 | 
			
		||||
                                    onUngrab: Lang.bind(this, this._closeMenu, menu) });
 | 
			
		||||
        } else {
 | 
			
		||||
            if (this._menuStack.length > 0) {
 | 
			
		||||
                this._activeMenu = this._menuStack.pop();
 | 
			
		||||
                if (menu.sourceActor)
 | 
			
		||||
                    menu.sourceActor.grab_key_focus();
 | 
			
		||||
                this._didPop = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check what the focus was before calling pushModal/popModal
 | 
			
		||||
        let focus = global.stage.key_focus;
 | 
			
		||||
        let hadFocus = focus && this._activeMenuContains(focus);
 | 
			
		||||
 | 
			
		||||
        if (open) {
 | 
			
		||||
            if (!this.grabbed) {
 | 
			
		||||
                this._preGrabInputMode = global.stage_input_mode;
 | 
			
		||||
                this._grabbedFromKeynav = hadFocus;
 | 
			
		||||
                this._grab();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (hadFocus)
 | 
			
		||||
                focus.grab_key_focus();
 | 
			
		||||
            else
 | 
			
		||||
                menu.actor.grab_key_focus();
 | 
			
		||||
        } else if (menu == this._activeMenu) {
 | 
			
		||||
            if (this.grabbed)
 | 
			
		||||
                this._ungrab();
 | 
			
		||||
            this._activeMenu = null;
 | 
			
		||||
 | 
			
		||||
            if (this._grabbedFromKeynav) {
 | 
			
		||||
                if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED)
 | 
			
		||||
                    global.stage_input_mode = Shell.StageInputMode.FOCUSED;
 | 
			
		||||
                if (hadFocus && menu.sourceActor)
 | 
			
		||||
                    menu.sourceActor.grab_key_focus();
 | 
			
		||||
                else if (focus)
 | 
			
		||||
                    focus.grab_key_focus();
 | 
			
		||||
            }
 | 
			
		||||
            this._grabHelper.ungrab({ actor: menu.actor });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -2186,87 +2115,37 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
        this.removeMenu(childMenu);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // change the currently-open menu without dropping grab
 | 
			
		||||
    _changeMenu: function(newMenu) {
 | 
			
		||||
        if (this._activeMenu) {
 | 
			
		||||
            // _onOpenMenuState will drop the grab if it sees
 | 
			
		||||
            // this._activeMenu being closed; so clear _activeMenu
 | 
			
		||||
            // before closing it to keep that from happening
 | 
			
		||||
            let oldMenu = this._activeMenu;
 | 
			
		||||
            this._activeMenu = null;
 | 
			
		||||
            for (let i = this._menuStack.length - 1; i >= 0; i--)
 | 
			
		||||
                this._menuStack[i].close(BoxPointer.PopupAnimation.FADE);
 | 
			
		||||
            oldMenu.close(BoxPointer.PopupAnimation.FADE);
 | 
			
		||||
            newMenu.open(BoxPointer.PopupAnimation.FADE);
 | 
			
		||||
        } else
 | 
			
		||||
            newMenu.open(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
        if (this.activeMenu) {
 | 
			
		||||
            this._closeMenu(this.activeMenu);
 | 
			
		||||
            newMenu.open(false);
 | 
			
		||||
        } else {
 | 
			
		||||
            newMenu.open(true);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuSourceEnter: function(menu) {
 | 
			
		||||
        if (!this.grabbed || menu == this._activeMenu)
 | 
			
		||||
        if (!this._grabHelper.grabbed)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (this._activeMenu && this._activeMenu.isChildMenu(menu))
 | 
			
		||||
        if (this._grabHelper.isActorGrabbed(menu.actor))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (this._menuStack.indexOf(menu) != -1)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu))
 | 
			
		||||
        let isChildMenu = this._grabHelper.grabStack.some(function(grab) {
 | 
			
		||||
            let existingMenu = grab.actor._delegate;
 | 
			
		||||
            return existingMenu.isChildMenu(menu);
 | 
			
		||||
        });
 | 
			
		||||
        if (isChildMenu)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._changeMenu(menu);
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyFocusChanged: function() {
 | 
			
		||||
        if (!this.grabbed || !this._activeMenu)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let focus = global.stage.key_focus;
 | 
			
		||||
        if (focus) {
 | 
			
		||||
            if (this._activeMenuContains(focus))
 | 
			
		||||
                return;
 | 
			
		||||
            if (this._menuStack.length > 0)
 | 
			
		||||
                return;
 | 
			
		||||
            if (focus._delegate && focus._delegate.menu &&
 | 
			
		||||
                this._findMenu(focus._delegate.menu) != -1)
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._closeMenu();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuDestroy: function(menu) {
 | 
			
		||||
        this.removeMenu(menu);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _activeMenuContains: function(actor) {
 | 
			
		||||
        return this._activeMenu != null
 | 
			
		||||
                && (this._activeMenu.actor.contains(actor) ||
 | 
			
		||||
                    (this._activeMenu.sourceActor && this._activeMenu.sourceActor.contains(actor)));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _eventIsOnActiveMenu: function(event) {
 | 
			
		||||
        return this._activeMenuContains(event.get_source());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _shouldBlockEvent: function(event) {
 | 
			
		||||
        let src = event.get_source();
 | 
			
		||||
 | 
			
		||||
        if (this._activeMenu != null && this._activeMenu.actor.contains(src))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._menus.length; i++) {
 | 
			
		||||
            let menu = this._menus[i].menu;
 | 
			
		||||
            if (menu.sourceActor && !menu.blockSourceEvents && menu.sourceActor.contains(src)) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _findMenu: function(item) {
 | 
			
		||||
        for (let i = 0; i < this._menus.length; i++) {
 | 
			
		||||
            let menudata = this._menus[i];
 | 
			
		||||
@@ -2276,41 +2155,7 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
        return -1;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEventCapture: function(actor, event) {
 | 
			
		||||
        if (!this.grabbed)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        if (this._owner.menuEventFilter &&
 | 
			
		||||
            this._owner.menuEventFilter(event))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        if (this._didPop) {
 | 
			
		||||
            this._didPop = false;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let activeMenuContains = this._eventIsOnActiveMenu(event);
 | 
			
		||||
        let eventType = event.type();
 | 
			
		||||
 | 
			
		||||
        if (eventType == Clutter.EventType.BUTTON_RELEASE) {
 | 
			
		||||
            if (activeMenuContains) {
 | 
			
		||||
                return false;
 | 
			
		||||
            } else {
 | 
			
		||||
                this._closeMenu();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (eventType == Clutter.EventType.BUTTON_PRESS && !activeMenuContains) {
 | 
			
		||||
            this._closeMenu();
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (!this._shouldBlockEvent(event)) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _closeMenu: function() {
 | 
			
		||||
        if (this._activeMenu != null)
 | 
			
		||||
            this._activeMenu.close(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
    _closeMenu: function(menu) {
 | 
			
		||||
        menu.close(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -194,15 +194,9 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
                                     expand: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     y_align: St.Align.START });
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                if (!this.actor.mapped)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                let adjustment = scrollView.vscroll.adjustment;
 | 
			
		||||
                let direction = Overview.SwipeScrollDirection.VERTICAL;
 | 
			
		||||
                Main.overview.setScrollAdjustment(adjustment, direction);
 | 
			
		||||
            }));
 | 
			
		||||
        let action = new Clutter.PanAction({ interpolate: true });
 | 
			
		||||
        action.connect('pan', Lang.bind(this, this._onPan));
 | 
			
		||||
        this.actor.add_action(action);
 | 
			
		||||
 | 
			
		||||
        this._statusText = new St.Label({ style_class: 'search-statustext' });
 | 
			
		||||
        this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
 | 
			
		||||
@@ -219,6 +213,13 @@ const SearchResults = new Lang.Class({
 | 
			
		||||
        this._defaultResult = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPan: function(action) {
 | 
			
		||||
        let [dist, dx, dy] = action.get_motion_delta(0);
 | 
			
		||||
        let adjustment = this.actor.vscroll.adjustment;
 | 
			
		||||
        adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createProviderMeta: function(provider) {
 | 
			
		||||
        let providerBox = new St.BoxLayout({ style_class: 'search-section',
 | 
			
		||||
                                             vertical: true });
 | 
			
		||||
 
 | 
			
		||||
@@ -66,24 +66,32 @@ const VolumeMenu = new Lang.Class({
 | 
			
		||||
        this._onControlStateChanged();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    scroll: function(direction) {
 | 
			
		||||
    scroll: function(event) {
 | 
			
		||||
        let direction = event.get_scroll_direction();
 | 
			
		||||
        let currentVolume = this._output.volume;
 | 
			
		||||
        let delta;
 | 
			
		||||
 | 
			
		||||
        if (event.is_pointer_emulated())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.ScrollDirection.DOWN) {
 | 
			
		||||
            let prev_muted = this._output.is_muted;
 | 
			
		||||
            this._output.volume = Math.max(0, currentVolume - this._volumeMax * 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();
 | 
			
		||||
            delta = -VOLUME_ADJUSTMENT_STEP;
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
            delta = +VOLUME_ADJUSTMENT_STEP;
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.SMOOTH) {
 | 
			
		||||
            let [dx, dy] = event.get_scroll_delta();
 | 
			
		||||
            // Use the same math as in the slider.
 | 
			
		||||
            delta = -dy / 10;
 | 
			
		||||
        }
 | 
			
		||||
        else if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
            this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP);
 | 
			
		||||
            this._output.change_is_muted(false);
 | 
			
		||||
            this._output.push_volume();
 | 
			
		||||
 | 
			
		||||
        let prev_muted = this._output.is_muted;
 | 
			
		||||
        this._output.volume = Math.max(0, currentVolume + this._volumeMax * delta);
 | 
			
		||||
        if (this._output.volume < 1) {
 | 
			
		||||
            this._output.volume = 0;
 | 
			
		||||
            if (!prev_muted)
 | 
			
		||||
                this._output.change_is_muted(true);
 | 
			
		||||
        }
 | 
			
		||||
        this._output.push_volume();
 | 
			
		||||
 | 
			
		||||
        this._notifyVolumeChange();
 | 
			
		||||
    },
 | 
			
		||||
@@ -242,6 +250,6 @@ const Indicator = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function(actor, event) {
 | 
			
		||||
        this._volumeMenu.scroll(event.get_scroll_direction());
 | 
			
		||||
        this._volumeMenu.scroll(event);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -211,18 +211,19 @@ const ViewSelector = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if(this._activePage) {
 | 
			
		||||
            let oldPage = this._activePage;
 | 
			
		||||
            Tweener.addTween(this._activePage,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: 0.1,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this,
 | 
			
		||||
                                   function() {
 | 
			
		||||
                                       this._activePage.hide();
 | 
			
		||||
                                       this._activePage = page;
 | 
			
		||||
                                       oldPage.hide();
 | 
			
		||||
                                   })
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._activePage = page;
 | 
			
		||||
        page.show();
 | 
			
		||||
        Tweener.addTween(page,
 | 
			
		||||
                         { opacity: 255,
 | 
			
		||||
 
 | 
			
		||||
@@ -265,23 +265,37 @@ const WindowClone = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onScroll : function (actor, event) {
 | 
			
		||||
        let direction = event.get_scroll_direction();
 | 
			
		||||
        if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
        let delta;
 | 
			
		||||
 | 
			
		||||
        if (event.is_pointer_emulated())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.ScrollDirection.DOWN) {
 | 
			
		||||
            delta = -SCROLL_SCALE_AMOUNT;
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.UP) {
 | 
			
		||||
            delta = +SCROLL_SCALE_AMOUNT;
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.SMOOTH) {
 | 
			
		||||
            let [dx, dy] = event.get_scroll_delta();
 | 
			
		||||
            delta = -dy * 10;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (delta > 0) {
 | 
			
		||||
            if (this._zoomStep == undefined)
 | 
			
		||||
                this._zoomStart();
 | 
			
		||||
            if (this._zoomStep < 100) {
 | 
			
		||||
                this._zoomStep += SCROLL_SCALE_AMOUNT;
 | 
			
		||||
                this._zoomStep += delta;
 | 
			
		||||
                this._zoomStep = Math.min(100, this._zoomStep);
 | 
			
		||||
                this._zoomUpdate();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (direction == Clutter.ScrollDirection.DOWN) {
 | 
			
		||||
        } else if (delta < 0) {
 | 
			
		||||
            if (this._zoomStep > 0) {
 | 
			
		||||
                this._zoomStep -= SCROLL_SCALE_AMOUNT;
 | 
			
		||||
                this._zoomStep += delta;
 | 
			
		||||
                this._zoomStep = Math.max(0, this._zoomStep);
 | 
			
		||||
                this._zoomUpdate();
 | 
			
		||||
            }
 | 
			
		||||
            if (this._zoomStep <= 0.0)
 | 
			
		||||
                this._zoomEnd();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _zoomUpdate : function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -205,11 +205,11 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
            this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _scrollToActive: function(showAnimation) {
 | 
			
		||||
    _scrollToActive: function() {
 | 
			
		||||
        let active = global.screen.get_active_workspace_index();
 | 
			
		||||
 | 
			
		||||
        this._updateWorkspaceActors(showAnimation);
 | 
			
		||||
        this._updateScrollAdjustment(active, showAnimation);
 | 
			
		||||
        this._updateWorkspaceActors(true);
 | 
			
		||||
        this._updateScrollAdjustment(active);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Update workspace actors parameters
 | 
			
		||||
@@ -267,26 +267,21 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateScrollAdjustment: function(index, showAnimation) {
 | 
			
		||||
    _updateScrollAdjustment: function(index) {
 | 
			
		||||
        if (this._scrolling)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._animatingScroll = true;
 | 
			
		||||
 | 
			
		||||
        if (showAnimation) {
 | 
			
		||||
            Tweener.addTween(this.scrollAdjustment, {
 | 
			
		||||
               value: index,
 | 
			
		||||
               time: WORKSPACE_SWITCH_TIME,
 | 
			
		||||
               transition: 'easeOutQuad',
 | 
			
		||||
               onComplete: Lang.bind(this,
 | 
			
		||||
                   function() {
 | 
			
		||||
                       this._animatingScroll = false;
 | 
			
		||||
                   })
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.scrollAdjustment.value = index;
 | 
			
		||||
            this._animatingScroll = false;
 | 
			
		||||
        }
 | 
			
		||||
        Tweener.addTween(this.scrollAdjustment, {
 | 
			
		||||
            value: index,
 | 
			
		||||
            time: WORKSPACE_SWITCH_TIME,
 | 
			
		||||
            transition: 'easeOutQuad',
 | 
			
		||||
            onComplete: Lang.bind(this,
 | 
			
		||||
                                  function() {
 | 
			
		||||
                                      this._animatingScroll = false;
 | 
			
		||||
                                  })
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces) {
 | 
			
		||||
@@ -313,7 +308,7 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
        if (this._scrolling)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._scrollToActive(true);
 | 
			
		||||
        this._scrollToActive();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
@@ -387,24 +382,11 @@ const WorkspacesView = new Lang.Class({
 | 
			
		||||
        this._scrolling = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    endSwipeScroll: function(result) {
 | 
			
		||||
    endSwipeScroll: function() {
 | 
			
		||||
        this._scrolling = false;
 | 
			
		||||
 | 
			
		||||
        if (result == Overview.SwipeScrollResult.CLICK) {
 | 
			
		||||
            let [x, y, mod] = global.get_pointer();
 | 
			
		||||
            let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL,
 | 
			
		||||
                                                      x, y);
 | 
			
		||||
 | 
			
		||||
            // 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.
 | 
			
		||||
            let active = global.screen.get_active_workspace_index();
 | 
			
		||||
            if (this._workspaces[active].isEmpty() &&
 | 
			
		||||
                this.actor.contains(actor))
 | 
			
		||||
                Main.overview.hide();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Make sure title captions etc are shown as necessary
 | 
			
		||||
        this._scrollToActive();
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -456,9 +438,23 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
        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('notify::mapped', Lang.bind(this, this._setupSwipeScrolling));
 | 
			
		||||
        this.actor.connect('parent-set', Lang.bind(this, this._parentSet));
 | 
			
		||||
        this.actor.set_clip_to_allocation(true);
 | 
			
		||||
        let action = new Clutter.PanAction();
 | 
			
		||||
        action.connect('pan', Lang.bind(this, this._onPan));
 | 
			
		||||
        action.connect('gesture-begin', Lang.bind(this, function() {
 | 
			
		||||
            for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
                this._workspacesViews[i].startSwipeScroll();
 | 
			
		||||
            return true;
 | 
			
		||||
        }));
 | 
			
		||||
        action.connect('gesture-end', Lang.bind(this, function() {
 | 
			
		||||
            for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
                this._workspacesViews[i].endSwipeScroll();
 | 
			
		||||
        }));
 | 
			
		||||
        Main.overview.addAction(action);
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this, function() {
 | 
			
		||||
            action.enabled = this.actor.mapped;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        let controls = new St.Bin({ style_class: 'workspace-controls',
 | 
			
		||||
                                    request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT,
 | 
			
		||||
@@ -531,6 +527,13 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
            Lang.bind(this, this._updateSwitcherVisibility));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPan: function(action) {
 | 
			
		||||
        let [dist, dx, dy] = action.get_motion_delta(0);
 | 
			
		||||
        let adjustment = this._scrollAdjustment;
 | 
			
		||||
        adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSwitcherVisibility: function() {
 | 
			
		||||
        this._thumbnailsBox.actor.visible =
 | 
			
		||||
            this._settings.get_boolean('dynamic-workspaces') ||
 | 
			
		||||
@@ -640,33 +643,6 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
            }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setupSwipeScrolling: function() {
 | 
			
		||||
        if (this._swipeScrollBeginId)
 | 
			
		||||
            Main.overview.disconnect(this._swipeScrollBeginId);
 | 
			
		||||
        this._swipeScrollBeginId = 0;
 | 
			
		||||
 | 
			
		||||
        if (this._swipeScrollEndId)
 | 
			
		||||
            Main.overview.disconnect(this._swipeScrollEndId);
 | 
			
		||||
        this._swipeScrollEndId = 0;
 | 
			
		||||
 | 
			
		||||
        if (!this.actor.mapped)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let direction = Overview.SwipeScrollDirection.VERTICAL;
 | 
			
		||||
        Main.overview.setScrollAdjustment(this._scrollAdjustment,
 | 
			
		||||
                                          direction);
 | 
			
		||||
        this._swipeScrollBeginId = Main.overview.connect('swipe-scroll-begin',
 | 
			
		||||
            Lang.bind(this, function() {
 | 
			
		||||
                for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
                    this._workspacesViews[i].startSwipeScroll();
 | 
			
		||||
            }));
 | 
			
		||||
        this._swipeScrollEndId = Main.overview.connect('swipe-scroll-end',
 | 
			
		||||
           Lang.bind(this, function(overview, result) {
 | 
			
		||||
                for (let i = 0; i < this._workspacesViews.length; i++)
 | 
			
		||||
                    this._workspacesViews[i].endSwipeScroll(result);
 | 
			
		||||
           }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _workspacesOnlyOnPrimaryChanged: function() {
 | 
			
		||||
        this._workspacesOnlyOnPrimary = this._settings.get_boolean('workspaces-only-on-primary');
 | 
			
		||||
 | 
			
		||||
@@ -706,7 +682,6 @@ const WorkspacesDisplay = new Lang.Class({
 | 
			
		||||
                this._scrollAdjustment = view.scrollAdjustment;
 | 
			
		||||
                this._scrollAdjustment.connect('notify::value',
 | 
			
		||||
                                               Lang.bind(this, this._scrollValueChanged));
 | 
			
		||||
                this._setupSwipeScrolling();
 | 
			
		||||
            }
 | 
			
		||||
            this._workspacesViews.push(view);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1007,54 +1007,6 @@ shell_global_end_modal (ShellGlobal *global,
 | 
			
		||||
  meta_plugin_end_modal (global->plugin, timestamp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * shell_global_create_pointer_barrier:
 | 
			
		||||
 * @global: a #ShellGlobal
 | 
			
		||||
 * @x1: left X coordinate
 | 
			
		||||
 * @y1: top Y coordinate
 | 
			
		||||
 * @x2: right X coordinate
 | 
			
		||||
 * @y2: bottom Y coordinate
 | 
			
		||||
 * @directions: The directions we're allowed to pass through
 | 
			
		||||
 *
 | 
			
		||||
 * If supported by X creates a pointer barrier.
 | 
			
		||||
 *
 | 
			
		||||
 * Return value: value you can pass to shell_global_destroy_pointer_barrier()
 | 
			
		||||
 */
 | 
			
		||||
guint32
 | 
			
		||||
shell_global_create_pointer_barrier (ShellGlobal *global,
 | 
			
		||||
                                     int x1, int y1, int x2, int y2,
 | 
			
		||||
                                     int directions)
 | 
			
		||||
{
 | 
			
		||||
#if HAVE_XFIXESCREATEPOINTERBARRIER
 | 
			
		||||
  return (guint32)
 | 
			
		||||
    XFixesCreatePointerBarrier (global->xdisplay,
 | 
			
		||||
                                DefaultRootWindow (global->xdisplay),
 | 
			
		||||
                                x1, y1,
 | 
			
		||||
                                x2, y2,
 | 
			
		||||
                                directions,
 | 
			
		||||
                                0, NULL);
 | 
			
		||||
#else
 | 
			
		||||
  return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * shell_global_destroy_pointer_barrier:
 | 
			
		||||
 * @global: a #ShellGlobal
 | 
			
		||||
 * @barrier: a pointer barrier
 | 
			
		||||
 *
 | 
			
		||||
 * Destroys the @barrier created by shell_global_create_pointer_barrier().
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
shell_global_destroy_pointer_barrier (ShellGlobal *global, guint32 barrier)
 | 
			
		||||
{
 | 
			
		||||
#if HAVE_XFIXESCREATEPOINTERBARRIER
 | 
			
		||||
  g_return_if_fail (barrier > 0);
 | 
			
		||||
 | 
			
		||||
  XFixesDestroyPointerBarrier (global->xdisplay, (PointerBarrier)barrier);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Code to close all file descriptors before we exec; copied from gspawn.c in GLib.
 | 
			
		||||
 *
 | 
			
		||||
 * Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering
 | 
			
		||||
 
 | 
			
		||||
@@ -70,15 +70,6 @@ void    shell_global_set_cursor              (ShellGlobal         *global,
 | 
			
		||||
                                              ShellCursor          type);
 | 
			
		||||
void    shell_global_unset_cursor            (ShellGlobal         *global);
 | 
			
		||||
 | 
			
		||||
guint32 shell_global_create_pointer_barrier  (ShellGlobal         *global,
 | 
			
		||||
                                              int                  x1,
 | 
			
		||||
                                              int                  y1,
 | 
			
		||||
                                              int                  x2,
 | 
			
		||||
                                              int                  y2,
 | 
			
		||||
                                              int                  directions);
 | 
			
		||||
void    shell_global_destroy_pointer_barrier (ShellGlobal         *global,
 | 
			
		||||
                                              guint32              barrier);
 | 
			
		||||
 | 
			
		||||
void    shell_global_get_pointer             (ShellGlobal         *global,
 | 
			
		||||
                                              int                 *x,
 | 
			
		||||
                                              int                 *y,
 | 
			
		||||
 
 | 
			
		||||
@@ -665,6 +665,45 @@ st_scroll_view_allocate (ClutterActor          *actor,
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adjust_with_delta (StAdjustment *adj,
 | 
			
		||||
                   gdouble       delta)
 | 
			
		||||
{
 | 
			
		||||
  gdouble new_value, page_size, scroll_unit;
 | 
			
		||||
 | 
			
		||||
  g_object_get (adj,
 | 
			
		||||
                "page-size", &page_size,
 | 
			
		||||
                NULL);
 | 
			
		||||
  scroll_unit = pow (page_size, 2.0 / 3.0);
 | 
			
		||||
 | 
			
		||||
  new_value = st_adjustment_get_value (adj) + delta * scroll_unit;
 | 
			
		||||
  st_adjustment_set_value (adj, new_value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
adjust_with_direction (StAdjustment           *adj,
 | 
			
		||||
                       ClutterScrollDirection  direction)
 | 
			
		||||
{
 | 
			
		||||
  gdouble delta;
 | 
			
		||||
 | 
			
		||||
  switch (direction)
 | 
			
		||||
    {
 | 
			
		||||
    case CLUTTER_SCROLL_UP:
 | 
			
		||||
    case CLUTTER_SCROLL_LEFT:
 | 
			
		||||
      delta = -1.0;
 | 
			
		||||
      break;
 | 
			
		||||
    case CLUTTER_SCROLL_RIGHT:
 | 
			
		||||
    case CLUTTER_SCROLL_DOWN:
 | 
			
		||||
      delta = 1.0;
 | 
			
		||||
      break;
 | 
			
		||||
    case CLUTTER_SCROLL_SMOOTH:
 | 
			
		||||
      g_assert_not_reached ();
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  adjust_with_delta (adj, delta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
st_scroll_view_style_changed (StWidget *widget)
 | 
			
		||||
{
 | 
			
		||||
@@ -687,57 +726,32 @@ st_scroll_view_scroll_event (ClutterActor       *self,
 | 
			
		||||
                             ClutterScrollEvent *event)
 | 
			
		||||
{
 | 
			
		||||
  StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
 | 
			
		||||
  gdouble value, step, hvalue, vvalue, delta_x, delta_y;
 | 
			
		||||
 | 
			
		||||
  /* don't handle scroll events if requested not to */
 | 
			
		||||
  if (!priv->mouse_scroll)
 | 
			
		||||
    return FALSE;
 | 
			
		||||
 | 
			
		||||
  switch (event->direction)
 | 
			
		||||
    {
 | 
			
		||||
    case CLUTTER_SCROLL_SMOOTH:
 | 
			
		||||
      clutter_event_get_scroll_delta ((ClutterEvent *)event,
 | 
			
		||||
                                      &delta_x, &delta_y);
 | 
			
		||||
      g_object_get (priv->hadjustment,
 | 
			
		||||
                    "value", &hvalue,
 | 
			
		||||
                    NULL);
 | 
			
		||||
      g_object_get (priv->vadjustment,
 | 
			
		||||
                    "value", &vvalue,
 | 
			
		||||
                    NULL);
 | 
			
		||||
      break;
 | 
			
		||||
    case CLUTTER_SCROLL_UP:
 | 
			
		||||
    case CLUTTER_SCROLL_DOWN:
 | 
			
		||||
      g_object_get (priv->vadjustment,
 | 
			
		||||
                    "step-increment", &step,
 | 
			
		||||
                    "value", &value,
 | 
			
		||||
                    NULL);
 | 
			
		||||
      break;
 | 
			
		||||
    case CLUTTER_SCROLL_LEFT:
 | 
			
		||||
    case CLUTTER_SCROLL_RIGHT:
 | 
			
		||||
      g_object_get (priv->hadjustment,
 | 
			
		||||
                    "step-increment", &step,
 | 
			
		||||
                    "value", &value,
 | 
			
		||||
                    NULL);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  /* throw away this garbage event. we want smooth scrolling. */
 | 
			
		||||
  if (clutter_event_is_pointer_emulated ((ClutterEvent *) event))
 | 
			
		||||
    return TRUE;
 | 
			
		||||
 | 
			
		||||
  switch (event->direction)
 | 
			
		||||
    {
 | 
			
		||||
    case CLUTTER_SCROLL_SMOOTH:
 | 
			
		||||
      st_adjustment_set_value (priv->hadjustment, hvalue + delta_x);
 | 
			
		||||
      st_adjustment_set_value (priv->vadjustment, vvalue + delta_y);
 | 
			
		||||
      {
 | 
			
		||||
        gdouble delta_x, delta_y;
 | 
			
		||||
        clutter_event_get_scroll_delta ((ClutterEvent *)event, &delta_x, &delta_y);
 | 
			
		||||
        adjust_with_delta (priv->hadjustment, delta_x);
 | 
			
		||||
        adjust_with_delta (priv->vadjustment, delta_y);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case CLUTTER_SCROLL_UP:
 | 
			
		||||
      st_adjustment_set_value (priv->vadjustment, value - step);
 | 
			
		||||
      break;
 | 
			
		||||
    case CLUTTER_SCROLL_DOWN:
 | 
			
		||||
      st_adjustment_set_value (priv->vadjustment, value + step);
 | 
			
		||||
      adjust_with_direction (priv->vadjustment, event->direction);
 | 
			
		||||
      break;
 | 
			
		||||
    case CLUTTER_SCROLL_LEFT:
 | 
			
		||||
      st_adjustment_set_value (priv->hadjustment, value - step);
 | 
			
		||||
      break;
 | 
			
		||||
    case CLUTTER_SCROLL_RIGHT:
 | 
			
		||||
      st_adjustment_set_value (priv->hadjustment, value + step);
 | 
			
		||||
      adjust_with_direction (priv->hadjustment, event->direction);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user