ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
61.03k stars 10.29k forks source link

Color picker #346

Closed nem0 closed 7 years ago

nem0 commented 9 years ago

(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 commented 7 years ago

@johanwendin It's still there with the ImGuiColorEditFlags_AlphaBar flag I made it off by default for now (but could change it to be the opposite if enough people agree). Out of curiosity what is your layout/setup? I was thinking it didn't add extra useful information compared to what's available in the regular Alpha DragFloat() below (unless that's disabled with your layout?). Whereas the Hue bar needs a visual cue, Alpha is merely linear value.

ocornut commented 7 years ago

Added ImGuiColorEditFlags_Float which displays and edit color fields as float in the 0.0..1.0. With this flag on there should be no round-trip going through an integer.

image

Also added this option in the context menu:

float

jdumas commented 7 years ago

I'm considering just removing the ALPHA bar (or at least having it off by default). It doesn't actually feel really important/useful. What do you think? Perhaps it could requires an extra flag to enable.

I feel that the alpha bar should be ON by default when you call ImGui::ColorPicker4(), and OFF when you call ImGui::ColorPicker3(). Or maybe I'm missing something here?

ocornut commented 7 years ago

So, what may be unclear here is that if you call ColorEdit4() or ColorPicker4() you will by default always have the sliders/drags and those already have the Alpha in, this is why I feel the Alpha Bar is a duplicate.

johanwendin commented 7 years ago

@ocornut I use it like this: coloredit

I'm not sure the default had visible alpha or not, or if I added that myself a while back. Been away from this code for far too long. :)

In my pixel editor I have a checkbox to flag whether it's premultiplied alpha or not with visuals to match: color_picker

As long as I can keep the alpha slider with flags I'm happy. Although I'm a proponent of keeping the defaults "the same as they were", i.e. a flag to disable.

It's more intuitive to drag those sliders than the ones below. (imho)

ocornut commented 7 years ago

@johanwendin

I have a checkbox to flag whether it's premultiplied alpha or not with visuals to match:

What do you mean "with visuals to match", how does the checkbox affect anything in the color picker itself? Isn't that a priority for the purpose of your engine/material?

Although I'm a proponent of keeping the defaults "the same as they were", i.e. a flag to disable.

There's no really "same as they were" as this is a new feature and everything is in beta branch for the reason it's not locked down. That's just to say, we can take decision without worrying about that part at least :)

I'm now looking at displaying an Alpha Grid as well, will post an update. The AlphaBar is definitively still available, but I find it a little odd to make it the default.

johanwendin commented 7 years ago

@omar I've rewritten the GUI code to use premultiplied alpha internally, so without that checkbox the RGB gets divided by alpha but with it checked it leaves the values as is. The visuals I'm referring to in this case is the color square - not the gradient box. Sorry if that wasn't clear :)

ocornut commented 7 years ago

I've rewritten the GUI code to use premultiplied alpha internally Without that checkbox the RGB gets divided by alpha

I don't understand (and I understand how pre-multiplied alpha works and why it is better) how your color picker can apply pre-mul alpha and keep exposing non pre-mul values as shown in your picture, since it is a potentially lossy or destructive operation. Your screenshot shows Alpha 0.0 based on this you shouldn't be able to go back from a pre-mul color to the non-pre-mul color. Or do you mean your own code somewhere in the pipeline pre-multiply the color? In which case this has nothing to do with the color picker?

Can you elaborate? By "rewritten the GUI code" you mean the imgui code? What is the point of changing it to use premul alpha for imgui side? Do you have a patch for it?

johanwendin commented 7 years ago

@omar my own code does the pre-multiply, and it doesn't really have anything to do with the color picker. I just looked through the code and I'm confusing two different issues. Sorry.

I have my own copy that does what I want, so none of my worries apply to ImGui. Carry on :)

ocornut commented 7 years ago

and it doesn't really have anything to do with the color picker.

Ok well that's good then :)

I have my own copy that does what I want, so none of my worries apply to ImGui. Carry on :)

But my goal is to make the official Color Edit/Picker tools useful and modular for as many case as possible so you don't need to use your own picker, this is also why we are discussing this here.

