[gnome-boxes: 3/3] app: Seperate out main window



commit 16954ae6b5cafbc2c9f21dea773cf9b4720456f9
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Tue Feb 25 17:45:13 2014 +0000

    app: Seperate out main window
    
    Separate out Gtk.ApplicationWindow related code from App into another
    class, AppWindow and put its UI setup to a .ui file.
    
    I regret not being able to do this in smaller patches but I bumped into
    many unexpected issues on the way, making it extremely difficult to
    divide the changes afterwards.

 data/gnome-boxes.gresource.xml      |    1 +
 data/ui/app-window.ui               |  155 ++++++++++++++++
 src/Makefile.am                     |    2 +
 src/app-window.vala                 |  242 +++++++++++++++++++++++++
 src/app.vala                        |  338 ++++-------------------------------
 src/auth-notification.vala          |    4 +-
 src/collection-view.vala            |    2 +-
 src/display-page.vala               |   18 +-
 src/display-toolbar.vala            |   10 +-
 src/empty-boxes.vala                |    6 +-
 src/libvirt-machine-properties.vala |    6 +-
 src/libvirt-machine.vala            |    4 +-
 src/machine.vala                    |   26 ++--
 src/notificationbar.vala            |    6 +-
 src/ovirt-broker.vala               |    4 +-
 src/properties.vala                 |   26 ++--
 src/searchbar.vala                  |   12 +-
 src/selectionbar.vala               |    4 +-
 src/sidebar.vala                    |    4 +-
 src/topbar.vala                     |   14 +-
 src/vm-creator.vala                 |    2 +-
 src/vm-importer.vala                |    2 +-
 src/wizard-source.vala              |    2 +-
 src/wizard.vala                     |   28 ++--
 24 files changed, 522 insertions(+), 396 deletions(-)
---
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
index 8d1bde1..c21115b 100644
--- a/data/gnome-boxes.gresource.xml
+++ b/data/gnome-boxes.gresource.xml
@@ -6,6 +6,7 @@
     <file>icons/boxes-create.png</file>
     <file>icons/boxes-dark.png</file>
     <file>icons/boxes-gray.png</file>
+    <file preprocess="xml-stripblanks">ui/app-window.ui</file>
     <file preprocess="xml-stripblanks">ui/auth-notification.ui</file>
     <file preprocess="xml-stripblanks">ui/display-page.ui</file>
     <file preprocess="xml-stripblanks">ui/display-toolbar.ui</file>
diff --git a/data/ui/app-window.ui b/data/ui/app-window.ui
new file mode 100644
index 0000000..5941d70
--- /dev/null
+++ b/data/ui/app-window.ui
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.9 -->
+  <template class="BoxesAppWindow" parent="GtkApplicationWindow">
+    <property name="visible">False</property>
+    <property name="show-menubar">False</property>
+    <property name="title" translatable="yes">Boxes</property>
+    <signal name="delete-event" handler="on_delete_event"/>
+    <signal name="configure-event" handler="on_configure_event"/>
+    <signal name="window-state-event" handler="on_window_state_event"/>
+    <signal name="key-press-event" after="yes" handler="on_key_pressed"/>
+
+    <child>
+      <object class="GtkBox" id="main_vbox">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">0</property>
+
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="visible">True</property>
+            <property name="homogeneous">False</property>
+            <property name="transition-type">slide-left-right</property>
+
+            <child>
+              <object class="GtkBox" id="vbox">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">0</property>
+                <property name="halign">fill</property>
+                <property name="valign">fill</property>
+                <property name="hexpand">True</property>
+                <property name="vexpand">True</property>
+
+                <child>
+                  <object class="BoxesSearchbar" id="searchbar">
+                    <property name="visible">True</property>
+                  </object>
+                </child>
+
+                <child>
+                  <object class="GtkOverlay" id="notification_overlay">
+                    <property name="visible">True</property>
+
+                    <child type="overlay">
+                      <object class="BoxesNotificationbar" id="notificationbar">
+                        <property name="visible">True</property>
+                      </object>
+                    </child>
+
+                    <child>
+                      <object class="GtkStack" id="below_bin">
+                        <property name="visible">True</property>
+                        <property name="homogeneous">False</property>
+                        <property name="transition-type">crossfade</property>
+                        <property name="hexpand">True</property>
+                        <property name="vexpand">True</property>
+
+                        <child>
+                          <object class="BoxesEmptyBoxes" id="empty_boxes">
+                            <property name="visible">True</property>
+                          </object>
+                          <packing>
+                            <property name="name">empty-boxes</property>
+                          </packing>
+                        </child>
+
+                        <child>
+                          <object class="BoxesCollectionView" id="view">
+                            <property name="visible">True</property>
+                          </object>
+                          <packing>
+                            <property name="name">collection-view</property>
+                          </packing>
+                        </child>
+
+                        <child>
+                          <object class="GtkBox" id="below_bin_hbox">
+                            <property name="visible">True</property>
+                            <property name="orientation">horizontal</property>
+                            <property name="halign">fill</property>
+                            <property name="valign">fill</property>
+                            <property name="hexpand">True</property>
+                            <property name="vexpand">True</property>
+
+                            <child>
+                              <object class="BoxesSidebar" id="sidebar">
+                                <property name="visible">True</property>
+                              </object>
+                            </child>
+
+                            <child>
+                              <object class="GtkStack" id="content_bin">
+                                <property name="visible">True</property>
+                                <property name="homogeneous">False</property>
+                                <property name="transition-type">slide-left</property>
+                                <property name="hexpand">True</property>
+                                <property name="vexpand">True</property>
+
+                                <child>
+                                  <object class="BoxesWizard" id="wizard">
+                                    <property name="visible">True</property>
+                                  </object>
+                                </child>
+
+                                <child>
+                                  <object class="BoxesProperties" id="properties">
+                                    <property name="visible">True</property>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="name">below-bin-hbox</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+
+              <packing>
+                <property name="name">main-page</property>
+              </packing>
+            </child>
+
+            <child>
+              <object class="BoxesDisplayPage" id="display_page">
+                <property name="visible">True</property>
+              </object>
+
+              <packing>
+                <property name="name">display-page</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+
+        <child>
+          <object class="BoxesSelectionbar" id="selectionbar">
+            <property name="visible">True</property>
+          </object>
+        </child>
+      </object>
+    </child>
+
+    <child type="titlebar">
+      <object class="BoxesTopbar" id="topbar">
+        <property name="visible">True</property>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/Makefile.am b/src/Makefile.am
index 7d2e9e6..d0f2f1a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -38,6 +38,7 @@ AM_VALAFLAGS =                                                \
        --pkg linux                                     \
        --pkg spice-client-gtk-3.0                      \
        --pkg tracker-sparql-$(TRACKER_VERSION)         \
+       --pkg gmodule-2.0                               \
        --gresources $(gresource_file)                  \
        $(VALA_DEBUG_FLAGS)                             \
        $(NULL)
@@ -89,6 +90,7 @@ bin_PROGRAMS = gnome-boxes
 gnome_boxes_SOURCES =                          \
        $(BUILT_SOURCES)                        \
        app.vala                                \
+       app-window.vala                         \
        auth-notification.vala                  \
        collection-view.vala                    \
        collection.vala                         \
