Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions .agents/qtgui.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ background work and cancellation state.
### Key Components

- **`ColorScreenApplication`**: The application-level document manager. It creates and tracks `MainWindow` instances, routes open requests, manages tab/detached presentation, cycles between documents, and restores per-document crash-recovery sessions.
- **`WorkspaceWindow`**: The primary top-level application shell. Its central `QMdiArea` provides tabbed and subwindow views, while the shell temporarily presents the active document's menu bar, toolbar, and inspector. It owns presentation state only, never image-processing state.
- **`MainWindow`**: One complete image document. It owns the scan, parameters, undo stack, image/navigation widgets, panels, task queues, workers, progress entries, detached docks, and a unique recovery directory. Mutable document state must never be shared between different `MainWindow` instances.
- **`WorkspaceWindow`**: The primary top-level application shell. Its central `QMdiArea` provides tabbed and subwindow views, while the shell temporarily presents the active document's menu bar, toolbar, and inspector. It permanently hosts progress presentation for every logical document represented by an attached primary or secondary view. It owns presentation state only, never image-processing state.
- **`MainWindow`**: One complete image document. It owns the scan, parameters, undo stack, image/navigation widgets, panels, task queues, workers, progress entries, and a unique recovery directory. Mutable document state must never be shared between different `MainWindow` instances.
- **`NavigationView`**: This shows the whole image and indicates zoom and position of ImageWidget.
It lets user to effectively move around the image
- **`ImageWidget`**: This displays the image and provides high-performance interaction. It uses a modular architecture for rendering and event handling to manage complex interaction modes (Pan, Select, SetCenter, etc.).
Expand Down Expand Up @@ -69,9 +69,12 @@ dock. The workspace owns the one status bar for the whole top-level window.
Every attached `MainWindow` and `ImageViewWindow` routes `statusBar()` directly
to that same `QStatusBar`; tabs must not keep private status-message state or
mirror messages when activation changes. Only detached top-level windows use
their private status bars. The active document's transient progress controls are
temporarily moved into the shared status line, while dedicated long-running task
rows remain in the workspace task strip. Their Stop/Cancel buttons use
their private status bars. Every represented document's transient progress controls are attached to the
workspace once, independently of active chrome. The shared status line displays
the most recently started visible document task and provides document-level
previous/next controls when several documents work concurrently; changing tabs
must never choose or hide progress. Dedicated long-running task rows remain in
the workspace task strip. Their Stop/Cancel buttons use
`Qt::TabFocus`: keyboard users can reach them, but a mouse click must not steal
keyboard focus from the active MDI image. Before a keyboard-focused task control
is disabled or its row is removed, return focus to the image presentation that is
Expand Down Expand Up @@ -134,18 +137,22 @@ uses the source `MainWindow` as the authoritative parameter/undo/recovery
model. It never owns or suggests another `.par` file. Its inspector keeps the
standard `NavigationView` above a single **Sharpness** tab, and rendering is
limited to **Original digital capture** and **Image layer**. Measurements are
applied through the source document's undoable parameter path. Detachable
Sharpness diagnostics (MTF, dot spread, finetune images, and adaptive-sharpening
charts) are presentation owned by that reference view and detach into its own
floating docks; they must never be handed to the source document's diagnostic
docks. **Reload and demosaic** reloads both the source scan and every associated
applied through the source document's undoable parameter path. Every panel section created by `ParameterPanel::createDetachableSection()` owns
its own standard floating-dock lifecycle. This is the only detach implementation:
document windows, ordinary views, and specialized reference views do not create
parallel chart docks or probe nested layouts during reattachment. A detached section follows the actual top-level window currently presenting its
inspector and always returns its content when closed. This rule applies uniformly
to document, ordinary-view, and slanted-edge reference inspectors: while embedded,
the workspace owns their floating docks; after detaching a presentation, that
presentation window becomes the dock host. This avoids nested `QMainWindow` dock
ownership without reintroducing panel-specific wiring. **Reload and demosaic** reloads both the source scan and every associated
slanted-edge reference from its own filename using the current demosaic mode.

