Is there a color picker tool?

I’ve searched, no luck so far, just wondering if there is any kind of color picker for the C++ 3.16 or above?

If not, I can just let them type in something. Thanks.

There is not. (as far as I am aware)

There’s an old extension that may suffice, but might also require a bit of porting the code over to latests.

YMMV

1 Like

Thanks, Steve, I’ll take a look. I may just let the user enter RGB values, validate them, and show them the result. Thanks again.

1 Like

Here’s a quick search result for create your own with GL. Probably much better ones out there, but in the end it probably isn’t a ton of work (though not trivial).

Or you could choose your own palette of 8-32 colors and just create “button” squares of each color in a grid layout for a simpler version.

godspeed :wink:

2 Likes

Thanks! Hmmm, not a bad idea of providing them a pallette of button squares in a grid. I’ll think about that, I like it. So, a popup with a bunch of small buttons. I’ll see if I can do that soon.

Thanks again, great idea! And I’ll look for some other examples as well.

Hey, Steve, quick question. If I go with the grid approach, then he only way I know of picking up on mouse clicks is to make each square a button. Now, if I recall correctly, we’re limited to 127 buttons, is that right?

I know that’s plenty, just want to make sure I have it right. I’m thinking I’d like to offer as many colors as possible, so maybe 8x8 for 64 total, or something similar.

@dogwalker Have you considered using a color bar with a slider instead of buttons? The color bar would represent the Hue from the HSV color model, and then once the users slides it to the color they want to use, you just covert that HSV value to RGB. It will take much less space on the screen.

Alternatively, if you do opt to have separate color selections, then there is really no need to use buttons for it. Just have a plain white texture, let’s say 50x50 pixels, create sprites using that same texture, and call setColor() on those sprites to whichever colors you want. You can then set the touch end event handler to the same method for all of those sprites. Use event->getCurrentTarget() inside that handler, and cast the cocos2d::Node* to a Sprite*, and read the color value from it.

Edit: Corrected the section regarding how to get the node that sent the touch event.

Those are great suggestions, thanks! I don’t know how to do either one, so I’ll have to research them. Both sound really nice, especially the slider bar. Thanks again!

