iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IBubbleControl.h
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#pragma once
12
19#include "IControl.h"
20
21BEGIN_IPLUG_NAMESPACE
22BEGIN_IGRAPHICS_NAMESPACE
23
28{
29public:
32 {
33 kCollapsed = 0,
34 kExpanding,
35 kExpanded,
36 kCollapsing,
37 };
38
39 enum EArrowDir
40 {
41 kNorth,
42 kEast,
43 kSouth,
44 kWest
45 };
46
51 IBubbleControl(const IText& text = DEFAULT_TEXT.WithAlign(EAlign::Center), const IColor& fillColor = COLOR_WHITE, const IColor& strokeColor = COLOR_BLACK, float roundness = 5.f)
52 : IControl(IRECT())
53 , mRoundness(roundness)
54 , mFillColor(fillColor)
55 , mStrokeColor(strokeColor)
56 {
57 mText = text;
58 mHide = true;
59 mIgnoreMouse = true;
60
61 auto animationFunc = [&](IControl* pCaller) {
62 auto progress = pCaller->GetAnimationProgress();
63
64 if(progress > 1.) {
65 if(mState == kExpanding)
66 {
67 mBlend.mWeight = mOpacity;
68 mState = kExpanded;
69 }
70 else if(mState == kExpanded)
71 {
72 if(GetUI()->ControlIsCaptured(mCaller))
73 {
74 mState = kExpanded;
75 SetDirty(true); // triggers animation again
76 }
77 else
78 {
79 mBlend.mWeight = mOpacity;
80 mState = kCollapsing;
81 SetDirty(true); // triggers animation again
82 }
83
84 return; // don't remove animation
85 }
86 else if(mState == kCollapsing)
87 {
88 mBlend.mWeight = 0.;
89 Hide(true);
90 mState = kCollapsed;
91 mTouchId = 0;
92 SetDirty(false);
93 }
94
95 pCaller->OnEndAnimation();
96 return;
97 }
98
99 switch (mState) {
100 case kExpanding: mBlend.mWeight = (float) progress * mOpacity; break;
101 case kExpanded: mBlend.mWeight = mOpacity; break;
102 case kCollapsing: mBlend.mWeight = (float) (1.-progress) * mOpacity; break;
103 default:
104 break;
105 }
106 };
107
108 SetActionFunction([animationFunc, this](IControl* pCaller) {
109 SetAnimation(animationFunc, 200);
110 });
111
112 SetAnimationEndActionFunction([animationFunc, this](IControl* pCaller) {
113 if(mState == kExpanded) {
114 SetAnimation(animationFunc, 1000);
115 }
116 });
117 }
118
119 virtual ~IBubbleControl()
120 {
121 }
122
123 void Draw(IGraphics& g) override
124 {
125 IRECT r = mBubbleBounds.GetPadded(-mDropShadowSize);
126
127 DrawDropShadow(g, r);
128 DrawBubble(g, r);
129 DrawContent(g, r);
130 }
131
134 {
136 mBubbleBounds = mRECT;
137 mCaller = nullptr;
138 }
139
141 void SetMaxBounds(const IRECT& bounds) { mMaxBounds = bounds; }
142
144 void SetFillColor(const IColor& color) { mFillColor = color; }
145
147 void SetStrokeColor(const IColor& color) { mStrokeColor = color; }
148
149protected:
150 virtual void DrawContent(IGraphics& g, const IRECT& r)
151 {
152 g.DrawText(mText, mStr.Get(), r, &mBlend);
153 }
154
155 virtual void DrawBubble(IGraphics& g, const IRECT& r)
156 {
157 float halfCalloutSize = mCalloutSize/2.f;
158
159 g.PathMoveTo(r.L, r.T + mRoundness);
160 g.PathArc(r.L + mRoundness, r.T + mRoundness, mRoundness, 270.f, 360.f);
161
162 if(mArrowDir == kWest)
163 {
164 g.PathArc(r.R - mRoundness, r.T + mRoundness, mRoundness, 0.f, 90.f);
165 g.PathArc(r.R - mRoundness, r.B - mRoundness, mRoundness, 90.f, 180.f);
166 g.PathArc(r.L + mRoundness, r.B - mRoundness, mRoundness, 180.f, 270.f);
167 g.PathLineTo(r.L, r.MH() + halfCalloutSize);
168 g.PathLineTo(r.L - mCalloutSize, r.MH());
169 g.PathLineTo(r.L, r.MH() - halfCalloutSize);
170 }
171 else if(mArrowDir == kEast)
172 {
173 g.PathArc(r.R - mRoundness, r.T + mRoundness, mRoundness, 0.f, 90.f);
174 g.PathLineTo(r.R, r.MH() - halfCalloutSize);
175 g.PathLineTo(r.R + mCalloutSize, r.MH());
176 g.PathLineTo(r.R, r.MH() + halfCalloutSize);
177 g.PathArc(r.R - mRoundness, r.B - mRoundness, mRoundness, 90.f, 180.f);
178 g.PathArc(r.L + mRoundness, r.B - mRoundness, mRoundness, 180.f, 270.f);
179 }
180 else if(mArrowDir == kNorth)
181 {
182 g.PathLineTo(r.MW() - halfCalloutSize, r.T);
183 g.PathLineTo(r.MW(), r.T - mCalloutSize);
184 g.PathLineTo(r.MW() + halfCalloutSize, r.T);
185 g.PathArc(r.R - mRoundness, r.T + mRoundness, mRoundness, 0.f, 90.f);
186 g.PathArc(r.R - mRoundness, r.B - mRoundness, mRoundness, 90.f, 180.f);
187 g.PathArc(r.L + mRoundness, r.B - mRoundness, mRoundness, 180.f, 270.f);
188 }
189 else if(mArrowDir == kSouth)
190 {
191 g.PathArc(r.R - mRoundness, r.T + mRoundness, mRoundness, 0.f, 90.f);
192 g.PathArc(r.R - mRoundness, r.B - mRoundness, mRoundness, 90.f, 180.f);
193 g.PathLineTo(r.MW() + halfCalloutSize, r.B);
194 g.PathLineTo(r.MW(), r.B + mCalloutSize);
195 g.PathLineTo(r.MW() - halfCalloutSize, r.B);
196 g.PathArc(r.L + mRoundness, r.B - mRoundness, mRoundness, 180.f, 270.f);
197 }
198 g.PathClose();
199
200 g.PathFill(mFillColor, true, &mBlend);
201 g.PathStroke(mStrokeColor, mStrokeThickness, IStrokeOptions(), &mBlend);
202 }
203
204 void DrawDropShadow(IGraphics& g, const IRECT& r)
205 {
206 g.DrawFastDropShadow(r, mBubbleBounds, 2.0, mRoundness, 10.f, &mBlend);
207 }
208
209 virtual void MeasureText(const char* str, IRECT& contentBounds)
210 {
211 GetUI()->MeasureText(mText, str, contentBounds);
212 }
213
214 void ShowBubble(IControl* pCaller, float x, float y, const char* str, EDirection dir, IRECT minimumContentBounds, ITouchID touchID = 0)
215 {
216 if(mMaxBounds.W() == 0)
217 mMaxBounds = GetUI()->GetBounds();
218
219 mDirection = dir;
220 mTouchId = touchID;
221
222 mStr.Set(str);
223 IRECT contentBounds;
224 MeasureText(str, contentBounds);
225
226 if (!minimumContentBounds.Empty())
227 {
228 if(minimumContentBounds.W() > contentBounds.W())
229 {
230 contentBounds.R = contentBounds.L + minimumContentBounds.W();
231 }
232
233 if(minimumContentBounds.H() > contentBounds.H())
234 {
235 contentBounds.B = contentBounds.T + minimumContentBounds.H();
236 }
237 }
238
239 contentBounds.HPad(mHorizontalPadding);
240 contentBounds.Pad(mDropShadowSize);
241 const float halfHeight = contentBounds.H() / 2.f;
242 const float halfWidth = contentBounds.W() / 2.f;
243
244 mBubbleBounds = IRECT(x, y - halfHeight, x + contentBounds.W(), y + halfHeight);
245 IRECT controlBounds = pCaller->GetRECT();
246
247 if(mDirection == EDirection::Horizontal)
248 {
249 const float maxR = mMaxBounds.R - mHorizontalPadding - mDropShadowSize;
250
251 // check if it's gone off the right hand side
252 if(mBubbleBounds.R > maxR)
253 {
254 const float shiftLeft = mBubbleBounds.R-controlBounds.L;
255 mBubbleBounds.Translate(-shiftLeft, 0.f);
256 mArrowDir = EArrowDir::kEast;
257 }
258 else
259 mArrowDir = EArrowDir::kWest;
260 }
261 else
262 {
263 mBubbleBounds.Translate(-halfWidth, -halfHeight);
264
265 // check if it's gone off the top
266 if(mBubbleBounds.T < mMaxBounds.T)
267 {
268 mArrowDir = EArrowDir::kNorth;
269 const float shiftDown = mBubbleBounds.H() + controlBounds.H();
270 mBubbleBounds.Translate(0.f, shiftDown);
271 }
272 else
273 mArrowDir = EArrowDir::kSouth;
274 }
275
276 SetRECT(mRECT.Union(mBubbleBounds));
277
278 if(mState == kCollapsed)
279 {
280 Hide(false);
281 mState = kExpanding;
282 SetDirty(true);
283 }
284
285 if(pCaller != mCaller)
286 {
287 mRECT = mBubbleBounds;
289 }
290
291 mCaller = pCaller;
292 }
293
294 bool GetActive() const
295 {
296 return mState > EPopupState::kCollapsed;
297 }
298
299 ITouchID GetTouchID() const
300 {
301 return mTouchId;
302 }
303
304protected:
305 friend class IGraphics;
306
307 ITouchID mTouchId = 0;
308 EDirection mDirection = EDirection::Horizontal;
309 IRECT mMaxBounds; // if view is only showing a part of the graphics context, we need to know because menus can't go there
310 IRECT mBubbleBounds;
311 IControl* mCaller = nullptr;
312 EArrowDir mArrowDir = EArrowDir::kWest;
313 WDL_String mStr;
314 IBlend mBlend = { EBlend::Default, 0.f }; // blend for sub panels appearing
315 float mRoundness = 5.f; // The roundness of the corners of the menu panel backgrounds
316 float mDropShadowSize = 10.f; // The size in pixels of the drop shadow
317 float mCalloutSize = 10.f;
318 float mOpacity = 0.95f; // The opacity of the menu panel backgrounds when fully faded in
319 float mStrokeThickness = 2.f;
320 float mHorizontalPadding = 5.f;
321 IColor mFillColor = COLOR_WHITE;
322 IColor mStrokeColor = COLOR_BLACK;
323 EPopupState mState = kCollapsed; // The state of the pop-up, mainly used for animation
324};
325
326END_IGRAPHICS_NAMESPACE
327END_IPLUG_NAMESPACE
This file contains the base IControl implementation, along with some base classes for specific types ...
A special control to draw contextual info as a slider etc is moved If used in the main IControl stack...
void ResetBounds()
If controls get moved, you may need to call this to avoid the wrong regions getting dirtied.
IBubbleControl(const IText &text=DEFAULT_TEXT.WithAlign(EAlign::Center), const IColor &fillColor=COLOR_WHITE, const IColor &strokeColor=COLOR_BLACK, float roundness=5.f)
Create a new IBubbleControl.
void SetStrokeColor(const IColor &color)
Set the stroke color for the bubble.
EPopupState
An enumerated list, that is used to determine the state of the menu, mainly for animations.
void SetMaxBounds(const IRECT &bounds)
Set the bounds that the menu can potentially occupy, if not the full graphics context.
void Draw(IGraphics &g) override
Draw the control to the graphics context.
void SetFillColor(const IColor &color)
Set the background color for the bubble.
The lowest level base class of an IGraphics control.
Definition: IControl.h:49
IGraphics * GetUI()
Definition: IControl.h:467
virtual void Hide(bool hide)
Shows or hides the IControl.
Definition: IControl.cpp:239
const IRECT & GetRECT() const
Get the rectangular draw area for this control, within the graphics context.
Definition: IControl.h:311
IControl * SetAnimationEndActionFunction(IActionFunction actionFunc)
Set an Action Function to be called at the end of an animation.
Definition: IControl.h:211
void SetRECT(const IRECT &bounds)
Set the rectangular draw area for this control, within the graphics context.
Definition: IControl.h:315
void SetTargetAndDrawRECTs(const IRECT &bounds)
Set BOTH the draw rect and the target area, within the graphics context for this control.
Definition: IControl.h:327
virtual void SetDirty(bool triggerAction=true, int valIdx=kNoValIdx)
Mark the control as dirty, i.e.
Definition: IControl.cpp:198
IControl * SetActionFunction(IActionFunction actionFunc)
Set an Action Function for this control.
Definition: IControl.h:206
void SetAnimation(IAnimationFunction func)
Set the animation function.
Definition: IControl.h:492
The lowest level base class of an IGraphics context.
Definition: IGraphics.h:86
bool ControlIsCaptured() const
Check to see if any control is captured.
Definition: IGraphics.h:1395
virtual void PathFill(const IPattern &pattern, const IFillOptions &options=IFillOptions(), const IBlend *pBlend=0)=0
Fill the current current path.
void DrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend=0)
Draw some text to the graphics context in a specific rectangle.
Definition: IGraphics.cpp:670
virtual void PathClose()=0
Close the path that is being specified.
virtual void PathStroke(const IPattern &pattern, float thickness, const IStrokeOptions &options=IStrokeOptions(), const IBlend *pBlend=0)=0
Stroke the current current path.
virtual void DrawFastDropShadow(const IRECT &innerBounds, const IRECT &outerBounds, float xyDrop=5.f, float roundness=0.f, float blur=10.f, IBlend *pBlend=nullptr)
NanoVG only.
Definition: IGraphics.h:404
void SetAllControlsDirty()
Calls SetDirty() on every control.
Definition: IGraphics.cpp:582
virtual void PathMoveTo(float x, float y)=0
Move the current point in the current path.
virtual void PathArc(float cx, float cy, float r, float a1, float a2, EWinding winding=EWinding::CW)=0
Add an arc to the current path.
virtual void PathLineTo(float x, float y)=0
Add a line to the current path from the current point to the specified location.
IRECT GetBounds() const
Returns an IRECT that represents the entire UI bounds This is useful for programatically arranging UI...
Definition: IGraphics.h:1194
virtual float MeasureText(const IText &text, const char *str, IRECT &bounds) const
Measure the rectangular region that some text will occupy.
Definition: IGraphics.cpp:678
Used to manage stroke behaviour for path based drawing back ends.
Used to manage composite/blend operations, independent of draw class/platform.
Used to manage color data, independent of draw class/platform.
Used to manage a rectangular area, independent of draw class/platform.
void Pad(float padding)
Pad this IRECT N.B.
float MH() const
bool Empty() const
float W() const
void HPad(float padding)
Pad this IRECT in the X-axis N.B.
IRECT Union(const IRECT &rhs) const
Create a new IRECT that is a union of this IRECT and rhs.
float H() const
void Translate(float x, float y)
Translate this rectangle.
float MW() const
IRECT GetPadded(float padding) const
Get a copy of this IRECT with each value padded by padding N.B.
IText is used to manage font and text/text entry style for a piece of text on the UI,...