FlightGear next
LauncherMainWindow.cxx
Go to the documentation of this file.
1#include "config.h"
2
4
5// Qt headers
6#include <QMessageBox>
7#include <QSettings>
8#include <QDebug>
9#include <QMenu>
10#include <QMenuBar>
11
12#include <QOpenGLContext>
13#include <QQmlComponent>
14#include <QQmlContext>
15#include <QQmlEngine>
16#include <QQmlError>
17#include <QQmlFileSelector>
18#include <QQuickItem>
19
20// launcher headers
21#include "AddOnsController.hxx"
22#include "AircraftItemModel.hxx"
24#include "GettingStartedTip.hxx"
25#include "LaunchConfig.hxx"
32#include "QtLauncher.hxx"
33#include "UpdateChecker.hxx"
34
36
38
39LauncherMainWindow::LauncherMainWindow(bool inSimMode) : QQuickView()
40{
41 m_controller = new LauncherController(this, this);
42
43 setTitle("FlightGear " + m_controller->versionString());
44
45 int styleTypeId = 0;
46 m_controller->initQML(styleTypeId);
47
48 // use a direct connection to be notified synchronously when the render thread
49 // starts OpenGL. We use this to log the OpenGL information from the
50 // context at that time, for tracing purposes.
51 connect(this, &QQuickWindow::sceneGraphInitialized,
52 this, &LauncherMainWindow::renderTheadSceneGraphInitialized,
53 Qt::DirectConnection);
54
55 m_coloredIconProvider = new QmlColoredImageProvider;
56 engine()->addImageProvider("colored-icon", m_coloredIconProvider);
57
58 if (!inSimMode) {
59#if defined(Q_OS_MAC)
60 QMenuBar* mb = new QMenuBar();
61
62 QMenu* fileMenu = mb->addMenu(tr("File"));
63 QAction* openAction = new QAction(tr("Open saved configuration..."));
64 openAction->setMenuRole(QAction::NoRole);
65 connect(openAction, &QAction::triggered,
66 m_controller, &LauncherController::openConfig);
67
68 QAction* saveAction = new QAction(tr("Save configuration as..."));
69 saveAction->setMenuRole(QAction::NoRole);
70 connect(saveAction, &QAction::triggered,
72
73 fileMenu->addAction(openAction);
74 fileMenu->addAction(saveAction);
75
76 QMenu* toolsMenu = mb->addMenu(tr("Tools"));
77 QAction* restoreDefaultsAction = new QAction(tr("Restore defaults..."));
78 restoreDefaultsAction->setMenuRole(QAction::NoRole);
79 connect(restoreDefaultsAction, &QAction::triggered,
81
82 QAction* changeDataAction = new QAction(tr("Select data files location..."));
83 changeDataAction->setMenuRole(QAction::NoRole);
84 connect(changeDataAction, &QAction::triggered,
86
87 QAction* viewCommandLineAction = new QAction(tr("View command-line"));
88 connect(viewCommandLineAction, &QAction::triggered,
90
91 toolsMenu->addAction(restoreDefaultsAction);
92 toolsMenu->addAction(changeDataAction);
93 toolsMenu->addAction(viewCommandLineAction);
94#endif
95
96 QAction* qa = new QAction(this);
97 qa->setMenuRole(QAction::QuitRole); // will be addeed accordingly
98 qa->setShortcut(QKeySequence("Ctrl+Q"));
99 connect(qa, &QAction::triggered, m_controller, &LauncherController::quit);
100 }
101
102 if (!checkQQC2Availability()) {
103 QMessageBox::critical(nullptr, "Missing required component",
104 tr("Your system is missing a required UI component (QtQuick Controls 2). "
105 "This normally occurs on Linux platforms where Qt is split into many small packages. "
106 "On Ubuntu/Debian systems, the package is called 'qml-module-qtquick-controls2'"));
107 }
108
109 m_coloredIconProvider->loadStyleColors(engine(), styleTypeId);
110
111 connect(this, &QQuickView::statusChanged, this, &LauncherMainWindow::onQuickStatusChanged);
112
113 m_controller->initialRestoreSettings();
114
116#if defined(Q_OS_WIN)
117 const QString osName("win");
118#elif defined(Q_OS_MAC)
119 const QString osName("mac");
120#else
121 const QString osName("unix");
122#endif
123
124 setResizeMode(QQuickView::SizeRootObjectToView);
125 engine()->addImportPath("qrc:///");
126
127 QQmlContext* ctx = rootContext();
128 ctx->setContextProperty("_launcher", m_controller);
129 ctx->setContextProperty("_config", m_controller->config());
130 ctx->setContextProperty("_location", m_controller->location());
131 ctx->setContextProperty("_osName", osName);
132
133 auto updater = new UpdateChecker(this);
134 ctx->setContextProperty("_updates", updater);
135
136 auto packageDelegate = new LauncherPackageDelegate(this);
137 ctx->setContextProperty("_packages", packageDelegate);
138
139 auto notifications = new LauncherNotificationsController{this, engine()};
140 ctx->setContextProperty("_notifications", notifications);
141
142 if (!inSimMode) {
143 auto addOnsCtl = new AddOnsController(this, m_controller->config());
144 ctx->setContextProperty("_addOns", addOnsCtl);
145 }
146
147 auto weatherScenariosModel = new flightgear::WeatherScenariosModel(this);
148 ctx->setContextProperty("_weatherScenarios", weatherScenariosModel);
149
150 setSource(QUrl("qrc:///qml/Launcher.qml"));
151}
152
153void LauncherMainWindow::onQuickStatusChanged(QQuickView::Status status)
154{
155 if (status == QQuickView::Error) {
156 QString errorString;
157
158 Q_FOREACH (auto err, errors()) {
159 errorString.append("\n" + err.toString());
160 }
161
162 QMessageBox::critical(nullptr, "UI loading failures.",
163 tr("Problems occurred loading the user interface. This is usually due to missing modules on your system. "
164 "Please report this error to the FlightGear developer list or forum, and take care to mention your system "
165 "distribution, etc. Please also include the information provided below.\n") +
166 errorString);
167 }
168}
169
170bool LauncherMainWindow::checkQQC2Availability()
171{
172 QQmlComponent comp(engine());
173 comp.setData(R"(
174 import QtQuick.Controls 2.0
175 ScrollBar {
176 }
177 )",
178 {});
179 if (comp.isError()) {
180 return false;
181 }
182
183
184 auto item = comp.create();
185 const bool haveQQC2 = (item != nullptr);
186 if (item)
187 item->deleteLater();
188 return haveQQC2;
189}
190
194
196{
197 if (event->type() == QEvent::Close) {
198 m_controller->saveSettings();
199 }
200 return QQuickView::event(event);
201}
202
204{
205 m_controller->setInAppMode();
206
207 show();
208
209 while (m_controller->keepRunningInAppMode()) {
210 qApp->processEvents();
211 }
212
213 return m_controller->inAppResult();
214}
215
216// this slot runs in the context of the render thread. Don't modify
217void LauncherMainWindow::renderTheadSceneGraphInitialized()
218{
219 auto qContext = QOpenGLContext::currentContext();
220 if (!qContext) {
221 // can happen with alternative QML backends
222 qInfo() << Q_FUNC_INFO << "No current OpenGL context: Metal/D3D QML backend probably active.";
223 return;
224 }
225
226 std::string renderer = (char*)glGetString(GL_RENDERER);
227 // capture this to help with debugging this crash:
228 // https://sentry.io/share/issue/f98e38dceb4241dbaeed944d6ce4d746/
229 // https://bugreports.qt.io/browse/QTBUG-69703
230 flightgear::addSentryTag("qt-gl-vendor", (char*)glGetString(GL_VENDOR));
231 flightgear::addSentryTag("qt-gl-renderer", renderer.c_str());
232 flightgear::addSentryTag("qt-gl-version", (char*)glGetString(GL_VERSION));
233 flightgear::addSentryTag("qt-glsl-version", (char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
234
235 const char* gltype[] = {"Desktop", "GLES 2", "GLES 1"};
236 flightgear::addSentryTag("qt-gl-module-type", gltype[QOpenGLContext::openGLModuleType()]);
237
238 // if necessary, borrow more code from:
239 // https://code.qt.io/cgit/qt/qtbase.git/tree/examples/opengl/contextinfo/widget.cpp?h=5.15#n358
240 // to expand what this reports
241}
bool event(QEvent *event) override
LauncherMainWindow(bool inSimMode)
Heper image provider to allow re-colorizing images based on the active style.
void loadStyleColors(QQmlEngine *engine, int styleTypeId)
static int status
void addSentryTag(const char *, const char *)