Compare commits
	
		
			20 Commits
		
	
	
		
			3.10.2.1
			...
			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