[gnome-shell] cleanup: Use inheritance for Actor classes instead of composition



commit c4c5c4fd5ce1509fc0a60a77f0017337209e611c
Author: Marco Trevisan (Treviño) <mail 3v1n0 net>
Date:   Tue Jul 16 11:24:13 2019 +0200

    cleanup: Use inheritance for Actor classes instead of composition
    
    Remove the `this.actor = ...` and `this.actor._delegate = this` patterns in most
    of classes, by inheriting all the actor container classes.
    
    Uses interfaces when needed for making sure that multiple classes will implement
    some required methods or to avoid redefining the same code multiple times.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559

 js/gdm/authPrompt.js                |  95 ++++----
 js/gdm/loginDialog.js               | 182 +++++++-------
 js/ui/accessDialog.js               |   6 +-
 js/ui/altTab.js                     |  29 ++-
 js/ui/animation.js                  |  56 ++---
 js/ui/appDisplay.js                 | 470 ++++++++++++++++++++----------------
 js/ui/background.js                 |  56 ++---
 js/ui/calendar.js                   | 152 ++++++------
 js/ui/checkBox.js                   |  25 +-
 js/ui/components/keyring.js         |   8 +-
 js/ui/components/polkitAgent.js     |   8 +-
 js/ui/components/telepathyClient.js |  14 +-
 js/ui/dash.js                       |  33 +--
 js/ui/dateMenu.js                   | 106 ++++----
 js/ui/endSessionDialog.js           |  26 +-
 js/ui/ibusCandidatePopup.js         |  88 ++++---
 js/ui/iconGrid.js                   |   8 +-
 js/ui/keyboard.js                   | 183 +++++++-------
 js/ui/layout.js                     |  52 ++--
 js/ui/lightbox.js                   |  98 ++++----
 js/ui/lookingGlass.js               | 203 +++++++++-------
 js/ui/magnifier.js                  |  58 ++---
 js/ui/messageList.js                | 183 ++++++++------
 js/ui/messageTray.js                |  65 ++---
 js/ui/mpris.js                      |  20 +-
 js/ui/osdMonitorLabeler.js          |  26 +-
 js/ui/osdWindow.js                  |  43 ++--
 js/ui/overview.js                   |   2 +-
 js/ui/overviewControls.js           | 121 +++++-----
 js/ui/padOsd.js                     | 157 ++++++------
 js/ui/panel.js                      |  74 +++---
 js/ui/panelMenu.js                  |  29 ++-
 js/ui/screenShield.js               |  69 +++---
 js/ui/screenshot.js                 | 113 ++++-----
 js/ui/search.js                     | 186 +++++++-------
 js/ui/shellMountOperation.js        |  60 ++---
 js/ui/status/accessibility.js       |   2 +-
 js/ui/status/bluetooth.js           |  12 +-
 js/ui/status/brightness.js          |  10 +-
 js/ui/status/keyboard.js            |   4 +-
 js/ui/status/location.js            |  10 +-
 js/ui/status/network.js             |  14 +-
 js/ui/status/nightLight.js          |  12 +-
 js/ui/status/power.js               |  16 +-
 js/ui/status/remoteAccess.js        |  12 +-
 js/ui/status/rfkill.js              |  12 +-
 js/ui/status/screencast.js          |  12 +-
 js/ui/status/system.js              |  37 +--
 js/ui/status/thunderbolt.js         |  12 +-
 js/ui/status/volume.js              |  12 +-
 js/ui/unlockDialog.js               |   2 +-
 js/ui/userWidget.js                 |  76 +++---
 js/ui/viewSelector.js               |  30 ++-
 js/ui/windowManager.js              |  53 ++--
 js/ui/workspace.js                  |  72 +++---
 js/ui/workspaceThumbnail.js         |  79 +++---
 js/ui/workspacesView.js             | 164 ++++++-------
 tests/interactive/calendar.js       |   2 +-
 58 files changed, 2001 insertions(+), 1758 deletions(-)
---
diff --git a/js/gdm/authPrompt.js b/js/gdm/authPrompt.js
index 3704645147..1269ad8abf 100644
--- a/js/gdm/authPrompt.js
+++ b/js/gdm/authPrompt.js
@@ -1,7 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported AuthPrompt */
 
-const { Clutter, Pango, Shell, St } = imports.gi;
-const Signals = imports.signals;
+const { Clutter, GObject, Pango, Shell, St } = imports.gi;
 
 const Animation = imports.ui.animation;
 const Batch = imports.gdm.batch;
@@ -33,8 +33,21 @@ var BeginRequestType = {
     DONT_PROVIDE_USERNAME: 1
 };
 
-var AuthPrompt = class {
-    constructor(gdmClient, mode) {
+var AuthPrompt = GObject.registerClass({
+    Signals: {
+        'cancelled': {},
+        'failed': {},
+        'next': {},
+        'prompted': {},
+        'reset': { param_types: [GObject.TYPE_UINT] },
+    }
+}, class AuthPrompt extends St.BoxLayout {
+    _init(gdmClient, mode) {
+        super._init({
+            style_class: 'login-dialog-prompt-layout',
+            vertical: true
+        });
+
         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
 
         this._gdmClient = gdmClient;
@@ -67,38 +80,38 @@ var AuthPrompt = class {
             }
         });
 
-        this.actor = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
-                                        vertical: true });
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-        this.actor.connect('key-press-event', (actor, event) => {
+        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.actor.add(this._userWell,
-                       { x_align: St.Align.START,
-                         x_fill: true,
-                         y_fill: true,
-                         expand: true });
+        this._userWell = new St.Bin({ x_fill: true, x_align: St.Align.START });
+        this.add(this._userWell, {
+            x_align: St.Align.START,
+            x_fill: true,
+            y_fill: true,
+            expand: true
+        });
         this._label = new St.Label({ style_class: 'login-dialog-prompt-label' });
 
-        this.actor.add(this._label,
-                       { expand: true,
-                         x_fill: false,
-                         y_fill: true,
-                         x_align: St.Align.START });
+        this.add(this._label, {
+            expand: true,
+            x_fill: false,
+            y_fill: true,
+            x_align: St.Align.START
+        });
         this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
                                      can_focus: true });
         ShellEntry.addContextMenu(this._entry, { isPassword: true, actionMode: Shell.ActionMode.NONE });
 
-        this.actor.add(this._entry,
-                       { expand: true,
-                         x_fill: true,
-                         y_fill: false,
-                         x_align: St.Align.START });
+        this.add(this._entry, {
+            expand: true,
+            x_fill: true,
+            y_fill: false,
+            x_align: St.Align.START
+        });
 
         this._entry.grab_key_focus();
 
@@ -106,14 +119,15 @@ var AuthPrompt = class {
                                        styleClass: 'login-dialog-message' });
         this._message.clutter_text.line_wrap = true;
         this._message.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
-        this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START });
+        this.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START });
 
         this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
                                              vertical: false });
-        this.actor.add(this._buttonBox,
-                       { expand: true,
-                         x_align: St.Align.MIDDLE,
-                         y_align: St.Align.END });
+        this.add(this._buttonBox, {
+            expand: true,
+            x_align: St.Align.MIDDLE,
+            y_align: St.Align.END
+        });
 
         this._defaultButtonWell = new St.Widget({ layout_manager: new Clutter.BinLayout() });
         this._defaultButtonWellActor = null;
