New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color picker #346

Closed
nem0 opened this Issue Sep 22, 2015 · 105 comments

Comments

@nem0
Contributor

nem0 commented Sep 22, 2015

(ADMIN EDIT): COLOR PICKING TOOLS ARE NOW INCLUDED IN IMGUI. From version 1.51 (Aug 2017), ColorEdit3/ColorEdit4 wll allow you to open a picker by clicking on the colored square. Also added right-mouse click to open option. And you can call ColorPicker4 functions to directly embed a picker with custom options in your app. Read the release note and check the demo code.

I've implemented advanced color picker, maybe somebody find this useful:

color_picker

    void ImDrawList::AddTriangleFilledMultiColor(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col_a, ImU32 col_b, ImU32 col_c)
    {
        if (((col_a | col_b | col_c) >> 24) == 0)
            return;

        const ImVec2 uv = GImGui->FontTexUvWhitePixel;
        PrimReserve(3, 3);
        PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2));
        PrimWriteVtx(a, uv, col_a);
        PrimWriteVtx(b, uv, col_b);
        PrimWriteVtx(c, uv, col_c);
    }

    bool ColorPicker(const char* label, ImColor* color)
    {
        static const float HUE_PICKER_WIDTH = 20.0f;
        static const float CROSSHAIR_SIZE = 7.0f;
        static const ImVec2 SV_PICKER_SIZE = ImVec2(200, 200);

        bool value_changed = false;

        ImDrawList* draw_list = ImGui::GetWindowDrawList();

        ImVec2 picker_pos = ImGui::GetCursorScreenPos();

        ImColor colors[] = {ImColor(255, 0, 0),
            ImColor(255, 255, 0),
            ImColor(0, 255, 0),
            ImColor(0, 255, 255),
            ImColor(0, 0, 255),
            ImColor(255, 0, 255),
            ImColor(255, 0, 0)};

        for (int i = 0; i < 6; ++i)
        {
            draw_list->AddRectFilledMultiColor(
                ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y + i * (SV_PICKER_SIZE.y / 6)),
                ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10 + HUE_PICKER_WIDTH,
                    picker_pos.y + (i + 1) * (SV_PICKER_SIZE.y / 6)),
                colors[i],
                colors[i],
                colors[i + 1],
                colors[i + 1]);
        }

        float hue, saturation, value;
        ImGui::ColorConvertRGBtoHSV(
            color->Value.x, color->Value.y, color->Value.z, hue, saturation, value);
        auto hue_color = ImColor::HSV(hue, 1, 1);

        draw_list->AddLine(
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 8, picker_pos.y + hue * SV_PICKER_SIZE.y),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 12 + HUE_PICKER_WIDTH,
                picker_pos.y + hue * SV_PICKER_SIZE.y),
            ImColor(255, 255, 255));

        draw_list->AddTriangleFilledMultiColor(picker_pos,
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x, picker_pos.y + SV_PICKER_SIZE.y),
            ImVec2(picker_pos.x, picker_pos.y + SV_PICKER_SIZE.y),
            ImColor(0, 0, 0),
            hue_color,
            ImColor(255, 255, 255));

        float x = saturation * value;
        ImVec2 p(picker_pos.x + x * SV_PICKER_SIZE.x, picker_pos.y + value * SV_PICKER_SIZE.y);
        draw_list->AddLine(ImVec2(p.x - CROSSHAIR_SIZE, p.y), ImVec2(p.x - 2, p.y), ImColor(255, 255, 255));
        draw_list->AddLine(ImVec2(p.x + CROSSHAIR_SIZE, p.y), ImVec2(p.x + 2, p.y), ImColor(255, 255, 255));
        draw_list->AddLine(ImVec2(p.x, p.y + CROSSHAIR_SIZE), ImVec2(p.x, p.y + 2), ImColor(255, 255, 255));
        draw_list->AddLine(ImVec2(p.x, p.y - CROSSHAIR_SIZE), ImVec2(p.x, p.y - 2), ImColor(255, 255, 255));

        ImGui::InvisibleButton("saturation_value_selector", SV_PICKER_SIZE);
        if (ImGui::IsItemHovered())
        {
            ImVec2 mouse_pos_in_canvas = ImVec2(
                ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);
            if (ImGui::GetIO().MouseDown[0])
            {
                mouse_pos_in_canvas.x =
                    ImMin(mouse_pos_in_canvas.x, mouse_pos_in_canvas.y);

                value = mouse_pos_in_canvas.y / SV_PICKER_SIZE.y;
                saturation = value == 0 ? 0 : (mouse_pos_in_canvas.x / SV_PICKER_SIZE.x) / value;
                value_changed = true;
            }
        }

        ImGui::SetCursorScreenPos(ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y));
        ImGui::InvisibleButton("hue_selector", ImVec2(HUE_PICKER_WIDTH, SV_PICKER_SIZE.y));

        if (ImGui::IsItemHovered())
        {
            if (ImGui::GetIO().MouseDown[0])
            {
                hue = ((ImGui::GetIO().MousePos.y - picker_pos.y) / SV_PICKER_SIZE.y);
                value_changed = true;
            }
        }

        *color = ImColor::HSV(hue, saturation, value);
        return value_changed | ImGui::ColorEdit3(label, &color->Value.x);
    }
@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Sep 22, 2015

Owner

Thanks. Really useful!

I've been meaning to add a proper color picker by default in ImGui but haven't got around to do one with a proper feature set.

There's this one:
https://twitter.com/ApoorvaJ/status/644452534917009408
cpgokhhuyaaddyo png large
From https://github.com/ApoorvaJ/Papaya

There's this one
color picker - copy
https://github.com/benoitjacquier/imgui

We could probably combine some of those.

Micko's old imgui using nanovg also have a better color picker:
cmftstudio_win4-picker

That I wanted to replicate but haven't got around to do it yet.

Owner

ocornut commented Sep 22, 2015

Thanks. Really useful!

I've been meaning to add a proper color picker by default in ImGui but haven't got around to do one with a proper feature set.

There's this one:
https://twitter.com/ApoorvaJ/status/644452534917009408
cpgokhhuyaaddyo png large
From https://github.com/ApoorvaJ/Papaya

There's this one
color picker - copy
https://github.com/benoitjacquier/imgui

We could probably combine some of those.

Micko's old imgui using nanovg also have a better color picker:
cmftstudio_win4-picker

That I wanted to replicate but haven't got around to do it yet.

@nem0

This comment has been minimized.

Show comment
Hide comment
@nem0

nem0 Sep 22, 2015

Contributor

I thought I'd seen a task for this somewhere, however I was not able to find it again.

If I understand Papaya uses specific shader for the color picker. In general it is not possible to do the picker as in the first and second example with default shader or without runtime generated texture or huge amount of polygons. Mikko's solution is possible, it's basically the same as mine, only the hue selector has different shape

Contributor

nem0 commented Sep 22, 2015

I thought I'd seen a task for this somewhere, however I was not able to find it again.

If I understand Papaya uses specific shader for the color picker. In general it is not possible to do the picker as in the first and second example with default shader or without runtime generated texture or huge amount of polygons. Mikko's solution is possible, it's basically the same as mine, only the hue selector has different shape

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Sep 23, 2015

Owner

The second one didn't use a huge amount of polygon asap, it's using multiple layers with transparency. Unfortunately the code itself is pretty huge and unbearable for what it does so I'll probably prefer to start from your base. At least we could improve it and ship it as an Example first before it gets stable enough to be promoted as an API thing.

Owner

ocornut commented Sep 23, 2015

The second one didn't use a huge amount of polygon asap, it's using multiple layers with transparency. Unfortunately the code itself is pretty huge and unbearable for what it does so I'll probably prefer to start from your base. At least we could improve it and ship it as an Example first before it gets stable enough to be promoted as an API thing.

@WearyWanderer

This comment has been minimized.

Show comment
Hide comment
@WearyWanderer

WearyWanderer Sep 23, 2015

These are awesome, thanks for posting. I particularly like the last one you posted @ocornut, might work on trying to implement one. If I get one done I'll post it for people.

WearyWanderer commented Sep 23, 2015

These are awesome, thanks for posting. I particularly like the last one you posted @ocornut, might work on trying to implement one. If I get one done I'll post it for people.

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 15, 2016

Hey guys,

I merged @nem0's and @benoitjacquier's code.

The resulting snippet not as big as @benoitjacquier's, but still embeddable, standalone and does not require a new primitive (ImDrawList::AddTriangleFilledMultiColor) anymore.

image

I have improved the color picker to capture out-of-bounds hovers as well, as featured in the video below.

video

// [src] https://github.com/ocornut/imgui/issues/346

#include <imgui.h>

