diff --git a/imgui.cpp b/imgui.cpp index 21d4f56e..ebbbb584 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -194,9 +194,12 @@ Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Also read releases logs https://github.com/ocornut/imgui/releases for more details. - - 2017/07/20 (1.51) - Removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse - - 2017/05/26 (1.50) - Removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. - - 2017/05/01 (1.50) - Renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. + - 2017/07/30 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu. + - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options. + - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))' + - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse + - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. + - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild(). - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc. @@ -594,7 +597,6 @@ - tabs (#261, #351) - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y) !- color: the color helpers/typing is a mess and needs sorting out. - - color: add a better color picker (#346) - node/graph editor (#306) - pie menus patterns (#434) - drag'n drop, dragging helpers (carry dragging info, visualize drag source before clicking, drop target, etc.) (#143, #479) @@ -656,11 +658,11 @@ - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack. - examples: directx9: save/restore device state more thoroughly. - examples: window minimize, maximize (#583) + - optimization: add clipping for multi-component widgets (SliderFloatX, ColorEditX, etc.). one problem is that nav branch can't easily clip parent group when there is a move request. - optimization: add a flag to disable most of rendering, for the case where the user expect to skip it (#335) - optimization: use another hash function than crc32, e.g. FNV1a - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)? - optimization: turn some the various stack vectors into statically-sized arrays - - optimization: better clipping for multi-component widgets */ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) @@ -955,7 +957,21 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) #define IM_NEWLINE "\n" #endif -bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c) +ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) +{ + ImVec2 ap = p - a; + ImVec2 ab_dir = b - a; + float ab_len = sqrtf(ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y); + ab_dir *= 1.0f / ab_len; + float dot = ap.x * ab_dir.x + ap.y * ab_dir.y; + if (dot < 0.0f) + return a; + if (dot > ab_len) + return b; + return a + ab_dir * dot; +} + +bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) { bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; @@ -963,6 +979,33 @@ bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, cons return ((b1 == b2) && (b2 == b3)); } +void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w) +{ + ImVec2 v0 = b - a; + ImVec2 v1 = c - a; + ImVec2 v2 = p - a; + const float denom = v0.x * v1.y - v1.x * v0.y; + out_v = (v2.x * v1.y - v1.x * v2.y) / denom; + out_w = (v0.x * v2.y - v2.x * v0.y) / denom; + out_u = 1.0f - out_v - out_w; +} + +ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) +{ + ImVec2 proj_ab = ImLineClosestPoint(a, b, p); + ImVec2 proj_bc = ImLineClosestPoint(b, c, p); + ImVec2 proj_ca = ImLineClosestPoint(c, a, p); + float dist2_ab = ImLengthSqr(p - proj_ab); + float dist2_bc = ImLengthSqr(p - proj_bc); + float dist2_ca = ImLengthSqr(p - proj_ca); + float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca)); + if (m == dist2_ab) + return proj_ab; + if (m == dist2_bc) + return proj_bc; + return proj_ca; +} + int ImStricmp(const char* str1, const char* str2) { int d; @@ -1027,7 +1070,6 @@ const char* ImStristr(const char* haystack, const char* haystack_end, const char return NULL; } - // MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). // Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm. int ImFormatString(char* buf, int buf_size, const char* fmt, ...) @@ -1295,18 +1337,30 @@ ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) { - ImVec4 c = GImGui->Style.Colors[idx]; - c.w *= GImGui->Style.Alpha * alpha_mul; + ImGuiStyle& style = GImGui->Style; + ImVec4 c = style.Colors[idx]; + c.w *= style.Alpha * alpha_mul; return ColorConvertFloat4ToU32(c); } ImU32 ImGui::GetColorU32(const ImVec4& col) { + ImGuiStyle& style = GImGui->Style; ImVec4 c = col; - c.w *= GImGui->Style.Alpha; + c.w *= style.Alpha; return ColorConvertFloat4ToU32(c); } +ImU32 ImGui::GetColorU32(ImU32 col) +{ + float style_alpha = GImGui->Style.Alpha; + if (style_alpha >= 1.0f) + return col; + int a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; + a = (int)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. + return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); +} + // Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) @@ -2015,11 +2069,11 @@ void ImGui::ItemSize(const ImRect& bb, float text_offset_y) ItemSize(bb.GetSize(), text_offset_y); } -static ImGuiNavDir NavScoreItemGetQuadrant(float dx, float dy) +static ImGuiDir NavScoreItemGetQuadrant(float dx, float dy) { if (fabsf(dx) > fabsf(dy)) - return (dx > 0.0f) ? ImGuiNavDir_Right : ImGuiNavDir_Left; - return (dy > 0.0f) ? ImGuiNavDir_Down : ImGuiNavDir_Up; + return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; + return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; } static float NavScoreItemDistInterval(float a0, float a1, float b0, float b1) @@ -2041,7 +2095,7 @@ static bool NavScoreItem(ImRect cand) const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having lots of items with varied width) // We perform scoring on items bounding box clipped by their parent window on the other axis (clipping on our movement axis would give us equal scores for all clipped items) - if (g.NavMoveDir == ImGuiNavDir_Left || g.NavMoveDir == ImGuiNavDir_Right) + if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) { cand.Min.y = ImClamp(cand.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y); cand.Max.y = ImClamp(cand.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y); @@ -2066,7 +2120,7 @@ static bool NavScoreItem(ImRect cand) float dist_center = fabsf(dcx) + fabsf(dcy); // L1 metric (need this for our connectedness guarantee) // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance - ImGuiNavDir quadrant; + ImGuiDir quadrant; float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; if (dbx || dby) { @@ -2087,7 +2141,7 @@ static bool NavScoreItem(ImRect cand) else { // Degenerate case: two overlapping buttons with same center, break ties using order - quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiNavDir_Left : ImGuiNavDir_Right; + quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; } #if 0 // [DEBUG] @@ -2125,7 +2179,7 @@ static bool NavScoreItem(ImRect cand) // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" buttons // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if (((g.NavMoveDir == ImGuiNavDir_Up || g.NavMoveDir == ImGuiNavDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance + if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance new_best = true; } } @@ -2136,7 +2190,7 @@ static bool NavScoreItem(ImRect cand) // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. if (g.NavMoveResultDistBox == FLT_MAX) if (dist_axial < g.NavMoveResultDistAxial) // Check axial match - if ((g.NavMoveDir == ImGuiNavDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiNavDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiNavDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiNavDir_Down && day > 0.0f)) + if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) g.NavMoveResultDistAxial = dist_axial, new_best = true; return new_best; @@ -2200,7 +2254,7 @@ bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id, const ImRect* nav_bb_ar //const bool DEBUG_NAV = false; // [DEBUG] Enable to test scoring on all items. if ((g.NavMoveRequest /*|| DEBUG_NAV*/) && g.NavId != *id) { - //if (DEBUG_NAV && !g.NavMoveRequest) g.NavMoveDir = ImGuiNavDir_N; + //if (DEBUG_NAV && !g.NavMoveRequest) g.NavMoveDir = ImGuiDir_N; if (NavScoreItem(nav_bb)) //if (!DEBUG || g.NavMoveRequest) { g.NavMoveResultId = *id; @@ -2739,15 +2793,15 @@ static void NavUpdate() // Initiate directional inputs request const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; - g.NavMoveDir = ImGuiNavDir_None; + g.NavMoveDir = ImGuiDir_None; if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - if ((allowed_dir_flags & (1<CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (!g.NavWindow->DC.NavLayerActiveFlags && g.NavWindow->DC.NavHasScroll && g.NavMoveRequest && (g.NavMoveDir == ImGuiNavDir_Up || g.NavMoveDir == ImGuiNavDir_Down)) - SetWindowScrollY(g.NavWindow, ImFloor(g.NavWindow->Scroll.y + ((g.NavMoveDir == ImGuiNavDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + if (!g.NavWindow->DC.NavLayerActiveFlags && g.NavWindow->DC.NavHasScroll && g.NavMoveRequest && (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down)) + SetWindowScrollY(g.NavWindow, ImFloor(g.NavWindow->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); // Manual scroll with NavScrollXXX keys ImVec2 scroll_dir = GetNavInputAmount2d(1, ImGuiNavReadMode_Down, 1.0f/10.0f, 10.0f); @@ -2814,6 +2868,7 @@ void ImGui::NewFrame() IM_ASSERT(g.IO.Fonts->Fonts.Size > 0); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? IM_ASSERT(g.Style.CurveTessellationTol > 0.0f); // Invalid style setting + IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f); // Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations) if (!g.Initialized) { @@ -3103,7 +3158,6 @@ void ImGui::Shutdown() for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) g.RenderDrawLists[i].clear(); g.OverlayDrawList.ClearFreeMemory(); - g.ColorEditModeStorage.Clear(); g.PrivateClipboard.clear(); g.InputTextState.Text.clear(); g.InputTextState.InitialText.clear(); @@ -3674,6 +3728,16 @@ void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, } } +void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->Flags & ImGuiWindowFlags_ShowBorders) + { + window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding); + } +} + // Render a triangle to denote expanded/collapsed state void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool is_open, float scale) { @@ -3780,9 +3844,9 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items const ImVec2 pos = window->DC.CursorPos; int start = (int)((window->ClipRect.Min.y - pos.y) / items_height); int end = (int)((window->ClipRect.Max.y - pos.y) / items_height); - if (g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Up) // When performing a navigation request, ensure we have one item extra in the direction we are moving to + if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Up) // When performing a navigation request, ensure we have one item extra in the direction we are moving to start--; - if (g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Down) + if (g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) end++; start = ImClamp(start, 0, items_count); @@ -5155,7 +5219,6 @@ bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_us window->DC.TreeDepth = 0; window->DC.StateStorage = &window->StateStorage; window->DC.GroupStack.resize(0); - window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect; window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active); if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags)) @@ -6413,6 +6476,7 @@ void ImGui::LabelText(const char* label, const char* fmt, ...) static inline bool IsWindowContentHoverable(ImGuiWindow* window) { // An active popup disable hovering on other windows (apart from its own children) + // FIXME-OPT: This could be cached/stored within the window. ImGuiContext& g = *GImGui; if (g.NavWindow) if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) @@ -6494,7 +6558,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Set active id so it can be queried by user via IsItemActive(), etc. but don't react to it ourselves g.NavActivateId = id; SetActiveID(id, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiNavDir_Left) | (1 << ImGuiNavDir_Right) | (1 << ImGuiNavDir_Up) | (1 << ImGuiNavDir_Down); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); if (IsNavInputPressed(ImGuiNavInput_PadActivate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed)) pressed = true; } @@ -7313,7 +7377,7 @@ bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) SetActiveIDNoNav(g.ScalarAsInputTextId, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiNavDir_Up) | (1 << ImGuiNavDir_Down); + g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); SetHoveredID(0); FocusableItemUnregister(window); @@ -8746,7 +8810,7 @@ bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2 } SetActiveID(id, window); if (!is_multiline) - g.ActiveIdAllowNavDirFlags = ((1 << ImGuiNavDir_Up) | (1 << ImGuiNavDir_Down)); + g.ActiveIdAllowNavDirFlags = ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); FocusWindow(window); } else if (io.MouseClicked[0]) @@ -9910,7 +9974,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); - moving_within_opened_triangle = ImIsPointInTriangle(g.IO.MousePos, ta, tb, tc); + moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug } } @@ -9923,12 +9987,12 @@ bool ImGui::BeginMenu(const char* label, bool enabled) want_close = menu_is_open; want_open = !menu_is_open; } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Right) // Nav-Right to open + if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { want_open = true; g.NavMoveRequest = false; } - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Left && IsPopupOpen(id)) // Nav-Left to close + if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && IsPopupOpen(id)) // Nav-Left to close { want_close = true; g.NavMoveRequest = false; @@ -9941,7 +10005,7 @@ bool ImGui::BeginMenu(const char* label, bool enabled) } else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // Menu bar: click to open a first menu, then hover to open others want_open = true; - else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiNavDir_Down) // Menu bar: Nav-Down to open + else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Menu bar: Nav-Down to open { g.NavMoveRequest = false; want_open = true; @@ -9978,51 +10042,220 @@ void ImGui::EndMenu() EndPopup(); } +// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +void ImGui::ColorTooltip(const char* text, const float col[4], ImGuiColorEditFlags flags) +{ + ImGuiContext& g = *GImGui; + + int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); + BeginTooltipEx(true); + + const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; + if (text_end > text) + { + TextUnformatted(text, text_end); + Separator(); + } + + ImVec2 sz(g.FontSize * 3, g.FontSize * 3); + ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + SameLine(); + if (flags & ImGuiColorEditFlags_NoAlpha) + Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); + else + Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); + EndTooltip(); +} + +static inline float ColorSquareSize() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + g.Style.FramePadding.y * 2.0f; +} + +static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b) +{ + float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; + int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); + int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); + int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); + return IM_COL32(r, g, b, 0xFF); +} + +// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. +// I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether. +void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) + { + ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col)); + ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col)); + window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags); + + int yi = 0; + for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) + { + float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); + if (y2 <= y1) + continue; + for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) + { + float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); + if (x2 <= x1) + continue; + int rounding_corners_flags_cell = 0; + if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImGuiCorner_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImGuiCorner_TopRight; } + if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImGuiCorner_BottomLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImGuiCorner_BottomRight; } + rounding_corners_flags_cell &= rounding_corners_flags; + window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell); + } + } + } + else + { + window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags); + } +} + +void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) +{ + ImGuiContext& g = *GImGui; + if ((flags & ImGuiColorEditFlags__InputsMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask; + if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; + if ((flags & ImGuiColorEditFlags__PickerMask) == 0) + flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; + IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected + IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected + g.ColorEditOptions = flags; +} + // A little colored square. Return true when clicked. // FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. -bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border) +// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. +bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID("#colorbutton"); - const float square_size = g.FontSize; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.y*2, square_size + (small_height ? 0 : style.FramePadding.y*2))); - ItemSize(bb, small_height ? 0.0f : style.FramePadding.y); + const ImGuiID id = window->GetID(desc_id); + float default_size = ColorSquareSize(); + if (size.x == 0.0f) + size.x = default_size; + if (size.y == 0.0f) + size.y = default_size; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb); if (!ItemAdd(bb, &id)) return false; bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + if (flags & ImGuiColorEditFlags_NoAlpha) + flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); + + ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f); + float grid_step = ImMin(size.x, size.y) / 2.99f; + float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); + if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f) + { + float mid_x = (float)(int)((bb.Min.x + bb.Max.x) * 0.5f + 0.5f); + RenderColorRectWithAlphaCheckerboard(ImVec2(bb.Min.x + grid_step, bb.Min.y), bb.Max, GetColorU32(col), grid_step, ImVec2(-grid_step, 0.0f), rounding, ImGuiCorner_TopRight|ImGuiCorner_BottomRight); + window->DrawList->AddRectFilled(bb.Min, ImVec2(mid_x, bb.Max.y), GetColorU32(col_without_alpha), rounding, ImGuiCorner_TopLeft|ImGuiCorner_BottomLeft); + } + else + { + RenderColorRectWithAlphaCheckerboard(bb.Min, bb.Max, GetColorU32((flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha), grid_step, ImVec2(0,0), rounding); + } RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding); + if (window->Flags & ImGuiWindowFlags_ShowBorders) + RenderFrameBorder(bb.Min, bb.Max, rounding); + else + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border - if (IsItemHovered()) // hovered? - SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8_SAT(col.x), IM_F32_TO_INT8_SAT(col.y), IM_F32_TO_INT8_SAT(col.z), IM_F32_TO_INT8_SAT(col.w)); + if (!(flags & ImGuiColorEditFlags_NoTooltip) && IsItemHovered()) // FIXME-NAVIGATION: 'hovered' ? + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); return pressed; } -bool ImGui::ColorEdit3(const char* label, float col[3]) +bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) { - float col4[4]; - col4[0] = col[0]; - col4[1] = col[1]; - col4[2] = col[2]; - col4[3] = 1.0f; - const bool value_changed = ColorEdit4(label, col4, false); - col[0] = col4[0]; - col[1] = col4[1]; - col[2] = col4[2]; - return value_changed; + return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); +} + +static void ColorEditOptionsPopup(ImGuiColorEditFlags flags) +{ + bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask); + bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); + if ((!allow_opt_inputs && !allow_opt_datatype) || !ImGui::BeginPopup("context")) + return; + ImGuiContext& g = *GImGui; + ImGuiColorEditFlags opts = g.ColorEditOptions; + if (allow_opt_inputs) + { + if (ImGui::RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB; + if (ImGui::RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV; + if (ImGui::RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX; + } + if (allow_opt_datatype) + { + if (allow_opt_inputs) ImGui::Separator(); + if (ImGui::RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; + if (ImGui::RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) ? 1 : 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; + } + g.ColorEditOptions = opts; + ImGui::EndPopup(); } -// Edit colors components (each component in 0.0f..1.0f range -// Use CTRL-Click to input value and TAB to go to next item. -bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) +static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, float* ref_col) +{ + bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); + bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); + if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context")) + return; + ImGuiContext& g = *GImGui; + if (allow_opt_picker) + { + ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ColorSquareSize() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function + ImGui::PushItemWidth(picker_size.x); + for (int picker_type = 0; picker_type < 2; picker_type++) + { + // Draw small/thumbnail version of each picker type (over an invisible button for selection) + if (picker_type > 0) ImGui::Separator(); + ImGui::PushID(picker_type); + ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha); + if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; + if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; + ImVec2 backup_pos = ImGui::GetCursorScreenPos(); + if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup + g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); + ImGui::SetCursorScreenPos(backup_pos); + ImVec4 dummy_ref_col; + memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (ImGuiColorEditFlags_NoAlpha ? 3 : 4)); + ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); + ImGui::PopID(); + } + ImGui::PopItemWidth(); + } + if (allow_opt_alpha_bar) + { + if (allow_opt_picker) ImGui::Separator(); + ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); + } + ImGui::EndPopup(); +} + +// Edit colors components (each component in 0.0f..1.0f range). +// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. +bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -10030,139 +10263,508 @@ bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w_full = CalcItemWidth(); - const float square_sz = (g.FontSize + style.FramePadding.y * 2.0f); + const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (ColorSquareSize() + style.ItemInnerSpacing.x); + const float w_items_all = CalcItemWidth() - w_extra; + const char* label_display_end = FindRenderedTextEnd(label); - ImGuiColorEditMode edit_mode = window->DC.ColorEditMode; - if (edit_mode == ImGuiColorEditMode_UserSelect || edit_mode == ImGuiColorEditMode_UserSelectShowButton) - edit_mode = g.ColorEditModeStorage.GetInt(id, 0) % 3; + const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; + const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; + const int components = alpha ? 4 : 3; + const ImGuiColorEditFlags flags_untouched = flags; - float f[4] = { col[0], col[1], col[2], col[3] }; - if (edit_mode == ImGuiColorEditMode_HSV) - ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + BeginGroup(); + PushID(label); + // If we're not showing any slider there's no point in doing any HSV conversions + if (flags & ImGuiColorEditFlags_NoInputs) + flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions; + + // Context menu: display and modify options (before defaults are applied) + if (!(flags & ImGuiColorEditFlags_NoOptions)) + ColorEditOptionsPopup(flags); + + // Read stored options + if (!(flags & ImGuiColorEditFlags__InputsMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask); + if (!(flags & ImGuiColorEditFlags__DataTypeMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); + if (!(flags & ImGuiColorEditFlags__PickerMask)) + flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); + flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask)); + + // Convert to the formats we need + float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; + if (flags & ImGuiColorEditFlags_HSV) + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; - int components = alpha ? 4 : 3; bool value_changed = false; + bool value_changed_as_float = false; - BeginGroup(); - PushID(label); - - const bool hsv = (edit_mode == 1); - switch (edit_mode) + if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) { - case ImGuiColorEditMode_RGB: - case ImGuiColorEditMode_HSV: + // RGB/HSV 0..255 Sliders + const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); + const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); + + const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); + const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; + const char* fmt_table_int[3][4] = + { + { "%3.0f", "%3.0f", "%3.0f", "%3.0f" }, // Short display + { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, // Long display for RGBA + { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" } // Long display for HSVA + }; + const char* fmt_table_float[3][4] = { - // RGB/HSV 0..255 Sliders - const float w_items_all = w_full - (square_sz + style.ItemInnerSpacing.x); - const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); + { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display + { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA + { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA + }; + const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1; - const bool hide_prefix = (w_item_one <= CalcTextSize("M:999").x); - const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; - const char* fmt_table[3][4] = - { - { "%3.0f", "%3.0f", "%3.0f", "%3.0f" }, - { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, - { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" } - }; - const char** fmt = hide_prefix ? fmt_table[0] : hsv ? fmt_table[2] : fmt_table[1]; - - PushItemWidth(w_item_one); - for (int n = 0; n < components; n++) + PushItemWidth(w_item_one); + for (int n = 0; n < components; n++) + { + if (n > 0) + SameLine(0, style.ItemInnerSpacing.x); + if (n + 1 == components) + PushItemWidth(w_item_last); + if (flags & ImGuiColorEditFlags_Float) + value_changed |= value_changed_as_float |= DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); + else + value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); + if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1)) + OpenPopup("context"); + } + PopItemWidth(); + PopItemWidth(); + } + else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) + { + // RGB Hexadecimal Input + char buf[64]; + if (alpha) + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255)); + else + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255)); + PushItemWidth(w_items_all); + if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) + { + value_changed |= true; + char* p = buf; + while (*p == '#' || ImCharIsSpace(*p)) + p++; + i[0] = i[1] = i[2] = i[3] = 0; + if (alpha) + sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + else + sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + } + if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1)) + OpenPopup("context"); + PopItemWidth(); + } + + bool picker_active = false; + if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) + { + if (!(flags & ImGuiColorEditFlags_NoInputs)) + SameLine(0, style.ItemInnerSpacing.x); + + const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); + if (ColorButton("##ColorButton", col_v4, flags)) + { + if (!(flags & ImGuiColorEditFlags_NoPicker)) { - if (n > 0) - SameLine(0, style.ItemInnerSpacing.x); - if (n + 1 == components) - PushItemWidth(w_item_last); - value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]); + // Store current color and open a picker + g.ColorPickerRef = col_v4; + OpenPopup("picker"); + SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y)); } - PopItemWidth(); - PopItemWidth(); } - break; - case ImGuiColorEditMode_HEX: + if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1)) + OpenPopup("context"); + + if (BeginPopup("picker")) { - // RGB Hexadecimal Input - const float w_slider_all = w_full - square_sz; - char buf[64]; - if (alpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", i[0], i[1], i[2], i[3]); - else - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", i[0], i[1], i[2]); - PushItemWidth(w_slider_all - style.ItemInnerSpacing.x); - if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) + picker_active = true; + if (label != label_display_end) { - value_changed |= true; - char* p = buf; - while (*p == '#' || ImCharIsSpace(*p)) - p++; - i[0] = i[1] = i[2] = i[3] = 0; - if (alpha) - sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) - else - sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + TextUnformatted(label, label_display_end); + Separator(); } + float square_sz = ColorSquareSize(); + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? + value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); PopItemWidth(); + EndPopup(); } - break; } - SameLine(0, style.ItemInnerSpacing.x); + if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) + { + SameLine(0, style.ItemInnerSpacing.x); + TextUnformatted(label, label_display_end); + } + + // Convert back + if (!picker_active) + { + if (!value_changed_as_float) + for (int n = 0; n < 4; n++) + f[n] = i[n] / 255.0f; + if (flags & ImGuiColorEditFlags_HSV) + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + if (value_changed) + { + col[0] = f[0]; + col[1] = f[1]; + col[2] = f[2]; + if (alpha) + col[3] = f[3]; + } + } + + PopID(); + EndGroup(); + + return value_changed; +} + +bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) +{ + float col4[4] = { col[0], col[1], col[2], 1.0f }; + if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) + return false; + col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; + return true; +} + +// 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. +static void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col) +{ + switch (direction) + { + case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return; + case ImGuiDir_Left: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return; + case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return; + case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return; + } +} + +static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w) +{ + RenderArrow(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK); + RenderArrow(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE); + RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32_BLACK); + RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE); +} + +static void PaintVertsLinearGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1) +{ + ImVec2 gradient_extent = gradient_p1 - gradient_p0; + float gradient_inv_length = ImInvLength(gradient_extent, 0.0f); + for (ImDrawVert* vert = vert_start; vert < vert_end; vert++) + { + float d = ImDot(vert->pos - gradient_p0, gradient_extent); + float t = ImMin(sqrtf(ImMax(d, 0.0f)) * gradient_inv_length, 1.0f); + int r = ImLerp((int)(col0 >> IM_COL32_R_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_R_SHIFT) & 0xFF, t); + int g = ImLerp((int)(col0 >> IM_COL32_G_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_G_SHIFT) & 0xFF, t); + int b = ImLerp((int)(col0 >> IM_COL32_B_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_B_SHIFT) & 0xFF, t); + vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) | (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK); + } +} + +// ColorPicker +// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. +// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) +bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + ImDrawList* draw_list = window->DrawList; + + ImGuiStyle& style = g.Style; + ImGuiIO& io = g.IO; + + PushID(label); + BeginGroup(); + + if ((flags & ImGuiColorEditFlags_NoSidePreview) == 0) + flags |= ImGuiColorEditFlags_NoSmallPreview; + + if ((flags & ImGuiColorEditFlags_NoOptions) == 0) + { + // Context menu: display and store options. + ColorPickerOptionsPopup(flags, col); + + // Read stored options + if ((flags & ImGuiColorEditFlags__PickerMask) == 0) + flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; + IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected + flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); + } + + // Setup + bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); + ImVec2 picker_pos = window->DC.CursorPos; + float bars_width = ColorSquareSize(); // Arbitrary smallish width of Hue/Alpha picking bars + float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box + float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; + float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; + float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f); + + float wheel_thickness = sv_picker_size * 0.08f; + float wheel_r_outer = sv_picker_size * 0.50f; + float wheel_r_inner = wheel_r_outer - wheel_thickness; + ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f); + + // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. + float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); + ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. + ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. + ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. + + float H,S,V; + ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V); + + bool value_changed = false, value_changed_h = false, value_changed_sv = false; + + if (flags & ImGuiColorEditFlags_PickerHueWheel) + { + // Hue wheel + SV triangle logic + InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); + if (IsItemActive()) + { + ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; + ImVec2 current_off = g.IO.MousePos - wheel_center; + float initial_dist2 = ImLengthSqr(initial_off); + if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1)) + { + // Interactive with Hue wheel + H = atan2f(current_off.y, current_off.x) / IM_PI*0.5f; + if (H < 0.0f) + H += 1.0f; + value_changed = value_changed_h = true; + } + float cos_hue_angle = cosf(-H * 2.0f * IM_PI); + float sin_hue_angle = sinf(-H * 2.0f * IM_PI); + if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) + { + // Interacting with SV triangle + ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); + if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) + current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); + float uu, vv, ww; + ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); + V = ImClamp(1.0f - vv, 0.0001f, 1.0f); + S = ImClamp(uu / V, 0.0001f, 1.0f); + value_changed = value_changed_sv = true; + } + } + if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1)) + OpenPopup("context"); + } + else if (flags & ImGuiColorEditFlags_PickerHueBar) + { + // SV rectangle logic + InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); + if (IsItemActive()) + { + S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1)); + V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); + value_changed = value_changed_sv = true; + } + if (!(flags & ImGuiColorEditFlags_NoOptions) && IsItemHovered() && IsMouseClicked(1)) + OpenPopup("context"); - const ImVec4 col_display(col[0], col[1], col[2], 1.0f); - if (ColorButton(col_display)) - g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! + // Hue bar logic + SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); + InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); + if (IsItemActive()) + { + H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); + value_changed = value_changed_h = true; + } + } - // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here - if (IsItemHovered()) - SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8_SAT(col[0]), IM_F32_TO_INT8_SAT(col[1]), IM_F32_TO_INT8_SAT(col[2]), IM_F32_TO_INT8_SAT(col[3])); + // Alpha bar logic + if (alpha_bar) + { + SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); + InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); + if (IsItemActive()) + { + col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); + value_changed = true; + } + } - if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) { SameLine(0, style.ItemInnerSpacing.x); - const char* button_titles[3] = { "RGB", "HSV", "HEX" }; - if (ButtonEx(button_titles[edit_mode], ImVec2(0,0), ImGuiButtonFlags_DontClosePopups)) - g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! + BeginGroup(); } - const char* label_display_end = FindRenderedTextEnd(label); - if (label != label_display_end) + if (!(flags & ImGuiColorEditFlags_NoLabel)) { - SameLine(0, (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) ? -1.0f : style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); + const char* label_display_end = FindRenderedTextEnd(label); + if (label != label_display_end) + { + if ((flags & ImGuiColorEditFlags_NoSidePreview)) + SameLine(0, style.ItemInnerSpacing.x); + TextUnformatted(label, label_display_end); + } } - // Convert back - for (int n = 0; n < 4; n++) - f[n] = i[n] / 255.0f; - if (edit_mode == 1) - ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + if (!(flags & ImGuiColorEditFlags_NoSidePreview)) + { + ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); + float square_sz = ColorSquareSize(); + if ((flags & ImGuiColorEditFlags_NoLabel)) + Text("Current"); + ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)); + if (ref_col != NULL) + { + Text("Original"); + ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); + if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2))) + { + memcpy(col, ref_col, ((flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4) * sizeof(float)); + value_changed = true; + } + } + EndGroup(); + } + + // Convert back color to RGB + if (value_changed_h || value_changed_sv) + ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); + // R,G,B and H,S,V slider color editor + if ((flags & ImGuiColorEditFlags_NoInputs) == 0) + { + PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; + if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0) + value_changed |= ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB); + if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0) + value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV); + if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0) + value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX); + PopItemWidth(); + } + + // Try to cancel hue wrap (after ColorEdit), if any if (value_changed) { - col[0] = f[0]; - col[1] = f[1]; - col[2] = f[2]; - if (alpha) - col[3] = f[3]; + float new_H, new_S, new_V; + ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); + if (new_H <= 0 && H > 0) + { + if (new_V <= 0 && V != new_V) + ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); + else if (new_S <= 0) + ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); + } + } + + ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); + ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); + ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f)); + + const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) }; + ImVec2 sv_cursor_pos; + + if (flags & ImGuiColorEditFlags_PickerHueWheel) + { + // Render Hue Wheel + const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). + const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); + for (int n = 0; n < 6; n++) + { + const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; + const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; + int vert_start_idx = draw_list->_VtxCurrentIdx; + draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); + draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness); + + // Paint colors over existing vertices + ImVec2 gradient_p0(wheel_center.x + cosf(a0) * wheel_r_inner, wheel_center.y + sinf(a0) * wheel_r_inner); + ImVec2 gradient_p1(wheel_center.x + cosf(a1) * wheel_r_inner, wheel_center.y + sinf(a1) * wheel_r_inner); + PaintVertsLinearGradientKeepAlpha(draw_list->_VtxWritePtr - (draw_list->_VtxCurrentIdx - vert_start_idx), draw_list->_VtxWritePtr, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]); + } + + // Render Cursor + preview on Hue Wheel + float cos_hue_angle = cosf(H * 2.0f * IM_PI); + float sin_hue_angle = sinf(H * 2.0f * IM_PI); + ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f); + float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; + int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); + draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); + draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments); + draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments); + + // Render SV triangle (rotated according to hue) + ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); + ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); + ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); + ImVec2 uv_white = g.FontTexUvWhitePixel; + draw_list->PrimReserve(6, 6); + draw_list->PrimVtx(tra, uv_white, hue_color32); + draw_list->PrimVtx(trb, uv_white, hue_color32); + draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE); + draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS); + draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK); + draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS); + draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f); + sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); + } + else if (flags & ImGuiColorEditFlags_PickerHueBar) + { + // Render SV Square + draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE); + draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK); + RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f); + sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S) * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much + sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); + + // Render Hue Bar + for (int i = 0; i < 6; ++i) + draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]); + float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f); + RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); + RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); + } + + // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) + float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; + draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12); + + // Render alpha bar + if (alpha_bar) + { + float alpha = ImSaturate(col[3]); + ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); + RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); + draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK); + float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f); + RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); + RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); } - PopID(); EndGroup(); + PopID(); return value_changed; } -void ImGui::ColorEditMode(ImGuiColorEditMode mode) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.ColorEditMode = mode; -} - // Horizontal separating line. void ImGui::Separator() { @@ -10293,7 +10895,7 @@ void ImGui::EndGroup() window->DC.GroupStack.pop_back(); - //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // Debug + //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] } // Gets back to previous line and continue with horizontal layout @@ -10477,7 +11079,8 @@ void ImGui::Columns(int columns_count, const char* id, bool border) { float x = window->Pos.x + GetColumnOffset(i); const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i); - const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2)); + const float column_w = 4.0f; + const ImRect column_rect(ImVec2(x - column_w, y1), ImVec2(x + column_w, y2)); if (IsClippedEx(column_rect, &column_id, false)) continue; @@ -10494,7 +11097,7 @@ void ImGui::Columns(int columns_count, const char* id, bool border) if (held) { if (g.ActiveIdIsJustActivated) - g.ActiveIdClickOffset.x -= 4; // Store from center of column line (we used a 8 wide rect for columns clicking) + g.ActiveIdClickOffset.x -= column_w; // Store from center of column line (we used a 8 wide rect for columns clicking) x = GetDraggedColumnOffset(i); SetColumnOffset(i, x); } @@ -10624,14 +11227,14 @@ void ImGui::ValueColor(const char* prefix, const ImVec4& v) { Text("%s: (%.2f,%.2f,%.2f,%.2f)", prefix, v.x, v.y, v.z, v.w); SameLine(); - ColorButton(v, true); + ColorButton(prefix, v); } void ImGui::ValueColor(const char* prefix, ImU32 v) { Text("%s: %08X", prefix, v); SameLine(); - ColorButton(ColorConvertU32ToFloat4(v), true); + ColorButton(prefix, ColorConvertU32ToFloat4(v)); } //----------------------------------------------------------------------------- @@ -10754,7 +11357,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs); ImGui::Text("sizeof(ImGuiContext) = %u, sizeof(ImGuiWindow) = %u", (int)sizeof(ImGuiContext), (int)sizeof(ImGuiWindow)); static bool show_clip_rects = true; - ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects); + ImGui::Checkbox("Show clipping rectangles when hovering an ImDrawCmd", &show_clip_rects); ImGui::Separator(); struct Funcs @@ -10834,6 +11437,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::BulletText("Pos: (%.1f,%.1f)", window->Pos.x, window->Pos.y); ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); + ImGui::BulletText("Active: %d, Accessed: %d", window->Active, window->Accessed); ImGui::BulletText("NavLastId: 0x%08x, NavLayerActiveFlags: %02X", window->NavLastId, window->DC.NavLayerActiveFlags); if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); diff --git a/imgui.h b/imgui.h index 118872f3..4183f1da 100644 --- a/imgui.h +++ b/imgui.h @@ -71,7 +71,7 @@ typedef int ImGuiCol; // a color identifier for styling // e typedef int ImGuiStyleVar; // a variable identifier for styling // enum ImGuiStyleVar_ typedef int ImGuiKey; // a key identifier (ImGui-side enum) // enum ImGuiKey_ typedef int ImGuiNavInput; // an input identifier for gamepad nav // enum ImGuiNavInput_ -typedef int ImGuiColorEditMode; // color edit mode for ColorEdit*() // enum ImGuiColorEditMode_ +typedef int ImGuiColorEditFlags; // color edit flags for Color*() // enum ImGuiColorEditFlags_ typedef int ImGuiMouseCursor; // a mouse cursor identifier // enum ImGuiMouseCursor_ typedef int ImGuiWindowFlags; // window flags for Begin*() // enum ImGuiWindowFlags_ typedef int ImGuiSetCond; // condition flags for Set*() // enum ImGuiSetCond_ @@ -191,6 +191,7 @@ namespace ImGui IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied + IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied // Parameters stacks (current window) IMGUI_API void PushItemWidth(float item_width); // width of items for the common item+label case, pixels. 0.0f = default to ~2/3 of windows width, >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) @@ -245,7 +246,7 @@ namespace ImGui IMGUI_API void PushID(const void* ptr_id); IMGUI_API void PushID(int int_id); IMGUI_API void PopID(); - IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). useful if you want to query into ImGuiStorage yourself. otherwise rarely needed + IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). useful if you want to query into ImGuiStorage yourself IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); IMGUI_API ImGuiID GetID(const void* ptr_id); @@ -265,7 +266,7 @@ namespace ImGui IMGUI_API void BulletText(const char* fmt, ...) IM_PRINTFARGS(1); // shortcut for Bullet()+Text() IMGUI_API void BulletTextV(const char* fmt, va_list args); IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0)); // button - IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) + IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed in text IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding @@ -274,12 +275,8 @@ namespace ImGui IMGUI_API bool RadioButton(const char* label, bool active); IMGUI_API bool RadioButton(const char* label, int* v, int v_button); IMGUI_API bool Combo(const char* label, int* current_item, const char* const* items, int items_count, int height_in_items = -1); - IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items = -1); // separate items with \0, end item-list with \0\0 + IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items = -1); // separate items with \0, end item-list with \0\0 IMGUI_API bool Combo(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); - IMGUI_API bool ColorButton(const ImVec4& col, bool small_height = false, bool outline_border = true); - IMGUI_API bool ColorEdit3(const char* label, float col[3]); // Hint: 'float col[3]' function argument is same as 'float* col'. You can pass address of first element out of a contiguous set, e.g. &myvector.x - IMGUI_API bool ColorEdit4(const char* label, float col[4], bool show_alpha = true); // " - IMGUI_API void ColorEditMode(ImGuiColorEditMode mode); // FIXME-OBSOLETE: This is inconsistent with most of the API and will be obsoleted/replaced. IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); @@ -287,7 +284,7 @@ namespace ImGui IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1,0), const char* overlay = NULL); // Widgets: Drags (tip: ctrl+click on a drag box to input with keyboard. manually input values aren't clamped, can go off-bounds) - // For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, remember than a 'float v[3]' function argument is the same as 'float* v'. You can pass address of your first element out of a contiguous set, e.g. &myvector.x + // For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x // Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); @@ -313,7 +310,7 @@ namespace ImGui IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); // Widgets: Sliders (tip: ctrl+click on a slider to input with keyboard. manually input values aren't clamped, can go off-bounds) - IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix. Use power!=1.0 for logarithmic sliders + IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for logarithmic sliders IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); @@ -325,6 +322,15 @@ namespace ImGui IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f"); + // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little colored preview square that can be left-clicked to open a picker, and right-clicked to open an option menu.) + // Note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can the pass the address of a first float element out of a contiguous structure, e.g. &myvector.x + IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); + IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0); + IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); + IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL); + IMGUI_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. + IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. + // Widgets: Trees IMGUI_API bool TreeNode(const char* label); // if returning 'true' the node is open and the tree id is pushed into the id stack. user is responsible for calling TreePop(). IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_PRINTFARGS(2); // read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). @@ -456,7 +462,7 @@ namespace ImGui IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse positioning at the time of opening popup we have BeginPopup() into IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold IMGUI_API void ResetMouseDragDelta(int button = 0); // - IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you + IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type IMGUI_API void CaptureKeyboardFromApp(bool capture = true); // manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application handle). e.g. force capture keyboard when your widget is being hovered. IMGUI_API void CaptureMouseFromApp(bool capture = true); // manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application handle). @@ -697,15 +703,34 @@ enum ImGuiStyleVar_ ImGuiStyleVar_Count_ }; -// Enumeration for ColorEditMode() -// FIXME-OBSOLETE: Will be replaced by future color/picker api -enum ImGuiColorEditMode_ +// Enumeration for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() +enum ImGuiColorEditFlags_ { - ImGuiColorEditMode_UserSelect = -2, - ImGuiColorEditMode_UserSelectShowButton = -1, - ImGuiColorEditMode_RGB = 0, - ImGuiColorEditMode_HSV = 1, - ImGuiColorEditMode_HEX = 2 + ImGuiColorEditFlags_NoAlpha = 1 << 1, // // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (read 3 components from the input pointer). + ImGuiColorEditFlags_NoPicker = 1 << 2, // // ColorEdit: disable picker when clicking on colored square. + ImGuiColorEditFlags_NoOptions = 1 << 3, // // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview. + ImGuiColorEditFlags_NoSmallPreview = 1 << 4, // // ColorEdit, ColorPicker: disable colored square preview next to the inputs. (e.g. to show only the inputs) + ImGuiColorEditFlags_NoInputs = 1 << 5, // // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview colored square). + ImGuiColorEditFlags_NoTooltip = 1 << 6, // // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview. + ImGuiColorEditFlags_NoLabel = 1 << 7, // // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker). + ImGuiColorEditFlags_NoSidePreview = 1 << 8, // // ColorPicker: disable bigger color preview on right side of the picker, use small colored square preview instead. + // User Options (right-click on widget to change some of them). You can set application defaults using SetColorEditOptions(). The idea is that you probably don't want to override them in most of your calls, let the user choose and/or call SetColorEditOptions() during startup. + ImGuiColorEditFlags_AlphaBar = 1 << 9, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. + ImGuiColorEditFlags_AlphaPreview = 1 << 10, // // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque. + ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 11, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque. + ImGuiColorEditFlags_HDR = 1 << 12, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). + ImGuiColorEditFlags_RGB = 1 << 13, // [Inputs] // ColorEdit: choose one among RGB/HSV/HEX. ColorPicker: choose any combination using RGB/HSV/HEX. + ImGuiColorEditFlags_HSV = 1 << 14, // [Inputs] // " + ImGuiColorEditFlags_HEX = 1 << 15, // [Inputs] // " + ImGuiColorEditFlags_Uint8 = 1 << 16, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255. + ImGuiColorEditFlags_Float = 1 << 17, // [DataType] // 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_PickerHueBar = 1 << 18, // [PickerMode] // ColorPicker: bar for Hue, rectangle for Sat/Value. + ImGuiColorEditFlags_PickerHueWheel = 1 << 19, // [PickerMode] // ColorPicker: wheel for Hue, triangle for Sat/Value. + // Internals/Masks + ImGuiColorEditFlags__InputsMask = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX, + ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_Float, + ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel|ImGuiColorEditFlags_PickerHueBar, + ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_PickerHueBar // Change application default using SetColorEditOptions() }; // Enumeration for GetMouseCursor() diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c3d727ee..efb4a913 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -653,7 +653,7 @@ void ImGui::ShowTestWindow(bool* p_open) static float col1[3] = { 1.0f,0.0f,0.2f }; static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; ImGui::ColorEdit3("color 1", col1); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to change edit mode.\nCTRL+click on individual component to input value.\n"); + ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); ImGui::ColorEdit4("color 2", col2); @@ -666,6 +666,127 @@ void ImGui::ShowTestWindow(bool* p_open) //ImGui::ListBox("##listbox2", &listbox_item_current2, listbox_items, IM_ARRAYSIZE(listbox_items), 4); //ImGui::PopItemWidth(); + if (ImGui::TreeNode("Color/Picker Widgets")) + { + static ImVec4 color = ImColor(114, 144, 154, 200); + + static bool hdr = false; + static bool alpha_preview = true; + static bool alpha_half_preview = false; + static bool options_menu = true; + ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); ShowHelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); + ImGui::Checkbox("With Alpha Preview", &alpha_preview); + ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); + ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); ShowHelpMarker("Right-click on the individual color widget to show options."); + int misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); + + ImGui::Text("Color widget:"); + ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); + ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); + + ImGui::Text("Color widget HSV with Alpha:"); + ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_HSV | misc_flags); + + ImGui::Text("Color widget with Float Display:"); + ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); + + ImGui::Text("Color button with Picker:"); + ImGui::SameLine(); ShowHelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); + ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); + + 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, misc_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 CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); + ImGui::Separator(); + ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); + ImGui::SameLine(); + ImGui::BeginGroup(); + ImGui::Text("Current"); + ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40)); + ImGui::Text("Previous"); + if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, 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(); + } + + ImGui::Text("Color button only:"); + ImGui::ColorButton("MyColor##3b", *(ImVec4*)&color, misc_flags, ImVec2(80,80)); + + ImGui::Text("Color picker:"); + static bool alpha = true; + static bool alpha_bar = true; + static bool side_preview = true; + static bool ref_color = false; + static ImVec4 ref_color_v(1.0f,0.0f,1.0f,0.5f); + static int inputs_mode = 2; + static int picker_mode = 0; + ImGui::Checkbox("With Alpha", &alpha); + ImGui::Checkbox("With Alpha Bar", &alpha_bar); + ImGui::Checkbox("With Side Preview", &side_preview); + if (side_preview) + { + ImGui::SameLine(); + ImGui::Checkbox("With Ref Color", &ref_color); + if (ref_color) + { + ImGui::SameLine(); + ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); + } + } + ImGui::Combo("Inputs Mode", &inputs_mode, "All Inputs\0No Inputs\0RGB Input\0HSV Input\0HEX Input\0"); + ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0"); + ImGui::SameLine(); ShowHelpMarker("User can right-click the picker to change mode."); + ImGuiColorEditFlags flags = misc_flags; + if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() + if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; + if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; + if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; + if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; + if (inputs_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; + if (inputs_mode == 2) flags |= ImGuiColorEditFlags_RGB; + if (inputs_mode == 3) flags |= ImGuiColorEditFlags_HSV; + if (inputs_mode == 4) flags |= ImGuiColorEditFlags_HEX; + ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); + + ImGui::Text("Programmatically set defaults/options:"); + ImGui::SameLine(); ShowHelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); + if (ImGui::Button("Uint8 + HSV")) + ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_HSV); + ImGui::SameLine(); + if (ImGui::Button("Float + HDR + NoClamp")) + ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HDR); + + ImGui::TreePop(); + } + if (ImGui::TreeNode("Range Widgets")) { ImGui::Unindent(); @@ -1289,15 +1410,17 @@ void ImGui::ShowTestWindow(bool* p_open) { if (ImGui::Selectable("Set to zero")) value = 0.0f; if (ImGui::Selectable("Set to PI")) value = 3.1415f; + ImGui::DragFloat("Value", &value, 0.1f, 0.0f, 0.0f); ImGui::EndPopup(); } - static ImVec4 color = ImColor(0.8f, 0.5f, 1.0f, 1.0f); - ImGui::ColorButton(color); - if (ImGui::BeginPopupContextItem("color context menu")) + static char name[32] = "Label1"; + char buf[64]; sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceeding label + ImGui::Button(buf); + if (ImGui::BeginPopupContextItem("rename context menu")) { - ImGui::Text("Edit color"); - ImGui::ColorEdit3("##edit", (float*)&color); + ImGui::Text("Edit name:"); + ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); if (ImGui::Button("Close")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); @@ -1726,27 +1849,25 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SameLine(); ImGui::PushItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::PopItemWidth(); ImGui::SameLine(); ImGui::Checkbox("Only Modified Fields", &output_only_modified); - static ImGuiColorEditMode edit_mode = ImGuiColorEditMode_RGB; - ImGui::RadioButton("RGB", &edit_mode, ImGuiColorEditMode_RGB); - ImGui::SameLine(); - ImGui::RadioButton("HSV", &edit_mode, ImGuiColorEditMode_HSV); - ImGui::SameLine(); - ImGui::RadioButton("HEX", &edit_mode, ImGuiColorEditMode_HEX); - //ImGui::Text("Tip: Click on colored square to change edit mode."); + ImGui::Text("Tip: Left-click on colored square to open color picker,\nRight-click to open edit options menu."); static ImGuiTextFilter filter; filter.Draw("Filter colors", 200); + static ImGuiColorEditFlags alpha_flags = 0; + ImGui::RadioButton("Opaque", &alpha_flags, 0); ImGui::SameLine(); + ImGui::RadioButton("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine(); + ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf); + ImGui::BeginChild("#colors", ImVec2(0, 300), true, ImGuiWindowFlags_AlwaysVerticalScrollbar); ImGui::PushItemWidth(-160); - ImGui::ColorEditMode(edit_mode); for (int i = 0; i < ImGuiCol_COUNT; i++) { const char* name = ImGui::GetStyleColName(i); if (!filter.PassFilter(name)) continue; ImGui::PushID(i); - ImGui::ColorEdit4(name, (float*)&style.Colors[i], true); + ImGui::ColorEdit4(name, (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags); if (memcmp(&style.Colors[i], (ref ? &ref->Colors[i] : &default_style.Colors[i]), sizeof(ImVec4)) != 0) { ImGui::SameLine(); if (ImGui::Button("Revert")) style.Colors[i] = ref ? ref->Colors[i] : default_style.Colors[i]; diff --git a/imgui_draw.cpp b/imgui_draw.cpp index fa6838a6..42a87b47 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -432,7 +432,7 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 { // Anti-aliased stroke const float AA_SIZE = 1.0f; - const ImU32 col_trans = col & IM_COL32(255,255,255,0); + const ImU32 col_trans = col & ~IM_COL32_A_MASK; const int idx_count = thick_line ? count*18 : count*12; const int vtx_count = thick_line ? points_count*4 : points_count*3; @@ -605,7 +605,7 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun { // Anti-aliased Fill const float AA_SIZE = 1.0f; - const ImU32 col_trans = col & IM_COL32(255,255,255,0); + const ImU32 col_trans = col & ~IM_COL32_A_MASK; const int idx_count = (points_count-2)*3 + points_count*6; const int vtx_count = (points_count*2); PrimReserve(idx_count, vtx_count); @@ -774,9 +774,14 @@ void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImV void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners) { + const int corners_top = ImGuiCorner_TopLeft | ImGuiCorner_TopRight; + const int corners_bottom = ImGuiCorner_BottomLeft | ImGuiCorner_BottomRight; + const int corners_left = ImGuiCorner_TopLeft | ImGuiCorner_BottomLeft; + const int corners_right = ImGuiCorner_TopRight | ImGuiCorner_BottomRight; + float r = rounding; - r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners&(1|2))==(1|2)) || ((rounding_corners&(4|8))==(4|8)) ? 0.5f : 1.0f ) - 1.0f); - r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners&(1|8))==(1|8)) || ((rounding_corners&(2|4))==(2|4)) ? 0.5f : 1.0f ) - 1.0f); + r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners & corners_top) == corners_top) || ((rounding_corners & corners_bottom) == corners_bottom) ? 0.5f : 1.0f ) - 1.0f); + r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners & corners_left) == corners_left) || ((rounding_corners & corners_right) == corners_right) ? 0.5f : 1.0f ) - 1.0f); if (r <= 0.0f || rounding_corners == 0) { @@ -787,10 +792,10 @@ void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int } else { - const float r0 = (rounding_corners & 1) ? r : 0.0f; - const float r1 = (rounding_corners & 2) ? r : 0.0f; - const float r2 = (rounding_corners & 4) ? r : 0.0f; - const float r3 = (rounding_corners & 8) ? r : 0.0f; + const float r0 = (rounding_corners & ImGuiCorner_TopLeft) ? r : 0.0f; + const float r1 = (rounding_corners & ImGuiCorner_TopRight) ? r : 0.0f; + const float r2 = (rounding_corners & ImGuiCorner_BottomRight) ? r : 0.0f; + const float r3 = (rounding_corners & ImGuiCorner_BottomLeft) ? r : 0.0f; PathArcToFast(ImVec2(a.x+r0,a.y+r0), r0, 6, 9); PathArcToFast(ImVec2(b.x-r1,a.y+r1), r1, 9, 12); PathArcToFast(ImVec2(b.x-r2,b.y-r2), r2, 0, 3); diff --git a/imgui_internal.h b/imgui_internal.h index 7f2a3c9f..181d4f1e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -91,11 +91,17 @@ IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, cons // Helpers: Misc IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size = NULL, int padding_bytes = 0); -IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode); -IMGUI_API bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c); +IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode); static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; } +static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } +// Helpers: Geometry +IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); +IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); +IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); +IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w); + // Helpers: String IMGUI_API int ImStricmp(const char* str1, const char* str2); IMGUI_API int ImStrnicmp(const char* str1, const char* str2, int count); @@ -132,13 +138,17 @@ static inline int ImClamp(int v, int mn, int mx) static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); } static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } +static inline int ImLerp(int a, int b, float t) { return (int)(a + (b - a) * t); } static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; } +static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; } static inline float ImFloor(float f) { return (float)(int)f; } -static inline ImVec2 ImFloor(ImVec2 v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } +static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } +static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } +static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. // Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. @@ -211,13 +221,13 @@ enum ImGuiInputSource ImGuiInputSource_Count_, }; -enum ImGuiNavDir +enum ImGuiDir { - ImGuiNavDir_None = -1, - ImGuiNavDir_Left = 0, - ImGuiNavDir_Right = 1, - ImGuiNavDir_Up = 2, - ImGuiNavDir_Down = 3, + ImGuiDir_None = -1, + ImGuiDir_Left = 0, + ImGuiDir_Right = 1, + ImGuiDir_Up = 2, + ImGuiDir_Down = 3, }; enum ImGuiCorner @@ -444,7 +454,7 @@ struct ImGuiContext bool NavInitDefaultResultExplicit; // Whether the result was explicitly requested with SetItemDefaultFocus() bool NavMoveRequest; // Move request for this frame bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items - ImGuiNavDir NavMoveDir; // West/East/North/South + ImGuiDir NavMoveDir; // West/East/North/South ImGuiID NavMoveResultId; // Best move request candidate float NavMoveResultDistBox; // Best move request candidate box distance to current NavId float NavMoveResultDistCenter; // Best move request candidate center distance to current NavId @@ -480,7 +490,8 @@ struct ImGuiContext ImGuiTextEditState InputTextState; ImFont InputTextPasswordFont; ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. - ImGuiStorage ColorEditModeStorage; // Store user selection of color edit mode + ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets + ImVec4 ColorPickerRef; float DragCurrentValue; // Currently dragged value, always float, not rounded by end-user precision settings ImVec2 DragLastMouseDelta; float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio @@ -549,7 +560,7 @@ struct ImGuiContext NavInitDefaultResultId = 0; NavInitDefaultResultExplicit = false; NavMoveRequest = false; - NavMoveDir = ImGuiNavDir_None; + NavMoveDir = ImGuiDir_None; NavMoveResultId = 0; NavMoveResultDistBox = NavMoveResultDistCenter = NavMoveResultDistAxial = 0.0f; @@ -569,6 +580,7 @@ struct ImGuiContext SetNextTreeNodeOpenCond = 0; ScalarAsInputTextId = 0; + ColorEditOptions = ImGuiColorEditFlags__OptionsDefault; DragCurrentValue = 0.0f; DragLastMouseDelta = ImVec2(0.0f, 0.0f); DragSpeedDefaultRatio = 1.0f / 100.0f; @@ -642,7 +654,6 @@ struct IMGUI_API ImGuiDrawContext ImVector ItemWidthStack; ImVector TextWrapPosStack; ImVectorGroupStack; - ImGuiColorEditMode ColorEditMode; int StackSizesBackup[6]; // Store size of various stacks for asserting float IndentX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) @@ -679,7 +690,6 @@ struct IMGUI_API ImGuiDrawContext ItemWidth = 0.0f; ItemFlags = ImGuiItemFlags_Default_; TextWrapPos = -1.0f; - ColorEditMode = ImGuiColorEditMode_RGB; memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); IndentX = 0.0f; @@ -818,13 +828,14 @@ namespace ImGui IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); - // NB: All position are in absolute pixels coordinates (not window coordinates) - // FIXME: All those functions are a mess and needs to be refactored into something decent. AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. - // We need: a sort of symbol library, preferably baked into font atlas when possible + decent text rendering helpers. + // NB: All position are in absolute pixels coordinates (never using window coordinates internally) + // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); + IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); + IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); IMGUI_API void RenderCollapseTriangle(ImVec2 pos, bool is_open, float scale = 1.0f); IMGUI_API void RenderBullet(ImVec2 pos); IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col); @@ -849,6 +860,8 @@ namespace ImGui IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags); IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision); + IMGUI_API void ColorTooltip(const char* text, const float col[4], ImGuiColorEditFlags flags); + IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging IMGUI_API void TreePushRawID(ImGuiID id);