diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js index 78bf29dd9..b10c26788 100644 --- a/js/gdm/authPrompt.js +++ b/js/gdm/authPrompt.js @@ -15,7 +15,7 @@ const GdmUtil = imports.gdm.util; const Params = imports.misc.params; const ShellEntry = imports.ui.shellEntry; const UserWidget = imports.ui.userWidget; -const Util = imports.misc.util; +const {wiggle} = imports.misc.animationUtils; var DEFAULT_BUTTON_WELL_ICON_SIZE = 16; var DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1000; @@ -391,7 +391,7 @@ var AuthPrompt = GObject.registerClass({ this.verificationStatus = AuthPromptStatus.VERIFICATION_FAILED; if (wasQueryingService) - Util.wiggle(this._entry); + wiggle(this._entry); } _onVerificationComplete() { @@ -566,7 +566,7 @@ var AuthPrompt = GObject.registerClass({ this._message.opacity = 0; } - Util.wiggle(this._message, wiggleParameters); + wiggle(this._message, wiggleParameters); } updateSensitivity(sensitive) { diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml index b1f537cc8..6aa88ce21 100644 --- a/js/js-resources.gresource.xml +++ b/js/js-resources.gresource.xml @@ -11,6 +11,7 @@ gdm/realmd.js gdm/util.js + misc/animationUtils.js misc/config.js misc/extensionUtils.js misc/fileUtils.js diff --git a/js/misc/animationUtils.js b/js/misc/animationUtils.js new file mode 100644 index 000000000..619d719bd --- /dev/null +++ b/js/misc/animationUtils.js @@ -0,0 +1,117 @@ +/* exported adjustAnimationTime, ensureActorVisibleInScrollView, wiggle */ + +const St = imports.gi.St; +const Clutter = imports.gi.Clutter; + +const Params = imports.misc.params; + +var SCROLL_TIME = 100; + +const WIGGLE_OFFSET = 6; +const WIGGLE_DURATION = 65; +const N_WIGGLES = 3; + +/** + * adjustAnimationTime: + * + * @param {number} msecs - time in milliseconds + * + * Adjust `msecs` to account for St's enable-animations + * and slow-down-factor settings + */ +function adjustAnimationTime(msecs) { + const settings = St.Settings.get(); + + if (!settings.enable_animations) + return 0; + return settings.slow_down_factor * msecs; +} + +/** + * Animate scrolling a scrollview until an actor is visible. + * + * @param {St.ScrollView} scrollView - the scroll view the actor is in + * @param {Clutter.Actor} actor - the actor + */ +function ensureActorVisibleInScrollView(scrollView, actor) { + const {adjustment} = scrollView.vscroll; + let [value, lower_, upper, stepIncrement_, pageIncrement_, pageSize] = adjustment.get_values(); + + let offset = 0; + const vfade = scrollView.get_effect('fade'); + if (vfade) + offset = vfade.fade_margins.top; + + let box = actor.get_allocation_box(); + let y1 = box.y1, y2 = box.y2; + + let parent = actor.get_parent(); + while (parent !== scrollView) { + if (!parent) + throw new Error('actor not in scroll view'); + + box = parent.get_allocation_box(); + y1 += box.y1; + y2 += box.y1; + parent = parent.get_parent(); + } + + if (y1 < value + offset) + value = Math.max(0, y1 - offset); + else if (y2 > value + pageSize - offset) + value = Math.min(upper, y2 + offset - pageSize); + else + return; + + adjustment.ease(value, { + mode: Clutter.AnimationMode.EASE_OUT_QUAD, + duration: SCROLL_TIME, + }); +} + +/** + * "Wiggles" a clutter actor. A "wiggle" is an animation the moves an actor + * back and forth on the X axis a specified amount of times. + * + * @param {Clutter.Actor} actor - an actor to animate + * @param {object} params - options for the animation + * @param {number} params.offset - the offset to move the actor by per-wiggle + * @param {number} params.duration - the amount of time to move the actor per-wiggle + * @param {number} params.wiggleCount - the number of times to wiggle the actor + */ +function wiggle(actor, params) { + if (!St.Settings.get().enable_animations) + return; + + params = Params.parse(params, { + offset: WIGGLE_OFFSET, + duration: WIGGLE_DURATION, + wiggleCount: N_WIGGLES, + }); + 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/misc/util.js b/js/misc/util.js index e4097acc1..8dc6b9905 100644 --- a/js/misc/util.js +++ b/js/misc/util.js @@ -1,9 +1,8 @@ // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- /* exported findUrls, spawn, spawnCommandLine, spawnApp, trySpawnCommandLine, - createTimeLabel, insertSorted, ensureActorVisibleInScrollView, - wiggle, lerp, GNOMEversionCompare, DBusSenderChecker, Highlighter */ + createTimeLabel, insertSorted, lerp, GNOMEversionCompare, + DBusSenderChecker, Highlighter */ -const Clutter = imports.gi.Clutter; const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Shell = imports.gi.Shell; @@ -11,15 +10,8 @@ const St = imports.gi.St; const GnomeDesktop = imports.gi.GnomeDesktop; const Main = imports.ui.main; -const Params = imports.misc.params; const {formatTime} = imports.misc.dateUtils; -var SCROLL_TIME = 100; - -const WIGGLE_OFFSET = 6; -const WIGGLE_DURATION = 65; -const N_WIGGLES = 3; - // http://daringfireball.net/2010/07/improved_regex_for_matching_urls const _balancedParens = '\\([^\\s()<>]+\\)'; const _leadingJunk = '[\\s`(\\[{\'\\"<\u00AB\u201C\u2018]'; @@ -241,79 +233,6 @@ function insertSorted(array, val, cmp) { return pos; } -function ensureActorVisibleInScrollView(scrollView, actor) { - let adjustment = scrollView.vscroll.adjustment; - let [value, lower_, upper, stepIncrement_, pageIncrement_, pageSize] = adjustment.get_values(); - - let offset = 0; - let vfade = scrollView.get_effect("fade"); - if (vfade) - offset = vfade.fade_margins.top; - - let box = actor.get_allocation_box(); - let y1 = box.y1, y2 = box.y2; - - let parent = actor.get_parent(); - while (parent != scrollView) { - if (!parent) - throw new Error("actor not in scroll view"); - - box = parent.get_allocation_box(); - y1 += box.y1; - y2 += box.y1; - parent = parent.get_parent(); - } - - if (y1 < value + offset) - value = Math.max(0, y1 - offset); - else if (y2 > value + pageSize - offset) - value = Math.min(upper, y2 + offset - pageSize); - else - return; - - adjustment.ease(value, { - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - duration: SCROLL_TIME, - }); -} - -function wiggle(actor, params) { - if (!St.Settings.get().enable_animations) - return; - - params = Params.parse(params, { - offset: WIGGLE_OFFSET, - duration: WIGGLE_DURATION, - wiggleCount: N_WIGGLES, - }); - 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, - }); - }, - }); - }, - }); -} - function lerp(start, end, progress) { return start + progress * (end - start); } diff --git a/js/ui/calendar.js b/js/ui/calendar.js index aa597fc42..d87d5aff8 100644 --- a/js/ui/calendar.js +++ b/js/ui/calendar.js @@ -13,7 +13,7 @@ const MessageList = imports.ui.messageList; const MessageTray = imports.ui.messageTray; const Mpris = imports.ui.mpris; const PopupMenu = imports.ui.popupMenu; -const Util = imports.misc.util; +const {ensureActorVisibleInScrollView} = imports.misc.animationUtils; const {formatDateWithCFormatString, formatTimeSpan} = imports.misc.dateUtils; const {loadInterfaceXML} = imports.misc.fileUtils; @@ -1016,7 +1016,7 @@ class CalendarMessageList extends St.Widget { 'notify::can-clear', this._sync.bind(this), 'destroy', () => this._sectionList.remove_actor(section), 'message-focused', (_s, messageActor) => { - Util.ensureActorVisibleInScrollView(this._scrollView, messageActor); + ensureActorVisibleInScrollView(this._scrollView, messageActor); }, this); this._sectionList.add_actor(section); } diff --git a/js/ui/components/keyring.js b/js/ui/components/keyring.js index e4d0ad85f..fa96553bb 100644 --- a/js/ui/components/keyring.js +++ b/js/ui/components/keyring.js @@ -13,7 +13,7 @@ const Dialog = imports.ui.dialog; const ModalDialog = imports.ui.modalDialog; const ShellEntry = imports.ui.shellEntry; const CheckBox = imports.ui.checkBox; -const Util = imports.misc.util; +const {wiggle} = imports.misc.animationUtils; var KeyringDialog = GObject.registerClass( class KeyringDialog extends ModalDialog.ModalDialog { @@ -83,7 +83,7 @@ class KeyringDialog extends ModalDialog.ModalDialog { }); this.prompt.connect('notify::warning', () => { if (this._passwordEntry && this.prompt.warning !== '') - Util.wiggle(this._passwordEntry); + wiggle(this._passwordEntry); }); warningBox.add_child(warning); diff --git a/js/ui/components/polkitAgent.js b/js/ui/components/polkitAgent.js index 65c4dff71..de2f86270 100644 --- a/js/ui/components/polkitAgent.js +++ b/js/ui/components/polkitAgent.js @@ -16,7 +16,7 @@ const Main = imports.ui.main; const ModalDialog = imports.ui.modalDialog; const ShellEntry = imports.ui.shellEntry; const UserWidget = imports.ui.userWidget; -const Util = imports.misc.util; +const {wiggle} = imports.misc.animationUtils; const DialogMode = { AUTH: 0, @@ -267,7 +267,7 @@ var AuthenticationDialog = GObject.registerClass({ this._infoMessageLabel.hide(); this._nullMessageLabel.hide(); - Util.wiggle(this._passwordEntry); + wiggle(this._passwordEntry); } /* Try and authenticate again */ diff --git a/js/ui/environment.js b/js/ui/environment.js index 8f75871c3..aee0f8ae5 100644 --- a/js/ui/environment.js +++ b/js/ui/environment.js @@ -44,6 +44,7 @@ const Shell = imports.gi.Shell; const St = imports.gi.St; const Gettext = imports.gettext; const SignalTracker = imports.misc.signalTracker; +const {adjustAnimationTime} = imports.misc.animationUtils; Gio._promisify(Gio.DataInputStream.prototype, 'fill_async'); Gio._promisify(Gio.DataInputStream.prototype, 'read_line_async'); @@ -389,17 +390,3 @@ function init() { // Prevent extensions from opening a display connection to ourselves Gdk.set_allowed_backends(''); } - -// adjustAnimationTime: -// @msecs: time in milliseconds -// -// Adjust @msecs to account for St's enable-animations -// and slow-down-factor settings -function adjustAnimationTime(msecs) { - let settings = St.Settings.get(); - - if (!settings.enable_animations) - return 0; - return settings.slow_down_factor * msecs; -} - diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js index 8a10d7fe3..39ae22648 100644 --- a/js/ui/screenShield.js +++ b/js/ui/screenShield.js @@ -22,7 +22,7 @@ const MessageTray = imports.ui.messageTray; const ShellDBus = imports.ui.shellDBus; const SmartcardManager = imports.misc.smartcardManager; -const { adjustAnimationTime } = imports.ui.environment; +const {adjustAnimationTime} = imports.misc.animationUtils; const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; const LOCK_ENABLED_KEY = 'lock-enabled'; diff --git a/js/ui/search.js b/js/ui/search.js index 348f39799..9e42c9bce 100644 --- a/js/ui/search.js +++ b/js/ui/search.js @@ -14,7 +14,7 @@ const IconGrid = imports.ui.iconGrid; const Main = imports.ui.main; const ParentalControlsManager = imports.misc.parentalControlsManager; const RemoteSearch = imports.ui.remoteSearch; -const Util = imports.misc.util; +const {ensureActorVisibleInScrollView} = imports.misc.animationUtils; const { Highlighter } = imports.misc.util; @@ -746,7 +746,7 @@ var SearchResultsView = GObject.registerClass({ } _focusChildChanged(provider) { - Util.ensureActorVisibleInScrollView(this._scrollView, provider.focusChild); + ensureActorVisibleInScrollView(this._scrollView, provider.focusChild); } _ensureProviderDisplay(provider) { @@ -877,7 +877,7 @@ var SearchResultsView = GObject.registerClass({ if (selected) { result.add_style_pseudo_class('selected'); - Util.ensureActorVisibleInScrollView(this._scrollView, result); + ensureActorVisibleInScrollView(this._scrollView, result); } else { result.remove_style_pseudo_class('selected'); } diff --git a/js/ui/shellMountOperation.js b/js/ui/shellMountOperation.js index 86ec781fc..591d88b1a 100644 --- a/js/ui/shellMountOperation.js +++ b/js/ui/shellMountOperation.js @@ -19,7 +19,7 @@ const Params = imports.misc.params; const ShellEntry = imports.ui.shellEntry; const { loadInterfaceXML } = imports.misc.fileUtils; -const Util = imports.misc.util; +const {wiggle} = imports.misc.animationUtils; var LIST_ITEM_ICON_SIZE = 48; var WORK_SPINNER_ICON_SIZE = 16; @@ -421,7 +421,7 @@ var ShellMountPasswordDialog = GObject.registerClass({ this._errorMessageLabel.text = _('Sorry, that didn’t work. Please try again.'); this._errorMessageLabel.opacity = 255; - Util.wiggle(this._passwordEntry); + wiggle(this._passwordEntry); } _onCancelButton() {