diff --git a/src/app-window.vala b/src/app-window.vala
new file mode 100644
index 0000000..db3fff8
--- /dev/null
+++ b/src/app-window.vala
@@ -0,0 +1,242 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using Gtk;
+using Gdk;
+
+private enum Boxes.AppPage {
+    MAIN,
+    DISPLAY
+}
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/app-window.ui")]
+private class Boxes.AppWindow: Gtk.ApplicationWindow, Boxes.UI {
+    public UIState previous_ui_state { get; protected set; }
+    public UIState ui_state { get; protected set; }
+    public AppPage page {
+        get {
+            if (stack.get_visible_child_name () == "display-page")
+                return AppPage.DISPLAY;
+            else
+                return AppPage.MAIN;
+        }
+        set {
+            if (value == AppPage.DISPLAY)
+                stack.set_visible_child_name ("display-page");
+            else
+                stack.set_visible_child_name ("main-page");
+        }
+    }
+
+    [CCode (notify = false)]
+    public bool fullscreened {
+        get { return WindowState.FULLSCREEN in get_window ().get_state (); }
+        set {
+            if (value)
+                fullscreen ();
+            else
+                unfullscreen ();
+        }
+    }
+    private bool maximized { get { return WindowState.MAXIMIZED in get_window ().get_state (); } }
+
+    [GtkChild]
+    private Gtk.Stack stack;
+    [GtkChild]
+    public Searchbar searchbar;
+    [GtkChild]
+    public Topbar topbar;
+    [GtkChild]
+    public Notificationbar notificationbar;
+    [GtkChild]
+    public Sidebar sidebar;
+    [GtkChild]
+    public Wizard wizard;
+    [GtkChild]
+    public Properties properties;
+    [GtkChild]
+    public DisplayPage display_page;
+    [GtkChild]
+    public EmptyBoxes empty_boxes;
+    [GtkChild]
+    public Gtk.Stack below_bin;
+    [GtkChild]
+    private Gtk.Stack content_bin;
+    [GtkChild]
+    private Gtk.Box below_bin_hbox;
+    [GtkChild]
+    public CollectionView view;
+
+    public uint duration;
+    public GLib.Settings settings;
+
+    private uint configure_id;
+    public static const uint configure_id_timeout = 100;  // 100ms
+
+    public AppWindow (Gtk.Application app) {
+        Object (application: app, title: _("Boxes"));
+
+        settings = new GLib.Settings ("org.gnome.boxes");
+
+        notify["ui-state"].connect (ui_state_changed);
+
+        Gtk.Window.set_default_icon_name ("gnome-boxes");
+        Gtk.Settings.get_default ().gtk_application_prefer_dark_theme = true;
+
+        var provider = Boxes.load_css ("gtk-style.css");
+        Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (),
+                                                  provider,
+                                                  600);
+
+        duration = settings.get_int ("animation-duration");
+        // restore window geometry/position
+        var size = settings.get_value ("window-size");
+        if (size.n_children () == 2) {
+            var width = (int) size.get_child_value (0);
+            var height = (int) size.get_child_value (1);
+
+            set_default_size (width, height);
+        }
+
+        if (settings.get_boolean ("window-maximized"))
+            maximize ();
+
+        var position = settings.get_value ("window-position");
+        if (position.n_children () == 2) {
+            var x = (int) position.get_child_value (0);
+            var y = (int) position.get_child_value (1);
+
+            move (x, y);
+        }
+    }
+
+    public void setup_ui () {
+        topbar.setup_ui ();
+        wizard.setup_ui ();
+        display_page.setup_ui ();
+    }
+
+    private void save_window_geometry () {
+        int width, height, x, y;
+
+        if (maximized)
+            return;
+
+        get_size (out width, out height);
+        settings.set_value ("window-size", new int[] { width, height });
+
+        get_position (out x, out y);
+        settings.set_value ("window-position", new int[] { x, y });
+    }
+
+    private void set_main_ui_state () {
+        stack.set_visible_child_name ("main-page");
+    }
+
+    private void ui_state_changed () {
+        // The order is important for some widgets here (e.g properties must change its state before wizard 
so it can
+        // flush any deferred changes for wizard to pick-up when going back from properties to wizard 
(review).
+        foreach (var ui in new Boxes.UI[] { sidebar, topbar, view, properties, wizard, empty_boxes }) {
+            ui.set_state (ui_state);
+        }
+
+        if (ui_state != UIState.DISPLAY)
+            set_main_ui_state ();
+
+        if (ui_state != UIState.COLLECTION)
+            searchbar.search_mode_enabled = false;
+
+        switch (ui_state) {
+        case UIState.COLLECTION:
+            if (App.app.collection.items.length != 0)
+                below_bin.visible_child = view;
+            else
+                below_bin.visible_child = empty_boxes;
+            fullscreened = false;
+            view.visible = true;
+
+            break;
+
+        case UIState.CREDS:
+
+            break;
+
+        case UIState.WIZARD:
+            below_bin.visible_child = below_bin_hbox;
+            content_bin.visible_child = wizard;
+
+            break;
+
+        case UIState.PROPERTIES:
+            below_bin.visible_child = below_bin_hbox;
+            content_bin.visible_child = properties;
+
+            break;
+
+        case UIState.DISPLAY:
+            if (maximized)
+                fullscreened = true;
+
+            break;
+
+        default:
+            warning ("Unhandled UI state %s".printf (ui_state.to_string ()));
+            break;
+        }
+    }
+
+    [GtkCallback]
+    public bool on_key_pressed (Widget widget, Gdk.EventKey event) {
+        var default_modifiers = Gtk.accelerator_get_default_mod_mask ();
+
+        if (event.keyval == Gdk.Key.F11) {
+            fullscreened = !fullscreened;
+            return true;
+        } else if (event.keyval == Gdk.Key.Escape) {
+            if (App.app.selection_mode && ui_state == UIState.COLLECTION)
+               App.app.selection_mode = false;
+        } else if (event.keyval == Gdk.Key.q &&
+                   (event.state & default_modifiers) == Gdk.ModifierType.CONTROL_MASK) {
+            App.app.quit_app ();
+            return true;
+        } else if (event.keyval == Gdk.Key.a &&
+                   (event.state & default_modifiers) == Gdk.ModifierType.MOD1_MASK) {
+            App.app.quit_app ();
+            return true;
+        }
+
+        return false;
+    }
+
+    [GtkCallback]
+    private bool on_configure_event () {
+        if (fullscreened)
+            return false;
+
+        if (configure_id != 0)
+            GLib.Source.remove (configure_id);
+        configure_id = Timeout.add (configure_id_timeout, () => {
+            configure_id = 0;
+            save_window_geometry ();
+
+            return false;
+        });
+
+        return false;
+     }
+
+    [GtkCallback]
+    private bool on_window_state_event (Gdk.EventWindowState event) {
+        if (WindowState.FULLSCREEN in event.changed_mask)
+            this.notify_property ("fullscreened");
+
+        if (fullscreened)
+            return false;
+
+        settings.set_boolean ("window-maximized", maximized);
+        return false;
+    }
+
+    [GtkCallback]
+    private bool on_delete_event () {
+        return App.app.quit_app ();
+    }
+}
diff --git a/src/app.vala b/src/app.vala
index 96168b8..59b9ca6 100644
--- a/src/app.vala
+++ b/src/app.vala
@@ -1,6 +1,4 @@
 // This file is part of GNOME Boxes. License: LGPLv2+
-using Gtk;
-using Gdk;
 
 private abstract class Boxes.Broker : GLib.Object {
     // Overriding subclass should chain-up at the end of its implementation
@@ -17,83 +15,35 @@ private abstract class Boxes.Broker : GLib.Object {
     }
 }
 
