From 337f94b39d6fc82a90da0138f06a40e826dbc44a Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Sun, 17 Dec 2023 13:19:29 +0100 Subject: [PATCH 1/9] initial commit color picker dialog --- examples/gallery/dialog/DialogPage.cpp | 191 +++++++++++++++++++++---- 1 file changed, 162 insertions(+), 29 deletions(-) diff --git a/examples/gallery/dialog/DialogPage.cpp b/examples/gallery/dialog/DialogPage.cpp index 0e9e79460..316cdc8d7 100644 --- a/examples/gallery/dialog/DialogPage.cpp +++ b/examples/gallery/dialog/DialogPage.cpp @@ -4,12 +4,26 @@ *****************************************************************************/ #include "DialogPage.h" - +#include "QskBox.h" +#include "QskBoxBorderColors.h" +#include "QskControl.h" +#include "QskGradient.h" +#include "QskGridBox.h" +#include "QskSizePolicy.h" +#include "QskSlider.h" +#include "QskTextLabel.h" + +#include +#include #include #include #include #include -#include +#include +#include +#include +#include +#include namespace { @@ -51,26 +65,27 @@ namespace auto selectButton = new Button( "Selection", this ); connect( selectButton, &Button::clicked, this, &ButtonBox::execSelection ); + auto colortButton = new Button( "Color", this ); + connect( colortButton, &Button::clicked, this, [ this ]() { execColor( window() ); } ); + setExtraSpacingAt( Qt::BottomEdge ); } private: void execMessage() { - qskDialog->message( "Message", "Request vector, over.", - QskStandardSymbol::NoSymbol, QskDialog::Close ); + qskDialog->message( + "Message", "Request vector, over.", QskStandardSymbol::NoSymbol, QskDialog::Close ); } void execInformation() { - qskDialog->information( "Information", - "We have clearance, Clarence." ); + qskDialog->information( "Information", "We have clearance, Clarence." ); } void execQuestion() { - qskDialog->question( "Question", - "Roger, Roger. Do we have a vector, Victor ?" ); + qskDialog->question( "Question", "Roger, Roger. Do we have a vector, Victor ?" ); } void execWarning() @@ -86,30 +101,148 @@ namespace void execSelection() { // of course we all love "The Teens" - const QStringList entries = - { - "Give Me More", - "Gimme Gimme Your Love", - "1-2-3-4 Red Light", - "New York", - "If You Walk Away", - "Eloise", - "On The Radio", - "We Are The Teens", - "Never Gonna Tell No Lie To You", - "Walking On Sunshine ", - "Get Out Of My Mind", - "Cover Girl ", - "Here I Stand", - "Gypsy Caravan", - "It´s Good To Have A Friend", - "We´ll Have A Party Tonite ´nite", - "Automatic World", - "Gimme Gimme Gimme Gimme Gimme Your Love" - }; + const QStringList entries = { "Give Me More", "Gimme Gimme Your Love", + "1-2-3-4 Red Light", "New York", "If You Walk Away", "Eloise", "On The Radio", + "We Are The Teens", "Never Gonna Tell No Lie To You", "Walking On Sunshine ", + "Get Out Of My Mind", "Cover Girl ", "Here I Stand", "Gypsy Caravan", + "It´s Good To Have A Friend", "We´ll Have A Party Tonite ´nite", "Automatic World", + "Gimme Gimme Gimme Gimme Gimme Your Love" }; qskDialog->select( "Here we go ...", "Hot Hot Hot", entries, 7 ); } + + void execColor( QQuickWindow* window ) + { + auto* const layout = new QskLinearBox( Qt::Vertical, window->contentItem() ); + layout->setPanel( true ); + auto* const tabBar = new QskTabView( layout ); + tabBar->addTab( "Sliders", [ window ]() { + auto* const layout = new QskGridBox( window->contentItem() ); + auto* const color = new QskSlider( Qt::Horizontal ); + auto* const brightness = new QskSlider( Qt::Horizontal ); + auto* const grayscale = new QskSlider( Qt::Horizontal ); + auto* const alpha = new QskSlider( Qt::Horizontal ); + auto* const preview = new QskBox; + auto* const text = new QskTextLabel( "#00000000" ); + preview->setPanel( true ); + alpha->setValue( 1.0 ); + layout->addItem( preview, 0, 0, 4, 1 ); + layout->addItem( color, 0, 1 ); + layout->addItem( grayscale, 1, 1 ); + layout->addItem( brightness, 2, 1 ); + layout->addItem( alpha, 3, 1 ); + layout->addItem( text, 4, 0 ); + + preview->setSizePolicy( Qt::Vertical, QskSizePolicy::ConstrainedMinimum ); + text->setAlignmentHint( QskTextLabel::Text, Qt::AlignHCenter | Qt::AlignVCenter ); + + static const QskGradientStops stopsRGB = { + { 0.0000, QColor::fromRgb( 255, 0, 0 ) }, + { 0.1667, QColor::fromRgb( 255, 255, 0 ) }, + { 0.3333, QColor::fromRgb( 0, 255, 0 ) }, + { 0.5000, QColor::fromRgb( 0, 255, 255 ) }, + { 0.6667, QColor::fromRgb( 0, 0, 255 ) }, + { 0.8333, QColor::fromRgb( 255, 0, 255 ) }, + { 1.0000, QColor::fromRgb( 255, 0, 0 ) }, + }; + + static const QskGradientStops stopsGrayscale = { + { 0.0000, Qt::black }, + { 1.0000, Qt::white }, + }; + + QskGradient gradient; + gradient.setLinearDirection( Qt::Horizontal ); + gradient.setStops( stopsRGB ); + + { + QskGradient gradient; + gradient.setLinearDirection( Qt::Horizontal ); + gradient.setStops( stopsGrayscale ); + grayscale->setGradientHint( QskSlider::Groove, gradient ); + } + + color->setGradientHint( QskSlider::Groove, gradient ); + + for ( QskSlider* slider : { color, brightness, alpha, grayscale } ) + { + slider->setGradientHint( QskSlider::Fill, {} ); + } + + auto colorFrom = []( QskSlider* slider ) { + return slider->gradientHint( QskSlider::Groove ) + .extracted( slider->valueAsRatio(), slider->valueAsRatio() ) + .startColor(); + }; + + auto updateColor = [ = ]( QskSlider* sender ) { + const auto ratio = sender->valueAsRatio(); + const auto rgb = sender->gradientHint( QskSlider::Groove ) + .extracted( ratio, ratio ) + .startColor(); + + { + auto c = colorFrom( color ).rgb(); + + QskGradient gradient; + gradient.setLinearDirection( Qt::Horizontal ); + gradient.setStops( { { 0.0, Qt::white }, { 0.5, c }, { 1.0, Qt::black } } ); + brightness->setGradientHint( QskSlider::Groove, gradient ); + } + + { + QskGradient gradient; + gradient.setLinearDirection( Qt::Horizontal ); + gradient.setStops( { { 0.0, Qt::transparent }, + { 1.0, { rgb.red(), rgb.green(), rgb.blue() } } } ); + alpha->setGradientHint( QskSlider::Groove, gradient ); + } + + { + auto color = preview->color( QskBox::Panel ); + color.setRed( rgb.red() ); + color.setGreen( rgb.green() ); + color.setBlue( rgb.blue() ); + preview->setColor( QskBox::Panel, color ); + preview->setBoxBorderColorsHint( QskBox::Panel, color.rgb() | 0xff000000 ); + + text->setText( color.name( QColor::HexArgb ) ); + } + }; + + auto updateAlpha = [ = ]( qreal alpha ) { + auto color = preview->color( QskBox::Panel ); + color.setAlphaF( alpha ); + preview->setColor( QskBox::Panel, color ); + text->setText( color.name( QColor::HexArgb ) ); + }; + + QObject::connect( color, &QskSlider::valueChanged, preview, + [ updateColor, sender = color ]() { updateColor( sender ); } ); + QObject::connect( grayscale, &QskSlider::valueChanged, preview, + [ updateColor, sender = grayscale ]() { updateColor( sender ); } ); + QObject::connect( brightness, &QskSlider::valueChanged, preview, + [ updateColor, sender = brightness ]() { updateColor( sender ); } ); + QObject::connect( alpha, &QskSlider::valueChanged, preview, updateAlpha ); + + preview->setFixedSize( 80, 80 ); + preview->setBoxShapeHint( QskBox::Panel, { 4 } ); + preview->setBoxBorderColorsHint( QskBox::Panel, { Qt::black } ); + preview->setBoxBorderMetricsHint( QskBox::Panel, { 2 } ); + preview->setMarginHint( QskBox::Panel, 2 ); + + layout->setColumnStretchFactor(0, 0); + layout->setColumnStretchFactor(1, 99); + + updateColor( color ); + updateAlpha( alpha->valueAsRatio() ); + return layout; + }() ); + layout->addSpacer( -1, 99 ); + auto* const button = new QskPushButton( "Close", layout ); + layout->setDefaultAlignment( Qt::AlignRight | Qt::AlignHCenter ); + QObject::connect( button, &QskPushButton::clicked, layout, &QskLinearBox::deleteLater ); + } }; } From 7b4476f0735cd38f096f7b735fcb5625ac3b0563 Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Sun, 17 Dec 2023 16:45:14 +0100 Subject: [PATCH 2/9] color wheel --- examples/gallery/CMakeLists.txt | 1 + .../gallery/colorpickers/ColorPickerPage.cpp | 157 ++++++++++++++++++ .../gallery/colorpickers/ColorPickerPage.h | 50 ++++++ examples/gallery/main.cpp | 2 + src/nodes/shaders/colorwheel.frag | 94 +++++++++++ src/nodes/shaders/colorwheel.vert | 7 + 6 files changed, 311 insertions(+) create mode 100644 examples/gallery/colorpickers/ColorPickerPage.cpp create mode 100644 examples/gallery/colorpickers/ColorPickerPage.h create mode 100644 src/nodes/shaders/colorwheel.frag create mode 100644 src/nodes/shaders/colorwheel.vert diff --git a/examples/gallery/CMakeLists.txt b/examples/gallery/CMakeLists.txt index e229a560c..b887b10f0 100644 --- a/examples/gallery/CMakeLists.txt +++ b/examples/gallery/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES selector/SelectorPage.h selector/SelectorPage.cpp dialog/DialogPage.h dialog/DialogPage.cpp listbox/ListBoxPage.h listbox/ListBoxPage.cpp + colorpickers/ColorPickerPage.h colorpickers/ColorPickerPage.cpp Page.h Page.cpp main.cpp ) diff --git a/examples/gallery/colorpickers/ColorPickerPage.cpp b/examples/gallery/colorpickers/ColorPickerPage.cpp new file mode 100644 index 000000000..58b2bd6de --- /dev/null +++ b/examples/gallery/colorpickers/ColorPickerPage.cpp @@ -0,0 +1,157 @@ +#include "ColorPickerPage.h" +#include "QskLinearBox.h" +#include "QskSkinlet.h" +#include "colorpickers/ColorPickerPage.h" + +#include +#include +#include + +#include +#include +#include +#include + +struct Color +{ + float r, g, b, a; + QRectF rect; + QPointF mouse; +}; + +class MinimalShader : public QSGSimpleMaterialShader< Color > +{ + QSG_DECLARE_SIMPLE_SHADER( MinimalShader, Color ) + public: + MinimalShader() + { + setShaderSourceFile( QOpenGLShader::Fragment, ":/qskinny/shaders/colorwheel.frag" ); + setShaderSourceFile( QOpenGLShader::Vertex, ":/qskinny/shaders/colorwheel.vert" ); + } + + QList< QByteArray > attributes() const + { + return QList< QByteArray >() << "vertex"; + } + + void updateState( const Color* color, const Color* ) + { + program()->setUniformValue( "color", color->r, color->g, color->b, color->a ); + program()->setUniformValue( + "rect", color->rect.x(), color->rect.y(), color->rect.width(), color->rect.height() ); + program()->setUniformValue( + "mouse", color->mouse.x(), color->mouse.y(), color->mouse.x(), color->mouse.y() ); + qDebug() << color->mouse; + } +}; + +class QskColorWheelNode : public QSGGeometryNode +{ + using Material = QSGSimpleMaterial< Color >; + + public: + QskColorWheelNode() + { + auto* const geometry = + new QSGGeometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 ); + geometry->setDrawingMode( QSGGeometry::DrawTriangleStrip ); + setGeometry( geometry ); + setMaterial( MinimalShader::createMaterial() ); + material()->setFlag( QSGMaterial::Blending ); + } + + void update( const QRectF& rect, const QPointF& mouse, const QRectF& rectOnScreen, + const QSizeF& resolution ) + { + auto* const material = static_cast< Material* >( this->material() ); + material->state()->r = 1.0; + material->state()->g = 0.0; + material->state()->b = 1.0; + material->state()->a = 1.0; + material->state()->rect = { rectOnScreen.x(), resolution.height() - rectOnScreen.bottom(), + rectOnScreen.width(), rectOnScreen.height() }; + material->state()->mouse = { mouse.x(), resolution.height() - mouse.y() }; + + QSGGeometry::updateTexturedRectGeometry( geometry(), rect, { -0.5, -0.5, 1.0, 1.0 } ); + geometry()->markVertexDataDirty(); + markDirty( QSGNode::DirtyGeometry ); // ? + markDirty( QSGNode::DirtyMaterial ); // ? + } + + private: +}; + +QSK_SUBCONTROL( QskColorWheel, Panel ) +QSK_SUBCONTROL( QskColorWheel, Handle ) + +QskColorWheel::QskColorWheel( QQuickItem* parent ) + : QskControl( parent ) +{ + setAcceptedMouseButtons( Qt::LeftButton ); +} + +void QskColorWheel::mousePressEvent( QMouseEvent* event ) +{ +} + +void QskColorWheel::mouseReleaseEvent( QMouseEvent* event ) +{ +} + +void QskColorWheel::mouseMoveEvent( QMouseEvent* event ) +{ + setSkinHint( Handle, event->pos() ); +} + +QskColorWheelSkinlet::QskColorWheelSkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { PanelRole } ); +} + +QRectF QskColorWheelSkinlet::subControlRect( + const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const +{ + return contentsRect; +} + +QSGNode* QskColorWheelSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto* const q = static_cast< const QskColorWheel* >( skinnable ); + + if ( nodeRole == PanelRole ) + { + auto* const wheelNode = QskSGNode::ensureNode< QskColorWheelNode >( node ); + auto rect = q->contentsRect(); + const auto size = qMin( rect.width(), rect.height() ); + rect.setSize( { size, size } ); + const auto mouse = q->effectiveSkinHint( QskColorWheel::Handle ).toPointF(); + + + wheelNode->update( rect, q->mapToScene(mouse), q->mapRectToScene( rect ), q->window()->size() ); + return wheelNode; + } + return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); +} + +QSizeF QskColorWheelSkinlet::sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const +{ + if ( which != Qt::PreferredSize ) + return QSizeF(); + + const auto* const q = static_cast< const QskColorWheel* >( skinnable ); + const auto size = q->contentsRect(); + return { qMin( size.width(), size.height() ), qMin( size.width(), size.height() ) }; +} + +ColorPickerPage::ColorPickerPage( QQuickItem* parent ) + : Page( parent ) +{ + auto* const layout = new QskLinearBox( this ); + auto* const control = new QskColorWheel( layout ); + auto* const skinlet = new QskColorWheelSkinlet; + control->setSkinlet( skinlet ); + skinlet->setOwnedBySkinnable( true ); +} \ No newline at end of file diff --git a/examples/gallery/colorpickers/ColorPickerPage.h b/examples/gallery/colorpickers/ColorPickerPage.h new file mode 100644 index 000000000..6c894fdf6 --- /dev/null +++ b/examples/gallery/colorpickers/ColorPickerPage.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include "Page.h" +#include "QskAspect.h" +#include "QskSkin.h" + +#include +#include + +class ColorPickerPage : public Page +{ + public: + explicit ColorPickerPage( QQuickItem* = nullptr ); +}; + +class QskColorWheel : public QskControl +{ + public: + QSK_SUBCONTROLS( Panel, Handle ) + explicit QskColorWheel( QQuickItem* parent = nullptr ); + + void mousePressEvent( QMouseEvent* event ) override; + void mouseReleaseEvent( QMouseEvent* event ) override; + void mouseMoveEvent( QMouseEvent* event ) override; +}; + +class QskColorWheelSkinlet : public QskSkinlet +{ + public: + enum NodeRoles + { + PanelRole + }; + + QskColorWheelSkinlet( QskSkin* skin = nullptr ); + + QRectF subControlRect( + const QskSkinnable* skinnable, const QRectF&, QskAspect::Subcontrol ) const override; + + QSGNode* updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* ) const override; + + QSizeF sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const override; +}; \ No newline at end of file diff --git a/examples/gallery/main.cpp b/examples/gallery/main.cpp index 6861c7787..edae76a96 100644 --- a/examples/gallery/main.cpp +++ b/examples/gallery/main.cpp @@ -10,6 +10,7 @@ #include "selector/SelectorPage.h" #include "dialog/DialogPage.h" #include "listbox/ListBoxPage.h" +#include "colorpickers/ColorPickerPage.h" #include #include @@ -257,6 +258,7 @@ namespace tabView->addPage( "Selectors", new SelectorPage() ); tabView->addPage( "Dialogs", new DialogPage() ); tabView->addPage( "ListBox", new ListBoxPage() ); + tabView->addPage( "Color Pickers", new ColorPickerPage() ); connect( header, &Header::enabledToggled, tabView, &TabView::setPagesEnabled ); diff --git a/src/nodes/shaders/colorwheel.frag b/src/nodes/shaders/colorwheel.frag new file mode 100644 index 000000000..10822a070 --- /dev/null +++ b/src/nodes/shaders/colorwheel.frag @@ -0,0 +1,94 @@ +uniform lowp float qt_Opacity; +uniform lowp vec4 color; +uniform lowp vec4 rect; +uniform lowp vec4 mouse; + +#undef ANIMATE + +#define M_PI 3.1415926535897932384626433832795 + +// 0 1 2 3 4 5 +// R 1 1 0 0 0 1 +// G 0 1 1 1 0 0 +// B 0 0 0 1 1 1 +vec3 getHueColor(vec2 pos) +{ +#ifdef ANIMATE + float theta = mod(3.0 + 3.0 * atan(pos.x, pos.y) / M_PI + iTime, 6.0); +#else + float theta = 3.0 + 3.0 * atan(pos.x, pos.y) / M_PI; +#endif + + vec3 color = vec3(0.0); + + return clamp(abs(mod(theta + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0); +} + +void mainImage( out vec4 fragColor, in vec2 fragCoord ) +{ + vec2 iOffset = vec2(-rect.x, -rect.y); + vec2 iResolution = rect.zw; + vec4 iMouse = mouse; + + vec2 uv = vec2(2.0, -2.0) * (fragCoord.xy + iOffset - 0.5 * iResolution.xy) / iResolution.y; + vec2 mouse = vec2(2.0, -2.0) * (iMouse.xy + iOffset - 0.5 * iResolution.xy) / iResolution.y; + + float l = length(uv); + float m = length(mouse); + + fragColor = vec4(0.0); + + if (l >= 0.75 && l <= 1.0) + { + l = 1.0 - abs((l - 0.875) * 8.0); + l = clamp(l * iResolution.y * 0.0625, 0.0, 1.0); // Antialiasing approximation + + fragColor = vec4(l * getHueColor(uv), l); + } + else if (l < 0.75) + { + vec3 pickedHueColor; + + if (m < 0.75 || m > 1.0) + { +#ifdef ANIMATE + mouse = vec2(sin(iTime), cos(iTime)); +#else + mouse = vec2(0.0, -1.0); + pickedHueColor = vec3(1.0, 0.0, 0.0); +#endif + } +#ifndef ANIMATE + else + { +#endif + pickedHueColor = getHueColor(mouse); +#ifndef ANIMATE + } +#endif + + uv = uv / 0.75; + mouse = normalize(mouse); + + float sat = 1.5 - (dot(uv, mouse) + 0.5); // [0.0,1.5] + + if (sat < 1.5) + { + float h = sat / sqrt(3.0); + vec2 om = vec2(cross(vec3(mouse, 0.0), vec3(0.0, 0.0, 1.0))); + float lum = dot(uv, om); + + if (abs(lum) <= h) + { + l = clamp((h - abs(lum)) * iResolution.y * 0.5, 0.0, 1.0) * clamp((1.5 - sat) / 1.5 * iResolution.y * 0.5, 0.0, 1.0); // Fake antialiasing + fragColor = vec4(l * mix(pickedHueColor, vec3(0.5 * (lum + h) / h), sat / 1.5), l); + } + } + } +} + +void main() +{ + mainImage(gl_FragColor, gl_FragCoord); + gl_FragColor *= qt_Opacity; +} \ No newline at end of file diff --git a/src/nodes/shaders/colorwheel.vert b/src/nodes/shaders/colorwheel.vert new file mode 100644 index 000000000..9a5507ceb --- /dev/null +++ b/src/nodes/shaders/colorwheel.vert @@ -0,0 +1,7 @@ +attribute highp vec4 vertex; +uniform highp mat4 qt_Matrix; + +void main() +{ + gl_Position = qt_Matrix * vertex; +} \ No newline at end of file From 08049516227cd38d17479bf2f3b521d281674243 Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Sun, 17 Dec 2023 19:21:19 +0100 Subject: [PATCH 3/9] add qsk based arc + triangle --- .../gallery/colorpickers/ColorPickerPage.cpp | 215 +++++++++++++++++- .../gallery/colorpickers/ColorPickerPage.h | 33 +++ 2 files changed, 240 insertions(+), 8 deletions(-) diff --git a/examples/gallery/colorpickers/ColorPickerPage.cpp b/examples/gallery/colorpickers/ColorPickerPage.cpp index 58b2bd6de..e55317c87 100644 --- a/examples/gallery/colorpickers/ColorPickerPage.cpp +++ b/examples/gallery/colorpickers/ColorPickerPage.cpp @@ -1,16 +1,29 @@ #include "ColorPickerPage.h" +#include "QskBoxNode.h" +#include "QskControl.h" +#include "QskGradient.h" +#include "QskGradientStop.h" #include "QskLinearBox.h" +#include "QskSkin.h" #include "QskSkinlet.h" +#include "QskSkinnable.h" +#include "QskSlider.h" #include "colorpickers/ColorPickerPage.h" +#include +#include #include #include #include +#include #include #include +#include #include #include +#include +#include struct Color { @@ -41,7 +54,46 @@ class MinimalShader : public QSGSimpleMaterialShader< Color > "rect", color->rect.x(), color->rect.y(), color->rect.width(), color->rect.height() ); program()->setUniformValue( "mouse", color->mouse.x(), color->mouse.y(), color->mouse.x(), color->mouse.y() ); - qDebug() << color->mouse; + } +}; + +class QskBrightnessTriangleNode : public QSGGeometryNode +{ + static constexpr auto vertexCount = 3; + + public: + QskBrightnessTriangleNode() + { + auto* const geometry = + new QSGGeometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), vertexCount ); + geometry->setDrawingMode( QSGGeometry::DrawTriangles ); + setGeometry( geometry ); + + auto* const material = new QSGVertexColorMaterial; + setMaterial( material ); + } + + void update( const QRectF& rect, const QColor& color, const qreal rad ) + { + const QColor colors[]{ color, Qt::black, Qt::white }; + + const auto center = rect.center(); + const auto radius = qMin( rect.width(), rect.height() ) / 2; + + const auto step = 2 * M_PI / 3; + + auto* const vertices = geometry()->vertexDataAsColoredPoint2D(); + for ( int i = 0; i < vertexCount; ++i ) + { + const auto x = qFastCos( rad + i * step ) * radius + center.x(); + const auto y = qFastSin( rad + i * step ) * radius + center.y(); + const uchar c[ 4 ]{ ( uchar ) colors[ i ].red(), ( uchar ) colors[ i ].green(), + ( uchar ) colors[ i ].blue(), 255 }; + vertices[ i ].set( x, y, c[ 0 ], c[ 1 ], c[ 2 ], c[ 3 ] ); + } + geometry()->markVertexDataDirty(); + markDirty( QSGNode::DirtyGeometry ); + markDirty( QSGNode::DirtyMaterial ); } }; @@ -128,8 +180,8 @@ QSGNode* QskColorWheelSkinlet::updateSubNode( rect.setSize( { size, size } ); const auto mouse = q->effectiveSkinHint( QskColorWheel::Handle ).toPointF(); - - wheelNode->update( rect, q->mapToScene(mouse), q->mapRectToScene( rect ), q->window()->size() ); + wheelNode->update( + rect, q->mapToScene( mouse ), q->mapRectToScene( rect ), q->window()->size() ); return wheelNode; } return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); @@ -149,9 +201,156 @@ QSizeF QskColorWheelSkinlet::sizeHint( ColorPickerPage::ColorPickerPage( QQuickItem* parent ) : Page( parent ) { - auto* const layout = new QskLinearBox( this ); - auto* const control = new QskColorWheel( layout ); - auto* const skinlet = new QskColorWheelSkinlet; - control->setSkinlet( skinlet ); - skinlet->setOwnedBySkinnable( true ); + auto* const layout = new QskLinearBox( Qt::Horizontal, this ); + { + auto* const control = new QskColorWheel( layout ); + auto* const skinlet = new QskColorWheelSkinlet; + control->setSkinlet( skinlet ); + skinlet->setOwnedBySkinnable( true ); + } + { + auto* const row = new QskLinearBox( Qt::Vertical, layout ); + auto* const control = new QskBrightnessTriangle( row ); + auto* const skinlet = new QskBrightnessTriangleSkinlet; + control->setSkinlet( skinlet ); + skinlet->setOwnedBySkinnable( true ); + + auto* const thickness = new QskSlider( Qt::Horizontal, row ); + + connect( thickness, &QskSlider::valueChanged, this, [ = ]( qreal v ) { + auto metrics = control->arcMetricsHint( QskBrightnessTriangle::Arc ); + + metrics.setThickness( + control->subControlRect( QskBrightnessTriangle::Arc ).width() / 2 * v ); + control->setArcMetricsHint( QskBrightnessTriangle::Arc, metrics ); + } ); + } +} + +QSK_SUBCONTROL( QskBrightnessTriangle, Panel ) +QSK_SUBCONTROL( QskBrightnessTriangle, Handle ) +QSK_SUBCONTROL( QskBrightnessTriangle, Arc ) +QSK_SUBCONTROL( QskBrightnessTriangle, Triangle ) + +QskBrightnessTriangle::QskBrightnessTriangle( QQuickItem* parent ) + : QskControl( parent ) +{ + setAcceptedMouseButtons( Qt::LeftButton ); + + static const QskGradientStops stops = { + { 0.0000, QColor::fromRgb( 255, 0, 0 ) }, + { 0.1667, QColor::fromRgb( 255, 255, 0 ) }, + { 0.3333, QColor::fromRgb( 0, 255, 0 ) }, + { 0.5000, QColor::fromRgb( 0, 255, 255 ) }, + { 0.6667, QColor::fromRgb( 0, 0, 255 ) }, + { 0.8333, QColor::fromRgb( 255, 0, 255 ) }, + { 1.0000, QColor::fromRgb( 255, 0, 0 ) }, + }; + + QskGradient gradient( stops ); + setGradientHint( Arc, gradient ); + setArcMetricsHint( Arc, { 0, 360, 16 } ); +} + +void QskBrightnessTriangle::mousePressEvent( QMouseEvent* event ) +{ +} + +void QskBrightnessTriangle::mouseReleaseEvent( QMouseEvent* event ) +{ +} + +QColor getColorForPolarCoordinates( double angle ) +{ + angle = ( angle < 0.0 ? angle + 2 * M_PI : angle ); + + static const QskGradientStops stopsRGB = { + { 0.0000, QColor::fromRgb( 255, 0, 0 ) }, + { 0.1667, QColor::fromRgb( 255, 255, 0 ) }, + { 0.3333, QColor::fromRgb( 0, 255, 0 ) }, + { 0.5000, QColor::fromRgb( 0, 255, 255 ) }, + { 0.6667, QColor::fromRgb( 0, 0, 255 ) }, + { 0.8333, QColor::fromRgb( 255, 0, 255 ) }, + { 1.0000, QColor::fromRgb( 255, 0, 0 ) }, + }; + + static const QskGradient gradient( stopsRGB ); + const auto p = qAbs( angle / ( 2 * M_PI ) ); + return gradient.extracted( p, p ).startColor(); +} + +void QskBrightnessTriangle::mouseMoveEvent( QMouseEvent* event ) +{ + auto r = contentsRect(); + const auto x = event->pos().x() * 2 - r.width(); + const auto y = event->pos().y() * 2 - r.height(); + setPositionHint( Handle, qAtan2( y, x ) ); + setColor( Handle, getColorForPolarCoordinates( -qAtan2( y, x ) ) ); +} + +QskBrightnessTriangleSkinlet::QskBrightnessTriangleSkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { ArcRole, TriangleRole } ); +} + +QRectF QskBrightnessTriangleSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const +{ + const auto& r = contentsRect; + + if ( subcontrol == QskBrightnessTriangle::Arc ) + { + const auto size = qMin( r.width(), r.height() ) / 2; + auto rect = QRectF( 0, 0, size, size ); + + return { QPointF{ r.center().x() - size, r.center().y() - size }, + QPointF{ r.center().x() + size, r.center().y() + size } }; + } + if ( subcontrol == QskBrightnessTriangle::Triangle ) + { + const auto thickness = skinnable->arcMetricsHint( QskBrightnessTriangle::Arc ).thickness(); + const auto size = ( qMin( r.width(), contentsRect.height() ) ) / 2 - thickness; + auto rect = QRectF( 0, 0, size, size ); + + return { QPointF{ r.center().x() - size, r.center().y() - size }, + QPointF{ r.center().x() + size, r.center().y() + size } }; + } + return QskSkinlet::subControlRect( skinnable, contentsRect, subcontrol ); +} + +QSGNode* QskBrightnessTriangleSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + using Q = QskBrightnessTriangle; + const auto* const q = static_cast< const Q* >( skinnable ); + if ( nodeRole == ArcRole ) + { + const auto subcontrol = Q::Arc; + auto* const arcNode = QskSGNode::ensureNode< QskArcNode >( node ); + const auto rect = subControlRect( skinnable, q->contentsRect(), subcontrol ); + arcNode->setArcData( rect, q->arcMetricsHint( subcontrol ), q->gradientHint( subcontrol ) ); + return arcNode; + } + if ( nodeRole == TriangleRole ) + { + const auto subcontrol = Q::Triangle; + const auto rect = subControlRect( skinnable, q->contentsRect(), subcontrol ); + auto* const triangleNode = QskSGNode::ensureNode< QskBrightnessTriangleNode >( node ); + triangleNode->update( rect, q->color( Q::Handle ), q->positionHint( Q::Handle ) ); + return triangleNode; + } + return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); +} + +QSizeF QskBrightnessTriangleSkinlet::sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const +{ + if ( which != Qt::PreferredSize ) + return QSizeF(); + + const auto* const q = static_cast< const QskBrightnessTriangle* >( skinnable ); + const auto rect = q->contentsRect(); + const auto size = qMin( rect.width(), rect.height() ); + return { size, size }; } \ No newline at end of file diff --git a/examples/gallery/colorpickers/ColorPickerPage.h b/examples/gallery/colorpickers/ColorPickerPage.h index 6c894fdf6..35b12d88a 100644 --- a/examples/gallery/colorpickers/ColorPickerPage.h +++ b/examples/gallery/colorpickers/ColorPickerPage.h @@ -39,6 +39,39 @@ class QskColorWheelSkinlet : public QskSkinlet QskColorWheelSkinlet( QskSkin* skin = nullptr ); + QRectF subControlRect( + const QskSkinnable* skinnable, const QRectF&, QskAspect::Subcontrol ) const override; + + QSGNode* updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* ) const override; + + QSizeF sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const override; +}; + +class QskBrightnessTriangle : public QskControl +{ + public: + QSK_SUBCONTROLS( Panel, Arc, Triangle, Handle ) + explicit QskBrightnessTriangle( QQuickItem* parent = nullptr ); + + private: + void mousePressEvent( QMouseEvent* event ) override; + void mouseReleaseEvent( QMouseEvent* event ) override; + void mouseMoveEvent( QMouseEvent* event ) override; +}; + +class QskBrightnessTriangleSkinlet : public QskSkinlet +{ + public: + enum NodeRoles + { + ArcRole, + TriangleRole + }; + + QskBrightnessTriangleSkinlet( QskSkin* skin = nullptr ); + QRectF subControlRect( const QskSkinnable* skinnable, const QRectF&, QskAspect::Subcontrol ) const override; From 1ea0099c620c3e12b95e2f73267514794dfe9564 Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Mon, 18 Dec 2023 21:10:55 +0100 Subject: [PATCH 4/9] split up color wheel and triangle --- .../gallery/colorpickers/ColorPickerPage.cpp | 404 +++++++++++++----- .../gallery/colorpickers/ColorPickerPage.h | 59 ++- 2 files changed, 347 insertions(+), 116 deletions(-) diff --git a/examples/gallery/colorpickers/ColorPickerPage.cpp b/examples/gallery/colorpickers/ColorPickerPage.cpp index e55317c87..263db5483 100644 --- a/examples/gallery/colorpickers/ColorPickerPage.cpp +++ b/examples/gallery/colorpickers/ColorPickerPage.cpp @@ -1,16 +1,23 @@ #include "ColorPickerPage.h" +#include "QskAspect.h" #include "QskBoxNode.h" #include "QskControl.h" #include "QskGradient.h" #include "QskGradientStop.h" +#include "QskGridBox.h" #include "QskLinearBox.h" #include "QskSkin.h" #include "QskSkinlet.h" #include "QskSkinnable.h" #include "QskSlider.h" +#include "QskTextLabel.h" #include "colorpickers/ColorPickerPage.h" #include #include +#include +#include +#include +#include #include #include @@ -19,11 +26,82 @@ #include #include +#include +#include #include #include +#include #include #include #include +#include +#include +#include + +namespace +{ + double qskMapValueRange( + double value, double srcMin, double srcMax, double dstMin, double dstMax ) + { + value = std::min( std::max( value, srcMin ), srcMax ); + double percentage = ( value - srcMin ) / ( srcMax - srcMin ); + double mappedValue = dstMin + percentage * ( dstMax - dstMin ); + return mappedValue; + } + + QVector3D cartesian2barycentric( + const QPointF& p, const QPointF& t1, const QPointF& t2, const QPointF& t3 ) + { + const auto x = p.x(); + const auto y = p.y(); + + const auto x1 = t1.x(); + const auto y1 = t1.y(); + const auto x2 = t2.x(); + const auto y2 = t2.y(); + const auto x3 = t3.x(); + const auto y3 = t3.y(); + + const auto y2y3 = y2 - y3, x3x2 = x3 - x2, x1x3 = x1 - x3, y1y3 = y1 - y3, y3y1 = y3 - y1, + xx3 = x - x3, yy3 = y - y3; + + const auto d = y2y3 * x1x3 + x3x2 * y1y3, lambda1 = ( y2y3 * xx3 + x3x2 * yy3 ) / d, + lambda2 = ( y3y1 * xx3 + x1x3 * yy3 ) / d; + + return { ( float ) lambda1, ( float ) lambda2, ( float ) ( 1.0 - lambda1 - lambda2 ) }; + } + + QColor qskColorInTriangle( const QPointF& p, const QPointF& t1, const QPointF& t2, + const QPointF& t3, const QColor& c1, const QColor& c2, const QColor& c3 ) + { + const auto t = cartesian2barycentric( p, t1, t2, t3 ); + const auto r = + std::floor( ( c1.redF() * t[ 0 ] + c2.redF() * t[ 1 ] + c3.redF() * t[ 2 ] ) * 255.0 ); + const auto g = std::floor( + ( c1.greenF() * t[ 0 ] + c2.greenF() * t[ 1 ] + c3.greenF() * t[ 2 ] ) * 255.0 ); + const auto b = std::floor( + ( c1.blueF() * t[ 0 ] + c2.blueF() * t[ 1 ] + c3.blueF() * t[ 2 ] ) * 255.0 ); + + if ( t[ 0 ] < 0.0 || t[ 1 ] < 0.0 || t[ 2 ] < 0.0 ) + { + return {}; + } + + return { int( r ), int( g ), int( b ) }; + } + + QColor qskColorAt( const QskGradient& gradient, qreal ratio ) + { + return gradient.extracted( ratio, ratio ).startColor(); + } + + QColor qskColorForAngle( const QskGradient& gradient, qreal angle ) + { + angle = ( angle < 0.0 ? angle + 2 * M_PI : angle ); + const auto ratio = qAbs( angle / ( 2 * M_PI ) ); + return qskColorAt( gradient, ratio ); + } +} struct Color { @@ -57,12 +135,12 @@ class MinimalShader : public QSGSimpleMaterialShader< Color > } }; -class QskBrightnessTriangleNode : public QSGGeometryNode +class QskColorTriangleNode : public QSGGeometryNode { static constexpr auto vertexCount = 3; public: - QskBrightnessTriangleNode() + QskColorTriangleNode() { auto* const geometry = new QSGGeometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), vertexCount ); @@ -73,9 +151,10 @@ class QskBrightnessTriangleNode : public QSGGeometryNode setMaterial( material ); } - void update( const QRectF& rect, const QColor& color, const qreal rad ) + void update( + const QRectF& rect, const QColor& c1, const QColor& c2, const QColor& c3, const qreal rad ) { - const QColor colors[]{ color, Qt::black, Qt::white }; + const QColor colors[]{ c1, c2, c3 }; const auto center = rect.center(); const auto radius = qMin( rect.width(), rect.height() ) / 2; @@ -133,13 +212,47 @@ class QskColorWheelNode : public QSGGeometryNode private: }; -QSK_SUBCONTROL( QskColorWheel, Panel ) +QSK_SUBCONTROL( QskColorWheel, Arc ) QSK_SUBCONTROL( QskColorWheel, Handle ) QskColorWheel::QskColorWheel( QQuickItem* parent ) : QskControl( parent ) { setAcceptedMouseButtons( Qt::LeftButton ); + + static const QskGradientStops stops = { + { 0.0000, QColor::fromRgb( 255, 0, 0 ) }, + { 0.1667, QColor::fromRgb( 255, 255, 0 ) }, + { 0.3333, QColor::fromRgb( 0, 255, 0 ) }, + { 0.5000, QColor::fromRgb( 0, 255, 255 ) }, + { 0.6667, QColor::fromRgb( 0, 0, 255 ) }, + { 0.8333, QColor::fromRgb( 255, 0, 255 ) }, + { 1.0000, QColor::fromRgb( 255, 0, 0 ) }, + }; + + QskGradient gradient( stops ); + setGradientHint( Arc, gradient ); + setArcMetricsHint( Arc, { 0, 360, 16 } ); + + setColor( Handle, Qt::white ); + setBoxBorderColorsHint( Handle, { Qt::white } ); + setBoxBorderMetricsHint( Handle, { 2 } ); + setBoxShapeHint( Handle, { 100.0, 100.0, 100.0, 100.0, Qt::RelativeSize } ); +} + +QColor QskColorWheel::selectedColor() const +{ + return m_selectedColor; +} + +void QskColorWheel::setSelectedColor( const QColor& color ) +{ + if ( m_selectedColor != color ) + { + m_selectedColor = color; + setColor( Handle, color ); + Q_EMIT selectedColorChanged( m_selectedColor ); + } } void QskColorWheel::mousePressEvent( QMouseEvent* event ) @@ -152,38 +265,71 @@ void QskColorWheel::mouseReleaseEvent( QMouseEvent* event ) void QskColorWheel::mouseMoveEvent( QMouseEvent* event ) { - setSkinHint( Handle, event->pos() ); + const auto r = contentsRect(); + const auto ux = qskMapValueRange( event->pos().x(), r.left(), r.right(), -1, +1 ); + const auto uy = qskMapValueRange( event->pos().y(), r.top(), r.bottom(), +1, -1 ); + + if ( QVector2D( ux, uy ).length() > 1.0 ) // TODO && Pressed + { + event->ignore(); + return; + } + + const auto rad = qAtan2( uy, ux ); + const auto color = qskColorForAngle( gradientHint( Arc ), rad ); + setPositionHint( Handle, -rad ); + setSelectedColor( color ); } QskColorWheelSkinlet::QskColorWheelSkinlet( QskSkin* skin ) : QskSkinlet( skin ) { - setNodeRoles( { PanelRole } ); + setNodeRoles( { ArcRole, HandleRole } ); } -QRectF QskColorWheelSkinlet::subControlRect( - const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const +QRectF QskColorWheelSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const { - return contentsRect; + using Q = QskColorWheel; + const auto* const q = static_cast< const Q* >( skinnable ); + + if ( subcontrol == Q::Arc ) + { + const auto size = qMin( contentsRect.width(), contentsRect.height() ); + return { contentsRect.center().x() - size / 2, contentsRect.center().y() - size / 2, size, + size }; + } + + if ( subcontrol == Q::Handle ) + { + const auto& r = contentsRect; + const auto thickness = skinnable->arcMetricsHint( Q::Arc ).thickness() / 2; + const auto angle = q->positionHint( Q::Handle ); + const auto radius = qMin( r.width(), r.height() ) / 2 - thickness; + const auto x = contentsRect.center().x() + radius * qFastCos( angle ); + const auto y = contentsRect.center().y() + radius * qFastSin( angle ); + return { QPointF{ x - thickness, y - thickness }, QPointF{ x + thickness, y + thickness } }; + } + + return QskSkinlet::subControlRect( skinnable, contentsRect, subcontrol ); } QSGNode* QskColorWheelSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { - const auto* const q = static_cast< const QskColorWheel* >( skinnable ); + using Q = QskColorWheel; + const auto* const q = static_cast< const Q* >( skinnable ); + + if ( nodeRole == ArcRole ) + { + return updateArcNode( skinnable, node, Q::Arc ); + } - if ( nodeRole == PanelRole ) + if ( nodeRole == HandleRole ) { - auto* const wheelNode = QskSGNode::ensureNode< QskColorWheelNode >( node ); - auto rect = q->contentsRect(); - const auto size = qMin( rect.width(), rect.height() ); - rect.setSize( { size, size } ); - const auto mouse = q->effectiveSkinHint( QskColorWheel::Handle ).toPointF(); - - wheelNode->update( - rect, q->mapToScene( mouse ), q->mapRectToScene( rect ), q->window()->size() ); - return wheelNode; + return updateBoxNode( skinnable, node, Q::Handle ); } + return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); } @@ -191,165 +337,211 @@ QSizeF QskColorWheelSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const { if ( which != Qt::PreferredSize ) - return QSizeF(); + { + return {}; + } - const auto* const q = static_cast< const QskColorWheel* >( skinnable ); - const auto size = q->contentsRect(); - return { qMin( size.width(), size.height() ), qMin( size.width(), size.height() ) }; + const auto* const q = static_cast< const QskColorTriangle* >( skinnable ); + const auto rect = q->contentsRect(); + const auto size = qMin( rect.width(), rect.height() ); + return { size, size }; } ColorPickerPage::ColorPickerPage( QQuickItem* parent ) : Page( parent ) { - auto* const layout = new QskLinearBox( Qt::Horizontal, this ); + auto* const layout = new QskGridBox( this ); + auto* const colorWheel = new QskColorWheel( layout ); { - auto* const control = new QskColorWheel( layout ); auto* const skinlet = new QskColorWheelSkinlet; - control->setSkinlet( skinlet ); + colorWheel->setSkinlet( skinlet ); skinlet->setOwnedBySkinnable( true ); } + auto* const colorTriangle = new QskColorTriangle( layout ); { - auto* const row = new QskLinearBox( Qt::Vertical, layout ); - auto* const control = new QskBrightnessTriangle( row ); - auto* const skinlet = new QskBrightnessTriangleSkinlet; - control->setSkinlet( skinlet ); + auto* const skinlet = new QskColorTriangleSkinlet; + colorTriangle->setSkinlet( skinlet ); skinlet->setOwnedBySkinnable( true ); + } - auto* const thickness = new QskSlider( Qt::Horizontal, row ); - - connect( thickness, &QskSlider::valueChanged, this, [ = ]( qreal v ) { - auto metrics = control->arcMetricsHint( QskBrightnessTriangle::Arc ); + layout->addItem( new QskTextLabel( "QskColorWheel" ), 0, 0 ); + layout->addItem( colorWheel, 1, 0 ); + layout->addItem( new QskTextLabel( "QskColorTriangle" ), 0, 1 ); + layout->addItem( colorTriangle, 1, 1 ); - metrics.setThickness( - control->subControlRect( QskBrightnessTriangle::Arc ).width() / 2 * v ); - control->setArcMetricsHint( QskBrightnessTriangle::Arc, metrics ); - } ); + for ( int col = 0; col < layout->columnCount(); col++ ) + { + layout->setColumnStretchFactor( col, 1 ); } + + connect( colorWheel, &QskColorWheel::selectedColorChanged, colorTriangle, + [ colorTriangle ]( + const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); } -QSK_SUBCONTROL( QskBrightnessTriangle, Panel ) -QSK_SUBCONTROL( QskBrightnessTriangle, Handle ) -QSK_SUBCONTROL( QskBrightnessTriangle, Arc ) -QSK_SUBCONTROL( QskBrightnessTriangle, Triangle ) +QSK_SUBCONTROL( QskColorTriangle, Triangle ) +QSK_SUBCONTROL( QskColorTriangle, Handle ) -QskBrightnessTriangle::QskBrightnessTriangle( QQuickItem* parent ) +QskColorTriangle::QskColorTriangle( QQuickItem* parent ) : QskControl( parent ) { setAcceptedMouseButtons( Qt::LeftButton ); - static const QskGradientStops stops = { - { 0.0000, QColor::fromRgb( 255, 0, 0 ) }, - { 0.1667, QColor::fromRgb( 255, 255, 0 ) }, - { 0.3333, QColor::fromRgb( 0, 255, 0 ) }, - { 0.5000, QColor::fromRgb( 0, 255, 255 ) }, - { 0.6667, QColor::fromRgb( 0, 0, 255 ) }, - { 0.8333, QColor::fromRgb( 255, 0, 255 ) }, - { 1.0000, QColor::fromRgb( 255, 0, 0 ) }, - }; + setStrutSizeHint( Handle, strutSizeHint( QskSlider::Handle ) ); + setColor( Handle, Qt::white ); + setBoxBorderColorsHint( Handle, { Qt::white } ); + setBoxBorderMetricsHint( Handle, { 2 } ); + setBoxShapeHint( Handle, { 100.0, 100.0, 100.0, 100.0, Qt::RelativeSize } ); - QskGradient gradient( stops ); - setGradientHint( Arc, gradient ); - setArcMetricsHint( Arc, { 0, 360, 16 } ); + setPositionHint( Triangle, qDegreesToRadians( 45.0 ) ); } -void QskBrightnessTriangle::mousePressEvent( QMouseEvent* event ) +QColor QskColorTriangle::selectedColor() const { + return m_selectedColor; } -void QskBrightnessTriangle::mouseReleaseEvent( QMouseEvent* event ) +void QskColorTriangle::setSelectedColor( const QColor& color ) { + if ( m_selectedColor != color ) + { + m_selectedColor = color; + setColor( Handle, color ); + Q_EMIT selectedColorChanged( m_selectedColor ); + } } -QColor getColorForPolarCoordinates( double angle ) +void QskColorTriangle::setColors( const QColor& c1, const QColor& c2, const QColor& c3 ) { - angle = ( angle < 0.0 ? angle + 2 * M_PI : angle ); + m_c1 = c1; + m_c2 = c2; + m_c3 = c3; + update(); +} - static const QskGradientStops stopsRGB = { - { 0.0000, QColor::fromRgb( 255, 0, 0 ) }, - { 0.1667, QColor::fromRgb( 255, 255, 0 ) }, - { 0.3333, QColor::fromRgb( 0, 255, 0 ) }, - { 0.5000, QColor::fromRgb( 0, 255, 255 ) }, - { 0.6667, QColor::fromRgb( 0, 0, 255 ) }, - { 0.8333, QColor::fromRgb( 255, 0, 255 ) }, - { 1.0000, QColor::fromRgb( 255, 0, 0 ) }, - }; +QColor QskColorTriangle::color1() const +{ + return m_c1; +} + +QColor QskColorTriangle::color2() const +{ + return m_c2; +} - static const QskGradient gradient( stopsRGB ); - const auto p = qAbs( angle / ( 2 * M_PI ) ); - return gradient.extracted( p, p ).startColor(); +QColor QskColorTriangle::color3() const +{ + return m_c3; +} + +void QskColorTriangle::mousePressEvent( QMouseEvent* event ) +{ } -void QskBrightnessTriangle::mouseMoveEvent( QMouseEvent* event ) +void QskColorTriangle::mouseReleaseEvent( QMouseEvent* event ) { - auto r = contentsRect(); - const auto x = event->pos().x() * 2 - r.width(); - const auto y = event->pos().y() * 2 - r.height(); - setPositionHint( Handle, qAtan2( y, x ) ); - setColor( Handle, getColorForPolarCoordinates( -qAtan2( y, x ) ) ); } -QskBrightnessTriangleSkinlet::QskBrightnessTriangleSkinlet( QskSkin* skin ) +void QskColorTriangle::mouseMoveEvent( QMouseEvent* event ) +{ + const auto rect = subControlRect( Triangle ); + if ( rect.contains( event->pos() ) ) + { + static constexpr auto step = 2 * M_PI / 3; + const auto angle = 0.0; + const auto radius = qMin( rect.width(), rect.height() ) / 2; + QTransform transformation; + transformation.rotate( angle, Qt::ZAxis ); + + auto t1 = QPointF{ qFastCos( 0 * step ), qFastSin( 0 * step ) }; + auto t2 = QPointF{ qFastCos( 1 * step ), qFastSin( 1 * step ) }; + auto t3 = QPointF{ qFastCos( 2 * step ), qFastSin( 2 * step ) }; + + t1 *= radius; + t2 *= radius; + t3 *= radius; + + t1 += rect.center(); + t2 += rect.center(); + t3 += rect.center(); + + const auto color = qskColorInTriangle( event->pos(), t1, t2, t3, m_c1, m_c2, m_c3 ); + if ( color.isValid() ) + { + setSelectedColor( color ); + setSkinHint( Handle, event->pos() ); + } + } + event->ignore(); +} + +QskColorTriangleSkinlet::QskColorTriangleSkinlet( QskSkin* skin ) : QskSkinlet( skin ) { - setNodeRoles( { ArcRole, TriangleRole } ); + setNodeRoles( { TriangleRole, HandleRole } ); } -QRectF QskBrightnessTriangleSkinlet::subControlRect( const QskSkinnable* skinnable, +QRectF QskColorTriangleSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const { + using Q = QskColorTriangle; + const auto* const q = static_cast< const Q* >( skinnable ); + const auto& r = contentsRect; - if ( subcontrol == QskBrightnessTriangle::Arc ) + if ( subcontrol == Q::Triangle ) { - const auto size = qMin( r.width(), r.height() ) / 2; + const auto size = ( qMin( r.width(), contentsRect.height() ) ) / 2; auto rect = QRectF( 0, 0, size, size ); return { QPointF{ r.center().x() - size, r.center().y() - size }, QPointF{ r.center().x() + size, r.center().y() + size } }; } - if ( subcontrol == QskBrightnessTriangle::Triangle ) - { - const auto thickness = skinnable->arcMetricsHint( QskBrightnessTriangle::Arc ).thickness(); - const auto size = ( qMin( r.width(), contentsRect.height() ) ) / 2 - thickness; - auto rect = QRectF( 0, 0, size, size ); - return { QPointF{ r.center().x() - size, r.center().y() - size }, - QPointF{ r.center().x() + size, r.center().y() + size } }; + if ( subcontrol == Q::Handle ) + { + const auto rect = subControlRect( skinnable, contentsRect, Q::Triangle ); + const auto size = + qMin( q->strutSizeHint( Q::Handle ).width(), q->strutSizeHint( Q::Handle ).height() ); + const auto position = q->effectiveSkinHint( Q::Handle ).toPointF(); + return { QPointF{ position.x() - size, position.y() - size }, + QPointF{ position.x() + size, position.y() + size } }; } return QskSkinlet::subControlRect( skinnable, contentsRect, subcontrol ); } -QSGNode* QskBrightnessTriangleSkinlet::updateSubNode( +QSGNode* QskColorTriangleSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { - using Q = QskBrightnessTriangle; + using Q = QskColorTriangle; const auto* const q = static_cast< const Q* >( skinnable ); - if ( nodeRole == ArcRole ) - { - const auto subcontrol = Q::Arc; - auto* const arcNode = QskSGNode::ensureNode< QskArcNode >( node ); - const auto rect = subControlRect( skinnable, q->contentsRect(), subcontrol ); - arcNode->setArcData( rect, q->arcMetricsHint( subcontrol ), q->gradientHint( subcontrol ) ); - return arcNode; - } + if ( nodeRole == TriangleRole ) { const auto subcontrol = Q::Triangle; const auto rect = subControlRect( skinnable, q->contentsRect(), subcontrol ); - auto* const triangleNode = QskSGNode::ensureNode< QskBrightnessTriangleNode >( node ); - triangleNode->update( rect, q->color( Q::Handle ), q->positionHint( Q::Handle ) ); + auto* const triangleNode = QskSGNode::ensureNode< QskColorTriangleNode >( node ); + triangleNode->update( + rect, q->color1(), q->color2(), q->color3(), q->positionHint( Q::Handle ) ); return triangleNode; } + if ( nodeRole == HandleRole ) + { + return updateBoxNode( skinnable, node, Q::Handle ); + } + return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); } -QSizeF QskBrightnessTriangleSkinlet::sizeHint( +QSizeF QskColorTriangleSkinlet::sizeHint( const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const { if ( which != Qt::PreferredSize ) - return QSizeF(); + { + return {}; + } - const auto* const q = static_cast< const QskBrightnessTriangle* >( skinnable ); + const auto* const q = static_cast< const QskColorTriangle* >( skinnable ); const auto rect = q->contentsRect(); const auto size = qMin( rect.width(), rect.height() ); return { size, size }; diff --git a/examples/gallery/colorpickers/ColorPickerPage.h b/examples/gallery/colorpickers/ColorPickerPage.h index 35b12d88a..cd521400d 100644 --- a/examples/gallery/colorpickers/ColorPickerPage.h +++ b/examples/gallery/colorpickers/ColorPickerPage.h @@ -11,6 +11,9 @@ #include #include +#include +#include +#include class ColorPickerPage : public Page { @@ -20,13 +23,26 @@ class ColorPickerPage : public Page class QskColorWheel : public QskControl { + Q_OBJECT + Q_PROPERTY( + QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged ) public: - QSK_SUBCONTROLS( Panel, Handle ) + QSK_SUBCONTROLS( Arc, Handle ) explicit QskColorWheel( QQuickItem* parent = nullptr ); + QColor selectedColor() const; + void setSelectedColor( const QColor& color ); + + protected: void mousePressEvent( QMouseEvent* event ) override; void mouseReleaseEvent( QMouseEvent* event ) override; void mouseMoveEvent( QMouseEvent* event ) override; + + Q_SIGNALS: + void selectedColorChanged( const QColor& color ); + + private: + QColor m_selectedColor; }; class QskColorWheelSkinlet : public QskSkinlet @@ -34,7 +50,8 @@ class QskColorWheelSkinlet : public QskSkinlet public: enum NodeRoles { - PanelRole + ArcRole, + HandleRole }; QskColorWheelSkinlet( QskSkin* skin = nullptr ); @@ -49,28 +66,50 @@ class QskColorWheelSkinlet : public QskSkinlet const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const override; }; -class QskBrightnessTriangle : public QskControl +class QskColorTriangle : public QskControl { + Q_OBJECT + Q_PROPERTY( + QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged ) + public: - QSK_SUBCONTROLS( Panel, Arc, Triangle, Handle ) - explicit QskBrightnessTriangle( QQuickItem* parent = nullptr ); + QSK_SUBCONTROLS( Triangle, Handle ) + explicit QskColorTriangle( QQuickItem* parent = nullptr ); - private: + QColor selectedColor() const; + + void setColors(const QColor& c1, const QColor& c2, const QColor& c3); + QColor color1()const; + QColor color2()const; + QColor color3()const; + + Q_SIGNALS: + void selectedColorChanged( const QColor& color ); + + protected: void mousePressEvent( QMouseEvent* event ) override; void mouseReleaseEvent( QMouseEvent* event ) override; void mouseMoveEvent( QMouseEvent* event ) override; + + private: + void setSelectedColor( const QColor& color ); + + QColor m_selectedColor; + QColor m_c1; + QColor m_c2 = Qt::black; + QColor m_c3 = Qt::white; }; -class QskBrightnessTriangleSkinlet : public QskSkinlet +class QskColorTriangleSkinlet : public QskSkinlet { public: enum NodeRoles { - ArcRole, - TriangleRole + TriangleRole, + HandleRole }; - QskBrightnessTriangleSkinlet( QskSkin* skin = nullptr ); + QskColorTriangleSkinlet( QskSkin* skin = nullptr ); QRectF subControlRect( const QskSkinnable* skinnable, const QRectF&, QskAspect::Subcontrol ) const override; From 78c53458ed082404813a960b24d09fec08c67e60 Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Tue, 19 Dec 2023 19:24:07 +0100 Subject: [PATCH 5/9] move implementation into own files --- examples/gallery/CMakeLists.txt | 20 +- .../gallery/colorpickers/ColorPickerPage.cpp | 567 ++---------------- .../gallery/colorpickers/ColorPickerPage.h | 108 ---- .../colorpickers/QskColorRectangle.cpp | 128 ++++ .../gallery/colorpickers/QskColorRectangle.h | 33 + .../colorpickers/QskColorRectangleSkinlet.cpp | 102 ++++ .../colorpickers/QskColorRectangleSkinlet.h | 23 + .../gallery/colorpickers/QskColorTriangle.cpp | 160 +++++ .../gallery/colorpickers/QskColorTriangle.h | 40 ++ .../colorpickers/QskColorTriangleNode.cpp | 42 ++ .../colorpickers/QskColorTriangleNode.h | 19 + .../colorpickers/QskColorTriangleSkinlet.cpp | 87 +++ .../colorpickers/QskColorTriangleSkinlet.h | 29 + .../gallery/colorpickers/QskColorWheel.cpp | 94 +++ examples/gallery/colorpickers/QskColorWheel.h | 30 + .../colorpickers/QskColorWheelSkinlet.cpp | 71 +++ .../colorpickers/QskColorWheelSkinlet.h | 30 + src/common/QskFunctions.h | 9 + src/nodes/shaders/colorwheel.frag | 94 --- src/nodes/shaders/colorwheel.vert | 7 - 20 files changed, 963 insertions(+), 730 deletions(-) create mode 100644 examples/gallery/colorpickers/QskColorRectangle.cpp create mode 100644 examples/gallery/colorpickers/QskColorRectangle.h create mode 100644 examples/gallery/colorpickers/QskColorRectangleSkinlet.cpp create mode 100644 examples/gallery/colorpickers/QskColorRectangleSkinlet.h create mode 100644 examples/gallery/colorpickers/QskColorTriangle.cpp create mode 100644 examples/gallery/colorpickers/QskColorTriangle.h create mode 100644 examples/gallery/colorpickers/QskColorTriangleNode.cpp create mode 100644 examples/gallery/colorpickers/QskColorTriangleNode.h create mode 100644 examples/gallery/colorpickers/QskColorTriangleSkinlet.cpp create mode 100644 examples/gallery/colorpickers/QskColorTriangleSkinlet.h create mode 100644 examples/gallery/colorpickers/QskColorWheel.cpp create mode 100644 examples/gallery/colorpickers/QskColorWheel.h create mode 100644 examples/gallery/colorpickers/QskColorWheelSkinlet.cpp create mode 100644 examples/gallery/colorpickers/QskColorWheelSkinlet.h delete mode 100644 src/nodes/shaders/colorwheel.frag delete mode 100644 src/nodes/shaders/colorwheel.vert diff --git a/examples/gallery/CMakeLists.txt b/examples/gallery/CMakeLists.txt index b887b10f0..e32dde118 100644 --- a/examples/gallery/CMakeLists.txt +++ b/examples/gallery/CMakeLists.txt @@ -11,7 +11,25 @@ set(SOURCES selector/SelectorPage.h selector/SelectorPage.cpp dialog/DialogPage.h dialog/DialogPage.cpp listbox/ListBoxPage.h listbox/ListBoxPage.cpp - colorpickers/ColorPickerPage.h colorpickers/ColorPickerPage.cpp + + colorpickers/ColorPickerPage.cpp + colorpickers/ColorPickerPage.h + colorpickers/QskColorRectangle.cpp + colorpickers/QskColorRectangle.h + colorpickers/QskColorRectangleSkinlet.cpp + colorpickers/QskColorRectangleSkinlet.h + colorpickers/QskColorTriangle.cpp + colorpickers/QskColorTriangle.h + colorpickers/QskColorTriangleNode.cpp + colorpickers/QskColorTriangleNode.h + colorpickers/QskColorTriangleSkinlet.cpp + colorpickers/QskColorTriangleSkinlet.h + colorpickers/QskColorWheel.cpp + colorpickers/QskColorWheel.h + colorpickers/QskColorWheelSkinlet.cpp + colorpickers/QskColorWheelSkinlet.h + + Page.h Page.cpp main.cpp ) diff --git a/examples/gallery/colorpickers/ColorPickerPage.cpp b/examples/gallery/colorpickers/ColorPickerPage.cpp index 263db5483..d1477d51f 100644 --- a/examples/gallery/colorpickers/ColorPickerPage.cpp +++ b/examples/gallery/colorpickers/ColorPickerPage.cpp @@ -1,373 +1,50 @@ #include "ColorPickerPage.h" -#include "QskAspect.h" -#include "QskBoxNode.h" -#include "QskControl.h" -#include "QskGradient.h" -#include "QskGradientStop.h" -#include "QskGridBox.h" -#include "QskLinearBox.h" -#include "QskSkin.h" -#include "QskSkinlet.h" -#include "QskSkinnable.h" -#include "QskSlider.h" -#include "QskTextLabel.h" -#include "colorpickers/ColorPickerPage.h" -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace -{ - double qskMapValueRange( - double value, double srcMin, double srcMax, double dstMin, double dstMax ) - { - value = std::min( std::max( value, srcMin ), srcMax ); - double percentage = ( value - srcMin ) / ( srcMax - srcMin ); - double mappedValue = dstMin + percentage * ( dstMax - dstMin ); - return mappedValue; - } - - QVector3D cartesian2barycentric( - const QPointF& p, const QPointF& t1, const QPointF& t2, const QPointF& t3 ) - { - const auto x = p.x(); - const auto y = p.y(); - - const auto x1 = t1.x(); - const auto y1 = t1.y(); - const auto x2 = t2.x(); - const auto y2 = t2.y(); - const auto x3 = t3.x(); - const auto y3 = t3.y(); - - const auto y2y3 = y2 - y3, x3x2 = x3 - x2, x1x3 = x1 - x3, y1y3 = y1 - y3, y3y1 = y3 - y1, - xx3 = x - x3, yy3 = y - y3; - - const auto d = y2y3 * x1x3 + x3x2 * y1y3, lambda1 = ( y2y3 * xx3 + x3x2 * yy3 ) / d, - lambda2 = ( y3y1 * xx3 + x1x3 * yy3 ) / d; - - return { ( float ) lambda1, ( float ) lambda2, ( float ) ( 1.0 - lambda1 - lambda2 ) }; - } - - QColor qskColorInTriangle( const QPointF& p, const QPointF& t1, const QPointF& t2, - const QPointF& t3, const QColor& c1, const QColor& c2, const QColor& c3 ) - { - const auto t = cartesian2barycentric( p, t1, t2, t3 ); - const auto r = - std::floor( ( c1.redF() * t[ 0 ] + c2.redF() * t[ 1 ] + c3.redF() * t[ 2 ] ) * 255.0 ); - const auto g = std::floor( - ( c1.greenF() * t[ 0 ] + c2.greenF() * t[ 1 ] + c3.greenF() * t[ 2 ] ) * 255.0 ); - const auto b = std::floor( - ( c1.blueF() * t[ 0 ] + c2.blueF() * t[ 1 ] + c3.blueF() * t[ 2 ] ) * 255.0 ); - - if ( t[ 0 ] < 0.0 || t[ 1 ] < 0.0 || t[ 2 ] < 0.0 ) - { - return {}; - } - - return { int( r ), int( g ), int( b ) }; - } - - QColor qskColorAt( const QskGradient& gradient, qreal ratio ) - { - return gradient.extracted( ratio, ratio ).startColor(); - } - - QColor qskColorForAngle( const QskGradient& gradient, qreal angle ) - { - angle = ( angle < 0.0 ? angle + 2 * M_PI : angle ); - const auto ratio = qAbs( angle / ( 2 * M_PI ) ); - return qskColorAt( gradient, ratio ); - } -} - -struct Color -{ - float r, g, b, a; - QRectF rect; - QPointF mouse; -}; - -class MinimalShader : public QSGSimpleMaterialShader< Color > -{ - QSG_DECLARE_SIMPLE_SHADER( MinimalShader, Color ) - public: - MinimalShader() - { - setShaderSourceFile( QOpenGLShader::Fragment, ":/qskinny/shaders/colorwheel.frag" ); - setShaderSourceFile( QOpenGLShader::Vertex, ":/qskinny/shaders/colorwheel.vert" ); - } - - QList< QByteArray > attributes() const - { - return QList< QByteArray >() << "vertex"; - } - - void updateState( const Color* color, const Color* ) - { - program()->setUniformValue( "color", color->r, color->g, color->b, color->a ); - program()->setUniformValue( - "rect", color->rect.x(), color->rect.y(), color->rect.width(), color->rect.height() ); - program()->setUniformValue( - "mouse", color->mouse.x(), color->mouse.y(), color->mouse.x(), color->mouse.y() ); - } -}; - -class QskColorTriangleNode : public QSGGeometryNode -{ - static constexpr auto vertexCount = 3; - - public: - QskColorTriangleNode() - { - auto* const geometry = - new QSGGeometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), vertexCount ); - geometry->setDrawingMode( QSGGeometry::DrawTriangles ); - setGeometry( geometry ); - - auto* const material = new QSGVertexColorMaterial; - setMaterial( material ); - } - - void update( - const QRectF& rect, const QColor& c1, const QColor& c2, const QColor& c3, const qreal rad ) - { - const QColor colors[]{ c1, c2, c3 }; - - const auto center = rect.center(); - const auto radius = qMin( rect.width(), rect.height() ) / 2; - - const auto step = 2 * M_PI / 3; - - auto* const vertices = geometry()->vertexDataAsColoredPoint2D(); - for ( int i = 0; i < vertexCount; ++i ) - { - const auto x = qFastCos( rad + i * step ) * radius + center.x(); - const auto y = qFastSin( rad + i * step ) * radius + center.y(); - const uchar c[ 4 ]{ ( uchar ) colors[ i ].red(), ( uchar ) colors[ i ].green(), - ( uchar ) colors[ i ].blue(), 255 }; - vertices[ i ].set( x, y, c[ 0 ], c[ 1 ], c[ 2 ], c[ 3 ] ); - } - geometry()->markVertexDataDirty(); - markDirty( QSGNode::DirtyGeometry ); - markDirty( QSGNode::DirtyMaterial ); - } -}; - -class QskColorWheelNode : public QSGGeometryNode -{ - using Material = QSGSimpleMaterial< Color >; - - public: - QskColorWheelNode() - { - auto* const geometry = - new QSGGeometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 ); - geometry->setDrawingMode( QSGGeometry::DrawTriangleStrip ); - setGeometry( geometry ); - setMaterial( MinimalShader::createMaterial() ); - material()->setFlag( QSGMaterial::Blending ); - } - - void update( const QRectF& rect, const QPointF& mouse, const QRectF& rectOnScreen, - const QSizeF& resolution ) - { - auto* const material = static_cast< Material* >( this->material() ); - material->state()->r = 1.0; - material->state()->g = 0.0; - material->state()->b = 1.0; - material->state()->a = 1.0; - material->state()->rect = { rectOnScreen.x(), resolution.height() - rectOnScreen.bottom(), - rectOnScreen.width(), rectOnScreen.height() }; - material->state()->mouse = { mouse.x(), resolution.height() - mouse.y() }; - - QSGGeometry::updateTexturedRectGeometry( geometry(), rect, { -0.5, -0.5, 1.0, 1.0 } ); - geometry()->markVertexDataDirty(); - markDirty( QSGNode::DirtyGeometry ); // ? - markDirty( QSGNode::DirtyMaterial ); // ? - } - - private: -}; - -QSK_SUBCONTROL( QskColorWheel, Arc ) -QSK_SUBCONTROL( QskColorWheel, Handle ) - -QskColorWheel::QskColorWheel( QQuickItem* parent ) - : QskControl( parent ) -{ - setAcceptedMouseButtons( Qt::LeftButton ); - - static const QskGradientStops stops = { - { 0.0000, QColor::fromRgb( 255, 0, 0 ) }, - { 0.1667, QColor::fromRgb( 255, 255, 0 ) }, - { 0.3333, QColor::fromRgb( 0, 255, 0 ) }, - { 0.5000, QColor::fromRgb( 0, 255, 255 ) }, - { 0.6667, QColor::fromRgb( 0, 0, 255 ) }, - { 0.8333, QColor::fromRgb( 255, 0, 255 ) }, - { 1.0000, QColor::fromRgb( 255, 0, 0 ) }, - }; - - QskGradient gradient( stops ); - setGradientHint( Arc, gradient ); - setArcMetricsHint( Arc, { 0, 360, 16 } ); - - setColor( Handle, Qt::white ); - setBoxBorderColorsHint( Handle, { Qt::white } ); - setBoxBorderMetricsHint( Handle, { 2 } ); - setBoxShapeHint( Handle, { 100.0, 100.0, 100.0, 100.0, Qt::RelativeSize } ); -} - -QColor QskColorWheel::selectedColor() const -{ - return m_selectedColor; -} - -void QskColorWheel::setSelectedColor( const QColor& color ) -{ - if ( m_selectedColor != color ) - { - m_selectedColor = color; - setColor( Handle, color ); - Q_EMIT selectedColorChanged( m_selectedColor ); - } -} - -void QskColorWheel::mousePressEvent( QMouseEvent* event ) -{ -} - -void QskColorWheel::mouseReleaseEvent( QMouseEvent* event ) -{ -} - -void QskColorWheel::mouseMoveEvent( QMouseEvent* event ) -{ - const auto r = contentsRect(); - const auto ux = qskMapValueRange( event->pos().x(), r.left(), r.right(), -1, +1 ); - const auto uy = qskMapValueRange( event->pos().y(), r.top(), r.bottom(), +1, -1 ); - - if ( QVector2D( ux, uy ).length() > 1.0 ) // TODO && Pressed - { - event->ignore(); - return; - } - - const auto rad = qAtan2( uy, ux ); - const auto color = qskColorForAngle( gradientHint( Arc ), rad ); - setPositionHint( Handle, -rad ); - setSelectedColor( color ); -} - -QskColorWheelSkinlet::QskColorWheelSkinlet( QskSkin* skin ) - : QskSkinlet( skin ) -{ - setNodeRoles( { ArcRole, HandleRole } ); -} - -QRectF QskColorWheelSkinlet::subControlRect( const QskSkinnable* skinnable, - const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const -{ - using Q = QskColorWheel; - const auto* const q = static_cast< const Q* >( skinnable ); - - if ( subcontrol == Q::Arc ) - { - const auto size = qMin( contentsRect.width(), contentsRect.height() ); - return { contentsRect.center().x() - size / 2, contentsRect.center().y() - size / 2, size, - size }; - } - - if ( subcontrol == Q::Handle ) - { - const auto& r = contentsRect; - const auto thickness = skinnable->arcMetricsHint( Q::Arc ).thickness() / 2; - const auto angle = q->positionHint( Q::Handle ); - const auto radius = qMin( r.width(), r.height() ) / 2 - thickness; - const auto x = contentsRect.center().x() + radius * qFastCos( angle ); - const auto y = contentsRect.center().y() + radius * qFastSin( angle ); - return { QPointF{ x - thickness, y - thickness }, QPointF{ x + thickness, y + thickness } }; - } - - return QskSkinlet::subControlRect( skinnable, contentsRect, subcontrol ); -} - -QSGNode* QskColorWheelSkinlet::updateSubNode( - const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const -{ - using Q = QskColorWheel; - const auto* const q = static_cast< const Q* >( skinnable ); - if ( nodeRole == ArcRole ) - { - return updateArcNode( skinnable, node, Q::Arc ); - } +#include "QskColorRectangle.h" +#include "QskColorRectangleSkinlet.h" +#include "QskColorTriangle.h" +#include "QskColorTriangleSkinlet.h" +#include "QskColorWheel.h" +#include "QskColorWheelSkinlet.h" - if ( nodeRole == HandleRole ) - { - return updateBoxNode( skinnable, node, Q::Handle ); - } +#include +#include +#include +#include - return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); -} +#include -QSizeF QskColorWheelSkinlet::sizeHint( - const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const +template< typename Skinnable, typename Skinlet, class... Types > +Skinnable* create( Types&&... args, QQuickItem* parent = nullptr ) { - if ( which != Qt::PreferredSize ) - { - return {}; - } - - const auto* const q = static_cast< const QskColorTriangle* >( skinnable ); - const auto rect = q->contentsRect(); - const auto size = qMin( rect.width(), rect.height() ); - return { size, size }; + auto* const control = new Skinnable( std::forward< Types >( args )..., parent ); + auto* const skinlet = new Skinlet; + control->setSkinlet( skinlet ); + skinlet->setOwnedBySkinnable( true ); + return control; } ColorPickerPage::ColorPickerPage( QQuickItem* parent ) : Page( parent ) { auto* const layout = new QskGridBox( this ); - auto* const colorWheel = new QskColorWheel( layout ); - { - auto* const skinlet = new QskColorWheelSkinlet; - colorWheel->setSkinlet( skinlet ); - skinlet->setOwnedBySkinnable( true ); - } - auto* const colorTriangle = new QskColorTriangle( layout ); - { - auto* const skinlet = new QskColorTriangleSkinlet; - colorTriangle->setSkinlet( skinlet ); - skinlet->setOwnedBySkinnable( true ); - } + + auto* const colorWheel = create< QskColorWheel, QskColorWheelSkinlet >( layout ); + auto* const colorWheelSlider = new QskSlider( layout ); + + auto* const colorTriangle = create< QskColorTriangle, QskColorTriangleSkinlet >( layout ); + auto* const colorTriangleSlider = new QskSlider( layout ); + + auto* const colorRectangle = create< QskColorRectangle, QskColorRectangleSkinlet >( layout ); layout->addItem( new QskTextLabel( "QskColorWheel" ), 0, 0 ); layout->addItem( colorWheel, 1, 0 ); + layout->addItem( colorWheelSlider, 2, 0 ); layout->addItem( new QskTextLabel( "QskColorTriangle" ), 0, 1 ); layout->addItem( colorTriangle, 1, 1 ); + layout->addItem( colorTriangleSlider, 2, 1 ); + layout->addItem( new QskTextLabel( "QskColorRectangle" ), 0, 2 ); + layout->addItem( colorRectangle, 1, 2 ); for ( int col = 0; col < layout->columnCount(); col++ ) { @@ -377,172 +54,22 @@ ColorPickerPage::ColorPickerPage( QQuickItem* parent ) connect( colorWheel, &QskColorWheel::selectedColorChanged, colorTriangle, [ colorTriangle ]( const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); -} - -QSK_SUBCONTROL( QskColorTriangle, Triangle ) -QSK_SUBCONTROL( QskColorTriangle, Handle ) - -QskColorTriangle::QskColorTriangle( QQuickItem* parent ) - : QskControl( parent ) -{ - setAcceptedMouseButtons( Qt::LeftButton ); - - setStrutSizeHint( Handle, strutSizeHint( QskSlider::Handle ) ); - setColor( Handle, Qt::white ); - setBoxBorderColorsHint( Handle, { Qt::white } ); - setBoxBorderMetricsHint( Handle, { 2 } ); - setBoxShapeHint( Handle, { 100.0, 100.0, 100.0, 100.0, Qt::RelativeSize } ); - - setPositionHint( Triangle, qDegreesToRadians( 45.0 ) ); -} - -QColor QskColorTriangle::selectedColor() const -{ - return m_selectedColor; -} - -void QskColorTriangle::setSelectedColor( const QColor& color ) -{ - if ( m_selectedColor != color ) - { - m_selectedColor = color; - setColor( Handle, color ); - Q_EMIT selectedColorChanged( m_selectedColor ); - } -} - -void QskColorTriangle::setColors( const QColor& c1, const QColor& c2, const QColor& c3 ) -{ - m_c1 = c1; - m_c2 = c2; - m_c3 = c3; - update(); -} - -QColor QskColorTriangle::color1() const -{ - return m_c1; -} - -QColor QskColorTriangle::color2() const -{ - return m_c2; -} - -QColor QskColorTriangle::color3() const -{ - return m_c3; -} - -void QskColorTriangle::mousePressEvent( QMouseEvent* event ) -{ -} - -void QskColorTriangle::mouseReleaseEvent( QMouseEvent* event ) -{ -} - -void QskColorTriangle::mouseMoveEvent( QMouseEvent* event ) -{ - const auto rect = subControlRect( Triangle ); - if ( rect.contains( event->pos() ) ) - { - static constexpr auto step = 2 * M_PI / 3; - const auto angle = 0.0; - const auto radius = qMin( rect.width(), rect.height() ) / 2; - QTransform transformation; - transformation.rotate( angle, Qt::ZAxis ); - - auto t1 = QPointF{ qFastCos( 0 * step ), qFastSin( 0 * step ) }; - auto t2 = QPointF{ qFastCos( 1 * step ), qFastSin( 1 * step ) }; - auto t3 = QPointF{ qFastCos( 2 * step ), qFastSin( 2 * step ) }; - - t1 *= radius; - t2 *= radius; - t3 *= radius; - - t1 += rect.center(); - t2 += rect.center(); - t3 += rect.center(); - - const auto color = qskColorInTriangle( event->pos(), t1, t2, t3, m_c1, m_c2, m_c3 ); - if ( color.isValid() ) - { - setSelectedColor( color ); - setSkinHint( Handle, event->pos() ); - } - } - event->ignore(); -} - -QskColorTriangleSkinlet::QskColorTriangleSkinlet( QskSkin* skin ) - : QskSkinlet( skin ) -{ - setNodeRoles( { TriangleRole, HandleRole } ); -} - -QRectF QskColorTriangleSkinlet::subControlRect( const QskSkinnable* skinnable, - const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const -{ - using Q = QskColorTriangle; - const auto* const q = static_cast< const Q* >( skinnable ); - - const auto& r = contentsRect; - - if ( subcontrol == Q::Triangle ) - { - const auto size = ( qMin( r.width(), contentsRect.height() ) ) / 2; - auto rect = QRectF( 0, 0, size, size ); - - return { QPointF{ r.center().x() - size, r.center().y() - size }, - QPointF{ r.center().x() + size, r.center().y() + size } }; - } - - if ( subcontrol == Q::Handle ) - { - const auto rect = subControlRect( skinnable, contentsRect, Q::Triangle ); - const auto size = - qMin( q->strutSizeHint( Q::Handle ).width(), q->strutSizeHint( Q::Handle ).height() ); - const auto position = q->effectiveSkinHint( Q::Handle ).toPointF(); - return { QPointF{ position.x() - size, position.y() - size }, - QPointF{ position.x() + size, position.y() + size } }; - } - return QskSkinlet::subControlRect( skinnable, contentsRect, subcontrol ); -} - -QSGNode* QskColorTriangleSkinlet::updateSubNode( - const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const -{ - using Q = QskColorTriangle; - const auto* const q = static_cast< const Q* >( skinnable ); - - if ( nodeRole == TriangleRole ) - { - const auto subcontrol = Q::Triangle; - const auto rect = subControlRect( skinnable, q->contentsRect(), subcontrol ); - auto* const triangleNode = QskSGNode::ensureNode< QskColorTriangleNode >( node ); - triangleNode->update( - rect, q->color1(), q->color2(), q->color3(), q->positionHint( Q::Handle ) ); - return triangleNode; - } - if ( nodeRole == HandleRole ) - { - return updateBoxNode( skinnable, node, Q::Handle ); - } - - return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); -} - -QSizeF QskColorTriangleSkinlet::sizeHint( - const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const -{ - if ( which != Qt::PreferredSize ) - { - return {}; - } - const auto* const q = static_cast< const QskColorTriangle* >( skinnable ); - const auto rect = q->contentsRect(); - const auto size = qMin( rect.width(), rect.height() ); - return { size, size }; + connect( colorWheelSlider, &QskSlider::valueChanged, this, [ q = colorWheel ]( qreal v ) { + auto metrics = q->arcMetricsHint( QskColorWheel::Arc ); + metrics.setThickness( v * qMin( q->width(), q->height() ) / 2 ); + q->setArcMetricsHint( QskColorWheel::Arc, metrics ); + } ); + + connect( colorTriangleSlider, &QskSlider::valueChanged, this, [ = ]( qreal v ) { + colorTriangle->setPositionHint( QskColorTriangle::Triangle, 2 * M_PI * v ); + } ); + + connect( colorWheel, &QskColorWheel::selectedColorChanged, colorRectangle, + [ = ]( const QColor& color ) { + colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); + colorRectangle->setCornerColor( Qt::TopRightCorner, color ); + colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); + colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); + } ); } \ No newline at end of file diff --git a/examples/gallery/colorpickers/ColorPickerPage.h b/examples/gallery/colorpickers/ColorPickerPage.h index cd521400d..848b8df5e 100644 --- a/examples/gallery/colorpickers/ColorPickerPage.h +++ b/examples/gallery/colorpickers/ColorPickerPage.h @@ -6,117 +6,9 @@ #pragma once #include "Page.h" -#include "QskAspect.h" -#include "QskSkin.h" - -#include -#include -#include -#include -#include class ColorPickerPage : public Page { public: explicit ColorPickerPage( QQuickItem* = nullptr ); -}; - -class QskColorWheel : public QskControl -{ - Q_OBJECT - Q_PROPERTY( - QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged ) - public: - QSK_SUBCONTROLS( Arc, Handle ) - explicit QskColorWheel( QQuickItem* parent = nullptr ); - - QColor selectedColor() const; - void setSelectedColor( const QColor& color ); - - protected: - void mousePressEvent( QMouseEvent* event ) override; - void mouseReleaseEvent( QMouseEvent* event ) override; - void mouseMoveEvent( QMouseEvent* event ) override; - - Q_SIGNALS: - void selectedColorChanged( const QColor& color ); - - private: - QColor m_selectedColor; -}; - -class QskColorWheelSkinlet : public QskSkinlet -{ - public: - enum NodeRoles - { - ArcRole, - HandleRole - }; - - QskColorWheelSkinlet( QskSkin* skin = nullptr ); - - QRectF subControlRect( - const QskSkinnable* skinnable, const QRectF&, QskAspect::Subcontrol ) const override; - - QSGNode* updateSubNode( - const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* ) const override; - - QSizeF sizeHint( - const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const override; -}; - -class QskColorTriangle : public QskControl -{ - Q_OBJECT - Q_PROPERTY( - QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged ) - - public: - QSK_SUBCONTROLS( Triangle, Handle ) - explicit QskColorTriangle( QQuickItem* parent = nullptr ); - - QColor selectedColor() const; - - void setColors(const QColor& c1, const QColor& c2, const QColor& c3); - QColor color1()const; - QColor color2()const; - QColor color3()const; - - Q_SIGNALS: - void selectedColorChanged( const QColor& color ); - - protected: - void mousePressEvent( QMouseEvent* event ) override; - void mouseReleaseEvent( QMouseEvent* event ) override; - void mouseMoveEvent( QMouseEvent* event ) override; - - private: - void setSelectedColor( const QColor& color ); - - QColor m_selectedColor; - QColor m_c1; - QColor m_c2 = Qt::black; - QColor m_c3 = Qt::white; -}; - -class QskColorTriangleSkinlet : public QskSkinlet -{ - public: - enum NodeRoles - { - TriangleRole, - HandleRole - }; - - QskColorTriangleSkinlet( QskSkin* skin = nullptr ); - - QRectF subControlRect( - const QskSkinnable* skinnable, const QRectF&, QskAspect::Subcontrol ) const override; - - QSGNode* updateSubNode( - const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* ) const override; - - QSizeF sizeHint( - const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const override; }; \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorRectangle.cpp b/examples/gallery/colorpickers/QskColorRectangle.cpp new file mode 100644 index 000000000..b079fd6f4 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorRectangle.cpp @@ -0,0 +1,128 @@ +#include "QskColorRectangle.h" + +#include +#include +#include + +#include + +QSK_SUBCONTROL( QskColorRectangle, Panel ) +QSK_SUBCONTROL( QskColorRectangle, Handle ) + +namespace +{ + double qskMapValueRange( + double value, double srcMin, double srcMax, double dstMin, double dstMax ) + { + value = std::min( std::max( value, srcMin ), srcMax ); + double percentage = ( value - srcMin ) / ( srcMax - srcMin ); + double mappedValue = dstMin + percentage * ( dstMax - dstMin ); + return mappedValue; + } + + QColor qskInterpolatedColor( const QColor& topLeft, const QColor& topRight, + const QColor& bottomLeft, const QColor& bottomRight, const QPointF& position ) + { + const auto x = position.x(); + const auto y = position.y(); + + const QColor topInterpolated( int( ( 1.0 - x ) * topLeft.red() + x * topRight.red() ), + int( ( 1.0 - x ) * topLeft.green() + x * topRight.green() ), + int( ( 1.0 - x ) * topLeft.blue() + x * topRight.blue() ) ); + + const QColor bottomInterpolated( + int( ( 1.0 - x ) * bottomLeft.red() + x * bottomRight.red() ), + int( ( 1.0 - x ) * bottomLeft.green() + x * bottomRight.green() ), + int( ( 1.0 - x ) * bottomLeft.blue() + x * bottomRight.blue() ) ); + + return { int( ( 1.0 - y ) * topInterpolated.red() + y * bottomInterpolated.red() ), + int( ( 1.0 - y ) * topInterpolated.green() + y * bottomInterpolated.green() ), + int( ( 1.0 - y ) * topInterpolated.blue() + y * bottomInterpolated.blue() ) }; + } +} + +QskColorRectangle::QskColorRectangle( QQuickItem* parent ) + : QskControl( parent ) +{ + setAcceptedMouseButtons( Qt::LeftButton ); + + setBoxBorderColorsHint( Handle, Qt::white ); + setBoxBorderMetricsHint( Handle, 2 ); + setBoxShapeHint( Handle, { 100, 100, 100, 100, Qt::AbsoluteSize } ); + setStrutSizeHint( Handle, { 20, 20 } ); + setSkinHint( Handle, QPointF{ 50, 50 } ); +} + +QColor QskColorRectangle::selectedColor() const +{ + return m_selectedColor; +} + +void QskColorRectangle::setCornerColor( const Qt::Corner corner, const QColor& color ) +{ + switch ( corner ) + { + case Qt::TopLeftCorner: + case Qt::TopRightCorner: + case Qt::BottomLeftCorner: + case Qt::BottomRightCorner: + m_cornerColor[ corner ] = color; + update(); + break; + default: + break; + } +} + +QColor QskColorRectangle::cornerColor( const Qt::Corner corner ) const +{ + switch ( corner ) + { + case Qt::TopLeftCorner: + case Qt::TopRightCorner: + case Qt::BottomLeftCorner: + case Qt::BottomRightCorner: + return m_cornerColor[ corner ]; + default: + return {}; + } +} + +void QskColorRectangle::mousePressEvent( QMouseEvent* event ) +{ +} + +void QskColorRectangle::mouseReleaseEvent( QMouseEvent* event ) +{ +} + +void QskColorRectangle::mouseMoveEvent( QMouseEvent* event ) +{ + const auto r = subControlRect( Panel ); + if ( r.contains( event->pos() ) ) + { + const auto ux = qskMapValueRange( event->pos().x(), r.left(), r.right(), 0, 1 ); + const auto uy = qskMapValueRange( event->pos().y(), r.top(), r.bottom(), 0, 1 ); + const auto uv = QPointF{ ux, uy }; + + const auto color = qskInterpolatedColor( m_cornerColor[ Qt::TopLeftCorner ], + m_cornerColor[ Qt::TopRightCorner ], m_cornerColor[ Qt::BottomLeftCorner ], + m_cornerColor[ Qt::BottomRightCorner ], uv ); + + setColor( Handle, color ); + setSkinHint( Handle, uv ); + setSelectedColor( color ); + } +} + +void QskColorRectangle::setSelectedColor( const QColor& color ) +{ + if ( m_selectedColor != color ) + { + m_selectedColor = color; + update(); + Q_EMIT selectedColorChanged( m_selectedColor ); + } +} + +#include "moc_QskColorRectangle.cpp" \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorRectangle.h b/examples/gallery/colorpickers/QskColorRectangle.h new file mode 100644 index 000000000..551299b53 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorRectangle.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +class QskColorRectangle : public QskControl +{ + Q_OBJECT + Q_PROPERTY( QColor selectedColor READ selectedColor NOTIFY selectedColorChanged ) + + public: + QSK_SUBCONTROLS( Panel, Handle ) + explicit QskColorRectangle( QQuickItem* parent = nullptr ); + + QColor selectedColor() const; + + void setCornerColor( Qt::Corner corner, const QColor& color ); + QColor cornerColor( Qt::Corner corner ) const; + + protected: + void mousePressEvent( QMouseEvent* event ) override; + void mouseReleaseEvent( QMouseEvent* event ) override; + void mouseMoveEvent( QMouseEvent* event ) override; + + Q_SIGNALS: + void selectedColorChanged( const QColor& color ); + + private: + void setSelectedColor( const QColor& color ); + + QColor m_selectedColor; + QColor m_cornerColor[ 4 ]; +}; \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorRectangleSkinlet.cpp b/examples/gallery/colorpickers/QskColorRectangleSkinlet.cpp new file mode 100644 index 000000000..88c4b2632 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorRectangleSkinlet.cpp @@ -0,0 +1,102 @@ +#include "QskColorRectangleSkinlet.h" +#include "QskColorRectangle.h" + +#include +#include +#include + +#include +#include + +using Q = QskColorRectangle; + +namespace +{ + class QskVertexColoredRectangle : public QSGGeometryNode + { + public: + QskVertexColoredRectangle( const QColor& topLeft = {}, const QColor& topRight = {}, + const QColor& bottomLeft = {}, const QColor& bottomRight = {} ) + { + auto* const geometry = + new QSGGeometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 4 ); + geometry->setDrawingMode( QSGGeometry::DrawTriangleStrip ); + setGeometry( geometry ); + + auto* const material = new QSGVertexColorMaterial; + setMaterial( material ); + + update( {}, topLeft, topRight, bottomLeft, bottomRight ); + } + + void update( const QRectF& rect, const QColor& topLeft = {}, const QColor& topRight = {}, + const QColor& bottomLeft = {}, const QColor& bottomRight = {} ) + { + auto* const vertices = geometry()->vertexDataAsColoredPoint2D(); + vertices[ 0 ].set( rect.left(), rect.top(), topLeft.red(), topLeft.green(), + topLeft.blue(), topLeft.alpha() ); + vertices[ 1 ].set( rect.right(), rect.top(), topRight.red(), topRight.green(), + topRight.blue(), topRight.alpha() ); + vertices[ 2 ].set( rect.left(), rect.bottom(), bottomLeft.red(), bottomLeft.green(), + bottomLeft.blue(), bottomLeft.alpha() ); + vertices[ 3 ].set( rect.right(), rect.bottom(), bottomRight.red(), bottomRight.green(), + bottomRight.blue(), bottomRight.alpha() ); + + markDirty( QSGNode::DirtyGeometry ); + markDirty( QSGNode::DirtyMaterial ); + } + }; +} + +QskColorRectangleSkinlet::QskColorRectangleSkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { PanelRole, HandleRole } ); +} + +QRectF QskColorRectangleSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const +{ + const auto* const q = static_cast< const Q* >( skinnable ); + if ( subcontrol == Q::Panel ) + { + return contentsRect; + } + if ( subcontrol == Q::Handle ) + { + const auto size = q->strutSizeHint( Q::Handle ); + const auto w = qMax( 0.0, size.width() ) / 2; + const auto h = qMax( 0.0, size.height() ) / 2; + + auto p = q->effectiveSkinHint( Q::Handle ).toPointF(); + p.rx() *= contentsRect.width(); + p.ry() *= contentsRect.height(); + p.rx() += contentsRect.left(); + p.ry() += contentsRect.top(); + + return { QPointF{ p.x() - w, p.y() - h }, QPointF{ p.x() + w, p.y() + h } }; + } + return QskSkinlet::subControlRect( skinnable, contentsRect, subcontrol ); +} + +QSGNode* QskColorRectangleSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto* const q = static_cast< const Q* >( skinnable ); + + if ( nodeRole == PanelRole ) + { + auto* const rectangleNode = QskSGNode::ensureNode< QskVertexColoredRectangle >( node ); + rectangleNode->update( q->subControlRect( Q::Panel ), q->cornerColor( Qt::TopLeftCorner ), + q->cornerColor( Qt::TopRightCorner ), q->cornerColor( Qt::BottomLeftCorner ), + q->cornerColor( Qt::BottomRightCorner ) ); + return rectangleNode; + } + + if ( nodeRole == HandleRole ) + { + return updateBoxNode( skinnable, node, Q::Handle ); + } + + return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); +} diff --git a/examples/gallery/colorpickers/QskColorRectangleSkinlet.h b/examples/gallery/colorpickers/QskColorRectangleSkinlet.h new file mode 100644 index 000000000..75ddee316 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorRectangleSkinlet.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +class QskColorRectangleSkinlet : public QskSkinlet +{ + Q_GADGET + public: + enum NodeRoles + { + PanelRole, + HandleRole, + RoleCount + }; + Q_INVOKABLE QskColorRectangleSkinlet( QskSkin* skin = nullptr ); + + private: + QRectF subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, + QskAspect::Subcontrol subcontrol ) const; + + QSGNode* updateSubNode( const QskSkinnable* skinnable, + quint8 nodeRole, QSGNode* node) const; +}; \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorTriangle.cpp b/examples/gallery/colorpickers/QskColorTriangle.cpp new file mode 100644 index 000000000..e27f8ba58 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorTriangle.cpp @@ -0,0 +1,160 @@ +#include "QskColorTriangle.h" + +#include +#include +#include +#include + +#include +#include + +namespace +{ + QVector3D cartesian2barycentric( + const QPointF& p, const QPointF& t1, const QPointF& t2, const QPointF& t3 ) + { + const auto x = p.x(); + const auto y = p.y(); + + const auto x1 = t1.x(); + const auto y1 = t1.y(); + const auto x2 = t2.x(); + const auto y2 = t2.y(); + const auto x3 = t3.x(); + const auto y3 = t3.y(); + + const auto y2y3 = y2 - y3, x3x2 = x3 - x2, x1x3 = x1 - x3, y1y3 = y1 - y3, y3y1 = y3 - y1, + xx3 = x - x3, yy3 = y - y3; + + const auto d = y2y3 * x1x3 + x3x2 * y1y3, lambda1 = ( y2y3 * xx3 + x3x2 * yy3 ) / d, + lambda2 = ( y3y1 * xx3 + x1x3 * yy3 ) / d; + + return { ( float ) lambda1, ( float ) lambda2, ( float ) ( 1.0 - lambda1 - lambda2 ) }; + } + + QColor qskColorInTriangle( const QPointF& p, const QPointF& t1, const QPointF& t2, + const QPointF& t3, const QColor& c1, const QColor& c2, const QColor& c3 ) + { + const auto t = cartesian2barycentric( p, t1, t2, t3 ); + const auto r = + std::floor( ( c1.redF() * t[ 0 ] + c2.redF() * t[ 1 ] + c3.redF() * t[ 2 ] ) * 255.0 ); + const auto g = std::floor( + ( c1.greenF() * t[ 0 ] + c2.greenF() * t[ 1 ] + c3.greenF() * t[ 2 ] ) * 255.0 ); + const auto b = std::floor( + ( c1.blueF() * t[ 0 ] + c2.blueF() * t[ 1 ] + c3.blueF() * t[ 2 ] ) * 255.0 ); + + if ( t[ 0 ] < 0.0 || t[ 1 ] < 0.0 || t[ 2 ] < 0.0 ) + { + return {}; + } + + return { int( r ), int( g ), int( b ) }; + } +} + +QSK_SUBCONTROL( QskColorTriangle, Triangle ) +QSK_SUBCONTROL( QskColorTriangle, Handle ) + +QskColorTriangle::QskColorTriangle( QQuickItem* parent ) + : QskControl( parent ) +{ + setAcceptedMouseButtons( Qt::LeftButton ); + + setStrutSizeHint( Handle, { 20, 20 } ); + setColor( Handle, Qt::white ); + setBoxBorderColorsHint( Handle, { Qt::white } ); + setBoxBorderMetricsHint( Handle, { 2 } ); + setBoxShapeHint( Handle, { 100.0, 100.0, 100.0, 100.0, Qt::RelativeSize } ); + setSkinHint( Handle, QPointF{} ); +} + +QColor QskColorTriangle::selectedColor() const +{ + return m_selectedColor; +} + +void QskColorTriangle::setSelectedColor( const QColor& color ) +{ + if ( m_selectedColor != color ) + { + m_selectedColor = color; + setColor( Handle, color ); + Q_EMIT selectedColorChanged( m_selectedColor ); + } +} + +void QskColorTriangle::setColors( const QColor& c1, const QColor& c2, const QColor& c3 ) +{ + m_c1 = c1; + m_c2 = c2; + m_c3 = c3; + update(); +} + +QColor QskColorTriangle::color1() const +{ + return m_c1; +} + +QColor QskColorTriangle::color2() const +{ + return m_c2; +} + +QColor QskColorTriangle::color3() const +{ + return m_c3; +} + +void QskColorTriangle::mousePressEvent( QMouseEvent* event ) +{ +} + +void QskColorTriangle::mouseReleaseEvent( QMouseEvent* event ) +{ +} + +void QskColorTriangle::mouseMoveEvent( QMouseEvent* event ) +{ + const auto rect = subControlRect( Triangle ); + if ( rect.contains( event->pos() ) ) + { + static constexpr auto step = 2 * M_PI / 3; + const auto angle = positionHint( Triangle ); + const auto radius = qMin( rect.width(), rect.height() ) / 2; + QTransform transformation; + transformation.rotate( qRadiansToDegrees( angle ), Qt::ZAxis ); + + // TODO affine + + auto t1 = QPointF{ qFastCos( 0 * step ), qFastSin( 0 * step ) }; + auto t2 = QPointF{ qFastCos( 1 * step ), qFastSin( 1 * step ) }; + auto t3 = QPointF{ qFastCos( 2 * step ), qFastSin( 2 * step ) }; + + t1 = transformation.map( t1 ); + t2 = transformation.map( t2 ); + t3 = transformation.map( t3 ); + + t1 *= radius; + t2 *= radius; + t3 *= radius; + + t1 += rect.center(); + t2 += rect.center(); + t3 += rect.center(); + + const auto color = qskColorInTriangle( event->pos(), t1, t2, t3, m_c1, m_c2, m_c3 ); + if ( color.isValid() ) + { + setSelectedColor( color ); + + const auto x = qskMapValueRange( event->pos().x(), rect.left(), rect.right(), -1, +1 ); + const auto y = qskMapValueRange( event->pos().y(), rect.top(), rect.bottom(), +1, -1 ); + qDebug() << x << y; + setSkinHint( Handle, QPointF{ x, y } ); + } + } + event->ignore(); +} + +#include "moc_QskColorTriangle.cpp" \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorTriangle.h b/examples/gallery/colorpickers/QskColorTriangle.h new file mode 100644 index 000000000..2f3962b54 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorTriangle.h @@ -0,0 +1,40 @@ +#ifndef QSK_COLOR_TRIANGLE_H_ +#define QSK_COLOR_TRIANGLE_H_ + +#include + +class QskColorTriangle : public QskControl +{ + Q_OBJECT + Q_PROPERTY( + QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged ) + + public: + QSK_SUBCONTROLS( Triangle, Handle ) + explicit QskColorTriangle( QQuickItem* parent = nullptr ); + + QColor selectedColor() const; + + void setColors( const QColor& c1, const QColor& c2, const QColor& c3 ); + QColor color1() const; + QColor color2() const; + QColor color3() const; + + Q_SIGNALS: + void selectedColorChanged( const QColor& color ); + + protected: + void mousePressEvent( QMouseEvent* event ) override; + void mouseReleaseEvent( QMouseEvent* event ) override; + void mouseMoveEvent( QMouseEvent* event ) override; + + private: + void setSelectedColor( const QColor& color ); + + QColor m_selectedColor; + QColor m_c1 = Qt::transparent; + QColor m_c2 = Qt::black; + QColor m_c3 = Qt::white; +}; + +#endif \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorTriangleNode.cpp b/examples/gallery/colorpickers/QskColorTriangleNode.cpp new file mode 100644 index 000000000..28a609d6e --- /dev/null +++ b/examples/gallery/colorpickers/QskColorTriangleNode.cpp @@ -0,0 +1,42 @@ +#include "QskColorTriangleNode.h" +#include + +namespace +{ + static constexpr auto vertexCount = 3; +} + +QskColorTriangleNode::QskColorTriangleNode() +{ + auto* const geometry = + new QSGGeometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), vertexCount ); + geometry->setDrawingMode( QSGGeometry::DrawTriangles ); + setGeometry( geometry ); + + auto* const material = new QSGVertexColorMaterial; + setMaterial( material ); +} + +void QskColorTriangleNode::update( + const QRectF& rect, const QColor& c1, const QColor& c2, const QColor& c3, const qreal rad ) +{ + const QColor colors[]{ c1, c2, c3 }; + + const auto center = rect.center(); + const auto radius = qMin( rect.width(), rect.height() ) / 2; + + const auto step = 2 * M_PI / 3; + + auto* const vertices = geometry()->vertexDataAsColoredPoint2D(); + for ( int i = 0; i < vertexCount; ++i ) + { + const auto x = qFastCos( rad + i * step ) * radius + center.x(); + const auto y = qFastSin( rad + i * step ) * radius + center.y(); + const uchar c[ 4 ]{ ( uchar ) colors[ i ].red(), ( uchar ) colors[ i ].green(), + ( uchar ) colors[ i ].blue(), 255 }; + vertices[ i ].set( x, y, c[ 0 ], c[ 1 ], c[ 2 ], c[ 3 ] ); + } + geometry()->markVertexDataDirty(); + markDirty( QSGNode::DirtyGeometry ); + markDirty( QSGNode::DirtyMaterial ); +} \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorTriangleNode.h b/examples/gallery/colorpickers/QskColorTriangleNode.h new file mode 100644 index 000000000..d8a04b453 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorTriangleNode.h @@ -0,0 +1,19 @@ +#ifndef QSK_COLOR_TRIANGLE_NODE_H_ +#define QSK_COLOR_TRIANGLE_NODE_H_ + +#include +#include +#include + +class QskColorTriangleNode : public QSGGeometryNode +{ + using rad = qreal; + + public: + QskColorTriangleNode(); + + void update( + const QRectF& rect, const QColor& c1, const QColor& c2, const QColor& c3, rad angle ); +}; + +#endif \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorTriangleSkinlet.cpp b/examples/gallery/colorpickers/QskColorTriangleSkinlet.cpp new file mode 100644 index 000000000..bc620bef9 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorTriangleSkinlet.cpp @@ -0,0 +1,87 @@ +#include "QskColorTriangleSkinlet.h" +#include "QskColorTriangle.h" +#include "QskColorTriangleNode.h" +#include +#include + +using Q = QskColorTriangle; + +QskColorTriangleSkinlet::QskColorTriangleSkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { TriangleRole, HandleRole } ); +} + +QRectF QskColorTriangleSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const +{ + const auto* const q = static_cast< const Q* >( skinnable ); + + const auto& r = contentsRect; + + if ( subcontrol == Q::Triangle ) + { + const auto size = ( qMin( r.width(), contentsRect.height() ) ) / 2; + auto rect = QRectF( 0, 0, size, size ); + + return { QPointF{ r.center().x() - size, r.center().y() - size }, + QPointF{ r.center().x() + size, r.center().y() + size } }; + } + + if ( subcontrol == Q::Handle ) + { + const auto rect = subControlRect( skinnable, contentsRect, Q::Triangle ); + const auto size = + qMin( q->strutSizeHint( Q::Handle ).width(), q->strutSizeHint( Q::Handle ).height() ); + const auto position = q->effectiveSkinHint( Q::Handle ).toPointF(); + const auto radius = qMin( rect.width(), rect.height() ) / 2; + + if ( position == QPoint{} ) + { + return {}; + } + + QTransform t; + t.rotate( qRadiansToDegrees( q->positionHint( Q::Triangle ) ), Qt::ZAxis ); + const auto c = t.map( QPointF{ position.x(), -1 * position.y() } ) * radius + rect.center(); + + return { QPointF{ c.x() - size, c.y() - size }, QPointF{ c.x() + size, c.y() + size } }; + } + return QskSkinlet::subControlRect( skinnable, contentsRect, subcontrol ); +} + +QSGNode* QskColorTriangleSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto* const q = static_cast< const Q* >( skinnable ); + + if ( nodeRole == TriangleRole ) + { + const auto subcontrol = Q::Triangle; + const auto rect = subControlRect( skinnable, q->contentsRect(), subcontrol ); + auto* const triangleNode = QskSGNode::ensureNode< QskColorTriangleNode >( node ); + triangleNode->update( + rect, q->color1(), q->color2(), q->color3(), q->positionHint( Q::Triangle ) ); + return triangleNode; + } + if ( nodeRole == HandleRole ) + { + return updateBoxNode( skinnable, node, Q::Handle ); + } + + return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); +} + +QSizeF QskColorTriangleSkinlet::sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const +{ + if ( which != Qt::PreferredSize ) + { + return {}; + } + + const auto* const q = static_cast< const QskColorTriangle* >( skinnable ); + const auto rect = q->contentsRect(); + const auto size = qMin( rect.width(), rect.height() ); + return { size, size }; +} \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorTriangleSkinlet.h b/examples/gallery/colorpickers/QskColorTriangleSkinlet.h new file mode 100644 index 000000000..fe7929c65 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorTriangleSkinlet.h @@ -0,0 +1,29 @@ +#ifndef QSK_COLOR_TRIANGLE_SKINLET_H_ +#define QSK_COLOR_TRIANGLE_SKINLET_H_ + +#include + +class QskColorTriangleSkinlet : public QskSkinlet +{ + Q_GADGET + public: + enum NodeRoles + { + TriangleRole, + HandleRole + }; + + Q_INVOKABLE QskColorTriangleSkinlet( QskSkin* skin = nullptr ); + + private: + QRectF subControlRect( + const QskSkinnable* skinnable, const QRectF&, QskAspect::Subcontrol ) const override; + + QSGNode* updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* ) const override; + + QSizeF sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const override; +}; + +#endif diff --git a/examples/gallery/colorpickers/QskColorWheel.cpp b/examples/gallery/colorpickers/QskColorWheel.cpp new file mode 100644 index 000000000..5e02cf9c9 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorWheel.cpp @@ -0,0 +1,94 @@ +#include "QskColorWheel.h" +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + QColor qskColorAt( const QskGradient& gradient, qreal ratio ) + { + return gradient.extracted( ratio, ratio ).startColor(); + } + + QColor qskColorForAngle( const QskGradient& gradient, qreal angle ) + { + angle = ( angle < 0.0 ? angle + 2 * M_PI : angle ); + const auto ratio = qAbs( angle / ( 2 * M_PI ) ); + return qskColorAt( gradient, ratio ); + } +} + +QSK_SUBCONTROL( QskColorWheel, Arc ) +QSK_SUBCONTROL( QskColorWheel, Handle ) + +QskColorWheel::QskColorWheel( QQuickItem* parent ) + : QskControl( parent ) +{ + setAcceptedMouseButtons( Qt::LeftButton ); + + static const QskGradientStops stops = { + { 0.0000, QColor::fromRgb( 255, 0, 0 ) }, + { 0.1667, QColor::fromRgb( 255, 255, 0 ) }, + { 0.3333, QColor::fromRgb( 0, 255, 0 ) }, + { 0.5000, QColor::fromRgb( 0, 255, 255 ) }, + { 0.6667, QColor::fromRgb( 0, 0, 255 ) }, + { 0.8333, QColor::fromRgb( 255, 0, 255 ) }, + { 1.0000, QColor::fromRgb( 255, 0, 0 ) }, + }; + + QskGradient gradient( stops ); + setGradientHint( Arc, gradient ); + setArcMetricsHint( Arc, { 0, 360, 16 } ); + + setColor( Handle, Qt::white ); + setBoxBorderColorsHint( Handle, { Qt::white } ); + setBoxBorderMetricsHint( Handle, { 2 } ); + setBoxShapeHint( Handle, { 100.0, 100.0, 100.0, 100.0, Qt::RelativeSize } ); +} + +QColor QskColorWheel::selectedColor() const +{ + return m_selectedColor; +} + +void QskColorWheel::setSelectedColor( const QColor& color ) +{ + if ( m_selectedColor != color ) + { + m_selectedColor = color; + setColor( Handle, color ); + Q_EMIT selectedColorChanged( m_selectedColor ); + } +} + +void QskColorWheel::mousePressEvent( QMouseEvent* event ) +{ +} + +void QskColorWheel::mouseReleaseEvent( QMouseEvent* event ) +{ +} + +void QskColorWheel::mouseMoveEvent( QMouseEvent* event ) +{ + const auto r = contentsRect(); + const auto ux = qskMapValueRange( event->pos().x(), r.left(), r.right(), -1, +1 ); + const auto uy = qskMapValueRange( event->pos().y(), r.top(), r.bottom(), +1, -1 ); + + if ( QVector2D( ux, uy ).length() > 1.0 ) // TODO && Pressed + { + event->ignore(); + return; + } + + const auto rad = qAtan2( uy, ux ); + const auto color = qskColorForAngle( gradientHint( Arc ), rad ); + setPositionHint( Handle, -rad ); + setSelectedColor( color ); +} + +#include "moc_QskColorWheel.cpp" \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorWheel.h b/examples/gallery/colorpickers/QskColorWheel.h new file mode 100644 index 000000000..18fa9fa2d --- /dev/null +++ b/examples/gallery/colorpickers/QskColorWheel.h @@ -0,0 +1,30 @@ +#ifndef QSK_COLOR_WHEEL_H_ +#define QSK_COLOR_WHEEL_H_ + +#include + +class QskColorWheel : public QskControl +{ + Q_OBJECT + Q_PROPERTY( + QColor selectedColor READ selectedColor WRITE setSelectedColor NOTIFY selectedColorChanged ) + public: + QSK_SUBCONTROLS( Arc, Handle ) + explicit QskColorWheel( QQuickItem* parent = nullptr ); + + QColor selectedColor() const; + void setSelectedColor( const QColor& color ); + + protected: + void mousePressEvent( QMouseEvent* event ) override; + void mouseReleaseEvent( QMouseEvent* event ) override; + void mouseMoveEvent( QMouseEvent* event ) override; + + Q_SIGNALS: + void selectedColorChanged( const QColor& color ); + + private: + QColor m_selectedColor; +}; + +#endif \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorWheelSkinlet.cpp b/examples/gallery/colorpickers/QskColorWheelSkinlet.cpp new file mode 100644 index 000000000..d57148ad2 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorWheelSkinlet.cpp @@ -0,0 +1,71 @@ +#include "QskColorWheelSkinlet.h" +#include "QskColorWheel.h" + +#include +#include + +using Q = QskColorWheel; + +QskColorWheelSkinlet::QskColorWheelSkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { ArcRole, HandleRole } ); +} + +QRectF QskColorWheelSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const +{ + const auto* const q = static_cast< const Q* >( skinnable ); + + if ( subcontrol == Q::Arc ) + { + const auto size = qMin( contentsRect.width(), contentsRect.height() ); + return { contentsRect.center().x() - size / 2, contentsRect.center().y() - size / 2, size, + size }; + } + + if ( subcontrol == Q::Handle ) + { + const auto& r = contentsRect; + const auto thickness = skinnable->arcMetricsHint( Q::Arc ).thickness() / 2; + const auto angle = q->positionHint( Q::Handle ); + const auto radius = qMin( r.width(), r.height() ) / 2 - thickness; + const auto x = contentsRect.center().x() + radius * qFastCos( angle ); + const auto y = contentsRect.center().y() + radius * qFastSin( angle ); + return { QPointF{ x - thickness, y - thickness }, QPointF{ x + thickness, y + thickness } }; + } + + return QskSkinlet::subControlRect( skinnable, contentsRect, subcontrol ); +} + +QSGNode* QskColorWheelSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto* const q = static_cast< const Q* >( skinnable ); + + if ( nodeRole == ArcRole ) + { + return updateArcNode( skinnable, node, Q::Arc ); + } + + if ( nodeRole == HandleRole ) + { + return updateBoxNode( skinnable, node, Q::Handle ); + } + + return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); +} + +QSizeF QskColorWheelSkinlet::sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const +{ + if ( which != Qt::PreferredSize ) + { + return {}; + } + + const auto* const q = static_cast< const Q* >( skinnable ); + const auto rect = q->contentsRect(); + const auto size = qMin( rect.width(), rect.height() ); + return { size, size }; +} \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorWheelSkinlet.h b/examples/gallery/colorpickers/QskColorWheelSkinlet.h new file mode 100644 index 000000000..04f74dc1a --- /dev/null +++ b/examples/gallery/colorpickers/QskColorWheelSkinlet.h @@ -0,0 +1,30 @@ +#ifndef QSK_COLOR_WHEEL_SKINLET_H_ +#define QSK_COLOR_WHEEL_SKINLET_H_ + +#include +#include + +class QskColorWheelSkinlet : public QskSkinlet +{ + Q_GADGET + public: + enum NodeRoles + { + ArcRole, + HandleRole + }; + + Q_INVOKABLE QskColorWheelSkinlet( QskSkin* skin = nullptr ); + + private: + QRectF subControlRect( + const QskSkinnable* skinnable, const QRectF&, QskAspect::Subcontrol ) const override; + + QSGNode* updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* ) const override; + + QSizeF sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const override; +}; + +#endif \ No newline at end of file diff --git a/src/common/QskFunctions.h b/src/common/QskFunctions.h index 1ac3e3d92..50fd0d857 100644 --- a/src/common/QskFunctions.h +++ b/src/common/QskFunctions.h @@ -69,6 +69,15 @@ inline constexpr bool qskFuzzyCompare( qreal value1, qreal value2 ) return qFuzzyCompare( value1, value2 ); } +inline constexpr qreal qskMapValueRange( qreal value, qreal srcMin, qreal srcMax, qreal dstMin, qreal dstMax ) +{ + value = std::min( std::max( value, srcMin ), srcMax ); + double percentage = ( value - srcMin ) / ( srcMax - srcMin ); + double mappedValue = dstMin + percentage * ( dstMax - dstMin ); + return mappedValue; +} + + QSK_EXPORT qreal qskFuzzyFloor( qreal value, qreal stepSize ); QSK_EXPORT qreal qskFuzzyCeil( qreal value, qreal stepSize ); diff --git a/src/nodes/shaders/colorwheel.frag b/src/nodes/shaders/colorwheel.frag deleted file mode 100644 index 10822a070..000000000 --- a/src/nodes/shaders/colorwheel.frag +++ /dev/null @@ -1,94 +0,0 @@ -uniform lowp float qt_Opacity; -uniform lowp vec4 color; -uniform lowp vec4 rect; -uniform lowp vec4 mouse; - -#undef ANIMATE - -#define M_PI 3.1415926535897932384626433832795 - -// 0 1 2 3 4 5 -// R 1 1 0 0 0 1 -// G 0 1 1 1 0 0 -// B 0 0 0 1 1 1 -vec3 getHueColor(vec2 pos) -{ -#ifdef ANIMATE - float theta = mod(3.0 + 3.0 * atan(pos.x, pos.y) / M_PI + iTime, 6.0); -#else - float theta = 3.0 + 3.0 * atan(pos.x, pos.y) / M_PI; -#endif - - vec3 color = vec3(0.0); - - return clamp(abs(mod(theta + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0); -} - -void mainImage( out vec4 fragColor, in vec2 fragCoord ) -{ - vec2 iOffset = vec2(-rect.x, -rect.y); - vec2 iResolution = rect.zw; - vec4 iMouse = mouse; - - vec2 uv = vec2(2.0, -2.0) * (fragCoord.xy + iOffset - 0.5 * iResolution.xy) / iResolution.y; - vec2 mouse = vec2(2.0, -2.0) * (iMouse.xy + iOffset - 0.5 * iResolution.xy) / iResolution.y; - - float l = length(uv); - float m = length(mouse); - - fragColor = vec4(0.0); - - if (l >= 0.75 && l <= 1.0) - { - l = 1.0 - abs((l - 0.875) * 8.0); - l = clamp(l * iResolution.y * 0.0625, 0.0, 1.0); // Antialiasing approximation - - fragColor = vec4(l * getHueColor(uv), l); - } - else if (l < 0.75) - { - vec3 pickedHueColor; - - if (m < 0.75 || m > 1.0) - { -#ifdef ANIMATE - mouse = vec2(sin(iTime), cos(iTime)); -#else - mouse = vec2(0.0, -1.0); - pickedHueColor = vec3(1.0, 0.0, 0.0); -#endif - } -#ifndef ANIMATE - else - { -#endif - pickedHueColor = getHueColor(mouse); -#ifndef ANIMATE - } -#endif - - uv = uv / 0.75; - mouse = normalize(mouse); - - float sat = 1.5 - (dot(uv, mouse) + 0.5); // [0.0,1.5] - - if (sat < 1.5) - { - float h = sat / sqrt(3.0); - vec2 om = vec2(cross(vec3(mouse, 0.0), vec3(0.0, 0.0, 1.0))); - float lum = dot(uv, om); - - if (abs(lum) <= h) - { - l = clamp((h - abs(lum)) * iResolution.y * 0.5, 0.0, 1.0) * clamp((1.5 - sat) / 1.5 * iResolution.y * 0.5, 0.0, 1.0); // Fake antialiasing - fragColor = vec4(l * mix(pickedHueColor, vec3(0.5 * (lum + h) / h), sat / 1.5), l); - } - } - } -} - -void main() -{ - mainImage(gl_FragColor, gl_FragCoord); - gl_FragColor *= qt_Opacity; -} \ No newline at end of file diff --git a/src/nodes/shaders/colorwheel.vert b/src/nodes/shaders/colorwheel.vert deleted file mode 100644 index 9a5507ceb..000000000 --- a/src/nodes/shaders/colorwheel.vert +++ /dev/null @@ -1,7 +0,0 @@ -attribute highp vec4 vertex; -uniform highp mat4 qt_Matrix; - -void main() -{ - gl_Position = qt_Matrix * vertex; -} \ No newline at end of file From dc47f0dd5bf5b89b5d66eba3d9b86c1e326e18e8 Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Tue, 19 Dec 2023 23:23:07 +0100 Subject: [PATCH 6/9] add color circle --- examples/gallery/CMakeLists.txt | 6 + .../gallery/colorpickers/ColorPickerPage.cpp | 22 +++- .../gallery/colorpickers/QskCircleNode.cpp | 51 +++++++++ examples/gallery/colorpickers/QskCircleNode.h | 14 +++ .../gallery/colorpickers/QskColorCircle.cpp | 103 ++++++++++++++++++ .../gallery/colorpickers/QskColorCircle.h | 30 +++++ .../colorpickers/QskColorCircleSkinlet.cpp | 90 +++++++++++++++ .../colorpickers/QskColorCircleSkinlet.h | 29 +++++ .../colorpickers/QskColorTriangleSkinlet.cpp | 2 +- 9 files changed, 345 insertions(+), 2 deletions(-) create mode 100644 examples/gallery/colorpickers/QskCircleNode.cpp create mode 100644 examples/gallery/colorpickers/QskCircleNode.h create mode 100644 examples/gallery/colorpickers/QskColorCircle.cpp create mode 100644 examples/gallery/colorpickers/QskColorCircle.h create mode 100644 examples/gallery/colorpickers/QskColorCircleSkinlet.cpp create mode 100644 examples/gallery/colorpickers/QskColorCircleSkinlet.h diff --git a/examples/gallery/CMakeLists.txt b/examples/gallery/CMakeLists.txt index e32dde118..20825af1e 100644 --- a/examples/gallery/CMakeLists.txt +++ b/examples/gallery/CMakeLists.txt @@ -28,6 +28,12 @@ set(SOURCES colorpickers/QskColorWheel.h colorpickers/QskColorWheelSkinlet.cpp colorpickers/QskColorWheelSkinlet.h + colorpickers/QskColorCircle.h + colorpickers/QskColorCircle.cpp + colorpickers/QskColorCircleSkinlet.h + colorpickers/QskColorCircleSkinlet.cpp + colorpickers/QskCircleNode.h + colorpickers/QskCircleNode.cpp Page.h Page.cpp diff --git a/examples/gallery/colorpickers/ColorPickerPage.cpp b/examples/gallery/colorpickers/ColorPickerPage.cpp index d1477d51f..08294fd85 100644 --- a/examples/gallery/colorpickers/ColorPickerPage.cpp +++ b/examples/gallery/colorpickers/ColorPickerPage.cpp @@ -1,16 +1,20 @@ #include "ColorPickerPage.h" +#include "QskColorCircle.h" +#include "QskColorCircleSkinlet.h" #include "QskColorRectangle.h" #include "QskColorRectangleSkinlet.h" #include "QskColorTriangle.h" #include "QskColorTriangleSkinlet.h" #include "QskColorWheel.h" #include "QskColorWheelSkinlet.h" +#include "colorpickers/QskColorCircle.h" +#include "colorpickers/QskColorCircleSkinlet.h" +#include #include #include #include -#include #include @@ -37,6 +41,8 @@ ColorPickerPage::ColorPickerPage( QQuickItem* parent ) auto* const colorRectangle = create< QskColorRectangle, QskColorRectangleSkinlet >( layout ); + auto* const colorCircle = create< QskColorCircle, QskColorCircleSkinlet >( layout ); + layout->addItem( new QskTextLabel( "QskColorWheel" ), 0, 0 ); layout->addItem( colorWheel, 1, 0 ); layout->addItem( colorWheelSlider, 2, 0 ); @@ -45,6 +51,8 @@ ColorPickerPage::ColorPickerPage( QQuickItem* parent ) layout->addItem( colorTriangleSlider, 2, 1 ); layout->addItem( new QskTextLabel( "QskColorRectangle" ), 0, 2 ); layout->addItem( colorRectangle, 1, 2 ); + layout->addItem( new QskTextLabel( "QskColorCircle" ), 0, 3 ); + layout->addItem( colorCircle, 1, 3 ); for ( int col = 0; col < layout->columnCount(); col++ ) { @@ -55,6 +63,10 @@ ColorPickerPage::ColorPickerPage( QQuickItem* parent ) [ colorTriangle ]( const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); + connect( colorCircle, &QskColorCircle::selectedColorChanged, colorTriangle, + [ colorTriangle ]( + const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); + connect( colorWheelSlider, &QskSlider::valueChanged, this, [ q = colorWheel ]( qreal v ) { auto metrics = q->arcMetricsHint( QskColorWheel::Arc ); metrics.setThickness( v * qMin( q->width(), q->height() ) / 2 ); @@ -72,4 +84,12 @@ ColorPickerPage::ColorPickerPage( QQuickItem* parent ) colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); } ); + + connect( colorCircle, &QskColorCircle::selectedColorChanged, colorRectangle, + [ = ]( const QColor& color ) { + colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); + colorRectangle->setCornerColor( Qt::TopRightCorner, color ); + colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); + colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); + } ); } \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskCircleNode.cpp b/examples/gallery/colorpickers/QskCircleNode.cpp new file mode 100644 index 000000000..e92eb0a15 --- /dev/null +++ b/examples/gallery/colorpickers/QskCircleNode.cpp @@ -0,0 +1,51 @@ +#include "QskCircleNode.h" +#include "colorpickers/QskCircleNode.h" + +#include +#include + +#include +#include +#include + +#include + +QskCircleNode::QskCircleNode() +{ + auto* const geometry = new QSGGeometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ); + geometry->setDrawingMode( QSGGeometry::DrawTriangleFan ); + setGeometry( geometry ); + + auto* const material = new QSGVertexColorMaterial; + setMaterial( material ); +} + +void QskCircleNode::update( const QRectF& rect, const QskGradient& gradient, int vertexCount ) +{ + constexpr auto additional = 2; // +1 center, +1 for second 0 degree closing + vertexCount = qMax( 3, vertexCount ) + additional; + + if ( geometry()->vertexCount() != vertexCount ) + { + geometry()->allocate( vertexCount ); + } + + const auto step = 2 * M_PI / ( vertexCount - additional ); + const auto radius = qMin( rect.width(), rect.height() ) / 2; + + auto* const vertices = geometry()->vertexDataAsColoredPoint2D(); + vertices[ 0 ].set( rect.center().x(), rect.center().y(), 255, 255, 255, 255 ); + for ( int i = 1; i <= vertexCount; i++ ) + { + const auto angle = ( i - 1 ) * step; + const auto x = qFastCos( angle ) * radius + rect.center().x(); + const auto y = qFastSin( angle ) * radius + rect.center().y(); + const auto color = + gradient.extracted( angle / ( 2 * M_PI ), angle / ( 2 * M_PI ) ).startColor(); + vertices[ i ].set( x, y, color.red(), color.green(), color.blue(), color.alpha() ); + } + + geometry()->markVertexDataDirty(); + markDirty( QSGNode::DirtyGeometry ); + markDirty( QSGNode::DirtyMaterial ); +} \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskCircleNode.h b/examples/gallery/colorpickers/QskCircleNode.h new file mode 100644 index 000000000..83f187004 --- /dev/null +++ b/examples/gallery/colorpickers/QskCircleNode.h @@ -0,0 +1,14 @@ +#ifndef QSK_CIRCLE_NODE_H_ +#define QSK_CIRCLE_NODE_H_ + +#include "QskGradient.h" +#include + +class QskCircleNode : public QSGGeometryNode +{ + public: + QskCircleNode(); + void update( const QRectF& rect, const QskGradient& gradient, int vertexCount = 360 ); +}; + +#endif \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorCircle.cpp b/examples/gallery/colorpickers/QskColorCircle.cpp new file mode 100644 index 000000000..3e10b2783 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorCircle.cpp @@ -0,0 +1,103 @@ +#include "QskColorCircle.h" +#include "QskAspect.h" +#include "QskControl.h" +#include "QskGradient.h" +#include "colorpickers/QskColorCircle.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace +{ + QColor qskColorAt( const QskGradient& gradient, qreal ratio ) + { + return gradient.extracted( ratio, ratio ).startColor(); + } + + QColor qskColorForAngle( const QskGradient& gradient, qreal angle ) + { + angle = ( angle < 0.0 ? angle + 2 * M_PI : angle ); + const auto ratio = qAbs( angle / ( 2 * M_PI ) ); + return qskColorAt( gradient, ratio ); + } +} + +QSK_SUBCONTROL( QskColorCircle, Circle ) +QSK_SUBCONTROL( QskColorCircle, Handle ) + +QskColorCircle::QskColorCircle( QQuickItem* parent ) + : QskControl( parent ) +{ + const QskGradient gradient( { + { 0.0000, QColor::fromRgb( 255, 0, 0 ) }, + { 0.1667, QColor::fromRgb( 255, 255, 0 ) }, + { 0.3333, QColor::fromRgb( 0, 255, 0 ) }, + { 0.5000, QColor::fromRgb( 0, 255, 255 ) }, + { 0.6667, QColor::fromRgb( 0, 0, 255 ) }, + { 0.8333, QColor::fromRgb( 255, 0, 255 ) }, + { 1.0000, QColor::fromRgb( 255, 0, 0 ) }, + } ); + + setGradientHint( Circle, gradient ); + + setBoxBorderColorsHint( Handle, Qt::white ); + setBoxBorderMetricsHint( Handle, 2 ); + setBoxShapeHint( Handle, { 100, 100, 100, 100, Qt::AbsoluteSize } ); + setStrutSizeHint( Handle, { 20, 20 } ); + + setAcceptedMouseButtons( Qt::LeftButton ); +} + +void QskColorCircle::mousePressEvent( QMouseEvent* event ) +{ +} + +void QskColorCircle::mouseReleaseEvent( QMouseEvent* event ) +{ +} + +void QskColorCircle::mouseMoveEvent( QMouseEvent* event ) +{ + const auto r = subControlRect( Circle ); + const auto ux = qskMapValueRange( event->pos().x(), r.left(), r.right(), -1, +1 ); + const auto uy = qskMapValueRange( event->pos().y(), r.top(), r.bottom(), -1, +1 ); + + const auto radius = QVector2D( ux, uy ).length(); + if ( radius > 1.0 ) + { + event->ignore(); + return; + } + + const auto rad = qAtan2( uy, ux ); + const auto ratio = rad / ( 2 * M_PI ); + const auto color = qskColorForAngle( gradientHint( Circle ), rad ); + const QskGradient gradient(Qt::white, color); + + const auto effectiveColor = qskColorAt(gradient, radius); + + setColor( Handle, effectiveColor ); + setSkinHint( Handle, QPointF{ ux, uy } ); + setSelectedColor( effectiveColor ); +} + +QColor QskColorCircle::selectedColor() const +{ + return m_selectedColor; +} + +void QskColorCircle::setSelectedColor( const QColor& color ) +{ + if ( m_selectedColor != color ) + { + m_selectedColor = color; + Q_EMIT selectedColorChanged( m_selectedColor ); + } +} \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorCircle.h b/examples/gallery/colorpickers/QskColorCircle.h new file mode 100644 index 000000000..bf76c35b6 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorCircle.h @@ -0,0 +1,30 @@ +#ifndef QSK_COLOR_CIRCLE_H_ +#define QSK_COLOR_CIRCLE_H_ + +#include "QskAspect.h" +#include +#include + +class QskColorCircle : public QskControl +{ + Q_OBJECT + public: + QSK_SUBCONTROLS( Circle, Handle ) + explicit QskColorCircle( QQuickItem* parent = nullptr ); + + QColor selectedColor() const; + + protected: + void mousePressEvent( QMouseEvent* event ) override; + void mouseReleaseEvent( QMouseEvent* event ) override; + void mouseMoveEvent( QMouseEvent* event ) override; + + Q_SIGNALS: + void selectedColorChanged( const QColor& color ); + + private: + void setSelectedColor( const QColor& color ); + QColor m_selectedColor; +}; + +#endif \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorCircleSkinlet.cpp b/examples/gallery/colorpickers/QskColorCircleSkinlet.cpp new file mode 100644 index 000000000..761e05203 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorCircleSkinlet.cpp @@ -0,0 +1,90 @@ +#include "QskColorCircleSkinlet.h" +#include "QskCircleNode.h" +#include "QskColorCircle.h" +#include "QskSkinlet.h" +#include "colorpickers/QskCircleNode.h" +#include "colorpickers/QskColorCircleSkinlet.h" + +#include +#include + +using Q = QskColorCircle; + +QskColorCircleSkinlet::QskColorCircleSkinlet( QskSkin* skin ) + : QskSkinlet( skin ) +{ + setNodeRoles( { CircleRole, HandleRole } ); +} + +QRectF QskColorCircleSkinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const +{ + const auto* const q = static_cast< const Q* >( skinnable ); + + if ( subcontrol == Q::Circle ) + { + const auto c = contentsRect.center(); + const auto r = qMin( contentsRect.width(), contentsRect.height() ) / 2; + return { QPointF{ c.x() - r, c.y() - r }, QPointF{ c.x() + r, c.y() + r } }; + } + + if ( subcontrol == Q::Handle ) + { + const auto c = contentsRect.center(); + const auto r = qMin( contentsRect.width(), contentsRect.height() ) / 2; + const auto p = q->effectiveSkinHint( Q::Handle ).toPointF(); + const auto s = q->strutSizeHint( Q::Handle ); + const auto w = s.width() / 2; + const auto h = s.height() / 2; + + if ( p == QPointF{ -1, -1 } ) + { + return {}; + } + + const auto x = c.x() + p.x() * r; + const auto y = c.y() + p.y() * r; + + return { + QPointF{ x - w, y - h }, + QPointF{ x + w, y + h }, + }; + } + + return QskSkinlet::subControlRect( skinnable, contentsRect, subcontrol ); +} + +QSGNode* QskColorCircleSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto* const q = static_cast< const Q* >( skinnable ); + + if ( nodeRole == CircleRole ) + { + const auto gradient = q->gradientHint( Q::Circle ); + auto* const circle = QskSGNode::ensureNode< QskCircleNode >( node ); + circle->update( q->contentsRect(), gradient ); + return circle; + } + + if ( nodeRole == HandleRole ) + { + return updateBoxNode( skinnable, node, Q::Handle ); + } + + return QskSkinlet::updateSubNode( skinnable, nodeRole, node ); +} + +QSizeF QskColorCircleSkinlet::sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const +{ + if ( which != Qt::PreferredSize ) + { + return {}; + } + + const auto* const q = static_cast< const Q* >( skinnable ); + const auto rect = q->contentsRect(); + const auto size = qMin( rect.width(), rect.height() ); + return { size, size }; +} \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorCircleSkinlet.h b/examples/gallery/colorpickers/QskColorCircleSkinlet.h new file mode 100644 index 000000000..e6d592a05 --- /dev/null +++ b/examples/gallery/colorpickers/QskColorCircleSkinlet.h @@ -0,0 +1,29 @@ +#ifndef QSK_COLOR_CIRCLE_SKINLET_H_ +#define QSK_COLOR_CIRCLE_SKINLET_H_ + +#include + +class QskColorCircleSkinlet : public QskSkinlet +{ + Q_GADGET + public: + enum NodeRoles + { + CircleRole, + HandleRole + }; + + Q_INVOKABLE QskColorCircleSkinlet( QskSkin* skin = nullptr ); + + private: + QRectF subControlRect( const QskSkinnable* skinnable, const QRectF&, + QskAspect::Subcontrol subcontrol ) const override; + + QSGNode* updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override; + + QSizeF sizeHint( + const QskSkinnable* skinnable, Qt::SizeHint which, const QSizeF& ) const override; +}; + +#endif \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorTriangleSkinlet.cpp b/examples/gallery/colorpickers/QskColorTriangleSkinlet.cpp index bc620bef9..c34855868 100644 --- a/examples/gallery/colorpickers/QskColorTriangleSkinlet.cpp +++ b/examples/gallery/colorpickers/QskColorTriangleSkinlet.cpp @@ -32,7 +32,7 @@ QRectF QskColorTriangleSkinlet::subControlRect( const QskSkinnable* skinnable, { const auto rect = subControlRect( skinnable, contentsRect, Q::Triangle ); const auto size = - qMin( q->strutSizeHint( Q::Handle ).width(), q->strutSizeHint( Q::Handle ).height() ); + qMin( q->strutSizeHint( Q::Handle ).width(), q->strutSizeHint( Q::Handle ).height() ) / 2; const auto position = q->effectiveSkinHint( Q::Handle ).toPointF(); const auto radius = qMin( rect.width(), rect.height() ) / 2; From 937672a59cafbf393f626d46c5e07d47d3831828 Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Sat, 6 Jan 2024 14:34:45 +0100 Subject: [PATCH 7/9] add custom gradients --- .../gallery/colorpickers/ColorPickerPage.cpp | 119 +++++++++++------- .../gallery/colorpickers/QskColorTriangle.cpp | 1 - 2 files changed, 73 insertions(+), 47 deletions(-) diff --git a/examples/gallery/colorpickers/ColorPickerPage.cpp b/examples/gallery/colorpickers/ColorPickerPage.cpp index 08294fd85..1a7a102bc 100644 --- a/examples/gallery/colorpickers/ColorPickerPage.cpp +++ b/examples/gallery/colorpickers/ColorPickerPage.cpp @@ -8,8 +8,11 @@ #include "QskColorTriangleSkinlet.h" #include "QskColorWheel.h" #include "QskColorWheelSkinlet.h" + #include "colorpickers/QskColorCircle.h" #include "colorpickers/QskColorCircleSkinlet.h" +#include "colorpickers/QskColorRectangle.h" +#include "colorpickers/QskColorTriangle.h" #include #include @@ -34,62 +37,86 @@ ColorPickerPage::ColorPickerPage( QQuickItem* parent ) auto* const layout = new QskGridBox( this ); auto* const colorWheel = create< QskColorWheel, QskColorWheelSkinlet >( layout ); + auto* const colorWheel2 = create< QskColorWheel, QskColorWheelSkinlet >( layout ); auto* const colorWheelSlider = new QskSlider( layout ); auto* const colorTriangle = create< QskColorTriangle, QskColorTriangleSkinlet >( layout ); + auto* const colorTriangle2 = create< QskColorTriangle, QskColorTriangleSkinlet >( layout ); auto* const colorTriangleSlider = new QskSlider( layout ); auto* const colorRectangle = create< QskColorRectangle, QskColorRectangleSkinlet >( layout ); + auto* const colorRectangle2 = create< QskColorRectangle, QskColorRectangleSkinlet >( layout ); auto* const colorCircle = create< QskColorCircle, QskColorCircleSkinlet >( layout ); + auto* const colorCircle2 = create< QskColorCircle, QskColorCircleSkinlet >( layout ); + + colorWheel2->setGradientHint( QskColorWheel::Arc, + { { { 0.0, Qt::red }, { 0.3333, Qt::green }, { 0.6666, Qt::blue }, { 1.0, Qt::red } } } ); + colorTriangle2->setColors( Qt::red, Qt::green, Qt::blue ); + colorRectangle2->setCornerColor( Qt::TopLeftCorner, Qt::red ); + colorRectangle2->setCornerColor( Qt::TopRightCorner, Qt::green ); + colorRectangle2->setCornerColor( Qt::BottomLeftCorner, Qt::blue ); + colorRectangle2->setCornerColor( Qt::BottomRightCorner, Qt::yellow ); + + colorCircle2->setGradientHint( QskColorCircle::Circle, + {{ + { 0.0, Qt::red }, + { 0.3333, Qt::green }, + { 0.6666, Qt::blue }, + { 1.0, Qt::red } + }} ); + +layout->addItem( new QskTextLabel( "QskColorWheel" ), 0, 0 ); +layout->addItem( colorWheel, 1, 0 ); +layout->addItem( colorWheel2, 2, 0 ); +layout->addItem( colorWheelSlider, 3, 0 ); +layout->addItem( new QskTextLabel( "QskColorTriangle" ), 0, 1 ); +layout->addItem( colorTriangle, 1, 1 ); +layout->addItem( colorTriangle2, 2, 1 ); +layout->addItem( colorTriangleSlider, 3, 1 ); +layout->addItem( new QskTextLabel( "QskColorRectangle" ), 0, 2 ); +layout->addItem( colorRectangle, 1, 2 ); +layout->addItem( colorRectangle2, 2, 2 ); +layout->addItem( new QskTextLabel( "QskColorCircle" ), 0, 3 ); +layout->addItem( colorCircle, 1, 3 ); +layout->addItem( colorCircle2, 2, 3 ); + +for ( int col = 0; col < layout->columnCount(); col++ ) +{ + layout->setColumnStretchFactor( col, 1 ); +} - layout->addItem( new QskTextLabel( "QskColorWheel" ), 0, 0 ); - layout->addItem( colorWheel, 1, 0 ); - layout->addItem( colorWheelSlider, 2, 0 ); - layout->addItem( new QskTextLabel( "QskColorTriangle" ), 0, 1 ); - layout->addItem( colorTriangle, 1, 1 ); - layout->addItem( colorTriangleSlider, 2, 1 ); - layout->addItem( new QskTextLabel( "QskColorRectangle" ), 0, 2 ); - layout->addItem( colorRectangle, 1, 2 ); - layout->addItem( new QskTextLabel( "QskColorCircle" ), 0, 3 ); - layout->addItem( colorCircle, 1, 3 ); - - for ( int col = 0; col < layout->columnCount(); col++ ) - { - layout->setColumnStretchFactor( col, 1 ); - } - - connect( colorWheel, &QskColorWheel::selectedColorChanged, colorTriangle, - [ colorTriangle ]( - const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); - - connect( colorCircle, &QskColorCircle::selectedColorChanged, colorTriangle, - [ colorTriangle ]( - const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); - - connect( colorWheelSlider, &QskSlider::valueChanged, this, [ q = colorWheel ]( qreal v ) { - auto metrics = q->arcMetricsHint( QskColorWheel::Arc ); - metrics.setThickness( v * qMin( q->width(), q->height() ) / 2 ); - q->setArcMetricsHint( QskColorWheel::Arc, metrics ); +connect( colorWheel, &QskColorWheel::selectedColorChanged, colorTriangle, + [ colorTriangle ]( + const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); + +connect( colorCircle, &QskColorCircle::selectedColorChanged, colorTriangle, + [ colorTriangle ]( + const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); + +connect( colorWheelSlider, &QskSlider::valueChanged, this, [ q = colorWheel ]( qreal v ) { + auto metrics = q->arcMetricsHint( QskColorWheel::Arc ); + metrics.setThickness( v * qMin( q->width(), q->height() ) / 2 ); + q->setArcMetricsHint( QskColorWheel::Arc, metrics ); +} ); + +connect( colorTriangleSlider, &QskSlider::valueChanged, this, [ = ]( qreal v ) { + colorTriangle->setPositionHint( QskColorTriangle::Triangle, 2 * M_PI * v ); +} ); + +connect( + colorWheel, &QskColorWheel::selectedColorChanged, colorRectangle, [ = ]( const QColor& color ) { + colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); + colorRectangle->setCornerColor( Qt::TopRightCorner, color ); + colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); + colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); } ); - connect( colorTriangleSlider, &QskSlider::valueChanged, this, [ = ]( qreal v ) { - colorTriangle->setPositionHint( QskColorTriangle::Triangle, 2 * M_PI * v ); +connect( colorCircle, &QskColorCircle::selectedColorChanged, colorRectangle, + [ = ]( const QColor& color ) { + colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); + colorRectangle->setCornerColor( Qt::TopRightCorner, color ); + colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); + colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); } ); - - connect( colorWheel, &QskColorWheel::selectedColorChanged, colorRectangle, - [ = ]( const QColor& color ) { - colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); - colorRectangle->setCornerColor( Qt::TopRightCorner, color ); - colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); - colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); - } ); - - connect( colorCircle, &QskColorCircle::selectedColorChanged, colorRectangle, - [ = ]( const QColor& color ) { - colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); - colorRectangle->setCornerColor( Qt::TopRightCorner, color ); - colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); - colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); - } ); } \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskColorTriangle.cpp b/examples/gallery/colorpickers/QskColorTriangle.cpp index e27f8ba58..cc599fd81 100644 --- a/examples/gallery/colorpickers/QskColorTriangle.cpp +++ b/examples/gallery/colorpickers/QskColorTriangle.cpp @@ -150,7 +150,6 @@ void QskColorTriangle::mouseMoveEvent( QMouseEvent* event ) const auto x = qskMapValueRange( event->pos().x(), rect.left(), rect.right(), -1, +1 ); const auto y = qskMapValueRange( event->pos().y(), rect.top(), rect.bottom(), +1, -1 ); - qDebug() << x << y; setSkinHint( Handle, QPointF{ x, y } ); } } From 1c7d64272f0d53c13f3ae97abcf98bf87246181b Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Sun, 28 Jan 2024 16:35:30 +0100 Subject: [PATCH 8/9] add QskAlphaSlider --- examples/gallery/CMakeLists.txt | 4 + .../gallery/colorpickers/ColorPickerPage.cpp | 123 ++++++------ .../gallery/colorpickers/QskAlphaSlider.cpp | 13 ++ .../gallery/colorpickers/QskAlphaSlider.h | 17 ++ .../colorpickers/QskAlphaSliderSkinlet.cpp | 180 ++++++++++++++++++ .../colorpickers/QskAlphaSliderSkinlet.h | 11 ++ 6 files changed, 291 insertions(+), 57 deletions(-) create mode 100644 examples/gallery/colorpickers/QskAlphaSlider.cpp create mode 100644 examples/gallery/colorpickers/QskAlphaSlider.h create mode 100644 examples/gallery/colorpickers/QskAlphaSliderSkinlet.cpp create mode 100644 examples/gallery/colorpickers/QskAlphaSliderSkinlet.h diff --git a/examples/gallery/CMakeLists.txt b/examples/gallery/CMakeLists.txt index 20825af1e..19f2566c2 100644 --- a/examples/gallery/CMakeLists.txt +++ b/examples/gallery/CMakeLists.txt @@ -12,6 +12,10 @@ set(SOURCES dialog/DialogPage.h dialog/DialogPage.cpp listbox/ListBoxPage.h listbox/ListBoxPage.cpp + colorpickers/QskAlphaSlider.h + colorpickers/QskAlphaSlider.cpp + colorpickers/QskAlphaSliderSkinlet.h + colorpickers/QskAlphaSliderSkinlet.cpp colorpickers/ColorPickerPage.cpp colorpickers/ColorPickerPage.h colorpickers/QskColorRectangle.cpp diff --git a/examples/gallery/colorpickers/ColorPickerPage.cpp b/examples/gallery/colorpickers/ColorPickerPage.cpp index 1a7a102bc..09b8ba32f 100644 --- a/examples/gallery/colorpickers/ColorPickerPage.cpp +++ b/examples/gallery/colorpickers/ColorPickerPage.cpp @@ -9,11 +9,14 @@ #include "QskColorWheel.h" #include "QskColorWheelSkinlet.h" +#include "colorpickers/QskAlphaSlider.h" +#include "colorpickers/QskAlphaSliderSkinlet.h" #include "colorpickers/QskColorCircle.h" #include "colorpickers/QskColorCircleSkinlet.h" #include "colorpickers/QskColorRectangle.h" #include "colorpickers/QskColorTriangle.h" + #include #include #include @@ -50,6 +53,8 @@ ColorPickerPage::ColorPickerPage( QQuickItem* parent ) auto* const colorCircle = create< QskColorCircle, QskColorCircleSkinlet >( layout ); auto* const colorCircle2 = create< QskColorCircle, QskColorCircleSkinlet >( layout ); + auto* const alphaSlider = create< QskAlphaSlider, QskAlphaSliderSkinlet >( layout ); + colorWheel2->setGradientHint( QskColorWheel::Arc, { { { 0.0, Qt::red }, { 0.3333, Qt::green }, { 0.6666, Qt::blue }, { 1.0, Qt::red } } } ); colorTriangle2->setColors( Qt::red, Qt::green, Qt::blue ); @@ -58,65 +63,69 @@ ColorPickerPage::ColorPickerPage( QQuickItem* parent ) colorRectangle2->setCornerColor( Qt::BottomLeftCorner, Qt::blue ); colorRectangle2->setCornerColor( Qt::BottomRightCorner, Qt::yellow ); - colorCircle2->setGradientHint( QskColorCircle::Circle, - {{ - { 0.0, Qt::red }, - { 0.3333, Qt::green }, - { 0.6666, Qt::blue }, - { 1.0, Qt::red } - }} ); - -layout->addItem( new QskTextLabel( "QskColorWheel" ), 0, 0 ); -layout->addItem( colorWheel, 1, 0 ); -layout->addItem( colorWheel2, 2, 0 ); -layout->addItem( colorWheelSlider, 3, 0 ); -layout->addItem( new QskTextLabel( "QskColorTriangle" ), 0, 1 ); -layout->addItem( colorTriangle, 1, 1 ); -layout->addItem( colorTriangle2, 2, 1 ); -layout->addItem( colorTriangleSlider, 3, 1 ); -layout->addItem( new QskTextLabel( "QskColorRectangle" ), 0, 2 ); -layout->addItem( colorRectangle, 1, 2 ); -layout->addItem( colorRectangle2, 2, 2 ); -layout->addItem( new QskTextLabel( "QskColorCircle" ), 0, 3 ); -layout->addItem( colorCircle, 1, 3 ); -layout->addItem( colorCircle2, 2, 3 ); - -for ( int col = 0; col < layout->columnCount(); col++ ) -{ - layout->setColumnStretchFactor( col, 1 ); -} + colorCircle2->setGradientHint( QskColorCircle::Circle, + { { { 0.0, Qt::red }, { 0.3333, Qt::green }, { 0.6666, Qt::blue }, { 1.0, Qt::red } } } ); -connect( colorWheel, &QskColorWheel::selectedColorChanged, colorTriangle, - [ colorTriangle ]( - const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); - -connect( colorCircle, &QskColorCircle::selectedColorChanged, colorTriangle, - [ colorTriangle ]( - const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); - -connect( colorWheelSlider, &QskSlider::valueChanged, this, [ q = colorWheel ]( qreal v ) { - auto metrics = q->arcMetricsHint( QskColorWheel::Arc ); - metrics.setThickness( v * qMin( q->width(), q->height() ) / 2 ); - q->setArcMetricsHint( QskColorWheel::Arc, metrics ); -} ); - -connect( colorTriangleSlider, &QskSlider::valueChanged, this, [ = ]( qreal v ) { - colorTriangle->setPositionHint( QskColorTriangle::Triangle, 2 * M_PI * v ); -} ); - -connect( - colorWheel, &QskColorWheel::selectedColorChanged, colorRectangle, [ = ]( const QColor& color ) { - colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); - colorRectangle->setCornerColor( Qt::TopRightCorner, color ); - colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); - colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); + layout->addItem( new QskTextLabel( "QskColorWheel" ), 0, 0 ); + layout->addItem( colorWheel, 1, 0 ); + layout->addItem( colorWheel2, 2, 0 ); + layout->addItem( colorWheelSlider, 3, 0 ); + layout->addItem( new QskTextLabel( "QskColorTriangle" ), 0, 1 ); + layout->addItem( colorTriangle, 1, 1 ); + layout->addItem( colorTriangle2, 2, 1 ); + layout->addItem( colorTriangleSlider, 3, 1 ); + layout->addItem( new QskTextLabel( "QskColorRectangle" ), 0, 2 ); + layout->addItem( colorRectangle, 1, 2 ); + layout->addItem( colorRectangle2, 2, 2 ); + layout->addItem( new QskTextLabel( "QskColorCircle" ), 0, 3 ); + layout->addItem( colorCircle, 1, 3 ); + layout->addItem( colorCircle2, 2, 3 ); + layout->addItem( new QskTextLabel( "QskAlphaSlider" ), 0, 4 ); + layout->addItem( alphaSlider, 1, 4 ); + + for ( int col = 0; col < layout->columnCount(); col++ ) + { + layout->setColumnStretchFactor( col, 1 ); + } + + connect( colorWheel, &QskColorWheel::selectedColorChanged, colorTriangle, + [ colorTriangle ]( + const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); + + connect( colorCircle, &QskColorCircle::selectedColorChanged, colorTriangle, + [ colorTriangle ]( + const QColor& color ) { colorTriangle->setColors( color, Qt::black, Qt::white ); } ); + + connect( colorWheelSlider, &QskSlider::valueChanged, this, [ q = colorWheel ]( qreal v ) { + auto metrics = q->arcMetricsHint( QskColorWheel::Arc ); + metrics.setThickness( v * qMin( q->width(), q->height() ) / 2 ); + q->setArcMetricsHint( QskColorWheel::Arc, metrics ); } ); -connect( colorCircle, &QskColorCircle::selectedColorChanged, colorRectangle, - [ = ]( const QColor& color ) { - colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); - colorRectangle->setCornerColor( Qt::TopRightCorner, color ); - colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); - colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); + connect( colorTriangleSlider, &QskSlider::valueChanged, this, [ = ]( qreal v ) { + colorTriangle->setPositionHint( QskColorTriangle::Triangle, 2 * M_PI * v ); } ); + + connect( colorWheel, &QskColorWheel::selectedColorChanged, colorRectangle, + [ = ]( const QColor& color ) { + colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); + colorRectangle->setCornerColor( Qt::TopRightCorner, color ); + colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); + colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); + } ); + + connect( colorCircle, &QskColorCircle::selectedColorChanged, colorRectangle, + [ = ]( const QColor& color ) { + colorRectangle->setCornerColor( Qt::TopLeftCorner, Qt::white ); + colorRectangle->setCornerColor( Qt::TopRightCorner, color ); + colorRectangle->setCornerColor( Qt::BottomLeftCorner, Qt::black ); + colorRectangle->setCornerColor( Qt::BottomRightCorner, Qt::black ); + } ); + + connect( colorCircle, &QskColorCircle::selectedColorChanged, alphaSlider, + [ = ]( const QColor& color ) { + QskGradient gradient{color, Qt::transparent}; + gradient.setLinearDirection(alphaSlider->orientation()); + alphaSlider->setGradientHint(QskAlphaSlider::Groove, gradient); + } ); } \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskAlphaSlider.cpp b/examples/gallery/colorpickers/QskAlphaSlider.cpp new file mode 100644 index 000000000..65ba4b260 --- /dev/null +++ b/examples/gallery/colorpickers/QskAlphaSlider.cpp @@ -0,0 +1,13 @@ +#include "QskAlphaSlider.h" + +QSK_SUBCONTROL( QskAlphaSlider, Grid ) + +QskAlphaSlider::QskAlphaSlider( QQuickItem* const parent ) + : QskSlider( parent ) +{ + setGradientHint( Grid, { "#B2B2B2", "#EEEEEE" } ); + setGradientHint( Fill, {}); + setMetric( Groove | QskAspect::Size, 16 ); + setMetric( Fill | QskAspect::Size, 16 ); + setMetric( Grid | QskAspect::Size, 4 ); +} \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskAlphaSlider.h b/examples/gallery/colorpickers/QskAlphaSlider.h new file mode 100644 index 000000000..f802ad8ec --- /dev/null +++ b/examples/gallery/colorpickers/QskAlphaSlider.h @@ -0,0 +1,17 @@ +#ifndef ALPHASLIDER_H_ +#define ALPHASLIDER_H_ + +#include "QskQuickItem.h" +#include + +class QskAlphaSlider : public QskSlider +{ + Q_OBJECT +public: + QSK_SUBCONTROLS(Grid) + QskAlphaSlider(QQuickItem* parent = nullptr); +}; + +#endif + + diff --git a/examples/gallery/colorpickers/QskAlphaSliderSkinlet.cpp b/examples/gallery/colorpickers/QskAlphaSliderSkinlet.cpp new file mode 100644 index 000000000..8801eff15 --- /dev/null +++ b/examples/gallery/colorpickers/QskAlphaSliderSkinlet.cpp @@ -0,0 +1,180 @@ +#include "QskAlphaSliderSkinlet.h" +#include "QskAlphaSlider.h" + +#include "QskSlider.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace +{ + class Material : public QSGMaterial + { + public: + QSGMaterialType* type() const override + { + static QSGMaterialType type; + return &type; + } + + QSGMaterialShader* createShader() const override; + + QColor m_colorA; + QColor m_colorB; + float m_gridSize; + }; + + class Shader : public QSGMaterialShader + { + public: + const char* vertexShader() const + { + return "attribute highp vec4 vertex; \n" + "uniform highp mat4 matrix; \n" + "void main() { \n" + " gl_Position = matrix * vertex; \n" + "}"; + } + + const char* fragmentShader() const + { + return "uniform lowp float opacity; \n" + "uniform lowp vec4 colorA; \n" + "uniform lowp vec4 colorB; \n" + "uniform lowp float size; \n" + " \n" + "vec4 color(in vec4 fragColor, in vec2 fragCoord, in float gridSize) \n" + "{ \n" + " vec2 pos = floor(fragCoord / gridSize); \n" + " float mask = mod(pos.x + mod(pos.y, 2.0), 2.0); \n" + " return mask * fragColor; \n" + "} \n" + " \n" + "void main() { \n" + " vec2 fragCoordA = gl_FragCoord.xy; \n" + " vec2 fragCoordB = gl_FragCoord.xy + vec2(size, 0.0); \n" + " gl_FragColor = color(colorA, fragCoordA, size) + \n" + " color(colorB, fragCoordB, size) * \n" + " opacity; \n" + "}"; + } + + char const* const* attributeNames() const + { + static char const* const names[] = { "vertex", 0 }; + return names; + } + + void initialize() + { + QSGMaterialShader::initialize(); + m_id_matrix = program()->uniformLocation( "matrix" ); + m_id_opacity = program()->uniformLocation( "opacity" ); + m_id_color_a = program()->uniformLocation( "colorA" ); + m_id_color_b = program()->uniformLocation( "colorB" ); + m_id_grid_size = program()->uniformLocation( "size" ); + } + + void updateState( + const RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) + { + Q_ASSERT( program()->isLinked() ); + + if ( state.isMatrixDirty() ) + { + program()->setUniformValue( m_id_matrix, state.combinedMatrix() ); + } + + if ( state.isOpacityDirty() ) + { + program()->setUniformValue( m_id_opacity, state.opacity() ); + } + + program()->setUniformValue( + m_id_color_a, static_cast< const Material* >( newMaterial )->m_colorA ); + program()->setUniformValue( + m_id_color_b, static_cast< const Material* >( newMaterial )->m_colorB ); + program()->setUniformValue( + m_id_grid_size, static_cast< const Material* >( newMaterial )->m_gridSize ); + } + + private: + int m_id_matrix; + int m_id_opacity; + int m_id_color_a; + int m_id_color_b; + int m_id_grid_size; + }; + + QSGMaterialShader* Material::createShader() const + { + return new Shader; + } + + class GridNode : public QSGGeometryNode + { + public: + GridNode() + : m_geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) + { + setGeometry( &m_geometry ); + setMaterial( &m_material ); + setFlag( QSGGeometryNode::OwnsGeometry, false ); + setFlag( QSGGeometryNode::OwnsMaterial, false ); + } + + void update( const QRectF& rect, const QskBoxShapeMetrics& shape, + const QskBoxBorderMetrics& border, const QColor& color1, const QColor& color2, + const float gridSize ) + { + QskBoxRenderer::renderFillGeometry( rect, shape, border, m_geometry ); + m_material.m_colorA = color1; + m_material.m_colorB = color2; + m_material.m_gridSize = gridSize; + + m_geometry.markVertexDataDirty(); + markDirty( QSGNode::DirtyMaterial ); + markDirty( QSGNode::DirtyGeometry ); + } + + private: + QSGGeometry m_geometry; + Material m_material; + }; +} + +using Q = QskAlphaSlider; + +QskAlphaSliderSkinlet::QskAlphaSliderSkinlet( QskSkin* skin ) + : Inherited( skin ) +{ + setNodeRoles( { PanelRole, GridRole, GrooveRole, FillRole, HandleRole, RippleRole } ); +} + +QSGNode* QskAlphaSliderSkinlet::updateSubNode( + const QskSkinnable* skinnable, const quint8 nodeRole, QSGNode* const node ) const +{ + if ( nodeRole == GridRole ) + { + const auto* const q = static_cast< const Q* >( skinnable ); + const auto rect = q->subControlRect( Q::Groove ); + const auto shape = q->boxShapeHint( Q::Groove ).toAbsolute( rect.size() ); + const auto border = q->boxBorderMetricsHint( Q::Groove ); + const auto gradient = q->gradientHint( Q::Grid ); + const auto gridSize = q->metric( Q::Grid | QskAspect::Size ); + + auto* const grid = QskSGNode::ensureNode< GridNode >( node ); + grid->update( rect, shape, border, gradient.startColor(), gradient.endColor(), gridSize ); + return grid; + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); +} \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskAlphaSliderSkinlet.h b/examples/gallery/colorpickers/QskAlphaSliderSkinlet.h new file mode 100644 index 000000000..99d5ae631 --- /dev/null +++ b/examples/gallery/colorpickers/QskAlphaSliderSkinlet.h @@ -0,0 +1,11 @@ +#include + +class QskAlphaSliderSkinlet : public QskSliderSkinlet +{ + using Inherited = QskSliderSkinlet; +public: + enum NodeRoles { GridRole = Inherited::RoleCount, RoleCount }; + Q_INVOKABLE QskAlphaSliderSkinlet(QskSkin* skin = nullptr); +protected: + QSGNode* updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node) const override; +}; \ No newline at end of file From 4aff9ee2e387f62fdecc929008181ec5da9ef3c1 Mon Sep 17 00:00:00 2001 From: Rick Vogel Date: Sat, 3 Feb 2024 18:30:55 +0100 Subject: [PATCH 9/9] restructure uniform ids --- .../gallery/colorpickers/QskAlphaSlider.cpp | 2 +- .../colorpickers/QskAlphaSliderSkinlet.cpp | 39 ++++++++++--------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/examples/gallery/colorpickers/QskAlphaSlider.cpp b/examples/gallery/colorpickers/QskAlphaSlider.cpp index 65ba4b260..bb87ea78a 100644 --- a/examples/gallery/colorpickers/QskAlphaSlider.cpp +++ b/examples/gallery/colorpickers/QskAlphaSlider.cpp @@ -9,5 +9,5 @@ QskAlphaSlider::QskAlphaSlider( QQuickItem* const parent ) setGradientHint( Fill, {}); setMetric( Groove | QskAspect::Size, 16 ); setMetric( Fill | QskAspect::Size, 16 ); - setMetric( Grid | QskAspect::Size, 4 ); + setMetric( Grid | QskAspect::Size, 8 ); } \ No newline at end of file diff --git a/examples/gallery/colorpickers/QskAlphaSliderSkinlet.cpp b/examples/gallery/colorpickers/QskAlphaSliderSkinlet.cpp index 8801eff15..c6a6721ae 100644 --- a/examples/gallery/colorpickers/QskAlphaSliderSkinlet.cpp +++ b/examples/gallery/colorpickers/QskAlphaSliderSkinlet.cpp @@ -76,11 +76,11 @@ namespace void initialize() { QSGMaterialShader::initialize(); - m_id_matrix = program()->uniformLocation( "matrix" ); - m_id_opacity = program()->uniformLocation( "opacity" ); - m_id_color_a = program()->uniformLocation( "colorA" ); - m_id_color_b = program()->uniformLocation( "colorB" ); - m_id_grid_size = program()->uniformLocation( "size" ); + m_id.matrix = program()->uniformLocation( "matrix" ); + m_id.opacity = program()->uniformLocation( "opacity" ); + m_id.color_a = program()->uniformLocation( "colorA" ); + m_id.color_b = program()->uniformLocation( "colorB" ); + m_id.grid_size = program()->uniformLocation( "size" ); } void updateState( @@ -90,28 +90,29 @@ namespace if ( state.isMatrixDirty() ) { - program()->setUniformValue( m_id_matrix, state.combinedMatrix() ); + program()->setUniformValue( m_id.matrix, state.combinedMatrix() ); } if ( state.isOpacityDirty() ) { - program()->setUniformValue( m_id_opacity, state.opacity() ); + program()->setUniformValue( m_id.opacity, state.opacity() ); } - program()->setUniformValue( - m_id_color_a, static_cast< const Material* >( newMaterial )->m_colorA ); - program()->setUniformValue( - m_id_color_b, static_cast< const Material* >( newMaterial )->m_colorB ); - program()->setUniformValue( - m_id_grid_size, static_cast< const Material* >( newMaterial )->m_gridSize ); + const auto* const material = static_cast< const Material* >( newMaterial ); + program()->setUniformValue( m_id.color_a, material->m_colorA ); + program()->setUniformValue( m_id.color_b, material->m_colorB ); + program()->setUniformValue( m_id.grid_size, material->m_gridSize ); } private: - int m_id_matrix; - int m_id_opacity; - int m_id_color_a; - int m_id_color_b; - int m_id_grid_size; + struct + { + int matrix = -1; + int opacity = -1; + int color_a = -1; + int color_b = -1; + int grid_size = -1; + } m_id; }; QSGMaterialShader* Material::createShader() const @@ -139,6 +140,8 @@ namespace m_material.m_colorA = color1; m_material.m_colorB = color2; m_material.m_gridSize = gridSize; + + // TODO update only if necessary m_geometry.markVertexDataDirty(); markDirty( QSGNode::DirtyMaterial );