From 7b65ecace58d14175e83dbc8d8cb925e5c01b329 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Fri, 1 Oct 2021 13:31:22 -0700 Subject: [PATCH] implemented color picker in dialog Fixes #1757 --- app/config/config.cpp | 1 + app/core.cpp | 22 +- app/core.h | 19 ++ app/dialog/color/colordialog.cpp | 1 + app/panel/pixelsampler/pixelsamplerpanel.cpp | 5 + app/panel/viewer/viewerbase.cpp | 8 - app/panel/viewer/viewerbase.h | 2 - app/widget/colorbutton/colorbutton.cpp | 17 +- app/widget/colorbutton/colorbutton.h | 2 + app/widget/colorwheel/colorvalueswidget.cpp | 223 ++++++++++++++++++- app/widget/colorwheel/colorvalueswidget.h | 49 +++- app/widget/viewer/viewer.cpp | 4 + app/window/mainwindow/mainwindow.cpp | 3 - 13 files changed, 328 insertions(+), 28 deletions(-) diff --git a/app/config/config.cpp b/app/config/config.cpp index e212e2bad..d86271af0 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -98,6 +98,7 @@ void Config::SetDefaults() SetEntryInternal(QStringLiteral("ShowWelcomeDialog"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("ShowClipWhileDragging"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false); + SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000); diff --git a/app/core.cpp b/app/core.cpp index d6b314b2f..48830651a 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -82,7 +82,8 @@ Core::Core(const CoreParams& params) : tool_(Tool::kPointer), addable_object_(Tool::kAddableEmpty), snapping_(true), - core_params_(params) + core_params_(params), + pixel_sampling_users_(0) { // Store reference to this object, making the assumption that Core will only ever be made in // main(). This will obviously break if not. @@ -1206,6 +1207,25 @@ void Core::BrowseAutoRecoveries() ard.exec(); } +void Core::RequestPixelSamplingInViewers(bool e) +{ + if (e) { + if (pixel_sampling_users_ == 0) { + // Signal to start pixel sampling + emit ColorPickerEnabled(true); + } + + pixel_sampling_users_++; + } else { + pixel_sampling_users_--; + + if (pixel_sampling_users_ == 0) { + // Signal to end pixel sampling + emit ColorPickerEnabled(false); + } + } +} + bool Core::SaveProjectAs(Project* p) { QFileDialog fd(main_window_, tr("Save Project As")); diff --git a/app/core.h b/app/core.h index a9312599b..3ac9b1e70 100644 --- a/app/core.h +++ b/app/core.h @@ -432,6 +432,8 @@ public slots: void BrowseAutoRecoveries(); + void RequestPixelSamplingInViewers(bool e); + signals: /** * @brief Signal emitted when a project is opened @@ -472,6 +474,18 @@ signals: */ void OpenRecentListChanged(); + /** + * @brief Enable mouse color sampling functionality on all viewers + * + * This can be slow, so we only turn it on when we need it. + */ + void ColorPickerEnabled(bool e); + + /** + * @brief A viewer with color picked enabled has emitted a color + */ + void ColorPickerColorEmitted(const Color &reference, const Color &display); + private: /** * @brief Get the file filter than can be used with QFileDialog to open and save compatible projects @@ -595,6 +609,11 @@ private: */ QVector autorecovered_projects_; + /** + * @brief How many widgets currently need pixel sampling access + */ + int pixel_sampling_users_; + private slots: void SaveAutorecovery(); diff --git a/app/dialog/color/colordialog.cpp b/app/dialog/color/colordialog.cpp index f896cb225..cd36864e9 100644 --- a/app/dialog/color/colordialog.cpp +++ b/app/dialog/color/colordialog.cpp @@ -57,6 +57,7 @@ ColorDialog::ColorDialog(ColorManager* color_manager, const ManagedColor& start, splitter->addWidget(value_area); color_values_widget_ = new ColorValuesWidget(color_manager_); + color_values_widget_->IgnorePickFrom(this); value_layout->addWidget(color_values_widget_); chooser_ = new ColorSpaceChooser(color_manager_); diff --git a/app/panel/pixelsampler/pixelsamplerpanel.cpp b/app/panel/pixelsampler/pixelsamplerpanel.cpp index 24fcc0e21..2f17abb9e 100644 --- a/app/panel/pixelsampler/pixelsamplerpanel.cpp +++ b/app/panel/pixelsampler/pixelsamplerpanel.cpp @@ -20,6 +20,8 @@ #include "pixelsamplerpanel.h" +#include "core.h" + namespace olive { PixelSamplerPanel::PixelSamplerPanel(QWidget *parent) : @@ -28,6 +30,9 @@ PixelSamplerPanel::PixelSamplerPanel(QWidget *parent) : sampler_widget_ = new ManagedPixelSamplerWidget(); SetWidgetWithPadding(sampler_widget_); + connect(this, &PixelSamplerPanel::visibilityChanged, Core::instance(), &Core::RequestPixelSamplingInViewers); + connect(Core::instance(), &Core::ColorPickerColorEmitted, this, &PixelSamplerPanel::SetValues); + Retranslate(); } diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index ed1e456b1..879fe303f 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -72,14 +72,6 @@ void ViewerPanelBase::DisconnectTimeBasedPanel(TimeBasedPanel *panel) disconnect(panel, &TimeBasedPanel::ShuttleRightRequested, this, &ViewerPanelBase::ShuttleRight); } -void ViewerPanelBase::ConnectPixelSamplerPanel(PixelSamplerPanel *psp) -{ - ViewerWidget* vw = static_cast(GetTimeBasedWidget()); - - connect(psp, &PixelSamplerPanel::visibilityChanged, vw, &ViewerWidget::SetSignalCursorColorEnabled); - connect(vw, &ViewerWidget::CursorColor, psp, &PixelSamplerPanel::SetValues); -} - void ViewerPanelBase::SetFullScreen(QScreen *screen) { static_cast(GetTimeBasedWidget())->SetFullScreen(screen); diff --git a/app/panel/viewer/viewerbase.h b/app/panel/viewer/viewerbase.h index 2fc5f92e3..5b9556070 100644 --- a/app/panel/viewer/viewerbase.h +++ b/app/panel/viewer/viewerbase.h @@ -47,8 +47,6 @@ public: void DisconnectTimeBasedPanel(TimeBasedPanel* panel); - void ConnectPixelSamplerPanel(PixelSamplerPanel *psp); - /** * @brief Wrapper for ViewerWidget::SetFullScreen() */ diff --git a/app/widget/colorbutton/colorbutton.cpp b/app/widget/colorbutton/colorbutton.cpp index 8ddb105a8..92cc29b58 100644 --- a/app/widget/colorbutton/colorbutton.cpp +++ b/app/widget/colorbutton/colorbutton.cpp @@ -53,15 +53,26 @@ void ColorButton::SetColor(const ManagedColor &c) void ColorButton::ShowColorDialog() { - ColorDialog cd(color_manager_, color_, this); + ColorDialog *cd = new ColorDialog(color_manager_, color_, this); - if (cd.exec() == QDialog::Accepted) { - color_ = cd.GetSelectedColor(); + connect(cd, &ColorDialog::finished, this, &ColorButton::ColorDialogFinished); + + cd->show(); +} + +void ColorButton::ColorDialogFinished(int e) +{ + ColorDialog *cd = static_cast(sender()); + + if (e == QDialog::Accepted) { + color_ = cd->GetSelectedColor(); UpdateColor(); emit ColorChanged(color_); } + + cd->deleteLater(); } void ColorButton::UpdateColor() diff --git a/app/widget/colorbutton/colorbutton.h b/app/widget/colorbutton/colorbutton.h index 45060e144..c8cad6c95 100644 --- a/app/widget/colorbutton/colorbutton.h +++ b/app/widget/colorbutton/colorbutton.h @@ -45,6 +45,8 @@ signals: private slots: void ShowColorDialog(); + void ColorDialogFinished(int e); + private: void UpdateColor(); diff --git a/app/widget/colorwheel/colorvalueswidget.cpp b/app/widget/colorwheel/colorvalueswidget.cpp index dcd1f93d3..3b88cc22e 100644 --- a/app/widget/colorwheel/colorvalueswidget.cpp +++ b/app/widget/colorwheel/colorvalueswidget.cpp @@ -21,8 +21,12 @@ #include "colorvalueswidget.h" #include +#include #include +#include "config/config.h" +#include "core.h" + namespace olive { ColorValuesWidget::ColorValuesWidget(ColorManager *manager, QWidget *parent) : @@ -47,6 +51,12 @@ ColorValuesWidget::ColorValuesWidget(ColorManager *manager, QWidget *parent) : preview_->setFixedHeight(fontMetrics().height() * 3 / 2); preview_layout->addWidget(preview_); + color_picker_btn_ = new QPushButton(tr("Pick")); + color_picker_btn_->setCheckable(true); + connect(color_picker_btn_, &QPushButton::toggled, this, &ColorValuesWidget::ColorPickedBtnToggled); + connect(Core::instance(), &Core::ColorPickerColorEmitted, this, &ColorValuesWidget::SetReferenceColor); + preview_layout->addWidget(color_picker_btn_); + layout->addLayout(preview_layout); } @@ -54,7 +64,7 @@ ColorValuesWidget::ColorValuesWidget(ColorManager *manager, QWidget *parent) : { QTabWidget* tabs = new QTabWidget(); - input_tab_ = new ColorValuesTab(); + input_tab_ = new ColorValuesTab(true); tabs->addTab(input_tab_, tr("Input")); connect(input_tab_, &ColorValuesTab::ColorChanged, this, &ColorValuesWidget::UpdateValuesFromInput); connect(input_tab_, &ColorValuesTab::ColorChanged, this, &ColorValuesWidget::ColorChanged); @@ -92,6 +102,36 @@ void ColorValuesWidget::SetColorProcessor(ColorProcessorPtr input_to_ref, ColorP preview_->SetColorProcessor(input_to_ref_, ref_to_display_); } +bool ColorValuesWidget::eventFilter(QObject *watcher, QEvent *event) +{ + if (event->type() == QEvent::MouseButtonPress) { + // Should signal to Core to stop pixel sampling and to us to remove our event filter + bool use_this_color = true; + + foreach (QWidget *w, ignore_pick_from_) { + if (w->underMouse()) { + use_this_color = false; + break; + } + } + + if (use_this_color) { + picker_end_color_ = GetColor(); + } + color_picker_btn_->setChecked(false); + return true; + } else if (event->type() == QEvent::KeyPress) { + QKeyEvent *key_ev = static_cast(event); + + if (key_ev->key() == Qt::Key_Escape) { + color_picker_btn_->setChecked(false); + return true; + } + } + + return QWidget::eventFilter(watcher, event); +} + void ColorValuesWidget::SetColor(const Color &c) { input_tab_->SetColor(c); @@ -100,6 +140,13 @@ void ColorValuesWidget::SetColor(const Color &c) UpdateValuesFromInput(); } +void ColorValuesWidget::SetReferenceColor(const Color &c) +{ + reference_tab_->SetColor(c); + + UpdateValuesFromRef(); +} + void ColorValuesWidget::UpdateValuesFromInput() { UpdateRefFromInput(); @@ -118,6 +165,24 @@ void ColorValuesWidget::UpdateValuesFromDisplay() UpdateInputFromRef(); } +void ColorValuesWidget::ColorPickedBtnToggled(bool e) +{ + Core::instance()->RequestPixelSamplingInViewers(e); + + if (e) { + qApp->installEventFilter(this); + + // Store current color in case it needs to be restored + picker_end_color_ = GetColor(); + } else { + qApp->removeEventFilter(this); + + // Restore original color (or use overridden color from eventFilter) + SetColor(picker_end_color_); + emit ColorChanged(picker_end_color_); + } +} + void ColorValuesWidget::UpdateInputFromRef() { if (ref_to_input_) { @@ -157,16 +222,31 @@ void ColorValuesWidget::UpdateRefFromDisplay() } } -ColorValuesTab::ColorValuesTab(QWidget *parent) : +const double ColorValuesTab::kLegacyMultiplier = 255.0; + +ColorValuesTab::ColorValuesTab(bool with_legacy_option, QWidget *parent) : QWidget(parent) { QGridLayout* layout = new QGridLayout(this); int row = 0; + if (with_legacy_option) { + legacy_box_ = new QCheckBox(tr("Use legacy (8-bit) values")); + legacy_box_->setChecked(Config::Current()[QStringLiteral("UseLegacyColorInInputTab")].toBool()); + connect(legacy_box_, &QCheckBox::clicked, this, &ColorValuesTab::LegacyChanged); + layout->addWidget(legacy_box_, row, 0, 1, 2); + row++; + } else { + legacy_box_ = nullptr; + } + + sliders_.resize(3); + layout->addWidget(new QLabel(tr("Red")), row, 0); red_slider_ = CreateColorSlider(); + sliders_[0] = red_slider_; layout->addWidget(red_slider_, row, 1); row++; @@ -174,6 +254,7 @@ ColorValuesTab::ColorValuesTab(QWidget *parent) : layout->addWidget(new QLabel(tr("Green")), row, 0); green_slider_ = CreateColorSlider(); + sliders_[1] = green_slider_; layout->addWidget(green_slider_, row, 1); row++; @@ -181,28 +262,87 @@ ColorValuesTab::ColorValuesTab(QWidget *parent) : layout->addWidget(new QLabel(tr("Blue")), row, 0); blue_slider_ = CreateColorSlider(); + sliders_[2] = blue_slider_; layout->addWidget(blue_slider_, row, 1); + + row++; + + hex_lbl_ = new QLabel(tr("Hex")); + layout->addWidget(hex_lbl_, row, 0); + + hex_slider_ = new StringSlider(); + connect(hex_slider_, &StringSlider::ValueChanged, this, &ColorValuesTab::HexChanged); + layout->addWidget(hex_slider_, row, 1); + + LegacyChanged(AreSlidersLegacyValues()); } Color ColorValuesTab::GetColor() const { - return Color(red_slider_->GetValue(), - green_slider_->GetValue(), - blue_slider_->GetValue()); + return Color(GetRed(), GetGreen(), GetBlue()); } void ColorValuesTab::SetColor(const Color &c) { - red_slider_->SetValue(c.red()); - green_slider_->SetValue(c.green()); - blue_slider_->SetValue(c.blue()); + SetRed(c.red()); + SetGreen(c.green()); + SetBlue(c.blue()); +} + +double ColorValuesTab::GetRed() const +{ + return GetValueInternal(red_slider_); +} + +double ColorValuesTab::GetGreen() const +{ + return GetValueInternal(green_slider_); +} + +double ColorValuesTab::GetBlue() const +{ + return GetValueInternal(blue_slider_); +} + +void ColorValuesTab::SetRed(double r) +{ + SetValueInternal(red_slider_, r); +} + +void ColorValuesTab::SetGreen(double g) +{ + SetValueInternal(green_slider_, g); +} + +void ColorValuesTab::SetBlue(double b) +{ + SetValueInternal(blue_slider_, b); +} + +double ColorValuesTab::GetValueInternal(FloatSlider *slider) const +{ + double d = slider->GetValue(); + + if (AreSlidersLegacyValues()) { + d /= kLegacyMultiplier; + } + + return d; +} + +void ColorValuesTab::SetValueInternal(FloatSlider *slider, double v) +{ + if (AreSlidersLegacyValues()) { + v *= kLegacyMultiplier; + } + + slider->SetValue(v); + UpdateHex(); } FloatSlider *ColorValuesTab::CreateColorSlider() { FloatSlider* fs = new FloatSlider(); - fs->SetDragMultiplier(0.01); - fs->SetDecimalPlaces(5); fs->SetLadderElementCount(1); connect(fs, &FloatSlider::ValueChanged, this, &ColorValuesTab::SliderChanged); return fs; @@ -211,6 +351,69 @@ FloatSlider *ColorValuesTab::CreateColorSlider() void ColorValuesTab::SliderChanged() { emit ColorChanged(GetColor()); + UpdateHex(); +} + +void ColorValuesTab::LegacyChanged(bool legacy) +{ + Config::Current()[QStringLiteral("UseLegacyColorInInputTab")] = legacy; + + double legacy_multiplier = legacy ? kLegacyMultiplier : 1.0/kLegacyMultiplier; + int decimal_places = legacy ? 0 : 5; + double drag_multiplier = legacy ? 1.0 : 0.01; + + foreach (FloatSlider *s, sliders_) { + s->SetValue(s->GetValue() * legacy_multiplier); + s->SetDecimalPlaces(decimal_places); + s->SetDragMultiplier(drag_multiplier); + } + + hex_lbl_->setVisible(legacy); + hex_slider_->setVisible(legacy); + UpdateHex(); +} + +void ColorValuesTab::UpdateHex() +{ + if (AreSlidersLegacyValues()) { + double r = red_slider_->GetValue(); + double g = green_slider_->GetValue(); + double b = blue_slider_->GetValue(); + + if (r > kLegacyMultiplier || g > kLegacyMultiplier || b > kLegacyMultiplier) { + hex_slider_->SetValue(tr("(Invalid)")); + } else { + uint32_t rgb = (uint8_t(r) << 16) | (uint8_t(g) << 8) | uint8_t(b); + + hex_slider_->SetValue(QStringLiteral("%1").arg(rgb, 6, 16, QLatin1Char('0')).toUpper()); + } + } +} + +void ColorValuesTab::HexChanged(const QString &s) +{ + bool ok; + uint32_t hex = s.toULong(&ok, 16); + + if (ok) { + uint32_t r = (hex & 0xFF0000) >> 16; + uint32_t g = (hex & 0x00FF00) >> 8; + uint32_t b = (hex & 0x0000FF); + + red_slider_->SetValue(r); + green_slider_->SetValue(g); + blue_slider_->SetValue(b); + + emit ColorChanged(GetColor()); + } else { + // Return to original value + UpdateHex(); + } +} + +bool ColorValuesTab::AreSlidersLegacyValues() const +{ + return legacy_box_ && legacy_box_->isChecked(); } } diff --git a/app/widget/colorwheel/colorvalueswidget.h b/app/widget/colorwheel/colorvalueswidget.h index 5f0fd48a1..c8822d5f2 100644 --- a/app/widget/colorwheel/colorvalueswidget.h +++ b/app/widget/colorwheel/colorvalueswidget.h @@ -21,12 +21,15 @@ #ifndef COLORVALUESWIDGET_H #define COLORVALUESWIDGET_H +#include +#include #include #include "colorpreviewbox.h" #include "node/color/colormanager/colormanager.h" #include "render/color.h" #include "widget/slider/floatslider.h" +#include "widget/slider/stringslider.h" namespace olive { @@ -34,25 +37,52 @@ class ColorValuesTab : public QWidget { Q_OBJECT public: - ColorValuesTab(QWidget* parent = nullptr); + ColorValuesTab(bool with_legacy_option = false, QWidget* parent = nullptr); Color GetColor() const; void SetColor(const Color& c); + double GetRed() const; + double GetGreen() const; + double GetBlue() const; + void SetRed(double r); + void SetGreen(double g); + void SetBlue(double b); + signals: void ColorChanged(const Color& c); private: + static const double kLegacyMultiplier; + + double GetValueInternal(FloatSlider *slider) const; + void SetValueInternal(FloatSlider *slider, double v); + + bool AreSlidersLegacyValues() const; + FloatSlider* CreateColorSlider(); FloatSlider* red_slider_; FloatSlider* green_slider_; FloatSlider* blue_slider_; + QLabel *hex_lbl_; + StringSlider *hex_slider_; + + QVector sliders_; + + QCheckBox *legacy_box_; + private slots: void SliderChanged(); + void LegacyChanged(bool e); + + void UpdateHex(); + + void HexChanged(const QString &s); + }; class ColorValuesWidget : public QWidget @@ -68,9 +98,18 @@ public: ColorProcessorPtr display_to_ref, ColorProcessorPtr ref_to_input); + virtual bool eventFilter(QObject *watcher, QEvent *event) override; + + void IgnorePickFrom(QWidget *w) + { + ignore_pick_from_.append(w); + } + public slots: void SetColor(const Color& c); + void SetReferenceColor(const Color& c); + signals: void ColorChanged(const Color& c); @@ -101,6 +140,12 @@ private: ColorProcessorPtr ref_to_input_; + QPushButton *color_picker_btn_; + + Color picker_end_color_; + + QVector ignore_pick_from_; + private slots: void UpdateValuesFromInput(); @@ -108,6 +153,8 @@ private slots: void UpdateValuesFromDisplay(); + void ColorPickedBtnToggled(bool e); + }; } diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 2a2510a62..997ce745a 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -36,6 +36,7 @@ #include "common/ratiodialog.h" #include "common/timecodefunctions.h" #include "config/config.h" +#include "core.h" #include "node/project/project.h" #include "render/rendermanager.h" #include "task/taskmanager.h" @@ -128,6 +129,9 @@ ViewerWidget::ViewerWidget(QWidget *parent) : instances_.append(this); setAcceptDrops(true); + + connect(Core::instance(), &Core::ColorPickerEnabled, this, &ViewerWidget::SetSignalCursorColorEnabled); + connect(this, &ViewerWidget::CursorColor, Core::instance(), &Core::ColorPickerColorEmitted); } ViewerWidget::~ViewerWidget() diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 44462d93d..df9797d14 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -114,9 +114,6 @@ MainWindow::MainWindow(QWidget *parent) : sequence_viewer_panel_->ConnectTimeBasedPanel(param_panel_); sequence_viewer_panel_->ConnectTimeBasedPanel(curve_panel_); - footage_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_); - sequence_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_); - UpdateTitle(); QMetaObject::invokeMethod(this, "SetDefaultLayout", Qt::QueuedConnection);