Right now one thing I haven't solved is if you are opening the color picker as a popup from a ColorEdit widget, currently you won't easily be able to append your own stuff (e.g. Your "Additive" checkbox) in that popup. It's not clear from your screenshot if the picker shown is a popup emitted from ColorEdit4(), or a popup emitted from your own code. If it's not you won't have issue using the new picker inside a popup and appending extra stuff below it.

PS: My github login is @ocornut, someone else is getting notifications for this (sorry, other Omar!)

ocornut commented 7 years ago

Here's current APIs

bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);     // 3-4 components color edition. click on colored squared to open a color picker, right-click for options. Hint: 'float col[3]' function argument is same as 'float* col'. You can pass address of first element out of a contiguous structure, e.g. &myvector.x
bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0);
bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);
bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0);
bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0));  // display a colored square/button, hover for details, return true when pressed.

There's a specific/subtle reason ColorButton doesn't take a pointer to float but a ImVec4() is that it is more likely to be called with r-value (hardcoded or code-computed colors), whereas ColorEdit/ColorPicker by definition are to modify existing data (l-value). It's probably a bit odd at first glance. Down the line other API taking r-value colors should allow packed ImU32 colors too.

Here's the current set of option

// Enumeration for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton()
enum ImGuiColorEditFlags_
{
    ImGuiColorEditFlags_RGB             = 1 << 0,   // ColorEdit: Default to one among RGB/HSV/HEX. User can still use the options menu to change. ColorPicker: Choose any combination or RGB/HSV/HEX.
    ImGuiColorEditFlags_HSV             = 1 << 1,   // "
    ImGuiColorEditFlags_HEX             = 1 << 2,   // "
    ImGuiColorEditFlags_Float           = 1 << 3,   // ColorEdit, ColorPicker, ColorButton: display values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers.
    ImGuiColorEditFlags_AlphaBar        = 1 << 4,   // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker.
    ImGuiColorEditFlags_AlphaPreview    = 1 << 5,   // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque.
    ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 6,   // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard
    ImGuiColorEditFlags_NoAlpha         = 1 << 7,   // ColorEdit, ColorPicker, ColorButton: completely ignore Alpha component (read 3 components).
    ImGuiColorEditFlags_NoPicker        = 1 << 8,   // ColorEdit: disable picker when clicking on colored square.
    ImGuiColorEditFlags_NoOptions       = 1 << 9,   // ColorEdit: disable toggling options menu when right-clicking on colored square.
    ImGuiColorEditFlags_NoColorSquare   = 1 << 10,  // ColorEdit, ColorPicker: disable colored square.
    ImGuiColorEditFlags_NoInputs        = 1 << 12,  // ColorEdit, ColorPicker: disable inputs sliders/text widgets, show only the colored square.
    ImGuiColorEditFlags_NoTooltip       = 1 << 13,  // ColorEdit, ColorButton: disable tooltip when hovering the colored square.
    ImGuiColorEditFlags_NoLabel         = 1 << 14,  // ColorEdit, ColorPicker: disable display of inline text label (the label is still used in tooltip and picker).
    ImGuiColorEditFlags_ModeMask_       = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX,
    ImGuiColorEditFlags_StoredMask_     = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX|ImGuiColorEditFlags_Float
};

The small color square can preview alpha in different manners, e.g.: halfalpha

More demo variations: picker2

Context-menu (not sure with the fact that options/override are stored per-widget. maybe the id/key for that storage could be per based on parent scope? mode

Not done with a few things (e.g. especially the colored preview square placement and layout in non-oneliner configuration, inline vs popup picker - images above shows some inconsistent experiments).

johanwendin commented 7 years ago

@ocornut sorry about the wrong name-tag. Not much is going well lately. :)

Mine has no popup at all, it's visible as in the screenshots at all times. (the color square isn't a button there, just a filled rect with a checkerboard background)

omar commented 7 years ago

I have no idea what's going on here, but LGTM.

cgimenez commented 7 years ago

Everything LGTUs cos' sun is always shining on ImGui's world : we've been freed from retained GUIs that frightened us since we were children. We do remember the mid-eighties and the resources managed UI system of mac OS is still haunting us. For years we have been trying to find a way out without solution, but now we can have beautiful stuff up and running in no time. We all CAN code UIs. We have no fear, this is divine ;-)