**Window → New View** creates another MDI view of the same document. Ordinary
views present the same complete Navigation + parameter-panel inspector as the
primary view; the document owns one inspector instance and the active ordinary
view merely presents it, so panel state, undo routing, and detached diagnostic
widgets are never duplicated. Ordinary views also expose the document's Edit
widgets are never duplicated. Ordinary views also expose the document's complete File commands, Edit
and Registration menus and the same canvas-tool toolbar actions as the primary
presentation; render mode, Color/IR, and coordinate controls remain view-local.
Navigation and panel tools that act on an image (crop/area selection, distance
Expand Down
185 changes: 28 additions & 157 deletions src/qtgui/ImageViewWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,88 +46,6 @@ QIcon viewIcon(const char *resource) {
return QIcon(QString::fromLatin1(resource));
}

/** Run CALLBACK when a floating reference-view chart dock is closed. */
class ReferenceDockCloseEventFilter final : public QObject {
public:
ReferenceDockCloseEventFilter(QObject *parent, std::function<void()> callback)
: QObject(parent), m_callback(std::move(callback)) {}

protected:
bool eventFilter(QObject *object, QEvent *event) override {
if (event && event->type() == QEvent::Close && m_callback)
m_callback();
return QObject::eventFilter(object, event);
}

private:
std::function<void()> m_callback;
};

/** Move WIDGET into a floating DOCK using the standard resizable wrapper. */
void detachReferenceWidget(QDockWidget *dock, QWidget *widget) {
if (!dock || !widget || dock->widget())
return;

auto *wrapper = new QFrame();
wrapper->setObjectName(QStringLiteral("DetachedReferenceWrapper"));
wrapper->setFrameStyle(QFrame::Box | QFrame::Plain);
wrapper->setLineWidth(1);
wrapper->setStyleSheet(
QStringLiteral("QFrame#DetachedReferenceWrapper { border: 1px solid #555; "
"background: Palette(Window); }"));

auto *outerLayout = new QVBoxLayout(wrapper);
outerLayout->setContentsMargins(1, 1, 1, 1);
outerLayout->setSpacing(0);

auto *container = new QWidget(wrapper);
auto *gridLayout = new QGridLayout(container);
gridLayout->setContentsMargins(0, 0, 0, 0);
gridLayout->setSpacing(0);
gridLayout->addWidget(widget, 0, 0);
gridLayout->addWidget(new QSizeGrip(container), 0, 0,
Qt::AlignRight | Qt::AlignBottom);
outerLayout->addWidget(container);

dock->setWidget(wrapper);
widget->show();
dock->setFloating(true);
dock->show();
if (widget->sizeHint().isValid()) {
const QSize size = widget->sizeHint();
dock->resize(size.width() + 4, size.height() + 4);
}
}

/** Reattach the non-grip widget currently hosted by DOCK through REATTACH. */
void reattachReferenceWidget(
QDockWidget *dock, const std::function<void(QWidget *)> &reattach) {
if (!dock || !dock->widget())
return;

QWidget *wrapper = dock->widget();
QWidget *originalWidget = nullptr;
if (wrapper->layout() && wrapper->layout()->count() > 0) {
QLayoutItem *containerItem = wrapper->layout()->itemAt(0);
QWidget *container = containerItem ? containerItem->widget() : nullptr;
if (container && container->layout()) {
QLayout *layout = container->layout();
for (int i = 0; i < layout->count(); ++i) {
QWidget *child = layout->itemAt(i)->widget();
if (child && !qobject_cast<QSizeGrip *>(child)) {
originalWidget = child;
break;
}
}
}
}

if (originalWidget && reattach)
reattach(originalWidget);
dock->setWidget(nullptr);
wrapper->deleteLater();
}

} // namespace

/** Return the one status bar belonging to the current top-level window. */
Expand Down Expand Up @@ -230,7 +148,6 @@ ImageViewWindow::~ImageViewWindow() {
m_referenceLoadCondition.wait(
locker, [this]() { return !m_referenceWorkerActive; });
}
restoreReferenceChartDocks();
releaseDocumentInspector();
}