bool ColorPicker(const char* label, float col[3])
{
    static const float HUE_PICKER_WIDTH = 20.0f;
    static const float CROSSHAIR_SIZE = 7.0f;
    static const ImVec2 SV_PICKER_SIZE = ImVec2(200, 200);

    ImColor color(col[0], col[1], col[2]);
    bool value_changed = false;

    ImDrawList* draw_list = ImGui::GetWindowDrawList();

    ImVec2 picker_pos = ImGui::GetCursorScreenPos();

    ImColor colors[] = { ImColor(255, 0, 0),
        ImColor(255, 255, 0),
        ImColor(0, 255, 0),
        ImColor(0, 255, 255),
        ImColor(0, 0, 255),
        ImColor(255, 0, 255),
        ImColor(255, 0, 0) };

    for (int i = 0; i < 6; ++i)
    {
        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y + i * (SV_PICKER_SIZE.y / 6)),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10 + HUE_PICKER_WIDTH,
            picker_pos.y + (i + 1) * (SV_PICKER_SIZE.y / 6)),
            colors[i],
            colors[i],
            colors[i + 1],
            colors[i + 1]);
    }

    float hue, saturation, value;
    ImGui::ColorConvertRGBtoHSV(
        color.Value.x, color.Value.y, color.Value.z, hue, saturation, value);

    draw_list->AddLine(
        ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 8, picker_pos.y + hue * SV_PICKER_SIZE.y),
        ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 12 + HUE_PICKER_WIDTH, picker_pos.y + hue * SV_PICKER_SIZE.y),
        ImColor(255, 255, 255));

    {
        const int step = 5;
        ImVec2 pos = ImVec2(0, 0);

        ImVec4 c00(1, 1, 1, 1);
        ImVec4 c10(1, 1, 1, 1);
        ImVec4 c01(1, 1, 1, 1);
        ImVec4 c11(1, 1, 1, 1);
        for (int y = 0; y < step; y++) {
            for (int x = 0; x < step; x++) {
                float s0 = (float)x / (float)step;
                float s1 = (float)(x + 1) / (float)step;
                float v0 = 1.0 - (float)(y) / (float)step;
                float v1 = 1.0 - (float)(y + 1) / (float)step;

                ImGui::ColorConvertHSVtoRGB(hue, s0, v0, c00.x, c00.y, c00.z);
                ImGui::ColorConvertHSVtoRGB(hue, s1, v0, c10.x, c10.y, c10.z);
                ImGui::ColorConvertHSVtoRGB(hue, s0, v1, c01.x, c01.y, c01.z);
                ImGui::ColorConvertHSVtoRGB(hue, s1, v1, c11.x, c11.y, c11.z);

                draw_list->AddRectFilledMultiColor(
                    ImVec2(picker_pos.x + pos.x, picker_pos.y + pos.y), 
                    ImVec2(picker_pos.x + pos.x + SV_PICKER_SIZE.x / step, picker_pos.y + pos.y + SV_PICKER_SIZE.y / step),
                    ImGui::ColorConvertFloat4ToU32(c00),
                    ImGui::ColorConvertFloat4ToU32(c10),
                    ImGui::ColorConvertFloat4ToU32(c11),
                    ImGui::ColorConvertFloat4ToU32(c01));

                pos.x += SV_PICKER_SIZE.x / step;
            }
            pos.x = 0;
            pos.y += SV_PICKER_SIZE.y / step;
        }
    }

    float x = saturation * SV_PICKER_SIZE.x;
    float y = (1 -value) * SV_PICKER_SIZE.y;
    ImVec2 p(picker_pos.x + x, picker_pos.y + y);
    draw_list->AddLine(ImVec2(p.x - CROSSHAIR_SIZE, p.y), ImVec2(p.x - 2, p.y), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x + CROSSHAIR_SIZE, p.y), ImVec2(p.x + 2, p.y), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x, p.y + CROSSHAIR_SIZE), ImVec2(p.x, p.y + 2), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x, p.y - CROSSHAIR_SIZE), ImVec2(p.x, p.y - 2), ImColor(255, 255, 255));

    ImGui::InvisibleButton("saturation_value_selector", SV_PICKER_SIZE);

    if (ImGui::IsItemActive() && ImGui::GetIO().MouseDown[0])
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /**/ if( mouse_pos_in_canvas.x <                     0 ) mouse_pos_in_canvas.x = 0;
        else if( mouse_pos_in_canvas.x >= SV_PICKER_SIZE.x - 1 ) mouse_pos_in_canvas.x = SV_PICKER_SIZE.x - 1;

        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 1 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 1;

        value = 1 - (mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1));
        saturation = mouse_pos_in_canvas.x / (SV_PICKER_SIZE.x - 1);
        value_changed = true;
    }

    ImGui::SetCursorScreenPos(ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y));
    ImGui::InvisibleButton("hue_selector", ImVec2(HUE_PICKER_WIDTH, SV_PICKER_SIZE.y));

    if( (ImGui::IsItemHovered()||ImGui::IsItemActive()) && ImGui::GetIO().MouseDown[0])
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /* Previous horizontal bar will represent hue=1 (bottom) as hue=0 (top). Since both colors are red, we clamp at (-2, above edge) to avoid visual continuities */
        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 2 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 2;

        hue = mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1 );
        value_changed = true;
    }

    color = ImColor::HSV(hue > 0 ? hue : 1e-6, saturation > 0 ? saturation : 1e-6, value > 0 ? value : 1e-6);
    col[0] = color.Value.x;
    col[1] = color.Value.y;
    col[2] = color.Value.z;
    return value_changed | ImGui::ColorEdit3(label, col);
}

r-lyeh-archived commented Jan 15, 2016

Hey guys,

I merged @nem0's and @benoitjacquier's code.

The resulting snippet not as big as @benoitjacquier's, but still embeddable, standalone and does not require a new primitive (ImDrawList::AddTriangleFilledMultiColor) anymore.

image

I have improved the color picker to capture out-of-bounds hovers as well, as featured in the video below.

video

// [src] https://github.com/ocornut/imgui/issues/346

#include <imgui.h>

bool ColorPicker(const char* label, float col[3])
{
    static const float HUE_PICKER_WIDTH = 20.0f;
    static const float CROSSHAIR_SIZE = 7.0f;
    static const ImVec2 SV_PICKER_SIZE = ImVec2(200, 200);

    ImColor color(col[0], col[1], col[2]);
    bool value_changed = false;

    ImDrawList* draw_list = ImGui::GetWindowDrawList();

    ImVec2 picker_pos = ImGui::GetCursorScreenPos();

    ImColor colors[] = { ImColor(255, 0, 0),
        ImColor(255, 255, 0),
        ImColor(0, 255, 0),
        ImColor(0, 255, 255),
        ImColor(0, 0, 255),
        ImColor(255, 0, 255),
        ImColor(255, 0, 0) };

    for (int i = 0; i < 6; ++i)
    {
        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y + i * (SV_PICKER_SIZE.y / 6)),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10 + HUE_PICKER_WIDTH,
            picker_pos.y + (i + 1) * (SV_PICKER_SIZE.y / 6)),
            colors[i],
            colors[i],
            colors[i + 1],
            colors[i + 1]);
    }

    float hue, saturation, value;
    ImGui::ColorConvertRGBtoHSV(
        color.Value.x, color.Value.y, color.Value.z, hue, saturation, value);

    draw_list->AddLine(
        ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 8, picker_pos.y + hue * SV_PICKER_SIZE.y),
        ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 12 + HUE_PICKER_WIDTH, picker_pos.y + hue * SV_PICKER_SIZE.y),
        ImColor(255, 255, 255));

    {
        const int step = 5;
        ImVec2 pos = ImVec2(0, 0);

        ImVec4 c00(1, 1, 1, 1);
        ImVec4 c10(1, 1, 1, 1);
        ImVec4 c01(1, 1, 1, 1);
        ImVec4 c11(1, 1, 1, 1);
        for (int y = 0; y < step; y++) {
            for (int x = 0; x < step; x++) {
                float s0 = (float)x / (float)step;
                float s1 = (float)(x + 1) / (float)step;
                float v0 = 1.0 - (float)(y) / (float)step;
                float v1 = 1.0 - (float)(y + 1) / (float)step;

                ImGui::ColorConvertHSVtoRGB(hue, s0, v0, c00.x, c00.y, c00.z);
                ImGui::ColorConvertHSVtoRGB(hue, s1, v0, c10.x, c10.y, c10.z);
                ImGui::ColorConvertHSVtoRGB(hue, s0, v1, c01.x, c01.y, c01.z);
                ImGui::ColorConvertHSVtoRGB(hue, s1, v1, c11.x, c11.y, c11.z);

                draw_list->AddRectFilledMultiColor(
                    ImVec2(picker_pos.x + pos.x, picker_pos.y + pos.y), 
                    ImVec2(picker_pos.x + pos.x + SV_PICKER_SIZE.x / step, picker_pos.y + pos.y + SV_PICKER_SIZE.y / step),
                    ImGui::ColorConvertFloat4ToU32(c00),
                    ImGui::ColorConvertFloat4ToU32(c10),
                    ImGui::ColorConvertFloat4ToU32(c11),
                    ImGui::ColorConvertFloat4ToU32(c01));

                pos.x += SV_PICKER_SIZE.x / step;
            }
            pos.x = 0;
            pos.y += SV_PICKER_SIZE.y / step;
        }
    }

    float x = saturation * SV_PICKER_SIZE.x;
    float y = (1 -value) * SV_PICKER_SIZE.y;
    ImVec2 p(picker_pos.x + x, picker_pos.y + y);
    draw_list->AddLine(ImVec2(p.x - CROSSHAIR_SIZE, p.y), ImVec2(p.x - 2, p.y), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x + CROSSHAIR_SIZE, p.y), ImVec2(p.x + 2, p.y), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x, p.y + CROSSHAIR_SIZE), ImVec2(p.x, p.y + 2), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x, p.y - CROSSHAIR_SIZE), ImVec2(p.x, p.y - 2), ImColor(255, 255, 255));

    ImGui::InvisibleButton("saturation_value_selector", SV_PICKER_SIZE);

    if (ImGui::IsItemActive() && ImGui::GetIO().MouseDown[0])
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /**/ if( mouse_pos_in_canvas.x <                     0 ) mouse_pos_in_canvas.x = 0;
        else if( mouse_pos_in_canvas.x >= SV_PICKER_SIZE.x - 1 ) mouse_pos_in_canvas.x = SV_PICKER_SIZE.x - 1;

        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 1 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 1;

        value = 1 - (mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1));
        saturation = mouse_pos_in_canvas.x / (SV_PICKER_SIZE.x - 1);
        value_changed = true;
    }

    ImGui::SetCursorScreenPos(ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 10, picker_pos.y));
    ImGui::InvisibleButton("hue_selector", ImVec2(HUE_PICKER_WIDTH, SV_PICKER_SIZE.y));

    if( (ImGui::IsItemHovered()||ImGui::IsItemActive()) && ImGui::GetIO().MouseDown[0])
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /* Previous horizontal bar will represent hue=1 (bottom) as hue=0 (top). Since both colors are red, we clamp at (-2, above edge) to avoid visual continuities */
        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 2 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 2;

        hue = mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1 );
        value_changed = true;
    }

    color = ImColor::HSV(hue > 0 ? hue : 1e-6, saturation > 0 ? saturation : 1e-6, value > 0 ? value : 1e-6);
    col[0] = color.Value.x;
    col[1] = color.Value.y;
    col[2] = color.Value.z;
    return value_changed | ImGui::ColorEdit3(label, col);
}
@nem0

This comment has been minimized.

Show comment
Hide comment
@nem0

nem0 Jan 15, 2016

Contributor

👍

is the SV picker exact?

Contributor

nem0 commented Jan 15, 2016

👍

is the SV picker exact?

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 15, 2016

should be. it would be nice to have a H,S,V triad below those R,G,B numbers to debug & confirm

r-lyeh-archived commented Jan 15, 2016

should be. it would be nice to have a H,S,V triad below those R,G,B numbers to debug & confirm

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jan 15, 2016

Owner

Superb! My small feedback from looking at the video is that you could align the R/G/B+Square at the bottom to be the same width as the main frame, e.g. using PushItemWidth(), and perhaps in this context it would make more sense to not have a label visible and save the horizontal space all together.

Ideally it would be able to interact with the default ColorEdit3/4() widget as well:

  • perhaps ColorEdit3/4 can be expanded with a small button and show the full thing.
  • or clicking on the colored square open a popup with this picker :)

I'm sorry I've got so much to catch on with ImGui, been working often 7 days a week already and finding it tough to sit down (though I've made notable progress on a few branch of work, but not color picker, so thanks all for posting your stuff here!).

Owner

ocornut commented Jan 15, 2016

Superb! My small feedback from looking at the video is that you could align the R/G/B+Square at the bottom to be the same width as the main frame, e.g. using PushItemWidth(), and perhaps in this context it would make more sense to not have a label visible and save the horizontal space all together.

Ideally it would be able to interact with the default ColorEdit3/4() widget as well:

  • perhaps ColorEdit3/4 can be expanded with a small button and show the full thing.
  • or clicking on the colored square open a popup with this picker :)

