CARVIEW |
Navigation Menu
-
-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Description
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:
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)
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:
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));
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)