Open itamago opened 7 years ago
This feature is purely optional
Nothing is purely optional! The code has a cost even if you don't use it + maintainance cost + raising expectations of styling is a never-ending path.
It's not too hard to implement but it would incur a small overhead (and may be tricky to remove it even when gradients are disabled?). Basically the simplest would be to post-op rewrite the colors after drawing shapes. It'd be less intrusive, perhaps a little slower when gradient are involved, because we'd write over vertices twice, but probably worthy overall.
And we hard-code it for single-axis (Y) it'd be less computation.
Pseudo-code to express what I was thinking for this:
void ImDrawList::AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col_top, ImU32 col_bottom, float rounding, int rounding_corners_flags)
{
if ((col & IM_COL32_A_MASK) == 0)
return;
++ImDrawVert* vtx_paint_start = _VtxWritePtr;
if (rounding > 0.0f)
{
PathRect(a, b, rounding, rounding_corners_flags);
PathFillConvex(col);
}
else
{
PrimReserve(6, 4);
PrimRect(a, b, col);
}
++ImDrawVert* vtx_paint_end = _VtxWritePtr;
++PaintColorGradientOverVertices(vtx_paint_start, vtx_paint_end, a.y, b.y, col_top, col_bottom);
}
Biggest overhead is in plumbing it all and finding way for it to not cost much when disabled.
(Bonus opinion: Personally I find those gradients rather ugly but apparently I am a minority with this opinion!)
I think making the style system more complex to tweak and to dynamically change is also quite a problem. Suddenly you can't use PushStyleColor()
as quickly.
Idea for a solution: Perhaps gradient and hovered/active variations should be expressed by the user as delta in RGB or HSV space, and imgui would compute then automatically as you edit the style via PushStyleColor() or at the beginning of the frame.
This is also relate to your PR https://github.com/ocornut/imgui/pull/511 which I refused for similar reason or paying hard-to-remove pollution for a short term solution. Expressing variants as delta would be helpful there (HSV delta would be costly to naively recompute in case the user is abusing of PushStyleColor.?)
I would like to revamp the color storage so we can stop manipulating the ImVec4 and multiplying by Alpha everytime a color is used as well. So perhaps there would be user-side Colors[] array with fancy high-level concept such as BaseColor+HoveredDeltaHSV+ActiveDeltaHSV+GradientDeltaHSV? and imgui packs and maintain of all of that into ImU32.
This is also in line with what I initially wanted to do for v1.50 in https://github.com/ocornut/imgui/issues/707 and moved to probably v1.51. It would make a nice release of combining this (more styling options, less overhead) + rounding render optimization + merging color picker?
Color customisation is the only reel enhancement ImGui needs in terms of styling (that's my opinion, obviously). Today, colors customisation are accessible via a long list of enums (ImGuiCol_). Instead of enums, I would have seen a bitfield, which would make it more flexible for the future and reduce the complexity.
We may want to decompose ImGuiCol_ in 2 things : the color-target and the widget component.
If I take the current enum values, I would do something like this to identify which color target the bitfield is referring to:
enum class ColorTarget : int64_t
{
Background = INT64_C(1)<<(0 + 32),
Foreground = INT64_C(1)<<(1 + 32),
Hovered = INT64_C(1)<<(2 + 32),
Active = INT64_C(1)<<(3 + 32),
Disabled = INT64_C(1)<<(4 + 32),
Collapsed = INT64_C(1)<<(5 + 32),
// here, you could extend the list when you add new customisation features
}
and for the widget component:
enum class WidgetComponent : int64_t
{
Text = (1<<0),
TextSelected = (1<<1),
Window = (1<<2),
Popup = (1<<3),
Border = (1<<4),
Frame = (1<<5),
Title = (1<<6),
MenuBar = (1<<7),
Scrollbar = (1<<8),
Combo = (1<<9),
Checkmark = (1<<10),
SliderGrab = (1<<11),
Button = (1<<12),
Column = (1<<13),
ResizeGrip = (1<<14),
CloseButton = (1<<16),
PlotLines = (1<<17),
PlotHistogram = (1<<18),
// here, you could extend the list when you add new widget components
}
(I may have forgotten some values..) (if one doesn't like/want cpp11, the example would remain the same with standard int enums)
So, instead of calling today:
IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col);
Ex:
ImGui::PushStyleColor(ImGuiCol_ComboBg, ImVec4(1,1,1,1));
you would call:
IMGUI_API void PushStyleColor(int64_t bitfield, const ImVec4& col);
Ex:
ImGui::PushStyleColor( int64_t(WidgetComponent::Combo) | int64_t(ColorTarget::Background), ImVec4(1,1,1,1));
and you may even set multiple colors with a single call:
ImGui::PushStyleColor( int64_t(WidgetComponent::Combo) | int64_t(WidgetComponent::SliderGrab) | int64_t(WidgetComponent::Button) | int64_t(ColorTarget::Background), ImVec4(1,1,1,1));
About my PR #511, it wouldn't require any new enum values to be supported:
ImGui::PushStyleColor( int64_t(WidgetComponent::Text) | int64_t(ColorTarget::Hovered), ImVec4(1,1,1,1));
ImGui::PushStyleColor( int64_t(WidgetComponent::Text) | int64_t(ColorTarget::Active), ImVec4(1,1,1,1));
Obviously, not all the combination of (WidgetComponent) x (ColorTarget) would be supported at the beginning, and some of them wouldn't make any sense. The doc (in the cpp file) would describe for each widget which colors are supported.
Coming back to the original topic, adding a new color customisation becomes easier in terms of user interface without breaking previous compatibility. For gradient coloring, I would add something like:
enum class ColorTarget
{
...
Collapsed = INT64_C(1) <<(5 + 32),
// here, you could extend the list when you add new customisation features
BackgroundBottom = Background,
BackgroundTop = INT64_C(1)<<(6 + 32),
}
Maybe you have something completely different in mind. Or maybe I don't see hidden complexity of ImGui coloring. If so, forget my idea :)
Interesting ideas, thanks for writing that up. Writing down a few more things (sorry it's really unstructured)
Making the two sections of flags a sequential list of integers (not individual flags), would allow more data to be packed and be somehow more simple to parse/look at/use at runtime to index tables. Widgets needs to lookup their raw color they need with minimum operation, so ideally a linear enum exists somewhere which they can access (the drawback is to lose the ability to set multiple color together.)
On #511 there is still a misconception because you called your enum TextHovered/TextActive but the name is really wrong. The correct name would be along the lines of Text-over-Header, Text-over-Header-Active.
See @petrihakkinen spot-on comment and screenshots in #511 which is a generalization of your idea:
Personally I would just add text colors ImGuiCol_XXXText for every widget that has a customizable background color.
Except Petri implies 1 text color for all 3 bg variations, and Pacome explicitly wants different text color for bg variations.
So possibly Text
becomes a ColorTarget bit by Pacome's terminology, or can be a while third component in the flag set, e.g. Button | Text | Hovered
is a valid combo.
For button the list would be:
Which is quite a lot. Also consider if you want gradients ButtonBg is x2 in theory that's 12 values.
If we have a system where "NULL/default" can be encoded we can establish simple obvious rules such as "ButtonTextHovered and ButtonTextActive defaults to ButtonText", "ButtonText" default to "Text", "BgBottom" default to "Bg" etc. Dropping single enum and introducing flags as Pacome suggested would make this natural to comprehend and easy to implement (it's obviously out of question to have a 100+ list raw enum).
Using HSV-delta would be very useful to manage those inherited defaults, and the UI can still allow you to edit an absolute RGB value if you need such.
I'll try to prototype something to clarify this.
You're right : by generalising the color of text, then text
becomes a ColorTarget
(instead of WidgetComponent).
Pasting some discussions about colors from Anton M
I dunno if that's the fastest hsv/rgb conversion, I've not actually tried optimizing it in (I think we use this same one in dreams lol). I vaguely recall you can do a bit better with simd, but probably not worth it.
In terms of how good of an idea it is, seems good to me. I end up doing something like this on top of imgui often.
That being said, I often find that my results via an automatic scheme aren't always great :/ so it depends on your bar.
For example, say I have 6 kinds of things in my app, so I consistently use 6 hues to color their sliders or graphs or whatever. Presumably id want to just have one table that maps that enum to a hue. Then all my colorscheme code just pushes that pure hue in HSV(hue,1,1), and the rest of the "scheme" is via deltas that presumably work equally on any hue.
But what I find in practice is that depending on the hue, the delta fits better or worse. For example let's say I tuned some graph to be .7 saturation of base hue. I did this tuning with a Red hue. What you'll usually find is that when that same delta is applied to Green or Yellow, it really won't be the same saturation, visually, as your Red one. You've probably already read about this, but if not this is a good primer on why this happens:
https://www.vis4.net/blog/posts/avoid-equidistant-hsv-colors/
So while "using hsv color deltas to reduce number of hardcoded rgb colors" sounds like a good idea, it doesn't really yield as good results as hand tuned.
To remedy this you can go down the rabbit hole of perceptually uniform color spaces (like Lab mentioned in that article). But that is even slower, and it has other annoyances (such as being more gamut bound, and not necessarily having the same mathematical limits on all hues (so maybe max green saturation is like .9 but blue is .7) which can me annoying in code if you're not bought into it). In those spaces it's easy to delta move into "invalid color space", so that may be or may not be ok for you, I dunno. But at least the deltas are prettier and more consistent.
I made a "best effort" geometric and perceptual color space for dreams. It's hand tuned to be somewhere between HSV and Lab. So all values are 0-1, but instead of yielding invalid colors it's simply "not as uniform" as Lab, but a bit better than HSV. I'm mildly happy with it, but ultimately there isn't a perfect solution.
So it all depends on how "good" you want the deltas to look. For programmer ui, where color=label information, it's perfectly fine. For data vis, where color=value information, Lab is preferred (or other methods). For best looking ui artistically, it's very hard to beat hand tuned rgb, since designing a great looking delta set is nearly as hard as speccing the colors by hand.
This is kind of a good "high bar" for what schemes can look like. I'm not certain, but I think it uses an hsv-like space like the one I wrote for dreams.
http://paletton.com/#uid=1000u0kllllaFw0g0qFqFg0w0aF
If you do go down the rabbit hole let me know! I have many rabbit tunnel maps :)
I see what you mean, yeah it doesn't prevent you from hand a authoring it I suppose. And you're right, if I was going to hand author them, I'd probably use deltas anyway. In the past what I've done was used a two step delta, a "base delta" and a "per object override" for those that needed it. But it seemed really complicated for a job you kind of just do once and hardcode and leave :P
Either way, makes sense to me :)
TL;DR; I think HSV-delta are probably fine as long as the Style Editing UI is nice enough (e.g. Edit as absolute RGB and HSV, store as HSV-delta).
Copying some experiments done this summer: (aim was to see how/where we can push creation of custom widgets):
Nice, do you think making those prototype widgets available (as a gist maybe)?
I love the outline of texts
Do you expect to release it in a existing branch ? Thx
They are merely experiments at this point (all the branches are in a private repo we give access to supporters for toying and feedback).
Currently the gradient shading and shadows add too much overhead so i’d like us to fix a few things before releasing eg even a gist, and move toward making some variations of the shadow primitives available in master.
IMHO, the overhead of vertical gradients shouldn't be significant : even if it multiples by 4 or 5 the number of triangles (due to new vertices) to draw a background, it remains negligable in comparison with round borders or text rendering, right ?
For my understanding, by shadows you means the outline of text is rendered using "shadows" and not FreeType's outline feature ? Which means it would be compatible with stb_truetype, being a very good news !
Gradient don’t add vertices at this point there are a simple post-process on existing vertices, it is fast but could be done faster if we build things in one pass.
Yes the outline are done manually, not ideal but flexible. There are also (subtle) shadows under many widgets which was i was referring too initially.
These are good news, really ! These two features would help building high-quality UI. When some of them are available in public branches, I would be pleased to integrate them. Thanks !
Hi @ocornut , That experiments looks really good. Have you published any experimental branch yet?
Hello, I am playing with Blender app these days. The look-n-feel is great, the rest is discutable. Everything in Blender could be done with stock ImGui, except one missing background customisation : a vertical gradient.
Please look at the screenshot :
As you can see, all controls support a light vertical gradient in the background, giving a sense of perspective.
It could be implemented in ImGui by setting 2 (or 3, 4) colors for the Background which would be interpolated. I guess the 2-color version would even be trivial to implement now :)
This feature is purely optional, but having that possibility would allow to enhance the styling of ImGui. And I would be pleased to reproduce the Blender style configuration with ImGui !
Best regards