diff --git a/viewer/JFJochViewerWindow.cpp b/viewer/JFJochViewerWindow.cpp index 4dc9eaeb7..c5d9f9545 100644 --- a/viewer/JFJochViewerWindow.cpp +++ b/viewer/JFJochViewerWindow.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -163,6 +166,10 @@ JFJochViewerWindow::JFJochViewerWindow(QWidget *parent, bool dbus, const QString splitDockWidget(inspectorDock, magnifierDock, Qt::Vertical); resizeDocks({inspectorDock, magnifierDock}, {600, 400}, Qt::Vertical); menuBar->AddDockEntry(magnifierDock, "Magnifier"); + // Shown by anyone - the Window menu, a layout, a reset - it is no longer the window's to restore. + for (auto *dock: {inspectorDock, magnifierDock}) + connect(dock, &QDockWidget::visibilityChanged, this, + [this, dock](bool visible) { if (visible) autoHiddenDocks.removeAll(dock); }); magnifierWindow = new JFJochHelperWindow(this); magnifierWindow->setWindowTitle("Magnifier"); @@ -719,26 +726,59 @@ void JFJochViewerWindow::ResetAllSettings() { menuBar->ResetFontZoom(); } -// The inspector's width bounds are a character count, not a pixel count, so the value column keeps -// up with the text - the View menu's font zoom or the desktop's scaling. At the default font this -// is the 450/600 px it has always been. The count comes from the application font rather than the -// scroll area's: on a font change the window is told before its children are. +// The inspector's width bounds are measured from its content: at least what its widest section needs +// (folded ones included, see CollapsibleSection) plus the scroll bar, so nothing is cut off, and a third +// more at most - wider only adds empty space next to the text, and takes it from the image. Measured +// rather than counted in characters, as they once were, because a character count that suits one +// platform's font is far too wide on another's (600 px for 400 px of content on macOS). void JFJochViewerWindow::UpdateInspectorWidth() { - const int char_width = QFontMetrics(QApplication::font()).averageCharWidth(); - inspectorScroll->setMinimumWidth(75 * char_width); - inspectorScroll->setMaximumWidth(100 * char_width); + const int need = inspectorScroll->widget()->minimumSizeHint().width() + + inspectorScroll->verticalScrollBar()->sizeHint().width(); + inspectorScroll->setMinimumWidth(need); + inspectorScroll->setMaximumWidth(need * 4 / 3); } void JFJochViewerWindow::changeEvent(QEvent *event) { QMainWindow::changeEvent(event); // The menu bar sets the application font while the window is still being built, so the inspector - // is not there yet on the first one. + // is not there yet on the first one. The window hears of a font change before its children do, + // so the content is measured once they have all taken the new font. if (event->type() == QEvent::FontChange && inspectorScroll) - UpdateInspectorWidth(); + QTimer::singleShot(0, this, &JFJochViewerWindow::UpdateInspectorWidth); +} + +// The image comes first. When shrinking the window would leave it narrower than the inspector next +// to it, the inspector folds away, with the magnifier if it shares the inspector's column. They come +// back once the image would still be at least as wide as the inspector with it restored (plus a +// margin, so a window resized around the threshold does not flicker). Only a shrink folds: a panel +// the user reopens on a narrow window stays open, and one the user closed is never brought back. +void JFJochViewerWindow::resizeEvent(QResizeEvent *event) { + QMainWindow::resizeEvent(event); + if (!inspectorDock || !magnifierDock) + return; + const int image_width = centralWidget()->width(); + const bool shrinking = event->oldSize().width() < 0 || event->size().width() < event->oldSize().width(); + if (shrinking && inspectorDock->isVisible() && image_width < inspectorDock->width()) { + autoHiddenWidth = inspectorDock->width(); + QList docks = {inspectorDock}; + if (magnifierDock->isVisible() && dockWidgetArea(magnifierDock) == dockWidgetArea(inspectorDock)) + docks.append(magnifierDock); + for (auto *dock: docks) { + dock->hide(); + if (!autoHiddenDocks.contains(dock)) + autoHiddenDocks.append(dock); + } + } else if (!autoHiddenDocks.isEmpty() && image_width - autoHiddenWidth >= autoHiddenWidth + 40) { + for (auto *dock: std::exchange(autoHiddenDocks, {})) + dock->show(); + } } void JFJochViewerWindow::closeEvent(QCloseEvent *event) { - // Persist the dock/toolbar arrangement and window geometry so the next launch resumes it. + // Persist the dock/toolbar arrangement and window geometry so the next launch resumes it. Docks + // folded away only for a narrow window are saved as open: the next start may have the room. + for (auto *dock: std::exchange(autoHiddenDocks, {})) + dock->show(); QSettings settings("PSI", "jfjoch_viewer"); settings.setValue("geometry", saveGeometry()); settings.setValue("windowState", saveState(kLayoutVersion)); diff --git a/viewer/JFJochViewerWindow.h b/viewer/JFJochViewerWindow.h index 7e9ecfbbd..67accfb2c 100644 --- a/viewer/JFJochViewerWindow.h +++ b/viewer/JFJochViewerWindow.h @@ -68,6 +68,12 @@ private: void ResetAllSettings(); void UpdateInspectorWidth(); + // A narrow window folds the inspector away (with the magnifier, when it is docked in the same + // column) so the image keeps its room, and brings it back once the window is wide enough again. + QList autoHiddenDocks; // hidden by the window, not by the user + int autoHiddenWidth = 0; // the inspector column's width when it was folded away + void resizeEvent(QResizeEvent *event) override; + // Both ways of opening a dataset from outside the file manager - the File menu and D-Bus - // come through here, so the browser follows the same way for either. void FollowInFileBrowser(const QString &filename); diff --git a/viewer/ViewerTheme.cpp b/viewer/ViewerTheme.cpp index a715ed20d..2ada2c4be 100644 --- a/viewer/ViewerTheme.cpp +++ b/viewer/ViewerTheme.cpp @@ -66,6 +66,10 @@ namespace { pal.setColor(QPalette::Link, QColor(0x8A, 0xB4, 0xF8)); pal.setColor(QPalette::PlaceholderText, QColor(0x80, 0x84, 0xA8)); SetTextColor(pal, DARK_TEXT); + // The derived disabled text is nearly the panel colour, so a greyed-out field looked empty. + // A muted lavender still reads (about 4.8:1 on the panels, against 13:1 for normal text). + for (auto role: {QPalette::WindowText, QPalette::Text, QPalette::ButtonText}) + pal.setColor(QPalette::Disabled, role, QColor(0x85, 0x8A, 0xAE)); return pal; } diff --git a/viewer/jfjoch_viewer.cpp b/viewer/jfjoch_viewer.cpp index 4587560f9..d205029fd 100644 --- a/viewer/jfjoch_viewer.cpp +++ b/viewer/jfjoch_viewer.cpp @@ -127,8 +127,11 @@ int main(int argc, char *argv[]) { QSplashScreen *splash = new QSplashScreen(splash_pixmap); splash->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::SplashScreen); QString version(QString::fromStdString(jfjoch_version())); + // The splash is white in either theme, so its text is black in either theme too. The message is + // rich text, and rich text takes its colour from the palette, not from showMessage's colour. splash->showMessage( - "Jungfrujoch image viewer
Version " + version + "
Paul Scherrer Institut", + "Jungfrujoch image viewer
Version " + version + + "
Paul Scherrer Institut
", Qt::AlignBottom | Qt::AlignHCenter, Qt::black ); diff --git a/viewer/widgets/CollapsibleSection.cpp b/viewer/widgets/CollapsibleSection.cpp index e7eb2a130..106c1c37e 100644 --- a/viewer/widgets/CollapsibleSection.cpp +++ b/viewer/widgets/CollapsibleSection.cpp @@ -4,6 +4,7 @@ #include "CollapsibleSection.h" #include "../ViewerTheme.h" +#include #include #include @@ -45,3 +46,10 @@ void CollapsibleSection::setContentLayout(QLayout *layout) { void CollapsibleSection::setExpanded(bool on) { header_->setChecked(on); } + +QSize CollapsibleSection::minimumSizeHint() const { + // A hidden widget does not count towards its parent's layout, so the folded content is measured here. + QSize s = QWidget::minimumSizeHint(); + s.setWidth(std::max(s.width(), content_->minimumSizeHint().width())); + return s; +} diff --git a/viewer/widgets/CollapsibleSection.h b/viewer/widgets/CollapsibleSection.h index b66720eaa..225b59761 100644 --- a/viewer/widgets/CollapsibleSection.h +++ b/viewer/widgets/CollapsibleSection.h @@ -17,6 +17,8 @@ public: explicit CollapsibleSection(const QString &title, QWidget *parent = nullptr); void setContentLayout(QLayout *layout); void setExpanded(bool on); + // As wide as the content needs even while folded, so a panel keeps its width when a section opens. + QSize minimumSizeHint() const override; private: QToolButton *header_;