@dogwalker I use the color transformation methods in my code, and after some research these are the ones that worked out to be the best for me:

    // Refer to https://gist.github.com/mjackson/5311256 for the source of most of these implementations

    // Helper for HslToRgba
    float HueToRgb(float p, float q, float t)
    {
        if (t < 0.0f) t += 1.0f;
        if (t > 1.0f) t -= 1.0f;
        if (t < 1.0f / 6.0f) return p + (q - p) * 6.0f * t;
        if (t < 1.0f / 2.0f) return q;
        if (t < 2.0f / 3.0f) return p + (q - p) * (2.0f / 3.0f - t) * 6.0f;
        return p;
    }

    /// <summary>
    /// Converts an HSL color value to RGB.
    /// Input: 
    /// Output: Color ( R: [0, 255], G: [0, 255], B: [0, 255])
    /// </summary>
    /// <param name="hsl">Ranges [0, 1.0]</param>
    /// <returns>RGBA Color. Ranges [0, 255]</returns>
    cocos2d::Color3B HslToRgb(const cocos2d::Vec3& hsl)
    {
        float r, g, b;
        float h = hsl.x;
        float s = hsl.y;
        float l = hsl.z;

        if (s == 0.0f)
        {
            r = g = b = l;
        }
        else
        {
            auto q = (l < 0.5f) ? (l * (1.0f + s)) : (l + s) - (l * s);
            auto p = 2.0f * l - q;
            r = HueToRgb(p, q, h + 1.0f / 3.0f);
            g = HueToRgb(p, q, h);
            b = HueToRgb(p, q, h - 1.0f / 3.0f);
        }

        return { GLubyte(r * 255), GLubyte(g * 255), GLubyte(b * 255) };
    }

    /**
     * Converts an RGB color value to HSL. Conversion formula
     * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
     * Assumes r, g, and b are contained in the set [0, 255] and
     * returns h, s, and l in the set [0, 1].
     *
     * @param   {number}  r       The red color value
     * @param   {number}  g       The green color value
     * @param   {number}  b       The blue color value
     * @return  {Array}           The HSL representation
     */
    cocos2d::Vec3 RgbToHsl(const cocos2d::Color3B& color)
    {
        float r = color.r / 255.f, g = color.g / 255.f, b = color.b / 255.f;
        auto max = maximum(r, g, b);
        auto min = minimum(r, g, b);
        float h;
        float s;
        float l = (max + min) / 2.f;

        if (max == min) {
            h = s = 0; // achromatic
        }
        else 
        {
            auto d = max - min;
            s = l > 0.5f ? d / (2 - max - min) : d / (max + min);

            if (max == r)
            {
                h = (g - b) / d + (g < b ? 6 : 0);
            }
            else if (max == g)
            {
                h = (b - r) / d + 2;
            }
            else // max == b
            {
                h = (r - g) / d + 4;
            }

            h /= 6.f;
        }

        return cocos2d::Vec3(h, s, l);
    }

    /**
     * Converts an RGB color value to HSV. Conversion formula
     * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
     * Assumes r, g, and b are contained in the set [0, 255] and
     * returns h, s, and v in the set [0, 1].
     *
     * @param   Number  r       The red color value
     * @param   Number  g       The green color value
     * @param   Number  b       The blue color value
     * @return  Array           The HSV representation
     */
    cocos2d::Vec3 RgbToHsv(const cocos2d::Color3B& color)
    {
        float r = color.r / 255.f, g = color.g / 255.f, b = color.b / 255.f;
        auto max = maximum(r, g, b);
        auto min = minimum(r, g, b);
        float h;
        float s;
        float v = max;

        auto d = max - min;
        s = v == 0 ? 0 : d / max;

        if (max == min) {
            h = 0; // achromatic
        }
        else
        {
            if (max == r)
            {
                h = (g - b) / d;
            }
            else if (max == g)
            {
                h = (b - r) / d + 2;
            }
            else // max == b
            {
                h = (r - g) / d + 4;
            }

            h *= 60.f;

            if (h < 0)
            {
                h += 360.f;
            }
        }

        return cocos2d::Vec3(h, s, v);
    }

    // Check https://gist.github.com/mjackson/5311256  & https://gist.github.com/yoggy/8999625
    cocos2d::Color3B HsvToRgb(const cocos2d::Vec3& hsv)
    {
        float h = hsv.x; // 0-360
        float s = hsv.y; // 0.0-1.0
        float v = hsv.z; // 0.0-1.0

        float r, g, b; // 0.0-1.0

        int   hi = int(h / 60.0f) % 6;
        float f = (h / 60.0f) - hi;
        float p = v * (1.0f - s);
        float q = v * (1.0f - s * f);
        float t = v * (1.0f - s * (1.0f - f));

        switch (hi) 
        {
        case 0: r = v, g = t, b = p; break;
        case 1: r = q, g = v, b = p; break;
        case 2: r = p, g = v, b = t; break;
        case 3: r = p, g = q, b = v; break;
        case 4: r = t, g = p, b = v; break;
        case 5: r = v, g = p, b = q; break;
        default: r = 0, g = 0, b = 0; break;
        }

        return cocos2d::Color3B((GLubyte)(r * 255), (GLubyte)(g * 255), (GLubyte)(b * 255));
    }

Thanks for sharing! I’ll take a look this weekend, that would be great to get done. Thanks again!

Ah, I reread your comments about the touch handlers. I think I’ve done that before, I’m going to dig around. Thanks!

@dogwalker Many different ways to do it, but this is something similar to what I use:

auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true); // If you don't want the touch to pass through the object, then make sure this is set to true.

listener->onTouchBegan = [this](Touch* touch, Event* event) {
    // event->getCurrentTarget() returns the *listener's* sceneGraphPriority node.
    const auto target = static_cast<Sprite*>(event->getCurrentTarget());

    //Get the position of the current point relative to the button
    const auto locationInNode = target->convertToNodeSpace(touch->getLocation());
    const auto s = target->getContentSize();
    auto rect = cocos2d::Rect(0, 0, s.width, s.height);

    // Check the click area, and make sure it's within the correct bounds
    if (rect.containsPoint(locationInNode))
    {
        return true;
    }
    return false;
};
listener->onTouchMoved = [this](Touch* touch, Event* event) {

};
listener->onTouchEnded = [this](Touch* touch, Event* event) {
    // event->getCurrentTarget() returns the *listener's* sceneGraphPriority node.
    auto target = static_cast<Sprite*>(event->getCurrentTarget());
    if (target->getTag() == 1234)
    {
        // It's a valid color selection sprite, so read it's color value
        auto color = target->getColor();
        // ....use the color...
    }
};
listener->onTouchCancelled = [this](Touch* touch, Event* event) {

};

auto sprite1 = Sprite::create(...);
sprite1->setTag(1234); // Just as an example

// Register the event listener for each sprite:
getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, sprite1);
getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener->clone(), sprite2);
getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener->clone(), sprite3);
getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener->clone(), sprite4);

2 Likes

Thanks so much for sharing! I’m going to work on the grid of sprites as a start. I have an option where they can type in a hex number, but a few people have looked at it and said that’s not friendly enough. The grid of sprites sounds like an excellent start. I’ll let you know.

[Update] I just created a small grid as a proof of concept, using your suggestions and example code, and it works great! Thanks so very much!