[gnome-shell] cleanup: Replace signal connections with virtual functions



commit 55b57421dc3629cca028d309c4d104f98b305b11
Author: Marco Trevisan (Treviño) <mail 3v1n0 net>
Date:   Tue Sep 10 07:42:48 2019 +0200

    cleanup: Replace signal connections with virtual functions
    
    Inheriting from actors allows to use virtual functions instead of signal
    connections for multiple cases, so just use them when possible.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559

 js/gdm/authPrompt.js         |  11 +++--
 js/gdm/loginDialog.js        |   7 +--
 js/ui/appDisplay.js          | 106 ++++++++++++++++++++++-------------------
 js/ui/boxpointer.js          |  27 ++++-------
 js/ui/calendar.js            |  57 +++++++++++-----------
 js/ui/dateMenu.js            |  46 +++++++++---------
 js/ui/ibusCandidatePopup.js  |  25 +++++-----
 js/ui/iconGrid.js            |  14 +++---
 js/ui/keyboard.js            |  12 ++++-
 js/ui/layout.js              |   6 +--
 js/ui/lookingGlass.js        |  12 ++---
 js/ui/messageList.js         | 111 +++++++++++++++++++++----------------------
 js/ui/mpris.js               |   2 +-
 js/ui/padOsd.js              |  27 +++++------
 js/ui/pageIndicators.js      |  26 ++++++----
 js/ui/panel.js               |  79 +++++++++++++++---------------
 js/ui/panelMenu.js           |  12 ++---
 js/ui/popupMenu.js           |  52 +++++++++++---------
 js/ui/screenshot.js          |  23 +++------
 js/ui/search.js              |   4 +-
 js/ui/shellMountOperation.js |   4 +-
 js/ui/slider.js              |  50 +++++++++----------
 js/ui/status/brightness.js   |   2 +-
 js/ui/status/network.js      |   5 +-
 js/ui/status/system.js       |  11 ++++-
 js/ui/status/volume.js       |   8 ++--
 js/ui/switcherPopup.js       |  24 ++++------
 js/ui/workspace.js           |  39 ++++++++++-----
 js/ui/workspaceThumbnail.js  |  39 +++++++--------
 js/ui/workspacesView.js      |   3 +-
 30 files changed, 432 insertions(+), 412 deletions(-)
---
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 1269ad8abf..93a3810568 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -81,11 +81,6 @@ var AuthPrompt = GObject.registerClass({
         });
 
         this.connect('destroy', this._onDestroy.bind(this));
