-
-
Save galloscript/8a5d179e432e062550972afcd1ecf112 to your computer and use it in GitHub Desktop.
// | |
// imgui_color_gradient.cpp | |
// imgui extension | |
// | |
// Created by David Gallardo on 11/06/16. | |
#include "imgui_color_gradient.h" | |
#include "imgui_internal.h" | |
static const float GRADIENT_BAR_WIDGET_HEIGHT = 25; | |
static const float GRADIENT_BAR_EDITOR_HEIGHT = 40; | |
static const float GRADIENT_MARK_DELETE_DIFFY = 40; | |
ImGradient::ImGradient() | |
{ | |
addMark(0.0f, ImColor(0.0f,0.0f,0.0f)); | |
addMark(1.0f, ImColor(1.0f,1.0f,1.0f)); | |
} | |
ImGradient::~ImGradient() | |
{ | |
for (ImGradientMark* mark : m_marks) | |
{ | |
delete mark; | |
} | |
} | |
void ImGradient::addMark(float position, ImColor const color) | |
{ | |
position = ImClamp(position, 0.0f, 1.0f); | |
ImGradientMark* newMark = new ImGradientMark(); | |
newMark->position = position; | |
newMark->color[0] = color.Value.x; | |
newMark->color[1] = color.Value.y; | |
newMark->color[2] = color.Value.z; | |
m_marks.push_back(newMark); | |
refreshCache(); | |
} | |
void ImGradient::removeMark(ImGradientMark* mark) | |
{ | |
m_marks.remove(mark); | |
refreshCache(); | |
} | |
void ImGradient::getColorAt(float position, float* color) const | |
{ | |
position = ImClamp(position, 0.0f, 1.0f); | |
int cachePos = (position * 255); | |
cachePos *= 3; | |
color[0] = m_cachedValues[cachePos+0]; | |
color[1] = m_cachedValues[cachePos+1]; | |
color[2] = m_cachedValues[cachePos+2]; | |
} | |
void ImGradient::computeColorAt(float position, float* color) const | |
{ | |
position = ImClamp(position, 0.0f, 1.0f); | |
ImGradientMark* lower = nullptr; | |
ImGradientMark* upper = nullptr; | |
for(ImGradientMark* mark : m_marks) | |
{ | |
if(mark->position < position) | |
{ | |
if(!lower || lower->position < mark->position) | |
{ | |
lower = mark; | |
} | |
} | |
if(mark->position >= position) | |
{ | |
if(!upper || upper->position > mark->position) | |
{ | |
upper = mark; | |
} | |
} | |
} | |
if(upper && !lower) | |
{ | |
lower = upper; | |
} | |
else if(!upper && lower) | |
{ | |
upper = lower; | |
} | |
else if(!lower && !upper) | |
{ | |
color[0] = color[1] = color[2] = 0; | |
return; | |
} | |
if(upper == lower) | |
{ | |
color[0] = upper->color[0]; | |
color[1] = upper->color[1]; | |
color[2] = upper->color[2]; | |
} | |
else | |
{ | |
float distance = upper->position - lower->position; | |
float delta = (position - lower->position) / distance; | |
//lerp | |
color[0] = ((1.0f - delta) * lower->color[0]) + ((delta) * upper->color[0]); | |
color[1] = ((1.0f - delta) * lower->color[1]) + ((delta) * upper->color[1]); | |
color[2] = ((1.0f - delta) * lower->color[2]) + ((delta) * upper->color[2]); | |
} | |
} | |
void ImGradient::refreshCache() | |
{ | |
m_marks.sort([](const ImGradientMark * a, const ImGradientMark * b) { return a->position < b->position; }); | |
for(int i = 0; i < 256; ++i) | |
{ | |
computeColorAt(i/255.0f, &m_cachedValues[i*3]); | |
} | |
} | |
namespace ImGui | |
{ | |
static void DrawGradientBar(ImGradient* gradient, | |
struct ImVec2 const & bar_pos, | |
float maxWidth, | |
float height) | |
{ | |
ImVec4 colorA = {1,1,1,1}; | |
ImVec4 colorB = {1,1,1,1}; | |
float prevX = bar_pos.x; | |
float barBottom = bar_pos.y + height; | |
ImGradientMark* prevMark = nullptr; | |
ImDrawList* draw_list = ImGui::GetWindowDrawList(); | |
draw_list->AddRectFilled(ImVec2(bar_pos.x - 2, bar_pos.y - 2), | |
ImVec2(bar_pos.x + maxWidth + 2, barBottom + 2), | |
IM_COL32(100, 100, 100, 255)); | |
if(gradient->getMarks().size() == 0) | |
{ | |
draw_list->AddRectFilled(ImVec2(bar_pos.x, bar_pos.y), | |
ImVec2(bar_pos.x + maxWidth, barBottom), | |
IM_COL32(255, 255, 255, 255)); | |
} | |
ImU32 colorAU32 = 0; | |
ImU32 colorBU32 = 0; | |
for(auto markIt = gradient->getMarks().begin(); markIt != gradient->getMarks().end(); ++markIt ) | |
{ | |
ImGradientMark* mark = *markIt; | |
float from = prevX; | |
float to = prevX = bar_pos.x + mark->position * maxWidth; | |
if(prevMark == nullptr) | |
{ | |
colorA.x = mark->color[0]; | |
colorA.y = mark->color[1]; | |
colorA.z = mark->color[2]; | |
} | |
else | |
{ | |
colorA.x = prevMark->color[0]; | |
colorA.y = prevMark->color[1]; | |
colorA.z = prevMark->color[2]; | |
} | |
colorB.x = mark->color[0]; | |
colorB.y = mark->color[1]; | |
colorB.z = mark->color[2]; | |
colorAU32 = ImGui::ColorConvertFloat4ToU32(colorA); | |
colorBU32 = ImGui::ColorConvertFloat4ToU32(colorB); | |
if(mark->position > 0.0) | |
{ | |
draw_list->AddRectFilledMultiColor(ImVec2(from, bar_pos.y), | |
ImVec2(to, barBottom), | |
colorAU32, colorBU32, colorBU32, colorAU32); | |
} | |
prevMark = mark; | |
} | |
if(prevMark && prevMark->position < 1.0) | |
{ | |
draw_list->AddRectFilledMultiColor(ImVec2(prevX, bar_pos.y), | |
ImVec2(bar_pos.x + maxWidth, barBottom), | |
colorBU32, colorBU32, colorBU32, colorBU32); | |
} | |
ImGui::SetCursorScreenPos(ImVec2(bar_pos.x, bar_pos.y + height + 10.0f)); | |
} | |
static void DrawGradientMarks(ImGradient* gradient, | |
ImGradientMark* & draggingMark, | |
ImGradientMark* & selectedMark, | |
struct ImVec2 const & bar_pos, | |
float maxWidth, | |
float height) | |
{ | |
ImVec4 colorA = {1,1,1,1}; | |
ImVec4 colorB = {1,1,1,1}; | |
float barBottom = bar_pos.y + height; | |
ImGradientMark* prevMark = nullptr; | |
ImDrawList* draw_list = ImGui::GetWindowDrawList(); | |
ImU32 colorAU32 = 0; | |
ImU32 colorBU32 = 0; | |
for(auto markIt = gradient->getMarks().begin(); markIt != gradient->getMarks().end(); ++markIt ) | |
{ | |
ImGradientMark* mark = *markIt; | |
if(!selectedMark) | |
{ | |
selectedMark = mark; | |
} | |
float to = bar_pos.x + mark->position * maxWidth; | |
if(prevMark == nullptr) | |
{ | |
colorA.x = mark->color[0]; | |
colorA.y = mark->color[1]; | |
colorA.z = mark->color[2]; | |
} | |
else | |
{ | |
colorA.x = prevMark->color[0]; | |
colorA.y = prevMark->color[1]; | |
colorA.z = prevMark->color[2]; | |
} | |
colorB.x = mark->color[0]; | |
colorB.y = mark->color[1]; | |
colorB.z = mark->color[2]; | |
colorAU32 = ImGui::ColorConvertFloat4ToU32(colorA); | |
colorBU32 = ImGui::ColorConvertFloat4ToU32(colorB); | |
draw_list->AddTriangleFilled(ImVec2(to, bar_pos.y + (height - 6)), | |
ImVec2(to - 6, barBottom), | |
ImVec2(to + 6, barBottom), IM_COL32(100, 100, 100, 255)); | |
draw_list->AddRectFilled(ImVec2(to - 6, barBottom), | |
ImVec2(to + 6, bar_pos.y + (height + 12)), | |
IM_COL32(100, 100, 100, 255), 1.0f, 1.0f); | |
draw_list->AddRectFilled(ImVec2(to - 5, bar_pos.y + (height + 1)), | |
ImVec2(to + 5, bar_pos.y + (height + 11)), | |
IM_COL32(0, 0, 0, 255), 1.0f, 1.0f); | |
if(selectedMark == mark) | |
{ | |
draw_list->AddTriangleFilled(ImVec2(to, bar_pos.y + (height - 3)), | |
ImVec2(to - 4, barBottom + 1), | |
ImVec2(to + 4, barBottom + 1), IM_COL32(0, 255, 0, 255)); | |
draw_list->AddRect(ImVec2(to - 5, bar_pos.y + (height + 1)), | |
ImVec2(to + 5, bar_pos.y + (height + 11)), | |
IM_COL32(0, 255, 0, 255), 1.0f, 1.0f); | |
} | |
draw_list->AddRectFilledMultiColor(ImVec2(to - 3, bar_pos.y + (height + 3)), | |
ImVec2(to + 3, bar_pos.y + (height + 9)), | |
colorBU32, colorBU32, colorBU32, colorBU32); | |
ImGui::SetCursorScreenPos(ImVec2(to - 6, barBottom)); | |
ImGui::InvisibleButton("mark", ImVec2(12, 12)); | |
if(ImGui::IsItemHovered()) | |
{ | |
if(ImGui::IsMouseClicked(0)) | |
{ | |
selectedMark = mark; | |
draggingMark = mark; | |
} | |
} | |
prevMark = mark; | |
} | |
ImGui::SetCursorScreenPos(ImVec2(bar_pos.x, bar_pos.y + height + 20.0f)); | |
} | |
bool GradientButton(ImGradient* gradient) | |
{ | |
if(!gradient) return false; | |
ImVec2 widget_pos = ImGui::GetCursorScreenPos(); | |
// ImDrawList* draw_list = ImGui::GetWindowDrawList(); | |
float maxWidth = ImMax(250.0f, ImGui::GetContentRegionAvailWidth() - 100.0f); | |
bool clicked = ImGui::InvisibleButton("gradient_bar", ImVec2(maxWidth, GRADIENT_BAR_WIDGET_HEIGHT)); | |
DrawGradientBar(gradient, widget_pos, maxWidth, GRADIENT_BAR_WIDGET_HEIGHT); | |
return clicked; | |
} | |
bool GradientEditor(ImGradient* gradient, | |
ImGradientMark* & draggingMark, | |
ImGradientMark* & selectedMark) | |
{ | |
if(!gradient) return false; | |
bool modified = false; | |
ImVec2 bar_pos = ImGui::GetCursorScreenPos(); | |
bar_pos.x += 10; | |
float maxWidth = ImGui::GetContentRegionAvailWidth() - 20; | |
float barBottom = bar_pos.y + GRADIENT_BAR_EDITOR_HEIGHT; | |
ImGui::InvisibleButton("gradient_editor_bar", ImVec2(maxWidth, GRADIENT_BAR_EDITOR_HEIGHT)); | |
if(ImGui::IsItemHovered() && ImGui::IsMouseClicked(0)) | |
{ | |
float pos = (ImGui::GetIO().MousePos.x - bar_pos.x) / maxWidth; | |
float newMarkCol[4]; | |
gradient->getColorAt(pos, newMarkCol); | |
gradient->addMark(pos, ImColor(newMarkCol[0], newMarkCol[1], newMarkCol[2])); | |
} | |
DrawGradientBar(gradient, bar_pos, maxWidth, GRADIENT_BAR_EDITOR_HEIGHT); | |
DrawGradientMarks(gradient, draggingMark, selectedMark, bar_pos, maxWidth, GRADIENT_BAR_EDITOR_HEIGHT); | |
if(!ImGui::IsMouseDown(0) && draggingMark) | |
{ | |
draggingMark = nullptr; | |
} | |
if(ImGui::IsMouseDragging(0) && draggingMark) | |
{ | |
float increment = ImGui::GetIO().MouseDelta.x / maxWidth; | |
bool insideZone = (ImGui::GetIO().MousePos.x > bar_pos.x) && | |
(ImGui::GetIO().MousePos.x < bar_pos.x + maxWidth); | |
if(increment != 0.0f && insideZone) | |
{ | |
draggingMark->position += increment; | |
draggingMark->position = ImClamp(draggingMark->position, 0.0f, 1.0f); | |
gradient->refreshCache(); | |
modified = true; | |
} | |
float diffY = ImGui::GetIO().MousePos.y - barBottom; | |
if(diffY >= GRADIENT_MARK_DELETE_DIFFY) | |
{ | |
gradient->removeMark(draggingMark); | |
draggingMark = nullptr; | |
selectedMark = nullptr; | |
modified = true; | |
} | |
} | |
if(!selectedMark && gradient->getMarks().size() > 0) | |
{ | |
selectedMark = gradient->getMarks().front(); | |
} | |
if(selectedMark) | |
{ | |
bool colorModified = ImGui::ColorPicker3(selectedMark->color); | |
if(selectedMark && colorModified) | |
{ | |
modified = true; | |
gradient->refreshCache(); | |
} | |
} | |
return modified; | |
} | |
}; |
// | |
// imgui_color_gradient.h | |
// imgui extension | |
// | |
// Created by David Gallardo on 11/06/16. | |
/* | |
Usage: | |
::GRADIENT DATA:: | |
ImGradient gradient; | |
::BUTTON:: | |
if(ImGui::GradientButton(&gradient)) | |
{ | |
//set show editor flag to true/false | |
} | |
::EDITOR:: | |
static ImGradientMark* draggingMark = nullptr; | |
static ImGradientMark* selectedMark = nullptr; | |
bool updated = ImGui::GradientEditor(&gradient, draggingMark, selectedMark); | |
::GET A COLOR:: | |
float color[3]; | |
gradient.getColorAt(0.3f, color); //position from 0 to 1 | |
::MODIFY GRADIENT WITH CODE:: | |
gradient.getMarks().clear(); | |
gradient.addMark(0.0f, ImColor(0.2f, 0.1f, 0.0f)); | |
gradient.addMark(0.7f, ImColor(120, 200, 255)); | |
::WOOD BROWNS PRESET:: | |
gradient.getMarks().clear(); | |
gradient.addMark(0.0f, ImColor(0xA0, 0x79, 0x3D)); | |
gradient.addMark(0.2f, ImColor(0xAA, 0x83, 0x47)); | |
gradient.addMark(0.3f, ImColor(0xB4, 0x8D, 0x51)); | |
gradient.addMark(0.4f, ImColor(0xBE, 0x97, 0x5B)); | |
gradient.addMark(0.6f, ImColor(0xC8, 0xA1, 0x65)); | |
gradient.addMark(0.7f, ImColor(0xD2, 0xAB, 0x6F)); | |
gradient.addMark(0.8f, ImColor(0xDC, 0xB5, 0x79)); | |
gradient.addMark(1.0f, ImColor(0xE6, 0xBF, 0x83)); | |
*/ | |
#pragma once | |
#include "imgui.h" | |
#include <list> | |
struct ImGradientMark | |
{ | |
float color[4]; | |
float position; //0 to 1 | |
}; | |
class ImGradient | |
{ | |
public: | |
ImGradient(); | |
~ImGradient(); | |
void getColorAt(float position, float* color) const; | |
void addMark(float position, ImColor const color); | |
void removeMark(ImGradientMark* mark); | |
void refreshCache(); | |
std::list<ImGradientMark*> & getMarks(){ return m_marks; } | |
private: | |
void computeColorAt(float position, float* color) const; | |
std::list<ImGradientMark*> m_marks; | |
float m_cachedValues[256 * 3]; | |
}; | |
namespace ImGui | |
{ | |
bool GradientButton(ImGradient* gradient); | |
bool GradientEditor(ImGradient* gradient, | |
ImGradientMark* & draggingMark, | |
ImGradientMark* & selectedMark); | |
} |
this is really cool. better that expected;). thanks for sharing.
This is awesome! I was wondering, after a gradient instance is generated, is there a way to apply it to ImGuiStyleColor? In my case I would like to make the borders of some widgets a gradient (ImGuiCol_Border). Thanks for any insight!
Hey @paragon-747 ,
Your question is related to imgui, not to this widget itself.
the border color is one single color.
I think is not possible without modifying the imgui core by yourself.
but you could ask into the imgui repo issues.
there is a demo on imgui that has a theme editor. on the top menus on the right side, it is.
then you can see how to assign or modify the colors theme.
I didn´t see any tool to serialize and apply easily the edited theme on the fly.
I have seen this one related: https://github.com/Patitotective/ImTemplate
Also, there are some other gradient widgets around here. Maybe some are curated here:
https://github.com/HankiDesign/awesome-dear-imgui
Makes sense, thanks for the thought out response!
Yes of course @Iringham, but I will appreciate if you mention my twitter/github user @galloscript somewhere. Thanks.