-
-
Notifications
You must be signed in to change notification settings - Fork 10.6k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Knob widget? #942
Comments
I would like to add an official one but there's no immediate short-term plan. It's pretty trivial to create a custom one using the imgui_internal.h api tho.
|
Ohh, I thought I could do it with custom drawing and then IsItemActive to capture mouse but I see that Dummy item always returns false. |
You may use InvisibleButton() Dummy() doesn't interact (most interactions need an id). Browsing code is unfortunately the easiest way to get more detailed example if you want to rely on internal api, should be doable with just InvisibleButton() as long as you don't mind the axis-aligned hit check.
|
This is my luajit implementation of a knob with imgui (cimgui dialect) Being able to do a widget with a script languaje makes me a complete imgui fan!!!
|
Cool :) The string is necessary to uniquely identify the widget using the id-stack system (read FAQ about id if unsure). Namely, it is used for the button press-hold-release mechanism. For the color you can use I am not exactly sure what your spacing is exactly, but it may be
It gets really cool once you master those lower-level bits. The only issue I can think of here is that a language like Lua may not scale very well in term of performance, if you have hundreds of widgets you might want to implement in in C++ and have a single call to a native function? |
I wrote a little demo widget using the public API. This is also exhibiting some of the issues with the public API which hasn't been used so much for custom widgets. There's no current public api to trivially do text clipping and alignment whereas the internal api has those. Hoping to improve the situation later but I'm still very unhappy with the internal text functions (they word but their api isn't fit for public api).
|
Thanks for code, my new task will be testing performace of luajit vs a single call to MyKnob. |
(It is still totally overkill to use anti-aliased triangles to draw circles there, we're using like a hundred triangles there, I have a task to rasterize circles into the texture atlas to avoid that.) |
I have tested luajit versus C++ implementation of knob not so bad for luajit? c++ code
|
A very strange observation is that if this window is open on program start, FPS go as low as 100 and we get the expected 260 after hiding, showing and hiding the window. If window starts hidden this does not happen. |
luajit vs c++ looks quite ok here! On the number themselves, assuming that you are early returning with Begin() returns false,
On your mystery perf difference,
Thanks for querying, that sort of thing really ought to be improved! |
Hello Omar, Now I am making a dial (values represent angle) and I need current mouse_pos but also previous mouse pos. I am worried because it is part of internal and you say "Forward compatibility not guaranteed!" |
Good point, thanks for asking. No worry about those two. I think they are here to stay and I expect lots of code has been relying on them. I may move them elsewhere in the function to clarify. |
I think I will also make it that the publicly exposed MousePosPrev is set to the expected value to be used. But for now MouseDelta works. |
Great!! |
So, what's the state of knobs in imgui? The demo you've posted looks quite promising. (FR, I too am using Lua, but love-imgui and loving imgui so far) |
@turbo You can freely add this code and use it with your version of imgui. I don't think it'll be able to master very soon. I imagine in the future we'll add more widgets including perhaps some form of knobs but I also think the priority should be to stabilize the internal API and provide helpers to incentive people to build their own widgets. |
Here is another knob in Lua with a InputFloat added
|
Would love these in C++ :) |
the code is small it should be easy to port back to C++ |
It would be great to add something like this into ImGUI proper. Knobs are a common request. @ocornut If you're open to that I'll do the translation and PR... |
I think you can start convetting this into a standalone repo and we can decide late if they are suitable to merge into mainline. |
@frink I have not much to offer for your time... I could help test! But short on time to learn some RUST right now, and it looks familiar, but yeah I am a bit lost. I'd love these knobs and hundreds of ImGui users would too :) but I could offer a AUD$75 donation to the cause to bring them to everyone... or $100 if they get pulled (25% for ocornut's time). Not much I know, don't mean to offend. Or maybe one day I will learn to do myself... ¯_(ツ)_/¯ right now more focused on the use. |
Not suggesting it be merge-for-payment by the way... of course the code would need to be appropriate for maintainers. |
My time is all of a sudden limited for a while. Sorry. |
I've made a C++ port of https://github.com/DGriffin91/imgui-rs-knobs available here https://github.com/altschuler/imgui-knobs. PRs, advice, review etc is very welcome. |
Thanks @altschuler |
I've combined ocornut's visual style and sonoro's dragging behaviour, also added the option to snap the value (this is in C# but should be trivial to port to C++). public static bool Knob(string label, ref float value, float min, float max, int snapSteps)
{
ImGuiIOPtr io = ImGui.GetIO();
ImGuiStylePtr style = ImGui.GetStyle();
const float radiusOuter = 20.0f;
Vector2 cursorPosition = ImGui.GetCursorScreenPos();
Vector2 center = new Vector2(cursorPosition.X + radiusOuter, cursorPosition.Y + radiusOuter);
float lineHeight = ImGui.GetTextLineHeight();
ImDrawListPtr drawList = ImGui.GetWindowDrawList();
ImGui.InvisibleButton(label, new Vector2(radiusOuter * 2, radiusOuter * 2 + lineHeight + style.ItemInnerSpacing.Y));
bool valueChanged = false;
bool isActive = ImGui.IsItemActive();
bool isHovered = ImGui.IsItemActive();
float t = (value - min) / (max - min);
float gamma = MathF.PI / 4.0f;
float alpha = (MathF.PI - gamma) * t * 2.0f + gamma;
if(isActive && io.MouseDelta.Length() > 0.0f)
{
Vector2 mousePosition = ImGui.GetIO().MousePos;
alpha = MathF.Atan2(mousePosition.X - center.X, center.Y - mousePosition.Y) + MathF.PI;
alpha = MathF.Max(gamma, MathF.Min(2.0f * MathF.PI - gamma, alpha));
float val = 0.5f * (alpha - gamma) / (MathF.PI - gamma);
if(snapSteps > 0)
{
float stepSize = (max - min) / snapSteps;
float snappedValue = min + MathF.Round(val * snapSteps) * stepSize;
value = (float)Math.Clamp(snappedValue, min, max);
}
else
{
value = val * (max - min) + min;
}
valueChanged = true;
}
const float ANGLE_MIN = 3.141592f * 0.75f;
const float ANGLE_MAX = 3.141592f * 2.25f;
float angle = ANGLE_MIN + (ANGLE_MAX - ANGLE_MIN) * t;
float angleCos = MathF.Cos(angle), angle_sin = MathF.Sin(angle);
float radiusInner = radiusOuter * 0.40f;
drawList.AddCircleFilled(center, radiusOuter, ImGui.GetColorU32(ImGuiCol.FrameBg), 16);
drawList.AddLine(new Vector2(center.X + angleCos * radiusInner, center.Y + angle_sin * radiusInner), new Vector2(center.X + angleCos * (radiusOuter - 2), center.Y + angle_sin * (radiusOuter - 2)), ImGui.GetColorU32(ImGuiCol.SliderGrabActive), 2.0f);
drawList.AddCircleFilled(center, radiusInner, ImGui.GetColorU32(isActive ? ImGuiCol.FrameBgActive : isHovered ? ImGuiCol.FrameBgHovered : ImGuiCol.FrameBg), 16);
drawList.AddText(new Vector2(cursorPosition.X, cursorPosition.Y + radiusOuter * 2 + style.ItemInnerSpacing.Y), ImGui.GetColorU32(ImGuiCol.Text), label);
if (isActive || isHovered)
{
string text = value.ToString("0.000");
ImGui.SetNextWindowPos(new Vector2(cursorPosition.X - style.WindowPadding.X, cursorPosition.Y - lineHeight - style.ItemInnerSpacing.Y - style.WindowPadding.Y));
ImGui.BeginTooltip();
ImGui.Text(text);
ImGui.EndTooltip();
}
return valueChanged;
} |
Here is another version that uses a spritesheet. struct ImKnobInfo
{
ImTextureID textureId;
int numberOfSprites;
int rows;
int columns;
ImKnobInfo(ImTextureID textureId, int numberOfSprites, int rows, int columns)
{
this->textureId = textureId;
this->numberOfSprites = numberOfSprites;
this->rows = rows;
this->columns = columns;
}
};
bool Knob(const char *label, const ImKnobInfo &knobInfo, const ImVec2 &size, float *value, float min, float max, int snapSteps)
{
ImVec2 cursorPosition = ImGui::GetCursorScreenPos();
ImVec2 center(cursorPosition.x + (size.x * 0.5f), cursorPosition.y + (size.y * 0.5f));
float lineHeight = ImGui::GetTextLineHeight();
auto &style = ImGui::GetStyle();
ImGui::InvisibleButton(label, ImVec2(size.x, size.y + lineHeight + style.ItemInnerSpacing.y));
bool valueChanged = false;
bool isActive = ImGui::IsItemActive();
bool isHovered = ImGui::IsItemHovered();
bool isDragging = ImGui::IsMouseDragging(ImGuiMouseButton_Left);
float t = (*value - min) / (max - min);
float gamma = M_PI / 4.0f;
float alpha = (M_PI - gamma) * t * 2.0f + gamma;
auto clamp = [] (float v, float mn, float mx) {
return v < mn ? mn : (v > mx ? mx : v);
};
if(isActive && isDragging)
{
auto &io = ImGui::GetIO();
ImVec2 mousePosition = io.MousePos;
alpha = atan2(mousePosition.x - center.x, center.y - mousePosition.y) + M_PI;
alpha = fmax(gamma, fmin(2.0f * M_PI - gamma, alpha));
float val = 0.5f * (alpha - gamma) / (M_PI - gamma);
if(snapSteps > 0)
{
float stepSize = (max - min) / snapSteps;
float snappedValue = min + round(val * snapSteps) * stepSize;
*value = (float)clamp(snappedValue, min, max);
}
else
{
*value = val * (max - min) + min;
}
valueChanged = true;
}
auto getUVCoordinates = [] (float percentage, size_t numSprites, int rows, int columns, float *uv0, float *uv1) {
size_t index = static_cast<size_t>(floor(percentage * (numSprites-1)));
float spriteWidth = 1.0f / columns;
float spriteHeight = 1.0f / rows;
int colIndex = index % columns;
int rowIndex = index / columns;
uv0[0] = colIndex * spriteWidth;
uv0[1] = rowIndex * spriteHeight;
uv1[0] = uv0[0] + spriteWidth;
uv1[1] = uv0[1] + spriteHeight;
};
ImVec2 uv0, uv1;
getUVCoordinates(t, knobInfo.numberOfSprites -1, knobInfo.rows, knobInfo.columns, &uv0.x, &uv1.x);
ImVec2 pMin = cursorPosition;
ImVec2 pMax(pMin.x + size.x, pMin.y + size.y);
auto drawList = ImGui::GetWindowDrawList();
drawList->AddImage(knobInfo.textureId, pMin, pMax, uv0, uv1);
if (isActive || isHovered)
{
ImGui::SetNextWindowPos(ImVec2(cursorPosition.x - style.WindowPadding.x, cursorPosition.y - lineHeight - style.ItemInnerSpacing.y - style.WindowPadding.y));
ImGui::BeginTooltip();
ImGui::Text("%s %.3f", label, *value);
ImGui::EndTooltip();
}
return valueChanged;
} I've used this sheet for testing which I got from Knobman. // There are a total of 89 separate knob images.
// The image has 9 rows and 10 columns.
ImKnobInfo knobInfo(knobsTextureId, 89, 9, 10);
ImGui::Begin("Audio Settings");
float masterVolume = GetMasterVolume();
if(Knob("Master Volume", knobInfo, ImVec2(64, 64), &masterVolume, 0.0f, 1.0f, 0))
SetMasterVolume(masterVolume);
ImGui::End(); |
Hi,
Is there any plan to do a rotary knob widget?
Thanks
The text was updated successfully, but these errors were encountered: