11namespace tracktion {
inline namespace engine
14CurveEditorPoint::~CurveEditorPoint()
16 notifyListenersOfDeletion();
19static CurveEditor* getPointIndexes (
juce::Array<int>& set,
const SelectableList& items)
22 CurveEditor* ed =
nullptr;
24 for (
auto point : items.getItemsOfType<CurveEditorPoint>())
27 set.
add (point->index);
34bool CurveEditorPoint::arePointsOnSameCurve (
const SelectableList& items)
36 CurveEditor* ed =
nullptr;
38 for (
auto point : items.getItemsOfType<CurveEditorPoint>())
42 else if (ed != point->editor)
49bool CurveEditorPoint::arePointsConsecutive (
const SelectableList& items)
52 getPointIndexes (set, items);
54 for (
int i = 0; i < set.
size() - 1; ++i)
55 if (set[i + 1] > set[i] + 1)
58 return arePointsOnSameCurve (items);
61TimeRange CurveEditorPoint::getPointTimeRange (
const SelectableList& items)
65 if (
auto ed = getPointIndexes (set, items))
66 return { ed->getPointTime (set.
getFirst()),
67 ed->getPointTime (set.
getLast()) };
74 if (editor !=
nullptr)
76 editor->updateLineThickness();
85 undoManager (edit.getUndoManager()),
88 setAlwaysOnTop (
true);
89 setHoverComponent (
this,
false);
90 selectionManager.addChangeListener (
this);
93CurveEditor::~CurveEditor()
98 if (p->editor == this)
99 selectionManager.deselect (p);
104void CurveEditor::updateLineThickness()
109 if (lineThickness != newthickness)
111 lineThickness = newthickness;
120 if ((rightTime - leftTime) == TimeDuration())
124 auto curveColour = getCurrentLineColour();
125 auto backgroundColour = getBackgroundColour();
128 if (isOver || isCurveSelected)
130 g.
setColour (getCurveNameTextBackgroundColour());
131 auto text = getCurveName();
134 auto tx = getCurveNameOffset() - (tw + 8);
138 g.
drawText (text, tx, 0, tw + 6, 16,
143 const int start =
std::max (0, nextIndexAfter (leftTime) - 1);
144 const int numPoints = getNumPoints();
149 const auto startX =
std::max (0.0f, timeToX ({}));
150 auto lastY = valueToY (getValueAt (leftTime));
158 for (
int index = start; index < numPoints - 1; ++index)
164 auto c = getPointCurve (index);
174 if (c >= -0.5 && c <= 0.5)
180 double lineX1, lineX2;
181 float lineY1, lineY2;
182 getBezierEnds (index, lineX1, lineY1, lineX2, lineY2);
192 if (p2.x > clipBounds.getRight())
199 if (
auto fillCol = getCurrentFillColour(); ! fillCol.
isTransparent())
203 fillPath.lineTo ((
float)
getWidth(), y);
204 fillPath.lineTo (startX, y);
205 fillPath.closeSubPath();
216 const bool anySelected = areAnyPointsSelected();
218 if (isOver || isCurveSelected || anySelected)
227 for (
int i = start; i < numPoints; ++i)
237 if (r.getX() > clipBounds.getRight())
240 const bool isSelected = isPointSelected (i);
247 if (! isSelected && i != pointUnderMouse)
261 for (
int i = start; i < numPoints - 1; ++i)
271 if (r.getX() > clipBounds.getRight())
277 if (i != curveUnderMouse && ! isPointSelected (i))
286bool CurveEditor::hitTest (
int x,
int y)
288 auto py1 = valueToY (getValueAt (xToTime (x - 3.0f)));
289 auto py2 = valueToY (getValueAt (xToTime (x + 3.0f)));
294 for (
int i = firstIndexOnScreen; i < getNumPoints(); ++i)
296 auto t = getPointTime (i);
301 auto px = timeToX (t);
302 auto py = valueToY (getPointValue (i));
304 if (std::abs (x - px) < pointRadius
305 && std::abs (y - py) < pointRadius + 2)
312void CurveEditor::visibilityChanged()
314 updateLineThickness();
321 if (getItem() ==
nullptr)
331 pointBeingMoved = pointUnderMouse;
333 if (pointBeingMoved >= 0)
336 movingAllPoints =
false;
338 if (pointUnderMouse != -1)
344 if (
auto firstPoint = selectionManager.getFirstItemOfType<CurveEditorPoint>())
345 selPoint = firstPoint->index;
347 int start =
std::min (selPoint, pointUnderMouse);
350 selectionManager.deselectAll();
352 for (
int i = start; i <=
end; ++i)
353 selectPoint (i,
true);
357 if (isPointSelected (pointUnderMouse))
358 selectionManager.deselect (getSelectedPoint (pointUnderMouse));
360 selectionManager.addToSelection (createPoint (pointUnderMouse));
364 selectionManager.deselectAll();
365 selectPoint (pointUnderMouse,
false);
368 else if (curveUnderMouse != -1)
370 selectionManager.deselectAll();
371 selectPoint (curveUnderMouse,
false);
381 return juce::Colours::transparentWhite;
384void CurveEditor::selectPoint (
int pointIdx,
bool addToSelection)
386 if (pointIdx >= 0 && pointIdx < getNumPoints() && ! isPointSelected (pointIdx))
387 selectionManager.select (createPoint (pointIdx), addToSelection);
394 if (getItem() ==
nullptr)
409 nonRealTimeDragStart();
413 mouseDownCurve = curveUnderMouse >= 0 ? getPointCurve (curveUnderMouse) : 0.0f;
415 if (lineUnderMouse >= 0)
420 auto t1 = getPointTime (lineUnderMouse);
421 auto t2 = getPointTime (lineUnderMouse + 1);
422 auto v1 = getPointValue (lineUnderMouse);
423 auto v2 = getPointValue (lineUnderMouse + 1);
424 auto c1 = getPointCurve (lineUnderMouse);
425 auto c2 = getPointCurve (lineUnderMouse + 1);
427 addPoint (t1, v1, c1);
428 addPoint (t2 - TimeDuration::fromSeconds (0.0000001), v2, c2);
432 selectionManager.deselectAll();
433 selectPoint (lineUnderMouse,
true);
434 selectPoint (lineUnderMouse + 1,
true);
437 point1 = getPointValue (lineUnderMouse);
438 point2 = getPointValue (lineUnderMouse + 1);
440 else if (curveUnderMouse == -1 && pointUnderMouse == -1 && getNumPoints() <= 1)
442 if (getNumPoints() == 1)
443 point1 = getPointValue (0);
445 point1 = getValueAt ({});
447 else if (getNumPoints())
449 point1 = getPointValue (0);
450 point2 = getPointValue (getNumPoints() - 1);
454 if (pointBeingMoved >= 0 && realTimeDrag)
458 if (curveUnderMouse >= 0)
463 auto mult = getPointValue (curveUnderMouse) < getPointValue (curveUnderMouse + 1) ? 1 : -1;
465 curvePoint (curveUnderMouse, c);
466 showBubbleForPointUnderMouse();
469 else if (pointBeingMoved >= 0)
476 auto delta = t - mouseDownTime;
478 if (delta > TimeDuration())
480 for (
int i = getNumPoints(); --i >= pointBeingMoved;)
481 movePoint (i, getPointTime (i) + delta, getPointValue (i),
false);
485 bool firstPoint =
true;
487 for (
int i = pointBeingMoved; i < getNumPoints(); ++i)
489 auto newIndex = movePoint (i,
490 getPointTime (i) + delta,
500 i -= pointBeingMoved - newIndex;
501 pointUnderMouse = newIndex;
514 newValue = getPointValue(pointBeingMoved);
516 newTime = getPointTime(pointBeingMoved);
519 pointUnderMouse = movePoint (pointBeingMoved, newTime, newValue,
524 showBubbleForPointUnderMouse();
526 else if (lineUnderMouse >= 0)
530 auto offset = mouseDownValue - yToValue (e.
position.
y);
532 movePoint (lineUnderMouse, getPointTime(lineUnderMouse), point1 - offset,
false);
533 movePoint (lineUnderMouse + 1, getPointTime(lineUnderMouse + 1), point2 - offset,
false);
538 auto offset = mouseDownValue - yToValue (e.
position.
y);
540 if (getNumPoints() <= 0)
541 setValueWhenNoPoints (point1 - offset);
542 else if (getNumPoints() == 1 || mouseDownTime < getPointTime (0))
543 movePoint (0, getPointTime (0), point1 - offset,
false);
544 else if (mouseDownTime > getPointTime (getNumPoints() - 1))
545 movePoint (getNumPoints() - 1, getPointTime (getNumPoints() - 1), point2 - offset,
false);
549 updateLineThickness();
552 if (areAnyPointsSelected())
553 for (
auto c : selectionManager.getItemsOfType<CurveEditorPoint>())
559 if (getItem() ==
nullptr)
564 if (pointBeingMoved >= 0)
568 for (
int i =
std::max (0, pointBeingMoved - 1); i <
std::min (pointBeingMoved + 2, getNumPoints()); ++i)
572 if (i != pointBeingMoved && pos.getDistanceFrom (newPoint) < 7.0f)
575 removePoint (pointBeingMoved);
584 nonRealTimeDragEnd();
588 pointBeingMoved = -1;
591 updateLineThickness();
599 if (getItem() ==
nullptr)
602 if (pointUnderMouse >= 0 && ! dragged)
604 if (
auto p = getSelectedPoint (pointUnderMouse))
607 removePoint (pointUnderMouse);
609 else if (pointUnderMouse < 0)
612 auto value = getValueAt (t);
619 auto pnt = addPoint (t, value, defaultCurve);
622 selectPoint (pnt,
false);
631 if (getItem() ==
nullptr)
635 updateLineThickness();
641 updateLineThickness();
647 updateLineThickness();
650void CurveEditor::mouseHovered (
int,
int)
652 showBubbleForPointUnderMouse();
655void CurveEditor::mouseMovedAfterHover()
662 if (pointUnderMouse != -1 || curveUnderMouse != -1
671 if (getItem() ==
nullptr || pointBeingMoved >= 0)
676 const auto captureRadius = pointRadius * pointRadius;
680 for (
int i = firstIndexOnScreen; i < getNumPoints(); ++i)
682 auto t = getPointTime (i);
688 if (
getPosition ({ t, getPointValue (i) }).getDistanceSquaredFrom (pos) < captureRadius)
695 if (i < getNumPoints() - 1)
696 if (
getPosition (getBezierHandle (i)).getDistanceSquaredFrom (pos) < captureRadius)
700 if (pointUnderMouse != point)
702 pointUnderMouse = point;
708 if (pointUnderMouse != -1)
711 if (curveUnderMouse != curvePoint)
713 curveUnderMouse = curvePoint;
717 int newLineUnderMouse = -1;
719 if (pointUnderMouse == -1 && curveUnderMouse == -1)
721 auto numPoints = getNumPoints();
723 for (
int i = 0; i < numPoints - 1; ++i)
725 auto t = xToTime (pos.
x);
727 if (t > getPointTime(i) && t < getPointTime (i + 1))
728 newLineUnderMouse = i;
732 if (newLineUnderMouse != lineUnderMouse)
734 lineUnderMouse = newLineUnderMouse;
740void CurveEditor::setTimes (TimePosition l, TimePosition r)
746float CurveEditor::timeToX (TimePosition t)
const
748 return (
float) (
getWidth() * ((t - leftTime) / (rightTime - leftTime)));
751TimePosition CurveEditor::xToTime (
double x)
const
753 return TimePosition::fromSeconds (
std::max (0.0, leftTime.
inSeconds() + (x * (rightTime - leftTime).inSeconds()) /
getWidth()));
756float CurveEditor::valueToY (
float val)
const
758 return 1.0f + (1.0f - (val - parameterMinValue) / parameterRange) * (
getHeight() - 2);
761float CurveEditor::yToValue (
double y)
const
763 return (
float) ((1.0 - (y - 1) / (
getHeight() - 2)) * parameterRange + parameterMinValue);
773 return getPosition ({ getPointTime (index), getPointValue (index) });
776void CurveEditor::selectableObjectChanged (Selectable*)
778 updateLineThickness();
781void CurveEditor::selectableObjectAboutToBeDeleted (Selectable*)
783 updateLineThickness();
788 if (cb == &selectionManager)
790 const bool selectedNow = selectionManager.isSelected (getItem());
792 if (isCurveSelected != selectedNow)
794 isCurveSelected = selectedNow;
805 updateLineThickness();
808Edit& CurveEditor::getEdit()
const
818bool CurveEditor::isPointSelected (
int idx)
820 for (
auto p : selectionManager.getItemsOfType<CurveEditorPoint>())
821 if (p->editor == this && p->index == idx)
827bool CurveEditor::areAnyPointsSelected()
829 for (
auto p : selectionManager.getItemsOfType<CurveEditorPoint>())
830 if (p->editor == this)
836CurveEditorPoint* CurveEditor::getSelectedPoint (
int idx)
838 for (
auto p : selectionManager.getItemsOfType<CurveEditorPoint>())
839 if (p->editor == this && p->index == idx)
void ensureStorageAllocated(int minNumElements)
int size() const noexcept
ElementType getFirst() const noexcept
ElementType * begin() noexcept
ElementType * end() noexcept
void add(const ElementType &newElement)
ElementType getLast() const noexcept
void removeChangeListener(ChangeListener *listener)
bool isTransparent() const noexcept
bool isMouseOverOrDragging(bool includeChildren=false) const
bool isVisible() const noexcept
Point< int > getPosition() const noexcept
int getHeight() const noexcept
Point< int > getMouseXYRelative() const
int getWidth() const noexcept
int getStringWidth(const String &text) const
void drawText(const String &text, int x, int y, int width, int height, Justification justificationType, bool useEllipsesIfTooBig=true) const
Font getCurrentFont() const
void setFont(const Font &newFont)
void fillRectList(const RectangleList< float > &rectangles) const
void fillRect(Rectangle< int > rectangle) const
void fillPath(const Path &path) const
Rectangle< int > getClipBounds() const
void setColour(Colour newColour)
void strokePath(const Path &path, const PathStrokeType &strokeType, const AffineTransform &transform={}) const
void fillEllipse(float x, float y, float width, float height) const
bool testFlags(int flagsToTest) const noexcept
static ModifierKeys getCurrentModifiers() noexcept
bool isCommandDown() const noexcept
bool isShiftDown() const noexcept
const Point< float > mouseDownPosition
const Point< float > position
int getDistanceFromDragStart() const noexcept
int getDistanceFromDragStartY() const noexcept
int getDistanceFromDragStartX() const noexcept
void startNewSubPath(float startX, float startY)
void preallocateSpace(int numExtraCoordsToMakeSpaceFor)
void quadraticTo(float controlPointX, float controlPointY, float endPointX, float endPointY)
void lineTo(float endX, float endY)
void addWithoutMerging(RectangleType rect)
void ensureStorageAllocated(int minNumRectangles)
void beginNewTransaction()
bool undoCurrentTransactionOnly()
void selectionStatusChanged(bool isNowSelected) override
Can be overridden to tell this object that it has just been selected or deselected.
The Tracktion Edit class!
TempoSequence tempoSequence
The global TempoSequence of this Edit.
Manages a list of items that are currently selected.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
TimePosition toTime(BeatPosition bp, const TempoSequence &ts)
Converts a BeatPosition to a TimePosition given a TempoSequence.
RangeType< TimePosition > TimeRange
A RangeType based on real time (i.e.
constexpr double inSeconds() const
Returns the TimePosition as a number of seconds.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.