swift
loadindicator.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 // Class based on qLed: Copyright (C) 2010 by P. Sereno, http://www.sereno-online.com
4 
5 #include "loadindicator.h"
6 
7 #include <QColor>
8 #include <QPainter>
9 #include <QPointer>
10 #include <QRect>
11 #include <QSizePolicy>
12 #include <QtGlobal>
13 
14 #include "gui/guiapplication.h"
15 #include "misc/threadutils.h"
16 #include "misc/verify.h"
17 
18 using namespace swift::misc;
19 
20 namespace swift::gui
21 {
22  CLoadIndicator::CLoadIndicator(int width, int height, QWidget *parent) : QWidget(parent)
23  {
24  this->setObjectName("CLoadIndicator");
25  this->resize(width, height);
26  this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
27  this->setFocusPolicy(Qt::NoFocus);
28  this->setAutoFillBackground(true);
29  this->setStyleSheet("background-color: transparent;");
30  }
31 
32  bool CLoadIndicator::isAnimated() const { return (m_timerId != -1); }
33 
35  {
36  m_displayedWhenStopped = state;
37  this->update();
38  }
39 
40  bool CLoadIndicator::isDisplayedWhenStopped() const { return m_displayedWhenStopped; }
41 
42  int CLoadIndicator::startAnimation(std::chrono::milliseconds timeout, bool processEvents)
43  {
44  using namespace std::chrono_literals;
45  m_angle = 0;
46  this->show();
47  this->setEnabled(true);
48 
49  QPointer<CLoadIndicator> myself(this);
50  if (m_timerId == -1) { m_timerId = startTimer(m_delayMs); }
51  if (processEvents && sGui)
52  {
54  if (!myself) // cppcheck-suppress knownConditionTrueFalse
55  {
56  return -1; // deleted in meantime (process events)
57  }
58  }
59 
60  const int stopId = m_currentId++; // copy
61  if (timeout > 0ms)
62  {
63  QTimer::singleShot(timeout, this, [=] {
64  if (!myself) { return; }
65 
66  // only timeout myself id
67  this->stopAnimation(stopId);
68  emit this->timedOut();
69  });
70  }
71  m_pendingIds.push_back(stopId);
72  return stopId;
73  }
74 
75  void CLoadIndicator::stopAnimation(int indicatorId)
76  {
77  if (indicatorId > 0)
78  {
79  m_pendingIds.removeOne(indicatorId);
80  // if others pending do not stop
81  if (!m_pendingIds.isEmpty()) { return; }
82  }
83  m_pendingIds.clear();
84  if (m_timerId != -1)
85  {
86  SWIFT_AUDIT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Try to kill timer from another thread");
87  this->killTimer(m_timerId);
88  }
89  m_timerId = -1;
90  this->hide();
91  this->setEnabled(false);
92  this->update();
93  }
94 
96  {
97  m_delayMs = delay;
98  if (m_timerId != -1)
99  {
100  SWIFT_AUDIT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Try to kill timer from another thread");
101  this->killTimer(m_timerId);
102  }
103  m_timerId = this->startTimer(m_delayMs);
104  }
105 
106  void CLoadIndicator::setColor(const QColor &color)
107  {
108  m_color = color;
109  update();
110  }
111 
112  QSize CLoadIndicator::sizeHint() const { return QSize(64, 64); }
113 
114  int CLoadIndicator::heightForWidth(int w) const { return w; }
115 
116  void CLoadIndicator::timerEvent(QTimerEvent *event)
117  {
118  Q_UNUSED(event)
119  m_angle = (m_angle + 30) % 360;
120  this->update();
121  }
122 
123  void CLoadIndicator::paintEvent(QPaintEvent *event)
124  {
125  Q_UNUSED(event)
126  QPainter p(this);
127  this->paint(p);
128  }
129 
131  {
132  if (this->parentWidget()) { return parentWidget()->isVisible(); }
133  return false;
134  }
135 
136  void CLoadIndicator::paint(QPainter &painter) const
137  {
138  if (!m_displayedWhenStopped && !isAnimated()) { return; }
139  if (!this->isVisible() || !this->isEnabled()) { return; }
140  if (!isParentVisible()) { return; }
141 
142  int width = qMin(this->width(), this->height());
143  painter.setRenderHint(QPainter::Antialiasing);
144 
145  // painter.setBrush(QBrush(QColor(0, 0, 255)));
146  // painter.drawEllipse(0, 0, width, width);
147 
148  int outerRadius = qRound((width - 1) * 0.5);
149  int innerRadius = qRound((width - 1) * 0.5 * 0.38);
150 
151  int capsuleHeight = outerRadius - innerRadius;
152  int capsuleWidth = (width > 32) ? qRound(capsuleHeight * .23) : qRound(capsuleHeight * .35);
153  int capsuleRadius = capsuleWidth / 2;
154 
155  for (int i = 0; i < 12; i++)
156  {
157  QColor color = m_color;
158  color.setAlphaF(qRound(1.0f - (i / 12.0f)));
159  painter.setPen(Qt::NoPen);
160  painter.setBrush(color);
161  painter.save();
162  painter.translate(rect().center());
163  painter.rotate(m_angle - qRound(i * 30.0f));
164  painter.drawRoundedRect(-qRound(capsuleWidth * 0.5), -(innerRadius + capsuleHeight), capsuleWidth,
165  capsuleHeight, capsuleRadius, capsuleRadius);
166  painter.restore();
167  }
168  }
169 
170  void CLoadIndicator::centerLoadIndicator(const QPoint &middle)
171  {
172  const int w = this->width();
173  const int h = this->height();
174  const int x = middle.x() - w / 2;
175  const int y = middle.y() - h / 2;
176  this->setGeometry(x, y, w, h);
177  }
178 
179  CLoadIndicatorEnabled::CLoadIndicatorEnabled(QWidget *usingWidget) : m_usingWidget(usingWidget)
180  {
181  Q_ASSERT_X(usingWidget, Q_FUNC_INFO, "need widget");
182  }
183 
185  {
186  return m_loadIndicator && m_usingWidget->isVisible() && m_loadIndicator->isAnimated();
187  }
188 
190 
191  void CLoadIndicatorEnabled::showLoading(std::chrono::milliseconds timeout, bool processEvents)
192  {
193  if (!m_loadIndicator)
194  {
196  QObject::connect(m_loadIndicator, &CLoadIndicator::timedOut, [this] { this->indicatorTimedOut(); });
197  }
198 
199  this->centerLoadIndicator();
200  m_indicatorId = m_loadIndicator->startAnimation(timeout, processEvents);
201  }
202 
204  {
206  }
207 
209  {
210  if (!m_loadIndicator) { return; }
211  const QPoint middle = m_usingWidget->visibleRegion().boundingRect().center();
213  }
214 
216  {
217  // to be overridden
218  }
219 } // namespace swift::gui
void processEventsToRefreshGui() const
Allow the GUI to refresh by processing events, call the event loop.
QWidget * m_usingWidget
widget which uses load indicator
void centerLoadIndicator()
Center load indicator.
bool m_loadInProgress
flag indicating loading
CLoadIndicator * m_loadIndicator
indicator itself
void showLoading(std::chrono::milliseconds timeout=std::chrono::milliseconds(0), bool processEvents=true)
Show load indicator.
bool isLoadInProgress() const
Loading in progress?
virtual void indicatorTimedOut()
Indicator timed out.
void hideLoading()
Hide load indicator.
CLoadIndicatorEnabled(QWidget *usingWidget)
Ctor.
int m_indicatorId
last indicator id returned
bool isShowingLoadIndicator() const
Showing load indicator?
The QProgressIndicator class lets an application display a progress indicator to show that a lengthy ...
Definition: loadindicator.h:31
void centerLoadIndicator(const QPoint &middle)
Center this load indicator.
bool isAnimated() const
Returns a Boolean value indicating whether the component is currently animated.
void setColor(const QColor &color)
Sets the color of the components to the given color.
virtual int heightForWidth(int w) const
bool isDisplayedWhenStopped() const
Returns a Boolean value indicating whether the receiver shows itself even when it is not animating.
const QColor & color() const
Returns the color of the component.
Definition: loadindicator.h:55
int startAnimation(std::chrono::milliseconds timeout=std::chrono::milliseconds(0), bool processEvents=false)
Starts the spin animation.
bool isParentVisible() const
Is parent widget visible?
void paint(QPainter &painter) const
Paint to another painter.
virtual void paintEvent(QPaintEvent *event)
void timedOut()
Timed out.
virtual void timerEvent(QTimerEvent *event)
void stopAnimation(int indicatorId=-1)
Stops the spin animation.
virtual QSize sizeHint() const
void setDisplayedWhenStopped(bool state)
Sets whether the component hides itself when it is not animating.
void setAnimationDelay(int delay)
Sets the delay between animation steps. Setting the delay to a value larger than 40 slows the animati...
SWIFT_GUI_EXPORT swift::gui::CGuiApplication * sGui
Single instance of GUI application object.
GUI related classes.
Free functions in swift::misc.
auto singleShot(int msec, QObject *target, F &&task)
Starts a single-shot timer which will call a task in the thread of the given object when it times out...
Definition: threadutils.h:30
#define SWIFT_AUDIT_X(COND, WHERE, WHAT)
A weaker kind of verify.
Definition: verify.h:38