diff --git a/krita/pics/Breeze-dark/breeze-dark-icons.qrc b/krita/pics/Breeze-dark/breeze-dark-icons.qrc
index e001e4162b..22a8f234aa 100644
--- a/krita/pics/Breeze-dark/breeze-dark-icons.qrc
+++ b/krita/pics/Breeze-dark/breeze-dark-icons.qrc
@@ -1,89 +1,90 @@
dark_application-exit.svgdark_application-pdf.svgdark_applications-system.svgdark_arrow-down.svgdark_arrow-downright.svgdark_arrow-downleft.svgdark_arrow-topright.svgdark_arrow-topleft.svgdark_arrow-left.svgdark_arrow-right.svgdark_arrow-up.svgdark_bookmarks.svgdark_applications-system.svgdark_configure.svgdark_configure.svgdark_dialog-cancel.svgdark_window-close.svgdark_dialog-ok.svgdark_dialog-warning.svgdark_document-edit.svgdark_document-export.svgdark_document-import.svgdark_document-new.svgdark_document-open-recent.svgdark_document-open.svgdark_document-print-preview.svgdark_configure.svgdark_document-save-as.svgdark_document-save.svgdark_download.svgdark_drive-harddisk.svgdark_edit-clear.svgdark_edit-copy.svgdark_edit-cut.svgdark_edit-delete.svgdark_edit-paste.svgdark_edit-redo.svgdark_edit-undo.svgdark_folder-documents.svgdark_folder-pictures.svgdark_folder.svgdark_format-list-unordered.svgdark_go-home.svgdark_arrow-right.svgdark_arrow-left.svgdark_arrow-up.svgdark_system-help.svgdark_im-user.svgdark_kde.svgdark_layer-visible-off.svgdark_link.svgdark_list-add.svgdark_locked.svgdark_media-playback-start.svgdark_media-playback-stop.svgdark_media-record.svgdark_locked.svgdark_object-rotate-left.svgdark_object-rotate-right.svgdark_unlocked.svgdark_preferences-desktop-color.svgdark_preferences-desktop-display.svgdark_preferences-desktop-locale.svgdark_process-stop.svgdark_select-all.svgdark_select-clear.svgdark_system-help.svg
+ dark_tag.svgdark_tools-report-bug.svgdark_tools-wizard.svgdark_unlocked.svgdark_view-choose.svgdark_view-filter.svgdark_view-fullscreen.svgdark_view-grid.svgdark_view-list-details.svgdark_view-list-text.svgdark_view-preview.svgdark_view-refresh.svgdark_window-close.svgdark_window-new.svgdark_zoom-in.svgdark_zoom-original.svgdark_zoom-out.svg
diff --git a/krita/pics/Breeze-dark/dark_tag.svg b/krita/pics/Breeze-dark/dark_tag.svg
new file mode 100644
index 0000000000..3f6d279aae
--- /dev/null
+++ b/krita/pics/Breeze-dark/dark_tag.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/krita/pics/Breeze-light/breeze-light-icons.qrc b/krita/pics/Breeze-light/breeze-light-icons.qrc
index 55b86f3944..a7688a6699 100644
--- a/krita/pics/Breeze-light/breeze-light-icons.qrc
+++ b/krita/pics/Breeze-light/breeze-light-icons.qrc
@@ -1,90 +1,91 @@
light_application-exit.svglight_application-pdf.svglight_applications-system.svglight_arrow-down.svglight_arrow-downright.svglight_arrow-downleft.svglight_arrow-topright.svglight_arrow-topleft.svglight_arrow-left.svglight_arrow-right.svglight_arrow-up.svglight_bookmarks.svglight_applications-system.svglight_configure.svglight_configure.svglight_dialog-cancel.svglight_window-close.svglight_dialog-ok.svglight_dialog-warning.svglight_document-edit.svglight_document-export.svglight_document-import.svglight_document-new.svglight_document-open-recent.svglight_document-open.svglight_document-print-preview.svglight_document-print.svglight_configure.svglight_document-save-as.svglight_document-save.svglight_download.svglight_drive-harddisk.svglight_edit-clear.svglight_edit-copy.svglight_edit-cut.svglight_edit-delete.svglight_edit-paste.svglight_edit-redo.svglight_edit-undo.svglight_folder-documents.svglight_folder-pictures.svglight_folder.svglight_format-list-unordered.svglight_go-home.svglight_arrow-right.svglight_arrow-left.svglight_arrow-up.svglight_system-help.svglight_im-user.svglight_kde.svglight_layer-visible-off.svglight_link.svglight_list-add.svglight_locked.svglight_media-playback-start.svglight_media-playback-stop.svglight_media-record.svglight_locked.svglight_object-rotate-left.svglight_object-rotate-right.svglight_unlocked.svglight_preferences-desktop-color.svglight_preferences-desktop-display.svglight_preferences-desktop-locale.svglight_process-stop.svglight_select-all.svglight_select-clear.svglight_system-help.svg
+ light_tag.svglight_tools-report-bug.svglight_tools-wizard.svglight_unlocked.svglight_view-choose.svglight_view-filter.svglight_view-fullscreen.svglight_view-grid.svglight_view-list-details.svglight_view-list-text.svglight_view-preview.svglight_view-refresh.svglight_window-close.svglight_window-new.svglight_zoom-in.svglight_zoom-original.svglight_zoom-out.svg
diff --git a/krita/pics/Breeze-light/light_tag.svg b/krita/pics/Breeze-light/light_tag.svg
new file mode 100644
index 0000000000..97b3910b8f
--- /dev/null
+++ b/krita/pics/Breeze-light/light_tag.svg
@@ -0,0 +1,62 @@
+
+
diff --git a/libs/ui/kis_popup_palette.cpp b/libs/ui/kis_popup_palette.cpp
index c883f3ec13..7991b509e6 100644
--- a/libs/ui/kis_popup_palette.cpp
+++ b/libs/ui/kis_popup_palette.cpp
@@ -1,919 +1,920 @@
/* This file is part of the KDE project
Copyright 2009 Vera Lukman
Copyright 2011 Sven Langkamp
Copyright 2016 Scott Petrovic
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kis_canvas2.h"
#include "kis_config.h"
#include "kis_popup_palette.h"
#include "kis_favorite_resource_manager.h"
#include "kis_icon_utils.h"
#include "KisResourceServerProvider.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_signal_compressor.h"
#include "brushhud/kis_brush_hud.h"
#include "brushhud/kis_round_hud_button.h"
#include "kis_signals_blocker.h"
#include "kis_canvas_controller.h"
#include "kis_acyclic_signal_connector.h"
class PopupColorTriangle : public KoTriangleColorSelector
{
public:
PopupColorTriangle(const KoColorDisplayRendererInterface *displayRenderer, QWidget* parent)
: KoTriangleColorSelector(displayRenderer, parent)
, m_dragging(false) {
}
~PopupColorTriangle() override {}
void tabletEvent(QTabletEvent* event) override {
event->accept();
QMouseEvent* mouseEvent = 0;
switch (event->type()) {
case QEvent::TabletPress:
mouseEvent = new QMouseEvent(QEvent::MouseButtonPress, event->pos(),
Qt::LeftButton, Qt::LeftButton, event->modifiers());
m_dragging = true;
mousePressEvent(mouseEvent);
break;
case QEvent::TabletMove:
mouseEvent = new QMouseEvent(QEvent::MouseMove, event->pos(),
(m_dragging) ? Qt::LeftButton : Qt::NoButton,
(m_dragging) ? Qt::LeftButton : Qt::NoButton, event->modifiers());
mouseMoveEvent(mouseEvent);
break;
case QEvent::TabletRelease:
mouseEvent = new QMouseEvent(QEvent::MouseButtonRelease, event->pos(),
Qt::LeftButton,
Qt::LeftButton,
event->modifiers());
m_dragging = false;
mouseReleaseEvent(mouseEvent);
break;
default: break;
}
delete mouseEvent;
}
private:
bool m_dragging;
};
KisPopupPalette::KisPopupPalette(KisViewManager* viewManager, KisCoordinatesConverter* coordinatesConverter ,KisFavoriteResourceManager* manager,
const KoColorDisplayRendererInterface *displayRenderer, KisCanvasResourceProvider *provider, QWidget *parent)
: QWidget(parent, Qt::FramelessWindowHint)
, m_coordinatesConverter(coordinatesConverter)
, m_viewManager(viewManager)
, m_actionManager(viewManager->actionManager())
, m_resourceManager(manager)
, m_displayRenderer(displayRenderer)
, m_colorChangeCompressor(new KisSignalCompressor(50, KisSignalCompressor::POSTPONE))
, m_actionCollection(viewManager->actionCollection())
, m_acyclicConnector(new KisAcyclicSignalConnector(this))
{
// some UI controls are defined and created based off these variables
const int borderWidth = 3;
if (KisConfig(true).readEntry("popuppalette/usevisualcolorselector", false)) {
m_triangleColorSelector = new KisVisualColorSelector(this);
}
else {
m_triangleColorSelector = new PopupColorTriangle(displayRenderer, this);
}
m_triangleColorSelector->setDisplayRenderer(displayRenderer);
m_triangleColorSelector->setConfig(true,false);
m_triangleColorSelector->move(m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth, m_popupPaletteSize/2-m_colorHistoryInnerRadius+borderWidth);
m_triangleColorSelector->resize(m_colorHistoryInnerRadius*2-borderWidth*2, m_colorHistoryInnerRadius*2-borderWidth*2);
m_triangleColorSelector->setVisible(true);
KoColor fgcolor(Qt::black, KoColorSpaceRegistry::instance()->rgb8());
if (m_resourceManager) {
fgcolor = provider->fgColor();
}
m_triangleColorSelector->slotSetColor(fgcolor);
QRegion maskedRegion(0, 0, m_triangleColorSelector->width(), m_triangleColorSelector->height(), QRegion::Ellipse );
m_triangleColorSelector->setMask(maskedRegion);
//setAttribute(Qt::WA_TranslucentBackground, true);
connect(m_triangleColorSelector, SIGNAL(sigNewColor(KoColor)),
m_colorChangeCompressor.data(), SLOT(start()));
connect(m_colorChangeCompressor.data(), SIGNAL(timeout()),
SLOT(slotEmitColorChanged()));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_triangleColorSelector, SLOT(configurationChanged()));
m_acyclicConnector->connectForwardKoColor(m_resourceManager, SIGNAL(sigChangeFGColorSelector(KoColor)),
this, SLOT(slotExternalFgColorChanged(KoColor)));
m_acyclicConnector->connectBackwardKoColor(this, SIGNAL(sigChangefGColor(KoColor)),
m_resourceManager, SIGNAL(sigSetFGColor(KoColor)));
connect(this, SIGNAL(sigChangeActivePaintop(int)), m_resourceManager, SLOT(slotChangeActivePaintop(int)));
connect(this, SIGNAL(sigUpdateRecentColor(int)), m_resourceManager, SLOT(slotUpdateRecentColor(int)));
connect(m_resourceManager, SIGNAL(setSelectedColor(int)), SLOT(slotSetSelectedColor(int)));
connect(m_resourceManager, SIGNAL(updatePalettes()), SLOT(slotUpdate()));
connect(m_resourceManager, SIGNAL(hidePalettes()), SLOT(slotHide()));
// This is used to handle a bug:
// If pop up palette is visible and a new colour is selected, the new colour
// will be added when the user clicks on the canvas to hide the palette
// In general, we want to be able to store recent color if the pop up palette
// is not visible
m_timer.setSingleShot(true);
connect(this, SIGNAL(sigTriggerTimer()), this, SLOT(slotTriggerTimer()));
connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotEnableChangeFGColor()));
connect(this, SIGNAL(sigEnableChangeFGColor(bool)), m_resourceManager, SIGNAL(sigEnableChangeColor(bool)));
setCursor(Qt::ArrowCursor);
setMouseTracking(true);
setHoveredPreset(-1);
setHoveredColor(-1);
setSelectedColor(-1);
m_brushHud = new KisBrushHud(provider, parent);
m_brushHud->setFixedHeight(int(m_popupPaletteSize));
m_brushHud->setVisible(false);
const int auxButtonSize = 35;
m_settingsButton = new KisRoundHudButton(this);
m_settingsButton->setGeometry(m_popupPaletteSize - 2.2 * auxButtonSize, m_popupPaletteSize - auxButtonSize,
auxButtonSize, auxButtonSize);
connect(m_settingsButton, SIGNAL(clicked()), SLOT(slotShowTagsPopup()));
KisConfig cfg(true);
m_brushHudButton = new KisRoundHudButton(this);
m_brushHudButton->setCheckable(true);
m_brushHudButton->setGeometry(m_popupPaletteSize - 1.0 * auxButtonSize, m_popupPaletteSize - auxButtonSize,
auxButtonSize, auxButtonSize);
connect(m_brushHudButton, SIGNAL(toggled(bool)), SLOT(showHudWidget(bool)));
m_brushHudButton->setChecked(cfg.showBrushHud());
// add some stuff below the pop-up palette that will make it easier to use for tablet people
QVBoxLayout* vLayout = new QVBoxLayout(this); // main layout
QSpacerItem* verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
vLayout->addSpacerItem(verticalSpacer); // this should push the box to the bottom
QHBoxLayout* hLayout = new QHBoxLayout();
vLayout->addLayout(hLayout);
mirrorMode = new KisHighlightedToolButton(this);
mirrorMode->setFixedSize(35, 35);
mirrorMode->setToolTip(i18n("Mirror Canvas"));
mirrorMode->setDefaultAction(m_actionCollection->action("mirror_canvas"));
canvasOnlyButton = new KisHighlightedToolButton(this);
canvasOnlyButton->setFixedSize(35, 35);
canvasOnlyButton->setToolTip(i18n("Canvas Only"));
canvasOnlyButton->setDefaultAction(m_actionCollection->action("view_show_canvas_only"));
zoomToOneHundredPercentButton = new QPushButton(this);
zoomToOneHundredPercentButton->setText(i18n("100%"));
zoomToOneHundredPercentButton->setFixedHeight(35);
zoomToOneHundredPercentButton->setToolTip(i18n("Zoom to 100%"));
connect(zoomToOneHundredPercentButton, SIGNAL(clicked(bool)), this, SLOT(slotZoomToOneHundredPercentClicked()));
zoomCanvasSlider = new QSlider(Qt::Horizontal, this);
zoomSliderMinValue = 10; // set in %
zoomSliderMaxValue = 200; // set in %
zoomCanvasSlider->setRange(zoomSliderMinValue, zoomSliderMaxValue);
zoomCanvasSlider->setFixedHeight(35);
zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent());
zoomCanvasSlider->setSingleStep(1);
zoomCanvasSlider->setPageStep(1);
connect(zoomCanvasSlider, SIGNAL(valueChanged(int)), this, SLOT(slotZoomSliderChanged(int)));
connect(zoomCanvasSlider, SIGNAL(sliderPressed()), this, SLOT(slotZoomSliderPressed()));
connect(zoomCanvasSlider, SIGNAL(sliderReleased()), this, SLOT(slotZoomSliderReleased()));
slotUpdateIcons();
hLayout->addWidget(mirrorMode);
hLayout->addWidget(canvasOnlyButton);
hLayout->addWidget(zoomToOneHundredPercentButton);
hLayout->addWidget(zoomCanvasSlider);
setVisible(true);
setVisible(false);
opacityChange = new QGraphicsOpacityEffect(this);
setGraphicsEffect(opacityChange);
// Prevent tablet events from being captured by the canvas
setAttribute(Qt::WA_NoMousePropagation, true);
}
void KisPopupPalette::slotExternalFgColorChanged(const KoColor &color)
{
//hack to get around cmyk for now.
if (color.colorSpace()->colorChannelCount()>3) {
KoColor c(KoColorSpaceRegistry::instance()->rgb8());
c.fromKoColor(color);
m_triangleColorSelector->slotSetColor(c);
} else {
m_triangleColorSelector->slotSetColor(color);
}
}
void KisPopupPalette::slotEmitColorChanged()
{
if (isVisible()) {
update();
emit sigChangefGColor(m_triangleColorSelector->getCurrentColor());
}
}
//setting KisPopupPalette properties
int KisPopupPalette::hoveredPreset() const
{
return m_hoveredPreset;
}
void KisPopupPalette::setHoveredPreset(int x)
{
m_hoveredPreset = x;
}
int KisPopupPalette::hoveredColor() const
{
return m_hoveredColor;
}
void KisPopupPalette::setHoveredColor(int x)
{
m_hoveredColor = x;
}
int KisPopupPalette::selectedColor() const
{
return m_selectedColor;
}
void KisPopupPalette::setSelectedColor(int x)
{
m_selectedColor = x;
}
void KisPopupPalette::slotTriggerTimer()
{
m_timer.start(750);
}
void KisPopupPalette::slotEnableChangeFGColor()
{
emit sigEnableChangeFGColor(true);
}
void KisPopupPalette::slotZoomSliderChanged(int zoom) {
emit zoomLevelChanged(zoom);
}
void KisPopupPalette::slotZoomSliderPressed()
{
m_isZoomingCanvas = true;
}
void KisPopupPalette::slotZoomSliderReleased()
{
m_isZoomingCanvas = false;
}
void KisPopupPalette::adjustLayout(const QPoint &p)
{
KIS_ASSERT_RECOVER_RETURN(m_brushHud);
if (isVisible() && parentWidget()) {
float hudMargin = 30.0;
const QRect fitRect = kisGrowRect(parentWidget()->rect(), -20.0); // -20 is widget margin
const QPoint paletteCenterOffset(m_popupPaletteSize / 2, m_popupPaletteSize / 2);
QRect paletteRect = rect();
paletteRect.moveTo(p - paletteCenterOffset);
if (m_brushHudButton->isChecked()) {
m_brushHud->updateGeometry();
paletteRect.adjust(0, 0, m_brushHud->width() + hudMargin, 0);
}
paletteRect = kisEnsureInRect(paletteRect, fitRect);
move(paletteRect.topLeft());
m_brushHud->move(paletteRect.topLeft() + QPoint(m_popupPaletteSize + hudMargin, 0));
m_lastCenterPoint = p;
}
}
void KisPopupPalette::slotUpdateIcons()
{
zoomToOneHundredPercentButton->setIcon(m_actionCollection->action("zoom_to_100pct")->icon());
m_brushHud->updateIcons();
+ m_settingsButton->setIcon(KisIconUtils::loadIcon("tag"));
m_brushHudButton->setOnOffIcons(KisIconUtils::loadIcon("arrow-left"), KisIconUtils::loadIcon("arrow-right"));
}
void KisPopupPalette::showHudWidget(bool visible)
{
KIS_ASSERT_RECOVER_RETURN(m_brushHud);
const bool reallyVisible = visible && m_brushHudButton->isChecked();
if (reallyVisible) {
m_brushHud->updateProperties();
}
m_brushHud->setVisible(reallyVisible);
adjustLayout(m_lastCenterPoint);
KisConfig cfg(false);
cfg.setShowBrushHud(visible);
}
void KisPopupPalette::showPopupPalette(const QPoint &p)
{
showPopupPalette(!isVisible());
adjustLayout(p);
}
void KisPopupPalette::showPopupPalette(bool show)
{
if (show) {
m_hadMousePressSinceOpening = false;
m_timeSinceOpening.start();
// don't set the zoom slider if we are outside of the zoom slider bounds. It will change the zoom level to within
// the bounds and cause the canvas to jump between the slider's min and max
if (m_coordinatesConverter->zoomInPercent() > zoomSliderMinValue &&
m_coordinatesConverter->zoomInPercent() < zoomSliderMaxValue ){
KisSignalsBlocker b(zoomCanvasSlider);
zoomCanvasSlider->setValue(m_coordinatesConverter->zoomInPercent()); // sync the zoom slider
}
emit sigEnableChangeFGColor(!show);
} else {
emit sigTriggerTimer();
}
setVisible(show);
m_brushHud->setVisible(show && m_brushHudButton->isChecked());
}
//redefinition of setVariable function to change the scope to private
void KisPopupPalette::setVisible(bool b)
{
QWidget::setVisible(b);
}
void KisPopupPalette::setParent(QWidget *parent) {
m_brushHud->setParent(parent);
QWidget::setParent(parent);
}
QSize KisPopupPalette::sizeHint() const
{
return QSize(m_popupPaletteSize, m_popupPaletteSize + 50); // last number is the space for the toolbar below
}
void KisPopupPalette::resizeEvent(QResizeEvent*)
{
}
void KisPopupPalette::paintEvent(QPaintEvent* e)
{
Q_UNUSED(e);
QPainter painter(this);
QPen pen(palette().color(QPalette::Text));
pen.setWidth(3);
painter.setPen(pen);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
// painting background color indicator
QPainterPath bgColor;
bgColor.addEllipse(QPoint( 50, 80), 30, 30);
painter.fillPath(bgColor, m_displayRenderer->toQColor(m_resourceManager->bgColor()));
painter.drawPath(bgColor);
// painting foreground color indicator
QPainterPath fgColor;
fgColor.addEllipse(QPoint( 60, 50), 30, 30);
painter.fillPath(fgColor, m_displayRenderer->toQColor(m_triangleColorSelector->getCurrentColor()));
painter.drawPath(fgColor);
// create a circle background that everything else will go into
QPainterPath backgroundContainer;
float shrinkCircleAmount = 3;// helps the circle when the stroke is put around it
QRectF circleRect(shrinkCircleAmount, shrinkCircleAmount,
m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2);
backgroundContainer.addEllipse( circleRect );
painter.fillPath(backgroundContainer,palette().brush(QPalette::Background));
painter.drawPath(backgroundContainer);
// create a path slightly inside the container circle. this will create a 'track' to indicate that we can rotate the canvas
// with the indicator
QPainterPath rotationTrackPath;
shrinkCircleAmount = 18;
QRectF circleRect2(shrinkCircleAmount, shrinkCircleAmount,
m_popupPaletteSize - shrinkCircleAmount*2,m_popupPaletteSize - shrinkCircleAmount*2);
rotationTrackPath.addEllipse( circleRect2 );
pen.setWidth(1);
painter.setPen(pen);
painter.drawPath(rotationTrackPath);
// this thing will help indicate where the starting brush preset is at.
// also what direction they go to give sor order to the presets populated
/*
pen.setWidth(6);
pen.setCapStyle(Qt::RoundCap);
painter.setPen(pen);
painter.drawArc(circleRect, (16*90), (16*-30)); // span angle (last parameter) is in 16th of degrees
QPainterPath brushDir;
brushDir.arcMoveTo(circleRect, 60);
brushDir.lineTo(brushDir.currentPosition().x()-5, brushDir.currentPosition().y() - 14);
painter.drawPath(brushDir);
brushDir.lineTo(brushDir.currentPosition().x()-2, brushDir.currentPosition().y() + 6);
painter.drawPath(brushDir);
*/
// the following things needs to be based off the center, so let's translate the painter
painter.translate(m_popupPaletteSize / 2, m_popupPaletteSize / 2);
// create the canvas rotation handle
QPainterPath rotationIndicator = drawRotationIndicator(m_coordinatesConverter->rotationAngle(), true);
painter.fillPath(rotationIndicator,palette().brush(QPalette::Text));
// hover indicator for the canvas rotation
if (m_isOverCanvasRotationIndicator == true) {
painter.save();
QPen pen(palette().color(QPalette::Highlight));
pen.setWidth(2);
painter.setPen(pen);
painter.drawPath(rotationIndicator);
painter.restore();
}
// create a reset canvas rotation indicator to bring the canvas back to 0 degrees
QPainterPath resetRotationIndicator = drawRotationIndicator(0, false);
QPen resetPen(palette().color(QPalette::Text));
resetPen.setWidth(1);
painter.save();
painter.setPen(resetPen);
painter.drawPath(resetRotationIndicator);
painter.restore();
// painting favorite brushes
QList images(m_resourceManager->favoritePresetImages());
// painting favorite brushes pixmap/icon
QPainterPath presetPath;
for (int pos = 0; pos < numSlots(); pos++) {
painter.save();
presetPath = createPathFromPresetIndex(pos);
if (pos < images.size()) {
painter.setClipPath(presetPath);
QRect bounds = presetPath.boundingRect().toAlignedRect();
painter.drawImage(bounds.topLeft() , images.at(pos).scaled(bounds.size() , Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation));
}
else {
painter.fillPath(presetPath, palette().brush(QPalette::Window)); // brush slot that has no brush in it
}
QPen pen = painter.pen();
pen.setWidth(1);
painter.setPen(pen);
painter.drawPath(presetPath);
painter.restore();
}
if (hoveredPreset() > -1) {
presetPath = createPathFromPresetIndex(hoveredPreset());
QPen pen(palette().color(QPalette::Highlight));
pen.setWidth(3);
painter.setPen(pen);
painter.drawPath(presetPath);
}
// paint recent colors area.
painter.setPen(Qt::NoPen);
float rotationAngle = -360.0 / m_resourceManager->recentColorsTotal();
// there might be no recent colors at the start, so paint a placeholder
if (m_resourceManager->recentColorsTotal() == 0) {
painter.setBrush(Qt::transparent);
QPainterPath emptyRecentColorsPath(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
painter.setPen(QPen(palette().color(QPalette::Background).lighter(150), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
painter.drawPath(emptyRecentColorsPath);
} else {
for (int pos = 0; pos < m_resourceManager->recentColorsTotal(); pos++) {
QPainterPath recentColorsPath(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal()));
//accessing recent color of index pos
painter.fillPath(recentColorsPath, m_displayRenderer->toQColor( m_resourceManager->recentColorAt(pos) ));
painter.drawPath(recentColorsPath);
painter.rotate(rotationAngle);
}
}
// painting hovered color
if (hoveredColor() > -1) {
painter.setPen(QPen(palette().color(QPalette::Highlight), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
if (m_resourceManager->recentColorsTotal() == 1) {
QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
painter.drawPath(path_ColorDonut);
} else {
painter.rotate((m_resourceManager->recentColorsTotal() + hoveredColor()) *rotationAngle);
QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal()));
painter.drawPath(path);
painter.rotate(hoveredColor() * -1 * rotationAngle);
}
}
// painting selected color
if (selectedColor() > -1) {
painter.setPen(QPen(palette().color(QPalette::Highlight).darker(130), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));
if (m_resourceManager->recentColorsTotal() == 1) {
QPainterPath path_ColorDonut(drawDonutPathFull(0, 0, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
painter.drawPath(path_ColorDonut);
} else {
painter.rotate((m_resourceManager->recentColorsTotal() + selectedColor()) *rotationAngle);
QPainterPath path(drawDonutPathAngle(m_colorHistoryInnerRadius, m_colorHistoryOuterRadius, m_resourceManager->recentColorsTotal()));
painter.drawPath(path);
painter.rotate(selectedColor() * -1 * rotationAngle);
}
}
// if we are actively rotating the canvas or zooming, make the panel slightly transparent to see the canvas better
if(m_isRotatingCanvasIndicator || m_isZoomingCanvas) {
opacityChange->setOpacity(0.4);
} else {
opacityChange->setOpacity(1.0);
}
}
QPainterPath KisPopupPalette::drawDonutPathFull(int x, int y, int inner_radius, int outer_radius)
{
QPainterPath path;
path.addEllipse(QPointF(x, y), outer_radius, outer_radius);
path.addEllipse(QPointF(x, y), inner_radius, inner_radius);
path.setFillRule(Qt::OddEvenFill);
return path;
}
QPainterPath KisPopupPalette::drawDonutPathAngle(int inner_radius, int outer_radius, int limit)
{
QPainterPath path;
path.moveTo(-0.999 * outer_radius * sin(M_PI / limit), 0.999 * outer_radius * cos(M_PI / limit));
path.arcTo(-1 * outer_radius, -1 * outer_radius, 2 * outer_radius, 2 * outer_radius, -90.0 - 180.0 / limit,
360.0 / limit);
path.arcTo(-1 * inner_radius, -1 * inner_radius, 2 * inner_radius, 2 * inner_radius, -90.0 + 180.0 / limit,
- 360.0 / limit);
path.closeSubpath();
return path;
}
QPainterPath KisPopupPalette::drawRotationIndicator(qreal rotationAngle, bool canDrag)
{
// used for canvas rotation. This function gets called twice. Once by the canvas rotation indicator,
// and another time by the reset canvas position
float canvasRotationRadians = qDegreesToRadians(rotationAngle - 90); // -90 will make 0 degrees be at the top
float rotationDialXPosition = qCos(canvasRotationRadians) * (m_popupPaletteSize/2 - 10); // m_popupPaletteSize/2 = radius
float rotationDialYPosition = qSin(canvasRotationRadians) * (m_popupPaletteSize/2 - 10);
QPainterPath canvasRotationIndicator;
int canvasIndicatorSize = 15;
float canvasIndicatorMiddle = canvasIndicatorSize/2;
QRect indicatorRectangle = QRect( rotationDialXPosition - canvasIndicatorMiddle, rotationDialYPosition - canvasIndicatorMiddle,
canvasIndicatorSize, canvasIndicatorSize );
if (canDrag) {
m_canvasRotationIndicatorRect = indicatorRectangle;
} else {
m_resetCanvasRotationIndicatorRect = indicatorRectangle;
}
canvasRotationIndicator.addEllipse(indicatorRectangle.x(), indicatorRectangle.y(),
indicatorRectangle.width(), indicatorRectangle.height() );
return canvasRotationIndicator;
}
void KisPopupPalette::mouseMoveEvent(QMouseEvent *event)
{
QPointF point = event->localPos();
event->accept();
setToolTip(QString());
setHoveredPreset(-1);
setHoveredColor(-1);
// calculate if we are over the canvas rotation knob
// before we started painting, we moved the painter to the center of the widget, so the X/Y positions are offset. we need to
// correct them first before looking for a click event intersection
float rotationCorrectedXPos = m_canvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2);
float rotationCorrectedYPos = m_canvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2);
QRect correctedCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos,
m_canvasRotationIndicatorRect.width(), m_canvasRotationIndicatorRect.height());
if (correctedCanvasRotationIndicator.contains(point.x(), point.y())) {
m_isOverCanvasRotationIndicator = true;
} else {
m_isOverCanvasRotationIndicator = false;
}
if (m_isRotatingCanvasIndicator) {
// we are rotating the canvas, so calculate the rotation angle based off the center
// calculate the angle we are at first
QPoint widgetCenterPoint = QPoint(m_popupPaletteSize/2, m_popupPaletteSize/2);
float dX = point.x() - widgetCenterPoint.x();
float dY = point.y() - widgetCenterPoint.y();
float finalAngle = qAtan2(dY,dX) * 180 / M_PI; // what we need if we have two points, but don't know the angle
finalAngle = finalAngle + 90; // add 90 degrees so 0 degree position points up
float angleDifference = finalAngle - m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs, so find it out
KisCanvasController *canvasController =
dynamic_cast(m_viewManager->canvasBase()->canvasController());
canvasController->rotateCanvas(angleDifference);
emit sigUpdateCanvas();
}
// don't highlight the presets if we are in the middle of rotating the canvas
if (m_isRotatingCanvasIndicator == false) {
QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
{
int pos = calculatePresetIndex(point, m_resourceManager->numFavoritePresets());
if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()) {
setToolTip(m_resourceManager->favoritePresetList().at(pos).data()->name());
setHoveredPreset(pos);
}
}
if (pathColor.contains(point)) {
int pos = calculateIndex(point, m_resourceManager->recentColorsTotal());
if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) {
setHoveredColor(pos);
}
}
}
update();
}
void KisPopupPalette::mousePressEvent(QMouseEvent *event)
{
QPointF point = event->localPos();
event->accept();
/**
* Tablet support code generates a spurious right-click right after opening
* the window, so we should ignore it. Next right-click will be used for
* closing the popup palette
*/
if (!m_hadMousePressSinceOpening && m_timeSinceOpening.elapsed() > 100) {
m_hadMousePressSinceOpening = true;
}
if (event->button() == Qt::LeftButton) {
//in favorite brushes area
int pos = calculateIndex(point, m_resourceManager->numFavoritePresets());
if (pos >= 0 && pos < m_resourceManager->numFavoritePresets()
&& isPointInPixmap(point, pos)) {
//setSelectedBrush(pos);
update();
}
if (m_isOverCanvasRotationIndicator) {
m_isRotatingCanvasIndicator = true;
}
// reset the canvas if we are over the reset canvas rotation indicator
float rotationCorrectedXPos = m_resetCanvasRotationIndicatorRect.x() + (m_popupPaletteSize / 2);
float rotationCorrectedYPos = m_resetCanvasRotationIndicatorRect.y() + (m_popupPaletteSize / 2);
QRect correctedResetCanvasRotationIndicator = QRect(rotationCorrectedXPos, rotationCorrectedYPos,
m_resetCanvasRotationIndicatorRect.width(), m_resetCanvasRotationIndicatorRect.height());
if (correctedResetCanvasRotationIndicator.contains(point.x(), point.y())) {
float angleDifference = -m_coordinatesConverter->rotationAngle(); // the rotation function accepts diffs
KisCanvasController *canvasController =
dynamic_cast(m_viewManager->canvasBase()->canvasController());
canvasController->rotateCanvas(angleDifference);
emit sigUpdateCanvas();
}
}
}
void KisPopupPalette::slotShowTagsPopup()
{
KisPaintOpPresetResourceServer *rServer = KisResourceServerProvider::instance()->paintOpPresetServer();
QStringList tags = rServer->tagNamesList();
std::sort(tags.begin(), tags.end());
if (!tags.isEmpty()) {
QMenu menu;
Q_FOREACH (const QString& tag, tags) {
menu.addAction(tag);
}
QAction *action = menu.exec(QCursor::pos());
if (action) {
m_resourceManager->setCurrentTag(action->text());
}
} else {
QWhatsThis::showText(QCursor::pos(),
i18n("There are no tags available to show in this popup. To add presets, you need to tag them and then select the tag here."));
}
}
void KisPopupPalette::slotZoomToOneHundredPercentClicked() {
QAction *action = m_actionCollection->action("zoom_to_100pct");
if (action) {
action->trigger();
}
// also move the zoom slider to 100% position so they are in sync
zoomCanvasSlider->setValue(100);
}
void KisPopupPalette::tabletEvent(QTabletEvent *event) {
event->ignore();
}
void KisPopupPalette::mouseReleaseEvent(QMouseEvent *event)
{
QPointF point = event->localPos();
event->accept();
// see a comment in KisPopupPalette::mousePressEvent
if (m_hadMousePressSinceOpening &&
event->buttons() == Qt::NoButton &&
event->button() == Qt::RightButton) {
showPopupPalette(false);
return;
}
m_isOverCanvasRotationIndicator = false;
m_isRotatingCanvasIndicator = false;
if (event->button() == Qt::LeftButton) {
QPainterPath pathColor(drawDonutPathFull(m_popupPaletteSize / 2, m_popupPaletteSize / 2, m_colorHistoryInnerRadius, m_colorHistoryOuterRadius));
//in favorite brushes area
if (hoveredPreset() > -1) {
//setSelectedBrush(hoveredBrush());
emit sigChangeActivePaintop(hoveredPreset());
}
if (pathColor.contains(point)) {
int pos = calculateIndex(point, m_resourceManager->recentColorsTotal());
if (pos >= 0 && pos < m_resourceManager->recentColorsTotal()) {
emit sigUpdateRecentColor(pos);
}
}
}
}
int KisPopupPalette::calculateIndex(QPointF point, int n)
{
calculatePresetIndex(point, n);
//translate to (0,0)
point.setX(point.x() - m_popupPaletteSize / 2);
point.setY(point.y() - m_popupPaletteSize / 2);
//rotate
float smallerAngle = M_PI / 2 + M_PI / n - atan2(point.y(), point.x());
float radius = sqrt((float)point.x() * point.x() + point.y() * point.y());
point.setX(radius * cos(smallerAngle));
point.setY(radius * sin(smallerAngle));
//calculate brush index
int pos = floor(acos(point.x() / radius) * n / (2 * M_PI));
if (point.y() < 0) pos = n - pos - 1;
return pos;
}
bool KisPopupPalette::isPointInPixmap(QPointF &point, int pos)
{
if (createPathFromPresetIndex(pos).contains(point + QPointF(-m_popupPaletteSize / 2, -m_popupPaletteSize / 2))) {
return true;
}
return false;
}
KisPopupPalette::~KisPopupPalette()
{
}
QPainterPath KisPopupPalette::createPathFromPresetIndex(int index)
{
qreal angleSlice = 360.0 / numSlots() ; // how many degrees each slice will get
// the starting angle of the slice we need to draw. the negative sign makes us go clockwise.
// adding 90 degrees makes us start at the top. otherwise we would start at the right
qreal startingAngle = -(index * angleSlice) + 90;
// the radius will get smaller as the amount of presets shown increases. 10 slots == 41
qreal presetRadius = m_colorHistoryOuterRadius * qSin(qDegreesToRadians(angleSlice/2)) / (1-qSin(qDegreesToRadians(angleSlice/2)));
QPainterPath path;
float pathX = (m_colorHistoryOuterRadius + presetRadius) * qCos(qDegreesToRadians(startingAngle)) - presetRadius;
float pathY = -(m_colorHistoryOuterRadius + presetRadius) * qSin(qDegreesToRadians(startingAngle)) - presetRadius;
float pathDiameter = 2 * presetRadius; // distance is used to calculate the X/Y in addition to the preset circle size
path.addEllipse(pathX, pathY, pathDiameter, pathDiameter);
return path;
}
int KisPopupPalette::calculatePresetIndex(QPointF point, int /*n*/)
{
for(int i = 0; i < numSlots(); i++)
{
QPointF adujustedPoint = point - QPointF(m_popupPaletteSize/2, m_popupPaletteSize/2);
if(createPathFromPresetIndex(i).contains(adujustedPoint))
{
return i;
}
}
return -1;
}
int KisPopupPalette::numSlots()
{
KisConfig config(true);
return qMax(config.favoritePresets(), 10);
}