From 4f080b9d3db259b87c46113a4a34cf120519ac80 Mon Sep 17 00:00:00 2001
From: Don Gagne <don@thegagnes.com>
Date: Tue, 8 Dec 2015 11:36:54 -0800
Subject: [PATCH] Mobile runs Native QML

---
 QGCCommon.pri                                |   1 -
 qgroundcontrol.pro                           |   4 +-
 qgroundcontrol.qrc                           |   4 +-
 src/AutoPilotPlugins/AutoPilotPlugin.cc      |   3 +-
 src/QGCApplication.cc                        |  91 +++++-
 src/QGCApplication.h                         |  23 +-
 src/QmlControls/ParameterEditorController.cc |   2 +-
 src/QmlControls/QGroundControlQmlGlobal.cc   |   6 -
 src/QmlControls/QGroundControlQmlGlobal.h    |   5 -
 src/QmlControls/ScreenToolsController.cc     |   1 -
 src/VehicleSetup/SetupViewTest.cc            |  10 +-
 src/comm/LinkManager.cc                      |  12 +-
 src/comm/LinkManager.h                       |   2 +-
 src/main.cc                                  |   1 -
 src/ui/MainWindow.cc                         |  20 +-
 src/ui/MainWindow.h                          |  25 +-
 src/ui/MainWindow.qml                        | 424 --------------------------
 src/ui/MainWindowHybrid.qml                  |  75 +++++
 src/ui/MainWindowInner.qml                   | 438 +++++++++++++++++++++++++++
 src/ui/MainWindowNative.qml                  |  90 ++++++
 src/ui/preferences/GeneralSettings.qml       |   9 -
 src/ui/toolbar/MainToolBar.qml               |  35 ++-
 src/ui/toolbar/MainToolBarController.cc      |  16 -
 src/ui/toolbar/MainToolBarController.h       |   4 -
 24 files changed, 754 insertions(+), 547 deletions(-)
 delete mode 100644 src/ui/MainWindow.qml
 create mode 100644 src/ui/MainWindowHybrid.qml
 create mode 100644 src/ui/MainWindowInner.qml
 create mode 100644 src/ui/MainWindowNative.qml

diff --git a/QGCCommon.pri b/QGCCommon.pri
index ef571bd..52d71ef 100644
--- a/QGCCommon.pri
+++ b/QGCCommon.pri
@@ -37,7 +37,6 @@ linux {
         DEFINES += __android__
         DEFINES += __STDC_LIMIT_MACROS
         target.path = $$DESTDIR
-        warning("Android build is experimental and not fully functional")
     } else {
         error("Unsuported Linux toolchain, only GCC 32- or 64-bit is supported")
     }
diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro
index c4f265c..98dc921 100644
--- a/qgroundcontrol.pro
+++ b/qgroundcontrol.pro
@@ -278,7 +278,6 @@ HEADERS += \
     src/uas/UAS.h \
     src/uas/UASInterface.h \
     src/uas/UASMessageHandler.h \
-    src/ui/MainWindow.h \
     src/ui/toolbar/MainToolBarController.h \
     src/AutoPilotPlugins/PX4/PX4AirframeLoader.h \
     src/QmlControls/QGCImageProvider.h \
@@ -327,6 +326,7 @@ HEADERS += \
     src/ui/linechart/Scrollbar.h \
     src/ui/linechart/ScrollZoomer.h \
     src/ui/LogReplayLinkConfigurationWidget.h \
+    src/ui/MainWindow.h \
     src/ui/MAVLinkDecoder.h \
     src/ui/MAVLinkSettingsWidget.h \
     src/ui/MultiVehicleDockWidget.h \
@@ -400,7 +400,6 @@ SOURCES += \
     src/QmlControls/QmlObjectListModel.cc \
     src/uas/UAS.cc \
     src/uas/UASMessageHandler.cc \
-    src/ui/MainWindow.cc \
     src/ui/toolbar/MainToolBarController.cc \
     src/AutoPilotPlugins/PX4/PX4AirframeLoader.cc \
     src/QmlControls/QGCImageProvider.cc \
@@ -453,6 +452,7 @@ SOURCES += \
     src/ui/linechart/LinechartWidget.cc \
     src/ui/linechart/Scrollbar.cc \
     src/ui/linechart/ScrollZoomer.cc \
+    src/ui/MainWindow.cc \
     src/ui/MultiVehicleDockWidget.cc \
     src/ui/QGCDataPlot2D.cc \
     src/ui/QGCHilConfiguration.cc \
diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc
index f6e741b..54bf367 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -32,7 +32,9 @@
 
         <file alias="JoystickConfig.qml">src/VehicleSetup/JoystickConfig.qml</file>
         <file alias="MainToolBar.qml">src/ui/toolbar/MainToolBar.qml</file>
-        <file alias="MainWindow.qml">src/ui/MainWindow.qml</file>
+        <file alias="MainWindowHybrid.qml">src/ui/MainWindowHybrid.qml</file>
+        <file alias="MainWindowInner.qml">src/ui/MainWindowInner.qml</file>
+        <file alias="MainWindowNative.qml">src/ui/MainWindowNative.qml</file>
         <file alias="MainWindowLeftPanel.qml">src/ui/MainWindowLeftPanel.qml</file>
         <file alias="MissionEditor.qml">src/MissionEditor/MissionEditor.qml</file>
         <file alias="MissionEditorHelp.qml">src/MissionEditor/MissionEditorHelp.qml</file>
diff --git a/src/AutoPilotPlugins/AutoPilotPlugin.cc b/src/AutoPilotPlugins/AutoPilotPlugin.cc
index d44f95d..dd92394 100644
--- a/src/AutoPilotPlugins/AutoPilotPlugin.cc
+++ b/src/AutoPilotPlugins/AutoPilotPlugin.cc
@@ -26,7 +26,6 @@
 
 #include "AutoPilotPlugin.h"
 #include "QGCApplication.h"
-#include "MainWindow.h"
 #include "ParameterLoader.h"
 #include "UAS.h"
 #include "FirmwarePlugin.h"
@@ -65,7 +64,7 @@ void AutoPilotPlugin::_parametersReadyChanged(bool parametersReady)
             qgcApp()->showMessage("One or more vehicle components require setup prior to flight.");
 			
 			// Take the user to Vehicle Summary
-            MainWindow::instance()->showSetupView();
+            qgcApp()->showSetupView();
 			qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
 		}
 	}
diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc
index 50ee6e7..a0462f6 100644
--- a/src/QGCApplication.cc
+++ b/src/QGCApplication.cc
@@ -43,10 +43,8 @@
 
 #include "QGC.h"
 #include "QGCApplication.h"
-#include "MainWindow.h"
 #include "GAudioOutput.h"
 #include "CmdLineOptParser.h"
-#include "MainWindow.h"
 #include "UDPLink.h"
 #include "LinkManager.h"
 #include "HomePositionManager.h"
@@ -101,6 +99,7 @@
     #include "QGCMessageBox.h"
     #include "FirmwareUpgradeController.h"
     #include "JoystickConfigController.h"
+    #include "MainWindow.h"
 #endif
 
 #ifdef QGC_RTLAB_ENABLED
@@ -154,7 +153,12 @@ static QObject* qgroundcontrolQmlGlobalSingletonFactory(QQmlEngine*, QJSEngine*)
  **/
 
 QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
+#ifdef __mobile__
+    : QGuiApplication(argc, argv)
+    , _qmlAppEngine(NULL)
+#else
     : QApplication(argc, argv)
+#endif
     , _runningUnitTests(unitTesting)
 #if defined (__mobile__)
     , _styleIsDark(false)
@@ -323,10 +327,12 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
 
 QGCApplication::~QGCApplication()
 {
+#ifndef __mobile__
     MainWindow* mainWindow = MainWindow::instance();
     if (mainWindow) {
         delete mainWindow;
     }
+#endif
     shutdownVideoStreaming();
     delete _toolbox;
 }
@@ -446,18 +452,23 @@ bool QGCApplication::_initForNormalAppBoot(void)
     // Exit main application when last window is closed
     connect(this, SIGNAL(lastWindowClosed()), this, SLOT(quit()));
 