-private enum Boxes.AppPage {
-    MAIN,
-    DISPLAY
-}
-
 private class Boxes.App: Gtk.Application, Boxes.UI {
     public static App app;
+    public static Boxes.AppWindow window;
+
     public UIState previous_ui_state { get; protected set; }
     public UIState ui_state { get; protected set; }
-    public Gtk.ApplicationWindow window;
-    [CCode (notify = false)]
-    public bool fullscreen {
-        get { return WindowState.FULLSCREEN in window.get_window ().get_state (); }
-        set {
-            if (value)
-                window.fullscreen ();
-            else
-                window.unfullscreen ();
-        }
-    }
-    public AppPage page {
-        get {
-            if (stack.get_visible_child_name () == "display-page")
-                return AppPage.DISPLAY;
-            else
-                return AppPage.MAIN;
-        }
-        set {
-            if (value == AppPage.DISPLAY)
-                stack.set_visible_child_name ("display-page");
-            else
-                stack.set_visible_child_name ("main-page");
-        }
-    }
 
-    private bool maximized { get { return WindowState.MAXIMIZED in window.get_window ().get_state (); } }
-    private Gtk.Stack stack;
     public CollectionItem current_item; // current object/vm manipulated
-    public Searchbar searchbar;
-    public Topbar topbar;
-    public Notificationbar notificationbar;
-    public Sidebar sidebar;
-    public Selectionbar selectionbar;
-    public uint duration;
-    public GLib.Settings settings;
-    public Wizard wizard;
-    public Properties properties;
-    public DisplayPage display_page;
-    public EmptyBoxes empty_boxes;
     public string? uri { get; set; }
     public Collection collection;
     public CollectionFilter filter;
-    public Gtk.Stack below_bin;
-    private Gtk.Stack content_bin;
-    private Gtk.Box below_bin_hbox;
 
     private bool is_ready;
     public signal void ready ();
     public signal void item_selected (CollectionItem item);
-    public CollectionView view;
 
     private HashTable<string,Broker> brokers;
     private HashTable<string,CollectionSource> sources;
     public GVir.Connection default_connection { owned get { return LibvirtBroker.get_default 
().get_connection ("QEMU Session"); } }
     public CollectionSource default_source { get { return sources.get ("QEMU Session"); } }
 
-    private uint configure_id;
     private GLib.Binding status_bind;
     private ulong got_error_id;
-    public static const uint configure_id_timeout = 100;  // 100ms
 
     public App () {
         application_id = "org.gnome.Boxes";
         flags |= ApplicationFlags.HANDLES_COMMAND_LINE;
 
         app = this;
-        settings = new GLib.Settings ("org.gnome.boxes");
         sources = new HashTable<string,CollectionSource> (str_hash, str_equal);
         brokers = new HashTable<string,Broker> (str_hash, str_equal);
         filter = new Boxes.CollectionFilter ();
@@ -106,15 +56,15 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
         add_action (action);
 
         action = new GLib.SimpleAction ("select-all", null);
-        action.activate.connect (() => { view.select (SelectionCriteria.ALL); });
+        action.activate.connect (() => { window.view.select (SelectionCriteria.ALL); });
         add_action (action);
 
         action = new GLib.SimpleAction ("select-running", null);
-        action.activate.connect (() => { view.select (SelectionCriteria.RUNNING); });
+        action.activate.connect (() => { window.view.select (SelectionCriteria.RUNNING); });
         add_action (action);
 
         action = new GLib.SimpleAction ("select-none", null);
-        action.activate.connect (() => { view.select (SelectionCriteria.NONE); });
+        action.activate.connect (() => { window.view.select (SelectionCriteria.NONE); });
         add_action (action);
 
         action = new GLib.SimpleAction ("help", null);
@@ -179,13 +129,12 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
         set_app_menu (menu);
 
         collection = new Collection ();
-        duration = settings.get_int ("animation-duration");
 
         collection.item_added.connect ((item) => {
-            view.add_item (item);
+            window.view.add_item (item);
         });
         collection.item_removed.connect ((item) => {
-            view.remove_item (item);
+            window.view.remove_item (item);
         });
 
         brokers.insert ("libvirt", LibvirtBroker.get_default ());
@@ -218,7 +167,13 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
     public override void activate () {
         base.activate ();
 
-        setup_ui ();
+        if (window != null)
+            return;
+
+        window = new Boxes.AppWindow (this);
+        window.setup_ui ();
+        set_state (UIState.COLLECTION);
+
         window.present ();
     }
 
@@ -290,11 +245,11 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
 
                 if (file.query_exists ()) {
                     if (is_uri)
-                        wizard.open_with_uri (file.get_uri ());
+                        window.wizard.open_with_uri (file.get_uri ());
                     else
-                        wizard.open_with_uri (arg);
+                        window.wizard.open_with_uri (arg);
                 } else if (is_uri)
-                    wizard.open_with_uri (file.get_uri ());
+                    window.wizard.open_with_uri (file.get_uri ());
                 else
                     open_name (arg);
             });
@@ -302,19 +257,19 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
 
         if (opt_search != null) {
             call_when_ready (() => {
-                searchbar.text = string.joinv (" ", opt_search);
+                window.searchbar.text = string.joinv (" ", opt_search);
                 if (ui_state == UIState.COLLECTION)
-                    searchbar.search_mode_enabled = true;
+                    window.searchbar.search_mode_enabled = true;
             });
         }
 
         if (opt_fullscreen)
-            app.fullscreen = true;
+            window.fullscreened = true;
 
         return 0;
     }
 
