26#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR
33 private DeletedAtShutdown
36 AllComponentRepainter() {}
37 ~AllComponentRepainter()
override { clearSingletonInstance(); }
43 if (! isTimerRunning())
48 void timerCallback()
override
52 Array<Component*> alreadyDone;
54 for (
int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;)
55 if (
auto* c = TopLevelWindow::getTopLevelWindow (i))
56 repaintAndResizeAllComps (c, alreadyDone);
58 auto& desktop = Desktop::getInstance();
60 for (
int i = desktop.getNumComponents(); --i >= 0;)
61 if (
auto* c = desktop.getComponent (i))
62 repaintAndResizeAllComps (c, alreadyDone);
65 static void repaintAndResizeAllComps (Component::SafePointer<Component> c,
66 Array<Component*>& alreadyDone)
68 if (c->isVisible() && ! alreadyDone.contains (c))
73 for (
int i = c->getNumChildComponents(); --i >= 0;)
75 if (
auto* child = c->getChildComponent (i))
77 repaintAndResizeAllComps (child, alreadyDone);
78 alreadyDone.add (child);
92int64 parseInt (String s)
96 if (s.startsWithChar (
'-'))
97 return -parseInt (s.substring (1));
99 if (s.startsWith (
"0x"))
100 return s.substring (2).getHexValue64();
102 return s.getLargeIntValue();
107 return s.retainCharacters (
"0123456789.eE-").getDoubleValue();
114LiveValueBase::LiveValueBase (
const char* file,
int line)
117 name = File (sourceFile).getFileName() +
" : " + String (sourceLine);
120LiveValueBase::~LiveValueBase()
125LivePropertyEditorBase::LivePropertyEditorBase (LiveValueBase& v, CodeDocument& d)
126 : value (v), document (d), sourceEditor (document, &tokeniser)
130 addAndMakeVisible (name);
131 addAndMakeVisible (resetButton);
132 addAndMakeVisible (valueEditor);
133 addAndMakeVisible (sourceEditor);
135 findOriginalValueInCode();
136 selectOriginalValue();
138 name.setFont (13.0f);
139 name.setText (v.name, dontSendNotification);
140 valueEditor.setMultiLine (v.isString());
141 valueEditor.setReturnKeyStartsNewLine (v.isString());
142 valueEditor.setText (v.getStringValue (wasHex), dontSendNotification);
143 valueEditor.onTextChange = [
this] { applyNewValue (valueEditor.getText()); };
144 sourceEditor.setReadOnly (
true);
145 sourceEditor.setFont (sourceEditor.getFont().withHeight (13.0f));
146 resetButton.onClick = [
this] { applyNewValue (value.getOriginalStringValue (wasHex)); };
149void LivePropertyEditorBase::paint (Graphics& g)
151 g.setColour (Colours::white);
152 g.fillRect (getLocalBounds().removeFromBottom (1));
155void LivePropertyEditorBase::resized()
157 auto r = getLocalBounds().reduced (0, 3).withTrimmedBottom (1);
159 auto left = r.removeFromLeft (jmax (200, r.getWidth() / 3));
161 auto top =
left.removeFromTop (25);
162 resetButton.setBounds (top.removeFromRight (35).reduced (0, 3));
163 name.setBounds (top);
165 if (customComp !=
nullptr)
167 valueEditor.setBounds (
left.removeFromTop (25));
168 left.removeFromTop (2);
169 customComp->setBounds (left);
173 valueEditor.setBounds (left);
176 r.removeFromLeft (4);
177 sourceEditor.setBounds (r);
180void LivePropertyEditorBase::applyNewValue (
const String& s)
182 value.setStringValue (s);
184 document.replaceSection (valueStart.getPosition(), valueEnd.getPosition(), value.getCodeValue (wasHex));
185 document.clearUndoHistory();
186 selectOriginalValue();
188 valueEditor.setText (s, dontSendNotification);
189 AllComponentRepainter::getInstance()->trigger();
192void LivePropertyEditorBase::selectOriginalValue()
194 sourceEditor.selectRegion (valueStart, valueEnd);
197void LivePropertyEditorBase::findOriginalValueInCode()
199 CodeDocument::Position pos (document, value.sourceLine, 0);
200 auto line = pos.getLineText();
201 auto p = line.getCharPointer();
203 p = CharacterFunctions::find (p, CharPointer_ASCII (
"JUCE_LIVE_CONSTANT"));
212 p += (
int) (
sizeof (
"JUCE_LIVE_CONSTANT") - 1);
213 p.incrementToEndOfWhitespace();
215 if (! CharacterFunctions::find (p, CharPointer_ASCII (
"JUCE_LIVE_CONSTANT")).isEmpty())
223 if (p.getAndAdvance() ==
'(')
225 auto start = p,
end = p;
229 while (!
end.isEmpty())
231 auto c =
end.getAndAdvance();
233 if (c ==
'(') ++depth;
234 if (c ==
')') --depth;
245 valueStart = CodeDocument::Position (document, value.sourceLine, (
int) (start - line.getCharPointer()));
246 valueEnd = CodeDocument::Position (document, value.sourceLine, (
int) (end - line.getCharPointer()));
248 valueStart.setPositionMaintained (
true);
249 valueEnd.setPositionMaintained (
true);
251 wasHex = String (start, end).containsIgnoreCase (
"0x");
257class ValueListHolderComponent final :
public Component
260 ValueListHolderComponent (ValueList& l) : valueList (l)
265 void addItem (
int width, LiveValueBase& v, CodeDocument& doc)
267 addAndMakeVisible (editors.add (v.createPropertyComponent (doc)));
271 void layout (
int width)
273 setSize (width, editors.size() * itemHeight);
277 void resized()
override
279 auto r = getLocalBounds().reduced (2, 0);
281 for (
int i = 0; i < editors.size(); ++i)
282 editors.getUnchecked (i)->setBounds (r.removeFromTop (itemHeight));
285 enum { itemHeight = 120 };
287 ValueList& valueList;
288 OwnedArray<LivePropertyEditorBase> editors;
292class ValueList::EditorWindow final :
public DocumentWindow,
293 private DeletedAtShutdown
296 EditorWindow (ValueList& list)
297 : DocumentWindow (
"Live Values", Colours::lightgrey, DocumentWindow::closeButton)
299 setLookAndFeel (&lookAndFeel);
300 setUsingNativeTitleBar (
true);
302 viewport.setViewedComponent (
new ValueListHolderComponent (list),
true);
303 viewport.setSize (700, 600);
304 viewport.setScrollBarsShown (
true,
false);
306 setContentNonOwned (&viewport,
true);
307 setResizable (
true,
false);
308 setResizeLimits (500, 400, 10000, 10000);
309 centreWithSize (getWidth(), getHeight());
313 ~EditorWindow()
override
315 setLookAndFeel (
nullptr);
318 void closeButtonPressed()
override
323 void updateItems (ValueList& list)
325 if (
auto* l =
dynamic_cast<ValueListHolderComponent*
> (viewport.getViewedComponent()))
327 while (l->getNumChildComponents() < list.values.size())
329 if (
auto* v = list.values [l->getNumChildComponents()])
330 l->addItem (viewport.getMaximumVisibleWidth(), *v, list.getDocument (v->sourceFile));
339 void resized()
override
341 DocumentWindow::resized();
343 if (
auto* l =
dynamic_cast<ValueListHolderComponent*
> (viewport.getViewedComponent()))
344 l->layout (viewport.getMaximumVisibleWidth());
348 LookAndFeel_V3 lookAndFeel;
352ValueList::ValueList() {}
353ValueList::~ValueList() { clearSingletonInstance(); }
355void ValueList::addValue (LiveValueBase* v)
358 triggerAsyncUpdate();
361void ValueList::handleAsyncUpdate()
363 if (editorWindow ==
nullptr)
364 editorWindow =
new EditorWindow (*
this);
366 editorWindow->updateItems (*
this);
369CodeDocument& ValueList::getDocument (
const File& file)
371 const int index = documentFiles.indexOf (file.getFullPathName());
374 return *documents.getUnchecked (index);
376 auto* doc = documents.add (
new CodeDocument());
377 documentFiles.add (file);
378 doc->replaceAllContent (file.loadFileAsString());
379 doc->clearUndoHistory();
384struct ColourEditorComp final :
public Component,
385 private ChangeListener
387 ColourEditorComp (LivePropertyEditorBase& e) : editor (e)
389 setMouseCursor (MouseCursor::PointingHandCursor);
392 Colour getColour()
const
394 return Colour ((uint32) parseInt (editor.value.getStringValue (
false)));
397 void paint (Graphics& g)
override
399 g.fillCheckerBoard (getLocalBounds().toFloat(), 6.0f, 6.0f,
400 Colour (0xffdddddd).overlaidWith (getColour()),
401 Colour (0xffffffff).overlaidWith (getColour()));
404 void mouseDown (
const MouseEvent&)
override
406 auto colourSelector = std::make_unique<ColourSelector>();
407 colourSelector->setName (
"Colour");
408 colourSelector->setCurrentColour (getColour());
409 colourSelector->addChangeListener (
this);
410 colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
411 colourSelector->setSize (300, 400);
413 CallOutBox::launchAsynchronously (std::move (colourSelector), getScreenBounds(),
nullptr);
416 void changeListenerCallback (ChangeBroadcaster* source)
override
418 if (
auto* cs =
dynamic_cast<ColourSelector*
> (source))
419 editor.applyNewValue (getAsString (cs->getCurrentColour(),
true));
424 LivePropertyEditorBase& editor;
427Component* createColourEditor (LivePropertyEditorBase& editor)
429 return new ColourEditorComp (editor);
433struct SliderComp :
public Component
435 SliderComp (LivePropertyEditorBase& e,
bool useFloat)
436 : editor (e), isFloat (useFloat)
438 slider.setTextBoxStyle (Slider::NoTextBox,
true, 0, 0);
439 addAndMakeVisible (slider);
441 slider.onDragEnd = [
this] { updateRange(); };
442 slider.onValueChange = [
this]
444 editor.applyNewValue (isFloat ? getAsString ((
double) slider.getValue(), editor.wasHex)
445 : getAsString ((int64) slider.getValue(), editor.wasHex));
449 virtual void updateRange()
451 double v = isFloat ? parseDouble (editor.value.getStringValue (
false))
452 : (
double) parseInt (editor.value.getStringValue (false));
454 double range = isFloat ? 10 : 100;
456 slider.setRange (v - range, v + range);
457 slider.setValue (v, dontSendNotification);
460 void resized()
override
462 slider.setBounds (getLocalBounds().removeFromTop (25));
465 LivePropertyEditorBase& editor;
471struct BoolSliderComp final :
public SliderComp
473 BoolSliderComp (LivePropertyEditorBase& e)
474 : SliderComp (e, false)
476 slider.onValueChange = [
this] { editor.applyNewValue (slider.getValue() > 0.5 ?
"true" :
"false"); };
479 void updateRange()
override
481 slider.setRange (0.0, 1.0, dontSendNotification);
482 slider.setValue (editor.value.getStringValue (
false) ==
"true", dontSendNotification);
486Component* createIntegerSlider (LivePropertyEditorBase& editor) {
return new SliderComp (editor,
false); }
487Component* createFloatSlider (LivePropertyEditorBase& editor) {
return new SliderComp (editor,
true); }
488Component* createBoolSlider (LivePropertyEditorBase& editor) {
return new BoolSliderComp (editor); }
static String toHexString(IntegerType number)
Returns a string representing this numeric value in hexadecimal.
#define JUCE_IMPLEMENT_SINGLETON(Classname)
This is a counterpart to the JUCE_DECLARE_SINGLETON macros.
#define JUCE_DECLARE_SINGLETON(Classname, doNotRecreateAfterDeletion)
Macro to generate the appropriate methods and boilerplate for a singleton class.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
long long int64
A platform-independent 64-bit integer type.