+#ifdef __mobile__
+    _qmlAppEngine = new QQmlApplicationEngine(this);
+    _qmlAppEngine->addImportPath("qrc:/qml");
+    _qmlAppEngine->rootContext()->setContextProperty("multiVehicleManager", toolbox()->multiVehicleManager());
+    _qmlAppEngine->rootContext()->setContextProperty("joystickManager", toolbox()->joystickManager());
+    _qmlAppEngine->load(QUrl(QStringLiteral("qrc:/qml/MainWindowNative.qml")));
+#else
     // Start the user interface
     MainWindow* mainWindow = MainWindow::_create();
     Q_CHECK_PTR(mainWindow);
 
-#ifndef __mobile__
     // If we made it this far and we still don't have a location. Either the specfied location was invalid
     // or we coudn't create a default location. Either way, we need to let the user know and prompt for a new
     /// settings.
     QString savedFilesLocation = settings.value(_savedFilesLocationKey).toString();
     if (savedFilesLocation.isEmpty()) {
         showMessage("The location to save files to is invalid, or cannot be written to. Please provide a new one.");
-        mainWindow->showSettings();
     }
 
     // Now that main window is up check for lost log files
@@ -653,12 +664,10 @@ void QGCApplication::setStyle(bool styleIsDark)
 
 void QGCApplication::_loadCurrentStyle(void)
 {
+#ifndef __mobile__
     bool success = true;
     QString styles;
 
-    // Signal to the user that the app will pause to apply a new stylesheet
-    setOverrideCursor(Qt::WaitCursor);
-
     // The dark style sheet is the master. Any other selected style sheet just overrides
     // the colors of the master sheet.
     QFile masterStyleSheet(_darkStyleFile);
@@ -687,11 +696,9 @@ void QGCApplication::_loadCurrentStyle(void)
         // Fall back to plastique if we can't load our own
         setStyle("plastique");
     }
+#endif
 
     QGCPalette::setGlobalTheme(_styleIsDark ? QGCPalette::Dark : QGCPalette::Light);
-
-    // Finally restore the cursor before returning.
-    restoreOverrideCursor();
 }
 
 void QGCApplication::reportMissingParameter(int componentId, const QString& name)
@@ -718,12 +725,64 @@ void QGCApplication::_missingParamsDisplay(void)
     showMessage(QString("Parameters missing from firmware: %1.\n\nYou should quit QGroundControl immediately and update your firmware.").arg(params));
 }
 
+QObject* QGCApplication::_rootQmlObject(void)
+{
+#ifdef __mobile__
+    return _qmlAppEngine->rootObjects()[0];
+#else
+    return MainWindow::instance()->rootQmlObject();
+#endif
+}
+
+
 void QGCApplication::showMessage(const QString& message)
 {
-    MainWindow* mainWindow = MainWindow::instance();
-    if (mainWindow) {
-        mainWindow->showMessage(message);
-    } else {
-        qWarning() << "showMessage with no mainWindow" << message;
-    }
+    QVariant varReturn;
+    QVariant varMessage = QVariant::fromValue(message);
+
+    QMetaObject::invokeMethod(_rootQmlObject(), "showMessage", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varMessage));
+}
+
+void QGCApplication::showFlyView(void)
+{
+    QMetaObject::invokeMethod(_rootQmlObject(), "showFlyView");
+}
+
+void QGCApplication::showPlanView(void)
+{
+    QMetaObject::invokeMethod(_rootQmlObject(), "showPlanView");
+}
+
+void QGCApplication::showSetupView(void)
+{
+    QMetaObject::invokeMethod(_rootQmlObject(), "showSetupView");
+}
+
+void QGCApplication::showWindowCloseMessage(void)
+{
+    QMetaObject::invokeMethod(_rootQmlObject(), "showWindowCloseMessage");
+}
+
+
+void QGCApplication::_showSetupFirmware(void)
+{
+    QMetaObject::invokeMethod(_rootQmlObject(), "showSetupFirmware");
+}
+
+void QGCApplication::_showSetupParameters(void)
+{
+    QMetaObject::invokeMethod(_rootQmlObject(), "showSetupParameters");
+}
+
+void QGCApplication::_showSetupSummary(void)
+{
+    QMetaObject::invokeMethod(_rootQmlObject(), "showSetupSummary");
+}
+
+void QGCApplication::_showSetupVehicleComponent(VehicleComponent* vehicleComponent)
+{
+    QVariant varReturn;
+    QVariant varComponent = QVariant::fromValue(vehicleComponent);
+
+    QMetaObject::invokeMethod(_rootQmlObject(), "showSetupVehicleComponent", Q_RETURN_ARG(QVariant, varReturn), Q_ARG(QVariant, varComponent));
 }
diff --git a/src/QGCApplication.h b/src/QGCApplication.h
index 3d0e62d..4928039 100644
--- a/src/QGCApplication.h
+++ b/src/QGCApplication.h
@@ -64,7 +64,12 @@ class QGCToolbox;
  * the central management unit of the groundstation application.
  *
  **/
-class QGCApplication : public QApplication
+class QGCApplication : public
+#ifdef __mobile__
+    QGuiApplication // Native Qml based application
+#else
+    QApplication    // QtWidget based application
+#endif
 {
     Q_OBJECT
     
@@ -138,6 +143,12 @@ public slots:
     /// You can connect to this slot to show a critical message box from a different thread.
     void criticalMessageBoxOnMainThread(const QString& title, const QString& msg);
     
+    void showFlyView(void);
+    void showPlanView(void);
+    void showSetupView(void);
+
+    void showWindowCloseMessage(void);
+
 #ifndef __mobile__
     /// Save the specified Flight Data Log
     void saveTempFlightDataLogOnMainThread(QString tempLogfile);
@@ -166,6 +177,11 @@ public:
     /// @brief Intialize the application for normal application boot. Or in other words we are not going to run
     ///         unit tests. Although public should only be called by main.
     bool _initForUnitTests(void);
+
+    void _showSetupFirmware(void);
+    void _showSetupParameters(void);
+    void _showSetupSummary(void);
+    void _showSetupVehicleComponent(VehicleComponent* vehicleComponent);
     
     static QGCApplication*  _app;   ///< Our own singleton. Should be reference directly by qgcApp
     
@@ -174,6 +190,11 @@ private slots:
     
 private:
     void _loadCurrentStyle(void);
+    QObject* _rootQmlObject(void);
+
+#ifdef __mobile__
+    QQmlApplicationEngine* _qmlAppEngine;
+#endif
     
     static const char* _settingsVersionKey;             ///< Settings key which hold settings version
     static const char* _deleteAllSettingsKey;           ///< If this settings key is set on boot, all settings will be deleted
diff --git a/src/QmlControls/ParameterEditorController.cc b/src/QmlControls/ParameterEditorController.cc
index 364e14c..6f441ec 100644
--- a/src/QmlControls/ParameterEditorController.cc
+++ b/src/QmlControls/ParameterEditorController.cc
@@ -26,12 +26,12 @@
 
 #include "ParameterEditorController.h"
 #include "AutoPilotPluginManager.h"
-#include "MainWindow.h"
 #include "QGCApplication.h"
 
 #ifndef __mobile__
 #include "QGCFileDialog.h"
 #include "QGCMapRCToParamDialog.h"
+#include "MainWindow.h"
 #endif
 
 /// @Brief Constructs a new ParameterEditorController Widget. This widget is used within the PX4VehicleConfig set of screens.
diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc
index 5feb34a..67e6483 100644
--- a/src/QmlControls/QGroundControlQmlGlobal.cc
+++ b/src/QmlControls/QGroundControlQmlGlobal.cc
@@ -138,12 +138,6 @@ void QGroundControlQmlGlobal::setIsAudioMuted(bool muted)
     emit isAudioMutedChanged(muted);
 }
 