ocornut commented 7 years ago

I think it is near ready to be merged into Master.

Everyone interested in the color picker: I would appreciate if you could check out this branch or merge it into your copy and experiment with it. You can play around in the demo window in Widgets->Color/Picker Widgets.

If you already had a color picker in place: is this is satisfactory as a stock replacement (= are you happy to ditch your own color picker?). Answering that would be very useful.

I am going to rename ImGuiColorEditFlags_NoColorSquare to something more persistent with some of the other options, maybe it'll become ImGuiColorEditFlags_NoPreview or ImGuiColorEditFlags_NoSmallPreview. (But I'm off now so will think about it tomorrow).

I changed the options storage to be keyed by window ID, so changing a single ColorEdit4() from RGB to HSV in the context menu would change all of them which make much more sense. (Rambling/notes below:) There's still some confusion/problem remaining with those options: how to behave when explicit RGB/HSV/HEX mode are provided as flags in code, and the NoOptions flag isn't set. Should we block those menus from the context menu? Should we ignore the last context menu setting (it makes sense but remove some flexibility, especially as there are other options)? Should be we override the passed mode with the last context-menu setting (which could feel like we are ignoring a parameter?).

nem0 commented 7 years ago

@ocornut I found only a very small issue:

image

cursors are invisible against white background.

What do you think about adding something like

image

I would replace my color picker with the imgui's version.

ocornut commented 7 years ago

Will look at the cursor. Not sure what is more suitable, even the Border color may not be suitable everywhere (eg i don't think border color would work well with current default theme).

For the palette: it's probably better if you add it yourself over/next to the picker. If you called the picker and made the popup yourself it is trivial (you should need to calculate the starting position yourself).

If you called it from ColorEdit4() maybe call that without the colored button and then add a colored button yourself, so you can have control over the popup?

I would replace my color picker with the imgui's version

My question then is: even without the palette selection, why wouldn't you replace it right now?

nem0 commented 7 years ago

I miss nothing else. I would use your version and add the last used colors (palette).

ocornut commented 7 years ago

I'll try to add a custom palette in the demo. Do you drag'n drop to add to the palette?

nem0 commented 7 years ago

Nope, it's the last used colors, so it's automatically added. It's useful when I'm setting several different properties in a row to the same value.

ocornut commented 7 years ago

You can actually also use the (optional) reference color for that, except the currently hardcoded string "Original" is misleading.

r-lyeh-archived commented 7 years ago

About extending a color picker with custom imgui code... would it be possible to do something like following?

if( ImGui::ColorPicker4("my picker", rgba, 0) ) {
    ImGui::Button("this button is after picker, inside popup"); ImGui::SameLine();
    ImGui::Button("another one");
}
ImGui::End();
ocornut commented 7 years ago

ColorPicker4() doesn't create a popup, its a widget you can use anywhere (so, compose your own popup)

ColorEdit4() is the one creating a popup. You can use ColorButton() and create your popup. Or You can split into ColorEdit4() + ColorButton(). I'll try that, maybe some sizing issues to be solved to align that easily.

ocornut commented 7 years ago
  1. I have made the vertical bar cursors uses white+black arrows
  2. And within the color matrix itself I am now using a circle which previews the colors, it seems much nicer.

picker cursor

ocornut commented 7 years ago

Added a custom picker popup example:

custom palette

Example code:

ImGui::Text("Color button with Custom Picker Popup:");
static bool saved_palette_inited = false;
static ImVec4 saved_palette[32];
static ImVec4 backup_color;
if (!saved_palette_inited)
    for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
        ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z);
