repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / haikudepot / ui_generic / BarberPole.cpp
blob4b7488a4619ab9751953930b9f179dfc3cec70bc
1 /*
2 * Copyright 2017 Julian Harnath <julian.harnath@rwth-aachen.de>
3 * All rights reserved. Distributed under the terms of the MIT license.
4 */
7 #include "BarberPole.h"
9 #include <AutoLocker.h>
10 #include <ControlLook.h>
11 #include <Locker.h>
12 #include <ObjectList.h>
13 #include <Messenger.h>
15 #include <pthread.h>
16 #include <stdio.h>
19 // #pragma mark - MachineRoom
22 /*! The machine room spins all the barber poles.
23 Keeps a list of all barber poles of this team and runs its own
24 thread to invalidate them in regular intervals.
26 class MachineRoom
28 private:
29 enum {
30 kSpinInterval = 20000 // us
33 private:
34 MachineRoom()
36 fMessengers(20, true)
38 fSpinLoopLock = create_sem(0, "BarberPole lock");
39 fSpinLoopThread = spawn_thread(&MachineRoom::_StartSpinLoop,
40 "The Barber Machine", B_DISPLAY_PRIORITY, this);
41 resume_thread(fSpinLoopThread);
44 public:
45 static void AttachBarberPole(BarberPole* pole)
47 _InitializeIfNeeded();
48 sInstance->_Attach(pole);
51 static void DetachBarberPole(BarberPole* pole)
53 sInstance->_Detach(pole);
56 private:
57 static void _Initialize()
59 sInstance = new MachineRoom();
62 static status_t _StartSpinLoop(void* instance)
64 static_cast<MachineRoom*>(instance)->_SpinLoop();
65 return B_OK;
68 static void _InitializeIfNeeded()
70 pthread_once(&sOnceControl, &MachineRoom::_Initialize);
73 void _Attach(BarberPole* pole)
75 AutoLocker<BLocker> locker(fLock);
77 bool wasEmpty = fMessengers.IsEmpty();
79 BMessenger* messenger = new BMessenger(pole);
80 fMessengers.AddItem(messenger);
82 if (wasEmpty)
83 release_sem(fSpinLoopLock);
86 void _Detach(BarberPole* pole)
88 AutoLocker<BLocker> locker(fLock);
90 for (int32 i = 0; i < fMessengers.CountItems(); i++) {
91 BMessenger* messenger = fMessengers.ItemAt(i);
92 if (messenger->Target(NULL) == pole) {
93 fMessengers.RemoveItem(messenger, true);
94 break;
98 if (fMessengers.IsEmpty())
99 acquire_sem(fSpinLoopLock);
102 void _SpinLoop()
104 for (;;) {
105 AutoLocker<BLocker> locker(fLock);
107 for (int32 i = 0; i < fMessengers.CountItems(); i++) {
108 BMessenger* messenger = fMessengers.ItemAt(i);
109 messenger->SendMessage(BarberPole::kRefreshMessage);
112 locker.Unset();
114 acquire_sem(fSpinLoopLock);
115 release_sem(fSpinLoopLock);
117 snooze(kSpinInterval);
121 private:
122 static MachineRoom* sInstance;
123 static pthread_once_t sOnceControl;
125 thread_id fSpinLoopThread;
126 sem_id fSpinLoopLock;
128 BLocker fLock;
129 BObjectList<BMessenger> fMessengers;
133 MachineRoom* MachineRoom::sInstance = NULL;
134 pthread_once_t MachineRoom::sOnceControl = PTHREAD_ONCE_INIT;
137 // #pragma mark - BarberPole
140 BarberPole::BarberPole(const char* name)
142 BView(name, B_WILL_DRAW | B_FRAME_EVENTS),
143 fIsSpinning(false),
144 fSpinSpeed(0.05),
145 fColors(NULL),
146 fNumColors(0),
147 fScrollOffset(0.0),
148 fStripeWidth(0.0),
149 fNumStripes(0)
151 // Default colors, chosen from system color scheme
152 rgb_color defaultColors[2];
153 rgb_color otherColor = tint_color(ui_color(B_STATUS_BAR_COLOR), 1.3);
154 otherColor.alpha = 50;
155 defaultColors[0] = otherColor;
156 defaultColors[1] = B_TRANSPARENT_COLOR;
157 SetColors(defaultColors, 2);
161 BarberPole::~BarberPole()
163 Stop();
164 delete[] fColors;
168 void
169 BarberPole::MessageReceived(BMessage* message)
171 switch (message->what) {
172 case kRefreshMessage:
173 _Spin();
174 break;
176 default:
177 BView::MessageReceived(message);
178 break;
183 void
184 BarberPole::Draw(BRect updateRect)
187 if (fIsSpinning) {
188 // Draw color stripes
189 float position = -fStripeWidth * (fNumColors + 0.5) + fScrollOffset;
190 // Starting position: beginning of the second color cycle
191 // The + 0.5 is so we start out without a partially visible stripe
192 // on the left side (makes it simpler to loop)
193 BRect bounds = Bounds();
194 bounds.InsetBy(-2, -2);
195 be_control_look->DrawStatusBar(this, bounds, updateRect,
196 ui_color(B_PANEL_BACKGROUND_COLOR), ui_color(B_STATUS_BAR_COLOR),
197 bounds.Width());
198 SetDrawingMode(B_OP_ALPHA);
199 uint32 colorIndex = 0;
200 for (uint32 i = 0; i < fNumStripes; i++) {
201 SetHighColor(fColors[colorIndex]);
202 colorIndex++;
203 if (colorIndex >= fNumColors)
204 colorIndex = 0;
206 BRect stripeFrame = fStripe.Frame();
207 fStripe.MapTo(stripeFrame,
208 stripeFrame.OffsetToCopy(position, 0.0));
209 FillPolygon(&fStripe);
211 position += fStripeWidth;
213 SetDrawingMode(B_OP_COPY);
215 // Draw box around it
216 BRect bounds = Bounds();
217 be_control_look->DrawBorder(this, bounds, updateRect,
218 ui_color(B_PANEL_BACKGROUND_COLOR), B_PLAIN_BORDER);
222 void
223 BarberPole::FrameResized(float width, float height)
225 // Choose stripe width so that at least 2 full stripes fit into the view,
226 // but with a minimum of 5px. Larger views get wider stripes, but they
227 // grow slower than the view and are capped to a maximum of 200px.
228 fStripeWidth = (width / 4) + 5;
229 if (fStripeWidth > 200)
230 fStripeWidth = 200;
232 BPoint stripePoints[4];
233 stripePoints[0].Set(fStripeWidth * 0.5, 0.0); // top left
234 stripePoints[1].Set(fStripeWidth * 1.5, 0.0); // top right
235 stripePoints[2].Set(fStripeWidth, height); // bottom right
236 stripePoints[3].Set(0.0, height); // bottom left
238 fStripe = BPolygon(stripePoints, 4);
240 fNumStripes = (int32)ceilf((width) / fStripeWidth) + 1 + fNumColors;
241 // Number of color stripes drawn in total for the barber pole, the
242 // user-visible part is a "window" onto the complete pole. We need
243 // as many stripes as are visible, an extra one on the right side
244 // (will be partially visible, that's the + 1); and then a whole color
245 // cycle of strips extra which we scroll into until we loop.
247 // Example with 3 colors and a visible area of 2*fStripeWidth (which means
248 // that 2 will be fully visible, and a third one partially):
249 // ........
250 // X___________v______v___
251 // / 1 / 2 / 3 / 1 / 2 / 3 /
252 // `````````````````````````
253 // Pole is scrolled to the right into the visible region, which is marked
254 // between the two 'v'. Once the left edge of the visible area reaches
255 // point X, we can jump back to the initial region position.
259 BSize
260 BarberPole::MinSize()
262 return BSize(50, 5);
266 void
267 BarberPole::Start()
269 if (fIsSpinning)
270 return;
271 MachineRoom::AttachBarberPole(this);
272 fIsSpinning = true;
276 void
277 BarberPole::Stop()
279 if (!fIsSpinning)
280 return;
281 MachineRoom::DetachBarberPole(this);
282 fIsSpinning = false;
283 Invalidate();
287 void
288 BarberPole::SetSpinSpeed(float speed)
290 if (speed > 1.0f)
291 speed = 1.0f;
292 if (speed < -1.0f)
293 speed = -1.0f;
294 fSpinSpeed = speed;
298 void
299 BarberPole::SetColors(const rgb_color* colors, uint32 numColors)
301 delete[] fColors;
302 rgb_color* colorsCopy = new rgb_color[numColors];
303 for (uint32 i = 0; i < numColors; i++)
304 colorsCopy[i] = colors[i];
306 fColors = colorsCopy;
307 fNumColors = numColors;
311 void
312 BarberPole::_Spin()
314 fScrollOffset += fStripeWidth / (1.0f / fSpinSpeed);
315 if (fScrollOffset >= fStripeWidth * fNumColors) {
316 // Cycle completed, jump back to where we started
317 fScrollOffset = 0;
319 Invalidate();
320 //Parent()->Invalidate();