I'm sorry I've got so much to catch on with ImGui, been working often 7 days a week already and finding it tough to sit down (though I've made notable progress on a few branch of work, but not color picker, so thanks all for posting your stuff here!).

@nem0

This comment has been minimized.

Show comment
Hide comment
@nem0

nem0 Jan 15, 2016

Contributor

I mean this

                ImGui::ColorConvertHSVtoRGB(hue, s0, v0, c00.x, c00.y, c00.z);
                ImGui::ColorConvertHSVtoRGB(hue, s1, v0, c10.x, c10.y, c10.z);
                ImGui::ColorConvertHSVtoRGB(hue, s0, v1, c01.x, c01.y, c01.z);
                ImGui::ColorConvertHSVtoRGB(hue, s1, v1, c11.x, c11.y, c11.z);

                draw_list->AddRectFilledMultiColor(
                    ImVec2(picker_pos.x + pos.x, picker_pos.y + pos.y), 
                    ImVec2(picker_pos.x + pos.x + SV_PICKER_SIZE.x / step, picker_pos.y + pos.y + SV_PICKER_SIZE.y / step),
                    ImGui::ColorConvertFloat4ToU32(c00),
                    ImGui::ColorConvertFloat4ToU32(c10),
                    ImGui::ColorConvertFloat4ToU32(c11),
                    ImGui::ColorConvertFloat4ToU32(c01));

Interpolating HSV in RGB space - the difference is very visible when the big square is made from only two triangles

Contributor

nem0 commented Jan 15, 2016

I mean this

                ImGui::ColorConvertHSVtoRGB(hue, s0, v0, c00.x, c00.y, c00.z);
                ImGui::ColorConvertHSVtoRGB(hue, s1, v0, c10.x, c10.y, c10.z);
                ImGui::ColorConvertHSVtoRGB(hue, s0, v1, c01.x, c01.y, c01.z);
                ImGui::ColorConvertHSVtoRGB(hue, s1, v1, c11.x, c11.y, c11.z);

                draw_list->AddRectFilledMultiColor(
                    ImVec2(picker_pos.x + pos.x, picker_pos.y + pos.y), 
                    ImVec2(picker_pos.x + pos.x + SV_PICKER_SIZE.x / step, picker_pos.y + pos.y + SV_PICKER_SIZE.y / step),
                    ImGui::ColorConvertFloat4ToU32(c00),
                    ImGui::ColorConvertFloat4ToU32(c10),
                    ImGui::ColorConvertFloat4ToU32(c11),
                    ImGui::ColorConvertFloat4ToU32(c01));

Interpolating HSV in RGB space - the difference is very visible when the big square is made from only two triangles

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 15, 2016

@nem0

Ah yup, it is not that exact. I also experienced that while building the widget before.
@benoitjacquier "fixed" the canvas picker by rendering many small squares (step size=5 px wide, customizable in src), which I found an elegant workaround considering the limitations.

I think it is overall smooth enough, though. I cannot spot very big issues when zooming, so it is ok for me as it is :)

image

@ocornut
yep that would be cool. I think another sidebar going from black to white to define alpha would be a nice addition as well.

r-lyeh-archived commented Jan 15, 2016

@nem0

Ah yup, it is not that exact. I also experienced that while building the widget before.
@benoitjacquier "fixed" the canvas picker by rendering many small squares (step size=5 px wide, customizable in src), which I found an elegant workaround considering the limitations.

I think it is overall smooth enough, though. I cannot spot very big issues when zooming, so it is ok for me as it is :)

image

@ocornut
yep that would be cool. I think another sidebar going from black to white to define alpha would be a nice addition as well.

@thennequin

This comment has been minimized.

Show comment
Hide comment
@thennequin

thennequin Jan 15, 2016

Another simple way (2 drawcalls), draw one quad with horizontal gradient from white to hue color and an other quad over the first with vertical gradient from black to transparant black.

const ImU32 c_oColorBlack = ImGui::ColorConvertFloat4ToU32(ImVec4(0.f,0.f,0.f,1.f));
const ImU32 c_oColorBlackTransparent = ImGui::ColorConvertFloat4ToU32(ImVec4(0.f,0.f,0.f,0.f));
const ImU32 c_oColorWhite = ImGui::ColorConvertFloat4ToU32(ImVec4(1.f,1.f,1.f,1.f));

ImVec4 cHueValue(1, 1, 1, 1);
ImGui::ColorConvertHSVtoRGB(hue, 1, 1, cHueValue.x, cHueValue.y, cHueValue.z);
ImU32 oHueColor = ImGui::ColorConvertFloat4ToU32(cHueValue);

ImVec2 oSaturationAreaMin /*= ImGui::GetItemRectMin()*/;
ImVec2 oSaturationAreaMax /*= ImGui::GetItemRectMax()*/;

pDrawList->AddRectFilledMultiColor(
    oSaturationAreaMin,
    oSaturationAreaMax,
    c_oColorWhite,
    oHueColor,
    oHueColor,
    c_oColorWhite
    );

pDrawList->AddRectFilledMultiColor(
    oSaturationAreaMin,
    oSaturationAreaMax,
    c_oColorBlackTransparent,
    c_oColorBlackTransparent,
    c_oColorBlack,
    c_oColorBlack
    );

thennequin commented Jan 15, 2016

Another simple way (2 drawcalls), draw one quad with horizontal gradient from white to hue color and an other quad over the first with vertical gradient from black to transparant black.

const ImU32 c_oColorBlack = ImGui::ColorConvertFloat4ToU32(ImVec4(0.f,0.f,0.f,1.f));
const ImU32 c_oColorBlackTransparent = ImGui::ColorConvertFloat4ToU32(ImVec4(0.f,0.f,0.f,0.f));
const ImU32 c_oColorWhite = ImGui::ColorConvertFloat4ToU32(ImVec4(1.f,1.f,1.f,1.f));

ImVec4 cHueValue(1, 1, 1, 1);
ImGui::ColorConvertHSVtoRGB(hue, 1, 1, cHueValue.x, cHueValue.y, cHueValue.z);
ImU32 oHueColor = ImGui::ColorConvertFloat4ToU32(cHueValue);

ImVec2 oSaturationAreaMin /*= ImGui::GetItemRectMin()*/;
ImVec2 oSaturationAreaMax /*= ImGui::GetItemRectMax()*/;

pDrawList->AddRectFilledMultiColor(
    oSaturationAreaMin,
    oSaturationAreaMax,
    c_oColorWhite,
    oHueColor,
    oHueColor,
    c_oColorWhite
    );

pDrawList->AddRectFilledMultiColor(
    oSaturationAreaMin,
    oSaturationAreaMax,
    c_oColorBlackTransparent,
    c_oColorBlackTransparent,
    c_oColorBlack,
    c_oColorBlack
    );
@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 15, 2016

aha very cool :) will try asap
i am fixing the align issues and adding an alpha bar as well.

Uploading image.png…

r-lyeh-archived commented Jan 15, 2016

aha very cool :) will try asap
i am fixing the align issues and adding an alpha bar as well.

Uploading image.png…

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 16, 2016

ok, v2
thanks for the suggestions guys

image

// [src] https://github.com/ocornut/imgui/issues/346
// v2

#include <imgui.h>