-void QGroundControlQmlGlobal::setIsLowPowerMode(bool low)
-{
-    MainWindow::instance()->enableLowPowerMode(low);
-    emit isLowPowerModeChanged(low);
-}
-
 void QGroundControlQmlGlobal::setIsSaveLogPrompt(bool prompt)
 {
     qgcApp()->setPromptFlightDataSave(prompt);
diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h
index d543698..ab7057f 100644
--- a/src/QmlControls/QGroundControlQmlGlobal.h
+++ b/src/QmlControls/QGroundControlQmlGlobal.h
@@ -30,7 +30,6 @@
 #include <QObject>
 
 #include "QGCApplication.h"
-#include "MainWindow.h"
 #include "LinkManager.h"
 #include "HomePositionManager.h"
 #include "FlightMapSettings.h"
@@ -63,7 +62,6 @@ public:
     Q_PROPERTY(bool     isAdvancedMode          READ isAdvancedMode                                                 CONSTANT)                               ///< Global "Advance Mode" preference. Certain UI elements and features are different based on this.
     Q_PROPERTY(bool     isDarkStyle             READ isDarkStyle                WRITE setIsDarkStyle                NOTIFY isDarkStyleChanged)              // TODO: Should be in ScreenTools?
     Q_PROPERTY(bool     isAudioMuted            READ isAudioMuted               WRITE setIsAudioMuted               NOTIFY isAudioMutedChanged)
-    Q_PROPERTY(bool     isLowPowerMode          READ isLowPowerMode             WRITE setIsLowPowerMode             NOTIFY isLowPowerModeChanged)
     Q_PROPERTY(bool     isSaveLogPrompt         READ isSaveLogPrompt            WRITE setIsSaveLogPrompt            NOTIFY isSaveLogPromptChanged)
     Q_PROPERTY(bool     isSaveLogPromptNotArmed READ isSaveLogPromptNotArmed    WRITE setIsSaveLogPromptNotArmed    NOTIFY isSaveLogPromptNotArmedChanged)
     Q_PROPERTY(bool     isHeartBeatEnabled      READ isHeartBeatEnabled         WRITE setIsHeartBeatEnabled         NOTIFY isHeartBeatEnabledChanged)
@@ -99,7 +97,6 @@ public:
 
     bool    isDarkStyle             () { return qgcApp()->styleIsDark(); }
     bool    isAudioMuted            () { return qgcApp()->toolbox()->audioOutput()->isMuted(); }
-    bool    isLowPowerMode          () { return MainWindow::instance()->lowPowerModeEnabled(); }
     bool    isSaveLogPrompt         () { return qgcApp()->promptFlightDataSave(); }
     bool    isSaveLogPromptNotArmed () { return qgcApp()->promptFlightDataSaveNotArmed(); }
     bool    isHeartBeatEnabled      () { return qgcApp()->toolbox()->mavlinkProtocol()->heartbeatsEnabled(); }
@@ -112,7 +109,6 @@ public:
 
     void    setIsDarkStyle              (bool dark);
     void    setIsAudioMuted             (bool muted);
-    void    setIsLowPowerMode           (bool low);
     void    setIsSaveLogPrompt          (bool prompt);
     void    setIsSaveLogPromptNotArmed  (bool prompt);
     void    setIsHeartBeatEnabled       (bool enable);
@@ -123,7 +119,6 @@ public:
 signals:
     void isDarkStyleChanged             (bool dark);
     void isAudioMutedChanged            (bool muted);
-    void isLowPowerModeChanged          (bool lowPower);
     void isSaveLogPromptChanged         (bool prompt);
     void isSaveLogPromptNotArmedChanged (bool prompt);
     void isHeartBeatEnabledChanged      (bool enabled);
diff --git a/src/QmlControls/ScreenToolsController.cc b/src/QmlControls/ScreenToolsController.cc
index 9380bf7..9717284 100644
--- a/src/QmlControls/ScreenToolsController.cc
+++ b/src/QmlControls/ScreenToolsController.cc
@@ -25,7 +25,6 @@
 ///     @author Gus Grubba <mavlink@grubba.com>
 
 #include "ScreenToolsController.h"
-#include "MainWindow.h"
 
 #ifdef Q_OS_WIN
 const double ScreenToolsController::_defaultFontPixelSizeRatio  = 1.0;
diff --git a/src/VehicleSetup/SetupViewTest.cc b/src/VehicleSetup/SetupViewTest.cc
index 649d9a3..5da316b 100644
--- a/src/VehicleSetup/SetupViewTest.cc
+++ b/src/VehicleSetup/SetupViewTest.cc
@@ -39,25 +39,25 @@ void SetupViewTest::_clickThrough_test(void)
     _createMainWindow();
 
     // Switch to the Setup view
-    _mainWindow->showSetupView();
+    qgcApp()->showSetupView();
     QTest::qWait(1000);
     
     // Click through fixed buttons
     qDebug() << "Showing firmware";
-    _mainWindow->showSetupFirmware();
+    qgcApp()->_showSetupFirmware();
     QTest::qWait(1000);
     qDebug() << "Showing parameters";
-    _mainWindow->showSetupParameters();
+    qgcApp()->_showSetupParameters();
     QTest::qWait(1000);
     qDebug() << "Showing summary";
-    _mainWindow->showSetupSummary();
+    qgcApp()->_showSetupSummary();
     QTest::qWait(1000);
     
     const QVariantList& components = autopilot->vehicleComponents();
     foreach(QVariant varComponent, components) {
         VehicleComponent* component = qobject_cast<VehicleComponent*>(qvariant_cast<QObject *>(varComponent));
         qDebug() << "Showing" << component->name();
-        _mainWindow->showSetupVehicleComponent(component);
+        qgcApp()->_showSetupVehicleComponent(component);
         QTest::qWait(1000);
     }
 
diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc
index 0e12c74..a023ad5 100644
--- a/src/comm/LinkManager.cc
+++ b/src/comm/LinkManager.cc
@@ -38,7 +38,6 @@ This file is part of the QGROUNDCONTROL project
 #endif
 
 #include "LinkManager.h"
-#include "MainWindow.h"
 #include "QGCApplication.h"
 #include "QGCApplication.h"
 #include "UDPLink.h"
@@ -189,14 +188,11 @@ void LinkManager::_addLink(LinkInterface* link)
         emit newLink(link);
     }
 
-    // MainWindow may be around when doing things like running unit tests
-    if (MainWindow::instance()) {
-        connect(link, &LinkInterface::communicationError, _app, &QGCApplication::criticalMessageBoxOnMainThread);
-    }
+    connect(link, &LinkInterface::communicationError,   _app,               &QGCApplication::criticalMessageBoxOnMainThread);
+    connect(link, &LinkInterface::bytesReceived,        _mavlinkProtocol,   &MAVLinkProtocol::receiveBytes);
+    connect(link, &LinkInterface::connected,            _mavlinkProtocol,   &MAVLinkProtocol::linkConnected);
+    connect(link, &LinkInterface::disconnected,         _mavlinkProtocol,   &MAVLinkProtocol::linkDisconnected);
 
-    connect(link, &LinkInterface::bytesReceived,    _mavlinkProtocol, &MAVLinkProtocol::receiveBytes);
-    connect(link, &LinkInterface::connected,        _mavlinkProtocol, &MAVLinkProtocol::linkConnected);
-    connect(link, &LinkInterface::disconnected,     _mavlinkProtocol, &MAVLinkProtocol::linkDisconnected);
     _mavlinkProtocol->resetMetadataForLink(link);
 
     connect(link, &LinkInterface::connected,    this, &LinkManager::_linkConnected);
diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h
index ff1b1a8..473a206 100644
--- a/src/comm/LinkManager.h
+++ b/src/comm/LinkManager.h
@@ -169,7 +169,7 @@ public:
     void _addLink(LinkInterface* link);
 
     // Called to signal app shutdown. Disconnects all links while turning off auto-connect.
-    void shutdown(void);
+    Q_INVOKABLE void shutdown(void);
 
 #ifdef QT_DEBUG
     // Only used by unit test tp restart after a shutdown
diff --git a/src/main.cc b/src/main.cc
index 12ab581..d377c9d 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -34,7 +34,6 @@ This file is part of the QGROUNDCONTROL project
 #include <QProcessEnvironment>
 
 #include "QGCApplication.h"
-#include "MainWindow.h"
 
 #ifndef __mobile__
     #include "QGCSerialPortInfo.h"
diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc
index 39089fb..b577618 100644
--- a/src/ui/MainWindow.cc
+++ b/src/ui/MainWindow.cc
@@ -159,7 +159,7 @@ MainWindow::MainWindow()
     _mainQmlWidgetHolder->setVisible(true);
 
     _mainQmlWidgetHolder->setContextPropertyObject("controller", this);
-    _mainQmlWidgetHolder->setSource(QUrl::fromUserInput("qrc:qml/MainWindow.qml"));
+    _mainQmlWidgetHolder->setSource(QUrl::fromUserInput("qrc:qml/MainWindowHybrid.qml"));
 
     // Image provider
     QQuickImageProvider* pImgProvider = dynamic_cast<QQuickImageProvider*>(qgcApp()->toolbox()->imageProvider());
