mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-11 23:54:18 +00:00
Fixed a very very subtle component bug. Added peak level detection to AudioThumbnail. Tidied up some old demo plugin UI code.
This commit is contained in:
parent
4d6b2daa93
commit
098abe4a2f
11 changed files with 199 additions and 132 deletions
|
|
@ -13,32 +13,38 @@
|
|||
|
||||
//==============================================================================
|
||||
JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemoPluginAudioProcessor* ownerFilter)
|
||||
: AudioProcessorEditor (ownerFilter)
|
||||
: AudioProcessorEditor (ownerFilter),
|
||||
midiKeyboard (ownerFilter->keyboardState, MidiKeyboardComponent::horizontalKeyboard),
|
||||
infoLabel (String::empty),
|
||||
gainLabel ("", "Throughput level:"),
|
||||
delayLabel ("", "Delay:"),
|
||||
gainSlider ("gain"),
|
||||
delaySlider ("delay")
|
||||
{
|
||||
addAndMakeVisible (gainSlider = new Slider ("gain"));
|
||||
gainSlider->setSliderStyle (Slider::Rotary);
|
||||
gainSlider->addListener (this);
|
||||
gainSlider->setRange (0.0, 1.0, 0.01);
|
||||
Label* l = new Label ("", "Throughput level:");
|
||||
l->attachToComponent (gainSlider, false);
|
||||
l->setFont (Font (11.0f));
|
||||
// add some sliders..
|
||||
addAndMakeVisible (&gainSlider);
|
||||
gainSlider.setSliderStyle (Slider::Rotary);
|
||||
gainSlider.addListener (this);
|
||||
gainSlider.setRange (0.0, 1.0, 0.01);
|
||||
|
||||
addAndMakeVisible (delaySlider = new Slider ("delay"));
|
||||
delaySlider->setSliderStyle (Slider::Rotary);
|
||||
delaySlider->addListener (this);
|
||||
delaySlider->setRange (0.0, 1.0, 0.01);
|
||||
l = new Label ("", "Delay:");
|
||||
l->attachToComponent (delaySlider, false);
|
||||
l->setFont (Font (11.0f));
|
||||
addAndMakeVisible (&delaySlider);
|
||||
delaySlider.setSliderStyle (Slider::Rotary);
|
||||
delaySlider.addListener (this);
|
||||
delaySlider.setRange (0.0, 1.0, 0.01);
|
||||
|
||||
// create and add the midi keyboard component..
|
||||
addAndMakeVisible (midiKeyboard
|
||||
= new MidiKeyboardComponent (ownerFilter->keyboardState,
|
||||
MidiKeyboardComponent::horizontalKeyboard));
|
||||
// add some labels for the sliders..
|
||||
gainLabel.attachToComponent (&gainSlider, false);
|
||||
gainLabel.setFont (Font (11.0f));
|
||||
|
||||
delayLabel.attachToComponent (&delaySlider, false);
|
||||
delayLabel.setFont (Font (11.0f));
|
||||
|
||||
// add the midi keyboard component..
|
||||
addAndMakeVisible (&midiKeyboard);
|
||||
|
||||
// add a label that will display the current timecode and status..
|
||||
addAndMakeVisible (infoLabel = new Label (String::empty, String::empty));
|
||||
infoLabel->setColour (Label::textColourId, Colours::blue);
|
||||
addAndMakeVisible (&infoLabel);
|
||||
infoLabel.setColour (Label::textColourId, Colours::blue);
|
||||
|
||||
// add the triangular resizer component for the bottom-right of the UI
|
||||
addAndMakeVisible (resizer = new ResizableCornerComponent (this, &resizeLimits));
|
||||
|
|
@ -53,7 +59,6 @@ JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemo
|
|||
|
||||
JuceDemoPluginAudioProcessorEditor::~JuceDemoPluginAudioProcessorEditor()
|
||||
{
|
||||
deleteAllChildren();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -65,12 +70,12 @@ void JuceDemoPluginAudioProcessorEditor::paint (Graphics& g)
|
|||
|
||||
void JuceDemoPluginAudioProcessorEditor::resized()
|
||||
{
|
||||
infoLabel->setBounds (10, 4, 400, 25);
|
||||
gainSlider->setBounds (20, 60, 150, 40);
|
||||
delaySlider->setBounds (200, 60, 150, 40);
|
||||
infoLabel.setBounds (10, 4, 400, 25);
|
||||
gainSlider.setBounds (20, 60, 150, 40);
|
||||
delaySlider.setBounds (200, 60, 150, 40);
|
||||
|
||||
const int keyboardHeight = 70;
|
||||
midiKeyboard->setBounds (4, getHeight() - keyboardHeight - 4, getWidth() - 8, keyboardHeight);
|
||||
midiKeyboard.setBounds (4, getHeight() - keyboardHeight - 4, getWidth() - 8, keyboardHeight);
|
||||
|
||||
resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16);
|
||||
|
||||
|
|
@ -89,25 +94,25 @@ void JuceDemoPluginAudioProcessorEditor::timerCallback()
|
|||
if (lastDisplayedPosition != newPos)
|
||||
displayPositionInfo (newPos);
|
||||
|
||||
gainSlider->setValue (ourProcessor->gain, false);
|
||||
delaySlider->setValue (ourProcessor->delay, false);
|
||||
gainSlider.setValue (ourProcessor->gain, false);
|
||||
delaySlider.setValue (ourProcessor->delay, false);
|
||||
}
|
||||
|
||||
// This is our Slider::Listener callback, when the user drags a slider.
|
||||
void JuceDemoPluginAudioProcessorEditor::sliderValueChanged (Slider* slider)
|
||||
{
|
||||
if (slider == gainSlider)
|
||||
if (slider == &gainSlider)
|
||||
{
|
||||
// It's vital to use setParameterNotifyingHost to change any parameters that are automatable
|
||||
// by the host, rather than just modifying them directly, otherwise the host won't know
|
||||
// that they've changed.
|
||||
getProcessor()->setParameterNotifyingHost (JuceDemoPluginAudioProcessor::gainParam,
|
||||
(float) gainSlider->getValue());
|
||||
(float) gainSlider.getValue());
|
||||
}
|
||||
else if (slider == delaySlider)
|
||||
else if (slider == &delaySlider)
|
||||
{
|
||||
getProcessor()->setParameterNotifyingHost (JuceDemoPluginAudioProcessor::delayParam,
|
||||
(float) delaySlider->getValue());
|
||||
(float) delaySlider.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,5 +174,5 @@ void JuceDemoPluginAudioProcessorEditor::displayPositionInfo (const AudioPlayHea
|
|||
else if (pos.isPlaying)
|
||||
displayText << " (playing)";
|
||||
|
||||
infoLabel->setText (displayText, false);
|
||||
infoLabel.setText (displayText, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,11 +34,11 @@ public:
|
|||
void sliderValueChanged (Slider*);
|
||||
|
||||
private:
|
||||
MidiKeyboardComponent* midiKeyboard;
|
||||
Label* infoLabel;
|
||||
Slider* gainSlider;
|
||||
Slider* delaySlider;
|
||||
ResizableCornerComponent* resizer;
|
||||
MidiKeyboardComponent midiKeyboard;
|
||||
Label infoLabel, gainLabel, delayLabel;
|
||||
Slider gainSlider;
|
||||
Slider delaySlider;
|
||||
ScopedPointer<ResizableCornerComponent> resizer;
|
||||
ComponentBoundsConstrainer resizeLimits;
|
||||
|
||||
AudioPlayHead::CurrentPositionInfo lastDisplayedPosition;
|
||||
|
|
|
|||
|
|
@ -21988,6 +21988,12 @@ struct AudioThumbnail::MinMaxValue
|
|||
return maxValue > minValue;
|
||||
}
|
||||
|
||||
inline int getPeak() const throw()
|
||||
{
|
||||
return jmax (std::abs ((int) minValue),
|
||||
std::abs ((int) maxValue));
|
||||
}
|
||||
|
||||
inline void read (InputStream& input)
|
||||
{
|
||||
minValue = input.readByte();
|
||||
|
|
@ -22186,6 +22192,7 @@ class AudioThumbnail::ThumbData
|
|||
{
|
||||
public:
|
||||
ThumbData (const int numThumbSamples)
|
||||
: peakLevel (-1)
|
||||
{
|
||||
ensureSize (numThumbSamples);
|
||||
}
|
||||
|
|
@ -22232,6 +22239,8 @@ public:
|
|||
|
||||
void write (const MinMaxValue* const source, const int startIndex, const int numValues)
|
||||
{
|
||||
resetPeak();
|
||||
|
||||
if (startIndex + numValues > data.size())
|
||||
ensureSize (startIndex + numValues);
|
||||
|
||||
|
|
@ -22241,8 +22250,29 @@ public:
|
|||
dest[i] = source[i];
|
||||
}
|
||||
|
||||
void resetPeak()
|
||||
{
|
||||
peakLevel = -1;
|
||||
}
|
||||
|
||||
int getPeak()
|
||||
{
|
||||
if (peakLevel < 0)
|
||||
{
|
||||
for (int i = 0; i < data.size(); ++i)
|
||||
{
|
||||
const int peak = data[i].getPeak();
|
||||
if (peak > peakLevel)
|
||||
peakLevel = peak;
|
||||
}
|
||||
}
|
||||
|
||||
return peakLevel;
|
||||
}
|
||||
|
||||
private:
|
||||
Array <MinMaxValue> data;
|
||||
int peakLevel;
|
||||
|
||||
void ensureSize (const int thumbSamples)
|
||||
{
|
||||
|
|
@ -22619,6 +22649,16 @@ bool AudioThumbnail::isFullyLoaded() const throw()
|
|||
return numSamplesFinished >= totalSamples - samplesPerThumbSample;
|
||||
}
|
||||
|
||||
float AudioThumbnail::getApproximatePeak() const
|
||||
{
|
||||
int peak = 0;
|
||||
|
||||
for (int i = channels.size(); --i >= 0;)
|
||||
peak = jmax (peak, channels.getUnchecked(i)->getPeak());
|
||||
|
||||
return jlimit (0, 127, peak) / 127.0f;
|
||||
}
|
||||
|
||||
void AudioThumbnail::drawChannel (Graphics& g, const Rectangle<int>& area, double startTime,
|
||||
double endTime, int channelNum, float verticalZoomFactor)
|
||||
{
|
||||
|
|
@ -36682,10 +36722,10 @@ public:
|
|||
: graph (graph_),
|
||||
orderedNodes (orderedNodes_)
|
||||
{
|
||||
nodeIds.add (zeroNodeID); // first buffer is read-only zeros
|
||||
nodeIds.add ((uint32) zeroNodeID); // first buffer is read-only zeros
|
||||
channels.add (0);
|
||||
|
||||
midiNodeIds.add (zeroNodeID);
|
||||
midiNodeIds.add ((uint32) zeroNodeID);
|
||||
|
||||
for (int i = 0; i < orderedNodes.size(); ++i)
|
||||
{
|
||||
|
|
@ -36969,7 +37009,7 @@ private:
|
|||
if (midiNodeIds.getUnchecked(i) == freeNodeID)
|
||||
return i;
|
||||
|
||||
midiNodeIds.add (freeNodeID);
|
||||
midiNodeIds.add ((uint32) freeNodeID);
|
||||
return midiNodeIds.size() - 1;
|
||||
}
|
||||
else
|
||||
|
|
@ -36978,7 +37018,7 @@ private:
|
|||
if (nodeIds.getUnchecked(i) == freeNodeID)
|
||||
return i;
|
||||
|
||||
nodeIds.add (freeNodeID);
|
||||
nodeIds.add ((uint32) freeNodeID);
|
||||
channels.add (0);
|
||||
return nodeIds.size() - 1;
|
||||
}
|
||||
|
|
@ -37018,7 +37058,7 @@ private:
|
|||
nodeIds.getUnchecked(i),
|
||||
channels.getUnchecked(i)))
|
||||
{
|
||||
nodeIds.set (i, freeNodeID);
|
||||
nodeIds.set (i, (uint32) freeNodeID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -37029,7 +37069,7 @@ private:
|
|||
midiNodeIds.getUnchecked(i),
|
||||
AudioProcessorGraph::midiChannelIndex))
|
||||
{
|
||||
midiNodeIds.set (i, freeNodeID);
|
||||
midiNodeIds.set (i, (uint32) freeNodeID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40198,8 +40238,7 @@ void Component::setVisible (bool shouldBeVisible)
|
|||
|
||||
if (! shouldBeVisible)
|
||||
{
|
||||
if (currentlyFocusedComponent == this
|
||||
|| isParentOf (currentlyFocusedComponent))
|
||||
if (currentlyFocusedComponent == this || isParentOf (currentlyFocusedComponent))
|
||||
{
|
||||
if (parentComponent_ != 0)
|
||||
parentComponent_->grabKeyboardFocus();
|
||||
|
|
@ -41074,32 +41113,18 @@ Component* Component::removeChildComponent (const int index)
|
|||
childComponentList_.remove (index);
|
||||
child->parentComponent_ = 0;
|
||||
|
||||
if (childShowing)
|
||||
// (NB: there are obscure situations where a childShowing = false, but it still has the focus)
|
||||
if (currentlyFocusedComponent == child || child->isParentOf (currentlyFocusedComponent))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
if ((currentlyFocusedComponent == child)
|
||||
|| child->isParentOf (currentlyFocusedComponent))
|
||||
{
|
||||
// get rid first to force the grabKeyboardFocus to change to us.
|
||||
giveAwayFocus();
|
||||
grabKeyboardFocus();
|
||||
}
|
||||
}
|
||||
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
currentlyFocusedComponent = 0;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
currentlyFocusedComponent = 0;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__);
|
||||
}
|
||||
#endif
|
||||
SafePointer<Component> thisPointer (this);
|
||||
|
||||
giveAwayFocus();
|
||||
|
||||
if (thisPointer == 0)
|
||||
return child;
|
||||
|
||||
if (childShowing)
|
||||
grabKeyboardFocus();
|
||||
}
|
||||
|
||||
child->internalHierarchyChanged();
|
||||
|
|
@ -42545,14 +42570,13 @@ Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() throw()
|
|||
|
||||
void Component::giveAwayFocus()
|
||||
{
|
||||
// use a copy so we can clear the value before the call
|
||||
SafePointer<Component> componentLosingFocus (currentlyFocusedComponent);
|
||||
|
||||
Component* const componentLosingFocus = currentlyFocusedComponent;
|
||||
currentlyFocusedComponent = 0;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
|
||||
if (componentLosingFocus != 0)
|
||||
componentLosingFocus->internalFocusLoss (focusChangedDirectly);
|
||||
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
}
|
||||
|
||||
bool Component::isMouseOver (const bool includeChildren) const
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 52
|
||||
#define JUCE_BUILDNUMBER 105
|
||||
#define JUCE_BUILDNUMBER 106
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
@ -28178,9 +28178,9 @@ private:
|
|||
bool isDisabledFlag : 1;
|
||||
bool childCompFocusedFlag : 1;
|
||||
bool dontClipGraphicsFlag : 1;
|
||||
#if JUCE_DEBUG
|
||||
#if JUCE_DEBUG
|
||||
bool isInsidePaintCall : 1;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
union
|
||||
|
|
@ -29126,18 +29126,19 @@ private:
|
|||
class InternalTimerThread;
|
||||
|
||||
/**
|
||||
Repeatedly calls a user-defined method at a specified time interval.
|
||||
Makes repeated callbacks to a virtual method at a specified time interval.
|
||||
|
||||
A Timer's timerCallback() method will be repeatedly called at a given
|
||||
interval. Initially when a Timer object is created, they will do nothing
|
||||
until the startTimer() method is called, then the message thread will
|
||||
start calling it back until stopTimer() is called.
|
||||
interval. When you create a Timer object, it will do nothing until the
|
||||
startTimer() method is called, which will cause the message thread to
|
||||
start making callbacks at the specified interval, until stopTimer() is called
|
||||
or the object is deleted.
|
||||
|
||||
The time interval isn't guaranteed to be precise to any more than maybe
|
||||
10-20ms, and the intervals may end up being much longer than requested if the
|
||||
system is busy. Because it's the message thread that is doing the callbacks,
|
||||
any messages that take a significant amount of time to process will block
|
||||
all the timers for that period.
|
||||
system is busy. Because the callbacks are made by the main message thread,
|
||||
anything that blocks the message queue for a period of time will also prevent
|
||||
any timers from running until it can carry on.
|
||||
|
||||
If you need to have a single callback that is shared by multiple timers with
|
||||
different frequencies, then the MultiTimer class allows you to do that - its
|
||||
|
|
@ -32741,6 +32742,12 @@ public:
|
|||
/** Returns true if the low res preview is fully generated. */
|
||||
bool isFullyLoaded() const throw();
|
||||
|
||||
/** Returns the highest level in the thumbnail.
|
||||
Note that because the thumb only stores low-resolution data, this isn't
|
||||
an accurate representation of the highest value, it's only a rough approximation.
|
||||
*/
|
||||
float getApproximatePeak() const;
|
||||
|
||||
/** Returns the hash code that was set by setSource() or setReader(). */
|
||||
int64 getHashCode() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,12 @@ struct AudioThumbnail::MinMaxValue
|
|||
return maxValue > minValue;
|
||||
}
|
||||
|
||||
inline int getPeak() const throw()
|
||||
{
|
||||
return jmax (std::abs ((int) minValue),
|
||||
std::abs ((int) maxValue));
|
||||
}
|
||||
|
||||
inline void read (InputStream& input)
|
||||
{
|
||||
minValue = input.readByte();
|
||||
|
|
@ -262,6 +268,7 @@ class AudioThumbnail::ThumbData
|
|||
{
|
||||
public:
|
||||
ThumbData (const int numThumbSamples)
|
||||
: peakLevel (-1)
|
||||
{
|
||||
ensureSize (numThumbSamples);
|
||||
}
|
||||
|
|
@ -308,6 +315,8 @@ public:
|
|||
|
||||
void write (const MinMaxValue* const source, const int startIndex, const int numValues)
|
||||
{
|
||||
resetPeak();
|
||||
|
||||
if (startIndex + numValues > data.size())
|
||||
ensureSize (startIndex + numValues);
|
||||
|
||||
|
|
@ -317,8 +326,29 @@ public:
|
|||
dest[i] = source[i];
|
||||
}
|
||||
|
||||
void resetPeak()
|
||||
{
|
||||
peakLevel = -1;
|
||||
}
|
||||
|
||||
int getPeak()
|
||||
{
|
||||
if (peakLevel < 0)
|
||||
{
|
||||
for (int i = 0; i < data.size(); ++i)
|
||||
{
|
||||
const int peak = data[i].getPeak();
|
||||
if (peak > peakLevel)
|
||||
peakLevel = peak;
|
||||
}
|
||||
}
|
||||
|
||||
return peakLevel;
|
||||
}
|
||||
|
||||
private:
|
||||
Array <MinMaxValue> data;
|
||||
int peakLevel;
|
||||
|
||||
void ensureSize (const int thumbSamples)
|
||||
{
|
||||
|
|
@ -700,6 +730,16 @@ bool AudioThumbnail::isFullyLoaded() const throw()
|
|||
return numSamplesFinished >= totalSamples - samplesPerThumbSample;
|
||||
}
|
||||
|
||||
float AudioThumbnail::getApproximatePeak() const
|
||||
{
|
||||
int peak = 0;
|
||||
|
||||
for (int i = channels.size(); --i >= 0;)
|
||||
peak = jmax (peak, channels.getUnchecked(i)->getPeak());
|
||||
|
||||
return jlimit (0, 127, peak) / 127.0f;
|
||||
}
|
||||
|
||||
void AudioThumbnail::drawChannel (Graphics& g, const Rectangle<int>& area, double startTime,
|
||||
double endTime, int channelNum, float verticalZoomFactor)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -177,6 +177,12 @@ public:
|
|||
/** Returns true if the low res preview is fully generated. */
|
||||
bool isFullyLoaded() const throw();
|
||||
|
||||
/** Returns the highest level in the thumbnail.
|
||||
Note that because the thumb only stores low-resolution data, this isn't
|
||||
an accurate representation of the highest value, it's only a rough approximation.
|
||||
*/
|
||||
float getApproximatePeak() const;
|
||||
|
||||
/** Returns the hash code that was set by setSource() or setReader(). */
|
||||
int64 getHashCode() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -546,10 +546,10 @@ public:
|
|||
: graph (graph_),
|
||||
orderedNodes (orderedNodes_)
|
||||
{
|
||||
nodeIds.add (zeroNodeID); // first buffer is read-only zeros
|
||||
nodeIds.add ((uint32) zeroNodeID); // first buffer is read-only zeros
|
||||
channels.add (0);
|
||||
|
||||
midiNodeIds.add (zeroNodeID);
|
||||
midiNodeIds.add ((uint32) zeroNodeID);
|
||||
|
||||
for (int i = 0; i < orderedNodes.size(); ++i)
|
||||
{
|
||||
|
|
@ -835,7 +835,7 @@ private:
|
|||
if (midiNodeIds.getUnchecked(i) == freeNodeID)
|
||||
return i;
|
||||
|
||||
midiNodeIds.add (freeNodeID);
|
||||
midiNodeIds.add ((uint32) freeNodeID);
|
||||
return midiNodeIds.size() - 1;
|
||||
}
|
||||
else
|
||||
|
|
@ -844,7 +844,7 @@ private:
|
|||
if (nodeIds.getUnchecked(i) == freeNodeID)
|
||||
return i;
|
||||
|
||||
nodeIds.add (freeNodeID);
|
||||
nodeIds.add ((uint32) freeNodeID);
|
||||
channels.add (0);
|
||||
return nodeIds.size() - 1;
|
||||
}
|
||||
|
|
@ -884,7 +884,7 @@ private:
|
|||
nodeIds.getUnchecked(i),
|
||||
channels.getUnchecked(i)))
|
||||
{
|
||||
nodeIds.set (i, freeNodeID);
|
||||
nodeIds.set (i, (uint32) freeNodeID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -895,7 +895,7 @@ private:
|
|||
midiNodeIds.getUnchecked(i),
|
||||
AudioProcessorGraph::midiChannelIndex))
|
||||
{
|
||||
midiNodeIds.set (i, freeNodeID);
|
||||
midiNodeIds.set (i, (uint32) freeNodeID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
*/
|
||||
#define JUCE_MAJOR_VERSION 1
|
||||
#define JUCE_MINOR_VERSION 52
|
||||
#define JUCE_BUILDNUMBER 105
|
||||
#define JUCE_BUILDNUMBER 106
|
||||
|
||||
/** Current Juce version number.
|
||||
|
||||
|
|
|
|||
|
|
@ -31,18 +31,19 @@ class InternalTimerThread;
|
|||
|
||||
//==============================================================================
|
||||
/**
|
||||
Repeatedly calls a user-defined method at a specified time interval.
|
||||
Makes repeated callbacks to a virtual method at a specified time interval.
|
||||
|
||||
A Timer's timerCallback() method will be repeatedly called at a given
|
||||
interval. Initially when a Timer object is created, they will do nothing
|
||||
until the startTimer() method is called, then the message thread will
|
||||
start calling it back until stopTimer() is called.
|
||||
interval. When you create a Timer object, it will do nothing until the
|
||||
startTimer() method is called, which will cause the message thread to
|
||||
start making callbacks at the specified interval, until stopTimer() is called
|
||||
or the object is deleted.
|
||||
|
||||
The time interval isn't guaranteed to be precise to any more than maybe
|
||||
10-20ms, and the intervals may end up being much longer than requested if the
|
||||
system is busy. Because it's the message thread that is doing the callbacks,
|
||||
any messages that take a significant amount of time to process will block
|
||||
all the timers for that period.
|
||||
system is busy. Because the callbacks are made by the main message thread,
|
||||
anything that blocks the message queue for a period of time will also prevent
|
||||
any timers from running until it can carry on.
|
||||
|
||||
If you need to have a single callback that is shared by multiple timers with
|
||||
different frequencies, then the MultiTimer class allows you to do that - its
|
||||
|
|
|
|||
|
|
@ -470,8 +470,7 @@ void Component::setVisible (bool shouldBeVisible)
|
|||
|
||||
if (! shouldBeVisible)
|
||||
{
|
||||
if (currentlyFocusedComponent == this
|
||||
|| isParentOf (currentlyFocusedComponent))
|
||||
if (currentlyFocusedComponent == this || isParentOf (currentlyFocusedComponent))
|
||||
{
|
||||
if (parentComponent_ != 0)
|
||||
parentComponent_->grabKeyboardFocus();
|
||||
|
|
@ -1358,32 +1357,18 @@ Component* Component::removeChildComponent (const int index)
|
|||
childComponentList_.remove (index);
|
||||
child->parentComponent_ = 0;
|
||||
|
||||
if (childShowing)
|
||||
// (NB: there are obscure situations where a childShowing = false, but it still has the focus)
|
||||
if (currentlyFocusedComponent == child || child->isParentOf (currentlyFocusedComponent))
|
||||
{
|
||||
JUCE_TRY
|
||||
{
|
||||
if ((currentlyFocusedComponent == child)
|
||||
|| child->isParentOf (currentlyFocusedComponent))
|
||||
{
|
||||
// get rid first to force the grabKeyboardFocus to change to us.
|
||||
giveAwayFocus();
|
||||
grabKeyboardFocus();
|
||||
}
|
||||
}
|
||||
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
currentlyFocusedComponent = 0;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
currentlyFocusedComponent = 0;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__);
|
||||
}
|
||||
#endif
|
||||
SafePointer<Component> thisPointer (this);
|
||||
|
||||
giveAwayFocus();
|
||||
|
||||
if (thisPointer == 0)
|
||||
return child;
|
||||
|
||||
if (childShowing)
|
||||
grabKeyboardFocus();
|
||||
}
|
||||
|
||||
child->internalHierarchyChanged();
|
||||
|
|
@ -2854,14 +2839,13 @@ Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() throw()
|
|||
|
||||
void Component::giveAwayFocus()
|
||||
{
|
||||
// use a copy so we can clear the value before the call
|
||||
SafePointer<Component> componentLosingFocus (currentlyFocusedComponent);
|
||||
|
||||
Component* const componentLosingFocus = currentlyFocusedComponent;
|
||||
currentlyFocusedComponent = 0;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
|
||||
if (componentLosingFocus != 0)
|
||||
componentLosingFocus->internalFocusLoss (focusChangedDirectly);
|
||||
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -2171,9 +2171,9 @@ private:
|
|||
bool isDisabledFlag : 1;
|
||||
bool childCompFocusedFlag : 1;
|
||||
bool dontClipGraphicsFlag : 1;
|
||||
#if JUCE_DEBUG
|
||||
#if JUCE_DEBUG
|
||||
bool isInsidePaintCall : 1;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
union
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue