git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce

subrepo:
  subdir:   "deps/juce"
  merged:   "b13f9084e"
upstream:
  origin:   "https://github.com/essej/JUCE.git"
  branch:   "sono6good"
  commit:   "b13f9084e"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
This commit is contained in:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

View File

@ -0,0 +1,209 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
class JucerColourPropertyComponent : public PropertyComponent
{
public:
JucerColourPropertyComponent (const String& name,
const bool canReset)
: PropertyComponent (name)
{
colourPropEditor.reset (new ColourPropEditorComponent (this, canReset));
addAndMakeVisible (colourPropEditor.get());
}
virtual void setColour (Colour newColour) = 0;
virtual Colour getColour() const = 0;
virtual void resetToDefault() = 0;
void refresh() override
{
((ColourPropEditorComponent*) getChildComponent (0))->refresh();
}
class ColourEditorComponent : public Component,
private ChangeListener
{
public:
ColourEditorComponent (const bool canReset)
: canResetToDefault (canReset)
{
}
void paint (Graphics& g) override
{
g.fillAll (Colours::grey);
g.fillCheckerBoard (getLocalBounds().reduced (2, 2).toFloat(),
10.0f, 10.0f,
Colour (0xffdddddd).overlaidWith (colour),
Colour (0xffffffff).overlaidWith (colour));
g.setColour (Colours::white.overlaidWith (colour).contrasting());
g.setFont (Font ((float) getHeight() * 0.6f, Font::bold));
g.drawFittedText (colour.toDisplayString (true),
2, 1, getWidth() - 4, getHeight() - 1,
Justification::centred, 1);
}
virtual void setColour (Colour newColour) = 0;
virtual void resetToDefault() = 0;
virtual Colour getColour() const = 0;
void refresh()
{
const Colour col (getColour());
if (col != colour)
{
colour = col;
repaint();
}
}
void mouseDown (const MouseEvent&) override
{
CallOutBox::launchAsynchronously (std::make_unique<ColourSelectorComp> (this, canResetToDefault),
getScreenBounds(),
nullptr);
}
class ColourSelectorComp : public Component
{
public:
ColourSelectorComp (ColourEditorComponent* owner_,
const bool canReset)
: owner (owner_),
defaultButton ("Reset to Default")
{
addAndMakeVisible (selector);
selector.setName ("Colour");
selector.setCurrentColour (owner->getColour());
selector.addChangeListener (owner);
if (canReset)
{
addAndMakeVisible (defaultButton);
defaultButton.onClick = [this]
{
owner->resetToDefault();
owner->refresh();
selector.setCurrentColour (owner->getColour());
};
}
setSize (300, 400);
}
void resized() override
{
if (defaultButton.isVisible())
{
selector.setBounds (0, 0, getWidth(), getHeight() - 30);
defaultButton.changeWidthToFitText (22);
defaultButton.setTopLeftPosition (10, getHeight() - 26);
}
else
{
selector.setBounds (getLocalBounds());
}
}
private:
class ColourSelectorWithSwatches : public ColourSelector
{
public:
ColourSelectorWithSwatches()
{
}
int getNumSwatches() const override
{
return getAppSettings().swatchColours.size();
}
Colour getSwatchColour (int index) const override
{
return getAppSettings().swatchColours [index];
}
void setSwatchColour (int index, const Colour& newColour) override
{
getAppSettings().swatchColours.set (index, newColour);
}
};
ColourEditorComponent* owner;
ColourSelectorWithSwatches selector;
TextButton defaultButton;
};
private:
void changeListenerCallback (ChangeBroadcaster* source) override
{
const ColourSelector* const cs = (const ColourSelector*) source;
if (cs->getCurrentColour() != getColour())
setColour (cs->getCurrentColour());
}
Colour colour;
bool canResetToDefault;
};
class ColourPropEditorComponent : public ColourEditorComponent
{
JucerColourPropertyComponent* const owner;
public:
ColourPropEditorComponent (JucerColourPropertyComponent* const owner_,
const bool canReset)
: ColourEditorComponent (canReset),
owner (owner_)
{}
void setColour (Colour newColour) override
{
owner->setColour (newColour);
}
Colour getColour() const override
{
return owner->getColour();
}
void resetToDefault() override
{
owner->resetToDefault();
}
};
std::unique_ptr<ColourPropEditorComponent> colourPropEditor;
};