@@ -436,7 +436,7 @@ void MainWindow::closeEvent(QCloseEvent *event)
 {
     // Disallow window close if there are active connections
     if (qgcApp()->toolbox()->multiVehicleManager()->vehicles()->count()) {
-        emit showWindowCloseMessage();
+        qgcApp()->showWindowCloseMessage();
         event->ignore();
         return;
     }
@@ -548,12 +548,12 @@ void MainWindow::connectCommonActions()
     connect(_ui.actionSettings, SIGNAL(triggered()), this, SLOT(showSettings()));
 
     // Views actions
-    connect(_ui.actionFlight,   &QAction::triggered,    this, &MainWindow::showFlyView);
-    connect(_ui.actionPlan,     &QAction::triggered,    this, &MainWindow::showPlanView);
-    connect(_ui.actionSetup,    &QAction::triggered,    this, &MainWindow::showSetupView);
-    connect(_ui.actionFlight,   &QAction::triggered,    this, &MainWindow::handleActiveViewActionState);
-    connect(_ui.actionPlan,     &QAction::triggered,    this, &MainWindow::handleActiveViewActionState);
-    connect(_ui.actionSetup,    &QAction::triggered,    this, &MainWindow::handleActiveViewActionState);
+    connect(_ui.actionFlight,   &QAction::triggered,    qgcApp(),   &QGCApplication::showFlyView);
+    connect(_ui.actionPlan,     &QAction::triggered,    qgcApp(),   &QGCApplication::showPlanView);
+    connect(_ui.actionSetup,    &QAction::triggered,    qgcApp(),   &QGCApplication::showSetupView);
+    connect(_ui.actionFlight,   &QAction::triggered,    this,       &MainWindow::handleActiveViewActionState);
+    connect(_ui.actionPlan,     &QAction::triggered,    this,       &MainWindow::handleActiveViewActionState);
+    connect(_ui.actionSetup,    &QAction::triggered,    this,       &MainWindow::handleActiveViewActionState);
 
     // Connect internal actions
     connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::vehicleAdded, this, &MainWindow::_vehicleAdded);
@@ -670,7 +670,7 @@ void MainWindow::_storeVisibleWidgetsSettings(void)
 }
 #endif
 
-void MainWindow::showMessage(const QString message)
+QObject* MainWindow::rootQmlObject(void)
 {
-    emit showCriticalMessage(message);
+    return _mainQmlWidgetHolder->getRootObject();
 }
diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h
index 6f9b103..4077f05 100644
--- a/src/ui/MainWindow.h
+++ b/src/ui/MainWindow.h
@@ -31,6 +31,10 @@ This file is part of the QGROUNDCONTROL project
 #ifndef _MAINWINDOW_H_
 #define _MAINWINDOW_H_
 
+#ifdef __mobile__
+#error Should not be include in mobile build
+#endif
+
 #include <QMainWindow>
 #include <QStatusBar>
 #include <QStackedWidget>
@@ -91,12 +95,12 @@ public:
     /// @brief Saves the last used connection
     void saveLastUsedConnection(const QString connection);
 
-    /// @brief Show message in lower message window
-    void showMessage(const QString message);
-
     // Called from MainWindow.qml when the user accepts the window close dialog
     Q_INVOKABLE void acceptWindowClose(void);
 
+    /// @return Root qml object of main window QML
+    QObject* rootQmlObject(void);
+
 public slots:
 #ifndef __mobile__
     void showSettings();
@@ -140,20 +144,6 @@ protected slots:
     void handleActiveViewActionState(bool triggered);
 
 signals:
-    // Signals the Qml to show the specified view
-    void showFlyView(void);
-    void showPlanView(void);
-    void showSetupView(void);
-
-    void showCriticalMessage(const QString& message);
-    void showWindowCloseMessage(void);
-
-    // These are used for unit testing
-    void showSetupFirmware(void);
-    void showSetupParameters(void);
-    void showSetupSummary(void);
-    void showSetupVehicleComponent(VehicleComponent* vehicleComponent);
-
     void initStatusChanged(const QString& message, int alignment, const QColor &color);
     /** Emitted when any value changes from any source */
     void valueChanged(const int uasId, const QString& name, const QString& unit, const QVariant& value, const quint64 msec);
@@ -173,6 +163,7 @@ public:
         return logPlayer;
     }
 #endif
+
 protected:
     void connectCommonActions();
 
