// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "JFJochViewerToolbarDisplay.h" #include "../ViewerTheme.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); // 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(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); 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(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); } }