Browse Source

Update juce again

tags/1.9.8
falkTX 7 years ago
parent
commit
6c107aba5b
100 changed files with 4431 additions and 863 deletions
  1. +1
    -1
      source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h
  2. +22
    -23
      source/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h
  3. +34
    -121
      source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp
  4. +16
    -9
      source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h
  5. +5
    -0
      source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
  6. +7
    -1
      source/modules/juce_audio_devices/native/juce_ios_Audio.cpp
  7. +41
    -13
      source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
  8. +6
    -15
      source/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp
  9. +20
    -7
      source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
  10. +3
    -0
      source/modules/juce_audio_processors/format_types/juce_VST3Headers.h
  11. +64
    -0
      source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  12. +2
    -2
      source/modules/juce_audio_processors/format_types/juce_VSTCommon.h
  13. +3
    -0
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  14. +26
    -0
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.h
  15. +4
    -1
      source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp
  16. +22
    -0
      source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h
  17. +22
    -2
      source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
  18. +3
    -0
      source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
  19. +1
    -1
      source/modules/juce_audio_processors/scanning/juce_PluginListComponent.h
  20. +2
    -2
      source/modules/juce_core/containers/juce_DynamicObject.cpp
  21. +2
    -2
      source/modules/juce_core/containers/juce_DynamicObject.h
  22. +9
    -5
      source/modules/juce_core/containers/juce_HashMap.h
  23. +31
    -42
      source/modules/juce_core/containers/juce_SortedSet.h
  24. +15
    -9
      source/modules/juce_core/javascript/juce_JSON.cpp
  25. +6
    -2
      source/modules/juce_core/javascript/juce_JSON.h
  26. +1
    -1
      source/modules/juce_core/javascript/juce_Javascript.cpp
  27. +7
    -0
      source/modules/juce_core/memory/juce_HeapBlock.h
  28. +971
    -0
      source/modules/juce_core/native/java/IInAppBillingService.java
  29. +38
    -47
      source/modules/juce_core/native/java/JuceAppActivity.java
  30. +86
    -0
      source/modules/juce_core/native/juce_android_Files.cpp
  31. +125
    -28
      source/modules/juce_core/native/juce_android_JNIHelpers.h
  32. +93
    -0
      source/modules/juce_core/native/juce_android_SystemStats.cpp
  33. +5
    -0
      source/modules/juce_core/native/juce_mac_ClangBugWorkaround.h
  34. +5
    -0
      source/modules/juce_core/native/juce_mac_Files.mm
  35. +2
    -0
      source/modules/juce_core/native/juce_mac_Network.mm
  36. +10
    -0
      source/modules/juce_core/native/juce_osx_ObjCHelpers.h
  37. +8
    -14
      source/modules/juce_core/native/juce_posix_SharedCode.h
  38. +13
    -1
      source/modules/juce_core/native/juce_win32_SystemStats.cpp
  39. +18
    -0
      source/modules/juce_core/system/juce_CompilerSupport.h
  40. +5
    -5
      source/modules/juce_core/text/juce_String.cpp
  41. +16
    -8
      source/modules/juce_core/text/juce_String.h
  42. +31
    -2
      source/modules/juce_core/threads/juce_Thread.cpp
  43. +15
    -9
      source/modules/juce_core/threads/juce_Thread.h
  44. +79
    -44
      source/modules/juce_core/threads/juce_ThreadPool.cpp
  45. +24
    -11
      source/modules/juce_core/threads/juce_ThreadPool.h
  46. +3
    -1
      source/modules/juce_core/time/juce_PerformanceCounter.h
  47. +9
    -6
      source/modules/juce_events/broadcasters/juce_AsyncUpdater.h
  48. +80
    -16
      source/modules/juce_events/native/juce_android_Messaging.cpp
  49. +19
    -29
      source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp
  50. +6
    -12
      source/modules/juce_graphics/contexts/juce_GraphicsContext.h
  51. +19
    -20
      source/modules/juce_graphics/geometry/juce_Path.cpp
  52. +18
    -18
      source/modules/juce_graphics/geometry/juce_Path.h
  53. +30
    -30
      source/modules/juce_graphics/geometry/juce_Point.h
  54. +6
    -3
      source/modules/juce_graphics/images/juce_Image.cpp
  55. +9
    -10
      source/modules/juce_graphics/native/juce_RenderingHelpers.h
  56. +5
    -12
      source/modules/juce_gui_basics/buttons/juce_Button.cpp
  57. +1
    -1
      source/modules/juce_gui_basics/buttons/juce_Button.h
  58. +6
    -9
      source/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp
  59. +5
    -2
      source/modules/juce_gui_basics/buttons/juce_DrawableButton.h
  60. +50
    -54
      source/modules/juce_gui_basics/components/juce_Component.cpp
  61. +2
    -2
      source/modules/juce_gui_basics/components/juce_Component.h
  62. +5
    -5
      source/modules/juce_gui_basics/components/juce_Desktop.h
  63. +1
    -34
      source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp
  64. +3
    -3
      source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h
  65. +1
    -1
      source/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h
  66. +1
    -1
      source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h
  67. +2
    -2
      source/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h
  68. +8
    -0
      source/modules/juce_gui_basics/juce_gui_basics.cpp
  69. +9
    -0
      source/modules/juce_gui_basics/juce_gui_basics.h
  70. +1025
    -0
      source/modules/juce_gui_basics/layout/juce_Grid.cpp
  71. +174
    -0
      source/modules/juce_gui_basics/layout/juce_Grid.h
  72. +182
    -0
      source/modules/juce_gui_basics/layout/juce_GridItem.cpp
  73. +223
    -0
      source/modules/juce_gui_basics/layout/juce_GridItem.h
  74. +257
    -0
      source/modules/juce_gui_basics/layout/juce_GridUnitTests.cpp
  75. +1
    -1
      source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp
  76. +1
    -0
      source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h
  77. +35
    -18
      source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp
  78. +6
    -3
      source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h
  79. +6
    -3
      source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp
  80. +2
    -4
      source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp
  81. +11
    -8
      source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp
  82. +6
    -0
      source/modules/juce_gui_basics/menus/juce_PopupMenu.h
  83. +10
    -5
      source/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp
  84. +3
    -0
      source/modules/juce_gui_basics/mouse/juce_MouseInputSource.h
  85. +68
    -9
      source/modules/juce_gui_basics/native/juce_android_Windowing.cpp
  86. +2
    -2
      source/modules/juce_gui_basics/native/juce_mac_MainMenu.mm
  87. +40
    -0
      source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp
  88. +1
    -1
      source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.h
  89. +1
    -1
      source/modules/juce_gui_basics/properties/juce_ButtonPropertyComponent.h
  90. +1
    -1
      source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp
  91. +1
    -1
      source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h
  92. +1
    -1
      source/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.h
  93. +1
    -1
      source/modules/juce_gui_basics/widgets/juce_ComboBox.cpp
  94. +2
    -2
      source/modules/juce_gui_basics/widgets/juce_ComboBox.h
  95. +1
    -1
      source/modules/juce_gui_basics/widgets/juce_Label.cpp
  96. +2
    -2
      source/modules/juce_gui_basics/widgets/juce_Label.h
  97. +122
    -96
      source/modules/juce_gui_basics/widgets/juce_Slider.cpp
  98. +8
    -2
      source/modules/juce_gui_basics/widgets/juce_Slider.h
  99. +1
    -1
      source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp
  100. +20
    -1
      source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h

+ 1
- 1
source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h View File

@@ -25,7 +25,7 @@
#if JUCE_INTEL
#define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8f || n > 1.0e-8f)) n = 0;
#else
#define JUCE_SNAP_TO_ZERO(n)
#define JUCE_SNAP_TO_ZERO(n) ignoreUnused (n)
#endif
//==============================================================================


+ 22
- 23
source/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h View File

@@ -36,6 +36,15 @@ struct CoreAudioLayouts
return AudioChannelSet::channelSetWithChannels (getCoreAudioLayoutChannels (layout));
}
/** Convert CoreAudio's native AudioChannelLayoutTag to JUCE's AudioChannelSet.
Note that this method cannot preserve the order of channels.
*/
static AudioChannelSet fromCoreAudio (AudioChannelLayoutTag layoutTag)
{
return AudioChannelSet::channelSetWithChannels (getSpeakerLayoutForCoreAudioTag (layoutTag));
}
/** Convert JUCE's AudioChannelSet to CoreAudio's AudioChannelLayoutTag.
Note that this method cannot preserve the order of channels.
@@ -67,7 +76,7 @@ struct CoreAudioLayouts
/** Convert CoreAudio's native AudioChannelLayout to an array of JUCE ChannelTypes. */
static Array<AudioChannelSet::ChannelType> getCoreAudioLayoutChannels (const AudioChannelLayout& layout)
{
switch (layout.mChannelLayoutTag)
switch (layout.mChannelLayoutTag & 0xffff0000)
{
case kAudioChannelLayoutTag_UseChannelBitmap:
return AudioChannelSet::fromWaveChannelMask (static_cast<int> (layout.mChannelBitmap)).getChannelTypes();
@@ -94,30 +103,12 @@ struct CoreAudioLayouts
return getSpeakerLayoutForCoreAudioTag (layout.mChannelLayoutTag);
}
//==============================================================================
/* Convert between a CoreAudio and JUCE channel indices - and vice versa. */
// TODO: Fabian remove this
// static int convertChannelIndex (const AudioChannelLayout& caLayout, const AudioChannelSet& juceLayout, int index, bool fromJUCE)
// {
// auto coreAudioChannels = getCoreAudioLayoutChannels (caLayout);
//
// jassert (juceLayout.size() == coreAudioChannels.size());
// jassert (index >= 0 && index < juceLayout.size());
//
// return (fromJUCE ? coreAudioChannels.indexOf (juceLayout.getTypeOfChannel (index))
// : juceLayout.getChannelIndexForType (coreAudioChannels.getReference (index)));
// }
private:
//==============================================================================
struct LayoutTagSpeakerList
{
AudioChannelLayoutTag tag;
AudioChannelSet::ChannelType channelTypes[16];
};
static Array<AudioChannelSet::ChannelType> getSpeakerLayoutForCoreAudioTag (AudioChannelLayoutTag tag)
{
// You need to specify the full AudioChannelLayout when using
// the UseChannelBitmap and UseChannelDescriptions layout tag
jassert (tag != kAudioChannelLayoutTag_UseChannelBitmap && tag != kAudioChannelLayoutTag_UseChannelDescriptions);
Array<AudioChannelSet::ChannelType> speakers;
for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl)
@@ -139,6 +130,14 @@ private:
return speakers;
}
private:
//==============================================================================
struct LayoutTagSpeakerList
{
AudioChannelLayoutTag tag;
AudioChannelSet::ChannelType channelTypes[16];
};
static Array<AudioChannelLayoutTag> createKnownCoreAudioTags()
{
Array<AudioChannelLayoutTag> tags;


+ 34
- 121
source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp View File

@@ -24,20 +24,8 @@ SynthesiserSound::SynthesiserSound() {}
SynthesiserSound::~SynthesiserSound() {}
//==============================================================================
SynthesiserVoice::SynthesiserVoice()
: currentSampleRate (44100.0),
currentlyPlayingNote (-1),
currentPlayingMidiChannel (0),
noteOnTime (0),
keyIsDown (false),
sustainPedalDown (false),
sostenutoPedalDown (false)
{
}
SynthesiserVoice::~SynthesiserVoice()
{
}
SynthesiserVoice::SynthesiserVoice() {}
SynthesiserVoice::~SynthesiserVoice() {}
bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const
{
@@ -83,11 +71,6 @@ void SynthesiserVoice::renderNextBlock (AudioBuffer<double>& outputBuffer,
//==============================================================================
Synthesiser::Synthesiser()
: sampleRate (0),
lastNoteOnCounter (0),
minimumSubBlockSize (32),
subBlockSubdivisionIsStrict (false),
shouldStealNotes (true)
{
for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
lastPitchWheelValues[i] = 0x2000;
@@ -159,13 +142,11 @@ void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
if (sampleRate != newRate)
{
const ScopedLock sl (lock);
allNotesOff (0, false);
sampleRate = newRate;
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->setCurrentPlaybackSampleRate (newRate);
for (auto* voice : voices)
voice->setCurrentPlaybackSampleRate (newRate);
}
}
@@ -230,25 +211,19 @@ void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
}
// explicit template instantiation
template void Synthesiser::processNextBlock<float> (AudioBuffer<float>& outputAudio,
const MidiBuffer& midiData,
int startSample,
int numSamples);
template void Synthesiser::processNextBlock<double> (AudioBuffer<double>& outputAudio,
const MidiBuffer& midiData,
int startSample,
int numSamples);
template void Synthesiser::processNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
template void Synthesiser::processNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
void Synthesiser::renderVoices (AudioBuffer<float>& buffer, int startSample, int numSamples)
{
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);
for (auto* voice : voices)
voice->renderNextBlock (buffer, startSample, numSamples);
}
void Synthesiser::renderVoices (AudioBuffer<double>& buffer, int startSample, int numSamples)
{
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);
for (auto* voice : voices)
voice->renderNextBlock (buffer, startSample, numSamples);
}
void Synthesiser::handleMidiEvent (const MidiMessage& m)
@@ -298,23 +273,15 @@ void Synthesiser::noteOn (const int midiChannel,
{
const ScopedLock sl (lock);
for (int i = sounds.size(); --i >= 0;)
for (auto* sound : sounds)
{
SynthesiserSound* const sound = sounds.getUnchecked(i);
if (sound->appliesToNote (midiNoteNumber)
&& sound->appliesToChannel (midiChannel))
if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel))
{
// If hitting a note that's still ringing, stop it first (it could be
// still playing because of the sustain or sostenuto pedal).
for (int j = voices.size(); --j >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (j);
if (voice->getCurrentlyPlayingNote() == midiNoteNumber
&& voice->isPlayingChannel (midiChannel))
for (auto* voice : voices)
if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel))
stopVoice (voice, 1.0f, true);
}
startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes),
sound, midiChannel, midiNoteNumber, velocity);
@@ -337,7 +304,7 @@ void Synthesiser::startVoice (SynthesiserVoice* const voice,
voice->currentPlayingMidiChannel = midiChannel;
voice->noteOnTime = ++lastNoteOnCounter;
voice->currentlyPlayingSound = sound;
voice->keyIsDown = true;
voice->setKeyDown (true);
voice->sostenutoPedalDown = false;
voice->sustainPedalDown = sustainPedalsDown[midiChannel];
@@ -363,10 +330,8 @@ void Synthesiser::noteOff (const int midiChannel,
{
const ScopedLock sl (lock);
for (int i = voices.size(); --i >= 0;)
for (auto* voice : voices)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->getCurrentlyPlayingNote() == midiNoteNumber
&& voice->isPlayingChannel (midiChannel))
{
@@ -377,7 +342,7 @@ void Synthesiser::noteOff (const int midiChannel,
{
jassert (! voice->keyIsDown || voice->sustainPedalDown == sustainPedalsDown [midiChannel]);
voice->keyIsDown = false;
voice->setKeyDown (false);
if (! (voice->sustainPedalDown || voice->sostenutoPedalDown))
stopVoice (voice, velocity, allowTailOff);
@@ -391,13 +356,9 @@ void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff)
{
const ScopedLock sl (lock);
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
for (auto* voice : voices)
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
voice->stopNote (1.0f, allowTailOff);
}
sustainPedalsDown.clear();
}
@@ -406,13 +367,9 @@ void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue)
{
const ScopedLock sl (lock);
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
for (auto* voice : voices)
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
voice->pitchWheelMoved (wheelValue);
}
}
void Synthesiser::handleController (const int midiChannel,
@@ -429,40 +386,28 @@ void Synthesiser::handleController (const int midiChannel,
const ScopedLock sl (lock);
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
for (auto* voice : voices)
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
voice->controllerMoved (controllerNumber, controllerValue);
}
}
void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue)
{
const ScopedLock sl (lock);
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
for (auto* voice : voices)
if (voice->getCurrentlyPlayingNote() == midiNoteNumber
&& (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)))
voice->aftertouchChanged (aftertouchValue);
}
}
void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue)
{
const ScopedLock sl (lock);
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
for (auto* voice : voices)
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
voice->channelPressureChanged (channelPressureValue);
}
}
void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
@@ -474,25 +419,19 @@ void Synthesiser::handleSustainPedal (int midiChannel, bool isDown)
{
sustainPedalsDown.setBit (midiChannel);
for (int i = voices.size(); --i >= 0;)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
for (auto* voice : voices)
if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown())
voice->sustainPedalDown = true;
}
}
else
{
for (int i = voices.size(); --i >= 0;)
for (auto* voice : voices)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->isPlayingChannel (midiChannel))
{
voice->sustainPedalDown = false;
if (! voice->isKeyDown())
if (! (voice->isKeyDown() || voice->isSostenutoPedalDown()))
stopVoice (voice, 1.0f, true);
}
}
@@ -506,10 +445,8 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
jassert (midiChannel > 0 && midiChannel <= 16);
const ScopedLock sl (lock);
for (int i = voices.size(); --i >= 0;)
for (auto* voice : voices)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->isPlayingChannel (midiChannel))
{
if (isDown)
@@ -539,13 +476,9 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay,
{
const ScopedLock sl (lock);
for (int i = 0; i < voices.size(); ++i)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
for (auto* voice : voices)
if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay))
return voice;
}
if (stealIfNoneAvailable)
return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber);
@@ -569,7 +502,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
// - Protect the lowest & topmost notes, even if sustained, but not if they've been released.
// apparently you are trying to render audio without having any voices...
jassert (voices.size() > 0);
jassert (! voices.isEmpty());
// These are the voices we want to protect (ie: only steal if unavoidable)
SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
@@ -579,10 +512,8 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
Array<SynthesiserVoice*> usableVoices;
usableVoices.ensureStorageAllocated (voices.size());
for (int i = 0; i < voices.size(); ++i)
for (auto* voice : voices)
{
SynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->canPlaySound (soundToPlay))
{
jassert (voice->isVoiceActive()); // We wouldn't be here otherwise
@@ -592,7 +523,7 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
if (! voice->isPlayingButReleased()) // Don't protect released notes
{
const int note = voice->getCurrentlyPlayingNote();
auto note = voice->getCurrentlyPlayingNote();
if (low == nullptr || note < low->getCurrentlyPlayingNote())
low = voice;
@@ -607,43 +538,25 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
if (top == low)
top = nullptr;
const int numUsableVoices = usableVoices.size();
// The oldest note that's playing with the target pitch is ideal..
for (int i = 0; i < numUsableVoices; ++i)
{
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
for (auto* voice : usableVoices)
if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
return voice;
}
// Oldest voice that has been released (no finger on it and not held by sustain pedal)
for (int i = 0; i < numUsableVoices; ++i)
{
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
for (auto* voice : usableVoices)
if (voice != low && voice != top && voice->isPlayingButReleased())
return voice;
}
// Oldest voice that doesn't have a finger on it:
for (int i = 0; i < numUsableVoices; ++i)
{
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
for (auto* voice : usableVoices)
if (voice != low && voice != top && ! voice->isKeyDown())
return voice;
}
// Oldest voice that isn't protected
for (int i = 0; i < numUsableVoices; ++i)
{
SynthesiserVoice* const voice = usableVoices.getUnchecked (i);
for (auto* voice : usableVoices)
if (voice != low && voice != top)
return voice;
}
// We've only got "protected" voices now: lowest note takes priority
jassert (low != nullptr);


+ 16
- 9
source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h View File

@@ -182,6 +182,8 @@ public:
virtual void renderNextBlock (AudioBuffer<float>& outputBuffer,
int startSample,
int numSamples) = 0;
/** A double-precision version of renderNextBlock() */
virtual void renderNextBlock (AudioBuffer<double>& outputBuffer,
int startSample,
int numSamples);
@@ -214,6 +216,11 @@ public:
*/
bool isKeyDown() const noexcept { return keyIsDown; }
/** Allows you to modify the flag indicating that the key that triggered this voice is still held down.
@see isKeyDown
*/
void setKeyDown (bool isNowDown) noexcept { keyIsDown = isNowDown; }
/** Returns true if the sustain pedal is currently active for this voice. */
bool isSustainPedalDown() const noexcept { return sustainPedalDown; }
@@ -249,11 +256,11 @@ private:
//==============================================================================
friend class Synthesiser;
double currentSampleRate;
int currentlyPlayingNote, currentPlayingMidiChannel;
uint32 noteOnTime;
double currentSampleRate = 44100.0;
int currentlyPlayingNote = -1, currentPlayingMidiChannel = 0;
uint32 noteOnTime = 0;
SynthesiserSound::Ptr currentlyPlayingSound;
bool keyIsDown, sustainPedalDown, sostenutoPedalDown;
bool keyIsDown = false, sustainPedalDown = false, sostenutoPedalDown = false;
AudioBuffer<float> tempBuffer;
@@ -615,11 +622,11 @@ private:
int startSample,
int numSamples);
//==============================================================================
double sampleRate;
uint32 lastNoteOnCounter;
int minimumSubBlockSize;
bool subBlockSubdivisionIsStrict;
bool shouldStealNotes;
double sampleRate = 0;
uint32 lastNoteOnCounter = 0;
int minimumSubBlockSize = 32;
bool subBlockSubdivisionIsStrict = false;
bool shouldStealNotes = true;
BigInteger sustainPedalsDown;
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE


+ 5
- 0
source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp View File

@@ -651,11 +651,16 @@ public:
{
OpenSLSession::stop();
while (! guard.compareAndSetBool (1, 0))
Thread::sleep (1);
if (inputChannels > 0)
recorder->setState (false);
if (outputChannels > 0)
player->setState (false);
guard.set (0);
}
bool setAudioPreprocessingEnabled (bool shouldEnable) override


+ 7
- 1
source/modules/juce_audio_devices/native/juce_ios_Audio.cpp View File

@@ -576,7 +576,13 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
&hostUrl,
&dataSize);
if (err == noErr)
[[UIApplication sharedApplication] openURL:(NSURL*)hostUrl];
{
#if (! defined __IPHONE_OS_VERSION_MIN_REQUIRED) || (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0)
[[UIApplication sharedApplication] openURL: (NSURL*)hostUrl];
#else
[[UIApplication sharedApplication] openURL: (NSURL*)hostUrl options: @{} completionHandler: nil];
#endif
}
}
//==============================================================================


+ 41
- 13
source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp View File

@@ -357,7 +357,8 @@ public:
actualBufferSize (0),
bytesPerSample (0),
bytesPerFrame (0),
sampleRateHasChanged (false)
sampleRateHasChanged (false),
shouldClose (false)
{
clientEvent = CreateEvent (nullptr, false, false, nullptr);
@@ -468,6 +469,11 @@ public:
sampleRateHasChanged = true;
}
void deviceBecameInactive()
{
shouldClose = true;
}
//==============================================================================
ComSmartPtr<IMMDevice> device;
ComSmartPtr<IAudioClient> client;
@@ -482,7 +488,7 @@ public:
Array<int> channelMaps;
UINT32 actualBufferSize;
int bytesPerSample, bytesPerFrame;
bool sampleRateHasChanged;
bool sampleRateHasChanged, shouldClose;
virtual void updateFormat (bool isFloat) = 0;
@@ -498,10 +504,17 @@ private:
JUCE_COMRESULT OnSimpleVolumeChanged (float, BOOL, LPCGUID) { return S_OK; }
JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) { return S_OK; }
JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) { return S_OK; }
JUCE_COMRESULT OnStateChanged (AudioSessionState) { return S_OK; }
JUCE_COMRESULT OnStateChanged(AudioSessionState state)
{
if (state == AudioSessionStateInactive || state == AudioSessionStateExpired)
owner.deviceBecameInactive();
return S_OK;
}
JUCE_COMRESULT OnSessionDisconnected (AudioSessionDisconnectReason reason)
{
Logger::writeToLog("OnSessionDisconnected");
if (reason == DisconnectReasonFormatChanged)
owner.deviceSampleRateChanged();
@@ -969,7 +982,8 @@ public:
isStarted (false),
currentBufferSizeSamples (0),
currentSampleRate (0),
callback (nullptr)
callback (nullptr),
deviceBecameInactive (false)
{
}
@@ -1107,6 +1121,8 @@ public:
if (inputDevice != nullptr) ResetEvent (inputDevice->clientEvent);
if (outputDevice != nullptr) ResetEvent (outputDevice->clientEvent);
deviceBecameInactive = false;
startThread (8);
Thread::sleep (5);
@@ -1226,8 +1242,14 @@ public:
while (! threadShouldExit())
{
if (inputDevice != nullptr)
if (outputDevice != nullptr && outputDevice->shouldClose)
deviceBecameInactive = true;
if (inputDevice != nullptr && ! deviceBecameInactive)
{
if (inputDevice->shouldClose)
deviceBecameInactive = true;
if (outputDevice == nullptr)
{
if (WaitForSingleObject (inputDevice->clientEvent, 1000) == WAIT_TIMEOUT)
@@ -1253,6 +1275,7 @@ public:
}
}
if (! deviceBecameInactive)
{
const ScopedTryLock sl (startStopLock);
@@ -1263,7 +1286,7 @@ public:
outs.clear();
}
if (outputDevice != nullptr)
if (outputDevice != nullptr && !deviceBecameInactive)
{
// Note that this function is handed the input device so it can check for the event and make sure
// the input reservoir is filled up correctly even when bufferSize > device actualBufferSize
@@ -1276,7 +1299,7 @@ public:
}
}
if (sampleRateHasChanged)
if (sampleRateHasChanged || deviceBecameInactive)
{
triggerAsyncUpdate();
break; // Quit the thread... will restart it later!
@@ -1303,7 +1326,7 @@ private:
bool isOpen_, isStarted;
int currentBufferSizeSamples;
double currentSampleRate;
bool sampleRateChangedByOutput;
bool sampleRateChangedByOutput, deviceBecameInactive;
AudioIODeviceCallback* callback;
CriticalSection startStopLock;
@@ -1354,12 +1377,17 @@ private:
outputDevice = nullptr;
inputDevice = nullptr;
initialise();
open (lastKnownInputChannels, lastKnownOutputChannels,
getChangedSampleRate(), currentBufferSizeSamples);
// sample rate change
if (! deviceBecameInactive)
{
initialise();
open (lastKnownInputChannels, lastKnownOutputChannels,
getChangedSampleRate(), currentBufferSizeSamples);
start (callback);
start (callback);
}
}
double getChangedSampleRate() const
@@ -1481,7 +1509,7 @@ private:
HRESULT STDMETHODCALLTYPE OnDeviceAdded (LPCWSTR) { return notify(); }
HRESULT STDMETHODCALLTYPE OnDeviceRemoved (LPCWSTR) { return notify(); }
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged (LPCWSTR, DWORD) { return notify(); }
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) { return notify(); }
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); }
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); }


+ 6
- 15
source/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp View File

