FOSSredder 0.5.0
Loading...
Searching...
No Matches
MainWindow.cpp
Go to the documentation of this file.
1
5
6#include "MainWindow.h"
7
9
10#include <QCloseEvent>
11#include <QEvent>
12#include <QIcon>
13#include <QMessageBox>
14#include <QMetaObject>
15#include <QQmlContext>
16#include <QQmlEngine>
17#include <QQmlImageProviderBase>
18#include <QQuickItem>
19#include <QQuickView>
20#include <QSize>
21#include <QSizePolicy>
22#include <QTimer>
23#include <QWidget>
24#include <qqml.h>
25#include <string>
26#include <utility>
27
28#include "ui/i18n/Text.h"
30#include "ui/shell/AppActions.h"
31#include "ui/shell/AppContext.h"
32#include "ui/shell/Defaults.h"
35#include "ui/shell/QmlRuntime.h"
39
40namespace {
41
43
46void syncRootObjectSize(QQuickView* quickView, QWidget* hostWidget) {
47 if (!quickView || !hostWidget || !quickView->rootObject())
48 return;
49
50 QObject* root = quickView->rootObject();
52 hostWidget->width());
54 hostWidget->height());
55}
56
57} // namespace
58
60 std::shared_ptr<core::ports::diagnostics::IErrorReporter> errorReporter,
61 QWidget* parent)
62 : QMainWindow(parent), errorReporter_(std::move(errorReporter)) {
66
67 setupQuickHost();
68 setupUiContext();
69 setCentralWidget(m_quickContainer);
70
71 setupActionRouting();
72
73 setupQmlRuntime();
74}
75
76void MainWindow::setupQuickHost() {
77 if (m_quickView || m_quickContainer)
78 return;
79
80 m_quickView = new QQuickView();
81 m_quickView->setResizeMode(QQuickView::SizeRootObjectToView);
82 appContext_ = new ui::bootstrap::AppContext(this);
83
84 m_quickContainer = QWidget::createWindowContainer(m_quickView, this);
85 m_quickContainer->setSizePolicy(QSizePolicy::Expanding,
86 QSizePolicy::Expanding);
87 m_quickContainer->setMinimumSize(0, 0);
88 m_quickContainer->setAcceptDrops(true);
89 m_quickContainer->installEventFilter(this);
90}
91
92void MainWindow::setupUiContext() {
93 if (!m_quickView || !m_quickView->rootContext())
94 return;
95
96 if (appContext_) {
97 m_quickView->rootContext()->setContextProperty(
99 }
100
101 const auto services =
102 ui::window::installMainWindowContext(m_quickView->engine(), this);
103 actions_ = services.actions;
104 workspaceStore_ = services.workspaceStore;
105 workspaceCommands_ = services.workspaceCommands;
106 workspaceSelection_ = services.workspaceSelection;
107 workspaceSelectors_ = services.workspaceSelectors;
108 settings_ = services.settings;
109 status_ = services.status;
110
111 setupAutosaveTimer();
112
113 if (appContext_) {
114 appContext_->setActions(services.actions);
115 appContext_->setNavigation(services.navigation);
116 appContext_->setFileSystemBrowser(services.fileSystemBrowser);
117 appContext_->setLanguageService(services.languageService);
118 appContext_->setStatus(services.status);
119#ifdef QT_DEBUG
120 appContext_->setIsDebugBuild(true);
121#else
122 appContext_->setIsDebugBuild(false);
123#endif
124 }
125}
126
127void MainWindow::setupActionRouting() {
129 {actions_, nullptr, workspaceStore_,
130 workspaceCommands_, workspaceSelection_,
131 workspaceSelectors_, nullptr, nullptr,
132 settings_, status_},
133 errorReporter_,
134 [this]() {
135 onAbout();
136 });
137}
138
139void MainWindow::setupQmlRuntime() {
140 auto* engine = m_quickView ? m_quickView->engine() : nullptr;
143}
144
145void MainWindow::prepareForQmlShutdown() {
146 if (qmlShutdownPrepared_ || (!m_quickView && !m_quickContainer))
147 return;
148
149 qmlShutdownPrepared_ = true;
150 auto* quickView = m_quickView;
151 auto* quickContainer = m_quickContainer;
152 m_quickView = nullptr;
153 m_quickContainer = nullptr;
154
155 if (quickContainer)
156 quickContainer->removeEventFilter(this);
157 if (quickView && !quickView->source().isEmpty())
158 quickView->setSource(QUrl());
159 if (quickContainer && centralWidget() == quickContainer)
160 takeCentralWidget();
161 if (quickContainer) {
162 quickContainer->hide();
163 quickContainer->setParent(nullptr);
164 delete quickContainer;
165 } else if (quickView) {
166 delete quickView;
167 }
168}
169
171 prepareForQmlShutdown();
172}
173
174void MainWindow::addImageProvider(const QString& id,
175 QQmlImageProviderBase* provider) {
176 if (!m_quickView)
177 return;
178 if (!m_quickView->engine())
179 return;
180 m_quickView->engine()->addImageProvider(id, provider);
181}
182
183void MainWindow::loadQml(const QUrl& source) {
184 if (!m_quickView)
185 return;
186 if (!source.isEmpty() && m_quickView->source() == source)
187 return;
188
189 if (source.isEmpty()) {
190 m_quickView->loadFromModule(ui::qml::contracts::module::kName,
193 m_quickView, QUrl(QStringLiteral("module:FossRedder/Main")),
194 errorReporter_.get());
195 } else {
196 m_quickView->setSource(source);
197 ui::bootstrap::reportQmlLoadErrors(m_quickView, source,
198 errorReporter_.get());
199 }
200 syncRootObjectSize(m_quickView, m_quickContainer);
201}
202
203QQmlEngine* MainWindow::qmlEngine() const noexcept {
204 return m_quickView ? m_quickView->engine() : nullptr;
205}
206
207bool MainWindow::eventFilter(QObject* obj, QEvent* ev) {
208 if (obj == m_quickContainer && ev->type() == QEvent::Resize) {
209 syncRootObjectSize(m_quickView, m_quickContainer);
210 }
211
212 if (obj == m_quickContainer) {
213 const auto outcome = dropHandler_.handle(ev);
214 if (outcome.handled) {
215 if (ev->type() == QEvent::Drop && outcome.accepted && actions_) {
217 errorReporter_.get(),
219 "Import files dropped", core::errors::ErrorSeverity::Info,
220 ui::window::makeFileListContext(outcome.files));
221 emit actions_->importFilesDropped(outcome.files);
222 if (outcome.files.size() == 1)
223 emit actions_->importFileDropped(outcome.files.first());
224 }
225 return true;
226 }
227 }
228 return QMainWindow::eventFilter(obj, ev);
229}
230
231void MainWindow::setupAutosaveTimer() {
232 if (!autosaveTimer_) {
233 autosaveTimer_ = new QTimer(this);
234 connect(autosaveTimer_, &QTimer::timeout, this, [this]() {
235 if (!workspaceStore_ || workspaceStore_->currentPath().isEmpty()) {
236 return;
237 }
239 errorReporter_.get(),
241 "Periodic autosave requested");
242 emit saveFileRequested();
243 });
244 }
245
246 if (!settings_) {
247 if (autosaveTimer_) {
248 autosaveTimer_->stop();
249 }
250 return;
251 }
252
253 connect(settings_, &ui::Settings::autosaveIntervalMinutesChanged, this,
254 &MainWindow::applyAutosaveSchedule, Qt::UniqueConnection);
255 connect(settings_, &ui::Settings::stateChanged, this,
256 &MainWindow::applyAutosaveSchedule, Qt::UniqueConnection);
257 applyAutosaveSchedule();
258}
259
260void MainWindow::applyAutosaveSchedule() {
261 if (!autosaveTimer_ || !settings_) {
262 return;
263 }
264
265 const int minutes = settings_->autosaveIntervalMinutes();
266 if (minutes <= 0) {
267 autosaveTimer_->stop();
268 return;
269 }
270
271 const int intervalMs = minutes * 60 * 1000;
272 if (autosaveTimer_->isActive() && autosaveTimer_->interval() == intervalMs) {
273 return;
274 }
275
276 autosaveTimer_->start(intervalMs);
277}
278
279void MainWindow::closeEvent(QCloseEvent* event) {
280 if (closeWorkflow_.allowImmediateClose(event)) {
281 prepareForQmlShutdown();
282 QMainWindow::closeEvent(event);
283 return;
284 }
285
286 if (!settings_ || !settings_->autosaveOnClose()) {
288 errorReporter_.get(),
290 "Main window close requested; autosave on close disabled");
291 prepareForQmlShutdown();
292 event->accept();
293 QMainWindow::closeEvent(event);
294 return;
295 }
296
298 errorReporter_.get(),
300 "Main window close requested; triggering save workflow");
301 closeWorkflow_.requestClose(event, [this]() {
302 emit saveFileRequested();
303 });
304}
305
306void MainWindow::handleStorageOperationSucceeded(const QString& operation) {
307 if (!closeWorkflow_.handleStorageOperationSucceeded(
308 operation, ui::config::operationKeys::kSaveFile, [this]() {
309 QMetaObject::invokeMethod(
310 this,
311 [this]() {
312 close();
313 },
314 Qt::QueuedConnection);
315 })) {
316 return;
317 }
318
320 errorReporter_.get(),
322 "Pending close save finished; closing main window");
323}
324
325void MainWindow::handleStorageOperationFailed(const QString& operation,
326 const QString& error) {
327 if (!closeWorkflow_.handleStorageOperationFailed(
329 return;
330
331 const QString message =
332 error.isEmpty() ? ui::text::workflowErrors::storageSaveFailed() : error;
333 if (status_)
334 status_->setText(message);
335
337 errorReporter_.get(),
339 "Pending close save failed; keeping main window open",
341 {{kError, ui::strings::toStdString(message)}});
342
343 QMetaObject::invokeMethod(
344 this,
345 [this]() {
346 close();
347 },
348 Qt::QueuedConnection);
349}
350
351void MainWindow::onAbout() {
352 QMessageBox dialog(this);
353 dialog.setWindowTitle(ui::text::mainWindow::aboutTitle());
354 dialog.setIconPixmap(QIcon(ui::config::kAppIconResource)
357 dialog.setText(ui::text::mainWindow::aboutHeadline());
358 dialog.setInformativeText(ui::text::mainWindow::aboutBody());
359 dialog.setStandardButtons(QMessageBox::Ok);
360 dialog.exec();
361}
Declares QAction wrappers exposed to the UI layer and QML.
Declares the QML application context object.
Defines UI-specific runtime constants that are not shared with core.
Declares the structured diagnostics reporting port.
Main window declaration for the UI application.
Declarations for the UI MainWindowContext component.
Declarations for the UI MainWindowTrace component.
Declarations for the UI Origins component.
QML registration and context property name constants used by the UI.
QML runtime diagnostics reporting helpers.
QML runtime setup helpers for registering types and configuring the engine.
Declares lightweight conversions between common Qt string types and standard strings.
Provides translated C++ UI strings for shell, workflow, adapter and view model code.
void closeEvent(QCloseEvent *event) override
void saveFileRequested()
void loadQml(const QUrl &source=QUrl())
void handleStorageOperationSucceeded(const QString &operation)
void handleStorageOperationFailed(const QString &operation, const QString &error)
QQmlEngine * qmlEngine() const noexcept
bool eventFilter(QObject *obj, QEvent *ev) override
void addImageProvider(const QString &id, QQmlImageProviderBase *provider)
MainWindow(std::shared_ptr< core::ports::diagnostics::IErrorReporter > errorReporter, QWidget *parent=nullptr)
void stateChanged()
Emitted when any settings value changes.
void autosaveIntervalMinutesChanged()
QString currentPath() const
Returns the current workspace path.
void configureRuntime(QQmlEngine *engine)
Configure QQmlEngine import paths and plugin library paths.
void reportQmlLoadErrors(QQuickView *quickView, const QUrl &source, core::ports::diagnostics::IErrorReporter *reporter)
Reports QQuickView load errors through the structured error reporter.
void registerTypes()
Register QML types and meta objects used by the application.
const auto kSaveFile
Operation key emitted for save-file actions.
Definition Defaults.h:106
const auto kApplicationDisplayName
Human-readable application name shown by the UI shell.
Definition Defaults.h:13
const auto kAppIconResource
Resource URL of the application icon embedded into the executable.
Definition Defaults.h:15
constexpr int kAboutDialogIconSize
Pixel size used for the About dialog application icon.
Definition Defaults.h:23
constexpr int kMainWindowDefaultWidth
Default width of the widget-hosted desktop main window.
Definition Defaults.h:19
constexpr int kMainWindowDefaultHeight
Default height of the widget-hosted desktop main window.
Definition Defaults.h:21
constexpr auto kError
Definition Trace.h:24
constexpr auto kMainTypeName
constexpr auto kAppContextTypeName
QString aboutTitle()
About dialog window title.
Definition Text.h:185
QString aboutBody()
About dialog body copy.
Definition Text.h:193
QString aboutHeadline()
About dialog headline.
Definition Text.h:189
QString storageSaveFailed()
Error text for failed storage save operations.
Definition Text.h:223
void reportMainWindowFlow(core::ports::diagnostics::IErrorReporter *reporter, const char *origin, std::string message, core::errors::ErrorSeverity severity=core::errors::ErrorSeverity::Info, core::errors::ErrorContext context={})
Emits a structured main-window flow diagnostic.
MainWindowServices installMainWindowContext(QQmlEngine *qmlEngine, QObject *parent)
Creates shell services used by the main window and QML context.
core::errors::ErrorContext makeFileListContext(const QStringList &files)
Builds trace context for a list of selected or dropped files.
void wireMainWindowActions(MainWindow &window, const MainWindowServices &services, const std::shared_ptr< core::ports::diagnostics::IErrorReporter > &errorReporter, const std::function< void()> &showAbout)
Wires main window actions to dialogs, status text and window signals.