// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "JFJochViewerToolbarDisplay.h" #include #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); const QString sliderStyle = "QSlider::groove:horizontal { height:6px; background:#F3D9D4; border-radius:3px; }" "QSlider::sub-page:horizontal { background:#FA7268; border-radius:3px; }" "QSlider::handle:horizontal { background:#1F3A5F; width:14px; margin:-5px 0; border-radius:7px; }" "QSlider::handle:horizontal:hover { background:#16314F; }"; // 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"); foreground_slider->setStyleSheet(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"); background_slider->setStyleSheet(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); b->setStyleSheet(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(ColorScaleEnum::Indigo)); color_map_select->addItem("Viridis", static_cast(ColorScaleEnum::Viridis)); color_map_select->addItem("Magma", static_cast(ColorScaleEnum::Magma)); color_map_select->addItem("Inferno", static_cast(ColorScaleEnum::Inferno)); color_map_select->addItem("Heat", static_cast(ColorScaleEnum::Heat)); color_map_select->addItem("Black on white", static_cast(ColorScaleEnum::BW)); color_map_select->addItem("White on black", static_cast(ColorScaleEnum::WB)); color_map_select->addItem("Green", static_cast(ColorScaleEnum::Green)); color_map_select->setCurrentIndex(static_cast(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::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); } void JFJochViewerToolbarDisplay::foregroundSet(double val) { emit setForeground(static_cast(val)); } void JFJochViewerToolbarDisplay::backgroundSet(double val) { emit setBackground(static_cast(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 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); } }