@@ -121,9 +135,9 @@ var AuthPrompt = class {
         this._initButtons();
 
         this._spinner = new Animation.Spinner(DEFAULT_BUTTON_WELL_ICON_SIZE);
-        this._spinner.actor.opacity = 0;
-        this._spinner.actor.show();
-        this._defaultButtonWell.add_child(this._spinner.actor);
+        this._spinner.opacity = 0;
+        this._spinner.show();
+        this._defaultButtonWell.add_child(this._spinner);
     }
 
     _onDestroy() {
@@ -269,13 +283,13 @@ var AuthPrompt = class {
             oldActor.remove_all_transitions();
 
         let wasSpinner;
-        if (oldActor == this._spinner.actor)
+        if (oldActor == this._spinner)
             wasSpinner = true;
         else
             wasSpinner = false;
 
         let isSpinner;
-        if (actor == this._spinner.actor)
+        if (actor == this._spinner)
             isSpinner = true;
         else
             isSpinner = false;
@@ -323,7 +337,7 @@ var AuthPrompt = class {
     }
 
     startSpinning() {
-        this.setActorInDefaultButtonWell(this._spinner.actor, true);
+        this.setActorInDefaultButtonWell(this._spinner, true);
     }
 
     stopSpinning() {
@@ -404,9 +418,9 @@ var AuthPrompt = class {
         this._entry.clutter_text.editable = sensitive;
     }
 
-    hide() {
+    vfunc_hide() {
         this.setActorInDefaultButtonWell(null, true);
-        this.actor.hide();
+        super.vfunc_hide();
         this._message.opacity = 0;
 
         this.setUser(null);
@@ -422,7 +436,7 @@ var AuthPrompt = class {
 
         if (user) {
             let userWidget = new UserWidget.UserWidget(user);
-            this._userWell.set_child(userWidget.actor);
+            this._userWell.set_child(userWidget);
         }
     }
 
@@ -507,5 +521,4 @@ var AuthPrompt = class {
         this.reset();
         this.emit('cancelled');
     }
-};
-Signals.addSignalMethods(AuthPrompt.prototype);
+});
diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
index c3f90dc584..acefb395f7 100644
--- a/js/gdm/loginDialog.js
+++ b/js/gdm/loginDialog.js
@@ -19,7 +19,6 @@
 
 const { AccountsService, Atk, Clutter, Gdm, Gio,
         GLib, GObject, Meta, Pango, Shell, St } = imports.gi;
-const Signals = imports.signals;
 
 const AuthPrompt = imports.gdm.authPrompt;
 const Batch = imports.gdm.batch;
@@ -39,56 +38,65 @@ const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
 const _LOGO_ICON_HEIGHT = 48;
 const _MAX_BOTTOM_MENU_ITEMS = 5;
 
-var UserListItem = class {
-    constructor(user) {
+var UserListItem = GObject.registerClass({
+    GTypeName: 'LoginDialog_UserListItem',
+    Signals: { 'activate': {} }
+}, class UserListItem extends St.Button {
+    _init(user) {
+        let layout = new St.BoxLayout({ vertical: true });
+        super._init({
+            style_class: 'login-dialog-user-list-item',
+            button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
+            can_focus: true,
+            child: layout,
+            reactive: true,
+            x_align: St.Align.START,
+            x_fill: true
+        });
+
         this.user = user;
         this._userChangedId = this.user.connect('changed',
                                                 this._onUserChanged.bind(this));
 
-        let layout = new St.BoxLayout({ vertical: true });
-        this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
-                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
-                                     can_focus: true,
-                                     child: layout,
-                                     reactive: true,
-                                     x_align: St.Align.START,
-                                     x_fill: true });
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-
-        this.actor.connect('key-focus-in', () => {
-            this._setSelected(true);
-        });
-        this.actor.connect('key-focus-out', () => {
-            this._setSelected(false);
-        });
-        this.actor.connect('notify::hover', () => {
-            this._setSelected(this.actor.hover);
+        this.connect('destroy', this._onDestroy.bind(this));
+        this.connect('notify::hover', () => {
+            this._setSelected(this.hover);
         });
 
         this._userWidget = new UserWidget.UserWidget(this.user);
-        layout.add(this._userWidget.actor);
+        layout.add(this._userWidget);
 
-        this._userWidget.actor.bind_property('label-actor', this.actor, 'label-actor',
-                                             GObject.BindingFlags.SYNC_CREATE);
+        this._userWidget.bind_property('label-actor', this, 'label-actor',
+                                       GObject.BindingFlags.SYNC_CREATE);
 
         this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
                                                  scale_x: 0,
                                                  visible: false });
         layout.add(this._timedLoginIndicator);
 
-        this.actor.connect('clicked', this._onClicked.bind(this));
+        this.connect('clicked', this._onClicked.bind(this));
         this._onUserChanged();
     }
 
+    vfunc_key_focus_in() {
+        super.vfunc_key_focus_in();
+        this._setSelected(true);
+    }
+
+    vfunc_key_focus_out() {
+        super.vfunc_key_focus_out();
+        this._setSelected(false);
+    }
+
     _onUserChanged() {
         this._updateLoggedIn();
     }
 
     _updateLoggedIn() {
         if (this.user.is_logged_in())
-            this.actor.add_style_pseudo_class('logged-in');
+            this.add_style_pseudo_class('logged-in');
         else
-            this.actor.remove_style_pseudo_class('logged-in');
+            this.remove_style_pseudo_class('logged-in');
     }
 
     _onDestroy() {
@@ -101,10 +109,10 @@ var UserListItem = class {
 
     _setSelected(selected) {
         if (selected) {
-            this.actor.add_style_pseudo_class('selected');
-            this.actor.grab_key_focus();
+            this.add_style_pseudo_class('selected');
+            this.grab_key_focus();
         } else {
-            this.actor.remove_style_pseudo_class('selected');
+            this.remove_style_pseudo_class('selected');
         }
     }
 
@@ -145,23 +153,28 @@ var UserListItem = class {
         this._timedLoginIndicator.visible = false;
         this._timedLoginIndicator.scale_x = 0.;
     }
-};
-Signals.addSignalMethods(UserListItem.prototype);
+});
 
-var UserList = class {
-    constructor() {
-        this.actor = new St.ScrollView({ style_class: 'login-dialog-user-list-view' });
-        this.actor.set_policy(St.PolicyType.NEVER,
-                              St.PolicyType.AUTOMATIC);
+var UserList = GObject.registerClass({
+    GTypeName: 'LoginDialog_UserList',
+    Signals: {
+        'activate': { param_types: [UserListItem.$gtype] },
+        'item-added': { param_types: [UserListItem.$gtype] },
+    }
+}, class UserList extends St.ScrollView {
+    _init() {
+        super._init({ style_class: 'login-dialog-user-list-view' });
+        this.set_policy(St.PolicyType.NEVER,
+                        St.PolicyType.AUTOMATIC);
 
         this._box = new St.BoxLayout({ vertical: true,
                                        style_class: 'login-dialog-user-list',
                                        pseudo_class: 'expanded' });
 
-        this.actor.add_actor(this._box);
+        this.add_actor(this._box);
         this._items = {};
 
-        this.actor.connect('key-focus-in', this._moveFocusToItems.bind(this));
+        this.connect('key-focus-in', this._moveFocusToItems.bind(this));
     }
 
     _moveFocusToItems() {
@@ -170,10 +183,10 @@ var UserList = class {
         if (!hasItems)
             return;
 
-        if (global.stage.get_key_focus() != this.actor)
+        if (global.stage.get_key_focus() != this)
             return;
 
-        let focusSet = this.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+        let focusSet = this.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
         if (!focusSet) {
             Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
                 this._moveFocusToItems();
@@ -194,14 +207,14 @@ var UserList = class {
 
         for (let userName in this._items) {
             let item = this._items[userName];
-            item.actor.sync_hover();
+            item.sync_hover();
         }
     }
 
     scrollToItem(item) {
-        let box = item.actor.get_allocation_box();
+        let box = item.get_allocation_box();
 
-        let adjustment = this.actor.get_vscroll_bar().get_adjustment();
+        let adjustment = this.get_vscroll_bar().get_adjustment();
 
         let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
         adjustment.ease(value, {
@@ -211,9 +224,9 @@ var UserList = class {
     }
 
     jumpToItem(item) {
-        let box = item.actor.get_allocation_box();
+        let box = item.get_allocation_box();
 
-        let adjustment = this.actor.get_vscroll_bar().get_adjustment();
+        let adjustment = this.get_vscroll_bar().get_adjustment();
 
         let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0);
 
@@ -251,14 +264,14 @@ var UserList = class {
         this.removeUser(user);
 
         let item = new UserListItem(user);
-        this._box.add(item.actor, { x_fill: true });
+        this._box.add(item, { x_fill: true });
 
         this._items[userName] = item;
 
         item.connect('activate', this._onItemActivated.bind(this));
 
         // Try to keep the focused item front-and-center
-        item.actor.connect('key-focus-in', () => this.scrollToItem(item));
+        item.connect('key-focus-in', () => this.scrollToItem(item));
 
         this._moveFocusToItems();
 
@@ -279,33 +292,38 @@ var UserList = class {
         if (!item)
             return;
 
-        item.actor.destroy();
+        item.destroy();
         delete this._items[userName];
     }
 
     numItems() {
         return Object.keys(this._items).length;
     }
-};
-Signals.addSignalMethods(UserList.prototype);
+});
 
-var SessionMenuButton = class {
-    constructor() {
+var SessionMenuButton = GObject.registerClass({
+    GTypeName: 'LoginDialog_SessionMenuButton',
+    Signals: { 'session-activated': { param_types: [GObject.TYPE_STRING] } }
+}, class SessionMenuButton extends St.Bin {
+    _init() {
         let gearIcon = new St.Icon({ icon_name: 'emblem-system-symbolic' });
-        this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
-                                       reactive: true,
-                                       track_hover: true,
-                                       can_focus: true,
-                                       accessible_name: _("Choose Session"),
-                                       accessible_role: Atk.Role.MENU,
-                                       child: gearIcon });
+        let button = new St.Button({
+            style_class: 'login-dialog-session-list-button',
+            reactive: true,
+            track_hover: true,
+            can_focus: true,
+            accessible_name: _("Choose Session"),
+            accessible_role: Atk.Role.MENU,
+            child: gearIcon
+        });
 
-        this.actor = new St.Bin({ child: this._button });
+        super._init({ child: button });
+        this._button = button;
 
         let side = St.Side.TOP;
         let align = 0;
         if (Gdm.get_session_ids().length > _MAX_BOTTOM_MENU_ITEMS) {
-            if (this.actor.text_direction == Clutter.TextDirection.RTL)
+            if (this.text_direction == Clutter.TextDirection.RTL)
                 side = St.Side.RIGHT;
             else
                 side = St.Side.LEFT;
@@ -384,15 +402,13 @@ var SessionMenuButton = class {
             });
         }
     }
-};
-Signals.addSignalMethods(SessionMenuButton.prototype);
+});
 
 var LoginDialog = GObject.registerClass({
     Signals: { 'failed': {} },
 }, class LoginDialog extends St.Widget {
     _init(parentActor) {
-        super._init({ style_class: 'login-dialog',
-                      visible: false });
+        super._init({ style_class: 'login-dialog', visible: false });
 
         this.get_accessible().set_role(Atk.Role.WINDOW);
 
@@ -426,7 +442,7 @@ var LoginDialog = GObject.registerClass({
         this.add_child(this._userSelectionBox);
 
         this._userList = new UserList();
-        this._userSelectionBox.add(this._userList.actor,
+        this._userSelectionBox.add(this._userList,
                                    { expand: true,
                                      x_fill: true,
                                      y_fill: true });
@@ -435,7 +451,7 @@ var LoginDialog = GObject.registerClass({
         this._authPrompt.connect('prompted', this._onPrompted.bind(this));
         this._authPrompt.connect('reset', this._onReset.bind(this));
         this._authPrompt.hide();
-        this.add_child(this._authPrompt.actor);
+        this.add_child(this._authPrompt);
 
         // translators: this message is shown below the user list on the
         // login screen. It can be activated to reveal an entry for
@@ -494,9 +510,9 @@ var LoginDialog = GObject.registerClass({
             (list, sessionId) => {
                 this._greeter.call_select_session_sync (sessionId, null);
             });
-        this._sessionMenuButton.actor.opacity = 0;
-        this._sessionMenuButton.actor.show();
-        this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
+        this._sessionMenuButton.opacity = 0;
+        this._sessionMenuButton.show();
+        this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton);
 
         this._disableUserList = undefined;
         this._userListLoaded = false;
@@ -579,8 +595,8 @@ var LoginDialog = GObject.registerClass({
 
         let authPromptAllocation = null;
         let authPromptWidth = 0;
-        if (this._authPrompt.actor.visible) {
-            authPromptAllocation = this._getCenterActorAllocation(dialogBox, this._authPrompt.actor);
+        if (this._authPrompt.visible) {
+            authPromptAllocation = this._getCenterActorAllocation(dialogBox, this._authPrompt);
             authPromptWidth = authPromptAllocation.x2 - authPromptAllocation.x1;
         }
 
@@ -690,7 +706,7 @@ var LoginDialog = GObject.registerClass({
         }
 
         if (authPromptAllocation)
-            this._authPrompt.actor.allocate(authPromptAllocation, flags);
+            this._authPrompt.allocate(authPromptAllocation, flags);
 
         if (userSelectionAllocation)
             this._userSelectionBox.allocate(userSelectionAllocation, flags);
@@ -794,7 +810,7 @@ var LoginDialog = GObject.registerClass({
     _onPrompted() {
         if (this._shouldShowSessionMenuButton()) {
             this._sessionMenuButton.updateSensitivity(true);
-            this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton.actor);
+            this._authPrompt.setActorInDefaultButtonWell(this._sessionMenuButton);
         } else {
             this._sessionMenuButton.updateSensitivity(false);
         }
@@ -854,11 +870,11 @@ var LoginDialog = GObject.registerClass({
     }
 
     _showPrompt() {
-        if (this._authPrompt.actor.visible)
+        if (this._authPrompt.visible)
             return;
-        this._authPrompt.actor.opacity = 0;
-        this._authPrompt.actor.show();
-        this._authPrompt.actor.ease({
+        this._authPrompt.opacity = 0;
+        this._authPrompt.show();
+        this._authPrompt.ease({
             opacity: 255,
             duration: _FADE_ANIMATION_TIME,
             mode: Clutter.AnimationMode.EASE_OUT_QUAD
@@ -1045,12 +1061,12 @@ var LoginDialog = GObject.registerClass({
                      () => {
                          // If idle timeout is done, make sure the timed login indicator is shown
                          if (delay > _TIMED_LOGIN_IDLE_THRESHOLD &&
-                             this._authPrompt.actor.visible)
+                             this._authPrompt.visible)
                              this._authPrompt.cancel();
 
                          if (delay > _TIMED_LOGIN_IDLE_THRESHOLD || firstRun) {
                              this._userList.scrollToItem(loginItem);
-                             loginItem.actor.grab_key_focus();
+                             loginItem.grab_key_focus();
                          }
                      },
 
@@ -1111,7 +1127,7 @@ var LoginDialog = GObject.registerClass({
         this._sessionMenuButton.close();
         this._setUserListExpanded(true);
         this._notListedButton.show();
-        this._userList.actor.grab_key_focus();
+        this._userList.grab_key_focus();
     }
 
     _beginVerificationForItem(item) {
@@ -1219,7 +1235,7 @@ var LoginDialog = GObject.registerClass({
                                         _("Login Window"),
                                         'dialog-password-symbolic',
                                         { sortGroup: CtrlAltTab.SortGroup.MIDDLE });
-        this._userList.actor.grab_key_focus();
+        this._userList.grab_key_focus();
         this.show();
         this.opacity = 0;
 
diff --git a/js/ui/accessDialog.js b/js/ui/accessDialog.js
index 30389eda46..abd32da7b4 100644
--- a/js/ui/accessDialog.js
+++ b/js/ui/accessDialog.js
@@ -56,8 +56,8 @@ class AccessDialog extends ModalDialog.ModalDialog {
 
             let check = new CheckBox.CheckBox();
             check.getLabelActor().text = name;
-            check.actor.checked = selected == "true";
-            content.insertBeforeBody(check.actor);
+            check.checked = selected == "true";
+            content.insertBeforeBody(check);
 
             this._choices.set(id, check);
         }
@@ -99,7 +99,7 @@ class AccessDialog extends ModalDialog.ModalDialog {
         let results = {};
         if (response == DialogResponse.OK) {
             for (let [id, check] of this._choices) {
-                let checked = check.actor.checked ? 'true' : 'false';
+                let checked = check.checked ? 'true' : 'false';
                 results[id] = new GLib.Variant('s', checked);
             }
         }
diff --git a/js/ui/altTab.js b/js/ui/altTab.js
index 4cf18e672d..2500a45d00 100644
--- a/js/ui/altTab.js
+++ b/js/ui/altTab.js
@@ -405,27 +405,26 @@ class AppSwitcherPopup extends SwitcherPopup.SwitcherPopup {
     }
 });
 
-class CyclerHighlight {
-    constructor() {
+var CyclerHighlight = GObject.registerClass(
+class CyclerHighlight extends St.Widget {
+    _init() {
+        super._init({ layout_manager: new Clutter.BinLayout() });
         this._window = null;
 
-        this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout() });
-
         this._clone = new Clutter.Clone();
-        this.actor.add_actor(this._clone);
+        this.add_actor(this._clone);
 
         this._highlight = new St.Widget({ style_class: 'cycler-highlight' });
-        this.actor.add_actor(this._highlight);
+        this.add_actor(this._highlight);
 
         let coordinate = Clutter.BindCoordinate.ALL;
         let constraint = new Clutter.BindConstraint({ coordinate: coordinate });
         this._clone.bind_property('source', constraint, 'source', 0);
 
-        this.actor.add_constraint(constraint);
+        this.add_constraint(constraint);
 
-        this.actor.connect('notify::allocation',
-                           this._onAllocationChanged.bind(this));
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('notify::allocation', this._onAllocationChanged.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
     set window(w) {
@@ -451,7 +450,7 @@ class CyclerHighlight {
             this._highlight.set_size(0, 0);
             this._highlight.hide();
         } else {
-            let [x, y] = this.actor.allocation.get_origin();
+            let [x, y] = this.allocation.get_origin();
             let rect = this._window.get_frame_rect();
             this._highlight.set_size(rect.width, rect.height);
             this._highlight.set_position(rect.x - x, rect.y - y);
@@ -462,7 +461,7 @@ class CyclerHighlight {
     _onDestroy() {
         this.window = null;
     }
-}
+});
 
 // We don't show an actual popup, so just provide what SwitcherPopup
 // expects instead of inheriting from SwitcherList
@@ -489,7 +488,7 @@ var CyclerPopup = GObject.registerClass({
             return;
 
         this._highlight = new CyclerHighlight();
-        global.window_group.add_actor(this._highlight.actor);
+        global.window_group.add_actor(this._highlight);
 
         this._switcherList = new CyclerList();
         this._switcherList.connect('item-highlighted', (list, index) => {
@@ -499,7 +498,7 @@ var CyclerPopup = GObject.registerClass({
 
     _highlightItem(index, _justOutline) {
         this._highlight.window = this._items[index];
-        global.window_group.set_child_above_sibling(this._highlight.actor, null);
+        global.window_group.set_child_above_sibling(this._highlight, null);
     }
 
     _finish() {
@@ -529,7 +528,7 @@ var CyclerPopup = GObject.registerClass({
     }
 
     _onDestroy() {
-        this._highlight.actor.destroy();
+        this._highlight.destroy();
 
         super._onDestroy();
     }
diff --git a/js/ui/animation.js b/js/ui/animation.js
index 3ad1358d57..b8d5ad5c32 100644
--- a/js/ui/animation.js
+++ b/js/ui/animation.js
@@ -1,18 +1,18 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported Animation, AnimatedIcon, Spinner */
 
-const { Clutter, GLib, Gio, St } = imports.gi;
+const { Clutter, GLib, GObject, Gio, St } = imports.gi;
 
 var ANIMATED_ICON_UPDATE_TIMEOUT = 16;
 var SPINNER_ANIMATION_TIME = 300;
 var SPINNER_ANIMATION_DELAY = 1000;
 
-var Animation = class {
-    constructor(file, width, height, speed) {
-        this.actor = new St.Bin();
-        this.actor.set_size(width, height);
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-        this.actor.connect('resource-scale-changed',
+var Animation = GObject.registerClass(
+class Animation extends St.Bin {
+    _init(file, width, height, speed) {
+        super._init({ width: width, height: height });
+        this.connect('destroy', this._onDestroy.bind(this));
+        this.connect('resource-scale-changed',
             this._loadFile.bind(this, file, width, height));
 
         let themeContext = St.ThemeContext.get_for_stage(global.stage);
@@ -51,14 +51,14 @@ var Animation = class {
     }
 
     _loadFile(file, width, height) {
-        let [validResourceScale, resourceScale] = this.actor.get_resource_scale();
+        let [validResourceScale, resourceScale] = this.get_resource_scale();
         let wasPlaying = this._isPlaying;
 
         if (this._isPlaying)
             this.stop();
 
         this._isLoaded = false;
-        this.actor.destroy_all_children();
+        this.destroy_all_children();
 
         if (!validResourceScale) {
             if (wasPlaying)
@@ -71,7 +71,7 @@ var Animation = class {
         this._animations = textureCache.load_sliced_image(file, width, height,
                                                           scaleFactor, resourceScale,
                                                           this._animationsLoaded.bind(this));
-        this.actor.set_child(this._animations);
+        this.set_child(this._animations);
 
         if (wasPlaying)
             this.play();
@@ -98,7 +98,7 @@ var Animation = class {
         if (!this._isLoaded)
             return;
 
-        let [width, height] = this.actor.get_size();
+        let [width, height] = this.get_size();
 
         for (let i = 0; i < this._animations.get_n_children(); ++i)
             this._animations.get_child_at_index(i).set_size(width, height);
@@ -121,20 +121,22 @@ var Animation = class {
             themeContext.disconnect(this._scaleChangedId);
         this._scaleChangedId = 0;
     }
-};
+});
 
-var AnimatedIcon = class extends Animation {
-    constructor(file, size) {
-        super(file, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
+var AnimatedIcon = GObject.registerClass(
+class AnimatedIcon extends Animation {
+    _init(file, size) {
+        super._init(file, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
     }
-};
+});
 
-var Spinner = class extends AnimatedIcon {
-    constructor(size, animate = false) {
+var Spinner = GObject.registerClass(
+class Spinner extends AnimatedIcon {
+    _init(size, animate = false) {
         let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg');
-        super(file, size);
+        super._init(file, size);
 
-        this.actor.opacity = 0;
+        this.opacity = 0;
         this._animate = animate;
     }
 
@@ -144,35 +146,35 @@ var Spinner = class extends AnimatedIcon {
     }
 
     play() {
-        this.actor.remove_all_transitions();
+        this.remove_all_transitions();
 
         if (this._animate) {
             super.play();
-            this.actor.ease({
+            this.ease({
                 opacity: 255,
                 delay: SPINNER_ANIMATION_DELAY,
                 duration: SPINNER_ANIMATION_TIME,
                 mode: Clutter.AnimationMode.LINEAR
             });
         } else {
-            this.actor.opacity = 255;
+            this.opacity = 255;
             super.play();
         }
     }
 
     stop() {
-        this.actor.remove_all_transitions();
+        this.remove_all_transitions();
 
         if (this._animate) {
-            this.actor.ease({
+            this.ease({
                 opacity: 0,
                 duration: SPINNER_ANIMATION_TIME,
                 mode: Clutter.AnimationMode.LINEAR,
                 onComplete: () => super.stop()
             });
         } else {
-            this.actor.opacity = 0;
+            this.opacity = 0;
             super.stop();
         }
     }
-};
+});
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 1754d9c70d..6edf03c406 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -84,10 +84,11 @@ function clamp(value, min, max) {
 }
 
 function _getViewFromIcon(icon) {
-    let parent = icon.actor.get_parent();
-    if (!parent._delegate || !(parent._delegate instanceof BaseAppView))
-        return null;
-    return parent._delegate;
+    for (let parent = icon.get_parent(); parent; parent = parent.get_parent()) {
+        if (parent instanceof BaseAppView)
+            return parent;
+    }
+    return null;
 }
 
 function _findBestFolderName(apps) {
@@ -128,20 +129,29 @@ function _findBestFolderName(apps) {
     return null;
 }
 
-class BaseAppView {
-    constructor(params, gridParams) {
-        if (this.constructor === BaseAppView)
-            throw new TypeError(`Cannot instantiate abstract class ${this.constructor.name}`);
-
-        gridParams = Params.parse(gridParams, { xAlign: St.Align.MIDDLE,
-                                                columnLimit: MAX_COLUMNS,
-                                                minRows: MIN_ROWS,
-                                                minColumns: MIN_COLUMNS,
-                                                fillParent: false,
-                                                padWithSpacing: true });
-        params = Params.parse(params, { usePagination: false });
-
-        if (params.usePagination)
+var BaseAppView = GObject.registerClass({
+    GTypeFlags: GObject.TypeFlags.ABSTRACT,
+    Properties: {
+        'use-pagination': GObject.ParamSpec.boolean(
+            'use-pagination', 'use-pagination', 'use-pagination',
+            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+            false)
+    },
+    Signals: {
+        'view-loaded': {},
+    }
+}, class BaseAppView extends St.Widget {
+    _init(params = {}, gridParams) {
+        super._init(params);
+
+        gridParams = Params.parse(gridParams, {
+            columnLimit: MAX_COLUMNS,
+            minRows: MIN_ROWS,
+            minColumns: MIN_COLUMNS,
+            padWithSpacing: true
+        }, true);
+
+        if (this.use_pagination)
             this._grid = new IconGrid.PaginatedIconGrid(gridParams);
         else
             this._grid = new IconGrid.IconGrid(gridParams);
@@ -201,17 +211,17 @@ class BaseAppView {
 
     _selectAppInternal(id) {
         if (this._items[id])
-            this._items[id].actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+            this._items[id].navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
         else
             log(`No such application ${id}`);
     }
 
     selectApp(id) {
-        if (this._items[id] && this._items[id].actor.mapped) {
+        if (this._items[id] && this._items[id].mapped) {
             this._selectAppInternal(id);
         } else if (this._items[id]) {
             // Need to wait until the view is mapped
-            let signalId = this._items[id].actor.connect('notify::mapped',
+            let signalId = this._items[id].connect('notify::mapped',
                 actor => {
                     if (actor.mapped) {
                         actor.disconnect(signalId);
@@ -253,7 +263,7 @@ class BaseAppView {
     }
 
     animateSwitch(animationDirection) {
-        this.actor.remove_all_transitions();
+        this.remove_all_transitions();
         this._grid.remove_all_transitions();
 
         let params = {
@@ -261,23 +271,34 @@ class BaseAppView {
             mode: Clutter.AnimationMode.EASE_OUT_QUAD
         };
         if (animationDirection == IconGrid.AnimationDirection.IN) {
-            this.actor.show();
+            this.show();
             params.opacity = 255;
             params.delay = VIEWS_SWITCH_ANIMATION_DELAY;
         } else {
             params.opacity = 0;
             params.delay = 0;
-            params.onComplete = () => this.actor.hide();
+            params.onComplete = () => this.hide();
         }
 
         this._grid.ease(params);
     }
-}
-Signals.addSignalMethods(BaseAppView.prototype);
 
-var AllView = class AllView extends BaseAppView {
-    constructor() {
-        super({ usePagination: true }, null);
+    adaptToSize(_width, _height) {
+        throw new GObject.NotImplementedError(`adaptToSize in ${this.constructor.name}`);
+    }
+});
+
+var AllView = GObject.registerClass({
+    Signals: { 'space-ready': {} }
+}, class AllView extends BaseAppView {
+    _init() {
+        super._init({
+            layout_manager: new Clutter.BinLayout(),
+            x_expand: true,
+            y_expand: true,
+            use_pagination: true
+        });
+
         this._scrollView = new St.ScrollView({ style_class: 'all-apps',
                                                x_expand: true,
                                                y_expand: true,
@@ -285,10 +306,7 @@ var AllView = class AllView extends BaseAppView {
                                                y_fill: false,
                                                reactive: true,
                                                y_align: St.Align.START });
-
-        this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
-                                     x_expand: true, y_expand: true });
-        this.actor.add_actor(this._scrollView);
+        this.add_actor(this._scrollView);
         this._grid._delegate = this;
 
         this._scrollView.set_policy(St.PolicyType.NEVER,
@@ -301,7 +319,7 @@ var AllView = class AllView extends BaseAppView {
                 this.goToPage(pageIndex);
             });
         this._pageIndicators.connect('scroll-event', this._onScroll.bind(this));
-        this.actor.add_actor(this._pageIndicators);
+        this.add_actor(this._pageIndicators);
 
         this.folderIcons = [];
 
@@ -332,7 +350,7 @@ var AllView = class AllView extends BaseAppView {
 
             let [x, y] = this._clickAction.get_coords();
             let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
-            if (!this._currentPopup.actor.contains(actor))
+            if (!this._currentPopup.contains(actor))
                 this._currentPopup.popdown();
         });
         this._eventBlocker.add_action(this._clickAction);
@@ -355,8 +373,8 @@ var AllView = class AllView extends BaseAppView {
             this._displayingPopup = false;
         });
 
-        this.actor.connect('notify::mapped', () => {
-            if (this.actor.mapped) {
+        this.connect('notify::mapped', () => {
+            if (this.mapped) {
                 this._keyPressEventId =
                     global.stage.connect('key-press-event',
                                          this._onKeyPressEvent.bind(this));
@@ -367,7 +385,7 @@ var AllView = class AllView extends BaseAppView {
             }
         });
 
-        this._redisplayWorkId = Main.initializeDeferredWork(this.actor, this._redisplay.bind(this));
+        this._redisplayWorkId = Main.initializeDeferredWork(this, this._redisplay.bind(this));
 
         Shell.AppSystem.get_default().connect('installed-changed', () => {
             Main.queueDeferredWork(this._redisplayWorkId);
@@ -400,23 +418,23 @@ var AllView = class AllView extends BaseAppView {
     }
 
     _refilterApps() {
-        let filteredApps = this._allItems.filter(icon => !icon.actor.visible);
+        let filteredApps = this._allItems.filter(icon => !icon.visible);
 
         this._allItems.forEach(icon => {
             if (icon instanceof AppIcon)
-                icon.actor.visible = true;
+                icon.visible = true;
         });
 
         this.folderIcons.forEach(folder => {
             let folderApps = folder.getAppIds();
             folderApps.forEach(appId => {
                 let appIcon = this._items[appId];
-                appIcon.actor.visible = false;
+                appIcon.visible = false;
             });
         });
 
         // Scale in app icons that weren't visible, but now are
-        filteredApps.filter(icon => icon.actor.visible).forEach(icon => {
+        filteredApps.filter(icon => icon.visible).forEach(icon => {
             if (icon instanceof AppIcon)
                 icon.scaleIn();
         });
@@ -503,7 +521,7 @@ var AllView = class AllView extends BaseAppView {
 
         if (this._currentPopup && this._displayingPopup &&
             animationDirection == IconGrid.AnimationDirection.OUT)
-            this._currentPopup.actor.ease({
+            this._currentPopup.ease({
                 opacity: 0,
                 duration: VIEWS_SWITCH_TIME,
                 mode: Clutter.AnimationMode.EASE_OUT_QUAD,
@@ -526,7 +544,7 @@ var AllView = class AllView extends BaseAppView {
         if (this._displayingPopup && this._currentPopup)
             this._currentPopup.popdown();
 
-        if (!this.actor.mapped) {
+        if (!this.mapped) {
             this._adjustment.value = this._grid.getPageY(pageNumber);
             this._pageIndicators.setCurrentPage(pageNumber);
             this._grid.currentPage = pageNumber;
@@ -648,12 +666,12 @@ var AllView = class AllView extends BaseAppView {
     }
 
     addFolderPopup(popup) {
-        this._stack.add_actor(popup.actor);
+        this._stack.add_actor(popup);
         popup.connect('open-state-changed', (popup, isOpen) => {
             this._eventBlocker.reactive = isOpen;
 
             if (this._currentPopup) {
-                this._currentPopup.actor.disconnect(this._currentPopupDestroyId);
+                this._currentPopup.disconnect(this._currentPopupDestroyId);
                 this._currentPopupDestroyId = 0;
             }
 
@@ -661,7 +679,7 @@ var AllView = class AllView extends BaseAppView {
 
             if (isOpen) {
                 this._currentPopup = popup;
-                this._currentPopupDestroyId = popup.actor.connect('destroy', () => {
+                this._currentPopupDestroyId = popup.connect('destroy', () => {
                     this._currentPopup = null;
                     this._currentPopupDestroyId = 0;
                     this._eventBlocker.reactive = false;
@@ -681,11 +699,11 @@ var AllView = class AllView extends BaseAppView {
     _updateIconOpacities(folderOpen) {
         for (let id in this._items) {
             let opacity;
-            if (folderOpen && !this._items[id].actor.checked)
+            if (folderOpen && !this._items[id].checked)
                 opacity =  INACTIVE_GRID_OPACITY;
             else
                 opacity = 255;
-            this._items[id].actor.ease({
+            this._items[id].ease({
                 opacity: opacity,
                 duration: INACTIVE_GRID_OPACITY_ANIMATION_TIME,
                 mode: Clutter.AnimationMode.EASE_OUT_QUAD
@@ -700,7 +718,7 @@ var AllView = class AllView extends BaseAppView {
         box.x2 = width;
         box.y1 = 0;
         box.y2 = height;
-        box = this.actor.get_theme_node().get_content_box(box);
+        box = this.get_theme_node().get_content_box(box);
         box = this._scrollView.get_theme_node().get_content_box(box);
         box = this._grid.get_theme_node().get_content_box(box);
         let availWidth = box.x2 - box.x1;
@@ -733,8 +751,8 @@ var AllView = class AllView extends BaseAppView {
     }
 
     _handleDragOvershoot(dragEvent) {
-        let [, gridY] = this.actor.get_transformed_position();
-        let [, gridHeight] = this.actor.get_transformed_size();
+        let [, gridY] = this.get_transformed_position();
+        let [, gridHeight] = this.get_transformed_size();
         let gridBottom = gridY + gridHeight;
 
         // Within the grid boundaries, or already animating
@@ -773,7 +791,7 @@ var AllView = class AllView extends BaseAppView {
         // Handle the drag overshoot. When dragging to above the
         // icon grid, move to the page above; when dragging below,
         // move to the page below.
-        if (this._grid.contains(appIcon.actor))
+        if (this._grid.contains(appIcon))
             this._handleDragOvershoot(dragEvent);
 
         return DND.DragMotionResult.CONTINUE;
@@ -860,16 +878,17 @@ var AllView = class AllView extends BaseAppView {
 
         return true;
     }
-};
-Signals.addSignalMethods(AllView.prototype);
-
-var FrequentView = class FrequentView extends BaseAppView {
-    constructor() {
-        super(null, { fillParent: true });
+});
 
-        this.actor = new St.Widget({ style_class: 'frequent-apps',
-                                     layout_manager: new Clutter.BinLayout(),
-                                     x_expand: true, y_expand: true });
+var FrequentView = GObject.registerClass(
+class FrequentView extends BaseAppView {
+    _init() {
+        super._init({
+            style_class: 'frequent-apps',
+            layout_manager: new Clutter.BinLayout(),
+            x_expand: true,
+            y_expand: true
+        }, { fillParent: true });
 
         this._noFrequentAppsLabel = new St.Label({ text: _("Frequently used applications will appear here"),
                                                    style_class: 'no-frequent-applications-label',
@@ -880,14 +899,14 @@ var FrequentView = class FrequentView extends BaseAppView {
 
         this._grid.y_expand = true;
 
-        this.actor.add_actor(this._grid);
-        this.actor.add_actor(this._noFrequentAppsLabel);
+        this.add_actor(this._grid);
+        this.add_actor(this._noFrequentAppsLabel);
         this._noFrequentAppsLabel.hide();
 
         this._usage = Shell.AppUsage.get_default();
 
-        this.actor.connect('notify::mapped', () => {
-            if (this.actor.mapped)
+        this.connect('notify::mapped', () => {
+            if (this.mapped)
                 this._redisplay();
         });
     }
@@ -934,13 +953,13 @@ var FrequentView = class FrequentView extends BaseAppView {
         box.x1 = box.y1 = 0;
         box.x2 = width;
         box.y2 = height;
-        box = this.actor.get_theme_node().get_content_box(box);
+        box = this.get_theme_node().get_content_box(box);
         box = this._grid.get_theme_node().get_content_box(box);
         let availWidth = box.x2 - box.x1;
         let availHeight = box.y2 - box.y1;
         this._grid.adaptToSize(availWidth, availHeight);
     }
-};
+});
 
 var Views = {
     FREQUENT: 0,
@@ -984,8 +1003,16 @@ var ViewStackLayout = GObject.registerClass({
     }
 });
 
-var AppDisplay = class AppDisplay {
-    constructor() {
+var AppDisplay = GObject.registerClass(
+class AppDisplay extends St.BoxLayout {
+    _init() {
+        super._init({
+            style_class: 'app-display',
+            vertical: true,
+            x_expand: true,
+            y_expand: true
+        });
+
         this._privacySettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.privacy' });
         this._privacySettings.connect('changed::remember-app-usage',
                                       this._updateFrequentVisibility.bind(this));
@@ -1007,14 +1034,11 @@ var AppDisplay = class AppDisplay {
                                  x_expand: true });
         this._views[Views.ALL] = { 'view': view, 'control': button };
 
-        this.actor = new St.BoxLayout ({ style_class: 'app-display',
-                                         x_expand: true, y_expand: true,
-                                         vertical: true });
         this._viewStackLayout = new ViewStackLayout();
         this._viewStack = new St.Widget({ x_expand: true, y_expand: true,
                                           layout_manager: this._viewStackLayout });
         this._viewStackLayout.connect('allocated-size-changed', this._onAllocatedSizeChanged.bind(this));
-        this.actor.add_actor(this._viewStack);
+        this.add_actor(this._viewStack);
         let layout = new ControlsBoxLayout({ homogeneous: true });
         this._controls = new St.Widget({ style_class: 'app-view-controls',
                                          layout_manager: layout });
@@ -1031,10 +1055,10 @@ var AppDisplay = class AppDisplay {
         });
 
         layout.hookup_style(this._controls);
-        this.actor.add_actor(new St.Bin({ child: this._controls }));
+        this.add_actor(new St.Bin({ child: this._controls }));
 
         for (let i = 0; i < this._views.length; i++) {
-            this._viewStack.add_actor(this._views[i].view.actor);
+            this._viewStack.add_actor(this._views[i].view);
             this._controls.add_actor(this._views[i].control);
 
             let viewIndex = i;
@@ -1122,7 +1146,7 @@ var AppDisplay = class AppDisplay {
         let visibleViews = this._views.filter(v => v.control.visible);
         this._controls.visible = visibleViews.length > 1;
 
-        if (!enabled && this._views[Views.FREQUENT].view.actor.visible)
+        if (!enabled && this._views[Views.FREQUENT].view.visible)
             this._showView(Views.ALL);
     }
 
@@ -1142,7 +1166,7 @@ var AppDisplay = class AppDisplay {
         for (let i = 0; i < this._views.length; i++)
             this._views[i].view.adaptToSize(availWidth, availHeight);
     }
-};
+});
 
 var AppSearchProvider = class AppSearchProvider {
     constructor() {
@@ -1217,9 +1241,15 @@ var AppSearchProvider = class AppSearchProvider {
     }
 };
 
-var FolderView = class FolderView extends BaseAppView {
-    constructor(folder, id, parentView) {
-        super(null, null);
+var FolderView = GObject.registerClass(
+class FolderView extends BaseAppView {
+    _init(folder, id, parentView) {
+        super._init({
+            layout_manager: new Clutter.BinLayout(),
+            x_expand: true,
+            y_expand: true
+        });
+
         // If it not expand, the parent doesn't take into account its preferred_width when allocating
         // the second time it allocates, so we apply the "Standard hack for ClutterBinLayout"
         this._grid.x_expand = true;
@@ -1228,7 +1258,6 @@ var FolderView = class FolderView extends BaseAppView {
         this._parentView = parentView;
         this._grid._delegate = this;
 
-        this.actor = new St.Widget();
         this._scrollView = new St.ScrollView({
             overlay_scrollbars: true,
             x_fill: true,
@@ -1237,7 +1266,7 @@ var FolderView = class FolderView extends BaseAppView {
             y_expand: true
         });
         this._scrollView.set_policy(St.PolicyType.NEVER, St.PolicyType.AUTOMATIC);
-        this.actor.add_actor(this._scrollView);
+        this.add_actor(this._scrollView);
 
         let scrollableContainer = new St.BoxLayout({ vertical: true, reactive: true });
         scrollableContainer.add_actor(this._grid);
@@ -1307,8 +1336,8 @@ var FolderView = class FolderView extends BaseAppView {
         this._grid.leftPadding = Math.max(this._grid.leftPadding - this._offsetForEachSide, 0);
         this._grid.rightPadding = Math.max(this._grid.rightPadding - this._offsetForEachSide, 0);
 
-        this._scrollView.set_width(this.usedWidth());
-        this._scrollView.set_height(this.usedHeight());
+        this.set_width(this.usedWidth());
+        this.set_height(this.usedHeight());
     }
 
     _getPageAvailableSize() {
@@ -1317,7 +1346,7 @@ var FolderView = class FolderView extends BaseAppView {
         pageBox.x2 = this._parentAvailableWidth;
         pageBox.y2 = this._parentAvailableHeight;
 
-        let contentBox = this.actor.get_theme_node().get_content_box(pageBox);
+        let contentBox = this.get_theme_node().get_content_box(pageBox);
         // We only can show icons inside the collection view boxPointer
         // so we have to subtract the required padding etc of the boxpointer
         return [(contentBox.x2 - contentBox.x1) - 2 * this._offsetForEachSide, (contentBox.y2 - 
contentBox.y1) - 2 * this._offsetForEachSide];
@@ -1413,23 +1442,31 @@ var FolderView = class FolderView extends BaseAppView {
 
         return true;
     }
-};
+});
 
-var FolderIcon = class FolderIcon {
-    constructor(id, path, parentView) {
+var FolderIcon = GObject.registerClass({
+    GTypeName: 'AppDisplay_FolderIcon',
+    Signals: {
+        'apps-changed': {},
+        'name-changed': {},
+    }
+}, class FolderIcon extends St.Button {
+    _init(id, path, parentView) {
+        super._init({
+            style_class: 'app-well-app app-folder',
+            button_mask: St.ButtonMask.ONE,
+            toggle_mode: true,
+            can_focus: true,
+            x_fill: true,
+            y_fill: true
+        });
         this.id = id;
         this.name = '';
         this._parentView = parentView;
 
         this._folder = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders.folder',
                                           path: path });
-        this.actor = new St.Button({ style_class: 'app-well-app app-folder',
-                                     button_mask: St.ButtonMask.ONE,
-                                     toggle_mode: true,
-                                     can_focus: true,
-                                     x_fill: true,
-                                     y_fill: true });
-        this.actor._delegate = this;
+        this._delegate = this;
         // whether we need to update arrow side, position etc.
         this._popupInvalidated = false;
 
@@ -1437,8 +1474,8 @@ var FolderIcon = class FolderIcon {
             createIcon: this._createIcon.bind(this),
             setSizeManually: true
         });
-        this.actor.set_child(this.icon);
-        this.actor.label_actor = this.icon.label;
+        this.set_child(this.icon);
+        this.label_actor = this.icon.label;
 
         this.view = new FolderView(this._folder, id, parentView);
 
@@ -1449,15 +1486,15 @@ var FolderIcon = class FolderIcon {
 
         this._popupTimeoutId = 0;
 
-        this.actor.connect('leave-event', this._onLeaveEvent.bind(this));
-        this.actor.connect('button-press-event', this._onButtonPress.bind(this));
-        this.actor.connect('touch-event', this._onTouchEvent.bind(this));
-        this.actor.connect('popup-menu', this._popupRenamePopup.bind(this));
+        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.actor.connect('clicked', this.open.bind(this));
-        this.actor.connect('destroy', this.onDestroy.bind(this));
-        this.actor.connect('notify::mapped', () => {
-            if (!this.actor.mapped && this._popup)
+        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();
         });
 
@@ -1465,11 +1502,11 @@ var FolderIcon = class FolderIcon {
         this._redisplay();
     }
 
-    onDestroy() {
+    _onDestroy() {
         Main.overview.disconnect(this._itemDragBeginId);
         Main.overview.disconnect(this._itemDragEndId);
 
-        this.view.actor.destroy();
+        this.view.destroy();
 
         if (this._spaceReadySignalId) {
             this._parentView.disconnect(this._spaceReadySignalId);
@@ -1477,7 +1514,7 @@ var FolderIcon = class FolderIcon {
         }
 
         if (this._popup)
-            this._popup.actor.destroy();
+            this._popup.destroy();
 
         this._removeMenuTimeout();
     }
@@ -1505,16 +1542,16 @@ var FolderIcon = class FolderIcon {
     _onDragMotion(dragEvent) {
         let target = dragEvent.targetActor;
 
-        if (!this.actor.contains(target) || !this._canAccept(dragEvent.source))
-            this.actor.remove_style_pseudo_class('drop');
+        if (!this.contains(target) || !this._canAccept(dragEvent.source))
+            this.remove_style_pseudo_class('drop');
         else
-            this.actor.add_style_pseudo_class('drop');
+            this.add_style_pseudo_class('drop');
 
         return DND.DragMotionResult.CONTINUE;
     }
 
     _onDragEnd() {
-        this.actor.remove_style_pseudo_class('drop');
+        this.remove_style_pseudo_class('drop');
         this._parentView.uninhibitEventBlocker();
         DND.removeDragMonitor(this._dragMonitor);
     }
@@ -1574,7 +1611,7 @@ var FolderIcon = class FolderIcon {
 
     _redisplay() {
         this._updateName();
-        this.actor.visible = this.view.getAllItems().length > 0;
+        this.visible = this.view.getAllItems().length > 0;
         this.icon.update();
         this.emit('apps-changed');
     }
@@ -1599,8 +1636,8 @@ var FolderIcon = class FolderIcon {
     }
 
     _calculateBoxPointerArrowSide() {
-        let spaceTop = this.actor.y - this._parentView.getCurrentPageY();
-        let spaceBottom = this._parentView.actor.height - (spaceTop + this.actor.height);
+        let spaceTop = this.y - this._parentView.getCurrentPageY();
+        let spaceBottom = this._parentView.height - (spaceTop + this.height);
 
         return spaceTop > spaceBottom ? St.Side.BOTTOM : St.Side.TOP;
     }
@@ -1622,9 +1659,9 @@ var FolderIcon = class FolderIcon {
             return;
 
         if (this._boxPointerArrowside == St.Side.BOTTOM)
-            this._popup.actor.y = this.actor.allocation.y1 + this.actor.translation_y - this._popupHeight();
+            this._popup.y = this.allocation.y1 + this.translation_y - this._popupHeight();
         else
-            this._popup.actor.y = this.actor.allocation.y1 + this.actor.translation_y + this.actor.height;
+            this._popup.y = this.allocation.y1 + this.translation_y + this.height;
     }
 
     _ensurePopup() {
@@ -1636,7 +1673,7 @@ var FolderIcon = class FolderIcon {
             this._parentView.addFolderPopup(this._popup);
             this._popup.connect('open-state-changed', (popup, isOpen) => {
                 if (!isOpen)
-                    this.actor.checked = false;
+                    this.checked = false;
             });
         } else {
             this._popup.updateArrowSide(this._boxPointerArrowside);
@@ -1665,7 +1702,7 @@ var FolderIcon = class FolderIcon {
     }
 
     _onLeaveEvent(_actor, _event) {
-        this.actor.fake_release();
+        this.fake_release();
         this._removeMenuTimeout();
     }
 
@@ -1689,27 +1726,27 @@ var FolderIcon = class FolderIcon {
 
     _popupRenamePopup() {
         this._removeMenuTimeout();
-        this.actor.fake_release();
+        this.fake_release();
 
         if (!this._menu) {
-            this._menuManager = new PopupMenu.PopupMenuManager(this.actor);
+            this._menuManager = new PopupMenu.PopupMenuManager(this);
 
             this._menu = new RenameFolderMenu(this, this._folder);
             this._menuManager.addMenu(this._menu);
 
             this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
                 if (!isPoppedUp)
-                    this.actor.sync_hover();
+                    this.sync_hover();
             });
             let id = Main.overview.connect('hiding', () => {
                 this._menu.close();
             });
-            this.actor.connect('destroy', () => {
+            this.connect('destroy', () => {
                 Main.overview.disconnect(id);
             });
         }
 
-        this.actor.set_hover(true);
+        this.set_hover(true);
         this._menu.open();
         this._menuManager.ignoreRelease();
     }
@@ -1721,8 +1758,7 @@ var FolderIcon = class FolderIcon {
             this.view.adaptToSize(width, height);
         this._popupInvalidated = true;
     }
-};
-Signals.addSignalMethods(FolderIcon.prototype);
+});
 
 var RenameFolderMenuItem = GObject.registerClass(
 class RenameFolderMenuItem extends PopupMenu.PopupBaseMenuItem {
@@ -1797,7 +1833,7 @@ class RenameFolderMenuItem extends PopupMenu.PopupBaseMenuItem {
 
 var RenameFolderMenu = class RenameFolderMenu extends PopupMenu.PopupMenu {
     constructor(source, folder) {
-        super(source.actor, 0.5, St.Side.BOTTOM);
+        super(source, 0.5, St.Side.BOTTOM);
         this.actor.add_style_class_name('rename-folder-popup');
 
         // We want to keep the item hovered while the menu is up
@@ -1810,12 +1846,12 @@ var RenameFolderMenu = class RenameFolderMenu extends PopupMenu.PopupMenu {
         this.focusActor = menuItem;
 
         // Chain our visibility and lifecycle to that of the source
-        this._sourceMappedId = source.actor.connect('notify::mapped', () => {
-            if (!source.actor.mapped)
+        this._sourceMappedId = source.connect('notify::mapped', () => {
+            if (!source.mapped)
                 this.close();
         });
-        source.actor.connect('destroy', () => {
-            source.actor.disconnect(this._sourceMappedId);
+        source.connect('destroy', () => {
+            source.disconnect(this._sourceMappedId);
             this.destroy();
         });
 
@@ -1824,8 +1860,27 @@ var RenameFolderMenu = class RenameFolderMenu extends PopupMenu.PopupMenu {
 };
 Signals.addSignalMethods(RenameFolderMenu.prototype);
 
-var AppFolderPopup = class AppFolderPopup {
-    constructor(source, side) {
+var AppFolderPopup = GObject.registerClass({
+    GTypeName: 'AppDisplay_AppFolderPopup',
+    Signals: {
+        'open-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
+    }
+}, class AppFolderPopup extends St.Widget {
+    _init(source, side) {
+        super._init({
+            layout_manager: new Clutter.BinLayout(),
+            visible: false,
+            // We don't want to expand really, but look
+            // at the layout manager of our parent...
+            //
+            // DOUBLE HACK: if you set one, you automatically
+            // get the effect for the other direction too, so
+            // we need to set the y_align
+            x_expand: true,
+            y_expand: true,
+            x_align: Clutter.ActorAlign.CENTER,
+            y_align: Clutter.ActorAlign.START
+        });
         this._source = source;
         this._view = source.view;
         this._arrowSide = side;
@@ -1833,18 +1888,6 @@ var AppFolderPopup = class AppFolderPopup {
         this._isOpen = false;
         this.parentOffset = 0;
 
-        this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
-                                     visible: false,
-                                     // We don't want to expand really, but look
-                                     // at the layout manager of our parent...
-                                     //
-                                     // DOUBLE HACK: if you set one, you automatically
-                                     // get the effect for the other direction too, so
-                                     // we need to set the y_align
-                                     x_expand: true,
-                                     y_expand: true,
-                                     x_align: Clutter.ActorAlign.CENTER,
-                                     y_align: Clutter.ActorAlign.START });
         this._boxPointer = new BoxPointer.BoxPointer(this._arrowSide,
                                                      { style_class: 'app-folder-popup-bin',
                                                        x_fill: true,
@@ -1853,30 +1896,30 @@ var AppFolderPopup = class AppFolderPopup {
                                                        x_align: St.Align.START });
 
         this._boxPointer.style_class = 'app-folder-popup';
-        this.actor.add_actor(this._boxPointer);
-        this._boxPointer.bin.set_child(this._view.actor);
+        this.add_actor(this._boxPointer);
+        this._boxPointer.bin.set_child(this._view);
 
         this.closeButton = Util.makeCloseButton(this._boxPointer);
         this.closeButton.connect('clicked', this.popdown.bind(this));
-        this.actor.add_actor(this.closeButton);
+        this.add_actor(this.closeButton);
 
         this._boxPointer.bind_property('opacity', this.closeButton, 'opacity',
                                        GObject.BindingFlags.SYNC_CREATE);
 
-        global.focus_manager.add_group(this.actor);
+        global.focus_manager.add_group(this);
 
-        this._grabHelper = new GrabHelper.GrabHelper(this.actor, {
+        this._grabHelper = new GrabHelper.GrabHelper(this, {
             actionMode: Shell.ActionMode.POPUP
         });
         this._grabHelper.addActor(Main.layoutManager.overviewGroup);
-        this.actor.connect('key-press-event', this._onKeyPress.bind(this));
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('key-press-event', this._onKeyPress.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
     _onDestroy() {
         if (this._isOpen) {
             this._isOpen = false;
-            this._grabHelper.ungrab({ actor: this.actor });
+            this._grabHelper.ungrab({ actor: this });
             this._grabHelper = null;
         }
     }
@@ -1938,23 +1981,23 @@ var AppFolderPopup = class AppFolderPopup {
         if (this._isOpen)
             return;
 
-        this._isOpen = this._grabHelper.grab({ actor: this.actor,
+        this._isOpen = this._grabHelper.grab({ actor: this,
                                                onUngrab: this.popdown.bind(this) });
 
         if (!this._isOpen)
             return;
 
-        this.actor.show();
+        this.show();
 
-        this._boxPointer.setArrowActor(this._source.actor);
+        this._boxPointer.setArrowActor(this._source);
         // We need to hide the icons of the view until the boxpointer animation
         // is completed so we can animate the icons after as we like without
         // showing them while boxpointer is animating.
-        this._view.actor.opacity = 0;
+        this._view.opacity = 0;
         this._boxPointer.open(BoxPointer.PopupAnimation.FADE |
                               BoxPointer.PopupAnimation.SLIDE,
                               () => {
-                                  this._view.actor.opacity = 255;
+                                  this._view.opacity = 255;
                                   this._view.animate(IconGrid.AnimationDirection.IN);
                               });
 
@@ -1965,7 +2008,7 @@ var AppFolderPopup = class AppFolderPopup {
         if (!this._isOpen)
             return;
 
-        this._grabHelper.ungrab({ actor: this.actor });
+        this._grabHelper.ungrab({ actor: this });
 
         this._boxPointer.close(BoxPointer.PopupAnimation.FADE |
                                BoxPointer.PopupAnimation.SLIDE);
@@ -1988,23 +2031,30 @@ var AppFolderPopup = class AppFolderPopup {
         this._arrowSide = side;
         this._boxPointer.updateArrowSide(side);
     }
-};
-Signals.addSignalMethods(AppFolderPopup.prototype);
+});
+
+var AppIcon = GObject.registerClass({
+    GTypeName: 'AppDisplay_AppIcon',
+    Signals: {
+        'menu-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
+        'sync-tooltip': {},
+    }
+}, class AppIcon extends St.Button {
+    _init(app, iconParams = {}) {
+        super._init({
+            style_class: 'app-well-app',
+            pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+            reactive: true,
+            button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
+            can_focus: true,
+            x_fill: true,
+            y_fill: true
+        });
 
-var AppIcon = class AppIcon {
-    constructor(app, iconParams = {}) {
         this.app = app;
         this.id = app.get_id();
         this.name = app.get_name();
 
-        this.actor = new St.Button({ style_class: 'app-well-app',
-                                     pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
-                                     reactive: true,
-                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
-                                     can_focus: true,
-                                     x_fill: true,
-                                     y_fill: true });
-
         this._dot = new St.Widget({ style_class: 'app-well-app-running-dot',
                                     layout_manager: new Clutter.BinLayout(),
                                     x_expand: true, y_expand: true,
@@ -2014,10 +2064,10 @@ var AppIcon = class AppIcon {
         this._iconContainer = new St.Widget({ layout_manager: new Clutter.BinLayout(),
                                               x_expand: true, y_expand: true });
 
-        this.actor.set_child(this._iconContainer);
+        this.set_child(this._iconContainer);
         this._iconContainer.add_child(this._dot);
 
-        this.actor._delegate = this;
+        this._delegate = this;
 
         this._hasDndHover = false;
         this._folderPreviewId = 0;
@@ -2032,19 +2082,19 @@ var AppIcon = class AppIcon {
         this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
         this._iconContainer.add_child(this.icon);
 
-        this.actor.label_actor = this.icon.label;
+        this.label_actor = this.icon.label;
 
-        this.actor.connect('leave-event', this._onLeaveEvent.bind(this));
-        this.actor.connect('button-press-event', this._onButtonPress.bind(this));
-        this.actor.connect('touch-event', this._onTouchEvent.bind(this));
-        this.actor.connect('clicked', this._onClicked.bind(this));
-        this.actor.connect('popup-menu', this._onKeyboardPopupMenu.bind(this));
+        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;
-        this._menuManager = new PopupMenu.PopupMenuManager(this.actor);
+        this._menuManager = new PopupMenu.PopupMenuManager(this);
 
         if (isDraggable) {
-            this._draggable = DND.makeDraggable(this.actor);
+            this._draggable = DND.makeDraggable(this);
             this._draggable.connect('drag-begin', () => {
                 this._dragging = true;
                 this.scaleAndFade();
@@ -2067,13 +2117,13 @@ var AppIcon = class AppIcon {
         this._itemDragEndId = Main.overview.connect(
             'item-drag-end', this._onDragEnd.bind(this));
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-
         this._menuTimeoutId = 0;
         this._stateChangedId = this.app.connect('notify::state', () => {
             this._updateRunningStyle();
         });
         this._updateRunningStyle();
+
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
     _onDestroy() {
@@ -2124,7 +2174,7 @@ var AppIcon = class AppIcon {
     }
 
     _onLeaveEvent(_actor, _event) {
-        this.actor.fake_release();
+        this.fake_release();
         this._removeMenuTimeout();
     }
 
@@ -2162,7 +2212,7 @@ var AppIcon = class AppIcon {
 
     popupMenu() {
         this._removeMenuTimeout();
-        this.actor.fake_release();
+        this.fake_release();
 
         if (this._draggable)
             this._draggable.fakeRelease();
@@ -2179,7 +2229,7 @@ var AppIcon = class AppIcon {
             let id = Main.overview.connect('hiding', () => {
                 this._menu.close();
             });
-            this.actor.connect('destroy', () => {
+            this.connect('destroy', () => {
                 Main.overview.disconnect(id);
             });
 
@@ -2188,7 +2238,7 @@ var AppIcon = class AppIcon {
 
         this.emit('menu-state-changed', true);
 
-        this.actor.set_hover(true);
+        this.set_hover(true);
         this._menu.popup();
         this._menuManager.ignoreRelease();
         this.emit('sync-tooltip');
@@ -2205,7 +2255,7 @@ var AppIcon = class AppIcon {
     }
 
     _onMenuPoppedDown() {
-        this.actor.sync_hover();
+        this.sync_hover();
         this.emit('menu-state-changed', false);
     }
 
@@ -2238,10 +2288,10 @@ var AppIcon = class AppIcon {
     }
 
     scaleIn() {
-        this.actor.scale_x = 0;
-        this.actor.scale_y = 0;
+        this.scale_x = 0;
+        this.scale_y = 0;
 
-        this.actor.ease({
+        this.ease({
             scale_x: 1,
             scale_y: 1,
             duration: APP_ICON_SCALE_IN_TIME,
@@ -2271,12 +2321,12 @@ var AppIcon = class AppIcon {
     }
 
     shouldShowTooltip() {
-        return this.actor.hover && (!this._menu || !this._menu.isOpen);
+        return this.hover && (!this._menu || !this._menu.isOpen);
     }
 
     scaleAndFade() {
-        this.actor.reactive = false;
-        this.actor.ease({
+        this.reactive = false;
+        this.ease({
             scale_x: 0.75,
             scale_y: 0.75,
             opacity: 128
@@ -2284,8 +2334,8 @@ var AppIcon = class AppIcon {
     }
 
     undoScaleAndFade() {
-        this.actor.reactive = true;
-        this.actor.ease({
+        this.reactive = true;
+        this.ease({
             scale_x: 1.0,
             scale_y: 1.0,
             opacity: 255
@@ -2323,7 +2373,7 @@ var AppIcon = class AppIcon {
 
             this._folderPreviewId =
                 GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
-                    this.actor.add_style_pseudo_class('drop');
+                    this.add_style_pseudo_class('drop');
                     this._showFolderPreview();
                     this._folderPreviewId = 0;
                     return GLib.SOURCE_REMOVE;
@@ -2334,7 +2384,7 @@ var AppIcon = class AppIcon {
                 this._folderPreviewId = 0;
             }
             this._hideFolderPreview();
-            this.actor.remove_style_pseudo_class('drop');
+            this.remove_style_pseudo_class('drop');
         }
     }
 
@@ -2347,7 +2397,7 @@ var AppIcon = class AppIcon {
 
     _onDragMotion(dragEvent) {
         let target = dragEvent.targetActor;
-        let isHovering = target == this.actor || this.actor.contains(target);
+        let isHovering = target == this || this.contains(target);
         let canDrop = this._canAccept(dragEvent.source);
         let hasDndHover = isHovering && canDrop;
 
@@ -2360,7 +2410,7 @@ var AppIcon = class AppIcon {
     }
 
     _onDragEnd() {
-        this.actor.remove_style_pseudo_class('drop');
+        this.remove_style_pseudo_class('drop');
         DND.removeDragMonitor(this._dragMonitor);
     }
 
@@ -2385,8 +2435,7 @@ var AppIcon = class AppIcon {
 
         return view.createFolder(apps);
     }
-};
-Signals.addSignalMethods(AppIcon.prototype);
+});
 
 var AppIconMenu = class AppIconMenu extends PopupMenu.PopupMenu {
     constructor(source) {
@@ -2394,7 +2443,7 @@ var AppIconMenu = class AppIconMenu extends PopupMenu.PopupMenu {
         if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
             side = St.Side.RIGHT;
 
-        super(source.actor, 0.5, side);
+        super(source, 0.5, side);
 
         // We want to keep the item hovered while the menu is up
         this.blockSourceEvents = true;
@@ -2404,12 +2453,12 @@ var AppIconMenu = class AppIconMenu extends PopupMenu.PopupMenu {
         this.actor.add_style_class_name('app-well-menu');
 
         // Chain our visibility and lifecycle to that of the source
-        this._sourceMappedId = source.actor.connect('notify::mapped', () => {
-            if (!source.actor.mapped)
+        this._sourceMappedId = source.connect('notify::mapped', () => {
+            if (!source.mapped)
                 this.close();
         });
-        source.actor.connect('destroy', () => {
-            source.actor.disconnect(this._sourceMappedId);
+        source.connect('destroy', () => {
+            source.disconnect(this._sourceMappedId);
             this.destroy();
         });
 
@@ -2540,9 +2589,10 @@ var AppIconMenu = class AppIconMenu extends PopupMenu.PopupMenu {
 };
 Signals.addSignalMethods(AppIconMenu.prototype);
 
-var SystemActionIcon = class SystemActionIcon extends Search.GridSearchResult {
+var SystemActionIcon = GObject.registerClass(
+class SystemActionIcon extends Search.GridSearchResult {
     activate() {
         SystemActions.getDefault().activateAction(this.metaInfo['id']);
         Main.overview.hide();
     }
-};
+});
diff --git a/js/ui/background.js b/js/ui/background.js
index b7307e5a45..44476418fc 100644
--- a/js/ui/background.js
+++ b/js/ui/background.js
@@ -1,4 +1,5 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+/* exported SystemBackground */
 
 // READ THIS FIRST
 // Background handling is a maze of objects, both objects in this file, and
@@ -93,7 +94,7 @@
 //     MetaBackgroundImage         MetaBackgroundImage
 //     MetaBackgroundImage         MetaBackgroundImage
 
-const { Clutter, GDesktopEnums, Gio, GLib, GnomeDesktop, Meta } = imports.gi;
+const { Clutter, GDesktopEnums, Gio, GLib, GObject, GnomeDesktop, Meta } = imports.gi;
 const Signals = imports.signals;
 
 const LoginManager = imports.misc.loginManager;
@@ -220,16 +221,17 @@ function getBackgroundCache() {
     return _backgroundCache;
 }
 
-var Background = class Background {
-    constructor(params) {
+var Background = GObject.registerClass({
+    Signals: { 'loaded': {}, 'bg-changed': {} }
+}, class Background extends Meta.Background {
+    _init(params) {
         params = Params.parse(params, { monitorIndex: 0,
                                         layoutManager: Main.layoutManager,
                                         settings: null,
                                         file: null,
                                         style: null });
 
-        this.background = new Meta.Background({ meta_display: global.display });
-        this.background._delegate = this;
+        super._init({ meta_display: global.display });
 
         this._settings = params.settings;
         this._file = params.file;
@@ -262,8 +264,6 @@ var Background = class Background {
     }
 
     destroy() {
-        this.background = null;
-
         this._cancellable.cancel();
         this._removeAnimationTimeout();
 
@@ -330,7 +330,7 @@ var Background = class Background {
             this.emit('loaded');
             return GLib.SOURCE_REMOVE;
         });
-        GLib.Source.set_name_by_id(id, '[gnome-shell] this.emit');
+        GLib.Source.set_name_by_id(id, '[gnome-shell] Background._setLoaded Idle');
     }
 
     _loadPattern() {
@@ -344,9 +344,9 @@ var Background = class Background {
         let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
 
         if (shadingType == GDesktopEnums.BackgroundShading.SOLID)
-            this.background.set_color(color);
+            this.set_color(color);
         else
-            this.background.set_gradient(shadingType, color, secondColor);
+            this.set_gradient(shadingType, color, secondColor);
     }
 
     _watchFile(file) {
@@ -382,13 +382,13 @@ var Background = class Background {
         let finish = () => {
             this._setLoaded();
             if (files.length > 1) {
-                this.background.set_blend(files[0], files[1],
-                                          this._animation.transitionProgress,
-                                          this._style);
+                this.set_blend(files[0], files[1],
+                               this._animation.transitionProgress,
+                               this._style);
             } else if (files.length > 0) {
-                this.background.set_file(files[0], this._style);
+                this.set_file(files[0], this._style);
             } else {
-                this.background.set_file(null, this._style);
+                this.set_file(null, this._style);
             }
             this._queueUpdateAnimation();
         };
@@ -461,7 +461,7 @@ var Background = class Background {
     }
 
     _loadImage(file) {
-        this.background.set_file(file, this._style);
+        this.set_file(file, this._style);
         this._watchFile(file);
 
         let cache = Meta.BackgroundImageCache.get_default();
@@ -495,13 +495,14 @@ var Background = class Background {
 
         this._loadFile(this._file);
     }
-};
-Signals.addSignalMethods(Background.prototype);
+});
 
 let _systemBackground;
 
-var SystemBackground = class SystemBackground {
-    constructor() {
+var SystemBackground = GObject.registerClass({
+    Signals: { 'loaded': {} }
+}, class SystemBackground extends Meta.BackgroundActor {
+    _init() {
         let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/noise-texture.png');
 
         if (_systemBackground == null) {
@@ -510,9 +511,11 @@ var SystemBackground = class SystemBackground {
             _systemBackground.set_file(file, GDesktopEnums.BackgroundStyle.WALLPAPER);
         }
 
-        this.actor = new Meta.BackgroundActor({ meta_display: global.display,
-                                                monitor: 0,
-                                                background: _systemBackground });
+        super._init({
+            meta_display: global.display,
+            monitor: 0,
+            background: _systemBackground
+        });
 
         let cache = Meta.BackgroundImageCache.get_default();
         let image = cache.load(file);
@@ -531,8 +534,7 @@ var SystemBackground = class SystemBackground {
             });
         }
     }
-};
-Signals.addSignalMethods(SystemBackground.prototype);
+});
 
 var BackgroundSource = class BackgroundSource {
     constructor(layoutManager, settingsSchema) {
@@ -734,7 +736,7 @@ var BackgroundManager = class BackgroundManager {
 
         this._newBackgroundActor = newBackgroundActor;
 
-        let background = newBackgroundActor.background._delegate;
+        let background = newBackgroundActor.background;
 
         if (background.isLoaded) {
             this._swapBackgroundActor();
@@ -754,7 +756,7 @@ var BackgroundManager = class BackgroundManager {
         let backgroundActor = new Meta.BackgroundActor({
             meta_display: global.display,
             monitor: this._monitorIndex,
-            background: background.background,
+            background,
             vignette: this._vignette,
             vignette_sharpness: 0.5,
             brightness: 0.5,
diff --git a/js/ui/calendar.js b/js/ui/calendar.js
index ec89ebf00d..cf1025a96f 100644
--- a/js/ui/calendar.js
+++ b/js/ui/calendar.js
@@ -313,8 +313,10 @@ var DBusEventSource = class DBusEventSource {
 };
 Signals.addSignalMethods(DBusEventSource.prototype);
 
-var Calendar = class Calendar {
-    constructor() {
+var Calendar = GObject.registerClass({
+    Signals: { 'selected-date-changed': { param_types: [GLib.DateTime.$gtype] } }
+}, class Calendar extends St.Widget {
+    _init() {
         this._weekStart = Shell.util_get_week_start();
         this._settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.calendar' });
 
@@ -344,12 +346,13 @@ var Calendar = class Calendar {
 
         this._shouldDateGrabFocus = false;
 
-        this.actor = new St.Widget({ style_class: 'calendar',
-                                     layout_manager: new Clutter.TableLayout(),
-                                     reactive: true });
+        super._init({
+            style_class: 'calendar',
+            layout_manager: new Clutter.TableLayout(),
+            reactive: true
+        });
 
-        this.actor.connect('scroll-event',
-                           this._onScroll.bind(this));
+        this.connect('scroll-event', this._onScroll.bind(this));
 
         this._buildHeader ();
     }
@@ -387,9 +390,9 @@ var Calendar = class Calendar {
     }
 
     _buildHeader() {
-        let layout = this.actor.layout_manager;
+        let layout = this.layout_manager;
         let offsetCols = this._useWeekdate ? 1 : 0;
-        this.actor.destroy_all_children();
+        this.destroy_all_children();
 
         // Top line of the calendar '<| September 2009 |>'
         this._topBox = new St.BoxLayout();
@@ -431,7 +434,7 @@ var Calendar = class Calendar {
                                        can_focus: true });
             label.accessible_name = iter.toLocaleFormat('%A');
             let col;
-            if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
+            if (this.get_text_direction() == Clutter.TextDirection.RTL)
                 col = 6 - (7 + iter.getDay() - this._weekStart) % 7;
             else
                 col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
@@ -440,7 +443,7 @@ var Calendar = class Calendar {
         }
 
         // All the children after this are days, and get removed when we update the calendar
-        this._firstDayIndex = this.actor.get_n_children();
+        this._firstDayIndex = this.get_n_children();
     }
 
     _onScroll(actor, event) {
@@ -514,7 +517,7 @@ var Calendar = class Calendar {
         let now = new Date();
 
         // Remove everything but the topBox and the weekday labels
-        let children = this.actor.get_children();
+        let children = this.get_children();
         for (let i = this._firstDayIndex; i < children.length; i++)
             children[i].destroy();
 
@@ -551,7 +554,7 @@ var Calendar = class Calendar {
 
         beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
 
-        let layout = this.actor.layout_manager;
+        let layout = this.layout_manager;
         let iter = new Date(beginDate);
         let row = 2;
         // nRows here means 6 weeks + one header + one navbar
@@ -651,12 +654,12 @@ var Calendar = class Calendar {
             }
         });
     }
-};
-Signals.addSignalMethods(Calendar.prototype);
+});
 
-var EventMessage = class EventMessage extends MessageList.Message {
-    constructor(event, date) {
-        super('', event.summary);
+var EventMessage = GObject.registerClass(
+class EventMessage extends MessageList.Message {
+    _init(event, date) {
+        super._init('', event.summary);
 
         this._event = event;
         this._date = date;
@@ -666,8 +669,8 @@ var EventMessage = class EventMessage extends MessageList.Message {
         this._icon = new St.Icon({ icon_name: 'x-office-calendar-symbolic' });
         this.setIcon(this._icon);
 
-        this.actor.connect('style-changed', () => {
-            let iconVisible = this.actor.get_parent().has_style_pseudo_class('first-child');
+        this.connect('style-changed', () => {
+            let iconVisible = this.get_parent().has_style_pseudo_class('first-child');
             this._icon.opacity = (iconVisible ? 255 : 0);
         });
     }
@@ -705,12 +708,12 @@ var EventMessage = class EventMessage extends MessageList.Message {
         }
         return title;
     }
-};
+});
 
-var NotificationMessage =
+var NotificationMessage = GObject.registerClass(
 class NotificationMessage extends MessageList.Message {
-    constructor(notification) {
-        super(notification.title, notification.bannerBodyText);
+    _init(notification) {
+        super._init(notification.title, notification.bannerBodyText);
         this.setUseBodyMarkup(notification.bannerBodyMarkup);
 
         this.notification = notification;
@@ -769,11 +772,12 @@ class NotificationMessage extends MessageList.Message {
     canClose() {
         return true;
     }
-};
+});
 
-var EventsSection = class EventsSection extends MessageList.MessageListSection {
-    constructor() {
-        super();
+var EventsSection = GObject.registerClass(
+class EventsSection extends MessageList.MessageListSection {
+    _init() {
+        super._init();
 
         this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
         this._desktopSettings.connect('changed', this._reloadEvents.bind(this));
@@ -785,7 +789,7 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection {
                                       label: '',
                                       x_align: St.Align.START,
                                       can_focus: true });
-        this.actor.insert_child_below(this._title, null);
+        this.insert_child_below(this._title, null);
 
         this._title.connect('clicked', this._onTitleClicked.bind(this));
         this._title.connect('key-focus-in', this._onKeyFocusIn.bind(this));
@@ -904,12 +908,12 @@ var EventsSection = class EventsSection extends MessageList.MessageListSection {
 
         super._sync();
     }
-};
+});
 
-var NotificationSection =
+var NotificationSection = GObject.registerClass(
 class NotificationSection extends MessageList.MessageListSection {
-    constructor() {
-        super();
+    _init() {
+        super._init();
 
         this._sources = new Map();
         this._nUrgent = 0;
@@ -919,7 +923,7 @@ class NotificationSection extends MessageList.MessageListSection {
             this._sourceAdded(Main.messageTray, source);
         });
 
-        this.actor.connect('notify::mapped', this._onMapped.bind(this));
+        this.connect('notify::mapped', this._onMapped.bind(this));
     }
 
     get allowed() {
@@ -961,7 +965,7 @@ class NotificationSection extends MessageList.MessageListSection {
 
         let updatedId = notification.connect('updated', () => {
             message.setSecondaryActor(this._createTimeLabel(notification.datetime));
-            this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.actor.mapped);
+            this.moveMessage(message, isUrgent ? 0 : this._nUrgent, this.mapped);
         });
         let destroyId = notification.connect('destroy', () => {
             notification.disconnect(destroyId);
@@ -981,7 +985,7 @@ class NotificationSection extends MessageList.MessageListSection {
         }
 
         let index = isUrgent ? 0 : this._nUrgent;
-        this.addMessageAtIndex(message, index, this.actor.mapped);
+        this.addMessageAtIndex(message, index, this.mapped);
     }
 
     _onSourceDestroy(source, obj) {
@@ -992,7 +996,7 @@ class NotificationSection extends MessageList.MessageListSection {
     }
 
     _onMapped() {
-        if (!this.actor.mapped)
+        if (!this.mapped)
             return;
 
         for (let message of this._messages.keys())
@@ -1003,13 +1007,12 @@ class NotificationSection extends MessageList.MessageListSection {
     _shouldShow() {
         return !this.empty && isToday(this._date);
     }
-};
-
-var Placeholder = class Placeholder {
-    constructor() {
-        this.actor = new St.BoxLayout({ style_class: 'message-list-placeholder',
-                                        vertical: true });
+});
 
+var Placeholder = GObject.registerClass(
+class Placeholder extends St.BoxLayout {
+    _init() {
+        super._init({ style_class: 'message-list-placeholder', vertical: true });
         this._date = new Date();
 
         let todayFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-notifications.svg');
@@ -1018,10 +1021,10 @@ var Placeholder = class Placeholder {
         this._otherIcon = new Gio.FileIcon({ file: otherFile });
 
         this._icon = new St.Icon();
-        this.actor.add_actor(this._icon);
+        this.add_actor(this._icon);
 
         this._label = new St.Label();
-        this.actor.add_actor(this._label);
+        this.add_actor(this._label);
 
         this._sync();
     }
@@ -1048,20 +1051,24 @@ var Placeholder = class Placeholder {
             this._label.text = _("No Events");
         }
     }
-};
+});
 
-var CalendarMessageList = class CalendarMessageList {
-    constructor() {
-        this.actor = new St.Widget({ style_class: 'message-list',
-                                     layout_manager: new Clutter.BinLayout(),
-                                     x_expand: true, y_expand: true });
+var CalendarMessageList = GObject.registerClass(
+class CalendarMessageList extends St.Widget {
+    _init() {
+        super._init({
+            style_class: 'message-list',
+            layout_manager: new Clutter.BinLayout(),
+            x_expand: true,
+            y_expand: true
+        });
 
         this._placeholder = new Placeholder();
-        this.actor.add_actor(this._placeholder.actor);
+        this.add_actor(this._placeholder);
 
         let box = new St.BoxLayout({ vertical: true,
                                      x_expand: true, y_expand: true });
-        this.actor.add_actor(box);
+        this.add_actor(box);
 
         this._scrollView = new St.ScrollView({ style_class: 'vfade',
                                                overlay_scrollbars: true,
@@ -1080,7 +1087,7 @@ var CalendarMessageList = class CalendarMessageList {
         });
         box.add_actor(this._clearButton);
 
-        this._placeholder.actor.bind_property('visible',
+        this._placeholder.bind_property('visible',
             this._clearButton, 'visible',
             GObject.BindingFlags.INVERT_BOOLEAN);
 
@@ -1107,37 +1114,34 @@ var CalendarMessageList = class CalendarMessageList {
         let obj = {
             destroyId: 0,
             visibleId: 0,
-            emptyChangedId: 0,
-            canClearChangedId: 0,
+            emptyNotifyId: 0,
+            canClearNotifyId: 0,
             messageFocusedId: 0
         };
-        obj.destroyId = section.actor.connect('destroy', () => {
+        obj.destroyId = section.connect('destroy', () => {
             this._removeSection(section);
         });
-        obj.visibleId = section.actor.connect('notify::visible',
-                                              this._sync.bind(this));
-        obj.emptyChangedId = section.connect('empty-changed',
-                                             this._sync.bind(this));
-        obj.canClearChangedId = section.connect('can-clear-changed',
-                                                this._sync.bind(this));
+        obj.visibleId = section.connect('notify::visible', this._sync.bind(this));
+        obj.emptyNotifyId = section.connect('notify::empty', this._sync.bind(this));
+        obj.canClearNotifyId = section.connect('notify::can-clear', this._sync.bind(this));
         obj.messageFocusedId = section.connect('message-focused',
             this._onMessageFocused.bind(this));
 
         this._sections.set(section, obj);
-        this._sectionList.add_actor(section.actor);
+        this._sectionList.add_actor(section);
         this._sync();
     }
 
     _removeSection(section) {
         let obj = this._sections.get(section);
-        section.actor.disconnect(obj.destroyId);
-        section.actor.disconnect(obj.visibleId);
-        section.disconnect(obj.emptyChangedId);
-        section.disconnect(obj.canClearChangedId);
+        section.disconnect(obj.destroyId);
+        section.disconnect(obj.visibleId);
+        section.disconnect(obj.emptyNotifyId);
+        section.disconnect(obj.canClearNotifyId);
         section.disconnect(obj.messageFocusedId);
 
         this._sections.delete(section);
-        this._sectionList.remove_actor(section.actor);
+        this._sectionList.remove_actor(section);
         this._sync();
     }
 
@@ -1148,14 +1152,14 @@ var CalendarMessageList = class CalendarMessageList {
     _sync() {
         let sections = [...this._sections.keys()];
         let visible = sections.some(s => s.allowed);
-        this.actor.visible = visible;
+        this.visible = visible;
         if (!visible)
             return;
 
-        let empty = sections.every(s => s.empty || !s.actor.visible);
-        this._placeholder.actor.visible = empty;
+        let empty = sections.every(s => s.empty || !s.visible);
+        this._placeholder.visible = empty;
 
-        let canClear = sections.some(s => s.canClear && s.actor.visible);
+        let canClear = sections.some(s => s.canClear && s.visible);
         this._clearButton.reactive = canClear;
     }
 
@@ -1168,4 +1172,4 @@ var CalendarMessageList = class CalendarMessageList {
             section.setDate(date);
         this._placeholder.setDate(date);
     }
-};
+});
diff --git a/js/ui/checkBox.js b/js/ui/checkBox.js
index 58fec16337..c4683e0e54 100644
--- a/js/ui/checkBox.js
+++ b/js/ui/checkBox.js
@@ -1,16 +1,19 @@
 /* exported CheckBox */
-const { Clutter, Pango, St } = imports.gi;
+const { Clutter, GObject, Pango, St } = imports.gi;
 
-var CheckBox = class CheckBox {
-    constructor(label) {
+var CheckBox = GObject.registerClass(
+class CheckBox extends St.Button {
+    _init(label) {
         let container = new St.BoxLayout();
-        this.actor = new St.Button({ style_class: 'check-box',
-                                     child: container,
-                                     button_mask: St.ButtonMask.ONE,
-                                     toggle_mode: true,
-                                     can_focus: true,
-                                     x_fill: true,
-                                     y_fill: true });
+        super._init({
+            style_class: 'check-box',
+            child: container,
+            button_mask: St.ButtonMask.ONE,
+            toggle_mode: true,
+            can_focus: true,
+            x_fill: true,
+            y_fill: true
+        });
 
         this._box = new St.Bin();
         this._box.set_y_align(Clutter.ActorAlign.START);
@@ -32,4 +35,4 @@ var CheckBox = class CheckBox {
     getLabelActor() {
         return this._label;
     }
-};
+});
diff --git a/js/ui/components/keyring.js b/js/ui/components/keyring.js
index 66948b65d9..87628bedfe 100644
--- a/js/ui/components/keyring.js
+++ b/js/ui/components/keyring.js
@@ -77,13 +77,13 @@ class KeyringDialog extends ModalDialog.ModalDialog {
             this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, true);
 
             if (rtl) {
-                layout.attach(this._workSpinner.actor, 0, row, 1, 1);
+                layout.attach(this._workSpinner, 0, row, 1, 1);
                 layout.attach(this._passwordEntry, 1, row, 1, 1);
                 layout.attach(label, 2, row, 1, 1);
             } else {
                 layout.attach(label, 0, row, 1, 1);
                 layout.attach(this._passwordEntry, 1, row, 1, 1);
-                layout.attach(this._workSpinner.actor, 2, row, 1, 1);
+                layout.attach(this._workSpinner, 2, row, 1, 1);
             }
             row++;
         } else {
@@ -121,8 +121,8 @@ class KeyringDialog extends ModalDialog.ModalDialog {
         if (this.prompt.choice_visible) {
             let choice = new CheckBox.CheckBox();
             this.prompt.bind_property('choice-label', choice.getLabelActor(), 'text', 
GObject.BindingFlags.SYNC_CREATE);
-            this.prompt.bind_property('choice-chosen', choice.actor, 'checked', 
GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL);
-            layout.attach(choice.actor, rtl ? 0 : 1, row, 1, 1);
+            this.prompt.bind_property('choice-chosen', choice, 'checked', GObject.BindingFlags.SYNC_CREATE | 
GObject.BindingFlags.BIDIRECTIONAL);
+            layout.attach(choice, rtl ? 0 : 1, row, 1, 1);
             row++;
         }
 
diff --git a/js/ui/components/polkitAgent.js b/js/ui/components/polkitAgent.js
index 1c1b8e610e..b93e6700bf 100644
--- a/js/ui/components/polkitAgent.js
+++ b/js/ui/components/polkitAgent.js
@@ -76,8 +76,8 @@ var AuthenticationDialog = GObject.registerClass({
             this._userAvatar = new UserWidget.Avatar(this._user,
                                                      { iconSize: DIALOG_ICON_SIZE,
                                                        styleClass: 'polkit-dialog-user-icon' });
-            this._userAvatar.actor.hide();
-            userBox.add(this._userAvatar.actor,
+            this._userAvatar.hide();
+            userBox.add(this._userAvatar,
                         { x_fill: true,
                           y_fill: false,
                           x_align: St.Align.END,
@@ -106,7 +106,7 @@ var AuthenticationDialog = GObject.registerClass({
                               { expand: true });
 
         this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, true);
-        this._passwordBox.add(this._workSpinner.actor);
+        this._passwordBox.add(this._workSpinner);
 
         this.setInitialKeyFocus(this._passwordEntry);
         this._passwordBox.hide();
@@ -305,7 +305,7 @@ var AuthenticationDialog = GObject.registerClass({
     _onUserChanged() {
         if (this._user.is_loaded && this._userAvatar) {
             this._userAvatar.update();
-            this._userAvatar.actor.show();
+            this._userAvatar.show();
         }
     }
 
diff --git a/js/ui/components/telepathyClient.js b/js/ui/components/telepathyClient.js
index 5ab5e47f37..4dacc16407 100644
--- a/js/ui/components/telepathyClient.js
+++ b/js/ui/components/telepathyClient.js
@@ -327,7 +327,7 @@ class ChatSource extends MessageTray.Source {
 
         // We ack messages when the user expands the new notification
         let id = this._banner.connect('expanded', this._ackMessages.bind(this));
-        this._banner.actor.connect('destroy', () => {
+        this._banner.connect('destroy', () => {
             this._banner.disconnect(id);
             this._banner = null;
         });
@@ -799,9 +799,10 @@ class ChatLineBox extends St.BoxLayout {
     }
 });
 
-var ChatNotificationBanner = class extends MessageTray.NotificationBanner {
-    constructor(notification) {
-        super(notification);
+var ChatNotificationBanner = GObject.registerClass(
+class ChatNotificationBanner extends MessageTray.NotificationBanner {
+    _init(notification) {
+        super._init(notification);
 
         this._responseEntry = new St.Entry({ style_class: 'chat-response',
                                              x_expand: true,
@@ -886,8 +887,7 @@ var ChatNotificationBanner = class extends MessageTray.NotificationBanner {
     }
 
     _addMessage(message) {
-        let highlighter = new MessageList.URLHighlighter(message.body, true, true);
-        let body = highlighter.actor;
+        let body = new MessageList.URLHighlighter(message.body, true, true);
 
         let styles = message.styles;
         for (let i = 0; i < styles.length; i++)
@@ -975,6 +975,6 @@ var ChatNotificationBanner = class extends MessageTray.NotificationBanner {
             this.notification.source.setChatState(Tp.ChannelChatState.ACTIVE);
         }
     }
-};
+});
 
 var Component = TelepathyComponent;
diff --git a/js/ui/dash.js b/js/ui/dash.js
index 73418b1e60..e22cbfbb3d 100644
--- a/js/ui/dash.js
+++ b/js/ui/dash.js
@@ -3,7 +3,6 @@
 
 const { Clutter, GLib, GObject,
         Graphene, Meta, Shell, St } = imports.gi;
-const Signals = imports.signals;
 
 const AppDisplay = imports.ui.appDisplay;
 const AppFavorites = imports.ui.appFavorites;
@@ -24,9 +23,10 @@ function getAppFromSource(source) {
     }
 }
 
-var DashIcon = class DashIcon extends AppDisplay.AppIcon {
-    constructor(app) {
-        super(app, {
+var DashIcon = GObject.registerClass(
+class DashIcon extends AppDisplay.AppIcon {
+    _init(app) {
+        super._init(app, {
             setSizeManually: true,
             showLabel: false
         });
@@ -46,7 +46,7 @@ var DashIcon = class DashIcon extends AppDisplay.AppIcon {
     acceptDrop() {
         return false;
     }
-};
+});
 
 // A container like StBin, but taking the child's scale into account
 // when requesting a size
@@ -331,8 +331,10 @@ class DashActor extends St.Widget {
 
 const baseIconSizes = [16, 22, 24, 32, 48, 64];
 
-var Dash = class Dash {
-    constructor() {
+var Dash = GObject.registerClass({
+    Signals: { 'icon-size-changed': {} }
+}, class Dash extends St.Bin {
+    _init() {
         this._maxHeight = -1;
         this.iconSize = 64;
         this._shownInitially = false;
@@ -360,11 +362,11 @@ var Dash = class Dash {
 
         this._container.add_actor(this._showAppsIcon);
 
-        this.actor = new St.Bin({ child: this._container });
-        this.actor.connect('notify::height', () => {
-            if (this._maxHeight != this.actor.height)
+        super._init({ child: this._container });
+        this.connect('notify::height', () => {
+            if (this._maxHeight != this.height)
                 this._queueRedisplay();
-            this._maxHeight = this.actor.height;
+            this._maxHeight = this.height;
         });
 
         this._workId = Main.initializeDeferredWork(this._box, this._redisplay.bind(this));
@@ -387,7 +389,7 @@ var Dash = class Dash {
 
         // Translators: this is the name of the dock/favorites area on
         // the left of the overview
-        Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic');
+        Main.ctrlAltTabManager.addGroup(this, _("Dash"), 'user-bookmarks-symbolic');
     }
 
     _onDragBegin() {
@@ -482,11 +484,11 @@ var Dash = class Dash {
                         });
 
         let item = new DashItemContainer();
-        item.setChild(appIcon.actor);
+        item.setChild(appIcon);
 
         // Override default AppIcon label_actor, now the
         // accessible_name is set at DashItemContainer.setLabelText
-        appIcon.actor.label_actor = null;
+        appIcon.label_actor = null;
         item.setLabelText(app.get_name());
 
         appIcon.icon.setIconSize(this.iconSize);
@@ -903,5 +905,4 @@ var Dash = class Dash {
 
         return true;
     }
-};
-Signals.addSignalMethods(Dash.prototype);
+});
diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js
index e82d3da537..1492a888fb 100644
--- a/js/ui/dateMenu.js
+++ b/js/ui/dateMenu.js
@@ -29,24 +29,25 @@ function _gDateTimeToDate(datetime) {
     return new Date(datetime.to_unix() * 1000 + datetime.get_microsecond() / 1000);
 }
 
-var TodayButton = class TodayButton {
-    constructor(calendar) {
+var TodayButton = GObject.registerClass(
+class TodayButton extends St.Button {
+    _init(calendar) {
         // Having the ability to go to the current date if the user is already
         // on the current date can be confusing. So don't make the button reactive
         // until the selected date changes.
-        this.actor = new St.Button({
+        super._init({
             style_class: 'datemenu-today-button',
             x_align: St.Align.START,
             x_expand: true,
             can_focus: true,
-            reactive: false,
+            reactive: false
         });
-        this.actor.connect('clicked', () => {
+        this.connect('clicked', () => {
             this._calendar.setDate(new Date(), false);
         });
 
         let hbox = new St.BoxLayout({ vertical: true });
-        this.actor.add_actor(hbox);
+        this.add_actor(hbox);
 
         this._dayLabel = new St.Label({ style_class: 'day-label',
                                         x_align: Clutter.ActorAlign.START });
@@ -59,7 +60,7 @@ var TodayButton = class TodayButton {
         this._calendar.connect('selected-date-changed', (_calendar, datetime) => {
             // Make the button reactive only if the selected date is not the
             // current date.
-            this.actor.reactive = !_isToday(_gDateTimeToDate(datetime));
+            this.reactive = !_isToday(_gDateTimeToDate(datetime));
         });
     }
 
@@ -79,21 +80,24 @@ var TodayButton = class TodayButton {
          * date, e.g. "Tuesday February 17 2015".
          */
         dateFormat = Shell.util_translate_time_string (N_("%A %B %e %Y"));
-        this.actor.accessible_name = date.toLocaleFormat(dateFormat);
+        this.accessible_name = date.toLocaleFormat(dateFormat);
     }
-};
+});
 
-var WorldClocksSection = class WorldClocksSection {
-    constructor() {
+var WorldClocksSection = GObject.registerClass(
+class WorldClocksSection extends St.Button {
+    _init() {
+        super._init({
+            style_class: 'world-clocks-button',
+            x_fill: true,
+            can_focus: true
+        });
         this._clock = new GnomeDesktop.WallClock();
         this._clockNotifyId = 0;
 
         this._locations = [];
 
-        this.actor = new St.Button({ style_class: 'world-clocks-button',
-                                     x_fill: true,
-                                     can_focus: true });
-        this.actor.connect('clicked', () => {
+        this.connect('clicked', () => {
             if (this._clocksApp)
                 this._clocksApp.activate();
 
@@ -106,7 +110,7 @@ var WorldClocksSection = class WorldClocksSection {
                                      layout_manager: layout });
         layout.hookup_style(this._grid);
 
-        this.actor.child = this._grid;
+        this.child = this._grid;
 
         this._clocksApp = null;
         this._clocksProxy = new ClocksProxy(
@@ -131,7 +135,7 @@ var WorldClocksSection = class WorldClocksSection {
 
     _sync() {
         this._clocksApp = this._appSystem.lookup_app('org.gnome.clocks.desktop');
-        this.actor.visible = this._clocksApp != null;
+        this.visible = this._clocksApp != null;
     }
 
     _clocksChanged() {
@@ -159,7 +163,7 @@ var WorldClocksSection = class WorldClocksSection {
                                     x_align: Clutter.ActorAlign.START,
                                     text: title });
         layout.attach(header, 0, 0, 2, 1);
-        this.actor.label_actor = header;
+        this.label_actor = header;
 
         let localOffset = GLib.DateTime.new_now_local().get_utc_offset();
 
@@ -240,30 +244,34 @@ var WorldClocksSection = class WorldClocksSection {
         this._settings.set_value('locations',
             new GLib.Variant('av', this._clocksProxy.Locations));
     }
-};
+});
+
+var WeatherSection = GObject.registerClass(
+class WeatherSection extends St.Button {
+    _init() {
+        super._init({
+            style_class: 'weather-button',
+            x_fill: true,
+            can_focus: true
+        });
 
-var WeatherSection = class WeatherSection {
-    constructor() {
         this._weatherClient = new Weather.WeatherClient();
 
-        this.actor = new St.Button({ style_class: 'weather-button',
-                                     x_fill: true,
-                                     can_focus: true });
-        this.actor.connect('clicked', () => {
+        this.connect('clicked', () => {
             this._weatherClient.activateApp();
 
             Main.overview.hide();
             Main.panel.closeCalendar();
         });
-        this.actor.connect('notify::mapped', () => {
-            if (this.actor.mapped)
+        this.connect('notify::mapped', () => {
+            if (this.mapped)
                 this._weatherClient.update();
         });
 
         let box = new St.BoxLayout({ style_class: 'weather-box',
                                      vertical: true });
 
-        this.actor.child = box;
+        this.child = box;
 
         let titleBox = new St.BoxLayout();
         titleBox.add_child(new St.Label({ style_class: 'weather-header',
@@ -376,23 +384,27 @@ var WeatherSection = class WeatherSection {
     }
 
     _sync() {
-        this.actor.visible = this._weatherClient.available;
+        this.visible = this._weatherClient.available;
 
-        if (!this.actor.visible)
+        if (!this.visible)
             return;
 
         this._titleLocation.visible = this._weatherClient.hasLocation;
 
         this._updateForecasts();
     }
-};
+});
 
-var MessagesIndicator = class MessagesIndicator {
-    constructor() {
-        this.actor = new St.Icon({ icon_name: 'message-indicator-symbolic',
-                                   icon_size: 16,
-                                   visible: false, y_expand: true,
-                                   y_align: Clutter.ActorAlign.CENTER });
+var MessagesIndicator = GObject.registerClass(
+class MessagesIndicator extends St.Icon {
+    _init() {
+        super._init({
+            icon_name: 'message-indicator-symbolic',
+            icon_size: 16,
+            visible: false,
+            y_expand: true,
+            y_align: Clutter.ActorAlign.CENTER
+        });
 
         this._sources = [];
 
@@ -420,9 +432,9 @@ var MessagesIndicator = class MessagesIndicator {
         this._sources.forEach(source => (count += source.unseenCount));
         count -= Main.messageTray.queueCount;
 
-        this.actor.visible = (count > 0);
+        this.visible = (count > 0);
     }
-};
+});
 
 var IndicatorPad = GObject.registerClass(
 class IndicatorPad extends St.Widget {
@@ -515,9 +527,9 @@ class DateMenuButton extends PanelMenu.Button {
         this._indicator = new MessagesIndicator();
 
         let box = new St.BoxLayout();
-        box.add_actor(new IndicatorPad(this._indicator.actor));
+        box.add_actor(new IndicatorPad(this._indicator));
         box.add_actor(this._clockDisplay);
-        box.add_actor(this._indicator.actor);
+        box.add_actor(this._indicator);
 
         this.label_actor = this._clockDisplay;
         this.add_actor(box);
@@ -551,19 +563,19 @@ class DateMenuButton extends PanelMenu.Button {
 
         // Fill up the first column
         this._messageList = new Calendar.CalendarMessageList();
-        hbox.add(this._messageList.actor, { expand: true, y_fill: false, y_align: St.Align.START });
+        hbox.add(this._messageList, { expand: true, y_fill: false, y_align: St.Align.START });
 
         // Fill up the second column
-        let boxLayout = new CalendarColumnLayout(this._calendar.actor);
+        let boxLayout = new CalendarColumnLayout(this._calendar);
         vbox = new St.Widget({ style_class: 'datemenu-calendar-column',
                                layout_manager: boxLayout });
         boxLayout.hookup_style(vbox);
         hbox.add(vbox);
 
         this._date = new TodayButton(this._calendar);
-        vbox.add_actor(this._date.actor);
+        vbox.add_actor(this._date);
 
-        vbox.add_actor(this._calendar.actor);
+        vbox.add_actor(this._calendar);
 
         this._displaysSection = new St.ScrollView({ style_class: 'datemenu-displays-section vfade',
                                                     x_expand: true, x_fill: true,
@@ -576,10 +588,10 @@ class DateMenuButton extends PanelMenu.Button {
         this._displaysSection.add_actor(displaysBox);
 
         this._clocksItem = new WorldClocksSection();
-        displaysBox.add(this._clocksItem.actor, { x_fill: true });
+        displaysBox.add(this._clocksItem, { x_fill: true });
 
         this._weatherItem = new WeatherSection();
-        displaysBox.add(this._weatherItem.actor, { x_fill: true });
+        displaysBox.add(this._weatherItem, { x_fill: true });
 
         // Done with hbox for calendar and event list
 
diff --git a/js/ui/endSessionDialog.js b/js/ui/endSessionDialog.js
index e1c6517f46..ff4991f9a0 100644
--- a/js/ui/endSessionDialog.js
+++ b/js/ui/endSessionDialog.js
@@ -207,10 +207,10 @@ function _setCheckBoxLabel(checkBox, text) {
 
     if (text) {
         label.set_text(text);
-        checkBox.actor.show();
+        checkBox.show();
     } else {
         label.set_text('');
-        checkBox.actor.hide();
+        checkBox.hide();
     }
 }
 
@@ -297,8 +297,8 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
                             y_align: St.Align.START });
 
         this._checkBox = new CheckBox.CheckBox();
-        this._checkBox.actor.connect('clicked', this._sync.bind(this));
-        messageLayout.add(this._checkBox.actor);
+        this._checkBox.connect('clicked', this._sync.bind(this));
+        messageLayout.add(this._checkBox);
 
         this._batteryWarning = new St.Label({ style_class: 'end-session-dialog-warning',
                                               text: _("Running on battery power: please plug in before 
installing updates.") });
@@ -376,12 +376,12 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
         let subject = dialogContent.subject;
 
         // Use different title when we are installing updates
-        if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked)
+        if (dialogContent.subjectWithUpdates && this._checkBox.checked)
             subject = dialogContent.subjectWithUpdates;
 
         if (dialogContent.showBatteryWarning) {
             // Warn when running on battery power
-            if (this._powerProxy.OnBattery && this._checkBox.actor.checked)
+            if (this._powerProxy.OnBattery && this._checkBox.checked)
                 this._batteryWarning.opacity = 255;
             else
                 this._batteryWarning.opacity = 0;
@@ -429,7 +429,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
             let avatarWidget = new UserWidget.Avatar(this._user,
                                                      { iconSize: _DIALOG_ICON_SIZE,
                                                        styleClass: dialogContent.iconStyleClass });
-            this._iconBin.child = avatarWidget.actor;
+            this._iconBin.child = avatarWidget;
             avatarWidget.update();
         }
 
@@ -485,13 +485,13 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
         };
 
         // Offline update not available; just emit the signal
-        if (!this._checkBox.actor.visible) {
+        if (!this._checkBox.visible) {
             callback();
             return;
         }
 
         // Trigger the offline update as requested
-        if (this._checkBox.actor.checked) {
+        if (this._checkBox.checked) {
             switch (signal) {
             case "ConfirmedReboot":
                 this._triggerOfflineUpdateReboot(callback);
@@ -656,7 +656,7 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
 
         let actor = new St.BoxLayout({ style_class: 'end-session-dialog-session-list-item',
                                        can_focus: true });
-        actor.add(avatar.actor);
+        actor.add(avatar);
 
         let nameLabel = new St.Label({ text: userLabelText,
                                        style_class: 'end-session-dialog-session-list-item-name',
@@ -754,14 +754,14 @@ class EndSessionDialog extends ModalDialog.ModalDialog {
         let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
 
         _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText || '');
-        this._checkBox.actor.visible = (dialogContent.checkBoxText && updatePrepared && updatesAllowed);
-        this._checkBox.actor.checked = (updatePrepared && updateTriggered);
+        this._checkBox.visible = (dialogContent.checkBoxText && updatePrepared && updatesAllowed);
+        this._checkBox.checked = (updatePrepared && updateTriggered);
 
         // We show the warning either together with the checkbox, or when
         // updates have already been triggered, but the user doesn't have
         // enough permissions to cancel them.
         this._batteryWarning.visible = (dialogContent.showBatteryWarning &&
-                                        (this._checkBox.actor.visible || updatePrepared && updateTriggered 
&& !updatesAllowed));
+                                        (this._checkBox.visible || updatePrepared && updateTriggered && 
!updatesAllowed));
 
         this._updateButtons();
 
diff --git a/js/ui/ibusCandidatePopup.js b/js/ui/ibusCandidatePopup.js
index 9f23f578c0..52f6495ef9 100644
--- a/js/ui/ibusCandidatePopup.js
+++ b/js/ui/ibusCandidatePopup.js
@@ -1,8 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported CandidatePopup */
 
-const { Clutter, IBus, St } = imports.gi;
-const Signals = imports.signals;
+const { Clutter, GObject, IBus, St } = imports.gi;
 
 const BoxPointer = imports.ui.boxpointer;
 const Main = imports.ui.main;
@@ -12,11 +11,23 @@ var MAX_CANDIDATES_PER_PAGE = 16;
 var DEFAULT_INDEX_LABELS = ['1', '2', '3', '4', '5', '6', '7', '8',
                             '9', '0', 'a', 'b', 'c', 'd', 'e', 'f'];
 
-var CandidateArea = class CandidateArea {
-    constructor() {
-        this.actor = new St.BoxLayout({ vertical: true,
-                                        reactive: true,
-                                        visible: false });
+var CandidateArea = GObject.registerClass({
+    Signals: {
+        'candidate-clicked': { param_types: [GObject.TYPE_UINT,
+                                             GObject.TYPE_UINT,
+                                             Clutter.ModifierType.$gtype] },
+        'cursor-down': {},
+        'cursor-up': {},
+        'next-page': {},
+        'previous-page': {},
+    }
+}, class CandidateArea extends St.BoxLayout {
+    _init() {
+        super._init({
+            vertical: true,
+            reactive: true,
+            visible: false
+        });
         this._candidateBoxes = [];
         for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
             let box = new St.BoxLayout({ style_class: 'candidate-box',
@@ -27,7 +38,7 @@ var CandidateArea = class CandidateArea {
             box.add(box._indexLabel, { y_fill: false });
             box.add(box._candidateLabel, { y_fill: false });
             this._candidateBoxes.push(box);
-            this.actor.add(box);
+            this.add(box);
 
             let j = i;
             box.connect('button-release-event', (actor, event) => {
@@ -36,7 +47,7 @@ var CandidateArea = class CandidateArea {
             });
         }
 
-        this.actor.connect('scroll-event', (actor, event) => {
+        this.connect('scroll-event', (actor, event) => {
             let direction = event.get_scroll_direction();
             switch (direction) {
             case Clutter.ScrollDirection.UP:
@@ -59,7 +70,7 @@ var CandidateArea = class CandidateArea {
         this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
         this._buttonBox.add(this._nextButton, { expand: true });
 
-        this.actor.add(this._buttonBox);
+        this.add(this._buttonBox);
 
         this._previousButton.connect('clicked', () => {
             this.emit('previous-page');
@@ -79,15 +90,15 @@ var CandidateArea = class CandidateArea {
         this._orientation = orientation;
 
         if (this._orientation == IBus.Orientation.HORIZONTAL) {
-            this.actor.vertical = false;
-            this.actor.remove_style_class_name('vertical');
-            this.actor.add_style_class_name('horizontal');
+            this.vertical = false;
+            this.remove_style_class_name('vertical');
+            this.add_style_class_name('horizontal');
             this._previousButton.child.icon_name = 'go-previous-symbolic';
             this._nextButton.child.icon_name = 'go-next-symbolic';
         } else {                // VERTICAL || SYSTEM
-            this.actor.vertical = true;
-            this.actor.add_style_class_name('vertical');
-            this.actor.remove_style_class_name('horizontal');
+            this.vertical = true;
+            this.add_style_class_name('vertical');
+            this.remove_style_class_name('horizontal');
             this._previousButton.child.icon_name = 'go-up-symbolic';
             this._nextButton.child.icon_name = 'go-down-symbolic';
         }
@@ -121,22 +132,23 @@ var CandidateArea = class CandidateArea {
         this._previousButton.reactive = wrapsAround || page > 0;
         this._nextButton.reactive = wrapsAround || page < nPages - 1;
     }
-};
-Signals.addSignalMethods(CandidateArea.prototype);
+});
+
+var CandidatePopup = GObject.registerClass(
+class IbusCandidatePopup extends BoxPointer.BoxPointer {
+    _init() {
+        super._init(St.Side.TOP);
+        this.visible = false;
+        this.style_class = 'candidate-popup-boxpointer';
 
-var CandidatePopup = class CandidatePopup {
-    constructor() {
         this._dummyCursor = new St.Widget({ opacity: 0 });
         Main.layoutManager.uiGroup.add_actor(this._dummyCursor);
 
-        this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
-        this._boxPointer.visible = false;
-        this._boxPointer.style_class = 'candidate-popup-boxpointer';
-        Main.layoutManager.addChrome(this._boxPointer);
+        Main.layoutManager.addChrome(this);
 
         let box = new St.BoxLayout({ style_class: 'candidate-popup-content',
                                      vertical: true });
-        this._boxPointer.bin.set_child(box);
+        this.bin.set_child(box);
 
         this._preeditText = new St.Label({ style_class: 'candidate-popup-text',
                                            visible: false });
@@ -147,7 +159,7 @@ var CandidatePopup = class CandidatePopup {
         box.add(this._auxText);
 
         this._candidateArea = new CandidateArea();
-        box.add(this._candidateArea.actor);
+        box.add(this._candidateArea);
 
         this._candidateArea.connect('previous-page', () => {
             this._panelService.page_up();
@@ -225,7 +237,7 @@ var CandidatePopup = class CandidatePopup {
             this._updateVisibility();
         });
         panelService.connect('update-lookup-table', (_ps, lookupTable, visible) => {
-            this._candidateArea.actor.visible = visible;
+            this._candidateArea.visible = visible;
             this._updateVisibility();
 
             let nCandidates = lookupTable.get_number_of_candidates();
@@ -261,15 +273,15 @@ var CandidatePopup = class CandidatePopup {
             this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
         });
         panelService.connect('show-lookup-table', () => {
-            this._candidateArea.actor.show();
+            this._candidateArea.show();
             this._updateVisibility();
         });
         panelService.connect('hide-lookup-table', () => {
-            this._candidateArea.actor.hide();
+            this._candidateArea.hide();
             this._updateVisibility();
         });
         panelService.connect('focus-out', () => {
-            this._boxPointer.close(BoxPointer.PopupAnimation.NONE);
+            this.close(BoxPointer.PopupAnimation.NONE);
             Main.keyboard.resetSuggestions();
         });
     }
@@ -278,22 +290,22 @@ var CandidatePopup = class CandidatePopup {
         this._dummyCursor.set_position(Math.round(x), Math.round(y));
         this._dummyCursor.set_size(Math.round(w), Math.round(h));
 
-        if (this._boxPointer.visible)
-            this._boxPointer.setPosition(this._dummyCursor, 0);
+        if (this.visible)
+            this.setPosition(this._dummyCursor, 0);
     }
 
     _updateVisibility() {
         let isVisible = (!Main.keyboard.visible &&
                          (this._preeditText.visible ||
                           this._auxText.visible ||
-                          this._candidateArea.actor.visible));
+                          this._candidateArea.visible));
 
         if (isVisible) {
-            this._boxPointer.setPosition(this._dummyCursor, 0);
-            this._boxPointer.open(BoxPointer.PopupAnimation.NONE);
-            this._boxPointer.raise_top();
+            this.setPosition(this._dummyCursor, 0);
+            this.open(BoxPointer.PopupAnimation.NONE);
+            this.raise_top();
         } else {
-            this._boxPointer.close(BoxPointer.PopupAnimation.NONE);
+            this.close(BoxPointer.PopupAnimation.NONE);
         }
     }
 
@@ -303,4 +315,4 @@ var CandidatePopup = class CandidatePopup {
             if (attr.get_attr_type() == IBus.AttrType.BACKGROUND)
                 clutterText.set_selection(attr.get_start_index(), attr.get_end_index());
     }
-};
+});
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index 0a5c78ad81..04573f5858 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -717,13 +717,13 @@ var IconGrid = GObject.registerClass({
 
         this._items.push(item);
         if (index !== undefined)
-            this.insert_child_at_index(item.actor, index);
+            this.insert_child_at_index(item, index);
         else
-            this.add_actor(item.actor);
+            this.add_actor(item);
     }
 
     removeItem(item) {
-        this.remove_child(item.actor);
+        this.remove_child(item);
     }
 
     getItemAtIndex(index) {
@@ -963,7 +963,7 @@ var PaginatedIconGrid = GObject.registerClass({
     */
     openExtraSpace(sourceItem, side, nRows) {
         let children = this._getVisibleChildren();
-        let index = children.indexOf(sourceItem.actor);
+        let index = children.indexOf(sourceItem);
         if (index == -1)
             throw new Error('Item not found.');
 
diff --git a/js/ui/keyboard.js b/js/ui/keyboard.js
index 109c0a770e..2f66c240a0 100644
--- a/js/ui/keyboard.js
+++ b/js/ui/keyboard.js
@@ -89,8 +89,11 @@ class KeyContainer extends St.Widget {
         let gridLayout = new Clutter.GridLayout({ orientation: Clutter.Orientation.HORIZONTAL,
                                                   column_homogeneous: true,
                                                   row_homogeneous: true });
-        super._init({ layout_manager: gridLayout,
-                      x_expand: true, y_expand: true });
+        super._init({
+            layout_manager: gridLayout,
+            x_expand: true,
+            y_expand: true
+        });
         this._gridLayout = gridLayout;
         this._currentRow = 0;
         this._currentCol = 0;
@@ -161,24 +164,23 @@ class KeyContainer extends St.Widget {
     }
 });
 
-var Suggestions = class {
-    constructor() {
-        this.actor = new St.BoxLayout({ style_class: 'word-suggestions',
-                                        vertical: false });
-        this.actor.show();
+var Suggestions = GObject.registerClass(
+class Suggestions extends St.BoxLayout {
+    _init() {
+        super._init({ style_class: 'word-suggestions', vertical: false });
+        this.show();
     }
 
     add(word, callback) {
         let button = new St.Button({ label: word });
         button.connect('clicked', callback);
-        this.actor.add(button);
+        this.add(button);
     }
 
     clear() {
-        this.actor.remove_all_children();
+        this.remove_all_children();
     }
-};
-Signals.addSignalMethods(Suggestions.prototype);
+});
 
 var LanguageSelectionPopup = class extends PopupMenu.PopupMenu {
     constructor(actor) {
@@ -243,17 +245,25 @@ var LanguageSelectionPopup = class extends PopupMenu.PopupMenu {
     }
 };
 
-var Key = class Key {
-    constructor(key, extendedKeys) {
+var Key = GObject.registerClass({
+    Signals: {
+        'activated': {},
+        'long-press': {},
+        'pressed': { param_types: [GObject.TYPE_UINT, GObject.TYPE_STRING] },
+        'released': { param_types: [GObject.TYPE_UINT, GObject.TYPE_STRING] },
+    }
+}, class Key extends St.BoxLayout {
+    _init(key, extendedKeys) {
+        super._init({ style_class: 'key-container' });
+
         this.key = key || "";
         this.keyButton = this._makeKey(this.key);
 
         /* Add the key in a container, so keys can be padded without losing
          * logical proportions between those.
          */
-        this.actor = new St.BoxLayout ({ style_class: 'key-container' });
-        this.actor.add(this.keyButton, { expand: true, x_fill: true });
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.add(this.keyButton, { expand: true, x_fill: true });
+        this.connect('destroy', this._onDestroy.bind(this));
 
         this._extended_keys = extendedKeys;
         this._extended_keyboard = null;
@@ -461,8 +471,7 @@ var Key = class Key {
         else
             this.keyButton.remove_style_pseudo_class('latched');
     }
-};
-Signals.addSignalMethods(Key.prototype);
+});
 
 var KeyboardModel = class {
     constructor(groupName) {
@@ -590,7 +599,6 @@ var EmojiPager = GObject.registerClass({
             reactive: true,
             clip_to_allocation: true
         });
-
         this._sections = sections;
         this._nCols = nCols;
         this._nRows = nRows;
@@ -801,7 +809,7 @@ var EmojiPager = GObject.registerClass({
                 this.emit('emoji', str);
             });
 
-            gridLayout.attach(key.actor, col, row, 1, 1);
+            gridLayout.attach(key, col, row, 1, 1);
 
             col++;
             if (col >= this._nCols) {
@@ -843,7 +851,7 @@ var EmojiPager = GObject.registerClass({
         }
 
         let page = this._pages[nPage];
-        this.emit('page-changed', page.section, page.page, page.nPages);
+        this.emit('page-changed', page.section.label, page.page, page.nPages);
     }
 
     setCurrentSection(section, nPage) {
@@ -858,8 +866,21 @@ var EmojiPager = GObject.registerClass({
     }
 });
 
-var EmojiSelection = class EmojiSelection {
-    constructor() {
+var EmojiSelection = GObject.registerClass({
+    Signals: {
+        'emoji-selected': { param_types: [GObject.TYPE_STRING] },
+        'close-request': {},
+        'toggle': {},
+    }
+}, class EmojiSelection extends St.BoxLayout {
+    _init() {
+        super._init({
+            style_class: 'emoji-panel',
+            x_expand: true,
+            y_expand: true,
+            vertical: true
+        });
+
         this._sections = [
             { first: 'grinning face', label: '🙂️' },
             { first: 'selfie', label: '👍️' },
@@ -874,38 +895,34 @@ var EmojiSelection = class EmojiSelection {
 
         this._populateSections();
 
-        this.actor = new St.BoxLayout({ style_class: 'emoji-panel',
-                                        x_expand: true,
-                                        y_expand: true,
-                                        vertical: true });
-        this.actor.connect('notify::mapped', () => this._emojiPager.setCurrentPage(0));
+        this.connect('notify::mapped', () => this._emojiPager.setCurrentPage(0));
 
         this._emojiPager = new EmojiPager(this._sections, 11, 3);
-        this._emojiPager.connect('page-changed', (pager, section, page, nPages) => {
-            this._onPageChanged(section, page, nPages);
+        this._emojiPager.connect('page-changed', (pager, sectionLabel, page, nPages) => {
+            this._onPageChanged(sectionLabel, page, nPages);
         });
         this._emojiPager.connect('emoji', (pager, str) => {
             this.emit('emoji-selected', str);
         });
-        this.actor.add(this._emojiPager.actor, { expand: true });
+        this.add(this._emojiPager, { expand: true });
 
         this._pageIndicator = new PageIndicators.PageIndicators(false);
-        this.actor.add(this._pageIndicator, { expand: true, x_fill: false, y_fill: false });
+        this.add(this._pageIndicator, { expand: true, x_fill: false, y_fill: false });
         this._pageIndicator.setReactive(false);
 
         let bottomRow = this._createBottomRow();
-        this.actor.add(bottomRow, { expand: true, x_fill: false, y_fill: false });
+        this.add(bottomRow, { expand: true, x_fill: false, y_fill: false });
 
         this._emojiPager.setCurrentPage(0);
     }
 
-    _onPageChanged(section, page, nPages) {
+    _onPageChanged(sectionLabel, page, nPages) {
         this._pageIndicator.setNPages(nPages);
         this._pageIndicator.setCurrentPage(page);
 
         for (let i = 0; i < this._sections.length; i++) {
             let sect = this._sections[i];
-            sect.button.setLatched(section == sect);
+            sect.button.setLatched(sectionLabel == sect.label);
         }
     }
 
@@ -961,14 +978,14 @@ var EmojiSelection = class EmojiSelection {
         key = new Key('ABC', []);
         key.keyButton.add_style_class_name('default-key');
         key.connect('released', () => this.emit('toggle'));
-        row.appendKey(key.actor, 1.5);
+        row.appendKey(key, 1.5);
 
         for (let i = 0; i < this._sections.length; i++) {
             let section = this._sections[i];
 
             key = new Key(section.label, []);
             key.connect('released', () => this._emojiPager.setCurrentSection(section, 0));
-            row.appendKey(key.actor);
+            row.appendKey(key);
 
             section.button = key;
         }
@@ -979,7 +996,7 @@ var EmojiSelection = class EmojiSelection {
         key.connect('released', () => {
             this.emit('close-request');
         });
-        row.appendKey(key.actor);
+        row.appendKey(key);
         row.layoutButtons();
 
         let actor = new AspectContainer({ layout_manager: new Clutter.BinLayout(),
@@ -993,11 +1010,14 @@ var EmojiSelection = class EmojiSelection {
 
         return actor;
     }
-};
-Signals.addSignalMethods(EmojiSelection.prototype);
+});
 
-var Keypad = class Keypad {
-    constructor() {
+var Keypad = GObject.registerClass({
+    Signals: {
+        'keyval': { param_types: [GObject.TYPE_UINT] },
+    }
+}, class Keypad extends AspectContainer {
+    _init() {
         let keys = [
             { label: '1', keyval: Clutter.KEY_1, left: 0, top: 0 },
             { label: '2', keyval: Clutter.KEY_2, left: 1, top: 0 },
@@ -1013,14 +1033,17 @@ var Keypad = class Keypad {
             { keyval: Clutter.KEY_Return, extraClassName: 'enter-key', left: 3, top: 1, height: 2 },
         ];
 
-        this.actor = new AspectContainer({ layout_manager: new Clutter.BinLayout(),
-                                           x_expand: true, y_expand: true });
+        super._init({
+            layout_manager: new Clutter.BinLayout(),
+            x_expand: true,
+            y_expand: true
+        });
 
         let gridLayout = new Clutter.GridLayout({ orientation: Clutter.Orientation.HORIZONTAL,
                                                   column_homogeneous: true,
                                                   row_homogeneous: true });
         this._box = new St.Widget({ layout_manager: gridLayout, x_expand: true, y_expand: true });
-        this.actor.add_child(this._box);
+        this.add_child(this._box);
 
         for (let i = 0; i < keys.length; i++) {
             let cur = keys[i];
@@ -1032,15 +1055,14 @@ var Keypad = class Keypad {
             let w, h;
             w = cur.width || 1;
             h = cur.height || 1;
-            gridLayout.attach(key.actor, cur.left, cur.top, w, h);
+            gridLayout.attach(key, cur.left, cur.top, w, h);
 
             key.connect('released', () => {
                 this.emit('keyval', cur.keyval);
             });
         }
     }
-};
-Signals.addSignalMethods(Keypad.prototype);
+});
 
 var KeyboardManager = class KeyBoardManager {
     constructor() {
@@ -1091,11 +1113,11 @@ var KeyboardManager = class KeyBoardManager {
     }
 
     get keyboardActor() {
-        return this._keyboard.actor;
+        return this._keyboard;
     }
 
     get visible() {
-        return this._keyboard && this._keyboard.actor.visible;
+        return this._keyboard && this._keyboard.visible;
     }
 
     open(monitor) {
@@ -1128,9 +1150,10 @@ var KeyboardManager = class KeyBoardManager {
     }
 };
 
-var Keyboard = class Keyboard {
-    constructor() {
-        this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true });
+var Keyboard = GObject.registerClass(
+class Keyboard extends St.BoxLayout {
+    _init() {
+        super._init({ name: 'keyboard', vertical: true });
         this._focusInExtendedKeys = false;
         this._emojiActive = false;
 
@@ -1175,11 +1198,7 @@ var Keyboard = class Keyboard {
 
         this._setupKeyboard();
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-    }
-
-    destroy() {
-        this.actor.destroy();
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
     _connectSignal(obj, signal, callback) {
@@ -1192,7 +1211,7 @@ var Keyboard = class Keyboard {
     }
 
     get visible() {
-        return this._keyboardVisible && this.actor.visible;
+        return this._keyboardVisible && super.visible;
     }
 
     _onFocusPositionChanged(focusTracker) {
@@ -1213,8 +1232,8 @@ var Keyboard = class Keyboard {
         this._emojiSelection = null;
         this._keypad = null;
 
-        Main.layoutManager.untrackChrome(this.actor);
-        Main.layoutManager.keyboardBox.remove_actor(this.actor);
+        Main.layoutManager.untrackChrome(this);
+        Main.layoutManager.keyboardBox.remove_actor(this);
 
         if (this._languagePopup) {
             this._languagePopup.destroy();
@@ -1223,8 +1242,8 @@ var Keyboard = class Keyboard {
     }
 
     _setupKeyboard() {
-        Main.layoutManager.keyboardBox.add_actor(this.actor);
-        Main.layoutManager.trackChrome(this.actor);
+        Main.layoutManager.keyboardBox.add_actor(this);
+        Main.layoutManager.trackChrome(this);
 
         this._keyboardController = new KeyboardController();
 
@@ -1232,12 +1251,10 @@ var Keyboard = class Keyboard {
         this._currentPage = null;
 
         this._suggestions = new Suggestions();
-        this.actor.add(this._suggestions.actor,
-                       { x_align: St.Align.MIDDLE,
-                         x_fill: false });
+        this.add(this._suggestions, { x_align: St.Align.MIDDLE, x_fill: false });
 
         this._aspectContainer = new AspectContainer({ layout_manager: new Clutter.BinLayout() });
-        this.actor.add(this._aspectContainer, { expand: true });
+        this.add(this._aspectContainer, { expand: true });
 
         this._emojiSelection = new EmojiSelection();
         this._emojiSelection.connect('toggle', this._toggleEmoji.bind(this));
@@ -1246,16 +1263,16 @@ var Keyboard = class Keyboard {
             this._keyboardController.commitString(emoji);
         });
 
-        this._aspectContainer.add_child(this._emojiSelection.actor);
-        this._emojiSelection.actor.hide();
+        this._aspectContainer.add_child(this._emojiSelection);
+        this._emojiSelection.hide();
 
         this._keypad = new Keypad();
         this._connectSignal(this._keypad, 'keyval', (_keypad, keyval) => {
             this._keyboardController.keyvalPress(keyval);
             this._keyboardController.keyvalRelease(keyval);
         });
-        this._aspectContainer.add_child(this._keypad.actor);
-        this._keypad.actor.hide();
+        this._aspectContainer.add_child(this._keypad);
+        this._keypad.hide();
         this._keypadVisible = false;
 
         this._ensureKeysForGroup(this._keyboardController.getCurrentGroup());
@@ -1264,7 +1281,7 @@ var Keyboard = class Keyboard {
         // Keyboard models are defined in LTR, we must override
         // the locale setting in order to avoid flipping the
         // keyboard on RTL locales.
-        this.actor.text_direction = Clutter.TextDirection.LTR;
+        this.text_direction = Clutter.TextDirection.LTR;
 
         this._connectSignal(this._keyboardController, 'active-group',
             this._onGroupChanged.bind(this));
@@ -1369,7 +1386,7 @@ var Keyboard = class Keyboard {
                     this._setActiveLayer(0);
             });
 
-            layout.appendKey(button.actor, button.keyButton.keyWidth);
+            layout.appendKey(button, button.keyButton.keyWidth);
         }
     }
 
@@ -1440,7 +1457,7 @@ var Keyboard = class Keyboard {
                     /* Only hide the key actor, so the container still takes space */
                     extraButton.keyButton.hide();
                 } else {
-                    extraButton.actor.hide();
+                    extraButton.hide();
                 }
                 extraButton.setWidth(1.5);
             } else if (key.right && numKeys > 8) {
@@ -1451,7 +1468,7 @@ var Keyboard = class Keyboard {
                 extraButton.setWidth(1.5);
             }
 
-            layout.appendKey(extraButton.actor, extraButton.keyButton.keyWidth);
+            layout.appendKey(extraButton, extraButton.keyButton.keyWidth);
         }
     }
 
@@ -1462,7 +1479,7 @@ var Keyboard = class Keyboard {
 
     _setEmojiActive(active) {
         this._emojiActive = active;
-        this._emojiSelection.actor.visible = this._emojiActive;
+        this._emojiSelection.visible = this._emojiActive;
         this._updateCurrentPageVisible();
     }
 
@@ -1530,12 +1547,12 @@ var Keyboard = class Keyboard {
     _relayout() {
         let monitor = Main.layoutManager.keyboardMonitor;
 
-        if (this.actor == null || monitor == null)
+        if (!monitor)
             return;
 
         let maxHeight = monitor.height / 3;
-        this.actor.width = monitor.width;
-        this.actor.height = maxHeight;
+        this.width = monitor.width;
+        this.height = maxHeight;
     }
 
     _onGroupChanged() {
@@ -1544,7 +1561,7 @@ var Keyboard = class Keyboard {
     }
 
     _onKeyboardGroupsChanged() {
-        let nonGroupActors = [this._emojiSelection.actor, this._keypad.actor];
+        let nonGroupActors = [this._emojiSelection, this._keypad];
         this._aspectContainer.get_children().filter(c => !nonGroupActors.includes(c)).forEach(c => {
             c.destroy();
         });
@@ -1558,7 +1575,7 @@ var Keyboard = class Keyboard {
             return;
 
         this._keypadVisible = visible;
-        this._keypad.actor.visible = this._keypadVisible;
+        this._keypad.visible = this._keypadVisible;
         this._updateCurrentPageVisible();
     }
 
@@ -1693,7 +1710,7 @@ var Keyboard = class Keyboard {
         if (!this._suggestions)
             return;
         this._suggestions.add(text, callback);
-        this._suggestions.actor.show();
+        this._suggestions.show();
     }
 
     _clearShowIdle() {
@@ -1770,7 +1787,7 @@ var Keyboard = class Keyboard {
 
         this._oskFocusWindow = window;
     }
-};
+});
 
 var KeyboardController = class {
     constructor() {
diff --git a/js/ui/layout.js b/js/ui/layout.js
index 744a8f1192..767bfbd16c 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -606,17 +606,17 @@ var LayoutManager = GObject.registerClass({
             return;
         }
         this._systemBackground = new Background.SystemBackground();
-        this._systemBackground.actor.hide();
+        this._systemBackground.hide();
 
-        global.stage.insert_child_below(this._systemBackground.actor, null);
+        global.stage.insert_child_below(this._systemBackground, null);
 
         let constraint = new Clutter.BindConstraint({ source: global.stage,
                                                       coordinate: Clutter.BindCoordinate.ALL });
-        this._systemBackground.actor.add_constraint(constraint);
+        this._systemBackground.add_constraint(constraint);
 
         let signalId = this._systemBackground.connect('loaded', () => {
             this._systemBackground.disconnect(signalId);
-            this._systemBackground.actor.show();
+            this._systemBackground.show();
             global.stage.show();
 
             this._prepareStartupAnimation();
@@ -722,7 +722,7 @@ var LayoutManager = GObject.registerClass({
         this._coverPane.destroy();
         this._coverPane = null;
 
-        this._systemBackground.actor.destroy();
+        this._systemBackground.destroy();
         this._systemBackground = null;
 
         this._startingUp = false;
@@ -1112,8 +1112,11 @@ var LayoutManager = GObject.registerClass({
 //
 // This class manages a "hot corner" that can toggle switching to
 // overview.
-var HotCorner = class HotCorner {
-    constructor(layoutManager, monitor, x, y) {
+var HotCorner = GObject.registerClass(
+class HotCorner extends Clutter.Actor {
+    _init(layoutManager, monitor, x, y) {
+        super._init();
+
         // We use this flag to mark the case where the user has entered the
         // hot corner and has not left both the hot corner and a surrounding
         // guard area (the "environs"). This avoids triggering the hot corner
@@ -1142,6 +1145,8 @@ var HotCorner = class HotCorner {
 
         this._ripples = new Ripples.Ripples(px, py, 'ripple-box');
         this._ripples.addTo(layoutManager.uiGroup);
+
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
     setBarrierSize(size) {
@@ -1181,11 +1186,14 @@ var HotCorner = class HotCorner {
 
     _setupFallbackCornerIfNeeded(layoutManager) {
         if (!global.display.supports_extended_barriers()) {
-            this.actor = new Clutter.Actor({ name: 'hot-corner-environs',
-                                             x: this._x, y: this._y,
-                                             width: 3,
-                                             height: 3,
-                                             reactive: true });
+            this.set({
+                name: 'hot-corner-environs',
+                x: this._x,
+                y: this._y,
+                width: 3,
+                height: 3,
+                reactive: true
+            });
 
             this._corner = new Clutter.Actor({ name: 'hot-corner',
                                                width: 1,
@@ -1194,18 +1202,17 @@ var HotCorner = class HotCorner {
                                                reactive: true });
             this._corner._delegate = this;
 
-            this.actor.add_child(this._corner);
-            layoutManager.addChrome(this.actor);
+            this.add_child(this._corner);
+            layoutManager.addChrome(this);
 
             if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) {
-                this._corner.set_position(this.actor.width - this._corner.width, 0);
-                this.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
+                this._corner.set_position(this.width - this._corner.width, 0);
+                this.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
             } else {
                 this._corner.set_position(0, 0);
             }
 
-            this.actor.connect('leave-event',
-                               this._onEnvironsLeft.bind(this));
+            this.connect('leave-event', this._onEnvironsLeft.bind(this));
 
             this._corner.connect('enter-event',
                                  this._onCornerEntered.bind(this));
@@ -1214,14 +1221,11 @@ var HotCorner = class HotCorner {
         }
     }
 
-    destroy() {
+    _onDestroy() {
         this.setBarrierSize(0);
         this._pressureBarrier.destroy();
         this._pressureBarrier = null;
 
-        if (this.actor)
-            this.actor.destroy();
-
         this._ripples.destroy();
     }
 
@@ -1253,7 +1257,7 @@ var HotCorner = class HotCorner {
     }
 
     _onCornerLeft(actor, event) {
-        if (event.get_related() != this.actor)
+        if (event.get_related() != this)
             this._entered = false;
         // Consume event, otherwise this will confuse onEnvironsLeft
         return Clutter.EVENT_STOP;
@@ -1264,7 +1268,7 @@ var HotCorner = class HotCorner {
             this._entered = false;
         return Clutter.EVENT_PROPAGATE;
     }
-};
+});
 
 var PressureBarrier = class PressureBarrier {
     constructor(threshold, timeout, actionMode) {
diff --git a/js/ui/lightbox.js b/js/ui/lightbox.js
index 3ae6df26bc..35e40016f7 100644
--- a/js/ui/lightbox.js
+++ b/js/ui/lightbox.js
@@ -2,7 +2,6 @@
 /* exported Lightbox */
 
 const { Clutter, GObject, Shell, St } = imports.gi;
-const Signals = imports.signals;
 
 const Params = imports.misc.params;
 
@@ -89,8 +88,8 @@ var RadialShaderEffect = GObject.registerClass({
  *           - inhibitEvents: whether to inhibit events for @container
  *           - width: shade actor width
  *           - height: shade actor height
- *           - fadeInTime: milliseconds used to fade in
- *           - fadeOutTime: milliseconds used to fade out
+ *           - fadeFactor: fading opacity factor
+ *           - radialEffect: whether to enable the GLSL radial effect
  *
  * Lightbox creates a dark translucent "shade" actor to hide the
  * contents of @container, and allows you to specify particular actors
@@ -106,8 +105,13 @@ var RadialShaderEffect = GObject.registerClass({
  * @container and will track any changes in its size. You can override
  * this by passing an explicit width and height in @params.
  */
-var Lightbox = class Lightbox {
-    constructor(container, params) {
+var Lightbox = GObject.registerClass({
+    Properties: {
+        'active': GObject.ParamSpec.boolean(
+            'active', 'active', 'active', GObject.ParamFlags.READABLE, false),
+    }
+}, class Lightbox extends St.Bin {
+    _init(container, params) {
         params = Params.parse(params, {
             inhibitEvents: false,
             width: null,
@@ -116,32 +120,34 @@ var Lightbox = class Lightbox {
             radialEffect: false,
         });
 
+        super._init({
+            reactive: params.inhibitEvents,
+            width: params.width,
+            height: params.height,
+            visible: false
+        });
+
         this._active = false;
         this._container = container;
         this._children = container.get_children();
         this._fadeFactor = params.fadeFactor;
         this._radialEffect = Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL) && 
params.radialEffect;
 
-        this.actor = new St.Bin({ reactive: params.inhibitEvents });
-
         if (this._radialEffect)
-            this.actor.add_effect(new RadialShaderEffect({ name: 'radial' }));
+            this.add_effect(new RadialShaderEffect({ name: 'radial' }));
         else
-            this.actor.set({ opacity: 0, style_class: 'lightbox' });
+            this.set({ opacity: 0, style_class: 'lightbox' });
 
-        container.add_actor(this.actor);
-        this.actor.raise_top();
-        this.actor.hide();
+        container.add_actor(this);
+        this.raise_top();
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
 
-        if (params.width && params.height) {
-            this.actor.width = params.width;
-            this.actor.height = params.height;
-        } else {
-            let constraint = new Clutter.BindConstraint({ source: container,
-                                                          coordinate: Clutter.BindCoordinate.ALL });
-            this.actor.add_constraint(constraint);
+        if (!params.width || !params.height) {
+            this.add_constraint(new Clutter.BindConstraint({
+                source: container,
+                coordinate: Clutter.BindCoordinate.ALL
+            }));
         }
 
         this._actorAddedSignalId = container.connect('actor-added', this._actorAdded.bind(this));
@@ -156,14 +162,14 @@ var Lightbox = class Lightbox {
 
     _actorAdded(container, newChild) {
         let children = this._container.get_children();
-        let myIndex = children.indexOf(this.actor);
+        let myIndex = children.indexOf(this);
         let newChildIndex = children.indexOf(newChild);
 
         if (newChildIndex > myIndex) {
             // The child was added above the shade (presumably it was
             // made the new top-most child). Move it below the shade,
             // and add it to this._children as the new topmost actor.
-            newChild.lower(this.actor);
+            this._container.set_child_above_sibling(this, newChild);
             this._children.push(newChild);
         } else if (newChildIndex == 0) {
             // Bottom of stack
@@ -177,7 +183,7 @@ var Lightbox = class Lightbox {
     }
 
     lightOn(fadeInTime) {
-        this.actor.remove_all_transitions();
+        this.remove_all_transitions();
 
         let easeProps = {
             duration: fadeInTime || 0,
@@ -186,19 +192,19 @@ var Lightbox = class Lightbox {
 
         let onComplete = () => {
             this._active = true;
-            this.emit('active-changed');
+            this.notify('active');
         };
 
-        this.actor.show();
+        this.show();
 
         if (this._radialEffect) {
-            this.actor.ease_property(
+            this.ease_property(
                 '@effects.radial.brightness', VIGNETTE_BRIGHTNESS, easeProps);
-            this.actor.ease_property(
+            this.ease_property(
                 '@effects.radial.sharpness', VIGNETTE_SHARPNESS,
                 Object.assign({ onComplete }, easeProps));
         } else {
-            this.actor.ease(Object.assign(easeProps, {
+            this.ease(Object.assign(easeProps, {
                 opacity: 255 * this._fadeFactor,
                 onComplete
             }));
@@ -206,25 +212,25 @@ var Lightbox = class Lightbox {
     }
 
     lightOff(fadeOutTime) {
-        this.actor.remove_all_transitions();
+        this.remove_all_transitions();
 
         this._active = false;
-        this.emit('active-changed');
+        this.notify('active');
 
         let easeProps = {
             duration: fadeOutTime || 0,
             mode: Clutter.AnimationMode.EASE_OUT_QUAD
         };
 
-        let onComplete = () => this.actor.hide();
+        let onComplete = () => this.hide();
 
         if (this._radialEffect) {
-            this.actor.ease_property(
+            this.ease_property(
                 '@effects.radial.brightness', 1.0, easeProps);
-            this.actor.ease_property(
+            this.ease_property(
                 '@effects.radial.sharpness', 0.0, Object.assign({ onComplete }, easeProps));
         } else {
-            this.actor.ease(Object.assign(easeProps, { opacity: 0, onComplete }));
+            this.ease(Object.assign(easeProps, { opacity: 0, onComplete }));
         }
     }
 
@@ -255,7 +261,7 @@ var Lightbox = class Lightbox {
         // case we may need to indicate some *other* actor as the new
         // sibling of the to-be-lowered one.
 
-        let below = this.actor;
+        let below = this;
         for (let i = this._children.length - 1; i >= 0; i--) {
             if (this._children[i] == window)
                 this._children[i].raise_top();
@@ -268,15 +274,6 @@ var Lightbox = class Lightbox {
         this._highlighted = window;
     }
 
-    /**
-     * destroy:
-     *
-     * Destroys the lightbox.
-     */
-    destroy() {
-        this.actor.destroy();
-    }
-
     /**
      * _onDestroy:
      *
@@ -284,10 +281,15 @@ var Lightbox = class Lightbox {
      * by destroying its container or by explicitly calling this.destroy().
      */
     _onDestroy() {
-        this._container.disconnect(this._actorAddedSignalId);
-        this._container.disconnect(this._actorRemovedSignalId);
+        if (this._actorAddedSignalId) {
+            this._container.disconnect(this._actorAddedSignalId);
+            this._actorAddedSignalId = 0;
+        }
+        if (this._actorRemovedSignalId) {
+            this._container.disconnect(this._actorRemovedSignalId);
+            this._actorRemovedSignalId = 0;
+        }
 
         this.highlight(null);
     }
-};
-Signals.addSignalMethods(Lightbox.prototype);
+});
diff --git a/js/ui/lookingGlass.js b/js/ui/lookingGlass.js
index 03017d6109..85e8874b19 100644
--- a/js/ui/lookingGlass.js
+++ b/js/ui/lookingGlass.js
@@ -110,9 +110,11 @@ var AutoComplete = class AutoComplete {
 Signals.addSignalMethods(AutoComplete.prototype);
 
 
-var Notebook = class Notebook {
-    constructor() {
-        this.actor = new St.BoxLayout({ vertical: true });
+var Notebook = GObject.registerClass({
+    Signals: { 'selection': { param_types: [Clutter.Actor.$gtype] } },
+}, class Notebook extends St.BoxLayout {
+    _init() {
+        super._init({ vertical: true });
 
         this.tabControls = new St.BoxLayout({ style_class: 'labels' });
 
@@ -143,7 +145,7 @@ var Notebook = class Notebook {
                         _scrollToBottom: false };
         this._tabs.push(tabData);
         scrollview.hide();
-        this.actor.add(scrollview, { expand: true });
+        this.add(scrollview, { expand: true });
 
         let vAdjust = scrollview.vscroll.adjustment;
         vAdjust.connect('changed', () => this._onAdjustScopeChanged(tabData));
@@ -174,7 +176,7 @@ var Notebook = class Notebook {
         // Focus the new tab before unmapping the old one
         let tabData = this._tabs[index];
         if (!tabData.scrollView.navigate_focus(null, St.DirectionType.TAB_FORWARD, false))
-            this.actor.grab_key_focus();
+            this.grab_key_focus();
 
         this._unselect();
 
@@ -234,8 +236,7 @@ var Notebook = class Notebook {
 
         this.selectIndex(prevIndex);
     }
-};
-Signals.addSignalMethods(Notebook.prototype);
+});
 
 function objectToString(o) {
     if (typeof o == typeof objectToString) {
@@ -246,57 +247,65 @@ function objectToString(o) {
     }
 }
 
-var ObjLink = class ObjLink {
-    constructor(lookingGlass, o, title) {
+var ObjLink = GObject.registerClass(
+class ObjLink extends St.Button {
+    _init(lookingGlass, o, title) {
         let text;
         if (title)
             text = title;
         else
             text = objectToString(o);
         text = GLib.markup_escape_text(text, -1);
-        this._obj = o;
 
-        this.actor = new St.Button({ reactive: true,
-                                     track_hover: true,
-                                     style_class: 'shell-link',
-                                     label: text });
-        this.actor.get_child().single_line_mode = true;
-        this.actor.connect('clicked', this._onClicked.bind(this));
+        super._init({
+            reactive: true,
+            track_hover: true,
+            style_class: 'shell-link',
+            label: text
+        });
+        this.get_child().single_line_mode = true;
+        this.connect('clicked', this._onClicked.bind(this));
 
+        this._obj = o;
         this._lookingGlass = lookingGlass;
     }
 
     _onClicked() {
-        this._lookingGlass.inspectObject(this._obj, this.actor);
+        this._lookingGlass.inspectObject(this._obj, this);
     }
-};
+});
+
+var Result = GObject.registerClass({
+    GTypeName: 'LookingClass_Result'
+}, class Result extends St.BoxLayout {
+    _init(lookingGlass, command, o, index) {
+        super._init({ vertical: true });
 
-var Result = class Result {
-    constructor(lookingGlass, command, o, index) {
         this.index = index;
         this.o = o;
 
-        this.actor = new St.BoxLayout({ vertical: true });
         this._lookingGlass = lookingGlass;
 
         let cmdTxt = new St.Label({ text: command });
         cmdTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
-        this.actor.add(cmdTxt);
+        this.add(cmdTxt);
         let box = new St.BoxLayout({});
-        this.actor.add(box);
+        this.add(box);
         let resultTxt = new St.Label({ text: `r(${index}) = ` });
         resultTxt.clutter_text.ellipsize = Pango.EllipsizeMode.END;
         box.add(resultTxt);
         let objLink = new ObjLink(this._lookingGlass, o);
-        box.add(objLink.actor);
+        box.add(objLink);
     }
-};
+});
 
-var WindowList = class WindowList {
-    constructor(lookingGlass) {
-        this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
+var WindowList = GObject.registerClass({
+    GTypeName: 'LookingClass_WindowList'
+}, class WindowList extends St.BoxLayout {
+    _init(lookingGlass) {
+        super._init({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
         let tracker = Shell.WindowTracker.get_default();
-        this._updateId = Main.initializeDeferredWork(this.actor, this._updateWindowList.bind(this));
+        this._updateId = Main.initializeDeferredWork(this, this._updateWindowList.bind(this));
         global.display.connect('window-created', this._updateWindowList.bind(this));
         tracker.connect('tracked-windows-changed', this._updateWindowList.bind(this));
 
@@ -307,7 +316,7 @@ var WindowList = class WindowList {
         if (!this._lookingGlass.isOpen)
             return;
 
-        this.actor.destroy_all_children();
+        this.destroy_all_children();
         let windows = global.get_window_actors();
         let tracker = Shell.WindowTracker.get_default();
         for (let i = 0; i < windows.length; i++) {
@@ -318,9 +327,9 @@ var WindowList = class WindowList {
                 metaWindow._lookingGlassManaged = true;
             }
             let box = new St.BoxLayout({ vertical: true });
-            this.actor.add(box);
+            this.add(box);
             let windowLink = new ObjLink(this._lookingGlass, metaWindow, metaWindow.title);
-            box.add(windowLink.actor, { x_align: St.Align.START, x_fill: false });
+            box.add(windowLink, { x_align: St.Align.START, x_fill: false });
             let propsBox = new St.BoxLayout({ vertical: true, style: 'padding-left: 6px;' });
             box.add(propsBox);
             propsBox.add(new St.Label({ text: `wmclass: ${metaWindow.get_wm_class()}` }));
@@ -331,7 +340,7 @@ var WindowList = class WindowList {
                 propsBox.add(propBox);
                 propBox.add(new St.Label({ text: 'app: ' }), { y_fill: false });
                 let appLink = new ObjLink(this._lookingGlass, app, app.get_id());
-                propBox.add(appLink.actor, { y_fill: false });
+                propBox.add(appLink, { y_fill: false });
                 propBox.add(icon, { y_fill: false });
             } else {
                 propsBox.add(new St.Label({ text: '<untracked>' }));
@@ -342,23 +351,27 @@ var WindowList = class WindowList {
     update() {
         this._updateWindowList();
     }
-};
-Signals.addSignalMethods(WindowList.prototype);
+});
+
+var ObjInspector = GObject.registerClass(
+class ObjInspector extends St.ScrollView {
+    _init(lookingGlass) {
+        super._init({
+            pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
+            x_fill: true,
+            y_fill: true
+        });
 
-var ObjInspector = class ObjInspector {
-    constructor(lookingGlass) {
         this._obj = null;
         this._previousObj = null;
 
         this._parentList = [];
 
-        this.actor = new St.ScrollView({ pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
-                                         x_fill: true, y_fill: true });
-        this.actor.get_hscroll_bar().hide();
+        this.get_hscroll_bar().hide();
         this._container = new St.BoxLayout({ name: 'LookingGlassPropertyInspector',
                                              style_class: 'lg-dialog',
                                              vertical: true });
-        this.actor.add_actor(this._container);
+        this.add_actor(this._container);
 
         this._lookingGlass = lookingGlass;
     }
@@ -404,7 +417,7 @@ var ObjInspector = class ObjInspector {
                 let link;
                 try {
                     let prop = obj[propName];
-                    link = new ObjLink(this._lookingGlass, prop).actor;
+                    link = new ObjLink(this._lookingGlass, prop);
                 } catch (e) {
                     link = new St.Label({ text: '<error>' });
                 }
@@ -421,17 +434,17 @@ var ObjInspector = class ObjInspector {
             return;
         this._previousObj = null;
         this._open = true;
-        this.actor.show();
+        this.show();
         if (sourceActor) {
-            this.actor.set_scale(0, 0);
-            this.actor.ease({
+            this.set_scale(0, 0);
+            this.ease({
                 scale_x: 1,
                 scale_y: 1,
                 mode: Clutter.AnimationMode.EASE_OUT_QUAD,
                 duration: 200
             });
         } else {
-            this.actor.set_scale(1, 1);
+            this.set_scale(1, 1);
         }
     }
 
@@ -439,7 +452,7 @@ var ObjInspector = class ObjInspector {
         if (!this._open)
             return;
         this._open = false;
-        this.actor.hide();
+        this.hide();
         this._previousObj = null;
         this._obj = null;
     }
@@ -453,7 +466,7 @@ var ObjInspector = class ObjInspector {
     _onBack() {
         this.selectObject(this._previousObj, true);
     }
-};
+});
 
 var RedBorderEffect = GObject.registerClass(
 class RedBorderEffect extends Clutter.Effect {
@@ -484,8 +497,7 @@ var Inspector = GObject.registerClass({
                'target': { param_types: [Clutter.Actor.$gtype, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] } },
 }, class Inspector extends Clutter.Actor {
     _init(lookingGlass) {
-        super._init({ width: 0,
-                      height: 0 });
+        super._init({ width: 0, height: 0 });
 
         Main.uiGroup.add_actor(this);
 
@@ -620,18 +632,20 @@ var Inspector = GObject.registerClass({
     }
 });
 
-var Extensions = class Extensions {
-    constructor(lookingGlass) {
+var Extensions = GObject.registerClass({
+    GTypeName: 'LookingClass_Extensions'
+}, class Extensions extends St.BoxLayout {
+    _init(lookingGlass) {
+        super._init({ vertical: true, name: 'lookingGlassExtensions' });
+
         this._lookingGlass = lookingGlass;
-        this.actor = new St.BoxLayout({ vertical: true,
-                                        name: 'lookingGlassExtensions' });
         this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
                                             text: _("No extensions installed") });
         this._numExtensions = 0;
         this._extensionsList = new St.BoxLayout({ vertical: true,
                                                   style_class: 'lg-extensions-list' });
         this._extensionsList.add(this._noExtensions);
-        this.actor.add(this._extensionsList);
+        this.add(this._extensionsList);
 
         Main.extensionManager.getUuids().forEach(uuid => {
             this._loadExtension(null, uuid);
@@ -759,10 +773,19 @@ var Extensions = class Extensions {
 
         return box;
     }
-};
+});
+
+var LookingGlass = GObject.registerClass(
+class LookingGlass extends St.BoxLayout {
+    _init() {
+        super._init({
+            name: 'LookingGlassDialog',
+            style_class: 'lg-dialog',
+            vertical: true,
+            visible: false,
+            reactive: true
+        });
 
-var LookingGlass = class LookingGlass {
-    constructor() {
         this._borderPaintTarget = null;
         this._redBorderEffect = new RedBorderEffect();
 
@@ -775,12 +798,7 @@ var LookingGlass = class LookingGlass {
         // Sort of magic, but...eh.
         this._maxItems = 150;
 
-        this.actor = new St.BoxLayout({ name: 'LookingGlassDialog',
-                                        style_class: 'lg-dialog',
-                                        vertical: true,
-                                        visible: false,
-                                        reactive: true });
-        this.actor.connect('key-press-event', this._globalKeyPressEvent.bind(this));
+        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',
@@ -788,8 +806,8 @@ var LookingGlass = class LookingGlass {
         this._updateFont();
 
         // We want it to appear to slide out from underneath the panel
-        Main.uiGroup.add_actor(this.actor);
-        Main.uiGroup.set_child_below_sibling(this.actor,
+        Main.uiGroup.add_actor(this);
+        Main.uiGroup.set_child_below_sibling(this,
                                              Main.layoutManager.panelBox);
         Main.layoutManager.panelBox.connect('allocation-changed',
                                             this._queueResize.bind(this));
@@ -797,11 +815,11 @@ var LookingGlass = class LookingGlass {
                                                this._queueResize.bind(this));
 
         this._objInspector = new ObjInspector(this);
-        Main.uiGroup.add_actor(this._objInspector.actor);
-        this._objInspector.actor.hide();
+        Main.uiGroup.add_actor(this._objInspector);
+        this._objInspector.hide();
 
         let toolbar = new St.BoxLayout({ name: 'Toolbar' });
-        this.actor.add_actor(toolbar);
+        this.add_actor(toolbar);
         let inspectIcon = new St.Icon({ icon_name: 'gtk-color-picker',
                                         icon_size: 24 });
         toolbar.add_actor(inspectIcon);
@@ -812,10 +830,10 @@ var LookingGlass = class LookingGlass {
                 this._pushResult(`inspect(${Math.round(stageX)}, ${Math.round(stageY)})`, target);
             });
             inspector.connect('closed', () => {
-                this.actor.show();
+                this.show();
                 global.stage.set_key_focus(this._entry);
             });
-            this.actor.hide();
+            this.hide();
             return Clutter.EVENT_STOP;
         });
 
@@ -837,7 +855,7 @@ var LookingGlass = class LookingGlass {
 
         let notebook = new Notebook();
         this._notebook = notebook;
-        this.actor.add(notebook.actor, { expand: true });
+        this.add(notebook, { expand: true });
 
         let emptyBox = new St.Bin();
         toolbar.add(emptyBox, { expand: true });
@@ -860,10 +878,10 @@ var LookingGlass = class LookingGlass {
         this._entryArea.add(this._entry, { expand: true });
 
         this._windowList = new WindowList(this);
-        notebook.appendPage('Windows', this._windowList.actor);
+        notebook.appendPage('Windows', this._windowList);
 
         this._extensions = new Extensions(this);
-        notebook.appendPage('Extensions', this._extensions.actor);
+        notebook.appendPage('Extensions', this._extensions);
 
         this._entry.clutter_text.connect('activate', (o, _e) => {
             // Hide any completions we are currently showing
@@ -905,7 +923,7 @@ var LookingGlass = class LookingGlass {
         // monospace font to be bold/oblique/etc. Could easily be added here.
         let size = fontDesc.get_size() / 1024.;
         let unit = fontDesc.get_size_is_absolute() ? 'px' : 'pt';
-        this.actor.style = `
+        this.style = `
             font-size: ${size}${unit};
             font-family: "${fontDesc.get_family()}";`;
     }
@@ -922,7 +940,7 @@ var LookingGlass = class LookingGlass {
         let index = this._results.length + this._offset;
         let result = new Result(this, CHEVRON + command, obj, index);
         this._results.push(result);
-        this._resultsArea.add(result.actor);
+        this._resultsArea.add(result);
         if (obj instanceof Clutter.Actor)
             this.setBorderPaintTarget(obj);
 
@@ -1041,15 +1059,15 @@ var LookingGlass = class LookingGlass {
         let myWidth = primary.width * 0.7;
         let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
         let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
-        this.actor.x = primary.x + (primary.width - myWidth) / 2;
+        this.x = primary.x + (primary.width - myWidth) / 2;
         this._hiddenY = primary.y + Main.layoutManager.panelBox.height - myHeight;
         this._targetY = this._hiddenY + myHeight;
-        this.actor.y = this._hiddenY;
-        this.actor.width = myWidth;
-        this.actor.height = myHeight;
-        this._objInspector.actor.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
-        this._objInspector.actor.set_position(this.actor.x + Math.floor(myWidth * 0.1),
-                                              this._targetY + Math.floor(myHeight * 0.1));
+        this.y = this._hiddenY;
+        this.width = myWidth;
+        this.height = myHeight;
+        this._objInspector.set_size(Math.floor(myWidth * 0.8), Math.floor(myHeight * 0.8));
+        this._objInspector.set_position(this.x + Math.floor(myWidth * 0.1),
+                                        this._targetY + Math.floor(myHeight * 0.1));
     }
 
     insertObject(obj) {
@@ -1066,7 +1084,7 @@ var LookingGlass = class LookingGlass {
         let symbol = event.get_key_symbol();
         let modifierState = event.get_state();
         if (symbol == Clutter.Escape) {
-            if (this._objInspector.actor.visible) {
+            if (this._objInspector.visible) {
                 this._objInspector.close();
             } else {
                 this.close();
@@ -1092,16 +1110,16 @@ var LookingGlass = class LookingGlass {
             return;
 
         this._notebook.selectIndex(0);
-        this.actor.show();
+        this.show();
         this._open = true;
         this._history.lastItem();
 
-        this.actor.remove_all_transitions();
+        this.remove_all_transitions();
 
         // We inverse compensate for the slow-down so you can change the factor
         // through LookingGlass without long waits.
         let duration = LG_ANIMATION_TIME / St.Settings.get().slow_down_factor;
-        this.actor.ease({
+        this.ease({
             y: this._targetY,
             duration,
             mode: Clutter.AnimationMode.EASE_OUT_QUAD
@@ -1114,10 +1132,10 @@ var LookingGlass = class LookingGlass {
         if (!this._open)
             return;
 
-        this._objInspector.actor.hide();
+        this._objInspector.hide();
 
         this._open = false;
-        this.actor.remove_all_transitions();
+        this.remove_all_transitions();
 
         this.setBorderPaintTarget(null);
 
@@ -1126,16 +1144,15 @@ var LookingGlass = class LookingGlass {
         let settings = St.Settings.get();
         let duration = Math.min(LG_ANIMATION_TIME / settings.slow_down_factor,
                                 LG_ANIMATION_TIME);
-        this.actor.ease({
+        this.ease({
             y: this._hiddenY,
             duration,
             mode: Clutter.AnimationMode.EASE_OUT_QUAD,
-            onComplete: () => this.actor.hide()
+            onComplete: () => this.hide()
         });
     }
 
     get isOpen() {
         return this._open;
     }
-};
-Signals.addSignalMethods(LookingGlass.prototype);
+});
diff --git a/js/ui/magnifier.js b/js/ui/magnifier.js
index 0ec30ee00e..316abc6bf0 100644
--- a/js/ui/magnifier.js
+++ b/js/ui/magnifier.js
@@ -1290,7 +1290,7 @@ var ZoomRegion = class ZoomRegion {
 
         // Add a background for when the magnified uiGroup is scrolled
         // out of view (don't want to see desktop showing through).
-        this._background = (new Background.SystemBackground()).actor;
+        this._background = new Background.SystemBackground();
         mainGroup.add_actor(this._background);
 
         // Clone the group that contains all of UI on the screen.  This is the
@@ -1587,8 +1587,9 @@ var ZoomRegion = class ZoomRegion {
     }
 };
 
-var Crosshairs = class Crosshairs {
-    constructor() {
+var Crosshairs = GObject.registerClass(
+class Crosshairs extends Clutter.Actor {
+    _init() {
 
         // Set the group containing the crosshairs to three times the desktop
         // size in case the crosshairs need to appear to be infinite in
@@ -1596,7 +1597,7 @@ var Crosshairs = class Crosshairs {
         let groupWidth = global.screen_width * 3;
         let groupHeight = global.screen_height * 3;
 
-        this._actor = new Clutter.Actor({
+        super._init({
             clip_to_allocation: false,
             width: groupWidth,
             height: groupHeight
@@ -1605,10 +1606,10 @@ var Crosshairs = class Crosshairs {
         this._horizRightHair = new Clutter.Actor();
         this._vertTopHair = new Clutter.Actor();
         this._vertBottomHair = new Clutter.Actor();
-        this._actor.add_actor(this._horizLeftHair);
-        this._actor.add_actor(this._horizRightHair);
-        this._actor.add_actor(this._vertTopHair);
-        this._actor.add_actor(this._vertBottomHair);
+        this.add_actor(this._horizLeftHair);
+        this.add_actor(this._horizRightHair);
+        this.add_actor(this._vertTopHair);
+        this.add_actor(this._vertBottomHair);
         this._clipSize = [0, 0];
         this._clones = [];
         this.reCenter();
@@ -1618,7 +1619,7 @@ var Crosshairs = class Crosshairs {
     }
 
     _monitorsChanged() {
-        this._actor.set_size(global.screen_width * 3, global.screen_height * 3);
+        this.set_size(global.screen_width * 3, global.screen_height * 3);
         this.reCenter();
     }
 
@@ -1639,12 +1640,15 @@ var Crosshairs = class Crosshairs {
         if (zoomRegion && magnifiedMouse) {
             let container = magnifiedMouse.get_parent();
             if (container) {
-                crosshairsActor = this._actor;
-                if (this._actor.get_parent() != null) {
-                    crosshairsActor = new Clutter.Clone({ source: this._actor });
+                crosshairsActor = this;
+                if (this.get_parent() != null) {
+                    crosshairsActor = new Clutter.Clone({ source: this });
                     this._clones.push(crosshairsActor);
+
+                    // Clones don't share visibility.
+                    this.bind_property('visible', crosshairsActor, 'visible',
+                                       GObject.BindingFlags.SYNC_CREATE);
                 }
-                crosshairsActor.visible = this._actor.visible;
 
                 container.add_actor(crosshairsActor);
                 container.raise_child(magnifiedMouse, crosshairsActor);
@@ -1663,7 +1667,7 @@ var Crosshairs = class Crosshairs {
      * child actor if it was just a clone of the crosshairs actor.
      */
     removeFromParent(childActor) {
-        if (childActor == this._actor)
+        if (childActor == this)
             childActor.get_parent().remove_actor(childActor);
         else
             childActor.destroy();
@@ -1773,28 +1777,6 @@ var Crosshairs = class Crosshairs {
         }
     }
 
-    /**
-     * show:
-     * Show the crosshairs.
-     */
-    show() {
-        this._actor.show();
-        // Clones don't share visibility.
-        for (let i = 0; i < this._clones.length; i++)
-            this._clones[i].show();
-    }
-
-    /**
-     * hide:
-     * Hide the crosshairs.
-     */
-    hide() {
-        this._actor.hide();
-        // Clones don't share visibility.
-        for (let i = 0; i < this._clones.length; i++)
-            this._clones[i].hide();
-    }
-
     /**
      * reCenter:
      * Reposition the horizontal and vertical hairs such that they cross at
@@ -1803,7 +1785,7 @@ var Crosshairs = class Crosshairs {
      * @clipSize:  Optional.  If present, an array of the form [width, height].
      */
     reCenter(clipSize) {
-        let [groupWidth, groupHeight] = this._actor.get_size();
+        let [groupWidth, groupHeight] = this.get_size();
         let leftLength = this._horizLeftHair.get_width();
         let topLength = this._vertTopHair.get_height();
         let thickness = this._horizLeftHair.get_height();
@@ -1825,7 +1807,7 @@ var Crosshairs = class Crosshairs {
         this._vertTopHair.set_position((groupWidth - thickness) / 2, top);
         this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
     }
-};
+});
 
 var MagShaderEffects = class MagShaderEffects {
     constructor(uiGroupClone) {
diff --git a/js/ui/messageList.js b/js/ui/messageList.js
index c846637726..6d87c87594 100644
--- a/js/ui/messageList.js
+++ b/js/ui/messageList.js
@@ -1,8 +1,8 @@
+/* exported MessageListSection */
 const { Atk, Clutter, Gio, GLib,
         GObject, Graphene, Meta, Pango, St } = imports.gi;
 const Main = imports.ui.main;
 const MessageTray = imports.ui.messageTray;
-const Signals = imports.signals;
 
 const Calendar = imports.ui.calendar;
 const Util = imports.misc.util;
@@ -32,13 +32,18 @@ function _fixMarkup(text, allowMarkup) {
     return GLib.markup_escape_text(text, -1);
 }
 
-var URLHighlighter = class URLHighlighter {
-    constructor(text = '', lineWrap, allowMarkup) {
-        this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter',
-                                    x_expand: true, x_align: Clutter.ActorAlign.START });
+var URLHighlighter = GObject.registerClass(
+class URLHighlighter extends St.Label {
+    _init(text = '', lineWrap, allowMarkup) {
+        super._init({
+            reactive: true,
+            style_class: 'url-highlighter',
+            x_expand: true,
+            x_align: Clutter.ActorAlign.START
+        });
         this._linkColor = '#ccccff';
-        this.actor.connect('style-changed', () => {
-            let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false);
+        this.connect('style-changed', () => {
+            let [hasColor, color] = this.get_theme_node().lookup_color('link-color', false);
             if (hasColor) {
                 let linkColor = color.to_string().substr(0, 7);
                 if (linkColor != this._linkColor) {
@@ -47,11 +52,11 @@ var URLHighlighter = class URLHighlighter {
                 }
             }
         });
-        this.actor.clutter_text.line_wrap = lineWrap;
-        this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
+        this.clutter_text.line_wrap = lineWrap;
+        this.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR;
 
         this.setMarkup(text, allowMarkup);
-        this.actor.connect('button-press-event', (actor, event) => {
+        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.
@@ -63,7 +68,7 @@ var URLHighlighter = class URLHighlighter {
             // handler, if an URL is clicked
             return this._findUrlAtPos(event) != -1;
         });
-        this.actor.connect('button-release-event', (actor, event) => {
+        this.connect('button-release-event', (actor, event) => {
             if (!actor.visible || actor.get_paint_opacity() == 0)
                 return Clutter.EVENT_PROPAGATE;
 
@@ -78,7 +83,7 @@ var URLHighlighter = class URLHighlighter {
             }
             return Clutter.EVENT_PROPAGATE;
         });
-        this.actor.connect('motion-event', (actor, event) => {
+        this.connect('motion-event', (actor, event) => {
             if (!actor.visible || actor.get_paint_opacity() == 0)
                 return Clutter.EVENT_PROPAGATE;
 
@@ -92,8 +97,8 @@ var URLHighlighter = class URLHighlighter {
             }
             return Clutter.EVENT_PROPAGATE;
         });
-        this.actor.connect('leave-event', () => {
-            if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
+        this.connect('leave-event', () => {
+            if (!this.visible || this.get_paint_opacity() == 0)
                 return Clutter.EVENT_PROPAGATE;
 
             if (this._cursorChanged) {
@@ -108,9 +113,9 @@ var URLHighlighter = class URLHighlighter {
         text = text ? _fixMarkup(text, allowMarkup) : '';
         this._text = text;
 
-        this.actor.clutter_text.set_markup(text);
+        this.clutter_text.set_markup(text);
         /* clutter_text.text contain text without markup */
-        this._urls = Util.findUrls(this.actor.clutter_text.text);
+        this._urls = Util.findUrls(this.clutter_text.text);
         this._highlightUrls();
     }
 
@@ -126,16 +131,15 @@ var URLHighlighter = class URLHighlighter {
             pos = url.pos + url.url.length;
         }
         markup += this._text.substr(pos);
-        this.actor.clutter_text.set_markup(markup);
+        this.clutter_text.set_markup(markup);
     }
 
     _findUrlAtPos(event) {
-        let success_;
         let [x, y] = event.get_coords();
-        [success_, x, y] = this.actor.transform_stage_point(x, y);
+        [, x, y] = this.transform_stage_point(x, y);
         let findPos = -1;
-        for (let i = 0; i < this.actor.clutter_text.text.length; i++) {
-            let [success_, px, py, lineHeight] = this.actor.clutter_text.position_to_coords(i);
+        for (let i = 0; i < this.clutter_text.text.length; i++) {
+            let [, px, py, lineHeight] = this.clutter_text.position_to_coords(i);
             if (py > y || py + lineHeight < y || x < px)
                 continue;
             findPos = i;
@@ -148,7 +152,7 @@ var URLHighlighter = class URLHighlighter {
         }
         return -1;
     }
-};
+});
 
 var ScaleLayout = GObject.registerClass(
 class ScaleLayout extends Clutter.BinLayout {
@@ -284,21 +288,31 @@ var LabelExpanderLayout = GObject.registerClass({
     }
 });
 
-var Message = class Message {
-    constructor(title, body) {
-        this.expanded = false;
 
+var Message = GObject.registerClass({
+    GTypeName: 'MessageList_Message',
+    Signals: {
+        'close': {},
+        'expanded': {},
+        'unexpanded': {},
+    }
+}, class Message extends St.Button {
+    _init(title, body) {
+        super._init({
+            style_class: 'message',
+            accessible_role: Atk.Role.NOTIFICATION,
+            can_focus: true,
+            x_expand: true,
+            x_fill: true
+        });
+
+        this.expanded = false;
         this._useBodyMarkup = false;
 
-        this.actor = new St.Button({ style_class: 'message',
-                                     accessible_role: Atk.Role.NOTIFICATION,
-                                     can_focus: true,
-                                     x_expand: true, x_fill: true });
-        this.actor.connect('key-press-event',
-                           this._onKeyPressed.bind(this));
+        this.connect('key-press-event', this._onKeyPressed.bind(this));
 
         let vbox = new St.BoxLayout({ vertical: true });
-        this.actor.set_child(vbox);
+        this.set_child(vbox);
 
         let hbox = new St.BoxLayout();
         vbox.add_actor(hbox);
@@ -342,15 +356,15 @@ var Message = class Message {
         contentBox.add_actor(this._bodyStack);
 
         this.bodyLabel = new URLHighlighter('', false, this._useBodyMarkup);
-        this.bodyLabel.actor.add_style_class_name('message-body');
-        this._bodyStack.add_actor(this.bodyLabel.actor);
+        this.bodyLabel.add_style_class_name('message-body');
+        this._bodyStack.add_actor(this.bodyLabel);
         this.setBody(body);
 
         this._closeButton.connect('clicked', this.close.bind(this));
-        let actorHoverId = this.actor.connect('notify::hover', this._sync.bind(this));
-        this._closeButton.connect('destroy', this.actor.disconnect.bind(this.actor, actorHoverId));
-        this.actor.connect('clicked', this._onClicked.bind(this));
-        this.actor.connect('destroy', this._onDestroy.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();
     }
 
@@ -436,7 +450,7 @@ var Message = class Message {
         if (this._bodyStack.get_n_children() < 2) {
             this._expandedLabel = new URLHighlighter(this._bodyText,
                                                      true, this._useBodyMarkup);
-            this.setExpandedBody(this._expandedLabel.actor);
+            this.setExpandedBody(this._expandedLabel);
         }
 
         if (animate) {
@@ -489,7 +503,7 @@ var Message = class Message {
     }
 
     _sync() {
-        let visible = this.actor.hover && this.canClose();
+        let visible = this.hover && this.canClose();
         this._closeButton.opacity = visible ? 255 : 0;
         this._closeButton.reactive = visible;
     }
@@ -510,35 +524,61 @@ var Message = class Message {
         }
         return Clutter.EVENT_PROPAGATE;
     }
-};
-Signals.addSignalMethods(Message.prototype);
+});
 
-var MessageListSection = class MessageListSection {
-    constructor() {
-        this.actor = new St.BoxLayout({ style_class: 'message-list-section',
-                                        clip_to_allocation: true,
-                                        x_expand: true, vertical: true });
+var MessageListSection = GObject.registerClass({
+    Properties: {
+        'can-clear': GObject.ParamSpec.boolean(
+            'can-clear', 'can-clear', 'can-clear',
+            GObject.ParamFlags.READABLE,
+            false),
+        'empty': GObject.ParamSpec.boolean(
+            'empty', 'empty', 'empty',
+            GObject.ParamFlags.READABLE,
+            true),
+    },
+    Signals: {
+        'can-clear-changed': {},
+        'empty-changed': {},
+        'message-focused': { param_types: [Message.$gtype] },
+    }
+}, class MessageListSection extends St.BoxLayout {
+    _init() {
+        super._init({
+            style_class: 'message-list-section',
+            clip_to_allocation: true,
+            vertical: true,
+            x_expand: true
+        });
 
         this._list = new St.BoxLayout({ style_class: 'message-list-section-list',
                                         vertical: true });
-        this.actor.add_actor(this._list);
+        this.add_actor(this._list);
 
         this._list.connect('actor-added', this._sync.bind(this));
         this._list.connect('actor-removed', this._sync.bind(this));
 
         let id = Main.sessionMode.connect('updated',
                                           this._sync.bind(this));
-        this.actor.connect('destroy', () => {
+        this.connect('destroy', () => {
             Main.sessionMode.disconnect(id);
         });
 
         this._messages = new Map();
         this._date = new Date();
-        this.empty = true;
-        this.canClear = false;
+        this._empty = true;
+        this._canClear = false;
         this._sync();
     }
 
+    get empty() {
+        return this._empty;
+    }
+
+    get canClear() {
+        return this._canClear;
+    }
+
     _onKeyFocusIn(messageActor) {
         this.emit('message-focused', messageActor);
     }
@@ -570,9 +610,9 @@ var MessageListSection = class MessageListSection {
         obj.container = new St.Widget({ layout_manager: new ScaleLayout(),
                                         pivot_point: pivot,
                                         scale_x: scale, scale_y: scale });
-        obj.keyFocusId = message.actor.connect('key-focus-in',
+        obj.keyFocusId = message.connect('key-focus-in',
             this._onKeyFocusIn.bind(this));
-        obj.destroyId = message.actor.connect('destroy', () => {
+        obj.destroyId = message.connect('destroy', () => {
             this.removeMessage(message, false);
         });
         obj.closeId = message.connect('close', () => {
@@ -580,7 +620,7 @@ var MessageListSection = class MessageListSection {
         });
 
         this._messages.set(message, obj);
-        obj.container.add_actor(message.actor);
+        obj.container.add_actor(message);
 
         this._list.insert_child_at_index(obj.container, index);
 
@@ -622,8 +662,8 @@ var MessageListSection = class MessageListSection {
     removeMessage(message, animate) {
         let obj = this._messages.get(message);
 
-        message.actor.disconnect(obj.destroyId);
-        message.actor.disconnect(obj.keyFocusId);
+        message.disconnect(obj.destroyId);
+        message.disconnect(obj.keyFocusId);
         message.disconnect(obj.closeId);
 
         this._messages.delete(message);
@@ -672,33 +712,24 @@ var MessageListSection = class MessageListSection {
         }
     }
 
-    _canClear() {
-        for (let message of this._messages.keys())
-            if (message.canClose())
-                return true;
-        return false;
-    }
-
     _shouldShow() {
         return !this.empty;
     }
 
     _sync() {
         let empty = this._list.get_n_children() == 0;
-        let changed = this.empty !== empty;
-        this.empty = empty;
-
-        if (changed)
-            this.emit('empty-changed');
 
-        let canClear = this._canClear();
-        changed = this.canClear !== canClear;
-        this.canClear = canClear;
+        if (this._empty != empty) {
+            this._empty = empty;
+            this.notify('empty');
+        }
 
-        if (changed)
-            this.emit('can-clear-changed');
+        let canClear = [...this._messages.keys()].some(m => m.canClose());
+        if (this._canClear != canClear) {
+            this._canClear = canClear;
+            this.notify('can-clear');
+        }
 
-        this.actor.visible = this.allowed && this._shouldShow();
+        this.visible = this.allowed && this._shouldShow();
     }
-};
-Signals.addSignalMethods(MessageListSection.prototype);
+});
diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js
index 0d8e884c60..0d86fc5035 100644
--- a/js/ui/messageTray.js
+++ b/js/ui/messageTray.js
@@ -4,7 +4,6 @@
    SystemNotificationSource, MessageTray */
 
 const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
-const Signals = imports.signals;
 
 const Calendar = imports.ui.calendar;
 const GnomeSession = imports.misc.gnomeSession;
@@ -523,13 +522,17 @@ var Notification = GObject.registerClass({
     }
 });
 
-var NotificationBanner =
-class NotificationBanner extends Calendar.NotificationMessage {
-    constructor(notification) {
-        super(notification);
+var NotificationBanner = GObject.registerClass({
+    Signals: {
+        'done-displaying': {},
+        'unfocused': {},
+    }
+}, class NotificationBanner extends Calendar.NotificationMessage {
+    _init(notification) {
+        super._init(notification);
 
-        this.actor.can_focus = false;
-        this.actor.add_style_class_name('notification-banner');
+        this.can_focus = false;
+        this.add_style_class_name('notification-banner');
 
         this._buttonBox = null;
 
@@ -616,7 +619,7 @@ class NotificationBanner extends Calendar.NotificationMessage {
 
         return this.addButton(button, callback);
     }
-};
+});
 
 var SourceActor = GObject.registerClass(
 class SourceActor extends St.Widget {
@@ -910,8 +913,20 @@ var Source = GObject.registerClass({
     }
 });
 
-var MessageTray = class MessageTray {
-    constructor() {
+var MessageTray = GObject.registerClass({
+    Signals: {
+        'queue-changed': {},
+        'source-added': { param_types: [Source.$gtype] },
+        'source-removed': { param_types: [Source.$gtype] },
+    }
+}, class MessageTray extends St.Widget {
+    _init() {
+        super._init({
+            visible: false,
+            clip_to_allocation: true,
+            layout_manager: new Clutter.BinLayout()
+        });
+
         this._presence = new GnomeSession.Presence((proxy, _error) => {
             this._onStatusChanged(proxy.status);
         });
@@ -928,18 +943,15 @@ var MessageTray = class MessageTray {
             // so fix up Clutter's view of the pointer position in
             // that case.
             let related = ev.get_related();
-            if (!related || this.actor.contains(related))
+            if (!related || this.contains(related))
                 global.sync_pointer();
         });
 
-        this.actor = new St.Widget({ visible: false,
-                                     clip_to_allocation: true,
-                                     layout_manager: new Clutter.BinLayout() });
         let constraint = new Layout.MonitorConstraint({ primary: true });
         Main.layoutManager.panelBox.bind_property('visible',
                                                   constraint, 'work-area',
                                                   GObject.BindingFlags.SYNC_CREATE);
-        this.actor.add_constraint(constraint);
+        this.add_constraint(constraint);
 
         this._bannerBin = new St.Widget({ name: 'notification-container',
                                           reactive: true,
@@ -953,7 +965,7 @@ var MessageTray = class MessageTray {
                                 this._onNotificationKeyRelease.bind(this));
         this._bannerBin.connect('notify::hover',
                                 this._onNotificationHoverChanged.bind(this));
-        this.actor.add_actor(this._bannerBin);
+        this.add_actor(this._bannerBin);
 
         this._notificationFocusGrabber = new FocusGrabber(this._bannerBin);
         this._notificationQueue = [];
@@ -982,7 +994,7 @@ var MessageTray = class MessageTray {
         this._notificationTimeoutId = 0;
         this._notificationRemoved = false;
 
-        Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
+        Main.layoutManager.addChrome(this, { affectsInputRegion: false });
         Main.layoutManager.trackChrome(this._bannerBin, { affectsInputRegion: true });
 
         global.display.connect('in-fullscreen-changed', this._updateState.bind(this));
@@ -1025,11 +1037,11 @@ var MessageTray = class MessageTray {
     }
 
     _onDragBegin() {
-        Shell.util_set_hidden_from_pick(this.actor, true);
+        Shell.util_set_hidden_from_pick(this, true);
     }
 
     _onDragEnd() {
-        Shell.util_set_hidden_from_pick(this.actor, false);
+        Shell.util_set_hidden_from_pick(this, false);
     }
 
     get bannerAlignment() {
@@ -1276,7 +1288,7 @@ var MessageTray = class MessageTray {
     // at the present time.
     _updateState() {
         let hasMonitor = Main.layoutManager.primaryMonitor != null;
-        this.actor.visible = !this._bannerBlocked && hasMonitor && this._banner != null;
+        this.visible = !this._bannerBlocked && hasMonitor && this._banner != null;
         if (this._bannerBlocked || !hasMonitor)
             return;
 
@@ -1356,11 +1368,11 @@ var MessageTray = class MessageTray {
             this._updateState();
         });
 
-        this._bannerBin.add_actor(this._banner.actor);
+        this._bannerBin.add_actor(this._banner);
 
         this._bannerBin.opacity = 0;
-        this._bannerBin.y = -this._banner.actor.height;
-        this.actor.show();
+        this._bannerBin.y = -this._banner.height;
+        this.show();
 
         Meta.disable_unredirect_for_display(global.display);
         this._updateShowingNotification();
@@ -1514,9 +1526,9 @@ var MessageTray = class MessageTray {
         this._notificationRemoved = false;
         Meta.enable_unredirect_for_display(global.display);
 
-        this._banner.actor.destroy();
+        this._banner.destroy();
         this._banner = null;
-        this.actor.hide();
+        this.hide();
     }
 
     _expandActiveNotification() {
@@ -1538,8 +1550,7 @@ var MessageTray = class MessageTray {
     _ensureBannerFocused() {
         this._notificationFocusGrabber.grabFocus();
     }
-};
-Signals.addSignalMethods(MessageTray.prototype);
+});
 
 var SystemNotificationSource = GObject.registerClass(
 class SystemNotificationSource extends Source {
diff --git a/js/ui/mpris.js b/js/ui/mpris.js
index 9debc0b2e0..15f7b68e30 100644
--- a/js/ui/mpris.js
+++ b/js/ui/mpris.js
@@ -1,5 +1,5 @@
 /* exported MediaSection */
-const { Gio, Shell, St } = imports.gi;
+const { Gio, GObject, Shell, St } = imports.gi;
 const Signals = imports.signals;
 
 const Calendar = imports.ui.calendar;
@@ -19,9 +19,10 @@ const MprisPlayerProxy = Gio.DBusProxy.makeProxyWrapper(MprisPlayerIface);
 
 const MPRIS_PLAYER_PREFIX = 'org.mpris.MediaPlayer2.';
 
-var MediaMessage = class MediaMessage extends MessageList.Message {
-    constructor(player) {
-        super('', '');
+var MediaMessage = GObject.registerClass(
+class MediaMessage extends MessageList.Message {
+    _init(player) {
+        super._init('', '');
 
         this._player = player;
 
@@ -79,7 +80,7 @@ var MediaMessage = class MediaMessage extends MessageList.Message {
         this._updateNavButton(this._prevButton, this._player.canGoPrevious);
         this._updateNavButton(this._nextButton, this._player.canGoNext);
     }
-};
+});
 
 var MprisPlayer = class MprisPlayer {
     constructor(busName) {
@@ -194,9 +195,10 @@ var MprisPlayer = class MprisPlayer {
 };
 Signals.addSignalMethods(MprisPlayer.prototype);
 
-var MediaSection = class MediaSection extends MessageList.MessageListSection {
-    constructor() {
-        super();
+var MediaSection = GObject.registerClass(
+class MediaSection extends MessageList.MessageListSection {
+    _init() {
+        super._init();
 
         this._players = new Map();
 
@@ -247,4 +249,4 @@ var MediaSection = class MediaSection extends MessageList.MessageListSection {
         if (newOwner && !oldOwner)
             this._addPlayer(name);
     }
-};
+});
diff --git a/js/ui/osdMonitorLabeler.js b/js/ui/osdMonitorLabeler.js
index 9c0b4ee204..d84f043805 100644
--- a/js/ui/osdMonitorLabeler.js
+++ b/js/ui/osdMonitorLabeler.js
@@ -1,30 +1,33 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported OsdMonitorLabeler */
 
-const { Clutter, Gio, Meta, St } = imports.gi;
+const { Clutter, Gio, GObject, Meta, St } = imports.gi;
 
 const Main = imports.ui.main;
 
-var OsdMonitorLabel = class {
-    constructor(monitor, label) {
-        this._actor = new St.Widget({ x_expand: true,
-                                      y_expand: true });
+var OsdMonitorLabel = GObject.registerClass(
+class OsdMonitorLabel extends St.Widget {
+    _init(monitor, label) {
+        super._init({ x_expand: true, y_expand: true });
 
         this._monitor = monitor;
 
         this._box = new St.BoxLayout({ style_class: 'osd-window',
                                        vertical: true });
-        this._actor.add_actor(this._box);
+        this.add_actor(this._box);
 
         this._label = new St.Label({ style_class: 'osd-monitor-label',
                                      text: label });
         this._box.add(this._label);
 
-        Main.uiGroup.add_child(this._actor);
-        Main.uiGroup.set_child_above_sibling(this._actor, null);
+        Main.uiGroup.add_child(this);
+        Main.uiGroup.set_child_above_sibling(this, null);
         this._position();
 
         Meta.disable_unredirect_for_display(global.display);
+        this.connect('destroy', () => {
+            Meta.enable_unredirect_for_display(global.display);
+        });
     }
 
     _position() {
@@ -37,12 +40,7 @@ var OsdMonitorLabel = class {
 
         this._box.y = workArea.y;
     }
-
-    destroy() {
-        this._actor.destroy();
-        Meta.enable_unredirect_for_display(global.display);
-    }
-};
+});
 
 var OsdMonitorLabeler = class {
     constructor() {
diff --git a/js/ui/osdWindow.js b/js/ui/osdWindow.js
index 8c92347909..7c279c2200 100644
--- a/js/ui/osdWindow.js
+++ b/js/ui/osdWindow.js
@@ -41,22 +41,25 @@ class OsdWindowConstraint extends Clutter.Constraint {
     }
 });
 
-var OsdWindow = class {
-    constructor(monitorIndex) {
-        this.actor = new St.Widget({ x_expand: true,
-                                     y_expand: true,
-                                     x_align: Clutter.ActorAlign.CENTER,
-                                     y_align: Clutter.ActorAlign.CENTER });
+var OsdWindow = GObject.registerClass(
+class OsdWindow extends St.Widget {
+    _init(monitorIndex) {
+        super._init({
+            x_expand: true,
+            y_expand: true,
+            x_align: Clutter.ActorAlign.CENTER,
+            y_align: Clutter.ActorAlign.CENTER
+        });
 
         this._monitorIndex = monitorIndex;
         let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
-        this.actor.add_constraint(constraint);
+        this.add_constraint(constraint);
 
         this._boxConstraint = new OsdWindowConstraint();
         this._box = new St.BoxLayout({ style_class: 'osd-window',
                                        vertical: true });
         this._box.add_constraint(this._boxConstraint);
-        this.actor.add_actor(this._box);
+        this.add_actor(this._box);
 
         this._icon = new St.Icon();
         this._box.add(this._icon, { expand: true });
@@ -73,7 +76,7 @@ var OsdWindow = class {
         this._hideTimeoutId = 0;
         this._reset();
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
 
         this._monitorsChangedId =
             Main.layoutManager.connect('monitors-changed',
@@ -83,7 +86,7 @@ var OsdWindow = class {
             themeContext.connect('notify::scale-factor',
                                  this._relayout.bind(this));
         this._relayout();
-        Main.uiGroup.add_child(this.actor);
+        Main.uiGroup.add_child(this);
     }
 
     _onDestroy() {
@@ -110,7 +113,7 @@ var OsdWindow = class {
     setLevel(value) {
         this._level.visible = (value != undefined);
         if (value != undefined) {
-            if (this.actor.visible)
+            if (this.visible)
                 this._level.ease_property('value', value, {
                     mode: Clutter.AnimationMode.EASE_OUT_QUAD,
                     duration: LEVEL_ANIMATION_TIME
@@ -128,13 +131,13 @@ var OsdWindow = class {
         if (!this._icon.gicon)
             return;
 
-        if (!this.actor.visible) {
+        if (!this.visible) {
             Meta.disable_unredirect_for_display(global.display);
-            this.actor.show();
-            this.actor.opacity = 0;
-            this.actor.get_parent().set_child_above_sibling(this.actor, null);
+            super.show();
+            this.opacity = 0;
+            this.get_parent().set_child_above_sibling(this, null);
 
-            this.actor.ease({
+            this.ease({
                 opacity: 255,
                 duration: FADE_TIME,
                 mode: Clutter.AnimationMode.EASE_OUT_QUAD
@@ -158,7 +161,7 @@ var OsdWindow = class {
 
     _hide() {
         this._hideTimeoutId = 0;
-        this.actor.ease({
+        this.ease({
             opacity: 0,
             duration: FADE_TIME,
             mode: Clutter.AnimationMode.EASE_OUT_QUAD,
@@ -171,7 +174,7 @@ var OsdWindow = class {
     }
 
     _reset() {
-        this.actor.hide();
+        super.hide();
         this.setLabel(null);
         this.setMaxLevel(null);
         this.setLevel(null);
@@ -193,7 +196,7 @@ var OsdWindow = class {
         this._box.translation_y = Math.round(monitor.height / 4);
         this._boxConstraint.minSize = popupSize;
     }
-};
+});
 
 var OsdWindowManager = class {
     constructor() {
@@ -210,7 +213,7 @@ var OsdWindowManager = class {
         }
 
         for (let i = Main.layoutManager.monitors.length; i < this._osdWindows.length; i++) {
-            this._osdWindows[i].actor.destroy();
+            this._osdWindows[i].destroy();
             this._osdWindows[i] = null;
         }
 
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 255d6e478d..dcf96e320c 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -117,7 +117,7 @@ class OverviewActor extends St.BoxLayout {
         this._controls = new OverviewControls.ControlsManager(this._searchEntry);
 
         // Add our same-line elements after the search entry
-        this.add(this._controls.actor, { y_fill: true, expand: true });
+        this.add(this._controls, { y_fill: true, expand: true });
     }
 
     get dash() {
diff --git a/js/ui/overviewControls.js b/js/ui/overviewControls.js
index 0edf8cd5d1..3726f983a0 100644
--- a/js/ui/overviewControls.js
+++ b/js/ui/overviewControls.js
@@ -118,18 +118,21 @@ var SlideLayout = GObject.registerClass({
     }
 });
 
-var SlidingControl = class {
-    constructor(params) {
+var SlidingControl = GObject.registerClass(
+class SlidingControl extends St.Widget {
+    _init(params) {
         params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
 
-        this._visible = true;
-        this._inDrag = false;
-
         this.layout = new SlideLayout();
         this.layout.slideDirection = params.slideDirection;
-        this.actor = new St.Widget({ layout_manager: this.layout,
-                                     style_class: 'overview-controls',
-                                     clip_to_allocation: true });
+        super._init({
+            layout_manager: this.layout,
+            style_class: 'overview-controls',
+            clip_to_allocation: true
+        });
+
+        this._visible = true;
+        this._inDrag = false;
 
         Main.overview.connect('hiding', this._onOverviewHiding.bind(this));
 
@@ -147,20 +150,20 @@ var SlidingControl = class {
     }
 
     _updateSlide() {
-        this.actor.ease_property('@layout.slide-x', this._getSlide(), {
+        this.ease_property('@layout.slide-x', this._getSlide(), {
             mode: Clutter.AnimationMode.EASE_OUT_QUAD,
             duration: SIDE_CONTROLS_ANIMATION_TIME,
         });
     }
 
     getVisibleWidth() {
-        let child = this.actor.get_first_child();
+        let child = this.get_first_child();
         let [, , natWidth] = child.get_preferred_size();
         return natWidth;
     }
 
     _getTranslation() {
-        let child = this.actor.get_first_child();
+        let child = this.get_first_child();
         let direction = getRtlSlideDirection(this.layout.slideDirection, child);
         let visibleWidth = this.getVisibleWidth();
 
@@ -186,7 +189,7 @@ var SlidingControl = class {
             return;
 
         this.layout.translation_x = translationStart;
-        this.actor.ease_property('@layout.translation-x', translationEnd, {
+        this.ease_property('@layout.translation-x', translationEnd, {
             mode: Clutter.AnimationMode.EASE_OUT_QUAD,
             duration: SIDE_CONTROLS_ANIMATION_TIME,
         });
@@ -218,7 +221,7 @@ var SlidingControl = class {
     }
 
     fadeIn() {
-        this.actor.ease({
+        this.ease({
             opacity: 255,
             duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
             mode: Clutter.AnimationMode.EASE_IN_QUAD
@@ -226,7 +229,7 @@ var SlidingControl = class {
     }
 
     fadeHalf() {
-        this.actor.ease({
+        this.ease({
             opacity: 128,
             duration: SIDE_CONTROLS_ANIMATION_TIME / 2,
             mode: Clutter.AnimationMode.EASE_OUT_QUAD
@@ -249,37 +252,38 @@ var SlidingControl = class {
         // selector; this means we can now safely set the full slide for
         // the next page, since slideIn or slideOut might have been called,
         // changing the visiblity
-        this.actor.remove_transition('@layout.slide-x');
+        this.remove_transition('@layout.slide-x');
         this.layout.slide_x = this._getSlide();
         this._updateTranslation();
     }
-};
+});
 
-var ThumbnailsSlider = class extends SlidingControl {
-    constructor(thumbnailsBox) {
-        super({ slideDirection: SlideDirection.RIGHT });
+var ThumbnailsSlider = GObject.registerClass(
+class ThumbnailsSlider extends SlidingControl {
+    _init(thumbnailsBox) {
+        super._init({ slideDirection: SlideDirection.RIGHT });
 
         this._thumbnailsBox = thumbnailsBox;
 
-        this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
-        this.actor.reactive = true;
-        this.actor.track_hover = true;
-        this.actor.add_actor(this._thumbnailsBox);
+        this.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
+        this.reactive = true;
+        this.track_hover = true;
+        this.add_actor(this._thumbnailsBox);
 
         Main.layoutManager.connect('monitors-changed', this._updateSlide.bind(this));
         global.workspace_manager.connect('active-workspace-changed',
                                          this._updateSlide.bind(this));
         global.workspace_manager.connect('notify::n-workspaces',
                                          this._updateSlide.bind(this));
-        this.actor.connect('notify::hover', this._updateSlide.bind(this));
-        this._thumbnailsBox.bind_property('visible', this.actor, 'visible', 
GObject.BindingFlags.SYNC_CREATE);
+        this.connect('notify::hover', this._updateSlide.bind(this));
+        this._thumbnailsBox.bind_property('visible', this, 'visible', GObject.BindingFlags.SYNC_CREATE);
     }
 
     _getAlwaysZoomOut() {
         // Always show the pager on hover, during a drag, or if workspaces are
         // actually used, e.g. there are windows on any non-active workspace
         let workspaceManager = global.workspace_manager;
-        let alwaysZoomOut = this.actor.hover ||
+        let alwaysZoomOut = this.hover ||
                             this._inDrag ||
                             !Meta.prefs_get_dynamic_workspaces() ||
                             workspaceManager.n_workspaces > 2 ||
@@ -304,12 +308,12 @@ var ThumbnailsSlider = class extends SlidingControl {
     }
 
     getNonExpandedWidth() {
-        let child = this.actor.get_first_child();
+        let child = this.get_first_child();
         return child.get_theme_node().get_length('visible-width');
     }
 
     _onDragEnd() {
-        this.actor.sync_hover();
+        this.sync_hover();
         super._onDragEnd();
     }
 
@@ -321,7 +325,7 @@ var ThumbnailsSlider = class extends SlidingControl {
         if (alwaysZoomOut)
             return 1;
 
-        let child = this.actor.get_first_child();
+        let child = this.get_first_child();
         let preferredHeight = child.get_preferred_height(-1)[1];
         let expandedWidth = child.get_preferred_width(preferredHeight)[1];
 
@@ -335,24 +339,25 @@ var ThumbnailsSlider = class extends SlidingControl {
         else
             return this.getNonExpandedWidth();
     }
-};
+});
 
-var DashSlider = class extends SlidingControl {
-    constructor(dash) {
-        super({ slideDirection: SlideDirection.LEFT });
+var DashSlider = GObject.registerClass(
+class DashSlider extends SlidingControl {
+    _init(dash) {
+        super._init({ slideDirection: SlideDirection.LEFT });
 
         this._dash = dash;
 
         // SlideLayout reads the actor's expand flags to decide
         // whether to allocate the natural size to its child, or the whole
         // available allocation
-        this._dash.actor.x_expand = true;
+        this._dash.x_expand = true;
 
-        this.actor.x_expand = true;
-        this.actor.x_align = Clutter.ActorAlign.START;
-        this.actor.y_expand = true;
+        this.x_expand = true;
+        this.x_align = Clutter.ActorAlign.START;
+        this.y_expand = true;
 
-        this.actor.add_actor(this._dash.actor);
+        this.add_actor(this._dash);
 
         this._dash.connect('icon-size-changed', this._updateSlide.bind(this));
     }
@@ -371,7 +376,7 @@ var DashSlider = class extends SlidingControl {
     _onWindowDragEnd() {
         this.fadeIn();
     }
-};
+});
 
 var DashSpacer = GObject.registerClass(
 class DashSpacer extends St.Widget {
@@ -416,12 +421,21 @@ var ControlsLayout = GObject.registerClass({
     }
 });
 
-var ControlsManager = class {
-    constructor(searchEntry) {
+var ControlsManager = GObject.registerClass(
+class ControlsManager extends St.Widget {
+    _init(searchEntry) {
+        let layout = new ControlsLayout();
+        super._init({
+            layout_manager: layout,
+            x_expand: true,
+            y_expand: true,
+            clip_to_allocation: true
+        });
+
         this.dash = new Dash.Dash();
         this._dashSlider = new DashSlider(this.dash);
         this._dashSpacer = new DashSpacer();
-        this._dashSpacer.setDashActor(this._dashSlider.actor);
+        this._dashSpacer.setDashActor(this._dashSlider);
 
         this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
         this._thumbnailsSlider = new ThumbnailsSlider(this._thumbnailsBox);
@@ -431,20 +445,15 @@ var ControlsManager = class {
         this.viewSelector.connect('page-changed', this._setVisibility.bind(this));
         this.viewSelector.connect('page-empty', this._onPageEmpty.bind(this));
 
-        let layout = new ControlsLayout();
-        this.actor = new St.Widget({ layout_manager: layout,
-                                     x_expand: true, y_expand: true,
-                                     clip_to_allocation: true });
         this._group = new St.BoxLayout({ name: 'overview-group',
                                          x_expand: true, y_expand: true });
-        this.actor.add_actor(this._group);
+        this.add_actor(this._group);
 
-        this.actor.add_actor(this._dashSlider.actor);
+        this.add_actor(this._dashSlider);
 
         this._group.add_actor(this._dashSpacer);
-        this._group.add(this.viewSelector.actor, { x_fill: true,
-                                                   expand: true });
-        this._group.add_actor(this._thumbnailsSlider.actor);
+        this._group.add(this.viewSelector, { x_fill: true, expand: true });
+        this._group.add_actor(this._thumbnailsSlider);
 
         layout.connect('allocation-changed', this._updateWorkspacesGeometry.bind(this));
 
@@ -452,18 +461,18 @@ var ControlsManager = class {
     }
 
     _updateWorkspacesGeometry() {
-        let [x, y] = this.actor.get_transformed_position();
-        let [width, height] = this.actor.get_transformed_size();
+        let [x, y] = this.get_transformed_position();
+        let [width, height] = this.get_transformed_size();
         let geometry = { x: x, y: y, width: width, height: height };
 
-        let spacing = this.actor.get_theme_node().get_length('spacing');
+        let spacing = this.get_theme_node().get_length('spacing');
         let dashWidth = this._dashSlider.getVisibleWidth() + spacing;
         let thumbnailsWidth = this._thumbnailsSlider.getNonExpandedWidth() + spacing;
 
         geometry.width -= dashWidth;
         geometry.width -= thumbnailsWidth;
 
-        if (this.actor.get_text_direction() == Clutter.TextDirection.LTR)
+        if (this.get_text_direction() == Clutter.TextDirection.LTR)
             geometry.x += dashWidth;
         else
             geometry.x += thumbnailsWidth;
@@ -510,4 +519,4 @@ var ControlsManager = class {
 
         this._updateSpacerVisibility();
     }
-};
+});
diff --git a/js/ui/padOsd.js b/js/ui/padOsd.js
index a900a01162..7634ce7bc1 100644
--- a/js/ui/padOsd.js
+++ b/js/ui/padOsd.js
@@ -1,5 +1,5 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-/* exported PadOsdService */
+/* exported PadOsd, PadOsdService */
 
 const { Atk, Clutter, GDesktopEnums, Gio,
         GLib, GObject, Gtk, Meta, Rsvg, St } = imports.gi;
@@ -22,25 +22,29 @@ const CCW = 1;
 const UP = 0;
 const DOWN = 1;
 
-var PadChooser = class {
-    constructor(device, groupDevices) {
-        this.actor = new St.Button({ style_class: 'pad-chooser-button',
-                                     toggle_mode: true,
-                                     x_fill: false,
-                                     y_fill: false,
-                                     x_align: St.Align.MIDDLE,
-                                     y_align: St.Align.MIDDLE });
+var PadChooser = GObject.registerClass({
+    Signals: { 'pad-selected': { param_types: [Clutter.InputDevice.$gtype] } }
+}, class PadChooser extends St.Button {
+    _init(device, groupDevices) {
+        super._init({
+            style_class: 'pad-chooser-button',
+            toggle_mode: true,
+            x_fill: false,
+            y_fill: false,
+            x_align: St.Align.MIDDLE,
+            y_align: St.Align.MIDDLE
+        });
         this.currentDevice = device;
         this._padChooserMenu = null;
 
         let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
                                   icon_name: 'pan-down-symbolic',
                                   accessible_role: Atk.Role.ARROW });
-        this.actor.set_child(arrow);
+        this.set_child(arrow);
         this._ensureMenu(groupDevices);
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-        this.actor.connect('clicked', actor => {
+        this.connect('destroy', this._onDestroy.bind(this));
+        this.connect('clicked', actor => {
             if (actor.get_checked()) {
                 if (this._padChooserMenu != null)
                     this._padChooserMenu.open(true);
@@ -53,9 +57,9 @@ var PadChooser = class {
     }
 
     _ensureMenu(devices) {
-        this._padChooserMenu =  new PopupMenu.PopupMenu(this.actor, 0.5, St.Side.TOP);
+        this._padChooserMenu =  new PopupMenu.PopupMenu(this, 0.5, St.Side.TOP);
         this._padChooserMenu.connect('menu-closed', () => {
-            this.actor.set_checked(false);
+            this.set_checked(false);
         });
         this._padChooserMenu.actor.hide();
         Main.uiGroup.add_actor(this._padChooserMenu.actor);
@@ -78,21 +82,18 @@ var PadChooser = class {
     update(devices) {
         if (this._padChooserMenu)
             this._padChooserMenu.actor.destroy();
-        this.actor.set_checked(false);
+        this.set_checked(false);
         this._ensureMenu(devices);
     }
+});
 
-    destroy() {
-        this.actor.destroy();
-    }
-};
-Signals.addSignalMethods(PadChooser.prototype);
-
-var KeybindingEntry = class {
-    constructor() {
-        this.actor = new St.Entry({ hint_text: _("New shortcut…"),
-                                    style: 'width: 10em' });
-        this.actor.connect('captured-event', this._onCapturedEvent.bind(this));
+var KeybindingEntry = GObject.registerClass({
+    GTypeName: 'PadOsd_KeybindingEntry',
+    Signals: { 'keybinding-edited': {} }
+}, 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) {
@@ -103,23 +104,25 @@ var KeybindingEntry = class {
                                                     event.get_key_symbol(),
                                                     event.get_key_code(),
                                                     event.get_state());
-        this.actor.set_text(str);
+        this.set_text(str);
         this.emit('keybinding-edited', str);
         return Clutter.EVENT_STOP;
     }
-};
-Signals.addSignalMethods(KeybindingEntry.prototype);
+});
 
-var ActionComboBox = class {
-    constructor() {
-        this.actor = new St.Button({ style_class: 'button' });
-        this.actor.connect('clicked', this._onButtonClicked.bind(this));
-        this.actor.set_toggle_mode(true);
+var ActionComboBox = GObject.registerClass({
+    GTypeName: 'PadOsd_ActionComboBox',
+    Signals: { 'action-selected': { param_types: [GObject.TYPE_INT] } }
+}, 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,
                                                 spacing: 6 });
         let box = new St.Widget({ layout_manager: boxLayout });
-        this.actor.set_child(box);
+        this.set_child(box);
 
         this._label = new St.Label({ style_class: 'combo-box-label' });
         box.add_child(this._label);
@@ -131,9 +134,9 @@ var ActionComboBox = class {
                                   y_align: Clutter.ActorAlign.CENTER });
         box.add_child(arrow);
 
-        this._editMenu = new PopupMenu.PopupMenu(this.actor, 0, St.Side.TOP);
+        this._editMenu = new PopupMenu.PopupMenu(this, 0, St.Side.TOP);
         this._editMenu.connect('menu-closed', () => {
-            this.actor.set_checked(false);
+            this.set_checked(false);
         });
         this._editMenu.actor.hide();
         Main.uiGroup.add_actor(this._editMenu.actor);
@@ -180,7 +183,7 @@ var ActionComboBox = class {
     }
 
     _onButtonClicked() {
-        if (this.actor.get_checked())
+        if (this.get_checked())
             this.popup();
         else
             this.popdown();
@@ -189,38 +192,40 @@ var ActionComboBox = class {
     setButtonActionsActive(active) {
         this._buttonItems.forEach(item => item.setSensitive(active));
     }
-};
-Signals.addSignalMethods(ActionComboBox.prototype);
+});
 
-var ActionEditor = class {
-    constructor() {
+var ActionEditor = GObject.registerClass({
+    GTypeName: 'PadOsd_ActionEditor',
+    Signals: { 'done': {} }
+}, class ActionEditor extends St.Widget {
+    _init() {
         let boxLayout = new Clutter.BoxLayout({ orientation: Clutter.Orientation.HORIZONTAL,
                                                 spacing: 12 });
 
-        this.actor = new St.Widget({ layout_manager: boxLayout });
+        super._init({ layout_manager: boxLayout });
 
         this._actionComboBox = new ActionComboBox();
         this._actionComboBox.connect('action-selected', this._onActionSelected.bind(this));
-        this.actor.add_actor(this._actionComboBox.actor);
+        this.add_actor(this._actionComboBox);
 
         this._keybindingEdit = new KeybindingEntry();
         this._keybindingEdit.connect('keybinding-edited', this._onKeybindingEdited.bind(this));
-        this.actor.add_actor(this._keybindingEdit.actor);
+        this.add_actor(this._keybindingEdit);
 
         this._doneButton = new St.Button({ label: _("Done"),
                                            style_class: 'button',
                                            x_expand: false });
         this._doneButton.connect('clicked', this._onEditingDone.bind(this));
-        this.actor.add_actor(this._doneButton);
+        this.add_actor(this._doneButton);
     }
 
     _updateKeybindingEntryState() {
         if (this._currentAction == GDesktopEnums.PadButtonAction.KEYBINDING) {
-            this._keybindingEdit.actor.set_text(this._currentKeybinding);
-            this._keybindingEdit.actor.show();
-            this._keybindingEdit.actor.grab_key_focus();
+            this._keybindingEdit.set_text(this._currentKeybinding);
+            this._keybindingEdit.show();
+            this._keybindingEdit.grab_key_focus();
         } else {
-            this._keybindingEdit.actor.hide();
+            this._keybindingEdit.hide();
         }
     }
 
@@ -238,7 +243,7 @@ var ActionEditor = class {
 
     close() {
         this._actionComboBox.popdown();
-        this.actor.hide();
+        this.hide();
     }
 
     _onKeybindingEdited(entry, keybinding) {
@@ -272,8 +277,7 @@ var ActionEditor = class {
         this.close();
         this.emit('done');
     }
-};
-Signals.addSignalMethods(ActionEditor.prototype);
+});
 
 var PadDiagram = GObject.registerClass({
     Properties: {
@@ -615,8 +619,18 @@ var PadDiagram = GObject.registerClass({
     }
 });
 
-var PadOsd = class {
-    constructor(padDevice, settings, imagePath, editionMode, monitorIndex) {
+var PadOsd = GObject.registerClass({
+    Signals: { 'pad-selected': { param_types: [Clutter.InputDevice.$gtype] } }
+}, class PadOsd extends St.BoxLayout {
+    _init(padDevice, settings, imagePath, editionMode, monitorIndex) {
+        super._init({
+            style_class: 'pad-osd-window',
+            vertical: true,
+            x_expand: true,
+            y_expand: true,
+            reactive: true
+        });
+
         this.padDevice = padDevice;
         this._groupPads = [padDevice];
         this._settings = settings;
@@ -653,23 +667,18 @@ var PadOsd = class {
                 this._groupPads.push(device);
         });
 
-        this.actor = new St.BoxLayout({ style_class: 'pad-osd-window',
-                                        x_expand: true,
-                                        y_expand: true,
-                                        vertical: true,
-                                        reactive: true });
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-        Main.uiGroup.add_actor(this.actor);
+        this.connect('destroy', this._onDestroy.bind(this));
+        Main.uiGroup.add_actor(this);
 
         this._monitorIndex = monitorIndex;
         let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
-        this.actor.add_constraint(constraint);
+        this.add_constraint(constraint);
 
         this._titleBox = new St.BoxLayout({ style_class: 'pad-osd-title-box',
                                             vertical: false,
                                             x_expand: false,
                                             x_align: Clutter.ActorAlign.CENTER });
-        this.actor.add_actor(this._titleBox);
+        this.add_actor(this._titleBox);
 
         let labelBox = new St.BoxLayout({ style_class: 'pad-osd-title-menu-box',
                                           vertical: true });
@@ -690,10 +699,10 @@ var PadOsd = class {
 
         this._padDiagram = new PadDiagram({ image: this._imagePath,
                                             left_handed: settings.get_boolean('left-handed'),
-                                            editor_actor: this._actionEditor.actor,
+                                            editor_actor: this._actionEditor,
                                             x_expand: true,
                                             y_expand: true });
-        this.actor.add_actor(this._padDiagram);
+        this.add_actor(this._padDiagram);
 
         // FIXME: Fix num buttons.
         let i = 0;
@@ -724,7 +733,7 @@ var PadOsd = class {
                                         x_expand: true,
                                         x_align: Clutter.ActorAlign.CENTER,
                                         y_align: Clutter.ActorAlign.CENTER });
-        this.actor.add_actor(buttonBox);
+        this.add_actor(buttonBox);
         this._editButton = new St.Button({ label: _("Edit…"),
                                            style_class: 'button',
                                            x_align: Clutter.ActorAlign.CENTER,
@@ -735,7 +744,7 @@ var PadOsd = class {
         buttonBox.add_actor(this._editButton);
 
         this._syncEditionMode();
-        Main.pushModal(this.actor);
+        Main.pushModal(this);
     }
 
     _updatePadChooser() {
@@ -745,7 +754,7 @@ var PadOsd = class {
                 this._padChooser.connect('pad-selected', (chooser, pad) => {
                     this._requestForOtherPad(pad);
                 });
-                this._titleBox.add_child(this._padChooser.actor);
+                this._titleBox.add_child(this._padChooser);
             } else {
                 this._padChooser.update(this._groupPads);
             }
@@ -918,12 +927,8 @@ var PadOsd = class {
         this._syncEditionMode();
     }
 
-    destroy() {
-        this.actor.destroy();
-    }
-
     _onDestroy() {
-        Main.popModal(this.actor);
+        Main.popModal(this);
         this._actionEditor.close();
 
         let deviceManager = Clutter.DeviceManager.get_default();
@@ -941,11 +946,9 @@ var PadOsd = class {
             this._capturedEventId = 0;
         }
 
-        this.actor = null;
         this.emit('closed');
     }
-};
-Signals.addSignalMethods(PadOsd.prototype);
+});
 
 const PadOsdIface = loadInterfaceXML('org.gnome.Shell.Wacom.PadOsd');
 
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 49c0038fac..37a84290c7 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -235,7 +235,7 @@ var AppMenuButton = GObject.registerClass({
         this._overviewShowingId = Main.overview.connect('showing', this._sync.bind(this));
 
         this._spinner = new Animation.Spinner(PANEL_ICON_SIZE, true);
-        this._container.add_actor(this._spinner.actor);
+        this._container.add_actor(this._spinner);
 
         let menu = new AppMenu(this);
         this.setMenu(menu);
@@ -501,13 +501,12 @@ class ActivitiesButton extends PanelMenu.Button {
     }
 });
 
-var PanelCorner = class {
-    constructor(side) {
+var PanelCorner = GObject.registerClass(
+class PanelCorner extends St.DrawingArea {
+    _init(side) {
         this._side = side;
 
-        this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
-        this.actor.connect('style-changed', this._styleChanged.bind(this));
-        this.actor.connect('repaint', this._repaint.bind(this));
+        super._init({ style_class: 'panel-corner' });
     }
 
     _findRightmostButton(container) {
@@ -597,7 +596,7 @@ var PanelCorner = class {
             this._buttonStyleChangedSignalId = button.connect('style-changed',
                 () => {
                     let pseudoClass = button.get_style_pseudo_class();
-                    this.actor.set_style_pseudo_class(pseudoClass);
+                    this.set_style_pseudo_class(pseudoClass);
                 });
 
             // The corner doesn't support theme transitions, so override
@@ -606,8 +605,8 @@ var PanelCorner = class {
         }
     }
 
-    _repaint() {
-        let node = this.actor.get_theme_node();
+    vfunc_repaint() {
+        let node = this.get_theme_node();
 
         let cornerRadius = node.get_length("-panel-corner-radius");
         let borderWidth = node.get_length('-panel-corner-border-width');
@@ -618,7 +617,7 @@ var PanelCorner = class {
         let overlap = borderColor.alpha != 0;
         let offsetY = overlap ? 0 : borderWidth;
 
-        let cr = this.actor.get_context();
+        let cr = this.get_context();
         cr.setOperator(Cairo.Operator.SOURCE);
 
         cr.moveTo(0, offsetY);
@@ -654,16 +653,17 @@ var PanelCorner = class {
         cr.$dispose();
     }
 
-    _styleChanged() {
-        let node = this.actor.get_theme_node();
+    vfunc_style_changed() {
+        super.vfunc_style_changed();
+        let node = this.get_theme_node();
 
         let cornerRadius = node.get_length("-panel-corner-radius");
         let borderWidth = node.get_length('-panel-corner-border-width');
 
-        this.actor.set_size(cornerRadius, borderWidth + cornerRadius);
-        this.actor.set_anchor_point(0, borderWidth);
+        this.set_size(cornerRadius, borderWidth + cornerRadius);
+        this.set_anchor_point(0, borderWidth);
     }
-};
+});
 
 var AggregateLayout = GObject.registerClass(
 class AggregateLayout extends Clutter.BoxLayout {
@@ -728,20 +728,20 @@ class AggregateMenu extends PanelMenu.Button {
         this._nightLight = new imports.ui.status.nightLight.Indicator();
         this._thunderbolt = new imports.ui.status.thunderbolt.Indicator();
 
-        this._indicators.add_child(this._thunderbolt.indicators);
-        this._indicators.add_child(this._screencast.indicators);
-        this._indicators.add_child(this._location.indicators);
-        this._indicators.add_child(this._nightLight.indicators);
+        this._indicators.add_child(this._thunderbolt);
+        this._indicators.add_child(this._screencast);
+        this._indicators.add_child(this._location);
+        this._indicators.add_child(this._nightLight);
         if (this._network) {
-            this._indicators.add_child(this._network.indicators);
+            this._indicators.add_child(this._network);
         }
         if (this._bluetooth) {
-            this._indicators.add_child(this._bluetooth.indicators);
+            this._indicators.add_child(this._bluetooth);
         }
-        this._indicators.add_child(this._remoteAccess.indicators);
-        this._indicators.add_child(this._rfkill.indicators);
-        this._indicators.add_child(this._volume.indicators);
-        this._indicators.add_child(this._power.indicators);
+        this._indicators.add_child(this._remoteAccess);
+        this._indicators.add_child(this._rfkill);
+        this._indicators.add_child(this._volume);
+        this._indicators.add_child(this._power);
         this._indicators.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
 
         this.menu.addMenuItem(this._volume.menu);
@@ -799,10 +799,10 @@ class Panel extends St.Widget {
         this.add_child(this._rightBox);
 
         this._leftCorner = new PanelCorner(St.Side.LEFT);
-        this.add_child(this._leftCorner.actor);
+        this.add_child(this._leftCorner);
 
         this._rightCorner = new PanelCorner(St.Side.RIGHT);
-        this.add_child(this._rightCorner.actor);
+        this.add_child(this._rightCorner);
 
         this.connect('button-press-event', this._onButtonPress.bind(this));
         this.connect('touch-event', this._onButtonPress.bind(this));
@@ -895,21 +895,21 @@ class Panel extends St.Widget {
 
         let cornerWidth, cornerHeight;
 
-        [, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
-        [, cornerHeight] = this._leftCorner.actor.get_preferred_height(-1);
+        [, cornerWidth] = this._leftCorner.get_preferred_width(-1);
+        [, cornerHeight] = this._leftCorner.get_preferred_height(-1);
         childBox.x1 = 0;
         childBox.x2 = cornerWidth;
         childBox.y1 = allocHeight;
         childBox.y2 = allocHeight + cornerHeight;
-        this._leftCorner.actor.allocate(childBox, flags);
+        this._leftCorner.allocate(childBox, flags);
 
-        [, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
-        [, cornerHeight] = this._rightCorner.actor.get_preferred_height(-1);
+        [, cornerWidth] = this._rightCorner.get_preferred_width(-1);
+        [, cornerHeight] = this._rightCorner.get_preferred_height(-1);
         childBox.x1 = allocWidth - cornerWidth;
         childBox.x2 = allocWidth;
         childBox.y1 = allocHeight;
         childBox.y2 = allocHeight + cornerHeight;
-        this._rightCorner.actor.allocate(childBox, flags);
+        this._rightCorner.allocate(childBox, flags);
     }
 
     _onButtonPress(actor, event) {
@@ -1114,14 +1114,14 @@ class Panel extends St.Widget {
 
     _addStyleClassName(className) {
         this.add_style_class_name(className);
-        this._rightCorner.actor.add_style_class_name(className);
-        this._leftCorner.actor.add_style_class_name(className);
+        this._rightCorner.add_style_class_name(className);
+        this._leftCorner.add_style_class_name(className);
     }
 
     _removeStyleClassName(className) {
         this.remove_style_class_name(className);
-        this._rightCorner.actor.remove_style_class_name(className);
-        this._leftCorner.actor.remove_style_class_name(className);
+        this._rightCorner.remove_style_class_name(className);
+        this._leftCorner.remove_style_class_name(className);
     }
 
     _onMenuSet(indicator) {
diff --git a/js/ui/panelMenu.js b/js/ui/panelMenu.js
index b8cbd8c052..169ea0d249 100644
--- a/js/ui/panelMenu.js
+++ b/js/ui/panelMenu.js
@@ -2,7 +2,6 @@
 /* exported Button, SystemIndicator */
 
 const { Atk, Clutter, GObject, St } = imports.gi;
-const Signals = imports.signals;
 
 const Main = imports.ui.main;
 const Params = imports.misc.params;
@@ -200,24 +199,34 @@ var Button = GObject.registerClass({
  * of an icon and a menu section, which will be composed into the
  * aggregate menu.
  */
-var SystemIndicator = class {
-    constructor() {
-        this.indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box',
-                                             reactive: true });
-        this.indicators.hide();
+var SystemIndicator = GObject.registerClass({
+    GTypeName: 'PanelMenu_SystemIndicator',
+}, class SystemIndicator extends St.BoxLayout {
+    _init() {
+        super._init({
+            style_class: 'panel-status-indicators-box',
+            reactive: true,
+            visible: false
+        });
         this.menu = new PopupMenu.PopupMenuSection();
     }
 
+    get indicators() {
+        let klass = this.constructor.name;
+        let { stack } = new Error();
+        log(`Usage of indicator.indicators is deprecated for ${klass}\n${stack}`);
+        return this;
+    }
+
     _syncIndicatorsVisible() {
-        this.indicators.visible = this.indicators.get_children().some(a => a.visible);
+        this.visible = this.get_children().some(a => a.visible);
     }
 
     _addIndicator() {
         let icon = new St.Icon({ style_class: 'system-status-icon' });
-        this.indicators.add_actor(icon);
+        this.add_actor(icon);
         icon.connect('notify::visible', this._syncIndicatorsVisible.bind(this));
         this._syncIndicatorsVisible();
         return icon;
     }
-};
-Signals.addSignalMethods(SystemIndicator.prototype);
+});
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
index ff3a697ffc..c0354239e7 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -48,21 +48,23 @@ var STANDARD_FADE_TIME = 10000;
 var MANUAL_FADE_TIME = 300;
 var CURTAIN_SLIDE_TIME = 300;
 
-var Clock = class {
-    constructor() {
-        this.actor = new St.BoxLayout({ style_class: 'screen-shield-clock',
-                                        vertical: true });
+var Clock = GObject.registerClass(
+class ScreenShieldClock extends St.BoxLayout {
+    _init() {
+        super._init({ style_class: 'screen-shield-clock', vertical: true });
 
         this._time = new St.Label({ style_class: 'screen-shield-clock-time' });
         this._date = new St.Label({ style_class: 'screen-shield-clock-date' });
 
-        this.actor.add(this._time, { x_align: St.Align.MIDDLE });
-        this.actor.add(this._date, { x_align: St.Align.MIDDLE });
+        this.add(this._time, { x_align: St.Align.MIDDLE });
+        this.add(this._date, { x_align: St.Align.MIDDLE });
 
         this._wallClock = new GnomeDesktop.WallClock({ time_only: true });
         this._wallClock.connect('notify::clock', this._updateClock.bind(this));
 
         this._updateClock();
+
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
     _updateClock() {
@@ -75,17 +77,20 @@ var Clock = class {
         this._date.text = date.toLocaleFormat(dateFormat);
     }
 
-    destroy() {
-        this.actor.destroy();
+    _onDestroy() {
         this._wallClock.run_dispose();
     }
-};
+});
 
-var NotificationsBox = class {
-    constructor() {
-        this.actor = new St.BoxLayout({ vertical: true,
-                                        name: 'screenShieldNotifications',
-                                        style_class: 'screen-shield-notifications-container' });
+var NotificationsBox = GObject.registerClass({
+    Signals: { 'wake-up-screen': {} }
+}, class NotificationsBox extends St.BoxLayout {
+    _init() {
+        super._init({
+            vertical: true,
+            name: 'screenShieldNotifications',
+            style_class: 'screen-shield-notifications-container'
+        });
 
         this._scrollView = new St.ScrollView({ x_fill: false, x_align: St.Align.START,
                                                hscrollbar_policy: St.PolicyType.NEVER });
@@ -93,7 +98,7 @@ var NotificationsBox = class {
                                                    style_class: 'screen-shield-notifications-container' });
         this._scrollView.add_actor(this._notificationBox);
 
-        this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START });
+        this.add(this._scrollView, { x_fill: true, x_align: St.Align.START });
 
         this._sources = new Map();
         Main.messageTray.getSources().forEach(source => {
@@ -102,9 +107,11 @@ var NotificationsBox = class {
         this._updateVisibility();
 
         this._sourceAddedId = Main.messageTray.connect('source-added', this._sourceAdded.bind(this));
+
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
-    destroy() {
+    _onDestroy() {
         if (this._sourceAddedId) {
             Main.messageTray.disconnect(this._sourceAddedId);
             this._sourceAddedId = 0;
@@ -114,15 +121,13 @@ var NotificationsBox = class {
         for (let [source, obj] of items) {
             this._removeSource(source, obj);
         }
-
-        this.actor.destroy();
     }
 
     _updateVisibility() {
         this._notificationBox.visible =
             this._notificationBox.get_children().some(a => a.visible);
 
-        this.actor.visible = this._notificationBox.visible;
+        this.visible = this._notificationBox.visible;
     }
 
     _makeNotificationCountText(count, isChat) {
@@ -338,8 +343,7 @@ var NotificationsBox = class {
 
         this._sources.delete(source);
     }
-};
-Signals.addSignalMethods(NotificationsBox.prototype);
+});
 
 var Arrow = GObject.registerClass(
 class ScreenShieldArrow extends St.Bin {
@@ -559,11 +563,11 @@ var ScreenShield = class {
         this._longLightbox = new Lightbox.Lightbox(Main.uiGroup,
                                                    { inhibitEvents: true,
                                                      fadeFactor: 1 });
-        this._longLightbox.connect('active-changed', this._onLongLightbox.bind(this));
+        this._longLightbox.connect('notify::active', this._onLongLightbox.bind(this));
         this._shortLightbox = new Lightbox.Lightbox(Main.uiGroup,
                                                     { inhibitEvents: true,
                                                       fadeFactor: 1 });
-        this._shortLightbox.connect('active-changed', this._onShortLightbox.bind(this));
+        this._shortLightbox.connect('notify::active', this._onShortLightbox.bind(this));
 
         this.idleMonitor = Meta.IdleMonitor.get_core();
         this._cursorTracker = Meta.CursorTracker.get_for_display(global.display);
@@ -809,7 +813,7 @@ var ScreenShield = class {
 
         this._maybeCancelDialog();
 
-        if (this._longLightbox.actor.visible) {
+        if (this._longLightbox.visible) {
             // We're in the process of showing.
             return;
         }
@@ -851,7 +855,7 @@ var ScreenShield = class {
     }
 
     _activateFade(lightbox, time) {
-        Main.uiGroup.set_child_above_sibling(lightbox.actor, null);
+        Main.uiGroup.set_child_above_sibling(lightbox, null);
         lightbox.lightOn(time);
 
         if (this._becameActiveId == 0)
@@ -969,7 +973,6 @@ var ScreenShield = class {
 
             this._dialog = new constructor(this._lockDialogGroup);
 
-
             let time = global.get_current_time();
             if (!this._dialog.open(time, onPrimary)) {
                 // This is kind of an impossible error: we're already modal
@@ -1135,16 +1138,20 @@ var ScreenShield = class {
                                                          vertical: true,
                                                          style_class: 'screen-shield-contents-box' });
         this._clock = new Clock();
-        this._lockScreenContentsBox.add(this._clock.actor, { x_fill: true,
-                                                             y_fill: true });
+        this._lockScreenContentsBox.add(this._clock, {
+            x_fill: true,
+            y_fill: true
+        });
 
         this._lockScreenContents.add_actor(this._lockScreenContentsBox);
 
         this._notificationsBox = new NotificationsBox();
         this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', 
this._wakeUpScreen.bind(this));
-        this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
-                                                                        y_fill: true,
-                                                                        expand: true });
+        this._lockScreenContentsBox.add(this._notificationsBox, {
+            x_fill: true,
+            y_fill: true,
+            expand: true
+        });
 
         this._hasLockScreen = true;
     }
diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js
index f3d1d02ddf..5d27da3393 100644
--- a/js/ui/screenshot.js
+++ b/js/ui/screenshot.js
@@ -1,8 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported ScreenshotService */
 
-const { Clutter, Graphene, Gio, GLib, Meta, Shell, St } = imports.gi;
-const Signals = imports.signals;
+const { Clutter, Graphene, Gio, GObject, GLib, Meta, Shell, St } = imports.gi;
 
 const GrabHelper = imports.ui.grabHelper;
 const Lightbox = imports.ui.lightbox;
@@ -219,48 +218,53 @@ var ScreenshotService = class {
     }
 };
 
-var SelectArea = class {
-    constructor() {
+var SelectArea = GObject.registerClass({
+    GTypeName: 'Screenshot_SelectArea',
+    Signals: { 'finished': { param_types: [Meta.Rectangle.$gtype] } }
+}, class SelectArea extends St.Widget {
+    _init() {
         this._startX = -1;
         this._startY = -1;
         this._lastX = 0;
         this._lastY = 0;
         this._result = null;
 
-        this._group = new St.Widget({ visible: false,
-                                      reactive: true,
-                                      x: 0,
-                                      y: 0 });
-        Main.uiGroup.add_actor(this._group);
+        super._init({
+            visible: false,
+            reactive: true,
+            x: 0,
+            y: 0
+        });
+        Main.uiGroup.add_actor(this);
 
-        this._grabHelper = new GrabHelper.GrabHelper(this._group);
+        this._grabHelper = new GrabHelper.GrabHelper(this);
 
-        this._group.connect('button-press-event',
-                            this._onButtonPress.bind(this));
-        this._group.connect('button-release-event',
-                            this._onButtonRelease.bind(this));
-        this._group.connect('motion-event',
-                            this._onMotionEvent.bind(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._group.add_constraint(constraint);
+        this.add_constraint(constraint);
 
         this._rubberband = new St.Widget({
             style_class: 'select-area-rubberband',
             visible: false
         });
-        this._group.add_actor(this._rubberband);
+        this.add_actor(this._rubberband);
     }
 
-    show() {
-        if (!this._grabHelper.grab({ actor: this._group,
+    vfunc_show() {
+        if (!this._grabHelper.grab({ actor: this,
                                      onUngrab: this._onUngrab.bind(this) }))
             return;
 
         global.display.set_cursor(Meta.Cursor.CROSSHAIR);
-        Main.uiGroup.set_child_above_sibling(this._group, null);
-        this._group.visible = true;
+        Main.uiGroup.set_child_above_sibling(this, null);
+        super.vfunc_show();
     }
 
     _getGeometry() {
@@ -299,7 +303,7 @@ var SelectArea = class {
 
     _onButtonRelease() {
         this._result = this._getGeometry();
-        this._group.ease({
+        this.ease({
             opacity: 0,
             duration: 200,
             mode: Clutter.AnimationMode.EASE_OUT_QUAD,
@@ -313,39 +317,40 @@ var SelectArea = class {
         this.emit('finished', this._result);
 
         GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
-            this._group.destroy();
+            this.destroy();
             return GLib.SOURCE_REMOVE;
         });
     }
-};
-Signals.addSignalMethods(SelectArea.prototype);
+});
+
+var PickPixel = GObject.registerClass({
+    GTypeName: 'Screenshot_PickPixel',
+    Signals: { 'finished': { param_types: [Graphene.Point.$gtype] } }
+}, class PickPixel extends St.Widget {
+    _init() {
+        super._init({ visible: false, reactive: true });
 
-var PickPixel = class {
-    constructor() {
         this._result = null;
 
-        this._group = new St.Widget({ visible: false,
-                                      reactive: true });
-        Main.uiGroup.add_actor(this._group);
+        Main.uiGroup.add_actor(this);
 
-        this._grabHelper = new GrabHelper.GrabHelper(this._group);
+        this._grabHelper = new GrabHelper.GrabHelper(this);
 
-        this._group.connect('button-release-event',
-                            this._onButtonRelease.bind(this));
+        this.connect('button-release-event', this._onButtonRelease.bind(this));
 
         let constraint = new Clutter.BindConstraint({ source: global.stage,
                                                       coordinate: Clutter.BindCoordinate.ALL });
-        this._group.add_constraint(constraint);
+        this.add_constraint(constraint);
     }
 
-    show() {
-        if (!this._grabHelper.grab({ actor: this._group,
+    vfunc_show() {
+        if (!this._grabHelper.grab({ actor: this,
                                      onUngrab: this._onUngrab.bind(this) }))
             return;
 
         global.display.set_cursor(Meta.Cursor.CROSSHAIR);
-        Main.uiGroup.set_child_above_sibling(this._group, null);
-        this._group.visible = true;
+        Main.uiGroup.set_child_above_sibling(this, null);
+        super.vfunc_show();
     }
 
     _onButtonRelease(actor, event) {
@@ -360,29 +365,29 @@ var PickPixel = class {
         this.emit('finished', this._result);
 
         GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
-            this._group.destroy();
+            this.destroy();
             return GLib.SOURCE_REMOVE;
         });
     }
-};
-Signals.addSignalMethods(PickPixel.prototype);
+});
 
 var FLASHSPOT_ANIMATION_OUT_TIME = 500; // milliseconds
 
-var Flashspot = class extends Lightbox.Lightbox {
-    constructor(area) {
-        super(Main.uiGroup, { inhibitEvents: true,
-                              width: area.width,
-                              height: area.height });
-
-        this.actor.style_class = 'flashspot';
-        this.actor.set_position(area.x, area.y);
+var Flashspot = GObject.registerClass(
+class Flashspot extends Lightbox.Lightbox {
+    _init(area) {
+        super._init(Main.uiGroup, {
+            inhibitEvents: true,
+            width: area.width,
+            height: area.height
+        });
+        this.style_class = 'flashspot';
+        this.set_position(area.x, area.y);
     }
 
     fire(doneCallback) {
-        this.actor.show();
-        this.actor.opacity = 255;
-        this.actor.ease({
+        this.set({ visible: true, opacity: 255 });
+        this.ease({
             opacity: 0,
             duration: FLASHSPOT_ANIMATION_OUT_TIME,
             mode: Clutter.AnimationMode.EASE_OUT_QUAD,
@@ -393,4 +398,4 @@ var Flashspot = class extends Lightbox.Lightbox {
             }
         });
     }
-};
+});
diff --git a/js/ui/search.js b/js/ui/search.js
index a06f040cec..7e7645a3db 100644
--- a/js/ui/search.js
+++ b/js/ui/search.js
@@ -2,7 +2,6 @@
 /* exported SearchResultsView */
 
 const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
-const Signals = imports.signals;
 
 const AppDisplay = imports.ui.appDisplay;
 const IconGrid = imports.ui.iconGrid;
@@ -33,20 +32,22 @@ class MaxWidthBox extends St.BoxLayout {
     }
 });
 
-var SearchResult = class {
-    constructor(provider, metaInfo, resultsView) {
+var SearchResult = GObject.registerClass(
+class SearchResult extends St.Button {
+    _init(provider, metaInfo, resultsView) {
         this.provider = provider;
         this.metaInfo = metaInfo;
         this._resultsView = resultsView;
 
-        this.actor = new St.Button({ reactive: true,
-                                     can_focus: true,
-                                     track_hover: true,
-                                     x_align: St.Align.START,
-                                     y_fill: true });
+        super._init({
+            reactive: true,
+            can_focus: true,
+            track_hover: true,
+            x_align: St.Align.START,
+            y_fill: true
+        });
 
-        this.actor._delegate = this;
-        this.actor.connect('clicked', this.activate.bind(this));
+        this.connect('clicked', this.activate.bind(this));
     }
 
     activate() {
@@ -57,20 +58,19 @@ var SearchResult = class {
                 St.ClipboardType.CLIPBOARD, this.metaInfo.clipboardText);
         Main.overview.toggle();
     }
-};
-Signals.addSignalMethods(SearchResult.prototype);
-
-var ListSearchResult = class extends SearchResult {
+});
 
-    constructor(provider, metaInfo, resultsView) {
-        super(provider, metaInfo, resultsView);
+var ListSearchResult = GObject.registerClass(
+class ListSearchResult extends SearchResult {
+    _init(provider, metaInfo, resultsView) {
+        super._init(provider, metaInfo, resultsView);
 
-        this.actor.style_class = 'list-search-result';
-        this.actor.x_fill = true;
+        this.style_class = 'list-search-result';
+        this.x_fill = true;
 
         let content = new St.BoxLayout({ style_class: 'list-search-result-content',
                                          vertical: false });
-        this.actor.set_child(content);
+        this.set_child(content);
 
         this._termsChangedId = 0;
 
@@ -93,7 +93,7 @@ var ListSearchResult = class extends SearchResult {
                               x_align: St.Align.START,
                               y_align: St.Align.MIDDLE });
 
-        this.actor.label_actor = title;
+        this.label_actor = title;
 
         if (this.metaInfo['description']) {
             this._descriptionLabel = new St.Label({ style_class: 'list-search-result-description' });
@@ -109,7 +109,7 @@ var ListSearchResult = class extends SearchResult {
             this._highlightTerms();
         }
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
     get ICON_SIZE() {
@@ -126,49 +126,53 @@ var ListSearchResult = class extends SearchResult {
             this._resultsView.disconnect(this._termsChangedId);
         this._termsChangedId = 0;
     }
-};
+});
 
-var GridSearchResult = class extends SearchResult {
-    constructor(provider, metaInfo, resultsView) {
-        super(provider, metaInfo, resultsView);
+var GridSearchResult = GObject.registerClass(
+class GridSearchResult extends SearchResult {
+    _init(provider, metaInfo, resultsView) {
+        super._init(provider, metaInfo, resultsView);
 
-        this.actor.style_class = 'grid-search-result';
+        this.style_class = 'grid-search-result';
 
         this.icon = new IconGrid.BaseIcon(this.metaInfo['name'],
                                           { createIcon: this.metaInfo['createIcon'] });
         let content = new St.Bin({ child: this.icon });
-        this.actor.set_child(content);
-        this.actor.label_actor = this.icon.label;
+        this.set_child(content);
+        this.label_actor = this.icon.label;
     }
-};
+});
+
+var SearchResultsBase = GObject.registerClass({
+    GTypeFlags: GObject.TypeFlags.ABSTRACT,
+    Properties: {
+        'focus-child': GObject.ParamSpec.object(
+            'focus-child', 'focus-child', 'focus-child',
+            GObject.ParamFlags.READABLE,
+            Clutter.Actor.$gtype),
+    }
+}, class SearchResultsBase extends St.BoxLayout {
+    _init(provider, resultsView) {
+        super._init({ style_class: 'search-section', vertical: true });
 
-var SearchResultsBase = class {
-    constructor(provider, resultsView) {
         this.provider = provider;
         this._resultsView = resultsView;
 
         this._terms = [];
         this._focusChild = null;
 
-        this.actor = new St.BoxLayout({ style_class: 'search-section',
-                                        vertical: true });
-
         this._resultDisplayBin = new St.Bin({ x_fill: true,
                                               y_fill: true });
-        this.actor.add(this._resultDisplayBin, { expand: true });
+        this.add(this._resultDisplayBin, { expand: true });
 
         let separator = new St.Widget({ style_class: 'search-section-separator' });
-        this.actor.add(separator);
+        this.add(separator);
 
         this._resultDisplays = {};
 
         this._cancellable = new Gio.Cancellable();
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-    }
-
-    destroy() {
-        this.actor.destroy();
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
     _onDestroy() {
@@ -185,10 +189,10 @@ var SearchResultsBase = class {
     clear() {
         this._cancellable.cancel();
         for (let resultId in this._resultDisplays)
-            this._resultDisplays[resultId].actor.destroy();
+            this._resultDisplays[resultId].destroy();
         this._resultDisplays = {};
         this._clearResultDisplay();
-        this.actor.hide();
+        this.hide();
     }
 
     get focusChild() {
@@ -199,7 +203,7 @@ var SearchResultsBase = class {
         if (this._focusChild == actor)
             return;
         this._focusChild = actor;
-        this.emit('focus-child-changed');
+        this.notify('focus-child');
     }
 
     _setMoreCount(_count) {
@@ -238,7 +242,7 @@ var SearchResultsBase = class {
                 metasNeeded.forEach((resultId, i) => {
                     let meta = metas[i];
                     let display = this._createResultDisplay(meta);
-                    display.actor.connect('key-focus-in', this._keyFocusIn.bind(this));
+                    display.connect('key-focus-in', this._keyFocusIn.bind(this));
                     this._resultDisplays[resultId] = display;
                 });
                 callback(true);
@@ -250,7 +254,7 @@ var SearchResultsBase = class {
         this._terms = terms;
         if (providerResults.length == 0) {
             this._clearResultDisplay();
-            this.actor.hide();
+            this.hide();
             callback();
         } else {
             let maxResults = this._getMaxDisplayedResults();
@@ -269,22 +273,23 @@ var SearchResultsBase = class {
                 // To avoid CSS transitions causing flickering when
                 // the first search result stays the same, we hide the
                 // content while filling in the results.
-                this.actor.hide();
+                this.hide();
                 this._clearResultDisplay();
                 results.forEach(resultId => {
                     this._addItem(this._resultDisplays[resultId]);
                 });
                 this._setMoreCount(this.provider.canLaunchSearch ? moreCount : 0);
-                this.actor.show();
+                this.show();
                 callback();
             });
         }
     }
-};
+});
 
-var ListSearchResults = class extends SearchResultsBase {
-    constructor(provider, resultsView) {
-        super(provider, resultsView);
+var ListSearchResults = GObject.registerClass(
+class ListSearchResults extends SearchResultsBase {
+    _init(provider, resultsView) {
+        super._init(provider, resultsView);
 
         this._container = new St.BoxLayout({ style_class: 'search-section-content' });
         this.providerInfo = new ProviderInfo(provider);
@@ -325,21 +330,21 @@ var ListSearchResults = class extends SearchResultsBase {
     }
 
     _addItem(display) {
-        this._content.add_actor(display.actor);
+        this._content.add_actor(display);
     }
 
     getFirstResult() {
         if (this._content.get_n_children() > 0)
-            return this._content.get_child_at_index(0)._delegate;
+            return this._content.get_child_at_index(0);
         else
             return null;
     }
-};
-Signals.addSignalMethods(ListSearchResults.prototype);
+});
 
-var GridSearchResults = class extends SearchResultsBase {
-    constructor(provider, resultsView) {
-        super(provider, resultsView);
+var GridSearchResults = GObject.registerClass(
+class GridSearchResults extends SearchResultsBase {
+    _init(provider, resultsView) {
+        super._init(provider, resultsView);
 
         this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
                                              xAlign: St.Align.START });
@@ -361,7 +366,7 @@ var GridSearchResults = class extends SearchResultsBase {
 
     updateSearch(...args) {
         if (this._notifyAllocationId)
-            this.actor.disconnect(this._notifyAllocationId);
+            this.disconnect(this._notifyAllocationId);
         if (this._updateSearchLater) {
             Meta.later_remove(this._updateSearchLater);
             delete this._updateSearchLater;
@@ -369,7 +374,7 @@ var GridSearchResults = class extends SearchResultsBase {
 
         // Make sure the maximum number of results calculated by
         // _getMaxDisplayedResults() is updated after width changes.
-        this._notifyAllocationId = this.actor.connect('notify::allocation', () => {
+        this._notifyAllocationId = this.connect('notify::allocation', () => {
             if (this._updateSearchLater)
                 return;
             this._updateSearchLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
@@ -383,7 +388,7 @@ var GridSearchResults = class extends SearchResultsBase {
     }
 
     _getMaxDisplayedResults() {
-        let width = this.actor.allocation.x2 - this.actor.allocation.x1;
+        let width = this.allocation.get_width();
         if (width == 0)
             return -1;
 
@@ -406,17 +411,17 @@ var GridSearchResults = class extends SearchResultsBase {
 
     getFirstResult() {
         if (this._grid.visibleItemsCount() > 0)
-            return this._grid.getItemAtIndex(0)._delegate;
+            return this._grid.getItemAtIndex(0);
         else
             return null;
     }
-};
-Signals.addSignalMethods(GridSearchResults.prototype);
+});
 
-var SearchResultsView = class {
-    constructor() {
-        this.actor = new St.BoxLayout({ name: 'searchResults',
-                                        vertical: true });
+var SearchResultsView = GObject.registerClass({
+    Signals: { 'terms-changed': {} }
+}, class SearchResultsView extends St.BoxLayout {
+    _init() {
+        super._init({ name: 'searchResults', vertical: true });
 
         this._content = new MaxWidthBox({ name: 'searchResultsContent',
                                           vertical: true });
@@ -432,16 +437,18 @@ var SearchResultsView = class {
         action.connect('pan', this._onPan.bind(this));
         this._scrollView.add_action(action);
 
-        this.actor.add(this._scrollView, { x_fill: true,
-                                           y_fill: true,
-                                           expand: true,
-                                           x_align: St.Align.START,
-                                           y_align: St.Align.START });
+        this.add(this._scrollView, {
+            x_fill: true,
+            y_fill: true,
+            expand: true,
+            x_align: St.Align.START,
+            y_align: St.Align.STAR
+        });
 
         this._statusText = new St.Label({ style_class: 'search-statustext' });
         this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
                                        y_align: St.Align.MIDDLE });
-        this.actor.add(this._statusBin, { expand: true });
+        this.add(this._statusBin, { expand: true });
         this._statusBin.add_actor(this._statusText);
 
         this._highlightDefault = false;
@@ -599,7 +606,7 @@ var SearchResultsView = class {
     _onPan(action) {
         let [dist_, dx_, dy] = action.get_motion_delta(0);
         let adjustment = this._scrollView.vscroll.adjustment;
-        adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
+        adjustment.value -= (dy / this.height) * adjustment.page_size;
         return false;
     }
 
@@ -617,9 +624,9 @@ var SearchResultsView = class {
         else
             providerDisplay = new GridSearchResults(provider, this);
 
-        providerDisplay.connect('focus-child-changed', this._focusChildChanged.bind(this));
-        providerDisplay.actor.hide();
-        this._content.add(providerDisplay.actor);
+        providerDisplay.connect('notify::focus-child', this._focusChildChanged.bind(this));
+        providerDisplay.hide();
+        this._content.add(providerDisplay);
         provider.display = providerDisplay;
     }
 
@@ -637,7 +644,7 @@ var SearchResultsView = class {
             let provider = providers[i];
             let display = provider.display;
 
-            if (!display.actor.visible)
+            if (!display.visible)
                 continue;
 
             let firstResult = display.getFirstResult();
@@ -712,22 +719,22 @@ var SearchResultsView = class {
             this._doSearch();
 
         if (this._defaultResult)
-            this._defaultResult.actor.popup_menu();
+            this._defaultResult.popup_menu();
     }
 
     navigateFocus(direction) {
-        let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
+        let rtl = this.get_text_direction() == Clutter.TextDirection.RTL;
         if (direction == St.DirectionType.TAB_BACKWARD ||
             direction == (rtl
                 ? St.DirectionType.RIGHT
                 : St.DirectionType.LEFT) ||
             direction == St.DirectionType.UP) {
-            this.actor.navigate_focus(null, direction, false);
+            this.navigate_focus(null, direction, false);
             return;
         }
 
-        let from = this._defaultResult ? this._defaultResult.actor : null;
-        this.actor.navigate_focus(from, direction, false);
+        let from = this._defaultResult ? this._defaultResult : null;
+        this.navigate_focus(from, direction, false);
     }
 
     _setSelected(result, selected) {
@@ -735,10 +742,10 @@ var SearchResultsView = class {
             return;
 
         if (selected) {
-            result.actor.add_style_pseudo_class('selected');
-            Util.ensureActorVisibleInScrollView(this._scrollView, result.actor);
+            result.add_style_pseudo_class('selected');
+            Util.ensureActorVisibleInScrollView(this._scrollView, result);
         } else {
-            result.actor.remove_style_pseudo_class('selected');
+            result.remove_style_pseudo_class('selected');
         }
     }
 
@@ -751,8 +758,7 @@ var SearchResultsView = class {
 
         return description.replace(this._highlightRegex, '<b>$1</b>');
     }
-};
-Signals.addSignalMethods(SearchResultsView.prototype);
+});
 
 var ProviderInfo = GObject.registerClass(
 class ProviderInfo extends St.Button {
diff --git a/js/ui/shellMountOperation.js b/js/ui/shellMountOperation.js
index 84da5f9d52..4e664ded44 100644
--- a/js/ui/shellMountOperation.js
+++ b/js/ui/shellMountOperation.js
@@ -2,7 +2,6 @@
 /* exported ShellMountOperation, GnomeShellMountOpHandler */
 
 const { Clutter, Gio, GLib, GObject, Pango, Shell, St } = imports.gi;
-const Signals = imports.signals;
 
 const Animation = imports.ui.animation;
 const CheckBox = imports.ui.checkBox;
@@ -44,18 +43,22 @@ function _setLabelsForMessage(content, message) {
 
 /* -------------------------------------------------------- */
 
-var ListItem = class {
-    constructor(app) {
-        this._app = app;
-
+var ListItem = GObject.registerClass({
+    GTypeName: 'ShellMountOperation_ListItem',
+    Signals: { 'activate': {} }
+}, class ListItem extends St.Button {
+    _init(app) {
         let layout = new St.BoxLayout({ vertical: false });
+        super._init({
+            style_class: 'mount-dialog-app-list-item',
+            can_focus: true,
+            child: layout,
+            reactive: true,
+            x_align: St.Align.START,
+            x_fill: true
+        });
 
-        this.actor = new St.Button({ style_class: 'mount-dialog-app-list-item',
-                                     can_focus: true,
-                                     child: layout,
-                                     reactive: true,
-                                     x_align: St.Align.START,
-                                     x_fill: true });
+        this._app = app;
 
         this._icon = this._app.create_icon_texture(LIST_ITEM_ICON_SIZE);
 
@@ -69,15 +72,14 @@ var ListItem = class {
                                     child: this._nameLabel });
         layout.add(labelBin);
 
-        this.actor.connect('clicked', this._onClicked.bind(this));
+        this.connect('clicked', this._onClicked.bind(this));
     }
 
     _onClicked() {
         this.emit('activate');
         this._app.activate();
     }
-};
-Signals.addSignalMethods(ListItem.prototype);
+});
 
 var ShellMountOperation = class {
     constructor(source, params) {
@@ -304,14 +306,14 @@ var ShellMountPasswordDialog = GObject.registerClass({
                                                   visible: false }));
 
             this._hiddenVolume = new CheckBox.CheckBox(_("Hidden Volume"));
-            content.messageBox.add(this._hiddenVolume.actor);
+            content.messageBox.add(this._hiddenVolume);
 
             this._systemVolume = new CheckBox.CheckBox(_("Windows System Volume"));
-            content.messageBox.add(this._systemVolume.actor);
+            content.messageBox.add(this._systemVolume);
 
             this._keyfilesCheckbox = new CheckBox.CheckBox(_("Uses Keyfiles"));
-            this._keyfilesCheckbox.actor.connect("clicked", this._onKeyfilesCheckboxClicked.bind(this));
-            content.messageBox.add(this._keyfilesCheckbox.actor);
+            this._keyfilesCheckbox.connect("clicked", this._onKeyfilesCheckboxClicked.bind(this));
+            content.messageBox.add(this._keyfilesCheckbox);
 
             this._keyfilesLabel.clutter_text.set_markup(
                 /* Translators: %s is the Disks application */
@@ -361,7 +363,7 @@ var ShellMountPasswordDialog = GObject.registerClass({
         ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
         this.setInitialKeyFocus(this._passwordEntry);
         this._workSpinner = new Animation.Spinner(WORK_SPINNER_ICON_SIZE, true);
-        this._passwordEntry.secondary_icon = this._workSpinner.actor;
+        this._passwordEntry.secondary_icon = this._workSpinner;
 
         if (rtl) {
             layout.attach(this._passwordEntry, 0, 1, 1, 1);
@@ -382,9 +384,9 @@ var ShellMountPasswordDialog = GObject.registerClass({
 
         if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
             this._rememberChoice = new CheckBox.CheckBox(_("Remember Password"));
-            this._rememberChoice.actor.checked =
+            this._rememberChoice.checked =
                 global.settings.get_boolean(REMEMBER_MOUNT_PASSWORD_KEY);
-            content.messageBox.add(this._rememberChoice.actor);
+            content.messageBox.add(this._rememberChoice);
         } else {
             this._rememberChoice = null;
         }
@@ -440,22 +442,22 @@ var ShellMountPasswordDialog = GObject.registerClass({
         }
 
         global.settings.set_boolean(REMEMBER_MOUNT_PASSWORD_KEY,
-            this._rememberChoice && this._rememberChoice.actor.checked);
+            this._rememberChoice && this._rememberChoice.checked);
 
         this._workSpinner.play();
         this.emit('response', 1,
             this._passwordEntry.get_text(),
             this._rememberChoice &&
-            this._rememberChoice.actor.checked,
+            this._rememberChoice.checked,
             this._hiddenVolume &&
-            this._hiddenVolume.actor.checked,
+            this._hiddenVolume.checked,
             this._systemVolume &&
-            this._systemVolume.actor.checked,
+            this._systemVolume.checked,
             parseInt(pim));
     }
 
     _onKeyfilesCheckboxClicked() {
-        let useKeyfiles = this._keyfilesCheckbox.actor.checked;
+        let useKeyfiles = this._keyfilesCheckbox.checked;
         this._passwordEntry.reactive = !useKeyfiles;
         this._passwordEntry.can_focus = !useKeyfiles;
         this._passwordEntry.clutter_text.editable = !useKeyfiles;
@@ -464,8 +466,8 @@ var ShellMountPasswordDialog = GObject.registerClass({
         this._pimEntry.can_focus = !useKeyfiles;
         this._pimEntry.clutter_text.editable = !useKeyfiles;
         this._pimEntry.clutter_text.selectable = !useKeyfiles;
-        this._rememberChoice.actor.reactive = !useKeyfiles;
-        this._rememberChoice.actor.can_focus = !useKeyfiles;
+        this._rememberChoice.reactive = !useKeyfiles;
+        this._rememberChoice.can_focus = !useKeyfiles;
         this._keyfilesLabel.visible = useKeyfiles;
         this.setButtons(useKeyfiles ? this._usesKeyfilesButtons : this._defaultButtons);
     }
@@ -528,7 +530,7 @@ var ShellProcessesDialog = GObject.registerClass({
                 return;
 
             let item = new ListItem(app);
-            this._applicationList.add(item.actor, { x_fill: true });
+            this._applicationList.add(item, { x_fill: true });
 
             item.connect('activate', () => {
                 // use -1 to indicate Cancel
diff --git a/js/ui/status/accessibility.js b/js/ui/status/accessibility.js
index 0423fd510b..b323b4f419 100644
--- a/js/ui/status/accessibility.js
+++ b/js/ui/status/accessibility.js
@@ -102,7 +102,7 @@ class ATIndicator extends PanelMenu.Button {
     _buildItemExtended(string, initialValue, writable, onSet) {
         let widget = new PopupMenu.PopupSwitchMenuItem(string, initialValue);
         if (!writable)
-            widget.actor.reactive = false;
+            widget.reactive = false;
         else
             widget.connect('toggled', item => {
                 onSet(item.state);
diff --git a/js/ui/status/bluetooth.js b/js/ui/status/bluetooth.js
index a69db2fc86..355312a70e 100644
--- a/js/ui/status/bluetooth.js
+++ b/js/ui/status/bluetooth.js
@@ -1,7 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported Indicator */
 
-const { Gio, GnomeBluetooth } = imports.gi;
+const { Gio, GnomeBluetooth, GObject } = imports.gi;
 
 const Main = imports.ui.main;
 const PanelMenu = imports.ui.panelMenu;
@@ -17,9 +17,11 @@ const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface
 
 const HAD_BLUETOOTH_DEVICES_SETUP = 'had-bluetooth-devices-setup';
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var Indicator = GObject.registerClass({
+    GTypeName: 'Bluetooth_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         this._indicator = this._addIndicator();
         this._indicator.icon_name = 'bluetooth-active-symbolic';
@@ -133,4 +135,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
 
         this._toggleItem.label.text = this._proxy.BluetoothAirplaneMode ? _("Turn On") : _("Turn Off");
     }
-};
+});
diff --git a/js/ui/status/brightness.js b/js/ui/status/brightness.js
index 662b39a860..2a933a524f 100644
--- a/js/ui/status/brightness.js
+++ b/js/ui/status/brightness.js
@@ -15,9 +15,11 @@ const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
 const BrightnessInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Power.Screen');
 const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super('display-brightness-symbolic');
+var Indicator = GObject.registerClass({
+    GTypeName: 'Brightness_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
         this._proxy = new BrightnessProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
                                           (proxy, error) => {
                                               if (error) {
@@ -67,4 +69,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
         if (visible)
             this._changeSlider(this._proxy.Brightness / 100.0);
     }
-};
+});
diff --git a/js/ui/status/keyboard.js b/js/ui/status/keyboard.js
index 9a6252ad62..3da5a4ca58 100644
--- a/js/ui/status/keyboard.js
+++ b/js/ui/status/keyboard.js
@@ -923,7 +923,7 @@ class InputSourceIndicator extends PanelMenu.Button {
     }
 
     _buildPropSection(properties) {
-        this._propSeparator.actor.hide();
+        this._propSeparator.hide();
         this._propSection.actor.hide();
         this._propSection.removeAll();
 
@@ -931,7 +931,7 @@ class InputSourceIndicator extends PanelMenu.Button {
 
         if (!this._propSection.isEmpty()) {
             this._propSection.actor.show();
-            this._propSeparator.actor.show();
+            this._propSeparator.show();
         }
     }
 
diff --git a/js/ui/status/location.js b/js/ui/status/location.js
index 0679e22b72..cbdab434c2 100644
--- a/js/ui/status/location.js
+++ b/js/ui/status/location.js
@@ -42,9 +42,11 @@ const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface);
 
 var AgentIface = loadInterfaceXML('org.freedesktop.GeoClue2.Agent');
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var Indicator = GObject.registerClass({
+    GTypeName: 'Location_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         this._settings = new Gio.Settings({ schema_id: LOCATION_SCHEMA });
         this._settings.connect(`changed::${ENABLED}`,
@@ -222,7 +224,7 @@ var Indicator = class extends PanelMenu.SystemIndicator {
 
         this._permStoreProxy = proxy;
     }
-};
+});
 
 function clamp(value, min, max) {
     return Math.max(min, Math.min(max, value));
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index b6c9de088f..f87057edb1 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -860,7 +860,7 @@ class NMWirelessDialog extends ModalDialog.ModalDialog {
                                                  y_align: Clutter.ActorAlign.CENTER });
 
         this._noNetworksSpinner = new Animation.Spinner(16);
-        this._noNetworksBox.add_actor(this._noNetworksSpinner.actor);
+        this._noNetworksBox.add_actor(this._noNetworksSpinner);
         this._noNetworksBox.add_actor(new St.Label({ style_class: 'no-networks-label',
                                                      text: _("No Networks") }));
         this._stack.add_child(this._noNetworksBox);
@@ -1588,9 +1588,11 @@ var DeviceCategory = class extends PopupMenu.PopupMenuSection {
     }
 };
 
-var NMApplet = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var NMApplet = GObject.registerClass({
+    GTypeName: 'Network_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         this._primaryIndicator = this._addIndicator();
         this._vpnIndicator = this._addIndicator();
@@ -1939,7 +1941,7 @@ var NMApplet = class extends PanelMenu.SystemIndicator {
     }
 
     _syncNMState() {
-        this.indicators.visible = this._client.nm_running;
+        this.visible = this._client.nm_running;
         this.menu.actor.visible = this._client.networking_enabled;
 
         this._updateIcon();
@@ -2058,4 +2060,4 @@ var NMApplet = class extends PanelMenu.SystemIndicator {
         this._vpnIndicator.icon_name = this._vpnSection.getIndicatorIcon();
         this._vpnIndicator.visible = (this._vpnIndicator.icon_name != '');
     }
-};
+});
diff --git a/js/ui/status/nightLight.js b/js/ui/status/nightLight.js
index fceb3c68d6..8277530c93 100644
--- a/js/ui/status/nightLight.js
+++ b/js/ui/status/nightLight.js
@@ -1,7 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported Indicator */
 
-const Gio = imports.gi.Gio;
+const { Gio, GObject } = imports.gi;
 
 const Main = imports.ui.main;
 const PanelMenu = imports.ui.panelMenu;
@@ -15,9 +15,11 @@ const OBJECT_PATH = '/org/gnome/SettingsDaemon/Color';
 const ColorInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Color');
 const ColorProxy = Gio.DBusProxy.makeProxyWrapper(ColorInterface);
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var Indicator = GObject.registerClass({
+    GTypeName: 'NightLight_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         this._indicator = this._addIndicator();
         this._indicator.icon_name = 'night-light-symbolic';
@@ -66,4 +68,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
             : _("Disable Until Tomorrow");
         this._item.visible = this._indicator.visible = visible;
     }
-};
+});
diff --git a/js/ui/status/power.js b/js/ui/status/power.js
index 486a7fdd35..8aefc5ad49 100644
--- a/js/ui/status/power.js
+++ b/js/ui/status/power.js
@@ -1,7 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported Indicator */
 
-const { Clutter, Gio, St, UPowerGlib: UPower } = imports.gi;
+const { Clutter, Gio, GObject, St, UPowerGlib: UPower } = imports.gi;
 
 const Main = imports.ui.main;
 const PanelMenu = imports.ui.panelMenu;
@@ -17,9 +17,11 @@ const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(DisplayDeviceInterface)
 
 const SHOW_BATTERY_PERCENTAGE       = 'show-battery-percentage';
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var Indicator = GObject.registerClass({
+    GTypeName: 'Power_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
         this._desktopSettings.connect(`changed::${SHOW_BATTERY_PERCENTAGE}`,
@@ -28,8 +30,8 @@ var Indicator = class extends PanelMenu.SystemIndicator {
         this._indicator = this._addIndicator();
         this._percentageLabel = new St.Label({ y_expand: true,
                                                y_align: Clutter.ActorAlign.CENTER });
-        this.indicators.add(this._percentageLabel, { expand: true, y_fill: true });
-        this.indicators.add_style_class_name('power-status');
+        this.add(this._percentageLabel, { expand: true, y_fill: true });
+        this.add_style_class_name('power-status');
 
         this._proxy = new PowerManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH,
                                             (proxy, error) => {
@@ -140,4 +142,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
         // The status label
         this._item.label.text = this._getStatus();
     }
-};
+});
diff --git a/js/ui/status/remoteAccess.js b/js/ui/status/remoteAccess.js
index 794377dde1..eba686e900 100644
--- a/js/ui/status/remoteAccess.js
+++ b/js/ui/status/remoteAccess.js
@@ -1,14 +1,16 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported RemoteAccessApplet */
 
-const Meta = imports.gi.Meta;
+const { GObject, Meta } = imports.gi;
 
 const PanelMenu = imports.ui.panelMenu;
 const PopupMenu = imports.ui.popupMenu;
 
-var RemoteAccessApplet = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var RemoteAccessApplet = GObject.registerClass({
+    GTypeName: 'RemoteAccess_Indicator'
+}, class RemoteAccessApplet extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         let backend = Meta.get_backend();
         let controller = backend.get_remote_access_controller();
@@ -75,4 +77,4 @@ var RemoteAccessApplet = class extends PanelMenu.SystemIndicator {
             this._sync();
         }
     }
-};
+});
diff --git a/js/ui/status/rfkill.js b/js/ui/status/rfkill.js
index 9afb5ac905..7ba8220d4d 100644
--- a/js/ui/status/rfkill.js
+++ b/js/ui/status/rfkill.js
@@ -1,7 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported Indicator */
 
-const Gio = imports.gi.Gio;
+const { Gio, GObject } = imports.gi;
 const Signals = imports.signals;
 
 const Main = imports.ui.main;
@@ -61,9 +61,11 @@ function getRfkillManager() {
     return _manager;
 }
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var Indicator = GObject.registerClass({
+    GTypeName: 'Rfkill_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         this._manager = getRfkillManager();
         this._manager.connect('airplane-mode-changed', this._sync.bind(this));
@@ -106,4 +108,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
         else
             this._offItem.label.text = _("Turn Off");
     }
-};
+});
diff --git a/js/ui/status/screencast.js b/js/ui/status/screencast.js
index df248309da..124236f09b 100644
--- a/js/ui/status/screencast.js
+++ b/js/ui/status/screencast.js
@@ -1,12 +1,16 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported Indicator */
 
+const GObject = imports.gi.GObject;
+
 const Main = imports.ui.main;
 const PanelMenu = imports.ui.panelMenu;
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var Indicator = GObject.registerClass({
+    GTypeName: 'Screencast_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         this._indicator = this._addIndicator();
         this._indicator.icon_name = 'media-record-symbolic';
@@ -19,4 +23,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
     _sync() {
         this._indicator.visible = Main.screencastService.isRecording;
     }
-};
+});
diff --git a/js/ui/status/system.js b/js/ui/status/system.js
index 8ca04481e0..d66046c4c6 100644
--- a/js/ui/status/system.js
+++ b/js/ui/status/system.js
@@ -10,8 +10,10 @@ const PanelMenu = imports.ui.panelMenu;
 const PopupMenu = imports.ui.popupMenu;
 
 
-var AltSwitcher = class {
-    constructor(standard, alternate) {
+var AltSwitcher = GObject.registerClass(
+class AltSwitcher extends St.Bin {
+    _init(standard, alternate) {
+        super._init();
         this._standard = standard;
         this._standard.connect('notify::visible', this._sync.bind(this));
         if (this._standard instanceof St.Button)
@@ -31,9 +33,8 @@ var AltSwitcher = class {
         this._clickAction = new Clutter.ClickAction();
         this._clickAction.connect('long-press', this._onLongPress.bind(this));
 
-        this.actor = new St.Bin();
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-        this.actor.connect('notify::mapped', () => (this._flipped = false));
+        this.connect('destroy', this._onDestroy.bind(this));
+        this.connect('notify::mapped', () => (this._flipped = false));
     }
 
     _sync() {
@@ -51,11 +52,11 @@ var AltSwitcher = class {
         } else if (this._alternate.visible) {
             childToShow = this._alternate;
         } else {
-            this.actor.hide();
+            this.hide();
             return;
         }
 
-        let childShown = this.actor.get_child();
+        let childShown = this.get_child();
         if (childShown != childToShow) {
             if (childShown) {
                 if (childShown.fake_release)
@@ -64,8 +65,8 @@ var AltSwitcher = class {
             }
             childToShow.add_action(this._clickAction);
 
-            let hasFocus = this.actor.contains(global.stage.get_key_focus());
-            this.actor.set_child(childToShow);
+            let hasFocus = this.contains(global.stage.get_key_focus());
+            this.set_child(childToShow);
             if (hasFocus)
                 childToShow.grab_key_focus();
 
@@ -74,7 +75,7 @@ var AltSwitcher = class {
             global.sync_pointer();
         }
 
-        this.actor.show();
+        this.show();
     }
 
     _onDestroy() {
@@ -104,11 +105,13 @@ var AltSwitcher = class {
         this._sync();
         return true;
     }
-};
+});
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var Indicator = GObject.registerClass({
+    GTypeName: 'System_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         let userManager = AccountsService.UserManager.get_default();
         this._user = userManager.get_user(GLib.get_user_name());
@@ -289,7 +292,7 @@ var Indicator = class extends PanelMenu.SystemIndicator {
                                           bindFlags);
 
         this._altSwitcher = new AltSwitcher(this._powerOffAction, this._suspendAction);
-        item.add(this._altSwitcher.actor, { expand: true, x_fill: false });
+        item.add(this._altSwitcher, { expand: true, x_fill: false });
 
         this.menu.addMenuItem(item);
 
@@ -297,7 +300,7 @@ var Indicator = class extends PanelMenu.SystemIndicator {
             this._settingsAction,
             this._orientationLockAction,
             this._lockScreenAction,
-            this._altSwitcher.actor,
+            this._altSwitcher,
         ];
 
         for (let actor of visibilityGroup) {
@@ -312,4 +315,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
         Main.overview.hide();
         this._settingsApp.activate();
     }
-};
+});
diff --git a/js/ui/status/thunderbolt.js b/js/ui/status/thunderbolt.js
index be68acdfe2..3c97148d5c 100644
--- a/js/ui/status/thunderbolt.js
+++ b/js/ui/status/thunderbolt.js
@@ -3,7 +3,7 @@
 
 // the following is a modified version of bolt/contrib/js/client.js
 
-const { Gio, GLib, Polkit, Shell } = imports.gi;
+const { Gio, GLib, GObject, Polkit, Shell } = imports.gi;
 const Signals = imports.signals;
 
 const Main = imports.ui.main;
@@ -221,9 +221,11 @@ Signals.addSignalMethods(AuthRobot.prototype);
 
 /* eof client.js  */
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var Indicator = GObject.registerClass({
+    GTypeName: 'Thunderbolt_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         this._indicator = this._addIndicator();
         this._indicator.icon_name = 'thunderbolt-symbolic';
@@ -334,4 +336,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
         const body = _("Could not authorize the Thunderbolt device: %s").format(error.message);
         this._notify(title, body);
     }
-};
+});
diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js
index 3d23044dac..b25c00b90a 100644
--- a/js/ui/status/volume.js
+++ b/js/ui/status/volume.js
@@ -347,9 +347,11 @@ var VolumeMenu = class extends PopupMenu.PopupMenuSection {
     }
 };
 
-var Indicator = class extends PanelMenu.SystemIndicator {
-    constructor() {
-        super();
+var Indicator = GObject.registerClass({
+    GTypeName: 'Volume_Indicator'
+}, class Indicator extends PanelMenu.SystemIndicator {
+    _init() {
+        super._init();
 
         this._primaryIndicator = this._addIndicator();
         this._inputIndicator = this._addIndicator();
@@ -374,7 +376,7 @@ var Indicator = class extends PanelMenu.SystemIndicator {
 
         this.menu.addMenuItem(this._volumeMenu);
 
-        this.indicators.connect('scroll-event', this._onScrollEvent.bind(this));
+        this.connect('scroll-event', this._onScrollEvent.bind(this));
     }
 
     _onScrollEvent(actor, event) {
@@ -388,4 +390,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
         Main.osdWindowManager.show(-1, gicon, null, level, maxLevel);
         return result;
     }
-};
+});
diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
index 5af1b4823f..a584dce6df 100644
--- a/js/ui/unlockDialog.js
+++ b/js/ui/unlockDialog.js
@@ -44,7 +44,7 @@ var UnlockDialog = GObject.registerClass({
         this._authPrompt.setPasswordChar('\u25cf');
         this._authPrompt.nextButton.label = _("Unlock");
 
-        this._promptBox.add_child(this._authPrompt.actor);
+        this._promptBox.add_child(this._authPrompt);
 
         this.allowCancel = false;
 
diff --git a/js/ui/userWidget.js b/js/ui/userWidget.js
index a24f37a3f8..5cd4bb36dd 100644
--- a/js/ui/userWidget.js
+++ b/js/ui/userWidget.js
@@ -14,29 +14,44 @@ var AVATAR_ICON_SIZE = 64;
 // Copyright (C) 2004-2005 James M. Cape <jcape ignore-your tv>.
 // Copyright (C) 2008,2009 Red Hat, Inc.
 
-var Avatar = class {
-    constructor(user, params) {
-        this._user = user;
+var Avatar = GObject.registerClass({
+    GTypeName: 'UserWidget_Avatar'
+}, class Avatar extends St.Bin {
+    _init(user, params) {
+        let themeContext = St.ThemeContext.get_for_stage(global.stage);
         params = Params.parse(params, { reactive: false,
                                         iconSize: AVATAR_ICON_SIZE,
                                         styleClass: 'user-icon' });
-        this._iconSize = params.iconSize;
 
-        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
-        this.actor = new St.Bin({ style_class: params.styleClass,
-                                  track_hover: params.reactive,
-                                  reactive: params.reactive,
-                                  width: this._iconSize * scaleFactor,
-                                  height: this._iconSize * scaleFactor });
+        super._init({
+            style_class: params.styleClass,
+            track_hover: params.reactive,
+            reactive: params.reactive,
+            width: params.iconSize * themeContext.scaleFactor,
+            height: params.iconSize * themeContext.scaleFactor
+        });
+
+        this._iconSize = params.iconSize;
+        this._user = user;
 
         // Monitor the scaling factor to make sure we recreate the avatar when needed.
-        let themeContext = St.ThemeContext.get_for_stage(global.stage);
-        themeContext.connect('notify::scale-factor', this.update.bind(this));
+        this._scaleFactorChangeId =
+            themeContext.connect('notify::scale-factor', this.update.bind(this));
+
+        this.connect('destroy', this._onDestroy.bind(this));
+    }
+
+    _onDestroy() {
+        if (this._scaleFactorChangeId) {
+            let themeContext = St.ThemeContext.get_for_stage(global.stage);
+            themeContext.disconnect(this._scaleFactorChangeId);
+            delete this._scaleFactorChangeId;
+        }
     }
 
     setSensitive(sensitive) {
-        this.actor.can_focus = sensitive;
-        this.actor.reactive = sensitive;
+        this.can_focus = sensitive;
+        this.reactive = sensitive;
     }
 
     update() {
@@ -45,21 +60,21 @@ var Avatar = class {
             iconFile = null;
 
         if (iconFile) {
-            this.actor.child = null;
+            this.child = null;
             let { scaleFactor } = St.ThemeContext.get_for_stage(global.stage);
-            this.actor.set_size(
+            this.set_size(
                 this._iconSize * scaleFactor,
                 this._iconSize * scaleFactor);
-            this.actor.style = `
+            this.style = `
                 background-image: url("${iconFile}");
                 background-size: cover;`;
         } else {
-            this.actor.style = null;
-            this.actor.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
-                                             icon_size: this._iconSize });
+            this.style = null;
+            this.child = new St.Icon({ icon_name: 'avatar-default-symbolic',
+                                       icon_size: this._iconSize });
         }
     }
-};
+});
 
 var UserWidgetLabel = GObject.registerClass(
 class UserWidgetLabel extends St.Widget {
@@ -140,21 +155,22 @@ class UserWidgetLabel extends St.Widget {
     }
 });
 
-var UserWidget = class {
-    constructor(user) {
+var UserWidget = GObject.registerClass(
+class UserWidget extends St.BoxLayout {
+    _init(user) {
+        super._init({ style_class: 'user-widget', vertical: false });
+
         this._user = user;
 
-        this.actor = new St.BoxLayout({ style_class: 'user-widget',
-                                        vertical: false });
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
 
         this._avatar = new Avatar(user);
-        this.actor.add_child(this._avatar.actor);
+        this.add_child(this._avatar);
 
         this._label = new UserWidgetLabel(user);
-        this.actor.add_child(this._label);
+        this.add_child(this._label);
 
-        this._label.bind_property('label-actor', this.actor, 'label-actor',
+        this._label.bind_property('label-actor', this, 'label-actor',
                                   GObject.BindingFlags.SYNC_CREATE);
 
         this._userLoadedId = this._user.connect('notify::is-loaded', this._updateUser.bind(this));
@@ -177,4 +193,4 @@ var UserWidget = class {
     _updateUser() {
         this._avatar.update();
     }
-};
+});
diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js
index b7b7104d09..628b5fda06 100644
--- a/js/ui/viewSelector.js
+++ b/js/ui/viewSelector.js
@@ -123,9 +123,14 @@ var ShowOverviewAction = GObject.registerClass({
     }
 });
 
-var ViewSelector = class {
-    constructor(searchEntry, showAppsButton) {
-        this.actor = new Shell.Stack({ name: 'viewSelector' });
+var ViewSelector = GObject.registerClass({
+    Signals: {
+        'page-changed': {},
+        'page-empty': {},
+    }
+}, class ViewSelector extends Shell.Stack {
+    _init(searchEntry, showAppsButton) {
+        super._init({ name: 'viewSelector' });
 
         this._showAppsButton = showAppsButton;
         this._showAppsButton.connect('notify::checked', this._onShowAppsButtonToggled.bind(this));
@@ -165,15 +170,15 @@ var ViewSelector = class {
         this._capturedEventId = 0;
 
         this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
-        this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
+        this._workspacesPage = this._addPage(this._workspacesDisplay,
                                              _("Windows"), 'focus-windows-symbolic');
 
         this.appDisplay = new AppDisplay.AppDisplay();
-        this._appsPage = this._addPage(this.appDisplay.actor,
+        this._appsPage = this._addPage(this.appDisplay,
                                        _("Applications"), 'view-app-grid-symbolic');
 
         this._searchResults = new Search.SearchResultsView();
-        this._searchPage = this._addPage(this._searchResults.actor,
+        this._searchPage = this._addPage(this._searchResults,
                                          _("Search"), 'edit-find-symbolic',
                                          { a11yFocus: this._entry });
 
@@ -184,9 +189,9 @@ var ViewSelector = class {
         this._focusTrap.connect('key-focus-in', () => {
             this._entry.grab_key_focus();
         });
-        this._searchResults.actor.add_actor(this._focusTrap);
+        this._searchResults.add_actor(this._focusTrap);
 
-        global.focus_manager.add_group(this._searchResults.actor);
+        global.focus_manager.add_group(this._searchResults);
 
         this._stageKeyPressId = 0;
         Main.overview.connect('showing', () => {
@@ -310,11 +315,11 @@ var ViewSelector = class {
             Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon);
         else
             Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon, {
-                proxy: this.actor,
+                proxy: this,
                 focusCallback: () => this._a11yFocusPage(page),
             });
         page.hide();
-        this.actor.add_actor(page);
+        this.add_actor(page);
         return page;
     }
 
@@ -454,7 +459,7 @@ var ViewSelector = class {
     _onStageKeyFocusChanged() {
         let focus = global.stage.get_key_focus();
         let appearFocused = (this._entry.contains(focus) ||
-                             this._searchResults.actor.contains(focus));
+                             this._searchResults.contains(focus));
 
         this._text.set_cursor_visible(appearFocused);
 
@@ -599,5 +604,4 @@ var ViewSelector = class {
         else
             return ViewPage.SEARCH;
     }
-};
-Signals.addSignalMethods(ViewSelector.prototype);
+});
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index 740c09ebc8..b5ea54a208 100644
--- a/js/ui/windowManager.js
+++ b/js/ui/windowManager.js
@@ -379,10 +379,11 @@ var WorkspaceTracker = class {
     }
 };
 
-var TilePreview = class {
-    constructor() {
-        this.actor = new St.Widget();
-        global.window_group.add_actor(this.actor);
+var TilePreview = GObject.registerClass(
+class TilePreview extends St.Widget {
+    _init() {
+        super._init();
+        global.window_group.add_actor(this);
 
         this._reset();
         this._showing = false;
@@ -393,7 +394,7 @@ var TilePreview = class {
         if (!windowActor)
             return;
 
-        global.window_group.set_child_below_sibling(this.actor, windowActor);
+        global.window_group.set_child_below_sibling(this, windowActor);
 
         if (this._rect && this._rect.equal(tileRect))
             return;
@@ -414,14 +415,14 @@ var TilePreview = class {
                                                    width: monitor.width,
                                                    height: monitor.height });
             let [, rect] = window.get_frame_rect().intersect(monitorRect);
-            this.actor.set_size(rect.width, rect.height);
-            this.actor.set_position(rect.x, rect.y);
-            this.actor.opacity = 0;
+            this.set_size(rect.width, rect.height);
+            this.set_position(rect.x, rect.y);
+            this.opacity = 0;
         }
 
         this._showing = true;
-        this.actor.show();
-        this.actor.ease({
+        this.show();
+        this.ease({
             x: tileRect.x,
             y: tileRect.y,
             width: tileRect.width,
@@ -437,7 +438,7 @@ var TilePreview = class {
             return;
 
         this._showing = false;
-        this.actor.ease({
+        this.ease({
             opacity: 0,
             duration: WINDOW_ANIMATION_TIME,
             mode: Clutter.AnimationMode.EASE_OUT_QUAD,
@@ -446,7 +447,7 @@ var TilePreview = class {
     }
 
     _reset() {
-        this.actor.hide();
+        this.hide();
         this._rect = null;
         this._monitorIndex = -1;
     }
@@ -460,9 +461,9 @@ var TilePreview = class {
         if (this._rect.x + this._rect.width == monitor.x + monitor.width)
             styles.push('tile-preview-right');
 
-        this.actor.style_class = styles.join(' ');
+        this.style_class = styles.join(' ');
     }
-};
+});
 
 var TouchpadWorkspaceSwitchAction = class {
     constructor(actor, allowedModes) {
@@ -669,15 +670,16 @@ var AppSwitchAction = GObject.registerClass({
     }
 });
 
-var ResizePopup = class {
-    constructor() {
-        this._widget = new St.Widget({ layout_manager: new Clutter.BinLayout() });
+var ResizePopup = GObject.registerClass(
+class ResizePopup extends St.Widget {
+    _init() {
+        super._init({ layout_manager: new Clutter.BinLayout() });
         this._label = new St.Label({ style_class: 'resize-popup',
                                      x_align: Clutter.ActorAlign.CENTER,
                                      y_align: Clutter.ActorAlign.CENTER,
                                      x_expand: true, y_expand: true });
-        this._widget.add_child(this._label);
-        Main.uiGroup.add_actor(this._widget);
+        this.add_child(this._label);
+        Main.uiGroup.add_actor(this);
     }
 
     set(rect, displayW, displayH) {
@@ -686,15 +688,10 @@ var ResizePopup = class {
         let text = _("%d × %d").format(displayW, displayH);
         this._label.set_text(text);
 
-        this._widget.set_position(rect.x, rect.y);
-        this._widget.set_size(rect.width, rect.height);
-    }
-
-    destroy() {
-        this._widget.destroy();
-        this._widget = null;
+        this.set_position(rect.x, rect.y);
+        this.set_size(rect.width, rect.height);
     }
-};
+});
 
 var WindowManager = class {
     constructor() {
@@ -1119,7 +1116,7 @@ var WindowManager = class {
         this._currentPadOsd = new PadOsd.PadOsd(device, settings, imagePath, editionMode, monitorIndex);
         this._currentPadOsd.connect('closed', () => (this._currentPadOsd = null));
 
-        return this._currentPadOsd.actor;
+        return this._currentPadOsd;
     }
 
     _switchWorkspaceMotion(action, xRel, yRel) {
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index a93b19add9..86403b0577 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -93,6 +93,7 @@ class WindowCloneLayout extends Clutter.LayoutManager {
 });
 
 var WindowClone = GObject.registerClass({
+    GTypeName: 'Workspace_WindowClone',
     Signals: {
         'drag-begin': {},
         'drag-cancelled': {},
@@ -102,7 +103,7 @@ var WindowClone = GObject.registerClass({
         'show-chrome': {},
         'size-changed': {}
     },
-}, class WorkspaceWindowClone extends St.Widget {
+}, class WindowClone extends St.Widget {
     _init(realWindow, workspace) {
         this.realWindow = realWindow;
         this.metaWindow = realWindow.meta_window;
@@ -1094,23 +1095,14 @@ function rectEqual(one, two) {
             one.height == two.height);
 }
 
-const WorkspaceActor = GObject.registerClass(
-class WorkspaceActor extends St.Widget {
-    vfunc_get_focus_chain() {
-        return this.get_children().filter(c => c.visible).sort((a, b) => {
-            if (a instanceof WindowClone && b instanceof WindowClone)
-                return a.slotId - b.slotId;
-
-            return 0;
-        });
-    }
-});
-
 /**
  * @metaWorkspace: a #Meta.Workspace, or null
  */
-var Workspace = class {
-    constructor(metaWorkspace, monitorIndex) {
+var Workspace = GObject.registerClass(
+class Workspace extends St.Widget {
+    _init(metaWorkspace, monitorIndex) {
+        super._init({ style_class: 'window-picker' });
+
         // When dragging a window, we use this slot for reserve space.
         this._reservedSlot = null;
         this._reservedSlotWindow = null;
@@ -1136,18 +1128,17 @@ var Workspace = class {
         // Without this the drop area will be overlapped.
         this._windowOverlaysGroup.set_size(0, 0);
 
-        this.actor = new WorkspaceActor({ style_class: 'window-picker' });
         if (monitorIndex != Main.layoutManager.primaryIndex)
-            this.actor.add_style_class_name('external-monitor');
-        this.actor.set_size(0, 0);
+            this.add_style_class_name('external-monitor');
+        this.set_size(0, 0);
 
         this._dropRect = new Clutter.Actor({ opacity: 0 });
         this._dropRect._delegate = this;
 
-        this.actor.add_actor(this._dropRect);
-        this.actor.add_actor(this._windowOverlaysGroup);
+        this.add_actor(this._dropRect);
+        this.add_actor(this._windowOverlaysGroup);
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
 
         let windows = global.get_window_actors().filter(this._isMyWindow, this);
 
@@ -1178,19 +1169,28 @@ var Workspace = class {
         this._positionWindowsFlags = 0;
         this._positionWindowsId = 0;
 
-        this.actor.connect('notify::mapped', () => {
-            if (this.actor.mapped)
+        this.connect('notify::mapped', () => {
+            if (this.mapped)
                 this._syncActualGeometry();
         });
     }
 
+    vfunc_get_focus_chain() {
+        return this.get_children().filter(c => c.visible).sort((a, b) => {
+            if (a instanceof WindowClone && b instanceof WindowClone)
+                return a.slotId - b.slotId;
+
+            return 0;
+        });
+    }
+
     setFullGeometry(geom) {
         if (rectEqual(this._fullGeometry, geom))
             return;
 
         this._fullGeometry = geom;
 
-        if (this.actor.mapped)
+        if (this.mapped)
             this._recalculateWindowPositions(WindowPositionFlags.NONE);
     }
 
@@ -1201,7 +1201,7 @@ var Workspace = class {
         this._actualGeometry = geom;
         this._actualGeometryDirty = true;
 
-        if (this.actor.mapped)
+        if (this.mapped)
             this._syncActualGeometry();
     }
 
@@ -1213,7 +1213,7 @@ var Workspace = class {
 
         this._actualGeometryLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
             this._actualGeometryLater = 0;
-            if (!this.actor.mapped)
+            if (!this.mapped)
                 return false;
 
             let geom = this._actualGeometry;
@@ -1504,8 +1504,7 @@ var Workspace = class {
             // Newly-created windows are added to a workspace before
             // the compositor finds out about them...
             let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
-                if (this.actor &&
-                    metaWin.get_compositor_private() &&
+                if (metaWin.get_compositor_private() &&
                     metaWin.get_workspace() == this.metaWorkspace)
                     this._doAddWindow(metaWin);
                 return GLib.SOURCE_REMOVE;
@@ -1541,8 +1540,8 @@ var Workspace = class {
         let [clone, overlay_] = this._addWindowClone(win, false);
 
         if (win._overviewHint) {
-            let x = win._overviewHint.x - this.actor.x;
-            let y = win._overviewHint.y - this.actor.y;
+            let x = win._overviewHint.x - this.x;
+            let y = win._overviewHint.y - this.y;
             let scale = win._overviewHint.scale;
             delete win._overviewHint;
 
@@ -1777,10 +1776,6 @@ var Workspace = class {
         }
     }
 
-    destroy() {
-        this.actor.destroy();
-    }
-
     _onDestroy() {
         if (this._overviewHiddenId) {
             Main.overview.disconnect(this._overviewHiddenId);
@@ -1861,11 +1856,11 @@ var Workspace = class {
             this._removeWindowClone(clone.metaWindow);
         });
 
-        this.actor.add_actor(clone);
+        this.add_actor(clone);
 
         overlay.connect('chrome-visible', () => {
             let focus = global.stage.key_focus;
-            if (focus == null || this.actor.contains(focus))
+            if (focus == null || this.contains(focus))
                 clone.grab_key_focus();
 
             this._windowOverlays.forEach(o => {
@@ -1949,7 +1944,7 @@ var Workspace = class {
     }
 
     _getSpacingAndPadding() {
-        let node = this.actor.get_theme_node();
+        let node = this.get_theme_node();
 
         // Window grid spacing
         let columnSpacing = node.get_length('-horizontal-spacing');
@@ -2047,5 +2042,4 @@ var Workspace = class {
 
         return false;
     }
-};
-Signals.addSignalMethods(Workspace.prototype);
+});
diff --git a/js/ui/workspaceThumbnail.js b/js/ui/workspaceThumbnail.js
index 206fbd8632..f8e072e15b 100644
--- a/js/ui/workspaceThumbnail.js
+++ b/js/ui/workspaceThumbnail.js
@@ -2,7 +2,6 @@
 /* exported WorkspaceThumbnail, ThumbnailsBox */
 
 const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
-const Signals = imports.signals;
 
 const Background = imports.ui.background;
 const DND = imports.ui.dnd;
@@ -44,36 +43,43 @@ class PrimaryActorLayout extends Clutter.FixedLayout {
     }
 });
 
-var WindowClone = class {
-    constructor(realWindow) {
-        this.clone = new Clutter.Clone({ source: realWindow });
+var WindowClone = GObject.registerClass({
+    GTypeName: 'WorkspaceThumbnail_WindowClone',
+    Signals: {
+        'drag-begin': {},
+        'drag-cancelled': {},
+        'drag-end': {},
+        'selected': { param_types: [GObject.TYPE_UINT] },
+    }
+}, class WindowClone extends Clutter.Actor {
+    _init(realWindow) {
+        let clone = new Clutter.Clone({ source: realWindow });
+        super._init({
+            layout_manager: new PrimaryActorLayout(clone),
+            reactive: true
+        });
+        this._delegate = this;
 
-        /* Can't use a Shell.GenericContainer because of DND and reparenting... */
-        this.actor = new Clutter.Actor({ layout_manager: new PrimaryActorLayout(this.clone),
-                                         reactive: true });
-        this.actor._delegate = this;
-        this.actor.add_child(this.clone);
+        this.add_child(clone);
         this.realWindow = realWindow;
         this.metaWindow = realWindow.meta_window;
 
-        this.clone._updateId = this.realWindow.connect('notify::position',
-                                                       this._onPositionChanged.bind(this));
-        this.clone._destroyId = this.realWindow.connect('destroy', () => {
+        clone._updateId = this.realWindow.connect('notify::position',
+                                                  this._onPositionChanged.bind(this));
+        clone._destroyId = this.realWindow.connect('destroy', () => {
             // First destroy the clone and then destroy everything
             // This will ensure that we never see it in the _disconnectSignals loop
-            this.clone.destroy();
+            clone.destroy();
             this.destroy();
         });
         this._onPositionChanged();
 
-        this.actor.connect('button-release-event',
-                           this._onButtonRelease.bind(this));
-        this.actor.connect('touch-event',
-                           this._onTouchEvent.bind(this));
+        this.connect('button-release-event', this._onButtonRelease.bind(this));
+        this.connect('touch-event', this._onTouchEvent.bind(this));
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
 
-        this._draggable = DND.makeDraggable(this.actor,
+        this._draggable = DND.makeDraggable(this,
                                             { restoreOnSuccess: true,
                                               dragActorMaxSize: Workspace.WINDOW_DND_SIZE,
                                               dragActorOpacity: Workspace.DRAGGING_WINDOW_OPACITY });
@@ -124,13 +130,9 @@ var WindowClone = class {
 
         let actualAbove = this.getActualStackAbove();
         if (actualAbove == null)
-            this.actor.lower_bottom();
+            this.lower_bottom();
         else
-            this.actor.raise(actualAbove);
-    }
-
-    destroy() {
-        this.actor.destroy();
+            this.raise(actualAbove);
     }
 
     addAttachedDialog(win) {
@@ -147,7 +149,7 @@ var WindowClone = class {
         clone._destroyId = realDialog.connect('destroy', () => {
             clone.destroy();
         });
-        this.actor.add_child(clone);
+        this.add_child(clone);
     }
 
     _updateDialogPosition(realDialog, cloneDialog) {
@@ -159,11 +161,11 @@ var WindowClone = class {
     }
 
     _onPositionChanged() {
-        this.actor.set_position(this.realWindow.x, this.realWindow.y);
+        this.set_position(this.realWindow.x, this.realWindow.y);
     }
 
     _disconnectSignals() {
-        this.actor.get_children().forEach(child => {
+        this.get_children().forEach(child => {
             let realWindow = child.source;
 
             realWindow.disconnect(child._updateId);
@@ -174,14 +176,12 @@ var WindowClone = class {
     _onDestroy() {
         this._disconnectSignals();
 
-        this.actor._delegate = null;
+        this._delegate = null;
 
         if (this.inDrag) {
             this.emit('drag-end');
             this.inDrag = false;
         }
-
-        this.disconnectAll();
     }
 
     _onButtonRelease(actor, event) {
@@ -214,18 +214,17 @@ var WindowClone = class {
         // We may not have a parent if DnD completed successfully, in
         // which case our clone will shortly be destroyed and replaced
         // with a new one on the target workspace.
-        if (this.actor.get_parent() != null) {
+        if (this.get_parent() != null) {
             if (this._stackAbove == null)
-                this.actor.lower_bottom();
+                this.lower_bottom();
             else
-                this.actor.raise(this._stackAbove);
+                this.raise(this._stackAbove);
         }
 
 
         this.emit('drag-end');
     }
-};
-Signals.addSignalMethods(WindowClone.prototype);
+});
 
 
 var ThumbnailState = {
@@ -340,7 +339,7 @@ var WorkspaceThumbnail = GObject.registerClass({
                 clone.setStackAbove(this._bgManager.backgroundActor);
             } else {
                 let previousClone = this._windows[i - 1];
-                clone.setStackAbove(previousClone.actor);
+                clone.setStackAbove(previousClone);
             }
         }
     }
@@ -522,15 +521,15 @@ var WorkspaceThumbnail = GObject.registerClass({
         clone.connect('drag-end', () => {
             Main.overview.endWindowDrag(clone.metaWindow);
         });
-        clone.actor.connect('destroy', () => {
+        clone.connect('destroy', () => {
             this._removeWindowClone(clone.metaWindow);
         });
-        this._contents.add_actor(clone.actor);
+        this._contents.add_actor(clone);
 
         if (this._windows.length == 0)
             clone.setStackAbove(this._bgManager.backgroundActor);
         else
-            clone.setStackAbove(this._windows[this._windows.length - 1].actor);
+            clone.setStackAbove(this._windows[this._windows.length - 1]);
 
         this._windows.push(clone);
 
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 172d2d4905..11b15c9519 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -1,8 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-/* exported WorkspacesView */
+/* exported WorkspacesView, WorkspacesDisplay */
 
 const { Clutter, Gio, GObject, Meta, Shell, St } = imports.gi;
-const Signals = imports.signals;
 
 const Main = imports.ui.main;
 const WindowManager = imports.ui.windowManager;
@@ -17,15 +16,16 @@ var AnimationType = {
 
 const MUTTER_SCHEMA = 'org.gnome.mutter';
 
-var WorkspacesViewBase = class {
-    constructor(monitorIndex) {
-        this.actor = new St.Widget({ style_class: 'workspaces-view',
-                                     reactive: true });
-        this.actor.connect('destroy', this._onDestroy.bind(this));
-        global.focus_manager.add_group(this.actor);
+var WorkspacesViewBase = GObject.registerClass({
+    GTypeFlags: GObject.TypeFlags.ABSTRACT
+}, class WorkspacesViewBase extends St.Widget {
+    _init(monitorIndex) {
+        super._init({ style_class: 'workspaces-view', reactive: true });
+        this.connect('destroy', this._onDestroy.bind(this));
+        global.focus_manager.add_group(this);
 
         // The actor itself isn't a drop target, so we don't want to pick on its area
-        this.actor.set_size(0, 0);
+        this.set_size(0, 0);
 
         this._monitorIndex = monitorIndex;
 
@@ -60,10 +60,6 @@ var WorkspacesViewBase = class {
         this._setReservedSlot(null);
     }
 
-    destroy() {
-        this.actor.destroy();
-    }
-
     setFullGeometry(geom) {
         this._fullGeometry = geom;
         this._syncFullGeometry();
@@ -73,13 +69,14 @@ var WorkspacesViewBase = class {
         this._actualGeometry = geom;
         this._syncActualGeometry();
     }
-};
+});
 
-var WorkspacesView = class extends WorkspacesViewBase {
-    constructor(monitorIndex) {
+var WorkspacesView = GObject.registerClass(
+class WorkspacesView extends WorkspacesViewBase {
+    _init(monitorIndex) {
         let workspaceManager = global.workspace_manager;
 
-        super(monitorIndex);
+        super._init(monitorIndex);
 
         this._animating = false; // tweening
         this._scrolling = false; // swipe-scrolling
@@ -112,8 +109,8 @@ var WorkspacesView = class extends WorkspacesViewBase {
 
         this._overviewShownId =
             Main.overview.connect('shown', () => {
-                this.actor.set_clip(this._fullGeometry.x, this._fullGeometry.y,
-                                    this._fullGeometry.width, this._fullGeometry.height);
+                this.set_clip(this._fullGeometry.x, this._fullGeometry.y,
+                              this._fullGeometry.width, this._fullGeometry.height);
             });
 
         this._switchWorkspaceNotifyId =
@@ -153,7 +150,7 @@ var WorkspacesView = class extends WorkspacesViewBase {
     }
 
     animateFromOverview(animationType) {
-        this.actor.remove_clip();
+        this.remove_clip();
 
         for (let w = 0; w < this._workspaces.length; w++) {
             if (animationType == AnimationType.ZOOM)
@@ -187,12 +184,12 @@ var WorkspacesView = class extends WorkspacesViewBase {
         for (let w = 0; w < this._workspaces.length; w++) {
             let workspace = this._workspaces[w];
 
-            workspace.actor.remove_all_transitions();
+            workspace.remove_all_transitions();
 
             let params = {};
             if (workspaceManager.layout_rows == -1)
                 params.y = (w - active) * this._fullGeometry.height;
-            else if (this.actor.text_direction == Clutter.TextDirection.RTL)
+            else if (this.text_direction == Clutter.TextDirection.RTL)
                 params.x = (active - w) * this._fullGeometry.width;
             else
                 params.x = (w - active) * this._fullGeometry.width;
@@ -212,9 +209,9 @@ var WorkspacesView = class extends WorkspacesViewBase {
                         this._updateVisibility();
                     };
                 }
-                workspace.actor.ease(easeParams);
+                workspace.ease(easeParams);
             } else {
-                workspace.actor.set(params);
+                workspace.set(params);
                 if (w == 0)
                     this._updateVisibility();
             }
@@ -228,12 +225,12 @@ var WorkspacesView = class extends WorkspacesViewBase {
         for (let w = 0; w < this._workspaces.length; w++) {
             let workspace = this._workspaces[w];
             if (this._animating || this._scrolling || this._gestureActive) {
-                workspace.actor.show();
+                workspace.show();
             } else {
                 if (this._inDrag)
-                    workspace.actor.visible = (Math.abs(w - active) <= 1);
+                    workspace.visible = (Math.abs(w - active) <= 1);
                 else
-                    workspace.actor.visible = (w == active);
+                    workspace.visible = (w == active);
             }
         }
     }
@@ -263,7 +260,7 @@ var WorkspacesView = class extends WorkspacesViewBase {
 
             if (j >= this._workspaces.length) { /* added */
                 workspace = new Workspace.Workspace(metaWorkspace, this._monitorIndex);
-                this.actor.add_actor(workspace.actor);
+                this.add_actor(workspace);
                 this._workspaces[j] = workspace;
             } else  {
                 workspace = this._workspaces[j];
@@ -355,8 +352,8 @@ var WorkspacesView = class extends WorkspacesViewBase {
         let last = this._workspaces.length - 1;
 
         if (workspaceManager.layout_rows == -1) {
-            let firstWorkspaceY = this._workspaces[0].actor.y;
-            let lastWorkspaceY = this._workspaces[last].actor.y;
+            let firstWorkspaceY = this._workspaces[0].y;
+            let lastWorkspaceY = this._workspaces[last].y;
             let workspacesHeight = lastWorkspaceY - firstWorkspaceY;
 
             let currentY = firstWorkspaceY;
@@ -365,12 +362,12 @@ var WorkspacesView = class extends WorkspacesViewBase {
             let dy = newY - currentY;
 
             for (let i = 0; i < this._workspaces.length; i++) {
-                this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
-                this._workspaces[i].actor.y += dy;
+                this._workspaces[i].visible = Math.abs(i - adj.value) <= 1;
+                this._workspaces[i].y += dy;
             }
         } else {
-            let firstWorkspaceX = this._workspaces[0].actor.x;
-            let lastWorkspaceX = this._workspaces[last].actor.x;
+            let firstWorkspaceX = this._workspaces[0].x;
+            let lastWorkspaceX = this._workspaces[last].x;
             let workspacesWidth = lastWorkspaceX - firstWorkspaceX;
 
             let currentX = firstWorkspaceX;
@@ -379,19 +376,19 @@ var WorkspacesView = class extends WorkspacesViewBase {
             let dx = newX - currentX;
 
             for (let i = 0; i < this._workspaces.length; i++) {
-                this._workspaces[i].actor.visible = Math.abs(i - adj.value) <= 1;
-                this._workspaces[i].actor.x += dx;
+                this._workspaces[i].visible = Math.abs(i - adj.value) <= 1;
+                this._workspaces[i].x += dx;
             }
         }
     }
-};
-Signals.addSignalMethods(WorkspacesView.prototype);
+});
 
-var ExtraWorkspaceView = class extends WorkspacesViewBase {
-    constructor(monitorIndex) {
-        super(monitorIndex);
+var ExtraWorkspaceView = GObject.registerClass(
+class ExtraWorkspaceView extends WorkspacesViewBase {
+    _init(monitorIndex) {
+        super._init(monitorIndex);
         this._workspace = new Workspace.Workspace(null, monitorIndex);
-        this.actor.add_actor(this._workspace.actor);
+        this.add_actor(this._workspace);
     }
 
     _setReservedSlot(window) {
@@ -439,21 +436,14 @@ var ExtraWorkspaceView = class extends WorkspacesViewBase {
 
     endTouchGesture() {
     }
-};
-
-var DelegateFocusNavigator = GObject.registerClass(
-class DelegateFocusNavigator extends St.Widget {
-    vfunc_navigate_focus(from, direction) {
-        return this._delegate.navigateFocus(from, direction);
-    }
 });
 
-var WorkspacesDisplay = class {
-    constructor() {
-        this.actor = new DelegateFocusNavigator({ clip_to_allocation: true });
-        this.actor._delegate = this;
-        this.actor.connect('notify::allocation', this._updateWorkspacesActualGeometry.bind(this));
-        this.actor.connect('parent-set', this._parentSet.bind(this));
+var WorkspacesDisplay = GObject.registerClass(
+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 => {
@@ -467,7 +457,7 @@ var WorkspacesDisplay = class {
                 Main.overview.hide();
         });
         Main.overview.addAction(clickAction);
-        this.actor.bind_property('mapped', clickAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
+        this.bind_property('mapped', clickAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
 
         let panAction = new Clutter.PanAction({ threshold_trigger_edge: Clutter.GestureTriggerEdge.AFTER });
         panAction.connect('pan', this._onPan.bind(this));
@@ -490,7 +480,7 @@ var WorkspacesDisplay = class {
             this._endSwipeScroll();
         });
         Main.overview.addAction(panAction);
-        this.actor.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
+        this.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
 
         let allowedModes = Shell.ActionMode.OVERVIEW;
         let switchGesture = new WindowManager.WorkspaceSwitchAction(allowedModes);
@@ -498,20 +488,15 @@ var WorkspacesDisplay = class {
         switchGesture.connect('activated', this._onSwitchWorkspaceActivated.bind(this));
         switchGesture.connect('cancel', this._endTouchGesture.bind(this));
         Main.overview.addAction(switchGesture);
-        this.actor.bind_property('mapped', switchGesture, 'enabled', GObject.BindingFlags.SYNC_CREATE);
+        this.bind_property('mapped', switchGesture, 'enabled', GObject.BindingFlags.SYNC_CREATE);
 
         switchGesture = new WindowManager.TouchpadWorkspaceSwitchAction(global.stage, allowedModes);
         switchGesture.connect('motion', this._onSwitchWorkspaceMotion.bind(this));
         switchGesture.connect('activated', this._onSwitchWorkspaceActivated.bind(this));
         switchGesture.connect('cancel', this._endTouchGesture.bind(this));
-        this.actor.connect('notify::mapped', () => {
-            switchGesture.enabled = this.actor.mapped;
-        });
 
         this._primaryIndex = Main.layoutManager.primaryIndex;
-
         this._workspacesViews = [];
-        switchGesture.enabled = this.actor.mapped;
 
         this._settings = new Gio.Settings({ schema_id: MUTTER_SCHEMA });
         this._settings.connect('changed::workspaces-only-on-primary',
@@ -525,12 +510,12 @@ var WorkspacesDisplay = class {
 
         this._fullGeometry = null;
 
-        this.actor.connect('destroy', this._onDestroy.bind(this));
+        this.connect('destroy', this._onDestroy.bind(this));
     }
 
     _onDestroy() {
         if (this._notifyOpacityId) {
-            let parent = this.actor.get_parent();
+            let parent = this.get_parent();
             if (parent)
                 parent.disconnect(this._notifyOpacityId);
             this._notifyOpacityId = 0;
@@ -546,11 +531,11 @@ var WorkspacesDisplay = class {
         let [dist_, dx, dy] = action.get_motion_delta(0);
         let adjustment = this._scrollAdjustment;
         if (global.workspace_manager.layout_rows == -1)
-            adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
-        else if (this.actor.text_direction == Clutter.TextDirection.RTL)
-            adjustment.value += (dx / this.actor.width) * adjustment.page_size;
+            adjustment.value -= (dy / this.height) * adjustment.page_size;
+        else if (this.text_direction == Clutter.TextDirection.RTL)
+            adjustment.value += (dx / this.width) * adjustment.page_size;
         else
-            adjustment.value -= (dx / this.actor.width) * adjustment.page_size;
+            adjustment.value -= (dx / this.width) * adjustment.page_size;
         return false;
     }
 
@@ -583,11 +568,11 @@ var WorkspacesDisplay = class {
         let active = workspaceManager.get_active_workspace_index();
         let adjustment = this._scrollAdjustment;
         if (workspaceManager.layout_rows == -1)
-            adjustment.value = (active - yRel / this.actor.height) * adjustment.page_size;
-        else if (this.actor.text_direction == Clutter.TextDirection.RTL)
-            adjustment.value = (active + xRel / this.actor.width) * adjustment.page_size;
+            adjustment.value = (active - yRel / this.height) * adjustment.page_size;
+        else if (this.text_direction == Clutter.TextDirection.RTL)
+            adjustment.value = (active + xRel / this.width) * adjustment.page_size;
         else
-            adjustment.value = (active - xRel / this.actor.width) * adjustment.page_size;
+            adjustment.value = (active - xRel / this.width) * adjustment.page_size;
     }
 
     _onSwitchWorkspaceActivated(action, direction) {
@@ -600,8 +585,8 @@ var WorkspacesDisplay = class {
         this._endTouchGesture();
     }
 
-    navigateFocus(from, direction) {
-        return this._getPrimaryView().actor.navigate_focus(from, direction, false);
+    vfunc_navigate_focus(from, direction) {
+        return this._getPrimaryView().navigate_focus(from, direction, false);
     }
 
     show(fadeOnPrimary) {
@@ -677,7 +662,7 @@ var WorkspacesDisplay = class {
             else
                 view = new WorkspacesView(i);
 
-            view.actor.connect('scroll-event', this._onScrollEvent.bind(this));
+            view.connect('scroll-event', this._onScrollEvent.bind(this));
             if (i == this._primaryIndex) {
                 this._scrollAdjustment = view.scrollAdjustment;
                 this._scrollAdjustment.connect('notify::value',
@@ -685,13 +670,13 @@ var WorkspacesDisplay = class {
             }
 
             // HACK: Avoid spurious allocation changes while updating views
-            view.actor.hide();
+            view.hide();
 
             this._workspacesViews.push(view);
-            Main.layoutManager.overviewGroup.add_actor(view.actor);
+            Main.layoutManager.overviewGroup.add_actor(view);
         }
 
-        this._workspacesViews.forEach(v => v.actor.show());
+        this._workspacesViews.forEach(v => v.show());
 
         this._updateWorkspacesFullGeometry();
         this._updateWorkspacesActualGeometry();
@@ -738,20 +723,20 @@ var WorkspacesDisplay = class {
 
         this._parentSetLater = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
             this._parentSetLater = 0;
-            let newParent = this.actor.get_parent();
+            let newParent = this.get_parent();
             if (!newParent)
                 return;
 
             // This is kinda hackish - we want the primary view to
-            // appear as parent of this.actor, though in reality it
+            // appear as parent of this, though in reality it
             // is added directly to Main.layoutManager.overviewGroup
             this._notifyOpacityId = newParent.connect('notify::opacity', () => {
-                let opacity = this.actor.get_parent().opacity;
+                let opacity = this.get_parent().opacity;
                 let primaryView = this._getPrimaryView();
                 if (!primaryView)
                     return;
-                primaryView.actor.opacity = opacity;
-                primaryView.actor.visible = opacity != 0;
+                primaryView.opacity = opacity;
+                primaryView.visible = opacity != 0;
             });
         });
     }
@@ -779,8 +764,8 @@ var WorkspacesDisplay = class {
         if (!this._workspacesViews.length)
             return;
 
-        let [x, y] = this.actor.get_transformed_position();
-        let allocation = this.actor.allocation;
+        let [x, y] = this.get_transformed_position();
+        let allocation = this.allocation;
         let width = allocation.x2 - allocation.x1;
         let height = allocation.y2 - allocation.y1;
         let primaryGeometry = { x: x, y: y, width: width, height: height };
@@ -798,7 +783,7 @@ var WorkspacesDisplay = class {
     }
 
     _onScrollEvent(actor, event) {
-        if (!this.actor.mapped)
+        if (!this.mapped)
             return Clutter.EVENT_PROPAGATE;
 
         if (this._workspacesOnlyOnPrimary &&
@@ -829,7 +814,7 @@ var WorkspacesDisplay = class {
     }
 
     _onKeyPressEvent(actor, event) {
-        if (!this.actor.mapped)
+        if (!this.mapped)
             return Clutter.EVENT_PROPAGATE;
         let workspaceManager = global.workspace_manager;
         let activeWs = workspaceManager.get_active_workspace();
@@ -847,5 +832,4 @@ var WorkspacesDisplay = class {
         Main.wm.actionMoveWorkspace(ws);
         return Clutter.EVENT_STOP;
     }
-};
-Signals.addSignalMethods(WorkspacesDisplay.prototype);
+});
diff --git a/tests/interactive/calendar.js b/tests/interactive/calendar.js
index 6428cb462e..d1d435a528 100644
--- a/tests/interactive/calendar.js
+++ b/tests/interactive/calendar.js
@@ -17,7 +17,7 @@ function test() {
     // Calendar can only be imported after Environment.init()
     const Calendar = imports.ui.calendar;
     let calendar = new Calendar.Calendar();
-    vbox.add(calendar.actor,
+    vbox.add(calendar,
              { expand: true,
                x_fill: false, x_align: St.Align.MIDDLE,
                y_fill: false, y_align: St.Align.START });


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