Skip to content

Commit 261ce9a

Browse files
author
j4kuuu
committed
element/progressbar: add ProgressBar widget with determinate and indeterminate modes
1 parent eec30dc commit 261ce9a

6 files changed

Lines changed: 309 additions & 0 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#pragma once
2+
3+
#include "Element.hpp"
4+
#include "../types/SizeType.hpp"
5+
6+
#include <hyprutils/memory/UniquePtr.hpp>
7+
8+
namespace Hyprtoolkit {
9+
10+
struct SProgressBarImpl;
11+
struct SProgressBarData;
12+
class CProgressBarElement;
13+
14+
class CProgressBarBuilder {
15+
public:
16+
~CProgressBarBuilder() = default;
17+
18+
static Hyprutils::Memory::CSharedPointer<CProgressBarBuilder> begin();
19+
20+
// 0.0 - 1.0 (clamped). ignored when indeterminate is true.
21+
Hyprutils::Memory::CSharedPointer<CProgressBarBuilder> value(float);
22+
23+
// when true, the bar shows a looping pulse instead of value-driven fill
24+
Hyprutils::Memory::CSharedPointer<CProgressBarBuilder> indeterminate(bool);
25+
26+
Hyprutils::Memory::CSharedPointer<CProgressBarBuilder> size(CDynamicSize&&);
27+
28+
Hyprutils::Memory::CSharedPointer<CProgressBarElement> commence();
29+
30+
private:
31+
Hyprutils::Memory::CWeakPointer<CProgressBarBuilder> m_self;
32+
Hyprutils::Memory::CUniquePointer<SProgressBarData> m_data;
33+
Hyprutils::Memory::CWeakPointer<CProgressBarElement> m_element;
34+
35+
CProgressBarBuilder() = default;
36+
37+
friend class CProgressBarElement;
38+
};
39+
40+
class CProgressBarElement : public IElement {
41+
public:
42+
virtual ~CProgressBarElement() = default;
43+
44+
Hyprutils::Memory::CSharedPointer<CProgressBarBuilder> rebuild();
45+
virtual Hyprutils::Math::Vector2D size();
46+
47+
// imperative updates that skip the builder allocation
48+
float value();
49+
void setValue(float);
50+
51+
private:
52+
CProgressBarElement(const SProgressBarData& data);
53+
static Hyprutils::Memory::CSharedPointer<CProgressBarElement> create(const SProgressBarData& data);
54+
55+
void replaceData(const SProgressBarData& data);
56+
57+
virtual void paint();
58+
virtual void reposition(const Hyprutils::Math::CBox& box, const Hyprutils::Math::Vector2D& maxSize = {-1, -1});
59+
virtual std::optional<Hyprutils::Math::Vector2D> preferredSize(const Hyprutils::Math::Vector2D& parent);
60+
virtual std::optional<Hyprutils::Math::Vector2D> minimumSize(const Hyprutils::Math::Vector2D& parent);
61+
virtual std::optional<Hyprutils::Math::Vector2D> maximumSize(const Hyprutils::Math::Vector2D& parent);
62+
virtual bool positioningDependsOnChild();
63+
64+
Hyprutils::Memory::CUniquePointer<SProgressBarImpl> m_impl;
65+
66+
friend class CProgressBarBuilder;
67+
};
68+
};

src/core/AnimationManager.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ CHTAnimationManager::CHTAnimationManager() {
1212
m_animationTree.createNode("global");
1313

1414
m_animationTree.createNode("fast", "global");
15+
m_animationTree.createNode("indeterminate", "global");
1516

1617
m_animationTree.setConfigForNode("fast", 1, 3.3F, "easeOutQuint");
18+
m_animationTree.setConfigForNode("indeterminate", 1, 0.7F, "linear");
1719
}
1820