diff --git a/src/ui/MainWindow.qml b/src/ui/MainWindow.qml
deleted file mode 100644
index 1c373d4..0000000
--- a/src/ui/MainWindow.qml
+++ /dev/null
@@ -1,424 +0,0 @@
-/*=====================================================================
-
-QGroundControl Open Source Ground Control Station
-
-(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
-
-This file is part of the QGROUNDCONTROL project
-
-QGROUNDCONTROL is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-QGROUNDCONTROL is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
-
-======================================================================*/
-
-import QtQuick          2.5
-import QtQuick.Controls 1.2
-import QtQuick.Dialogs  1.2
-import QtPositioning    5.2
-
-import QGroundControl                       1.0
-import QGroundControl.Palette               1.0
-import QGroundControl.Controls              1.0
-import QGroundControl.FlightDisplay         1.0
-import QGroundControl.ScreenTools           1.0
-import QGroundControl.MultiVehicleManager   1.0
-
-/// Qml for MainWindow
-Item {
-    id: mainWindow
-
-    readonly property string _planViewSource:   "MissionEditor.qml"
-    readonly property string _setupViewSource:  "SetupView.qml"
-
-    QGCPalette { id: __qgcPal; colorGroupEnabled: true }
-
-    property real   tbHeight:           ScreenTools.isMobile ? (ScreenTools.isTinyScreen ? (mainWindow.width * 0.0666) : (mainWindow.width * 0.05)) : ScreenTools.defaultFontPixelSize * 4
-    property int    tbCellHeight:       tbHeight * 0.75
-    property real   tbSpacing:          ScreenTools.isMobile ? width * 0.00824 : 9.54
-    property real   tbButtonWidth:      tbCellHeight * 1.35
-    property real   availableHeight:    height - tbHeight
-    property real   menuButtonWidth:    (tbButtonWidth * 2) + (tbSpacing * 4) + 1
-
-    property var    defaultPosition:    QtPositioning.coordinate(37.803784, -122.462276)
-    property var    tabletPosition:     defaultPosition
-
-    property var    currentPopUp:       null
-    property real   currentCenterX:     0
-    property var    activeVehicle:      multiVehicleManager.activeVehicle
-    property string formatedMessage:    activeVehicle ? activeVehicle.formatedMessage : ""
-
-    Connections {
-
-        target: controller
-
-        onShowFlyView: {
-            if(currentPopUp) {
-                currentPopUp.close()
-            }
-            flightView.visible          = true
-            setupViewLoader.visible     = false
-            planViewLoader.visible      = false
-        }
-
-        onShowPlanView: {
-            if(currentPopUp) {
-                currentPopUp.close()
-            }
-            if (planViewLoader.source   != _planViewSource) {
-                planViewLoader.source   = _planViewSource
-            }
-            flightView.visible          = false
-            setupViewLoader.visible     = false
-            planViewLoader.visible      = true
-        }
-
-        onShowSetupView: {
-            if(currentPopUp) {
-                currentPopUp.close()
-            }
-            if (setupViewLoader.source  != _setupViewSource) {
-                setupViewLoader.source  = _setupViewSource
-            }
-            flightView.visible          = false
-            setupViewLoader.visible     = true
-            planViewLoader.visible      = false
-        }
-
-        onShowCriticalMessage: showCriticalMessage(message)
-
-        onShowWindowCloseMessage: windowCloseDialog.open()
-
-        // The following are use for unit testing only
-
-        onShowSetupFirmware:            setupViewLoader.item.showFirmwarePanel()
-        onShowSetupParameters:          setupViewLoader.item.showParametersPanel()
-        onShowSetupSummary:             setupViewLoader.item.showSummaryPanel()
-        onShowSetupVehicleComponent:    setupViewLoader.item.showVehicleComponentPanel(vehicleComponent)
-    }
-
-    //-- Detect tablet position
-    PositionSource {
-        id:             positionSource
-        updateInterval: 1000
-        active:         false
-        onPositionChanged: {
-            if(positionSource.valid) {
-                if(positionSource.position.coordinate.latitude) {
-                    if(Math.abs(positionSource.position.coordinate.latitude)  > 0.001) {
-                        if(positionSource.position.coordinate.longitude) {
-                            if(Math.abs(positionSource.position.coordinate.longitude)  > 0.001) {
-                                tabletPosition = positionSource.position.coordinate
-                            }
-                        }
-                    }
-                }
-            }
-            positionSource.stop()
-        }
-    }
-
-    property var messageQueue: []
-
-    function showCriticalMessage(message) {
-        if(criticalMmessageArea.visible) {
-            messageQueue.push(message)
-        } else {
-            criticalMessageText.text = message
-            criticalMmessageArea.visible = true
-            mainWindow.setMapInteractive(false)
-        }
-    }
-
-    function showLeftMenu() {
-        if(!leftPanel.visible && !leftPanel.item.animateShowDialog.running) {
-            leftPanel.visible = true
-            leftPanel.item.animateShowDialog.start()
-        } else if(leftPanel.visible && !leftPanel.item.animateShowDialog.running) {
-            //-- If open, toggle it closed
-            hideLeftMenu()
-        }
-    }
-
-    function hideLeftMenu() {
-        if(leftPanel.visible && !leftPanel.item.animateHideDialog.running) {
-            leftPanel.item.animateHideDialog.start()
-        }
-    }
-
-    function setMapInteractive(enabled) {
-        flightView.interactive = enabled
-    }
-
-    onFormatedMessageChanged: {
-        if(messageArea.visible) {
-            messageText.append(formatedMessage)
-            //-- Hack to scroll down
-            messageFlick.flick(0,-500)
-        }
-    }
-
-    function showMessageArea() {
-        if(currentPopUp) {
-            currentPopUp.close()
-        }
-        if(multiVehicleManager.activeVehicleAvailable) {
-            messageText.text = activeVehicle.formatedMessages
-            //-- Hack to scroll to last message
-            for (var i = 0; i < activeVehicle.messageCount; i++)
-                messageFlick.flick(0,-5000)
-            activeVehicle.resetMessages()
-        } else {
-            messageText.text = "No Messages"
-        }
-        currentPopUp = messageArea
-        messageArea.visible = true
-        mainWindow.setMapInteractive(false)
-    }
-
-    function showPopUp(dropItem, centerX) {
-        if(currentPopUp) {
-            currentPopUp.close()
-          }
-        indicatorDropdown.centerX = centerX
-        indicatorDropdown.sourceComponent = dropItem
-        indicatorDropdown.visible = true
-        currentPopUp = indicatorDropdown
-    }
-
-    MessageDialog {
-        id:                 windowCloseDialog
-        title:              "QGroundControl close"
-        text:               "There are still active connections to vehicles. Do you want to disconnect these before closing?"
-        standardButtons:    StandardButton.Yes | StandardButton.Cancel
-        modality:           Qt.ApplicationModal
-        visible:            false
-
-        onYes: controller.acceptWindowClose()
-    }
-
-    //-- Left Settings Menu
-    Loader {
-        id:                 leftPanel
-        anchors.fill:       mainWindow
-        visible:            false
-        z:                  QGroundControl.zOrderTopMost + 100
-    }
-
-    //-- Main UI
-
-    MainToolBar {
-        id:                 toolBar
-        height:             tbHeight
-        anchors.left:       parent.left
-        anchors.right:      parent.right
-        anchors.top:        parent.top
-        mainWindow:         mainWindow
-        opaqueBackground:   leftPanel.visible
-        isBackgroundDark:   flightView.isBackgroundDark
-        z:                  QGroundControl.zOrderTopMost
-        Component.onCompleted: {
-            leftPanel.source = "MainWindowLeftPanel.qml"
-        }
-    }
-
-    FlightDisplayView {
-        id:                 flightView
-        anchors.fill:       parent
-        availableHeight:    mainWindow.availableHeight
-        visible:            true
-        Component.onCompleted: {
-            positionSource.start()
-        }
-    }
-
-    Loader {
-        id:                 planViewLoader
-        anchors.fill:       parent
-        visible:            false
-    }
-
-    Loader {
-        id:                 setupViewLoader
-        anchors.fill:       parent
-        visible:            false
-    }
-
-    //-------------------------------------------------------------------------
-    //-- Dismiss Pop Up Messages
-    MouseArea {
-        visible:        currentPopUp != null
-        enabled:        currentPopUp != null
-        anchors.fill:   parent
-        onClicked: {
-            currentPopUp.close()
-        }
-    }
-
-    //-------------------------------------------------------------------------
-    //-- Indicator Drop Down Info
-    Loader {
-        id: indicatorDropdown
-        visible: false
-        property real centerX: 0
-        function close() {
-            sourceComponent = null
-            mainWindow.currentPopUp = null
-        }
-    }
-
-    //-------------------------------------------------------------------------
-    //-- System Message Area
-    Rectangle {
-        id:                 messageArea
-
-        function close() {
-            currentPopUp = null
-            messageText.text    = ""
-            mainWindow.setMapInteractive(true)
-            messageArea.visible = false
-        }
-
-        width:              mainWindow.width  * 0.5
-        height:             mainWindow.height * 0.5
-        color:              Qt.rgba(0,0,0,0.8)
-        visible:            false
-        radius:             ScreenTools.defaultFontPixelHeight * 0.5
-        anchors.horizontalCenter:   parent.horizontalCenter
-        anchors.top:                parent.top
-        anchors.topMargin:          tbHeight + ScreenTools.defaultFontPixelHeight
-        Flickable {
-            id:                 messageFlick
-            anchors.margins:    ScreenTools.defaultFontPixelHeight
-            anchors.fill:       parent
-            contentHeight:      messageText.height
-            contentWidth:       messageText.width
-            boundsBehavior:     Flickable.StopAtBounds
-            pixelAligned:       true
-            clip:               true
-            TextEdit {
-                id:         messageText
-                readOnly:   true
-                textFormat: TextEdit.RichText
-                color:      "white"
-            }
-        }
-        //-- Dismiss System Message
-        Image {
-            anchors.margins:    ScreenTools.defaultFontPixelHeight
-            anchors.top:        parent.top
-            anchors.right:      parent.right
-            width:              ScreenTools.defaultFontPixelHeight * 1.5
-            height:             ScreenTools.defaultFontPixelHeight * 1.5
-            source:             "/res/XDelete.svg"
-            fillMode:           Image.PreserveAspectFit
-            mipmap:             true
-            smooth:             true
-            MouseArea {
-                anchors.fill:   parent
-                onClicked: {
-                    messageArea.close()
-                }
-            }
-        }
-    }
-    //-------------------------------------------------------------------------
-    //-- Critical Message Area
-    Rectangle {
-        id:                 criticalMmessageArea
-
-        function close() {
-            //-- Are there messages in the waiting queue?
-            if(mainWindow.messageQueue.length) {
-                criticalMessageText.text = ""
-                //-- Show all messages in queue
-                for (var i = 0; i < mainWindow.messageQueue.length; i++) {
-                    criticalMessageText.append(mainWindow.messageQueue[i])
-                }
-                //-- Clear it
-                mainWindow.messageQueue = []
-            } else {
-                criticalMessageText.text = ""
-                mainWindow.setMapInteractive(true)
-                criticalMmessageArea.visible = false
-            }
-        }
-
-        width:              mainWindow.width  * 0.55
-        height:             ScreenTools.defaultFontPixelHeight * ScreenTools.fontHRatio * 6
-        color:              Qt.rgba(0,0,0,0.8)
-        visible:            false
-        radius:             ScreenTools.defaultFontPixelHeight * 0.5
-        anchors.horizontalCenter:   parent.horizontalCenter
-        anchors.bottom:             parent.bottom
-        anchors.bottomMargin:       ScreenTools.defaultFontPixelHeight
-        Flickable {
-            id:                 criticalMessageFlick
-            anchors.margins:    ScreenTools.defaultFontPixelHeight
-            anchors.fill:       parent
-            contentHeight:      criticalMessageText.height
-            contentWidth:       criticalMessageText.width
-            boundsBehavior:     Flickable.StopAtBounds
-            pixelAligned:       true
-            clip:               true
-            TextEdit {
-                id:             criticalMessageText
-                width:          criticalMmessageArea.width - criticalClose.width - (ScreenTools.defaultFontPixelHeight * 2)
-                anchors.left:   parent.left
-                readOnly:       true
-                textFormat:     TextEdit.RichText
-                font.weight:    Font.DemiBold
-                wrapMode:       TextEdit.WordWrap
-                color:          "#fdfd3b"
-            }
-        }
-        //-- Dismiss Critical Message
-        Image {
-            id:                 criticalClose
-            anchors.margins:    ScreenTools.defaultFontPixelHeight
-            anchors.top:        parent.top
-            anchors.right:      parent.right
-            width:              ScreenTools.defaultFontPixelHeight * 1.5
-            height:             ScreenTools.defaultFontPixelHeight * 1.5
-            source:             "/res/XDelete.svg"
-            fillMode:           Image.PreserveAspectFit
-            mipmap:             true
-            smooth:             true
-            MouseArea {
-                anchors.fill:   parent
-                onClicked: {
-                    criticalMmessageArea.close()
-                }
-            }
-        }
-        //-- More text below indicator
-        Image {
-            anchors.margins:    ScreenTools.defaultFontPixelHeight
-            anchors.bottom:     parent.bottom
-            anchors.right:      parent.right
-            width:              ScreenTools.defaultFontPixelHeight * 1.5
-            height:             ScreenTools.defaultFontPixelHeight * 1.5
-            source:             "/res/ArrowDown.svg"
-            fillMode:           Image.PreserveAspectFit
-            mipmap:             true
-            smooth:             true
-            visible:            criticalMessageText.lineCount > 5
-            MouseArea {
-                anchors.fill:   parent
-                onClicked: {
-                    criticalMessageFlick.flick(0,-500)
-                }
-            }
-        }
-    }
-
-}
diff --git a/src/ui/MainWindowHybrid.qml b/src/ui/MainWindowHybrid.qml
new file mode 100644
index 0000000..aa8b206
--- /dev/null
+++ b/src/ui/MainWindowHybrid.qml
@@ -0,0 +1,75 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+
+This file is part of the QGROUNDCONTROL project
+
+QGROUNDCONTROL is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+QGROUNDCONTROL is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
+
+======================================================================*/
+
+import QtQuick          2.5
+import QtQuick.Controls 1.2
+
+import QGroundControl.Controls 1.0
+
+/// Native QML top level window
+Item {
+    function showFlyView() {
+        mainWindowInner.item.showFlyView()
+    }
+
+    function showPlanView() {
+        mainWindowInner.item.showPlanView()
+    }
+
+    function showSetupView() {
+        mainWindowInner.item.showSetupView()
+    }
+
+    function showWindowCloseMessage() {
+        mainWindowInner.item.showWindowCloseMessage()
+    }
+
+    // The following are use for unit testing only
+
+    function showSetupFirmware() {
+        mainWindowInner.item.showSetupFirmware()
+    }
+
+    function showSetupParameters() {
+        mainWindowInner.item.showSetupParameters()
+    }
+
+    function showSetupSummary() {
+        mainWindowInner.item.showSetupSummary()
+    }
+
+    function showSetupVehicleComponent(vehicleComponent) {
+        mainWindowInner.item.showSetupVehicleComponent(vehicleComponent)
+    }
+
+    function showMessage(message) {
+        mainWindowInner.item.showMessage(message)
+    }
+
+    Loader {
+        id:             mainWindowInner
+        anchors.fill:   parent
+        source:         "MainWindowInner.qml"
+    }
+}
+
diff --git a/src/ui/MainWindowInner.qml b/src/ui/MainWindowInner.qml
new file mode 100644
index 0000000..40ed423
--- /dev/null
+++ b/src/ui/MainWindowInner.qml
@@ -0,0 +1,438 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+
+This file is part of the QGROUNDCONTROL project
+
+QGROUNDCONTROL is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+QGROUNDCONTROL is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
+
+======================================================================*/
+
+import QtQuick          2.5
+import QtQuick.Controls 1.2
+import QtQuick.Dialogs  1.2
+import QtPositioning    5.2
+
+import QGroundControl                       1.0
+import QGroundControl.Palette               1.0
+import QGroundControl.Controls              1.0
+import QGroundControl.FlightDisplay         1.0
+import QGroundControl.ScreenTools           1.0
+import QGroundControl.MultiVehicleManager   1.0
+
+/// Inner common QML for MainWindow
+Item {
+    id:         mainWindow
+
+    readonly property string _planViewSource:   "MissionEditor.qml"
+    readonly property string _setupViewSource:  "SetupView.qml"
+
+    QGCPalette { id: __qgcPal; colorGroupEnabled: true }
+
+    property real   tbHeight:           ScreenTools.isMobile ? (ScreenTools.isTinyScreen ? (mainWindow.width * 0.0666) : (mainWindow.width * 0.05)) : ScreenTools.defaultFontPixelSize * 4
+    property int    tbCellHeight:       tbHeight * 0.75
+    property real   tbSpacing:          ScreenTools.isMobile ? width * 0.00824 : 9.54
+    property real   tbButtonWidth:      tbCellHeight * 1.35
+    property real   availableHeight:    height - tbHeight
+    property real   menuButtonWidth:    (tbButtonWidth * 2) + (tbSpacing * 4) + 1
+
+    property var    defaultPosition:    QtPositioning.coordinate(37.803784, -122.462276)
+    property var    tabletPosition:     defaultPosition
+
+    property var    currentPopUp:       null
+    property real   currentCenterX:     0
+    property var    activeVehicle:      multiVehicleManager.activeVehicle
+    property string formatedMessage:    activeVehicle ? activeVehicle.formatedMessage : ""
+
+    function showFlyView() {
+        if(currentPopUp) {
+            currentPopUp.close()
+        }
+        flightView.visible          = true
+        setupViewLoader.visible     = false
+        planViewLoader.visible      = false
+        toolbar.checkFlyButton()
+    }
+
+    function showPlanView() {
+        if(currentPopUp) {
+            currentPopUp.close()
+        }
+        if (planViewLoader.source   != _planViewSource) {
+            planViewLoader.source   = _planViewSource
+        }
+        flightView.visible          = false
+        setupViewLoader.visible     = false
+        planViewLoader.visible      = true
+        toolBar.checkPlanButton()
+    }
+
+    function showSetupView() {
+        if(currentPopUp) {
+            currentPopUp.close()
+        }
+        if (setupViewLoader.source  != _setupViewSource) {
+            setupViewLoader.source  = _setupViewSource
+        }
+        flightView.visible          = false
+        setupViewLoader.visible     = true
+        planViewLoader.visible      = false
+        toolBar.checkSetupButton()
+    }
+
+    function showWindowCloseMessage() {
+        windowCloseDialog.open()
+    }
+
+    // The following are use for unit testing only
+
+    function showSetupFirmware() {
+        setupViewLoader.item.showFirmwarePanel()
+    }
+
+    function showSetupParameters() {
+        setupViewLoader.item.showParametersPanel()
+    }
+
+    function showSetupSummary() {
+        setupViewLoader.item.showSummaryPanel()
+    }
+
+    function showSetupVehicleComponent(vehicleComponent) {
+        setupViewLoader.item.showVehicleComponentPanel(vehicleComponent)
+    }
+
+    //-- Detect tablet position
+    PositionSource {
+        id:             positionSource
+        updateInterval: 1000
+        active:         false
+        onPositionChanged: {
+            if(positionSource.valid) {
+                if(positionSource.position.coordinate.latitude) {
+                    if(Math.abs(positionSource.position.coordinate.latitude)  > 0.001) {
+                        if(positionSource.position.coordinate.longitude) {
+                            if(Math.abs(positionSource.position.coordinate.longitude)  > 0.001) {
+                                tabletPosition = positionSource.position.coordinate
+                            }
+                        }
+                    }
+                }
+            }
+            positionSource.stop()
+        }
+    }
+
+    property var messageQueue: []
+
+    function showMessage(message) {
+        if(criticalMmessageArea.visible) {
+            messageQueue.push(message)
+        } else {
+            criticalMessageText.text = message
+            criticalMmessageArea.visible = true
+            mainWindow.setMapInteractive(false)
+        }
+    }
+
+    function showLeftMenu() {
+        if(!leftPanel.visible && !leftPanel.item.animateShowDialog.running) {
+            leftPanel.visible = true
+            leftPanel.item.animateShowDialog.start()
+        } else if(leftPanel.visible && !leftPanel.item.animateShowDialog.running) {
+            //-- If open, toggle it closed
+            hideLeftMenu()
+        }
+    }
+
+    function hideLeftMenu() {
+        if(leftPanel.visible && !leftPanel.item.animateHideDialog.running) {
+            leftPanel.item.animateHideDialog.start()
+        }
+    }
+
+    function setMapInteractive(enabled) {
+        flightView.interactive = enabled
+    }
+
+    onFormatedMessageChanged: {
+        if(messageArea.visible) {
+            messageText.append(formatedMessage)
+            //-- Hack to scroll down
+            messageFlick.flick(0,-500)
+        }
+    }
+
+    function showMessageArea() {
+        if(currentPopUp) {
+            currentPopUp.close()
+        }
+        if(multiVehicleManager.activeVehicleAvailable) {
+            messageText.text = activeVehicle.formatedMessages
+            //-- Hack to scroll to last message
+            for (var i = 0; i < activeVehicle.messageCount; i++)
+                messageFlick.flick(0,-5000)
+            activeVehicle.resetMessages()
+        } else {
+            messageText.text = "No Messages"
+        }
+        currentPopUp = messageArea
+        messageArea.visible = true
+        mainWindow.setMapInteractive(false)
+    }
+
+    function showPopUp(dropItem, centerX) {
+        if(currentPopUp) {
+            currentPopUp.close()
+          }
+        indicatorDropdown.centerX = centerX
+        indicatorDropdown.sourceComponent = dropItem
+        indicatorDropdown.visible = true
+        currentPopUp = indicatorDropdown
+    }
+
+    MessageDialog {
+        id:                 windowCloseDialog
+        title:              "QGroundControl close"
+        text:               "There are still active connections to vehicles. Do you want to disconnect these before closing?"
+        standardButtons:    StandardButton.Yes | StandardButton.Cancel
+        modality:           Qt.ApplicationModal
+        visible:            false
+
+        onYes: controller.acceptWindowClose()
+    }
+
+    //-- Left Settings Menu
+    Loader {
+        id:                 leftPanel
+        anchors.fill:       parent
+        visible:            false
+        z:                  QGroundControl.zOrderTopMost + 100
+    }
+
+    //-- Main UI
+
+    MainToolBar {
+        id:                 toolBar
+        height:             tbHeight
+        anchors.left:       parent.left
+        anchors.right:      parent.right
+        anchors.top:        parent.top
+        mainWindow:         mainWindow
+        opaqueBackground:   leftPanel.visible
+        isBackgroundDark:   flightView.isBackgroundDark
+        z:                  QGroundControl.zOrderTopMost
+
+        Component.onCompleted: {
+            leftPanel.source = "MainWindowLeftPanel.qml"
+        }
+
+        onShowSetupView:    mainWindow.showSetupView()
+        onShowPlanView:     mainWindow.showPlanView()
+        onShowFlyView:      mainWindow.showFlyView()
+    }
+
+    FlightDisplayView {
+        id:                 flightView
+        anchors.fill:       parent
+        availableHeight:    mainWindow.availableHeight
+        visible:            true
+        Component.onCompleted: {
+            positionSource.start()
+        }
+    }
+
+    Loader {
+        id:                 planViewLoader
+        anchors.fill:       parent
+        visible:            false
+    }
+
+    Loader {
+        id:                 setupViewLoader
+        anchors.fill:       parent
+        visible:            false
+    }
+
+    //-------------------------------------------------------------------------
+    //-- Dismiss Pop Up Messages
+    MouseArea {
+        visible:        currentPopUp != null
+        enabled:        currentPopUp != null
+        anchors.fill:   parent
+        onClicked: {
+            currentPopUp.close()
+        }
+    }
+
+    //-------------------------------------------------------------------------
+    //-- Indicator Drop Down Info
+    Loader {
+        id: indicatorDropdown
+        visible: false
+        property real centerX: 0
+        function close() {
+            sourceComponent = null
+            mainWindow.currentPopUp = null
+        }
+    }
+
+    //-------------------------------------------------------------------------
+    //-- System Message Area
+    Rectangle {
+        id:                 messageArea
+
+        function close() {
+            currentPopUp = null
+            messageText.text    = ""
+            mainWindow.setMapInteractive(true)
+            messageArea.visible = false
+        }
+
+        width:              mainWindow.width  * 0.5
+        height:             mainWindow.height * 0.5
+        color:              Qt.rgba(0,0,0,0.8)
+        visible:            false
+        radius:             ScreenTools.defaultFontPixelHeight * 0.5
+        anchors.horizontalCenter:   parent.horizontalCenter
+        anchors.top:                parent.top
+        anchors.topMargin:          tbHeight + ScreenTools.defaultFontPixelHeight
+        Flickable {
+            id:                 messageFlick
+            anchors.margins:    ScreenTools.defaultFontPixelHeight
+            anchors.fill:       parent
+            contentHeight:      messageText.height
+            contentWidth:       messageText.width
+            boundsBehavior:     Flickable.StopAtBounds
+            pixelAligned:       true
+            clip:               true
+            TextEdit {
+                id:         messageText
+                readOnly:   true
+                textFormat: TextEdit.RichText
+                color:      "white"
+            }
+        }
+        //-- Dismiss System Message
+        Image {
+            anchors.margins:    ScreenTools.defaultFontPixelHeight
+            anchors.top:        parent.top
+            anchors.right:      parent.right
+            width:              ScreenTools.defaultFontPixelHeight * 1.5
+            height:             ScreenTools.defaultFontPixelHeight * 1.5
+            source:             "/res/XDelete.svg"
+            fillMode:           Image.PreserveAspectFit
+            mipmap:             true
+            smooth:             true
+            MouseArea {
+                anchors.fill:   parent
+                onClicked: {
+                    messageArea.close()
+                }
+            }
+        }
+    }
+    //-------------------------------------------------------------------------
+    //-- Critical Message Area
+    Rectangle {
+        id:                 criticalMmessageArea
+
+        function close() {
+            //-- Are there messages in the waiting queue?
+            if(mainWindow.messageQueue.length) {
+                criticalMessageText.text = ""
+                //-- Show all messages in queue
+                for (var i = 0; i < mainWindow.messageQueue.length; i++) {
+                    criticalMessageText.append(mainWindow.messageQueue[i])
+                }
+                //-- Clear it
+                mainWindow.messageQueue = []
+            } else {
+                criticalMessageText.text = ""
+                mainWindow.setMapInteractive(true)
+                criticalMmessageArea.visible = false
+            }
+        }
+
+        width:              mainWindow.width  * 0.55
+        height:             ScreenTools.defaultFontPixelHeight * ScreenTools.fontHRatio * 6
+        color:              Qt.rgba(0,0,0,0.8)
+        visible:            false
+        radius:             ScreenTools.defaultFontPixelHeight * 0.5
+        anchors.horizontalCenter:   parent.horizontalCenter
+        anchors.bottom:             parent.bottom
+        anchors.bottomMargin:       ScreenTools.defaultFontPixelHeight
+        Flickable {
+            id:                 criticalMessageFlick
+            anchors.margins:    ScreenTools.defaultFontPixelHeight
+            anchors.fill:       parent
+            contentHeight:      criticalMessageText.height
+            contentWidth:       criticalMessageText.width
+            boundsBehavior:     Flickable.StopAtBounds
+            pixelAligned:       true
+            clip:               true
+            TextEdit {
+                id:             criticalMessageText
+                width:          criticalMmessageArea.width - criticalClose.width - (ScreenTools.defaultFontPixelHeight * 2)
+                anchors.left:   parent.left
+                readOnly:       true
+                textFormat:     TextEdit.RichText
+                font.weight:    Font.DemiBold
+                wrapMode:       TextEdit.WordWrap
+                color:          "#fdfd3b"
+            }
+        }
+        //-- Dismiss Critical Message
+        Image {
+            id:                 criticalClose
+            anchors.margins:    ScreenTools.defaultFontPixelHeight
+            anchors.top:        parent.top
+            anchors.right:      parent.right
+            width:              ScreenTools.defaultFontPixelHeight * 1.5
+            height:             ScreenTools.defaultFontPixelHeight * 1.5
+            source:             "/res/XDelete.svg"
+            fillMode:           Image.PreserveAspectFit
+            mipmap:             true
+            smooth:             true
+            MouseArea {
+                anchors.fill:   parent
+                onClicked: {
+                    criticalMmessageArea.close()
+                }
+            }
+        }
+        //-- More text below indicator
+        Image {
+            anchors.margins:    ScreenTools.defaultFontPixelHeight
+            anchors.bottom:     parent.bottom
+            anchors.right:      parent.right
+            width:              ScreenTools.defaultFontPixelHeight * 1.5
+            height:             ScreenTools.defaultFontPixelHeight * 1.5
+            source:             "/res/ArrowDown.svg"
+            fillMode:           Image.PreserveAspectFit
+            mipmap:             true
+            smooth:             true
+            visible:            criticalMessageText.lineCount > 5
+            MouseArea {
+                anchors.fill:   parent
+                onClicked: {
+                    criticalMessageFlick.flick(0,-500)
+                }
+            }
+        }
+    }
+}
+
diff --git a/src/ui/MainWindowNative.qml b/src/ui/MainWindowNative.qml
new file mode 100644
index 0000000..26399ab
--- /dev/null
+++ b/src/ui/MainWindowNative.qml
@@ -0,0 +1,90 @@
+/*=====================================================================
+
+QGroundControl Open Source Ground Control Station
+
+(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
+
+This file is part of the QGROUNDCONTROL project
+
+QGROUNDCONTROL is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+QGROUNDCONTROL is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
+
+======================================================================*/
+
+import QtQuick          2.5
+import QtQuick.Window   2.2
+
+import QGroundControl   1.0
+
+/// Native QML top level window
+Window {
+    visible: true
+
+    onClosing: {
+        // Disallow window close if there are active connections
+        if (QGroundControl.multiVehicleManager.activeVehicle) {
+            showWindowCloseMessage()
+            close.accepted = false
+            return
+        }
+
+        // We still need to shutdown LinkManager even though no active connections so that we don't get any
+        // more auto-connect links during shutdown.
+        QGroundControl.linkManager.shutdown();
+    }
+
+    function showFlyView() {
+        mainWindowInner.item.showFlyView()
+    }
+
+    function showPlanView() {
+        mainWindowInner.item.showPlanView()
+    }
+
+    function showSetupView() {
+        mainWindowInner.item.showSetupView()
+    }
+
+    function showWindowCloseMessage() {
+        mainWindowInner.item.showWindowCloseMessage()
+    }
+
+    // The following are use for unit testing only
+
+    function showSetupFirmware() {
+        mainWindowInner.item.showSetupFirmware()
+    }
+
+    function showSetupParameters() {
+        mainWindowInner.item.showSetupParameters()
+    }
+
+    function showSetupSummary() {
+        mainWindowInner.item.showSetupSummary()
+    }
+
+    function showSetupVehicleComponent(vehicleComponent) {
+        mainWindowInner.showSetupVehicleComponent(vehicleComponent)
+    }
+
+    function showMessage(message) {
+        mainWindowInner.item.showMessage(message)
+    }
+
+    Loader {
+        id:             mainWindowInner
+        anchors.fill:   parent
+        source:         "MainWindowInner.qml"
+    }
+}
+
diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml
index 5ad4f90..d4a9218 100644
--- a/src/ui/preferences/GeneralSettings.qml
+++ b/src/ui/preferences/GeneralSettings.qml
@@ -81,15 +81,6 @@ Rectangle {
                 }
             }
             //-----------------------------------------------------------------