View File

@ -0,0 +1,60 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
template <class ComponentType>
class ComponentBooleanProperty : public BooleanPropertyComponent,
private ChangeListener
{
public:
ComponentBooleanProperty (const String& name,
const String& onText_,
const String& offText_,
ComponentType* comp,
JucerDocument& doc)
: BooleanPropertyComponent (name, onText_, offText_),
component (comp),
document (doc)
{
document.addChangeListener (this);
}
~ComponentBooleanProperty()
{
document.removeChangeListener (this);
}
void changeListenerCallback (ChangeBroadcaster*)
{
refresh();
}
protected:
ComponentType* component;
JucerDocument& document;
};

View File

@ -0,0 +1,58 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
template <class ComponentType>
class ComponentChoiceProperty : public ChoicePropertyComponent,
private ChangeListener
{
public:
ComponentChoiceProperty (const String& name,
ComponentType* comp,
JucerDocument& doc)
: ChoicePropertyComponent (name),
component (comp),
document (doc)
{
document.addChangeListener (this);
}
~ComponentChoiceProperty()
{
document.removeChangeListener (this);
}
void changeListenerCallback (ChangeBroadcaster*)
{
refresh();
}
protected:
ComponentType* component;
JucerDocument& document;
};

View File

@ -0,0 +1,165 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
#include "jucer_ColourPropertyComponent.h"
//==============================================================================
template <class ComponentType>
class ComponentColourProperty : public JucerColourPropertyComponent,
private ChangeListener
{
public:
ComponentColourProperty (const String& name,
ComponentType* comp,
JucerDocument& doc,
const bool canResetToDefault)
: JucerColourPropertyComponent (name, canResetToDefault),
component (comp),
document (doc)
{
document.addChangeListener (this);
}
~ComponentColourProperty()
{
document.removeChangeListener (this);
}
void changeListenerCallback (ChangeBroadcaster*)
{
refresh();
}
protected:
ComponentType* component;
JucerDocument& document;
};
//==============================================================================
class ComponentColourIdProperty : public ComponentColourProperty <Component>
{
public:
//==============================================================================
ComponentColourIdProperty (Component* const comp,
JucerDocument& doc,
const int colourId_,
const String& name,
const bool canResetToDefault)
: ComponentColourProperty <Component> (name, comp, doc, canResetToDefault),
colourId (colourId_)
{
}
//==============================================================================
Colour getColour() const
{
return component->findColour (colourId);
}
void setColour (Colour newColour)
{
if (component->findColour (colourId) != newColour)
{
document.getUndoManager().undoCurrentTransactionOnly();
document.perform (new ColourChangeAction (component,
*document.getComponentLayout(),
colourId,
newColour,
false),
"Change colour");
}
}
void resetToDefault()
{
document.getUndoManager().undoCurrentTransactionOnly();
document.perform (new ColourChangeAction (component,
*document.getComponentLayout(),
colourId,
Colours::black,
true),
"Reset colour");
}
private:
const int colourId;
class ColourChangeAction : public ComponentUndoableAction <Component>
{
public:
ColourChangeAction (Component* const comp,
ComponentLayout& l,
const int colourId_,
Colour newColour_,
const bool newColourIsDefault)
: ComponentUndoableAction<Component> (comp, l),
colourId (colourId_),
newColour (newColour_),
isDefault (newColourIsDefault)
{
}
bool perform()
{
showCorrectTab();
wasSpecified = getComponent()->isColourSpecified (colourId);
oldColour = getComponent()->findColour (colourId);
if (isDefault)
getComponent()->removeColour (colourId);
else
getComponent()->setColour (colourId, newColour);
changed();
return true;
}
bool undo()
{
showCorrectTab();
if (wasSpecified)
getComponent()->setColour (colourId, oldColour);
else
getComponent()->removeColour (colourId);
if (TextEditor* const te = dynamic_cast<TextEditor*> (getComponent()))
te->applyFontToAllText (te->getFont());
changed();
return true;
}
int colourId;
Colour newColour, oldColour;
bool isDefault, wasSpecified;
};
};