@@ -619,8 +619,7 @@ public:
for (auto tagEntry : knownTags)
{
AudioChannelLayout layout { tagEntry.tag };
auto labels = CoreAudioLayouts::fromCoreAudio (layout);
auto labels = CoreAudioLayouts::fromCoreAudio (tagEntry.tag);
expect (! labels.isDiscreteLayout(), String ("Tag \"") + String (tagEntry.name) + "\" is not handled by JUCE");
}
@@ -631,8 +630,7 @@ public:
for (auto tagEntry : knownTags)
{
AudioChannelLayout layout { tagEntry.tag };
auto labels = CoreAudioLayouts::getCoreAudioLayoutChannels (layout);
auto labels = CoreAudioLayouts::getSpeakerLayoutForCoreAudioTag (tagEntry.tag);
expect (labels.size() == (tagEntry.tag & 0xffff), String ("Tag \"") + String (tagEntry.name) + "\" has incorrect channel count");
}
@@ -643,8 +641,7 @@ public:
for (auto tagEntry : knownTags)
{
AudioChannelLayout layout { tagEntry.tag };
auto labels = CoreAudioLayouts::getCoreAudioLayoutChannels (layout);
auto labels = CoreAudioLayouts::getSpeakerLayoutForCoreAudioTag (tagEntry.tag);
labels.sort();
for (int i = 0; i < (labels.size() - 1); ++i)
@@ -657,13 +654,9 @@ public:
beginTest ("CA speaker list and juce layouts are consistent");
for (auto tagEntry : knownTags)
{
AudioChannelLayout layout { tagEntry.tag };
expect (AudioChannelSet::channelSetWithChannels (CoreAudioLayouts::getCoreAudioLayoutChannels (layout))
== CoreAudioLayouts::fromCoreAudio (layout),
expect (AudioChannelSet::channelSetWithChannels (CoreAudioLayouts::getSpeakerLayoutForCoreAudioTag (tagEntry.tag))
== CoreAudioLayouts::fromCoreAudio (tagEntry.tag),
String ("Tag \"") + String (tagEntry.name) + "\" is not converted consistantly by JUCE");
}
}
{
@@ -674,9 +667,7 @@ public:
if (tagEntry.equivalentChannelSet.isDisabled())
continue;
AudioChannelLayout layout { tagEntry.tag };
expect (CoreAudioLayouts::fromCoreAudio (layout) == tagEntry.equivalentChannelSet,
expect (CoreAudioLayouts::fromCoreAudio (tagEntry.tag) == tagEntry.equivalentChannelSet,
String ("Documentation for tag \"") + String (tagEntry.name) + "\" is incorrect");
}
}


+ 20
- 7
source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm View File

@@ -1065,6 +1065,19 @@ public:
jassertfalse; // xxx not implemented!
}
//==============================================================================
void updateTrackProperties (const TrackProperties& properties) override
{
if (properties.name.isNotEmpty())
{
CFStringRef contextName = properties.name.toCFString();
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ContextName, kAudioUnitScope_Global,
0, &contextName, sizeof (CFStringRef));
CFRelease (contextName);
}
}
//==============================================================================
void getStateInformation (MemoryBlock& destData) override
{
@@ -1128,6 +1141,7 @@ public:
void refreshParameterList() override
{
parameters.clear();
paramIDToIndex.clear();
if (audioUnit != nullptr)
{
@@ -1158,6 +1172,7 @@ public:
ParamInfo* const param = new ParamInfo();
parameters.add (param);
param->paramID = ids[i];
paramIDToIndex[ids[i]] = i;
param->minValue = info.minValue;
param->maxValue = info.maxValue;
param->automatable = (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0;
@@ -1250,6 +1265,7 @@ private:
};
OwnedArray<ParamInfo> parameters;
std::unordered_map<AudioUnitParameterID, int> paramIDToIndex;
MidiDataConcatenator midiConcatenator;
CriticalSection midiInLock;
@@ -1335,13 +1351,10 @@ private:
|| event.mEventType == kAudioUnitEvent_BeginParameterChangeGesture
|| event.mEventType == kAudioUnitEvent_EndParameterChangeGesture)
{
for (paramIndex = 0; paramIndex < parameters.size(); ++paramIndex)
{
const ParamInfo& p = *parameters.getUnchecked(paramIndex);
auto it = paramIDToIndex.find (event.mArgument.mParameter.mParameterID)
if (p.paramID == event.mArgument.mParameter.mParameterID)
break;
}
if (it != paramIDToIndex.end())
paramIndex = it->second;
if (! isPositiveAndBelow (paramIndex, parameters.size()))
return;
@@ -1351,7 +1364,7 @@ private:
{
case kAudioUnitEvent_ParameterValueChange:
{
const ParamInfo& p = *parameters.getUnchecked(paramIndex);
auto& p = *parameters.getUnchecked (paramIndex);
sendParamChangeMessageToListeners (paramIndex, (newValue - p.minValue) / (p.maxValue - p.minValue));
}
break;


+ 3
- 0
source/modules/juce_audio_processors/format_types/juce_VST3Headers.h View File

@@ -68,6 +68,7 @@
#include <pluginterfaces/base/ipluginbase.h>
#include <pluginterfaces/base/ustring.h>
#include <pluginterfaces/gui/iplugview.h>
#include <pluginterfaces/gui/iplugviewcontentscalesupport.h>
#include <pluginterfaces/vst/ivstattributes.h>
#include <pluginterfaces/vst/ivstaudioprocessor.h>
#include <pluginterfaces/vst/ivstcomponent.h>
@@ -83,6 +84,7 @@
#include <pluginterfaces/vst/vsttypes.h>
#include <pluginterfaces/vst/ivstunits.h>
#include <pluginterfaces/vst/ivstmidicontrollers.h>
#include <pluginterfaces/vst/ivstchannelcontextinfo.h>
#include <public.sdk/source/common/memorystream.h>
#include <public.sdk/source/vst/vsteditcontroller.h>
#else
@@ -105,6 +107,7 @@
#include <pluginterfaces/gui/iplugview.h>
#include <pluginterfaces/gui/iplugviewcontentscalesupport.h>
#include <pluginterfaces/vst/ivstmidicontrollers.h>
#include <pluginterfaces/vst/ivstchannelcontextinfo.h>
#include <public.sdk/source/common/memorystream.cpp>
#include <public.sdk/source/common/pluginview.cpp>
#include <public.sdk/source/vst/vsteditcontroller.cpp>


+ 64
- 0
source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -2101,6 +2101,67 @@ struct VST3PluginInstance : public AudioPluginInstance
return result;
}
//==============================================================================
void updateTrackProperties (const TrackProperties& properties) override
{
if (trackInfoListener != nullptr)
{
ComSmartPtr<Vst::IAttributeList> l (new TrackPropertiesAttributeList (properties));
trackInfoListener->setChannelContextInfos (l);
}
}
struct TrackPropertiesAttributeList : public Vst::IAttributeList
{
TrackPropertiesAttributeList (const TrackProperties& properties) : props (properties) {}
virtual ~TrackPropertiesAttributeList() {}
JUCE_DECLARE_VST3_COM_REF_METHODS
tresult PLUGIN_API queryInterface (const TUID queryIid, void** obj) override
{
TEST_FOR_AND_RETURN_IF_VALID (queryIid, Vst::IAttributeList)
TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (queryIid, FUnknown, Vst::IAttributeList)
*obj = nullptr;
return kNotImplemented;
}
tresult PLUGIN_API setInt (AttrID, Steinberg::int64) override { return kOutOfMemory; }
tresult PLUGIN_API setFloat (AttrID, double) override { return kOutOfMemory; }
tresult PLUGIN_API setString (AttrID, const Vst::TChar*) override { return kOutOfMemory; }
tresult PLUGIN_API setBinary (AttrID, const void*, Steinberg::uint32) override { return kOutOfMemory; }
tresult PLUGIN_API getFloat (AttrID, double&) override { return kResultFalse; }
tresult PLUGIN_API getBinary (AttrID, const void*&, Steinberg::uint32&) override { return kResultFalse; }
tresult PLUGIN_API getString (AttrID id, Vst::TChar* string, Steinberg::uint32 size) override
{
if (! std::strcmp (id, Vst::ChannelContext::kChannelNameKey))
{
Steinberg::String str (props.name.toRawUTF8());
str.copyTo (string, 0, (Steinberg::int32) jmin (size, (Steinberg::uint32) std::numeric_limits<Steinberg::int32>::max()));
return kResultTrue;
}
return kResultFalse;
}
tresult PLUGIN_API getInt (AttrID id, Steinberg::int64& value) override
{
if (! std::strcmp (Vst::ChannelContext::kChannelNameLengthKey, id)) value = props.name.length();
else if (! std::strcmp (Vst::ChannelContext::kChannelColorKey, id)) value = static_cast<Steinberg::int64> (props.colour.getARGB());
else return kResultFalse;
return kResultTrue;
}
Atomic<int> refCount;
TrackProperties props;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TrackPropertiesAttributeList)
};
//==============================================================================
String getChannelName (int channelIndex, bool forInput, bool forAudioChannel) const
{
@@ -2450,6 +2511,7 @@ private:
ComSmartPtr<Vst::IUnitData> unitData;
ComSmartPtr<Vst::IProgramListData> programListData;
ComSmartPtr<Vst::IConnectionPoint> componentConnection, editControllerConnection;
ComSmartPtr<Vst::ChannelContext::IInfoListener> trackInfoListener;
/** The number of IO buses MUST match that of the plugin,
even if there aren't enough channels to process,
@@ -2534,6 +2596,7 @@ private:
editController2.loadFrom (holder->component);
componentHandler.loadFrom (holder->component);
componentHandler2.loadFrom (holder->component);
trackInfoListener.loadFrom (holder->component);
if (processor == nullptr) processor.loadFrom (editController);
if (unitInfo == nullptr) unitInfo.loadFrom (editController);
@@ -2542,6 +2605,7 @@ private:
if (editController2 == nullptr) editController2.loadFrom (editController);
if (componentHandler == nullptr) componentHandler.loadFrom (editController);
if (componentHandler2 == nullptr) componentHandler2.loadFrom (editController);
if (trackInfoListener == nullptr) trackInfoListener.loadFrom (editController);
}
void setStateForAllMidiBuses (bool newState)


+ 2
- 2
source/modules/juce_audio_processors/format_types/juce_VSTCommon.h View File

@@ -180,8 +180,8 @@ struct SpeakerMappings : private AudioChannelSet // (inheritance only to give e
VstSpeakerConfiguration* allocate (int numChannels)
{
auto arrangementSize = (sizeof (VstSpeakerConfiguration) - (sizeof(VstIndividualSpeakerInfo) * 8))
+ (sizeof (VstIndividualSpeakerInfo) * static_cast<size_t> (numChannels));
auto arrangementSize = sizeof (VstSpeakerConfiguration)
+ sizeof (VstIndividualSpeakerInfo) * static_cast<size_t> (jmax (8, numChannels) - 8);
storage.malloc (1, arrangementSize);
return storage.getData();


+ 3
- 0
source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp View File

@@ -1029,6 +1029,9 @@ void AudioProcessor::setCurrentProgramStateInformation (const void* data, int si
setStateInformation (data, sizeInBytes);
}
//==============================================================================
void AudioProcessor::updateTrackProperties (const AudioProcessor::TrackProperties&) {}
//==============================================================================
// magic number to identify memory blocks that we've stored as XML
const uint32 magicXmlNumber = 0x21324356;


+ 26
- 0
source/modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -1320,6 +1320,32 @@ public:
*/
WrapperType wrapperType;
/** A struct containing information about the DAW track inside which your
AudioProcessor is loaded. */
struct TrackProperties
{
String name; // The name of the track - this will be empty if the track name is not known
Colour colour; // The colour of the track - this will be transparentBlack if the colour is not known
// other properties may be added in the future
};
/** Informs the AudioProcessor that track properties such as the track's name or
colour has been changed.
If you are hosting this AudioProcessor then use this method to inform the
AudioProcessor about which track the AudioProcessor is loaded on. This method
may only be called on the message thread.
If you are implemeting an AudioProcessor then you can override this callback
to do something useful with the track properties such as changing the colour
of your AudioProcessor's editor. It's entirely up to the host when and how
often this callback will be called.
The default implementation of this callback will do nothing.
*/
virtual void updateTrackProperties (const TrackProperties& properties);
//==============================================================================
#ifndef DOXYGEN
/** Deprecated: use getTotalNumInputChannels instead. */


+ 4
- 1
source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp View File

@@ -45,7 +45,10 @@ AudioProcessorEditor::~AudioProcessorEditor()
}
void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {}
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }
bool AudioProcessorEditor::supportsHostMIDIControllerPresence (bool) { return true; }
void AudioProcessorEditor::hostMIDIControllerIsAvailable (bool) {}
void AudioProcessorEditor::initialise()
{


+ 22
- 0
source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h View File

@@ -85,6 +85,28 @@ public:
*/
virtual int getControlParameterIndex (Component&);
/** Override this method to indicate if your editor supports the presence or
absence of a host-provided MIDI controller.
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later)
support this functionality, and even then the host may choose to ignore this
information.
The default behaviour is to report support for both cases.
*/
virtual bool supportsHostMIDIControllerPresence (bool hostMIDIControllerIsAvailable);
/** Called to indicate if a host is providing a MIDI controller when the host
reconfigures its layout.
Use this as an opportunity to hide or display your own onscreen keyboard or
other input component.
Currently only AUv3 plug-ins compiled for MacOS 10.13 or iOS 11.0 (or later)
support this functionality.
*/
virtual void hostMIDIControllerIsAvailable (bool controllerIsAvailable);
/** Can be called by a host to tell the editor that it should use a non-unity
GUI scale.
*/


+ 22
- 2
source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp View File

@@ -1466,6 +1466,7 @@ void AudioProcessorGraph::processAudio (AudioBuffer<FloatType>& buffer, MidiBuff
AudioBuffer<FloatType>& currentAudioOutputBuffer = audioBuffers->currentAudioOutputBuffer.get<FloatType>();
const int numSamples = buffer.getNumSamples();
jassert (numSamples <= getBlockSize());
currentAudioInputBuffer = &buffer;
currentAudioOutputBuffer.setSize (jmax (1, buffer.getNumChannels()), numSamples);
@@ -1488,6 +1489,25 @@ void AudioProcessorGraph::processAudio (AudioBuffer<FloatType>& buffer, MidiBuff
midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0);
}
template <typename FloatType>
void AudioProcessorGraph::sliceAndProcess (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages)
{
auto n = buffer.getNumSamples();
auto ch = buffer.getNumChannels();
auto max = 0;
for (auto pos = 0; pos < n; pos += max)
{
max = jmin (n - pos, getBlockSize());
AudioBuffer<FloatType> audioSlice (buffer.getArrayOfWritePointers(), ch, pos, max);
MidiBuffer midiSlice;
midiSlice.addEvents (midiMessages, pos, max, 0);
processAudio (audioSlice, midiSlice);
}
}
double AudioProcessorGraph::getTailLengthSeconds() const { return 0; }
bool AudioProcessorGraph::acceptsMidi() const { return true; }
bool AudioProcessorGraph::producesMidi() const { return true; }
@@ -1496,12 +1516,12 @@ void AudioProcessorGraph::setStateInformation (const void*, int) {}
void AudioProcessorGraph::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
processAudio (buffer, midiMessages);
sliceAndProcess (buffer, midiMessages);
}
void AudioProcessorGraph::processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
{
processAudio (buffer, midiMessages);
sliceAndProcess (buffer, midiMessages);
}
// explicit template instantiation


+ 3
- 0
source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h View File

@@ -380,6 +380,9 @@ private:
template <typename floatType>
void processAudio (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages);
template <typename floatType>
void sliceAndProcess (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages);
//==============================================================================
ReferenceCountedArray<Node> nodes;
OwnedArray<Connection> connections;


+ 1
- 1
source/modules/juce_audio_processors/scanning/juce_PluginListComponent.h View File

@@ -35,7 +35,7 @@
class JUCE_API PluginListComponent : public Component,
public FileDragAndDropTarget,
private ChangeListener,
private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
private Button::Listener
{
public:
//==============================================================================


+ 2
- 2
source/modules/juce_core/containers/juce_DynamicObject.cpp View File

@@ -91,7 +91,7 @@ DynamicObject::Ptr DynamicObject::clone()
return d;
}
void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine)
void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine, int maximumDecimalPlaces)
{
out << '{';
if (! allOnOneLine)
@@ -107,7 +107,7 @@ void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const
out << '"';
JSONFormatter::writeString (out, properties.getName (i));
out << "\": ";
JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine);
JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine, maximumDecimalPlaces);
if (i < numValues - 1)
{


+ 2
- 2
source/modules/juce_core/containers/juce_DynamicObject.h View File

@@ -113,14 +113,14 @@ public:
never need to call it directly, but it's virtual so that custom object types
can stringify themselves appropriately.
*/
virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine);
virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine, int maximumDecimalPlaces);
private:
//==============================================================================
NamedValueSet properties;
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// These methods have been deprecated - use var::invoke instead
// This method has been deprecated - use var::invoke instead
virtual void invokeMethod (const Identifier&, const var*, int) {}
#endif


+ 9
- 5
source/modules/juce_core/containers/juce_HashMap.h View File

@@ -31,16 +31,20 @@
*/
struct DefaultHashFunctions
{
/** Generates a simple hash from an unsigned int. */
static int generateHash (uint32 key, int upperLimit) noexcept { return (int) (key % (uint32) upperLimit); }
/** Generates a simple hash from an integer. */
int generateHash (const int key, const int upperLimit) const noexcept { return std::abs (key) % upperLimit; }
static int generateHash (int32 key, int upperLimit) noexcept { return generateHash ((uint32) key, upperLimit); }
/** Generates a simple hash from a uint64. */
static int generateHash (uint64 key, int upperLimit) noexcept { return (int) (key % (uint64) upperLimit); }
/** Generates a simple hash from an int64. */
int generateHash (const int64 key, const int upperLimit) const noexcept { return std::abs ((int) key) % upperLimit; }
static int generateHash (int64 key, int upperLimit) noexcept { return generateHash ((uint64) key, upperLimit); }
/** Generates a simple hash from a string. */
int generateHash (const String& key, const int upperLimit) const noexcept { return (int) (((uint32) key.hashCode()) % (uint32) upperLimit); }
static int generateHash (const String& key, int upperLimit) noexcept { return generateHash ((uint32) key.hashCode(), upperLimit); }
/** Generates a simple hash from a variant. */
int generateHash (const var& key, const int upperLimit) const noexcept { return generateHash (key.toString(), upperLimit); }
static int generateHash (const var& key, int upperLimit) noexcept { return generateHash (key.toString(), upperLimit); }
/** Generates a simple hash from a void ptr. */
int generateHash (const void* key, const int upperLimit) const noexcept { return (int)(((pointer_sized_uint) key) % ((pointer_sized_uint) upperLimit)); }
static int generateHash (const void* key, int upperLimit) noexcept { return generateHash ((pointer_sized_uint) key, upperLimit); }
};


+ 31
- 42
source/modules/juce_core/containers/juce_SortedSet.h View File

@@ -55,31 +55,22 @@ class SortedSet
public:
//==============================================================================
/** Creates an empty set. */
SortedSet() noexcept
{
}
SortedSet() noexcept = default;
/** Creates a copy of another set.
@param other the set to copy
*/
SortedSet (const SortedSet& other)
: data (other.data)
{
}
/** Creates a copy of another set. */
SortedSet (const SortedSet&) = default;
/** Destructor. */
~SortedSet() noexcept
{
}
/** Creates a copy of another set. */
SortedSet (SortedSet&&) noexcept = default;
/** Copies another set over this one.
@param other the set to copy
*/
SortedSet& operator= (const SortedSet& other) noexcept
{
data = other.data;
return *this;
}
/** Makes a copy of another set. */
SortedSet& operator= (const SortedSet&) = default;
/** Makes a copy of another set. */
SortedSet& operator= (SortedSet&&) noexcept = default;
/** Destructor. */
~SortedSet() noexcept {}
//==============================================================================
/** Compares this set to another one.
@@ -234,7 +225,7 @@ public:
if (elementToLookFor == data.getReference (s))
return s;
const int halfway = (s + e) / 2;
auto halfway = (s + e) / 2;
if (halfway == s)
return -1;
@@ -277,15 +268,16 @@ public:
while (s < e)
{
ElementType& elem = data.getReference (s);
auto& elem = data.getReference (s);
if (newElement == elem)
{
elem = newElement; // force an update in case operator== permits differences.
return false;
}
const int halfway = (s + e) / 2;
const bool isBeforeHalfway = (newElement < data.getReference (halfway));
auto halfway = (s + e) / 2;
bool isBeforeHalfway = (newElement < data.getReference (halfway));
if (halfway == s)
{
@@ -335,25 +327,22 @@ public:
int numElementsToAdd = -1) noexcept
{
const typename OtherSetType::ScopedLockType lock1 (setToAddFrom.getLock());
const ScopedLockType lock2 (getLock());
jassert (this != &setToAddFrom);
if (this != &setToAddFrom)
{
const ScopedLockType lock2 (getLock());
jassert (this != &setToAddFrom);
if (this != &setToAddFrom)
if (startIndex < 0)
{
if (startIndex < 0)
{
jassertfalse;
startIndex = 0;
}
jassertfalse;
startIndex = 0;
}
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
numElementsToAdd = setToAddFrom.size() - startIndex;
if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size())
numElementsToAdd = setToAddFrom.size() - startIndex;
if (numElementsToAdd > 0)
addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
}
if (numElementsToAdd > 0)
addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd);
}
}
@@ -400,7 +389,7 @@ public:
{
clear();
}
else if (otherSet.size() > 0)
else if (! otherSet.isEmpty())
{
for (int i = data.size(); --i >= 0;)
if (otherSet.contains (data.getReference (i)))
@@ -423,7 +412,7 @@ public:
if (this != &otherSet)
{
if (otherSet.size() <= 0)
if (otherSet.isEmpty())
{
clear();
}


+ 15
- 9
source/modules/juce_core/javascript/juce_JSON.cpp View File

@@ -323,7 +323,8 @@ class JSONFormatter
{
public:
static void write (OutputStream& out, const var& v,
const int indentLevel, const bool allOnOneLine)
const int indentLevel, const bool allOnOneLine,
int maximumDecimalPlaces)
{
if (v.isString())
{
@@ -343,14 +344,18 @@ public:
{
out << (static_cast<bool> (v) ? "true" : "false");
}
else if (v.isDouble())
{
out << String (static_cast<double> (v), maximumDecimalPlaces);
}
else if (v.isArray())
{
writeArray (out, *v.getArray(), indentLevel, allOnOneLine);
writeArray (out, *v.getArray(), indentLevel, allOnOneLine, maximumDecimalPlaces);
}
else if (v.isObject())
{
if (DynamicObject* object = v.getDynamicObject())
object->writeAsJSON (out, indentLevel, allOnOneLine);
object->writeAsJSON (out, indentLevel, allOnOneLine, maximumDecimalPlaces);
else
jassertfalse; // Only DynamicObjects can be converted to JSON!
}
@@ -420,7 +425,8 @@ public:
}
static void writeArray (OutputStream& out, const Array<var>& array,
const int indentLevel, const bool allOnOneLine)
const int indentLevel, const bool allOnOneLine,
int maximumDecimalPlaces)
{
out << '[';
@@ -434,7 +440,7 @@ public:
if (! allOnOneLine)
writeSpaces (out, indentLevel + indentSize);
write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine);
write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine, maximumDecimalPlaces);
if (i < array.size() - 1)
{
@@ -493,16 +499,16 @@ Result JSON::parse (const String& text, var& result)
return JSONParser::parseObjectOrArray (text.getCharPointer(), result);
}
String JSON::toString (const var& data, const bool allOnOneLine)
String JSON::toString (const var& data, const bool allOnOneLine, int maximumDecimalPlaces)
{
MemoryOutputStream mo (1024);
JSONFormatter::write (mo, data, 0, allOnOneLine);
JSONFormatter::write (mo, data, 0, allOnOneLine, maximumDecimalPlaces);
return mo.toUTF8();
}
void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine)
void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine, int maximumDecimalPlaces)
{
JSONFormatter::write (output, data, 0, allOnOneLine);
JSONFormatter::write (output, data, 0, allOnOneLine, maximumDecimalPlaces);
}
String JSON::escapeString (StringRef s)


+ 6
- 2
source/modules/juce_core/javascript/juce_JSON.h View File

@@ -90,10 +90,12 @@ public:
/** Returns a string which contains a JSON-formatted representation of the var object.
If allOnOneLine is true, the result will be compacted into a single line of text
with no carriage-returns. If false, it will be laid-out in a more human-readable format.
The maximumDecimalPlaces parameter determines the precision of floating point numbers.
@see writeToStream
*/
static String toString (const var& objectToFormat,
bool allOnOneLine = false);
bool allOnOneLine = false,
int maximumDecimalPlaces = 20);
/** Parses a string that was created with the toString() method.
This is slightly different to the parse() methods because they will reject primitive
@@ -105,11 +107,13 @@ public:
/** Writes a JSON-formatted representation of the var object to the given stream.
If allOnOneLine is true, the result will be compacted into a single line of text
with no carriage-returns. If false, it will be laid-out in a more human-readable format.
The maximumDecimalPlaces parameter determines the precision of floating point numbers.
@see toString
*/
static void writeToStream (OutputStream& output,
const var& objectToFormat,
bool allOnOneLine = false);
bool allOnOneLine = false,
int maximumDecimalPlaces = 20);
/** Returns a version of a string with any extended characters escaped. */
static String escapeString (StringRef);


+ 1
- 1
source/modules/juce_core/javascript/juce_Javascript.cpp View File

@@ -827,7 +827,7 @@ struct JavascriptEngine::RootObject : public DynamicObject
DynamicObject::Ptr clone() override { return new FunctionObject (*this); }
void writeAsJSON (OutputStream& out, int /*indentLevel*/, bool /*allOnOneLine*/) override
void writeAsJSON (OutputStream& out, int /*indentLevel*/, bool /*allOnOneLine*/, int /*maximumDecimalPlaces*/) override
{
out << "function " << functionCode;
}


+ 7
- 0
source/modules/juce_core/memory/juce_HeapBlock.h View File

