Skip to content
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

Using gradients in widgets #4722

Open
ocornut opened this issue Nov 15, 2021 · 3 comments
Open

Using gradients in widgets #4722

ocornut opened this issue Nov 15, 2021 · 3 comments

Comments

@ocornut
Copy link
Owner

ocornut commented Nov 15, 2021

Following a nice screenshot posted recently I thought about opening a topic to gather ideas about using gradients.

We've experimented with this for a while actually. The plan is that Styling V2 (NO ETA) should support the use of gradients on filled surfaces. This has been the plan for a long time (#1223) and still is, but hey, resources. In the meanwhile it is reasonably easy to add gradients to SOME widgets by rewiring a few functions. I noticed that merely using a button with gradient can help a lot with visuals (the problem with this approach is that mixing your own buttons with other codes you won't get the nicer style on the "other code", something which styling v2 should address).

As part of work on shadows (#1329 (comment)) I attempted such hack:

image

I also commissioned @ShironekoBen last year to run some private experiments of 100% custom widgets trying to mimick the style of Unity and OSX: (those 2 are dear imgui screenshots with custom widgets)

image

image

One of the underlying goal is to figure out two things:

  • Help designing reasonable specs for Styling V2.
  • Help refactoring internals toward making the creation of custom widgets easier (and less prone to breaking changes andforward compatibility issue).

Recently @martinpetkovski posted about their work on NST (#4451 (comment)) which suggested that with a simple custom button widget you can actually get really good results without much code nor hacking:

image

Today I sat down to write a button widget.
I copied imgui_widgets.cpp ButtonEx() into my own source file and modified it.
It's currently an awkward amount of code because some internals are not prepared for it yet.

// Header
namespace ImGui
{
    bool ColoredButtonV1(const char* label, const ImVec2& size, ImU32 text_color, ImU32 bg_color_1, ImU32 bg_color_2);
}

#define IMGUI_DEFINE_MATH_OPERATORS
#include "imgui_internal.h"

// Implementation
bool ImGui::ColoredButtonV1(const char* label, const ImVec2& size_arg, ImU32 text_color, ImU32 bg_color_1, ImU32 bg_color_2)
{
    ImGuiWindow* window = GetCurrentWindow();
    if (window->SkipItems)
        return false;

    ImGuiContext& g = *GImGui;
    const ImGuiStyle& style = g.Style;
    const ImGuiID id = window->GetID(label);
    const ImVec2 label_size = CalcTextSize(label, NULL, true);

    ImVec2 pos = window->DC.CursorPos;
    ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);

    const ImRect bb(pos, pos + size);
    ItemSize(size, style.FramePadding.y);
    if (!ItemAdd(bb, id))
        return false;

    ImGuiButtonFlags flags = ImGuiButtonFlags_None;
    if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat)
        flags |= ImGuiButtonFlags_Repeat;

    bool hovered, held;
    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);

    // Render
    const bool is_gradient = bg_color_1 != bg_color_2;
    if (held || hovered)
    {
        // Modify colors (ultimately this can be prebaked in the style)
        float h_increase = (held && hovered) ? 0.02f : 0.02f;
        float v_increase = (held && hovered) ? 0.20f : 0.07f;

        ImVec4 bg1f = ColorConvertU32ToFloat4(bg_color_1);
        ColorConvertRGBtoHSV(bg1f.x, bg1f.y, bg1f.z, bg1f.x, bg1f.y, bg1f.z);
        bg1f.x = ImMin(bg1f.x + h_increase, 1.0f);
        bg1f.z = ImMin(bg1f.z + v_increase, 1.0f);
        ColorConvertHSVtoRGB(bg1f.x, bg1f.y, bg1f.z, bg1f.x, bg1f.y, bg1f.z);
        bg_color_1 = GetColorU32(bg1f);
        if (is_gradient)
        {
            ImVec4 bg2f = ColorConvertU32ToFloat4(bg_color_2);
            ColorConvertRGBtoHSV(bg2f.x, bg2f.y, bg2f.z, bg2f.x, bg2f.y, bg2f.z);
            bg2f.z = ImMin(bg2f.z + h_increase, 1.0f);
            bg2f.z = ImMin(bg2f.z + v_increase, 1.0f);
            ColorConvertHSVtoRGB(bg2f.x, bg2f.y, bg2f.z, bg2f.x, bg2f.y, bg2f.z);
            bg_color_2 = GetColorU32(bg2f);
        }
        else
        {
            bg_color_2 = bg_color_1;
        }
    }
    RenderNavHighlight(bb, id);