static bool ColorPicker( float *col, bool alphabar )
{
    const int    EDGE_SIZE = 200; // = int( ImGui::GetWindowWidth() * 0.75f );
    const ImVec2 SV_PICKER_SIZE = ImVec2(EDGE_SIZE, EDGE_SIZE);
    const float  SPACING = ImGui::GetStyle().ItemInnerSpacing.x;
    const float  HUE_PICKER_WIDTH = 20.f;
    const float  CROSSHAIR_SIZE = 7.0f;

    ImColor color(col[0], col[1], col[2]);
    bool value_changed = false;

    ImDrawList* draw_list = ImGui::GetWindowDrawList();

    // setup

    ImVec2 picker_pos = ImGui::GetCursorScreenPos();

    float hue, saturation, value;
    ImGui::ColorConvertRGBtoHSV(
        color.Value.x, color.Value.y, color.Value.z, hue, saturation, value);

    // draw hue bar

    ImColor colors[] = { ImColor(255, 0, 0),
        ImColor(255, 255, 0),
        ImColor(0, 255, 0),
        ImColor(0, 255, 255),
        ImColor(0, 0, 255),
        ImColor(255, 0, 255),
        ImColor(255, 0, 0) };

    for (int i = 0; i < 6; ++i)
    {
        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + SPACING, picker_pos.y + i * (SV_PICKER_SIZE.y / 6)),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + SPACING + HUE_PICKER_WIDTH,
            picker_pos.y + (i + 1) * (SV_PICKER_SIZE.y / 6)),
            colors[i],
            colors[i],
            colors[i + 1],
            colors[i + 1]);
    }

    draw_list->AddLine(
        ImVec2(picker_pos.x + SV_PICKER_SIZE.x + SPACING - 2, picker_pos.y + hue * SV_PICKER_SIZE.y),
        ImVec2(picker_pos.x + SV_PICKER_SIZE.x + SPACING + 2 + HUE_PICKER_WIDTH, picker_pos.y + hue * SV_PICKER_SIZE.y),
        ImColor(255, 255, 255));

    // draw alpha bar

    if( alphabar ) {
        float alpha = col[3];

        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 2*SPACING + HUE_PICKER_WIDTH, picker_pos.y),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 2*SPACING + 2*HUE_PICKER_WIDTH, picker_pos.y + SV_PICKER_SIZE.y),
            ImColor(0, 0, 0), ImColor(0, 0, 0), ImColor(255, 255, 255), ImColor(255, 255, 255) );

        draw_list->AddLine(
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 2*(SPACING - 2) + HUE_PICKER_WIDTH, picker_pos.y + alpha * SV_PICKER_SIZE.y),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 2*(SPACING + 2) + 2*HUE_PICKER_WIDTH, picker_pos.y + alpha * SV_PICKER_SIZE.y),
            ImColor(255.f - alpha, 255.f, 255.f));
    }

    // draw color matrix

    {
        const ImU32 c_oColorBlack = ImGui::ColorConvertFloat4ToU32(ImVec4(0.f,0.f,0.f,1.f));
        const ImU32 c_oColorBlackTransparent = ImGui::ColorConvertFloat4ToU32(ImVec4(0.f,0.f,0.f,0.f));
        const ImU32 c_oColorWhite = ImGui::ColorConvertFloat4ToU32(ImVec4(1.f,1.f,1.f,1.f));

        ImVec4 cHueValue(1, 1, 1, 1);
        ImGui::ColorConvertHSVtoRGB(hue, 1, 1, cHueValue.x, cHueValue.y, cHueValue.z);
        ImU32 oHueColor = ImGui::ColorConvertFloat4ToU32(cHueValue);

        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x, picker_pos.y),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x, picker_pos.y + SV_PICKER_SIZE.y),
            c_oColorWhite,
            oHueColor,
            oHueColor,
            c_oColorWhite
            );

        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x, picker_pos.y),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x, picker_pos.y + SV_PICKER_SIZE.y),
            c_oColorBlackTransparent,
            c_oColorBlackTransparent,
            c_oColorBlack,
            c_oColorBlack
            );
    }

    // draw cross-hair

    float x = saturation * SV_PICKER_SIZE.x;
    float y = (1 -value) * SV_PICKER_SIZE.y;
    ImVec2 p(picker_pos.x + x, picker_pos.y + y);
    draw_list->AddLine(ImVec2(p.x - CROSSHAIR_SIZE, p.y), ImVec2(p.x - 2, p.y), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x + CROSSHAIR_SIZE, p.y), ImVec2(p.x + 2, p.y), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x, p.y + CROSSHAIR_SIZE), ImVec2(p.x, p.y + 2), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x, p.y - CROSSHAIR_SIZE), ImVec2(p.x, p.y - 2), ImColor(255, 255, 255));

    // color matrix logic

    ImGui::InvisibleButton("saturation_value_selector", SV_PICKER_SIZE);

    if (ImGui::IsItemActive() && ImGui::GetIO().MouseDown[0])
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /**/ if( mouse_pos_in_canvas.x <                     0 ) mouse_pos_in_canvas.x = 0;
        else if( mouse_pos_in_canvas.x >= SV_PICKER_SIZE.x - 1 ) mouse_pos_in_canvas.x = SV_PICKER_SIZE.x - 1;

        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 1 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 1;

        value = 1 - (mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1));
        saturation = mouse_pos_in_canvas.x / (SV_PICKER_SIZE.x - 1);
        value_changed = true;
    }

    // hue bar logic

    ImGui::SetCursorScreenPos(ImVec2(picker_pos.x + SPACING + SV_PICKER_SIZE.x, picker_pos.y));
    ImGui::InvisibleButton("hue_selector", ImVec2(HUE_PICKER_WIDTH, SV_PICKER_SIZE.y));

    if( ImGui::GetIO().MouseDown[0] && (ImGui::IsItemHovered() || ImGui::IsItemActive()) )
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 1 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 1;

        hue = mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1 );
        value_changed = true;
    }

    // alpha bar logic

    if( alphabar ) {

    ImGui::SetCursorScreenPos(ImVec2(picker_pos.x + SPACING * 2 + HUE_PICKER_WIDTH + SV_PICKER_SIZE.x, picker_pos.y));
    ImGui::InvisibleButton("alpha_selector", ImVec2(HUE_PICKER_WIDTH, SV_PICKER_SIZE.y));

    if( ImGui::GetIO().MouseDown[0] && (ImGui::IsItemHovered() || ImGui::IsItemActive()) )
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 1 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 1;

        float alpha = mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1 );
        col[3] = alpha;
        value_changed = true;
    }

    }

    // R,G,B or H,S,V color editor

    color = ImColor::HSV(hue >= 1 ? hue - 10 * 1e-6 : hue, saturation > 0 ? saturation : 10*1e-6, value > 0 ? value : 1e-6);
    col[0] = color.Value.x;
    col[1] = color.Value.y;
    col[2] = color.Value.z;

    bool widget_used;
    ImGui::PushItemWidth( ( alphabar ? SPACING + HUE_PICKER_WIDTH : 0 ) +
        SV_PICKER_SIZE.x + SPACING + HUE_PICKER_WIDTH - 2*ImGui::GetStyle().FramePadding.x );
    widget_used = alphabar ? ImGui::ColorEdit4("", col) : ImGui::ColorEdit3("", col);
    ImGui::PopItemWidth();

    // try to cancel hue wrap (after ColorEdit), if any
    {
        float new_hue, new_sat, new_val;
        ImGui::ColorConvertRGBtoHSV( col[0], col[1], col[2], new_hue, new_sat, new_val );
        if( new_hue <= 0 && hue > 0 ) {
            if( new_val <= 0 && value != new_val ) {
                color = ImColor::HSV(hue, saturation, new_val <= 0 ? value * 0.5f : new_val );
                col[0] = color.Value.x;
                col[1] = color.Value.y;
                col[2] = color.Value.z;
            }
            else
            if( new_sat <= 0 ) {
                color = ImColor::HSV(hue, new_sat <= 0 ? saturation * 0.5f : new_sat, new_val );
                col[0] = color.Value.x;
                col[1] = color.Value.y;
                col[2] = color.Value.z;
            }
        }
    }

    return value_changed | widget_used;
}

bool ColorPicker3( float col[3] ) {
    return ColorPicker( col, false );
}

bool ColorPicker4( float col[4] ) {
    return ColorPicker( col, true );
}

r-lyeh-archived commented Jan 16, 2016

ok, v2
thanks for the suggestions guys

image

// [src] https://github.com/ocornut/imgui/issues/346
// v2

#include <imgui.h>

static bool ColorPicker( float *col, bool alphabar )
{
    const int    EDGE_SIZE = 200; // = int( ImGui::GetWindowWidth() * 0.75f );
    const ImVec2 SV_PICKER_SIZE = ImVec2(EDGE_SIZE, EDGE_SIZE);
    const float  SPACING = ImGui::GetStyle().ItemInnerSpacing.x;
    const float  HUE_PICKER_WIDTH = 20.f;
    const float  CROSSHAIR_SIZE = 7.0f;

    ImColor color(col[0], col[1], col[2]);
    bool value_changed = false;

    ImDrawList* draw_list = ImGui::GetWindowDrawList();

    // setup

    ImVec2 picker_pos = ImGui::GetCursorScreenPos();

    float hue, saturation, value;
    ImGui::ColorConvertRGBtoHSV(
        color.Value.x, color.Value.y, color.Value.z, hue, saturation, value);

    // draw hue bar

    ImColor colors[] = { ImColor(255, 0, 0),
        ImColor(255, 255, 0),
        ImColor(0, 255, 0),
        ImColor(0, 255, 255),
        ImColor(0, 0, 255),
        ImColor(255, 0, 255),
        ImColor(255, 0, 0) };

    for (int i = 0; i < 6; ++i)
    {
        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + SPACING, picker_pos.y + i * (SV_PICKER_SIZE.y / 6)),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + SPACING + HUE_PICKER_WIDTH,
            picker_pos.y + (i + 1) * (SV_PICKER_SIZE.y / 6)),
            colors[i],
            colors[i],
            colors[i + 1],
            colors[i + 1]);
    }

    draw_list->AddLine(
        ImVec2(picker_pos.x + SV_PICKER_SIZE.x + SPACING - 2, picker_pos.y + hue * SV_PICKER_SIZE.y),
        ImVec2(picker_pos.x + SV_PICKER_SIZE.x + SPACING + 2 + HUE_PICKER_WIDTH, picker_pos.y + hue * SV_PICKER_SIZE.y),
        ImColor(255, 255, 255));

    // draw alpha bar

    if( alphabar ) {
        float alpha = col[3];

        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 2*SPACING + HUE_PICKER_WIDTH, picker_pos.y),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 2*SPACING + 2*HUE_PICKER_WIDTH, picker_pos.y + SV_PICKER_SIZE.y),
            ImColor(0, 0, 0), ImColor(0, 0, 0), ImColor(255, 255, 255), ImColor(255, 255, 255) );

        draw_list->AddLine(
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 2*(SPACING - 2) + HUE_PICKER_WIDTH, picker_pos.y + alpha * SV_PICKER_SIZE.y),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x + 2*(SPACING + 2) + 2*HUE_PICKER_WIDTH, picker_pos.y + alpha * SV_PICKER_SIZE.y),
            ImColor(255.f - alpha, 255.f, 255.f));
    }

    // draw color matrix

    {
        const ImU32 c_oColorBlack = ImGui::ColorConvertFloat4ToU32(ImVec4(0.f,0.f,0.f,1.f));
        const ImU32 c_oColorBlackTransparent = ImGui::ColorConvertFloat4ToU32(ImVec4(0.f,0.f,0.f,0.f));
        const ImU32 c_oColorWhite = ImGui::ColorConvertFloat4ToU32(ImVec4(1.f,1.f,1.f,1.f));

        ImVec4 cHueValue(1, 1, 1, 1);
        ImGui::ColorConvertHSVtoRGB(hue, 1, 1, cHueValue.x, cHueValue.y, cHueValue.z);
        ImU32 oHueColor = ImGui::ColorConvertFloat4ToU32(cHueValue);

        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x, picker_pos.y),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x, picker_pos.y + SV_PICKER_SIZE.y),
            c_oColorWhite,
            oHueColor,
            oHueColor,
            c_oColorWhite
            );

        draw_list->AddRectFilledMultiColor(
            ImVec2(picker_pos.x, picker_pos.y),
            ImVec2(picker_pos.x + SV_PICKER_SIZE.x, picker_pos.y + SV_PICKER_SIZE.y),
            c_oColorBlackTransparent,
            c_oColorBlackTransparent,
            c_oColorBlack,
            c_oColorBlack
            );
    }

    // draw cross-hair

    float x = saturation * SV_PICKER_SIZE.x;
    float y = (1 -value) * SV_PICKER_SIZE.y;
    ImVec2 p(picker_pos.x + x, picker_pos.y + y);
    draw_list->AddLine(ImVec2(p.x - CROSSHAIR_SIZE, p.y), ImVec2(p.x - 2, p.y), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x + CROSSHAIR_SIZE, p.y), ImVec2(p.x + 2, p.y), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x, p.y + CROSSHAIR_SIZE), ImVec2(p.x, p.y + 2), ImColor(255, 255, 255));
    draw_list->AddLine(ImVec2(p.x, p.y - CROSSHAIR_SIZE), ImVec2(p.x, p.y - 2), ImColor(255, 255, 255));

    // color matrix logic

    ImGui::InvisibleButton("saturation_value_selector", SV_PICKER_SIZE);

    if (ImGui::IsItemActive() && ImGui::GetIO().MouseDown[0])
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /**/ if( mouse_pos_in_canvas.x <                     0 ) mouse_pos_in_canvas.x = 0;
        else if( mouse_pos_in_canvas.x >= SV_PICKER_SIZE.x - 1 ) mouse_pos_in_canvas.x = SV_PICKER_SIZE.x - 1;

        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 1 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 1;

        value = 1 - (mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1));
        saturation = mouse_pos_in_canvas.x / (SV_PICKER_SIZE.x - 1);
        value_changed = true;
    }

    // hue bar logic

    ImGui::SetCursorScreenPos(ImVec2(picker_pos.x + SPACING + SV_PICKER_SIZE.x, picker_pos.y));
    ImGui::InvisibleButton("hue_selector", ImVec2(HUE_PICKER_WIDTH, SV_PICKER_SIZE.y));

    if( ImGui::GetIO().MouseDown[0] && (ImGui::IsItemHovered() || ImGui::IsItemActive()) )
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 1 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 1;

        hue = mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1 );
        value_changed = true;
    }

    // alpha bar logic

    if( alphabar ) {

    ImGui::SetCursorScreenPos(ImVec2(picker_pos.x + SPACING * 2 + HUE_PICKER_WIDTH + SV_PICKER_SIZE.x, picker_pos.y));
    ImGui::InvisibleButton("alpha_selector", ImVec2(HUE_PICKER_WIDTH, SV_PICKER_SIZE.y));

    if( ImGui::GetIO().MouseDown[0] && (ImGui::IsItemHovered() || ImGui::IsItemActive()) )
    {
        ImVec2 mouse_pos_in_canvas = ImVec2(
            ImGui::GetIO().MousePos.x - picker_pos.x, ImGui::GetIO().MousePos.y - picker_pos.y);

        /**/ if( mouse_pos_in_canvas.y <                     0 ) mouse_pos_in_canvas.y = 0;
        else if( mouse_pos_in_canvas.y >= SV_PICKER_SIZE.y - 1 ) mouse_pos_in_canvas.y = SV_PICKER_SIZE.y - 1;

        float alpha = mouse_pos_in_canvas.y / (SV_PICKER_SIZE.y - 1 );
        col[3] = alpha;
        value_changed = true;
    }

    }

    // R,G,B or H,S,V color editor

    color = ImColor::HSV(hue >= 1 ? hue - 10 * 1e-6 : hue, saturation > 0 ? saturation : 10*1e-6, value > 0 ? value : 1e-6);
    col[0] = color.Value.x;
    col[1] = color.Value.y;
    col[2] = color.Value.z;

    bool widget_used;
    ImGui::PushItemWidth( ( alphabar ? SPACING + HUE_PICKER_WIDTH : 0 ) +
        SV_PICKER_SIZE.x + SPACING + HUE_PICKER_WIDTH - 2*ImGui::GetStyle().FramePadding.x );
    widget_used = alphabar ? ImGui::ColorEdit4("", col) : ImGui::ColorEdit3("", col);
    ImGui::PopItemWidth();

    // try to cancel hue wrap (after ColorEdit), if any
    {
        float new_hue, new_sat, new_val;
        ImGui::ColorConvertRGBtoHSV( col[0], col[1], col[2], new_hue, new_sat, new_val );
        if( new_hue <= 0 && hue > 0 ) {
            if( new_val <= 0 && value != new_val ) {
                color = ImColor::HSV(hue, saturation, new_val <= 0 ? value * 0.5f : new_val );
                col[0] = color.Value.x;
                col[1] = color.Value.y;
                col[2] = color.Value.z;
            }
            else
            if( new_sat <= 0 ) {
                color = ImColor::HSV(hue, new_sat <= 0 ? saturation * 0.5f : new_sat, new_val );
                col[0] = color.Value.x;
                col[1] = color.Value.y;
                col[2] = color.Value.z;
            }
        }
    }

    return value_changed | widget_used;
}

