Commit 076e17c6 authored by David Hurka's avatar David Hurka 🐬

Create GuiUtils functions createColorIcon() and createOpacityIcon()

* createColorIcon creates a QIcon which visualizes the given colors
  using rectangles. A background icon can be provided, in that case
  only the lowest 25% of the icon will be filled by the rectangles.
* createOpacityIcon creates a QIcon that visualizes a given opacity
  using the current foreground color and a checkerboard background.

These functions are now used in place of colorIcon, colorPicerIcon,
opacityIcon in AnnotationActionHandler. The new functions have some
advantages: support most common icon sizes, and dark color schemes.
parent 3cc016b6
Pipeline #32809 passed with stage
in 9 minutes and 25 seconds
......@@ -89,10 +89,7 @@ public:
void populateQuickAnnotations();
KSelectAction *colorPickerAction(AnnotationColor colorType);
const QIcon colorIcon(const QColor &color);
const QIcon widthIcon(double width);
const QIcon colorPickerIcon(const QString &iconName, const QColor &color);
const QIcon opacityIcon(double opacity);
const QIcon stampIcon(const QString &stampIconName);
void selectTool(int toolID);
......@@ -154,7 +151,7 @@ const QList<QPair<QString, QColor>> AnnotationActionHandlerPrivate::defaultColor
const QList<double> AnnotationActionHandlerPrivate::widthStandardValues = {1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5};
const QList<double> AnnotationActionHandlerPrivate::opacityStandardValues = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
const QList<double> AnnotationActionHandlerPrivate::opacityStandardValues = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0};
QAction *AnnotationActionHandlerPrivate::selectActionItem(KSelectAction *aList, QAction *aCustomCurrent, double value, const QList<double> &defaultValues, const QIcon &icon, const QString &label)
{
......@@ -241,8 +238,8 @@ void AnnotationActionHandlerPrivate::parseTool(int toolID)
// if the opacity value is not a default one, insert a new action in the opacity list
if (annElement.hasAttribute(QStringLiteral("opacity"))) {
double opacity = 100 * annElement.attribute(QStringLiteral("opacity")).toDouble();
aCustomOpacity = selectActionItem(aOpacity, aCustomOpacity, opacity, opacityStandardValues, opacityIcon(opacity), i18nc("@item:inlistbox", "%1\%", opacity));
double opacity = annElement.attribute(QStringLiteral("opacity")).toDouble();
aCustomOpacity = selectActionItem(aOpacity, aCustomOpacity, opacity, opacityStandardValues, GuiUtils::createOpacityIcon(opacity), i18nc("@item:inlistbox", "%1\%", opacity * 100));
} else {
aOpacity->setCurrentItem(opacityStandardValues.size() - 1); // 100 %
}
......@@ -269,11 +266,11 @@ void AnnotationActionHandlerPrivate::updateConfigActions(const QString &annotTyp
const bool isStamp = annotType == QStringLiteral("stamp");
if (isTypewriter) {
aColor->setIcon(colorPickerIcon(QStringLiteral("format-text-color"), currentColor));
aColor->setIcon(GuiUtils::createColorIcon({currentColor}, QIcon::fromTheme(QStringLiteral("format-text-color"))));
} else {
aColor->setIcon(colorPickerIcon(QStringLiteral("format-stroke-color"), currentColor));
aColor->setIcon(GuiUtils::createColorIcon({currentColor}, QIcon::fromTheme(QStringLiteral("format-stroke-color"))));
}
aInnerColor->setIcon(colorPickerIcon(QStringLiteral("format-fill-color"), currentInnerColor));
aInnerColor->setIcon(GuiUtils::createColorIcon({currentInnerColor}, QIcon::fromTheme(QStringLiteral("format-fill-color"))));
aAddToQuickTools->setEnabled(isAnnotationSelected);
aWidth->setEnabled(isLine || isShape);
......@@ -393,7 +390,7 @@ KSelectAction *AnnotationActionHandlerPrivate::colorPickerAction(AnnotationColor
aColorPicker->setToolBarMode(KSelectAction::MenuMode);
for (const auto &colorNameValue : colorList) {
QColor color(colorNameValue.second);
QAction *aColor = new QAction(colorIcon(color), i18nc("@item:inlistbox Color name", "%1", colorNameValue.first), q);
QAction *aColor = new QAction(GuiUtils::createColorIcon({color}, QIcon(), GuiUtils::VisualizeTransparent), i18nc("@item:inlistbox Color name", "%1", colorNameValue.first), q);
aColorPicker->addAction(aColor);
QObject::connect(aColor, &QAction::triggered, q, [this, colorType, color]() { slotSetColor(colorType, color); });
}
......@@ -415,53 +412,6 @@ const QIcon AnnotationActionHandlerPrivate::widthIcon(double width)
return QIcon(pm);
}
const QIcon AnnotationActionHandlerPrivate::colorPickerIcon(const QString &iconName, const QColor &color)
{
QIcon icon = QIcon::fromTheme(iconName);
if (!color.isValid()) {
return icon;
}
QSize iconSize = QSize(32, 32);
QPixmap pm = icon.pixmap(iconSize);
QPainter p(&pm);
p.fillRect(0, iconSize.height() - 8, iconSize.width(), 8, color);
p.end();
return QIcon(pm);
}
const QIcon AnnotationActionHandlerPrivate::colorIcon(const QColor &color)
{
QSize iconSize = QSize(32, 32);
QPixmap pm(iconSize);
QPainter p(&pm);
if (color == Qt::transparent) {
p.fillRect(0, 0, iconSize.width(), iconSize.height(), Qt::white);
p.setPen(QPen(Qt::red, 2));
p.drawLine(iconSize.width() - 1, 0, 0, iconSize.height() - 1);
} else {
p.fillRect(0, 0, iconSize.width(), iconSize.height(), color);
}
p.setPen(Qt::black);
p.drawRect(0, 0, iconSize.width() - 1, iconSize.height() - 1);
p.end();
return QIcon(pm);
}
const QIcon AnnotationActionHandlerPrivate::opacityIcon(double opacity)
{
QPixmap pm(32, 32);
pm.fill(Qt::transparent);
QPainter p(&pm);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(Qt::NoPen);
QColor color(Qt::black);
color.setAlpha(opacity * 255 / 100);
p.setBrush(QBrush(color));
p.drawRect(4, 4, 24, 24);
p.end();
return QIcon(pm);
}
const QIcon AnnotationActionHandlerPrivate::stampIcon(const QString &stampIconName)
{
QPixmap stampPix = GuiUtils::loadStamp(stampIconName, 32);
......@@ -683,10 +633,10 @@ AnnotationActionHandler::AnnotationActionHandler(PageViewAnnotator *parent, KAct
// Opacity list
d->aOpacity = new KSelectAction(QIcon::fromTheme(QStringLiteral("edit-opacity")), i18nc("@action:intoolbar Current annotation config option", "Opacity"), this);
d->aOpacity->setToolBarMode(KSelectAction::MenuMode);
for (auto opacity : d->opacityStandardValues) {
KToggleAction *ann = new KToggleAction(d->opacityIcon(opacity), QStringLiteral("%1\%").arg(opacity), this);
for (double opacity : d->opacityStandardValues) {
KToggleAction *ann = new KToggleAction(GuiUtils::createOpacityIcon(opacity), QStringLiteral("%1\%").arg(opacity * 100), this);
d->aOpacity->addAction(ann);
connect(ann, &QAction::triggered, this, [this, opacity]() { d->annotator->setAnnotationOpacity(opacity / 100); });
connect(ann, &QAction::triggered, this, [this, opacity]() { d->annotator->setAnnotationOpacity(opacity); });
}
connect(d->aAddToQuickTools, &QAction::triggered, d->annotator, &PageViewAnnotator::addToQuickAnnotations);
......
......@@ -13,6 +13,7 @@
#include <KIconLoader>
#include <KLocalizedString>
#include <KMessageBox>
#include <QApplication>
#include <QFileDialog>
#include <QPainter>
#include <QStandardPaths>
......@@ -278,4 +279,93 @@ void colorizeImage(QImage &grayImage, const QColor &color, unsigned int destAlph
}
}
QIcon createColorIcon(const QList<QColor> &colors, const QIcon &background, ColorIconFlags flags)
{
QIcon colorIcon;
// Create a pixmap for each common icon size.
for (int size : {16, 22, 24, 32, 48}) {
QPixmap pixmap(QSize(size, size) * qApp->devicePixelRatio());
pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
// Configure hairlines for visualization of outline or transparency (visualizeTransparent):
painter.setPen(QPen(qApp->palette().color(QPalette::Active, QPalette::WindowText), 0));
painter.setBrush(Qt::NoBrush);
if (background.isNull()) {
// Full-size color rectangles.
// Draw rectangles left to right:
for (int i = 0; i < colors.count(); ++i) {
if (!colors.at(i).isValid()) {
continue;
}
QRect rect(QPoint(size * i / colors.count(), 0), QPoint(size * (i + 1) / colors.count(), size));
if ((flags & VisualizeTransparent) && (colors.at(i) == Qt::transparent)) {
painter.drawLine(rect.topLeft(), rect.bottomRight());
painter.drawLine(rect.bottomLeft(), rect.topRight());
} else {
painter.fillRect(rect, colors.at(i));
}
}
// Draw hairline outline:
// To get the hairline on the outermost pixels, we shrink the rectangle by a half pixel on each edge.
const qreal halfPixelWidth = 0.5 / pixmap.devicePixelRatio();
painter.drawRect(QRectF(QPointF(halfPixelWidth, halfPixelWidth), QPointF(qreal(size) - halfPixelWidth, qreal(size) - halfPixelWidth)));
} else {
// Lower 25% color rectangles.
// Draw background icon:
background.paint(&painter, QRect(QPoint(0, 0), QSize(size, size)));
// Draw rectangles left to right:
for (int i = 0; i < colors.count(); ++i) {
if (!colors.at(i).isValid()) {
continue;
}
QRect rect(QPoint(size * i / colors.count(), size * 3 / 4), QPoint(size * (i + 1) / colors.count(), size));
if ((flags & VisualizeTransparent) && (colors.at(i) == Qt::transparent)) {
painter.drawLine(rect.topLeft(), rect.bottomRight());
painter.drawLine(rect.bottomLeft(), rect.topRight());
} else {
painter.fillRect(rect, colors.at(i));
}
}
}
painter.end();
colorIcon.addPixmap(pixmap);
}
return colorIcon;
}
QIcon createOpacityIcon(qreal opacity)
{
QIcon opacityIcon;
// Create a pixmap for each common icon size.
for (int size : {16, 22, 24, 32, 48}) {
QPixmap pixmap(QSize(size, size) * qApp->devicePixelRatio());
pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
painter.setPen(Qt::NoPen);
painter.setBrush(qApp->palette().color(QPalette::Active, QPalette::WindowText));
// Checkerboard pattern
painter.drawRect(QRectF(QPoint(0, 0), QPoint(size, size) / 2));
painter.drawRect(QRectF(QPoint(size, size) / 2, QPoint(size, size)));
// Opacity
painter.setOpacity(opacity);
painter.drawRect(QRect(QPoint(0, 0), QPoint(size, size)));
painter.end();
opacityIcon.addPixmap(pixmap);
}
return opacityIcon;
}
}
......@@ -10,9 +10,10 @@
#ifndef OKULAR_GUIUTILS_H
#define OKULAR_GUIUTILS_H
#include <QColor>
#include <QIcon>
#include <QString>
class QColor;
class QImage;
class QPixmap;
class QSize;
......@@ -65,6 +66,37 @@ Okular::Movie *renditionMovieFromScreenAnnotation(const Okular::ScreenAnnotation
// colorize a gray image to the given color
void colorizeImage(QImage &image, const QColor &color, unsigned int alpha = 255);
enum ColorIconFlags {
NoFlags = 0x0,
VisualizeTransparent = 0x1 ///< Visualizes Qt::transparent with a cross.
};
/**
* Paints color rectangles on the lower 25% of an icon.
*
* If no icon is given, the whole icon square is filled, and a 1px border is added.
*
* Examples:
* * Different icons for different kinds of color selection, like fill and outline.
* * Selection of a color scheme preset, where each scheme has a different icon and ~1..3 specific colors.
*
* @param colors Which color rectangles to paint, from left to right (even on RTL). Colors may be transparent. Invalid colors are skipped.
* @param background Which icon to use as background.
* @param flags Special wishes.
*
* @returns A newly created QIcon.
*/
QIcon createColorIcon(const QList<QColor> &colors, const QIcon &background = QIcon(), ColorIconFlags flags = NoFlags);
/**
* Creates an opacity icon, using QPalette foreground color
* painted on top of a checkerboard pattern using @p opacity.
*
* @param opacity 0 = invisible, 1 = opaque.
*/
QIcon createOpacityIcon(qreal opacity);
}
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment