Files
Jungfraujoch/viewer/toolbar/JFJochViewerToolbarDisplay.cpp
T
Filip LeonarskiandClaude Opus 5.5 9f369567f1 Viewer: font size and theme buttons on the display toolbar, matching title bar
The right end of the display toolbar now carries three "A" buttons for the
100/125/150 % font sizes and a light/dark switch (a sun with a half-filled
disc), so both settings are visible without opening the View menu. The size
buttons and View > Font size stay in sync through JFJochViewerMenu's new
SetFontZoom / fontZoomChanged; the View > Theme tick follows ThemeNotifier,
so it moves when the toolbar switches the theme.

With Qt 6.8+ an explicit Light or Dark choice is also requested from the OS
(QStyleHints::setColorScheme), so the title bar macOS and Windows draw
matches the window, as apps with their own theme switch do. Follow system
withdraws the request first, since Qt otherwise reports the requested scheme
instead of the desktop's.

The image counter reads "1 / 1800" instead of "1/1800".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 17:51:50 +02:00

215 lines
8.7 KiB
C++

// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochViewerToolbarDisplay.h"
#include "../ViewerTheme.h"
#include <QLabel>
#include "../../common/ColorScale.h"
#include "../RemoteDisplayMode.h"
#include "../widgets/ToolbarIcons.h"
constexpr double MAX_SLIDER_NO_IMAGE = 65000;
JFJochViewerToolbarDisplay::JFJochViewerToolbarDisplay(QWidget *parent)
: QToolBar(parent) {
setWindowTitle("Display"); // shown in the toolbar/dock context menu
foreground_tail_timer_ = new QTimer(this);
foreground_tail_timer_->setSingleShot(true);
connect(foreground_tail_timer_, &QTimer::timeout,
this, &JFJochViewerToolbarDisplay::ApplyPendingForeground);
background_tail_timer_ = new QTimer(this);
background_tail_timer_->setSingleShot(true);
connect(background_tail_timer_, &QTimer::timeout,
this, &JFJochViewerToolbarDisplay::ApplyPendingBackground);
// Two sliders share the strip, so each is capped rather than expanding across the window.
const int slider_cap = fontMetrics().averageCharWidth() * 36;
addWidget(new QLabel(" Foreground ", this));
foreground_slider = new SliderPlusBox(1, MAX_SLIDER_NO_IMAGE, 1.0, 1, this,
SliderPlusBox::ScaleType::Logarithmic);
foreground_slider->setValue(10);
foreground_slider->setToolTip("White point of the color map (image contrast); hold F and use"
" the mouse wheel, Ctrl+wheel for coarse steps");
SetThemedStyleSheet(foreground_slider, SliderStyle);
foreground_slider->setMaximumWidth(slider_cap);
addWidget(foreground_slider);
addWidget(new QLabel(" Background ", this));
background_slider = new SliderPlusBox(0, MAX_SLIDER_NO_IMAGE, 1.0, 1, this,
SliderPlusBox::ScaleType::Logarithmic);
background_slider->setValue(0);
background_slider->setToolTip("Black point of the color map (0 = off); hold B and use"
" the mouse wheel");
SetThemedStyleSheet(background_slider, SliderStyle);
background_slider->setMaximumWidth(slider_cap);
addWidget(background_slider);
auto makeToggle = [this](const QString &text, const QString &tip) {
auto *b = new QToolButton(this);
b->setText(text);
b->setToolButtonStyle(Qt::ToolButtonTextOnly);
b->setCheckable(true);
b->setToolTip(tip);
b->setCursor(Qt::PointingHandCursor);
SetThemedStyleSheet(b, ToolbarIcons::buttonStyle);
return b;
};
auto_foreground_button = makeToggle("Auto", "Auto-contrast to the current image");
addWidget(auto_foreground_button);
hdr_mode_button = makeToggle("HDR", "High dynamic range: show the full intensity range unclipped");
addWidget(hdr_mode_button);
addWidget(new QLabel(" Color map ", this));
// Initialize QComboBox with the options
color_map_select = new QComboBox(this);
color_map_select->addItem("Indigo", static_cast<int>(ColorScaleEnum::Indigo));
color_map_select->addItem("Viridis", static_cast<int>(ColorScaleEnum::Viridis));
color_map_select->addItem("Magma", static_cast<int>(ColorScaleEnum::Magma));
color_map_select->addItem("Inferno", static_cast<int>(ColorScaleEnum::Inferno));
color_map_select->addItem("Heat", static_cast<int>(ColorScaleEnum::Heat));
color_map_select->addItem("Black on white", static_cast<int>(ColorScaleEnum::BW));
color_map_select->addItem("White on black", static_cast<int>(ColorScaleEnum::WB));
color_map_select->addItem("Green", static_cast<int>(ColorScaleEnum::Green));
color_map_select->setCurrentIndex(static_cast<int>(ColorScaleEnum::Indigo));
addWidget(color_map_select);
connect(foreground_slider, &SliderPlusBox::valueChanged, this, &JFJochViewerToolbarDisplay::foregroundSet);
connect(background_slider, &SliderPlusBox::valueChanged, this, &JFJochViewerToolbarDisplay::backgroundSet);
connect(color_map_select, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &JFJochViewerToolbarDisplay::colorComboBoxSet);
connect(auto_foreground_button, &QToolButton::clicked, this, &JFJochViewerToolbarDisplay::autoForegroundButtonPressed);
connect(hdr_mode_button, &QToolButton::clicked, this, &JFJochViewerToolbarDisplay::HDRModeButtonPressed);
auto *stretch = new QWidget(this);
stretch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
addWidget(stretch);
auto makeIconButton = [this](const QIcon &icon, const QString &tip, bool checkable) {
auto *b = new QToolButton(this);
b->setIcon(icon);
b->setIconSize(QSize(20, 20));
b->setToolTip(tip);
b->setCheckable(checkable);
b->setCursor(Qt::PointingHandCursor);
SetThemedStyleSheet(b, ToolbarIcons::buttonStyle);
return b;
};
font_zoom_group = new QButtonGroup(this);
font_zoom_group->setExclusive(true);
for (int step = 0; step < 3; step++) {
const int percent = 100 + 25 * step;
auto *b = makeIconButton(ToolbarIcons::letterA(step), QString("Font size %1 %").arg(percent), true);
font_zoom_group->addButton(b, percent);
addWidget(b);
}
connect(font_zoom_group, &QButtonGroup::idClicked, this, &JFJochViewerToolbarDisplay::fontZoomSelected);
addSeparator();
theme_button = makeIconButton(ToolbarIcons::halfSun(), QString(), false);
addWidget(theme_button);
connect(theme_button, &QToolButton::clicked, this,
[] { SetThemeChoice(DarkTheme() ? ThemeChoice::Light : ThemeChoice::Dark); });
connect(ThemeNotifier::Instance(), &ThemeNotifier::changed, this, &JFJochViewerToolbarDisplay::UpdateThemeButton);
UpdateThemeButton();
}
void JFJochViewerToolbarDisplay::UpdateThemeButton() {
theme_button->setToolTip(DarkTheme() ? "Switch to the light theme" : "Switch to the dark theme");
}
void JFJochViewerToolbarDisplay::updateFontZoom(int percent) {
if (auto *b = font_zoom_group->button(percent))
b->setChecked(true);
}
void JFJochViewerToolbarDisplay::foregroundSet(double val) {
emit setForeground(static_cast<float>(val));
}
void JFJochViewerToolbarDisplay::backgroundSet(double val) {
emit setBackground(static_cast<float>(val));
}
void JFJochViewerToolbarDisplay::updateBackground(float val) {
pending_background_ = val;
if (RemoteDisplayMode() && background_rate_.isValid()
&& background_rate_.elapsed() < kRemoteRepaintIntervalMs) {
background_tail_timer_->start(kRemoteRepaintIntervalMs);
return;
}
background_tail_timer_->stop();
ApplyPendingBackground();
}
void JFJochViewerToolbarDisplay::ApplyPendingBackground() {
background_rate_.restart();
QSignalBlocker blocker(background_slider);
background_slider->setValue(pending_background_);
}
void JFJochViewerToolbarDisplay::updateForeground(float val) {
pending_foreground_ = val;
if (RemoteDisplayMode() && foreground_rate_.isValid()
&& foreground_rate_.elapsed() < kRemoteRepaintIntervalMs) {
foreground_tail_timer_->start(kRemoteRepaintIntervalMs);
return;
}
foreground_tail_timer_->stop();
ApplyPendingForeground();
}
void JFJochViewerToolbarDisplay::ApplyPendingForeground() {
foreground_rate_.restart();
QSignalBlocker blocker(foreground_slider);
foreground_slider->setValue(pending_foreground_);
}
void JFJochViewerToolbarDisplay::colorComboBoxSet(int val) {
emit colorMapChanged(val);
}
void JFJochViewerToolbarDisplay::autoForegroundButtonPressed() {
emit setAutoForeground(auto_foreground_button->isChecked());
}
void JFJochViewerToolbarDisplay::updateAutoForeground(bool val) {
QSignalBlocker blocker(auto_foreground_button);
auto_foreground_button->setChecked(val);
}
void JFJochViewerToolbarDisplay::updateColorMap(int val) {
QSignalBlocker blocker(color_map_select);
color_map_select->setCurrentIndex(val);
}
void JFJochViewerToolbarDisplay::updateHDRMode(bool val) {
QSignalBlocker blocker(hdr_mode_button);
hdr_mode_button->setChecked(val);
}
void JFJochViewerToolbarDisplay::HDRModeButtonPressed() {
emit setHDRMode(hdr_mode_button->isChecked());
}
void JFJochViewerToolbarDisplay::imageLoaded(std::shared_ptr<const JFJochReaderImage> image) {
if (image) {
auto valid = image->ValidMinMax();
if (valid.has_value() && valid->second > 1) {
foreground_slider->setMax(valid->second);
background_slider->setMax(valid->second);
} else {
foreground_slider->setMax(MAX_SLIDER_NO_IMAGE);
background_slider->setMax(MAX_SLIDER_NO_IMAGE);
}
} else {
foreground_slider->setMax(MAX_SLIDER_NO_IMAGE);
background_slider->setMax(MAX_SLIDER_NO_IMAGE);
}
}