Compare commits

...

20 Commits

Author SHA1 Message Date
Jasper St. Pierre
64bacc6286 popupMenu: Port to GrabHelper
https://bugzilla.gnome.org/show_bug.cgi?id=689109
2012-11-26 15:13:04 -05:00
Jasper St. Pierre
50fec9e1f8 grabHelper: Drop to the actor clicked on
This is necessary for child popups in menus, e.g. while in a combo box,
clicking outside of the user menu should drop the entire menu, but
clicking on the user menu itself should only drop the combo box.
2012-11-26 15:13:04 -05:00
Jasper St. Pierre
b03f7564e3 grabHelper: Treat the current grabbed actor as a grabbed actor
This should be obvious, but I guess it wasn't necessary for the
message tray case.
2012-11-26 15:13:04 -05:00
Jasper St. Pierre
ef9006fa65 grabHelper: Use captured-event for escape ungrabs
I have no idea why we used 'event' rather than 'captured-event' before.
'event' has some really strange quirks that came up when porting PopupMenu
to the GrabHelper
2012-11-26 15:13:04 -05:00
Jasper St. Pierre
485d16ca4e overview: Remove custom swipe scrolling implementation
https://bugzilla.gnome.org/show_bug.cgi?id=689062
2012-11-26 15:13:04 -05:00
Jasper St. Pierre
1e40264ee4 workspacesView: Don't use the overview swipe scrolling system
Switch to a ClutterPanAction instead too.

https://bugzilla.gnome.org/show_bug.cgi?id=689062
2012-11-26 15:13:04 -05:00
Jasper St. Pierre
ae22bde368 workspacesView: Clean up scroll code a bit
There's no code path that results in us not animating.

https://bugzilla.gnome.org/show_bug.cgi?id=689062
2012-11-26 15:13:04 -05:00
Jasper St. Pierre
32502af652 searchDisplay, appDisplay: Use ClutterPanAction for re-swipe scrolling
The sooner we can kill off the custom overview swipe scroll code, the
better.

https://bugzilla.gnome.org/show_bug.cgi?id=689062
2012-11-26 15:13:03 -05:00
Jasper St. Pierre
7d693cbd17 viewSelector: Set the active page immediately
This prevents some focus management issues.

https://bugzilla.gnome.org/show_bug.cgi?id=689062
2012-11-26 15:13:03 -05:00
Jasper St. Pierre
d86e57a8d9 layout: Dummy hack for now because the X server is amazing
dummy demo garbage
2012-11-26 15:12:52 -05:00
Jasper St. Pierre
75985ae0ce messageTray: Remove dwell to open
This is now covered by pointer barriers.
2012-11-26 15:12:52 -05:00
Jasper St. Pierre
7ee897ead5 layout: Don't show the tray if the user is just skirting the barrier
If the user is just pushing their mouse along the barrier's edge, rather
than pushing down onto the barrier, we shouldn't show the tray. Do this
by keeping track of the distance traveled perpendicular to the barrier,
and releasing the accumulated pressure if we pass a threshold.
2012-11-26 15:12:52 -05:00
Jasper St. Pierre
7c371392da layout: Trigger the message tray by downward pressure
https://bugzilla.gnome.org/show_bug.cgi?id=677215
2012-11-26 15:12:52 -05:00
Jasper St. Pierre
ab549f763d layout: Port to the new mutter-based barrier wrappers
... and remove our old ones.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
2012-11-26 15:12:52 -05:00
Jasper St. Pierre
ea25331a55 messageTray: Remove an unused instance variable 2012-11-26 15:12:52 -05:00
Jasper St. Pierre
c6bc1526fa workspace: Add smooth scrolling support to zoom windows
https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-11-26 15:12:43 -05:00
Jasper St. Pierre
871ae3f9b2 volume: Add smooth scrolling support to adjust output volume
https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-11-26 15:12:43 -05:00
Jasper St. Pierre
8ebbf442cd popupMenu: Add smooth scrolling support for sliders
Allowing smooth scrolling on the Y axis to accurately adjust the value
of the slider.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-11-26 15:12:43 -05:00
Jasper St. Pierre
b936f094d1 st-scroll-view: Throw away emulated pointer events
These are sent by the X server and have large deltas. They really
should be filtered out by Clutter (or the X server) somehow, but we
don't have the means to do that yet.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-11-26 15:12:43 -05:00
Jasper St. Pierre
165265f4fb st-scroll-view: Add proper smooth scrolling
The code here before was added as dummy code to satisfy an error
in the missing switch, and wasn't ever tested due to the lack of XI2
in mutter. Use the same math as GtkRange does to calculate scroll bar
positions from raw XI2 deltas to allow for proper smooth scrolling.

