Skip to content

Commit 99058f3

Browse files
v-einhoffstadt
authored andcommitted
fix (mvLoadingIndicator): Corrected size calculations for the ring-style indicator.
Old indicator rendering is kept at style=1 for compatibility; new corrected rendering can be selected with style=mvLoadInd_Ring. Also, colors are taken from Button/ButtonHovered styles, as was intended in the original design. Also, the indicator should not receive navigation focus (since it's not actionable anyway).
1 parent b7f747d commit 99058f3

File tree

9 files changed

+138
-39
lines changed

9 files changed

+138
-39
lines changed

dearpygui/_dearpygui.pyi

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dearpygui/_dearpygui_RTD.py

Lines changed: 9 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dearpygui/dearpygui.py

Lines changed: 10 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dearpygui.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ GetModuleConstants()
8484
ModuleConstants.push_back({"mvScrollDirection_Horizontal", mvScrollDirection_Horizontal });
8585
ModuleConstants.push_back({"mvScrollDirection_Vertical", mvScrollDirection_Vertical });
8686

87+
ModuleConstants.push_back({"mvLoadInd_DottedCircle", mvLoadingIndicator::Style_DottedCircle });
88+
ModuleConstants.push_back({"mvLoadInd_Ring", mvLoadingIndicator::Style_Ring });
89+
8790
ModuleConstants.push_back({"mvPlatform_Windows", 0L });
8891
ModuleConstants.push_back({"mvPlatform_Apple", 1L });
8992
ModuleConstants.push_back({"mvPlatform_Linux", 2L });

src/mvAppItem.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4542,13 +4542,13 @@ DearPyGui::GetEntityParser(mvAppItemType type)
45424542
MV_PARSER_ARG_POS)
45434543
);
45444544

4545-
args.push_back({ mvPyDataType::Integer, "style", mvArgType::KEYWORD_ARG, "0", "0 is rotating dots style, 1 is rotating bar style." });
4546-
args.push_back({ mvPyDataType::Integer, "circle_count", mvArgType::KEYWORD_ARG, "8", "Number of dots show if dots or size of circle if circle." });
4547-
args.push_back({ mvPyDataType::Float, "speed", mvArgType::KEYWORD_ARG, "1.0", "Speed the anamation will rotate." });
4548-
args.push_back({ mvPyDataType::Float, "radius", mvArgType::KEYWORD_ARG, "3.0", "Radius size of the loading indicator." });
4549-
args.push_back({ mvPyDataType::Float, "thickness", mvArgType::KEYWORD_ARG, "1.0", "Thickness of the circles or line." });
4550-
args.push_back({ mvPyDataType::IntList, "color", mvArgType::KEYWORD_ARG, "(51, 51, 55, 255)", "Color of the growing center circle." });
4551-
args.push_back({ mvPyDataType::IntList, "secondary_color", mvArgType::KEYWORD_ARG, "(29, 151, 236, 103)", "Background of the dots in dot mode." });
4545+
args.push_back({ mvPyDataType::Integer, "style", mvArgType::KEYWORD_ARG, "0", "mvLoadInd_DottedCircle is rotating dots style, mvLoadInd_Ring is rotating bar style." });
4546+
args.push_back({ mvPyDataType::Integer, "circle_count", mvArgType::KEYWORD_ARG, "8", "DottedCircle style: number of dots to show." });
4547+
args.push_back({ mvPyDataType::Float, "speed", mvArgType::KEYWORD_ARG, "1.0", "Speed with which the animation will rotate." });
4548+
args.push_back({ mvPyDataType::Float, "radius", mvArgType::KEYWORD_ARG, "3.0", "Scale factor for the loading indicator radius. The size of the indicator is determined by font size and this scale factor." });
4549+
args.push_back({ mvPyDataType::Float, "thickness", mvArgType::KEYWORD_ARG, "1.0", "Ring style: scale factor of line thickness; thickness=1 corresponds to line width being 1/8 of the ring diameter." });
4550+
args.push_back({ mvPyDataType::IntList, "color", mvArgType::KEYWORD_ARG, "None", "Main color of the indicator. If omitted, the color for mvThemeCol_Button will be used." });
4551+
args.push_back({ mvPyDataType::IntList, "secondary_color", mvArgType::KEYWORD_ARG, "None", "DottedCircle style: color of 'inactive' dots. If omitted, the color for mvThemeCol_ButtonHovered will be used." });
45524552