-    private bool quit_app () {
+    public bool quit_app () {
         window.hide ();
 
         Idle.add (() => {
@@ -329,8 +284,8 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
     public override void shutdown () {
         base.shutdown ();
 
-        notificationbar.cancel ();
-        wizard.cleanup ();
+        window.notificationbar.cancel ();
+        window.wizard.cleanup ();
         suspend_machines ();
     }
 
@@ -338,7 +293,7 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
         set_state (UIState.COLLECTION);
         // we don't want to show the collection items as it will
         // appear as a glitch when opening a box immediately
-        view.visible = false;
+        window.view.visible = false;
 
         // after "ready" all items should be listed
         foreach (var item in collection.items.data) {
@@ -354,7 +309,7 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
         set_state (UIState.COLLECTION);
         // we don't want to show the collection items as it will
         // appear as a glitch when opening a box immediately
-        view.visible = false;
+        window.view.visible = false;
 
         // after "ready" all items should be listed
         foreach (var item in collection.items.data) {
@@ -442,185 +397,12 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
         }
     }
 
-    private void save_window_geometry () {
-        int width, height, x, y;
-
-        if (maximized)
-            return;
-
-        window.get_size (out width, out height);
-        settings.set_value ("window-size", new int[] { width, height });
-
-        window.get_position (out x, out y);
-        settings.set_value ("window-position", new int[] { x, y });
-    }
-
-    private bool ui_is_setup;
-    private void setup_ui () {
-        if (ui_is_setup)
-            return;
-        ui_is_setup = true;
-        Gtk.Window.set_default_icon_name ("gnome-boxes");
-        Gtk.Settings.get_default ().gtk_application_prefer_dark_theme = true;
-
-        var provider = Boxes.load_css ("gtk-style.css");
-        Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (),
-                                                  provider,
-                                                  600);
-
-        window = new Gtk.ApplicationWindow (this);
-        window.show_menubar = false;
-        window.delete_event.connect (() => { return quit_app (); });
-
-        // restore window geometry/position
-        var size = settings.get_value ("window-size");
-        if (size.n_children () == 2) {
-            var width = (int) size.get_child_value (0);
-            var height = (int) size.get_child_value (1);
-
-            window.set_default_size (width, height);
-        }
-
-        if (settings.get_boolean ("window-maximized"))
-            window.maximize ();
-
-        var position = settings.get_value ("window-position");
-        if (position.n_children () == 2) {
-            var x = (int) position.get_child_value (0);
-            var y = (int) position.get_child_value (1);
-
-            window.move (x, y);
-        }
-
-        window.configure_event.connect (() => {
-            if (fullscreen)
-                return false;
-
-            if (configure_id != 0)
-                GLib.Source.remove (configure_id);
-            configure_id = Timeout.add (configure_id_timeout, () => {
-                configure_id = 0;
-                save_window_geometry ();
-
-                return false;
-            });
-
-            return false;
-        });
-        window.window_state_event.connect ((event) => {
-            if (WindowState.FULLSCREEN in event.changed_mask)
-                this.notify_property ("fullscreen");
-
-            if (fullscreen)
-                return false;
-
-            settings.set_boolean ("window-maximized", maximized);
-            return false;
-        });
-
-        var main_vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
-        window.add (main_vbox);
-
-        stack = new Gtk.Stack ();
-        stack.homogeneous = false;
-        stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT_RIGHT;
-        main_vbox.add (stack);
-
-        var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
-        vbox.halign = Gtk.Align.FILL;
-        vbox.valign = Gtk.Align.FILL;
-        vbox.hexpand = true;
-        vbox.vexpand = true;
-        stack.add_named (vbox, "main-page");
-
-        searchbar = new Searchbar ();
-        vbox.add (searchbar);
-
-        notificationbar = new Notificationbar ();
-        var notification_overlay = new Gtk.Overlay ();
-        notification_overlay.add_overlay (notificationbar);
-
-        vbox.add (notification_overlay);
-
-        display_page = new DisplayPage ();
-        stack.add_named (display_page, "display-page");
-
-        selectionbar = new Selectionbar ();
-        main_vbox.add (selectionbar);
-
-        window.key_press_event.connect_after (on_key_pressed);
-
-        sidebar = new Sidebar ();
-        view = new CollectionView ();
-        topbar = new Topbar ();
-        wizard = new Wizard ();
-        properties = new Properties ();
-        empty_boxes = new EmptyBoxes ();
-
-        window.set_titlebar (topbar);
-
-        below_bin = new Gtk.Stack ();
-        below_bin.homogeneous = false;
-        below_bin.transition_type = Gtk.StackTransitionType.CROSSFADE;
-        below_bin.hexpand = true;
-        below_bin.vexpand = true;
-        notification_overlay.add (below_bin);
-
-        below_bin.add_named (empty_boxes, "empty-boxes");
-        below_bin.add_named (view, "collection-view");
-
-        below_bin_hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
-        below_bin_hbox.halign = Gtk.Align.FILL;
-        below_bin_hbox.valign = Gtk.Align.FILL;
-        below_bin_hbox.hexpand = true;
-        below_bin_hbox.vexpand = true;
-
-        below_bin.add_named (below_bin_hbox, "below-bin-hbox");
-
-        content_bin = new Gtk.Stack ();
-        content_bin.transition_type = Gtk.StackTransitionType.SLIDE_LEFT;
-        content_bin.homogeneous = false;
-        content_bin.vexpand = true;
-        content_bin.hexpand = true;
-        content_bin.add (wizard);
-        content_bin.add (properties);
-
-        below_bin_hbox.add (sidebar);
-        below_bin_hbox.add (content_bin);
-        below_bin_hbox.show_all ();
-
-        below_bin.show_all ();
-
-        main_vbox.show_all ();
-
-        set_state (UIState.COLLECTION);
-    }
-
-    private void set_main_ui_state () {
-        stack.set_visible_child_name ("main-page");
-    }
-
     private void ui_state_changed () {
-        // The order is important for some widgets here (e.g properties must change its state before wizard 
so it can
-        // flush any deferred changes for wizard to pick-up when going back from properties to wizard 
(review).
-        foreach (var ui in new Boxes.UI[] { sidebar, topbar, view, properties, wizard, empty_boxes }) {
-            ui.set_state (ui_state);
-        }
-
-        if (ui_state != UIState.DISPLAY)
-            set_main_ui_state ();
+        window.set_state (ui_state);
 
-        if (ui_state != UIState.COLLECTION)
-            searchbar.search_mode_enabled = false;
-
-        switch (ui_state) {
-        case UIState.COLLECTION:
-            if (collection.items.length != 0)
-                below_bin.visible_child = view;
-            else
-                below_bin.visible_child = empty_boxes;
-            topbar.status = null;
+        if (ui_state == UIState.COLLECTION) {
             status_bind = null;
+            window.topbar.status = null;
             if (current_item is Machine) {
                 var machine = current_item as Machine;
                 if (got_error_id != 0) {
@@ -630,36 +412,6 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
 
                 machine.connecting_cancellable.cancel (); // Cancel any in-progress connections
             }
-            fullscreen = false;
-            view.visible = true;
-
-            break;
-
-        case UIState.CREDS:
-
-            break;
-
-        case UIState.WIZARD:
-            below_bin.visible_child = below_bin_hbox;
-            content_bin.visible_child = wizard;
-
-            break;
-
-        case UIState.PROPERTIES:
-            below_bin.visible_child = below_bin_hbox;
-            content_bin.visible_child = properties;
-
-            break;
-
-        case UIState.DISPLAY:
-            if (maximized)
-                fullscreen = true;
-
-            break;
-
-        default:
-            warning ("Unhandled UI state %s".printf (ui_state.to_string ()));
-            break;
         }
 
         if (current_item != null)
@@ -713,11 +465,11 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
     }
 
     public List<CollectionItem> selected_items {
-        owned get { return view.get_selected_items (); }
+        owned get { return window.view.get_selected_items (); }
     }
 
     public void show_properties () {
-        var selected_items = view.get_selected_items ();
+        var selected_items = window.view.get_selected_items ();
 
         selection_mode = false;
 
@@ -730,7 +482,7 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
     }
 
     public void remove_selected_items () {
-        var selected_items = view.get_selected_items ();
+        var selected_items = window.view.get_selected_items ();
         var num_selected = selected_items.length ();
         if (num_selected == 0)
             return;
@@ -761,39 +513,17 @@ private class Boxes.App: Gtk.Application, Boxes.UI {
             }
         };
 
-        notificationbar.display_for_action (message, _("_Undo"), (owned) undo, (owned) really_remove);
-    }
-
-    private bool on_key_pressed (Widget widget, Gdk.EventKey event) {
-        var default_modifiers = Gtk.accelerator_get_default_mod_mask ();
-
-        if (event.keyval == Gdk.Key.F11) {
-            fullscreen = !fullscreen;
-            return true;
-        } else if (event.keyval == Gdk.Key.Escape) {
-            if (selection_mode && ui_state == UIState.COLLECTION)
-               selection_mode = false;
-        } else if (event.keyval == Gdk.Key.q &&
-                   (event.state & default_modifiers) == Gdk.ModifierType.CONTROL_MASK) {
-            quit_app ();
-            return true;
-        } else if (event.keyval == Gdk.Key.a &&
-                   (event.state & default_modifiers) == Gdk.ModifierType.MOD1_MASK) {
-            quit_app ();
-            return true;
-        }
-
-        return false;
+        window.notificationbar.display_for_action (message, _("_Undo"), (owned) undo, (owned) really_remove);
     }
 
     public void connect_to (Machine machine) {
         current_item = machine;
 
         // Track machine status in toobar
-        status_bind = machine.bind_property ("status", topbar, "status", BindingFlags.SYNC_CREATE);
+        status_bind = machine.bind_property ("status", window.topbar, "status", BindingFlags.SYNC_CREATE);
 
         got_error_id = machine.got_error.connect ( (message) => {
-            App.app.notificationbar.display_error (message);
+            window.notificationbar.display_error (message);
         });
 
         if (ui_state != UIState.CREDS)
diff --git a/src/auth-notification.vala b/src/auth-notification.vala
index 2022f91..c91f1f9 100644
--- a/src/auth-notification.vala
+++ b/src/auth-notification.vala
@@ -39,14 +39,14 @@ private class Boxes.AuthNotification: Gd.Notification {
 
     [GtkCallback]
     private bool on_entry_focus_in_event () {
-        App.app.searchbar.enable_key_handler = false;
+        App.window.searchbar.enable_key_handler = false;
 
         return false;
     }
 
     [GtkCallback]
     private bool on_entry_focus_out_event () {
-        App.app.searchbar.enable_key_handler = true;
+        App.window.searchbar.enable_key_handler = true;
 
         return false;
     }
diff --git a/src/collection-view.vala b/src/collection-view.vala
index 3725a2d..175264c 100644
--- a/src/collection-view.vala
+++ b/src/collection-view.vala
@@ -33,7 +33,7 @@ private class Boxes.CollectionView: Gd.MainView, Boxes.UI {
     private Gtk.ListStore store;
     private Gtk.TreeModelFilter model_filter;
 
-    public CollectionView () {
+    construct {
         category = new Category (_("New and Recent"), Category.Kind.NEW);
         setup_view ();
         notify["ui-state"].connect (ui_state_changed);
diff --git a/src/display-page.vala b/src/display-page.vala
index 4ad35d7..0d22403 100644
--- a/src/display-page.vala
+++ b/src/display-page.vala
@@ -35,11 +35,11 @@ private class Boxes.DisplayPage: Gtk.Box {
     private ulong display_can_grab_id;
     private ulong display_grabbed_id;
 
-    public DisplayPage () {
-        overlay_toolbar_invisible_timeout = App.app.duration;
+    public void setup_ui () {
+        overlay_toolbar_invisible_timeout = App.window.duration;
         event_box.set_events (EventMask.POINTER_MOTION_MASK | EventMask.SCROLL_MASK);
 
-        App.app.window.window_state_event.connect ((event) => {
+        App.window.window_state_event.connect ((event) => {
             update_toolbar_visible ();
 
             return false;
@@ -54,7 +54,7 @@ private class Boxes.DisplayPage: Gtk.Box {
     }
 
      private void update_toolbar_visible() {
-         if (App.app.fullscreen && can_grab_mouse)
+         if (App.window.fullscreened && can_grab_mouse)
              toolbar.visible = true;
          else
              toolbar.visible = false;
@@ -122,7 +122,7 @@ private class Boxes.DisplayPage: Gtk.Box {
         Idle.add (() => {
             update_toolbar_visible ();
             overlay_toolbar_invisible_timeout = 1000; // 1 seconds
-            set_overlay_toolbar_visible (App.app.fullscreen);
+            set_overlay_toolbar_visible (App.window.fullscreened);
 
             return false;
         });
@@ -142,7 +142,7 @@ private class Boxes.DisplayPage: Gtk.Box {
             return false;
         });
 
-        App.app.page = Boxes.AppPage.DISPLAY;
+        App.window.page = Boxes.AppPage.DISPLAY;
         widget.grab_focus ();
     }
 
@@ -172,7 +172,7 @@ private class Boxes.DisplayPage: Gtk.Box {
 
     [GtkCallback]
     private bool on_event_box_event (Gdk.Event event) {
-        if (App.app.fullscreen && event.type == EventType.MOTION_NOTIFY) {
+        if (App.window.fullscreened && event.type == EventType.MOTION_NOTIFY) {
             var y = event.motion.y;
             if (y <= 0 && toolbar_show_id == 0) {
                 toolbar_event_stop ();
@@ -183,7 +183,7 @@ private class Boxes.DisplayPage: Gtk.Box {
                       ModifierType.BUTTON1_MASK | ModifierType.BUTTON2_MASK |
                       ModifierType.BUTTON3_MASK | ModifierType.BUTTON4_MASK |
                       ModifierType.BUTTON5_MASK)) == 0) {
-                    toolbar_show_id = Timeout.add (App.app.duration, () => {
+                    toolbar_show_id = Timeout.add (App.window.duration, () => {
                         set_overlay_toolbar_visible (true);
                         toolbar_show_id = 0;
                         return false;
@@ -194,7 +194,7 @@ private class Boxes.DisplayPage: Gtk.Box {
                 toolbar_hide_id = Timeout.add (overlay_toolbar_invisible_timeout, () => {
                     set_overlay_toolbar_visible (false);
                     toolbar_hide_id = 0;
-                    overlay_toolbar_invisible_timeout = App.app.duration;
+                    overlay_toolbar_invisible_timeout = App.window.duration;
                     return false;
                 });
             }
diff --git a/src/display-toolbar.vala b/src/display-toolbar.vala
index 263554c..cad2823 100644
--- a/src/display-toolbar.vala
+++ b/src/display-toolbar.vala
@@ -43,8 +43,8 @@ private class Boxes.DisplayToolbar: Gtk.HeaderBar {
             props.get_style_context ().add_class ("raised");
         }
 
-        App.app.notify["fullscreen"].connect_after ( () => {
-            if (App.app.fullscreen)
+        App.app.notify["fullscreened"].connect_after ( () => {
+            if (App.window.fullscreened)
                 fullscreen_image.icon_name = "view-restore-symbolic";
             else
                 fullscreen_image.icon_name = "view-fullscreen-symbolic";
@@ -88,14 +88,14 @@ private class Boxes.DisplayToolbar: Gtk.HeaderBar {
             // Break out when the dragged distance is 40 pixels
             if (dx * dx + dy * dy > 40 * 40) {
                 button_down = false;
-                App.app.fullscreen = false;
+                App.window.fullscreened = false;
 
                 var window = get_toplevel () as Gtk.Window;
                 int old_width;
                 window.get_size (out old_width, null);
 
                 ulong id = 0;
-                id = App.app.notify["fullscreen"].connect ( () => {
+                id = App.app.notify["fullscreened"].connect ( () => {
                     int root_x, root_y, width;
                     window.get_position (out root_x, out root_y);
                     window.get_window ().get_geometry (null, null, out width, null);
@@ -119,7 +119,7 @@ private class Boxes.DisplayToolbar: Gtk.HeaderBar {
 
     [GtkCallback]
     private void on_fullscreen_clicked () {
-        App.app.fullscreen = !App.app.fullscreen;
+        App.window.fullscreened = !App.window.fullscreened;
     }
 
     [GtkCallback]
diff --git a/src/empty-boxes.vala b/src/empty-boxes.vala
index a811caf..46c24d1 100644
--- a/src/empty-boxes.vala
+++ b/src/empty-boxes.vala
@@ -8,7 +8,7 @@ private class Boxes.EmptyBoxes : Gtk.Stack, Boxes.UI {
     [GtkChild]
     private Gtk.Box grid_box;
 
-    public EmptyBoxes () {
+    construct {
         App.app.call_when_ready (on_app_ready);
     }
 
@@ -28,8 +28,8 @@ private class Boxes.EmptyBoxes : Gtk.Stack, Boxes.UI {
             return;
 
         if (visible)
-            App.app.below_bin.set_visible_child_name ("empty-boxes");
+            App.window.below_bin.set_visible_child_name ("empty-boxes");
         else
-            App.app.below_bin.set_visible_child_name ("collection-view");
+            App.window.below_bin.set_visible_child_name ("collection-view");
     }
 }
diff --git a/src/libvirt-machine-properties.vala b/src/libvirt-machine-properties.vala
index 685cb55..3166bee 100644
--- a/src/libvirt-machine-properties.vala
+++ b/src/libvirt-machine-properties.vala
@@ -131,7 +131,7 @@ private class Boxes.LibvirtMachineProperties: GLib.Object, Boxes.IPropertiesProv
             button.clicked.connect (() => {
                 var log = collect_logs ();
                 var dialog = new Gtk.Dialog.with_buttons (_("Troubleshooting log"),
-                                                          App.app.window,
+                                                          App.window,
                                                           DialogFlags.DESTROY_WITH_PARENT,
                                                           _("_Save"), 100,
                                                           _("Copy to clipboard"), 101,
@@ -149,7 +149,7 @@ private class Boxes.LibvirtMachineProperties: GLib.Object, Boxes.IPropertiesProv
 
                 dialog.response.connect ( (response_id) => {
                     if (response_id == 100) {
-                        var chooser = new Gtk.FileChooserDialog (_("Save log"), App.app.window,
+                        var chooser = new Gtk.FileChooserDialog (_("Save log"), App.window,
                                                                  Gtk.FileChooserAction.SAVE,
                                                                  _("_Save"), ResponseType.OK);
                         chooser.local_only = false;
@@ -304,7 +304,7 @@ private class Boxes.LibvirtMachineProperties: GLib.Object, Boxes.IPropertiesProv
         button.clicked.connect ( () => {
             if (empty) {
                 var dialog = new Gtk.FileChooserDialog (_("Select a device or ISO file"),
-                                                        App.app.window,
+                                                        App.window,
                                                         Gtk.FileChooserAction.OPEN,
                                                         _("_Cancel"), Gtk.ResponseType.CANCEL,
                                                         _("_Open"), Gtk.ResponseType.ACCEPT);
diff --git a/src/libvirt-machine.vala b/src/libvirt-machine.vala
index e0f093d..414ee61 100644
--- a/src/libvirt-machine.vala
+++ b/src/libvirt-machine.vala
@@ -439,7 +439,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
 
     public void force_shutdown (bool confirm = true) {
         if (confirm) {
-            var dialog = new Gtk.MessageDialog (App.app.window,
+            var dialog = new Gtk.MessageDialog (App.window,
                                                 Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                                 Gtk.MessageType.QUESTION,
                                                 Gtk.ButtonsType.NONE,
@@ -599,7 +599,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
             };
 
             var message = _("Restart of '%s' is taking too long. Force it to shutdown?").printf (name);
-            notification = App.app.notificationbar.display_for_action (message,
+            notification = App.window.notificationbar.display_for_action (message,
                                                                        _("_Shutdown"),
                                                                        (owned) really_force_shutdown,
                                                                        null,
diff --git a/src/machine.vala b/src/machine.vala
index 7d15dcc..dcc50c7 100644
--- a/src/machine.vala
+++ b/src/machine.vala
@@ -90,7 +90,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
             var widget = display.get_display (0);
             widget_remove (widget);
             display.set_enable_inputs (widget, true);
-            App.app.display_page.show_display (display, widget);
+            App.window.display_page.show_display (display, widget);
             widget.grab_focus ();
 
             break;
@@ -126,7 +126,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
             show_id = _display.show.connect ((id) => { show_display (); });
 
             hide_id = _display.hide.connect ((id) => {
-                App.app.display_page.remove_display ();
+                App.window.display_page.remove_display ();
             });
 
             got_error_id = _display.got_error.connect ((message) => {
@@ -138,7 +138,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
                 if (!stay_on_display && App.app.current_item == this)
                     App.app.set_state (Boxes.UIState.COLLECTION);
                 if (failed)
-                    App.app.notificationbar.display_error (_("Connection to '%s' failed").printf (name));
+                    App.window.notificationbar.display_error (_("Connection to '%s' failed").printf (name));
             });
 
             need_password_id = _display.notify["need-password"].connect (handle_auth);
@@ -193,7 +193,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
             if (screenshot_id != 0)
                 return;
             update_screenshot.begin (false, true);
-            var interval = App.app.settings.get_int ("screenshot-interval");
+            var interval = App.window.settings.get_int ("screenshot-interval");
             screenshot_id = Timeout.add_seconds (interval, () => {
                 update_screenshot.begin ();
 
@@ -270,7 +270,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
             }
         }
 
-        App.app.display_page.remove_display ();
+        App.window.display_page.remove_display ();
         if (!display.should_keep_alive ()) {
             display.disconnect_it ();
             display = null;
@@ -368,7 +368,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
 
             orig_pixbuf = small_screenshot;
             pixbuf = draw_vm (small_screenshot, SCREENSHOT_WIDTH, SCREENSHOT_HEIGHT);
-            App.app.sidebar.screenshot.set_from_pixbuf (pixbuf);
+            App.window.sidebar.screenshot.set_from_pixbuf (pixbuf);
             if (save)
                 save_pixbuf_as_screenshot (small_screenshot);
 
@@ -500,7 +500,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
             break;
         case Boxes.UIState.DISPLAY:
             if (previous_ui_state == UIState.PROPERTIES)
-                App.app.page = Boxes.AppPage.DISPLAY;
+                App.window.page = Boxes.AppPage.DISPLAY;
 
             break;
 
@@ -518,7 +518,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
             yield connect_display (flags);
         } catch (Boxes.Error.RESTORE_FAILED e) {
             var message = _("'%s' could not be restored from disk\nTry without saved state?").printf (name);
-            var notification = App.app.notificationbar.display_for_action (message, _("Restart"), () => {
+            var notification = App.window.notificationbar.display_for_action (message, _("Restart"), () => {
                 try_connect_display.begin (flags | Machine.ConnectFlags.IGNORE_SAVED_STATE);
             });
             notification.dismissed.connect (() => {
@@ -527,7 +527,7 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
         } catch (GLib.Error e) {
             debug ("connect display failed: %s", e.message);
             App.app.set_state (UIState.COLLECTION);
-            App.app.notificationbar.display_error (_("Connection to '%s' failed").printf (name));
+            App.window.notificationbar.display_error (_("Connection to '%s' failed").printf (name));
         }
     }
 
@@ -557,10 +557,10 @@ private abstract class Boxes.Machine: Boxes.CollectionItem, Boxes.IPropertiesPro
 
         // Translators: %s => name of launched box
         var auth_string = _("'%s' requires authentication").printf (name);
-        auth_notification = App.app.notificationbar.display_for_auth (auth_string,
-                                                                      (owned) auth_func,
-                                                                      (owned) cancel_func,
-                                                                      need_username);
+        auth_notification = App.window.notificationbar.display_for_auth (auth_string,
+                                                                         (owned) auth_func,
+                                                                         (owned) cancel_func,
+                                                                         need_username);
     }
 
     public override int compare (CollectionItem other) {
diff --git a/src/notificationbar.vala b/src/notificationbar.vala
index d90cdf3..51a2fc4 100644
--- a/src/notificationbar.vala
+++ b/src/notificationbar.vala
@@ -6,7 +6,7 @@ private class Boxes.Notificationbar: Gtk.Grid {
 
     GLib.List<Widget> active_notifications;
 
-    public Notificationbar () {
+    construct {
         valign = Gtk.Align.START;
         halign = Gtk.Align.CENTER;
         get_style_context ().add_class ("boxes-bg");
@@ -78,10 +78,10 @@ private class Boxes.Notificationbar: Gtk.Grid {
     }
 
     private void add_notification (Widget w) {
-        if (App.app.page == AppPage.MAIN)
+        if (App.window.page == AppPage.MAIN)
             attach (w, 0, 0, 1, 1);
         else
-            App.app.display_page.add_notification (w);
+            App.window.display_page.add_notification (w);
     }
 
     private Gd.Notification display (string                         message,
diff --git a/src/ovirt-broker.vala b/src/ovirt-broker.vala
index 2b5940b..d0114ab 100644
--- a/src/ovirt-broker.vala
+++ b/src/ovirt-broker.vala
@@ -55,7 +55,7 @@ private class Boxes.OvirtBroker : Boxes.Broker {
                 // finish, otherwise yield add_source() will never return
                 auth.unpause ();
             };
-            App.app.notificationbar.display_for_optional_auth ("oVirt broker", (owned) auth_cb, (owned) 
cancel_cb);
+            App.window.notificationbar.display_for_optional_auth ("oVirt broker", (owned) auth_cb, (owned) 
cancel_cb);
             auth.pause ();
 
             return false;
@@ -66,7 +66,7 @@ private class Boxes.OvirtBroker : Boxes.Broker {
             yield proxy.fetch_vms_async (null);
         } catch (GLib.Error error) {
             debug ("Failed to connect to broker: %s", error.message);
-            App.app.notificationbar.display_error (_("Connection to oVirt broker failed"));
+            App.window.notificationbar.display_error (_("Connection to oVirt broker failed"));
         }
         proxies.insert (source.name, proxy);
 
diff --git a/src/properties.vala b/src/properties.vala
index fa0889b..d988f12 100644
--- a/src/properties.vala
+++ b/src/properties.vala
@@ -101,7 +101,7 @@ private class Boxes.Properties: Gtk.Notebook, Boxes.UI {
         }
     }
 
-    public Properties () {
+    construct {
         notify["ui-state"].connect (ui_state_changed);
         setup_ui ();
     }
@@ -115,14 +115,14 @@ private class Boxes.Properties: Gtk.Notebook, Boxes.UI {
     }
 
     private void populate () {
-        App.app.sidebar.props_listmodel.clear ();
+        App.window.sidebar.props_listmodel.clear ();
         for (var i = 0; i < PropertiesPage.LAST; i++)
             remove_page (-1);
 
         var machine = App.app.current_item as Machine;
         var libvirt_machine = App.app.current_item as LibvirtMachine;
 
-        App.app.sidebar.shutdown_button.sensitive = libvirt_machine != null && libvirt_machine.is_running ();
+        App.window.sidebar.shutdown_button.sensitive = libvirt_machine != null && libvirt_machine.is_running 
();
 
         if (machine == null)
             return;
@@ -136,11 +136,11 @@ private class Boxes.Properties: Gtk.Notebook, Boxes.UI {
                 var current_page = page;
                 this.populate ();
                 var path = new Gtk.TreePath.from_indices (current_page);
-                App.app.sidebar.props_selection.select_path (path);
+                App.window.sidebar.props_selection.select_path (path);
                 page = current_page;
             });
 
-            list_append (App.app.sidebar.props_listmodel, page.name, !page.empty);
+            list_append (App.window.sidebar.props_listmodel, page.name, !page.empty);
         }
 
         PropertiesPage current_page;
@@ -151,7 +151,7 @@ private class Boxes.Properties: Gtk.Notebook, Boxes.UI {
             current_page = PropertiesPage.LOGIN;
 
         var path = new Gtk.TreePath.from_indices (current_page);
-        App.app.sidebar.props_selection.select_path (path);
+        App.window.sidebar.props_selection.select_path (path);
         set_current_page (current_page);
     }
 
@@ -170,15 +170,15 @@ private class Boxes.Properties: Gtk.Notebook, Boxes.UI {
         }
 
         if (ui_state == UIState.PROPERTIES) {
-            restore_fullscreen = (previous_ui_state == UIState.DISPLAY && App.app.fullscreen);
-            App.app.fullscreen = false;
+            restore_fullscreen = (previous_ui_state == UIState.DISPLAY && App.window.fullscreened);
+            App.window.fullscreened = false;
 
             if (App.app.current_item is LibvirtMachine) {
                 var libvirt_machine = App.app.current_item as LibvirtMachine;
                 stats_id = libvirt_machine.stats_updated.connect (() => {
-                    App.app.sidebar.cpu_graph.points = libvirt_machine.cpu_stats;
-                    App.app.sidebar.net_graph.points = libvirt_machine.net_stats;
-                    App.app.sidebar.io_graph.points = libvirt_machine.io_stats;
+                    App.window.sidebar.cpu_graph.points = libvirt_machine.cpu_stats;
+                    App.window.sidebar.net_graph.points = libvirt_machine.net_stats;
+                    App.window.sidebar.io_graph.points = libvirt_machine.io_stats;
                 });
             }
 
@@ -194,13 +194,13 @@ private class Boxes.Properties: Gtk.Notebook, Boxes.UI {
             var machine = App.app.current_item as Machine;
             if (reboot_required && (machine.is_on () || machine.state == Machine.MachineState.SAVED)) {
                 var message = _("Changes require restart of '%s'.").printf (machine.name);
-                App.app.notificationbar.display_for_action (message, _("_Restart"), () => {
+                App.window.notificationbar.display_for_action (message, _("_Restart"), () => {
                     machine.restart ();
                 });
             }
 
             if (restore_fullscreen) {
-                App.app.fullscreen = true;
+                App.window.fullscreened = true;
                 restore_fullscreen = false;
             }
         }
diff --git a/src/searchbar.vala b/src/searchbar.vala
index a3dc329..c669d1f 100644
--- a/src/searchbar.vala
+++ b/src/searchbar.vala
@@ -5,9 +5,9 @@ private class Boxes.Searchbar: Gtk.SearchBar {
     public bool enable_key_handler {
         set {
             if (value)
-                GLib.SignalHandler.unblock (App.app.window, key_handler_id);
+                GLib.SignalHandler.unblock (App.window, key_handler_id);
             else
-                GLib.SignalHandler.block (App.app.window, key_handler_id);
+                GLib.SignalHandler.block (App.window, key_handler_id);
         }
     }
     [GtkChild]
@@ -15,7 +15,7 @@ private class Boxes.Searchbar: Gtk.SearchBar {
 
     private ulong key_handler_id;
 
-    public Searchbar () {
+    construct {
         search_mode_enabled = false;
 
         App.app.call_when_ready (on_app_ready);
@@ -24,12 +24,12 @@ private class Boxes.Searchbar: Gtk.SearchBar {
     [GtkCallback]
     private void on_search_changed () {
         App.app.filter.text = text;
-        App.app.view.refilter ();
+        App.window.view.refilter ();
     }
 
     [GtkCallback]
     private void on_search_activated () {
-        App.app.view.activate_first_item ();
+        App.window.view.activate_first_item ();
     }
 
     [GtkCallback]
@@ -44,7 +44,7 @@ private class Boxes.Searchbar: Gtk.SearchBar {
     }
 
     private void on_app_ready () {
-        key_handler_id = App.app.window.key_press_event.connect (on_app_key_pressed);
+        key_handler_id = App.window.key_press_event.connect (on_app_key_pressed);
     }
 
     private bool on_app_key_pressed (Gtk.Widget widget, Gdk.EventKey event) {
diff --git a/src/selectionbar.vala b/src/selectionbar.vala
index d605cbf..4e6d902 100644
--- a/src/selectionbar.vala
+++ b/src/selectionbar.vala
@@ -12,7 +12,7 @@ private class Boxes.Selectionbar: Gtk.Revealer {
     [GtkChild]
     private Gtk.Button properties_btn;
 
-    public Selectionbar () {
+    construct {
         App.app.notify["selection-mode"].connect (() => {
             reveal_child = App.app.selection_mode;
         });
@@ -51,7 +51,7 @@ private class Boxes.Selectionbar: Gtk.Revealer {
                 try {
                     machine.save.end (result);
                 } catch (GLib.Error e) {
-                    App.app.notificationbar.display_error (_("Pausing '%s' failed").printf (machine.name));
+                    App.window.notificationbar.display_error (_("Pausing '%s' failed").printf 
(machine.name));
                 }
             });
         }
diff --git a/src/sidebar.vala b/src/sidebar.vala
index e463a08..3d6c443 100644
--- a/src/sidebar.vala
+++ b/src/sidebar.vala
@@ -45,7 +45,7 @@ private class Boxes.Sidebar: Gtk.Revealer, Boxes.UI {
     [GtkChild]
     public MiniGraph net_graph;
 
-    public Sidebar () {
+    construct {
         notify["ui-state"].connect (ui_state_changed);
         setup_sidebar ();
     }
@@ -98,7 +98,7 @@ private class Boxes.Sidebar: Gtk.Revealer, Boxes.UI {
         Gtk.TreeIter filter_iter, iter;
         props_model_filter.get_iter (out filter_iter, path);
         props_model_filter.convert_iter_to_child_iter (out iter, filter_iter);
-        App.app.properties.page = props_listmodel.get_path (iter).get_indices ()[0];
+        App.window.properties.page = props_listmodel.get_path (iter).get_indices ()[0];
     }
 
     [GtkCallback]
diff --git a/src/topbar.vala b/src/topbar.vala
index 2bb2ae7..b2be1e7 100644
--- a/src/topbar.vala
+++ b/src/topbar.vala
@@ -68,24 +68,24 @@ private class Boxes.Topbar: Gtk.Notebook, Boxes.UI {
         }
     }
 
-    public Topbar () {
+    construct {
         notify["ui-state"].connect (ui_state_changed);
 
-        setup_topbar ();
-
         App.app.notify["selected-items"].connect (() => {
             update_selection_label ();
         });
     }
 
-    private void setup_topbar () {
+    public void setup_ui () {
         var back_icon = (get_direction () == Gtk.TextDirection.RTL)? "go-previous-rtl-symbolic" :
                                                                      "go-previous-symbolic";
         back_image.set_from_icon_name (back_icon, Gtk.IconSize.MENU);
         props_back_image.set_from_icon_name (back_icon, Gtk.IconSize.MENU);
 
-        search_btn.bind_property ("active", App.app.searchbar, "search-mode-enabled", 
BindingFlags.BIDIRECTIONAL);
-        search2_btn.bind_property ("active", App.app.searchbar, "search-mode-enabled", 
BindingFlags.BIDIRECTIONAL);
+        assert (App.window != null);
+        assert (App.window.searchbar != null);
+        search_btn.bind_property ("active", App.window.searchbar, "search-mode-enabled", 
BindingFlags.BIDIRECTIONAL);
+        search2_btn.bind_property ("active", App.window.searchbar, "search-mode-enabled", 
BindingFlags.BIDIRECTIONAL);
 
         App.app.notify["selection-mode"].connect (() => {
             page = App.app.selection_mode ?
@@ -96,7 +96,7 @@ private class Boxes.Topbar: Gtk.Notebook, Boxes.UI {
         App.app.collection.item_removed.connect (update_select_btn);
         update_selection_label ();
 
-        var toolbar = App.app.display_page.toolbar;
+        var toolbar = App.window.display_page.toolbar;
         toolbar.bind_property ("title", display_toolbar, "title", BindingFlags.SYNC_CREATE);
         toolbar.bind_property ("subtitle", display_toolbar, "subtitle", BindingFlags.SYNC_CREATE);
 
diff --git a/src/vm-creator.vala b/src/vm-creator.vala
index c2a1de4..de0dbe3 100644
--- a/src/vm-creator.vala
+++ b/src/vm-creator.vala
@@ -45,7 +45,7 @@ private class Boxes.VMCreator {
         try {
             yield install_media.prepare_for_installation (name, cancellable);
         } catch (GLib.Error error) {
-            App.app.notificationbar.display_error (_("An error occurred during installation preparation. 
Express Install disabled."));
+            App.window.notificationbar.display_error (_("An error occurred during installation preparation. 
Express Install disabled."));
             debug("Disabling unattended installation: %s", error.message);
         }
 
diff --git a/src/vm-importer.vala b/src/vm-importer.vala
index a53232c..f01c918 100644
--- a/src/vm-importer.vala
+++ b/src/vm-importer.vala
@@ -51,7 +51,7 @@ private class Boxes.VMImporter : Boxes.VMCreator {
                      source_media.device_file,
                      error.message);
             var ui_message = _("Box import from file '%s' failed.").printf (source_media.device_file);
-            App.app.notificationbar.display_error (ui_message);
+            App.window.notificationbar.display_error (ui_message);
             machine.delete ();
 
             return;
diff --git a/src/wizard-source.vala b/src/wizard-source.vala
index 3cb2c0f..c66e3b1 100644
--- a/src/wizard-source.vala
+++ b/src/wizard-source.vala
@@ -241,7 +241,7 @@ private class Boxes.WizardSource: Gtk.Notebook {
     [GtkCallback]
     private void on_select_file_button_clicked () {
         var dialog = new Gtk.FileChooserDialog (_("Select a device or ISO file"),
-                                                App.app.window,
+                                                App.window,
                                                 Gtk.FileChooserAction.OPEN,
                                                 _("_Cancel"), Gtk.ResponseType.CANCEL,
                                                 _("_Open"), Gtk.ResponseType.ACCEPT);
diff --git a/src/wizard.vala b/src/wizard.vala
index 89b6c34..185b478 100644
--- a/src/wizard.vala
+++ b/src/wizard.vala
@@ -122,11 +122,7 @@ private class Boxes.Wizard: Gtk.Notebook, Boxes.UI {
 
         base.switch_page (page_widget, page_num);
 
-        App.app.sidebar.set_wizard_page ((WizardPage) page_num);
-    }
-
-    construct {
-        media_manager = MediaManager.get_instance ();
+        App.window.sidebar.set_wizard_page ((WizardPage) page_num);
     }
 
     private void wizard_source_update_next () {
@@ -168,7 +164,8 @@ private class Boxes.Wizard: Gtk.Notebook, Boxes.UI {
         }
     }
 
-    public Wizard () {
+   construct {
+        media_manager = MediaManager.get_instance ();
         wizard_source.notify["page"].connect(wizard_source_update_next);
         wizard_source.notify["selected"].connect(wizard_source_update_next);
         wizard_source.url_entry.changed.connect (wizard_source_update_next);
@@ -181,7 +178,6 @@ private class Boxes.Wizard: Gtk.Notebook, Boxes.UI {
         // Changing page to something other than INTRODUCTION here so that Gtk.Notebook doesn't ignore us 
setting
         // it to INTRODUCTION later (also read the comment in switch_page method above).
         page = WizardPage.PREPARATION;
-        setup_ui ();
     }
 
     public void cleanup () {
@@ -292,7 +288,7 @@ private class Boxes.Wizard: Gtk.Notebook, Boxes.UI {
         } catch (IOError.CANCELLED cancel_error) { // We did this, so no warning!
         } catch (GLib.Error error) {
             debug("Failed to analyze installer image: %s", error.message);
-            App.app.notificationbar.display_error (_("Failed to analyze installer media. Corrupted or 
incomplete media?"));
+            App.window.notificationbar.display_error (_("Failed to analyze installer media. Corrupted or 
incomplete media?"));
             page = WizardPage.SOURCE;
         }
     }
@@ -331,7 +327,7 @@ private class Boxes.Wizard: Gtk.Notebook, Boxes.UI {
             try {
                 prepare_for_location (this.wizard_source.uri);
             } catch (GLib.Error error) {
-                App.app.notificationbar.display_error (error.message);
+                App.window.notificationbar.display_error (error.message);
 
                 return false;
             }
@@ -390,7 +386,7 @@ private class Boxes.Wizard: Gtk.Notebook, Boxes.UI {
                 machine = yield vm_creator.create_vm (review_cancellable);
             } catch (IOError.CANCELLED cancel_error) { // We did this, so ignore!
             } catch (GLib.Error error) {
-                App.app.notificationbar.display_error (_("Box setup failed"));
+                App.window.notificationbar.display_error (_("Box setup failed"));
                 warning (error.message);
             }
 
@@ -523,29 +519,29 @@ private class Boxes.Wizard: Gtk.Notebook, Boxes.UI {
         return false;
     }
 
-    private void setup_ui () {
-        cancel_button = App.app.topbar.wizard_cancel_btn;
+    public void setup_ui () {
+        cancel_button = App.window.topbar.wizard_cancel_btn;
         cancel_button.clicked.connect (() => {
             destroy_machine ();
             vm_creator = null;
             wizard_source.page = SourcePage.MAIN;
             App.app.set_state (UIState.COLLECTION);
         });
-        back_button = App.app.topbar.wizard_back_btn;
+        back_button = App.window.topbar.wizard_back_btn;
         back_button.clicked.connect (() => {
             page = page - 1;
         });
-        continue_button = App.app.topbar.wizard_continue_btn;
+        continue_button = App.window.topbar.wizard_continue_btn;
         continue_button.clicked.connect (() => {
             page = page + 1;
         });
-        create_button = App.app.topbar.wizard_create_btn;
+        create_button = App.window.topbar.wizard_create_btn;
         create_button.clicked.connect (() => {
             create.begin ((obj, result) => {
             if (create.end (result))
                 App.app.set_state (UIState.COLLECTION);
             else
-                App.app.notificationbar.display_error (_("Box creation failed"));
+                App.window.notificationbar.display_error (_("Box creation failed"));
             });
         });
     }


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