Files
Jungfraujoch/viewer/toolbar/JFJochViewerToolbarDisplay.cpp
T
leonarski_fandClaude Opus 5.5 9aae218086 Viewer: background slider on demand, Auto zeroes the background, no black plot line
- The background (black-point) slider is hidden by default. It is shown from
  View > Background slider, or as soon as the background becomes non-zero
  (B + wheel), so an active background is never invisible. The choice is kept
  in QSettings ("backgroundSlider") and cleared by "Reset all settings".
- Auto contrast, continuous or the one-shot A key, sets the background back
  to zero.
- A dataset-info plot without a run colour kept the default QPen: black, and
  near-invisible on the dark theme. This is every plot of a live HTTP dataset,
  whose run list is empty. It now takes #1F77B4 on the light theme and
  #FF7F0E on the dark one (the spots / background colours of the split plot).
  Checked headlessly: the rendered line pixels are exactly those colours on
  the #FFFFFF and #12142B plot backgrounds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:52:16 +02:00

228 lines
9.3 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);
background_label_action = 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);
background_slider_action = addWidget(background_slider);
show_background_action = new QAction("Background slider", this);
show_background_action->setCheckable(true);
connect(show_background_action, &QAction::toggled, this, &JFJochViewerToolbarDisplay::showBackgroundSlider);
showBackgroundSlider(false);
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) {
if (val > 0.0f)
showBackgroundSlider(true);
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::showBackgroundSlider(bool val) {
show_background_action->setChecked(val);
background_label_action->setVisible(val);
background_slider_action->setVisible(val);
}
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);
}
}