JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_ConcertinaPanel.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
30{
31 struct Panel
32 {
33 Panel() = default;
34
35 Panel (int sz, int mn, int mx) noexcept
36 : size (sz), minSize (mn), maxSize (mx) {}
37
38 int setSize (int newSize) noexcept
39 {
40 jassert (minSize <= maxSize);
41 auto oldSize = size;
42 size = jlimit (minSize, maxSize, newSize);
43 return size - oldSize;
44 }
45
46 int expand (int amount) noexcept
47 {
48 amount = jmin (amount, maxSize - size);
49 size += amount;
50 return amount;
51 }
52
53 int reduce (int amount) noexcept
54 {
55 amount = jmin (amount, size - minSize);
56 size -= amount;
57 return amount;
58 }
59
60 bool canExpand() const noexcept { return size < maxSize; }
61 bool isMinimised() const noexcept { return size <= minSize; }
62
63 int size, minSize, maxSize;
64 };
65
66 Array<Panel> sizes;
67
68 Panel& get (int index) noexcept { return sizes.getReference (index); }
69 const Panel& get (int index) const noexcept { return sizes.getReference (index); }
70
71 PanelSizes withMovedPanel (int index, int targetPosition, int totalSpace) const
72 {
73 auto num = sizes.size();
74 totalSpace = jmax (totalSpace, getMinimumSize (0, num));
75 targetPosition = jmax (targetPosition, totalSpace - getMaximumSize (index, num));
76
77 PanelSizes newSizes (*this);
78 newSizes.stretchRange (0, index, targetPosition - newSizes.getTotalSize (0, index), stretchLast);
79 newSizes.stretchRange (index, num, totalSpace - newSizes.getTotalSize (0, index) - newSizes.getTotalSize (index, num), stretchFirst);
80 return newSizes;
81 }
82
83 PanelSizes fittedInto (int totalSpace) const
84 {
85 auto newSizes (*this);
86 auto num = newSizes.sizes.size();
87 totalSpace = jmax (totalSpace, getMinimumSize (0, num));
88 newSizes.stretchRange (0, num, totalSpace - newSizes.getTotalSize (0, num), stretchAll);
89 return newSizes;
90 }
91
92 PanelSizes withResizedPanel (int index, int panelHeight, int totalSpace) const
93 {
94 PanelSizes newSizes (*this);
95
96 if (totalSpace <= 0)
97 {
98 newSizes.get (index).size = panelHeight;
99 }
100 else
101 {
102 auto num = sizes.size();
103 auto minSize = getMinimumSize (0, num);
104 totalSpace = jmax (totalSpace, minSize);
105
106 newSizes.get (index).setSize (panelHeight);
107 newSizes.stretchRange (0, index, totalSpace - newSizes.getTotalSize (0, num), stretchLast);
108 newSizes.stretchRange (index, num, totalSpace - newSizes.getTotalSize (0, num), stretchLast);
109 newSizes = newSizes.fittedInto (totalSpace);
110 }
111
112 return newSizes;
113 }
114
115private:
116 enum ExpandMode
117 {
118 stretchAll,
119 stretchFirst,
120 stretchLast
121 };
122
123 void growRangeFirst (int start, int end, int spaceDiff) noexcept
124 {
125 for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
126 for (int i = start; i < end && spaceDiff > 0; ++i)
127 spaceDiff -= get (i).expand (spaceDiff);
128 }
129
130 void growRangeLast (int start, int end, int spaceDiff) noexcept
131 {
132 for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
133 for (int i = end; --i >= start && spaceDiff > 0;)
134 spaceDiff -= get (i).expand (spaceDiff);
135 }
136
137 void growRangeAll (int start, int end, int spaceDiff) noexcept
138 {
140
141 for (int i = start; i < end; ++i)
142 if (get (i).canExpand() && ! get (i).isMinimised())
143 expandableItems.add (& get (i));
144
145 for (int attempts = 4; --attempts >= 0 && spaceDiff > 0;)
146 for (int i = expandableItems.size(); --i >= 0 && spaceDiff > 0;)
147 spaceDiff -= expandableItems.getUnchecked (i)->expand (spaceDiff / (i + 1));
148
149 growRangeLast (start, end, spaceDiff);
150 }
151
152 void shrinkRangeFirst (int start, int end, int spaceDiff) noexcept
153 {
154 for (int i = start; i < end && spaceDiff > 0; ++i)
155 spaceDiff -= get (i).reduce (spaceDiff);
156 }
157
158 void shrinkRangeLast (int start, int end, int spaceDiff) noexcept
159 {
160 for (int i = end; --i >= start && spaceDiff > 0;)
161 spaceDiff -= get (i).reduce (spaceDiff);
162 }
163
164 void stretchRange (int start, int end, int amountToAdd, ExpandMode expandMode) noexcept
165 {
166 if (end > start)
167 {
168 if (amountToAdd > 0)
169 {
170 if (expandMode == stretchAll) growRangeAll (start, end, amountToAdd);
171 else if (expandMode == stretchFirst) growRangeFirst (start, end, amountToAdd);
172 else if (expandMode == stretchLast) growRangeLast (start, end, amountToAdd);
173 }
174 else
175 {
176 if (expandMode == stretchFirst) shrinkRangeFirst (start, end, -amountToAdd);
177 else shrinkRangeLast (start, end, -amountToAdd);
178 }
179 }
180 }
181
182 int getTotalSize (int start, int end) const noexcept
183 {
184 int tot = 0;
185 while (start < end) tot += get (start++).size;
186 return tot;
187 }
188
189 int getMinimumSize (int start, int end) const noexcept
190 {
191 int tot = 0;
192 while (start < end) tot += get (start++).minSize;
193 return tot;
194 }
195
196 int getMaximumSize (int start, int end) const noexcept
197 {
198 int tot = 0;
199
200 while (start < end)
201 {
202 auto mx = get (start++).maxSize;
203
204 if (mx > 0x100000)
205 return mx;
206
207 tot += mx;
208 }
209
210 return tot;
211 }
212};
213
214//==============================================================================
216{
217public:
219 : component (comp, takeOwnership)
220 {
222 setWantsKeyboardFocus (false);
223 addAndMakeVisible (comp);
224 }
225
226 void paint (Graphics& g) override
227 {
228 if (customHeader.get() != nullptr)
229 return;
230
231 const Rectangle<int> area (getWidth(), getHeaderSize());
232 g.reduceClipRegion (area);
233
234 getLookAndFeel().drawConcertinaPanelHeader (g, area, isMouseOver(), isMouseButtonDown(),
235 getPanel(), *component);
236 }
237
238 void resized() override
239 {
240 auto bounds = getLocalBounds();
241 auto headerBounds = bounds.removeFromTop (getHeaderSize());
242
243 if (customHeader.get() != nullptr)
244 customHeader.get()->setBounds (headerBounds);
245
246 component->setBounds (bounds);
247 }
248
249 void mouseDown (const MouseEvent&) override
250 {
251 mouseDownY = getY();
252 dragStartSizes = getPanel().getFittedSizes();
253 }
254
255 void mouseDrag (const MouseEvent& e) override
256 {
258 {
259 auto& panel = getPanel();
260 panel.setLayout (dragStartSizes.withMovedPanel (panel.holders.indexOf (this),
261 mouseDownY + e.getDistanceFromDragStartY(),
262 panel.getHeight()), false);
263 }
264 }
265
266 void mouseDoubleClick (const MouseEvent&) override
267 {
268 getPanel().panelHeaderDoubleClicked (component);
269 }
270
271 void setCustomHeaderComponent (Component* headerComponent, bool shouldTakeOwnership)
272 {
273 customHeader = CustomHeader (this, OptionalScopedPointer (headerComponent, shouldTakeOwnership));
274 addAndMakeVisible (headerComponent);
275 }
276
278
279private:
280 PanelSizes dragStartSizes;
281 int mouseDownY;
282
283 struct CustomHeader
284 {
285 CustomHeader() = default;
286
288 : listener (l),
289 customHeaderComponent (std::move (c))
290 {
291 if (customHeaderComponent != nullptr)
292 customHeaderComponent->addMouseListener (listener, false);
293 }
294
295 CustomHeader (CustomHeader&& other) noexcept
296 : listener (std::exchange (other.listener, nullptr)),
297 customHeaderComponent (std::exchange (other.customHeaderComponent, {})) {}
298
299 CustomHeader& operator= (CustomHeader&& other) noexcept
300 {
301 std::swap (other.listener, listener);
302 std::swap (other.customHeaderComponent, customHeaderComponent);
303 return *this;
304 }
305
306 CustomHeader (const CustomHeader& other) = delete;
307 CustomHeader& operator= (const CustomHeader& other) = delete;
308
309 ~CustomHeader() noexcept
310 {
311 if (customHeaderComponent != nullptr)
312 customHeaderComponent->removeMouseListener (listener);
313 }
314
315 Component* get() const { return customHeaderComponent.get(); }
316
317 private:
318 MouseListener* listener = nullptr;
319 OptionalScopedPointer<Component> customHeaderComponent;
320 };
321
322 CustomHeader customHeader;
323
324 int getHeaderSize() const noexcept
325 {
326 ConcertinaPanel& panel = getPanel();
327 auto ourIndex = panel.holders.indexOf (this);
328 return panel.currentSizes->get (ourIndex).minSize;
329 }
330
331 ConcertinaPanel& getPanel() const
332 {
333 auto panel = dynamic_cast<ConcertinaPanel*> (getParentComponent());
334 jassert (panel != nullptr);
335 return *panel;
336 }
337
339};
340
341//==============================================================================
343 : currentSizes (new PanelSizes()),
344 headerHeight (20)
345{
346}
347
349
351{
352 return holders.size();
353}
354
355Component* ConcertinaPanel::getPanel (int index) const noexcept
356{
357 if (PanelHolder* h = holders[index])
358 return h->component;
359
360 return nullptr;
361}
362
363void ConcertinaPanel::addPanel (int insertIndex, Component* component, bool takeOwnership)
364{
365 jassert (component != nullptr); // can't use a null pointer here!
366 jassert (indexOfComp (component) < 0); // You can't add the same component more than once!
367
368 auto holder = new PanelHolder (component, takeOwnership);
369 holders.insert (insertIndex, holder);
370 currentSizes->sizes.insert (insertIndex, PanelSizes::Panel (headerHeight, headerHeight, std::numeric_limits<int>::max()));
371 addAndMakeVisible (holder);
372 resized();
373}
374
376{
377 auto index = indexOfComp (component);
378
379 if (index >= 0)
380 {
381 currentSizes->sizes.remove (index);
382 holders.remove (index);
383 resized();
384 }
385}
386
387bool ConcertinaPanel::setPanelSize (Component* panelComponent, int height, bool animate)
388{
389 auto index = indexOfComp (panelComponent);
390 jassert (index >= 0); // The specified component doesn't seem to have been added!
391
392 height += currentSizes->get (index).minSize;
393 auto oldSize = currentSizes->get (index).size;
394 setLayout (currentSizes->withResizedPanel (index, height, getHeight()), animate);
395 return oldSize != currentSizes->get (index).size;
396}
397
399{
400 return setPanelSize (component, getHeight(), animate);
401}
402
404{
405 auto index = indexOfComp (component);
406 jassert (index >= 0); // The specified component doesn't seem to have been added!
407
408 if (index >= 0)
409 {
410 currentSizes->get (index).maxSize = currentSizes->get (index).minSize + maximumSize;
411 resized();
412 }
413}
414
415void ConcertinaPanel::setPanelHeaderSize (Component* component, int headerSize)
416{
417 auto index = indexOfComp (component);
418 jassert (index >= 0); // The specified component doesn't seem to have been added!
419
420 if (index >= 0)
421 {
422 auto oldMin = currentSizes->get (index).minSize;
423
424 currentSizes->get (index).minSize = headerSize;
425 currentSizes->get (index).size += headerSize - oldMin;
426 resized();
427 }
428}
429
431{
432 OptionalScopedPointer<Component> optional (customComponent, takeOwnership);
433
434 auto index = indexOfComp (component);
435 jassert (index >= 0); // The specified component doesn't seem to have been added!
436
437 if (index >= 0)
438 holders.getUnchecked (index)->setCustomHeaderComponent (optional.release(), takeOwnership);
439}
440
441void ConcertinaPanel::resized()
442{
443 applyLayout (getFittedSizes(), false);
444}
445
446int ConcertinaPanel::indexOfComp (Component* comp) const noexcept
447{
448 for (int i = 0; i < holders.size(); ++i)
449 if (holders.getUnchecked (i)->component == comp)
450 return i;
451
452 return -1;
453}
454
455ConcertinaPanel::PanelSizes ConcertinaPanel::getFittedSizes() const
456{
457 return currentSizes->fittedInto (getHeight());
458}
459
460void ConcertinaPanel::applyLayout (const PanelSizes& sizes, bool animate)
461{
462 if (! animate)
463 animator.cancelAllAnimations (false);
464
465 const int animationDuration = 150;
466 auto w = getWidth();
467 int y = 0;
468
469 for (int i = 0; i < holders.size(); ++i)
470 {
471 PanelHolder& p = *holders.getUnchecked (i);
472
473 auto h = sizes.get (i).size;
474 const Rectangle<int> pos (0, y, w, h);
475
476 if (animate)
477 animator.animateComponent (&p, pos, 1.0f, animationDuration, false, 1.0, 1.0);
478 else
479 p.setBounds (pos);
480
481 y += h;
482 }
483}
484
485void ConcertinaPanel::setLayout (const PanelSizes& sizes, bool animate)
486{
487 *currentSizes = sizes;
488 applyLayout (getFittedSizes(), animate);
489}
490
491void ConcertinaPanel::panelHeaderDoubleClicked (Component* component)
492{
493 if (! expandPanelFully (component, true))
494 setPanelSize (component, 0, true);
495}
496
497//==============================================================================
499{
500 return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
501}
502
503} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
int size() const noexcept
Returns the current number of elements in the array.
Definition juce_Array.h:215
ElementType & getReference(int index) noexcept
Returns a direct reference to one of the elements in the array, without checking the index passed in.
Definition juce_Array.h:267
void animateComponent(Component *component, const Rectangle< int > &finalBounds, float finalAlpha, int animationDurationMilliseconds, bool useProxyComponent, double startSpeed, double endSpeed)
Starts a component moving from its current position to a specified position.
void cancelAllAnimations(bool moveComponentsToTheirFinalPositions)
Clears all of the active animations.
The base class for all JUCE user-interface objects.
bool isMouseButtonDown(bool includeChildren=false) const
Returns true if the mouse button is currently held down in this component.
void setRepaintsOnMouseActivity(bool shouldRepaint) noexcept
Causes automatic repaints when the mouse enters or exits this component.
Component * getParentComponent() const noexcept
Returns the component which this component is inside.
int getHeight() const noexcept
Returns the component's height in pixels.
void addAndMakeVisible(Component *child, int zOrder=-1)
Adds a child component to this one, and also makes the child visible if it isn't already.
Component() noexcept
Creates a component.
int getY() const noexcept
Returns the y coordinate of the top of this component.
void setWantsKeyboardFocus(bool wantsFocus) noexcept
Sets a flag to indicate whether this component wants keyboard focus or not.
bool isMouseOver(bool includeChildren=false) const
Returns true if the mouse is currently over this component.
int getWidth() const noexcept
Returns the component's width in pixels.
LookAndFeel & getLookAndFeel() const noexcept
Finds the appropriate look-and-feel to use for this component.
Rectangle< int > getLocalBounds() const noexcept
Returns the component's bounds, relative to its own origin.
void mouseDown(const MouseEvent &) override
Called when a mouse button is pressed.
void resized() override
Called when this component's size has been changed.
void paint(Graphics &g) override
Components can override this method to draw their content.
void mouseDrag(const MouseEvent &e) override
Called when the mouse is moved while a button is held down.
void mouseDoubleClick(const MouseEvent &) override
Called when a mouse button has been double-clicked on a component.
void setMaximumPanelSize(Component *panelComponent, int maximumSize)
Sets a maximum size for one of the panels.
int getNumPanels() const noexcept
Returns the number of panels.
void setCustomPanelHeader(Component *panelComponent, Component *customHeaderComponent, bool takeOwnership)
Sets a custom header Component for one of the panels.
void setPanelHeaderSize(Component *panelComponent, int headerSize)
Sets the height of the header section for one of the panels.
void addPanel(int insertIndex, Component *component, bool takeOwnership)
Adds a component to the panel.
Component * getPanel(int index) const noexcept
Returns one of the panels.
bool expandPanelFully(Component *panelComponent, bool animate)
Attempts to make one of the panels full-height.
bool setPanelSize(Component *panelComponent, int newHeight, bool animate)
Resizes one of the panels.
~ConcertinaPanel() override
Destructor.
ConcertinaPanel()
Creates an empty concertina panel.
std::unique_ptr< AccessibilityHandler > createAccessibilityHandler() override
Override this method to return a custom AccessibilityHandler for this component.
void removePanel(Component *panelComponent)
Removes one of the panels.
A graphics context, used for drawing a component or image.
bool reduceClipRegion(int x, int y, int width, int height)
Intersects the current clipping region with another region.
Contains position and status information about a mouse event.
bool mouseWasDraggedSinceMouseDown() const noexcept
Returns true if the user seems to be performing a drag gesture.
int getDistanceFromDragStartY() const noexcept
Returns the difference between the mouse's current y position and where it was when the button was la...
A MouseListener can be registered with a component to receive callbacks about mouse events that happe...
Holds a pointer to an object which can optionally be deleted when this pointer goes out of scope.
ObjectType * release() noexcept
Removes the current object from this OptionalScopedPointer without deleting it.
Manages a rectangle and allows geometric operations to be performed on it.
T exchange(T... args)
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
auto & get(ProcessorChain< Processors... > &chain) noexcept
Non-member equivalent of ProcessorChain::get which avoids awkward member template syntax.
JUCE Namespace.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Returns a default-constructed sentinel value.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
T swap(T... args)