bool open_popup = ImGui::ColorButton("MyColor##3b", color, alpha_preview_flags);
ImGui::SameLine();
open_popup |= ImGui::Button("Palette");
if (open_popup)
{
    ImGui::OpenPopup("mypicker");
    backup_color = color;
}
if (ImGui::BeginPopup("mypicker"))
{
    // FIXME: Adding a drag and drop example here would be perfect!
    ImGui::Text("MY FANCY COLOR PICKER!");
    ImGui::Separator();
    ImGui::ColorPicker4("##picker", (float*)&color, alpha_preview_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoColorSquare);
    ImGui::SameLine();
    ImGui::BeginGroup();
    ImGui::Text("Current");
    ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreview, ImVec2(60,40));
    ImGui::Text("Previous");
    if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreview, ImVec2(60,40)))
        color = backup_color;
    ImGui::Separator();
    ImGui::Text("Palette");
    for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)
    {
        ImGui::PushID(n);
        if ((n % 8) != 0)
            ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);
        if (ImGui::ColorButton("##palette", saved_palette[n], ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip, ImVec2(20,20)))
            color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!
        ImGui::PopID();
    }
    ImGui::EndGroup();
    ImGui::EndPopup();
}
ocornut commented 7 years ago

Massive tagging (sorry!) of interested parties for feedback before merging to Master.

@jdumas @johanwendin @nem0 @benoitjacquier @ApoorvaJ @WearyWanderer @r-lyeh @thennequin @DubbleClick @cgimenez

One thing to understand is that the three main functions ColorEdit4, ColorPicker4 ColorButton, with those millions flags are designed so you may be able to ignore/hide bits and reassemble something custom if it makes sense for your application. But the default should be good enough for many user (e.g. just call ColorEdit4 exactly as done before and you can click on the small square to get a picker). Check the demo window + code under Widgets->Color/Picker Widgets to play around and get a feel of the various options.

Edit to clarify, the palette here is part of the demo for building a custom popup use of a base picker and adding your own stuff. picker f 04

API:

bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0));  // display a colored square/button, hover for details, return true when pressed.
bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);     // 3-4 components color edition. click on colored squared to open a color picker, right-click for options. Hint: 'float col[3]' function argument is same as 'float* col'. You can pass address of first element out of a contiguous structure, e.g. &myvector.x
bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0);
bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);
bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL);

Flags: (edited)

// Enumeration for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton()
enum ImGuiColorEditFlags_
{
    ImGuiColorEditFlags_RGB             = 1 << 1,   // ColorEdit: default to one among RGB/HSV/HEX. User can still use the options menu to change. ColorPicker: Choose any combination or RGB/HSV/HEX.
    ImGuiColorEditFlags_HSV             = 1 << 2,   // "
    ImGuiColorEditFlags_HEX             = 1 << 3,   // "
    ImGuiColorEditFlags_Float           = 1 << 4,   // ColorEdit, ColorPicker, ColorButton: display values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers.
    ImGuiColorEditFlags_HDR             = 1 << 5,   // ColorEdit: disable 0.0f..1.0f limits (note: you probably want to use ImGuiColorEditFlags_Float flag as well).
    ImGuiColorEditFlags_AlphaBar        = 1 << 6,   // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker.
    ImGuiColorEditFlags_AlphaPreview    = 1 << 7,   // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque.
    ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 8,   // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque.
    ImGuiColorEditFlags_NoAlpha         = 1 << 9,   // ColorEdit, ColorPicker, ColorButton: completely ignore Alpha component (read 3 components from the input pointer).
    ImGuiColorEditFlags_NoPicker        = 1 << 10,   // ColorEdit: disable picker when clicking on colored square.
    ImGuiColorEditFlags_NoOptions       = 1 << 11,  // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview.
    ImGuiColorEditFlags_NoSmallPreview  = 1 << 12,  // ColorEdit, ColorPicker: disable colored square preview next to the inputs. (e.g. to show only the inputs)
    ImGuiColorEditFlags_NoInputs        = 1 << 13,  // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview colored square).
    ImGuiColorEditFlags_NoTooltip       = 1 << 14,  // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview.
    ImGuiColorEditFlags_NoLabel         = 1 << 15,  // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker).
    ImGuiColorEditFlags_NoSidePreview   = 1 << 16,  // ColorPicker: disable bigger color preview on right side of the picker, use small colored square preview instead.
    ImGuiColorEditFlags_ModeMask_       = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX,
    ImGuiColorEditFlags_StoredMask_     = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX|ImGuiColorEditFlags_Float
};
johanwendin commented 7 years ago

I checked it out, built it with 2017 community and it works really well.

You even got the "click on reference color to return to it". Well done!

It's Just missing the color wheel from Micko now. ;) ;) (just kidding)

ocornut commented 7 years ago