@@ -149,6 +149,13 @@ public:
*/
inline operator ElementType*() const noexcept { return data; }
/** Returns a raw pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.
*/
inline ElementType* get() const noexcept { return data; }
/** Returns a raw pointer to the allocated data.
This may be a null pointer if the data hasn't yet been allocated, or if it has been
freed by calling the free() method.


+ 971
- 0
source/modules/juce_core/native/java/IInAppBillingService.java View File

@@ -0,0 +1,971 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
package com.android.vending.billing;
/**
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
* This service provides the following features:
* 1. Provides a new API to get details of in-app items published for the app including
* price, type, title and description.
* 2. The purchase flow is synchronous and purchase information is available immediately
* after it completes.
* 3. Purchase information of in-app purchases is maintained within the Google Play system
* till the purchase is consumed.
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
* in-app items are consumable and thereafter can be purchased again.
* 5. An API to get current purchases of the user immediately. This will not contain any
* consumed purchases.
*
* All calls will give a response code with the following possible values
* RESULT_OK = 0 - success
* RESULT_USER_CANCELED = 1 - User pressed back or canceled a dialog
* RESULT_SERVICE_UNAVAILABLE = 2 - The network connection is down
* RESULT_BILLING_UNAVAILABLE = 3 - This billing API version is not supported for the type requested
* RESULT_ITEM_UNAVAILABLE = 4 - Requested SKU is not available for purchase
* RESULT_DEVELOPER_ERROR = 5 - Invalid arguments provided to the API
* RESULT_ERROR = 6 - Fatal error during the API action
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
*/
public interface IInAppBillingService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.vending.billing.IInAppBillingService
{
private static final java.lang.String DESCRIPTOR = "com.android.vending.billing.IInAppBillingService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.android.vending.billing.IInAppBillingService interface,
* generating a proxy if needed.
*/
public static com.android.vending.billing.IInAppBillingService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.vending.billing.IInAppBillingService))) {
return ((com.android.vending.billing.IInAppBillingService)iin);
}
return new com.android.vending.billing.IInAppBillingService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_isBillingSupported:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.isBillingSupported(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getSkuDetails:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
android.os.Bundle _arg3;
if ((0!=data.readInt())) {
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg3 = null;
}
android.os.Bundle _result = this.getSkuDetails(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBuyIntent:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
android.os.Bundle _result = this.getBuyIntent(_arg0, _arg1, _arg2, _arg3, _arg4);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getPurchases:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
android.os.Bundle _result = this.getPurchases(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_consumePurchase:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.consumePurchase(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_stub:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.stub(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getBuyIntentToReplaceSkus:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.util.List<java.lang.String> _arg2;
_arg2 = data.createStringArrayList();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
java.lang.String _arg5;
_arg5 = data.readString();
android.os.Bundle _result = this.getBuyIntentToReplaceSkus(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBuyIntentExtraParams:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
android.os.Bundle _arg5;
if ((0!=data.readInt())) {
_arg5 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg5 = null;
}
android.os.Bundle _result = this.getBuyIntentExtraParams(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getPurchaseHistory:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
android.os.Bundle _arg4;
if ((0!=data.readInt())) {
_arg4 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg4 = null;
}
android.os.Bundle _result = this.getPurchaseHistory(_arg0, _arg1, _arg2, _arg3, _arg4);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_isBillingSupportedExtraParams:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
android.os.Bundle _arg3;
if ((0!=data.readInt())) {
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg3 = null;
}
int _result = this.isBillingSupportedExtraParams(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.android.vending.billing.IInAppBillingService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
mRemote.transact(Stub.TRANSACTION_isBillingSupported, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the app is using
* @param packageName the package name of the calling app
* @param type of the in-app items ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku",
* "type" : "inapp",
* "price" : "$5.00",
* "price_currency": "USD",
* "price_amount_micros": 5000000,
* "title : "Example Title",
* "description" : "This is an example description" }'
*/
@Override public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
if ((skusBundle!=null)) {
_data.writeInt(1);
skusBundle.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getSkuDetails, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
@Override public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(sku);
_data.writeString(type);
_data.writeString(developerPayload);
mRemote.transact(Stub.TRANSACTION_getBuyIntent, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
on failures.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
@Override public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
_data.writeString(continuationToken);
mRemote.transact(Stub.TRANSACTION_getPurchases, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(purchaseToken);
mRemote.transact(Stub.TRANSACTION_consumePurchase, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
mRemote.transact(Stub.TRANSACTION_stub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
* the user is upgrading or downgrading to.
* @param apiVersion billing API version that the app is using, must be 5 or later
* @param packageName package name of the calling app
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
* if null or empty this method will behave like {@link #getBuyIntent}
* @param newSku the SKU that the user is upgrading or downgrading to
* @param type of the item being purchased, currently must be "subs"
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
@Override public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeStringList(oldSkus);
_data.writeString(newSku);
_data.writeString(type);
_data.writeString(developerPayload);
mRemote.transact(Stub.TRANSACTION_getBuyIntentToReplaceSkus, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
* parameter. This parameter is a Bundle of optional keys and values that affect the
* operation of the method.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @extraParams a Bundle with the following optional keys:
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
* upgrading or downgrading from.
* Pass this field if the purchase is upgrading or downgrading
* existing subscriptions.
* The specified SKUs are replaced with the SKUs that the user is
* purchasing. Google Play replaces the specified SKUs at the start of
* the next billing cycle.
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
* subscription time on the SKUs they are upgrading or downgrading.
* If you set this field to true, Google Play swaps out the old SKUs
* and credits the user with the unused value of their subscription
* time on a pro-rated basis.
* Google Play applies this credit to the new subscription, and does
* not begin billing the user for the new subscription until after
* the credit is used up.
* If you set this field to false, the user does not receive credit for
* any unused subscription time and the recurrence date does not
* change.
* Default value is true. Ignored if you do not pass skusToReplace.
* "accountId" - String - an optional obfuscated string that is uniquely
* associated with the user's account in your app.
* If you pass this value, Google Play can use it to detect irregular
* activity, such as many devices making purchases on the same
* account in a short period of time.
* Do not use the developer ID or the user's Google ID for this field.
* In addition, this field should not contain the user's ID in
* cleartext.
* We recommend that you use a one-way hash to generate a string from
* the user's ID, and store the hashed string in this field.
* "vr" - Boolean - an optional flag indicating whether the returned intent
* should start a VR purchase flow. The apiVersion must also be 7 or
* later to use this flag.
*/
@Override public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(sku);
_data.writeString(type);
_data.writeString(developerPayload);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getBuyIntentExtraParams, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
* expired, canceled, or consumed.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus is too large, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @param extraParams a Bundle with extra params that would be appended into http request
* query string. Not used at this moment. Reserved for future functionality.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
*
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
@Override public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
_data.writeString(continuationToken);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getPurchaseHistory, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_isBillingSupportedExtraParams, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_isBillingSupported = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getSkuDetails = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getBuyIntent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getPurchases = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_consumePurchase = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_stub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
static final int TRANSACTION_getBuyIntentToReplaceSkus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
static final int TRANSACTION_getBuyIntentExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
static final int TRANSACTION_getPurchaseHistory = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
static final int TRANSACTION_isBillingSupportedExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 9);
}
public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the app is using
* @param packageName the package name of the calling app
* @param type of the in-app items ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku",
* "type" : "inapp",
* "price" : "$5.00",
* "price_currency": "USD",
* "price_amount_micros": 5000000,
* "title : "Example Title",
* "description" : "This is an example description" }'
*/
public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
on failures.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException;
public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException;
public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
* the user is upgrading or downgrading to.
* @param apiVersion billing API version that the app is using, must be 5 or later
* @param packageName package name of the calling app
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
* if null or empty this method will behave like {@link #getBuyIntent}
* @param newSku the SKU that the user is upgrading or downgrading to
* @param type of the item being purchased, currently must be "subs"
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
* parameter. This parameter is a Bundle of optional keys and values that affect the
* operation of the method.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @extraParams a Bundle with the following optional keys:
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
* upgrading or downgrading from.
* Pass this field if the purchase is upgrading or downgrading
* existing subscriptions.
* The specified SKUs are replaced with the SKUs that the user is
* purchasing. Google Play replaces the specified SKUs at the start of
* the next billing cycle.
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
* subscription time on the SKUs they are upgrading or downgrading.
* If you set this field to true, Google Play swaps out the old SKUs
* and credits the user with the unused value of their subscription
* time on a pro-rated basis.
* Google Play applies this credit to the new subscription, and does
* not begin billing the user for the new subscription until after
* the credit is used up.
* If you set this field to false, the user does not receive credit for
* any unused subscription time and the recurrence date does not
* change.
* Default value is true. Ignored if you do not pass skusToReplace.
* "accountId" - String - an optional obfuscated string that is uniquely
* associated with the user's account in your app.
* If you pass this value, Google Play can use it to detect irregular
* activity, such as many devices making purchases on the same
* account in a short period of time.
* Do not use the developer ID or the user's Google ID for this field.
* In addition, this field should not contain the user's ID in
* cleartext.
* We recommend that you use a one-way hash to generate a string from
* the user's ID, and store the hashed string in this field.
* "vr" - Boolean - an optional flag indicating whether the returned intent
* should start a VR purchase flow. The apiVersion must also be 7 or
* later to use this flag.
*/
public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException;
/**
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
* expired, canceled, or consumed.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus is too large, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @param extraParams a Bundle with extra params that would be appended into http request
* query string. Not used at this moment. Reserved for future functionality.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
*
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException;
public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException;
}

+ 38
- 47
source/modules/juce_core/native/java/JuceAppActivity.java View File

@@ -55,8 +55,6 @@ import java.io.*;
import java.net.URL;
import java.net.HttpURLConnection;
import android.media.AudioManager;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.Manifest;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Future;
@@ -68,7 +66,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.*;
$$JuceAndroidMidiImports$$ // If you get an error here, you need to re-save your project with the Projucer!
$$JuceAndroidMidiImports$$ // If you get an error here, you need to re-save your project with the Projucer!
//==============================================================================
@@ -301,23 +299,7 @@ public class JuceAppActivity extends Activity
private native void suspendApp();
private native void resumeApp();
private native void setScreenSize (int screenWidth, int screenHeight, int dpi);
//==============================================================================
public native void deliverMessage (long value);
private android.os.Handler messageHandler = new android.os.Handler();
public final void postMessage (long value)
{
messageHandler.post (new MessageCallback (value));
}
private final class MessageCallback implements Runnable
{
public MessageCallback (long value_) { value = value_; }
public final void run() { deliverMessage (value); }
private long value;
}
private native void appActivityResult (int requestCode, int resultCode, Intent data);
//==============================================================================
private ViewHolder viewHolder;
@@ -882,6 +864,38 @@ public class JuceAppActivity extends Activity
private int[] cachedRenderArray = new int [256];
//==============================================================================
public static class NativeInvocationHandler implements InvocationHandler
{
public NativeInvocationHandler (long nativeContextRef)
{
nativeContext = nativeContextRef;
}
@Override
public void finalize()
{
dispatchFinalize (nativeContext);
}
@Override
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable
{
return dispatchInvoke (nativeContext, proxy, method, args);
}
//==============================================================================
private long nativeContext = 0;
private native void dispatchFinalize (long nativeContextRef);
private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args);
}
public static InvocationHandler createInvocationHandler (long nativeContextRef)
{
return new NativeInvocationHandler (nativeContextRef);
}
//==============================================================================
public static class HTTPStream
{
@@ -1185,36 +1199,13 @@ public class JuceAppActivity extends Activity
public static final String getDownloadsFolder() { return getFileLocation (Environment.DIRECTORY_DOWNLOADS); }
//==============================================================================
private final class SingleMediaScanner implements MediaScannerConnectionClient
{
public SingleMediaScanner (Context context, String filename)
{
file = filename;
msc = new MediaScannerConnection (context, this);
msc.connect();
}
@Override
public void onMediaScannerConnected()
{
msc.scanFile (file, null);
}
@Override
public void onScanCompleted (String path, Uri uri)
{
msc.disconnect();
}
private MediaScannerConnection msc;
private String file;
}
public final void scanFile (String filename)
@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data)
{
new SingleMediaScanner (this, filename);
appActivityResult (requestCode, resultCode, data);
}
//==============================================================================
public final Typeface getTypeFaceFromAsset (String assetName)
{
try


+ 86
- 0
source/modules/juce_core/native/juce_android_Files.cpp View File

@@ -20,6 +20,46 @@
==============================================================================
*/
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(Landroid/content/Context;Landroid/media/MediaScannerConnection$MediaScannerConnectionClient;)V") \
METHOD (connect, "connect", "()V") \
METHOD (disconnect, "disconnect", "()V") \
METHOD (scanFile, "scanFile", "(Ljava/lang/String;Ljava/lang/String;)V") \
DECLARE_JNI_CLASS (MediaScannerConnection, "android/media/MediaScannerConnection");
#undef JNI_CLASS_MEMBERS
//==============================================================================
class MediaScannerConnectionClient : public AndroidInterfaceImplementer
{
public:
virtual void onMediaScannerConnected() = 0;
virtual void onScanCompleted() = 0;
private:
jobject invoke (jobject proxy, jobject method, jobjectArray args) override
{
auto* env = getEnv();
auto methodName = juceString ((jstring) env->CallObjectMethod (method, Method.getName));
if (methodName == "onMediaScannerConnected")
{
onMediaScannerConnected();
return nullptr;
}
else if (methodName == "onScanCompleted")
{
onScanCompleted();
return nullptr;
}
return AndroidInterfaceImplementer::invoke (proxy, method, args);
}
};
//==============================================================================
bool File::isOnCDRomDrive() const
{
return false;
@@ -100,3 +140,49 @@ JUCE_API bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const
void File::revealToUser() const
{
}
//==============================================================================
class SingleMediaScanner : public MediaScannerConnectionClient
{
public:
SingleMediaScanner (const String& filename)
: msc (getEnv()->NewObject (MediaScannerConnection,
MediaScannerConnection.constructor,
android.activity.get(),
CreateJavaInterface (this, "android/media/MediaScannerConnection$MediaScannerConnectionClient").get())),
file (filename)
{
getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.connect);
}
void onMediaScannerConnected() override
{
auto* env = getEnv();
env->CallVoidMethod (msc.get(), MediaScannerConnection.scanFile, javaString (file).get(), 0);
}
void onScanCompleted() override
{
getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.disconnect);
}
private:
GlobalRef msc;
String file;
};
void FileOutputStream::flushInternal()
{
if (fileHandle != 0)
{
if (fsync (getFD (fileHandle)) == -1)
status = getResultForErrno();
// This stuff tells the OS to asynchronously update the metadata
// that the OS has cached aboud the file - this metadata is used
// when the device is acting as a USB drive, and unless it's explicitly
// refreshed, it'll get out of step with the real file.
new SingleMediaScanner (file.getFullPathName());
}
}

+ 125
- 28
source/modules/juce_core/native/juce_android_JNIHelpers.h View File

@@ -40,10 +40,11 @@ extern JNIEnv* attachAndroidJNI() noexcept;
class GlobalRef
{
public:
inline GlobalRef() noexcept : obj (0) {}
inline explicit GlobalRef (jobject o) : obj (retain (o)) {}
inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj)) {}
~GlobalRef() { clear(); }
inline GlobalRef() noexcept : obj (0) {}
inline explicit GlobalRef (jobject o) : obj (retain (o)) {}
inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj)) {}
inline GlobalRef (GlobalRef && other) noexcept : obj (0) { std::swap (other.obj, obj); }
~GlobalRef() { clear(); }
inline void clear()
{
@@ -62,6 +63,14 @@ public:
return *this;
}
inline GlobalRef& operator= (GlobalRef&& other)
{
clear();
std::swap (obj, other.obj);
return *this;
}
//==============================================================================
inline operator jobject() const noexcept { return obj; }
inline jobject get() const noexcept { return obj; }
@@ -98,7 +107,7 @@ public:
private:
//==============================================================================
jobject obj;
jobject obj = 0;
static inline jobject retain (jobject obj)
{
@@ -111,14 +120,19 @@ template <typename JavaType>
class LocalRef
{
public:
explicit inline LocalRef () noexcept : obj (0) {}
explicit inline LocalRef (JavaType o) noexcept : obj (o) {}
inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {}
inline LocalRef (LocalRef&& other) noexcept : obj (0) { std::swap (obj, other.obj); }
~LocalRef() { clear(); }
void clear()
{
if (obj != 0)
{
getEnv()->DeleteLocalRef (obj);
obj = 0;
}
}
LocalRef& operator= (const LocalRef& other)
@@ -129,6 +143,13 @@ public:
return *this;
}
LocalRef& operator= (LocalRef&& other)
{
clear();
std::swap (other.obj, obj);
return *this;
}
inline operator JavaType() const noexcept { return obj; }
inline JavaType get() const noexcept { return obj; }
@@ -258,29 +279,27 @@ extern AndroidSystem android;
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \
METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \
METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \
METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \
METHOD (postMessage, "postMessage", "(J)V") \
METHOD (finish, "finish", "()V") \
METHOD (finish, "finish", "()V") \
METHOD (setRequestedOrientation,"setRequestedOrientation", "(I)V") \
METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \
METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \
METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
METHOD (renderGlyph, "renderGlyph", "(CCLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \
METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \
METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V") \
METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \
STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \
STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \
METHOD (scanFile, "scanFile", "(Ljava/lang/String;)V") \
METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \
METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \
METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
METHOD (renderGlyph, "renderGlyph", "(CCLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \
METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \
METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V") \
METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \
STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \
STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \
METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
METHOD (getTypeFaceFromByteArray,"getTypeFaceFromByteArray","([B)Landroid/graphics/Typeface;") \
METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \
METHOD (getScreenSaver, "getScreenSaver", "()Z") \
@@ -288,11 +307,16 @@ extern AndroidSystem android;
METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \
METHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \
METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \
METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \
METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \
METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \
STATICMETHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \
METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \
METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \
METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \
METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \
DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH);
#undef JNI_CLASS_MEMBERS
@@ -332,3 +356,76 @@ DECLARE_JNI_CLASS (Matrix, "android/graphics/Matrix");
DECLARE_JNI_CLASS (RectClass, "android/graphics/Rect");
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getName, "getName", "()Ljava/lang/String;") \
METHOD (getModifiers, "getModifiers", "()I") \
METHOD (getParameterTypes, "getParameterTypes", "()[Ljava/lang/Class;") \
METHOD (getReturnType, "getReturnType", "()Ljava/lang/Class;") \
METHOD (invoke, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;") \
METHOD (hashCode, "hashCode", "()I") \
METHOD (equals, "equals", "(Ljava/lang/Object;)Z") \
DECLARE_JNI_CLASS (Method, "java/lang/reflect/Method");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getName, "getName", "()Ljava/lang/String;") \
METHOD (getModifiers, "getModifiers", "()I") \
METHOD (isAnnotation, "isAnnotation", "()Z") \
METHOD (isAnonymousClass, "isAnonymousClass", "()Z") \
METHOD (isArray, "isArray", "()Z") \
METHOD (isEnum, "isEnum", "()Z") \
METHOD (isInterface, "isInterface", "()Z") \
METHOD (isLocalClass, "isLocalClass", "()Z") \
METHOD (isMemberClass, "isMemberClass", "()Z") \
METHOD (isPrimitive, "isPrimitive", "()Z") \
METHOD (isSynthetic, "isSynthetic", "()Z") \
METHOD (getComponentType, "getComponentType", "()Ljava/lang/Class;") \
METHOD (getSuperclass, "getSuperclass", "()Ljava/lang/Class;") \
METHOD (getClassLoader, "getClassLoader", "()Ljava/lang/ClassLoader;") \
DECLARE_JNI_CLASS (JavaClass, "java/lang/Class");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "()V") \
DECLARE_JNI_CLASS (JavaObject, "java/lang/Object");
#undef JNI_CLASS_MEMBERS
//==============================================================================
class AndroidInterfaceImplementer;
// This function takes ownership of the implementer. When the returned GlobalRef
// goes out of scope (and no other Java routine has a reference on the return-value)
// then the implementer will be deleted as well.
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames,
LocalRef<jobject> subclass);
//==============================================================================
jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray);
void juce_dispatchDelete (JNIEnv*, jlong);
//==============================================================================
class AndroidInterfaceImplementer
{
protected:
virtual ~AndroidInterfaceImplementer() {}
virtual jobject invoke (jobject proxy, jobject method, jobjectArray args);
//==============================================================================
friend LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer*, const StringArray&, LocalRef<jobject>);
friend jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray);
friend void juce_dispatchDelete (JNIEnv*, jlong);
private:
GlobalRef javaSubClass;
};
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames);
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const String& interfaceName);

+ 93
- 0
source/modules/juce_core/native/juce_android_SystemStats.cpp View File

@@ -20,6 +20,13 @@
==============================================================================
*/
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (newProxyInstance, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;") \
DECLARE_JNI_CLASS (JavaProxy, "java/lang/reflect/Proxy");
#undef JNI_CLASS_MEMBERS
JNIClassBase::JNIClassBase (const char* cp) : classPath (cp), classRef (0)
{
getClasses().add (this);
@@ -91,6 +98,92 @@ jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, c
return f;
}
//==============================================================================
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames,
LocalRef<jobject> subclass)
{
auto* env = getEnv();
implementer->javaSubClass = GlobalRef (subclass);
// you need to override at least one interface
jassert (interfaceNames.size() > 0);
auto classArray = LocalRef<jobject> (env->NewObjectArray (interfaceNames.size(), JavaClass, nullptr));
LocalRef<jobject> classLoader;
for (auto i = 0; i < interfaceNames.size(); ++i)
{
auto aClass = LocalRef<jobject> (env->FindClass (interfaceNames[i].toRawUTF8()));
if (aClass != nullptr)
{
if (i == 0)
classLoader = LocalRef<jobject> (env->CallObjectMethod (aClass, JavaClass.getClassLoader));
env->SetObjectArrayElement ((jobjectArray) classArray.get(), i, aClass);
}
else
{
// interface class not found
jassertfalse;
}
}
auto invocationHandler = LocalRef<jobject> (env->CallStaticObjectMethod (JuceAppActivity,
JuceAppActivity.createInvocationHandler,
reinterpret_cast<jlong> (implementer)));
return LocalRef<jobject> (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance,
classLoader.get(), classArray.get(),
invocationHandler.get()));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames)
{
return CreateJavaInterface (implementer, interfaceNames,
LocalRef<jobject> (getEnv()->NewObject (JavaObject,
JavaObject.constructor)));
}
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const String& interfaceName)
{
return CreateJavaInterface (implementer, StringArray (interfaceName));
}
jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args)
{
auto* env = getEnv();
return env->CallObjectMethod (method, Method.invoke, javaSubClass.get(), args);
}
jobject juce_invokeImplementer (JNIEnv* env, jlong thisPtr, jobject proxy, jobject method, jobjectArray args)
{
setEnv (env);
return reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr)->invoke (proxy, method, args);
}
void juce_dispatchDelete (JNIEnv* env, jlong thisPtr)
{
setEnv (env);
delete reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchInvoke,
jobject, (JNIEnv* env, jobject /*object*/, jlong thisPtr, jobject proxy, jobject method, jobjectArray args))
{
return juce_invokeImplementer (env, thisPtr, proxy, method, args);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchFinalize,
void, (JNIEnv* env, jobject /*object*/, jlong thisPtr))
{
juce_dispatchDelete (env, thisPtr);
}
//==============================================================================
JavaVM* androidJNIJavaVM = nullptr;


+ 5
- 0
source/modules/juce_core/native/juce_mac_ClangBugWorkaround.h View File

@@ -27,4 +27,9 @@
#include <CoreFoundation/CFAvailability.h>
#undef CF_OPTIONS
#define CF_OPTIONS(_type, _name) _type _name; enum
// This is a workaround for the XCode 9 version of NSUUID.h causing some errors
// in the live-build engine.
#define _Nullable
#define _Nonnull
#endif

+ 5
- 0
source/modules/juce_core/native/juce_mac_Files.mm View File

@@ -401,7 +401,12 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&
if (SystemStats::isRunningInAppExtensionSandbox())
return false;
#if (! defined __IPHONE_OS_VERSION_MIN_REQUIRED) || (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0)
return [[UIApplication sharedApplication] openURL: filenameAsURL];
#else
[[UIApplication sharedApplication] openURL: filenameAsURL options: @{} completionHandler: nil];
return true;
#endif
#else
NSWorkspace* workspace = [NSWorkspace sharedWorkspace];


+ 2
- 0
source/modules/juce_core/native/juce_mac_Network.mm View File

@@ -137,6 +137,8 @@ public:
[task release];
[request release];
[headers release];
[session finishTasksAndInvalidate];
[session release];
const ScopedLock sl (dataLock);


+ 10
- 0
source/modules/juce_core/native/juce_osx_ObjCHelpers.h View File

@@ -59,6 +59,16 @@ namespace
return createNSURLFromFile (f.getFullPathName());
}
static inline NSArray* createNSArrayFromStringArray (const StringArray& strings)
{
auto* array = [[NSMutableArray alloc] init];
for (auto string: strings)
[array addObject:juceStringToNS (string)];
return [array autorelease];
}
#if JUCE_MAC
template <typename RectangleType>
static NSRect makeNSRect (const RectangleType& r) noexcept


+ 8
- 14
source/modules/juce_core/native/juce_posix_SharedCode.h View File

@@ -393,7 +393,11 @@ void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int
{
modificationTime = (int64) info.st_mtime * 1000;
accessTime = (int64) info.st_atime * 1000;
#if (JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) || JUCE_IOS
creationTime = (int64) info.st_birthtime * 1000;
#else
creationTime = (int64) info.st_ctime * 1000;
#endif
}
}
@@ -554,23 +558,13 @@ ssize_t FileOutputStream::writeInternal (const void* const data, const size_t nu
return result;
}
#ifndef JUCE_ANDROID
void FileOutputStream::flushInternal()
{
if (fileHandle != 0)
{
if (fsync (getFD (fileHandle)) == -1)
status = getResultForErrno();
#if JUCE_ANDROID
// This stuff tells the OS to asynchronously update the metadata
// that the OS has cached aboud the file - this metadata is used
// when the device is acting as a USB drive, and unless it's explicitly
// refreshed, it'll get out of step with the real file.
const LocalRef<jstring> t (javaString (file.getFullPathName()));
android.activity.callVoidMethod (JuceAppActivity.scanFile, t.get());
#endif
}
if (fileHandle != 0 && fsync (getFD (fileHandle)) == -1)
status = getResultForErrno();
}
#endif
Result FileOutputStream::truncate()
{


+ 13
- 1
source/modules/juce_core/native/juce_win32_SystemStats.cpp View File

@@ -227,7 +227,19 @@ String SystemStats::getOperatingSystemName()
String SystemStats::getDeviceDescription()
{
return {};
#if WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP
return "Windows (Desktop)";
#elif WINAPI_FAMILY == WINAPI_FAMILY_PC_APP
return "Windows (Store)";
#elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
return "Windows (Phone)";
#elif WINAPI_FAMILY == WINAPI_FAMILY_SYSTEM
return "Windows (System)";
#elif WINAPI_FAMILY == WINAPI_FAMILY_SERVER
return "Windows (Server)";
#else
return "Windows";
#endif
}
bool SystemStats::isOperatingSystem64Bit()


+ 18
- 0
source/modules/juce_core/system/juce_CompilerSupport.h View File

@@ -47,6 +47,10 @@
#define JUCE_COMPILER_SUPPORTS_THREAD_LOCAL 1
#endif
#if __cpp_constexpr >= 201304
#define JUCE_HAS_CONSTEXPR 1
#endif
#ifndef JUCE_EXCEPTIONS_DISABLED
#if ! __EXCEPTIONS
#define JUCE_EXCEPTIONS_DISABLED 1
@@ -86,6 +90,10 @@
#define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1
#endif
#if __has_feature(cxx_relaxed_constexpr)
#define JUCE_HAS_CONSTEXPR 1
#endif
#ifndef JUCE_COMPILER_SUPPORTS_ARC
#define JUCE_COMPILER_SUPPORTS_ARC 1
#endif
@@ -118,6 +126,10 @@
#define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1
#endif
#if _MSC_VER >= 1910
#define JUCE_HAS_CONSTEXPR 1
#endif
#ifndef JUCE_EXCEPTIONS_DISABLED
#if ! _CPPUNWIND
#define JUCE_EXCEPTIONS_DISABLED 1
@@ -137,6 +149,12 @@
#define JUCE_DELETED_FUNCTION
#endif
#if JUCE_HAS_CONSTEXPR
#define JUCE_CONSTEXPR constexpr
#else
#define JUCE_CONSTEXPR
#endif
#if ! DOXYGEN
#if ! JUCE_COMPILER_SUPPORTS_NOEXCEPT
#ifdef noexcept


+ 5
- 5
source/modules/juce_core/text/juce_String.cpp View File

@@ -563,7 +563,7 @@ struct HashGenerator
template <typename CharPointer>
static Type calculate (CharPointer t) noexcept
{
Type result = Type();
Type result = {};
while (! t.isEmpty())
result = ((Type) multiplier) * result + (Type) t.getAndAdvance();
@@ -574,9 +574,9 @@ struct HashGenerator
enum { multiplier = sizeof (Type) > 4 ? 101 : 31 };
};
int String::hashCode() const noexcept { return HashGenerator<int> ::calculate (text); }
int64 String::hashCode64() const noexcept { return HashGenerator<int64> ::calculate (text); }
size_t String::hash() const noexcept { return HashGenerator<size_t> ::calculate (text); }
int String::hashCode() const noexcept { return (int) HashGenerator<uint32> ::calculate (text); }
int64 String::hashCode64() const noexcept { return (int64) HashGenerator<uint64> ::calculate (text); }
size_t String::hash() const noexcept { return HashGenerator<size_t> ::calculate (text); }
//==============================================================================
JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const String& s2) noexcept { return s1.compare (s2) == 0; }
@@ -1884,7 +1884,7 @@ String String::formattedRaw (const char* pf, ...)
va_end (args);
if (num > 0)
return String (temp);
return String (temp.get());
bufferSize += 256;