45534553
setup.about = "Adds a rotating animated loading symbol.";
45544554
break;

src/mvLoadingIndicator.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,33 @@ void mvLoadingIndicator::draw(ImDrawList* drawlist, float x, float y)
5757
{
5858
ScopedID id(uuid);
5959

60-
if (_style == 0)
60+
switch (_style)
61+
{
62+
case Style_OldDottedCircle:
6163
LoadingIndicatorCircle(config.specifiedLabel.c_str(), _radius, _mainColor, _optionalColor, _circleCount, _speed);
62-
else
64+
break;
65+
case Style_OldRing:
6366
LoadingIndicatorCircle2(config.specifiedLabel.c_str(), _radius, _thickness, _mainColor);
67+
break;
68+
case Style_DottedCircle:
69+
// This is to align the indicator body to text rather than to framed rect
70+
// (can be useful with radius=1).
71+
ImGui::AlignTextToFramePadding();
72+
LoadingIndicatorCircle(config.specifiedLabel.c_str(), _radius, _mainColor, _optionalColor, _circleCount, _speed);
73+
break;
74+
case Style_Ring:
75+
{
76+
const float pixSize = (_radius > 0? _radius : 1.f) * ImGui::GetTextLineHeight();
77+
// `thickness=1` corresponds to line thickness being 1/4 of the ring radius,
78+
// i.e. 1/8 of the diameter (pixSize).
79+
const float pixThickness = (_thickness > 0? _thickness : 1.f) * pixSize * 0.125f;
80+
// This is to align the indicator body to text rather than to framed rect
81+
// (can be useful with radius=1).
82+
ImGui::AlignTextToFramePadding();
83+
LoadingIndicatorRing(config.specifiedLabel.c_str(), pixSize, pixThickness, _speed, _mainColor);
84+
}
85+
break;
86+
}
6487
}
6588

6689
//-----------------------------------------------------------------------------
@@ -98,13 +121,19 @@ void mvLoadingIndicator::handleSpecificKeywordArgs(PyObject* dict)
98121
if (dict == nullptr)
99122
return;
100123

101-
if (PyObject* item = PyDict_GetItemString(dict, "style")) _style = ToInt(item);
124+
if (PyObject* item = PyDict_GetItemString(dict, "style")) _style = static_cast<Style>(ToInt(item));
102125
if (PyObject* item = PyDict_GetItemString(dict, "circle_count")) _circleCount = ToInt(item);
103126
if (PyObject* item = PyDict_GetItemString(dict, "radius")) _radius = ToFloat(item);
104127
if (PyObject* item = PyDict_GetItemString(dict, "thickness")) _thickness = ToFloat(item);
105128
if (PyObject* item = PyDict_GetItemString(dict, "speed")) _speed = ToFloat(item);
106-
if (PyObject* item = PyDict_GetItemString(dict, "color")) _mainColor = ToColor(item);
107-
if (PyObject* item = PyDict_GetItemString(dict, "secondary_color")) _optionalColor = ToColor(item);
129+
if (PyObject* item = PyDict_GetItemString(dict, "color"))
130+
{
131+
_mainColor = (item != Py_None)? ToColor(item) : mvColor();
132+
}
133+
if (PyObject* item = PyDict_GetItemString(dict, "secondary_color"))
134+
{
135+
_optionalColor = (item != Py_None)? ToColor(item) : mvColor();
136+
}
108137
}
109138

110139
void mvLoadingIndicator::getSpecificConfiguration(PyObject* dict)