bool ColorPicker3( float col[3] ) {
    return ColorPicker( col, false );
}

bool ColorPicker4( float col[4] ) {
    return ColorPicker( col, true );
}
@brucelane

This comment has been minimized.

Show comment
Hide comment
@brucelane

brucelane Jan 16, 2016

thanks to all of you, that is going to be useful!

brucelane commented Jan 16, 2016

thanks to all of you, that is going to be useful!

@nem0

This comment has been minimized.

Show comment
Hide comment
@nem0

nem0 Jan 16, 2016

Contributor

@r-lyeh Nice, can we freely use your code?

Contributor

nem0 commented Jan 16, 2016

@r-lyeh Nice, can we freely use your code?

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jan 16, 2016

Owner

I'm looking at simplifying the code and making it match ImGui coding style.

v2.1 (-50 lines, bit faster)
https://gist.github.com/ocornut/9a55357df27d73cb8b34

Owner

ocornut commented Jan 16, 2016

I'm looking at simplifying the code and making it match ImGui coding style.

v2.1 (-50 lines, bit faster)
https://gist.github.com/ocornut/9a55357df27d73cb8b34

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 16, 2016

@nem0: sure, my contribs are public domain : ) there is that thennequin's snippet in it too
@ocornut: 👍

r-lyeh-archived commented Jan 16, 2016

@nem0: sure, my contribs are public domain : ) there is that thennequin's snippet in it too
@ocornut: 👍

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jan 16, 2016

Owner

Normally I'd be inclined to make that stuff optional and a separate file, but I think it would make more sense here to include a basic color picker in core imgui for all to us by default?

Owner

ocornut commented Jan 16, 2016

Normally I'd be inclined to make that stuff optional and a separate file, but I think it would make more sense here to include a basic color picker in core imgui for all to us by default?

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jan 16, 2016

Owner

Update again
https://gist.github.com/ocornut/9a55357df27d73cb8b34

AFAIK all those:

if (ImGui::IsItemActive() && io.MouseDown[0])
if (io.MouseDown[0] && (ImGui::IsItemHovered() || ImGui::IsItemActive()))

Can be remplaced by if ImGui::isItemActive()
Any reason you added the mouse button check?

I have removed some of the use of ImColor helpers but that's mainly because I'm rather unhappy about this helper, if you want to emit a U32 bits it'd do a back and forth to float which is really a waste of cpu. The color helpers are quite a sorry state right now and needs some cleanup.

How about adding an ImColor32 that also provide the same service but provide an ImU32 storage? It may be confusing, seeing ImColor in the first place is here to bridge the gap between usage of the float4 or the u32 (the later are used by the low-level api).
EDIT Using a macro IMCOL32() for now. May add it to imgui.h or imgui_internal.h

Owner

ocornut commented Jan 16, 2016

Update again
https://gist.github.com/ocornut/9a55357df27d73cb8b34

AFAIK all those:

if (ImGui::IsItemActive() && io.MouseDown[0])
if (io.MouseDown[0] && (ImGui::IsItemHovered() || ImGui::IsItemActive()))

Can be remplaced by if ImGui::isItemActive()
Any reason you added the mouse button check?

I have removed some of the use of ImColor helpers but that's mainly because I'm rather unhappy about this helper, if you want to emit a U32 bits it'd do a back and forth to float which is really a waste of cpu. The color helpers are quite a sorry state right now and needs some cleanup.

How about adding an ImColor32 that also provide the same service but provide an ImU32 storage? It may be confusing, seeing ImColor in the first place is here to bridge the gap between usage of the float4 or the u32 (the later are used by the low-level api).
EDIT Using a macro IMCOL32() for now. May add it to imgui.h or imgui_internal.h

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 16, 2016

ah, because I messed it up. I dont know the full API at all :) first baby steps on your lib, sorry.

On the other hand, since most of us are using the lib as a GUI/interface toolkit I guess a color picker is mandatory to have :) We're all making editors with it anyways.

r-lyeh-archived commented Jan 16, 2016

ah, because I messed it up. I dont know the full API at all :) first baby steps on your lib, sorry.

On the other hand, since most of us are using the lib as a GUI/interface toolkit I guess a color picker is mandatory to have :) We're all making editors with it anyways.

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 16, 2016

the color round trip conversion seems the next avoidable step indeed

r-lyeh-archived commented Jan 16, 2016

the color round trip conversion seems the next avoidable step indeed

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jan 16, 2016

Owner

I would also like to handle inputs fully because doing any drawing to remove one frame of lack of visual update.

Owner

ocornut commented Jan 16, 2016

I would also like to handle inputs fully because doing any drawing to remove one frame of lack of visual update.

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 16, 2016

Off-topic: Can the bottom-right color button width be retrieved by checking style? The fact that the hue/alpha bars do not align perfectly on both sides with the color button is getting me out of my nerves xD

r-lyeh-archived commented Jan 16, 2016

Off-topic: Can the bottom-right color button width be retrieved by checking style? The fact that the hue/alpha bars do not align perfectly on both sides with the color button is getting me out of my nerves xD

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jan 16, 2016

Owner

Off-topic: Can the bottom-right color button width be retrieved by checking style? The fact that the hue/alpha bars do not align perfectly on both sides with the color button is getting me out of my nerves xD

There's a bunch of small issues with size which I've having fixing locally recently. Right now the width passed to PushItemWidth() doesn't result in consistent result. Working on this separately but I wouldn't worry too much right now (and I know it is super frustrating!).

Owner

ocornut commented Jan 16, 2016

Off-topic: Can the bottom-right color button width be retrieved by checking style? The fact that the hue/alpha bars do not align perfectly on both sides with the color button is getting me out of my nerves xD

There's a bunch of small issues with size which I've having fixing locally recently. Right now the width passed to PushItemWidth() doesn't result in consistent result. Working on this separately but I wouldn't worry too much right now (and I know it is super frustrating!).

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 16, 2016

I mean, current hue color width is 20.f. I suspect the color button width is around 22.f in my theme. I would like to retrieve this 22.f programatically. Therefore, I would set 22 as the hue bar width before rendering the widget, and it would fit perfectly (I guess this width is the only variable missing because inner spacing is already present in formulae).

The other solution/workaround would be to edit ColorEditX and render the color button on the left, and the RGB/HSV/#hex input boxes on the right :) And forget about retrieving sizes 😃

r-lyeh-archived commented Jan 16, 2016

I mean, current hue color width is 20.f. I suspect the color button width is around 22.f in my theme. I would like to retrieve this 22.f programatically. Therefore, I would set 22 as the hue bar width before rendering the widget, and it would fit perfectly (I guess this width is the only variable missing because inner spacing is already present in formulae).

The other solution/workaround would be to edit ColorEditX and render the color button on the left, and the RGB/HSV/#hex input boxes on the right :) And forget about retrieving sizes 😃

@ocornut ocornut added the in progress label Jan 23, 2016

ocornut added a commit that referenced this issue Jan 23, 2016