A) alpha bar I tried changing the alpha bar to show a checkerboard with the color overlayed:

alpha_bar_with_checkerboard

I think looks nicer - but somehow feels imprecise to look at? I'm not sure what's best.

B)

It's Just missing the color wheel from Micko now. ;) ;) (just kidding)

Well, at the very least it would be nice if it was easy for user to plug in new features without forking imgui!

C) HDR cupe_cupe on twitter says he'd need an HDR color picker.

main feedback: We need HDR color pickers, and we need lots of them on screen in non-modal way. Will show you ours when I get home. circle for hue+saturation, vertical slider for multiplier (0 to 10000 or so). both are relative, so dragging with modifier is 1/10 delta there's a little indicator on the circle to show current hue+sat. But you don't have to grab it, just start dragging anywhere on circle Ours doesn't even display the current color as a colored box or similar, because what matters is its effect that can be seen realtime anyway

I'm not exactly sure how that specific interface would look like, but interesting in hearing feedback from people who use HDR.

e.g. Add an ImGuiColorEditFlags_HDR flag which could 1) lift all the clamping/limits (currently you can already ctrl+click to set values outside the ranges) 2) scale the picker Y axis according to current brightness? 3) perhaps default to _Float display? 4) somehow display colors in a way that shows the "falloff" color. is there's any standard on how to display that color? scale the hdr color vector so that isn't max component is 1.0 ?

If you have any references about HDR picker I'll take them, I don't know much about colors.

HDR picking related links: https://docs.unity3d.com/Manual/HDRColorPicker.html (Unity's HDR picker) https://www.filterforge.com/features/version2/hdr-support.html

D) Other interesting picker related links for future work: http://help.lightmap.co.uk/hdrlightstudio5/reference_color_picker.html https://help.thefoundry.co.uk/modo/901/content/help/pages/modo_interface/viewports/utility/color_picker.html

ocornut commented 7 years ago

@dariomanesku @bkaradzic Do you mind if I clone/rewrite that old color wheel and relicence as MIT? (bgfx is BSD-2). Did it have any issues / things you would prefer to be changed?

cmftstudio_win4-picker

https://github.com/bkaradzic/bgfx/commit/5084649832edadbc35b4454516fabc88eb8c4004 https://github.com/bkaradzic/bgfx/blob/5084649832edadbc35b4454516fabc88eb8c4004/examples/common/imgui/imgui.cpp https://github.com/bkaradzic/bgfx/blob/5084649832edadbc35b4454516fabc88eb8c4004/examples/20-nanovg/nanovg.cpp

(One bottleneck here is that we don't have nanovg-style Paint system for coloring over arbitrary shape (here a gradient over the 6 arcs). I have a basic patch to add a similar feature to ImDrawList::AddConvexPolyFilled, but perhaps we can just get away and render the circle with thick segment. The basic linear-gradient paint is simple but if we start going into that direction of adding that to ImDrawList I will probably want to design it further)

ocornut commented 7 years ago

One bottleneck here is that we don't have nanovg-style Paint system for coloring over arbitrary shape (here a gradient over the 6 arcs).

Solved my problem by painting over the vertex colors after submitting the arcs, making it easy to draft a vertex color painting API without having to expose it in ImDrawList.

I've got the basic rendering working locally (but no inputs yet): wheel

dariomanesku commented 7 years ago

@dariomanesku @bkaradzic Do you mind if I clone/rewrite that old color wheel and relicence as MIT? (bgfx is BSD-2).

If you ask me, feel free to take it. I wrote intput/output for that color wheel back in the day on top of nanovg. Right now I'm using Dear Imgui and I would love to have it there, so :+1:

ocornut commented 7 years ago

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 commented 7 years ago

What's it with the green pixels all over?

ocornut commented 7 years ago

@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 commented 7 years ago

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

ocornut commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

Aww so cute :)

bkaradzic commented 7 years ago

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

ocornut commented 7 years ago

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 commented 7 years ago

That's awesome! :) 👍

ntpopgetdope commented 7 years ago

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

ocornut commented 7 years ago

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:

pickier_option3

Added the SetColorEditOptions() which allows to

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

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 commented 7 years ago

Closing the color picker topic (exactly 100 messages :)

Some ideas for later:

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.