View File

@ -0,0 +1,60 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
template <class ComponentType>
class ComponentTextProperty : public TextPropertyComponent,
private ChangeListener
{
public:
ComponentTextProperty (const String& name,
const int maxNumChars_,
const bool isMultiLine_,
ComponentType* const comp,
JucerDocument& doc)
: TextPropertyComponent (name, maxNumChars_, isMultiLine_),
component (comp),
document (doc)
{
document.addChangeListener (this);
}
~ComponentTextProperty()
{
document.removeChangeListener (this);
}
void changeListenerCallback (ChangeBroadcaster*)
{
refresh();
}
protected:
ComponentType* component;
JucerDocument& document;
};

View File

@ -0,0 +1,63 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
class FilePropertyComponent : public PropertyComponent,
private FilenameComponentListener
{
public:
FilePropertyComponent (const String& name,
const bool isDirectory,
const bool allowEditingOfFilename,
const String& fileBrowserWildcard = "*")
: PropertyComponent (name),
filenameComp (name, File(), allowEditingOfFilename,
isDirectory, false, fileBrowserWildcard,
String(), String())
{
addAndMakeVisible (filenameComp);
filenameComp.addListener (this);
}
virtual void setFile (const File& newFile) = 0;
virtual File getFile() const = 0;
void refresh() override
{
filenameComp.setCurrentFile (getFile(), false);
}
private:
void filenameComponentChanged (FilenameComponent*) override
{
if (getFile() != filenameComp.getCurrentFile())
setFile (filenameComp.getCurrentFile());
}
FilenameComponent filenameComp;
};

View File

@ -0,0 +1,142 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
class FontPropertyComponent : public ChoicePropertyComponent
{
public:
FontPropertyComponent (const String& name)
: ChoicePropertyComponent (name)
{
choices.add (getDefaultFont());
choices.add (getDefaultSans());
choices.add (getDefaultSerif());
choices.add (getDefaultMono());
choices.add (String());
static StringArray fontNames;
if (fontNames.size() == 0)
{
Array<Font> fonts;
Font::findFonts (fonts);
for (int i = 0; i < fonts.size(); ++i)
fontNames.add (fonts[i].getTypefaceName());
}
choices.addArray (fontNames);
}
static String getDefaultFont() { return "Default font"; }
static String getDefaultSans() { return "Default sans-serif font"; }
static String getDefaultSerif() { return "Default serif font"; }
static String getDefaultMono() { return "Default monospaced font"; }
//==============================================================================
virtual void setTypefaceName (const String& newFontName) = 0;
virtual String getTypefaceName() const = 0;
//==============================================================================
void setIndex (int newIndex)
{
String type (choices [newIndex]);
if (type.isEmpty())
type = getDefaultFont();
if (getTypefaceName() != type)
setTypefaceName (type);
}
int getIndex() const
{
return choices.indexOf (getTypefaceName());
}
static Font applyNameToFont (const String& typefaceName, const Font& font)
{
auto extraKerning = font.getExtraKerningFactor();
if (typefaceName == getDefaultFont()) return Font (font.getHeight(), font.getStyleFlags()).withExtraKerningFactor (extraKerning);
if (typefaceName == getDefaultSans()) return Font (Font::getDefaultSansSerifFontName(), font.getHeight(), font.getStyleFlags()).withExtraKerningFactor (extraKerning);
if (typefaceName == getDefaultSerif()) return Font (Font::getDefaultSerifFontName(), font.getHeight(), font.getStyleFlags()).withExtraKerningFactor (extraKerning);
if (typefaceName == getDefaultMono()) return Font (Font::getDefaultMonospacedFontName(), font.getHeight(), font.getStyleFlags()).withExtraKerningFactor (extraKerning);
auto f = Font (typefaceName, font.getHeight(), font.getStyleFlags()).withExtraKerningFactor (extraKerning);
if (f.getAvailableStyles().contains (font.getTypefaceStyle()))
f.setTypefaceStyle (font.getTypefaceStyle());
return f;
}
static String getTypefaceNameCode (const String& typefaceName)
{
if (typefaceName == getDefaultFont()) return {};
if (typefaceName == getDefaultSans()) return "juce::Font::getDefaultSansSerifFontName(), ";
if (typefaceName == getDefaultSerif()) return "juce::Font::getDefaultSerifFontName(), ";
if (typefaceName == getDefaultMono()) return "juce::Font::getDefaultMonospacedFontName(), ";
return "\"" + typefaceName + "\", ";
}
static String getFontStyleCode (const Font& font)
{
if (font.isBold() && font.isItalic()) return "juce::Font::bold | juce::Font::italic";
if (font.isBold()) return "juce::Font::bold";
if (font.isItalic()) return "juce::Font::italic";
return "juce::Font::plain";
}
static String getCompleteFontCode (const Font& font, const String& typefaceName)
{
String s;
s << "juce::Font ("
<< getTypefaceNameCode (typefaceName)
<< CodeHelpers::floatLiteral (font.getHeight(), 2)
<< ", ";
if (font.getAvailableStyles().contains(font.getTypefaceStyle()))
s << "juce::Font::plain).withTypefaceStyle ("
<< CodeHelpers::stringLiteral (font.getTypefaceStyle())
<< ")";
else
s << getFontStyleCode (font)
<< ")";
if (font.getExtraKerningFactor() != 0.0f)
s << ".withExtraKerningFactor ("
<< CodeHelpers::floatLiteral (font.getExtraKerningFactor(), 3)
<< ")";
return s;
}
};

