diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js index 93a381056808cf7c1241982ec068ddb291e0c3f3..5300c4318db1215c0412643598213ee756c1ef26 100644 --- a/js/gdm/authPrompt.js +++ b/js/gdm/authPrompt.js @@ -6,6 +6,7 @@ const { Clutter, GObject, Pango, Shell, St } = imports.gi; const Animation = imports.ui.animation; const Batch = imports.gdm.batch; const GdmUtil = imports.gdm.util; +const Util = imports.misc.util; const Params = imports.misc.params; const ShellEntry = imports.ui.shellEntry; const UserWidget = imports.ui.userWidget; @@ -16,6 +17,10 @@ var DEFAULT_BUTTON_WELL_ANIMATION_TIME = 300; var MESSAGE_FADE_OUT_ANIMATION_TIME = 500; +const WIGGLE_OFFSET = 6; +const WIGGLE_DURATION = 65; +const N_WIGGLES = 3; + var AuthPromptMode = { UNLOCK_ONLY: 0, UNLOCK_OR_LOG_IN: 1 @@ -256,6 +261,12 @@ var AuthPrompt = GObject.registerClass({ this.updateSensitivity(canRetry); this.setActorInDefaultButtonWell(null); this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED; + + Util.wiggle(this._entry, { + offset: WIGGLE_OFFSET, + duration: WIGGLE_DURATION, + wiggleCount: N_WIGGLES, + }); } _onVerificationComplete() { diff --git a/js/misc/util.js b/js/misc/util.js index db3742eb3dd543668f630f8b5d1b9d60ef734df6..541f1876bdcc8c6f9bf15e3d76b5ae53a5be130f 100644 --- a/js/misc/util.js +++ b/js/misc/util.js @@ -1,7 +1,7 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* exported findUrls, spawn, spawnCommandLine, spawnApp, trySpawnCommandLine, formatTime, formatTimeSpan, createTimeLabel, insertSorted, - makeCloseButton, ensureActorVisibleInScrollView */ + makeCloseButton, ensureActorVisibleInScrollView, wiggle */ const { Clutter, Gio, GLib, GObject, Shell, St } = imports.gi; const Gettext = imports.gettext; @@ -429,3 +429,37 @@ function ensureActorVisibleInScrollView(scrollView, actor) { duration: SCROLL_TIME }); } + +function wiggle(actor, params) { + params = Params.parse(params, { + offset: 0, + duration: 0, + wiggleCount: 0, + }); + actor.translation_x = 0; + + // Accelerate before wiggling + actor.ease({ + translation_x: -params.offset, + duration: params.duration, + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + onComplete: () => { + // Wiggle + actor.ease({ + translation_x: params.offset, + duration: params.duration, + mode: Clutter.AnimationMode.LINEAR, + repeatCount: params.wiggleCount, + autoReverse: true, + onComplete: () => { + // Decelerate and return to the original position + actor.ease({ + translation_x: 0, + duration: params.duration, + mode: Clutter.AnimationMode.EASE_IN_QUAD, + }); + } + }); + } + }); +} diff --git a/js/ui/environment.js b/js/ui/environment.js index bbd33c965401a3bf5b0ea9c2b9e22b95144ca795..20b546844dffab083b4c9c7fe9b5bf416fab4133 100644 --- a/js/ui/environment.js +++ b/js/ui/environment.js @@ -105,6 +105,16 @@ function _easeActor(actor, params) { actor.set_easing_delay(params.delay); delete params.delay; + let repeatCount = 0; + if (params.repeatCount != undefined) + repeatCount = params.repeatCount; + delete params.repeatCount; + + let autoReverse = false; + if (params.autoReverse != undefined) + autoReverse = params.autoReverse; + delete params.autoReverse; + if (params.mode != undefined) actor.set_easing_mode(params.mode); delete params.mode; @@ -127,10 +137,12 @@ function _easeActor(actor, params) { else Meta.disable_unredirect_for_display(global.display); - if (transition) + if (transition) { + transition.set({ repeatCount, autoReverse }); transition.connect('stopped', (t, finished) => callback(finished)); - else + } else { callback(true); + } } function _easeActorProperty(actor, propName, target, params) { @@ -143,6 +155,16 @@ function _easeActorProperty(actor, propName, target, params) { params.duration = adjustAnimationTime(params.duration); let duration = Math.floor(params.duration || 0); + let repeatCount = 0; + if (params.repeatCount != undefined) + repeatCount = params.repeatCount; + delete params.repeatCount; + + let autoReverse = false; + if (params.autoReverse != undefined) + autoReverse = params.autoReverse; + delete params.autoReverse; + // Copy Clutter's behavior for implicit animations, see // should_skip_implicit_transition() if (actor instanceof Clutter.Actor && !actor.mapped) @@ -168,7 +190,9 @@ function _easeActorProperty(actor, propName, target, params) { let transition = new Clutter.PropertyTransition(Object.assign({ property_name: propName, interval: new Clutter.Interval({ value_type: pspec.value_type }), - remove_on_complete: true + remove_on_complete: true, + repeat_count: repeatCount, + auto_reverse: autoReverse, }, params)); actor.add_transition(propName, transition);