8#include "swatchbook_p.h"
10#include "abstractdiagram.h"
11#include "constpropagatingrawpointer.h"
12#include "constpropagatinguniquepointer.h"
13#include "genericcolor.h"
15#include "helpermath.h"
16#include "initializetranslation.h"
17#include "rgbcolorspace.h"
20#include <qapplication.h>
21#include <qcoreapplication.h>
22#include <qcoreevent.h>
24#include <qfontmetrics.h>
29#include <qnamespace.h>
31#include <qpainterpath.h>
35#include <qsharedpointer.h>
36#include <qsizepolicy.h>
37#include <qstringliteral.h>
39#include <qstyleoption.h>
40#include <qtransform.h>
43#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
44#include <qcontainerfwd.h>
46#include <qstringlist.h>
65void SwatchBookPrivate::retranslateUi()
93 auto ucs4 = translation.
toUcs4();
96 for (
int i = 0; okay && (i < ucs4.count()); ++i) {
97 okay = myFontMetrics.inFontUcs4(ucs4.at(i));
102 m_selectionMark = translation;
125 : AbstractDiagram(parent)
126 , d_pointer(new SwatchBookPrivate(this, swatchGrid, wideSpacing))
128 qRegisterMetaType<Swatches>();
130 d_pointer->m_rgbColorSpace = colorSpace;
132 setFocusPolicy(Qt::FocusPolicy::StrongFocus);
141 d_pointer->selectSwatch(8, 0);
148 std::optional<QStringList>());
149 d_pointer->retranslateUi();
153SwatchBook::~SwatchBook() noexcept
165 : m_swatchGrid(swatchGrid)
166 , m_wideSpacing(wideSpacing)
167 , q_pointer(backLink)
178QSize SwatchBook::sizeHint()
const
180 return minimumSizeHint();
190QSize SwatchBook::minimumSizeHint()
const
194 d_pointer->initStyleOption(&myOption);
195 const auto contentSize = d_pointer->colorPatchesSizeWithMargin();
204 const auto lineWidth = myOption.lineWidth;
205 QMargins margins{lineWidth, lineWidth, lineWidth, lineWidth};
213QColor SwatchBook::currentColor()
const
215 return d_pointer->m_currentColor;
221void SwatchBook::setCurrentColor(
const QColor &newCurrentColor)
224 QColor temp = newCurrentColor;
228 if (temp.
spec() != QColor::Spec::Rgb) {
240 if (temp == d_pointer->m_currentColor) {
244 d_pointer->m_currentColor = temp;
246 d_pointer->selectSwatchFromCurrentColor();
248 Q_EMIT currentColorChanged(temp);
255Swatches SwatchBook::swatchGrid()
const
257 return d_pointer->m_swatchGrid;
265 if (newSwatchGrid == d_pointer->m_swatchGrid) {
269 d_pointer->m_swatchGrid = newSwatchGrid;
271 d_pointer->selectSwatchFromCurrentColor();
273 Q_EMIT swatchGridChanged(newSwatchGrid);
295void SwatchBookPrivate::selectSwatch(QListSizeType newCurrentColomn, QListSizeType newCurrentRow)
297 const auto newColor = m_swatchGrid.value(newCurrentColomn, newCurrentRow);
298 if (!newColor.isValid()) {
301 m_selectedColumn = newCurrentColomn;
302 m_selectedRow = newCurrentRow;
303 if (newColor != m_currentColor) {
304 m_currentColor = newColor;
305 Q_EMIT q_pointer->currentColorChanged(newColor);
316void SwatchBookPrivate::selectSwatchFromCurrentColor()
318 if (m_selectedColumn > 0 && m_selectedRow > 0) {
319 if (m_swatchGrid.value(m_selectedColumn, m_selectedRow) == m_currentColor) {
324 bool colorFound =
false;
325 const qsizetype myColumnCount = m_swatchGrid.iCount();
326 const qsizetype myRowCount = m_swatchGrid.jCount();
329 for (columnIndex = 0;
330 columnIndex < myColumnCount;
332 for (rowIndex = 0; rowIndex < myRowCount; ++rowIndex) {
333 if (m_swatchGrid.value(columnIndex, rowIndex) == m_currentColor) {
343 m_selectedColumn = columnIndex;
344 m_selectedRow = rowIndex;
346 m_selectedColumn = -1;
358int SwatchBookPrivate::horizontalPatchSpacing()
const
360 if (m_wideSpacing.testFlag(Qt::Orientation::Horizontal)) {
361 return widePatchSpacing();
363 return normalPatchSpacing();
373int SwatchBookPrivate::widePatchSpacing()
const
381 int temp = q_pointer->style()->pixelMetric(
384 q_pointer.toPointerToConstObject());
392 temp = q_pointer->style()->pixelMetric(
395 q_pointer.toPointerToConstObject());
399 temp = q_pointer->style()->pixelMetric(
402 q_pointer.toPointerToConstObject());
405 return qMax(temp, 2);
414int SwatchBookPrivate::verticalPatchSpacing()
const
416 if (m_wideSpacing.testFlag(Qt::Orientation::Vertical)) {
417 return widePatchSpacing();
419 return normalPatchSpacing();
428int SwatchBookPrivate::normalPatchSpacing()
const
430 return qMax(widePatchSpacing() / 3,
446 if (option ==
nullptr) {
449 option->
initFrom(q_pointer.toPointerToConstObject());
450 option->lineWidth = q_pointer->style()->pixelMetric(
453 q_pointer.toPointerToConstObject());
454 option->midLineWidth = 0;
476 const QRectF frameContentRectangle = q_pointer->style()->subElementRect(
479 q_pointer.toPointerToConstObject());
480 const QSizeF swatchbookContentSize = colorPatchesSizeWithMargin();
493 frameOffset.
rx() -= swatchbookContentSize.
width() / 2.;
494 frameOffset.
ry() -= swatchbookContentSize.
height() / 2.;
496 return (frameOffset + innerMarginOffset).toPoint();
504void SwatchBook::mousePressEvent(
QMouseEvent *event)
517 const QSize myColorPatchSize = d_pointer->patchSizeOuter();
518 const int myPatchWidth = myColorPatchSize.
width();
519 const int myPatchHeight = myColorPatchSize.
height();
521 d_pointer->initStyleOption(&myFrameStyleOption);
522 const QPoint temp =
event->pos() - d_pointer->offset(myFrameStyleOption);
524 if ((temp.
x() < 0) || (temp.
y() < 0)) {
528 const auto columnWidth = myPatchWidth + d_pointer->horizontalPatchSpacing();
529 const int xWithinPatch = temp.
x() % columnWidth;
530 if (xWithinPatch >= myPatchWidth) {
534 const auto rowHeight = myPatchHeight + d_pointer->verticalPatchSpacing();
535 const int yWithinPatch = temp.
y() % rowHeight;
536 if (yWithinPatch >= myPatchHeight) {
540 const int rowIndex = temp.
y() / rowHeight;
541 if (!isInRange<qsizetype>(0, rowIndex, d_pointer->m_swatchGrid.jCount() - 1)) {
548 const int visualColumnIndex = temp.
x() / columnWidth;
549 QListSizeType columnIndex;
550 if (layoutDirection() == Qt::LayoutDirection::LeftToRight) {
551 columnIndex = visualColumnIndex;
554 d_pointer->m_swatchGrid.iCount() - 1 - visualColumnIndex;
556 if (!isInRange<qsizetype>(0, columnIndex, d_pointer->m_swatchGrid.iCount() - 1)) {
565 d_pointer->selectSwatch(columnIndex, rowIndex);
576QSize SwatchBookPrivate::patchSizeOuter()
const
578 q_pointer->ensurePolished();
579 const QSize myInnerSize = patchSizeInner();
581 myOptions.
initFrom(q_pointer.toPointerToConstObject());
582 myOptions.rect.setSize(myInnerSize);
583 const auto myStyledOuterSize = q_pointer->style()->sizeFromContents(
587 q_pointer.toPointerToConstObject());
591 const int extra = 2 * cornerRadius();
592 return myStyledOuterSize.expandedTo(myInnerSize +
QSize(extra, extra));
601QSize SwatchBookPrivate::patchSizeInner()
const
603 const int metric = q_pointer->style()->pixelMetric(
606 q_pointer.toPointerToConstObject());
607 const int size = std::max({metric,
608 horizontalPatchSpacing(),
609 verticalPatchSpacing()});
610 return QSize(size, size);
619int SwatchBookPrivate::cornerRadius()
const
621 const auto defaultFrameWidth =
623 return qMax(defaultFrameWidth, 0);
639 d_pointer->initStyleOption(&frameStyleOption);
640 const int horizontalSpacing = d_pointer->horizontalPatchSpacing();
641 const int verticalSpacing = d_pointer->verticalPatchSpacing();
642 const QSize patchSizeOuter = d_pointer->patchSizeOuter();
643 const int patchWidthOuter = patchSizeOuter.
width();
644 const int patchHeightOuter = patchSizeOuter.
height();
654 QStringLiteral(
"windowsvista"),
657 const int shrink = vistaStyle ? 1 : 0;
658 const QMargins margins(shrink, shrink, shrink, shrink);
659 auto shrunkFrameStyleOption = frameStyleOption;
660 shrunkFrameStyleOption.rect = frameStyleOption.rect - margins;
673 &shrunkFrameStyleOption,
679 const QPoint offset = d_pointer->offset(frameStyleOption);
680 const QListSizeType columnCount = d_pointer->m_swatchGrid.iCount();
681 const int myCornerRadius = d_pointer->cornerRadius();
682 QListSizeType visualColumn;
683 for (
int columnIndex = 0; columnIndex < columnCount; ++columnIndex) {
685 row < d_pointer->m_swatchGrid.jCount();
688 const auto swatchColor =
689 d_pointer->m_swatchGrid.value(columnIndex, row);
690 if (swatchColor.isValid()) {
691 widgetPainter.setBrush(swatchColor);
693 if (layoutDirection() == Qt::LayoutDirection::LeftToRight) {
694 visualColumn = columnIndex;
696 visualColumn = columnCount - 1 - columnIndex;
698 widgetPainter.drawRoundedRect(
700 + (
static_cast<int>(visualColumn)
701 * (patchWidthOuter + horizontalSpacing)),
703 + row * (patchHeightOuter + verticalSpacing),
713 if (d_pointer->m_selectedColumn < 0 || d_pointer->m_selectedRow < 0) {
718 const QListSizeType visualSelectedColumnIndex =
719 (layoutDirection() == Qt::LayoutDirection::LeftToRight)
720 ? d_pointer->m_selectedColumn
721 : d_pointer->m_swatchGrid.iCount() - 1 - d_pointer->m_selectedColumn;
722 const auto colorCielchD50 =
723 d_pointer->m_rgbColorSpace->toCielchD50(
726 .value(d_pointer->m_selectedColumn, d_pointer->m_selectedRow)
729 const QColor selectionMarkColor =
730 handleColorFromBackgroundLightness(colorCielchD50.first);
733 + (
static_cast<int>(visualSelectedColumnIndex)
734 * (patchWidthOuter + horizontalSpacing)),
736 + (
static_cast<int>(d_pointer->m_selectedRow)
737 * (patchHeightOuter + verticalSpacing)));
738 const QSize patchSizeInner = d_pointer->patchSizeInner();
739 const int patchWidthInner = patchSizeInner.
width();
740 const int patchHeightInner = patchSizeInner.height();
741 if (d_pointer->m_selectionMark.isEmpty()) {
744 const QSize sizeDifference = patchSizeOuter - patchSizeInner;
746 QPointF selectionMarkOffset = QPointF(
747 sizeDifference.width() / 2.0,
748 sizeDifference.height() / 2.0);
749 if (patchWidthInner > patchHeightInner) {
750 selectionMarkOffset.rx() +=
751 ((patchWidthInner - patchHeightInner) / 2.0);
753 if (patchHeightInner > patchWidthInner) {
754 selectionMarkOffset.ry() +=
755 ((patchHeightInner - patchWidthInner) / 2.0);
757 const int effectiveSquareSize = qMin(
760 qreal penWidth = effectiveSquareSize * 0.08;
765 widgetPainter.setPen(pen);
767 0.7 * effectiveSquareSize);
768 point1 += selectedPatchOffset + selectionMarkOffset;
769 QPointF point2(0.35 * effectiveSquareSize,
770 1 * effectiveSquareSize - penWidth);
771 point2 += selectedPatchOffset + selectionMarkOffset;
772 QPointF point3(1 * effectiveSquareSize - penWidth,
774 point3 += selectedPatchOffset + selectionMarkOffset;
775 widgetPainter.drawLine(
QLineF(point1, point2));
776 widgetPainter.drawLine(
QLineF(point2, point3));
780 textPath.
addText(0, 0, font(), d_pointer->m_selectionMark);
787 if (!boundingRectangleSize.
isEmpty()) {
793 selectedPatchOffset.x()
794 + (patchWidthOuter - patchWidthInner) / 2,
796 selectedPatchOffset.y()
797 + (patchHeightOuter - patchHeightInner) / 2);
800 const qreal scaleFactor = qMin(
802 patchWidthInner / boundingRectangleSize.
width(),
804 patchHeightInner / boundingRectangleSize.
height());
805 QSizeF scaledSelectionMarkSize =
806 boundingRectangleSize * scaleFactor;
808 (patchSizeInner - scaledSelectionMarkSize) / 2;
810 textTransform.
scale(scaleFactor, scaleFactor);
813 widgetPainter.setTransform(textTransform);
815 widgetPainter.setBrush(selectionMarkColor);
816 widgetPainter.drawPath(textPath);
834void SwatchBook::keyPressEvent(
QKeyEvent *event)
836 QListSizeType columnShift = 0;
837 QListSizeType rowShift = 0;
838 const int writingDirection =
842 switch (
event->key()) {
850 columnShift = -1 * writingDirection;
853 columnShift = 1 * writingDirection;
856 rowShift = (-1) * d_pointer->m_swatchGrid.jCount();
859 rowShift = d_pointer->m_swatchGrid.jCount();
862 columnShift = (-1) * d_pointer->m_swatchGrid.iCount();
865 columnShift = d_pointer->m_swatchGrid.iCount();
887 if ((d_pointer->m_selectedColumn < 0) && (d_pointer->m_selectedRow < 0)) {
888 d_pointer->selectSwatch(0, 0);
892 const int accelerationFactor = 2;
894 columnShift *= accelerationFactor;
895 rowShift *= accelerationFactor;
898 d_pointer->selectSwatch(
899 qBound<QListSizeType>(0,
900 d_pointer->m_selectedColumn + columnShift,
901 d_pointer->m_swatchGrid.iCount() - 1),
902 qBound<QListSizeType>(0,
903 d_pointer->m_selectedRow + rowShift,
904 d_pointer->m_swatchGrid.jCount() - 1));
914void SwatchBook::changeEvent(
QEvent *event)
923 d_pointer->retranslateUi();
933QSize SwatchBookPrivate::colorPatchesSizeWithMargin()
const
935 q_pointer->ensurePolished();
936 const QSize patchSize = patchSizeOuter();
937 const int columnCount =
static_cast<int>(m_swatchGrid.iCount());
938 const int rowCount =
static_cast<int>(m_swatchGrid.jCount());
941 + columnCount * patchSize.
width()
942 + (columnCount - 1) * horizontalPatchSpacing()
946 + rowCount * patchSize.
height()
947 + (rowCount - 1) * verticalPatchSpacing()
949 return QSize(width, height);
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void update(Part *part, const QByteArray &data, qint64 dataSize)
The namespace of this library.
Array2D< QColor > Swatches
Swatches organized in a grid.
float alphaF() const const
float blueF() const const
QColor fromRgbF(float r, float g, float b, float a)
float greenF() const const
bool isValid() const const
QCoreApplication * instance()
QString tr(const char *sourceText, const char *disambiguation, int n)
void addText(const QPointF &point, const QFont &font, const QString &text)
QRectF boundingRect() const const
void translate(const QPointF &offset)
void setCapStyle(Qt::PenCapStyle style)
void setColor(const QColor &color)
void setWidthF(qreal width)
QPointF center() const const
QSizeF size() const const
QSize expandedTo(const QSize &otherSize) const const
QSize grownBy(QMargins margins) const const
qreal height() const const
bool isEmpty() const const
qreal width() const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QList< uint > toUcs4() const const
PM_LayoutHorizontalSpacing
void initFrom(const QWidget *widget)