File indexing completed on 2024-12-22 04:15:02

0001 /*
0002  * SPDX-FileCopyrightText: 2021 Mathias Wein <lynx.mw+kde@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include "WGActionManager.h"
0008 
0009 #include "WGColorSelectorDock.h"
0010 #include "WGColorPatches.h"
0011 #include "WGColorPreviewToolTip.h"
0012 #include "WGConfigSelectorTypes.h"
0013 #include "WGMyPaintShadeSelector.h"
0014 #include "WGSelectorPopup.h"
0015 #include "WGSelectorWidgetBase.h"
0016 #include "WGShadeSelector.h"
0017 
0018 #include <kis_action.h>
0019 #include <kis_action_manager.h>
0020 #include <kis_canvas2.h>
0021 #include <kis_canvas_resource_provider.h>
0022 #include <kis_display_color_converter.h>
0023 #include <kis_signal_compressor.h>
0024 #include <KisViewManager.h>
0025 #include <KisVisualColorSelector.h>
0026 
0027 #include <QVector4D>
0028 
0029 WGActionManager::WGActionManager(WGColorSelectorDock *parentDock)
0030     : QObject(parentDock)
0031     , m_docker(parentDock)
0032     , m_displayConfig(new WGSelectorDisplayConfig)
0033     , m_colorTooltip(new WGColorPreviewToolTip)
0034     , m_colorChangeCompressor(new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE, this))
0035     , m_colorModel(new KisVisualColorModel)
0036 {
0037     m_lastUsedColor.setOpacity(quint8(0));
0038     connect(m_colorChangeCompressor, SIGNAL(timeout()), SLOT(slotUpdateDocker()));
0039     connect(m_colorModel.data(), SIGNAL(sigChannelValuesChanged(QVector4D,quint32)), SLOT(slotChannelValuesChanged()));
0040     connect(WGConfig::notifier(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
0041     connect(WGConfig::notifier(), SIGNAL(selectorConfigChanged()), SLOT(slotSelectorConfigChanged()));
0042     slotConfigChanged();
0043 }
0044 
0045 WGActionManager::~WGActionManager()
0046 {
0047     delete m_colorSelectorPopup;
0048     delete m_shadeSelectorPopup;
0049     delete m_myPaintSelectorPopup;
0050     delete m_colorHistoryPopup;
0051 }
0052 
0053 void WGActionManager::setCanvas(KisCanvas2 *canvas, KisCanvas2 *oldCanvas)
0054 {
0055     Q_UNUSED(oldCanvas);
0056     KisDisplayColorConverter *converter = canvas ? canvas->displayColorConverter() : 0;
0057     m_displayConfig->setDisplayConverter(converter);
0058     if (m_colorSelector) {
0059         m_colorSelector->setDisplayRenderer(m_displayConfig->displayConverter()->displayRendererInterface());
0060     }
0061 }
0062 
0063 void WGActionManager::registerActions(KisViewManager *viewManager)
0064 {
0065     KisActionManager *actionManager = viewManager->actionManager();
0066     KisAction *action;
0067     action = actionManager->createAction("show_wg_color_selector");
0068     connect(action, SIGNAL(triggered()), SLOT(slotShowColorSelectorPopup()));
0069     action = actionManager->createAction("show_wg_shade_selector");
0070     connect(action, SIGNAL(triggered()), SLOT(slotShowShadeSelectorPopup()));
0071     action = actionManager->createAction("show_wg_mypaint_selector");
0072     connect(action, SIGNAL(triggered()), SLOT(slotShowMyPaintSelectorPopup()));
0073     action = actionManager->createAction("show_wg_color_history");
0074     connect(action, SIGNAL(triggered()), SLOT(slotShowColorHistoryPopup()));
0075     action = actionManager->createAction("wgcs_lighten_color");
0076     connect(action, SIGNAL(triggered(bool)), SLOT(slotIncreaseLightness()));
0077     action = actionManager->createAction("wgcs_darken_color");
0078     connect(action, SIGNAL(triggered(bool)), SLOT(slotDecreaseLightness()));
0079     action = actionManager->createAction("wgcs_increase_saturation");
0080     connect(action, SIGNAL(triggered(bool)), SLOT(slotIncreaseSaturation()));
0081     action = actionManager->createAction("wgcs_decrease_saturation");
0082     connect(action, SIGNAL(triggered(bool)), SLOT(slotDecreaseSaturation()));
0083     action = actionManager->createAction("wgcs_shift_hue_clockwise");
0084     connect(action, SIGNAL(triggered(bool)), SLOT(slotShiftHueCW()));
0085     action = actionManager->createAction("wgcs_shift_hue_counterclockwise");
0086     connect(action, SIGNAL(triggered(bool)), SLOT(slotShiftHueCCW()));
0087 }
0088 
0089 void WGActionManager::setLastUsedColor(const KoColor &col)
0090 {
0091     m_lastUsedColor = col;
0092 }
0093 
0094 void WGActionManager::updateWidgetSize(QWidget *widget, int size)
0095 {
0096     QSizePolicy hsp = widget->sizePolicy();
0097     if (hsp.horizontalPolicy() != QSizePolicy::Fixed) {
0098         widget->setFixedWidth(size);
0099     } else {
0100         widget->setFixedWidth(QWIDGETSIZE_MAX); // looks weird, but that really resets min size to 0...
0101     }
0102     if (hsp.verticalPolicy() != QSizePolicy::Fixed) {
0103         widget->setFixedHeight(size);
0104     } else {
0105         widget->setFixedHeight(QWIDGETSIZE_MAX);
0106     }
0107 }
0108 
0109 void WGActionManager::showPopup(WGSelectorPopup *popup)
0110 {
0111     // preparations
0112     m_isSynchronizing = true;
0113     if (m_currentPopup) {
0114         m_currentPopup->hide();
0115         m_currentPopup = 0;
0116     }
0117     const KisVisualColorModel &dockerModel = m_docker->colorModel();
0118     m_colorModel->copyState(dockerModel);
0119     m_colorTooltip->setLastUsedColor(m_displayConfig->displayConverter()->toQColor(m_lastUsedColor));
0120     QColor baseCol = m_displayConfig->displayConverter()->toQColor(m_colorModel->currentColor());
0121     m_colorTooltip->setCurrentColor(baseCol);
0122     m_colorTooltip->setPreviousColor(baseCol);
0123     m_isSynchronizing = false;
0124 
0125     m_currentPopup = popup;
0126     popup->slotShowPopup();
0127     m_colorTooltip->show(popup);
0128 }
0129 
0130 void WGActionManager::loadColorSelectorSettings(WGConfig::Accessor &cfg)
0131 {
0132     m_colorSelector->setRenderMode(cfg.get(WGConfig::selectorRenderMode));
0133     slotSelectorConfigChanged();
0134 }
0135 
0136 void WGActionManager::modifyHSX(int channel, float amount)
0137 {
0138     if (channel < 0 || channel > 2) {
0139         return;
0140     }
0141     if (m_docker->colorModel().isHSXModel()) {
0142         QVector4D channelValues = m_docker->colorModel().channelValues();
0143         channelValues[channel] = qBound(0.0f, channelValues[channel] + amount, 1.0f);
0144         m_docker->setChannelValues(channelValues);
0145     }
0146 }
0147 
0148 void WGActionManager::slotConfigChanged()
0149 {
0150     WGConfig::Accessor cfg;
0151     int popupSize = cfg.get(WGConfig::popupSize);
0152     bool proofColors = cfg.get(WGConfig::proofToPaintingColors);
0153     m_displayConfig->setPreviewInPaintingCS(proofColors);
0154 
0155     if (m_colorSelector) {
0156         loadColorSelectorSettings(cfg);
0157         m_colorSelector->setProofColors(proofColors);
0158         updateWidgetSize(m_colorSelector, popupSize);
0159     }
0160     if (m_shadeSelector) {
0161         m_shadeSelector->updateSettings();
0162         updateWidgetSize(m_shadeSelector, popupSize);
0163     }
0164     if (m_myPaintSelector) {
0165         m_myPaintSelector->updateSettings();
0166         updateWidgetSize(m_myPaintSelector, popupSize);
0167     }
0168     if (m_colorHistoryPopup) {
0169         WGSelectorWidgetBase *selector = m_colorHistoryPopup->selectorWidget();
0170         KIS_ASSERT(selector);
0171         selector->updateSettings();
0172         updateWidgetSize(selector, popupSize);
0173     }
0174 }
0175 
0176 void WGActionManager::slotSelectorConfigChanged()
0177 {
0178     if (m_colorSelector) {
0179         WGConfig::Accessor cfg;
0180         KisColorSelectorConfiguration selectorConf = cfg.colorSelectorConfiguration();
0181         m_colorSelector->setConfiguration(&selectorConf);
0182     }
0183 }
0184 
0185 void WGActionManager::slotPopupClosed(WGSelectorPopup *popup)
0186 {
0187     if (popup == m_currentPopup) {
0188         m_currentPopup = 0;
0189         m_colorTooltip->hide();
0190     }
0191 }
0192 
0193 void WGActionManager::slotShowColorSelectorPopup()
0194 {
0195     if (!m_colorSelectorPopup) {
0196         WGConfig::Accessor cfg;
0197         m_colorSelectorPopup = new WGSelectorPopup();
0198         m_colorSelector = new KisVisualColorSelector(m_colorSelectorPopup, m_colorModel);
0199         m_colorSelector->setDisplayRenderer(m_displayConfig->displayConverter()->displayRendererInterface());
0200         updateWidgetSize(m_colorSelector, cfg.get(WGConfig::popupSize));
0201         m_colorSelectorPopup->setSelectorWidget(m_colorSelector);
0202         connect(m_colorSelectorPopup, SIGNAL(sigPopupClosed(WGSelectorPopup*)),
0203                 SLOT(slotPopupClosed(WGSelectorPopup*)));
0204         connect(m_colorSelector, SIGNAL(sigInteraction(bool)), SLOT(slotColorInteraction(bool)));
0205 
0206         loadColorSelectorSettings(cfg);
0207     }
0208 
0209     // update gamut mask
0210     KisCanvas2 *canvas = qobject_cast<KisCanvas2*>(m_docker->observedCanvas());
0211     if (canvas) {
0212         KisCanvasResourceProvider *resourceProvider = canvas->imageView()->resourceProvider();
0213         if (resourceProvider->gamutMaskActive()) {
0214             m_colorSelector->slotGamutMaskChanged(resourceProvider->currentGamutMask());
0215         }
0216         else {
0217             m_colorSelector->slotGamutMaskUnset();
0218         }
0219     }
0220 
0221     showPopup(m_colorSelectorPopup);
0222 }
0223 
0224 void WGActionManager::slotShowShadeSelectorPopup()
0225 {
0226     if (!m_shadeSelectorPopup) {
0227         m_shadeSelectorPopup = new WGSelectorPopup();
0228         m_shadeSelector = new WGShadeSelector(m_displayConfig, m_colorModel, m_shadeSelectorPopup);
0229         m_shadeSelector->updateSettings();
0230         updateWidgetSize(m_shadeSelector, WGConfig::Accessor().get(WGConfig::popupSize));
0231         m_shadeSelectorPopup->setSelectorWidget(m_shadeSelector);
0232         connect(m_shadeSelectorPopup, SIGNAL(sigPopupClosed(WGSelectorPopup*)),
0233                 SLOT(slotPopupClosed(WGSelectorPopup*)));
0234         connect(m_shadeSelector, SIGNAL(sigColorInteraction(bool)), SLOT(slotColorInteraction(bool)));
0235     }
0236 
0237     showPopup(m_shadeSelectorPopup);
0238 }
0239 
0240 void WGActionManager::slotShowMyPaintSelectorPopup()
0241 {
0242     if (!m_myPaintSelectorPopup) {
0243         m_myPaintSelectorPopup = new WGSelectorPopup();
0244         m_myPaintSelector = new WGMyPaintShadeSelector(m_displayConfig, m_myPaintSelectorPopup,
0245                                                        WGSelectorWidgetBase::PopupMode);
0246         updateWidgetSize(m_myPaintSelector, WGConfig::Accessor().get(WGConfig::popupSize));
0247         m_myPaintSelector->setModel(m_colorModel);
0248         m_myPaintSelectorPopup->setSelectorWidget(m_myPaintSelector);
0249         connect(m_myPaintSelectorPopup, SIGNAL(sigPopupClosed(WGSelectorPopup*)),
0250                 SLOT(slotPopupClosed(WGSelectorPopup*)));
0251         connect(m_myPaintSelector, SIGNAL(sigColorInteraction(bool)), SLOT(slotColorInteraction(bool)));
0252     }
0253 
0254     showPopup(m_myPaintSelectorPopup);
0255 }
0256 
0257 void WGActionManager::slotShowColorHistoryPopup()
0258 {
0259     if (!m_colorHistoryPopup) {
0260         m_colorHistoryPopup = new WGSelectorPopup;
0261         WGColorPatches *history = new WGColorPatches(m_displayConfig, m_docker->colorHistory());
0262         history->setUiMode(WGSelectorWidgetBase::PopupMode);
0263         history->setPreset(WGColorPatches::History);
0264         history->updateSettings();
0265         updateWidgetSize(history, WGConfig::Accessor().get(WGConfig::popupSize));
0266         m_colorHistoryPopup->setSelectorWidget(history);
0267         connect(m_colorHistoryPopup, SIGNAL(sigPopupClosed(WGSelectorPopup*)),
0268                 SLOT(slotPopupClosed(WGSelectorPopup*)));
0269         connect(history, SIGNAL(sigColorInteraction(bool)), SLOT(slotColorPatchInteraction(bool)));
0270         connect(history, SIGNAL(sigColorChanged(KoColor)), SLOT(slotColorSelected(KoColor)));
0271     }
0272     showPopup(m_colorHistoryPopup);
0273 }
0274 
0275 void WGActionManager::slotIncreaseLightness()
0276 {
0277     modifyHSX(2, 0.1f);
0278 }
0279 
0280 void WGActionManager::slotDecreaseLightness()
0281 {
0282     modifyHSX(2, -0.1f);
0283 }
0284 
0285 void WGActionManager::slotIncreaseSaturation()
0286 {
0287     modifyHSX(1, 0.1f);
0288 }
0289 
0290 void WGActionManager::slotDecreaseSaturation()
0291 {
0292     modifyHSX(1, -0.1f);
0293 }
0294 
0295 void WGActionManager::slotShiftHueCW()
0296 {
0297     modifyHSX(0, 0.1f);
0298 }
0299 
0300 void WGActionManager::slotShiftHueCCW()
0301 {
0302     modifyHSX(0, -0.1f);
0303 }
0304 
0305 void WGActionManager::slotChannelValuesChanged()
0306 {
0307     // FIXME: KoColorDisplayRendererInterface's displayConfigurationChanged()
0308     // signal (e.g. layer switches) makes the color model emit new channel values
0309     // and this would overwrite the color resources with outdated data!
0310     // so make sure a popup is actually active
0311     if (!m_isSynchronizing && m_currentPopup) {
0312         m_colorChangeCompressor->start();
0313         QColor color = m_displayConfig->displayConverter()->toQColor(m_colorModel->currentColor());
0314         m_colorTooltip->setCurrentColor(color);
0315     }
0316 }
0317 
0318 void WGActionManager::slotColorInteraction(bool active)
0319 {
0320     if (active) {
0321         QColor baseCol = m_displayConfig->displayConverter()->toQColor(m_colorModel->currentColor());
0322         m_colorTooltip->setCurrentColor(baseCol);
0323         m_colorTooltip->setPreviousColor(baseCol);
0324     }
0325 }
0326 
0327 void WGActionManager::slotColorPatchInteraction(bool active)
0328 {
0329     KoCanvasBase *canvas = m_docker->observedCanvas();
0330     if (active && canvas) {
0331         KoColor fgColor = canvas->resourceManager()->foregroundColor();
0332         QColor baseCol = m_docker->displayColorConverter()->toQColor(fgColor);
0333         m_colorTooltip->setCurrentColor(baseCol);
0334         m_colorTooltip->setPreviousColor(baseCol);
0335     }
0336 }
0337 
0338 /* Directly set canvas resource for color history,
0339  */
0340 void WGActionManager::slotColorSelected(const KoColor &color)
0341 {
0342     // CAVEAT: currently, the color history does not allow background color setting
0343     if (!m_docker->observedCanvas()) {
0344         return;
0345     }
0346     m_docker->observedCanvas()->resourceManager()->setForegroundColor(color);
0347     QColor previewCol = m_docker->displayColorConverter()->toQColor(color);
0348     m_colorTooltip->setCurrentColor(previewCol);
0349 
0350 }
0351 
0352 void WGActionManager::slotUpdateDocker()
0353 {
0354     m_docker->setChannelValues(m_colorModel->channelValues());
0355 }