View File

@ -0,0 +1,100 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
//==============================================================================
class JustificationProperty : public ChoicePropertyComponent
{
public:
JustificationProperty (const String& name, const bool onlyHorizontalOptions)
: ChoicePropertyComponent (name)
{
if (onlyHorizontalOptions)
{
choices.add ("centre");
choices.add ("left");
choices.add ("right");
}
else
{
choices.add ("centred");
choices.add ("centred left");
choices.add ("centred right");
choices.add ("centred top");
choices.add ("centred bottom");
choices.add ("top left");
choices.add ("top right");
choices.add ("bottom left");
choices.add ("bottom right");
}
}
//==============================================================================
virtual void setJustification (Justification newJustification) = 0;
virtual Justification getJustification() const = 0;
//==============================================================================
void setIndex (int newIndex)
{
const int types[] = { Justification::centred,
Justification::centredLeft,
Justification::centredRight,
Justification::centredTop,
Justification::centredBottom,
Justification::topLeft,
Justification::topRight,
Justification::bottomLeft,
Justification::bottomRight };
if (((unsigned int) newIndex) < (unsigned int) numElementsInArray (types)
&& types [newIndex] != getJustification().getFlags())
{
setJustification (Justification (types [newIndex]));
}
}
int getIndex() const
{
const int types[] = { Justification::centred,
Justification::centredLeft,
Justification::centredRight,
Justification::centredTop,
Justification::centredBottom,
Justification::topLeft,
Justification::topRight,
Justification::bottomLeft,
Justification::bottomRight };
const int rawFlags = getJustification().getFlags();
for (int i = numElementsInArray (types); --i >= 0;)
if (types[i] == rawFlags)
return i;
return -1;
}
};

View File