1921
template <Animable VarType>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include "ProgressBar.hpp"
2+
3+
using namespace Hyprtoolkit;
4+
5+
SP<CProgressBarBuilder> CProgressBarBuilder::begin() {
6+
SP<CProgressBarBuilder> p = SP<CProgressBarBuilder>(new CProgressBarBuilder());
7+
p->m_data = makeUnique<SProgressBarData>();
8+
p->m_self = p;
9+
return p;
10+
}
11+
12+
SP<CProgressBarBuilder> CProgressBarBuilder::value(float v) {
13+
m_data->value = v;
14+
return m_self.lock();
15+
}
16+
17+
SP<CProgressBarBuilder> CProgressBarBuilder::indeterminate(bool v) {
18+
m_data->indeterminate = v;
19+
return m_self.lock();
20+
}
21+
22+
SP<CProgressBarBuilder> CProgressBarBuilder::size(CDynamicSize&& s) {
23+
m_data->size = std::move(s);
24+
return m_self.lock();
25+
}
26+
27+
SP<CProgressBarElement> CProgressBarBuilder::commence() {
28+
if (m_element) {
29+
m_element->replaceData(*m_data);
30+
return m_element.lock();
31+
}
32+
33+
return CProgressBarElement::create(*m_data);
34+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#include "ProgressBar.hpp"
2+
3+
#include <hyprtoolkit/palette/Palette.hpp>
4+
#include <algorithm>
5+
6+
#include "../../core/AnimationManager.hpp"
7+
#include "../../core/InternalBackend.hpp"
8+
#include "../../layout/Positioner.hpp"
9+
#include "../../window/ToolkitWindow.hpp"
10+
#include "../Element.hpp"
11+
12+
using namespace Hyprtoolkit;
13+
using namespace Hyprutils::Math;
14+
using CBAV = Hyprutils::Animation::CBaseAnimatedVariable;
15+
16+
SP<CProgressBarElement> CProgressBarElement::create(const SProgressBarData& data) {
17+
auto p = SP<CProgressBarElement>(new CProgressBarElement(data));
18+
p->impl->self = p;
19+
p->m_impl->self = p;
20+
return p;
21+
}
22+
23+
CProgressBarElement::CProgressBarElement(const SProgressBarData& data) : IElement(), m_impl(makeUnique<SProgressBarImpl>()) {
24+
m_impl->data = data;
25+
26+
m_impl->background = CRectangleBuilder::begin()
27+
->color([] { return g_palette->m_colors.base; })
28+
->rounding(g_palette->m_vars.smallRounding)
29+
->borderColor([] { return g_palette->m_colors.alternateBase; })
30+
->borderThickness(1)
31+
->size(CDynamicSize{CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_PERCENT, {1.F, 1.F}})
32+
->commence();
33+
34+
m_impl->background->setPositionMode(HT_POSITION_ABSOLUTE);
35+
36+
const float W = m_impl->data.indeterminate ? PROGRESSBAR_PULSE_WIDTH : std::clamp(m_impl->data.value, 0.F, 1.F);
37+
38+
m_impl->foreground = CRectangleBuilder::begin()
39+
->color([] { return g_palette->m_colors.accent; })
40+
->rounding(g_palette->m_vars.smallRounding)
41+
->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_PERCENT, {W, 1.F}})
42+
->commence();
43+
44+
m_impl->background->addChild(m_impl->foreground);
45+
addChild(m_impl->background);
46+
47+
impl->grouped = true;
48+
49+
if (m_impl->data.indeterminate)
50+
m_impl->startIndeterminate();
51+
}
52+
53+
void SProgressBarImpl::applyValue() {
54+
const float W = std::clamp(data.value, 0.F, 1.F);
55+
foreground->rebuild()->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_PERCENT, {W, 1.F}})->commence();
56+
}
57+
58+
void SProgressBarImpl::startIndeterminate() {
59+
if (phase)
60+
return;
61+
62+
foreground->setPositionMode(IElement::HT_POSITION_ABSOLUTE);
63+
64+
g_animationManager->createAnimation(0.F, phase, g_animationManager->m_animationTree.getConfig("indeterminate"));
65+
66+
phase->setUpdateCallback([this](WP<CBAV>) { onPulseTick(); });
67+
phase->setCallbackOnEnd(
68+
[this](WP<CBAV>) {
69+
phase->setValueAndWarp(0.F);
70+
*phase = 1.F;
71+
},
72+
false);
73+
74+
*phase = 1.F;
75+
}
76+
77+
void SProgressBarImpl::stopIndeterminate() {
78+
if (!phase)
79+
return;
80+
phase->resetAllCallbacks();
81+
phase.reset();
82+
83+
foreground->setPositionMode(IElement::HT_POSITION_AUTO);
84+
foreground->rebuild()
85+
->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_PERCENT, {std::clamp(data.value, 0.F, 1.F), 1.F}})
86+
->commence();
87+
}
88+
89+
void SProgressBarImpl::onPulseTick() {
90+
const float bgW = background->size().x;
91+
if (bgW <= 0.F)
92+
return;
93+
const float pulseW = bgW * PROGRESSBAR_PULSE_WIDTH;
94+
const float t = phase->value();
95+
const float x = t * (bgW + pulseW) - pulseW;
96+
foreground->setAbsolutePosition({x, 0.0});
97+
}
98+
99+
void CProgressBarElement::paint() {
100+
;
101+
}
102+
103+
void CProgressBarElement::reposition(const Hyprutils::Math::CBox& box, const Hyprutils::Math::Vector2D& maxSize) {
104+
IElement::reposition(box);
105+
g_positioner->positionChildren(impl->self.lock());
106+
}
107+
108+
SP<CProgressBarBuilder> CProgressBarElement::rebuild() {
109+
auto p = SP<CProgressBarBuilder>(new CProgressBarBuilder());
110+
p->m_self = p;
111+
p->m_data = makeUnique<SProgressBarData>(m_impl->data);
112+
p->m_element = m_impl->self;
113+
return p;
114+
}
115+
116+
void CProgressBarElement::replaceData(const SProgressBarData& data) {
117+
const bool VALUE_CHANGED = data.value != m_impl->data.value;
118+
const bool INDET_CHANGED = data.indeterminate != m_impl->data.indeterminate;
119+
120+
m_impl->data = data;
121+
122+
if (INDET_CHANGED) {
123+
if (m_impl->data.indeterminate)
124+
m_impl->startIndeterminate();
125+
else
126+
m_impl->stopIndeterminate();
127+
} else if (VALUE_CHANGED && !m_impl->data.indeterminate)
128+
m_impl->applyValue();
129+
130+
if (impl->window)
131+
impl->window->scheduleReposition(impl->self);
132+
}
133+
134+
float CProgressBarElement::value() {
135+
return m_impl->data.value;
136+
}
137+
138+
void CProgressBarElement::setValue(float v) {
139+
if (v == m_impl->data.value)
140+
return;
141+
m_impl->data.value = v;
142+
m_impl->applyValue();
143+
}
144+
145+
Hyprutils::Math::Vector2D CProgressBarElement::size() {
146+
return impl->position.size();
147+
}
148+
149+
std::optional<Vector2D> CProgressBarElement::preferredSize(const Hyprutils::Math::Vector2D& parent) {
150+
return m_impl->data.size.calculate(parent);
151+
}
152+
153+
std::optional<Vector2D> CProgressBarElement::minimumSize(const Hyprutils::Math::Vector2D& parent) {
154+
return m_impl->data.size.calculate(parent);
155+
}
156+
157+
std::optional<Vector2D> CProgressBarElement::maximumSize(const Hyprutils::Math::Vector2D& parent) {
158+
return m_impl->data.size.calculate(parent);
159+
}
160+
161+
bool CProgressBarElement::positioningDependsOnChild() {
162+
return m_impl->data.size.hasAuto();
163+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
3+
#include <hyprtoolkit/element/ProgressBar.hpp>
4+
#include <hyprtoolkit/element/Rectangle.hpp>
5+
#include <hyprtoolkit/element/Null.hpp>
6+
7+
#include "../../core/AnimatedVariable.hpp"
8+
#include "../../helpers/Memory.hpp"
9+
10+
namespace Hyprtoolkit {
11+
12+
struct SProgressBarData {
13+
float value = 0.F;
14+
bool indeterminate = false;
15+
CDynamicSize size{CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_ABSOLUTE, {1.F, 16.F}};
16+
};
17+
18+
// pulse rectangle width as a fraction of the bar
19+
static constexpr float PROGRESSBAR_PULSE_WIDTH = 0.3F;
20+
21+
struct SProgressBarImpl {
22+
SProgressBarData data;
23+
24+
WP<CProgressBarElement> self;
25+
26+
SP<CRectangleElement> background;
27+
SP<CRectangleElement> foreground;
28+
29+
// 0..1 cycling phase used for the indeterminate pulse
30+
PHLANIMVAR<float> phase;
31+
32+
void applyValue();
33+
void startIndeterminate();
34+
void stopIndeterminate();
35+
void onPulseTick();
36+
};
37+
}

tests/Controls.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <hyprtoolkit/element/Null.hpp>
1111
#include <hyprtoolkit/element/Checkbox.hpp>
1212
#include <hyprtoolkit/element/Spinbox.hpp>
13+
#include <hyprtoolkit/element/ProgressBar.hpp>
1314
#include <hyprtoolkit/element/Slider.hpp>
1415
#include <hyprtoolkit/element/ScrollArea.hpp>
1516
#include <hyprtoolkit/element/Combobox.hpp>
@@ -200,6 +201,9 @@ int main(int argc, char** argv, char** envp) {
200201
auto slider2 = stretchLayout(
201202
"Big Slider", CSliderBuilder::begin()->max(10000)->val(2500)->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_ABSOLUTE, {0.5F, SLIDER_HEIGHT}})->commence());
202203

204+
auto progress = stretchLayout(
205+
"Progress", CProgressBarBuilder::begin()->value(0.42F)->size({CDynamicSize::HT_SIZE_PERCENT, CDynamicSize::HT_SIZE_ABSOLUTE, {0.5F, 14.F}})->commence());
206+
203207
auto combo = stretchLayout(
204208
"Combo",
205209
CComboboxBuilder::begin()
@@ -242,6 +246,7 @@ int main(int argc, char** argv, char** envp) {
242246
mainLayout->addChild(spinbox);
243247
mainLayout->addChild(slider);
244248
mainLayout->addChild(slider2);
249+
mainLayout->addChild(progress);
245250
mainLayout->addChild(combo);
246251
mainLayout->addChild(textboxCont);
247252
mainLayout->addChild(text);

0 commit comments

Comments
 (0)