https://bugzilla.gnome.org/show_bug.cgi?id=687573
2012-11-26 15:12:42 -05:00
15 changed files with 349 additions and 678 deletions

View File

@ -114,14 +114,6 @@ AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION]) 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(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(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
PKG_CHECK_MODULES(TRAY, gtk+-3.0) PKG_CHECK_MODULES(TRAY, gtk+-3.0)

View File

@ -48,18 +48,16 @@ const AlphabeticalView = new Lang.Class({
style_class: 'vfade' }); style_class: 'vfade' });
this.actor.add_actor(box); this.actor.add_actor(box);
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.actor.connect('notify::mapped', Lang.bind(this, let action = new Clutter.PanAction({ interpolate: true });
function() { action.connect('pan', Lang.bind(this, this._onPan));
if (!this.actor.mapped) this.actor.add_action(action);
return; },
_onPan: function(action) {
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this.actor.vscroll.adjustment; let adjustment = this.actor.vscroll.adjustment;
let direction = Overview.SwipeScrollDirection.VERTICAL; adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
Main.overview.setScrollAdjustment(adjustment, direction); return false;
// Reset scroll on mapping
adjustment.value = 0;
}));
}, },
removeAll: function() { removeAll: function() {

View File

@ -43,7 +43,6 @@ const GrabHelper = new Lang.Class({
this._actors = []; this._actors = [];
this._capturedEventId = 0; this._capturedEventId = 0;
this._eventId = 0;
this._keyFocusNotifyId = 0; this._keyFocusNotifyId = 0;
this._focusWindowChangedId = 0; this._focusWindowChangedId = 0;
this._ignoreRelease = false; 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) { _isWithinGrabbedActor: function(actor) {
let currentActor = this.currentGrab.actor;
while (actor) { while (actor) {
if (this._actors.indexOf(actor) != -1) if (this._actors.indexOf(actor) != -1)
return true; return true;
if (actor == currentActor)
return true;
actor = actor.get_parent(); actor = actor.get_parent();
} }
return false; return false;
@ -90,6 +104,14 @@ const GrabHelper = new Lang.Class({
return this._grabStack[this._grabStack.length - 1] || {}; return this._grabStack[this._grabStack.length - 1] || {};
}, },
get grabbed() {
return this._grabStack.length > 0;
},
get grabStack() {
return this._grabStack;
},
_findStackIndex: function(actor) { _findStackIndex: function(actor) {
if (!actor) if (!actor)
return -1; return -1;
@ -171,7 +193,6 @@ const GrabHelper = new Lang.Class({
return false; return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent)); 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++; this._modalCount++;
@ -188,11 +209,6 @@ const GrabHelper = new Lang.Class({
this._capturedEventId = 0; this._capturedEventId = 0;
} }
if (this._eventId > 0) {
global.stage.disconnect(this._eventId);
this._eventId = 0;
}
Main.popModal(this._owner); Main.popModal(this._owner);
global.sync_pointer(); global.sync_pointer();
}, },
@ -323,27 +339,22 @@ const GrabHelper = new Lang.Class({
if (Main.keyboard.shouldTakeEvent(event)) if (Main.keyboard.shouldTakeEvent(event))
return false; return false;
if (button) { if (type == Clutter.EventType.KEY_PRESS &&
// 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 &&
event.get_key_symbol() == Clutter.KEY_Escape) { event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab(); this.ungrab();
return true; 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() { _onKeyFocusChanged: function() {

View File

@ -19,6 +19,12 @@ const STARTUP_ANIMATION_TIME = 0.2;
const KEYBOARD_ANIMATION_TIME = 0.5; const KEYBOARD_ANIMATION_TIME = 0.5;
const PLYMOUTH_TRANSITION_TIME = 1; 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({ const MonitorConstraint = new Lang.Class({
Name: 'MonitorConstraint', Name: 'MonitorConstraint',
Extends: Clutter.Constraint, Extends: Clutter.Constraint,
@ -106,9 +112,9 @@ const LayoutManager = new Lang.Class({
this._keyboardIndex = -1; this._keyboardIndex = -1;
this._hotCorners = []; this._hotCorners = [];
this._background = null; this._background = null;
this._leftPanelBarrier = 0; this._leftPanelBarrier = null;
this._rightPanelBarrier = 0; this._rightPanelBarrier = null;
this._trayBarrier = 0; this._trayBarrier = null;
this._chrome = new Chrome(this); this._chrome = new Chrome(this);
@ -129,6 +135,8 @@ const LayoutManager = new Lang.Class({
this.trayBox = new St.Widget({ name: 'trayBox', this.trayBox = new St.Widget({ name: 'trayBox',
layout_manager: new Clutter.BinLayout() }); layout_manager: new Clutter.BinLayout() });
this.addChrome(this.trayBox); this.addChrome(this.trayBox);
this.trayBox.connect('allocation-changed',
Lang.bind(this, this._updateTrayBarrier));
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox', this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true, reactive: true,
@ -257,24 +265,50 @@ const LayoutManager = new Lang.Class({
}, },
_updatePanelBarriers: function() { _updatePanelBarriers: function() {
if (this._leftPanelBarrier) if (this._leftPanelBarrier) {
global.destroy_pointer_barrier(this._leftPanelBarrier); this._leftPanelBarrier.destroy();
if (this._rightPanelBarrier) this._leftPanelBarrier = null;
global.destroy_pointer_barrier(this._rightPanelBarrier); }
if (this._rightPanelBarrier) {
this._rightPanelBarrier.destroy();
this._rightPanelBarrier = null;
}
if (this.panelBox.height) { if (this.panelBox.height) {
let primary = this.primaryMonitor; let primary = this.primaryMonitor;
this._leftPanelBarrier =
global.create_pointer_barrier(primary.x, primary.y, this._leftPanelBarrier = new Meta.Barrier({ display: global.display,
primary.x, primary.y + this.panelBox.height, x1: primary.x, y1: primary.y,
1 /* BarrierPositiveX */); x2: primary.x, y2: primary.y + this.panelBox.height,
this._rightPanelBarrier = directions: Meta.BarrierDirection.POSITIVE_X });
global.create_pointer_barrier(primary.x + primary.width, primary.y, this._rightPanelBarrier = new Meta.Barrier({ display: global.display,
primary.x + primary.width, primary.y + this.panelBox.height, x1: primary.x + primary.width, y1: primary.y,
4 /* BarrierNegativeX */); x2: primary.x + primary.width, y2: primary.y + this.panelBox.height,
} else { directions: Meta.BarrierDirection.NEGATIVE_X });
this._leftPanelBarrier = 0; }
this._rightPanelBarrier = 0; },
_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; return false;
} }
}); });
Signals.addSignalMethods(Chrome.prototype); 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);

View File

@ -40,11 +40,6 @@ const LONGER_HIDE_TIMEOUT = 0.6;
// range from the point where it left the tray. // range from the point where it left the tray.
const MOUSE_LEFT_ACTOR_THRESHOLD = 20; 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 IDLE_TIME = 1000;
const State = { const State = {
@ -1441,7 +1436,6 @@ const MessageTray = new Lang.Class({
this._clickedSummaryItem = null; this._clickedSummaryItem = null;
this._clickedSummaryItemMouseButton = -1; this._clickedSummaryItemMouseButton = -1;
this._clickedSummaryItemAllocationChangedId = 0; this._clickedSummaryItemAllocationChangedId = 0;
this._pointerBarrier = 0;
this._closeButton = makeCloseButton(); this._closeButton = makeCloseButton();
this._closeButton.hide(); this._closeButton.hide();
@ -1537,12 +1531,6 @@ const MessageTray = new Lang.Class({
this._summaryItems = []; this._summaryItems = [];
this._chatSummaryItemsCount = 0; 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._sessionUpdated();
this._updateNoMessagesLabel(); this._updateNoMessagesLabel();
}, },
@ -1572,52 +1560,7 @@ const MessageTray = new Lang.Class({
this._updateState(); this._updateState();
}, },
_checkTrayDwell: function(x, y) { openTray: function() {
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;
this._traySummoned = true; this._traySummoned = true;
this._updateState(); this._updateState();
@ -1849,9 +1792,6 @@ const MessageTray = new Lang.Class({
_onTrayHoverChanged: function() { _onTrayHoverChanged: function() {
if (this.actor.hover) { 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. // 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) if (this._trayState == State.HIDDEN && this._notificationState == State.HIDDEN)
return; return;

View File

@ -46,18 +46,6 @@ const GLSL_DIM_EFFECT_CODE = '\
cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \ cogl_color_out.xyz = cogl_color_out.xyz * (1.0 - a); \
cogl_color_out.a = 1.0;'; 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({ const ShellInfo = new Lang.Class({
Name: 'ShellInfo', Name: 'ShellInfo',
@ -165,8 +153,6 @@ const Overview = new Lang.Class({
} }
})); }));
this._scrollDirection = SwipeScrollDirection.NONE;
this._scrollAdjustment = null;
this._capturedEventId = 0; this._capturedEventId = 0;
this._buttonPressId = 0; this._buttonPressId = 0;
@ -341,159 +327,11 @@ const Overview = new Lang.Class({
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
}, },
setScrollAdjustment: function(adjustment, direction) { addAction: function(action) {
if (this.isDummy) if (this.isDummy)
return; return;
this._scrollAdjustment = adjustment; this._group.add_action(action);
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;
}, },
_getDesktopClone: function() { _getDesktopClone: function() {
@ -596,9 +434,6 @@ const Overview = new Lang.Class({
this._modal = true; this._modal = true;
this._animateVisible(); this._animateVisible();
this._shown = true; this._shown = true;
this._buttonPressId = this._group.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
}, },
fadeInDesktop: function() { fadeInDesktop: function() {
@ -698,10 +533,6 @@ const Overview = new Lang.Class({
this._shown = false; this._shown = false;
this._syncInputMode(); this._syncInputMode();
if (this._buttonPressId > 0)
this._group.disconnect(this._buttonPressId);
this._buttonPressId = 0;
}, },
// hideTemporarily: // hideTemporarily:

View File

@ -12,6 +12,7 @@ const St = imports.gi.St;
const Atk = imports.gi.Atk; const Atk = imports.gi.Atk;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const GrabHelper = imports.ui.grabHelper;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
@ -658,14 +659,23 @@ const PopupSliderMenuItem = new Lang.Class({
_onScrollEvent: function (actor, event) { _onScrollEvent: function (actor, event) {
let direction = event.get_scroll_direction(); let direction = event.get_scroll_direction();
let delta;
if (event.is_pointer_emulated())
return;
if (direction == Clutter.ScrollDirection.DOWN) { if (direction == Clutter.ScrollDirection.DOWN) {
this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP); delta = -SLIDER_SCROLL_STEP;
} } else if (direction == Clutter.ScrollDirection.UP) {
else if (direction == Clutter.ScrollDirection.UP) { delta = +SLIDER_SCROLL_STEP;
this._value = Math.min(1, this._value + 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._slider.queue_repaint();
this.emit('value-changed', this._value); this.emit('value-changed', this._value);
}, },
@ -1204,7 +1214,6 @@ const PopupMenu = new Lang.Class({
this.actor = this._boxPointer.actor; this.actor = this._boxPointer.actor;
this.actor._delegate = this; this.actor._delegate = this;
this.actor.style_class = 'popup-menu-boxpointer'; 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 = new Shell.GenericContainer();
this._boxWrapper.connect('get-preferred-width', Lang.bind(this, this._boxGetPreferredWidth)); 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); 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) { setArrowOrigin: function(origin) {
this._boxPointer.setArrowOrigin(origin); this._boxPointer.setArrowOrigin(origin);
}, },
@ -1537,7 +1537,6 @@ const PopupComboMenu = new Lang.Class({
this.actor = this.box; this.actor = this.box;
this.actor._delegate = this; 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)); this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
sourceActor.connect('style-changed', sourceActor.connect('style-changed',
Lang.bind(this, this._onSourceActorStyleChanged)); Lang.bind(this, this._onSourceActorStyleChanged));
@ -1545,15 +1544,6 @@ const PopupComboMenu = new Lang.Class({
global.focus_manager.add_group(this.actor); 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) { _onKeyFocusIn: function(actor) {
let items = this._getMenuItems(); let items = this._getMenuItems();
let activeItem = items[this._activeItemPos]; let activeItem = items[this._activeItemPos];
@ -2044,17 +2034,8 @@ const PopupMenuManager = new Lang.Class({
_init: function(owner) { _init: function(owner) {
this._owner = owner; this._owner = owner;
this.grabbed = false; this._grabHelper = new GrabHelper.GrabHelper(owner.actor);
this._eventCaptureId = 0;
this._enterEventId = 0;
this._leaveEventId = 0;
this._keyFocusNotifyId = 0;
this._activeMenu = null;
this._menus = []; this._menus = [];
this._menuStack = [];
this._preGrabInputMode = null;
this._grabbedFromKeynav = false;
}, },
addMenu: function(menu, position) { addMenu: function(menu, position) {
@ -2073,6 +2054,8 @@ const PopupMenuManager = new Lang.Class({
let source = menu.sourceActor; let source = menu.sourceActor;
if (source) { if (source) {
if (!menu.blockSourceEvents)
this._grabHelper.addActor(source);
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); })); 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); })); 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) { removeMenu: function(menu) {
if (menu == this._activeMenu) if (menu == this._activeMenu)
this._closeMenu(); this._closeMenu(menu);
let position = this._findMenu(menu); let position = this._findMenu(menu);
if (position == -1) // not a menu we manage if (position == -1) // not a menu we manage
@ -2102,79 +2085,25 @@ const PopupMenuManager = new Lang.Class({
if (menudata.focusInId) if (menudata.focusInId)
menu.sourceActor.disconnect(menudata.focusInId); menu.sourceActor.disconnect(menudata.focusInId);
if (menu.sourceActor)
this._grabHelper.removeActor(menu.sourceActor);
this._menus.splice(position, 1); this._menus.splice(position, 1);
}, },
_grab: function() { get activeMenu() {
Main.pushModal(this._owner.actor); let actor = this._grabHelper.currentGrab.actor;
if (actor)
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture)); return actor._delegate;
// captured-event doesn't see enter/leave events else
this._enterEventId = global.stage.connect('enter-event', Lang.bind(this, this._onEventCapture)); return null;
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);
}, },
_onMenuOpenState: function(menu, open) { _onMenuOpenState: function(menu, open) {
if (open) { if (open) {
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) { this._grabHelper.grab({ actor: menu.actor, modal: true,
this._menuStack.push(this._activeMenu); onUngrab: Lang.bind(this, this._closeMenu, menu) });
menu.actor.grab_key_focus();
}
this._activeMenu = menu;
} else { } else {
if (this._menuStack.length > 0) { this._grabHelper.ungrab({ actor: menu.actor });
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();
}
} }
}, },
@ -2186,87 +2115,37 @@ const PopupMenuManager = new Lang.Class({
this.removeMenu(childMenu); this.removeMenu(childMenu);
}, },
// change the currently-open menu without dropping grab
_changeMenu: function(newMenu) { _changeMenu: function(newMenu) {
if (this._activeMenu) { if (this.activeMenu) {
// _onOpenMenuState will drop the grab if it sees this._closeMenu(this.activeMenu);
// this._activeMenu being closed; so clear _activeMenu newMenu.open(false);
// before closing it to keep that from happening } else {
let oldMenu = this._activeMenu; newMenu.open(true);
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);
}, },
_onMenuSourceEnter: function(menu) { _onMenuSourceEnter: function(menu) {
if (!this.grabbed || menu == this._activeMenu) if (!this._grabHelper.grabbed)
return false; return false;
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) if (this._grabHelper.isActorGrabbed(menu.actor))
return false; return false;
if (this._menuStack.indexOf(menu) != -1) let isChildMenu = this._grabHelper.grabStack.some(function(grab) {
return false; let existingMenu = grab.actor._delegate;
return existingMenu.isChildMenu(menu);
if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu)) });
if (isChildMenu)
return false; return false;
this._changeMenu(menu); this._changeMenu(menu);
return false; 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) { _onMenuDestroy: function(menu) {
this.removeMenu(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) { _findMenu: function(item) {
for (let i = 0; i < this._menus.length; i++) { for (let i = 0; i < this._menus.length; i++) {
let menudata = this._menus[i]; let menudata = this._menus[i];
@ -2276,41 +2155,7 @@ const PopupMenuManager = new Lang.Class({
return -1; return -1;
}, },
_onEventCapture: function(actor, event) { _closeMenu: function(menu) {
if (!this.grabbed) menu.close(BoxPointer.PopupAnimation.FULL);
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);
} }
}); });

View File

@ -194,15 +194,9 @@ const SearchResults = new Lang.Class({
expand: true, expand: true,
x_align: St.Align.START, x_align: St.Align.START,
y_align: St.Align.START }); y_align: St.Align.START });
this.actor.connect('notify::mapped', Lang.bind(this, let action = new Clutter.PanAction({ interpolate: true });
function() { action.connect('pan', Lang.bind(this, this._onPan));
if (!this.actor.mapped) this.actor.add_action(action);
return;
let adjustment = scrollView.vscroll.adjustment;
let direction = Overview.SwipeScrollDirection.VERTICAL;
Main.overview.setScrollAdjustment(adjustment, direction);
}));
this._statusText = new St.Label({ style_class: 'search-statustext' }); this._statusText = new St.Label({ style_class: 'search-statustext' });
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE, this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
@ -219,6 +213,13 @@ const SearchResults = new Lang.Class({
this._defaultResult = null; 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) { createProviderMeta: function(provider) {
let providerBox = new St.BoxLayout({ style_class: 'search-section', let providerBox = new St.BoxLayout({ style_class: 'search-section',
vertical: true }); vertical: true });

View File

@ -66,24 +66,32 @@ const VolumeMenu = new Lang.Class({
this._onControlStateChanged(); this._onControlStateChanged();
}, },
scroll: function(direction) { scroll: function(event) {
let direction = event.get_scroll_direction();
let currentVolume = this._output.volume; let currentVolume = this._output.volume;
let delta;
if (event.is_pointer_emulated())
return;
if (direction == Clutter.ScrollDirection.DOWN) { if (direction == Clutter.ScrollDirection.DOWN) {
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;
}
let prev_muted = this._output.is_muted; let prev_muted = this._output.is_muted;
this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP); this._output.volume = Math.max(0, currentVolume + this._volumeMax * delta);
if (this._output.volume < 1) { if (this._output.volume < 1) {
this._output.volume = 0; this._output.volume = 0;
if (!prev_muted) if (!prev_muted)
this._output.change_is_muted(true); this._output.change_is_muted(true);
} }
this._output.push_volume(); this._output.push_volume();
}
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();
}
this._notifyVolumeChange(); this._notifyVolumeChange();
}, },
@ -242,6 +250,6 @@ const Indicator = new Lang.Class({
}, },
_onScrollEvent: function(actor, event) { _onScrollEvent: function(actor, event) {
this._volumeMenu.scroll(event.get_scroll_direction()); this._volumeMenu.scroll(event);
} }
}); });

View File

@ -211,18 +211,19 @@ const ViewSelector = new Lang.Class({
return; return;
if(this._activePage) { if(this._activePage) {
let oldPage = this._activePage;
Tweener.addTween(this._activePage, Tweener.addTween(this._activePage,
{ opacity: 0, { opacity: 0,
time: 0.1, time: 0.1,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, onComplete: Lang.bind(this,
function() { function() {
this._activePage.hide(); oldPage.hide();
this._activePage = page;
}) })
}); });
} }
this._activePage = page;
page.show(); page.show();
Tweener.addTween(page, Tweener.addTween(page,
{ opacity: 255, { opacity: 255,

View File

@ -265,23 +265,37 @@ const WindowClone = new Lang.Class({
_onScroll : function (actor, event) { _onScroll : function (actor, event) {
let direction = event.get_scroll_direction(); 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) if (this._zoomStep == undefined)
this._zoomStart(); this._zoomStart();
if (this._zoomStep < 100) { if (this._zoomStep < 100) {
this._zoomStep += SCROLL_SCALE_AMOUNT; this._zoomStep += delta;
this._zoomStep = Math.min(100, this._zoomStep);
this._zoomUpdate(); this._zoomUpdate();
} }
} else if (direction == Clutter.ScrollDirection.DOWN) { } else if (delta < 0) {
if (this._zoomStep > 0) { if (this._zoomStep > 0) {
this._zoomStep -= SCROLL_SCALE_AMOUNT; this._zoomStep += delta;
this._zoomStep = Math.max(0, this._zoomStep); this._zoomStep = Math.max(0, this._zoomStep);
this._zoomUpdate(); this._zoomUpdate();
} }
if (this._zoomStep <= 0.0) if (this._zoomStep <= 0.0)
this._zoomEnd(); this._zoomEnd();
} }
}, },
_zoomUpdate : function () { _zoomUpdate : function () {

View File

@ -205,11 +205,11 @@ const WorkspacesView = new Lang.Class({
this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE); this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE);
}, },
_scrollToActive: function(showAnimation) { _scrollToActive: function() {
let active = global.screen.get_active_workspace_index(); let active = global.screen.get_active_workspace_index();
this._updateWorkspaceActors(showAnimation); this._updateWorkspaceActors(true);
this._updateScrollAdjustment(active, showAnimation); this._updateScrollAdjustment(active);
}, },
// Update workspace actors parameters // Update workspace actors parameters
@ -267,13 +267,12 @@ const WorkspacesView = new Lang.Class({
} }
}, },
_updateScrollAdjustment: function(index, showAnimation) { _updateScrollAdjustment: function(index) {
if (this._scrolling) if (this._scrolling)
return; return;
this._animatingScroll = true; this._animatingScroll = true;
if (showAnimation) {
Tweener.addTween(this.scrollAdjustment, { Tweener.addTween(this.scrollAdjustment, {
value: index, value: index,
time: WORKSPACE_SWITCH_TIME, time: WORKSPACE_SWITCH_TIME,
@ -283,10 +282,6 @@ const WorkspacesView = new Lang.Class({
this._animatingScroll = false; this._animatingScroll = false;
}) })
}); });
} else {
this.scrollAdjustment.value = index;
this._animatingScroll = false;
}
}, },
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces) { updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces) {
@ -313,7 +308,7 @@ const WorkspacesView = new Lang.Class({
if (this._scrolling) if (this._scrolling)
return; return;
this._scrollToActive(true); this._scrollToActive();
}, },
_onDestroy: function() { _onDestroy: function() {
@ -387,24 +382,11 @@ const WorkspacesView = new Lang.Class({
this._scrolling = true; this._scrolling = true;
}, },
endSwipeScroll: function(result) { endSwipeScroll: function() {
this._scrolling = false; 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 // Make sure title captions etc are shown as necessary
this._scrollToActive();
this._updateVisibility(); 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-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate)); 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.connect('parent-set', Lang.bind(this, this._parentSet));
this.actor.set_clip_to_allocation(true); 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', let controls = new St.Bin({ style_class: 'workspace-controls',
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT, request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT,
@ -531,6 +527,13 @@ const WorkspacesDisplay = new Lang.Class({
Lang.bind(this, this._updateSwitcherVisibility)); 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() { _updateSwitcherVisibility: function() {
this._thumbnailsBox.actor.visible = this._thumbnailsBox.actor.visible =
this._settings.get_boolean('dynamic-workspaces') || 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() { _workspacesOnlyOnPrimaryChanged: function() {
this._workspacesOnlyOnPrimary = this._settings.get_boolean('workspaces-only-on-primary'); 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 = view.scrollAdjustment;
this._scrollAdjustment.connect('notify::value', this._scrollAdjustment.connect('notify::value',
Lang.bind(this, this._scrollValueChanged)); Lang.bind(this, this._scrollValueChanged));
this._setupSwipeScrolling();
} }
this._workspacesViews.push(view); this._workspacesViews.push(view);
} }

View File

@ -1007,54 +1007,6 @@ shell_global_end_modal (ShellGlobal *global,
meta_plugin_end_modal (global->plugin, timestamp); 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. /* Code to close all file descriptors before we exec; copied from gspawn.c in GLib.
* *
* Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering * Authors: Padraig O'Briain, Matthias Clasen, Lennart Poettering

View File

@ -70,15 +70,6 @@ void shell_global_set_cursor (ShellGlobal *global,
ShellCursor type); ShellCursor type);
void shell_global_unset_cursor (ShellGlobal *global); 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, void shell_global_get_pointer (ShellGlobal *global,
int *x, int *x,
int *y, int *y,

View File

@ -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 static void
st_scroll_view_style_changed (StWidget *widget) st_scroll_view_style_changed (StWidget *widget)
{ {
@ -687,57 +726,32 @@ st_scroll_view_scroll_event (ClutterActor *self,
ClutterScrollEvent *event) ClutterScrollEvent *event)
{ {
StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv; 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 */ /* don't handle scroll events if requested not to */
if (!priv->mouse_scroll) if (!priv->mouse_scroll)
return FALSE; return FALSE;
switch (event->direction) /* throw away this garbage event. we want smooth scrolling. */
{ if (clutter_event_is_pointer_emulated ((ClutterEvent *) event))
case CLUTTER_SCROLL_SMOOTH: return TRUE;
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;
}
switch (event->direction) switch (event->direction)
{ {
case CLUTTER_SCROLL_SMOOTH: 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; break;
case CLUTTER_SCROLL_UP: case CLUTTER_SCROLL_UP:
st_adjustment_set_value (priv->vadjustment, value - step);
break;
case CLUTTER_SCROLL_DOWN: case CLUTTER_SCROLL_DOWN:
st_adjustment_set_value (priv->vadjustment, value + step); adjust_with_direction (priv->vadjustment, event->direction);
break; break;
case CLUTTER_SCROLL_LEFT: case CLUTTER_SCROLL_LEFT:
st_adjustment_set_value (priv->hadjustment, value - step);
break;
case CLUTTER_SCROLL_RIGHT: case CLUTTER_SCROLL_RIGHT:
st_adjustment_set_value (priv->hadjustment, value + step); adjust_with_direction (priv->hadjustment, event->direction);
break; break;
} }