14#include <QtCore/QCoreApplication>
15#include <QtCore/QDebug>
16#include <QtCore/QSignalBlocker>
17#include <QtCore/QStringList>
18#include <QtCore/QVector>
19#include <QtGui/QCloseEvent>
20#include <QtWidgets/QHBoxLayout>
21#include <QtWidgets/QLabel>
22#include <QtWidgets/QProgressBar>
23#include <QtWidgets/QStackedWidget>
24#include <QtWidgets/QStatusBar>
33#ifndef QX_APP_FALLBACK_ORGANIZATION
34#define QX_APP_FALLBACK_ORGANIZATION "qtcanpool"
40const char *
const kGroup =
"ui";
41const char *
const kGeometryKey =
"windowGeometry";
42const char *
const kWindowStateKey =
"windowState";
43const char *
const kDockStateKey =
"dockState";
44const char *
const kCurrentPageKey =
"currentPage";
47const int kProgressWidth = 140;
51class QxAppShellPrivate
70 void resyncCurrentPage(
int previousIndex,
const QString &previousId);
71 int indexOfPage(
const QString &
id)
const;
72 QString pageId(
int index)
const;
76 QxToastManager *ensureToastManager()
const;
78 QxWorkspaceManager *ensureWorkspaceManager()
const;
80 QxNavigationBar *m_navigationBar =
Q_NULLPTR;
85 QVector<Page> m_pages;
87 mutable QxToastManager *m_toastManager =
Q_NULLPTR;
88 mutable QxWorkspaceManager *m_workspaceManager =
Q_NULLPTR;
91 int m_currentIndex = -1;
93 bool m_autoSaveLayout =
true;
94 int m_progressMinimum = 0;
95 int m_progressMaximum = 100;
101QxAppShellPrivate::QxAppShellPrivate() =
default;
103QxAppShellPrivate::~QxAppShellPrivate()
119 explicit QxAppShellContext(QxAppShell *shell)
124 void addPage(
const QString &
id,
const QIcon &icon,
const QString &title, QWidget *page)
override
126 m_shell->addPage(
id, icon, title, page);
131 m_shell->setCurrentPage(
id);
136 return m_shell->currentPageId();
140 QWidget *widget)
override
148 m_shell->setStatusMessage(message);
151 void setBusy(
bool busy)
override
153 m_shell->setBusy(busy);
158 m_shell->setProgressRange(minimum, maximum);
163 m_shell->setProgress(value);
168 m_shell->clearProgress();
174 m_shell->showToast(
text,
static_cast<QxToast::Level
>(level), timeoutMs);
185void QxAppShellPrivate::init()
191 QWidget *central =
new QWidget(q);
192 QHBoxLayout *layout =
new QHBoxLayout(central);
193 layout->setContentsMargins(0, 0, 0, 0);
194 layout->setSpacing(0);
196 m_navigationBar =
new QxNavigationBar(central);
197 layout->addWidget(m_navigationBar);
200 layout->addWidget(m_dockWindow, 1);
202 q->setCentralWidget(central);
207 m_pageStack =
new QStackedWidget();
209 workspace->setObjectName(QStringLiteral(
"qxapp_workspace"));
211 m_dockWindow->setCentralWidget(workspace);
213 QObject::connect(m_navigationBar, &QxNavigationBar::currentChanged, q, [
this](
int index) {
214 if (index < -1 || index >= m_pages.count()) {
217 const int previousIndex = m_currentIndex;
218 const QString previousId = pageId(previousIndex);
219 m_currentIndex = index;
220 resyncCurrentPage(previousIndex, previousId);
225 m_statusLabel =
new QLabel(q->statusBar());
226 q->statusBar()->addWidget(m_statusLabel, 1);
228 m_progress =
new QProgressBar(q->statusBar());
229 m_progress->setRange(0, 0);
230 m_progress->setTextVisible(
false);
231 m_progress->setFixedWidth(kProgressWidth);
233 q->statusBar()->addPermanentWidget(m_progress);
236void QxAppShellPrivate::resyncCurrentPage(
int previousIndex,
const QString &previousId)
243 const QSignalBlocker blocker(m_navigationBar);
244 m_navigationBar->setCurrentIndex(m_currentIndex);
246 if (m_currentIndex >= 0) {
247 m_pageStack->setCurrentIndex(m_currentIndex);
250 const QString
id = pageId(m_currentIndex);
251 if (m_currentIndex != previousIndex ||
id != previousId) {
252 Q_EMIT q->currentPageChanged(m_currentIndex,
id);
256int QxAppShellPrivate::indexOfPage(
const QString &
id)
const
258 for (
int i = 0; i < m_pages.count(); ++i) {
259 if (m_pages.at(i).id ==
id) {
266QString QxAppShellPrivate::pageId(
int index)
const
268 if (index < 0 || index >= m_pages.count()) {
271 return m_pages.at(index).id;
277 QString organization = QCoreApplication::organizationName();
278 if (organization.isEmpty()) {
281 QString application = QCoreApplication::applicationName();
282 if (application.isEmpty()) {
283 application = organization;
290QxToastManager *QxAppShellPrivate::ensureToastManager()
const
292 if (!m_toastManager) {
295 m_toastManager =
new QxToastManager(q_ptr, q_ptr);
297 return m_toastManager;
300QxWorkspaceManager *QxAppShellPrivate::ensureWorkspaceManager()
const
302 if (!m_workspaceManager) {
303 m_workspaceManager =
new QxWorkspaceManager(q_ptr, q_ptr);
305 return m_workspaceManager;
308QxAppShell::QxAppShell(QWidget *parent)
310 , d_ptr(new QxAppShellPrivate())
312 d_ptr->setPublic(
this);
328 d->m_context =
new QxAppShellContext(
const_cast<QxAppShell *
>(
this));
336 return d->m_navigationBar;
342 return d->m_dockWindow;
354 if (!
page || index < 0 || index > d->m_pages.count()) {
358 qWarning(
"QxAppShell: a page needs a non-empty id");
361 if (d->indexOfPage(
id) >= 0) {
362 qWarning(
"QxAppShell: a page with id '%s' already exists", qPrintable(
id));
366 QxAppShellPrivate::Page entry;
370 entry.m_widget =
page;
372 const bool wasEmpty = d->m_pages.isEmpty();
373 const int current = d->m_currentIndex;
374 const QString
previous = d->pageId(current);
376 d->m_pages.insert(index, entry);
377 d->m_pageStack->insertWidget(index,
page);
381 const QSignalBlocker blocker(d->m_navigationBar);
382 d->m_navigationBar->insertItem(index, icon, title);
387 d->m_currentIndex = index;
388 }
else if (index <= current) {
390 d->m_currentIndex = current + 1;
392 d->resyncCurrentPage(current,
previous);
398 const int index = d->indexOfPage(
id);
403 const int current = d->m_currentIndex;
404 const QString
previous = d->pageId(current);
406 QWidget *
page = d->m_pages.at(index).m_widget;
407 d->m_pages.removeAt(index);
408 d->m_pageStack->removeWidget(
page);
414 const QSignalBlocker blocker(d->m_navigationBar);
415 d->m_navigationBar->removeItem(index);
418 if (d->m_pages.isEmpty()) {
419 d->m_currentIndex = -1;
420 }
else if (index < current) {
421 d->m_currentIndex = current - 1;
422 }
else if (index == current) {
424 d->m_currentIndex = qMin(current, d->m_pages.count() - 1);
426 d->resyncCurrentPage(current,
previous);
432 return d->m_pages.count();
438 return d->indexOfPage(
id);
444 return d->pageId(index);
450 const int index = d->indexOfPage(
id);
454 return d->m_pages.at(index).m_widget;
460 return d->m_currentIndex;
466 return d->pageId(d->m_currentIndex);
472 return d->m_currentIndex >= 0 ? d->m_pages.at(d->m_currentIndex).m_widget :
Q_NULLPTR;
476 const QString &title, QWidget *widget)
480 qWarning(
"QxAppShell: a dock needs a non-empty id");
483 if (d->m_dockWindow->findDockWidget(
id)) {
484 qWarning(
"QxAppShell: a dock with id '%s' already exists", qPrintable(
id));
489 dock->setObjectName(
id);
493 d->m_dockWindow->addDockWidget(area,
dock);
500 return d->m_dockWindow->findDockWidget(
id);
506 d->m_statusLabel->setText(message);
512 return d->m_statusLabel->text();
518 if (d->m_busy == busy) {
525 d->m_progress->setRange(0, 0);
526 d->m_progress->setTextVisible(
false);
528 d->m_progress->setVisible(busy);
540 if (maximum <= minimum) {
542 qWarning(
"QxAppShell: a progress range has to run to a maximum above %d", minimum);
545 d->m_progressMinimum = minimum;
546 d->m_progressMaximum = maximum;
548 d->m_progress->setRange(minimum, maximum);
555 return d->m_progressMinimum;
561 return d->m_progressMaximum;
570 d->m_progress->setRange(d->m_progressMinimum, d->m_progressMaximum);
571 d->m_progress->setTextVisible(
true);
575 d->m_progress->setValue(qBound(d->m_progressMinimum, value, d->m_progressMaximum));
576 d->m_progress->setVisible(
true);
582 return d->m_progress->value();
589 d->m_progress->setValue(d->m_progressMinimum);
590 d->m_progress->setVisible(
false);
596 return d->m_progress->isVisibleTo(d->m_progress->parentWidget());
602 return d->ensureToastManager();
608 return d->ensureToastManager()->show(
text, level, timeoutMs);
614 return d->ensureWorkspaceManager();
620 return d->ensureWorkspaceManager()->workspaceNames();
626 return d->ensureWorkspaceManager()->saveWorkspace(
name);
632 return d->ensureWorkspaceManager()->applyWorkspace(
name);
638 return d->ensureSettings();
647 delete d->m_settings;
650 d->m_settings->setParent(
this);
660 settings->
setValue(QString::fromLatin1(kWindowStateKey), QMainWindow::saveState());
661 settings->
setValue(QString::fromLatin1(kDockStateKey), d->m_dockWindow->saveState());
672 const QByteArray geometry =
settings->
value(QString::fromLatin1(kGeometryKey)).toByteArray();
673 const QByteArray windowState =
settings->
value(QString::fromLatin1(kWindowStateKey)).toByteArray();
674 const QByteArray dockState =
settings->
value(QString::fromLatin1(kDockStateKey)).toByteArray();
678 if (!geometry.isEmpty()) {
679 restoreGeometry(geometry);
681 if (!windowState.isEmpty()) {
682 QMainWindow::restoreState(windowState);
684 if (!dockState.isEmpty()) {
685 d->m_dockWindow->restoreState(dockState);
695 return d->m_autoSaveLayout;
701 d->m_autoSaveLayout = on;
707 if (index < 0 || index >= d->m_pages.count()) {
710 const int previousIndex = d->m_currentIndex;
711 const QString previousId = d->pageId(previousIndex);
712 d->m_currentIndex = index;
713 d->resyncCurrentPage(previousIndex, previousId);
719 const int index = d->indexOfPage(
id);
729 if (d->m_autoSaveLayout) {
732 RibbonAppWindow::closeEvent(
event);
The window skeleton of a ribbon application.
void setBusy(bool busy)
Shows or hides the busy indicator in the status bar.
void insertPage(int index, const QString &id, const QIcon &icon, const QString &title, QWidget *page)
Inserts a page at the given index of the workspace and of the rail.
QxPlugin::QxPluginContext * pluginContext() const
Returns the QxPluginContext adapter.
void setStatusMessage(const QString &message)
Text of the permanent message line at the left of the status bar.
void closeEvent(QCloseEvent *event) override
int progressMaximum() const
int progressMinimum() const
void restoreLayout()
Restores whatever saveLayout() stored.
QWidget * page(const QString &id) const
The page widget with id, or null.
QString statusMessage() const
QxDock::DockWindow * dockWindow() const
The dock area; owned by the shell.
void addPage(const QString &id, const QIcon &icon, const QString &title, QWidget *page)
Adds a page at the end of the workspace and an entry with the same icon and title to the rail.
bool saveWorkspace(const QString &name)
Stores the current arrangement under name - see QxWorkspaceManager::saveWorkspace().
QxWorkspaceManager * workspaceManager() const
The named layouts of this shell, created on first use and owned by it.
int currentPageIndex() const
QStringList workspaceNames() const
Names of the stored workspaces, in the order they were saved.
void setProgressRange(int minimum, int maximum)
Range the progress runs in, 0 to 100 by default.
bool isProgressVisible() const
Whether the indicator is up, in either of its two modes.
QString pageId(int index) const
Id of the page at the given index, or an empty string.
void saveLayout()
Stores the geometry, the dock layout and the current page.
QString currentPageId() const
int indexOfPage(const QString &id) const
Index of the page with id, or -1 when there is no such page.
QxNavigationBar * navigationBar() const
The navigation rail; owned by the shell.
void clearProgress()
Hides the indicator and puts the progress back at the minimum.
void setAutoSaveLayout(bool on)
bool autoSaveLayout() const
Whether closeEvent() calls saveLayout(); true by default.
QxDock::DockWidget * addDock(Qx::DockWidgetArea area, const QString &id, const QString &title, QWidget *widget)
Creates a dock widget named id, titles it title, fills it with widget and drops it in area.
QxToast * showToast(const QString &text, QxToast::Level level=QxToast::Information, int timeoutMs=QxToast::DefaultTimeout)
Puts a toast over the shell and returns it, so that a caller who needs to can hold on to it or dismis...
bool applyWorkspace(const QString &name)
Brings back the arrangement stored under name - see QxWorkspaceManager::applyWorkspace().
void removePage(const QString &id)
Removes the page with id and its rail entry.
QxDock::DockWidget * dock(const QString &id) const
The dock widget created with addDock() under id, or null.
QxToastManager * toastManager() const
The stack of toasts that shows up over the shell, created on first use and owned by the shell.
void setCurrentPage(int index)
Brings the page at the given index to the front.
void setSettings(QxCore ::QxSettings *settings)
Replaces the settings object; ownership is transferred to the shell.
void setProgress(int value)
Shows how far the work has got, which also shows the indicator - setting a progress means wanting it ...
QWidget * currentPage() const
QxCore::QxSettings * settings() const
The settings object backing persistence; created on first use from the application name and owned by ...
A vertical rail of exclusive, checkable entries.
The stack of toasts of one window.
One short message, shown over the window it belongs to and gone a few seconds later.
Level
What the message is about; it selects the icon and the accent colour.
The named layouts of one shell.
virtual bool event(QEvent *e) override
Thin wrapper around QSettings that keeps call sites free of the usual boilerplate: typed accessors wi...
QSettings * settings() const
The underlying QSettings instance, never null.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
QString stringValue(const QString &key, const QString &defaultValue=QString()) const
void beginGroup(const QString &prefix)
void setValue(const QString &key, const QVariant &value)
The central dock window that maintains the complete docking system.
The thin surface a plugin programs against.
virtual void setStatusMessage(const QString &message)=0
Replaces the host's persistent status-line message.
ToastLevel
Toast severity understood by every host.
virtual QString currentPageId() const =0
Id of the page currently on screen, or an empty string when there is none.
DockArea
Where a newly added dock lands.
virtual void setProgress(int value)=0
Shows how far the work has got, clamped to the range set by setProgressRange().
virtual void setBusy(bool busy)=0
Shows or hides the host's indeterminate "working" indicator.
virtual void showToast(const QString &text, ToastLevel level=ToastLevel::Information, int timeoutMs=-1)=0
Shows a toast over the host; timeoutMs < 0 means the host's default.
virtual QxCore::QxSettings * settings() const =0
The settings object backing persistence; never null.
virtual void addPage(const QString &id, const QIcon &icon, const QString &title, QWidget *page)=0
Adds a page to the host's central workspace and registers it under id, which must be stable and uniqu...
virtual void setProgressRange(int minimum, int maximum)=0
Sets the range the progress runs in; maximum must be above minimum.
virtual QWidget * addDock(DockArea area, const QString &id, const QString &title, QWidget *widget)=0
Creates a dock widget named id, titled title, filled with widget and dropped in area.
virtual void clearProgress()=0
Hides the progress or busy indicator and resets it to the minimum.
virtual void setCurrentPage(const QString &id)=0
Brings the page registered under id to the front.
#define QX_APP_END_NAMESPACE
#define QX_DECLARE_PUBLIC(Class)
#define Q_NULLPTR
Copyleft (C) 2025 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
#define QX_APP_BEGIN_NAMESPACE
#define QX_FINI_PRIVATE()
#define QX_APP_FALLBACK_ORGANIZATION
Where settings land when the application never named itself.
#define QX_CORE_USE_NAMESPACE
#define QX_CORE_PREPEND_NAMESPACE(name)
#define QX_DOCK_USE_NAMESPACE
#define QX_DOCK_PREPEND_NAMESPACE(name)
QtMessageHandler previous
#define QX_PLUGIN_USE_NAMESPACE