@ -0,0 +1,463 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#pragma once
#include "../UI/jucer_PaintRoutineEditor.h"
#include "../UI/jucer_ComponentLayoutEditor.h"
//==============================================================================
/**
Base class for a property that edits the x, y, w, or h of a PositionedRectangle.
*/
class PositionPropertyBase : public PropertyComponent,
protected ChangeListener
{
public:
enum ComponentPositionDimension
{
componentX = 0,
componentY = 1,
componentWidth = 2,
componentHeight = 3
};
PositionPropertyBase (Component* comp,
const String& name,
ComponentPositionDimension dimension_,
const bool includeAnchorOptions_,
const bool allowRelativeOptions_,
ComponentLayout* layout_)
: PropertyComponent (name),
layout (layout_),
button ("mode"),
component (comp),
dimension (dimension_),
includeAnchorOptions (includeAnchorOptions_),
allowRelativeOptions (allowRelativeOptions_)
{
addAndMakeVisible (button);
button.setTriggeredOnMouseDown (true);
button.setConnectedEdges (TextButton::ConnectedOnLeft | TextButton::ConnectedOnRight);
button.onClick = [this]
{
SafePointer<PositionPropertyBase> safeThis { this };
showMenu (layout, [safeThis] (bool shouldRefresh)
{
if (safeThis == nullptr)
return;
if (shouldRefresh)
safeThis->refresh(); // (to clear the text editor if it's got focus)
});
};
textEditor.reset (new PositionPropLabel (*this));
addAndMakeVisible (textEditor.get());
}
String getText() const
{
RelativePositionedRectangle rpr (getPosition());
PositionedRectangle& p = rpr.rect;
String s;
switch (dimension)
{
case componentX:
if (p.getPositionModeX() == PositionedRectangle::proportionOfParentSize)
s << valueToString (p.getX() * 100.0) << '%';
else
s << valueToString (p.getX());
break;
case componentY:
if (p.getPositionModeY() == PositionedRectangle::proportionOfParentSize)
s << valueToString (p.getY() * 100.0) << '%';
else
s << valueToString (p.getY());
break;
case componentWidth:
if (p.getWidthMode() == PositionedRectangle::proportionalSize)
s << valueToString (p.getWidth() * 100.0) << '%';
else
s << valueToString (p.getWidth());
break;
case componentHeight:
if (p.getHeightMode() == PositionedRectangle::proportionalSize)
s << valueToString (p.getHeight() * 100.0) << '%';
else
s << valueToString (p.getHeight());
break;
default:
jassertfalse;
break;
};
return s;
}
static String valueToString (const double n)
{
return String (roundToInt (n * 1000.0) / 1000.0);
}
void setText (const String& newText)
{
RelativePositionedRectangle rpr (getPosition());
PositionedRectangle p (rpr.rect);
const double value = newText.getDoubleValue();
switch (dimension)
{
case componentX:
if (p.getPositionModeX() == PositionedRectangle::proportionOfParentSize)
p.setX (value / 100.0);
else
p.setX (value);
break;
case componentY:
if (p.getPositionModeY() == PositionedRectangle::proportionOfParentSize)
p.setY (value / 100.0);
else
p.setY (value);
break;
case componentWidth:
if (p.getWidthMode() == PositionedRectangle::proportionalSize)
p.setWidth (value / 100.0);
else
p.setWidth (value);
break;
case componentHeight:
if (p.getHeightMode() == PositionedRectangle::proportionalSize)
p.setHeight (value / 100.0);
else
p.setHeight (value);
break;
default:
jassertfalse;
break;
};
if (p != rpr.rect)
{
rpr.rect = p;
setPosition (rpr);
}
}
void changeListenerCallback (ChangeBroadcaster*)
{
refresh();
}
void showMenu (ComponentLayout* compLayout, std::function<void (bool)> callback)
{
RelativePositionedRectangle rpr (getPosition());
PositionedRectangle p (rpr.rect);
PositionedRectangle::AnchorPoint xAnchor = p.getAnchorPointX();
PositionedRectangle::AnchorPoint yAnchor = p.getAnchorPointY();
PositionedRectangle::PositionMode xMode = p.getPositionModeX();
PositionedRectangle::PositionMode yMode = p.getPositionModeY();
PositionedRectangle::SizeMode sizeW = p.getWidthMode();
PositionedRectangle::SizeMode sizeH = p.getHeightMode();
String relCompName ("parent");
if (Component* const relComp = compLayout != nullptr ? compLayout->getComponentRelativePosTarget (component, (int) dimension)
: nullptr)
relCompName = compLayout->getComponentMemberVariableName (relComp);
jassert (relCompName.isNotEmpty());
PopupMenu m;
if (dimension == componentX || dimension == componentY)
{
const PositionedRectangle::PositionMode posMode = (dimension == componentX) ? xMode : yMode;
m.addItem (10, ((dimension == componentX) ? "Absolute distance from left of "
: "Absolute distance from top of ") + relCompName,
true, posMode == PositionedRectangle::absoluteFromParentTopLeft);
m.addItem (11, ((dimension == componentX) ? "Absolute distance from right of "
: "Absolute distance from bottom of ") + relCompName,
true, posMode == PositionedRectangle::absoluteFromParentBottomRight);
m.addItem (12, "Absolute distance from centre of " + relCompName,
true, posMode == PositionedRectangle::absoluteFromParentCentre);
m.addItem (13, ((dimension == componentX) ? "Percentage of width of "
: "Percentage of height of ") + relCompName,
true, posMode == PositionedRectangle::proportionOfParentSize);
m.addSeparator();
if (includeAnchorOptions)
{
const PositionedRectangle::AnchorPoint anchor = (dimension == componentX) ? xAnchor : yAnchor;
m.addItem (14, (dimension == componentX) ? "Anchored at left of component"
: "Anchored at top of component",
true, anchor == PositionedRectangle::anchorAtLeftOrTop);
m.addItem (15, "Anchored at centre of component", true, anchor == PositionedRectangle::anchorAtCentre);
m.addItem (16, (dimension == componentX) ? "Anchored at right of component"
: "Anchored at bottom of component",
true, anchor == PositionedRectangle::anchorAtRightOrBottom);
}
}
else
{
const PositionedRectangle::SizeMode sizeMode = (dimension == componentWidth) ? sizeW : sizeH;
m.addItem (20, (dimension == componentWidth) ? "Absolute width"
: "Absolute height",
true, sizeMode == PositionedRectangle::absoluteSize);
m.addItem (21, ((dimension == componentWidth) ? "Percentage of width of "
: "Percentage of height of ") + relCompName,
true, sizeMode == PositionedRectangle::proportionalSize);
m.addItem (22, ((dimension == componentWidth) ? "Subtracted from width of "
: "Subtracted from height of ") + relCompName,
true, sizeMode == PositionedRectangle::parentSizeMinusAbsolute);
}
if (allowRelativeOptions && compLayout != nullptr)
{
m.addSeparator();
m.addSubMenu ("Relative to", compLayout->getRelativeTargetMenu (component, (int) dimension));
}
m.showMenuAsync (PopupMenu::Options().withTargetComponent (&button),
[compLayout, callback, xAnchor, yAnchor, xMode, yMode, sizeW, sizeH, p, rpr,
ref = SafePointer<PositionPropertyBase> { this }] (int menuResult) mutable
{
if (menuResult == 0 || ref == nullptr)
{
callback (false);
return;
}
switch (menuResult)
{
case 10:
if (ref->dimension == componentX)
xMode = PositionedRectangle::absoluteFromParentTopLeft;
else
yMode = PositionedRectangle::absoluteFromParentTopLeft;
break;
case 11:
if (ref->dimension == componentX)
xMode = PositionedRectangle::absoluteFromParentBottomRight;
else
yMode = PositionedRectangle::absoluteFromParentBottomRight;
break;
case 12:
if (ref->dimension == componentX)
xMode = PositionedRectangle::absoluteFromParentCentre;
else
yMode = PositionedRectangle::absoluteFromParentCentre;
break;
case 13:
if (ref->dimension == componentX)
xMode = PositionedRectangle::proportionOfParentSize;
else
yMode = PositionedRectangle::proportionOfParentSize;
break;
case 14:
if (ref->dimension == componentX)
xAnchor = PositionedRectangle::anchorAtLeftOrTop;
else
yAnchor = PositionedRectangle::anchorAtLeftOrTop;
break;
case 15:
if (ref->dimension == componentX)
xAnchor = PositionedRectangle::anchorAtCentre;
else
yAnchor = PositionedRectangle::anchorAtCentre;
break;
case 16:
if (ref->dimension == componentX)
xAnchor = PositionedRectangle::anchorAtRightOrBottom;
else
yAnchor = PositionedRectangle::anchorAtRightOrBottom;
break;
case 20:
if (ref->dimension == componentWidth)
sizeW = PositionedRectangle::absoluteSize;
else
sizeH = PositionedRectangle::absoluteSize;
break;
case 21:
if (ref->dimension == componentWidth)
sizeW = PositionedRectangle::proportionalSize;
else
sizeH = PositionedRectangle::proportionalSize;
break;
case 22:
if (ref->dimension == componentWidth)
sizeW = PositionedRectangle::parentSizeMinusAbsolute;
else
sizeH = PositionedRectangle::parentSizeMinusAbsolute;
break;
default:
if (ref->allowRelativeOptions && compLayout != nullptr)
compLayout->processRelativeTargetMenuResult (ref->component, (int) ref->dimension, menuResult);
break;
}
const auto parentArea = [&]() -> Rectangle<int>
{
if (ref->component->findParentComponentOfClass<ComponentLayoutEditor>() != nullptr)
return { ref->component->getParentWidth(), ref->component->getParentHeight() };
if (auto pre = dynamic_cast<PaintRoutineEditor*> (ref->component->getParentComponent()))
return pre->getComponentArea();
jassertfalse;
return {};
}();
int x, xw, y, yh, w, h;
rpr.getRelativeTargetBounds (parentArea, compLayout, x, xw, y, yh, w, h);
PositionedRectangle xyRect (p);
PositionedRectangle whRect (p);
xyRect.setModes (xAnchor, xMode, yAnchor, yMode, sizeW, sizeH,
Rectangle<int> (x, y, xw, yh));
whRect.setModes (xAnchor, xMode, yAnchor, yMode, sizeW, sizeH,
Rectangle<int> (x, y, w, h));
p.setModes (xAnchor, xMode, yAnchor, yMode, sizeW, sizeH,
Rectangle<int> (x, y, xw, yh));
p.setX (xyRect.getX());
p.setY (xyRect.getY());
p.setWidth (whRect.getWidth());
p.setHeight (whRect.getHeight());
if (p != rpr.rect)
{
rpr.rect = p;
ref->setPosition (rpr);
}
callback (true);
});
}
void resized()
{
const Rectangle<int> r (getLookAndFeel().getPropertyComponentContentPosition (*this));
button.changeWidthToFitText (r.getHeight());
button.setTopRightPosition (r.getRight(), r.getY());
textEditor->setBounds (r.getX(), r.getY(), button.getX() - r.getX(), r.getHeight());
}
void refresh()
{
textEditor->setText (getText(), dontSendNotification);
}
void textWasEdited()
{
const String newText (textEditor->getText());
if (getText() != newText)
setText (newText);
}
//==============================================================================
virtual void setPosition (const RelativePositionedRectangle& newPos) = 0;
virtual RelativePositionedRectangle getPosition() const = 0;
protected:
class PositionPropLabel : public Label
{
PositionPropertyBase& owner;
public:
PositionPropLabel (PositionPropertyBase& owner_)
: Label (String(), String()),
owner (owner_)
{
setEditable (true, true, false);
lookAndFeelChanged();
}
TextEditor* createEditorComponent() override
{
TextEditor* ed = Label::createEditorComponent();
ed->setInputRestrictions (14, "0123456789.-%");
return ed;
}
void textWasEdited() override
{
owner.textWasEdited();
}
void lookAndFeelChanged() override
{
setColour (backgroundColourId, findColour (widgetBackgroundColourId));
setColour (textColourId, findColour (widgetTextColourId));
}
};
ComponentLayout* layout;
std::unique_ptr<PositionPropLabel> textEditor;
TextButton button;
Component* component;
ComponentPositionDimension dimension;
const bool includeAnchorOptions, allowRelativeOptions;
};