Open Nightfallen opened 2 years ago
You are on the right track. Until a style color exits you should be rendering a background yourself through a drawlist and there is a much easier way to fix all your problems. You can use GetItemRectMin()
/GetItemRectMax()
to get a rect of last rendered item. This will save you a headache of correctly recalculating item size as it is not always obvious what style parameters go into the calculation. Then you should use a drawlist channel splitter to change rendering order and draw background behind Selectable()
even though in the code Selectable()
comes first (it has to, in order to use GetItemRectMin()
/GetItemRectMax()
). Here is a fixed version of your code doing these things:
void SelectableColor(ImU32 color)
{
ImVec2 p_min = ImGui::GetItemRectMin();
ImVec2 p_max = ImGui::GetItemRectMax();
ImGui::GetWindowDrawList()->AddRectFilled(p_min, p_max, color);
}
void MyImguiWindow(bool* is_open)
{
ImGui::Begin("##Temp Window", is_open);
constexpr int szArr = 9;
static bool selectables[szArr] = {};
ImVec2 szEl = ImVec2(40, 40);
auto flags = ImGuiSelectableFlags_SelectOnClick;
ImDrawList* draw_list = ImGui::GetWindowDrawList();
// Text of selectable is behind rect
bool separator = false;
for (int i = 0; i < 9; ++i)
{
if (separator) ImGui::SameLine();
std::string label = std::to_string(i + 1);
// The Magick.
draw_list->ChannelsSplit(2);
// Channel number is like z-order. Widgets in higher channels are rendered above widgets in lower channels.
draw_list->ChannelsSetCurrent(1);
ImGui::Selectable(label.data(), &selectables[i], flags, szEl);
if (!ImGui::IsItemHovered() && !selectables[i])
{
// Render background behind Selectable().
draw_list->ChannelsSetCurrent(0);
SelectableColor(IM_COL32(255, 0, 0, 200));
}
// And commit changes.
draw_list->ChannelsMerge();
separator = true;
}
ImGui::End();
}
A small advice: use of std::string
may allocate memory, best not create std::string
in a rendering loop as you may end up allocating and freeing memory constantly if you are not extremely careful.
Thank you, now it's working fine
It may be simpler and faster to just always mark the item as Selected and change the color used for Selection?
I think no, i have selectables matrix and i need their actual states. With the code above i can make something like this and just change calls to ImGui::Selectable() to CustomSelectable() without changing logic of items being selected based on colors:
bool CustomSelectable(const char* label, bool* p_selected, ImU32 bg_color, ImGuiSelectableFlags flags, const ImVec2& size_arg)
{
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->ChannelsSplit(2);
// Channel number is like z-order. Widgets in higher channels are rendered above widgets in lower channels.
draw_list->ChannelsSetCurrent(1);
bool result = ImGui::Selectable(label, p_selected, flags, size_arg);
if (!ImGui::IsItemHovered() && !ImGui::IsItemActive() && !*p_selected)
{
// Render background behind Selectable().
draw_list->ChannelsSetCurrent(0);
SelectableColor(bg_color);
}
// Commit changes.
draw_list->ChannelsMerge();
return result;
}
And now i have something like this:
Version/Branch of Dear ImGui:
Version: 1.86 WIP (18510) Branch: docking
Back-end/Renderer/Compiler/OS
Back-ends: imgui_impl_dx11.cpp + imgui_impl_win32.cpp Compiler: MSVC Operating System: Windows 10
Info from 'Demo>About Window'
``` Dear ImGui 1.86 WIP (18510) -------------------------------- sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20 define: __cplusplus=199711 define: _WIN32 define: _WIN64 define: _MSC_VER=1931 define: _MSVC_LANG=202004 define: IMGUI_HAS_VIEWPORT define: IMGUI_HAS_DOCK -------------------------------- io.BackendPlatformName: imgui_impl_win32 io.BackendRendererName: imgui_impl_dx11 io.ConfigFlags: 0x00000401 NavEnableKeyboard ViewportsEnable io.ConfigViewportsNoAutoMerge io.ConfigViewportsNoDecoration io.ConfigInputTextCursorBlink io.ConfigWindowsResizeFromEdges io.ConfigWindowsMoveFromTitleBarOnly io.ConfigMemoryCompactTimer = 60.0 io.BackendFlags: 0x00001C0E HasMouseCursors HasSetMousePos PlatformHasViewports HasMouseHoveredViewport RendererHasVtxOffset RendererHasViewports -------------------------------- io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,64 io.DisplaySize: 50.00,50.00 io.DisplayFramebufferScale: 1.00,1.00 -------------------------------- style.WindowPadding: 8.00,8.00 style.WindowBorderSize: 1.00 style.FramePadding: 4.00,3.00 style.FrameRounding: 0.00 style.FrameBorderSize: 0.00 style.ItemSpacing: 8.00,4.00 style.ItemInnerSpacing: 4.00,4.00 ```
My Issue/Question: How to make something like background color for 'ImGui::Selectable()'
I want to make background color for 'ImGui::Selectable()' when it's not hovered, held or selected (for which there are ImGuiCol_Header colors)
I found i can render rects, but i have troubles with them. In first case Selectable's text rendered behind rect. In second case i can't use 'ImGui::IsItemHovered()' Although there is 1-2 extra pixels at the top and bottom of rect, I'm not sure why
I want to get some help or ideas how could i implement it
Screenshots/Video
Standalone, minimal, complete and verifiable example:
Code snippet
``` void SelectableColor(ImVec2 szSelectable, ImU32 color) { auto& style = ImGui::GetStyle(); ImVec2 itemSpacing = style.ItemSpacing; ImVec2 p_min = ImGui::GetCursorScreenPos() - style.FramePadding; ImVec2 p_max = p_min + itemSpacing + szSelectable; ImGui::GetWindowDrawList()->AddRectFilled(p_min, p_max, color); } void SelectableColorEx(ImVec2 prevCurPos, ImVec2 szSelectable, ImU32 color) { auto& style = ImGui::GetStyle(); ImVec2 itemSpacing = style.ItemSpacing; ImVec2 p_min = prevCurPos - style.FramePadding; ImVec2 p_max = p_min + itemSpacing + szSelectable; ImGui::GetWindowDrawList()->AddRectFilled(p_min, p_max, color); } void MyImguiWindow(bool* is_open) { ImGui::Begin("##Temp Window", is_open); constexpr int szArr = 9; static bool selectables[szArr] = {}; ImVec2 szEl = ImVec2(40, 40); auto flags = ImGuiSelectableFlags_SelectOnClick; // Text of selectable is behind rect bool separator = false; for (int i = 0; i < 9; ++i) { if (separator) ImGui::SameLine(); std::string label = std::to_string(i + 1); auto prevCurPos = ImGui::GetCursorScreenPos(); ImGui::Selectable(label.data(), &selectables[i], flags, szEl); if (!ImGui::IsItemHovered() && !selectables[i]) { SelectableColorEx(prevCurPos, szEl, IM_COL32(255, 0, 0, 200)); } separator = true; } // Text of selectable is above rect, but i can't use 'ImGui::IsItemHovered()' separator = false; for (int i = 0; i < 9; ++i) { if (separator) ImGui::SameLine(); std::string label = std::to_string(i + 1); auto prevCurPos = ImGui::GetCursorScreenPos(); if (!ImGui::IsItemHovered() && !selectables[i]) { SelectableColor(szEl, IM_COL32(255, 0, 0, 200)); } ImGui::Selectable(label.data(), &selectables[i], flags, szEl); separator = true; } ImGui::End(); } ```