Open OswaldHurlem opened 6 years ago
I’m torn, I think it would be a bit feature creepy to add another layer of format parsing, you can fairly easily recreate the DragFloatN function and make that custom for now?
I can understand that. Just wanted to air my opinion on it. Feel free to close.
Hi, it is pretty easy to get the desired behavior. I am using this
static void PushMultiItemsWidthsAndLabels(const char* labels[], int components, float w_full)
{
ImGuiWindow* window = GetCurrentWindow();
const ImGuiStyle& style = GImGui->Style;
if(w_full <= 0.0f)
w_full = GetContentRegionAvailWidth();
const float w_item_one =
ImMax(1.0f, (w_full - (style.ItemInnerSpacing.x * 2.0f) * (components - 1)) / (float)components) -
style.ItemInnerSpacing.x;
for(int i = 0; i < components; i++)
window->DC.ItemWidthStack.push_back(w_item_one - CalcTextSize(labels[i]).x);
window->DC.ItemWidth = window->DC.ItemWidthStack.back();
}
bool DragFloatNEx(const char* labels[], float* v, int components, float v_speed, float v_min, float v_max,
const char* display_format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if(window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
bool value_changed = false;
BeginGroup();
PushMultiItemsWidthsAndLabels(labels, components, 0.0f);
for(int i = 0; i < components; i++)
{
PushID(labels[i]);
PushID(i);
TextUnformatted(labels[i], FindRenderedTextEnd(labels[i]));
SameLine();
value_changed |= DragFloat("", &v[i], v_speed, v_min, v_max, display_format, power);
SameLine(0, g.Style.ItemInnerSpacing.x);
PopID();
PopID();
PopItemWidth();
}
EndGroup();
return value_changed;
}
which is a wrapper on top of dragfloat. if you are fine with the labels being in front of the widget then this may help you. Example usage:
const char* names[] = {"X", "Y", "Z"};
if(gui::DragFloatNEx(names, &data[0], 3, 0.05f))
will produce something like this:
or you can tweak the function if you want the labels on the right, then instead of the TextUnformatted just fill the DragFloat label param. Hope that helps.
I think as shown in @OswaldHurlem screenshot, having the labels inside the format string is a better idea. It'll be more compact and look nicer with varying width (such as Pitch/Yaw/Roll). In that case you only need to call PushMultiItemsWidths()
.
I try my damndest to keep labels out of the picture and just echo the information already available (ie. highlight with the axis color, etc):
The fringe cases (like CSSM splits) are fringe, and probably need something beyond a basic set of spin-boxes anyways.
Code:
// Dupe of DragFloatN with a tweak to add colored lines
bool DragFloatN_Colored(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power)
{
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
ImGuiContext& g = *GImGui;
bool value_changed = false;
BeginGroup();
PushID(label);
PushMultiItemsWidths(components);
for (int i = 0; i < components; i++)
{
static const ImU32 colors[] = {
0xBB0000FF, // red
0xBB00FF00, // green
0xBBFF0000, // blue
0xBBFFFFFF, // white for alpha?
};
PushID(i);
value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power);
const ImVec2 min = GetItemRectMin();
const ImVec2 max = GetItemRectMax();
const float spacing = g.Style.FrameRounding;
const float halfSpacing = spacing / 2;
// This is the main change
window->DrawList->AddLine({ min.x + spacing, max.y - halfSpacing }, { max.x - spacing, max.y - halfSpacing }, colors[i], 4);
SameLine(0, g.Style.ItemInnerSpacing.x);
PopID();
PopItemWidth();
}
PopID();
TextUnformatted(label, FindRenderedTextEnd(label));
EndGroup();
return value_changed;
}
In case someone is interested, here is my version: https://github.com/Nahor/imgui/commit/e0c30aa69899a578a4908a9265717d408e73fa19.
This is done with a new flag to inline the label (ImGuiSliderFlags_InlineLabel
).
I've ported EditColor4 to use that flag as well, as a validation of the concept.
Usage example:
static float f{};
ImGui::DragFloat("Regular", &f, 1.0f, 0.0f, 0.0f, "%.3f", ImGuiSliderFlags_None);
ImGui::DragFloat("Inline", &f, 1.0f, 0.0f, 0.0f, "%.3f", ImGuiSliderFlags_InlineLabel);
ImGui::Separator();
ImGui::Text("Geometry");
ImGui::Indent();
static float pos[3]{-20.0f, 33.0f, 100.0f};
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::SliderFloat("X", pos, -100.0f, 100.0f, "%.3f", ImGuiSliderFlags_InlineLabel);
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::SliderFloat("Y", pos + 1, -100.0f, 100.0f, "%.3f", ImGuiSliderFlags_InlineLabel);
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::SliderFloat("Z", pos + 2, -100.0f, 100.0f, "%.3f", ImGuiSliderFlags_InlineLabel);
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::Text("Position");
static float orient[3]{-20.0f, 180.0f, 60.0f};
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::DragFloat("Pitch", orient, 0.1f, -180.0f, 180.0f, "%.1f", ImGuiSliderFlags_InlineLabel);
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("Roll", orient + 1, 0.1f, -180.0f, 180.0f, "%.1f", ImGuiSliderFlags_InlineLabel);
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("Yaw", orient + 2, 0.1f, -180.0f, 180.0f, "%.1f", ImGuiSliderFlags_InlineLabel);
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::Text("Orientation");
ImGui::Unindent();
ImGui::Separator();
static float color[4]{.5f, .3f, .6f, 1.0f};
ImGui::ColorEdit4("Color RGB", color, ImGuiColorEditFlags_Float | ImGuiColorEditFlags_DisplayRGB);
ImGui::ColorEdit3("Color HSV", color, ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV);
And this is what it looks like when used:
https://github.com/ocornut/imgui/assets/1198364/ebaf3fc9-4297-46a0-aa17-1de9df1b8f3b
Version/Branch of Dear ImGui: 1.61
My Issue/Question: It would be convenient to be able to pass separate labels corresponding to the individual components of multi-component controls, using some sort of special syntax in either the label or format string. Something like this
or this
To produce this