-        this.connect('key-press-event', (actor, event) => {
-            if (event.get_key_symbol() == Clutter.KEY_Escape)
-                this.cancel();
-            return Clutter.EVENT_PROPAGATE;
-        });
 
         this._userWell = new St.Bin({ x_fill: true, x_align: St.Align.START });
         this.add(this._userWell, {
@@ -145,6 +140,12 @@ var AuthPrompt = GObject.registerClass({
         this._userVerifier = null;
     }
 
+    vfunc_key_press_event(keyPressEvent) {
+        if (keyPressEvent.keyval == Clutter.KEY_Escape)
+            this.cancel();
+        return Clutter.EVENT_PROPAGATE;
+    }
+
     _initButtons() {
         this.cancelButton = new St.Button({ style_class: 'modal-dialog-button button',
                                             button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
index acefb395f7..2c5834d442 100644
--- a/js/gdm/loginDialog.js
+++ b/js/gdm/loginDialog.js
@@ -74,7 +74,6 @@ var UserListItem = GObject.registerClass({
                                                  visible: false });
         layout.add(this._timedLoginIndicator);
 
-        this.connect('clicked', this._onClicked.bind(this));
         this._onUserChanged();
     }
 
@@ -103,7 +102,7 @@ var UserListItem = GObject.registerClass({
         this.user.disconnect(this._userChangedId);
     }
 
-    _onClicked() {
+    vfunc_clicked() {
         this.emit('activate');
     }
 
@@ -173,8 +172,10 @@ var UserList = GObject.registerClass({
 
         this.add_actor(this._box);
         this._items = {};
+    }
 
-        this.connect('key-focus-in', this._moveFocusToItems.bind(this));
+    vfunc_key_focus_in() {
+        this._moveFocusToItems();
     }
 
     _moveFocusToItems() {
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 6ffe58b6c4..5e20c09257 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -373,18 +373,6 @@ var AllView = GObject.registerClass({
             this._displayingPopup = false;
         });
 
-        this.connect('notify::mapped', () => {
-            if (this.mapped) {
-                this._keyPressEventId =
-                    global.stage.connect('key-press-event',
-                                         this._onKeyPressEvent.bind(this));
-            } else {
-                if (this._keyPressEventId)
-                    global.stage.disconnect(this._keyPressEventId);
-                this._keyPressEventId = 0;
-            }
-        });
-
         this._redisplayWorkId = Main.initializeDeferredWork(this, this._redisplay.bind(this));
 
         Shell.AppSystem.get_default().connect('installed-changed', () => {
@@ -401,6 +389,21 @@ var AllView = GObject.registerClass({
         this._nEventBlockerInhibits = 0;
     }
 
+    vfunc_map() {
+        this._keyPressEventId =
+            global.stage.connect('key-press-event',
+                this._onKeyPressEvent.bind(this));
+        super.vfunc_map();
+    }
+
+    vfunc_unmap() {
+        if (this._keyPressEventId) {
+            global.stage.disconnect(this._keyPressEventId);
+            this._keyPressEventId = 0;
+        }
+        super.vfunc_unmap();
+    }
+
     _redisplay() {
         super._redisplay();
         this._refilterApps();
@@ -904,11 +907,11 @@ class FrequentView extends BaseAppView {
         this._noFrequentAppsLabel.hide();
 
         this._usage = Shell.AppUsage.get_default();
+    }
 
-        this.connect('notify::mapped', () => {
-            if (this.mapped)
-                this._redisplay();
-        });
+    vfunc_map() {
+        this._redisplay();
+        super.vfunc_map();
     }
 
     hasUsefulData() {
@@ -1486,17 +1489,9 @@ var FolderIcon = GObject.registerClass({
 
         this._popupTimeoutId = 0;
 
-        this.connect('leave-event', this._onLeaveEvent.bind(this));
-        this.connect('button-press-event', this._onButtonPress.bind(this));
-        this.connect('touch-event', this._onTouchEvent.bind(this));
         this.connect('popup-menu', this._popupRenamePopup.bind(this));
 
-        this.connect('clicked', this.open.bind(this));
         this.connect('destroy', this._onDestroy.bind(this));
-        this.connect('notify::mapped', () => {
-            if (!this.mapped && this._popup)
-                this._popup.popdown();
-        });
 
         this._folder.connect('changed', this._redisplay.bind(this));
         this._redisplay();
@@ -1519,6 +1514,17 @@ var FolderIcon = GObject.registerClass({
         this._removeMenuTimeout();
     }
 
+    vfunc_clicked() {
+        this.open();
+    }
+
+    vfunc_unmap() {
+        super.vfunc_unmap();
+
+        if (this._popup)
+            this._popup.popdown();
+    }
+
     open() {
         this._removeMenuTimeout();
         this._ensurePopup();
@@ -1701,24 +1707,29 @@ var FolderIcon = GObject.registerClass({
                                    '[gnome-shell] this._popupRenamePopup');
     }
 
-    _onLeaveEvent(_actor, _event) {
+    vfunc_leave_event(crossingEvent) {
+        let ret = super.vfunc_leave_event(crossingEvent);
         this.fake_release();
         this._removeMenuTimeout();
+        return ret;
     }
 
-    _onButtonPress(_actor, event) {
-        let button = event.get_button();
-        if (button == 1) {
+    vfunc_button_press_event(buttonEvent) {
+        super.vfunc_button_press_event(buttonEvent);
+
+        if (buttonEvent.button == 1) {
             this._setPopupTimeout();
-        } else if (button == 3) {
+        } else if (buttonEvent.button == 3) {
             this._popupRenamePopup();
             return Clutter.EVENT_STOP;
         }
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onTouchEvent(actor, event) {
-        if (event.type() == Clutter.EventType.TOUCH_BEGIN)
+    vfunc_touch_event(touchEvent) {
+        super.vfunc_touch_event(touchEvent);
+
+        if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN)
             this._setPopupTimeout();
 
         return Clutter.EVENT_PROPAGATE;
@@ -1912,7 +1923,6 @@ var AppFolderPopup = GObject.registerClass({
             actionMode: Shell.ActionMode.POPUP
         });
         this._grabHelper.addActor(Main.layoutManager.overviewGroup);
-        this.connect('key-press-event', this._onKeyPress.bind(this));
         this.connect('destroy', this._onDestroy.bind(this));
     }
 
@@ -1924,8 +1934,8 @@ var AppFolderPopup = GObject.registerClass({
         }
     }
 
-    _onKeyPress(actor, event) {
-        if (global.stage.get_key_focus() != actor)
+    vfunc_key_press_event(keyEvent) {
+        if (global.stage.get_key_focus() != this)
             return Clutter.EVENT_PROPAGATE;
 
         // Since we need to only grab focus on one item child when the user
@@ -1947,7 +1957,7 @@ var AppFolderPopup = GObject.registerClass({
         // languages
         let direction;
         let isLtr = Clutter.get_default_text_direction() == Clutter.TextDirection.LTR;
-        switch (event.get_key_symbol()) {
+        switch (keyEvent.keyval) {
         case Clutter.Down:
             direction = St.DirectionType.TAB_FORWARD;
             break;
@@ -1967,7 +1977,7 @@ var AppFolderPopup = GObject.registerClass({
         default:
             return Clutter.EVENT_PROPAGATE;
         }
-        return actor.navigate_focus(null, direction, false);
+        return this.navigate_focus(null, direction, false);
     }
 
     toggle() {
@@ -2085,10 +2095,6 @@ var AppIcon = GObject.registerClass({
 
         this.label_actor = this.icon.label;
 
-        this.connect('leave-event', this._onLeaveEvent.bind(this));
-        this.connect('button-press-event', this._onButtonPress.bind(this));
-        this.connect('touch-event', this._onTouchEvent.bind(this));
-        this.connect('clicked', this._onClicked.bind(this));
         this.connect('popup-menu', this._onKeyboardPopupMenu.bind(this));
 
         this._menu = null;
@@ -2174,30 +2180,34 @@ var AppIcon = GObject.registerClass({
         GLib.Source.set_name_by_id(this._menuTimeoutId, '[gnome-shell] this.popupMenu');
     }
 
-    _onLeaveEvent(_actor, _event) {
+    vfunc_leave_event(crossingEvent) {
+        let ret = super.vfunc_leave_event(crossingEvent);
+
         this.fake_release();
         this._removeMenuTimeout();
+        return ret;
     }
 
-    _onButtonPress(_actor, event) {
-        let button = event.get_button();
-        if (button == 1) {
+    vfunc_button_press_event(buttonEvent) {
+        super.vfunc_button_press_event(buttonEvent);
+        if (buttonEvent.button == 1) {
             this._setPopupTimeout();
-        } else if (button == 3) {
+        } else if (buttonEvent.button == 3) {
             this.popupMenu();
             return Clutter.EVENT_STOP;
         }
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onTouchEvent(actor, event) {
-        if (event.type() == Clutter.EventType.TOUCH_BEGIN)
+    vfunc_touch_event(touchEvent) {
+        super.vfunc_touch_event(touchEvent);
+        if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN)
             this._setPopupTimeout();
 
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onClicked(actor, button) {
+    vfunc_clicked(button) {
         this._removeMenuTimeout();
         this.activate(button);
     }
diff --git a/js/ui/boxpointer.js b/js/ui/boxpointer.js
index 285454e642..d55543a705 100644
--- a/js/ui/boxpointer.js
+++ b/js/ui/boxpointer.js
@@ -46,12 +46,18 @@ var BoxPointer = GObject.registerClass({
         this.add_actor(this._border);
         this.bin.raise(this._border);
         this._sourceAlignment = 0.5;
-        this._capturedEventId = 0;
-        this._muteInput();
+        this._muteInput = true;
 
         this.connect('destroy', this._onDestroy.bind(this));
     }
 
+    vfunc_captured_event() {
+        if (this._muteInput)
+            return Clutter.EVENT_STOP;
+
+        return Clutter.EVENT_PROPAGATE;
+    }
+
     _onDestroy() {
         if (this._sourceActorDestroyId) {
             this._sourceActor.disconnect(this._sourceActorDestroyId);
@@ -63,19 +69,6 @@ var BoxPointer = GObject.registerClass({
         return this._arrowSide;
     }
 
-    _muteInput() {
-        if (this._capturedEventId == 0)
-            this._capturedEventId = this.connect('captured-event',
-                                                 () => Clutter.EVENT_STOP);
-    }
-
-    _unmuteInput() {
-        if (this._capturedEventId != 0) {
-            this.disconnect(this._capturedEventId);
-            this._capturedEventId = 0;
-        }
-    }
-
     open(animate, onComplete) {
         let themeNode = this.get_theme_node();
         let rise = themeNode.get_length('-arrow-rise');
@@ -112,7 +105,7 @@ var BoxPointer = GObject.registerClass({
             duration: animationTime,
             mode: Clutter.AnimationMode.LINEAR,
             onComplete: () => {
-                this._unmuteInput();
+                this._muteInput = false;
                 if (onComplete)
                     onComplete();
             }
@@ -147,7 +140,7 @@ var BoxPointer = GObject.registerClass({
             }
         }
 
-        this._muteInput();
+        this._muteInput = true;
 
         this.remove_all_transitions();
         this.ease({
diff --git a/js/ui/calendar.js b/js/ui/calendar.js
index 5b404f6e44..40f5ba52ac 100644
--- a/js/ui/calendar.js
+++ b/js/ui/calendar.js
@@ -352,8 +352,6 @@ var Calendar = GObject.registerClass({
             reactive: true
         });
 
-        this.connect('scroll-event', this._onScroll.bind(this));
-
         this._buildHeader ();
     }
 
@@ -446,8 +444,8 @@ var Calendar = GObject.registerClass({
         this._firstDayIndex = this.get_n_children();
     }
 
-    _onScroll(actor, event) {
-        switch (event.get_scroll_direction()) {
+    vfunc_scroll_event(scrollEvent) {
+        switch (scrollEvent.direction) {
         case Clutter.ScrollDirection.UP:
         case Clutter.ScrollDirection.LEFT:
             this._onPrevMonthButtonClicked();
@@ -668,11 +666,12 @@ class EventMessage extends MessageList.Message {
 
         this._icon = new St.Icon({ icon_name: 'x-office-calendar-symbolic' });
         this.setIcon(this._icon);
+    }
 
-        this.connect('style-changed', () => {
-            let iconVisible = this.get_parent().has_style_pseudo_class('first-child');
-            this._icon.opacity = (iconVisible ? 255 : 0);
-        });
+    vfunc_style_changed() {
+        let iconVisible = this.get_parent().has_style_pseudo_class('first-child');
+        this._icon.opacity = (iconVisible ? 255 : 0);
+        super.vfunc_style_changed();
     }
 
     _formatEventTime() {
@@ -750,7 +749,7 @@ class NotificationMessage extends MessageList.Message {
         this.setUseBodyMarkup(n.bannerBodyMarkup);
     }
 
-    _onClicked() {
+    vfunc_clicked() {
         this.notification.activate();
     }
 
@@ -910,6 +909,23 @@ class EventsSection extends MessageList.MessageListSection {
     }
 });
 
+var TimeLabel = GObject.registerClass(
+class NotificationTimeLabel extends St.Label {
+    _init(datetime) {
+        super._init({
+            style_class: 'event-time',
+            x_align: Clutter.ActorAlign.START,
+            y_align: Clutter.ActorAlign.END
+        });
+        this._datetime = datetime;
+    }
+
+    vfunc_map() {
+        this.text = Util.formatTimeSpan(this._datetime);
+        super.vfunc_map();
+    }
+});
+
 var NotificationSection = GObject.registerClass(
 class NotificationSection extends MessageList.MessageListSection {
     _init() {
@@ -922,8 +938,6 @@ class NotificationSection extends MessageList.MessageListSection {
         Main.messageTray.getSources().forEach(source => {
             this._sourceAdded(Main.messageTray, source);
         });
-
-        this.connect('notify::mapped', this._onMapped.bind(this));
     }
 
     get allowed() {
@@ -931,17 +945,6 @@ class NotificationSection extends MessageList.MessageListSection {
                !Main.sessionMode.isGreeter;
     }
 
-    _createTimeLabel(datetime) {
-        let label = new St.Label({ style_class: 'event-time',
-                                   x_align: Clutter.ActorAlign.START,
-                                   y_align: Clutter.ActorAlign.END });
-        label.connect('notify::mapped', () => {
-            if (label.mapped)
-                label.text = Util.formatTimeSpan(datetime);
-        });
-        return label;
-    }
-
     _sourceAdded(tray, source) {
         let obj = {
             destroyId: 0,
@@ -959,12 +962,12 @@ class NotificationSection extends MessageList.MessageListSection {
 
     _onNotificationAdded(source, notification) {
         let message = new NotificationMessage(notification);
-        message.setSecondaryActor(this._createTimeLabel(notification.datetime));
+        message.setSecondaryActor(new TimeLabel(notification.datetime));
 
         let isUrgent = notification.urgency == MessageTray.Urgency.CRITICAL;
 
         let updatedId = notification.connect('updated', () => {
-            message.setSecondaryActor(this._createTimeLabel(notification.datetime));
+            message.setSecondaryActor(new TimeLabel(notification.datetime));
             this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.mapped);
         });
         let destroyId = notification.connect('destroy', () => {
@@ -995,14 +998,12 @@ class NotificationSection extends MessageList.MessageListSection {
         this._sources.delete(source);
     }
 
-    _onMapped() {
-        if (!this.mapped)
-            return;
-
+    vfunc_map() {
         this._messages.forEach(message => {
             if (message.notification.urgency != MessageTray.Urgency.CRITICAL)
                 message.notification.acknowledged = true;
         });
+        super.vfunc_map();
     }
 
     _shouldShow() {
diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js
index 1492a888fb..2b28d8ebc1 100644
--- a/js/ui/dateMenu.js
+++ b/js/ui/dateMenu.js
@@ -42,9 +42,6 @@ class TodayButton extends St.Button {
             can_focus: true,
             reactive: false
         });
-        this.connect('clicked', () => {
-            this._calendar.setDate(new Date(), false);
-        });
 
         let hbox = new St.BoxLayout({ vertical: true });
         this.add_actor(hbox);
@@ -64,6 +61,10 @@ class TodayButton extends St.Button {
         });
     }
 
+    vfunc_clicked() {
+        this._calendar.setDate(new Date(), false);
+    }
+
     setDate(date) {
         this._dayLabel.set_text(date.toLocaleFormat('%A'));
 
@@ -97,14 +98,6 @@ class WorldClocksSection extends St.Button {
 
         this._locations = [];
 
-        this.connect('clicked', () => {
-            if (this._clocksApp)
-                this._clocksApp.activate();
-
-            Main.overview.hide();
-            Main.panel.closeCalendar();
-        });
-
         let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
         this._grid = new St.Widget({ style_class: 'world-clocks-grid',
                                      layout_manager: layout });
@@ -133,6 +126,14 @@ class WorldClocksSection extends St.Button {
         this._sync();
     }
 
+    vfunc_clicked() {
+        if (this._clocksApp)
+            this._clocksApp.activate();
+
+        Main.overview.hide();
+        Main.panel.closeCalendar();
+    }
+
     _sync() {
         this._clocksApp = this._appSystem.lookup_app('org.gnome.clocks.desktop');
         this.visible = this._clocksApp != null;
@@ -257,17 +258,6 @@ class WeatherSection extends St.Button {
 
         this._weatherClient = new Weather.WeatherClient();
 
-        this.connect('clicked', () => {
-            this._weatherClient.activateApp();
-
-            Main.overview.hide();
-            Main.panel.closeCalendar();
-        });
-        this.connect('notify::mapped', () => {
-            if (this.mapped)
-                this._weatherClient.update();
-        });
-
         let box = new St.BoxLayout({ style_class: 'weather-box',
                                      vertical: true });
 
@@ -294,6 +284,18 @@ class WeatherSection extends St.Button {
         this._sync();
     }
 
+    vfunc_map() {
+        this._weatherClient.update();
+        super.vfunc_map();
+    }
+
+    vfunc_clicked() {
+        this._weatherClient.activateApp();
+
+        Main.overview.hide();
+        Main.panel.closeCalendar();
+    }
+
     _getInfos() {
         let info = this._weatherClient.info;
         let forecasts = info.get_forecast_list();
diff --git a/js/ui/ibusCandidatePopup.js b/js/ui/ibusCandidatePopup.js
index 52f6495ef9..d7fce6f93f 100644
--- a/js/ui/ibusCandidatePopup.js
+++ b/js/ui/ibusCandidatePopup.js
@@ -47,19 +47,6 @@ var CandidateArea = GObject.registerClass({
             });
         }
 
-        this.connect('scroll-event', (actor, event) => {
-            let direction = event.get_scroll_direction();
-            switch (direction) {
-            case Clutter.ScrollDirection.UP:
-                this.emit('cursor-up');
-                break;
-            case Clutter.ScrollDirection.DOWN:
-                this.emit('cursor-down');
-                break;
-            }
-            return Clutter.EVENT_PROPAGATE;
-        });
-
         this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
 
         this._previousButton = new St.Button({ style_class: 'candidate-page-button 
candidate-page-button-previous button' });
@@ -83,6 +70,18 @@ var CandidateArea = GObject.registerClass({
         this._cursorPosition = 0;
     }
 
+    vfunc_scroll_event(scrollEvent) {
+        switch (scrollEvent.direction) {
+        case Clutter.ScrollDirection.UP:
+            this.emit('cursor-up');
+            break;
+        case Clutter.ScrollDirection.DOWN:
+            this.emit('cursor-down');
+            break;
+        }
+        return Clutter.EVENT_PROPAGATE;
+    }
+
     setOrientation(orientation) {
         if (this._orientation == orientation)
             return;
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index 04573f5858..782756947b 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -231,18 +231,18 @@ var IconGrid = GObject.registerClass({
         this._fixedHItemSize = this._fixedVItemSize = undefined;
         this.connect('style-changed', this._onStyleChanged.bind(this));
 
-        // Cancel animations when hiding the overview, to avoid icons
-        // swarming into the void ...
-        this.connect('notify::mapped', () => {
-            if (!this.mapped)
-                this._resetAnimationActors();
-        });
-
         this.connect('actor-added', this._childAdded.bind(this));
         this.connect('actor-removed', this._childRemoved.bind(this));
         this.connect('destroy', this._onDestroy.bind(this));
     }
 
+    vfunc_unmap() {
+        // Cancel animations when hiding the overview, to avoid icons
+        // swarming into the void ...
+        this._resetAnimationActors();
+        super.vfunc_unmap();
+    }
+
     _onDestroy() {
         if (this._updateIconSizesLaterId) {
             Meta.later_remove (this._updateIconSizesLaterId);
diff --git a/js/ui/keyboard.js b/js/ui/keyboard.js
index d7a6999b2a..a92c227e05 100644
--- a/js/ui/keyboard.js
+++ b/js/ui/keyboard.js
@@ -895,8 +895,6 @@ var EmojiSelection = GObject.registerClass({
 
         this._populateSections();
 
-        this.connect('notify::mapped', () => this._emojiPager.setCurrentPage(0));
-
         this._emojiPager = new EmojiPager(this._sections, 11, 3);
         this._emojiPager.connect('page-changed', (pager, sectionLabel, page, nPages) => {
             this._onPageChanged(sectionLabel, page, nPages);
@@ -918,6 +916,16 @@ var EmojiSelection = GObject.registerClass({
         this._emojiPager.setCurrentPage(0);
     }
 
+    vfunc_map() {
+        this._emojiPager.setCurrentPage(0);
+        super.vfunc_map();
+    }
+
+    vfunc_unmap() {
+        super.vfunc_unmap();
+        this._emojiPager.setCurrentPage(0);
+    }
+
     _onPageChanged(sectionLabel, page, nPages) {
         this._pageIndicator.setNPages(nPages);
         this._pageIndicator.setCurrentPage(page);
diff --git a/js/ui/layout.js b/js/ui/layout.js
index 767bfbd16c..8d888c14a6 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -1212,8 +1212,6 @@ class HotCorner extends Clutter.Actor {
                 this._corner.set_position(0, 0);
             }
 
-            this.connect('leave-event', this._onEnvironsLeft.bind(this));
-
             this._corner.connect('enter-event',
                                  this._onCornerEntered.bind(this));
             this._corner.connect('leave-event',
@@ -1263,8 +1261,8 @@ class HotCorner extends Clutter.Actor {
         return Clutter.EVENT_STOP;
     }
 
-    _onEnvironsLeft(actor, event) {
-        if (event.get_related() != this._corner)
+    vfunc_leave_event(crossingEvent) {
+        if (crossingEvent.related != this._corner)
             this._entered = false;
         return Clutter.EVENT_PROPAGATE;
     }
diff --git a/js/ui/lookingGlass.js b/js/ui/lookingGlass.js
index 38e45edb45..9999502489 100644
--- a/js/ui/lookingGlass.js
+++ b/js/ui/lookingGlass.js
@@ -264,13 +264,12 @@ class ObjLink extends St.Button {
             label: text
         });
         this.get_child().single_line_mode = true;
-        this.connect('clicked', this._onClicked.bind(this));
 
         this._obj = o;
         this._lookingGlass = lookingGlass;
     }
 
-    _onClicked() {
+    vfunc_clicked() {
         this._lookingGlass.inspectObject(this._obj, this);
     }
 });
@@ -797,8 +796,6 @@ class LookingGlass extends St.BoxLayout {
         // Sort of magic, but...eh.
         this._maxItems = 150;
 
-        this.connect('key-press-event', this._globalKeyPressEvent.bind(this));
-
         this._interfaceSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
         this._interfaceSettings.connect('changed::monospace-font-name',
                                         this._updateFont.bind(this));
@@ -1076,9 +1073,8 @@ class LookingGlass extends St.BoxLayout {
     }
 
     // Handle key events which are relevant for all tabs of the LookingGlass
-    _globalKeyPressEvent(actor, event) {
-        let symbol = event.get_key_symbol();
-        let modifierState = event.get_state();
+    vfunc_key_press_event(keyPressEvent) {
+        let symbol = keyPressEvent.keyval;
         if (symbol == Clutter.Escape) {
             if (this._objInspector.visible) {
                 this._objInspector.close();
@@ -1088,7 +1084,7 @@ class LookingGlass extends St.BoxLayout {
             return Clutter.EVENT_STOP;
         }
         // Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
-        if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
+        if (keyPressEvent.modifier_state & Clutter.ModifierType.CONTROL_MASK) {
             if (symbol == Clutter.KEY_Page_Up) {
                 this._notebook.prevTab();
             } else if (symbol == Clutter.KEY_Page_Down) {
diff --git a/js/ui/messageList.js b/js/ui/messageList.js
index 7964a92067..ba3b3fd2a9 100644
--- a/js/ui/messageList.js
+++ b/js/ui/messageList.js
@@ -56,57 +56,62 @@ class URLHighlighter extends St.Label {
         this.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
 
         this.setMarkup(text, allowMarkup);
-        this.connect('button-press-event', (actor, event) => {
-            // Don't try to URL highlight when invisible.
-            // The MessageTray doesn't actually hide us, so
-            // we need to check for paint opacities as well.
-            if (!actor.visible || actor.get_paint_opacity() == 0)
-                return Clutter.EVENT_PROPAGATE;
-
-            // Keep Notification.actor from seeing this and taking
-            // a pointer grab, which would block our button-release-event
-            // handler, if an URL is clicked
-            return this._findUrlAtPos(event) != -1;
-        });
-        this.connect('button-release-event', (actor, event) => {
-            if (!actor.visible || actor.get_paint_opacity() == 0)
-                return Clutter.EVENT_PROPAGATE;
-
-            let urlId = this._findUrlAtPos(event);
-            if (urlId != -1) {
-                let url = this._urls[urlId].url;
-                if (!url.includes(':'))
-                    url = 'http://' + url;
-
-                Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1));
-                return Clutter.EVENT_STOP;
-            }
+    }
+
+    vfunc_button_press_event(buttonEvent) {
+        // Don't try to URL highlight when invisible.
+        // The MessageTray doesn't actually hide us, so
+        // we need to check for paint opacities as well.
+        if (!this.visible || this.get_paint_opacity() == 0)
             return Clutter.EVENT_PROPAGATE;
-        });
-        this.connect('motion-event', (actor, event) => {
-            if (!actor.visible || actor.get_paint_opacity() == 0)
-                return Clutter.EVENT_PROPAGATE;
-
-            let urlId = this._findUrlAtPos(event);
-            if (urlId != -1 && !this._cursorChanged) {
-                global.display.set_cursor(Meta.Cursor.POINTING_HAND);
-                this._cursorChanged = true;
-            } else if (urlId == -1) {
-                global.display.set_cursor(Meta.Cursor.DEFAULT);
-                this._cursorChanged = false;
-            }
+
+        // Keep Notification from seeing this and taking
+        // a pointer grab, which would block our button-release-event
+        // handler, if an URL is clicked
+        return this._findUrlAtPos(buttonEvent) != -1;
+    }
+
+    vfunc_button_release_event(buttonEvent) {
+        if (!this.visible || this.get_paint_opacity() == 0)
             return Clutter.EVENT_PROPAGATE;
-        });
-        this.connect('leave-event', () => {
-            if (!this.visible || this.get_paint_opacity() == 0)
-                return Clutter.EVENT_PROPAGATE;
 
-            if (this._cursorChanged) {
-                this._cursorChanged = false;
-                global.display.set_cursor(Meta.Cursor.DEFAULT);
-            }
+        let urlId = this._findUrlAtPos(buttonEvent);
+        if (urlId != -1) {
+            let url = this._urls[urlId].url;
+            if (!url.includes(':'))
+                url = 'http://' + url;
+
+            Gio.app_info_launch_default_for_uri(
+                url, global.create_app_launch_context(0, -1));
+            return Clutter.EVENT_STOP;
+        }
+        return Clutter.EVENT_PROPAGATE;
+    }
+
+    vfunc_motion_event(motionEvent) {
+        if (!this.visible || this.get_paint_opacity() == 0)
             return Clutter.EVENT_PROPAGATE;
-        });
+
+        let urlId = this._findUrlAtPos(motionEvent);
+        if (urlId != -1 && !this._cursorChanged) {
+            global.display.set_cursor(Meta.Cursor.POINTING_HAND);
+            this._cursorChanged = true;
+        } else if (urlId == -1) {
+            global.display.set_cursor(Meta.Cursor.DEFAULT);
+            this._cursorChanged = false;
+        }
+        return Clutter.EVENT_PROPAGATE;
+    }
+
+    vfunc_leave_event(crossingEvent) {
+        if (!this.visible || this.get_paint_opacity() == 0)
+            return Clutter.EVENT_PROPAGATE;
+
+        if (this._cursorChanged) {
+            this._cursorChanged = false;
+            global.display.set_cursor(Meta.Cursor.DEFAULT);
+        }
+        return super.vfunc_leave_event(crossingEvent);
     }
 
     setMarkup(text, allowMarkup) {
@@ -135,7 +140,7 @@ class URLHighlighter extends St.Label {
     }
 
     _findUrlAtPos(event) {
-        let [x, y] = event.get_coords();
+        let { x, y } = event;
         [, x, y] = this.transform_stage_point(x, y);
         let findPos = -1;
         for (let i = 0; i < this.clutter_text.text.length; i++) {
@@ -309,8 +314,6 @@ var Message = GObject.registerClass({
         this.expanded = false;
         this._useBodyMarkup = false;
 
-        this.connect('key-press-event', this._onKeyPressed.bind(this));
-
         let vbox = new St.BoxLayout({ vertical: true });
         this.set_child(vbox);
 
@@ -363,7 +366,6 @@ var Message = GObject.registerClass({
         this._closeButton.connect('clicked', this.close.bind(this));
         let actorHoverId = this.connect('notify::hover', this._sync.bind(this));
         this._closeButton.connect('destroy', this.disconnect.bind(this, actorHoverId));
-        this.connect('clicked', this._onClicked.bind(this));
         this.connect('destroy', this._onDestroy.bind(this));
         this._sync();
     }
@@ -508,14 +510,11 @@ var Message = GObject.registerClass({
         this._closeButton.reactive = visible;
     }
 
-    _onClicked() {
-    }
-
     _onDestroy() {
     }
 
-    _onKeyPressed(a, event) {
-        let keysym = event.get_key_symbol();
+    vfunc_key_press_event(keyEvent) {
+        let keysym = keyEvent.keyval;
 
         if (keysym == Clutter.KEY_Delete ||
             keysym == Clutter.KEY_KP_Delete) {
diff --git a/js/ui/mpris.js b/js/ui/mpris.js
index 15f7b68e30..c46f9f01c5 100644
--- a/js/ui/mpris.js
+++ b/js/ui/mpris.js
@@ -49,7 +49,7 @@ class MediaMessage extends MessageList.Message {
         this._update();
     }
 
-    _onClicked() {
+    vfunc_clicked() {
         this._player.raise();
         Main.panel.closeCalendar();
     }
diff --git a/js/ui/padOsd.js b/js/ui/padOsd.js
index 7634ce7bc1..b36c00b489 100644
--- a/js/ui/padOsd.js
+++ b/js/ui/padOsd.js
@@ -44,16 +44,17 @@ var PadChooser = GObject.registerClass({
         this._ensureMenu(groupDevices);
 
         this.connect('destroy', this._onDestroy.bind(this));
-        this.connect('clicked', actor => {
-            if (actor.get_checked()) {
-                if (this._padChooserMenu != null)
-                    this._padChooserMenu.open(true);
-                else
-                    this.set_checked(false);
-            } else {
-                this._padChooserMenu.close(true);
-            }
-        });
+    }
+
+    vfunc_clicked() {
+        if (this.get_checked()) {
+            if (this._padChooserMenu != null)
+                this._padChooserMenu.open(true);
+            else
+                this.set_checked(false);
+        } else {
+            this._padChooserMenu.close(true);
+        }
     }
 
     _ensureMenu(devices) {
@@ -93,10 +94,9 @@ var KeybindingEntry = GObject.registerClass({
 }, class KeybindingEntry extends St.Entry {
     _init() {
         super._init({ hint_text: _("New shortcut…"), style: 'width: 10em' });
-        this.connect('captured-event', this._onCapturedEvent.bind(this));
     }
 
-    _onCapturedEvent(actor, event) {
+    vfunc_captured_event(event) {
         if (event.type() != Clutter.EventType.KEY_PRESS)
             return Clutter.EVENT_PROPAGATE;
 
@@ -116,7 +116,6 @@ var ActionComboBox = GObject.registerClass({
 }, class ActionComboBox extends St.Button {
     _init() {
         super._init({ style_class: 'button' });
-        this.connect('clicked', this._onButtonClicked.bind(this));
         this.set_toggle_mode(true);
 
         let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL,
@@ -182,7 +181,7 @@ var ActionComboBox = GObject.registerClass({
         this._editMenu.close(true);
     }
 
-    _onButtonClicked() {
+    vfunc_clicked() {
         if (this.get_checked())
             this.popup();
         else
diff --git a/js/ui/pageIndicators.js b/js/ui/pageIndicators.js
index 5ed7f879d0..223f329708 100644
--- a/js/ui/pageIndicators.js
+++ b/js/ui/pageIndicators.js
@@ -97,17 +97,25 @@ var AnimatedPageIndicators = GObject.registerClass(
 class AnimatedPageIndicators extends PageIndicators {
     _init() {
         super._init();
+        this.connect('destroy', this._onDestroy.bind(this));
+    }
 
-        this.connect('notify::mapped', () => {
-            if (!this.mapped)
-                return;
+    _onDestroy() {
+        if (this.animateLater) {
+            Meta.later_remove(this.animateLater);
+            this.animateLater = 0;
+        }
+    }
 
-            // Implicit animations are skipped for unmapped actors, and our
-            // children aren't mapped yet, so defer to a later handler
-            Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
-                this.animateIndicators(AnimationDirection.IN);
-                return GLib.SOURCE_REMOVE;
-            });
+    vfunc_map() {
+        super.vfunc_map();
+
+        // Implicit animations are skipped for unmapped actors, and our
+        // children aren't mapped yet, so defer to a later handler
+        this.animateLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
+            this.animateLater = 0;
+            this.animateIndicators(AnimationDirection.IN);
+            return GLib.SOURCE_REMOVE;
         });
     }
 
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 37a84290c7..f6ba07d836 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -430,9 +430,6 @@ class ActivitiesButton extends PanelMenu.Button {
 
         this.label_actor = this._label;
 
-        this.connect('captured-event', this._onCapturedEvent.bind(this));
-        this.connect_after('key-release-event', this._onKeyRelease.bind(this));
-
         Main.overview.connect('showing', () => {
             this.add_style_pseudo_class('overview');
             this.add_accessible_state (Atk.StateType.CHECKED);
@@ -459,7 +456,7 @@ class ActivitiesButton extends PanelMenu.Button {
         return DND.DragMotionResult.CONTINUE;
     }
 
-    _onCapturedEvent(actor, event) {
+    vfunc_captured_event(event) {
         if (event.type() == Clutter.EventType.BUTTON_PRESS ||
             event.type() == Clutter.EventType.TOUCH_BEGIN) {
             if (!Main.overview.shouldToggleByCornerOrButton())
@@ -468,9 +465,7 @@ class ActivitiesButton extends PanelMenu.Button {
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onEvent(actor, event) {
-        super._onEvent(actor, event);
-
+    vfunc_event(event) {
         if (event.type() == Clutter.EventType.TOUCH_END ||
             event.type() == Clutter.EventType.BUTTON_RELEASE)
             if (Main.overview.shouldToggleByCornerOrButton())
@@ -479,13 +474,16 @@ class ActivitiesButton extends PanelMenu.Button {
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onKeyRelease(actor, event) {
-        let symbol = event.get_key_symbol();
-        if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
-            if (Main.overview.shouldToggleByCornerOrButton())
-                Main.overview.toggle();
+    vfunc_key_release_event(keyEvent) {
+        let ret = super.vfunc_key_release_event(keyEvent);
+        if (ret == Clutter.EVENT_PROPAGATE) {
+            let symbol = keyEvent.keyval;
+            if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
+                if (Main.overview.shouldToggleByCornerOrButton())
+                    Main.overview.toggle();
+            }
         }
-        return Clutter.EVENT_PROPAGATE;
+        return ret;
     }
 
     _xdndToggleOverview() {
@@ -804,10 +802,6 @@ class Panel extends St.Widget {
         this._rightCorner = new PanelCorner(St.Side.RIGHT);
         this.add_child(this._rightCorner);
 
-        this.connect('button-press-event', this._onButtonPress.bind(this));
-        this.connect('touch-event', this._onButtonPress.bind(this));
-        this.connect('key-press-event', this._onKeyPress.bind(this));
-
         Main.overview.connect('showing', () => {
             this.add_style_pseudo_class('overview');
         });
@@ -912,45 +906,48 @@ class Panel extends St.Widget {
         this._rightCorner.allocate(childBox, flags);
     }
 
-    _onButtonPress(actor, event) {
+    _tryDragWindow(event) {
         if (Main.modalCount > 0)
             return Clutter.EVENT_PROPAGATE;
 
-        if (event.get_source() != actor)
+        if (event.source != this)
             return Clutter.EVENT_PROPAGATE;
 
-        let type = event.type();
-        let isPress = type == Clutter.EventType.BUTTON_PRESS;
-        if (!isPress && type != Clutter.EventType.TOUCH_BEGIN)
-            return Clutter.EVENT_PROPAGATE;
+        let { x, y } = event;
+        let dragWindow = this._getDraggableWindowForPosition(x);
 
-        let button = isPress ? event.get_button() : -1;
-        if (isPress && button != 1)
+        if (!dragWindow)
             return Clutter.EVENT_PROPAGATE;
 
-        let [stageX, stageY] = event.get_coords();
-
-        let dragWindow = this._getDraggableWindowForPosition(stageX);
+        return global.display.begin_grab_op(
+            dragWindow,
+            Meta.GrabOp.MOVING,
+            false, /* pointer grab */
+            true, /* frame action */
+            event.button || -1,
+            event.modifier_state,
+            event.time,
+            x, y) ? Clutter.EVENT_STOP : Clutter.EVENT_PROPAGATE;
+    }
 
-        if (!dragWindow)
+    vfunc_button_press_event(buttonEvent) {
+        if (buttonEvent.button != 1)
             return Clutter.EVENT_PROPAGATE;
 
-        global.display.begin_grab_op(dragWindow,
-                                     Meta.GrabOp.MOVING,
-                                     false, /* pointer grab */
-                                     true, /* frame action */
-                                     button,
-                                     event.get_state(),
-                                     event.get_time(),
-                                     stageX, stageY);
+        return this._tryDragWindow(buttonEvent);
+    }
+
+    vfunc_touch_event(touchEvent) {
+        if (touchEvent.type != Clutter.EventType.TOUCH_BEGIN)
+            return Clutter.EVENT_PROPAGATE;
 
-        return Clutter.EVENT_STOP;
+        return this._tryDragWindow(touchEvent);
     }
 
-    _onKeyPress(actor, event) {
-        let symbol = event.get_key_symbol();
+    vfunc_key_press_event(keyEvent) {
+        let symbol = keyEvent.keyval;
         if (symbol == Clutter.KEY_Escape) {
-            global.display.focus_default_window(event.get_time());
+            global.display.focus_default_window(keyEvent.time);
             return Clutter.EVENT_STOP;
         }
 
diff --git a/js/ui/panelMenu.js b/js/ui/panelMenu.js
index 169ea0d249..f98fd3b9ac 100644
--- a/js/ui/panelMenu.js
+++ b/js/ui/panelMenu.js
@@ -100,9 +100,6 @@ var Button = GObject.registerClass({
                       accessible_name: nameText ? nameText : "",
                       accessible_role: Atk.Role.MENU });
 
-        this.connect('event', this._onEvent.bind(this));
-        this.connect('notify::visible', this._onVisibilityChanged.bind(this));
-
         if (dontCreateMenu)
             this.menu = new PopupMenu.PopupDummyMenu(this);
         else
@@ -131,7 +128,7 @@ var Button = GObject.registerClass({
         this.emit('menu-set');
     }
 
-    _onEvent(actor, event) {
+    vfunc_event(event) {
         if (this.menu &&
             (event.type() == Clutter.EventType.TOUCH_BEGIN ||
              event.type() == Clutter.EventType.BUTTON_PRESS))
@@ -140,11 +137,10 @@ var Button = GObject.registerClass({
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onVisibilityChanged() {
-        if (!this.menu)
-            return;
+    vfunc_hide() {
+        super.vfunc_hide();
 
-        if (!this.visible)
+        if (this.menu)
             this.menu.close();
     }
 
diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js
index 66a1c9cff7..062b2bf9e0 100644
--- a/js/ui/popupMenu.js
+++ b/js/ui/popupMenu.js
@@ -97,12 +97,6 @@ var PopupBaseMenuItem = GObject.registerClass({
         if (params.style_class)
             this.add_style_class_name(params.style_class);
 
-        if (this._activatable) {
-            this.connect('button-press-event', this._onButtonPressEvent.bind(this));
-            this.connect('button-release-event', this._onButtonReleaseEvent.bind(this));
-            this.connect('touch-event', this._onTouchEvent.bind(this));
-            this.connect('key-press-event', this._onKeyPressEvent.bind(this));
-        }
         if (params.reactive && params.hover)
             this.bind_property('hover', this, 'active', GObject.BindingFlags.SYNC_CREATE);
     }
@@ -124,32 +118,44 @@ var PopupBaseMenuItem = GObject.registerClass({
         this._parent = parent;
     }
 
-    _onButtonPressEvent() {
+    vfunc_button_press_event(buttonEvent) {
+        if (!this._activatable)
+            return super.vfunc_button_press_event(buttonEvent);
+
         // This is the CSS active state
         this.add_style_pseudo_class('active');
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onButtonReleaseEvent(actor, event) {
+    vfunc_button_release_event(buttonEvent) {
+        if (!this._activatable)
+            return super.vfunc_button_release_event(buttonEvent);
+
         this.remove_style_pseudo_class('active');
-        this.activate(event);
+        this.activate(Clutter.get_current_event());
         return Clutter.EVENT_STOP;
     }
 
-    _onTouchEvent(actor, event) {
-        if (event.type() == Clutter.EventType.TOUCH_END) {
+    vfunc_touch_event(touchEvent) {
+        if (!this._activatable)
+            return super.vfunc_touch_event(touchEvent);
+
+        if (touchEvent.type == Clutter.EventType.TOUCH_END) {
             this.remove_style_pseudo_class('active');
-            this.activate(event);
+            this.activate(Clutter.get_current_event());
             return Clutter.EVENT_STOP;
-        } else if (event.type() == Clutter.EventType.TOUCH_BEGIN) {
+        } else if (touchEvent.type == Clutter.EventType.TOUCH_BEGIN) {
             // This is the CSS active state
             this.add_style_pseudo_class('active');
         }
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onKeyPressEvent(actor, event) {
-        let state = event.get_state();
+    vfunc_key_press_event(keyEvent) {
+        if (!this._activatable)
+            return super.vfunc_key_press_event(keyEvent);
+
+        let state = keyEvent.modifier_state;
 
         // if user has a modifier down (except capslock and numlock)
         // then don't handle the key press here
@@ -160,9 +166,9 @@ var PopupBaseMenuItem = GObject.registerClass({
         if (state)
             return Clutter.EVENT_PROPAGATE;
 
-        let symbol = event.get_key_symbol();
+        let symbol = keyEvent.keyval;
         if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
-            this.activate(event);
+            this.activate(Clutter.get_current_event());
             return Clutter.EVENT_STOP;
         }
         return Clutter.EVENT_PROPAGATE;
@@ -1185,8 +1191,8 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem {
         return this.menu.isOpen;
     }
 
-    _onKeyPressEvent(actor, event) {
-        let symbol = event.get_key_symbol();
+    vfunc_key_press_event(keyPressEvent) {
+        let symbol = keyPressEvent.keyval;
 
         if (symbol == Clutter.KEY_Right) {
             this._setOpenState(true);
@@ -1197,14 +1203,14 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem {
             return Clutter.EVENT_STOP;
         }
 
-        return super._onKeyPressEvent(actor, event);
+        return super.vfunc_key_press_event(keyPressEvent);
     }
 
     activate(_event) {
         this._setOpenState(true);
     }
 
-    _onButtonReleaseEvent() {
+    vfunc_button_release_event() {
         // Since we override the parent, we need to manage what the parent does
         // with the active style class
         this.remove_style_pseudo_class('active');
@@ -1212,8 +1218,8 @@ class PopupSubMenuMenuItem extends PopupBaseMenuItem {
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onTouchEvent(actor, event) {
-        if (event.type() == Clutter.EventType.TOUCH_END) {
+    vfunc_touch_event(touchEvent) {
+        if (touchEvent.type == Clutter.EventType.TOUCH_END) {
             // Since we override the parent, we need to manage what the parent does
             // with the active style class
             this.remove_style_pseudo_class('active');
diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js
index 5d27da3393..8f2c297820 100644
--- a/js/ui/screenshot.js
+++ b/js/ui/screenshot.js
@@ -239,13 +239,6 @@ var SelectArea = GObject.registerClass({
 
         this._grabHelper = new GrabHelper.GrabHelper(this);
 
-        this.connect('button-press-event',
-                     this._onButtonPress.bind(this));
-        this.connect('button-release-event',
-                     this._onButtonRelease.bind(this));
-        this.connect('motion-event',
-                     this._onMotionEvent.bind(this));
-
         let constraint = new Clutter.BindConstraint({ source: global.stage,
                                                       coordinate: Clutter.BindCoordinate.ALL });
         this.add_constraint(constraint);
@@ -276,11 +269,11 @@ var SelectArea = GObject.registerClass({
         });
     }
 
-    _onMotionEvent(actor, event) {
+    vfunc_motion_event(motionEvent) {
         if (this._startX == -1 || this._startY == -1 || this._result)
             return Clutter.EVENT_PROPAGATE;
 
-        [this._lastX, this._lastY] = event.get_coords();
+        [this._lastX, this._lastY] = [motionEvent.x, motionEvent.y];
         this._lastX = Math.floor(this._lastX);
         this._lastY = Math.floor(this._lastY);
         let geometry = this._getGeometry();
@@ -292,8 +285,8 @@ var SelectArea = GObject.registerClass({
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onButtonPress(actor, event) {
-        [this._startX, this._startY] = event.get_coords();
+    vfunc_button_press_event(buttonEvent) {
+        [this._startX, this._startY] = [buttonEvent.x, buttonEvent.y];
         this._startX = Math.floor(this._startX);
         this._startY = Math.floor(this._startY);
         this._rubberband.set_position(this._startX, this._startY);
@@ -301,7 +294,7 @@ var SelectArea = GObject.registerClass({
         return Clutter.EVENT_PROPAGATE;
     }
 
-    _onButtonRelease() {
+    vfunc_button_release_event() {
         this._result = this._getGeometry();
         this.ease({
             opacity: 0,
@@ -336,8 +329,6 @@ var PickPixel = GObject.registerClass({
 
         this._grabHelper = new GrabHelper.GrabHelper(this);
 
-        this.connect('button-release-event', this._onButtonRelease.bind(this));
-
         let constraint = new Clutter.BindConstraint({ source: global.stage,
                                                       coordinate: Clutter.BindCoordinate.ALL });
         this.add_constraint(constraint);
@@ -353,8 +344,8 @@ var PickPixel = GObject.registerClass({
         super.vfunc_show();
     }
 
-    _onButtonRelease(actor, event) {
-        let [x, y] = event.get_coords();
+    vfunc_button_release_event(buttonEvent) {
+        let { x, y } = buttonEvent;
         this._result = new Graphene.Point({ x, y });
         this._grabHelper.ungrab();
         return Clutter.EVENT_PROPAGATE;
diff --git a/js/ui/search.js b/js/ui/search.js
index 169ceab8a3..0d31edb3c7 100644
--- a/js/ui/search.js
+++ b/js/ui/search.js
@@ -55,8 +55,10 @@ var SearchResult = GObject.registerClass({
             x_align: St.Align.START,
             y_fill: true
         });
+    }
 
-        this.connect('clicked', this.activate.bind(this));
+    vfunc_clicked() {
+        this.activate();
     }
 
     activate() {
diff --git a/js/ui/shellMountOperation.js b/js/ui/shellMountOperation.js
index 4e664ded44..966a92ec73 100644
--- a/js/ui/shellMountOperation.js
+++ b/js/ui/shellMountOperation.js
@@ -71,11 +71,9 @@ var ListItem = GObject.registerClass({
         let labelBin = new St.Bin({ y_align: St.Align.MIDDLE,
                                     child: this._nameLabel });
         layout.add(labelBin);
-
-        this.connect('clicked', this._onClicked.bind(this));
     }
 
-    _onClicked() {
+    vfunc_clicked() {
         this.emit('activate');
         this._app.activate();
     }
diff --git a/js/ui/slider.js b/js/ui/slider.js
index 662716d670..946994803f 100644
--- a/js/ui/slider.js
+++ b/js/ui/slider.js
@@ -22,12 +22,7 @@ var Slider = GObject.registerClass({
             accessible_role: Atk.Role.SLIDER
         });
 
-        this.connect('button-press-event', this._startDragging.bind(this));
-        this.connect('touch-event', this._touchDragging.bind(this));
-        this.connect('scroll-event', this._onScrollEvent.bind(this));
-        this.connect('key-press-event', this.onKeyPressEvent.bind(this));
-
-        this._releaseId = this._motionId = 0;
+        this._releaseId = 0;
         this._dragging = false;
 
         this._customAccessible.connect('get-minimum-increment', this._getMinimumIncrement.bind(this));
@@ -62,8 +57,8 @@ var Slider = GObject.registerClass({
         cr.$dispose();
     }
 
-    _startDragging(actor, event) {
-        return this.startDragging(event);
+    vfunc_button_press_event() {
+        return this.startDragging(Clutter.get_current_event());
     }
 
     startDragging(event) {
@@ -83,11 +78,6 @@ var Slider = GObject.registerClass({
         this._grabbedDevice = device;
         this._grabbedSequence = sequence;
 
-        if (sequence == null) {
-            this._releaseId = this.connect('button-release-event', this._endDragging.bind(this));
-            this._motionId = this.connect('motion-event', this._motionEvent.bind(this));
-        }
-
         // We need to emit 'drag-begin' before moving the handle to make
         // sure that no 'notify::value' signal is emitted before this one.
         this.emit('drag-begin');
@@ -105,11 +95,6 @@ var Slider = GObject.registerClass({
                 this._releaseId = 0;
             }
 
-            if (this._motionId) {
-                this.disconnect(this._motionId);
-                this._motionId = 0;
-            }
-
             if (this._grabbedSequence != null)
                 this._grabbedDevice.sequence_ungrab(this._grabbedSequence);
             else
@@ -124,7 +109,15 @@ var Slider = GObject.registerClass({
         return Clutter.EVENT_STOP;
     }
 
-    _touchDragging(actor, event) {
+    vfunc_button_release_event() {
+        if (this._dragging && !this._grabbedSequence)
+            return this._endDragging();
+
+        return Clutter.EVENT_PROPAGATE;
+    }
+
+    vfunc_touch_event() {
+        let event = Clutter.get_current_event();
         let device = event.get_device();
         let sequence = event.get_event_sequence();
 
@@ -132,9 +125,9 @@ var Slider = GObject.registerClass({
             event.type() == Clutter.EventType.TOUCH_BEGIN) {
             this.startDragging(event);
             return Clutter.EVENT_STOP;
-        } else if (device.sequence_get_grabbed_actor(sequence) == actor) {
+        } else if (device.sequence_get_grabbed_actor(sequence) == this) {
             if (event.type() == Clutter.EventType.TOUCH_UPDATE)
-                return this._motionEvent(actor, event);
+                return this._motionEvent(this, event);
             else if (event.type() == Clutter.EventType.TOUCH_END)
                 return this._endDragging();
         }
@@ -165,8 +158,15 @@ var Slider = GObject.registerClass({
         return Clutter.EVENT_STOP;
     }
 
-    _onScrollEvent(actor, event) {
-        return this.scroll(event);
+    vfunc_scroll_event() {
+        return this.scroll(Clutter.get_current_event());
+    }
+
+    vfunc_motion_event() {
+        if (this._dragging && !this._grabbedSequence)
+            return this._motionEvent(this, Clutter.get_current_event());
+
+        return Clutter.EVENT_PROPAGATE;
     }
 
     _motionEvent(actor, event) {
@@ -176,8 +176,8 @@ var Slider = GObject.registerClass({
         return Clutter.EVENT_STOP;
     }
 
-    onKeyPressEvent(actor, event) {
-        let key = event.get_key_symbol();
+    vfunc_key_press_event(keyPressEvent) {
+        let key = keyPressEvent.keyval;
         if (key == Clutter.KEY_Right || key == Clutter.KEY_Left) {
             let delta = key == Clutter.KEY_Right ? 0.1 : -0.1;
             this.emit('drag-begin');
diff --git a/js/ui/status/brightness.js b/js/ui/status/brightness.js
index 2a933a524f..f33d9dd8bd 100644
--- a/js/ui/status/brightness.js
+++ b/js/ui/status/brightness.js
@@ -47,7 +47,7 @@ var Indicator = GObject.registerClass({
             return this._slider.startDragging(event);
         });
         this._item.connect('key-press-event', (actor, event) => {
-            return this._slider.onKeyPressEvent(actor, event);
+            return this._slider.emit('key-press-event', event);
         });
 
     }
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index f87057edb1..958473591a 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -630,7 +630,6 @@ var NMWirelessDialogItem = GObject.registerClass({
                       can_focus: true,
                       reactive: true });
 
-        this.connect('key-focus-in', () => this.emit('selected'));
         let action = new Clutter.ClickAction();
         action.connect('clicked', () => this.grab_key_focus());
         this.add_action(action);
@@ -659,6 +658,10 @@ var NMWirelessDialogItem = GObject.registerClass({
         this._sync();
     }
 
+    vfunc_key_focus_in() {
+        this.emit('selected');
+    }
+
     _sync() {
         this._signalIcon.icon_name = this._getSignalIcon();
     }
diff --git a/js/ui/status/system.js b/js/ui/status/system.js
index d66046c4c6..78079a8d58 100644
--- a/js/ui/status/system.js
+++ b/js/ui/status/system.js
@@ -34,7 +34,16 @@ class AltSwitcher extends St.Bin {
         this._clickAction.connect('long-press', this._onLongPress.bind(this));
 
         this.connect('destroy', this._onDestroy.bind(this));
-        this.connect('notify::mapped', () => (this._flipped = false));
+    }
+
+    vfunc_map() {
+        super.vfunc_map();
+        this._flipped = false;
+    }
+
+    vfunc_unmap() {
+        super.vfunc_unmap();
+        this._flipped = false;
     }
 
     _sync() {
diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js
index b25c00b90a..53b48a8da2 100644
--- a/js/ui/status/volume.js
+++ b/js/ui/status/volume.js
@@ -47,7 +47,7 @@ var StreamSlider = class {
             return this._slider.startDragging(event);
         });
         this.item.connect('key-press-event', (actor, event) => {
-            return this._slider.onKeyPressEvent(actor, event);
+            return this._slider.emit('key-press-event', event);
         });
 
         this._stream = null;
@@ -375,12 +375,10 @@ var Indicator = GObject.registerClass({
         });
 
         this.menu.addMenuItem(this._volumeMenu);
-
-        this.connect('scroll-event', this._onScrollEvent.bind(this));
     }
 
-    _onScrollEvent(actor, event) {
-        let result = this._volumeMenu.scroll(event);
+    vfunc_scroll_event() {
+        let result = this._volumeMenu.scroll(Clutter.get_current_event());
         if (result == Clutter.EVENT_PROPAGATE || this.menu.actor.mapped)
             return result;
 
diff --git a/js/ui/switcherPopup.js b/js/ui/switcherPopup.js
index 9bb690c6eb..cc49ddf73f 100644
--- a/js/ui/switcherPopup.js
+++ b/js/ui/switcherPopup.js
@@ -105,12 +105,6 @@ var SwitcherPopup = GObject.registerClass({
         this._haveModal = true;
         this._modifierMask = primaryModifier(mask);
 
-        this.connect('key-press-event', this._keyPressEvent.bind(this));
-        this.connect('key-release-event', this._keyReleaseEvent.bind(this));
-
-        this.connect('button-press-event', this._clickedOutside.bind(this));
-        this.connect('scroll-event', this._scrollEvent.bind(this));
-
         this.add_actor(this._switcherList);
         this._switcherList.connect('item-activated', this._itemActivated.bind(this));
         this._switcherList.connect('item-entered', this._itemEntered.bind(this));
@@ -166,9 +160,10 @@ var SwitcherPopup = GObject.registerClass({
         throw new GObject.NotImplementedError(`_keyPressHandler in ${this.constructor.name}`);
     }
 
-    _keyPressEvent(actor, event) {
-        let keysym = event.get_key_symbol();
-        let action = global.display.get_keybinding_action(event.get_key_code(), event.get_state());
+    vfunc_key_press_event(keyEvent) {
+        let keysym = keyEvent.keyval;
+        let action = global.display.get_keybinding_action(
+            keyEvent.hardware_keycode, keyEvent.modifier_state);
 
         this._disableHover();
 
@@ -183,13 +178,13 @@ var SwitcherPopup = GObject.registerClass({
         return Clutter.EVENT_STOP;
     }
 
-    _keyReleaseEvent(actor, event) {
+    vfunc_key_release_event(keyEvent) {
         if (this._modifierMask) {
             let [x_, y_, mods] = global.get_pointer();
             let state = mods & this._modifierMask;
 
             if (state == 0)
-                this._finish(event.get_time());
+                this._finish(keyEvent.time);
         } else {
             this._resetNoModsTimeout();
         }
@@ -197,7 +192,8 @@ var SwitcherPopup = GObject.registerClass({
         return Clutter.EVENT_STOP;
     }
 
-    _clickedOutside() {
+    vfunc_button_press_event() {
+        /* We clicked outside */
         this.fadeAndDestroy();
         return Clutter.EVENT_PROPAGATE;
     }
@@ -209,8 +205,8 @@ var SwitcherPopup = GObject.registerClass({
             this._select(this._next());
     }
 
-    _scrollEvent(actor, event) {
-        this._scrollHandler(event.get_scroll_direction());
+    vfunc_scroll_event(scrollEvent) {
+        this._scrollHandler(scrollEvent.scroll_direction);
         return Clutter.EVENT_PROPAGATE;
     }
 
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index 86403b0577..ac53fa41cd 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -163,13 +163,6 @@ var WindowClone = GObject.registerClass({
         clickAction.connect('long-press', this._onLongPress.bind(this));
         this.add_action(clickAction);
         this.connect('destroy', this._onDestroy.bind(this));
-        this.connect('key-press-event', this._onKeyPress.bind(this));
-
-        this.connect('enter-event', () => this.emit('show-chrome'));
-        this.connect('key-focus-in', () => this.emit('show-chrome'));
-
-        this.connect('leave-event', () => this.emit('hide-chrome'));
-        this.connect('key-focus-out', () => this.emit('hide-chrome'));
 
         this._draggable = DND.makeDraggable(this,
                                             { restoreOnSuccess: true,
@@ -369,8 +362,28 @@ var WindowClone = GObject.registerClass({
         this.emit('selected', global.get_current_time());
     }
 
-    _onKeyPress(actor, event) {
-        let symbol = event.get_key_symbol();
+    vfunc_enter_event(crossingEvent) {
+        this.emit('show-chrome');
+        return super.vfunc_enter_event(crossingEvent);
+    }
+
+    vfunc_leave_event(crossingEvent) {
+        this.emit('hide-chrome');
+        return super.vfunc_leave_event(crossingEvent);
+    }
+
+    vfunc_key_focus_in() {
+        super.vfunc_key_focus_in();
+        this.emit('show-chrome');
+    }
+
+    vfunc_key_focus_out() {
+        super.vfunc_key_focus_out();
+        this.emit('hide-chrome');
+    }
+
+    vfunc_key_press_event(keyEvent) {
+        let symbol = keyEvent.keyval;
         let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
         if (isEnter) {
             this._activate();
@@ -1168,11 +1181,11 @@ class Workspace extends St.Widget {
 
         this._positionWindowsFlags = 0;
         this._positionWindowsId = 0;
+    }
 
-        this.connect('notify::mapped', () => {
-            if (this.mapped)
-                this._syncActualGeometry();
-        });
+    vfunc_map() {
+        super.vfunc_map();
+        this._syncActualGeometry();
     }
 
     vfunc_get_focus_chain() {
diff --git a/js/ui/workspaceThumbnail.js b/js/ui/workspaceThumbnail.js
index f8e072e15b..ff23d9e2e7 100644
--- a/js/ui/workspaceThumbnail.js
+++ b/js/ui/workspaceThumbnail.js
@@ -74,9 +74,6 @@ var WindowClone = GObject.registerClass({
         });
         this._onPositionChanged();
 
-        this.connect('button-release-event', this._onButtonRelease.bind(this));
-        this.connect('touch-event', this._onTouchEvent.bind(this));
-
         this.connect('destroy', this._onDestroy.bind(this));
 
         this._draggable = DND.makeDraggable(this,
@@ -184,18 +181,22 @@ var WindowClone = GObject.registerClass({
         }
     }
 
-    _onButtonRelease(actor, event) {
-        this.emit('selected', event.get_time());
+    vfunc_button_press_event() {
+        return Clutter.EVENT_STOP;
+    }
+
+    vfunc_button_release_event(buttonEvent) {
+        this.emit('selected', buttonEvent.time);
 
         return Clutter.EVENT_STOP;
     }
 
-    _onTouchEvent(actor, event) {
-        if (event.type() != Clutter.EventType.TOUCH_END ||
-            !global.display.is_pointer_emulating_sequence(event.get_event_sequence()))
+    vfunc_touch_event(touchEvent) {
+        if (touchEvent.type != Clutter.EventType.TOUCH_END ||
+            !global.display.is_pointer_emulating_sequence(touchEvent.sequence))
             return Clutter.EVENT_PROPAGATE;
 
-        this.emit('selected', event.get_time());
+        this.emit('selected', touchEvent.time);
         return Clutter.EVENT_STOP;
     }
 
@@ -667,10 +668,6 @@ var ThumbnailsBox = GObject.registerClass({
 
         this._thumbnails = [];
 
-        this.connect('button-press-event', () => Clutter.EVENT_STOP);
-        this.connect('button-release-event', this._onButtonRelease.bind(this));
-        this.connect('touch-event', this._onTouchEvent.bind(this));
-
         Main.overview.connect('showing',
                               this._createThumbnails.bind(this));
         Main.overview.connect('hidden',
@@ -727,17 +724,17 @@ var ThumbnailsBox = GObject.registerClass({
             thumbnail.activate(time);
     }
 
-    _onButtonRelease(actor, event) {
-        let [stageX, stageY] = event.get_coords();
-        this._activateThumbnailAtPoint(stageX, stageY, event.get_time());
+    vfunc_button_release_event(buttonEvent) {
+        let { x, y } = buttonEvent;
+        this._activateThumbnailAtPoint(x, y, buttonEvent.time);
         return Clutter.EVENT_STOP;
     }
 
-    _onTouchEvent(actor, event) {
-        if (event.type() == Clutter.EventType.TOUCH_END &&
-            global.display.is_pointer_emulating_sequence(event.get_event_sequence())) {
-            let [stageX, stageY] = event.get_coords();
-            this._activateThumbnailAtPoint(stageX, stageY, event.get_time());
+    vfunc_touch_event(touchEvent) {
+        if (touchEvent.type == Clutter.EventType.TOUCH_END &&
+            global.display.is_pointer_emulating_sequence(touchEvent.sequence)) {
+            let { x, y } = touchEvent;
+            this._activateThumbnailAtPoint(x, y, touchEvent.time);
         }
 
         return Clutter.EVENT_STOP;
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 11b15c9519..03b1b2a49d 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -443,7 +443,6 @@ class WorkspacesDisplay extends St.Widget {
     _init() {
         super._init({ clip_to_allocation: true });
         this.connect('notify::allocation', this._updateWorkspacesActualGeometry.bind(this));
-        this.connect('parent-set', this._parentSet.bind(this));
 
         let clickAction = new Clutter.ClickAction();
         clickAction.connect('clicked', action => {
@@ -713,7 +712,7 @@ class WorkspacesDisplay extends St.Widget {
         return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows();
     }
 
-    _parentSet(actor, oldParent) {
+    vfunc_parent_set(oldParent) {
         if (oldParent && this._notifyOpacityId)
             oldParent.disconnect(this._notifyOpacityId);
         this._notifyOpacityId = 0;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]