src/mvLoadingIndicator.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,21 @@ class mvLoadingIndicator : public mvAppItem
1313
void handleSpecificKeywordArgs(PyObject* dict) override;
1414
void getSpecificConfiguration(PyObject* dict) override;
1515

16+
enum Style {
17+
Style_OldDottedCircle = 0,
18+
Style_OldRing,
19+
Style_DottedCircle,
20+
Style_Ring,
21+
};
22+
1623
private:
1724

18-
int _style = 0;
25+
Style _style = Style_DottedCircle;
1926
int _circleCount = 8;
2027
float _radius = 3.0f;
2128
float _speed = 1.0f;
2229
float _thickness = 1.0f;
23-
mvColor _mainColor = mvColor(51, 51, 55, 255);
24-
mvColor _optionalColor = mvColor(29, 151, 236, 103);
30+
mvColor _mainColor;
31+
mvColor _optionalColor;
2532

2633
};

src/mvLoadingIndicatorCustom.cpp

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@ void LoadingIndicatorCircle(const char* label, float indicatorRadiusFactor,
1919

2020
if (circle_count <= 0) circle_count = 12;
2121
if (indicatorRadiusFactor <= 0.f) indicatorRadiusFactor = 1.f;
22-
if (!pOptionalMainColor) pOptionalMainColor = &style.Colors[ImGuiCol_Button];
23-
if (!pOptionalBackdropColor) pOptionalBackdropColor = &style.Colors[ImGuiCol_ButtonHovered];
22+
if (!pOptionalMainColor || pOptionalMainColor->w < 0) pOptionalMainColor = &style.Colors[ImGuiCol_Button];
23+
if (!pOptionalBackdropColor || pOptionalBackdropColor->w < 0) pOptionalBackdropColor = &style.Colors[ImGuiCol_ButtonHovered];
2424

2525
const float lineHeight = ImGui::GetTextLineHeight(); // or GetTextLineHeight() or GetTextLineHeightWithSpacing() ?
2626
float indicatorRadiusPixels = indicatorRadiusFactor * lineHeight * 0.5f;
2727

28-
const ImVec2 pos = window->DC.CursorPos;
28+
const ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
2929
const float circle_radius = indicatorRadiusPixels / 8.f;
3030
indicatorRadiusPixels -= 2.0f * circle_radius;
3131
const ImRect bb(pos, ImVec2(pos.x + indicatorRadiusPixels * 2.f + 4.f * circle_radius,
3232
pos.y + indicatorRadiusPixels * 2.f + 4.f * circle_radius));
33-
ImGui::ItemSize(bb, style.FramePadding.y);
34-
if (!ImGui::ItemAdd(bb, id)) {
33+
ImGui::ItemSize(bb, 0.0f);
34+
if (!ImGui::ItemAdd(bb, id, nullptr, ImGuiItemFlags_NoNav)) {
3535
return;
3636
}
3737
const float base_num_segments = circle_radius * 1.f;
@@ -69,7 +69,7 @@ void LoadingIndicatorCircle2(const char* label, float indicatorRadiusFactor, flo
6969

7070
if (indicatorRadiusFactor <= 0.f) indicatorRadiusFactor = 1.f;
7171
if (indicatorRadiusThicknessFactor <= 0.f) indicatorRadiusThicknessFactor = 1.f;
72-
if (!pOptionalColor) pOptionalColor = &style.Colors[ImGuiCol_Button];
72+
if (!pOptionalColor || pOptionalColor->w < 0) pOptionalColor = &style.Colors[ImGuiCol_Button];
7373
const ImU32 color = ImGui::GetColorU32(*pOptionalColor);
7474

7575
const float lineHeight = ImGui::GetTextLineHeight(); // or GetTextLineHeight() or GetTextLineHeightWithSpacing() ?
@@ -96,7 +96,7 @@ void LoadingIndicatorCircle2(const char* label, float indicatorRadiusFactor, flo
9696

9797
int num_segments = 30;
9898

99-
int start = abs(ImSin(g.Time * 1.8f) * (num_segments - 5));
99+
int start = abs((int)(ImSin(g.Time * 1.8f) * (num_segments - 5)));
100100

101101
const float a_min = IM_PI * 2.0f * ((float)start) / (float)num_segments;
102102
const float a_max = IM_PI * 2.0f * ((float)num_segments - 3) / (float)num_segments;
@@ -110,4 +110,56 @@ void LoadingIndicatorCircle2(const char* label, float indicatorRadiusFactor, flo
110110
}
111111

112112
window->DrawList->PathStroke(color, false, indicatorThicknessPixels);
113-
}
113+
}
114+
115+
// A corrected version of LoadingIndicatorCircle2 that calculates sizes/positions properly.
116+
void LoadingIndicatorRing(const char* label, float size, float thickness, float speed, const ImVec4* pOptionalColor) {
117+
ImGuiWindow* window = ImGui::GetCurrentWindow();
118+
if (window->SkipItems)
119+
return;
120+
121+
ImGuiContext& g = *GImGui;
122+
const ImGuiStyle& style = g.Style;
123+
const ImGuiID id = window->GetID(label);
124+
125+
if (!pOptionalColor || pOptionalColor->w < 0) pOptionalColor = &style.Colors[ImGuiCol_Button];
126+
const ImU32 color = ImGui::GetColorU32(*pOptionalColor);
127+
128+
float radius = size * 0.5f;
129+
130+
const ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
131+
const ImRect bb(pos, ImVec2(pos.x + size, pos.y + size));
132+
133+
ImGui::ItemSize(bb, 0.0f);
134+
if (!ImGui::ItemAdd(bb, id, nullptr, ImGuiItemFlags_NoNav))
135+
return;
136+
137+
// Render
138+
window->DrawList->PathClear();
139+
140+
//int num_segments = radius/8.f;
141+
//if (num_segments<4) num_segments=4;
142+
143+
int num_segments = 30;
144+
145+
float t = g.Time * speed;
146+
147+
// With this formula it looks very close to the original LoadingIndicatorCircle2
148+
// but does not have visual artifacts (jitter of the ring tail when it slows down).
149+
float start = 0.5f * (1 - ImSin(t*3.0f)) * (num_segments - 5);
150+
151+
const float a_min = IM_PI * 2.0f * start / (float)num_segments;
152+
const float a_max = IM_PI * 2.0f * ((float)num_segments - 2) / (float)num_segments;
153+
154+
const ImVec2 centre = ImVec2(pos.x + radius, pos.y + radius);
155+
156+
radius -= thickness * 0.5f;
157+
158+
for (int i = 0; i < num_segments; i++) {
159+
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
160+
window->DrawList->PathLineTo(ImVec2(centre.x + ImCos(a + t * 8) * radius,
161+
centre.y + ImSin(a + t * 8) * radius));
162+
}
163+
164+
window->DrawList->PathStroke(color, false, thickness);
165+
}

src/mvLoadingIndicatorCustom.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ void LoadingIndicatorCircle(const char* label, float indicatorRadiusFactor = 1.f
1010

1111
// Posted by @zfedoran here: https://github.com/ocornut/imgui/issues/1901
1212
// Sligthly modified to provide default behaviour with default args
13-
void LoadingIndicatorCircle2(const char* label, float indicatorRadiusFactor = 1.f, float indicatorRadiusThicknessFactor = 1.f, const ImVec4* pOptionalColor = NULL);
13+
void LoadingIndicatorCircle2(const char* label, float indicatorRadiusFactor = 1.f, float indicatorRadiusThicknessFactor = 1.f, const ImVec4* pOptionalColor = NULL);
14+
15+
void LoadingIndicatorRing(const char* label, float size, float thickness, float speed = 1.f, const ImVec4* pOptionalColor = nullptr);

0 commit comments

Comments
 (0)