-            //-- Low power mode
-            QGCCheckBox {
-                text:       "Enable low power mode"
-                checked:    QGroundControl.isLowPowerMode
-                onClicked: {
-                    QGroundControl.isLowPowerMode = checked
-                }
-            }
-            //-----------------------------------------------------------------
             //-- Prompt Save Log
             QGCCheckBox {
                 id:         promptSaveLog
diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml
index 37b9756..ab65895 100644
--- a/src/ui/toolbar/MainToolBar.qml
+++ b/src/ui/toolbar/MainToolBar.qml
@@ -145,8 +145,24 @@ Rectangle {
     readonly property var   colorBlue:      "#636efe"
     readonly property var   colorWhite:     "#ffffff"
 
+    signal showSetupView()
+    signal showPlanView()
+    signal showFlyView()
+
     MainToolBarController { id: _controller }
 
+    function checkSetupButton() {
+        setupButton.checked = true
+    }
+
+    function checkPlanButton() {
+        planButton.checked = true
+    }
+
+    function checkFlyButton() {
+        flyButton.checked = true
+    }
+
     function getBatteryColor() {
         if(activeVehicle) {
             if(activeVehicle.batteryPercent > 75) {
@@ -193,13 +209,6 @@ Rectangle {
         flyButton.checked = true
     }
 
-    Connections {
-        target:         controller
-        onShowFlyView:  { flyButton.checked   = true }
-        onShowPlanView: { planButton.checked  = true }
-        onShowSetupView:{ setupButton.checked = true }
-    }
-
     //---------------------------------------------
     // GPS Info
     Component {
@@ -496,9 +505,7 @@ Rectangle {
             height:             mainWindow.tbCellHeight
             exclusiveGroup:     mainActionGroup
             source:             "/qmlimages/Gears.svg"
-            onClicked: {
-                _controller.onSetupView();
-            }
+            onClicked:          toolBar.showSetupView()
         }
 
         Rectangle {
@@ -513,9 +520,7 @@ Rectangle {
             height:             mainWindow.tbCellHeight
             exclusiveGroup:     mainActionGroup
             source:             "/qmlimages/Plan.svg"
-            onClicked: {
-                _controller.onPlanView();
-            }
+            onClicked:          toolBar.showPlanView()
         }
 
         Rectangle {
@@ -530,9 +535,7 @@ Rectangle {
             height:             mainWindow.tbCellHeight
             exclusiveGroup:     mainActionGroup
             source:             "/qmlimages/PaperPlane.svg"
-            onClicked: {
-                _controller.onFlyView();
-            }
+            onClicked:          toolBar.showFlyView()
         }
 
         Rectangle {
diff --git a/src/ui/toolbar/MainToolBarController.cc b/src/ui/toolbar/MainToolBarController.cc
index bab5ff6..5af18e7 100644
--- a/src/ui/toolbar/MainToolBarController.cc
+++ b/src/ui/toolbar/MainToolBarController.cc
@@ -32,7 +32,6 @@ This file is part of the QGROUNDCONTROL project
 
 #include "MainToolBarController.h"
 #include "ScreenToolsController.h"
-#include "MainWindow.h"
 #include "UASMessageView.h"
 #include "UASMessageHandler.h"
 #include "QGCApplication.h"
@@ -60,21 +59,6 @@ MainToolBarController::~MainToolBarController()
 
 }
 
-void MainToolBarController::onSetupView()
-{
-    MainWindow::instance()->showSetupView();
-}
-
-void MainToolBarController::onPlanView()
-{
-    MainWindow::instance()->showPlanView();
-}
-
-void MainToolBarController::onFlyView()
-{
-    MainWindow::instance()->showFlyView();
-}
-
 void MainToolBarController::_activeVehicleChanged(Vehicle* vehicle)
 {
     // Disconnect the previous one (if any)
diff --git a/src/ui/toolbar/MainToolBarController.h b/src/ui/toolbar/MainToolBarController.h
index 9977eac..6b6bc55 100644
--- a/src/ui/toolbar/MainToolBarController.h
+++ b/src/ui/toolbar/MainToolBarController.h
@@ -50,10 +50,6 @@ public:
     MainToolBarController(QObject* parent = NULL);
     ~MainToolBarController();
 
-    Q_INVOKABLE void    onSetupView();
-    Q_INVOKABLE void    onPlanView();
-    Q_INVOKABLE void    onFlyView();
-
     Q_PROPERTY(double       height              MEMBER _toolbarHeight           NOTIFY heightChanged)
     Q_PROPERTY(float        progressBarValue    MEMBER _progressBarValue        NOTIFY progressBarValueChanged)
     Q_PROPERTY(int          telemetryRRSSI      READ telemetryRRSSI             NOTIFY telemetryRRSSIChanged)