+ 16
- 8
source/modules/juce_core/text/juce_String.h View File

@@ -987,6 +987,10 @@ public:
*/
String (double doubleValue, int numberOfDecimalPlaces);
// Automatically creating a String from a bool opens up lots of nasty type conversion edge cases.
// If you want a String representation of a bool you can cast the bool to an int first.
explicit String (bool) = delete;
/** Reads the value of the string as a decimal number (up to 32 bits in size).
@returns the value of the string as a 32 bit signed base-10 integer.
@@ -1323,23 +1327,27 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const String& string
/** Appends a string to the end of the first one. */
JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, StringRef string2);
/** Appends a decimal number at the end of a string. */
/** Appends a decimal number to the end of a string. */
JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, short number);
/** Appends a decimal number at the end of a string. */
/** Appends a decimal number to the end of a string. */
JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, int number);
/** Appends a decimal number at the end of a string. */
/** Appends a decimal number to the end of a string. */
JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, long number);
/** Appends a decimal number at the end of a string. */
/** Appends a decimal number to the end of a string. */
JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, unsigned long number);
/** Appends a decimal number at the end of a string. */
/** Appends a decimal number to the end of a string. */
JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, int64 number);
/** Appends a decimal number at the end of a string. */
/** Appends a decimal number to the end of a string. */
JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, uint64 number);
/** Appends a decimal number at the end of a string. */
/** Appends a decimal number to the end of a string. */
JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, float number);
/** Appends a decimal number at the end of a string. */
/** Appends a decimal number to the end of a string. */
JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, double number);
// Automatically creating a String from a bool opens up lots of nasty type conversion edge cases.
// If you want a String representation of a bool you can cast the bool to an int first.
JUCE_API String& JUCE_CALLTYPE operator<< (String&, bool) = delete;
//==============================================================================
/** Case-sensitive comparison of two strings. */
JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const String& string2) noexcept;


+ 31
- 2
source/modules/juce_core/threads/juce_Thread.cpp View File