Expand Down Expand Up @@ -377,7 +294,7 @@ void ImageViewWindow::setupUi() {
m_document->appendOrdinaryViewToolActions(m_toolbar);

QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
QAction *closeView = fileMenu->addAction(tr("&Close View"));
QAction *closeView = new QAction(tr("&Close View"), this);
closeView->setShortcut(QKeySequence::Close);
connect(closeView, &QAction::triggered, this, [this]() {
if (auto *application =
Expand All @@ -387,6 +304,33 @@ void ImageViewWindow::setupUi() {
close();
});

QMenu *documentFileMenu = nullptr;
if (m_document) {
for (QAction *menuAction : m_document->menuBar()->actions()) {
if (menuAction && QString(menuAction->text()).remove('&') ==
QStringLiteral("File")) {
documentFileMenu = menuAction->menu();
break;
}
}
}
bool addedCloseView = false;
if (documentFileMenu) {
for (QAction *action : documentFileMenu->actions()) {
if (!action)
continue;
if (QString(action->text()).remove('&') ==
QStringLiteral("Close Window")) {
fileMenu->addAction(closeView);
addedCloseView = true;
} else {
fileMenu->addAction(action);
}
}
}
if (!addedCloseView)
fileMenu->addAction(closeView);

if (!m_slantedEdgeReference && m_document)
if (QAction *edit = m_document->ordinaryViewEditMenuAction())
menuBar()->addAction(edit);
Expand Down Expand Up @@ -504,7 +448,6 @@ void ImageViewWindow::setupReferenceInspector() {
});
connect(m_sharpnessPanel, &SharpnessPanel::measureMtfRequested, this,
&ImageViewWindow::onMeasureMtfRequested);
setupReferenceChartDocks();

splitter->setStretchFactor(0, 0);
splitter->setStretchFactor(1, 1);
Expand All @@ -522,79 +465,7 @@ void ImageViewWindow::setupReferenceInspector() {
}

/** Create floating diagnostic docks owned by this specialized reference view. */
void ImageViewWindow::setupReferenceChartDocks() {
if (!m_slantedEdgeReference || !m_sharpnessPanel)
return;

auto createDock = [this](const QString &title, const QString &objectName,
Qt::DockWidgetArea area) {
auto *dock = new QDockWidget(title, this);
dock->setObjectName(objectName);
dock->setVisible(false);
addDockWidget(area, dock);
return dock;
};

m_referenceMtfDock =
createDock(tr("MTF Chart"), QStringLiteral("SlantedEdgeMTFChartDock"),
Qt::BottomDockWidgetArea);
m_referenceDotSpreadDock = createDock(
tr("Dot Spread Function"), QStringLiteral("SlantedEdgeDotSpreadDock"),
Qt::BottomDockWidgetArea);
m_referenceFinetuneDock = createDock(
tr("Finetune Diagnostic Images"),
QStringLiteral("SlantedEdgeFinetuneImagesDock"),
Qt::RightDockWidgetArea);
m_referenceAdaptiveDock = createDock(
tr("Adaptive Sharpening"), QStringLiteral("SlantedEdgeAdaptiveDock"),
Qt::BottomDockWidgetArea);

auto setupDock = [this](QDockWidget *dock, auto detachSignal,
auto reattachMethod) {
connect(m_sharpnessPanel, detachSignal, this,
[dock](QWidget *widget) { detachReferenceWidget(dock, widget); });
dock->installEventFilter(new ReferenceDockCloseEventFilter(
dock, [this, dock, reattachMethod]() {
if (!m_sharpnessPanel)
return;
reattachReferenceWidget(dock, [this, reattachMethod](QWidget *widget) {
(m_sharpnessPanel->*reattachMethod)(widget);
});
}));
};

setupDock(m_referenceMtfDock, &SharpnessPanel::detachMTFChartRequested,
&SharpnessPanel::reattachMTFChart);
setupDock(m_referenceDotSpreadDock,
&SharpnessPanel::detachDotSpreadRequested,
&SharpnessPanel::reattachDotSpread);
setupDock(m_referenceFinetuneDock,
&SharpnessPanel::detachFinetuneImagesRequested,
&SharpnessPanel::reattachFinetuneImages);
setupDock(m_referenceAdaptiveDock,
&SharpnessPanel::detachAdaptiveChartRequested,
&SharpnessPanel::reattachAdaptiveChart);
}

/** Return any floating reference diagnostics to the Sharpness panel. */
void ImageViewWindow::restoreReferenceChartDocks() {
if (!m_sharpnessPanel)
return;

reattachReferenceWidget(m_referenceMtfDock, [this](QWidget *widget) {
m_sharpnessPanel->reattachMTFChart(widget);
});
reattachReferenceWidget(m_referenceDotSpreadDock, [this](QWidget *widget) {
m_sharpnessPanel->reattachDotSpread(widget);
});
reattachReferenceWidget(m_referenceFinetuneDock, [this](QWidget *widget) {
m_sharpnessPanel->reattachFinetuneImages(widget);
});
reattachReferenceWidget(m_referenceAdaptiveDock, [this](QWidget *widget) {
m_sharpnessPanel->reattachAdaptiveChart(widget);
});
}

/** Load the reference scan asynchronously without touching document filenames. */
void ImageViewWindow::loadReferenceImage(const QString &fileName) {
if (!m_slantedEdgeReference || fileName.isEmpty() || m_referenceLoadPending)
Expand Down
10 changes: 0 additions & 10 deletions src/qtgui/ImageViewWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,6 @@ private slots:
/** Build the reduced Sharpness-only inspector for a reference image. */
void setupReferenceInspector();

/** Create view-owned floating docks for detachable Sharpness diagnostics. */
void setupReferenceChartDocks();

/** Reattach any floating reference diagnostics before view teardown. */
void restoreReferenceChartDocks();

/** Move the owning document's full inspector into a detached ordinary view. */
void claimDocumentInspector();

Expand Down Expand Up @@ -195,10 +189,6 @@ private slots:
MultiLineTabWidget *m_referenceTabs = nullptr;
SharpnessPanel *m_sharpnessPanel = nullptr;
QDockWidget *m_referenceInspectorDock = nullptr;
QDockWidget *m_referenceMtfDock = nullptr;
QDockWidget *m_referenceDotSpreadDock = nullptr;
QDockWidget *m_referenceFinetuneDock = nullptr;
QDockWidget *m_referenceAdaptiveDock = nullptr;
QDockWidget *m_documentInspectorDock = nullptr;
QWidget *m_documentInspectorHost = nullptr;
QPointer<QStatusBar> m_workspaceStatusBar;
Expand Down
Loading
Loading