#if 0
    // V1 : faster but prevents rounding
    window->DrawList->AddRectFilledMultiColor(bb.Min, bb.Max, bg_color_1, bg_color_1, bg_color_2, bg_color_2);
    if (g.Style.FrameBorderSize > 0.0f)
        window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), 0.0f, 0, g.Style.FrameBorderSize);
#endif

    // V2
    int vert_start_idx = window->DrawList->VtxBuffer.Size;
    window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_color_1, g.Style.FrameRounding);
    int vert_end_idx = window->DrawList->VtxBuffer.Size;
    if (is_gradient)
        ShadeVertsLinearColorGradientKeepAlpha(window->DrawList, vert_start_idx, vert_end_idx, bb.Min, bb.GetBL(), bg_color_1, bg_color_2);
    if (g.Style.FrameBorderSize > 0.0f)
        window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border), g.Style.FrameRounding, 0, g.Style.FrameBorderSize);

    if (g.LogEnabled)
        LogSetNextTextDecoration("[", "]");
    PushStyleColor(ImGuiCol_Text, text_color);
    RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);
    PopStyleColor();

    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
    return pressed;
}

Usage

GradientButtonV1("Hello", ImVec2(-FLT_MIN, 0.0f), IM_COL32(255, 255, 255, 255), IM_COL32(200, 60, 60, 255), IM_COL32(180, 40, 90, 255));

GradientButtonV1("You", ImVec2(-FLT_MIN, 0.0f), IM_COL32(255, 255, 255, 255), IM_COL32(50, 220, 60, 255), IM_COL32(69, 150, 70, 255));

image

image

Future simplification will be that:

  • down the line the colors can be in style so no need to do the hsv/rgb computation
  • will have a helper function to draw frame with gradients
  • render text function will accept a color to prevent the push/pop
  • and eventually this will essentially become the default (Button() with no parameter use gradient from style)
@athanggupte
Copy link

Looks great! Hoping to see this merged to Styling V2 soon. Till then I guess working up from this function for other widgets would be the way to go for most users who want to add gradient-colored widgets?

@ocornut
Copy link
Owner Author

ocornut commented Nov 15, 2021

Till then I guess working up from this function for other widgets would be the way to go for most users who want to add gradient-colored widgets?

I would suggest people to not go too far with this as you are likely to be increasingly dependent on internals and possible mods to your copy. But technically you could hack imgui sources (you are on your own) or just use this on a few selected widgets for now (Button being a great candidate).

@paragon-747
Copy link

Hello, I've been reading up on the state of gradient capabilities throughout the project, and explored some users' custom gradient widgets before phrasing a question (@moebiussurfing was kind enough to introduce me to the work of CoolLibs and effekseer). Something I have failed to understand as of writing is the application of these gradients to existing color settings in Dear ImGui. Essentially, I would like to apply a two-color gradient to ImGuiCol_Border to create a multicolored window border. I still plan to examine AddRectFilledMultiColor to see if I can generalize anything about how to incorporate more than one ImColor, but any insights or suggestions would be much appreciated.

(Also this is my first question directly to this GitHub, and after reading Contributing.md thought it be better placed under this issue than creating a new post; if there's any ediquette I didn't follow please lmk so I know for next time!). Thanks!

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

No branches or pull requests

3 participants