@@ -20,13 +20,16 @@
==============================================================================
*/
Thread::Thread (const String& name, const size_t stackSize)
Thread::Thread (const String& name, size_t stackSize)
: threadName (name), threadStackSize (stackSize)
{
}
Thread::~Thread()
{
if (deleteOnThreadEnd)
return;
/* If your thread class's destructor has been called without first stopping the thread, that
means that this partially destructed object is still performing some work - and that's
probably a Bad Thing!
@@ -97,6 +100,9 @@ void Thread::threadEntryPoint()
currentThreadHolder->value.releaseCurrentThreadStorage();
closeThreadHandle();
if (deleteOnThreadEnd)
delete this;
}
// used to wrap the incoming call from the platform-specific code
@@ -162,7 +168,7 @@ void Thread::signalThreadShouldExit()
bool Thread::currentThreadShouldExit()
{
if (Thread* currentThread = getCurrentThread())
if (auto* currentThread = getCurrentThread())
return currentThread->threadShouldExit();
return false;
@@ -273,6 +279,29 @@ void Thread::notify() const
defaultEvent.signal();
}
//==============================================================================
struct LambdaThread : public Thread
{
LambdaThread (std::function<void()> f) : Thread ("anonymous"), fn (f) {}
void run() override
{
fn();
fn = {}; // free any objects that the lambda might contain while the thread is still active
}
std::function<void()> fn;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LambdaThread)
};
void Thread::launch (std::function<void()> functionToRun)
{
auto anon = new LambdaThread (functionToRun);
anon->deleteOnThreadEnd = true;
anon->startThread();
}
//==============================================================================
void SpinLock::enter() const noexcept
{


+ 15
- 9
source/modules/juce_core/threads/juce_Thread.h View File

@@ -76,8 +76,6 @@ public:
virtual void run() = 0;
//==============================================================================
// Thread control functions..
/** Starts the thread running.
This will cause the thread's run() method to be called by a new thread.
@@ -118,6 +116,18 @@ public:
*/
bool stopThread (int timeOutMilliseconds);
//==============================================================================
/** Invokes a lambda or function on its own thread.
This will spin up a Thread object which calls the function and then exits.
Bear in mind that starting and stopping a thread can be a fairly heavyweight
operation, so you might prefer to use a ThreadPool if you're kicking off a lot
of short background tasks.
Also note that using an anonymous thread makes it very difficult to interrupt
the function when you need to stop it, e.g. when your app quits. So it's up to
you to deal with situations where the function may fail to stop in time.
*/
static void launch (std::function<void()> functionToRun);
//==============================================================================
/** Returns true if the thread is currently active */
bool isThreadRunning() const;
@@ -154,8 +164,7 @@ public:
static bool currentThreadShouldExit();
/** Waits for the thread to stop.
This will waits until isThreadRunning() is false or until a timeout expires.
This will wait until isThreadRunning() is false or until a timeout expires.
@param timeOutMilliseconds the time to wait, in milliseconds. If this value
is less than zero, it will wait forever.
@@ -274,21 +283,17 @@ public:
static Thread* JUCE_CALLTYPE getCurrentThread();
/** Returns the ID of this thread.
That means the ID of this thread object - not of the thread that's calling the method.
This can change when the thread is started and stopped, and will be invalid if the
thread's not actually running.
@see getCurrentThreadId
*/
ThreadID getThreadId() const noexcept { return threadId; }
/** Returns the name of the thread.
This is the name that gets set in the constructor.
*/
const String& getThreadName() const { return threadName; }
const String& getThreadName() const noexcept { return threadName; }
/** Changes the name of the caller thread.
Different OSes may place different length or content limits on this name.
@@ -306,6 +311,7 @@ private:
int threadPriority = 5;
size_t threadStackSize;
uint32 affinityMask = 0;
bool deleteOnThreadEnd = false;
bool volatile shouldExit = false;
#if JUCE_ANDROID


+ 79
- 44
source/modules/juce_core/threads/juce_ThreadPool.cpp View File

@@ -23,8 +23,8 @@
class ThreadPool::ThreadPoolThread : public Thread
{
public:
ThreadPoolThread (ThreadPool& p, size_t stackSize = 0)
: Thread ("Pool", stackSize), currentJob (nullptr), pool (p)
ThreadPoolThread (ThreadPool& p, size_t stackSize)
: Thread ("Pool", stackSize), pool (p)
{
}
@@ -35,16 +35,14 @@ public:
wait (500);
}
ThreadPoolJob* volatile currentJob;
ThreadPoolJob* volatile currentJob = nullptr;
ThreadPool& pool;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread)
};
//==============================================================================
ThreadPoolJob::ThreadPoolJob (const String& name)
: jobName (name), pool (nullptr),
shouldStop (false), isActive (false), shouldBeDeleted (false)
ThreadPoolJob::ThreadPoolJob (const String& name) : jobName (name)
{
}
@@ -72,7 +70,7 @@ void ThreadPoolJob::signalJobShouldExit()
ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob()
{
if (ThreadPool::ThreadPoolThread* t = dynamic_cast<ThreadPool::ThreadPoolThread*> (Thread::getCurrentThread()))
if (auto* t = dynamic_cast<ThreadPool::ThreadPoolThread*> (Thread::getCurrentThread()))
return t->currentJob;
return nullptr;
@@ -102,17 +100,17 @@ void ThreadPool::createThreads (int numThreads, size_t threadStackSize)
for (int i = jmax (1, numThreads); --i >= 0;)
threads.add (new ThreadPoolThread (*this, threadStackSize));
for (int i = threads.size(); --i >= 0;)
threads.getUnchecked(i)->startThread();
for (auto* t : threads)
t->startThread();
}
void ThreadPool::stopThreads()
{
for (int i = threads.size(); --i >= 0;)
threads.getUnchecked(i)->signalThreadShouldExit();
for (auto* t : threads)
t->signalThreadShouldExit();
for (int i = threads.size(); --i >= 0;)
threads.getUnchecked(i)->stopThread (500);
for (auto* t : threads)
t->stopThread (500);
}
void ThreadPool::addJob (ThreadPoolJob* const job, const bool deleteJobWhenFinished)
@@ -132,44 +130,83 @@ void ThreadPool::addJob (ThreadPoolJob* const job, const bool deleteJobWhenFinis
jobs.add (job);
}
for (int i = threads.size(); --i >= 0;)
threads.getUnchecked(i)->notify();
for (auto* t : threads)
t->notify();
}
}
int ThreadPool::getNumJobs() const
void ThreadPool::addJob (std::function<ThreadPoolJob::JobStatus()> jobToRun)
{
struct LambdaJobWrapper : public ThreadPoolJob
{
LambdaJobWrapper (std::function<ThreadPoolJob::JobStatus()> j) : ThreadPoolJob ("lambda"), job (j) {}
JobStatus runJob() override { return job(); }
std::function<ThreadPoolJob::JobStatus()> job;
};
addJob (new LambdaJobWrapper (jobToRun), true);
}
void ThreadPool::addJob (std::function<void()> jobToRun)
{
struct LambdaJobWrapper : public ThreadPoolJob
{
LambdaJobWrapper (std::function<void()> j) : ThreadPoolJob ("lambda"), job (j) {}
JobStatus runJob() override { job(); return ThreadPoolJob::jobHasFinished; }
std::function<void()> job;
};
addJob (new LambdaJobWrapper (jobToRun), true);
}
int ThreadPool::getNumJobs() const noexcept
{
return jobs.size();
}
int ThreadPool::getNumThreads() const
int ThreadPool::getNumThreads() const noexcept
{
return threads.size();
}
ThreadPoolJob* ThreadPool::getJob (const int index) const
ThreadPoolJob* ThreadPool::getJob (int index) const noexcept
{
const ScopedLock sl (lock);
return jobs [index];
}
bool ThreadPool::contains (const ThreadPoolJob* const job) const
bool ThreadPool::contains (const ThreadPoolJob* const job) const noexcept
{
const ScopedLock sl (lock);
return jobs.contains (const_cast<ThreadPoolJob*> (job));
}
bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const
bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const noexcept
{
const ScopedLock sl (lock);
return jobs.contains (const_cast<ThreadPoolJob*> (job)) && job->isActive;
}
void ThreadPool::moveJobToFront (const ThreadPoolJob* job) noexcept
{
const ScopedLock sl (lock);
if (! ! job->isActive)
{
auto index = jobs.indexOf (const_cast<ThreadPoolJob*> (job));
if (index > 0)
jobs.move (index, 0);
}
}
bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job, const int timeOutMs) const
{
if (job != nullptr)
{
const uint32 start = Time::getMillisecondCounter();
auto start = Time::getMillisecondCounter();
while (contains (job))
{
@@ -217,7 +254,7 @@ bool ThreadPool::removeJob (ThreadPoolJob* const job,
bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeOutMs,
ThreadPool::JobSelector* const selectedJobsToRemove)
{
Array <ThreadPoolJob*> jobsToWaitFor;
Array<ThreadPoolJob*> jobsToWaitFor;
{
OwnedArray<ThreadPoolJob> deletionList;
@@ -227,7 +264,7 @@ bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeO
for (int i = jobs.size(); --i >= 0;)
{
ThreadPoolJob* const job = jobs.getUnchecked(i);
auto* job = jobs.getUnchecked(i);
if (selectedJobsToRemove == nullptr || selectedJobsToRemove->isJobSuitable (job))
{
@@ -248,13 +285,13 @@ bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeO
}
}
const uint32 start = Time::getMillisecondCounter();
auto start = Time::getMillisecondCounter();
for (;;)
{
for (int i = jobsToWaitFor.size(); --i >= 0;)
{
ThreadPoolJob* const job = jobsToWaitFor.getUnchecked (i);
auto* job = jobsToWaitFor.getUnchecked (i);
if (! isJobRunning (job))
jobsToWaitFor.remove (i);
@@ -277,12 +314,9 @@ StringArray ThreadPool::getNamesOfAllJobs (const bool onlyReturnActiveJobs) cons
StringArray s;
const ScopedLock sl (lock);
for (int i = 0; i < jobs.size(); ++i)
{
const ThreadPoolJob* const job = jobs.getUnchecked(i);
for (auto* job : jobs)
if (job->isActive || ! onlyReturnActiveJobs)
s.add (job->getJobName());
}
return s;
}
@@ -291,8 +325,8 @@ bool ThreadPool::setThreadPriorities (const int newPriority)
{
bool ok = true;
for (int i = threads.size(); --i >= 0;)
if (! threads.getUnchecked(i)->setPriority (newPriority))
for (auto* t : threads)
if (! t->setPriority (newPriority))
ok = false;
return ok;
@@ -307,20 +341,21 @@ ThreadPoolJob* ThreadPool::pickNextJobToRun()
for (int i = 0; i < jobs.size(); ++i)
{
ThreadPoolJob* job = jobs[i];
if (job != nullptr && ! job->isActive)
if (auto* job = jobs[i])
{
if (job->shouldStop)
if (! job->isActive)
{
jobs.remove (i);
addToDeleteList (deletionList, job);
--i;
continue;
}
if (job->shouldStop)
{
jobs.remove (i);
addToDeleteList (deletionList, job);
--i;
continue;
}
job->isActive = true;
return job;
job->isActive = true;
return job;
}
}
}
}
@@ -330,9 +365,9 @@ ThreadPoolJob* ThreadPool::pickNextJobToRun()
bool ThreadPool::runNextJob (ThreadPoolThread& thread)
{
if (ThreadPoolJob* const job = pickNextJobToRun())
if (auto* job = pickNextJobToRun())
{
ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished;
auto result = ThreadPoolJob::jobHasFinished;
thread.currentJob = job;
try


+ 24
- 11
source/modules/juce_core/threads/juce_ThreadPool.h View File

@@ -123,8 +123,8 @@ private:
friend class ThreadPool;
friend class ThreadPoolThread;
String jobName;
ThreadPool* pool;
bool shouldStop, isActive, shouldBeDeleted;
ThreadPool* pool = nullptr;
bool shouldStop = false, isActive = false, shouldBeDeleted = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolJob)
};
@@ -207,6 +207,16 @@ public:
void addJob (ThreadPoolJob* job,
bool deleteJobWhenFinished);
/** Adds a lambda function to be called as a job.
This will create an internal ThreadPoolJob object to encapsulate and call the lambda.
*/
void addJob (std::function<ThreadPoolJob::JobStatus()> job);
/** Adds a lambda function to be called as a job.
This will create an internal ThreadPoolJob object to encapsulate and call the lambda.
*/
void addJob (std::function<void()> job);
/** Tries to remove a job from the pool.
If the job isn't yet running, this will simply remove it. If it is running, it
@@ -244,27 +254,26 @@ public:
JobSelector* selectedJobsToRemove = nullptr);
/** Returns the number of jobs currently running or queued. */
int getNumJobs() const;
int getNumJobs() const noexcept;
/** Returns the number of threads assigned to this thread pool. */
int getNumThreads() const;
int getNumThreads() const noexcept;
/** Returns one of the jobs in the queue.
Note that this can be a very volatile list as jobs might be continuously getting shifted
around in the list, and this method may return nullptr if the index is currently out-of-range.
*/
ThreadPoolJob* getJob (int index) const;
ThreadPoolJob* getJob (int index) const noexcept;
/** Returns true if the given job is currently queued or running.
@see isJobRunning()
*/
bool contains (const ThreadPoolJob* job) const;
bool contains (const ThreadPoolJob* job) const noexcept;
/** Returns true if the given job is currently being run by a thread.
*/
bool isJobRunning (const ThreadPoolJob* job) const;
/** Returns true if the given job is currently being run by a thread. */
bool isJobRunning (const ThreadPoolJob* job) const noexcept;
/** Waits until a job has finished running and has been removed from the pool.
@@ -277,13 +286,17 @@ public:
bool waitForJobToFinish (const ThreadPoolJob* job,
int timeOutMilliseconds) const;
/** If the given job is in the queue, this will move it to the front so that it
is the next one to be executed.
*/
void moveJobToFront (const ThreadPoolJob* jobToMove) noexcept;
/** Returns a list of the names of all the jobs currently running or queued.
If onlyReturnActiveJobs is true, only the ones currently running are returned.
*/
StringArray getNamesOfAllJobs (bool onlyReturnActiveJobs) const;
/** Changes the priority of all the threads.
This will call Thread::setPriority() for each thread in the pool.
May return false if for some reason the priority can't be changed.
*/
@@ -292,7 +305,7 @@ public:
private:
//==============================================================================
Array <ThreadPoolJob*> jobs;
Array<ThreadPoolJob*> jobs;
class ThreadPoolThread;
friend class ThreadPoolJob;


+ 3
- 1
source/modules/juce_core/time/juce_PerformanceCounter.h View File

@@ -139,7 +139,7 @@ private:
class JUCE_API ScopedTimeMeasurement
{
public:
ScopedTimeMeasurement (double& resultInSeconds)
ScopedTimeMeasurement (double& resultInSeconds) noexcept
: result (resultInSeconds)
{
result = 0.0;
@@ -154,4 +154,6 @@ public:
private:
int64 startTimeTicks = Time::getHighResolutionTicks();
double& result;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedTimeMeasurement)
};

+ 9
- 6
source/modules/juce_events/broadcasters/juce_AsyncUpdater.h View File

@@ -48,14 +48,17 @@ public:
//==============================================================================
/** Causes the callback to be triggered at a later time.
This method returns immediately, having made sure that a callback
to the handleAsyncUpdate() method will occur as soon as possible.
This method returns immediately, after which a callback to the
handleAsyncUpdate() method will be made by the message thread as
soon as possible.
If an update callback is already pending but hasn't happened yet, calls
to this method will be ignored.
If an update callback is already pending but hasn't happened yet, calling
this method will have no effect.
It's thread-safe to call this method from any number of threads without
needing to worry about locking.
It's thread-safe to call this method from any thread, BUT beware of calling
it from a real-time (e.g. audio) thread, because it involves posting a message
to the system queue, which means it may block (and in general will do on
most OSes).
*/
void triggerAsyncUpdate();


+ 80
- 16
source/modules/juce_events/native/juce_android_Messaging.cpp View File

@@ -20,8 +20,61 @@
==============================================================================
*/
void MessageManager::doPlatformSpecificInitialisation() {}
void MessageManager::doPlatformSpecificShutdown() {}
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "()V") \
METHOD (post, "post", "(Ljava/lang/Runnable;)Z") \
DECLARE_JNI_CLASS (JNIHandler, "android/os/Handler");
#undef JNI_CLASS_MEMBERS
//==============================================================================
namespace Android
{
class Runnable : public juce::AndroidInterfaceImplementer
{
public:
virtual void run() = 0;
private:
jobject invoke (jobject proxy, jobject method, jobjectArray args) override
{
auto* env = getEnv();
auto methodName = juce::juceString ((jstring) env->CallObjectMethod (method, Method.getName));
if (methodName == "run")
{
run();
return nullptr;
}
// invoke base class
return AndroidInterfaceImplementer::invoke (proxy, method, args);
}
};
struct Handler
{
juce_DeclareSingleton (Handler, false)
Handler() : nativeHandler (getEnv()->NewObject (JNIHandler, JNIHandler.constructor)) {}
bool post (Runnable* runnable)
{
return (getEnv()->CallBooleanMethod (nativeHandler.get(), JNIHandler.post,
CreateJavaInterface (runnable, "java/lang/Runnable").get()) != 0);
}
GlobalRef nativeHandler;
};
juce_ImplementSingleton (Handler);
}
//==============================================================================
void MessageManager::doPlatformSpecificInitialisation() { Android::Handler::getInstance(); }
void MessageManager::doPlatformSpecificShutdown() {}
//==============================================================================
bool MessageManager::dispatchNextMessageOnSystemQueue (const bool)
@@ -33,26 +86,37 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (const bool)
}
//==============================================================================
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
struct AndroidMessageCallback : public Android::Runnable
{
message->incReferenceCount();
android.activity.callVoidMethod (JuceAppActivity.postMessage, (jlong) (pointer_sized_uint) message);
return true;
}
AndroidMessageCallback (const MessageManager::MessageBase::Ptr& messageToDeliver)
: message (messageToDeliver)
{}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, deliverMessage, void, (JNIEnv* env, jobject, jlong value))
{
setEnv (env);
AndroidMessageCallback (MessageManager::MessageBase::Ptr && messageToDeliver)
: message (static_cast<MessageManager::MessageBase::Ptr&&> (messageToDeliver))
{}
JUCE_TRY
void run() override
{
MessageManager::MessageBase* const message = (MessageManager::MessageBase*) (pointer_sized_uint) value;
message->messageCallback();
message->decReferenceCount();
JUCE_TRY
{
message->messageCallback();
// delete the message already here as Java will only run the
// destructor of this runnable the next time the garbage
// collector kicks in.
message = nullptr;
}
JUCE_CATCH_EXCEPTION
}
JUCE_CATCH_EXCEPTION
}
MessageManager::MessageBase::Ptr message;
};
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
{
return Android::Handler::getInstance()->post (new AndroidMessageCallback (message));
}
//==============================================================================
void MessageManager::broadcastMessage (const String&)
{


+ 19
- 29
source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp View File

@@ -38,7 +38,7 @@ namespace
&& (int) h >= 0 && (int) h <= maxVal);
#endif
return Rectangle<Type> (x, y, w, h);
return { x, y, w, h };
}
}
@@ -49,15 +49,13 @@ LowLevelGraphicsContext::~LowLevelGraphicsContext() {}
//==============================================================================
Graphics::Graphics (const Image& imageToDrawOnto)
: context (*imageToDrawOnto.createLowLevelContext()),
contextToDelete (&context),
saveStatePending (false)
contextToDelete (&context)
{
jassert (imageToDrawOnto.isValid()); // Can't draw into a null image!
}
Graphics::Graphics (LowLevelGraphicsContext& internalContext) noexcept
: context (internalContext),
saveStatePending (false)
: context (internalContext)
{
}
@@ -85,7 +83,7 @@ bool Graphics::reduceClipRegion (Rectangle<int> area)
return context.clipToRectangle (area);
}
bool Graphics::reduceClipRegion (const int x, const int y, const int w, const int h)
bool Graphics::reduceClipRegion (int x, int y, int w, int h)
{
return reduceClipRegion (coordsToRectangle (x, y, w, h));
}
@@ -157,7 +155,7 @@ void Graphics::setOrigin (Point<int> newOrigin)
void Graphics::setOrigin (int x, int y)
{
setOrigin (Point<int> (x, y));
setOrigin ({ x, y });
}
void Graphics::addTransform (const AffineTransform& transform)
@@ -239,23 +237,20 @@ void Graphics::drawSingleLineText (const String& text, const int startX, const i
// Don't pass any vertical placement flags to this method - they'll be ignored.
jassert (justification.getOnlyVerticalFlags() == 0);
const int flags = justification.getOnlyHorizontalFlags();
auto flags = justification.getOnlyHorizontalFlags();
if (flags == Justification::right)
{
if (startX < context.getClipBounds().getX())
return;
}
else if (flags == Justification::left)
if (startX > context.getClipBounds().getRight())
return;
if (flags == Justification::right && startX < context.getClipBounds().getX())
return;
if (flags == Justification::left && startX > context.getClipBounds().getRight())
return;
GlyphArrangement arr;
arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY);
if (flags != Justification::left)
{
float w = arr.getBoundingBox (0, -1, true).getWidth();
auto w = arr.getBoundingBox (0, -1, true).getWidth();
if ((flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0)
w /= 2.0f;
@@ -371,11 +366,6 @@ void Graphics::fillRectList (const RectangleList<int>& rects) const
context.fillRect (r, false);
}
void Graphics::setPixel (int x, int y) const
{
context.fillRect (coordsToRectangle (x, y, 1, 1), false);
}
void Graphics::fillAll() const
{
fillRect (context.getClipBounds());
@@ -385,7 +375,7 @@ void Graphics::fillAll (Colour colourToUse) const
{
if (! colourToUse.isTransparent())
{
const Rectangle<int> clip (context.getClipBounds());
auto clip = context.getClipBounds();
context.saveState();
context.setFill (colourToUse);
@@ -507,7 +497,7 @@ void Graphics::drawRoundedRectangle (Rectangle<float> r, float cornerSize, float
strokePath (p, PathStrokeType (lineThickness));
}
void Graphics::drawArrow (const Line<float>& line, float lineThickness, float arrowheadWidth, float arrowheadLength) const
void Graphics::drawArrow (Line<float> line, float lineThickness, float arrowheadWidth, float arrowheadLength) const
{
Path p;
p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength);
@@ -531,7 +521,7 @@ void Graphics::fillCheckerBoard (Rectangle<int> area,
}
else
{
const Rectangle<int> clipped (context.getClipBounds().getIntersection (area));
auto clipped = context.getClipBounds().getIntersection (area);
if (! clipped.isEmpty())
{
@@ -573,7 +563,7 @@ void Graphics::drawHorizontalLine (const int y, float left, float right) const
context.fillRect (Rectangle<float> (left, (float) y, right - left, 1.0f));
}
void Graphics::drawLine (const Line<float>& line) const
void Graphics::drawLine (Line<float> line) const
{
context.drawLine (line);
}
@@ -588,15 +578,15 @@ void Graphics::drawLine (float x1, float y1, float x2, float y2, float lineThick
drawLine (Line<float> (x1, y1, x2, y2), lineThickness);
}
void Graphics::drawLine (const Line<float>& line, const float lineThickness) const
void Graphics::drawLine (Line<float> line, const float lineThickness) const
{
Path p;
p.addLineSegment (line, lineThickness);
fillPath (p);
}
void Graphics::drawDashedLine (const Line<float>& line, const float* const dashLengths,
const int numDashLengths, const float lineThickness, int n) const
void Graphics::drawDashedLine (Line<float> line, const float* dashLengths,
int numDashLengths, float lineThickness, int n) const
{
jassert (n >= 0 && n < numDashLengths); // your start index must be valid!


+ 6
- 12
source/modules/juce_graphics/contexts/juce_GraphicsContext.h View File

@@ -338,12 +338,6 @@ public:
void drawRoundedRectangle (Rectangle<float> rectangle,
float cornerSize, float lineThickness) const;
/** Fills a 1x1 pixel using the current colour or brush.
Note that because the context may be transformed, this is effectively the same as
calling fillRect (x, y, 1, 1), and the actual result may involve multiple pixels.
*/
void setPixel (int x, int y) const;
//==============================================================================
/** Fills an ellipse with the current colour or brush.
The ellipse is drawn to fit inside the given rectangle.
@@ -388,14 +382,14 @@ public:
TIP: If you're trying to draw horizontal or vertical lines, don't use this -
it's better to use fillRect() instead unless you really need an angled line.
*/
void drawLine (const Line<float>& line) const;
void drawLine (Line<float> line) const;
/** Draws a line between two points with a given thickness.
@see Path::addLineSegment
TIP: If you're trying to draw horizontal or vertical lines, don't use this -
it's better to use fillRect() instead unless you really need an angled line.
*/
void drawLine (const Line<float>& line, float lineThickness) const;
void drawLine (Line<float> line, float lineThickness) const;
/** Draws a dashed line using a custom set of dash-lengths.
@@ -408,7 +402,7 @@ public:
@param dashIndexToStartFrom the index in the dash-length array to use for the first segment
@see PathStrokeType::createDashedStroke
*/
void drawDashedLine (const Line<float>& line,
void drawDashedLine (Line<float> line,
const float* dashLengths, int numDashLengths,
float lineThickness = 1.0f,
int dashIndexToStartFrom = 0) const;
@@ -441,7 +435,7 @@ public:
/** Draws a path's outline using the currently selected colour or brush. */
void strokePath (const Path& path,
const PathStrokeType& strokeType,
const AffineTransform& transform = AffineTransform()) const;
const AffineTransform& transform = {}) const;
/** Draws a line with an arrowhead at its end.
@@ -450,7 +444,7 @@ public:
@param arrowheadWidth the width of the arrow head (perpendicular to the line)
@param arrowheadLength the length of the arrow head (along the length of the line)
*/
void drawArrow (const Line<float>& line,
void drawArrow (Line<float> line,
float lineThickness,
float arrowheadWidth,
float arrowheadLength) const;
@@ -743,7 +737,7 @@ private:
LowLevelGraphicsContext& context;
ScopedPointer<LowLevelGraphicsContext> contextToDelete;
bool saveStatePending;
bool saveStatePending = false;
void saveStateIfPending();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Graphics)


+ 19
- 20
source/modules/juce_graphics/geometry/juce_Path.cpp View File

@@ -280,7 +280,7 @@ void Path::startNewSubPath (const float x, const float y)
data.elements[numElements++] = y;
}
void Path::startNewSubPath (const Point<float> start)
void Path::startNewSubPath (Point<float> start)
{
startNewSubPath (start.x, start.y);
}
@@ -301,7 +301,7 @@ void Path::lineTo (const float x, const float y)
bounds.extend (x, y);
}
void Path::lineTo (const Point<float> end)
void Path::lineTo (Point<float> end)
{
lineTo (end.x, end.y);
}
@@ -326,8 +326,7 @@ void Path::quadraticTo (const float x1, const float y1,
bounds.extend (x1, y1, x2, y2);
}
void Path::quadraticTo (const Point<float> controlPoint,
const Point<float> endPoint)
void Path::quadraticTo (Point<float> controlPoint, Point<float> endPoint)
{
quadraticTo (controlPoint.x, controlPoint.y,
endPoint.x, endPoint.y);
@@ -358,9 +357,9 @@ void Path::cubicTo (const float x1, const float y1,
bounds.extend (x3, y3);
}
void Path::cubicTo (const Point<float> controlPoint1,
const Point<float> controlPoint2,
const Point<float> endPoint)
void Path::cubicTo (Point<float> controlPoint1,
Point<float> controlPoint2,
Point<float> endPoint)
{
cubicTo (controlPoint1.x, controlPoint1.y,
controlPoint2.x, controlPoint2.y,
@@ -675,7 +674,7 @@ void Path::addPieSegment (Rectangle<float> segmentBounds,
}
//==============================================================================
void Path::addLineSegment (const Line<float>& line, float lineThickness)
void Path::addLineSegment (Line<float> line, float lineThickness)
{
auto reversed = line.reversed();
lineThickness *= 0.5f;
@@ -687,7 +686,7 @@ void Path::addLineSegment (const Line<float>& line, float lineThickness)
closeSubPath();
}
void Path::addArrow (const Line<float>& line, float lineThickness,
void Path::addArrow (Line<float> line, float lineThickness,
float arrowheadWidth, float arrowheadLength)
{
auto reversed = line.reversed();
@@ -705,8 +704,8 @@ void Path::addArrow (const Line<float>& line, float lineThickness,
closeSubPath();
}
void Path::addPolygon (const Point<float> centre, const int numberOfSides,
const float radius, const float startAngle)
void Path::addPolygon (Point<float> centre, int numberOfSides,
float radius, float startAngle)
{
jassert (numberOfSides > 1); // this would be silly.
@@ -729,8 +728,8 @@ void Path::addPolygon (const Point<float> centre, const int numberOfSides,
}
}
void Path::addStar (const Point<float> centre, const int numberOfPoints,
const float innerRadius, const float outerRadius, const float startAngle)
void Path::addStar (Point<float> centre, int numberOfPoints, float innerRadius,
float outerRadius, float startAngle)
{
jassert (numberOfPoints > 1); // this would be silly.
@@ -755,9 +754,9 @@ void Path::addStar (const Point<float> centre, const int numberOfPoints,
}
}
void Path::addBubble (const Rectangle<float>& bodyArea,
const Rectangle<float>& maximumArea,
const Point<float> arrowTip,
void Path::addBubble (Rectangle<float> bodyArea,
Rectangle<float> maximumArea,
Point<float> arrowTip,
const float cornerSize,
const float arrowBaseWidth)
{
@@ -968,8 +967,8 @@ void Path::applyTransform (const AffineTransform& transform) noexcept
//==============================================================================
AffineTransform Path::getTransformToScaleToFit (const Rectangle<float>& area,
bool preserveProportions, Justification justification) const
AffineTransform Path::getTransformToScaleToFit (Rectangle<float> area, bool preserveProportions,
Justification justification) const
{
return getTransformToScaleToFit (area.getX(), area.getY(), area.getWidth(), area.getHeight(),
preserveProportions, justification);
@@ -1059,7 +1058,7 @@ bool Path::contains (const float x, const float y, const float tolerance) const
: ((negativeCrossings + positiveCrossings) & 1) != 0;
}
bool Path::contains (const Point<float> point, const float tolerance) const
bool Path::contains (Point<float> point, const float tolerance) const
{
return contains (point.x, point.y, tolerance);
}
@@ -1138,7 +1137,7 @@ Point<float> Path::getPointAlongPath (float distanceFromStart,
return { i.x2, i.y2 };
}
float Path::getNearestPoint (const Point<float> targetPoint, Point<float>& pointOnPath,
float Path::getNearestPoint (Point<float> targetPoint, Point<float>& pointOnPath,
const AffineTransform& transform,
float tolerance) const
{


+ 18
- 18
source/modules/juce_graphics/geometry/juce_Path.h View File

@@ -129,7 +129,7 @@ public:
@see closeSubPath, setUsingNonZeroWinding
*/
bool contains (const Point<float> point,
bool contains (Point<float> point,
float tolerance = defaultToleranceForTesting) const;
/** Checks whether a line crosses the path.
@@ -211,7 +211,7 @@ public:
@see lineTo, quadraticTo, cubicTo, closeSubPath
*/
void startNewSubPath (const Point<float> start);
void startNewSubPath (Point<float> start);
/** Closes a the current sub-path with a line back to its start-point.
@@ -247,7 +247,7 @@ public:
@see startNewSubPath, quadraticTo, cubicTo, closeSubPath
*/
void lineTo (const Point<float> end);
void lineTo (Point<float> end);
/** Adds a quadratic bezier curve from the shape's last position to a new position.
@@ -272,8 +272,8 @@ public:
@see startNewSubPath, lineTo, cubicTo, closeSubPath
*/
void quadraticTo (const Point<float> controlPoint,
const Point<float> endPoint);
void quadraticTo (Point<float> controlPoint,
Point<float> endPoint);
/** Adds a cubic bezier curve from the shape's last position to a new position.
@@ -300,9 +300,9 @@ public:
@see startNewSubPath, lineTo, quadraticTo, closeSubPath
*/
void cubicTo (const Point<float> controlPoint1,
const Point<float> controlPoint2,
const Point<float> endPoint);
void cubicTo (Point<float> controlPoint1,
Point<float> controlPoint2,
Point<float> endPoint);
/** Returns the last point that was added to the path by one of the drawing methods.
*/
@@ -320,7 +320,7 @@ public:
@see addRoundedRectangle, addTriangle
*/
template <typename ValueType>
void addRectangle (const Rectangle<ValueType>& rectangle)
void addRectangle (Rectangle<ValueType> rectangle)
{
addRectangle (static_cast<float> (rectangle.getX()), static_cast<float> (rectangle.getY()),
static_cast<float> (rectangle.getWidth()), static_cast<float> (rectangle.getHeight()));
@@ -355,7 +355,7 @@ public:
@see addRectangle, addTriangle
*/
template <typename ValueType>
void addRoundedRectangle (const Rectangle<ValueType>& rectangle, float cornerSizeX, float cornerSizeY)
void addRoundedRectangle (Rectangle<ValueType> rectangle, float cornerSizeX, float cornerSizeY)
{
addRoundedRectangle (static_cast<float> (rectangle.getX()), static_cast<float> (rectangle.getY()),
static_cast<float> (rectangle.getWidth()), static_cast<float> (rectangle.getHeight()),
@@ -367,7 +367,7 @@ public:
@see addRectangle, addTriangle
*/
template <typename ValueType>
void addRoundedRectangle (const Rectangle<ValueType>& rectangle, float cornerSize)
void addRoundedRectangle (Rectangle<ValueType> rectangle, float cornerSize)
{
addRoundedRectangle (rectangle, cornerSize, cornerSize);
}
@@ -534,13 +534,13 @@ public:
@see addArrow
*/
void addLineSegment (const Line<float>& line, float lineThickness);
void addLineSegment (Line<float> line, float lineThickness);
/** Adds a line with an arrowhead on the end.
The arrow is added as a new closed sub-path. (Any currently open paths will be left open).
@see PathStrokeType::createStrokeWithArrowheads
*/
void addArrow (const Line<float>& line,
void addArrow (Line<float> line,
float lineThickness,
float arrowheadWidth,
float arrowheadLength);
@@ -548,7 +548,7 @@ public:
/** Adds a polygon shape to the path.
@see addStar
*/
void addPolygon (const Point<float> centre,
void addPolygon (Point<float> centre,
int numberOfSides,
float radius,
float startAngle = 0.0f);
@@ -556,7 +556,7 @@ public:
/** Adds a star shape to the path.
@see addPolygon
*/
void addStar (const Point<float> centre,
void addStar (Point<float> centre,
int numberOfPoints,
float innerRadius,
float outerRadius,
@@ -572,8 +572,8 @@ public:
@param cornerSize the size of the rounded corners
@param arrowBaseWidth the width of the base of the arrow where it joins the main rectangle
*/
void addBubble (const Rectangle<float>& bodyArea,
const Rectangle<float>& maximumArea,
void addBubble (Rectangle<float> bodyArea,
Rectangle<float> maximumArea,
const Point<float> arrowTipPosition,
const float cornerSize,
const float arrowBaseWidth);
@@ -677,7 +677,7 @@ public:
@see applyTransform, scaleToFit
*/
AffineTransform getTransformToScaleToFit (const Rectangle<float>& area,
AffineTransform getTransformToScaleToFit (Rectangle<float> area,
bool preserveProportions,
Justification justificationType = Justification::centred) const;


+ 30
- 30
source/modules/juce_graphics/geometry/juce_Point.h View File

@@ -41,101 +41,101 @@ class Point
{
public:
/** Creates a point at the origin */
Point() noexcept : x(), y() {}
JUCE_CONSTEXPR Point() noexcept : x(), y() {}
/** Creates a copy of another point. */
Point (const Point& other) noexcept : x (other.x), y (other.y) {}
JUCE_CONSTEXPR Point (const Point& other) noexcept : x (other.x), y (other.y) {}
/** Creates a point from an (x, y) position. */
Point (ValueType initialX, ValueType initialY) noexcept : x (initialX), y (initialY) {}
JUCE_CONSTEXPR Point (ValueType initialX, ValueType initialY) noexcept : x (initialX), y (initialY) {}
//==============================================================================
/** Copies this point from another one. */
Point& operator= (const Point& other) noexcept { x = other.x; y = other.y; return *this; }
Point& operator= (const Point& other) noexcept { x = other.x; y = other.y; return *this; }
inline bool operator== (Point other) const noexcept { return x == other.x && y == other.y; }
inline bool operator!= (Point other) const noexcept { return x != other.x || y != other.y; }
JUCE_CONSTEXPR inline bool operator== (Point other) const noexcept { return x == other.x && y == other.y; }
JUCE_CONSTEXPR inline bool operator!= (Point other) const noexcept { return x != other.x || y != other.y; }
/** Returns true if the point is (0, 0). */
bool isOrigin() const noexcept { return x == ValueType() && y == ValueType(); }
JUCE_CONSTEXPR bool isOrigin() const noexcept { return x == ValueType() && y == ValueType(); }
/** Returns true if the coordinates are finite values. */
inline bool isFinite() const noexcept { return juce_isfinite(x) && juce_isfinite(y); }
JUCE_CONSTEXPR inline bool isFinite() const noexcept { return juce_isfinite(x) && juce_isfinite(y); }
/** Returns the point's x coordinate. */
inline ValueType getX() const noexcept { return x; }
JUCE_CONSTEXPR inline ValueType getX() const noexcept { return x; }
/** Returns the point's y coordinate. */
inline ValueType getY() const noexcept { return y; }
JUCE_CONSTEXPR inline ValueType getY() const noexcept { return y; }
/** Sets the point's x coordinate. */
inline void setX (ValueType newX) noexcept { x = newX; }
inline void setX (ValueType newX) noexcept { x = newX; }
/** Sets the point's y coordinate. */
inline void setY (ValueType newY) noexcept { y = newY; }
inline void setY (ValueType newY) noexcept { y = newY; }
/** Returns a point which has the same Y position as this one, but a new X. */
Point withX (ValueType newX) const noexcept { return Point (newX, y); }
JUCE_CONSTEXPR Point withX (ValueType newX) const noexcept { return Point (newX, y); }
/** Returns a point which has the same X position as this one, but a new Y. */
Point withY (ValueType newY) const noexcept { return Point (x, newY); }
JUCE_CONSTEXPR Point withY (ValueType newY) const noexcept { return Point (x, newY); }
/** Changes the point's x and y coordinates. */
void setXY (ValueType newX, ValueType newY) noexcept { x = newX; y = newY; }
void setXY (ValueType newX, ValueType newY) noexcept { x = newX; y = newY; }
/** Adds a pair of coordinates to this value. */
void addXY (ValueType xToAdd, ValueType yToAdd) noexcept { x += xToAdd; y += yToAdd; }
void addXY (ValueType xToAdd, ValueType yToAdd) noexcept { x += xToAdd; y += yToAdd; }
//==============================================================================
/** Returns a point with a given offset from this one. */
Point translated (ValueType deltaX, ValueType deltaY) const noexcept { return Point (x + deltaX, y + deltaY); }
JUCE_CONSTEXPR Point translated (ValueType deltaX, ValueType deltaY) const noexcept { return Point (x + deltaX, y + deltaY); }
/** Adds two points together */
Point operator+ (Point other) const noexcept { return Point (x + other.x, y + other.y); }
JUCE_CONSTEXPR Point operator+ (Point other) const noexcept { return Point (x + other.x, y + other.y); }
/** Adds another point's coordinates to this one */
Point& operator+= (Point other) noexcept { x += other.x; y += other.y; return *this; }
Point& operator+= (Point other) noexcept { x += other.x; y += other.y; return *this; }
/** Subtracts one points from another */
Point operator- (Point other) const noexcept { return Point (x - other.x, y - other.y); }
JUCE_CONSTEXPR Point operator- (Point other) const noexcept { return Point (x - other.x, y - other.y); }
/** Subtracts another point's coordinates to this one */
Point& operator-= (Point other) noexcept { x -= other.x; y -= other.y; return *this; }
Point& operator-= (Point other) noexcept { x -= other.x; y -= other.y; return *this; }
/** Multiplies two points together */
template <typename OtherType>
Point operator* (Point<OtherType> other) const noexcept { return Point ((ValueType) (x * other.x), (ValueType) (y * other.y)); }
JUCE_CONSTEXPR Point operator* (Point<OtherType> other) const noexcept { return Point ((ValueType) (x * other.x), (ValueType) (y * other.y)); }
/** Multiplies another point's coordinates to this one */
template <typename OtherType>
Point& operator*= (Point<OtherType> other) noexcept { *this = *this * other; return *this; }
Point& operator*= (Point<OtherType> other) noexcept { *this = *this * other; return *this; }
/** Divides one point by another */
template <typename OtherType>
Point operator/ (Point<OtherType> other) const noexcept { return Point ((ValueType) (x / other.x), (ValueType) (y / other.y)); }
JUCE_CONSTEXPR Point operator/ (Point<OtherType> other) const noexcept { return Point ((ValueType) (x / other.x), (ValueType) (y / other.y)); }
/** Divides this point's coordinates by another */
template <typename OtherType>
Point& operator/= (Point<OtherType> other) noexcept { *this = *this / other; return *this; }
Point& operator/= (Point<OtherType> other) noexcept { *this = *this / other; return *this; }
/** Returns a point whose coordinates are multiplied by a given scalar value. */
template <typename FloatType>
Point operator* (FloatType multiplier) const noexcept { return Point ((ValueType) (x * multiplier), (ValueType) (y * multiplier)); }
JUCE_CONSTEXPR Point operator* (FloatType multiplier) const noexcept { return Point ((ValueType) (x * multiplier), (ValueType) (y * multiplier)); }
/** Returns a point whose coordinates are divided by a given scalar value. */
template <typename FloatType>
Point operator/ (FloatType divisor) const noexcept { return Point ((ValueType) (x / divisor), (ValueType) (y / divisor)); }
JUCE_CONSTEXPR Point operator/ (FloatType divisor) const noexcept { return Point ((ValueType) (x / divisor), (ValueType) (y / divisor)); }
/** Multiplies the point's coordinates by a scalar value. */
template <typename FloatType>
Point& operator*= (FloatType multiplier) noexcept { x = (ValueType) (x * multiplier); y = (ValueType) (y * multiplier); return *this; }
Point& operator*= (FloatType multiplier) noexcept { x = (ValueType) (x * multiplier); y = (ValueType) (y * multiplier); return *this; }
/** Divides the point's coordinates by a scalar value. */
template <typename FloatType>
Point& operator/= (FloatType divisor) noexcept { x = (ValueType) (x / divisor); y = (ValueType) (y / divisor); return *this; }
Point& operator/= (FloatType divisor) noexcept { x = (ValueType) (x / divisor); y = (ValueType) (y / divisor); return *this; }
/** Returns the inverse of this point. */
Point operator-() const noexcept { return Point (-x, -y); }
JUCE_CONSTEXPR Point operator-() const noexcept { return Point (-x, -y); }
//==============================================================================
/** This type will be double if the Point's type is double, otherwise it will be float. */


+ 6
- 3
source/modules/juce_graphics/images/juce_Image.cpp View File

@@ -443,9 +443,12 @@ void Image::BitmapData::setPixelColour (const int x, const int y, Colour colour)
//==============================================================================
void Image::clear (const Rectangle<int>& area, Colour colourToClearTo)
{
const ScopedPointer<LowLevelGraphicsContext> g (image->createLowLevelContext());
g->setFill (colourToClearTo);
g->fillRect (area, true);
if (image != nullptr)
{
const ScopedPointer<LowLevelGraphicsContext> g (image->createLowLevelContext());
g->setFill (colourToClearTo);
g->fillRect (area, true);
}
}
//==============================================================================


+ 9
- 10
source/modules/juce_graphics/native/juce_RenderingHelpers.h View File

@@ -2295,11 +2295,11 @@ public:
}
}
void drawLine (const Line<float>& line)
void drawLine (Line<float> line)
{
Path p;
p.addLineSegment (line, 1.0f);
fillPath (p, AffineTransform());
fillPath (p, {});
}
void drawImage (const Image& sourceImage, const AffineTransform& trans)
@@ -2319,7 +2319,7 @@ public:
void renderImage (const Image& sourceImage, const AffineTransform& trans,
const BaseRegionType* const tiledFillClipRegion)
{
const AffineTransform t (transform.getTransformWith (trans));
auto t = transform.getTransformWith (trans);
const int alpha = fillType.colour.getAlpha();
@@ -2356,18 +2356,17 @@ public:
{
if (tiledFillClipRegion != nullptr)
{
tiledFillClipRegion->renderImageTransformed (getThis(), sourceImage, alpha, t, interpolationQuality, true);
tiledFillClipRegion->renderImageTransformed (getThis(), sourceImage, alpha,
t, interpolationQuality, true);
}
else
{
Path p;
p.addRectangle (sourceImage.getBounds());
typename BaseRegionType::Ptr c (clip->clone());
c = c->clipToPath (p, t);
if (c != nullptr)
c->renderImageTransformed (getThis(), sourceImage, alpha, t, interpolationQuality, false);
if (auto c = clip->clone()->clipToPath (p, t))
c->renderImageTransformed (getThis(), sourceImage, alpha,
t, interpolationQuality, false);
}
}
}
@@ -2386,7 +2385,7 @@ public:
ColourGradient g2 (*(fillType.gradient));
g2.multiplyOpacity (fillType.getOpacity());
AffineTransform t (transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f));
auto t = transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f);
const bool isIdentity = t.isOnlyTranslation();


+ 5
- 12
source/modules/juce_gui_basics/buttons/juce_Button.cpp View File

@@ -26,7 +26,7 @@
class Button::CallbackHelper : public Timer,
public ApplicationCommandManagerListener,
public ValueListener,
public Value::Listener,
public KeyListener
{
public:
@@ -394,15 +394,8 @@ void Button::handleCommandMessage (int commandId)
}
//==============================================================================
void Button::addListener (ButtonListener* const newListener)
{
buttonListeners.add (newListener);
}
void Button::removeListener (ButtonListener* const listener)
{
buttonListeners.remove (listener);
}
void Button::addListener (Listener* l) { buttonListeners.add (l); }
void Button::removeListener (Listener* l) { buttonListeners.remove (l); }
void Button::sendClickMessage (const ModifierKeys& modifiers)
{
@@ -420,7 +413,7 @@ void Button::sendClickMessage (const ModifierKeys& modifiers)
clicked (modifiers);
if (! checker.shouldBailOut())
buttonListeners.callChecked (checker, &ButtonListener::buttonClicked, this); // (can't use Button::Listener due to idiotic VC2005 bug)
buttonListeners.callChecked (checker, &Button::Listener::buttonClicked, this);
}
void Button::sendStateMessage()
@@ -430,7 +423,7 @@ void Button::sendStateMessage()
buttonStateChanged();
if (! checker.shouldBailOut())
buttonListeners.callChecked (checker, &ButtonListener::buttonStateChanged, this);
buttonListeners.callChecked (checker, &Button::Listener::buttonStateChanged, this);
}
//==============================================================================


+ 1
- 1
source/modules/juce_gui_basics/buttons/juce_Button.h View File

@@ -397,7 +397,7 @@ protected:
Subclasses can override this to perform whatever they actions they need
to do.
Alternatively, a ButtonListener can be added to the button, and these listeners
Alternatively, a Button::Listener can be added to the button, and these listeners
will be called when the click occurs.
@see triggerClick


+ 6
- 9
source/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp View File

@@ -25,10 +25,7 @@
*/
DrawableButton::DrawableButton (const String& name, const DrawableButton::ButtonStyle buttonStyle)
: Button (name),
style (buttonStyle),
currentImage (nullptr),
edgeIndent (3)
: Button (name), style (buttonStyle)
{
}
@@ -85,12 +82,12 @@ void DrawableButton::setEdgeIndent (const int numPixelsIndent)
Rectangle<float> DrawableButton::getImageBounds() const
{
Rectangle<int> r (getLocalBounds());
auto r = getLocalBounds();
if (style != ImageStretched)
{
int indentX = jmin (edgeIndent, proportionOfWidth (0.3f));
int indentY = jmin (edgeIndent, proportionOfHeight (0.3f));
auto indentX = jmin (edgeIndent, proportionOfWidth (0.3f));
auto indentY = jmin (edgeIndent, proportionOfHeight (0.3f));
if (style == ImageOnButtonBackground)
{
@@ -178,7 +175,7 @@ void DrawableButton::paintButton (Graphics& g,
const bool isMouseOverButton,
const bool isButtonDown)
{
LookAndFeel& lf = getLookAndFeel();
auto& lf = getLookAndFeel();
if (style == ImageOnButtonBackground)
lf.drawButtonBackground (g, *this,
@@ -217,7 +214,7 @@ Drawable* DrawableButton::getOverImage() const noexcept
Drawable* DrawableButton::getDownImage() const noexcept
{
if (Drawable* const d = getToggleState() ? downImageOn : downImage)
if (auto* d = getToggleState() ? downImageOn.get() : downImage.get())
return d;
return getOverImage();


+ 5
- 2
source/modules/juce_gui_basics/buttons/juce_DrawableButton.h View File

@@ -122,6 +122,9 @@ public:
*/
void setEdgeIndent (int numPixelsIndent);
/** Returns the current edge indent size. */
int getEdgeIndent() const noexcept { return edgeIndent; }
//==============================================================================
/** Returns the image that the button is currently displaying. */
Drawable* getCurrentImage() const noexcept;
@@ -179,8 +182,8 @@ private:
ButtonStyle style;
ScopedPointer<Drawable> normalImage, overImage, downImage, disabledImage,
normalImageOn, overImageOn, downImageOn, disabledImageOn;
Drawable* currentImage;
int edgeIndent;
Drawable* currentImage = nullptr;
int edgeIndent = 3;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DrawableButton)
};

+ 50
- 54
source/modules/juce_gui_basics/components/juce_Component.cpp View File

@@ -31,10 +31,7 @@ Component* Component::currentlyFocusedComponent = nullptr;
class Component::MouseListenerList
{
public:
MouseListenerList() noexcept
: numDeepMouseListeners (0)
{
}
MouseListenerList() noexcept {}
void addListener (MouseListener* const newListener, const bool wantsEventsForAllNestedChildComponents)
{
@@ -54,7 +51,7 @@ public:
void removeListener (MouseListener* const listenerToRemove)
{
const int index = listeners.indexOf (listenerToRemove);
auto index = listeners.indexOf (listenerToRemove);
if (index >= 0)
{
@@ -86,20 +83,21 @@ public:
for (Component* p = comp.parentComponent; p != nullptr; p = p->parentComponent)
{
auto* list = p->mouseListeners.get();
if (list != nullptr && list->numDeepMouseListeners > 0)
if (auto* list = p->mouseListeners.get())
{
BailOutChecker2 checker2 (checker, p);
for (int i = list->numDeepMouseListeners; --i >= 0;)
if (list->numDeepMouseListeners > 0)
{
(list->listeners.getUnchecked(i)->*eventMethod) (e);
BailOutChecker2 checker2 (checker, p);
if (checker2.shouldBailOut())
return;
for (int i = list->numDeepMouseListeners; --i >= 0;)
{
(list->listeners.getUnchecked(i)->*eventMethod) (e);
if (checker2.shouldBailOut())
return;
i = jmin (i, list->numDeepMouseListeners);
i = jmin (i, list->numDeepMouseListeners);
}
}
}
}
@@ -123,20 +121,21 @@ public:
for (Component* p = comp.parentComponent; p != nullptr; p = p->parentComponent)
{
MouseListenerList* const list = p->mouseListeners;
if (list != nullptr && list->numDeepMouseListeners > 0)
if (auto* list = p->mouseListeners.get())
{
BailOutChecker2 checker2 (checker, p);
for (int i = list->numDeepMouseListeners; --i >= 0;)
if (list->numDeepMouseListeners > 0)
{
list->listeners.getUnchecked(i)->mouseWheelMove (e, wheel);
BailOutChecker2 checker2 (checker, p);
if (checker2.shouldBailOut())
return;
for (int i = list->numDeepMouseListeners; --i >= 0;)
{
list->listeners.getUnchecked(i)->mouseWheelMove (e, wheel);
if (checker2.shouldBailOut())
return;
i = jmin (i, list->numDeepMouseListeners);
i = jmin (i, list->numDeepMouseListeners);
}
}
}
}
@@ -144,7 +143,7 @@ public:
private:
Array<MouseListener*> listeners;
int numDeepMouseListeners;
int numDeepMouseListeners = 0;
struct BailOutChecker2
{
@@ -175,11 +174,10 @@ struct FocusRestorer
~FocusRestorer()
{
if (lastFocus != nullptr && ! lastFocus->isCurrentlyBlockedByAnotherModalComponent())
{
if (lastFocus != nullptr && lastFocus->isShowing())
lastFocus->grabKeyboardFocus();
}
if (lastFocus != nullptr
&& lastFocus->isShowing()
&& ! lastFocus->isCurrentlyBlockedByAnotherModalComponent())
lastFocus->grabKeyboardFocus();
}
WeakReference<Component> lastFocus;
@@ -326,7 +324,7 @@ struct Component::ComponentHelpers
if (comp.isOnDesktop())
{
if (ComponentPeer* peer = comp.getPeer())
if (auto* peer = comp.getPeer())
pointInParentSpace = ScalingHelpers::unscaledScreenPosToScaled
(comp, peer->globalToLocal (ScalingHelpers::scaledScreenPosToUnscaled (pointInParentSpace)));
else
@@ -345,7 +343,7 @@ struct Component::ComponentHelpers
{
if (comp.isOnDesktop())
{
if (ComponentPeer* peer = comp.getPeer())
if (auto* peer = comp.getPeer())
pointInLocalSpace = ScalingHelpers::unscaledScreenPosToScaled
(peer->localToGlobal (ScalingHelpers::scaledScreenPosToUnscaled (comp, pointInLocalSpace)));
else
@@ -608,7 +606,7 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo)
// don't use getPeer(), so that we only get the peer that's specifically
// for this comp, and not for one of its parents.
ComponentPeer* peer = ComponentPeer::getPeerFor (this);
auto* peer = ComponentPeer::getPeerFor (this);
if (peer == nullptr || styleWanted != peer->getStyleFlags())
{
@@ -622,7 +620,7 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo)
jmax (1, getHeight()));
#endif
const Point<int> topLeft (getScreenPosition());
auto topLeft = getScreenPosition();
bool wasFullscreen = false;
bool wasMinimised = false;
@@ -703,7 +701,7 @@ void Component::removeFromDesktop()
if (flags.hasHeavyweightPeerFlag)
{
ComponentPeer* const peer = ComponentPeer::getPeerFor (this);
auto* peer = ComponentPeer::getPeerFor (this);
jassert (peer != nullptr);
flags.hasHeavyweightPeerFlag = false;
@@ -753,7 +751,7 @@ void Component::setOpaque (const bool shouldBeOpaque)
flags.opaqueFlag = shouldBeOpaque;
if (flags.hasHeavyweightPeerFlag)
if (const ComponentPeer* const peer = ComponentPeer::getPeerFor (this))
if (auto* peer = ComponentPeer::getPeerFor (this))
addToDesktop (peer->getStyleFlags()); // recreates the heavyweight window
repaint();
@@ -955,8 +953,8 @@ void Component::toBehind (Component* const other)
{
auto* us = getPeer();
auto* them = other->getPeer();
jassert (us != nullptr && them != nullptr);
if (us != nullptr && them != nullptr)
us->toBehind (them);
}
@@ -1008,7 +1006,7 @@ void Component::setAlwaysOnTop (const bool shouldStayOnTop)
{
// some kinds of peer can't change their always-on-top status, so
// for these, we'll need to create a new window
const int oldFlags = peer->getStyleFlags();
auto oldFlags = peer->getStyleFlags();
removeFromDesktop();
addToDesktop (oldFlags);
}
@@ -1248,8 +1246,8 @@ void Component::setBounds (const String& newBoundsExpression)
void Component::setBoundsRelative (const float x, const float y,
const float w, const float h)
{
const int pw = getParentWidth();
const int ph = getParentHeight();
auto pw = getParentWidth();
auto ph = getParentHeight();
setBounds (roundToInt (x * pw),
roundToInt (y * ph),
@@ -1448,7 +1446,7 @@ Component* Component::getComponentAt (Point<int> position)
Component* Component::getComponentAt (const int x, const int y)
{
return getComponentAt (Point<int> (x, y));
return getComponentAt ({ x, y });
}
//==============================================================================
@@ -1873,7 +1871,7 @@ void Component::repaint()
void Component::repaint (const int x, const int y, const int w, const int h)
{
internalRepaint (Rectangle<int> (x, y, w, h));
internalRepaint ({ x, y, w, h });
}
void Component::repaint (Rectangle<int> area)
@@ -1964,7 +1962,7 @@ void Component::paintComponentAndChildren (Graphics& g)
{
g.saveState();
if (! (ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, Point<int>()) && g.isClipEmpty()))
if (! (ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, {}) && g.isClipEmpty()))
paint (g);
g.restoreState();
@@ -2168,7 +2166,7 @@ void Component::sendLookAndFeelChange()
Colour Component::findColour (const int colourId, const bool inheritFromParent) const
{
if (const var* const v = properties.getVarPointer (ComponentHelpers::getColourPropertyId (colourId)))
if (auto* v = properties.getVarPointer (ComponentHelpers::getColourPropertyId (colourId)))
return Colour ((uint32) static_cast<int> (*v));
if (inheritFromParent && parentComponent != nullptr
@@ -2201,7 +2199,7 @@ void Component::copyAllExplicitColoursTo (Component& target) const
for (int i = properties.size(); --i >= 0;)
{
const Identifier name (properties.getName(i));
auto name = properties.getName(i);
if (name.toString().startsWith ("jcclr_"))
if (target.properties.set (name, properties [name]))
@@ -2418,7 +2416,7 @@ void Component::internalMouseExit (MouseInputSource source, Point<float> relativ
void Component::internalMouseDown (MouseInputSource source, Point<float> relativePos, Time time,
float pressure, float orientation, float rotation, float tiltX, float tiltY)
{
Desktop& desktop = Desktop::getInstance();
auto& desktop = Desktop::getInstance();
BailOutChecker checker (this);
if (isCurrentlyBlockedByAnotherModalComponent())
@@ -2445,7 +2443,7 @@ void Component::internalMouseDown (MouseInputSource source, Point<float> relativ
flags.mouseDownWasBlocked = false;
for (Component* c = this; c != nullptr; c = c->parentComponent)
for (auto* c = this; c != nullptr; c = c->parentComponent)
{
if (c->isBroughtToFrontOnMouseClick())
{
@@ -2502,7 +2500,7 @@ void Component::internalMouseUp (MouseInputSource source, Point<float> relativeP
if (checker.shouldBailOut())
return;
Desktop& desktop = Desktop::getInstance();
auto& desktop = Desktop::getInstance();
desktop.getMouseListeners().callChecked (checker, &MouseListener::mouseUp, me);
MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseUp, me);
@@ -2549,7 +2547,7 @@ void Component::internalMouseDrag (MouseInputSource source, Point<float> relativ
void Component::internalMouseMove (MouseInputSource source, Point<float> relativePos, Time time)
{
Desktop& desktop = Desktop::getInstance();
auto& desktop = Desktop::getInstance();
if (isCurrentlyBlockedByAnotherModalComponent())
{
@@ -2578,7 +2576,7 @@ void Component::internalMouseMove (MouseInputSource source, Point<float> relativ
void Component::internalMouseWheel (MouseInputSource source, Point<float> relativePos,
Time time, const MouseWheelDetails& wheel)
{
Desktop& desktop = Desktop::getInstance();
auto& desktop = Desktop::getInstance();
BailOutChecker checker (this);
const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
@@ -2854,9 +2852,7 @@ void Component::moveKeyboardFocusToSibling (const bool moveToNext)
if (parentComponent != nullptr)
{
ScopedPointer<KeyboardFocusTraverser> traverser (createFocusTraverser());
if (traverser != nullptr)
if (ScopedPointer<KeyboardFocusTraverser> traverser = createFocusTraverser())
{
auto* nextComp = moveToNext ? traverser->getNextComponent (this)
: traverser->getPreviousComponent (this);


+ 2
- 2
source/modules/juce_gui_basics/components/juce_Component.h View File

@@ -762,8 +762,8 @@ public:
template <class TargetClass>
TargetClass* findParentComponentOfClass() const
{
for (Component* p = parentComponent; p != nullptr; p = p->parentComponent)
if (TargetClass* const target = dynamic_cast<TargetClass*> (p))
for (auto* p = parentComponent; p != nullptr; p = p->parentComponent)
if (auto* target = dynamic_cast<TargetClass*> (p))
return target;
return nullptr;


+ 5
- 5
source/modules/juce_gui_basics/components/juce_Desktop.h View File

@@ -289,13 +289,13 @@ public:
void beginDragAutoRepeat (int millisecondsBetweenCallbacks);
//==============================================================================
/** In a tablet device which can be turned around, this is used to inidicate the orientation. */
/** In a tablet/mobile device which can be turned around, this is used to indicate the orientation. */
enum DisplayOrientation
{
upright = 1, /**< Indicates that the display is the normal way up. */
upsideDown = 2, /**< Indicates that the display is upside-down. */
rotatedClockwise = 4, /**< Indicates that the display is turned 90 degrees clockwise from its upright position. */
rotatedAntiClockwise = 8, /**< Indicates that the display is turned 90 degrees anti-clockwise from its upright position. */
upright = 1, /**< Indicates that the device is the normal way up. */
upsideDown = 2, /**< Indicates that the device is upside-down. */
rotatedClockwise = 4, /**< Indicates that the device is turned 90 degrees clockwise from its upright position. */
rotatedAntiClockwise = 8, /**< Indicates that the device is turned 90 degrees anti-clockwise from its upright position. */
allOrientations = 1 + 2 + 4 + 8 /**< A combination of all the orientation values */
};


+ 1
- 34
source/modules/juce_gui_basics/drawables/juce_SVGParser.cpp View File

@@ -75,20 +75,6 @@ public:
}
};
struct UseShapeOp
{
const SVGState* state;
Path* sourcePath;
AffineTransform* transform;
Drawable* target;
bool operator() (const XmlPath& xmlPath)
{
target = state->parseShape (xmlPath, *sourcePath, true, transform);
return target != nullptr;
}
};
struct UseTextOp
{
const SVGState* state;
@@ -690,22 +676,6 @@ private:
}
//==============================================================================
Drawable* useShape (const XmlPath& xml, Path& path) const
{
auto translation = AffineTransform::translation ((float) xml->getDoubleAttribute ("x", 0.0),
(float) xml->getDoubleAttribute ("y", 0.0));
UseShapeOp op = { this, &path, &translation, nullptr };
auto linkedID = getLinkedID (xml);
if (linkedID.isNotEmpty())
topLevelXml.applyOperationToChildWithID (linkedID, op);
return op.target;
}
Drawable* parseShape (const XmlPath& xml, Path& path,
const bool shouldParseTransform = true,
AffineTransform* additonalTransform = nullptr) const
@@ -718,9 +688,6 @@ private:
return newState.parseShape (xml, path, false, additonalTransform);
}
if (xml->hasTagName ("use"))
return useShape (xml, path);
auto dp = new DrawablePath();
setCommonAttributes (*dp, xml);
dp->setFill (Colours::transparentBlack);
@@ -1159,7 +1126,7 @@ private:
if (getStyleAttribute (xml, "font-weight").containsIgnoreCase ("bold"))
f.setBold (true);
return f.withPointHeight (getCoordLength (getStyleAttribute (xml, "font-size"), 1.0f));
return f.withPointHeight (getCoordLength (getStyleAttribute (xml, "font-size", "15"), 1.0f));
}
//==============================================================================


+ 3
- 3
source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h View File

@@ -39,9 +39,9 @@
*/
class JUCE_API FileBrowserComponent : public Component,
private FileBrowserListener,
private TextEditorListener,
private ButtonListener,
private ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug)
private TextEditor::Listener,
private Button::Listener,
private ComboBox::Listener,
private FileFilter,
private Timer
{


+ 1
- 1
source/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h View File

@@ -63,7 +63,7 @@
@see FileChooser
*/
class JUCE_API FileChooserDialogBox : public ResizableWindow,
private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
private Button::Listener,
private FileBrowserListener
{
public:


+ 1
- 1
source/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h View File

@@ -37,7 +37,7 @@
class JUCE_API FileSearchPathListComponent : public Component,
public SettableTooltipClient,
public FileDragAndDropTarget,
private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
private Button::Listener,
private ListBoxModel
{
public:


+ 2
- 2
source/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h View File

@@ -65,8 +65,8 @@ class JUCE_API FilenameComponent : public Component,
public SettableTooltipClient,
public FileDragAndDropTarget,
private AsyncUpdater,
private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
private ComboBoxListener
private Button::Listener,
private ComboBox::Listener
{
public:
//==============================================================================


+ 8
- 0
source/modules/juce_gui_basics/juce_gui_basics.cpp View File

@@ -129,6 +129,9 @@
#undef KeyPress
#endif
#include <map>
#include <set>
//==============================================================================
namespace juce
{
@@ -256,6 +259,11 @@ extern bool juce_areThereAnyAlwaysOnTopWindows();
// these classes are C++11-only
#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS
#include "layout/juce_FlexBox.cpp"
#include "layout/juce_GridItem.cpp"
#include "layout/juce_Grid.cpp"
#if JUCE_UNIT_TESTS
#include "layout/juce_GridUnitTests.cpp"
#endif
#endif
#if JUCE_IOS || JUCE_WINDOWS


+ 9
- 0
source/modules/juce_gui_basics/juce_gui_basics.h View File

@@ -157,6 +157,7 @@ class KeyPressMappingSet;
class ApplicationCommandManagerListener;
class DrawableButton;
class FlexBox;
class Grid;
#include "mouse/juce_MouseCursor.h"
#include "mouse/juce_MouseListener.h"
@@ -294,6 +295,14 @@ class FlexBox;
#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS
#include "layout/juce_FlexItem.h"
#include "layout/juce_FlexBox.h"
#include "layout/juce_GridItem.h"
#include "layout/juce_Grid.h"
constexpr Grid::Px operator"" _px (long double px) { return Grid::Px { px }; }
constexpr Grid::Px operator"" _px (unsigned long long px) { return Grid::Px { px }; }
constexpr Grid::Fr operator"" _fr (unsigned long long fr) { return Grid::Fr { fr }; }
#endif
}

+ 1025
- 0
source/modules/juce_gui_basics/layout/juce_Grid.cpp
File diff suppressed because it is too large
View File


+ 174
- 0
source/modules/juce_gui_basics/layout/juce_Grid.h View File

@@ -0,0 +1,174 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-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.
==============================================================================
*/
/**
Container that handles geometry for grid layouts (fixed columns and rows) using a set of declarative rules.
Implemented from the `CSS Grid Layout` specification as described at:
https://css-tricks.com/snippets/css/complete-guide-grid/
@see GridItem
*/
class JUCE_API Grid
{
public:
//==============================================================================
/** A size in pixels */
struct Px
{
explicit Px (float p) : pixels (static_cast<long double>(p)) { /*sta (p >= 0.0f);*/ }
explicit Px (int p) : pixels (static_cast<long double>(p)) { /*sta (p >= 0.0f);*/ }
explicit constexpr Px (long double p) : pixels (p) {}
explicit constexpr Px (unsigned long long p) : pixels (static_cast<long double>(p)) {}
long double pixels;
};
/** A fractional ratio integer */
struct Fr
{
explicit Fr (int f) : fraction (static_cast<unsigned long long> (f)) {}
explicit constexpr Fr (unsigned long long p) : fraction (p) {}
unsigned long long fraction;
};
//==============================================================================
/** */
struct TrackInfo
{
/** Creates a track with auto dimension. */
TrackInfo() noexcept;
/** */
TrackInfo (Px sizeInPixels) noexcept;
/** */
TrackInfo (Fr fractionOfFreeSpace) noexcept;
/** */
TrackInfo (Px sizeInPixels, const juce::String& endLineNameToUse) noexcept;
/** */
TrackInfo (Fr fractionOfFreeSpace, const juce::String& endLineNameToUse) noexcept;
/** */
TrackInfo (const juce::String& startLineNameToUse, Px sizeInPixels) noexcept;
/** */
TrackInfo (const juce::String& startLineNameToUse, Fr fractionOfFreeSpace) noexcept;
/** */
TrackInfo (const juce::String& startLineNameToUse, Px sizeInPixels, const juce::String& endLineNameToUse) noexcept;
/** */
TrackInfo (const juce::String& startLineNameToUse, Fr fractionOfFreeSpace, const juce::String& endLineNameToUse) noexcept;
private:
friend class Grid;
friend class GridItem;
float size = 0; // Either a fraction or an absolute size in pixels
bool isFraction = false;
bool hasKeyword = false;
juce::String startLineName, endLineName;
};
//==============================================================================
/** */
enum class JustifyItems : int { start = 0, end, center, stretch };
/** */
enum class AlignItems : int { start = 0, end, center, stretch };
/** */
enum class JustifyContent { start, end, center, stretch, spaceAround, spaceBetween, spaceEvenly };
/** */
enum class AlignContent { start, end, center, stretch, spaceAround, spaceBetween, spaceEvenly };
/** */
enum class AutoFlow { row, column, rowDense, columnDense };
//==============================================================================
/** */
Grid() noexcept;
/** Destructor */
~Grid() noexcept;
//==============================================================================
/** */
JustifyItems justifyItems = JustifyItems::stretch;
/** */
AlignItems alignItems = AlignItems::stretch;
/** */
JustifyContent justifyContent = JustifyContent::stretch;
/** */
AlignContent alignContent = AlignContent::stretch;
/** */
AutoFlow autoFlow = AutoFlow::row;
//==============================================================================
/** */
juce::Array<TrackInfo> templateColumns;
/** */
juce::Array<TrackInfo> templateRows;
/** Template areas */
juce::StringArray templateAreas;
/** */
TrackInfo autoRows;
/** */
TrackInfo autoColumns;
/** */
Px columnGap { 0 };
/** */
Px rowGap { 0 };
/** */
void setGap (Px sizeInPixels) noexcept { rowGap = columnGap = sizeInPixels; }
//==============================================================================
/** */
juce::Array<GridItem> items;
//==============================================================================
/** */
void performLayout (juce::Rectangle<int>);
//==============================================================================
/** */
int getNumberOfColumns() const noexcept { return templateColumns.size(); }
/** */
int getNumberOfRows() const noexcept { return templateRows.size(); }
private:
//==============================================================================
struct SizeCalculation;
struct PlacementHelpers;
struct AutoPlacement;
struct BoxAlignment;
};

+ 182
- 0
source/modules/juce_gui_basics/layout/juce_GridItem.cpp View File

@@ -0,0 +1,182 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-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.
==============================================================================
*/
GridItem::Property::Property() noexcept : isAuto (true)
{
}
GridItem::Property::Property (GridItem::Keyword keyword) noexcept : isAuto (keyword == GridItem::Keyword::autoValue)
{
jassert (keyword == GridItem::Keyword::autoValue);
}
GridItem::Property::Property (const char* lineNameToUse) noexcept : GridItem::Property (juce::String (lineNameToUse))
{
}
GridItem::Property::Property (const juce::String& lineNameToUse) noexcept : name (lineNameToUse), number (1)
{
}
GridItem::Property::Property (int numberToUse) noexcept : number (numberToUse)
{
}
GridItem::Property::Property (int numberToUse, const juce::String& lineNameToUse) noexcept
: name (lineNameToUse), number (numberToUse)
{
}
GridItem::Property::Property (Span spanToUse) noexcept
: name (spanToUse.name), number (spanToUse.number), isSpan (true)
{
}
//==============================================================================
GridItem::Margin::Margin() noexcept : left(), right(), top(), bottom()
{}
GridItem::Margin::Margin (int v) noexcept : GridItem::Margin::Margin (static_cast<float> (v))
{}
GridItem::Margin::Margin (float v) noexcept : left (v), right (v), top (v), bottom (v)
{}
GridItem::Margin::Margin (float t, float r, float b, float l) noexcept : left (l), right (r), top (t), bottom (b)
{}
//==============================================================================
GridItem::GridItem() noexcept {}
GridItem::~GridItem() noexcept {}
GridItem::GridItem (juce::Component& componentToUse) noexcept : associatedComponent (&componentToUse) {}
GridItem::GridItem (juce::Component* componentToUse) noexcept : associatedComponent (componentToUse) {}
void GridItem::setArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd)
{
column.start = columnStart;
column.end = columnEnd;
row.start = rowStart;
row.end = rowEnd;
}
void GridItem::setArea (Property rowStart, Property columnStart)
{
column.start = columnStart;
row.start = rowStart;
}
void GridItem::setArea (const juce::String& areaName)
{
area = areaName;
}
GridItem GridItem::withArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd) const noexcept
{
auto gi = *this;
gi.setArea (rowStart, columnStart, rowEnd, columnEnd);
return gi;
}
GridItem GridItem::withArea (Property rowStart, Property columnStart) const noexcept
{
auto gi = *this;
gi.setArea (rowStart, columnStart);
return gi;
}
GridItem GridItem::withArea (const juce::String& areaName) const noexcept
{
auto gi = *this;
gi.setArea (areaName);
return gi;
}
GridItem GridItem::withRow (StartAndEndProperty newRow) const noexcept
{
auto gi = *this;
gi.row = newRow;
return gi;
}
GridItem GridItem::withColumn (StartAndEndProperty newColumn) const noexcept
{
auto gi = *this;
gi.column = newColumn;
return gi;
}
GridItem GridItem::withAlignSelf (AlignSelf newAlignSelf) const noexcept
{
auto gi = *this;
gi.alignSelf = newAlignSelf;
return gi;
}
GridItem GridItem::withJustifySelf (JustifySelf newJustifySelf) const noexcept
{
auto gi = *this;
gi.justifySelf = newJustifySelf;
return gi;
}
GridItem GridItem::withWidth (float newWidth) const noexcept
{
auto gi = *this;
gi.width = newWidth;
return gi;
}
GridItem GridItem::withHeight (float newHeight) const noexcept
{
auto gi = *this;
gi.height = newHeight;
return gi;
}
GridItem GridItem::withSize (float newWidth, float newHeight) const noexcept
{
auto gi = *this;
gi.width = newWidth;
gi.height = newHeight;
return gi;
}
GridItem GridItem::withMargin (Margin newHeight) const noexcept
{
auto gi = *this;
gi.margin = newHeight;
return gi;
}
GridItem GridItem::withOrder (int newOrder) const noexcept
{
auto gi = *this;
gi.order = newOrder;
return gi;
}

+ 223
- 0
source/modules/juce_gui_basics/layout/juce_GridItem.h View File

@@ -0,0 +1,223 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-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.
==============================================================================
*/
/**
Defines an item in a Grid
@see Grid
*/
class JUCE_API GridItem
{
public:
enum class Keyword { autoValue };
//==============================================================================
/** */
struct Span
{
explicit Span (int numberToUse) noexcept : number (numberToUse)
{
/* Span must be at least one and positive */
jassert (numberToUse > 0);
}
explicit Span (int numberToUse, const juce::String& nameToUse) : Span (numberToUse)
{
/* Name must not be empty */
jassert (nameToUse.isNotEmpty());
name = nameToUse;
}
explicit Span (const juce::String& nameToUse) : name (nameToUse)
{
/* Name must not be empty */
jassert (nameToUse.isNotEmpty());
}
int number = 1;
juce::String name;
};
//==============================================================================
/** */
struct Property
{
/** */
Property() noexcept;
/** */
Property (Keyword keyword) noexcept;
/** */
Property (const char* lineNameToUse) noexcept;
/** */
Property (const juce::String& lineNameToUse) noexcept;
/** */
Property (int numberToUse) noexcept;
/** */
Property (int numberToUse, const juce::String& lineNameToUse) noexcept;
/** */
Property (Span spanToUse) noexcept;
private:
bool hasSpan() const noexcept { return isSpan && ! isAuto; }
bool hasAbsolute() const noexcept { return ! (isSpan || isAuto); }
bool hasAuto() const noexcept { return isAuto; }
bool hasName() const noexcept { return name.isNotEmpty(); }
friend class Grid;
juce::String name;
int number = 1; /** Either an absolute line number or number of lines to span across. */
bool isSpan = false;
bool isAuto = false;
};
//==============================================================================
/** */
struct StartAndEndProperty { Property start, end; };
//==============================================================================
/** */
enum class JustifySelf : int { start = 0, end, center, stretch, autoValue };
/** */
enum class AlignSelf : int { start = 0, end, center, stretch, autoValue };
/** */
GridItem() noexcept;
/** */
GridItem (juce::Component& componentToUse) noexcept;
/** */
GridItem (juce::Component* componentToUse) noexcept;
/** Destructor. */
~GridItem() noexcept;
//==============================================================================
/** */
juce::Component* associatedComponent = nullptr;
//==============================================================================
/** */
int order = 0;
/** */
JustifySelf justifySelf = JustifySelf::autoValue;
/** */
AlignSelf alignSelf = AlignSelf::autoValue;
/** */
StartAndEndProperty column = { Keyword::autoValue, Keyword::autoValue };
/** */
StartAndEndProperty row = { Keyword::autoValue, Keyword::autoValue };
/** */
juce::String area;
//==============================================================================
enum
{
useDefaultValue = -2, /* TODO: useDefaultValue should be named useAuto */
notAssigned = -1
};
/* TODO: move all of this into a common class that is shared with the FlexItem */
float width = notAssigned;
float minWidth = 0;
float maxWidth = notAssigned;
float height = notAssigned;
float minHeight = 0;
float maxHeight = notAssigned;
struct Margin
{
Margin() noexcept;
Margin (int size) noexcept;
Margin (float size) noexcept;
Margin (float top, float right, float bottom, float left) noexcept; /**< Creates a margin with these sizes. */
float left;
float right;
float top;
float bottom;
};
/** */
Margin margin;
/** */
juce::Rectangle<float> currentBounds;
/** Short-hand */
void setArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd);
/** Short-hand, span of 1 by default */
void setArea (Property rowStart, Property columnStart);
/** Short-hand */
void setArea (const juce::String& areaName);
/** Short-hand */
GridItem withArea (Property rowStart, Property columnStart, Property rowEnd, Property columnEnd) const noexcept;
/** Short-hand, span of 1 by default */
GridItem withArea (Property rowStart, Property columnStart) const noexcept;
/** Short-hand */
GridItem withArea (const juce::String& areaName) const noexcept;
/** */
GridItem withRow (StartAndEndProperty row) const noexcept;
/** */
GridItem withColumn (StartAndEndProperty column) const noexcept;
/** */
GridItem withAlignSelf (AlignSelf newAlignSelf) const noexcept;
/** */
GridItem withJustifySelf (JustifySelf newJustifySelf) const noexcept;
/** */
GridItem withWidth (float newWidth) const noexcept;
/** */
GridItem withHeight (float newHeight) const noexcept;
/** */
GridItem withSize (float newWidth, float newHeight) const noexcept;
/** */
GridItem withMargin (Margin newMargin) const noexcept;
/** Returns a copy of this object with a new order. */
GridItem withOrder (int newOrder) const noexcept;
};

+ 257
- 0
source/modules/juce_gui_basics/layout/juce_GridUnitTests.cpp View File

@@ -0,0 +1,257 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-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.
==============================================================================
*/
struct GridTests : public juce::UnitTest
{
GridTests() : juce::UnitTest ("Grid class") {}
void runTest() override
{
using Fr = juce::Grid::Fr;
using Tr = juce::Grid::TrackInfo;
using Rect = juce::Rectangle<float>;
using Grid = juce::Grid;
{
Grid grid;
grid.templateColumns.add (Tr (1_fr));
grid.templateRows.addArray ({ Tr (20_px), Tr (1_fr) });
grid.items.addArray ({ GridItem().withArea (1, 1),
GridItem().withArea (2, 1) });
grid.performLayout (juce::Rectangle<int> (200, 400));
beginTest ("Layout calculation test: 1 column x 2 rows: no gap");
expect (grid.items[0].currentBounds == Rect (0.0f, 0.0f, 200.f, 20.0f));
expect (grid.items[1].currentBounds == Rect (0.0f, 20.0f, 200.f, 380.0f));
grid.templateColumns.add (Tr (50_px));
grid.templateRows.add (Tr (2_fr));
grid.items.addArray ( { GridItem().withArea (1, 2),
GridItem().withArea (2, 2),
GridItem().withArea (3, 1),
GridItem().withArea (3, 2) });
grid.performLayout (juce::Rectangle<int> (150, 170));
beginTest ("Layout calculation test: 2 columns x 3 rows: no gap");
expect (grid.items[0].currentBounds == Rect (0.0f, 0.0f, 100.0f, 20.0f));
expect (grid.items[1].currentBounds == Rect (0.0f, 20.0f, 100.0f, 50.0f));
expect (grid.items[2].currentBounds == Rect (100.0f, 0.0f, 50.0f, 20.0f));
expect (grid.items[3].currentBounds == Rect (100.0f, 20.0f, 50.0f, 50.0f));
expect (grid.items[4].currentBounds == Rect (0.0f, 70.0f, 100.0f, 100.0f));
expect (grid.items[5].currentBounds == Rect (100.0f, 70.0f, 50.0f, 100.0f));
grid.columnGap = 20_px;
grid.rowGap = 10_px;
grid.performLayout (juce::Rectangle<int> (200, 310));
beginTest ("Layout calculation test: 2 columns x 3 rows: rowGap of 10 and columnGap of 20");
expect (grid.items[0].currentBounds == Rect (0.0f, 0.0f, 130.0f, 20.0f));
expect (grid.items[1].currentBounds == Rect (0.0f, 30.0f, 130.0f, 90.0f));
expect (grid.items[2].currentBounds == Rect (150.0f, 0.0f, 50.0f, 20.0f));
expect (grid.items[3].currentBounds == Rect (150.0f, 30.0f, 50.0f, 90.0f));
expect (grid.items[4].currentBounds == Rect (0.0f, 130.0f, 130.0f, 180.0f));
expect (grid.items[5].currentBounds == Rect (150.0f, 130.0f, 50.0f, 180.0f));
}
{
Grid grid;
grid.templateColumns.addArray ({ Tr ("first", 20_px, "in"), Tr ("in", 1_fr, "in"), Tr (20_px, "last") });
grid.templateRows.addArray ({ Tr (1_fr),
Tr (20_px)});
{
beginTest ("Grid items placement tests: integer and custom ident, counting forward");
GridItem i1, i2, i3, i4, i5;
i1.column = { 1, 4 };
i1.row = { 1, 2 };
i2.column = { 1, 3 };
i2.row = { 1, 3 };
i3.column = { "first", "in" };
i3.row = { 2, 3 };
i4.column = { "first", { 2, "in" } };
i4.row = { 1, 2 };
i5.column = { "first", "last" };
i5.row = { 1, 2 };
grid.items.addArray ({ i1, i2, i3, i4, i5 });
grid.performLayout ({ 140, 100 });
expect (grid.items[0].currentBounds == Rect (0.0f, 0.0f, 140.0f, 80.0f));
expect (grid.items[1].currentBounds == Rect (0.0f, 0.0f, 120.0f, 100.0f));
expect (grid.items[2].currentBounds == Rect (0.0f, 80.0f, 20.0f, 20.0f));
expect (grid.items[3].currentBounds == Rect (0.0f, 0.0f, 120.0f, 80.0f));
expect (grid.items[4].currentBounds == Rect (0.0f, 0.0f, 140.0f, 80.0f));
}
}
{
Grid grid;
grid.templateColumns.addArray ({ Tr ("first", 20_px, "in"), Tr ("in", 1_fr, "in"), Tr (20_px, "last") });
grid.templateRows.addArray ({ Tr (1_fr),
Tr (20_px)});
beginTest ("Grid items placement tests: integer and custom ident, counting forward, reversed end and start");
GridItem i1, i2, i3, i4, i5;
i1.column = { 4, 1 };
i1.row = { 2, 1 };
i2.column = { 3, 1 };
i2.row = { 3, 1 };
i3.column = { "in", "first" };
i3.row = { 3, 2 };
i4.column = { "first", { 2, "in" } };
i4.row = { 1, 2 };
i5.column = { "last", "first" };
i5.row = { 1, 2 };
grid.items.addArray ({ i1, i2, i3, i4, i5 });
grid.performLayout ({ 140, 100 });
expect (grid.items[0].currentBounds == Rect (0.0f, 0.0f, 140.0f, 80.0f));
expect (grid.items[1].currentBounds == Rect (0.0f, 0.0f, 120.0f, 100.0f));
expect (grid.items[2].currentBounds == Rect (0.0f, 80.0f, 20.0f, 20.0f));
expect (grid.items[3].currentBounds == Rect (0.0f, 0.0f, 120.0f, 80.0f));
expect (grid.items[4].currentBounds == Rect (0.0f, 0.0f, 140.0f, 80.0f));
}
{
beginTest ("Grid items placement tests: areas");
Grid grid;
grid.templateColumns = { Tr (50_px), Tr (100_px), Tr (Fr (1_fr)), Tr (50_px) };
grid.templateRows = { Tr (50_px),
Tr (1_fr),
Tr (50_px) };
grid.templateAreas = { "header header header header",
"main main . sidebar",
"footer footer footer footer" };
grid.items.addArray ({ GridItem().withArea ("header"),
GridItem().withArea ("main"),
GridItem().withArea ("sidebar"),
GridItem().withArea ("footer"),
});
grid.performLayout ({ 300, 150 });
expect (grid.items[0].currentBounds == Rect (0.f, 0.f, 300.f, 50.f));
expect (grid.items[1].currentBounds == Rect (0.f, 50.f, 150.f, 50.f));
expect (grid.items[2].currentBounds == Rect (250.f, 50.f, 50.f, 50.f));
expect (grid.items[3].currentBounds == Rect (0.f, 100.f, 300.f, 50.f));
}
{
beginTest ("Grid implicit rows and columns: triggered by areas");
Grid grid;
grid.templateColumns = { Tr (50_px), Tr (100_px), Tr (1_fr), Tr (50_px) };
grid.templateRows = { Tr (50_px),
Tr (1_fr),
Tr (50_px) };
grid.autoRows = Tr (30_px);
grid.autoColumns = Tr (30_px);
grid.templateAreas = { "header header header header header",
"main main . sidebar sidebar",
"footer footer footer footer footer",
"sub sub sub sub sub"};
grid.items.addArray ({ GridItem().withArea ("header"),
GridItem().withArea ("main"),
GridItem().withArea ("sidebar"),
GridItem().withArea ("footer"),
GridItem().withArea ("sub"),
});
grid.performLayout ({ 330, 180 });
expect (grid.items[0].currentBounds == Rect (0.f, 0.f, 330.f, 50.f));
expect (grid.items[1].currentBounds == Rect (0.f, 50.f, 150.f, 50.f));
expect (grid.items[2].currentBounds == Rect (250.f, 50.f, 80.f, 50.f));
expect (grid.items[3].currentBounds == Rect (0.f, 100.f, 330.f, 50.f));
expect (grid.items[4].currentBounds == Rect (0.f, 150.f, 330.f, 30.f));
}
{
beginTest ("Grid implicit rows and columns: triggered by areas");
Grid grid;
grid.templateColumns = { Tr (50_px), Tr (100_px), Tr (1_fr), Tr (50_px) };
grid.templateRows = { Tr (50_px),
Tr (1_fr),
Tr (50_px) };
grid.autoRows = Tr (1_fr);
grid.autoColumns = Tr (1_fr);
grid.templateAreas = { "header header header header",
"main main . sidebar",
"footer footer footer footer" };
grid.items.addArray ({ GridItem().withArea ("header"),
GridItem().withArea ("main"),
GridItem().withArea ("sidebar"),
GridItem().withArea ("footer"),
GridItem().withArea (4, 5, 6, 7)
});
grid.performLayout ({ 350, 250 });
expect (grid.items[0].currentBounds == Rect (0.f, 0.f, 250.f, 50.f));
expect (grid.items[1].currentBounds == Rect (0.f, 50.f, 150.f, 50.f));
expect (grid.items[2].currentBounds == Rect (200.f, 50.f, 50.f, 50.f));
expect (grid.items[3].currentBounds == Rect (0.f, 100.f, 250.f, 50.f));
expect (grid.items[4].currentBounds == Rect (250.f, 150.f, 100.f, 100.f));
}
}
};
static GridTests gridUnitTests;

+ 1
- 1
source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp View File

@@ -169,7 +169,7 @@ void TabBarButton::resized()
//==============================================================================
class TabbedButtonBar::BehindFrontTabComp : public Component,
public ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
public Button::Listener
{
public:
BehindFrontTabComp (TabbedButtonBar& tb) : owner (tb)


+ 1
- 0
source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h View File

@@ -311,6 +311,7 @@ public:
virtual Rectangle<int> getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle<int>& textArea, Component& extraComp) = 0;
virtual void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
virtual Font getTabButtonFont (TabBarButton&, float height) = 0;
virtual void drawTabButtonText (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 0;
virtual void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) = 0;
virtual void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int w, int h) = 0;


+ 35
- 18
source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp View File

@@ -173,6 +173,11 @@ LookAndFeel_V2::LookAndFeel_V2()
BubbleComponent::backgroundColourId, 0xeeeeeebb,
BubbleComponent::outlineColourId, 0x77000000,
TableHeaderComponent::textColourId, 0xff000000,
TableHeaderComponent::backgroundColourId, 0xffe8ebf9,
TableHeaderComponent::outlineColourId, 0x33000000,
TableHeaderComponent::highlightColourId, 0x8899aadd,
DirectoryContentsDisplayComponent::highlightColourId, textHighlightColour,
DirectoryContentsDisplayComponent::textColourId, 0xff000000,
@@ -336,11 +341,9 @@ void LookAndFeel_V2::drawToggleButton (Graphics& g, ToggleButton& button,
if (! button.isEnabled())
g.setOpacity (0.5f);
const int textX = (int) tickWidth + 5;
g.drawFittedText (button.getButtonText(),
textX, 0,
button.getWidth() - textX - 2, button.getHeight(),
button.getLocalBounds().withTrimmedLeft (roundToInt (tickWidth) + 5)
.withTrimmedRight (2),
Justification::centredLeft, 10);
}
@@ -830,22 +833,22 @@ void LookAndFeel_V2::drawTreeviewPlusMinusBox (Graphics& g, const Rectangle<floa
const int x = ((int) area.getWidth() - boxSize) / 2 + (int) area.getX();
const int y = ((int) area.getHeight() - boxSize) / 2 + (int) area.getY();
const int w = boxSize;
const int h = boxSize;
Rectangle<float> boxArea ((float) x, (float) y, (float) boxSize, (float) boxSize);
g.setColour (Colour (0xe5ffffff));
g.fillRect (x, y, w, h);
g.fillRect (boxArea);
g.setColour (Colour (0x80000000));
g.drawRect (x, y, w, h);
g.drawRect (boxArea);
const float size = boxSize / 2 + 1.0f;
const float centre = (float) (boxSize / 2);
g.fillRect (x + (w - size) * 0.5f, y + centre, size, 1.0f);
g.fillRect (x + (boxSize - size) * 0.5f, y + centre, size, 1.0f);
if (! isOpen)
g.fillRect (x + centre, y + (h - size) * 0.5f, 1.0f, size);
g.fillRect (x + centre, y + (boxSize - size) * 0.5f, 1.0f, size);
}
bool LookAndFeel_V2::areLinesDrawnForTreeView (TreeView&)
@@ -1106,6 +1109,8 @@ void LookAndFeel_V2::preparePopupMenuWindow (Component&) {}
bool LookAndFeel_V2::shouldPopupMenuScaleWithTargetComponent (const PopupMenu::Options&) { return true; }
int LookAndFeel_V2::getPopupMenuBorderSize() { return 2; }
//==============================================================================
void LookAndFeel_V2::fillTextEditorBackground (Graphics& g, int /*width*/, int /*height*/, TextEditor& textEditor)
{
@@ -2166,6 +2171,11 @@ void LookAndFeel_V2::fillTabButtonShape (TabBarButton& button, Graphics& g, cons
g.strokePath (path, PathStrokeType (isFrontTab ? 1.0f : 0.5f));
}
Font LookAndFeel_V2::getTabButtonFont (TabBarButton&, float height)
{
return { height * 0.6f };
}
void LookAndFeel_V2::drawTabButtonText (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown)
{
const Rectangle<float> area (button.getTextArea().toFloat());
@@ -2176,7 +2186,7 @@ void LookAndFeel_V2::drawTabButtonText (TabBarButton& button, Graphics& g, bool
if (button.getTabbedButtonBar().isVertical())
std::swap (length, depth);
Font font (depth * 0.6f);
Font font (getTabButtonFont (button, depth));
font.setUnderline (button.hasKeyboardFocus (false));
AffineTransform t;
@@ -2323,26 +2333,33 @@ void LookAndFeel_V2::drawTableHeaderBackground (Graphics& g, TableHeaderComponen
Rectangle<int> area (header.getLocalBounds());
area.removeFromTop (area.getHeight() / 2);
g.setGradientFill (ColourGradient (Colour (0xffe8ebf9), 0.0f, (float) area.getY(),
Colour (0xfff6f8f9), 0.0f, (float) area.getBottom(),
auto backgroundColour = header.findColour (TableHeaderComponent::backgroundColourId);
g.setGradientFill (ColourGradient (backgroundColour,
0.0f, (float) area.getY(),
backgroundColour.withMultipliedSaturation (.5f),
0.0f, (float) area.getBottom(),
false));
g.fillRect (area);
g.setColour (Colour (0x33000000));
g.setColour (header.findColour (TableHeaderComponent::outlineColourId));
g.fillRect (area.removeFromBottom (1));
for (int i = header.getNumColumns (true); --i >= 0;)
g.fillRect (header.getColumnPosition (i).removeFromRight (1));
}
void LookAndFeel_V2::drawTableHeaderColumn (Graphics& g, const String& columnName, int /*columnId*/,
void LookAndFeel_V2::drawTableHeaderColumn (Graphics& g, TableHeaderComponent& header,
const String& columnName, int /*columnId*/,
int width, int height, bool isMouseOver, bool isMouseDown,
int columnFlags)
{
auto highlightColour = header.findColour (TableHeaderComponent::highlightColourId);
if (isMouseDown)
g.fillAll (Colour (0x8899aadd));
g.fillAll (highlightColour);
else if (isMouseOver)
g.fillAll (Colour (0x5599aadd));
g.fillAll (highlightColour.withMultipliedAlpha (0.625f));
Rectangle<int> area (width, height);
area.reduce (4, 0);
@@ -2358,7 +2375,7 @@ void LookAndFeel_V2::drawTableHeaderColumn (Graphics& g, const String& columnNam
g.fillPath (sortArrow, sortArrow.getTransformToScaleToFit (area.removeFromRight (height / 2).reduced (2).toFloat(), true));
}
g.setColour (Colours::black);
g.setColour (header.findColour (TableHeaderComponent::textColourId));
g.setFont (Font (height * 0.5f, Font::bold));
g.drawFittedText (columnName, area, Justification::centredLeft, 1);
}


+ 6
- 3
source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h View File

@@ -182,6 +182,8 @@ public:
bool shouldPopupMenuScaleWithTargetComponent (const PopupMenu::Options& options) override;
int getPopupMenuBorderSize() override;
//==============================================================================
void drawComboBox (Graphics&, int width, int height, bool isButtonDown,
int buttonX, int buttonY, int buttonW, int buttonH,
@@ -272,6 +274,7 @@ public:
Rectangle<int> getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle<int>& textArea, Component& extraComp) override;
void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) override;
Font getTabButtonFont (TabBarButton&, float height) override;
void drawTabButtonText (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) override;
void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) override;
void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int w, int h) override;
@@ -289,9 +292,9 @@ public:
//==============================================================================
void drawTableHeaderBackground (Graphics&, TableHeaderComponent&) override;
void drawTableHeaderColumn (Graphics&, const String& columnName, int columnId,
int width, int height, bool isMouseOver, bool isMouseDown,
int columnFlags) override;
void drawTableHeaderColumn (Graphics&, TableHeaderComponent&, const String& columnName,
int columnId, int width, int height, bool isMouseOver,
bool isMouseDown, int columnFlags) override;
//==============================================================================
void paintToolbarBackground (Graphics&, int width, int height, Toolbar&) override;


+ 6
- 3
source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp View File

@@ -39,6 +39,8 @@ LookAndFeel_V3::LookAndFeel_V3()
setColour (Slider::thumbColourId, Colour (0xffddddff));
setColour (BubbleComponent::backgroundColourId, Colour (0xeeeeeedd));
setColour (ScrollBar::thumbColourId, Colour::greyLevel (0.8f).contrasting().withAlpha (0.13f));
setColour (TableHeaderComponent::backgroundColourId, Colours::white.withAlpha (0.6f));
setColour (TableHeaderComponent::outlineColourId, Colours::black.withAlpha (0.5f));
}
LookAndFeel_V3::~LookAndFeel_V3() {}
@@ -153,14 +155,15 @@ void LookAndFeel_V3::drawButtonBackground (Graphics& g, Button& button, const Co
void LookAndFeel_V3::drawTableHeaderBackground (Graphics& g, TableHeaderComponent& header)
{
Rectangle<int> r (header.getLocalBounds());
auto outlineColour = header.findColour (TableHeaderComponent::outlineColourId);
g.setColour (Colours::black.withAlpha (0.5f));
g.setColour (outlineColour);
g.fillRect (r.removeFromBottom (1));
g.setColour (Colours::white.withAlpha (0.6f));
g.setColour (header.findColour (TableHeaderComponent::backgroundColourId));
g.fillRect (r);
g.setColour (Colours::black.withAlpha (0.5f));
g.setColour (outlineColour);
for (int i = header.getNumColumns (true); --i >= 0;)
g.fillRect (header.getColumnPosition (i).removeFromRight (1));


+ 2
- 4
source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp View File

@@ -333,11 +333,9 @@ void LookAndFeel_V4::drawToggleButton (Graphics& g, ToggleButton& button,
if (! button.isEnabled())
g.setOpacity (0.5f);
const auto textX = roundToInt (tickWidth) + 10;
g.drawFittedText (button.getButtonText(),
textX, 0,
button.getWidth() - textX - 2, button.getHeight(),
button.getLocalBounds().withTrimmedLeft (roundToInt (tickWidth) + 10)
.withTrimmedRight (2),
Justification::centredLeft, 10);
}


+ 11
- 8
source/modules/juce_gui_basics/menus/juce_PopupMenu.cpp View File

@@ -27,7 +27,6 @@
namespace PopupMenuSettings
{
const int scrollZone = 24;
const int borderSize = 2;
const int dismissCommandId = 0x6287345f;
static bool menuWasHiddenBecauseOfAppChange = false;
@@ -88,7 +87,7 @@ struct ItemComponent : public Component
int itemW = 80;
int itemH = 16;
getIdealSize (itemW, itemH, standardItemHeight);
setSize (itemW, jlimit (2, 600, itemH));
setSize (itemW, jlimit (1, 600, itemH));
addMouseListener (&parent, false);
}
@@ -290,7 +289,8 @@ public:
auto& lf = getLookAndFeel();
if (parentComponent != nullptr)
lf.drawResizableFrame (g, getWidth(), getHeight(), BorderSize<int> (PopupMenuSettings::borderSize));
lf.drawResizableFrame (g, getWidth(), getHeight(),
BorderSize<int> (getLookAndFeel().getPopupMenuBorderSize()));
if (canScroll())
{
@@ -598,7 +598,7 @@ public:
return parentComponent->getLocalArea (nullptr,
parentComponent->getScreenBounds()
.reduced (PopupMenuSettings::borderSize)
.reduced (getLookAndFeel().getPopupMenuBorderSize())
.getIntersection (parentArea));
}
@@ -666,6 +666,9 @@ public:
else
x = jmax (parentArea.getX() + 4, target.getX() - widthToUse);
if (getLookAndFeel().getPopupMenuBorderSize() == 0) // workaround for dismissing the window on mouse up when border size is 0
x += tendTowardsRight ? 1 : -1;
y = target.getY();
if (target.getCentreY() > parentArea.getCentreY())
y = jmax (parentArea.getY(), target.getBottom() - heightToUse);
@@ -711,7 +714,7 @@ public:
needsToScroll = contentHeight > actualH;
width = updateYPositions();
height = actualH + PopupMenuSettings::borderSize * 2;
height = actualH + getLookAndFeel().getPopupMenuBorderSize() * 2;
}
int workOutBestSize (const int maxMenuW)
@@ -733,7 +736,7 @@ public:
colH += items.getUnchecked (childNum + i)->getHeight();
}
colW = jmin (maxMenuW / jmax (1, numColumns - 2), colW + PopupMenuSettings::borderSize * 2);
colW = jmin (maxMenuW / jmax (1, numColumns - 2), colW + getLookAndFeel().getPopupMenuBorderSize() * 2);
columnWidths.set (col, colW);
totalW += colW;
@@ -832,7 +835,7 @@ public:
childYOffset = jmax (childYOffset, 0);
else if (delta > 0)
childYOffset = jmin (childYOffset,
contentHeight - windowPos.getHeight() + PopupMenuSettings::borderSize);
contentHeight - windowPos.getHeight() + getLookAndFeel().getPopupMenuBorderSize());
updateYPositions();
}
@@ -857,7 +860,7 @@ public:
const int colW = columnWidths [col];
int y = PopupMenuSettings::borderSize - (childYOffset + (getY() - windowPos.getY()));
int y = getLookAndFeel().getPopupMenuBorderSize() - (childYOffset + (getY() - windowPos.getY()));
for (int i = 0; i < numChildren; ++i)
{


+ 6
- 0
source/modules/juce_gui_basics/menus/juce_PopupMenu.h View File

@@ -280,6 +280,8 @@ public:
This will add a user-defined component to use as a menu item. The component
passed in will be deleted by this menu when it's no longer needed.
Note that native macOS menus do not support custom components.
@see CustomComponent
*/
void addCustomItem (int itemResultID,
@@ -296,6 +298,8 @@ public:
detection of a mouse-click on your component, and use that to trigger the
menu ID specified in itemResultID. If this is false, the menu item can't
be triggered, so itemResultID is not used.
Note that native macOS menus do support custom components.
*/
void addCustomItem (int itemResultID,
Component* customComponent,
@@ -723,6 +727,8 @@ public:
/** Return true if you want your popup menus to scale with the target component's AffineTransform
or scale factor */
virtual bool shouldPopupMenuScaleWithTargetComponent (const PopupMenu::Options& options) = 0;
virtual int getPopupMenuBorderSize() = 0;
};
private:


+ 10
- 5
source/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp View File

@@ -84,13 +84,17 @@ public:
return nullptr;
}
Point<float> getScreenPosition() const
Point<float> getScreenPosition() const noexcept
{
// This needs to return the live position if possible, but it mustn't update the lastScreenPos
// value, because that can cause continuity problems.
return ScalingHelpers::unscaledScreenPosToScaled
(unboundedMouseOffset + (inputType != MouseInputSource::InputSourceType::touch ? MouseInputSource::getCurrentRawMousePosition()
: lastScreenPos));
return ScalingHelpers::unscaledScreenPosToScaled (getRawScreenPosition());
}
Point<float> getRawScreenPosition() const noexcept
{
return unboundedMouseOffset + (inputType != MouseInputSource::InputSourceType::touch ? MouseInputSource::getCurrentRawMousePosition()
: lastScreenPos);
}
void setScreenPosition (Point<float> p)
@@ -578,6 +582,7 @@ bool MouseInputSource::hasMouseWheel() const noexcept
int MouseInputSource::getIndex() const noexcept { return pimpl->index; }
bool MouseInputSource::isDragging() const noexcept { return pimpl->isDragging(); }
Point<float> MouseInputSource::getScreenPosition() const noexcept { return pimpl->getScreenPosition(); }
Point<float> MouseInputSource::getRawScreenPosition() const noexcept { return pimpl->getRawScreenPosition(); }
ModifierKeys MouseInputSource::getCurrentModifiers() const noexcept { return pimpl->getCurrentModifiers(); }
float MouseInputSource::getCurrentPressure() const noexcept { return pimpl->pressure; }
bool MouseInputSource::isPressureValid() const noexcept { return pimpl->isPressureValid(); }
@@ -730,7 +735,7 @@ struct MouseInputSource::SourceList : public Timer
// because on some OSes the queue can get overloaded with messages so that mouse-events don't get through..
if (s->isDragging() && ModifierKeys::getCurrentModifiersRealtime().isAnyMouseButtonDown())
{
s->lastScreenPos = s->getScreenPosition();
s->lastScreenPos = s->getRawScreenPosition();
s->triggerFakeMove();
anyDragging = true;
}


+ 3
- 0
source/modules/juce_gui_basics/mouse/juce_MouseInputSource.h View File

@@ -105,6 +105,9 @@ public:
/** Returns the last-known screen position of this source. */
Point<float> getScreenPosition() const noexcept;
/** Returns the last-known screen position of this source without any scaling applied. */
Point<float> getRawScreenPosition() const noexcept;
/** Returns a set of modifiers that indicate which buttons are currently
held down on this device.
*/


+ 68
- 9
source/modules/juce_gui_basics/native/juce_android_Windowing.cpp View File

@@ -31,6 +31,11 @@ extern juce::JUCEApplicationBase* juce_CreateApplication(); // (from START_JUCE_
namespace juce
{
//==============================================================================
#if JUCE_MODULE_AVAILABLE_juce_product_unlocking
extern void juce_inAppPurchaseCompleted (void*);
#endif
//==============================================================================
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, launchApp, void, (JNIEnv* env, jobject activity,
jstring appFile, jstring appDataDir))
@@ -83,6 +88,18 @@ JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, quitApp, void, (JNIEnv* env,
android.shutdown (env);
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, appActivityResult, void, (JNIEnv* env, jobject, jint requestCode, jint /*resultCode*/, jobject intentData))
{
setEnv (env);
#if JUCE_MODULE_AVAILABLE_juce_product_unlocking
if (requestCode == 1001)
juce_inAppPurchaseCompleted (intentData);
#else
ignoreUnused (intentData, requestCode);
#endif
}
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (drawBitmap, "drawBitmap", "([IIIFFIIZLandroid/graphics/Paint;)V") \
@@ -248,20 +265,20 @@ public:
setFullScreen (true);
}
Point<int> getScreenPosition() const
Point<float> getScreenPosition() const
{
return Point<int> (view.callIntMethod (ComponentPeerView.getLeft),
view.callIntMethod (ComponentPeerView.getTop)) / scale;
return Point<float> (view.callIntMethod (ComponentPeerView.getLeft),
view.callIntMethod (ComponentPeerView.getTop)) / scale;
}
Point<float> localToGlobal (Point<float> relativePosition) override
{
return relativePosition + getScreenPosition().toFloat();
return relativePosition + getScreenPosition();
}
Point<float> globalToLocal (Point<float> screenPosition) override
{
return screenPosition - getScreenPosition().toFloat();
return screenPosition - getScreenPosition();
}
void setMinimised (bool /*shouldBeMinimised*/) override
@@ -390,7 +407,7 @@ public:
void handleMouseDownCallback (int index, Point<float> sysPos, int64 time)
{
Point<float> pos = sysPos / scale;
lastMousePos = pos;
lastMousePos = localToGlobal (pos);
// this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
handleMouseEvent (MouseInputSource::InputSourceType::touch, pos, currentModifiers.withoutMouseButtons(),
@@ -403,7 +420,7 @@ public:
void handleMouseDragCallback (int index, Point<float> pos, int64 time)
{
pos /= scale;
lastMousePos = pos;
lastMousePos = localToGlobal (pos);
jassert (index < 64);
touchesDown = (touchesDown | (1 << (index & 63)));
@@ -415,7 +432,7 @@ public:
void handleMouseUpCallback (int index, Point<float> pos, int64 time)
{
pos /= scale;
lastMousePos = pos;
lastMousePos = localToGlobal (pos);
jassert (index < 64);
touchesDown = (touchesDown & ~(1 << (index & 63)));
@@ -678,6 +695,18 @@ ComponentPeer* Component::createNewPeer (int styleFlags, void*)
}
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getRotation, "getRotation", "()I")
DECLARE_JNI_CLASS (Display, "android/view/Display");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getDefaultDisplay, "getDefaultDisplay", "()Landroid/view/Display;")
DECLARE_JNI_CLASS (WindowManager, "android/view/WindowManager");
#undef JNI_CLASS_MEMBERS
bool Desktop::canUseSemiTransparentWindows() noexcept
{
return true;
@@ -690,7 +719,37 @@ double Desktop::getDefaultMasterScale()
Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
{
// TODO
enum
{
ROTATION_0 = 0,
ROTATION_90 = 1,
ROTATION_180 = 2,
ROTATION_270 = 3
};
JNIEnv* env = getEnv();
LocalRef<jstring> windowServiceString (javaString ("window"));
LocalRef<jobject> windowManager = LocalRef<jobject> (env->CallObjectMethod (android.activity, JuceAppActivity.getSystemService, windowServiceString.get()));
if (windowManager.get() != 0)
{
LocalRef<jobject> display = LocalRef<jobject> (env->CallObjectMethod (windowManager, WindowManager.getDefaultDisplay));
if (display.get() != 0)
{
int rotation = env->CallIntMethod (display, Display.getRotation);
switch (rotation)
{
case ROTATION_0: return upright;
case ROTATION_90: return rotatedAntiClockwise;
case ROTATION_180: return upsideDown;
case ROTATION_270: return rotatedClockwise;
}
}
}
jassertfalse;
return upright;
}


+ 2
- 2
source/modules/juce_gui_basics/native/juce_mac_MainMenu.mm View File

@@ -100,9 +100,9 @@ public:
{
NSMenu* superMenu = [menu supermenu];
auto menuNames = currentModel->getMenuBarNames();
auto indexOfMenu = (int) [superMenu indexOfItemWithSubmenu: menu];
auto indexOfMenu = (int) [superMenu indexOfItemWithSubmenu: menu] - 1;
[menu removeAllItems];
auto updatedPopup = currentModel->getMenuForIndex (indexOfMenu - 1, menuNames[indexOfMenu - 1]);
auto updatedPopup = currentModel->getMenuForIndex (indexOfMenu, menuNames[indexOfMenu]);
for (PopupMenu::MenuItemIterator iter (updatedPopup); iter.next();)
addMenuItem (iter, menu, 1, indexOfMenu);


+ 40
- 0
source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp View File

@@ -57,6 +57,46 @@ static bool shouldDeactivateTitleBar = true;
extern void* getUser32Function (const char*);
//==============================================================================
#ifndef WM_TOUCH
enum
{
WM_TOUCH = 0x0240,
TOUCHEVENTF_MOVE = 0x0001,
TOUCHEVENTF_DOWN = 0x0002,
TOUCHEVENTF_UP = 0x0004
};
typedef HANDLE HTOUCHINPUT;
typedef HANDLE HGESTUREINFO;
struct TOUCHINPUT
{
LONG x;
LONG y;
HANDLE hSource;
DWORD dwID;
DWORD dwFlags;
DWORD dwMask;
DWORD dwTime;
ULONG_PTR dwExtraInfo;
DWORD cxContact;
DWORD cyContact;
};
struct GESTUREINFO
{
UINT cbSize;
DWORD dwFlags;
DWORD dwID;
HWND hwndTarget;
POINTS ptsLocation;
DWORD dwInstanceID;
DWORD dwSequenceID;
ULONGLONG ullArguments;
UINT cbExtraArgs;
};
#endif
#ifndef WM_NCPOINTERUPDATE
enum
{


+ 1
- 1
source/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.h View File

@@ -37,7 +37,7 @@
@see PropertyComponent
*/
class JUCE_API BooleanPropertyComponent : public PropertyComponent,
private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
private Button::Listener
{
protected:
//==============================================================================


+ 1
- 1
source/modules/juce_gui_basics/properties/juce_ButtonPropertyComponent.h View File

@@ -37,7 +37,7 @@
@see PropertyComponent
*/
class JUCE_API ButtonPropertyComponent : public PropertyComponent,
private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
private Button::Listener
{
public:
//==============================================================================


+ 1
- 1
source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp View File

@@ -25,7 +25,7 @@
*/
class ChoicePropertyComponent::RemapperValueSource : public Value::ValueSource,
private ValueListener
private Value::Listener
{
public:
RemapperValueSource (const Value& source, const Array<var>& map)


+ 1
- 1
source/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h View File

@@ -47,7 +47,7 @@
@see PropertyComponent, PropertyPanel
*/
class JUCE_API ChoicePropertyComponent : public PropertyComponent,
private ComboBoxListener // (can't use ComboBox::Listener due to idiotic VC2005 bug)
private ComboBox::Listener
{
protected:
/** Creates the component.


+ 1
- 1
source/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.h View File

@@ -34,7 +34,7 @@
@see PropertyComponent, Slider
*/
class JUCE_API SliderPropertyComponent : public PropertyComponent,
private SliderListener // (can't use Slider::Listener due to idiotic VC2005 bug)
private Slider::Listener
{
protected:
//==============================================================================


+ 1
- 1
source/modules/juce_gui_basics/widgets/juce_ComboBox.cpp View File

@@ -627,7 +627,7 @@ void ComboBox::removeListener (ComboBoxListener* listener) { listeners.remove
void ComboBox::handleAsyncUpdate()
{
Component::BailOutChecker checker (this);
listeners.callChecked (checker, &ComboBoxListener::comboBoxChanged, this); // (can't use ComboBox::Listener due to idiotic VC2005 bug)
listeners.callChecked (checker, &ComboBox::Listener::comboBoxChanged, this);
}
void ComboBox::sendChange (const NotificationType notification)


+ 2
- 2
source/modules/juce_gui_basics/widgets/juce_ComboBox.h View File

@@ -44,8 +44,8 @@
*/
class JUCE_API ComboBox : public Component,
public SettableTooltipClient,
public LabelListener, // (can't use Label::Listener due to idiotic VC2005 bug)
public ValueListener,
public Label::Listener,
public Value::Listener,
private AsyncUpdater
{
public:


+ 1
- 1
source/modules/juce_gui_basics/widgets/juce_Label.cpp View File

@@ -416,7 +416,7 @@ void Label::removeListener (LabelListener* const listener)
void Label::callChangeListeners()
{
Component::BailOutChecker checker (this);
listeners.callChecked (checker, &LabelListener::labelTextChanged, this); // (can't use Label::Listener due to idiotic VC2005 bug)
listeners.callChecked (checker, &Label::Listener::labelTextChanged, this);
}
//==============================================================================


+ 2
- 2
source/modules/juce_gui_basics/widgets/juce_Label.h View File

@@ -34,9 +34,9 @@
*/
class JUCE_API Label : public Component,
public SettableTooltipClient,
protected TextEditorListener,
protected TextEditor::Listener,
private ComponentListener,
private ValueListener
private Value::Listener
{
public:
//==============================================================================


+ 122
- 96
source/modules/juce_gui_basics/widgets/juce_Slider.cpp View File

@@ -24,37 +24,16 @@
==============================================================================
*/
class Slider::Pimpl : public AsyncUpdater,
public ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug)
public LabelListener,
public ValueListener
class Slider::Pimpl : private AsyncUpdater,
private Button::Listener,
private Label::Listener,
private Value::Listener
{
public:
Pimpl (Slider& s, SliderStyle sliderStyle, TextEntryBoxPosition textBoxPosition)
: owner (s),
style (sliderStyle),
lastCurrentValue (0), lastValueMin (0), lastValueMax (0),
minimum (0), maximum (10), interval (0), doubleClickReturnValue (0),
skewFactor (1.0), symmetricSkew (false), velocityModeSensitivity (1.0),
velocityModeOffset (0.0), velocityModeThreshold (1),
sliderRegionStart (0), sliderRegionSize (1), sliderBeingDragged (-1),
pixelsForFullDragExtent (250),
textBoxPos (textBoxPosition),
numDecimalPlaces (7),
textBoxWidth (80), textBoxHeight (20),
incDecButtonMode (incDecButtonsNotDraggable),
editableText (true),
doubleClickToValue (false),
isVelocityBased (false),
userKeyOverridesVelocity (true),
incDecButtonsSideBySide (false),
sendChangeOnlyOnRelease (false),
popupDisplayEnabled (false),
menuEnabled (false),
useDragEvents (false),
scrollWheelEnabled (true),
snapsToMousePos (true),
parentForPopupDisplay (nullptr)
textBoxPos (textBoxPosition)
{
rotaryParams.startAngleRadians = float_Pi * 1.2f;
rotaryParams.endAngleRadians = float_Pi * 2.8f;
@@ -139,11 +118,13 @@ public:
int v = std::abs (roundToInt (newInt * 10000000));
if (v > 0)
{
while ((v % 10) == 0)
{
--numDecimalPlaces;
v /= 10;
}
}
}
// keep the current values inside the new range..
@@ -201,9 +182,7 @@ public:
updateText();
owner.repaint();
if (popupDisplay != nullptr)
popupDisplay->updatePosition (owner.getTextFromValue (newValue));
updatePopupDisplay (newValue);
triggerChangeMessage (notification);
}
@@ -238,9 +217,7 @@ public:
lastValueMin = newValue;
valueMin = newValue;
owner.repaint();
if (popupDisplay != nullptr)
popupDisplay->updatePosition (owner.getTextFromValue (newValue));
updatePopupDisplay (newValue);
triggerChangeMessage (notification);
}
@@ -275,9 +252,7 @@ public:
lastValueMax = newValue;
valueMax = newValue;
owner.repaint();
if (popupDisplay != nullptr)
popupDisplay->updatePosition (owner.getTextFromValue (valueMax.getValue()));
updatePopupDisplay (valueMax.getValue());
triggerChangeMessage (notification);
}
@@ -344,7 +319,7 @@ public:
Component::BailOutChecker checker (&owner);
Slider* slider = &owner; // (must use an intermediate variable here to avoid a VS2005 compiler bug)
listeners.callChecked (checker, &SliderListener::sliderValueChanged, slider); // (can't use Slider::Listener due to idiotic VC2005 bug)
listeners.callChecked (checker, &Slider::Listener::sliderValueChanged, slider);
}
void sendDragStart()
@@ -353,7 +328,7 @@ public:
Component::BailOutChecker checker (&owner);
Slider* slider = &owner; // (must use an intermediate variable here to avoid a VS2005 compiler bug)
listeners.callChecked (checker, &SliderListener::sliderDragStarted, slider);
listeners.callChecked (checker, &Slider::Listener::sliderDragStarted, slider);
}
void sendDragEnd()
@@ -364,7 +339,7 @@ public:
Component::BailOutChecker checker (&owner);
Slider* slider = &owner; // (must use an intermediate variable here to avoid a VS2005 compiler bug)
listeners.callChecked (checker, &SliderListener::sliderDragEnded, slider);
listeners.callChecked (checker, &Slider::Listener::sliderDragEnded, slider);
}
struct DragInProgress
@@ -382,9 +357,17 @@ public:
if (style == IncDecButtons)
{
const double delta = (button == incButton) ? interval : -interval;
auto newValue = owner.snapValue (getValue() + delta, notDragging);
DragInProgress drag (*this);
setValue (owner.snapValue (getValue() + delta, notDragging), sendNotificationSync);
if (currentDrag != nullptr)
{
setValue (newValue, sendNotificationSync);
}
else
{
DragInProgress drag (*this);
setValue (newValue, sendNotificationSync);
}
}
}
@@ -693,6 +676,7 @@ public:
if (dx * dx + dy * dy > 25.0f)
{
double angle = std::atan2 ((double) dx, (double) -dy);
while (angle < 0.0)
angle += double_Pi * 2.0;
@@ -743,12 +727,12 @@ public:
|| ((style == LinearHorizontal || style == LinearVertical || style == LinearBar || style == LinearBarVertical)
&& ! snapsToMousePos))
{
const float mouseDiff = (style == RotaryHorizontalDrag
|| style == LinearHorizontal
|| style == LinearBar
|| (style == IncDecButtons && incDecDragDirectionIsHorizontal()))
? e.position.x - mouseDragStartPos.x
: mouseDragStartPos.y - e.position.y;
auto mouseDiff = (style == RotaryHorizontalDrag
|| style == LinearHorizontal
|| style == LinearBar
|| (style == IncDecButtons && incDecDragDirectionIsHorizontal()))
? e.position.x - mouseDragStartPos.x
: mouseDragStartPos.y - e.position.y;
newPos = owner.valueToProportionOfLength (valueOnMouseDown)
+ mouseDiff * (1.0 / pixelsForFullDragExtent);
@@ -761,8 +745,8 @@ public:
}
else if (style == RotaryHorizontalVerticalDrag)
{
const float mouseDiff = (e.position.x - mouseDragStartPos.x)
+ (mouseDragStartPos.y - e.position.y);
auto mouseDiff = (e.position.x - mouseDragStartPos.x)
+ (mouseDragStartPos.y - e.position.y);
newPos = owner.valueToProportionOfLength (valueOnMouseDown)
+ mouseDiff * (1.0 / pixelsForFullDragExtent);
@@ -822,6 +806,7 @@ public:
useDragEvents = false;
mouseDragStartPos = mousePosWhenLastDragged = e.position;
currentDrag = nullptr;
popupDisplay = nullptr;
if (owner.isEnabled())
{
@@ -854,17 +839,10 @@ public:
: currentValue)).getValue();
valueOnMouseDown = valueWhenLastDragged;
if (popupDisplayEnabled)
if (showPopupOnDrag || showPopupOnHover)
{
PopupDisplayComponent* const popup = new PopupDisplayComponent (owner);
popupDisplay = popup;
if (parentForPopupDisplay != nullptr)
parentForPopupDisplay->addChildComponent (popup);
else
popup->addToDesktop (ComponentPeer::windowIsTemporary);
popup->setVisible (true);
showPopupDisplay();
popupDisplay->stopTimer();
}
currentDrag = new DragInProgress (*this);
@@ -963,12 +941,57 @@ public:
}
else if (popupDisplay != nullptr)
{
popupDisplay->startTimer (2000);
popupDisplay->startTimer (200);
}
currentDrag = nullptr;
}
void mouseMove()
{
if (showPopupOnHover
&& style != TwoValueHorizontal
&& style != TwoValueVertical)
{
if (owner.isMouseOver (true) && owner.getTopLevelComponent()->hasKeyboardFocus (true))
{
if (popupDisplay == nullptr)
showPopupDisplay();
if (popupDisplay != nullptr)
popupDisplay->startTimer (2000);
}
}
}
void mouseExit()
{
popupDisplay = nullptr;
}
void showPopupDisplay()
{
if (popupDisplay == nullptr)
{
popupDisplay = new PopupDisplayComponent (owner);
updatePopupDisplay (getValue());
if (parentForPopupDisplay != nullptr)
parentForPopupDisplay->addChildComponent (popupDisplay);
else
popupDisplay->addToDesktop (ComponentPeer::windowIsTemporary);
popupDisplay->setVisible (true);
}
}
void updatePopupDisplay (double valueToShow)
{
if (popupDisplay != nullptr)
popupDisplay->updatePosition (owner.getTextFromValue (valueToShow));
}
bool canDoubleClickToValue() const
{
return doubleClickToValue
@@ -1180,50 +1203,50 @@ public:
Slider& owner;
SliderStyle style;
ListenerList <SliderListener> listeners;
ListenerList<Slider::Listener> listeners;
Value currentValue, valueMin, valueMax;
double lastCurrentValue, lastValueMin, lastValueMax;
double minimum, maximum, interval, doubleClickReturnValue;
double valueWhenLastDragged, valueOnMouseDown, skewFactor, lastAngle;
bool symmetricSkew;
double velocityModeSensitivity, velocityModeOffset, minMaxDiff;
int velocityModeThreshold;
double lastCurrentValue = 0, lastValueMin = 0, lastValueMax = 0;
double minimum = 0, maximum = 10, interval = 0, doubleClickReturnValue = 0;
double valueWhenLastDragged = 0, valueOnMouseDown = 0, skewFactor = 1.0, lastAngle = 0;
bool symmetricSkew = false;
double velocityModeSensitivity = 1.0, velocityModeOffset = 0, minMaxDiff = 0;
int velocityModeThreshold = 1;
RotaryParameters rotaryParams;
Point<float> mouseDragStartPos, mousePosWhenLastDragged;
int sliderRegionStart, sliderRegionSize;
int sliderBeingDragged;
int pixelsForFullDragExtent;
int sliderRegionStart = 0, sliderRegionSize = 1;
int sliderBeingDragged = -1;
int pixelsForFullDragExtent = 250;
Time lastMouseWheelTime;
Rectangle<int> sliderRect;
ScopedPointer<DragInProgress> currentDrag;
TextEntryBoxPosition textBoxPos;
String textSuffix;
int numDecimalPlaces;
int textBoxWidth, textBoxHeight;
IncDecButtonMode incDecButtonMode;
bool editableText;
bool doubleClickToValue;
bool isVelocityBased;
bool userKeyOverridesVelocity;
bool incDecButtonsSideBySide;
bool sendChangeOnlyOnRelease;
bool popupDisplayEnabled;
bool menuEnabled;
bool useDragEvents;
bool incDecDragged;
bool scrollWheelEnabled;
bool snapsToMousePos;
int numDecimalPlaces = 7;
int textBoxWidth = 80, textBoxHeight = 20;
IncDecButtonMode incDecButtonMode = incDecButtonsNotDraggable;
bool editableText = true;
bool doubleClickToValue = false;
bool isVelocityBased = false;
bool userKeyOverridesVelocity = true;
bool incDecButtonsSideBySide = false;
bool sendChangeOnlyOnRelease = false;
bool showPopupOnDrag = false;
bool showPopupOnHover = false;
bool menuEnabled = false;
bool useDragEvents = false;
bool incDecDragged = false;
bool scrollWheelEnabled = true;
bool snapsToMousePos = true;
ScopedPointer<Label> valueBox;
ScopedPointer<Button> incButton, decButton;
//==============================================================================
class PopupDisplayComponent : public BubbleComponent,
public Timer
struct PopupDisplayComponent : public BubbleComponent,
public Timer
{
public:
PopupDisplayComponent (Slider& s)
: owner (s),
font (s.getLookAndFeel().getSliderPopupFont (s))
@@ -1267,10 +1290,10 @@ public:
};
ScopedPointer<PopupDisplayComponent> popupDisplay;
Component* parentForPopupDisplay;
Component* parentForPopupDisplay = nullptr;
//==============================================================================
static double smallestAngleBetween (const double a1, const double a2) noexcept
static double smallestAngleBetween (double a1, double a2) noexcept
{
return jmin (std::abs (a1 - a2),
std::abs (a1 + double_Pi * 2.0 - a2),
@@ -1311,8 +1334,8 @@ void Slider::init (SliderStyle style, TextEntryBoxPosition textBoxPos)
Slider::~Slider() {}
//==============================================================================
void Slider::addListener (SliderListener* const listener) { pimpl->listeners.add (listener); }
void Slider::removeListener (SliderListener* const listener) { pimpl->listeners.remove (listener); }
void Slider::addListener (Listener* l) { pimpl->listeners.add (l); }
void Slider::removeListener (Listener* l) { pimpl->listeners.remove (l); }
//==============================================================================
Slider::SliderStyle Slider::getSliderStyle() const noexcept { return pimpl->style; }
@@ -1404,10 +1427,11 @@ void Slider::setChangeNotificationOnlyOnRelease (bool onlyNotifyOnRelease)
bool Slider::getSliderSnapsToMousePosition() const noexcept { return pimpl->snapsToMousePos; }
void Slider::setSliderSnapsToMousePosition (const bool shouldSnapToMouse) { pimpl->snapsToMousePos = shouldSnapToMouse; }
void Slider::setPopupDisplayEnabled (const bool enabled, Component* const parentComponentToUse)
void Slider::setPopupDisplayEnabled (bool showOnDrag, bool showOnHover, Component* parent)
{
pimpl->popupDisplayEnabled = enabled;
pimpl->parentForPopupDisplay = parentComponentToUse;
pimpl->showPopupOnDrag = showOnDrag;
pimpl->showPopupOnHover = showOnHover;
pimpl->parentForPopupDisplay = parent;
}
Component* Slider::getCurrentPopupDisplay() const noexcept { return pimpl->popupDisplay.get(); }
@@ -1570,7 +1594,9 @@ void Slider::resized() { pimpl->resized (getLookAndFeel()); }
void Slider::focusOfChildComponentChanged (FocusChangeType) { repaint(); }
void Slider::mouseDown (const MouseEvent& e) { pimpl->mouseDown (e); }
void Slider::mouseUp (const MouseEvent&) { pimpl->mouseUp(); }
void Slider::mouseUp (const MouseEvent&) { pimpl->mouseUp(); }
void Slider::mouseMove (const MouseEvent&) { pimpl->mouseMove(); }
void Slider::mouseExit (const MouseEvent&) { pimpl->mouseExit(); }
void Slider::modifierKeysChanged (const ModifierKeys& modifiers)
{


+ 8
- 2
source/modules/juce_gui_basics/widgets/juce_Slider.h View File

@@ -616,7 +616,7 @@ public:
bool getSliderSnapsToMousePosition() const noexcept;
/** If enabled, this gives the slider a pop-up bubble which appears while the
slider is being dragged.
slider is being dragged or hovered-over.
This can be handy if your slider doesn't have a text-box, so that users can
see the value just when they're changing it.
@@ -627,7 +627,9 @@ public:
transparent window, so if you're using an OS that can't do transparent windows
you'll have to add it to a parent component instead).
*/
void setPopupDisplayEnabled (bool isEnabled, Component* parentComponentToUse);
void setPopupDisplayEnabled (bool shouldShowOnMouseDrag,
bool shouldShowOnMouseHover,
Component* parentComponentToUse);
/** If a popup display is enabled and is currently visible, this returns the component
that is being shown, or nullptr if none is currently in use.
@@ -904,6 +906,10 @@ public:
void focusOfChildComponentChanged (FocusChangeType) override;
/** @internal */
void colourChanged() override;
/** @internal */
void mouseMove (const MouseEvent&) override;
/** @internal */
void mouseExit (const MouseEvent&) override;
private:
//==============================================================================


+ 1
- 1
source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp View File

@@ -537,7 +537,7 @@ void TableHeaderComponent::paint (Graphics& g)
g.setOrigin (x, 0);
g.reduceClipRegion (0, 0, ci->width, getHeight());
lf.drawTableHeaderColumn (g, ci->name, ci->id, ci->width, getHeight(),
lf.drawTableHeaderColumn (g, *this, ci->name, ci->id, ci->width, getHeight(),
ci->id == columnIdUnderMouse,
ci->id == columnIdUnderMouse && isMouseButtonDown(),
ci->propertyFlags);


+ 20
- 1
source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h View File

@@ -366,6 +366,24 @@ public:
*/
virtual void reactToMenuItem (int menuReturnId, int columnIdClicked);
//==============================================================================
/** A set of colour IDs to use to change the colour of various aspects of the TableHeaderComponent.
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
*/
enum ColourIds
{
textColourId = 0x1003800, /**< The colour for the text in the header. */
backgroundColourId = 0x1003810, /**< The colour of the table header background.
It's up to the LookAndFeel how this is used. */
outlineColourId = 0x1003820, /**< The colour of the table header's outline. */
highlightColourId = 0x1003830, /**< The colour of the table header background when
the mouse is over or down above the the table
header. It's up to the LookAndFeel to use a
variant of this colour to destiuish between
the down and hover state. */
};
//==============================================================================
/** This abstract base class is implemented by LookAndFeel classes. */
struct JUCE_API LookAndFeelMethods
@@ -374,7 +392,8 @@ public:
virtual void drawTableHeaderBackground (Graphics&, TableHeaderComponent&) = 0;
virtual void drawTableHeaderColumn (Graphics&, const String& columnName, int columnId,
virtual void drawTableHeaderColumn (Graphics&, TableHeaderComponent&,
const String& columnName, int columnId,
int width, int height,
bool isMouseOver, bool isMouseDown, int columnFlags) = 0;
};


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save