Honoring exact width passed to PushItemWidth(), previously it would a…
…dd extra FramePadding.x*2 over that width. (ref #346)

If you had manual pixel-perfect alignment in place it might affect you.

ocornut added a commit that referenced this issue Jan 23, 2016

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jan 23, 2016

Owner

I have updated the gist
https://gist.github.com/ocornut/9a55357df27d73cb8b34

picker

Above commit fixes the issue I was referring to with PushItemWidth(). Exact alignment here if we want the color button to align with the picking bar is a little nasty, the color button uses (FontSize+FramePadding.y*2) for size so I'm using that. However I'm not super happy with ColorButton() in the first place, it's API should probably be fixed.

Now I am trying to figure out the relationship between ColorEdit and ColorPicker, how both can be used together and separately. It is easy to turn the colorbutton of ColorEdit into a picker popup. In the case of using a popup it may make sense to expose RGB and HSV and HEX together.

Owner

ocornut commented Jan 23, 2016

I have updated the gist
https://gist.github.com/ocornut/9a55357df27d73cb8b34

picker

Above commit fixes the issue I was referring to with PushItemWidth(). Exact alignment here if we want the color button to align with the picking bar is a little nasty, the color button uses (FontSize+FramePadding.y*2) for size so I'm using that. However I'm not super happy with ColorButton() in the first place, it's API should probably be fixed.

Now I am trying to figure out the relationship between ColorEdit and ColorPicker, how both can be used together and separately. It is easy to turn the colorbutton of ColorEdit into a picker popup. In the case of using a popup it may make sense to expose RGB and HSV and HEX together.

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 23, 2016

What about ColorEdit4( float*, show_alpha, show_picker ); ?
If show_picker = true, draw a triangle on top of button color to toggle color picker logic/rendering. If enabled, it should be displayed after the color edit widget and before the next one (ie, bottom).

r-lyeh-archived commented Jan 23, 2016

What about ColorEdit4( float*, show_alpha, show_picker ); ?
If show_picker = true, draw a triangle on top of button color to toggle color picker logic/rendering. If enabled, it should be displayed after the color edit widget and before the next one (ie, bottom).

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jan 23, 2016

The current snippet is misaligned on my build. Do I need to update the repo as well?

image

r-lyeh-archived commented Jan 23, 2016

The current snippet is misaligned on my build. Do I need to update the repo as well?

image

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jan 23, 2016

Owner

Yes I made minor breaking changes to imgui.cpp i've been wanting to do for a while.
Been adding various flags to ColorEdit functions right now. I'm out but i'll try to resume that work together.

Basically showing the picker in a popup when clicking on the color.

Owner

ocornut commented Jan 23, 2016

Yes I made minor breaking changes to imgui.cpp i've been wanting to do for a while.
Been adding various flags to ColorEdit functions right now. I'm out but i'll try to resume that work together.

Basically showing the picker in a popup when clicking on the color.

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment

r-lyeh-archived commented Jan 25, 2016

👍

ocornut added a commit that referenced this issue Feb 21, 2016

Added ColorPicker3/ColorPicker4, refactoring ColorEdit3/ColorEdit4 ad…
…ding a bunch of flags and access to picker (wip #346)

Still pretty much experimenting so may break API

ocornut added a commit that referenced this issue Feb 21, 2016

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Feb 21, 2016

Owner

I made a first pass in a branch.

2016-02-21_23-28-38

capture

ColorEdit

  • Integrated picker within ColorEdit3/ColorEdit4 - calls picker when clicking on colored squared
  • Added a context menu in ColorEdit3/ColorEdit4 to change the color mode
  • Refactored ColorEdit functions with a bunch of flags (below).
  • Can be used as a color button only with ImGuiColorEditFlags_NoSliders
  • Took old a rather bold approach of removing the last default parameter of ColorEdit4() bool show_alpha=true and replacing it with flags. Many bool parameters in the library ended up being a mistake, I remember reading about this exact same thing in API design articles and made similar mistakes. In this particular instance I could have kept the bool parameter but it would have been a drag. Since this is was a very rarely used parameter (because there is a ColorEdit3) in a relatively uncommon function, I removed it and including it in the flag set.. and for whose which compiler won't warn of bool->int conversion I took an extra step of supporting dodgy-silent compatibility by making the Alpha flag = 0x01. So for the few people who used ColorEdit4() which an explicit show_alpha=xxx it will either still work and be fine either warn/error.

ColorPicker

  • Added ColorPicker3/ColorPicker4
  • Can be opened as a popup from ColorEdit or be called as an independent inline widget. It uses the default ItemWidth mechanism to specify width.
  • Support flags: optional alpha, sliders for RGB/HSV/HEX can be selectively disabled

Inline with ImGui::ColorPicker4("color", (float*)&clear_color, ImGuiColorEditFlags_NoSliders);
capture

// Enumeration for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4()
enum ImGuiColorEditFlags_
{
    ImGuiColorEditFlags_Alpha           = 1 << 0,   // ColorEdit/ColorPicker: show/edit Alpha component. Must be 0x01 for compatibility with old API taking bool
    ImGuiColorEditFlags_RGB             = 1 << 1,   // ColorEdit: Choose one among RGB/HSV/HEX. User can still use the options menu to change. ColorPicker: Choose any combination or RGB/HSX/HEX.
    ImGuiColorEditFlags_HSV             = 1 << 2,
    ImGuiColorEditFlags_HEX             = 1 << 3,
    ImGuiColorEditFlags_NoPicker        = 1 << 4,   // ColorEdit: Disable picker when clicking on colored square
    ImGuiColorEditFlags_NoOptions       = 1 << 5,   // ColorEdit: Disable toggling options menu when right-clicking colored square
    ImGuiColorEditFlags_NoColorSquare   = 1 << 6,   // ColorEdit: Disable colored square
    ImGuiColorEditFlags_NoSliders       = 1 << 7,   // ColorPicker: Disable RGB/HSV/HEX sliders
    ImGuiColorEditFlags_ModeMask_       = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX
};

Some remaining issues to work on: (probably more to come)

  • I am not showing a preview of the color in the picker. Haven't decided where to put it yet. When opened from a ColorEdit the preview shows in the ColorEdit so it's ok in many cases but there isn't a strict guarantee that the popup won't override that preview.
  • ColorEdit now uses right-click context menu on the colored square to switch from RGB/HSV/HEX. It is a little slower to use and has the risk of not being noticed. I have mixed feelings about changing the edit mode per-instance. Perhaps it should be global? May add a comment in the tooltip to request right-click? Any other idea? Perhaps I could add a small ".." button next to the colored square to make it more obvious?
  • The contextual menu in ColorEdit shows options in English.
    2016-02-21_23-42-52
    This is actually sort of a problem. ImGui uses english for the code in imgui_demo.cpp but none of the core imgui code has visible English sentences. I don't know how to solve that.
Owner

ocornut commented Feb 21, 2016

I made a first pass in a branch.

2016-02-21_23-28-38

capture

ColorEdit

  • Integrated picker within ColorEdit3/ColorEdit4 - calls picker when clicking on colored squared
  • Added a context menu in ColorEdit3/ColorEdit4 to change the color mode
  • Refactored ColorEdit functions with a bunch of flags (below).
  • Can be used as a color button only with ImGuiColorEditFlags_NoSliders
  • Took old a rather bold approach of removing the last default parameter of ColorEdit4() bool show_alpha=true and replacing it with flags. Many bool parameters in the library ended up being a mistake, I remember reading about this exact same thing in API design articles and made similar mistakes. In this particular instance I could have kept the bool parameter but it would have been a drag. Since this is was a very rarely used parameter (because there is a ColorEdit3) in a relatively uncommon function, I removed it and including it in the flag set.. and for whose which compiler won't warn of bool->int conversion I took an extra step of supporting dodgy-silent compatibility by making the Alpha flag = 0x01. So for the few people who used ColorEdit4() which an explicit show_alpha=xxx it will either still work and be fine either warn/error.

ColorPicker

  • Added ColorPicker3/ColorPicker4
  • Can be opened as a popup from ColorEdit or be called as an independent inline widget. It uses the default ItemWidth mechanism to specify width.
  • Support flags: optional alpha, sliders for RGB/HSV/HEX can be selectively disabled

Inline with ImGui::ColorPicker4("color", (float*)&clear_color, ImGuiColorEditFlags_NoSliders);
capture

// Enumeration for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4()
enum ImGuiColorEditFlags_
{
    ImGuiColorEditFlags_Alpha           = 1 << 0,   // ColorEdit/ColorPicker: show/edit Alpha component. Must be 0x01 for compatibility with old API taking bool
    ImGuiColorEditFlags_RGB             = 1 << 1,   // ColorEdit: Choose one among RGB/HSV/HEX. User can still use the options menu to change. ColorPicker: Choose any combination or RGB/HSX/HEX.
    ImGuiColorEditFlags_HSV             = 1 << 2,
    ImGuiColorEditFlags_HEX             = 1 << 3,
    ImGuiColorEditFlags_NoPicker        = 1 << 4,   // ColorEdit: Disable picker when clicking on colored square
    ImGuiColorEditFlags_NoOptions       = 1 << 5,   // ColorEdit: Disable toggling options menu when right-clicking colored square
    ImGuiColorEditFlags_NoColorSquare   = 1 << 6,   // ColorEdit: Disable colored square
    ImGuiColorEditFlags_NoSliders       = 1 << 7,   // ColorPicker: Disable RGB/HSV/HEX sliders
    ImGuiColorEditFlags_ModeMask_       = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX
};

Some remaining issues to work on: (probably more to come)

  • I am not showing a preview of the color in the picker. Haven't decided where to put it yet. When opened from a ColorEdit the preview shows in the ColorEdit so it's ok in many cases but there isn't a strict guarantee that the popup won't override that preview.
  • ColorEdit now uses right-click context menu on the colored square to switch from RGB/HSV/HEX. It is a little slower to use and has the risk of not being noticed. I have mixed feelings about changing the edit mode per-instance. Perhaps it should be global? May add a comment in the tooltip to request right-click? Any other idea? Perhaps I could add a small ".." button next to the colored square to make it more obvious?
  • The contextual menu in ColorEdit shows options in English.
    2016-02-21_23-42-52
    This is actually sort of a problem. ImGui uses english for the code in imgui_demo.cpp but none of the core imgui code has visible English sentences. I don't know how to solve that.
@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Feb 22, 2016

not showing a preview of the color in the picker
what about the background for the #66b20080 hexcode?

uses right-click context menu + tooltip
What if the color button...

  1. uses left-button to show/hide the popup
  2. has the hovering tooltip as suggested
  3. has the ".." text printed inside the color button (no i18n issues with that :)

contextual menu in ColorEdit shows options in English
Two routes:

  1. Discard the Edit as... text and use RGB/HSV/Hex only. as they'll be probably the same in most if not all languages.
  2. Provide a i18n/L10n interface in the same way that we tweak colors. For me it would be something like...
enum ImGuiLang {
IMGUI_LANG_EDIT_AS_RGB,
IMGUI_LANG_EDIT_AS_HSV,
//...
};
ImGui &lang = ImGui::GetLang();
lang[ IMGUI_LANG_EDIT_AS_RGB ] = "Editar como RGB"; // ES-es
lang[ IMGUI_LANG_EDIT_AS_HSV ] = "Editar como HSV"; // ES-es
// ...

r-lyeh-archived commented Feb 22, 2016

not showing a preview of the color in the picker
what about the background for the #66b20080 hexcode?

uses right-click context menu + tooltip
What if the color button...

  1. uses left-button to show/hide the popup
  2. has the hovering tooltip as suggested
  3. has the ".." text printed inside the color button (no i18n issues with that :)

contextual menu in ColorEdit shows options in English
Two routes:

  1. Discard the Edit as... text and use RGB/HSV/Hex only. as they'll be probably the same in most if not all languages.
  2. Provide a i18n/L10n interface in the same way that we tweak colors. For me it would be something like...
enum ImGuiLang {
IMGUI_LANG_EDIT_AS_RGB,
IMGUI_LANG_EDIT_AS_HSV,
//...
};
ImGui &lang = ImGui::GetLang();
lang[ IMGUI_LANG_EDIT_AS_RGB ] = "Editar como RGB"; // ES-es
lang[ IMGUI_LANG_EDIT_AS_HSV ] = "Editar como HSV"; // ES-es
// ...

ocornut added a commit that referenced this issue Jul 31, 2017

ColorPicker: Hue wheel + SV triangle picker mode (mode selection flag…
…s still wip, missing context menu and persistent options). (#346)
@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jul 31, 2017

Owner

Pushed a first version of alternate color picker (which you can check in the demo window because I haven't exposed the buttons and/or context menu to change mode yet). Based on @dariomanesku one. Spent an absurd number of hours trying to compact this but it is still a good 150+ lines with the geometry/paint helpers involved (ImTriangleContainsPoint, ImTriangleBarycentricCoords, PaintVertsLinearGradientKeepAlpha). I'm emitting vertices directly for the two layers of the triangles to avoid anti-aliasing + a border above it. Once I add flags to ImDrawList to control the rendering more precisely that code can be simplified a little.

picker_wheel2

Owner

ocornut commented Jul 31, 2017

Pushed a first version of alternate color picker (which you can check in the demo window because I haven't exposed the buttons and/or context menu to change mode yet). Based on @dariomanesku one. Spent an absurd number of hours trying to compact this but it is still a good 150+ lines with the geometry/paint helpers involved (ImTriangleContainsPoint, ImTriangleBarycentricCoords, PaintVertsLinearGradientKeepAlpha). I'm emitting vertices directly for the two layers of the triangles to avoid anti-aliasing + a border above it. Once I add flags to ImDrawList to control the rendering more precisely that code can be simplified a little.

picker_wheel2

@DubbleClick

This comment has been minimized.

Show comment
Hide comment
@DubbleClick

DubbleClick Jul 31, 2017

What's it with the green pixels all over?

DubbleClick commented Jul 31, 2017

What's it with the green pixels all over?

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jul 31, 2017

Owner

@DubbleClick

What's it with the green pixels all over?

It's an artefact in the GIF file, because of the amount of colors and hue changes my GIF compressor messed up.

Owner

ocornut commented Jul 31, 2017

@DubbleClick

What's it with the green pixels all over?

It's an artefact in the GIF file, because of the amount of colors and hue changes my GIF compressor messed up.

@nem0

This comment has been minimized.

Show comment
Hide comment
@nem0

nem0 Jul 31, 2017

Contributor

Will there be just one version in the end or both?

Contributor

nem0 commented Jul 31, 2017

Will there be just one version in the end or both?

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Jul 31, 2017

Owner

Both. I'm still figuring out the details of interfaces to select/override in code vs. select/override at user level, and maybe an api call to set the default until that sort of thing is saved in an .ini file.

@nem0 do you see an issue with embedding both?

PS: The clarify, the combo box here is part of the demo, I don't expect people to stick a combo box. It'll be a context menu or miniature icon.

Owner

ocornut commented Jul 31, 2017

Both. I'm still figuring out the details of interfaces to select/override in code vs. select/override at user level, and maybe an api call to set the default until that sort of thing is saved in an .ini file.

@nem0 do you see an issue with embedding both?

PS: The clarify, the combo box here is part of the demo, I don't expect people to stick a combo box. It'll be a context menu or miniature icon.

@jdumas

This comment has been minimized.

Show comment
Hide comment
@jdumas

jdumas Jul 31, 2017

That's awesome work @ocornut ! I can't check it out until this week, but I'll give it a whirl when I come back.

jdumas commented Jul 31, 2017

That's awesome work @ocornut ! I can't check it out until this week, but I'll give it a whirl when I come back.

@r-lyeh-archived

This comment has been minimized.

Show comment
Hide comment
@r-lyeh-archived

r-lyeh-archived Jul 31, 2017

Aww so cute :)

r-lyeh-archived commented Jul 31, 2017

Aww so cute :)

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Aug 1, 2017

Owner

There is one already: https://github.com/bkaradzic/bgfx/blob/master/3rdparty/ocornut-imgui/widgets/color_wheel.inl

Yes it is basically same base, but cleaned up, turned the useful stuff into shared helpers, optimized (the circle rendering is super costly in your link), fixed a few issues (this one is always round-trip write back to original value even without interaction), and it'll be in master soon :)

You'll be able to repro your ColorWheel() function by calling ColorPicker4 with a few flags (ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoSidePreview).

Owner

ocornut commented Aug 1, 2017

There is one already: https://github.com/bkaradzic/bgfx/blob/master/3rdparty/ocornut-imgui/widgets/color_wheel.inl

Yes it is basically same base, but cleaned up, turned the useful stuff into shared helpers, optimized (the circle rendering is super costly in your link), fixed a few issues (this one is always round-trip write back to original value even without interaction), and it'll be in master soon :)

You'll be able to repro your ColorWheel() function by calling ColorPicker4 with a few flags (ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoSidePreview).

@bkaradzic

This comment has been minimized.

Show comment
Hide comment
@bkaradzic

bkaradzic Aug 1, 2017

Contributor

That's awesome! :) 👍

Contributor

bkaradzic commented Aug 1, 2017

That's awesome! :) 👍

@offlineJ

This comment has been minimized.

Show comment
Hide comment
@offlineJ

offlineJ Aug 1, 2017

damn these are beautiful you always make working with imgui a pleasure 😃

offlineJ commented Aug 1, 2017

damn these are beautiful you always make working with imgui a pleasure 😃

@ghost ghost referenced this issue Aug 5, 2017

Open

ImGui #48

ocornut added a commit that referenced this issue Aug 7, 2017

ocornut added a commit that referenced this issue Aug 8, 2017

ocornut added a commit that referenced this issue Aug 8, 2017

Added SetColorEditOptions(). ColorPicker4: context menu to select pic…
…ker type and alpha bar. Reorganized flags (again!). (#346)
@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Aug 8, 2017

Owner

I have done another pass at tidying how the persistent options would be specified/stored, and made it that right-clicking on the picker part opens a context menu allowing to:

  • Select the Picker Type (with a preview of what they look like)
  • Enable the Alpha Bar if it hasn't been selected

pickier_option3

Added the SetColorEditOptions() which allows to

  • Specify the default at boot time for user-changeable options (e.g. if you want HSV, and another type of picker)
  • Enforce some default options such as _AlphaBar or _HDR which having to pass it to the widgets everytime.

Passing one of the user-changeable option such as ImGuiColorEditFlags_RGB to a ColorEdit4/ColorPicker4 force that mode and disable the corresponding option menu for the widget.

Which of either picker do you think should be the default default (in case of not calling SetColorEditOptions) ?

Owner

ocornut commented Aug 8, 2017

I have done another pass at tidying how the persistent options would be specified/stored, and made it that right-clicking on the picker part opens a context menu allowing to:

  • Select the Picker Type (with a preview of what they look like)
  • Enable the Alpha Bar if it hasn't been selected

pickier_option3

Added the SetColorEditOptions() which allows to

  • Specify the default at boot time for user-changeable options (e.g. if you want HSV, and another type of picker)
  • Enforce some default options such as _AlphaBar or _HDR which having to pass it to the widgets everytime.

Passing one of the user-changeable option such as ImGuiColorEditFlags_RGB to a ColorEdit4/ColorPicker4 force that mode and disable the corresponding option menu for the widget.

Which of either picker do you think should be the default default (in case of not calling SetColorEditOptions) ?

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Aug 8, 2017

Owner

By the way I think this is ready to merge into Master so I will proceed. There will probably be some things to change/tweaks prior to 1.51 but it'll get more testing and feedback if it's in Master.

Owner

ocornut commented Aug 8, 2017

By the way I think this is ready to merge into Master so I will proceed. There will probably be some things to change/tweaks prior to 1.51 but it'll get more testing and feedback if it's in Master.

@DubbleClick

This comment has been minimized.

Show comment
Hide comment
@DubbleClick

DubbleClick Aug 8, 2017

Which of either picker do you think should be the default default (in case of not calling SetColorEditOptions) ?

The square one in my opinion, it's more commonly found on the internet. It would be nice if it was able to specify whether the little preview rectangle should show the alpha (with the transparency squares) or the filled colour (so alpha set to 255 for the preview). It could be extremely hard to tell the colour with a low alpha otherwise.

DubbleClick commented Aug 8, 2017

Which of either picker do you think should be the default default (in case of not calling SetColorEditOptions) ?

The square one in my opinion, it's more commonly found on the internet. It would be nice if it was able to specify whether the little preview rectangle should show the alpha (with the transparency squares) or the filled colour (so alpha set to 255 for the preview). It could be extremely hard to tell the colour with a low alpha otherwise.

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Aug 8, 2017

Owner

It would be nice if it was able to specify whether the little preview rectangle should show the alpha (with the transparency squares) or the filled colour (so alpha set to 255 for the preview). It could be extremely hard to tell the colour with a low alpha otherwise.

It's already the case, you have 3 different options (no alpha, alpha, half of each)

Owner

ocornut commented Aug 8, 2017

It would be nice if it was able to specify whether the little preview rectangle should show the alpha (with the transparency squares) or the filled colour (so alpha set to 255 for the preview). It could be extremely hard to tell the colour with a low alpha otherwise.

It's already the case, you have 3 different options (no alpha, alpha, half of each)

@RickLamb

This comment has been minimized.

Show comment
Hide comment
@RickLamb

RickLamb Aug 8, 2017

Sorry haven't been following this before. Have you already discussed if this belongs in core imgui.h rather than a separate file of 'batteries-included' widgets or something.

RickLamb commented Aug 8, 2017

Sorry haven't been following this before. Have you already discussed if this belongs in core imgui.h rather than a separate file of 'batteries-included' widgets or something.

@DubbleClick

This comment has been minimized.

Show comment
Hide comment
@DubbleClick

DubbleClick Aug 8, 2017

It's already the case, you have 3 different options (no alpha, alpha, half of each)

Then there's nothing else I could wish for

DubbleClick commented Aug 8, 2017

It's already the case, you have 3 different options (no alpha, alpha, half of each)

Then there's nothing else I could wish for

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Aug 8, 2017

Owner

Sorry haven't been following this before. Have you already discussed if this belongs in core imgui.h rather than a separate file of 'batteries-included' widgets or something.

Well, I just merged it to master! :)
Right now we don't have a mechanism to split thing easily considering that ColorEdit4 is already an existing/legacy entry point of imgui.

Depending on how imgui evolves I'm not against either splitting it into more files and/or splitting out more stuff. Right now the color-picker branch I think added about 300-400 lines and that includes many improvements to the old ColorEdit4 as well.

Owner

ocornut commented Aug 8, 2017

Sorry haven't been following this before. Have you already discussed if this belongs in core imgui.h rather than a separate file of 'batteries-included' widgets or something.

Well, I just merged it to master! :)
Right now we don't have a mechanism to split thing easily considering that ColorEdit4 is already an existing/legacy entry point of imgui.

Depending on how imgui evolves I'm not against either splitting it into more files and/or splitting out more stuff. Right now the color-picker branch I think added about 300-400 lines and that includes many improvements to the old ColorEdit4 as well.

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Aug 9, 2017

Owner

Closing the color picker topic (exactly 100 messages :)

Some ideas for later:

  • Better support for HDR: Right now I think our RGB-HSV roundtrip doesn't play well with unbounded values.
  • Potentially ColorButton could render HDR with a gradient showing the falloff
  • Would like to reorganize the code to allow for adding custom color pickers, but in the meanwhile if someone needs to use a really custom picker they can use ColorButton() and create their own popup.

For reference, here's code for a HDR friendly "always-relative" picker suggested by cupe_cupe
https://twitter.com/cupe_cupe/status/891755433714700289

dgap-ywxsaehbat jpg large

// color editor for 3 or 4 component colors
bool drawColorSelector(const char* label, float height, float* r, float* g, float* b, float* a = nullptr) {
	ImGui::PushID(label);

	ImVec2 buttonStart = ImGui::GetCursorScreenPos();
	
	ImGui::Image((void*)g_wheelTexture, ImVec2(height,height), ImVec2(0,0), ImVec2(1,1));
	ImGui::SetCursorScreenPos(buttonStart);
	ImGui::InvisibleButton(label, ImVec2(height,height)); ImGui::SameLine();

	vec3 rgb = vec3(max(0.f,*r),max(0.f,*g),max(0.f,*b));
	vec3 hsv = rgb_to_hsv(degamma(rgb));

	float h = hsv.r;
	float s = hsv.g;
	float v = hsv.b;

	vec2 onCircle = vec2(cos(h*TAU), sin(h*TAU)) * s;

	ImGui::GetWindowDrawList()->AddCircle(vec2(buttonStart) + vec2(height,height)*0.5f + onCircle * height * 0.5f, 3.0f, ImColor(0,0,0));

	bool changed = false;
	if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) {
		float speed = 0.015f;
		if (ImGui::GetIO().KeyShift) {
			speed *= 0.1f;
		}
		onCircle += vec2(ImGui::GetMouseDragDelta() * speed);
		ImGui::ResetMouseDragDelta();
		s = min(1.0f, length(onCircle));
		if (s == 0.0f) {
			h = 0.0f;
		} else { 
			h = atan2f(onCircle.y, onCircle.x) / TAU;
			if (h < 0) {
				h += 1.0f;
			}
		}
		changed = true;
	}
	ImVec4 c = vec4(hsv_to_rgb(h,s,0.5f), 1.0f);
	ImGui::PushStyleColor(ImGuiCol_FrameBg, c);
	changed |= ImGui::VSliderFloat("##v",ImVec2(10,height),&v, 0.0f, 10.0f, "",2.0f);
	ImGui::PopStyleColor();

	ImGui::SameLine();

	if (changed) {
		rgb = gamma(hsv_to_rgb(vec3(h,s,v)));
		*r = rgb.r;
		*g = rgb.g;
		*b = rgb.b;
	}


	if (a) {
		ImGui::VSliderFloat("##a",ImVec2(10,height),a, -10.0f, 10.0f, "",1.5f); ImGui::SameLine();
	}

	ImGui::PopID();
	return changed;
}

The main issue with it is that it relies on a texture (which is easy to render with code, but using polygons doesn't cut it). Currently the font atlas can in theory be Alpha-only so we'd either need to lift this limitation (or e.g: have a different set of available feature depending on if we render the atlas as Alpha-only or RGBA). Or, if that picker is shipped as a separate extension, which would be a healthier direction to go to, it could embed the code to create the texture and it's up to the user to upload it.

I'm starting to consider "repo number 2" which would hold optional extensions and helpers such as Docking, Memory Editor or that sort of Color Picker.

Owner

ocornut commented Aug 9, 2017

Closing the color picker topic (exactly 100 messages :)

Some ideas for later:

  • Better support for HDR: Right now I think our RGB-HSV roundtrip doesn't play well with unbounded values.
  • Potentially ColorButton could render HDR with a gradient showing the falloff
  • Would like to reorganize the code to allow for adding custom color pickers, but in the meanwhile if someone needs to use a really custom picker they can use ColorButton() and create their own popup.

For reference, here's code for a HDR friendly "always-relative" picker suggested by cupe_cupe
https://twitter.com/cupe_cupe/status/891755433714700289

dgap-ywxsaehbat jpg large

// color editor for 3 or 4 component colors
bool drawColorSelector(const char* label, float height, float* r, float* g, float* b, float* a = nullptr) {
	ImGui::PushID(label);

	ImVec2 buttonStart = ImGui::GetCursorScreenPos();
	
	ImGui::Image((void*)g_wheelTexture, ImVec2(height,height), ImVec2(0,0), ImVec2(1,1));
	ImGui::SetCursorScreenPos(buttonStart);
	ImGui::InvisibleButton(label, ImVec2(height,height)); ImGui::SameLine();

	vec3 rgb = vec3(max(0.f,*r),max(0.f,*g),max(0.f,*b));
	vec3 hsv = rgb_to_hsv(degamma(rgb));

	float h = hsv.r;
	float s = hsv.g;
	float v = hsv.b;

	vec2 onCircle = vec2(cos(h*TAU), sin(h*TAU)) * s;

	ImGui::GetWindowDrawList()->AddCircle(vec2(buttonStart) + vec2(height,height)*0.5f + onCircle * height * 0.5f, 3.0f, ImColor(0,0,0));

	bool changed = false;
	if (ImGui::IsItemActive() && ImGui::IsMouseDragging(0)) {
		float speed = 0.015f;
		if (ImGui::GetIO().KeyShift) {
			speed *= 0.1f;
		}
		onCircle += vec2(ImGui::GetMouseDragDelta() * speed);
		ImGui::ResetMouseDragDelta();
		s = min(1.0f, length(onCircle));
		if (s == 0.0f) {
			h = 0.0f;
		} else { 
			h = atan2f(onCircle.y, onCircle.x) / TAU;
			if (h < 0) {
				h += 1.0f;
			}
		}
		changed = true;
	}
	ImVec4 c = vec4(hsv_to_rgb(h,s,0.5f), 1.0f);
	ImGui::PushStyleColor(ImGuiCol_FrameBg, c);
	changed |= ImGui::VSliderFloat("##v",ImVec2(10,height),&v, 0.0f, 10.0f, "",2.0f);
	ImGui::PopStyleColor();

	ImGui::SameLine();

	if (changed) {
		rgb = gamma(hsv_to_rgb(vec3(h,s,v)));
		*r = rgb.r;
		*g = rgb.g;
		*b = rgb.b;
	}


	if (a) {
		ImGui::VSliderFloat("##a",ImVec2(10,height),a, -10.0f, 10.0f, "",1.5f); ImGui::SameLine();
	}

	ImGui::PopID();
	return changed;
}

The main issue with it is that it relies on a texture (which is easy to render with code, but using polygons doesn't cut it). Currently the font atlas can in theory be Alpha-only so we'd either need to lift this limitation (or e.g: have a different set of available feature depending on if we render the atlas as Alpha-only or RGBA). Or, if that picker is shipped as a separate extension, which would be a healthier direction to go to, it could embed the code to create the texture and it's up to the user to upload it.

I'm starting to consider "repo number 2" which would hold optional extensions and helpers such as Docking, Memory Editor or that sort of Color Picker.

@ocornut ocornut closed this Aug 9, 2017

@jdumas

This comment has been minimized.

Show comment
Hide comment
@jdumas

jdumas Aug 9, 2017

I'm starting to consider "repo number 2" which would hold optional extensions and helpers such as Docking, Memory Editor or that sort of Color Picker.

Does it need to be in a different repo? A folder named plugins/ would be fine imho.

jdumas commented Aug 9, 2017

I'm starting to consider "repo number 2" which would hold optional extensions and helpers such as Docking, Memory Editor or that sort of Color Picker.

Does it need to be in a different repo? A folder named plugins/ would be fine imho.

ocornut added a commit that referenced this issue Aug 9, 2017

ocornut added a commit that referenced this issue Aug 10, 2017

ColorPicker: Fixes for when ImGuiColorEditFlags_NoOptions flag is set…
…: always read a default picker type + forward flag to sub ColorEdit widgets. (#346)

ocornut added a commit that referenced this issue Sep 28, 2017

@lolnobody

This comment has been minimized.

Show comment
Hide comment
@lolnobody

lolnobody Nov 12, 2017

syntax error: identifier ImGuiColorEditFlags when i paste it in imgui.h

lolnobody commented Nov 12, 2017

syntax error: identifier ImGuiColorEditFlags when i paste it in imgui.h

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Nov 12, 2017

Owner

syntax error: identifier ImGuiColorEditFlags when i paste it in imgui.h

  1. This is not how you report an error.
  2. The color picker is now included in imgui by default so you don't need to use any of the code provided here.
Owner

ocornut commented Nov 12, 2017

syntax error: identifier ImGuiColorEditFlags when i paste it in imgui.h

  1. This is not how you report an error.
  2. The color picker is now included in imgui by default so you don't need to use any of the code provided here.
@nem0

This comment has been minimized.

Show comment
Hide comment
@nem0

nem0 Nov 20, 2017

Contributor

One reason why the triangle in the beginning of this thread was done how it was one https://twitter.com/omigamedev/status/932277181962735616, should I open a new issue?

Contributor

nem0 commented Nov 20, 2017

One reason why the triangle in the beginning of this thread was done how it was one https://twitter.com/omigamedev/status/932277181962735616, should I open a new issue?

@ocornut

This comment has been minimized.

Show comment
Hide comment
@ocornut

ocornut Nov 20, 2017

Owner

@nem0 a PR or new issue would be ideal, yes. Thanks!

Owner

ocornut commented Nov 20, 2017

@nem0 a PR or new issue would be ideal, yes. Thanks!

ocornut added a commit that referenced this issue Nov 20, 2017

Color picker: Tweak vertex shading code, since we aren't shading betw…
…een PrimReserve and PrimVert the code can be expressed more naturally. (#346)

ocornut added a commit that referenced this issue Dec 8, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment