diff --git a/data/copy-juce-carla b/data/copy-juce-carla new file mode 100755 index 000000000..dd4593071 --- /dev/null +++ b/data/copy-juce-carla @@ -0,0 +1,13 @@ +#!/bin/bash + +JUCE_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/distrho/libs/juce-2.0/source/modules" +CARLA_MODULES_DIR="/home/falktx/Personal/FOSS/GIT/Carla/source/modules" + +MODULES=("juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics") + +for M in $MODULES; do + echo $M; + cp -r -v $JUCE_MODULES_DIR/$M/* $CARLA_MODULES_DIR/$M/ +done + +find $CARLA_MODULES_DIR -name juce_module_info -delete diff --git a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp index ff87a47da..0a6a1f916 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp @@ -96,7 +96,7 @@ MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); } void MidiBuffer::clear() noexcept { data.clearQuick(); } -void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated (minimumNumBytes); } +void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); } bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; } void MidiBuffer::clear (const int startSample, const int numSamples) @@ -104,7 +104,7 @@ void MidiBuffer::clear (const int startSample, const int numSamples) uint8* const start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1); uint8* const end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1); - data.removeRange (start - data.begin(), end - data.begin()); + data.removeRange ((int) (start - data.begin()), (int) (end - data.begin())); } void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber) @@ -121,7 +121,7 @@ void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const const size_t newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); const int offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); - data.insertMultiple (offset, 0, newItemSize); + data.insertMultiple (offset, 0, (int) newItemSize); uint8* const d = data.begin() + offset; *reinterpret_cast (d) = sampleNumber; @@ -221,9 +221,9 @@ bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePositio return false; samplePosition = MidiBufferHelpers::getEventTime (data); - const int numBytes = MidiBufferHelpers::getEventDataSize (data); - result = MidiMessage (data + sizeof (int32) + sizeof (uint16), numBytes, samplePosition); - data += sizeof (int32) + sizeof (uint16) + numBytes; + const int itemSize = MidiBufferHelpers::getEventDataSize (data); + result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition); + data += sizeof (int32) + sizeof (uint16) + itemSize; return true; } diff --git a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index 5625d1f2e..7d47f0568 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -1210,7 +1210,7 @@ private: static String hintToString (const void* hints, const char* type) { char* const hint = snd_device_name_get_hint (hints, type); - const String s (hint); + const String s (String::fromUTF8 (hint)); ::free (hint); return s; } diff --git a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index e46f6bfb2..60b82b7d4 100644 --- a/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -155,9 +155,7 @@ public: bufferSize (512), numInputChans (0), numOutputChans (0), - callbacksAllowed (true), - numInputChannelInfos (0), - numOutputChannelInfos (0) + callbacksAllowed (true) { jassert (deviceID != 0); @@ -199,9 +197,17 @@ public: tempOutputBuffers[i] = audioBuffer + count++ * tempBufSize; } + struct CallbackDetailsForChannel + { + int streamNum; + int dataOffsetSamples; + int dataStrideSamples; + }; + // returns the number of actual available channels - void fillInChannelInfo (const bool input) + StringArray getChannelInfo (const bool input, Array& newChannelInfo) const { + StringArray newNames; int chanNum = 0; UInt32 size; @@ -212,7 +218,7 @@ public: if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) { - HeapBlock bufList; + HeapBlock bufList; bufList.calloc (size, 1); if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, bufList))) @@ -237,82 +243,35 @@ public: name = String::fromUTF8 (channelName, (int) nameSize); } - if (input) + if ((input ? activeInputChans : activeOutputChans) [chanNum]) { - if (activeInputChans[chanNum]) - { - inputChannelInfo [numInputChannelInfos].streamNum = i; - inputChannelInfo [numInputChannelInfos].dataOffsetSamples = (int) j; - inputChannelInfo [numInputChannelInfos].dataStrideSamples = (int) b.mNumberChannels; - ++numInputChannelInfos; - } - - if (name.isEmpty()) - name << "Input " << (chanNum + 1); - - inChanNames.add (name); - } - else - { - if (activeOutputChans[chanNum]) - { - outputChannelInfo [numOutputChannelInfos].streamNum = i; - outputChannelInfo [numOutputChannelInfos].dataOffsetSamples = (int) j; - outputChannelInfo [numOutputChannelInfos].dataStrideSamples = (int) b.mNumberChannels; - ++numOutputChannelInfos; - } - - if (name.isEmpty()) - name << "Output " << (chanNum + 1); - - outChanNames.add (name); + CallbackDetailsForChannel info = { i, (int) j, (int) b.mNumberChannels }; + newChannelInfo.add (info); } + if (name.isEmpty()) + name << (input ? "Input " : "Output ") << (chanNum + 1); + + newNames.add (name); ++chanNum; } } } } + + return newNames; } - void updateDetailsFromDevice() + Array getSampleRatesFromDevice() const { - stopTimer(); - - if (deviceID == 0) - return; - - const ScopedLock sl (callbackLock); + Array newSampleRates; + String rates; AudioObjectPropertyAddress pa; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; - - UInt32 isAlive; - UInt32 size = sizeof (isAlive); - pa.mSelector = kAudioDevicePropertyDeviceIsAlive; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &isAlive)) - && isAlive == 0) - return; - - Float64 sr; - size = sizeof (sr); - pa.mSelector = kAudioDevicePropertyNominalSampleRate; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &sr))) - sampleRate = sr; - - UInt32 framesPerBuf; - size = sizeof (framesPerBuf); - pa.mSelector = kAudioDevicePropertyBufferFrameSize; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &framesPerBuf))) - { - bufferSize = (int) framesPerBuf; - allocateTempBuffers(); - } - - bufferSizes.clear(); - - pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + UInt32 size = 0; if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) { @@ -321,33 +280,42 @@ public: if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) { - bufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); + static const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; - for (int i = 32; i < 2048; i += 32) + for (int i = 0; i < numElementsInArray (possibleRates); ++i) { for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) { - if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) + if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) { - bufferSizes.addIfNotAlreadyThere (i); + newSampleRates.add (possibleRates[i]); + rates << possibleRates[i] << ' '; break; } } } - - if (bufferSize > 0) - bufferSizes.addIfNotAlreadyThere (bufferSize); } } - if (bufferSizes.size() == 0 && bufferSize > 0) - bufferSizes.add (bufferSize); + if (newSampleRates.size() == 0 && sampleRate > 0) + { + newSampleRates.add (sampleRate); + rates << sampleRate; + } + + JUCE_COREAUDIOLOG ("rates: " + rates); + return newSampleRates; + } - sampleRates.clear(); - const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; - String rates; + Array getBufferSizesFromDevice() const + { + Array newBufferSizes; - pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + AudioObjectPropertyAddress pa; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + UInt32 size = 0; if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) { @@ -356,59 +324,98 @@ public: if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) { - for (int i = 0; i < numElementsInArray (possibleRates); ++i) - { - bool ok = false; + newBufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); + for (int i = 32; i < 2048; i += 32) + { for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) - if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) - ok = true; - - if (ok) { - sampleRates.add (possibleRates[i]); - rates << possibleRates[i] << ' '; + if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) + { + newBufferSizes.addIfNotAlreadyThere (i); + break; + } } } + + if (bufferSize > 0) + newBufferSizes.addIfNotAlreadyThere (bufferSize); } } - if (sampleRates.size() == 0 && sampleRate > 0) - { - sampleRates.add (sampleRate); - rates << sampleRate; - } + if (newBufferSizes.size() == 0 && bufferSize > 0) + newBufferSizes.add (bufferSize); - JUCE_COREAUDIOLOG ("sr: " + rates); + return newBufferSizes; + } - inputLatency = 0; - outputLatency = 0; - UInt32 lat; - size = sizeof (lat); + int getLatencyFromDevice (AudioObjectPropertyScope scope) const + { + UInt32 lat = 0; + UInt32 size = sizeof (lat); + AudioObjectPropertyAddress pa; + pa.mElement = kAudioObjectPropertyElementMaster; pa.mSelector = kAudioDevicePropertyLatency; - pa.mScope = kAudioDevicePropertyScopeInput; - if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) - inputLatency = (int) lat; + pa.mScope = scope; + AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat); + return (int) lat; + } - pa.mScope = kAudioDevicePropertyScopeOutput; - size = sizeof (lat); + void updateDetailsFromDevice() + { + stopTimer(); - if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) - outputLatency = (int) lat; + if (deviceID == 0) + return; + // this collects all the new details from the device without any locking, then + // locks + swaps them afterwards. + AudioObjectPropertyAddress pa; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + + UInt32 isAlive; + UInt32 size = sizeof (isAlive); + pa.mSelector = kAudioDevicePropertyDeviceIsAlive; + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &isAlive)) && isAlive == 0) + return; + + Float64 sr; + size = sizeof (sr); + pa.mSelector = kAudioDevicePropertyNominalSampleRate; + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &sr))) + sampleRate = sr; + + UInt32 framesPerBuf = bufferSize; + size = sizeof (framesPerBuf); + pa.mSelector = kAudioDevicePropertyBufferFrameSize; + AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &framesPerBuf); + + Array newBufferSizes (getBufferSizesFromDevice()); + Array newSampleRates (getSampleRatesFromDevice()); + + inputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput); + outputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput); JUCE_COREAUDIOLOG ("lat: " + String (inputLatency) + " " + String (outputLatency)); - inChanNames.clear(); - outChanNames.clear(); + Array newInChans, newOutChans; + StringArray newInNames (getChannelInfo (true, newInChans)); + StringArray newOutNames (getChannelInfo (false, newOutChans)); + + // after getting the new values, lock + apply them + const ScopedLock sl (callbackLock); + + bufferSize = (int) framesPerBuf; + allocateTempBuffers(); - inputChannelInfo.calloc ((size_t) numInputChans + 2); - numInputChannelInfos = 0; + sampleRates.swapWith (newSampleRates); + bufferSizes.swapWith (newBufferSizes); - outputChannelInfo.calloc ((size_t) numOutputChans + 2); - numOutputChannelInfos = 0; + inChanNames.swapWith (newInNames); + outChanNames.swapWith (newOutNames); - fillInChannelInfo (true); - fillInChannelInfo (false); + inputChannelInfo.swapWith (newInChans); + outputChannelInfo.swapWith (newOutChans); } //============================================================================== @@ -675,7 +682,7 @@ public: { for (int i = numInputChans; --i >= 0;) { - const CallbackDetailsForChannel& info = inputChannelInfo[i]; + const CallbackDetailsForChannel& info = inputChannelInfo.getReference(i); float* dest = tempInputBuffers [i]; const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; @@ -720,7 +727,7 @@ public: for (int i = numOutputChans; --i >= 0;) { - const CallbackDetailsForChannel& info = outputChannelInfo[i]; + const CallbackDetailsForChannel& info = outputChannelInfo.getReference(i); const float* src = tempOutputBuffers [i]; float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; @@ -739,9 +746,9 @@ public: } else { - for (int i = jmin (numOutputChans, numOutputChannelInfos); --i >= 0;) + for (int i = jmin (numOutputChans, outputChannelInfo.size()); --i >= 0;) { - const CallbackDetailsForChannel& info = outputChannelInfo[i]; + const CallbackDetailsForChannel& info = outputChannelInfo.getReference(i); float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; const int stride = info.dataStrideSamples; @@ -803,15 +810,7 @@ private: int numInputChans, numOutputChans; bool callbacksAllowed; - struct CallbackDetailsForChannel - { - int streamNum; - int dataOffsetSamples; - int dataStrideSamples; - }; - - int numInputChannelInfos, numOutputChannelInfos; - HeapBlock inputChannelInfo, outputChannelInfo; + Array inputChannelInfo, outputChannelInfo; HeapBlock tempInputBuffers, tempOutputBuffers; //============================================================================== diff --git a/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp index 455921dbe..140e1e003 100644 --- a/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp @@ -380,7 +380,7 @@ public: samplesToWrite = const_cast (channels.getData()); } - return FLAC__stream_encoder_process (encoder, (const FLAC__int32**) samplesToWrite, (size_t) numSamples) != 0; + return FLAC__stream_encoder_process (encoder, (const FLAC__int32**) samplesToWrite, (unsigned) numSamples) != 0; } bool writeData (const void* const data, const int size) const diff --git a/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp index 97a9c86a4..f31990184 100644 --- a/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp @@ -186,7 +186,7 @@ public: { float** dataIn = nullptr; - const int samps = OggVorbisNamespace::ov_read_float (&ovFile, &dataIn, numToRead, &bitStream); + const long samps = OggVorbisNamespace::ov_read_float (&ovFile, &dataIn, numToRead, &bitStream); if (samps <= 0) break; diff --git a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp index 64aa3c7a8..1d2c845b1 100644 --- a/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp +++ b/source/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp @@ -100,6 +100,18 @@ bool AudioFormatReader::read (int* const* destSamples, return true; } +static void readChannels (AudioFormatReader& reader, + int** const chans, AudioSampleBuffer* const buffer, + const int startSample, const int numSamples, + const int64 readerStartSample, const int numTargetChannels) +{ + for (int j = 0; j < numTargetChannels; ++j) + chans[j] = reinterpret_cast (buffer->getSampleData (j, startSample)); + + chans[numTargetChannels] = nullptr; + reader.read (chans, numTargetChannels, readerStartSample, numSamples, true); +} + void AudioFormatReader::read (AudioSampleBuffer* buffer, int startSample, int numSamples, @@ -113,49 +125,51 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer, if (numSamples > 0) { const int numTargetChannels = buffer->getNumChannels(); - int* chans[3]; - if (useReaderLeftChan == useReaderRightChan) + if (numTargetChannels <= 2) { - chans[0] = reinterpret_cast (buffer->getSampleData (0, startSample)); - chans[1] = (numChannels > 1 && numTargetChannels > 1) ? reinterpret_cast (buffer->getSampleData (1, startSample)) : nullptr; - } - else if (useReaderLeftChan || (numChannels == 1)) - { - chans[0] = reinterpret_cast (buffer->getSampleData (0, startSample)); - chans[1] = nullptr; - } - else if (useReaderRightChan) - { - chans[0] = nullptr; - chans[1] = reinterpret_cast (buffer->getSampleData (0, startSample)); - } + int* const dest0 = reinterpret_cast (buffer->getSampleData (0, startSample)); + int* const dest1 = reinterpret_cast (numTargetChannels > 1 ? buffer->getSampleData (1, startSample) : nullptr); + int* chans[3]; - chans[2] = nullptr; + if (useReaderLeftChan == useReaderRightChan) + { + chans[0] = dest0; + chans[1] = numChannels > 1 ? dest1 : nullptr; + } + else if (useReaderLeftChan || (numChannels == 1)) + { + chans[0] = dest0; + chans[1] = nullptr; + } + else if (useReaderRightChan) + { + chans[0] = nullptr; + chans[1] = dest0; + } - read (chans, 2, readerStartSample, numSamples, true); + chans[2] = nullptr; + read (chans, 2, readerStartSample, numSamples, true); - if (! usesFloatingPointData) + // if the target's stereo and the source is mono, dupe the first channel.. + if (numTargetChannels > 1 && (chans[0] == nullptr || chans[1] == nullptr)) + memcpy (dest1, dest0, sizeof (float) * (size_t) numSamples); + } + else if (numTargetChannels <= 64) { - for (int j = 0; j < 2; ++j) - { - if (float* const d = reinterpret_cast (chans[j])) - { - const float multiplier = 1.0f / 0x7fffffff; - - for (int i = 0; i < numSamples; ++i) - d[i] = *reinterpret_cast (d + i) * multiplier; - } - } + int* chans[65]; + readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels); } - - if (numTargetChannels > 1 && (chans[0] == nullptr || chans[1] == nullptr)) + else { - // if this is a stereo buffer and the source was mono, dupe the first channel.. - memcpy (buffer->getSampleData (1, startSample), - buffer->getSampleData (0, startSample), - sizeof (float) * (size_t) numSamples); + HeapBlock chans (numTargetChannels); + readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels); } + + if (! usesFloatingPointData) + for (int j = 0; j < numTargetChannels; ++j) + if (float* const d = buffer->getSampleData (j, startSample)) + FloatVectorOperations::convertFixedToFloat (d, reinterpret_cast (d), 1.0f / 0x7fffffff, numSamples); } } diff --git a/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index 5bab6d8e8..b6f849f7e 100644 --- a/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -733,7 +733,7 @@ public: return s; } - void changeProgramName (int index, const String& newName) override + void changeProgramName (int /*index*/, const String& /*newName*/) override { jassertfalse; // xxx not implemented! } @@ -1005,8 +1005,8 @@ private: } //============================================================================== - OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, + OSStatus renderGetInput (AudioUnitRenderActionFlags*, + const AudioTimeStamp*, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) const @@ -1147,7 +1147,7 @@ private: ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); } - static OSStatus renderMidiOutputCallback (void* hostRef, const AudioTimeStamp* timeStamp, UInt32 midiOutNum, + static OSStatus renderMidiOutputCallback (void* hostRef, const AudioTimeStamp*, UInt32 /*midiOutNum*/, const struct MIDIPacketList* pktlist) { return static_cast (hostRef)->renderMidiOutput (pktlist); @@ -1331,7 +1331,7 @@ public: startTimer (jmin (713, getTimerInterval() + 51)); } - void childBoundsChanged (Component* child) override + void childBoundsChanged (Component*) override { setSize (wrapper.getWidth(), wrapper.getHeight()); startTimer (70); diff --git a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index 8ff23ac7f..e0cc66c79 100644 --- a/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -1675,8 +1675,8 @@ private: void setHostTimeFrameRate (long frameRateIndex, double frameRate, double currentTime) noexcept { vstHostTime.flags |= kVstSmpteValid; - vstHostTime.smpteFrameRate = frameRateIndex; - vstHostTime.smpteOffset = (long) (currentTime * 80.0 * frameRate + 0.5); + vstHostTime.smpteFrameRate = (VstInt32) frameRateIndex; + vstHostTime.smpteOffset = (VstInt32) (currentTime * 80.0 * frameRate + 0.5); } bool restoreProgramSettings (const fxProgram* const prog) @@ -2510,7 +2510,7 @@ private: deleteWindow(); } - HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) override + HIViewRef attachView (WindowRef windowRef, HIViewRef /*rootView*/) override { owner.openPluginWindow (windowRef); return 0; diff --git a/source/modules/juce_audio_processors/juce_audio_processors.cpp b/source/modules/juce_audio_processors/juce_audio_processors.cpp index 0696808b4..955d770c3 100644 --- a/source/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/source/modules/juce_audio_processors/juce_audio_processors.cpp @@ -37,10 +37,7 @@ #include "../juce_core/native/juce_BasicNativeHeaders.h" #include "juce_audio_processors.h" - -#if JUCE_MODULE_AVAILABLE_juce_gui_extra - #include "../juce_gui_extra/juce_gui_extra.h" -#endif +#include "../juce_gui_extra/juce_gui_extra.h" //============================================================================== #if JUCE_MAC diff --git a/source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp b/source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp index 12c4c958d..06ef2b52a 100644 --- a/source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp +++ b/source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp @@ -287,8 +287,8 @@ XmlElement* KnownPluginList::createXml() const { XmlElement* const e = new XmlElement ("KNOWNPLUGINS"); - for (int i = 0; i < types.size(); ++i) - e->addChildElement (types.getUnchecked(i)->createXml()); + for (int i = types.size(); --i >= 0;) + e->prependChildElement (types.getUnchecked(i)->createXml()); for (int i = 0; i < blacklist.size(); ++i) e->createNewChildElement ("BLACKLISTED")->setAttribute ("id", blacklist[i]); diff --git a/source/modules/juce_core/javascript/juce_Javascript.cpp b/source/modules/juce_core/javascript/juce_Javascript.cpp index c7fe1f7be..fb22ec6db 100644 --- a/source/modules/juce_core/javascript/juce_Javascript.cpp +++ b/source/modules/juce_core/javascript/juce_Javascript.cpp @@ -56,6 +56,11 @@ namespace TokenTypes JUCE_DECLARE_JS_TOKEN (identifier, "$identifier") } +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4702) +#endif + //============================================================================== struct JavascriptEngine::RootObject : public DynamicObject { @@ -853,8 +858,8 @@ struct JavascriptEngine::RootObject : public DynamicObject bool matchToken (TokenType name, const size_t len) noexcept { - if (p.compareUpTo (String::CharPointerType (name), len) != 0) return false; - p += len; return true; + if (p.compareUpTo (CharPointer_ASCII (name), (int) len) != 0) return false; + p += (int) len; return true; } void skipWhitespaceAndComments() @@ -1211,6 +1216,9 @@ struct JavascriptEngine::RootObject : public DynamicObject return parseSuffixes (s.release()); } + if (matchIf (TokenTypes::plusplus)) return parsePostIncDec (input); + if (matchIf (TokenTypes::minusminus)) return parsePostIncDec (input); + return input.release(); } @@ -1339,8 +1347,6 @@ struct JavascriptEngine::RootObject : public DynamicObject { if (matchIf (TokenTypes::plus)) { ExpPtr b (parseMultiplyDivide()); a = new AdditionOp (location, a, b); } else if (matchIf (TokenTypes::minus)) { ExpPtr b (parseMultiplyDivide()); a = new SubtractionOp (location, a, b); } - else if (matchIf (TokenTypes::plusplus)) a = parsePostIncDec (a); - else if (matchIf (TokenTypes::minusminus)) a = parsePostIncDec (a); else break; } @@ -1429,7 +1435,7 @@ struct JavascriptEngine::RootObject : public DynamicObject } static Identifier getClassName() { static const Identifier i ("Object"); return i; } - static var dump (Args a) { DBG (JSON::toString (a.thisObject)); return var::undefined(); } + static var dump (Args a) { DBG (JSON::toString (a.thisObject)); (void) a; return var::undefined(); } static var clone (Args a) { return a.thisObject.clone(); } }; @@ -1681,3 +1687,7 @@ var JavascriptEngine::callFunction (Identifier function, const var::NativeFuncti return var::undefined(); } + +#if JUCE_MSVC + #pragma warning (pop) +#endif diff --git a/source/modules/juce_core/memory/juce_MemoryBlock.cpp b/source/modules/juce_core/memory/juce_MemoryBlock.cpp index e0a4cfbac..f3be0f03d 100644 --- a/source/modules/juce_core/memory/juce_MemoryBlock.cpp +++ b/source/modules/juce_core/memory/juce_MemoryBlock.cpp @@ -265,7 +265,7 @@ void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcep String MemoryBlock::toString() const { - return String::fromUTF8 (data, size); + return String::fromUTF8 (data, (int) size); } //============================================================================== diff --git a/source/modules/juce_core/native/juce_mac_Network.mm b/source/modules/juce_core/native/juce_mac_Network.mm index b042d0571..75c9ce19a 100644 --- a/source/modules/juce_core/native/juce_mac_Network.mm +++ b/source/modules/juce_core/native/juce_mac_Network.mm @@ -220,7 +220,7 @@ public: initialised = true; } - void didSendBodyData (int /*totalBytesWritten*/, int /*totalBytesExpected*/) + void didSendBodyData (NSInteger /*totalBytesWritten*/, NSInteger /*totalBytesExpected*/) { } diff --git a/source/modules/juce_core/native/juce_posix_SharedCode.h b/source/modules/juce_core/native/juce_posix_SharedCode.h index e3ce5fde2..243a1943a 100644 --- a/source/modules/juce_core/native/juce_posix_SharedCode.h +++ b/source/modules/juce_core/native/juce_posix_SharedCode.h @@ -114,8 +114,13 @@ bool WaitableEvent::wait (const int timeOutMillisecs) const noexcept void WaitableEvent::signal() const noexcept { pthread_mutex_lock (&mutex); - triggered = true; - pthread_cond_broadcast (&condition); + + if (! triggered) + { + triggered = true; + pthread_cond_broadcast (&condition); + } + pthread_mutex_unlock (&mutex); } diff --git a/source/modules/juce_core/streams/juce_InputStream.cpp b/source/modules/juce_core/streams/juce_InputStream.cpp index 12c3aef43..08ff61ccd 100644 --- a/source/modules/juce_core/streams/juce_InputStream.cpp +++ b/source/modules/juce_core/streams/juce_InputStream.cpp @@ -175,7 +175,7 @@ String InputStream::readString() } } - return String::fromUTF8 (data, i); + return String::fromUTF8 (data, (int) i); } String InputStream::readNextLine() diff --git a/source/modules/juce_core/system/juce_StandardHeader.h b/source/modules/juce_core/system/juce_StandardHeader.h index b249b8d6f..769a84837 100644 --- a/source/modules/juce_core/system/juce_StandardHeader.h +++ b/source/modules/juce_core/system/juce_StandardHeader.h @@ -36,7 +36,7 @@ */ #define JUCE_MAJOR_VERSION 2 #define JUCE_MINOR_VERSION 1 -#define JUCE_BUILDNUMBER 6 +#define JUCE_BUILDNUMBER 7 /** Current Juce version number. diff --git a/source/modules/juce_core/text/juce_String.cpp b/source/modules/juce_core/text/juce_String.cpp index f37daa326..77845db9b 100644 --- a/source/modules/juce_core/text/juce_String.cpp +++ b/source/modules/juce_core/text/juce_String.cpp @@ -524,41 +524,42 @@ juce_wchar String::operator[] (int index) const noexcept return text [index]; } -int String::hashCode() const noexcept +template +struct HashGenerator { - int result = 0; + template + static Type calculate (CharPointer t) noexcept + { + Type result = Type(); - for (CharPointerType t (text); ! t.isEmpty();) - result = 31 * result + (int) t.getAndAdvance(); + while (! t.isEmpty()) + result = multiplier * result + t.getAndAdvance(); - return result; -} + return result; + } -int64 String::hashCode64() const noexcept -{ - int64 result = 0; + enum { multiplier = sizeof (Type) > 4 ? 101 : 31 }; +}; - for (CharPointerType t (text); ! t.isEmpty();) - result = 101 * result + t.getAndAdvance(); - - return result; -} +int String::hashCode() const noexcept { return HashGenerator ::calculate (text); } +int64 String::hashCode64() const noexcept { return HashGenerator ::calculate (text); } +std::size_t String::hash() const noexcept { return HashGenerator::calculate (text); } //============================================================================== JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const String& s2) noexcept { return s1.compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const char* const s2) noexcept { return s1.compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const wchar_t* const s2) noexcept { return s1.compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const String& s2) noexcept { return s1.compare (s2) != 0; } -JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const char* const s2) noexcept { return s1.compare (s2) != 0; } -JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const wchar_t* const s2) noexcept { return s1.compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const char* s2) noexcept { return s1.compare (s2) == 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const char* s2) noexcept { return s1.compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const wchar_t* s2) noexcept { return s1.compare (s2) == 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const wchar_t* s2) noexcept { return s1.compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) == 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } -JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) != 0; } JUCE_API bool JUCE_CALLTYPE operator> (const String& s1, const String& s2) noexcept { return s1.compare (s2) > 0; } JUCE_API bool JUCE_CALLTYPE operator< (const String& s1, const String& s2) noexcept { return s1.compare (s2) < 0; } JUCE_API bool JUCE_CALLTYPE operator>= (const String& s1, const String& s2) noexcept { return s1.compare (s2) >= 0; } diff --git a/source/modules/juce_core/text/juce_String.h b/source/modules/juce_core/text/juce_String.h index e48a20f51..23ed308af 100644 --- a/source/modules/juce_core/text/juce_String.h +++ b/source/modules/juce_core/text/juce_String.h @@ -180,6 +180,9 @@ public: /** Generates a probably-unique 64-bit hashcode from this string. */ int64 hashCode64() const noexcept; + /** Generates a probably-unique hashcode from this string. */ + std::size_t hash() const noexcept; + /** Returns the number of characters in the string. */ int length() const noexcept; diff --git a/source/modules/juce_core/xml/juce_XmlElement.cpp b/source/modules/juce_core/xml/juce_XmlElement.cpp index 603215197..0a0a72593 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.cpp +++ b/source/modules/juce_core/xml/juce_XmlElement.cpp @@ -585,18 +585,36 @@ XmlElement* XmlElement::getChildByAttribute (StringRef attributeName, StringRef void XmlElement::addChildElement (XmlElement* const newNode) noexcept { if (newNode != nullptr) + { + // The element being added must not be a child of another node! + jassert (newNode->nextListItem == nullptr); + firstChildElement.append (newNode); + } } void XmlElement::insertChildElement (XmlElement* const newNode, int indexToInsertAt) noexcept { if (newNode != nullptr) { - removeChildElement (newNode, false); + // The element being added must not be a child of another node! + jassert (newNode->nextListItem == nullptr); + firstChildElement.insertAtIndex (indexToInsertAt, newNode); } } +void XmlElement::prependChildElement (XmlElement* newNode) noexcept +{ + if (newNode != nullptr) + { + // The element being added must not be a child of another node! + jassert (newNode->nextListItem == nullptr); + + firstChildElement.insertNext (newNode); + } +} + XmlElement* XmlElement::createNewChildElement (const String& childTagName) { XmlElement* const newElement = new XmlElement (childTagName); diff --git a/source/modules/juce_core/xml/juce_XmlElement.h b/source/modules/juce_core/xml/juce_XmlElement.h index ff47977ad..8fc3800d2 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.h +++ b/source/modules/juce_core/xml/juce_XmlElement.h @@ -504,6 +504,10 @@ public: make sure the object that you pass in will not be deleted by anything else, and make sure it's not already the child of another element. + Note that due to the XmlElement using a singly-linked-list, prependChildElement() + is an O(1) operation, but addChildElement() is an O(N) operation - so if + you're adding large number of elements, you may prefer to do so in reverse order! + @see getFirstChildElement, getNextElement, getNumChildElements, getChildElement, removeChildElement */ @@ -515,14 +519,28 @@ public: make sure the object that you pass in will not be deleted by anything else, and make sure it's not already the child of another element. - @param newChildNode the element to add + @param newChildElement the element to add @param indexToInsertAt the index at which to insert the new element - if this is below zero, it will be added to the end of the list @see addChildElement, insertChildElement */ - void insertChildElement (XmlElement* newChildNode, + void insertChildElement (XmlElement* newChildElement, int indexToInsertAt) noexcept; + /** Inserts an element at the beginning of this element's list of children. + + Child elements are deleted automatically when their parent is deleted, so + make sure the object that you pass in will not be deleted by anything else, + and make sure it's not already the child of another element. + + Note that due to the XmlElement using a singly-linked-list, prependChildElement() + is an O(1) operation, but addChildElement() is an O(N) operation - so if + you're adding large number of elements, you may prefer to do so in reverse order! + + @see addChildElement, insertChildElement + */ + void prependChildElement (XmlElement* newChildElement) noexcept; + /** Creates a new element with the given name and returns it, after adding it as a child element. diff --git a/source/modules/juce_data_structures/values/juce_ValueTree.cpp b/source/modules/juce_data_structures/values/juce_ValueTree.cpp index 5a823f727..2c4a94cef 100644 --- a/source/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/source/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -412,8 +412,9 @@ public: XmlElement* const xml = new XmlElement (type.toString()); properties.copyToXmlAttributes (*xml); - for (int i = 0; i < children.size(); ++i) - xml->addChildElement (children.getObjectPointerUnchecked(i)->createXml()); + // (NB: it's faster to add nodes to XML elements in reverse order) + for (int i = children.size(); --i >= 0;) + xml->prependChildElement (children.getObjectPointerUnchecked(i)->createXml()); return xml; } diff --git a/source/modules/juce_events/messages/juce_Initialisation.h b/source/modules/juce_events/messages/juce_Initialisation.h index 4a93b0f04..1b33fd7d8 100644 --- a/source/modules/juce_events/messages/juce_Initialisation.h +++ b/source/modules/juce_events/messages/juce_Initialisation.h @@ -89,7 +89,7 @@ public: #else #if JUCE_WINDOWS - #if defined (WINAPI) || defined (_WINDOWS_) + #if defined (WINAPI) || defined (_WINDOWS_) || defined(JUCE_MINGW) #define JUCE_MAIN_FUNCTION int __stdcall WinMain (HINSTANCE, HINSTANCE, const LPSTR, int) #elif defined (_UNICODE) #define JUCE_MAIN_FUNCTION int __stdcall WinMain (void*, void*, const wchar_t*, int) diff --git a/source/modules/juce_events/native/juce_mac_MessageManager.mm b/source/modules/juce_events/native/juce_mac_MessageManager.mm index f10f8aaa9..b8d83aff1 100644 --- a/source/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/source/modules/juce_events/native/juce_mac_MessageManager.mm @@ -333,6 +333,18 @@ void MessageManager::broadcastMessage (const String& message) void repostCurrentNSEvent(); void repostCurrentNSEvent() { - NSEvent* e = [NSApp currentEvent]; - [[NSOperationQueue mainQueue] addOperationWithBlock: ^{ [NSApp postEvent: e atStart: YES]; }]; + struct EventReposter : public CallbackMessage + { + EventReposter() : e ([[NSApp currentEvent] retain]) {} + ~EventReposter() { [e release]; } + + void messageCallback() override + { + [NSApp postEvent: e atStart: YES]; + } + + NSEvent* e; + }; + + (new EventReposter())->post(); } diff --git a/source/modules/juce_graphics/fonts/juce_Font.cpp b/source/modules/juce_graphics/fonts/juce_Font.cpp index 3d5d14eba..7ab54495b 100644 --- a/source/modules/juce_graphics/fonts/juce_Font.cpp +++ b/source/modules/juce_graphics/fonts/juce_Font.cpp @@ -155,9 +155,19 @@ void Typeface::setTypefaceCacheSize (int numFontsToCache) TypefaceCache::getInstance()->setSize (numFontsToCache); } +#if JUCE_MODULE_AVAILABLE_juce_opengl +extern void clearOpenGLGlyphCache(); +#endif + void Typeface::clearTypefaceCache() { TypefaceCache::getInstance()->clear(); + + RenderingHelpers::SoftwareRendererSavedState::clearGlyphCache(); + + #if JUCE_MODULE_AVAILABLE_juce_opengl + clearOpenGLGlyphCache(); + #endif } //============================================================================== diff --git a/source/modules/juce_graphics/geometry/juce_PathIterator.cpp b/source/modules/juce_graphics/geometry/juce_PathIterator.cpp index 965826456..0a15581ce 100644 --- a/source/modules/juce_graphics/geometry/juce_PathIterator.cpp +++ b/source/modules/juce_graphics/geometry/juce_PathIterator.cpp @@ -69,48 +69,45 @@ bool PathFlatteningIterator::next() float y3 = 0; float x4 = 0; float y4 = 0; - float type; for (;;) { + float type; + if (stackPos == stackBase) { if (index >= path.numElements) - { return false; - } - else + + type = points [index++]; + + if (type != Path::closeSubPathMarker) { - type = points [index++]; + x2 = points [index++]; + y2 = points [index++]; + + if (type == Path::quadMarker) + { + x3 = points [index++]; + y3 = points [index++]; + + if (! isIdentityTransform) + transform.transformPoints (x2, y2, x3, y3); + } + else if (type == Path::cubicMarker) + { + x3 = points [index++]; + y3 = points [index++]; + x4 = points [index++]; + y4 = points [index++]; - if (type != Path::closeSubPathMarker) + if (! isIdentityTransform) + transform.transformPoints (x2, y2, x3, y3, x4, y4); + } + else { - x2 = points [index++]; - y2 = points [index++]; - - if (type == Path::quadMarker) - { - x3 = points [index++]; - y3 = points [index++]; - - if (! isIdentityTransform) - transform.transformPoints (x2, y2, x3, y3); - } - else if (type == Path::cubicMarker) - { - x3 = points [index++]; - y3 = points [index++]; - x4 = points [index++]; - y4 = points [index++]; - - if (! isIdentityTransform) - transform.transformPoints (x2, y2, x3, y3, x4, y4); - } - else - { - if (! isIdentityTransform) - transform.transformPoint (x2, y2); - } + if (! isIdentityTransform) + transform.transformPoint (x2, y2); } } } @@ -150,7 +147,8 @@ bool PathFlatteningIterator::next() return true; } - else if (type == Path::quadMarker) + + if (type == Path::quadMarker) { const size_t offset = (size_t) (stackPos - stackBase); diff --git a/source/modules/juce_graphics/geometry/juce_Rectangle.h b/source/modules/juce_graphics/geometry/juce_Rectangle.h index 44f4e06eb..7dc0e736b 100644 --- a/source/modules/juce_graphics/geometry/juce_Rectangle.h +++ b/source/modules/juce_graphics/geometry/juce_Rectangle.h @@ -190,61 +190,73 @@ public: Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); } /** Returns a rectangle which has the same position and height as this one, but with a different width. */ - Rectangle withWidth (const ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } + Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } /** Returns a rectangle which has the same position and width as this one, but with a different height. */ - Rectangle withHeight (const ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); } + Rectangle withHeight (ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); } /** Returns a rectangle with the same position as this one, but a new size. */ - Rectangle withSize (const ValueType newWidth, const ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); } + Rectangle withSize (ValueType newWidth, const ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); } /** Moves the x position, adjusting the width so that the right-hand edge remains in the same place. If the x is moved to be on the right of the current right-hand edge, the width will be set to zero. @see withLeft */ - void setLeft (const ValueType newLeft) noexcept { w = jmax (ValueType(), pos.x + w - newLeft); pos.x = newLeft; } + void setLeft (ValueType newLeft) noexcept { w = jmax (ValueType(), pos.x + w - newLeft); pos.x = newLeft; } /** Returns a new rectangle with a different x position, but the same right-hand edge as this one. If the new x is beyond the right of the current right-hand edge, the width will be set to zero. @see setLeft */ - Rectangle withLeft (const ValueType newLeft) const noexcept { return Rectangle (newLeft, pos.y, jmax (ValueType(), pos.x + w - newLeft), h); } + Rectangle withLeft (ValueType newLeft) const noexcept { return Rectangle (newLeft, pos.y, jmax (ValueType(), pos.x + w - newLeft), h); } /** Moves the y position, adjusting the height so that the bottom edge remains in the same place. If the y is moved to be below the current bottom edge, the height will be set to zero. @see withTop */ - void setTop (const ValueType newTop) noexcept { h = jmax (ValueType(), pos.y + h - newTop); pos.y = newTop; } + void setTop (ValueType newTop) noexcept { h = jmax (ValueType(), pos.y + h - newTop); pos.y = newTop; } /** Returns a new rectangle with a different y position, but the same bottom edge as this one. If the new y is beyond the bottom of the current rectangle, the height will be set to zero. @see setTop */ - Rectangle withTop (const ValueType newTop) const noexcept { return Rectangle (pos.x, newTop, w, jmax (ValueType(), pos.y + h - newTop)); } + Rectangle withTop (ValueType newTop) const noexcept { return Rectangle (pos.x, newTop, w, jmax (ValueType(), pos.y + h - newTop)); } /** Adjusts the width so that the right-hand edge of the rectangle has this new value. If the new right is below the current X value, the X will be pushed down to match it. @see getRight, withRight */ - void setRight (const ValueType newRight) noexcept { pos.x = jmin (pos.x, newRight); w = newRight - pos.x; } + void setRight (ValueType newRight) noexcept { pos.x = jmin (pos.x, newRight); w = newRight - pos.x; } /** Returns a new rectangle with a different right-hand edge position, but the same left-hand edge as this one. If the new right edge is below the current left-hand edge, the width will be set to zero. @see setRight */ - Rectangle withRight (const ValueType newRight) const noexcept { return Rectangle (jmin (pos.x, newRight), pos.y, jmax (ValueType(), newRight - pos.x), h); } + Rectangle withRight (ValueType newRight) const noexcept { return Rectangle (jmin (pos.x, newRight), pos.y, jmax (ValueType(), newRight - pos.x), h); } /** Adjusts the height so that the bottom edge of the rectangle has this new value. If the new bottom is lower than the current Y value, the Y will be pushed down to match it. @see getBottom, withBottom */ - void setBottom (const ValueType newBottom) noexcept { pos.y = jmin (pos.y, newBottom); h = newBottom - pos.y; } + void setBottom (ValueType newBottom) noexcept { pos.y = jmin (pos.y, newBottom); h = newBottom - pos.y; } /** Returns a new rectangle with a different bottom edge position, but the same top edge as this one. If the new y is beyond the bottom of the current rectangle, the height will be set to zero. @see setBottom */ - Rectangle withBottom (const ValueType newBottom) const noexcept { return Rectangle (pos.x, jmin (pos.y, newBottom), w, jmax (ValueType(), newBottom - pos.y)); } + Rectangle withBottom (ValueType newBottom) const noexcept { return Rectangle (pos.x, jmin (pos.y, newBottom), w, jmax (ValueType(), newBottom - pos.y)); } + + /** Returns a version of this rectangle with the given amount removed from its left-hand edge. */ + Rectangle withTrimmedLeft (ValueType amountToRemove) const noexcept { return withLeft (pos.x + amountToRemove); } + + /** Returns a version of this rectangle with the given amount removed from its right-hand edge. */ + Rectangle withTrimmedRight (ValueType amountToRemove) const noexcept { return withWidth (w - amountToRemove); } + + /** Returns a version of this rectangle with the given amount removed from its top edge. */ + Rectangle withTrimmedTop (ValueType amountToRemove) const noexcept { return withTop (pos.y + amountToRemove); } + + /** Returns a version of this rectangle with the given amount removed from its bottom edge. */ + Rectangle withTrimmedBottom (ValueType amountToRemove) const noexcept { return withHeight (h - amountToRemove); } //============================================================================== /** Moves the rectangle's position by adding amount to its x and y coordinates. */ diff --git a/source/modules/juce_graphics/native/juce_RenderingHelpers.h b/source/modules/juce_graphics/native/juce_RenderingHelpers.h index 1f48f427d..6c9bde3b0 100644 --- a/source/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/source/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -142,7 +142,7 @@ class GlyphCache : private DeletedAtShutdown public: GlyphCache() { - addNewGlyphSlots (120); + reset(); } ~GlyphCache() @@ -207,6 +207,15 @@ public: glyph->draw (target, pos); } + void reset() + { + const ScopedWriteLock swl (lock); + glyphs.clear(); + addNewGlyphSlots (120); + hits.set (0); + misses.set (0); + } + private: friend struct ContainerDeletePolicy; OwnedArray glyphs; @@ -215,6 +224,8 @@ private: void addNewGlyphSlots (int num) { + glyphs.ensureStorageAllocated (glyphs.size() + num); + while (--num >= 0) glyphs.add (new CachedGlyphType()); } @@ -2419,6 +2430,13 @@ public: } } + typedef GlyphCache, SoftwareRendererSavedState> GlyphCacheType; + + static void clearGlyphCache() + { + GlyphCacheType::getInstance().reset(); + } + //============================================================================== void drawGlyph (int glyphNumber, const AffineTransform& trans) { @@ -2426,8 +2444,6 @@ public: { if (trans.isOnlyTranslation() && ! transform.isRotated) { - typedef GlyphCache , SoftwareRendererSavedState> GlyphCacheType; - GlyphCacheType& cache = GlyphCacheType::getInstance(); Point pos (trans.getTranslationX(), trans.getTranslationY()); diff --git a/source/modules/juce_graphics/native/juce_mac_Fonts.mm b/source/modules/juce_graphics/native/juce_mac_Fonts.mm index 4c482432a..aa1851a13 100644 --- a/source/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/source/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -340,7 +340,7 @@ namespace CoreTextTypeLayout CFArrayRef lines = CTFrameGetLines (frame); const CFIndex numLines = CFArrayGetCount (lines); - glyphLayout.ensureStorageAllocated (numLines); + glyphLayout.ensureStorageAllocated ((int) numLines); for (CFIndex i = 0; i < numLines; ++i) { diff --git a/source/modules/juce_gui_basics/buttons/juce_Button.cpp b/source/modules/juce_gui_basics/buttons/juce_Button.cpp index 4b688ee0f..9eaddebf3 100644 --- a/source/modules/juce_gui_basics/buttons/juce_Button.cpp +++ b/source/modules/juce_gui_basics/buttons/juce_Button.cpp @@ -309,7 +309,8 @@ void Button::internalClickCallback (const ModifierKeys& modifiers) { if (clickTogglesState) setToggleState (radioGroupId != 0 || ! lastToggleState, sendNotification); - else + + if (radioGroupId != 0 || ! clickTogglesState) sendClickMessage (modifiers); } diff --git a/source/modules/juce_gui_basics/buttons/juce_Button.h b/source/modules/juce_gui_basics/buttons/juce_Button.h index 2daf940b4..0516915b3 100644 --- a/source/modules/juce_gui_basics/buttons/juce_Button.h +++ b/source/modules/juce_gui_basics/buttons/juce_Button.h @@ -355,6 +355,33 @@ public: // This method's parameters have changed - see the new version. JUCE_DEPRECATED (void setToggleState (bool, bool)); + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + button-drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawButtonBackground (Graphics&, Button& button, const Colour& backgroundColour, + bool isMouseOverButton, bool isButtonDown) = 0; + + virtual Font getTextButtonFont (TextButton& button) = 0; + + /** Draws the text for a TextButton. */ + virtual void drawButtonText (Graphics&, TextButton&, bool isMouseOverButton, bool isButtonDown) = 0; + + /** Draws the contents of a standard ToggleButton. */ + virtual void drawToggleButton (Graphics&, ToggleButton&, bool isMouseOverButton, bool isButtonDown) = 0; + + virtual void changeToggleButtonWidthToFitText (ToggleButton&) = 0; + + virtual void drawTickBox (Graphics&, Component&, float x, float y, float w, float h, + bool ticked, bool isEnabled, bool isMouseOverButton, bool isButtonDown) = 0; + + virtual void drawDrawableButton (Graphics&, DrawableButton&, bool isMouseOverButton, bool isButtonDown) = 0; + }; + protected: //============================================================================== /** This method is called when the button has been clicked. diff --git a/source/modules/juce_gui_basics/buttons/juce_ImageButton.h b/source/modules/juce_gui_basics/buttons/juce_ImageButton.h index dd39e6209..64db59df2 100644 --- a/source/modules/juce_gui_basics/buttons/juce_ImageButton.h +++ b/source/modules/juce_gui_basics/buttons/juce_ImageButton.h @@ -124,6 +124,17 @@ public: */ Image getDownImage() const; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawImageButton (Graphics&, Image*, + int imageX, int imageY, int imageW, int imageH, + const Colour& overlayColour, float imageOpacity, ImageButton&) = 0; + }; + protected: //============================================================================== /** @internal */ diff --git a/source/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp b/source/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp index d25ae2930..28cd8de82 100644 --- a/source/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp +++ b/source/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp @@ -24,34 +24,32 @@ ShapeButton::ShapeButton (const String& t, Colour n, Colour o, Colour d) : Button (t), - normalColour (n), - overColour (o), - downColour (d), + normalColour (n), overColour (o), downColour (d), maintainShapeProportions (false), outlineWidth (0.0f) { } -ShapeButton::~ShapeButton() -{ -} +ShapeButton::~ShapeButton() {} -void ShapeButton::setColours (Colour newNormalColour, - Colour newOverColour, - Colour newDownColour) +void ShapeButton::setColours (Colour newNormalColour, Colour newOverColour, Colour newDownColour) { normalColour = newNormalColour; overColour = newOverColour; downColour = newDownColour; } -void ShapeButton::setOutline (Colour newOutlineColour, - const float newOutlineWidth) +void ShapeButton::setOutline (Colour newOutlineColour, const float newOutlineWidth) { outlineColour = newOutlineColour; outlineWidth = newOutlineWidth; } +void ShapeButton::setBorderSize (BorderSize newBorder) +{ + border = newBorder; +} + void ShapeButton::setShape (const Path& newShape, const bool resizeNowToFitThisShape, const bool maintainShapeProportions_, @@ -73,8 +71,8 @@ void ShapeButton::setShape (const Path& newShape, shape.applyTransform (AffineTransform::translation (-newBounds.getX(), -newBounds.getY())); - setSize (1 + (int) (newBounds.getWidth() + outlineWidth), - 1 + (int) (newBounds.getHeight() + outlineWidth)); + setSize (1 + (int) (newBounds.getWidth() + outlineWidth) + border.getLeftAndRight(), + 1 + (int) (newBounds.getHeight() + outlineWidth) + border.getTopAndBottom()); } repaint(); @@ -88,7 +86,7 @@ void ShapeButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButto isButtonDown = false; } - Rectangle r (getLocalBounds().toFloat().reduced (outlineWidth * 0.5f)); + Rectangle r (border.subtractedFrom (getLocalBounds()).toFloat().reduced (outlineWidth * 0.5f)); if (getComponentEffect() != nullptr) r = r.reduced (2.0f); diff --git a/source/modules/juce_gui_basics/buttons/juce_ShapeButton.h b/source/modules/juce_gui_basics/buttons/juce_ShapeButton.h index b797fffb5..ddc462521 100644 --- a/source/modules/juce_gui_basics/buttons/juce_ShapeButton.h +++ b/source/modules/juce_gui_basics/buttons/juce_ShapeButton.h @@ -80,8 +80,12 @@ public: @param outlineColour the colour to use @param outlineStrokeWidth the thickness of line to draw */ - void setOutline (Colour outlineColour, - float outlineStrokeWidth); + void setOutline (Colour outlineColour, float outlineStrokeWidth); + + /** This lets you specify a border to be left around the edge of the button when + drawing the shape. + */ + void setBorderSize (BorderSize border); /** @internal */ void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; @@ -91,6 +95,7 @@ private: Colour normalColour, overColour, downColour, outlineColour; DropShadowEffect shadow; Path shape; + BorderSize border; bool maintainShapeProportions; float outlineWidth; diff --git a/source/modules/juce_gui_basics/components/juce_Desktop.cpp b/source/modules/juce_gui_basics/components/juce_Desktop.cpp index 0faaeb4a8..5deb49089 100644 --- a/source/modules/juce_gui_basics/components/juce_Desktop.cpp +++ b/source/modules/juce_gui_basics/components/juce_Desktop.cpp @@ -89,7 +89,7 @@ LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept if (currentLookAndFeel == nullptr) { if (defaultLookAndFeel == nullptr) - defaultLookAndFeel = new LookAndFeel(); + defaultLookAndFeel = new LookAndFeel_V2(); currentLookAndFeel = defaultLookAndFeel; } diff --git a/source/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp b/source/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp index 5b185838f..a16d57d48 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp +++ b/source/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp @@ -56,6 +56,7 @@ void DirectoryContentsList::setDirectory (const File& directory, { clear(); root = directory; + changed(); // (this forces a refresh when setTypeFlags() is called, rather than triggering two refreshes) fileTypeFlags &= ~(File::findDirectories | File::findFiles); diff --git a/source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h b/source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h index ef78e4eb8..a1151ef02 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h +++ b/source/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h @@ -177,6 +177,42 @@ public: */ static void getDefaultRoots (StringArray& rootNames, StringArray& rootPaths); + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + various file-browser layout and drawing methods. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + // These return a pointer to an internally cached drawable - make sure you don't keep + // a copy of this pointer anywhere, as it may become invalid in the future. + virtual const Drawable* getDefaultFolderImage() = 0; + virtual const Drawable* getDefaultDocumentFileImage() = 0; + + virtual AttributedString createFileChooserHeaderText (const String& title, + const String& instructions) = 0; + + virtual void drawFileBrowserRow (Graphics&, int width, int height, + const String& filename, + Image* optionalIcon, + const String& fileSizeDescription, + const String& fileTimeDescription, + bool isDirectory, + bool isItemSelected, + int itemIndex, + DirectoryContentsDisplayComponent&) = 0; + + virtual Button* createFileBrowserGoUpButton() = 0; + + virtual void layoutFileBrowserComponent (FileBrowserComponent& browserComp, + DirectoryContentsDisplayComponent* fileListComponent, + FilePreviewComponent* previewComp, + ComboBox* currentPathBox, + TextEditor* filenameBox, + Button* goUpButton) = 0; + }; + //============================================================================== /** @internal */ void resized() override; diff --git a/source/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h b/source/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h index 83b6ae0d4..4456c11e9 100644 --- a/source/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h +++ b/source/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h @@ -180,6 +180,16 @@ public: /** Gives the component a tooltip. */ void setTooltip (const String& newTooltip) override; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual Button* createFilenameComponentBrowseButton (const String& text) = 0; + virtual void layoutFilenameComponent (FilenameComponent&, ComboBox* filenameBox, Button* browseButton) = 0; + }; + //============================================================================== /** @internal */ void paintOverChildren (Graphics&) override; diff --git a/source/modules/juce_gui_basics/juce_gui_basics.cpp b/source/modules/juce_gui_basics/juce_gui_basics.cpp index c168c679b..b18b41ccd 100644 --- a/source/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/source/modules/juce_gui_basics/juce_gui_basics.cpp @@ -198,6 +198,9 @@ extern bool juce_areThereAnyAlwaysOnTopWindows(); #include "layout/juce_TabbedComponent.cpp" #include "layout/juce_Viewport.cpp" #include "lookandfeel/juce_LookAndFeel.cpp" +#include "lookandfeel/juce_LookAndFeel_V2.cpp" +#include "lookandfeel/juce_LookAndFeel_V1.cpp" +#include "lookandfeel/juce_LookAndFeel_V3.cpp" #include "menus/juce_MenuBarComponent.cpp" #include "menus/juce_MenuBarModel.cpp" #include "menus/juce_PopupMenu.cpp" diff --git a/source/modules/juce_gui_basics/juce_gui_basics.h b/source/modules/juce_gui_basics/juce_gui_basics.h index 6272a79c3..6b20f1416 100644 --- a/source/modules/juce_gui_basics/juce_gui_basics.h +++ b/source/modules/juce_gui_basics/juce_gui_basics.h @@ -113,6 +113,7 @@ class CaretComponent; class BubbleComponent; class KeyPressMappingSet; class ApplicationCommandManagerListener; +class DrawableButton; #include "mouse/juce_MouseCursor.h" #include "mouse/juce_MouseListener.h" @@ -215,7 +216,6 @@ class ApplicationCommandManagerListener; #include "windows/juce_ThreadWithProgressWindow.h" #include "windows/juce_TooltipWindow.h" #include "layout/juce_MultiDocumentPanel.h" -#include "lookandfeel/juce_LookAndFeel.h" #include "filebrowser/juce_FileFilter.h" #include "filebrowser/juce_WildcardFileFilter.h" #include "filebrowser/juce_FileBrowserListener.h" @@ -239,6 +239,10 @@ class ApplicationCommandManagerListener; #include "properties/juce_TextPropertyComponent.h" #include "application/juce_Application.h" #include "misc/juce_BubbleComponent.h" +#include "lookandfeel/juce_LookAndFeel.h" +#include "lookandfeel/juce_LookAndFeel_V2.h" +#include "lookandfeel/juce_LookAndFeel_V1.h" +#include "lookandfeel/juce_LookAndFeel_V3.h" } diff --git a/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h b/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h index 919e72d92..b340a8bb8 100644 --- a/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h +++ b/source/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h @@ -93,6 +93,16 @@ public: /** Sets the height of the header section for one of the panels. */ void setPanelHeaderSize (Component* panelComponent, int headerSize); + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawConcertinaPanelHeader (Graphics&, const Rectangle& area, + bool isMouseOver, bool isMouseDown, ConcertinaPanel&, Component&) = 0; + }; + private: void resized() override; diff --git a/source/modules/juce_gui_basics/layout/juce_GroupComponent.cpp b/source/modules/juce_gui_basics/layout/juce_GroupComponent.cpp index 867d87080..593248734 100644 --- a/source/modules/juce_gui_basics/layout/juce_GroupComponent.cpp +++ b/source/modules/juce_gui_basics/layout/juce_GroupComponent.cpp @@ -31,11 +31,8 @@ GroupComponent::GroupComponent (const String& name, setInterceptsMouseClicks (false, true); } -GroupComponent::~GroupComponent() -{ -} +GroupComponent::~GroupComponent() {} -//============================================================================== void GroupComponent::setText (const String& newText) { if (text != newText) @@ -50,7 +47,6 @@ String GroupComponent::getText() const return text; } -//============================================================================== void GroupComponent::setTextLabelPosition (Justification newJustification) { if (justification != newJustification) @@ -62,18 +58,9 @@ void GroupComponent::setTextLabelPosition (Justification newJustification) void GroupComponent::paint (Graphics& g) { - getLookAndFeel() - .drawGroupComponentOutline (g, getWidth(), getHeight(), - text, justification, - *this); + getLookAndFeel().drawGroupComponentOutline (g, getWidth(), getHeight(), + text, justification, *this); } -void GroupComponent::enablementChanged() -{ - repaint(); -} - -void GroupComponent::colourChanged() -{ - repaint(); -} +void GroupComponent::enablementChanged() { repaint(); } +void GroupComponent::colourChanged() { repaint(); } diff --git a/source/modules/juce_gui_basics/layout/juce_GroupComponent.h b/source/modules/juce_gui_basics/layout/juce_GroupComponent.h index 066c00501..6f111e046 100644 --- a/source/modules/juce_gui_basics/layout/juce_GroupComponent.h +++ b/source/modules/juce_gui_basics/layout/juce_GroupComponent.h @@ -82,6 +82,16 @@ public: textColourId = 0x1005410 /**< The colour to use to draw the text label. */ }; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawGroupComponentOutline (Graphics&, int w, int h, const String& text, + const Justification&, GroupComponent&) = 0; + }; + //============================================================================== /** @internal */ void paint (Graphics&) override; diff --git a/source/modules/juce_gui_basics/layout/juce_ScrollBar.h b/source/modules/juce_gui_basics/layout/juce_ScrollBar.h index 1a73e35e8..2731af030 100644 --- a/source/modules/juce_gui_basics/layout/juce_ScrollBar.h +++ b/source/modules/juce_gui_basics/layout/juce_ScrollBar.h @@ -292,6 +292,73 @@ public: /** Deregisters a previously-registered listener. */ void removeListener (Listener* listener); + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + scrollbar-drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual bool areScrollbarButtonsVisible() = 0; + + /** Draws one of the buttons on a scrollbar. + + @param g the context to draw into + @param scrollbar the bar itself + @param width the width of the button + @param height the height of the button + @param buttonDirection the direction of the button, where 0 = up, 1 = right, 2 = down, 3 = left + @param isScrollbarVertical true if it's a vertical bar, false if horizontal + @param isMouseOverButton whether the mouse is currently over the button (also true if it's held down) + @param isButtonDown whether the mouse button's held down + */ + virtual void drawScrollbarButton (Graphics& g, + ScrollBar& scrollbar, + int width, int height, + int buttonDirection, + bool isScrollbarVertical, + bool isMouseOverButton, + bool isButtonDown) = 0; + + /** Draws the thumb area of a scrollbar. + + @param g the context to draw into + @param scrollbar the bar itself + @param x the x position of the left edge of the thumb area to draw in + @param y the y position of the top edge of the thumb area to draw in + @param width the width of the thumb area to draw in + @param height the height of the thumb area to draw in + @param isScrollbarVertical true if it's a vertical bar, false if horizontal + @param thumbStartPosition for vertical bars, the y coordinate of the top of the + thumb, or its x position for horizontal bars + @param thumbSize for vertical bars, the height of the thumb, or its width for + horizontal bars. This may be 0 if the thumb shouldn't be drawn. + @param isMouseOver whether the mouse is over the thumb area, also true if the mouse is + currently dragging the thumb + @param isMouseDown whether the mouse is currently dragging the scrollbar + */ + virtual void drawScrollbar (Graphics& g, ScrollBar& scrollbar, + int x, int y, int width, int height, + bool isScrollbarVertical, + int thumbStartPosition, + int thumbSize, + bool isMouseOver, + bool isMouseDown) = 0; + + /** Returns the component effect to use for a scrollbar */ + virtual ImageEffectFilter* getScrollbarEffect() = 0; + + /** Returns the minimum length in pixels to use for a scrollbar thumb. */ + virtual int getMinimumScrollbarThumbSize (ScrollBar&) = 0; + + /** Returns the default thickness to use for a scrollbar. */ + virtual int getDefaultScrollbarWidth() = 0; + + /** Returns the length in pixels to use for a scrollbar button. */ + virtual int getScrollbarButtonSize (ScrollBar&) = 0; + }; + //============================================================================== /** @internal */ bool keyPressed (const KeyPress&) override; diff --git a/source/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.h b/source/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.h index 926dd195b..f1aae0c81 100644 --- a/source/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.h +++ b/source/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.h @@ -72,6 +72,16 @@ public: */ virtual void hasBeenMoved(); + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawStretchableLayoutResizerBar (Graphics&, int w, int h, + bool isVerticalBar, bool isMouseOver, bool isMouseDragging) = 0; + }; + //============================================================================== /** @internal */ void paint (Graphics&) override; diff --git a/source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp b/source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp index ee2ab169a..35cd9d554 100644 --- a/source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp +++ b/source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.cpp @@ -93,7 +93,26 @@ void TabBarButton::calcAreas (Rectangle& extraComp, Rectangle& textAre } if (extraComponent != nullptr) + { extraComp = lf.getTabButtonExtraComponentBounds (*this, textArea, *extraComponent); + + const TabbedButtonBar::Orientation orientation = owner.getOrientation(); + + if (orientation == TabbedButtonBar::TabsAtLeft || orientation == TabbedButtonBar::TabsAtRight) + { + if (extraComp.getCentreY() > textArea.getCentreY()) + textArea.setBottom (jmin (textArea.getBottom(), extraComp.getY())); + else + textArea.setTop (jmax (textArea.getY(), extraComp.getBottom())); + } + else + { + if (extraComp.getCentreX() > textArea.getCentreX()) + textArea.setRight (jmin (textArea.getRight(), extraComp.getX())); + else + textArea.setLeft (jmax (textArea.getX(), extraComp.getRight())); + } + } } Rectangle TabBarButton::getTextArea() const diff --git a/source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h b/source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h index 9638dfbe2..042b02ecf 100644 --- a/source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h +++ b/source/modules/juce_gui_basics/layout/juce_TabbedButtonBar.h @@ -293,6 +293,30 @@ public: colour. */ }; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + window drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual int getTabButtonSpaceAroundImage() = 0; + virtual int getTabButtonOverlap (int tabDepth) = 0; + virtual int getTabButtonBestWidth (TabBarButton&, int tabDepth) = 0; + virtual Rectangle getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle& textArea, Component& extraComp) = 0; + + virtual void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) = 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; + + virtual void createTabButtonShape (TabBarButton&, Path& path, bool isMouseOver, bool isMouseDown) = 0; + virtual void fillTabButtonShape (TabBarButton&, Graphics&, const Path& path, bool isMouseOver, bool isMouseDown) = 0; + + virtual Button* createTabBarExtrasButton() = 0; + }; + //============================================================================== /** @internal */ void paint (Graphics&) override; diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp index 6915a0be8..bf20986d7 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.cpp @@ -22,43 +22,12 @@ ============================================================================== */ -namespace LookAndFeelHelpers +//============================================================================== +static Typeface::Ptr getTypefaceForFontFromLookAndFeel (const Font& font) { - static Colour createBaseColour (Colour buttonColour, - bool hasKeyboardFocus, - bool isMouseOverButton, - bool isButtonDown) noexcept - { - const float sat = hasKeyboardFocus ? 1.3f : 0.9f; - const Colour baseColour (buttonColour.withMultipliedSaturation (sat)); - - if (isButtonDown) return baseColour.contrasting (0.2f); - if (isMouseOverButton) return baseColour.contrasting (0.1f); - - return baseColour; - } - - static TextLayout layoutTooltipText (const String& text, Colour colour) noexcept - { - const float tooltipFontSize = 13.0f; - const int maxToolTipWidth = 400; - - AttributedString s; - s.setJustification (Justification::centred); - s.append (text, Font (tooltipFontSize, Font::bold), colour); - - TextLayout tl; - tl.createLayoutWithBalancedLineLengths (s, (float) maxToolTipWidth); - return tl; - } - - static Typeface::Ptr getTypefaceForFontFromLookAndFeel (const Font& font) - { - return LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); - } + return LookAndFeel::getDefaultLookAndFeel().getTypefaceForFont (font); } -//============================================================================== typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&); extern GetTypefaceForFont juce_getTypefaceForFont; @@ -107,6 +76,7 @@ LookAndFeel::LookAndFeel() TreeView::linesColourId, 0x4c000000, TreeView::backgroundColourId, 0x00000000, TreeView::dragAndDropIndicatorColourId, 0x80ff0000, + TreeView::selectedItemBackgroundColourId, 0x00000000, PopupMenu::backgroundColourId, 0xffffffff, PopupMenu::textColourId, 0xff000000, @@ -212,7 +182,7 @@ LookAndFeel::LookAndFeel() for (int i = 0; i < numElementsInArray (standardColours); i += 2) setColour ((int) standardColours [i], Colour ((uint32) standardColours [i + 1])); - juce_getTypefaceForFont = LookAndFeelHelpers::getTypefaceForFontFromLookAndFeel; + juce_getTypefaceForFont = getTypefaceForFontFromLookAndFeel; } LookAndFeel::~LookAndFeel() @@ -305,305 +275,6 @@ LowLevelGraphicsContext* LookAndFeel::createGraphicsContext (const Image& imageT } //============================================================================== -void LookAndFeel::drawButtonBackground (Graphics& g, - Button& button, - const Colour& backgroundColour, - bool isMouseOverButton, - bool isButtonDown) -{ - const int width = button.getWidth(); - const int height = button.getHeight(); - - const float outlineThickness = button.isEnabled() ? ((isButtonDown || isMouseOverButton) ? 1.2f : 0.7f) : 0.4f; - const float halfThickness = outlineThickness * 0.5f; - - const float indentL = button.isConnectedOnLeft() ? 0.1f : halfThickness; - const float indentR = button.isConnectedOnRight() ? 0.1f : halfThickness; - const float indentT = button.isConnectedOnTop() ? 0.1f : halfThickness; - const float indentB = button.isConnectedOnBottom() ? 0.1f : halfThickness; - - const Colour baseColour (LookAndFeelHelpers::createBaseColour (backgroundColour, - button.hasKeyboardFocus (true), - isMouseOverButton, isButtonDown) - .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f)); - - drawGlassLozenge (g, - indentL, - indentT, - width - indentL - indentR, - height - indentT - indentB, - baseColour, outlineThickness, -1.0f, - button.isConnectedOnLeft(), - button.isConnectedOnRight(), - button.isConnectedOnTop(), - button.isConnectedOnBottom()); -} - -Font LookAndFeel::getTextButtonFont (TextButton& button) -{ - return button.getFont(); -} - -void LookAndFeel::drawButtonText (Graphics& g, TextButton& button, - bool /*isMouseOverButton*/, bool /*isButtonDown*/) -{ - Font font (getTextButtonFont (button)); - g.setFont (font); - g.setColour (button.findColour (button.getToggleState() ? TextButton::textColourOnId - : TextButton::textColourOffId) - .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f)); - - const int yIndent = jmin (4, button.proportionOfHeight (0.3f)); - const int cornerSize = jmin (button.getHeight(), button.getWidth()) / 2; - - const int fontHeight = roundToInt (font.getHeight() * 0.6f); - const int leftIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnLeft() ? 4 : 2)); - const int rightIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnRight() ? 4 : 2)); - - g.drawFittedText (button.getButtonText(), - leftIndent, - yIndent, - button.getWidth() - leftIndent - rightIndent, - button.getHeight() - yIndent * 2, - Justification::centred, 2); -} - -void LookAndFeel::drawTickBox (Graphics& g, - Component& component, - float x, float y, float w, float h, - const bool ticked, - const bool isEnabled, - const bool isMouseOverButton, - const bool isButtonDown) -{ - const float boxSize = w * 0.7f; - - drawGlassSphere (g, x, y + (h - boxSize) * 0.5f, boxSize, - LookAndFeelHelpers::createBaseColour (component.findColour (TextButton::buttonColourId) - .withMultipliedAlpha (isEnabled ? 1.0f : 0.5f), - true, isMouseOverButton, isButtonDown), - isEnabled ? ((isButtonDown || isMouseOverButton) ? 1.1f : 0.5f) : 0.3f); - - if (ticked) - { - Path tick; - tick.startNewSubPath (1.5f, 3.0f); - tick.lineTo (3.0f, 6.0f); - tick.lineTo (6.0f, 0.0f); - - g.setColour (isEnabled ? Colours::black : Colours::grey); - - const AffineTransform trans (AffineTransform::scale (w / 9.0f, h / 9.0f) - .translated (x, y)); - - g.strokePath (tick, PathStrokeType (2.5f), trans); - } -} - -void LookAndFeel::drawToggleButton (Graphics& g, - ToggleButton& button, - bool isMouseOverButton, - bool isButtonDown) -{ - if (button.hasKeyboardFocus (true)) - { - g.setColour (button.findColour (TextEditor::focusedOutlineColourId)); - g.drawRect (0, 0, button.getWidth(), button.getHeight()); - } - - float fontSize = jmin (15.0f, button.getHeight() * 0.75f); - const float tickWidth = fontSize * 1.1f; - - drawTickBox (g, button, 4.0f, (button.getHeight() - tickWidth) * 0.5f, - tickWidth, tickWidth, - button.getToggleState(), - button.isEnabled(), - isMouseOverButton, - isButtonDown); - - g.setColour (button.findColour (ToggleButton::textColourId)); - g.setFont (fontSize); - - 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(), - Justification::centredLeft, 10); -} - -void LookAndFeel::changeToggleButtonWidthToFitText (ToggleButton& button) -{ - Font font (jmin (15.0f, button.getHeight() * 0.6f)); - - const int tickWidth = jmin (24, button.getHeight()); - - button.setSize (font.getStringWidth (button.getButtonText()) + tickWidth + 8, - button.getHeight()); -} - -void LookAndFeel::drawDrawableButton (Graphics& g, DrawableButton& button, - bool /*isMouseOverButton*/, bool /*isButtonDown*/) -{ - bool toggleState = button.getToggleState(); - - g.fillAll (button.findColour (toggleState ? DrawableButton::backgroundOnColourId - : DrawableButton::backgroundColourId)); - - const int textH = (button.getStyle() == DrawableButton::ImageAboveTextLabel) - ? jmin (16, button.proportionOfHeight (0.25f)) - : 0; - - if (textH > 0) - { - g.setFont ((float) textH); - - g.setColour (button.findColour (toggleState ? DrawableButton::textColourOnId - : DrawableButton::textColourId) - .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.4f)); - - g.drawFittedText (button.getButtonText(), - 2, button.getHeight() - textH - 1, - button.getWidth() - 4, textH, - Justification::centred, 1); - } -} - -//============================================================================== -AlertWindow* LookAndFeel::createAlertWindow (const String& title, - const String& message, - const String& button1, - const String& button2, - const String& button3, - AlertWindow::AlertIconType iconType, - int numButtons, - Component* associatedComponent) -{ - AlertWindow* aw = new AlertWindow (title, message, iconType, associatedComponent); - - if (numButtons == 1) - { - aw->addButton (button1, 0, - KeyPress (KeyPress::escapeKey), - KeyPress (KeyPress::returnKey)); - } - else - { - const KeyPress button1ShortCut ((int) CharacterFunctions::toLowerCase (button1[0]), 0, 0); - KeyPress button2ShortCut ((int) CharacterFunctions::toLowerCase (button2[0]), 0, 0); - if (button1ShortCut == button2ShortCut) - button2ShortCut = KeyPress(); - - if (numButtons == 2) - { - aw->addButton (button1, 1, KeyPress (KeyPress::returnKey), button1ShortCut); - aw->addButton (button2, 0, KeyPress (KeyPress::escapeKey), button2ShortCut); - } - else if (numButtons == 3) - { - aw->addButton (button1, 1, button1ShortCut); - aw->addButton (button2, 2, button2ShortCut); - aw->addButton (button3, 0, KeyPress (KeyPress::escapeKey)); - } - } - - return aw; -} - -void LookAndFeel::drawAlertBox (Graphics& g, - AlertWindow& alert, - const Rectangle& textArea, - TextLayout& textLayout) -{ - g.fillAll (alert.findColour (AlertWindow::backgroundColourId)); - - int iconSpaceUsed = 0; - - const int iconWidth = 80; - int iconSize = jmin (iconWidth + 50, alert.getHeight() + 20); - - if (alert.containsAnyExtraComponents() || alert.getNumButtons() > 2) - iconSize = jmin (iconSize, textArea.getHeight() + 50); - - const Rectangle iconRect (iconSize / -10, iconSize / -10, - iconSize, iconSize); - - if (alert.getAlertType() != AlertWindow::NoIcon) - { - Path icon; - uint32 colour; - char character; - - if (alert.getAlertType() == AlertWindow::WarningIcon) - { - colour = 0x55ff5555; - character = '!'; - - icon.addTriangle (iconRect.getX() + iconRect.getWidth() * 0.5f, (float) iconRect.getY(), - (float) iconRect.getRight(), (float) iconRect.getBottom(), - (float) iconRect.getX(), (float) iconRect.getBottom()); - - icon = icon.createPathWithRoundedCorners (5.0f); - } - else - { - colour = alert.getAlertType() == AlertWindow::InfoIcon ? (uint32) 0x605555ff : (uint32) 0x40b69900; - character = alert.getAlertType() == AlertWindow::InfoIcon ? 'i' : '?'; - - icon.addEllipse ((float) iconRect.getX(), (float) iconRect.getY(), - (float) iconRect.getWidth(), (float) iconRect.getHeight()); - } - - GlyphArrangement ga; - ga.addFittedText (Font (iconRect.getHeight() * 0.9f, Font::bold), - String::charToString ((juce_wchar) (uint8) character), - (float) iconRect.getX(), (float) iconRect.getY(), - (float) iconRect.getWidth(), (float) iconRect.getHeight(), - Justification::centred, false); - ga.createPath (icon); - - icon.setUsingNonZeroWinding (false); - g.setColour (Colour (colour)); - g.fillPath (icon); - - iconSpaceUsed = iconWidth; - } - - g.setColour (alert.findColour (AlertWindow::textColourId)); - - textLayout.draw (g, Rectangle (textArea.getX() + iconSpaceUsed, - textArea.getY(), - textArea.getWidth() - iconSpaceUsed, - textArea.getHeight()).toFloat()); - - g.setColour (alert.findColour (AlertWindow::outlineColourId)); - g.drawRect (0, 0, alert.getWidth(), alert.getHeight()); -} - -int LookAndFeel::getAlertBoxWindowFlags() -{ - return ComponentPeer::windowAppearsOnTaskbar - | ComponentPeer::windowHasDropShadow; -} - -int LookAndFeel::getAlertWindowButtonHeight() -{ - return 28; -} - -Font LookAndFeel::getAlertWindowMessageFont() -{ - return Font (15.0f); -} - -Font LookAndFeel::getAlertWindowFont() -{ - return Font (12.0f); -} - void LookAndFeel::setUsingNativeAlertWindows (bool shouldUseNativeAlerts) { useNativeAlertWindows = shouldUseNativeAlerts; @@ -617,2443 +288,3 @@ bool LookAndFeel::isUsingNativeAlertWindows() return useNativeAlertWindows; #endif } - -//============================================================================== -void LookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar, - int width, int height, - double progress, const String& textToShow) -{ - const Colour background (progressBar.findColour (ProgressBar::backgroundColourId)); - const Colour foreground (progressBar.findColour (ProgressBar::foregroundColourId)); - - g.fillAll (background); - - if (progress >= 0.0f && progress < 1.0f) - { - drawGlassLozenge (g, 1.0f, 1.0f, - (float) jlimit (0.0, width - 2.0, progress * (width - 2.0)), - (float) (height - 2), - foreground, - 0.5f, 0.0f, - true, true, true, true); - } - else - { - // spinning bar.. - g.setColour (foreground); - - const int stripeWidth = height * 2; - const int position = (int) (Time::getMillisecondCounter() / 15) % stripeWidth; - - Path p; - - for (float x = (float) (- position); x < width + stripeWidth; x += stripeWidth) - p.addQuadrilateral (x, 0.0f, - x + stripeWidth * 0.5f, 0.0f, - x, (float) height, - x - stripeWidth * 0.5f, (float) height); - - Image im (Image::ARGB, width, height, true); - - { - Graphics g2 (im); - drawGlassLozenge (g2, 1.0f, 1.0f, - (float) (width - 2), - (float) (height - 2), - foreground, - 0.5f, 0.0f, - true, true, true, true); - } - - g.setTiledImageFill (im, 0, 0, 0.85f); - g.fillPath (p); - } - - if (textToShow.isNotEmpty()) - { - g.setColour (Colour::contrasting (background, foreground)); - g.setFont (height * 0.6f); - - g.drawText (textToShow, 0, 0, width, height, Justification::centred, false); - } -} - -void LookAndFeel::drawSpinningWaitAnimation (Graphics& g, const Colour& colour, int x, int y, int w, int h) -{ - const float radius = jmin (w, h) * 0.4f; - const float thickness = radius * 0.15f; - Path p; - p.addRoundedRectangle (radius * 0.4f, thickness * -0.5f, - radius * 0.6f, thickness, - thickness * 0.5f); - - const float cx = x + w * 0.5f; - const float cy = y + h * 0.5f; - - const uint32 animationIndex = (Time::getMillisecondCounter() / (1000 / 10)) % 12; - - for (uint32 i = 0; i < 12; ++i) - { - const uint32 n = (i + 12 - animationIndex) % 12; - g.setColour (colour.withMultipliedAlpha ((n + 1) / 12.0f)); - - g.fillPath (p, AffineTransform::rotation (i * (float_Pi / 6.0f)) - .translated (cx, cy)); - } -} - -bool LookAndFeel::areScrollbarButtonsVisible() -{ - return true; -} - -void LookAndFeel::drawScrollbarButton (Graphics& g, - ScrollBar& scrollbar, - int width, int height, - int buttonDirection, - bool /*isScrollbarVertical*/, - bool /*isMouseOverButton*/, - bool isButtonDown) -{ - Path p; - - if (buttonDirection == 0) - p.addTriangle (width * 0.5f, height * 0.2f, - width * 0.1f, height * 0.7f, - width * 0.9f, height * 0.7f); - else if (buttonDirection == 1) - p.addTriangle (width * 0.8f, height * 0.5f, - width * 0.3f, height * 0.1f, - width * 0.3f, height * 0.9f); - else if (buttonDirection == 2) - p.addTriangle (width * 0.5f, height * 0.8f, - width * 0.1f, height * 0.3f, - width * 0.9f, height * 0.3f); - else if (buttonDirection == 3) - p.addTriangle (width * 0.2f, height * 0.5f, - width * 0.7f, height * 0.1f, - width * 0.7f, height * 0.9f); - - if (isButtonDown) - g.setColour (scrollbar.findColour (ScrollBar::thumbColourId).contrasting (0.2f)); - else - g.setColour (scrollbar.findColour (ScrollBar::thumbColourId)); - - g.fillPath (p); - - g.setColour (Colour (0x80000000)); - g.strokePath (p, PathStrokeType (0.5f)); -} - -void LookAndFeel::drawScrollbar (Graphics& g, - ScrollBar& scrollbar, - int x, int y, - int width, int height, - bool isScrollbarVertical, - int thumbStartPosition, - int thumbSize, - bool /*isMouseOver*/, - bool /*isMouseDown*/) -{ - g.fillAll (scrollbar.findColour (ScrollBar::backgroundColourId)); - - Path slotPath, thumbPath; - - const float slotIndent = jmin (width, height) > 15 ? 1.0f : 0.0f; - const float slotIndentx2 = slotIndent * 2.0f; - const float thumbIndent = slotIndent + 1.0f; - const float thumbIndentx2 = thumbIndent * 2.0f; - - float gx1 = 0.0f, gy1 = 0.0f, gx2 = 0.0f, gy2 = 0.0f; - - if (isScrollbarVertical) - { - slotPath.addRoundedRectangle (x + slotIndent, - y + slotIndent, - width - slotIndentx2, - height - slotIndentx2, - (width - slotIndentx2) * 0.5f); - - if (thumbSize > 0) - thumbPath.addRoundedRectangle (x + thumbIndent, - thumbStartPosition + thumbIndent, - width - thumbIndentx2, - thumbSize - thumbIndentx2, - (width - thumbIndentx2) * 0.5f); - gx1 = (float) x; - gx2 = x + width * 0.7f; - } - else - { - slotPath.addRoundedRectangle (x + slotIndent, - y + slotIndent, - width - slotIndentx2, - height - slotIndentx2, - (height - slotIndentx2) * 0.5f); - - if (thumbSize > 0) - thumbPath.addRoundedRectangle (thumbStartPosition + thumbIndent, - y + thumbIndent, - thumbSize - thumbIndentx2, - height - thumbIndentx2, - (height - thumbIndentx2) * 0.5f); - gy1 = (float) y; - gy2 = y + height * 0.7f; - } - - const Colour thumbColour (scrollbar.findColour (ScrollBar::thumbColourId)); - Colour trackColour1, trackColour2; - - if (scrollbar.isColourSpecified (ScrollBar::trackColourId) - || isColourSpecified (ScrollBar::trackColourId)) - { - trackColour1 = trackColour2 = scrollbar.findColour (ScrollBar::trackColourId); - } - else - { - trackColour1 = thumbColour.overlaidWith (Colour (0x44000000)); - trackColour2 = thumbColour.overlaidWith (Colour (0x19000000)); - } - - g.setGradientFill (ColourGradient (trackColour1, gx1, gy1, - trackColour2, gx2, gy2, false)); - g.fillPath (slotPath); - - if (isScrollbarVertical) - { - gx1 = x + width * 0.6f; - gx2 = (float) x + width; - } - else - { - gy1 = y + height * 0.6f; - gy2 = (float) y + height; - } - - g.setGradientFill (ColourGradient (Colours::transparentBlack,gx1, gy1, - Colour (0x19000000), gx2, gy2, false)); - g.fillPath (slotPath); - - g.setColour (thumbColour); - g.fillPath (thumbPath); - - g.setGradientFill (ColourGradient (Colour (0x10000000), gx1, gy1, - Colours::transparentBlack, gx2, gy2, false)); - - g.saveState(); - - if (isScrollbarVertical) - g.reduceClipRegion (x + width / 2, y, width, height); - else - g.reduceClipRegion (x, y + height / 2, width, height); - - g.fillPath (thumbPath); - g.restoreState(); - - g.setColour (Colour (0x4c000000)); - g.strokePath (thumbPath, PathStrokeType (0.4f)); -} - -ImageEffectFilter* LookAndFeel::getScrollbarEffect() -{ - return nullptr; -} - -int LookAndFeel::getMinimumScrollbarThumbSize (ScrollBar& scrollbar) -{ - return jmin (scrollbar.getWidth(), scrollbar.getHeight()) * 2; -} - -int LookAndFeel::getDefaultScrollbarWidth() -{ - return 18; -} - -int LookAndFeel::getScrollbarButtonSize (ScrollBar& scrollbar) -{ - return 2 + (scrollbar.isVertical() ? scrollbar.getWidth() - : scrollbar.getHeight()); -} - -//============================================================================== -Path LookAndFeel::getTickShape (const float height) -{ - static const unsigned char tickShapeData[] = - { - 109,0,224,168,68,0,0,119,67,108,0,224,172,68,0,128,146,67,113,0,192,148,68,0,0,219,67,0,96,110,68,0,224,56,68,113,0,64,51,68,0,32,130,68,0,64,20,68,0,224, - 162,68,108,0,128,3,68,0,128,168,68,113,0,128,221,67,0,192,175,68,0,0,207,67,0,32,179,68,113,0,0,201,67,0,224,173,68,0,0,181,67,0,224,161,68,108,0,128,168,67, - 0,128,154,68,113,0,128,141,67,0,192,138,68,0,128,108,67,0,64,131,68,113,0,0,62,67,0,128,119,68,0,0,5,67,0,128,114,68,113,0,0,102,67,0,192,88,68,0,128,155, - 67,0,192,88,68,113,0,0,190,67,0,192,88,68,0,128,232,67,0,224,131,68,108,0,128,246,67,0,192,139,68,113,0,64,33,68,0,128,87,68,0,0,93,68,0,224,26,68,113,0, - 96,140,68,0,128,188,67,0,224,168,68,0,0,119,67,99,101 - }; - - Path p; - p.loadPathFromData (tickShapeData, sizeof (tickShapeData)); - p.scaleToFit (0, 0, height * 2.0f, height, true); - return p; -} - -Path LookAndFeel::getCrossShape (const float height) -{ - static const unsigned char crossShapeData[] = - { - 109,0,0,17,68,0,96,145,68,108,0,192,13,68,0,192,147,68,113,0,0,213,67,0,64,174,68,0,0,168,67,0,64,174,68,113,0,0,104,67,0,64,174,68,0,0,5,67,0,64, - 153,68,113,0,0,18,67,0,64,153,68,0,0,24,67,0,64,153,68,113,0,0,135,67,0,64,153,68,0,128,207,67,0,224,130,68,108,0,0,220,67,0,0,126,68,108,0,0,204,67, - 0,128,117,68,113,0,0,138,67,0,64,82,68,0,0,138,67,0,192,57,68,113,0,0,138,67,0,192,37,68,0,128,210,67,0,64,10,68,113,0,128,220,67,0,64,45,68,0,0,8, - 68,0,128,78,68,108,0,192,14,68,0,0,87,68,108,0,64,20,68,0,0,80,68,113,0,192,57,68,0,0,32,68,0,128,88,68,0,0,32,68,113,0,64,112,68,0,0,32,68,0, - 128,124,68,0,64,68,68,113,0,0,121,68,0,192,67,68,0,128,119,68,0,192,67,68,113,0,192,108,68,0,192,67,68,0,32,89,68,0,96,82,68,113,0,128,69,68,0,0,97,68, - 0,0,56,68,0,64,115,68,108,0,64,49,68,0,128,124,68,108,0,192,55,68,0,96,129,68,113,0,0,92,68,0,224,146,68,0,192,129,68,0,224,146,68,113,0,64,110,68,0,64, - 168,68,0,64,87,68,0,64,168,68,113,0,128,66,68,0,64,168,68,0,64,27,68,0,32,150,68,99,101 - }; - - Path p; - p.loadPathFromData (crossShapeData, sizeof (crossShapeData)); - p.scaleToFit (0, 0, height * 2.0f, height, true); - return p; -} - -//============================================================================== -void LookAndFeel::drawTreeviewPlusMinusBox (Graphics& g, int x, int y, int w, int h, bool isPlus, bool /*isMouseOver*/) -{ - const int boxSize = ((jmin (16, w, h) << 1) / 3) | 1; - - x += (w - boxSize) >> 1; - y += (h - boxSize) >> 1; - w = boxSize; - h = boxSize; - - g.setColour (Colour (0xe5ffffff)); - g.fillRect (x, y, w, h); - - g.setColour (Colour (0x80000000)); - g.drawRect (x, y, w, h); - - 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); - - if (isPlus) - g.fillRect (x + centre, y + (h - size) * 0.5f, 1.0f, size); -} - -//============================================================================== -void LookAndFeel::drawBubble (Graphics& g, BubbleComponent& comp, - const Point& tip, const Rectangle& body) -{ - Path p; - p.addBubble (body, body.getUnion (Rectangle (tip.x, tip.y, 1.0f, 1.0f)), - tip, 5.0f, jmin (15.0f, body.getWidth() * 0.2f, body.getHeight() * 0.2f)); - - g.setColour (comp.findColour (BubbleComponent::backgroundColourId)); - g.fillPath (p); - - g.setColour (comp.findColour (BubbleComponent::outlineColourId)); - g.strokePath (p, PathStrokeType (1.33f)); -} - - -//============================================================================== -Font LookAndFeel::getPopupMenuFont() -{ - return Font (17.0f); -} - -void LookAndFeel::getIdealPopupMenuItemSize (const String& text, - const bool isSeparator, - int standardMenuItemHeight, - int& idealWidth, - int& idealHeight) -{ - if (isSeparator) - { - idealWidth = 50; - idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight / 2 : 10; - } - else - { - Font font (getPopupMenuFont()); - - if (standardMenuItemHeight > 0 && font.getHeight() > standardMenuItemHeight / 1.3f) - font.setHeight (standardMenuItemHeight / 1.3f); - - idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight : roundToInt (font.getHeight() * 1.3f); - idealWidth = font.getStringWidth (text) + idealHeight * 2; - } -} - -void LookAndFeel::drawPopupMenuBackground (Graphics& g, int width, int height) -{ - const Colour background (findColour (PopupMenu::backgroundColourId)); - - g.fillAll (background); - g.setColour (background.overlaidWith (Colour (0x2badd8e6))); - - for (int i = 0; i < height; i += 3) - g.fillRect (0, i, width, 1); - -#if ! JUCE_MAC - g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.6f)); - g.drawRect (0, 0, width, height); -#endif -} - -void LookAndFeel::drawPopupMenuUpDownArrow (Graphics& g, - int width, int height, - bool isScrollUpArrow) -{ - const Colour background (findColour (PopupMenu::backgroundColourId)); - - g.setGradientFill (ColourGradient (background, 0.0f, height * 0.5f, - background.withAlpha (0.0f), - 0.0f, isScrollUpArrow ? ((float) height) : 0.0f, - false)); - - g.fillRect (1, 1, width - 2, height - 2); - - const float hw = width * 0.5f; - const float arrowW = height * 0.3f; - const float y1 = height * (isScrollUpArrow ? 0.6f : 0.3f); - const float y2 = height * (isScrollUpArrow ? 0.3f : 0.6f); - - Path p; - p.addTriangle (hw - arrowW, y1, - hw + arrowW, y1, - hw, y2); - - g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.5f)); - g.fillPath (p); -} - -void LookAndFeel::drawPopupMenuItem (Graphics& g, - int width, int height, - const bool isSeparator, - const bool isActive, - const bool isHighlighted, - const bool isTicked, - const bool hasSubMenu, - const String& text, - const String& shortcutKeyText, - Image* image, - const Colour* const textColourToUse) -{ - const float halfH = height * 0.5f; - - if (isSeparator) - { - const float separatorIndent = 5.5f; - - g.setColour (Colour (0x33000000)); - g.drawLine (separatorIndent, halfH, width - separatorIndent, halfH); - - g.setColour (Colour (0x66ffffff)); - g.drawLine (separatorIndent, halfH + 1.0f, width - separatorIndent, halfH + 1.0f); - } - else - { - Colour textColour (findColour (PopupMenu::textColourId)); - - if (textColourToUse != nullptr) - textColour = *textColourToUse; - - if (isHighlighted) - { - g.setColour (findColour (PopupMenu::highlightedBackgroundColourId)); - g.fillRect (1, 1, width - 2, height - 2); - - g.setColour (findColour (PopupMenu::highlightedTextColourId)); - } - else - { - g.setColour (textColour); - } - - if (! isActive) - g.setOpacity (0.3f); - - Font font (getPopupMenuFont()); - - if (font.getHeight() > height / 1.3f) - font.setHeight (height / 1.3f); - - g.setFont (font); - - const int leftBorder = (height * 5) / 4; - const int rightBorder = 4; - - if (image != nullptr) - { - g.drawImageWithin (*image, - 2, 1, leftBorder - 4, height - 2, - RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); - } - else if (isTicked) - { - const Path tick (getTickShape (1.0f)); - const float th = font.getAscent(); - const float ty = halfH - th * 0.5f; - - g.fillPath (tick, tick.getTransformToScaleToFit (2.0f, ty, (float) (leftBorder - 4), - th, true)); - } - - g.drawFittedText (text, - leftBorder, 0, width - (leftBorder + rightBorder), height, - Justification::centredLeft, 1); - - if (shortcutKeyText.isNotEmpty()) - { - Font f2 (font); - f2.setHeight (f2.getHeight() * 0.75f); - f2.setHorizontalScale (0.95f); - g.setFont (f2); - - g.drawText (shortcutKeyText, - leftBorder, 0, width - (leftBorder + rightBorder + 4), height, - Justification::centredRight, - true); - } - - if (hasSubMenu) - { - const float arrowH = 0.6f * getPopupMenuFont().getAscent(); - const float x = width - height * 0.6f; - - Path p; - p.addTriangle (x, halfH - arrowH * 0.5f, - x, halfH + arrowH * 0.5f, - x + arrowH * 0.6f, halfH); - - g.fillPath (p); - } - } -} - -//============================================================================== -int LookAndFeel::getMenuWindowFlags() -{ - return ComponentPeer::windowHasDropShadow; -} - -void LookAndFeel::drawMenuBarBackground (Graphics& g, int width, int height, - bool, MenuBarComponent& menuBar) -{ - const Colour baseColour (LookAndFeelHelpers::createBaseColour (menuBar.findColour (PopupMenu::backgroundColourId), false, false, false)); - - if (menuBar.isEnabled()) - { - drawShinyButtonShape (g, - -4.0f, 0.0f, - width + 8.0f, (float) height, - 0.0f, - baseColour, - 0.4f, - true, true, true, true); - } - else - { - g.fillAll (baseColour); - } -} - -Font LookAndFeel::getMenuBarFont (MenuBarComponent& menuBar, int /*itemIndex*/, const String& /*itemText*/) -{ - return Font (menuBar.getHeight() * 0.7f); -} - -int LookAndFeel::getMenuBarItemWidth (MenuBarComponent& menuBar, int itemIndex, const String& itemText) -{ - return getMenuBarFont (menuBar, itemIndex, itemText) - .getStringWidth (itemText) + menuBar.getHeight(); -} - -void LookAndFeel::drawMenuBarItem (Graphics& g, - int width, int height, - int itemIndex, - const String& itemText, - bool isMouseOverItem, - bool isMenuOpen, - bool /*isMouseOverBar*/, - MenuBarComponent& menuBar) -{ - if (! menuBar.isEnabled()) - { - g.setColour (menuBar.findColour (PopupMenu::textColourId) - .withMultipliedAlpha (0.5f)); - } - else if (isMenuOpen || isMouseOverItem) - { - g.fillAll (menuBar.findColour (PopupMenu::highlightedBackgroundColourId)); - g.setColour (menuBar.findColour (PopupMenu::highlightedTextColourId)); - } - else - { - g.setColour (menuBar.findColour (PopupMenu::textColourId)); - } - - g.setFont (getMenuBarFont (menuBar, itemIndex, itemText)); - g.drawFittedText (itemText, 0, 0, width, height, Justification::centred, 1); -} - -//============================================================================== -void LookAndFeel::fillTextEditorBackground (Graphics& g, int /*width*/, int /*height*/, - TextEditor& textEditor) -{ - g.fillAll (textEditor.findColour (TextEditor::backgroundColourId)); -} - -void LookAndFeel::drawTextEditorOutline (Graphics& g, int width, int height, TextEditor& textEditor) -{ - if (textEditor.isEnabled()) - { - if (textEditor.hasKeyboardFocus (true) && ! textEditor.isReadOnly()) - { - const int border = 2; - - g.setColour (textEditor.findColour (TextEditor::focusedOutlineColourId)); - g.drawRect (0, 0, width, height, border); - - g.setOpacity (1.0f); - const Colour shadowColour (textEditor.findColour (TextEditor::shadowColourId).withMultipliedAlpha (0.75f)); - drawBevel (g, 0, 0, width, height + 2, border + 2, shadowColour, shadowColour); - } - else - { - g.setColour (textEditor.findColour (TextEditor::outlineColourId)); - g.drawRect (0, 0, width, height); - - g.setOpacity (1.0f); - const Colour shadowColour (textEditor.findColour (TextEditor::shadowColourId)); - drawBevel (g, 0, 0, width, height + 2, 3, shadowColour, shadowColour); - } - } -} - -CaretComponent* LookAndFeel::createCaretComponent (Component* keyFocusOwner) -{ - return new CaretComponent (keyFocusOwner); -} - -//============================================================================== -void LookAndFeel::drawComboBox (Graphics& g, int width, int height, - const bool isButtonDown, - int buttonX, int buttonY, - int buttonW, int buttonH, - ComboBox& box) -{ - g.fillAll (box.findColour (ComboBox::backgroundColourId)); - - if (box.isEnabled() && box.hasKeyboardFocus (false)) - { - g.setColour (box.findColour (ComboBox::buttonColourId)); - g.drawRect (0, 0, width, height, 2); - } - else - { - g.setColour (box.findColour (ComboBox::outlineColourId)); - g.drawRect (0, 0, width, height); - } - - const float outlineThickness = box.isEnabled() ? (isButtonDown ? 1.2f : 0.5f) : 0.3f; - - const Colour baseColour (LookAndFeelHelpers::createBaseColour (box.findColour (ComboBox::buttonColourId), - box.hasKeyboardFocus (true), - false, isButtonDown) - .withMultipliedAlpha (box.isEnabled() ? 1.0f : 0.5f)); - - drawGlassLozenge (g, - buttonX + outlineThickness, buttonY + outlineThickness, - buttonW - outlineThickness * 2.0f, buttonH - outlineThickness * 2.0f, - baseColour, outlineThickness, -1.0f, - true, true, true, true); - - if (box.isEnabled()) - { - const float arrowX = 0.3f; - const float arrowH = 0.2f; - - Path p; - p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.45f - arrowH), - buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.45f, - buttonX + buttonW * arrowX, buttonY + buttonH * 0.45f); - - p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.55f + arrowH), - buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.55f, - buttonX + buttonW * arrowX, buttonY + buttonH * 0.55f); - - g.setColour (box.findColour (ComboBox::arrowColourId)); - g.fillPath (p); - } -} - -Font LookAndFeel::getComboBoxFont (ComboBox& box) -{ - return Font (jmin (15.0f, box.getHeight() * 0.85f)); -} - -Label* LookAndFeel::createComboBoxTextBox (ComboBox&) -{ - return new Label (String::empty, String::empty); -} - -void LookAndFeel::positionComboBoxText (ComboBox& box, Label& label) -{ - label.setBounds (1, 1, - box.getWidth() + 3 - box.getHeight(), - box.getHeight() - 2); - - label.setFont (getComboBoxFont (box)); -} - -//============================================================================== -Font LookAndFeel::getLabelFont (Label& label) -{ - return label.getFont(); -} - -void LookAndFeel::drawLabel (Graphics& g, Label& label) -{ - g.fillAll (label.findColour (Label::backgroundColourId)); - - if (! label.isBeingEdited()) - { - const float alpha = label.isEnabled() ? 1.0f : 0.5f; - const Font font (getLabelFont (label)); - - g.setColour (label.findColour (Label::textColourId).withMultipliedAlpha (alpha)); - g.setFont (font); - g.drawFittedText (label.getText(), - label.getHorizontalBorderSize(), - label.getVerticalBorderSize(), - label.getWidth() - 2 * label.getHorizontalBorderSize(), - label.getHeight() - 2 * label.getVerticalBorderSize(), - label.getJustificationType(), - jmax (1, (int) (label.getHeight() / font.getHeight())), - label.getMinimumHorizontalScale()); - - g.setColour (label.findColour (Label::outlineColourId).withMultipliedAlpha (alpha)); - } - else if (label.isEnabled()) - { - g.setColour (label.findColour (Label::outlineColourId)); - } - - g.drawRect (label.getLocalBounds()); -} - -//============================================================================== -void LookAndFeel::drawLinearSliderBackground (Graphics& g, - int x, int y, - int width, int height, - float /*sliderPos*/, - float /*minSliderPos*/, - float /*maxSliderPos*/, - const Slider::SliderStyle /*style*/, - Slider& slider) -{ - const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); - - const Colour trackColour (slider.findColour (Slider::trackColourId)); - const Colour gradCol1 (trackColour.overlaidWith (Colours::black.withAlpha (slider.isEnabled() ? 0.25f : 0.13f))); - const Colour gradCol2 (trackColour.overlaidWith (Colour (0x14000000))); - Path indent; - - if (slider.isHorizontal()) - { - const float iy = y + height * 0.5f - sliderRadius * 0.5f; - const float ih = sliderRadius; - - g.setGradientFill (ColourGradient (gradCol1, 0.0f, iy, - gradCol2, 0.0f, iy + ih, false)); - - indent.addRoundedRectangle (x - sliderRadius * 0.5f, iy, - width + sliderRadius, ih, - 5.0f); - } - else - { - const float ix = x + width * 0.5f - sliderRadius * 0.5f; - const float iw = sliderRadius; - - g.setGradientFill (ColourGradient (gradCol1, ix, 0.0f, - gradCol2, ix + iw, 0.0f, false)); - - indent.addRoundedRectangle (ix, y - sliderRadius * 0.5f, - iw, height + sliderRadius, - 5.0f); - } - - g.fillPath (indent); - - g.setColour (Colour (0x4c000000)); - g.strokePath (indent, PathStrokeType (0.5f)); -} - -void LookAndFeel::drawLinearSliderThumb (Graphics& g, - int x, int y, - int width, int height, - float sliderPos, - float minSliderPos, - float maxSliderPos, - const Slider::SliderStyle style, - Slider& slider) -{ - const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); - - Colour knobColour (LookAndFeelHelpers::createBaseColour (slider.findColour (Slider::thumbColourId), - slider.hasKeyboardFocus (false) && slider.isEnabled(), - slider.isMouseOverOrDragging() && slider.isEnabled(), - slider.isMouseButtonDown() && slider.isEnabled())); - - const float outlineThickness = slider.isEnabled() ? 0.8f : 0.3f; - - if (style == Slider::LinearHorizontal || style == Slider::LinearVertical) - { - float kx, ky; - - if (style == Slider::LinearVertical) - { - kx = x + width * 0.5f; - ky = sliderPos; - } - else - { - kx = sliderPos; - ky = y + height * 0.5f; - } - - drawGlassSphere (g, - kx - sliderRadius, - ky - sliderRadius, - sliderRadius * 2.0f, - knobColour, outlineThickness); - } - else - { - if (style == Slider::ThreeValueVertical) - { - drawGlassSphere (g, x + width * 0.5f - sliderRadius, - sliderPos - sliderRadius, - sliderRadius * 2.0f, - knobColour, outlineThickness); - } - else if (style == Slider::ThreeValueHorizontal) - { - drawGlassSphere (g,sliderPos - sliderRadius, - y + height * 0.5f - sliderRadius, - sliderRadius * 2.0f, - knobColour, outlineThickness); - } - - if (style == Slider::TwoValueVertical || style == Slider::ThreeValueVertical) - { - const float sr = jmin (sliderRadius, width * 0.4f); - - drawGlassPointer (g, jmax (0.0f, x + width * 0.5f - sliderRadius * 2.0f), - minSliderPos - sliderRadius, - sliderRadius * 2.0f, knobColour, outlineThickness, 1); - - drawGlassPointer (g, jmin (x + width - sliderRadius * 2.0f, x + width * 0.5f), maxSliderPos - sr, - sliderRadius * 2.0f, knobColour, outlineThickness, 3); - } - else if (style == Slider::TwoValueHorizontal || style == Slider::ThreeValueHorizontal) - { - const float sr = jmin (sliderRadius, height * 0.4f); - - drawGlassPointer (g, minSliderPos - sr, - jmax (0.0f, y + height * 0.5f - sliderRadius * 2.0f), - sliderRadius * 2.0f, knobColour, outlineThickness, 2); - - drawGlassPointer (g, maxSliderPos - sliderRadius, - jmin (y + height - sliderRadius * 2.0f, y + height * 0.5f), - sliderRadius * 2.0f, knobColour, outlineThickness, 4); - } - } -} - -void LookAndFeel::drawLinearSlider (Graphics& g, - int x, int y, - int width, int height, - float sliderPos, - float minSliderPos, - float maxSliderPos, - const Slider::SliderStyle style, - Slider& slider) -{ - g.fillAll (slider.findColour (Slider::backgroundColourId)); - - if (style == Slider::LinearBar || style == Slider::LinearBarVertical) - { - const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); - - Colour baseColour (LookAndFeelHelpers::createBaseColour (slider.findColour (Slider::thumbColourId) - .withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f), - false, isMouseOver, - isMouseOver || slider.isMouseButtonDown())); - - drawShinyButtonShape (g, - (float) x, - style == Slider::LinearBarVertical ? sliderPos - : (float) y, - style == Slider::LinearBarVertical ? (float) width - : (sliderPos - x), - style == Slider::LinearBarVertical ? (height - sliderPos) - : (float) height, 0.0f, - baseColour, - slider.isEnabled() ? 0.9f : 0.3f, - true, true, true, true); - } - else - { - drawLinearSliderBackground (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider); - drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider); - } -} - -int LookAndFeel::getSliderThumbRadius (Slider& slider) -{ - return jmin (7, - slider.getHeight() / 2, - slider.getWidth() / 2) + 2; -} - -void LookAndFeel::drawRotarySlider (Graphics& g, - int x, int y, - int width, int height, - float sliderPos, - const float rotaryStartAngle, - const float rotaryEndAngle, - Slider& slider) -{ - const float radius = jmin (width / 2, height / 2) - 2.0f; - const float centreX = x + width * 0.5f; - const float centreY = y + height * 0.5f; - const float rx = centreX - radius; - const float ry = centreY - radius; - const float rw = radius * 2.0f; - const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); - const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); - - if (radius > 12.0f) - { - if (slider.isEnabled()) - g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f)); - else - g.setColour (Colour (0x80808080)); - - const float thickness = 0.7f; - - { - Path filledArc; - filledArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, angle, thickness); - g.fillPath (filledArc); - } - - if (thickness > 0) - { - const float innerRadius = radius * 0.2f; - Path p; - p.addTriangle (-innerRadius, 0.0f, - 0.0f, -radius * thickness * 1.1f, - innerRadius, 0.0f); - - p.addEllipse (-innerRadius, -innerRadius, innerRadius * 2.0f, innerRadius * 2.0f); - - g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY)); - } - - if (slider.isEnabled()) - g.setColour (slider.findColour (Slider::rotarySliderOutlineColourId)); - else - g.setColour (Colour (0x80808080)); - - Path outlineArc; - outlineArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, thickness); - outlineArc.closeSubPath(); - - g.strokePath (outlineArc, PathStrokeType (slider.isEnabled() ? (isMouseOver ? 2.0f : 1.2f) : 0.3f)); - } - else - { - if (slider.isEnabled()) - g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f)); - else - g.setColour (Colour (0x80808080)); - - Path p; - p.addEllipse (-0.4f * rw, -0.4f * rw, rw * 0.8f, rw * 0.8f); - PathStrokeType (rw * 0.1f).createStrokedPath (p, p); - - p.addLineSegment (Line (0.0f, 0.0f, 0.0f, -radius), rw * 0.2f); - - g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY)); - } -} - -Button* LookAndFeel::createSliderButton (const bool isIncrement) -{ - return new TextButton (isIncrement ? "+" : "-", String::empty); -} - -class LookAndFeel::SliderLabelComp : public Label -{ -public: - SliderLabelComp() : Label (String::empty, String::empty) {} - - void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) {} -}; - -Label* LookAndFeel::createSliderTextBox (Slider& slider) -{ - Label* const l = new SliderLabelComp(); - - l->setJustificationType (Justification::centred); - - l->setColour (Label::textColourId, slider.findColour (Slider::textBoxTextColourId)); - - l->setColour (Label::backgroundColourId, - (slider.getSliderStyle() == Slider::LinearBar || slider.getSliderStyle() == Slider::LinearBarVertical) - ? Colours::transparentBlack - : slider.findColour (Slider::textBoxBackgroundColourId)); - l->setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); - - l->setColour (TextEditor::textColourId, slider.findColour (Slider::textBoxTextColourId)); - - l->setColour (TextEditor::backgroundColourId, - slider.findColour (Slider::textBoxBackgroundColourId) - .withAlpha ((slider.getSliderStyle() == Slider::LinearBar || slider.getSliderStyle() == Slider::LinearBarVertical) - ? 0.7f : 1.0f)); - - l->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); - - return l; -} - -ImageEffectFilter* LookAndFeel::getSliderEffect() -{ - return nullptr; -} - -Font LookAndFeel::getSliderPopupFont() -{ - return Font (15.0f, Font::bold); -} - -int LookAndFeel::getSliderPopupPlacement() -{ - return BubbleComponent::above - | BubbleComponent::below - | BubbleComponent::left - | BubbleComponent::right; -} - -//============================================================================== -void LookAndFeel::getTooltipSize (const String& tipText, int& width, int& height) -{ - const TextLayout tl (LookAndFeelHelpers::layoutTooltipText (tipText, Colours::black)); - - width = (int) (tl.getWidth() + 14.0f); - height = (int) (tl.getHeight() + 6.0f); -} - -void LookAndFeel::drawTooltip (Graphics& g, const String& text, int width, int height) -{ - g.fillAll (findColour (TooltipWindow::backgroundColourId)); - - #if ! JUCE_MAC // The mac windows already have a non-optional 1 pix outline, so don't double it here.. - g.setColour (findColour (TooltipWindow::outlineColourId)); - g.drawRect (0, 0, width, height, 1); - #endif - - LookAndFeelHelpers::layoutTooltipText (text, findColour (TooltipWindow::textColourId)) - .draw (g, Rectangle ((float) width, (float) height)); -} - -//============================================================================== -Button* LookAndFeel::createFilenameComponentBrowseButton (const String& text) -{ - return new TextButton (text, TRANS("click to browse for a different file")); -} - -void LookAndFeel::layoutFilenameComponent (FilenameComponent& filenameComp, - ComboBox* filenameBox, - Button* browseButton) -{ - browseButton->setSize (80, filenameComp.getHeight()); - - if (TextButton* const tb = dynamic_cast (browseButton)) - tb->changeWidthToFitText(); - - browseButton->setTopRightPosition (filenameComp.getWidth(), 0); - - filenameBox->setBounds (0, 0, browseButton->getX(), filenameComp.getHeight()); -} - -//============================================================================== -void LookAndFeel::drawConcertinaPanelHeader (Graphics& g, const Rectangle& area, - bool isMouseOver, bool /*isMouseDown*/, - ConcertinaPanel&, Component& panel) -{ - g.fillAll (Colours::grey.withAlpha (isMouseOver ? 0.9f : 0.7f)); - g.setColour (Colours::black.withAlpha (0.5f)); - g.drawRect (area); - - g.setColour (Colours::white); - g.setFont (Font (area.getHeight() * 0.7f).boldened()); - g.drawFittedText (panel.getName(), 4, 0, area.getWidth() - 6, area.getHeight(), Justification::centredLeft, 1); -} - -//============================================================================== -void LookAndFeel::drawImageButton (Graphics& g, Image* image, - int imageX, int imageY, int imageW, int imageH, - const Colour& overlayColour, - float imageOpacity, - ImageButton& button) -{ - if (! button.isEnabled()) - imageOpacity *= 0.3f; - - AffineTransform t = RectanglePlacement (RectanglePlacement::stretchToFit) - .getTransformToFit (image->getBounds().toFloat(), - Rectangle (imageX, imageY, imageW, imageH).toFloat()); - - if (! overlayColour.isOpaque()) - { - g.setOpacity (imageOpacity); - g.drawImageTransformed (*image, t, false); - } - - if (! overlayColour.isTransparent()) - { - g.setColour (overlayColour); - g.drawImageTransformed (*image, t, true); - } -} - -//============================================================================== -void LookAndFeel::drawCornerResizer (Graphics& g, - int w, int h, - bool /*isMouseOver*/, - bool /*isMouseDragging*/) -{ - const float lineThickness = jmin (w, h) * 0.075f; - - for (float i = 0.0f; i < 1.0f; i += 0.3f) - { - g.setColour (Colours::lightgrey); - - g.drawLine (w * i, - h + 1.0f, - w + 1.0f, - h * i, - lineThickness); - - g.setColour (Colours::darkgrey); - - g.drawLine (w * i + lineThickness, - h + 1.0f, - w + 1.0f, - h * i + lineThickness, - lineThickness); - } -} - -void LookAndFeel::drawResizableFrame (Graphics& g, int w, int h, const BorderSize& border) -{ - if (! border.isEmpty()) - { - const Rectangle fullSize (0, 0, w, h); - const Rectangle centreArea (border.subtractedFrom (fullSize)); - - g.saveState(); - - g.excludeClipRegion (centreArea); - - g.setColour (Colour (0x50000000)); - g.drawRect (fullSize); - - g.setColour (Colour (0x19000000)); - g.drawRect (centreArea.expanded (1, 1)); - - g.restoreState(); - } -} - -//============================================================================== -void LookAndFeel::fillResizableWindowBackground (Graphics& g, int /*w*/, int /*h*/, - const BorderSize& /*border*/, ResizableWindow& window) -{ - g.fillAll (window.getBackgroundColour()); -} - -void LookAndFeel::drawResizableWindowBorder (Graphics&, int /*w*/, int /*h*/, - const BorderSize& /*border*/, ResizableWindow&) -{ -} - -void LookAndFeel::drawDocumentWindowTitleBar (DocumentWindow& window, - Graphics& g, int w, int h, - int titleSpaceX, int titleSpaceW, - const Image* icon, - bool drawTitleTextOnLeft) -{ - const bool isActive = window.isActiveWindow(); - - g.setGradientFill (ColourGradient (window.getBackgroundColour(), - 0.0f, 0.0f, - window.getBackgroundColour().contrasting (isActive ? 0.15f : 0.05f), - 0.0f, (float) h, false)); - g.fillAll(); - - Font font (h * 0.65f, Font::bold); - g.setFont (font); - - int textW = font.getStringWidth (window.getName()); - int iconW = 0; - int iconH = 0; - - if (icon != nullptr) - { - iconH = (int) font.getHeight(); - iconW = icon->getWidth() * iconH / icon->getHeight() + 4; - } - - textW = jmin (titleSpaceW, textW + iconW); - int textX = drawTitleTextOnLeft ? titleSpaceX - : jmax (titleSpaceX, (w - textW) / 2); - - if (textX + textW > titleSpaceX + titleSpaceW) - textX = titleSpaceX + titleSpaceW - textW; - - if (icon != nullptr) - { - g.setOpacity (isActive ? 1.0f : 0.6f); - g.drawImageWithin (*icon, textX, (h - iconH) / 2, iconW, iconH, - RectanglePlacement::centred, false); - textX += iconW; - textW -= iconW; - } - - if (window.isColourSpecified (DocumentWindow::textColourId) || isColourSpecified (DocumentWindow::textColourId)) - g.setColour (window.findColour (DocumentWindow::textColourId)); - else - g.setColour (window.getBackgroundColour().contrasting (isActive ? 0.7f : 0.4f)); - - g.drawText (window.getName(), textX, 0, textW, h, Justification::centredLeft, true); -} - -//============================================================================== -class LookAndFeel::GlassWindowButton : public Button -{ -public: - GlassWindowButton (const String& name, Colour col, - const Path& normalShape_, - const Path& toggledShape_) noexcept - : Button (name), - colour (col), - normalShape (normalShape_), - toggledShape (toggledShape_) - { - } - - //============================================================================== - void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override - { - float alpha = isMouseOverButton ? (isButtonDown ? 1.0f : 0.8f) : 0.55f; - - if (! isEnabled()) - alpha *= 0.5f; - - float x = 0, y = 0, diam; - - if (getWidth() < getHeight()) - { - diam = (float) getWidth(); - y = (getHeight() - getWidth()) * 0.5f; - } - else - { - diam = (float) getHeight(); - y = (getWidth() - getHeight()) * 0.5f; - } - - x += diam * 0.05f; - y += diam * 0.05f; - diam *= 0.9f; - - g.setGradientFill (ColourGradient (Colour::greyLevel (0.9f).withAlpha (alpha), 0, y + diam, - Colour::greyLevel (0.6f).withAlpha (alpha), 0, y, false)); - g.fillEllipse (x, y, diam, diam); - - x += 2.0f; - y += 2.0f; - diam -= 4.0f; - - LookAndFeel::drawGlassSphere (g, x, y, diam, colour.withAlpha (alpha), 1.0f); - - Path& p = getToggleState() ? toggledShape : normalShape; - - const AffineTransform t (p.getTransformToScaleToFit (x + diam * 0.3f, y + diam * 0.3f, - diam * 0.4f, diam * 0.4f, true)); - - g.setColour (Colours::black.withAlpha (alpha * 0.6f)); - g.fillPath (p, t); - } - -private: - Colour colour; - Path normalShape, toggledShape; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlassWindowButton) -}; - -Button* LookAndFeel::createDocumentWindowButton (int buttonType) -{ - Path shape; - const float crossThickness = 0.25f; - - if (buttonType == DocumentWindow::closeButton) - { - shape.addLineSegment (Line (0.0f, 0.0f, 1.0f, 1.0f), crossThickness * 1.4f); - shape.addLineSegment (Line (1.0f, 0.0f, 0.0f, 1.0f), crossThickness * 1.4f); - - return new GlassWindowButton ("close", Colour (0xffdd1100), shape, shape); - } - - if (buttonType == DocumentWindow::minimiseButton) - { - shape.addLineSegment (Line (0.0f, 0.5f, 1.0f, 0.5f), crossThickness); - - return new GlassWindowButton ("minimise", Colour (0xffaa8811), shape, shape); - } - - if (buttonType == DocumentWindow::maximiseButton) - { - shape.addLineSegment (Line (0.5f, 0.0f, 0.5f, 1.0f), crossThickness); - shape.addLineSegment (Line (0.0f, 0.5f, 1.0f, 0.5f), crossThickness); - - Path fullscreenShape; - fullscreenShape.startNewSubPath (45.0f, 100.0f); - fullscreenShape.lineTo (0.0f, 100.0f); - fullscreenShape.lineTo (0.0f, 0.0f); - fullscreenShape.lineTo (100.0f, 0.0f); - fullscreenShape.lineTo (100.0f, 45.0f); - fullscreenShape.addRectangle (45.0f, 45.0f, 100.0f, 100.0f); - PathStrokeType (30.0f).createStrokedPath (fullscreenShape, fullscreenShape); - - return new GlassWindowButton ("maximise", Colour (0xff119911), shape, fullscreenShape); - } - - jassertfalse; - return nullptr; -} - -void LookAndFeel::positionDocumentWindowButtons (DocumentWindow&, - int titleBarX, - int titleBarY, - int titleBarW, - int titleBarH, - Button* minimiseButton, - Button* maximiseButton, - Button* closeButton, - bool positionTitleBarButtonsOnLeft) -{ - const int buttonW = titleBarH - titleBarH / 8; - - int x = positionTitleBarButtonsOnLeft ? titleBarX + 4 - : titleBarX + titleBarW - buttonW - buttonW / 4; - - if (closeButton != nullptr) - { - closeButton->setBounds (x, titleBarY, buttonW, titleBarH); - x += positionTitleBarButtonsOnLeft ? buttonW : -(buttonW + buttonW / 4); - } - - if (positionTitleBarButtonsOnLeft) - std::swap (minimiseButton, maximiseButton); - - if (maximiseButton != nullptr) - { - maximiseButton->setBounds (x, titleBarY, buttonW, titleBarH); - x += positionTitleBarButtonsOnLeft ? buttonW : -buttonW; - } - - if (minimiseButton != nullptr) - minimiseButton->setBounds (x, titleBarY, buttonW, titleBarH); -} - -int LookAndFeel::getDefaultMenuBarHeight() -{ - return 24; -} - -//============================================================================== -DropShadower* LookAndFeel::createDropShadowerForComponent (Component*) -{ - return new DropShadower (DropShadow (Colours::black.withAlpha (0.4f), 10, Point (0, 2))); -} - -//============================================================================== -void LookAndFeel::drawStretchableLayoutResizerBar (Graphics& g, - int w, int h, - bool /*isVerticalBar*/, - bool isMouseOver, - bool isMouseDragging) -{ - float alpha = 0.5f; - - if (isMouseOver || isMouseDragging) - { - g.fillAll (Colour (0x190000ff)); - alpha = 1.0f; - } - - const float cx = w * 0.5f; - const float cy = h * 0.5f; - const float cr = jmin (w, h) * 0.4f; - - g.setGradientFill (ColourGradient (Colours::white.withAlpha (alpha), cx + cr * 0.1f, cy + cr, - Colours::black.withAlpha (alpha), cx, cy - cr * 4.0f, - true)); - - g.fillEllipse (cx - cr, cy - cr, cr * 2.0f, cr * 2.0f); -} - -//============================================================================== -void LookAndFeel::drawGroupComponentOutline (Graphics& g, int width, int height, - const String& text, - const Justification& position, - GroupComponent& group) -{ - const float textH = 15.0f; - const float indent = 3.0f; - const float textEdgeGap = 4.0f; - float cs = 5.0f; - - Font f (textH); - - Path p; - float x = indent; - float y = f.getAscent() - 3.0f; - float w = jmax (0.0f, width - x * 2.0f); - float h = jmax (0.0f, height - y - indent); - cs = jmin (cs, w * 0.5f, h * 0.5f); - const float cs2 = 2.0f * cs; - - float textW = text.isEmpty() ? 0 : jlimit (0.0f, jmax (0.0f, w - cs2 - textEdgeGap * 2), f.getStringWidth (text) + textEdgeGap * 2.0f); - float textX = cs + textEdgeGap; - - if (position.testFlags (Justification::horizontallyCentred)) - textX = cs + (w - cs2 - textW) * 0.5f; - else if (position.testFlags (Justification::right)) - textX = w - cs - textW - textEdgeGap; - - p.startNewSubPath (x + textX + textW, y); - p.lineTo (x + w - cs, y); - - p.addArc (x + w - cs2, y, cs2, cs2, 0, float_Pi * 0.5f); - p.lineTo (x + w, y + h - cs); - - p.addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi); - p.lineTo (x + cs, y + h); - - p.addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f); - p.lineTo (x, y + cs); - - p.addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f); - p.lineTo (x + textX, y); - - const float alpha = group.isEnabled() ? 1.0f : 0.5f; - - g.setColour (group.findColour (GroupComponent::outlineColourId) - .withMultipliedAlpha (alpha)); - - g.strokePath (p, PathStrokeType (2.0f)); - - g.setColour (group.findColour (GroupComponent::textColourId) - .withMultipliedAlpha (alpha)); - g.setFont (f); - g.drawText (text, - roundToInt (x + textX), 0, - roundToInt (textW), - roundToInt (textH), - Justification::centred, true); -} - -//============================================================================== -int LookAndFeel::getTabButtonOverlap (int tabDepth) -{ - return 1 + tabDepth / 3; -} - -int LookAndFeel::getTabButtonSpaceAroundImage() -{ - return 4; -} - -int LookAndFeel::getTabButtonBestWidth (TabBarButton& button, int tabDepth) -{ - int width = Font (tabDepth * 0.6f).getStringWidth (button.getButtonText().trim()) - + getTabButtonOverlap (tabDepth) * 2; - - if (Component* const extraComponent = button.getExtraComponent()) - width += button.getTabbedButtonBar().isVertical() ? extraComponent->getHeight() - : extraComponent->getWidth(); - - return jlimit (tabDepth * 2, tabDepth * 8, width); -} - -Rectangle LookAndFeel::getTabButtonExtraComponentBounds (const TabBarButton& button, Rectangle& textArea, Component& comp) -{ - Rectangle extraComp; - - const TabbedButtonBar::Orientation orientation = button.getTabbedButtonBar().getOrientation(); - - if (button.getExtraComponentPlacement() == TabBarButton::beforeText) - { - switch (orientation) - { - case TabbedButtonBar::TabsAtBottom: - case TabbedButtonBar::TabsAtTop: extraComp = textArea.removeFromLeft (comp.getWidth()); break; - case TabbedButtonBar::TabsAtLeft: extraComp = textArea.removeFromBottom (comp.getHeight()); break; - case TabbedButtonBar::TabsAtRight: extraComp = textArea.removeFromTop (comp.getHeight()); break; - default: jassertfalse; break; - } - } - else - { - switch (orientation) - { - case TabbedButtonBar::TabsAtBottom: - case TabbedButtonBar::TabsAtTop: extraComp = textArea.removeFromRight (comp.getWidth()); break; - case TabbedButtonBar::TabsAtLeft: extraComp = textArea.removeFromTop (comp.getHeight()); break; - case TabbedButtonBar::TabsAtRight: extraComp = textArea.removeFromBottom (comp.getHeight()); break; - default: jassertfalse; break; - } - } - - return extraComp; -} - -void LookAndFeel::createTabButtonShape (TabBarButton& button, Path& p, bool /*isMouseOver*/, bool /*isMouseDown*/) -{ - const Rectangle activeArea (button.getActiveArea()); - const float w = (float) activeArea.getWidth(); - const float h = (float) activeArea.getHeight(); - - float length = w; - float depth = h; - - if (button.getTabbedButtonBar().isVertical()) - std::swap (length, depth); - - const float indent = (float) getTabButtonOverlap ((int) depth); - const float overhang = 4.0f; - - switch (button.getTabbedButtonBar().getOrientation()) - { - case TabbedButtonBar::TabsAtLeft: - p.startNewSubPath (w, 0.0f); - p.lineTo (0.0f, indent); - p.lineTo (0.0f, h - indent); - p.lineTo (w, h); - p.lineTo (w + overhang, h + overhang); - p.lineTo (w + overhang, -overhang); - break; - - case TabbedButtonBar::TabsAtRight: - p.startNewSubPath (0.0f, 0.0f); - p.lineTo (w, indent); - p.lineTo (w, h - indent); - p.lineTo (0.0f, h); - p.lineTo (-overhang, h + overhang); - p.lineTo (-overhang, -overhang); - break; - - case TabbedButtonBar::TabsAtBottom: - p.startNewSubPath (0.0f, 0.0f); - p.lineTo (indent, h); - p.lineTo (w - indent, h); - p.lineTo (w, 0.0f); - p.lineTo (w + overhang, -overhang); - p.lineTo (-overhang, -overhang); - break; - - default: - p.startNewSubPath (0.0f, h); - p.lineTo (indent, 0.0f); - p.lineTo (w - indent, 0.0f); - p.lineTo (w, h); - p.lineTo (w + overhang, h + overhang); - p.lineTo (-overhang, h + overhang); - break; - } - - p.closeSubPath(); - - p = p.createPathWithRoundedCorners (3.0f); -} - -void LookAndFeel::fillTabButtonShape (TabBarButton& button, Graphics& g, const Path& path, bool /*isMouseOver*/, bool /*isMouseDown*/) -{ - const Colour tabBackground (button.getTabBackgroundColour()); - const bool isFrontTab = button.isFrontTab(); - - g.setColour (isFrontTab ? tabBackground - : tabBackground.withMultipliedAlpha (0.9f)); - - g.fillPath (path); - - g.setColour (button.findColour (isFrontTab ? TabbedButtonBar::frontOutlineColourId - : TabbedButtonBar::tabOutlineColourId, false) - .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f)); - - g.strokePath (path, PathStrokeType (isFrontTab ? 1.0f : 0.5f)); -} - -void LookAndFeel::drawTabButtonText (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown) -{ - const Rectangle area (button.getTextArea().toFloat()); - - float length = area.getWidth(); - float depth = area.getHeight(); - - if (button.getTabbedButtonBar().isVertical()) - std::swap (length, depth); - - Font font (depth * 0.6f); - font.setUnderline (button.hasKeyboardFocus (false)); - - AffineTransform t; - - switch (button.getTabbedButtonBar().getOrientation()) - { - case TabbedButtonBar::TabsAtLeft: t = t.rotated (float_Pi * -0.5f).translated (area.getX(), area.getBottom()); break; - case TabbedButtonBar::TabsAtRight: t = t.rotated (float_Pi * 0.5f).translated (area.getRight(), area.getY()); break; - case TabbedButtonBar::TabsAtTop: - case TabbedButtonBar::TabsAtBottom: t = t.translated (area.getX(), area.getY()); break; - default: jassertfalse; break; - } - - Colour col; - - if (button.isFrontTab() && (button.isColourSpecified (TabbedButtonBar::frontTextColourId) - || isColourSpecified (TabbedButtonBar::frontTextColourId))) - col = findColour (TabbedButtonBar::frontTextColourId); - else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId) - || isColourSpecified (TabbedButtonBar::tabTextColourId)) - col = findColour (TabbedButtonBar::tabTextColourId); - else - col = button.getTabBackgroundColour().contrasting(); - - const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f; - - g.setColour (col.withMultipliedAlpha (alpha)); - g.setFont (font); - g.addTransform (t); - - g.drawFittedText (button.getButtonText().trim(), - 0, 0, (int) length, (int) depth, - Justification::centred, - jmax (1, ((int) depth) / 12)); -} - -void LookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown) -{ - Path tabShape; - createTabButtonShape (button, tabShape, isMouseOver, isMouseDown); - - const Rectangle activeArea (button.getActiveArea()); - tabShape.applyTransform (AffineTransform::translation ((float) activeArea.getX(), - (float) activeArea.getY())); - - DropShadow (Colours::black.withAlpha (0.5f), 2, Point (0, 1)).drawForPath (g, tabShape); - - fillTabButtonShape (button, g, tabShape, isMouseOver, isMouseDown); - drawTabButtonText (button, g, isMouseOver, isMouseDown); -} - -void LookAndFeel::drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) {} - -void LookAndFeel::drawTabAreaBehindFrontButton (TabbedButtonBar& bar, Graphics& g, const int w, const int h) -{ - const float shadowSize = 0.2f; - - Rectangle shadowRect, line; - ColourGradient gradient (Colours::black.withAlpha (bar.isEnabled() ? 0.3f : 0.15f), 0, 0, - Colours::transparentBlack, 0, 0, false); - - switch (bar.getOrientation()) - { - case TabbedButtonBar::TabsAtLeft: - gradient.point1.x = (float) w; - gradient.point2.x = w * (1.0f - shadowSize); - shadowRect.setBounds ((int) gradient.point2.x, 0, w - (int) gradient.point2.x, h); - line.setBounds (w - 1, 0, 1, h); - break; - - case TabbedButtonBar::TabsAtRight: - gradient.point2.x = w * shadowSize; - shadowRect.setBounds (0, 0, (int) gradient.point2.x, h); - line.setBounds (0, 0, 1, h); - break; - - case TabbedButtonBar::TabsAtTop: - gradient.point1.y = (float) h; - gradient.point2.y = h * (1.0f - shadowSize); - shadowRect.setBounds (0, (int) gradient.point2.y, w, h - (int) gradient.point2.y); - line.setBounds (0, h - 1, w, 1); - break; - - case TabbedButtonBar::TabsAtBottom: - gradient.point2.y = h * shadowSize; - shadowRect.setBounds (0, 0, w, (int) gradient.point2.y); - line.setBounds (0, 0, w, 1); - break; - - default: break; - } - - g.setGradientFill (gradient); - g.fillRect (shadowRect.expanded (2, 2)); - - g.setColour (Colour (0x80000000)); - g.fillRect (line); -} - -Button* LookAndFeel::createTabBarExtrasButton() -{ - const float thickness = 7.0f; - const float indent = 22.0f; - - Path p; - p.addEllipse (-10.0f, -10.0f, 120.0f, 120.0f); - - DrawablePath ellipse; - ellipse.setPath (p); - ellipse.setFill (Colour (0x99ffffff)); - - p.clear(); - p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f); - p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f); - p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness); - p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness); - p.setUsingNonZeroWinding (false); - - DrawablePath dp; - dp.setPath (p); - dp.setFill (Colour (0x59000000)); - - DrawableComposite normalImage; - normalImage.addAndMakeVisible (ellipse.createCopy()); - normalImage.addAndMakeVisible (dp.createCopy()); - - dp.setFill (Colour (0xcc000000)); - - DrawableComposite overImage; - overImage.addAndMakeVisible (ellipse.createCopy()); - overImage.addAndMakeVisible (dp.createCopy()); - - DrawableButton* db = new DrawableButton ("tabs", DrawableButton::ImageFitted); - db->setImages (&normalImage, &overImage, nullptr); - return db; -} - - -//============================================================================== -void LookAndFeel::drawTableHeaderBackground (Graphics& g, TableHeaderComponent& header) -{ - g.fillAll (Colours::white); - - Rectangle 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(), - false)); - g.fillRect (area); - - g.setColour (Colour (0x33000000)); - g.fillRect (area.removeFromBottom (1)); - - for (int i = header.getNumColumns (true); --i >= 0;) - g.fillRect (header.getColumnPosition (i).removeFromRight (1)); -} - -void LookAndFeel::drawTableHeaderColumn (Graphics& g, const String& columnName, int /*columnId*/, - int width, int height, - bool isMouseOver, bool isMouseDown, - int columnFlags) -{ - if (isMouseDown) - g.fillAll (Colour (0x8899aadd)); - else if (isMouseOver) - g.fillAll (Colour (0x5599aadd)); - - Rectangle area (width, height); - area.reduce (4, 0); - - if ((columnFlags & (TableHeaderComponent::sortedForwards | TableHeaderComponent::sortedBackwards)) != 0) - { - Path sortArrow; - sortArrow.addTriangle (0.0f, 0.0f, - 0.5f, (columnFlags & TableHeaderComponent::sortedForwards) != 0 ? -0.8f : 0.8f, - 1.0f, 0.0f); - - g.setColour (Colour (0x99000000)); - g.fillPath (sortArrow, RectanglePlacement (RectanglePlacement::centred) - .getTransformToFit (sortArrow.getBounds(), - area.removeFromRight (height / 2).reduced (2).toFloat())); - } - - g.setColour (Colours::black); - g.setFont (Font (height * 0.5f, Font::bold)); - g.drawFittedText (columnName, area, Justification::centredLeft, 1); -} - -//============================================================================== -void LookAndFeel::drawLasso (Graphics& g, Component& lassoComp) -{ - const int outlineThickness = 1; - - g.fillAll (lassoComp.findColour (0x1000440 /*lassoFillColourId*/)); - - g.setColour (lassoComp.findColour (0x1000441 /*lassoOutlineColourId*/)); - g.drawRect (lassoComp.getLocalBounds(), outlineThickness); -} - -//============================================================================== -void LookAndFeel::paintToolbarBackground (Graphics& g, int w, int h, Toolbar& toolbar) -{ - const Colour background (toolbar.findColour (Toolbar::backgroundColourId)); - - g.setGradientFill (ColourGradient (background, 0.0f, 0.0f, - background.darker (0.1f), - toolbar.isVertical() ? w - 1.0f : 0.0f, - toolbar.isVertical() ? 0.0f : h - 1.0f, - false)); - g.fillAll(); -} - -Button* LookAndFeel::createToolbarMissingItemsButton (Toolbar& /*toolbar*/) -{ - return createTabBarExtrasButton(); -} - -void LookAndFeel::paintToolbarButtonBackground (Graphics& g, int /*width*/, int /*height*/, - bool isMouseOver, bool isMouseDown, - ToolbarItemComponent& component) -{ - if (isMouseDown) - g.fillAll (component.findColour (Toolbar::buttonMouseDownBackgroundColourId, true)); - else if (isMouseOver) - g.fillAll (component.findColour (Toolbar::buttonMouseOverBackgroundColourId, true)); -} - -void LookAndFeel::paintToolbarButtonLabel (Graphics& g, int x, int y, int width, int height, - const String& text, ToolbarItemComponent& component) -{ - g.setColour (component.findColour (Toolbar::labelTextColourId, true) - .withAlpha (component.isEnabled() ? 1.0f : 0.25f)); - - const float fontHeight = jmin (14.0f, height * 0.85f); - g.setFont (fontHeight); - - g.drawFittedText (text, - x, y, width, height, - Justification::centred, - jmax (1, height / (int) fontHeight)); -} - -//============================================================================== -void LookAndFeel::drawPropertyPanelSectionHeader (Graphics& g, const String& name, - bool isOpen, int width, int height) -{ - const int buttonSize = (height * 3) / 4; - const int buttonIndent = (height - buttonSize) / 2; - - drawTreeviewPlusMinusBox (g, buttonIndent, buttonIndent, buttonSize, buttonSize, ! isOpen, false); - - const int textX = buttonIndent * 2 + buttonSize + 2; - - g.setColour (Colours::black); - g.setFont (Font (height * 0.7f, Font::bold)); - g.drawText (name, textX, 0, width - textX - 4, height, Justification::centredLeft, true); -} - -void LookAndFeel::drawPropertyComponentBackground (Graphics& g, int width, int height, - PropertyComponent&) -{ - g.setColour (Colour (0x66ffffff)); - g.fillRect (0, 0, width, height - 1); -} - -void LookAndFeel::drawPropertyComponentLabel (Graphics& g, int, int height, - PropertyComponent& component) -{ - g.setColour (Colours::black); - - if (! component.isEnabled()) - g.setOpacity (0.6f); - - g.setFont (jmin (height, 24) * 0.65f); - - const Rectangle r (getPropertyComponentContentPosition (component)); - - g.drawFittedText (component.getName(), - 3, r.getY(), r.getX() - 5, r.getHeight(), - Justification::centredLeft, 2); -} - -Rectangle LookAndFeel::getPropertyComponentContentPosition (PropertyComponent& component) -{ - const int textW = jmin (200, component.getWidth() / 3); - return Rectangle (textW, 1, component.getWidth() - textW - 1, component.getHeight() - 3); -} - -//============================================================================== -void LookAndFeel::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, - const Path& path, Image& cachedImage) -{ - if (cachedImage.isNull()) - { - cachedImage = Image (Image::ARGB, box.getWidth(), box.getHeight(), true); - Graphics g2 (cachedImage); - - DropShadow (Colours::black.withAlpha (0.7f), 8, Point (0, 2)).drawForPath (g2, path); - } - - g.setColour (Colours::black); - g.drawImageAt (cachedImage, 0, 0); - - g.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f)); - g.fillPath (path); - - g.setColour (Colours::white.withAlpha (0.8f)); - g.strokePath (path, PathStrokeType (2.0f)); -} - - -//============================================================================== -AttributedString LookAndFeel::createFileChooserHeaderText (const String& title, - const String& instructions) -{ - AttributedString s; - s.setJustification (Justification::centred); - - const Colour colour (findColour (FileChooserDialogBox::titleTextColourId)); - s.append (title + "\n\n", Font (17.0f, Font::bold), colour); - s.append (instructions, Font (14.0f), colour); - - return s; -} - -void LookAndFeel::drawFileBrowserRow (Graphics& g, int width, int height, - const String& filename, Image* icon, - const String& fileSizeDescription, - const String& fileTimeDescription, - const bool isDirectory, - const bool isItemSelected, - const int /*itemIndex*/, - DirectoryContentsDisplayComponent& dcc) -{ - Component* const fileListComp = dynamic_cast (&dcc); - - if (isItemSelected) - g.fillAll (fileListComp != nullptr ? fileListComp->findColour (DirectoryContentsDisplayComponent::highlightColourId) - : findColour (DirectoryContentsDisplayComponent::highlightColourId)); - - const int x = 32; - g.setColour (Colours::black); - - if (icon != nullptr && icon->isValid()) - { - g.drawImageWithin (*icon, 2, 2, x - 4, height - 4, - RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, - false); - } - else - { - if (const Drawable* d = isDirectory ? getDefaultFolderImage() - : getDefaultDocumentFileImage()) - d->drawWithin (g, Rectangle (2.0f, 2.0f, x - 4.0f, height - 4.0f), - RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, 1.0f); - } - - g.setColour (fileListComp != nullptr ? fileListComp->findColour (DirectoryContentsDisplayComponent::textColourId) - : findColour (DirectoryContentsDisplayComponent::textColourId)); - g.setFont (height * 0.7f); - - if (width > 450 && ! isDirectory) - { - const int sizeX = roundToInt (width * 0.7f); - const int dateX = roundToInt (width * 0.8f); - - g.drawFittedText (filename, - x, 0, sizeX - x, height, - Justification::centredLeft, 1); - - g.setFont (height * 0.5f); - g.setColour (Colours::darkgrey); - - if (! isDirectory) - { - g.drawFittedText (fileSizeDescription, - sizeX, 0, dateX - sizeX - 8, height, - Justification::centredRight, 1); - - g.drawFittedText (fileTimeDescription, - dateX, 0, width - 8 - dateX, height, - Justification::centredRight, 1); - } - } - else - { - g.drawFittedText (filename, - x, 0, width - x, height, - Justification::centredLeft, 1); - - } -} - -Button* LookAndFeel::createFileBrowserGoUpButton() -{ - DrawableButton* goUpButton = new DrawableButton ("up", DrawableButton::ImageOnButtonBackground); - - Path arrowPath; - arrowPath.addArrow (Line (50.0f, 100.0f, 50.0f, 0.0f), 40.0f, 100.0f, 50.0f); - - DrawablePath arrowImage; - arrowImage.setFill (Colours::black.withAlpha (0.4f)); - arrowImage.setPath (arrowPath); - - goUpButton->setImages (&arrowImage); - - return goUpButton; -} - -void LookAndFeel::layoutFileBrowserComponent (FileBrowserComponent& browserComp, - DirectoryContentsDisplayComponent* fileListComponent, - FilePreviewComponent* previewComp, - ComboBox* currentPathBox, - TextEditor* filenameBox, - Button* goUpButton) -{ - const int x = 8; - int w = browserComp.getWidth() - x - x; - - if (previewComp != nullptr) - { - const int previewWidth = w / 3; - previewComp->setBounds (x + w - previewWidth, 0, previewWidth, browserComp.getHeight()); - - w -= previewWidth + 4; - } - - int y = 4; - - const int controlsHeight = 22; - const int bottomSectionHeight = controlsHeight + 8; - const int upButtonWidth = 50; - - currentPathBox->setBounds (x, y, w - upButtonWidth - 6, controlsHeight); - goUpButton->setBounds (x + w - upButtonWidth, y, upButtonWidth, controlsHeight); - - y += controlsHeight + 4; - - if (Component* const listAsComp = dynamic_cast (fileListComponent)) - { - listAsComp->setBounds (x, y, w, browserComp.getHeight() - y - bottomSectionHeight); - y = listAsComp->getBottom() + 4; - } - - filenameBox->setBounds (x + 50, y, w - 50, controlsHeight); -} - -// Pulls a drawable out of compressed valuetree data.. -Drawable* LookAndFeel::loadDrawableFromData (const void* data, size_t numBytes) -{ - MemoryInputStream m (data, numBytes, false); - GZIPDecompressorInputStream gz (m); - ValueTree drawable (ValueTree::readFromStream (gz)); - return Drawable::createFromValueTree (drawable.getChild (0), nullptr); -} - -const Drawable* LookAndFeel::getDefaultFolderImage() -{ - if (folderImage == nullptr) - { - static const unsigned char drawableData[] = - { 120,218,197,86,77,111,27,55,16,229,182,161,237,6,61,39,233,77,63,192,38,56,195,225,215,209,105,210,2,141,13,20,201,193,109,111,178,181,178,183,145,181,130,180,110,145,127,159,199,93,73,137,87,53,218,91,109,192,160,151,179,156,55,111,222,188,229,155,247, - 231,87,231,175,47,222,170,234,155,229,244,190,86,213,115,253,102,61,253,123,122,189,168,85,51,83,213,119,250,238,221,47,231,151,175,223,169,170,250,121,221,62,172,84,245,172,60,63,209,243,118,49,171,215,170,107,87,23,245,188,83,213,145,182,167,19,91, - 254,127,223,220,222,117,37,68,82,40,143,174,219,174,107,239,135,168,147,18,37,108,85,245,237,46,207,70,33,249,175,211,238,78,85,186,28,253,76,175,73,109,186,117,251,177,190,106,102,229,241,247,58,24,103,203,15,101,245,103,219,44,187,15,221,39,0,172,142, - 245,125,211,1,196,205,116,181,125,114,164,175,31,186,78,45,219,229,31,245,186,189,106,150,179,102,121,139,100,154,240,231,167,102,177,64,72,247,105,213,23,122,187,158,206,154,122,217,169,85,57,18,1,47,53,101,107,18,135,204,167,147,192,201,216,20,114, - 244,195,62,171,234,7,125,198,100,136,216,145,149,211,9,57,103,40,249,72,219,8,167,170,87,250,140,162,199,123,226,3,34,82,202,134,131,13,172,74,170,233,162,0,177,234,166,93,180,15,235,141,170,206,180,157,204,231,150,156,159,207,39,195,50,214,88,18,150, - 245,205,124,250,104,169,212,135,158,19,144,53,20,112,172,55,237,2,132,13,199,149,130,230,115,145,112,147,147,82,61,157,32,238,178,253,11,145,213,138,10,52,138,38,103,111,99,164,211,137,139,198,35,177,35,167,212,143,15,215,205,13,160,109,163,172,225,152, - 16,232,17,149,140,103,144,158,146,90,113,217,12,6,197,167,236,3,54,5,181,101,73,54,138,90,245,165,227,120,18,252,150,77,15,242,188,228,204,81,169,139,102,249,5,68,192,145,14,244,112,1,145,29,94,137,96,235,49,136,151,58,246,32,88,192,161,88,176,76,226, - 36,247,24,176,7,232,62,16,83,42,155,201,160,30,222,65,72,98,82,76,33,198,254,197,96,124,10,150,243,8,130,48,228,36,94,124,6,4,43,38,0,142,205,99,30,4,221,13,33,230,220,71,177,65,49,142,243,150,7,1,51,20,2,5,96,96,84,225,56,217,188,3,33,46,24,228,112, - 69,69,12,68,228,108,242,99,16,165,118,208,28,51,200,98,87,42,74,62,209,24,4,206,48,22,153,125,132,220,196,56,15,234,99,216,130,0,141,38,74,162,130,48,35,163,141,94,196,245,32,94,104,7,154,132,209,40,108,162,165,232,153,165,17,4,138,201,176,135,58,49, - 165,130,122,108,114,54,28,240,64,17,89,188,79,177,116,149,10,4,246,91,30,94,104,112,96,226,144,131,144,142,98,78,177,7,128,81,242,224,140,36,249,80,208,145,196,12,202,15,16,60,161,200,69,187,169,213,86,198,123,87,224,255,199,21,94,105,134,72,40,177,245, - 14,182,32,232,54,196,231,100,111,11,189,168,201,39,177,84,102,38,139,177,168,74,210,87,174,64,20,138,160,67,111,10,4,98,196,97,60,158,118,133,25,111,173,224,171,37,97,185,119,133,221,242,63,184,194,140,71,174,240,252,145,43,72,32,147,146,147,4,104,104, - 117,134,10,18,12,107,212,40,72,148,57,6,71,69,135,222,248,16,160,168,3,169,144,55,201,69,41,147,137,134,99,50,97,8,178,85,43,217,140,201,151,192,152,10,242,190,24,11,59,183,29,25,42,115,236,98,14,229,252,32,80,66,0,162,17,136,72,6,67,5,45,242,224,10, - 193,102,71,50,6,17,129,212,18,115,105,150,80,169,45,123,222,141,76,178,70,32,55,24,90,217,132,71,73,200,57,238,204,3,136,49,144,185,55,183,190,20,137,52,246,47,113,232,158,69,35,49,145,208,129,193,56,178,77,135,230,145,113,22,140,69,74,20,146,2,120,218, - 155,135,48,32,10,89,30,156,165,204,254,222,193,160,12,19,49,6,210,59,11,70,62,4,31,15,64,196,2,157,98,33,58,1,104,32,152,50,31,128,64,148,183,197,108,209,89,107,240,41,75,36,123,16,208,108,180,44,236,250,182,227,27,20,137,118,76,60,165,137,221,92,94, - 78,215,31,235,245,230,183,242,229,30,214,251,251,195,145,94,148,15,253,170,221,52,93,211,46,7,109,171,81,208,177,94,247,119,132,47,81,186,92,22,246,7,255,254,15,7,107,141,171,197,191,156,123,162,135,187,198,227,131,113,219,80,159,1,4,239,223,231,0,0 }; - - folderImage = loadDrawableFromData (drawableData, sizeof (drawableData)); - } - - return folderImage; -} - -const Drawable* LookAndFeel::getDefaultDocumentFileImage() -{ - if (documentImage == nullptr) - { - static const unsigned char drawableData[] = - { 120,218,213,88,77,115,219,54,16,37,147,208,246,228,214,75,155,246,164,123,29,12,176,216,197,199,49,105,218,94,156,153,78,114,72,219,155,108,75,137,26,89,212,200,116,59,233,175,239,3,105,201,164,68,50,158,166,233,76,196,11,69,60,173,128,197,123,139,183, - 124,241,234,217,155,103,207,207,126,204,242,7,171,233,213,44,203,31,23,47,54,211,191,166,231,203,89,182,184,204,242,147,226,195,165,219,252,125,150,229,249,207,155,242,102,157,229,143,210,227,199,197,101,121,113,115,53,91,85,89,85,174,207,102,243,42, - 203,143,10,125,58,209,233,251,171,197,219,119,85,250,173,97,151,30,157,151,85,85,94,53,168,147,132,50,226,179,252,225,246,143,174,179,44,63,254,101,90,189,203,242,34,5,127,84,172,77,118,93,109,202,247,179,55,139,203,244,248,97,161,179,63,202,197,170, - 122,93,125,192,196,242,227,226,106,81,205,54,217,197,116,125,251,228,168,56,191,169,170,108,85,174,126,159,109,202,55,139,213,229,98,245,182,249,97,254,240,167,197,114,137,5,86,31,214,245,111,175,203,37,254,230,162,92,150,55,155,180,148,249,237,39,203, - 94,215,127,58,10,213,245,39,203,234,249,102,249,87,47,203,63,129,204,49,227,252,73,225,149,145,104,131,245,254,116,34,202,82,164,16,153,179,236,108,177,234,7,49,41,237,130,144,167,17,144,15,42,104,239,93,12,35,32,99,68,9,187,24,125,7,244,77,23,36,164, - 40,56,226,61,12,107,229,130,215,100,105,24,227,89,17,246,211,105,55,140,49,218,43,207,100,245,72,28,195,70,17,230,201,118,8,243,164,139,233,95,88,23,52,152,162,54,104,48,217,237,105,15,111,91,107,253,131,160,118,34,239,69,128,54,232,135,101,121,61,203, - 110,169,181,147,2,253,159,82,48,180,229,247,167,74,193,41,141,188,35,93,241,116,18,148,113,214,120,207,113,47,19,109,16,51,182,153,193,5,59,2,10,90,69,114,218,135,48,2,50,198,43,171,189,152,81,144,88,108,85,136,78,246,64,54,42,163,35,69,30,3,121,82,38, - 98,81,98,70,64,70,139,34,111,163,167,49,144,13,202,138,179,58,220,23,52,180,186,54,104,48,79,109,208,96,198,219,19,31,220,187,118,10,6,65,237,100,222,139,5,109,80,191,30,236,151,162,135,147,142,30,68,105,182,58,6,22,84,43,229,124,148,116,97,145,55,231, - 139,11,76,228,16,37,14,48,205,145,77,134,34,176,55,152,182,200,57,99,93,204,144,145,253,65,97,229,132,72,104,63,62,71,21,140,54,186,41,226,59,84,19,63,130,15,222,235,224,185,59,104,27,226,68,101,153,241,227,177,248,29,20,136,26,8,252,178,183,241,219, - 131,137,160,209,107,109,92,79,124,16,211,184,104,93,77,130,110,124,2,65,172,67,201,60,157,88,163,2,91,99,92,216,198,55,78,69,75,190,150,119,84,98,200,71,150,109,124,36,204,227,52,8,33,229,223,68,167,173,167,131,248,137,212,226,141,19,233,160,154,248, - 144,142,195,140,137,185,59,104,15,247,119,40,126,23,69,81,200,242,110,254,123,20,49,94,112,110,245,199,111,241,167,87,36,252,101,138,132,149,22,22,38,65,134,29,182,139,24,230,192,31,144,184,133,130,72,44,131,210,142,111,147,216,30,76,123,30,113,206,242, - 150,196,157,65,129,130,76,180,194,61,34,225,160,5,228,233,160,118,34,137,26,202,115,212,29,108,72,134,243,223,90,114,226,199,226,119,80,6,245,152,197,122,217,146,184,53,24,140,210,30,21,59,80,79,124,182,202,71,207,218,112,159,72,80,53,140,109,68,2,191, - 227,217,210,78,36,94,137,88,231,82,157,8,176,61,0,122,191,19,137,3,255,13,39,183,228,20,193,151,144,119,166,79,36,40,253,156,138,72,11,181,19,137,14,46,176,217,27,180,135,251,219,31,255,235,61,148,165,96,72,122,118,23,229,81,52,135,24,250,163,183,216, - 211,43,17,217,151,136,253,116,137,28,53,188,127,92,188,221,76,47,23,169,59,90,167,144,141,239,197,86,104,141,189,60,157,80,84,142,140,4,31,154,241,122,105,132,41,107,13,201,39,86,120,24,82,114,206,198,6,96,27,227,172,36,232,168,201,36,219,24,113,62,163, - 154,101,233,143,166,203,102,26,141,206,174,179,252,89,161,39,243,249,197,121,186,38,233,246,146,211,53,1,123,56,194,231,122,143,103,179,217,60,204,167,19,147,110,41,93,173,219,123,72,89,248,35,173,16,220,50,179,111,60,181,24,88,103,156,235,7,78,248,14, - 4,119,78,162,93,60,112,35,109,16,124,126,12,17,71,67,24,1,165,142,1,181,215,248,56,6,66,235,193,137,167,61,22,30,5,3,27,101,71,64,169,25,112,216,2,63,22,169,110,43,18,200,140,129,208,160,88,44,220,208,125,65,67,171,107,131,6,243,212,6,13,102,188,61,241, - 225,189,107,165,96,16,212,78,230,189,88,208,6,245,235,214,237,235,150,62,167,110,155,106,170,53,133,192,117,193,20,84,78,74,174,98,39,92,156,8,112,21,46,80,106,12,209,207,225,228,16,113,59,225,126,87,60,133,25,209,34,36,2,99,242,52,197,48,30,75,244,247, - 212,238,246,182,173,221,185,78,215,127,167,221,162,163,221,250,152,217,146,196,222,145,100,223,235,105,108,28,250,149,212,74,224,86,2,213,118,110,119,204,224,144,208,38,214,131,200,14,214,223,120,189,230,53,1,193,70,133,154,131,56,223,16,229,48,188,14, - 201,205,213,121,71,233,68,89,15,124,103,37,53,26,11,118,176,127,169,88,166,158,219,178,117,173,83,108,75,95,55,68,186,193,53,246,146,206,127,6,63,53,78,58,228,204,155,224,113,74,91,232,221,195,240,105,215,34,29,138,64,128,183,8,130,233,71,173,56,54,101, - 99,75,186,111,65,58,28,229,145,82,19,152,12,99,180,81,130,131,75,234,229,220,247,53,231,154,79,205,185,185,155,199,249,172,38,85,253,204,76,68,95,92,204,207,255,221,75,178,227,14,187,224,224,97,202,172,173,219,12,167,130,133,9,54,135,245,92,176,29,134, - 165,110,139,141,18,16,223,29,188,183,65,207,144,106,144,151,143,128,224,176,168,110,140,32,62,56,110,219,195,54,235,20,68,209,216,34,232,21,6,41,234,157,39,211,201,107,160,230,66,225,56,153,9,101,21,37,237,150,204,14,115,208,22,221,54,216,230,33,116, - 14,65,14,44,19,8,236,73,71,246,182,110,125,224,75,132,195,214,247,163,36,51,252,84,76,124,37,212,100,88,62,183,179,76,67,217,218,242,244,229,116,243,126,182,185,254,21,105,126,208,220,239,94,229,30,21,203,244,202,117,93,94,47,170,69,185,106,246,60,219, - 3,29,23,155,250,109,237,29,170,72,175,109,119,129,127,235,9,92,20,85,185,254,72,220,147,162,121,235,219,13,44,144,225,63,241,244,165,51,0,0 }; - - documentImage = loadDrawableFromData (drawableData, sizeof (drawableData)); - } - - return documentImage; -} - -//============================================================================== -void LookAndFeel::drawLevelMeter (Graphics& g, int width, int height, float level) -{ - g.setColour (Colours::white.withAlpha (0.7f)); - g.fillRoundedRectangle (0.0f, 0.0f, (float) width, (float) height, 3.0f); - g.setColour (Colours::black.withAlpha (0.2f)); - g.drawRoundedRectangle (1.0f, 1.0f, width - 2.0f, height - 2.0f, 3.0f, 1.0f); - - const int totalBlocks = 7; - const int numBlocks = roundToInt (totalBlocks * level); - const float w = (width - 6.0f) / (float) totalBlocks; - - for (int i = 0; i < totalBlocks; ++i) - { - if (i >= numBlocks) - g.setColour (Colours::lightblue.withAlpha (0.6f)); - else - g.setColour (i < totalBlocks - 1 ? Colours::blue.withAlpha (0.5f) - : Colours::red); - - g.fillRoundedRectangle (3.0f + i * w + w * 0.1f, 3.0f, w * 0.8f, height - 6.0f, w * 0.4f); - } -} - -//============================================================================== -void LookAndFeel::drawKeymapChangeButton (Graphics& g, int width, int height, Button& button, const String& keyDescription) -{ - const Colour textColour (button.findColour (0x100ad01 /*KeyMappingEditorComponent::textColourId*/, true)); - - if (keyDescription.isNotEmpty()) - { - if (button.isEnabled()) - { - const float alpha = button.isDown() ? 0.3f : (button.isOver() ? 0.15f : 0.08f); - g.fillAll (textColour.withAlpha (alpha)); - - g.setOpacity (0.3f); - drawBevel (g, 0, 0, width, height, 2); - } - - g.setColour (textColour); - g.setFont (height * 0.6f); - g.drawFittedText (keyDescription, - 3, 0, width - 6, height, - Justification::centred, 1); - } - else - { - const float thickness = 7.0f; - const float indent = 22.0f; - - Path p; - p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f); - p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f); - p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness); - p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness); - p.setUsingNonZeroWinding (false); - - g.setColour (textColour.withAlpha (button.isDown() ? 0.7f : (button.isOver() ? 0.5f : 0.3f))); - g.fillPath (p, p.getTransformToScaleToFit (2.0f, 2.0f, width - 4.0f, height - 4.0f, true)); - } - - if (button.hasKeyboardFocus (false)) - { - g.setColour (textColour.withAlpha (0.4f)); - g.drawRect (0, 0, width, height); - } -} - -//============================================================================== -void LookAndFeel::drawBevel (Graphics& g, const int x, const int y, const int width, const int height, - const int bevelThickness, const Colour& topLeftColour, const Colour& bottomRightColour, - const bool useGradient, const bool sharpEdgeOnOutside) -{ - if (g.clipRegionIntersects (Rectangle (x, y, width, height))) - { - LowLevelGraphicsContext& context = g.getInternalContext(); - context.saveState(); - - for (int i = bevelThickness; --i >= 0;) - { - const float op = useGradient ? (sharpEdgeOnOutside ? bevelThickness - i : i) / (float) bevelThickness - : 1.0f; - - context.setFill (topLeftColour.withMultipliedAlpha (op)); - context.fillRect (Rectangle (x + i, y + i, width - i * 2, 1), false); - context.setFill (topLeftColour.withMultipliedAlpha (op * 0.75f)); - context.fillRect (Rectangle (x + i, y + i + 1, 1, height - i * 2 - 2), false); - context.setFill (bottomRightColour.withMultipliedAlpha (op)); - context.fillRect (Rectangle (x + i, y + height - i - 1, width - i * 2, 1), false); - context.setFill (bottomRightColour.withMultipliedAlpha (op * 0.75f)); - context.fillRect (Rectangle (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2), false); - } - - context.restoreState(); - } -} - -//============================================================================== -void LookAndFeel::drawShinyButtonShape (Graphics& g, - float x, float y, float w, float h, - float maxCornerSize, - const Colour& baseColour, - const float strokeWidth, - const bool flatOnLeft, - const bool flatOnRight, - const bool flatOnTop, - const bool flatOnBottom) noexcept -{ - if (w <= strokeWidth * 1.1f || h <= strokeWidth * 1.1f) - return; - - const float cs = jmin (maxCornerSize, w * 0.5f, h * 0.5f); - - Path outline; - outline.addRoundedRectangle (x, y, w, h, cs, cs, - ! (flatOnLeft || flatOnTop), - ! (flatOnRight || flatOnTop), - ! (flatOnLeft || flatOnBottom), - ! (flatOnRight || flatOnBottom)); - - ColourGradient cg (baseColour, 0.0f, y, - baseColour.overlaidWith (Colour (0x070000ff)), 0.0f, y + h, - false); - - cg.addColour (0.5, baseColour.overlaidWith (Colour (0x33ffffff))); - cg.addColour (0.51, baseColour.overlaidWith (Colour (0x110000ff))); - - g.setGradientFill (cg); - g.fillPath (outline); - - g.setColour (Colour (0x80000000)); - g.strokePath (outline, PathStrokeType (strokeWidth)); -} - -//============================================================================== -void LookAndFeel::drawGlassSphere (Graphics& g, - const float x, const float y, - const float diameter, - const Colour& colour, - const float outlineThickness) noexcept -{ - if (diameter <= outlineThickness) - return; - - Path p; - p.addEllipse (x, y, diameter, diameter); - - { - ColourGradient cg (Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y, - Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y + diameter, false); - - cg.addColour (0.4, Colours::white.overlaidWith (colour)); - - g.setGradientFill (cg); - g.fillPath (p); - } - - g.setGradientFill (ColourGradient (Colours::white, 0, y + diameter * 0.06f, - Colours::transparentWhite, 0, y + diameter * 0.3f, false)); - g.fillEllipse (x + diameter * 0.2f, y + diameter * 0.05f, diameter * 0.6f, diameter * 0.4f); - - ColourGradient cg (Colours::transparentBlack, - x + diameter * 0.5f, y + diameter * 0.5f, - Colours::black.withAlpha (0.5f * outlineThickness * colour.getFloatAlpha()), - x, y + diameter * 0.5f, true); - - cg.addColour (0.7, Colours::transparentBlack); - cg.addColour (0.8, Colours::black.withAlpha (0.1f * outlineThickness)); - - g.setGradientFill (cg); - g.fillPath (p); - - g.setColour (Colours::black.withAlpha (0.5f * colour.getFloatAlpha())); - g.drawEllipse (x, y, diameter, diameter, outlineThickness); -} - -//============================================================================== -void LookAndFeel::drawGlassPointer (Graphics& g, - const float x, const float y, - const float diameter, - const Colour& colour, const float outlineThickness, - const int direction) noexcept -{ - if (diameter <= outlineThickness) - return; - - Path p; - p.startNewSubPath (x + diameter * 0.5f, y); - p.lineTo (x + diameter, y + diameter * 0.6f); - p.lineTo (x + diameter, y + diameter); - p.lineTo (x, y + diameter); - p.lineTo (x, y + diameter * 0.6f); - p.closeSubPath(); - - p.applyTransform (AffineTransform::rotation (direction * (float_Pi * 0.5f), x + diameter * 0.5f, y + diameter * 0.5f)); - - { - ColourGradient cg (Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y, - Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y + diameter, false); - - cg.addColour (0.4, Colours::white.overlaidWith (colour)); - - g.setGradientFill (cg); - g.fillPath (p); - } - - ColourGradient cg (Colours::transparentBlack, - x + diameter * 0.5f, y + diameter * 0.5f, - Colours::black.withAlpha (0.5f * outlineThickness * colour.getFloatAlpha()), - x - diameter * 0.2f, y + diameter * 0.5f, true); - - cg.addColour (0.5, Colours::transparentBlack); - cg.addColour (0.7, Colours::black.withAlpha (0.07f * outlineThickness)); - - g.setGradientFill (cg); - g.fillPath (p); - - g.setColour (Colours::black.withAlpha (0.5f * colour.getFloatAlpha())); - g.strokePath (p, PathStrokeType (outlineThickness)); -} - -//============================================================================== -void LookAndFeel::drawGlassLozenge (Graphics& g, - const float x, const float y, - const float width, const float height, - const Colour& colour, - const float outlineThickness, - const float cornerSize, - const bool flatOnLeft, - const bool flatOnRight, - const bool flatOnTop, - const bool flatOnBottom) noexcept -{ - if (width <= outlineThickness || height <= outlineThickness) - return; - - const int intX = (int) x; - const int intY = (int) y; - const int intW = (int) width; - const int intH = (int) height; - - const float cs = cornerSize < 0 ? jmin (width * 0.5f, height * 0.5f) : cornerSize; - const float edgeBlurRadius = height * 0.75f + (height - cs * 2.0f); - const int intEdge = (int) edgeBlurRadius; - - Path outline; - outline.addRoundedRectangle (x, y, width, height, cs, cs, - ! (flatOnLeft || flatOnTop), - ! (flatOnRight || flatOnTop), - ! (flatOnLeft || flatOnBottom), - ! (flatOnRight || flatOnBottom)); - - { - ColourGradient cg (colour.darker (0.2f), 0, y, - colour.darker (0.2f), 0, y + height, false); - - cg.addColour (0.03, colour.withMultipliedAlpha (0.3f)); - cg.addColour (0.4, colour); - cg.addColour (0.97, colour.withMultipliedAlpha (0.3f)); - - g.setGradientFill (cg); - g.fillPath (outline); - } - - ColourGradient cg (Colours::transparentBlack, x + edgeBlurRadius, y + height * 0.5f, - colour.darker (0.2f), x, y + height * 0.5f, true); - - cg.addColour (jlimit (0.0, 1.0, 1.0 - (cs * 0.5f) / edgeBlurRadius), Colours::transparentBlack); - cg.addColour (jlimit (0.0, 1.0, 1.0 - (cs * 0.25f) / edgeBlurRadius), colour.darker (0.2f).withMultipliedAlpha (0.3f)); - - if (! (flatOnLeft || flatOnTop || flatOnBottom)) - { - g.saveState(); - g.setGradientFill (cg); - g.reduceClipRegion (intX, intY, intEdge, intH); - g.fillPath (outline); - g.restoreState(); - } - - if (! (flatOnRight || flatOnTop || flatOnBottom)) - { - cg.point1.setX (x + width - edgeBlurRadius); - cg.point2.setX (x + width); - - g.saveState(); - g.setGradientFill (cg); - g.reduceClipRegion (intX + intW - intEdge, intY, 2 + intEdge, intH); - g.fillPath (outline); - g.restoreState(); - } - - { - const float leftIndent = flatOnTop || flatOnLeft ? 0.0f : cs * 0.4f; - const float rightIndent = flatOnTop || flatOnRight ? 0.0f : cs * 0.4f; - - Path highlight; - highlight.addRoundedRectangle (x + leftIndent, - y + cs * 0.1f, - width - (leftIndent + rightIndent), - height * 0.4f, - cs * 0.4f, - cs * 0.4f, - ! (flatOnLeft || flatOnTop), - ! (flatOnRight || flatOnTop), - ! (flatOnLeft || flatOnBottom), - ! (flatOnRight || flatOnBottom)); - - g.setGradientFill (ColourGradient (colour.brighter (10.0f), 0, y + height * 0.06f, - Colours::transparentWhite, 0, y + height * 0.4f, false)); - g.fillPath (highlight); - } - - g.setColour (colour.darker().withMultipliedAlpha (1.5f)); - g.strokePath (outline, PathStrokeType (outlineThickness)); -} diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h index 8a67f8280..ef5c416cc 100644 --- a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h @@ -25,14 +25,80 @@ #ifndef JUCE_LOOKANDFEEL_H_INCLUDED #define JUCE_LOOKANDFEEL_H_INCLUDED +//============================================================================== +/** This class is used to hold a few look and feel base classes which are associated + with classes that may not be present because they're from modules other than + juce_gui_basics. +*/ +struct JUCE_API ExtraLookAndFeelBaseClasses +{ + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LassoComponentMethods + { + virtual ~LassoComponentMethods() {} + + virtual void drawLasso (Graphics&, Component& lassoComp) = 0; + }; + + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API KeyMappingEditorComponentMethods + { + virtual ~KeyMappingEditorComponentMethods() {} + + virtual void drawKeymapChangeButton (Graphics&, int width, int height, Button&, const String& keyDescription) = 0; + }; + + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API AudioDeviceSelectorComponentMethods + { + virtual ~AudioDeviceSelectorComponentMethods() {} + + virtual void drawLevelMeter (Graphics&, int width, int height, float level) = 0; + }; +}; + //============================================================================== /** LookAndFeel objects define the appearance of all the JUCE widgets, and subclasses can be used to apply different 'skins' to the application. + This class is an abstract base-class - for actual look-and-feels that you can + instantiate, see LookAndFeel_V1, LookAndFeel_V2 and LookAndFeel_V3. + + @see LookAndFeel_V1, LookAndFeel_V2, LookAndFeel_V3 */ -class JUCE_API LookAndFeel +class JUCE_API LookAndFeel : public ScrollBar::LookAndFeelMethods, + public Button::LookAndFeelMethods, + public ImageButton::LookAndFeelMethods, + public TextEditor::LookAndFeelMethods, + public FileBrowserComponent::LookAndFeelMethods, + public TreeView::LookAndFeelMethods, + public BubbleComponent::LookAndFeelMethods, + public AlertWindow::LookAndFeelMethods, + public PopupMenu::LookAndFeelMethods, + public ComboBox::LookAndFeelMethods, + public Label::LookAndFeelMethods, + public Slider::LookAndFeelMethods, + public ResizableWindow::LookAndFeelMethods, + public DocumentWindow::LookAndFeelMethods, + public TooltipWindow::LookAndFeelMethods, + public TabbedButtonBar::LookAndFeelMethods, + public PropertyComponent::LookAndFeelMethods, + public FilenameComponent::LookAndFeelMethods, + public GroupComponent::LookAndFeelMethods, + public TableHeaderComponent::LookAndFeelMethods, + public CallOutBox::LookAndFeelMethods, + public Toolbar::LookAndFeelMethods, + public ConcertinaPanel::LookAndFeelMethods, + public ProgressBar::LookAndFeelMethods, + public StretchableLayoutResizerBar::LookAndFeelMethods, + public ExtraLookAndFeelBaseClasses::KeyMappingEditorComponentMethods, + public ExtraLookAndFeelBaseClasses::AudioDeviceSelectorComponentMethods, + public ExtraLookAndFeelBaseClasses::LassoComponentMethods { public: //============================================================================== @@ -60,7 +126,6 @@ public: */ static void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel) noexcept; - //============================================================================== /** Looks for a colour that has been registered with the given colour ID number. @@ -82,9 +147,7 @@ public: Colour findColour (int colourId) const noexcept; /** Registers a colour to be used for a particular purpose. - For more details, see the comments for findColour(). - @see findColour, Component::findColour, Component::setColour */ void setColour (int colourId, Colour colour) noexcept; @@ -94,9 +157,12 @@ public: */ bool isColourSpecified (int colourId) const noexcept; - //============================================================================== - virtual Typeface::Ptr getTypefaceForFont (const Font& font); + /** Returns the typeface that should be used for a given font. + The default implementation just does what you'd expect it to, but you can override + this if you want to intercept fonts and use your own custom typeface object. + */ + virtual Typeface::Ptr getTypefaceForFont (const Font&); /** Allows you to change the default sans-serif font. @@ -110,509 +176,38 @@ public: /** Override this to get the chance to swap a component's mouse cursor for a customised one. */ - virtual MouseCursor getMouseCursorFor (Component& component); + virtual MouseCursor getMouseCursorFor (Component&); //============================================================================== - // Creates a new graphics context object. + /** Creates a new graphics context object. */ virtual LowLevelGraphicsContext* createGraphicsContext (const Image& imageToRenderOn, const Point& origin, const RectangleList& initialClip); - //============================================================================== - /** Draws the lozenge-shaped background for a standard button. */ - virtual void drawButtonBackground (Graphics&, - Button& button, - const Colour& backgroundColour, - bool isMouseOverButton, - bool isButtonDown); - - virtual Font getTextButtonFont (TextButton& button); - - /** Draws the text for a TextButton. */ - virtual void drawButtonText (Graphics&, - TextButton& button, - bool isMouseOverButton, - bool isButtonDown); - - /** Draws the contents of a standard ToggleButton. */ - virtual void drawToggleButton (Graphics&, - ToggleButton& button, - bool isMouseOverButton, - bool isButtonDown); - - virtual void changeToggleButtonWidthToFitText (ToggleButton& button); - - virtual void drawTickBox (Graphics&, - Component& component, - float x, float y, float w, float h, - bool ticked, - bool isEnabled, - bool isMouseOverButton, - bool isButtonDown); - - virtual void drawDrawableButton (Graphics&, - DrawableButton& button, - bool isMouseOverButton, - bool isButtonDown); - - //============================================================================== - // AlertWindow handling.. - - virtual AlertWindow* createAlertWindow (const String& title, - const String& message, - const String& button1, - const String& button2, - const String& button3, - AlertWindow::AlertIconType iconType, - int numButtons, - Component* associatedComponent); - - virtual void drawAlertBox (Graphics&, - AlertWindow& alert, - const Rectangle& textArea, - TextLayout& textLayout); - - virtual int getAlertBoxWindowFlags(); - - virtual int getAlertWindowButtonHeight(); - - virtual Font getAlertWindowMessageFont(); - virtual Font getAlertWindowFont(); - void setUsingNativeAlertWindows (bool shouldUseNativeAlerts); bool isUsingNativeAlertWindows(); - /** Draws a progress bar. - - If the progress value is less than 0 or greater than 1.0, this should draw a spinning - bar that fills the whole space (i.e. to say that the app is still busy but the progress - isn't known). It can use the current time as a basis for playing an animation. - - (Used by progress bars in AlertWindow). - */ - virtual void drawProgressBar (Graphics&, ProgressBar& progressBar, - int width, int height, - double progress, const String& textToShow); - - //============================================================================== - // Draws a small image that spins to indicate that something's happening.. - // This method should use the current time to animate itself, so just keep - // repainting it every so often. - virtual void drawSpinningWaitAnimation (Graphics&, const Colour& colour, - int x, int y, int w, int h); - //============================================================================== - virtual bool areScrollbarButtonsVisible(); - - /** Draws one of the buttons on a scrollbar. - - @param g the context to draw into - @param scrollbar the bar itself - @param width the width of the button - @param height the height of the button - @param buttonDirection the direction of the button, where 0 = up, 1 = right, 2 = down, 3 = left - @param isScrollbarVertical true if it's a vertical bar, false if horizontal - @param isMouseOverButton whether the mouse is currently over the button (also true if it's held down) - @param isButtonDown whether the mouse button's held down + /** Draws a small image that spins to indicate that something's happening. + This method should use the current time to animate itself, so just keep + repainting it every so often. */ - virtual void drawScrollbarButton (Graphics& g, - ScrollBar& scrollbar, - int width, int height, - int buttonDirection, - bool isScrollbarVertical, - bool isMouseOverButton, - bool isButtonDown); - - /** Draws the thumb area of a scrollbar. - - @param g the context to draw into - @param scrollbar the bar itself - @param x the x position of the left edge of the thumb area to draw in - @param y the y position of the top edge of the thumb area to draw in - @param width the width of the thumb area to draw in - @param height the height of the thumb area to draw in - @param isScrollbarVertical true if it's a vertical bar, false if horizontal - @param thumbStartPosition for vertical bars, the y coordinate of the top of the - thumb, or its x position for horizontal bars - @param thumbSize for vertical bars, the height of the thumb, or its width for - horizontal bars. This may be 0 if the thumb shouldn't be drawn. - @param isMouseOver whether the mouse is over the thumb area, also true if the mouse is - currently dragging the thumb - @param isMouseDown whether the mouse is currently dragging the scrollbar - */ - virtual void drawScrollbar (Graphics& g, - ScrollBar& scrollbar, - int x, int y, - int width, int height, - bool isScrollbarVertical, - int thumbStartPosition, - int thumbSize, - bool isMouseOver, - bool isMouseDown); - - /** Returns the component effect to use for a scrollbar */ - virtual ImageEffectFilter* getScrollbarEffect(); - - /** Returns the minimum length in pixels to use for a scrollbar thumb. */ - virtual int getMinimumScrollbarThumbSize (ScrollBar& scrollbar); - - /** Returns the default thickness to use for a scrollbar. */ - virtual int getDefaultScrollbarWidth(); - - /** Returns the length in pixels to use for a scrollbar button. */ - virtual int getScrollbarButtonSize (ScrollBar& scrollbar); + virtual void drawSpinningWaitAnimation (Graphics&, const Colour& colour, + int x, int y, int w, int h) = 0; //============================================================================== /** Returns a tick shape for use in yes/no boxes, etc. */ - virtual Path getTickShape (float height); + virtual Path getTickShape (float height) = 0; /** Returns a cross shape for use in yes/no boxes, etc. */ - virtual Path getCrossShape (float height); - - //============================================================================== - /** Draws the + or - box in a treeview. */ - virtual void drawTreeviewPlusMinusBox (Graphics&, int x, int y, int w, int h, bool isPlus, bool isMouseOver); + virtual Path getCrossShape (float height) = 0; //============================================================================== - virtual void fillTextEditorBackground (Graphics&, int width, int height, TextEditor& textEditor); - virtual void drawTextEditorOutline (Graphics&, int width, int height, TextEditor& textEditor); - - virtual CaretComponent* createCaretComponent (Component* keyFocusOwner); - - //============================================================================== - // These return a pointer to an internally cached drawable - make sure you don't keep - // a copy of this pointer anywhere, as it may become invalid in the future. - virtual const Drawable* getDefaultFolderImage(); - virtual const Drawable* getDefaultDocumentFileImage(); - - virtual AttributedString createFileChooserHeaderText (const String& title, - const String& instructions); - - virtual void drawFileBrowserRow (Graphics&, int width, int height, - const String& filename, Image* icon, - const String& fileSizeDescription, - const String& fileTimeDescription, - bool isDirectory, - bool isItemSelected, - int itemIndex, - DirectoryContentsDisplayComponent& component); - - virtual Button* createFileBrowserGoUpButton(); - - virtual void layoutFileBrowserComponent (FileBrowserComponent& browserComp, - DirectoryContentsDisplayComponent* fileListComponent, - FilePreviewComponent* previewComp, - ComboBox* currentPathBox, - TextEditor* filenameBox, - Button* goUpButton); - - //============================================================================== - virtual void drawBubble (Graphics&, BubbleComponent&, - const Point& tip, const Rectangle& body); - - //============================================================================== - virtual void drawLasso (Graphics&, Component& lassoComp); - - //============================================================================== - /** Fills the background of a popup menu component. */ - virtual void drawPopupMenuBackground (Graphics&, int width, int height); - - /** Draws one of the items in a popup menu. */ - virtual void drawPopupMenuItem (Graphics&, - int width, int height, - bool isSeparator, - bool isActive, - bool isHighlighted, - bool isTicked, - bool hasSubMenu, - const String& text, - const String& shortcutKeyText, - Image* image, - const Colour* const textColour); - - /** Returns the size and style of font to use in popup menus. */ - virtual Font getPopupMenuFont(); - - virtual void drawPopupMenuUpDownArrow (Graphics&, - int width, int height, - bool isScrollUpArrow); - - /** Finds the best size for an item in a popup menu. */ - virtual void getIdealPopupMenuItemSize (const String& text, - bool isSeparator, - int standardMenuItemHeight, - int& idealWidth, - int& idealHeight); - - virtual int getMenuWindowFlags(); - - virtual void drawMenuBarBackground (Graphics&, int width, int height, - bool isMouseOverBar, - MenuBarComponent& menuBar); - - virtual int getMenuBarItemWidth (MenuBarComponent& menuBar, int itemIndex, const String& itemText); - - virtual Font getMenuBarFont (MenuBarComponent& menuBar, int itemIndex, const String& itemText); - - virtual void drawMenuBarItem (Graphics&, - int width, int height, - int itemIndex, - const String& itemText, - bool isMouseOverItem, - bool isMenuOpen, - bool isMouseOverBar, - MenuBarComponent& menuBar); - - //============================================================================== - virtual void drawComboBox (Graphics&, int width, int height, - bool isButtonDown, - int buttonX, int buttonY, - int buttonW, int buttonH, - ComboBox& box); - - virtual Font getComboBoxFont (ComboBox& box); - - virtual Label* createComboBoxTextBox (ComboBox& box); - - virtual void positionComboBoxText (ComboBox& box, Label& labelToPosition); + virtual DropShadower* createDropShadowerForComponent (Component*) = 0; //============================================================================== - virtual void drawLabel (Graphics&, Label&); - - virtual Font getLabelFont (Label&); - - //============================================================================== - virtual void drawLinearSlider (Graphics&, - int x, int y, - int width, int height, - float sliderPos, - float minSliderPos, - float maxSliderPos, - const Slider::SliderStyle style, - Slider& slider); - - virtual void drawLinearSliderBackground (Graphics&, - int x, int y, - int width, int height, - float sliderPos, - float minSliderPos, - float maxSliderPos, - const Slider::SliderStyle style, - Slider& slider); - - virtual void drawLinearSliderThumb (Graphics&, - int x, int y, - int width, int height, - float sliderPos, - float minSliderPos, - float maxSliderPos, - const Slider::SliderStyle style, - Slider& slider); - - virtual int getSliderThumbRadius (Slider& slider); - - virtual void drawRotarySlider (Graphics&, - int x, int y, - int width, int height, - float sliderPosProportional, - float rotaryStartAngle, - float rotaryEndAngle, - Slider& slider); - - virtual Button* createSliderButton (bool isIncrement); - virtual Label* createSliderTextBox (Slider& slider); - - virtual ImageEffectFilter* getSliderEffect(); - - virtual Font getSliderPopupFont(); - virtual int getSliderPopupPlacement(); - - //============================================================================== - virtual void getTooltipSize (const String& tipText, int& width, int& height); - - virtual void drawTooltip (Graphics&, const String& text, int width, int height); - - //============================================================================== - virtual Button* createFilenameComponentBrowseButton (const String& text); - - virtual void layoutFilenameComponent (FilenameComponent& filenameComp, - ComboBox* filenameBox, Button* browseButton); - - //============================================================================== - virtual void drawConcertinaPanelHeader (Graphics&, const Rectangle& area, - bool isMouseOver, bool isMouseDown, - ConcertinaPanel&, Component& panel); - - //============================================================================== - virtual void drawCornerResizer (Graphics&, - int w, int h, - bool isMouseOver, - bool isMouseDragging); - - virtual void drawResizableFrame (Graphics&, - int w, int h, - const BorderSize&); - - //============================================================================== - virtual void fillResizableWindowBackground (Graphics&, int w, int h, - const BorderSize&, - ResizableWindow& window); - - virtual void drawResizableWindowBorder (Graphics&, - int w, int h, - const BorderSize& border, - ResizableWindow& window); - - //============================================================================== - virtual void drawDocumentWindowTitleBar (DocumentWindow& window, - Graphics&, int w, int h, - int titleSpaceX, int titleSpaceW, - const Image* icon, - bool drawTitleTextOnLeft); - - virtual Button* createDocumentWindowButton (int buttonType); - - virtual void positionDocumentWindowButtons (DocumentWindow& window, - int titleBarX, int titleBarY, - int titleBarW, int titleBarH, - Button* minimiseButton, - Button* maximiseButton, - Button* closeButton, - bool positionTitleBarButtonsOnLeft); - - virtual int getDefaultMenuBarHeight(); - - //============================================================================== - virtual DropShadower* createDropShadowerForComponent (Component* component); - - //============================================================================== - virtual void drawStretchableLayoutResizerBar (Graphics&, - int w, int h, - bool isVerticalBar, - bool isMouseOver, - bool isMouseDragging); - - //============================================================================== - virtual void drawGroupComponentOutline (Graphics&, int w, int h, - const String& text, - const Justification& position, - GroupComponent& group); - - //============================================================================== - virtual int getTabButtonSpaceAroundImage(); - virtual int getTabButtonOverlap (int tabDepth); - virtual int getTabButtonBestWidth (TabBarButton&, int tabDepth); - virtual Rectangle getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle& textArea, Component& extraComp); - - virtual void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown); - virtual void drawTabButtonText (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown); - virtual void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&); - virtual void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int w, int h); - - virtual void createTabButtonShape (TabBarButton&, Path& path, bool isMouseOver, bool isMouseDown); - virtual void fillTabButtonShape (TabBarButton&, Graphics&, const Path& path, bool isMouseOver, bool isMouseDown); - - virtual Button* createTabBarExtrasButton(); - - //============================================================================== - virtual void drawImageButton (Graphics&, Image* image, - int imageX, int imageY, int imageW, int imageH, - const Colour& overlayColour, - float imageOpacity, - ImageButton& button); - - //============================================================================== - virtual void drawTableHeaderBackground (Graphics&, TableHeaderComponent&); - - virtual void drawTableHeaderColumn (Graphics&, const String& columnName, int columnId, - int width, int height, - bool isMouseOver, bool isMouseDown, - int columnFlags); - - //============================================================================== - virtual void paintToolbarBackground (Graphics&, int width, int height, Toolbar& toolbar); - - virtual Button* createToolbarMissingItemsButton (Toolbar& toolbar); - - virtual void paintToolbarButtonBackground (Graphics&, int width, int height, - bool isMouseOver, bool isMouseDown, - ToolbarItemComponent& component); - - virtual void paintToolbarButtonLabel (Graphics&, int x, int y, int width, int height, - const String& text, ToolbarItemComponent& component); - - //============================================================================== - virtual void drawPropertyPanelSectionHeader (Graphics&, const String& name, - bool isOpen, int width, int height); - - virtual void drawPropertyComponentBackground (Graphics&, int width, int height, - PropertyComponent& component); - - virtual void drawPropertyComponentLabel (Graphics&, int width, int height, - PropertyComponent& component); - - virtual Rectangle getPropertyComponentContentPosition (PropertyComponent& component); - - //============================================================================== - virtual void drawCallOutBoxBackground (CallOutBox& box, Graphics&, const Path& path, Image& cachedImage); - - //============================================================================== - virtual void drawLevelMeter (Graphics&, int width, int height, float level); - - virtual void drawKeymapChangeButton (Graphics&, int width, int height, Button& button, const String& keyDescription); - - //============================================================================== - /** Plays the system's default 'beep' noise, to alert the user about something very important. - */ + /** Plays the system's default 'beep' noise, to alert the user about something very important. */ virtual void playAlertSound(); - //============================================================================== - /** Draws a 3D raised (or indented) bevel using two colours. - - The bevel is drawn inside the given rectangle, and greater bevel thicknesses - extend inwards. - - The top-left colour is used for the top- and left-hand edges of the - bevel; the bottom-right colour is used for the bottom- and right-hand - edges. - - If useGradient is true, then the bevel fades out to make it look more curved - and less angular. If sharpEdgeOnOutside is true, the outside of the bevel is - sharp, and it fades towards the centre; if sharpEdgeOnOutside is false, then - the centre edges are sharp and it fades towards the outside. - */ - static void drawBevel (Graphics&, - int x, int y, int width, int height, - int bevelThickness, - const Colour& topLeftColour = Colours::white, - const Colour& bottomRightColour = Colours::black, - bool useGradient = true, - bool sharpEdgeOnOutside = true); - - /** Utility function to draw a shiny, glassy circle (for round LED-type buttons). */ - static void drawGlassSphere (Graphics&, - float x, float y, - float diameter, - const Colour& colour, - float outlineThickness) noexcept; - - static void drawGlassPointer (Graphics&, - float x, float y, - float diameter, - const Colour& colour, float outlineThickness, - int direction) noexcept; - - /** Utility function to draw a shiny, glassy oblong (for text buttons). */ - static void drawGlassLozenge (Graphics&, - float x, float y, - float width, float height, - const Colour& colour, - float outlineThickness, - float cornerSize, - bool flatOnLeft, bool flatOnRight, - bool flatOnTop, bool flatOnBottom) noexcept; - - static Drawable* loadDrawableFromData (const void* data, size_t numBytes); - private: //============================================================================== friend class WeakReference; @@ -628,40 +223,9 @@ private: }; SortedSet colours; - - // default typeface names String defaultSans, defaultSerif, defaultFixed; - - ScopedPointer folderImage, documentImage; - bool useNativeAlertWindows; - void drawShinyButtonShape (Graphics&, - float x, float y, float w, float h, float maxCornerSize, - const Colour& baseColour, - float strokeWidth, - bool flatOnLeft, - bool flatOnRight, - bool flatOnTop, - bool flatOnBottom) noexcept; - - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // These methods have been deprecated - see their new parameter lists.. - virtual int drawFileBrowserRow (Graphics&, int, int, const String&, Image*, const String&, const String&, bool, bool, int) { return 0; } - virtual int drawTabButton (Graphics&, int, int, const Colour&, int, const String&, Button&, TabbedButtonBar::Orientation, bool, bool, bool) { return 0; } - virtual int createTabButtonShape (Path&, int, int, int, const String&, Button&, TabbedButtonBar::Orientation, bool, bool, bool) { return 0; } - virtual int fillTabButtonShape (Graphics&, const Path&, const Colour&, int, const String&, Button&, TabbedButtonBar::Orientation, bool, bool, bool) { return 0; } - virtual int drawTabAreaBehindFrontButton (Graphics&, int, int, TabbedButtonBar&, TabbedButtonBar::Orientation) { return 0; } - virtual int drawTabButtonText (Graphics&, int, int, int, int, const Colour&, int, const String&, Button&, TabbedButtonBar::Orientation, bool, bool, bool) { return 0; } - virtual int getTabButtonBestWidth (int, const String&, int, Button&) { return 0; } - virtual int drawBubble (Graphics&, float, float, float, float, float, float) { return 0; } - virtual int getFontForTextButton (TextButton&) { return 0; } - virtual int createFileChooserHeaderText (const String&, const String&, GlyphArrangement&, int) { return 0; } - #endif - - class GlassWindowButton; - class SliderLabelComp; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeel) }; diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.cpp b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.cpp new file mode 100644 index 000000000..d93b859e3 --- /dev/null +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.cpp @@ -0,0 +1,567 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +LookAndFeel_V1::LookAndFeel_V1() +{ + setColour (TextButton::buttonColourId, Colour (0xffbbbbff)); + setColour (ListBox::outlineColourId, findColour (ComboBox::outlineColourId)); + setColour (ScrollBar::thumbColourId, Colour (0xffbbbbdd)); + setColour (ScrollBar::backgroundColourId, Colours::transparentBlack); + setColour (Slider::thumbColourId, Colours::white); + setColour (Slider::trackColourId, Colour (0x7f000000)); + setColour (Slider::textBoxOutlineColourId, Colours::grey); + setColour (ProgressBar::backgroundColourId, Colours::white.withAlpha (0.6f)); + setColour (ProgressBar::foregroundColourId, Colours::green.withAlpha (0.7f)); + setColour (PopupMenu::backgroundColourId, Colour (0xffeef5f8)); + setColour (PopupMenu::highlightedBackgroundColourId, Colour (0xbfa4c2ce)); + setColour (PopupMenu::highlightedTextColourId, Colours::black); + setColour (TextEditor::focusedOutlineColourId, findColour (TextButton::buttonColourId)); + + scrollbarShadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 2, Point())); +} + +LookAndFeel_V1::~LookAndFeel_V1() +{ +} + +//============================================================================== +void LookAndFeel_V1::drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour, + bool isMouseOverButton, bool isButtonDown) +{ + const int width = button.getWidth(); + const int height = button.getHeight(); + + const float indent = 2.0f; + const int cornerSize = jmin (roundToInt (width * 0.4f), + roundToInt (height * 0.4f)); + + Path p; + p.addRoundedRectangle (indent, indent, + width - indent * 2.0f, + height - indent * 2.0f, + (float) cornerSize); + + Colour bc (backgroundColour.withMultipliedSaturation (0.3f)); + + if (isMouseOverButton) + { + if (isButtonDown) + bc = bc.brighter(); + else if (bc.getBrightness() > 0.5f) + bc = bc.darker (0.1f); + else + bc = bc.brighter (0.1f); + } + + g.setColour (bc); + g.fillPath (p); + + g.setColour (bc.contrasting().withAlpha ((isMouseOverButton) ? 0.6f : 0.4f)); + g.strokePath (p, PathStrokeType ((isMouseOverButton) ? 2.0f : 1.4f)); +} + +void LookAndFeel_V1::drawTickBox (Graphics& g, Component& /*component*/, + float x, float y, float w, float h, + const bool ticked, + const bool isEnabled, + const bool /*isMouseOverButton*/, + const bool isButtonDown) +{ + Path box; + box.addRoundedRectangle (0.0f, 2.0f, 6.0f, 6.0f, 1.0f); + + g.setColour (isEnabled ? Colours::blue.withAlpha (isButtonDown ? 0.3f : 0.1f) + : Colours::lightgrey.withAlpha (0.1f)); + + AffineTransform trans (AffineTransform::scale (w / 9.0f, h / 9.0f).translated (x, y)); + + g.fillPath (box, trans); + + g.setColour (Colours::black.withAlpha (0.6f)); + g.strokePath (box, PathStrokeType (0.9f), trans); + + if (ticked) + { + Path tick; + tick.startNewSubPath (1.5f, 3.0f); + tick.lineTo (3.0f, 6.0f); + tick.lineTo (6.0f, 0.0f); + + g.setColour (isEnabled ? Colours::black : Colours::grey); + g.strokePath (tick, PathStrokeType (2.5f), trans); + } +} + +void LookAndFeel_V1::drawToggleButton (Graphics& g, ToggleButton& button, bool isMouseOverButton, bool isButtonDown) +{ + if (button.hasKeyboardFocus (true)) + { + g.setColour (button.findColour (TextEditor::focusedOutlineColourId)); + g.drawRect (0, 0, button.getWidth(), button.getHeight()); + } + + const int tickWidth = jmin (20, button.getHeight() - 4); + + drawTickBox (g, button, 4.0f, (button.getHeight() - tickWidth) * 0.5f, + (float) tickWidth, (float) tickWidth, + button.getToggleState(), + button.isEnabled(), + isMouseOverButton, + isButtonDown); + + g.setColour (button.findColour (ToggleButton::textColourId)); + g.setFont (jmin (15.0f, button.getHeight() * 0.6f)); + + if (! button.isEnabled()) + g.setOpacity (0.5f); + + const int textX = tickWidth + 5; + + g.drawFittedText (button.getButtonText(), + textX, 4, + button.getWidth() - textX - 2, button.getHeight() - 8, + Justification::centredLeft, 10); +} + +void LookAndFeel_V1::drawProgressBar (Graphics& g, ProgressBar& progressBar, + int width, int height, + double progress, const String& textToShow) +{ + if (progress < 0 || progress >= 1.0) + { + LookAndFeel_V2::drawProgressBar (g, progressBar, width, height, progress, textToShow); + } + else + { + const Colour background (progressBar.findColour (ProgressBar::backgroundColourId)); + const Colour foreground (progressBar.findColour (ProgressBar::foregroundColourId)); + + g.fillAll (background); + g.setColour (foreground); + + g.fillRect (1, 1, + jlimit (0, width - 2, roundToInt (progress * (width - 2))), + height - 2); + + if (textToShow.isNotEmpty()) + { + g.setColour (Colour::contrasting (background, foreground)); + g.setFont (height * 0.6f); + + g.drawText (textToShow, 0, 0, width, height, Justification::centred, false); + } + } +} + +void LookAndFeel_V1::drawScrollbarButton (Graphics& g, ScrollBar& bar, + int width, int height, int buttonDirection, + bool isScrollbarVertical, + bool isMouseOverButton, + bool isButtonDown) +{ + if (isScrollbarVertical) + width -= 2; + else + height -= 2; + + Path p; + + if (buttonDirection == 0) + p.addTriangle (width * 0.5f, height * 0.2f, + width * 0.1f, height * 0.7f, + width * 0.9f, height * 0.7f); + else if (buttonDirection == 1) + p.addTriangle (width * 0.8f, height * 0.5f, + width * 0.3f, height * 0.1f, + width * 0.3f, height * 0.9f); + else if (buttonDirection == 2) + p.addTriangle (width * 0.5f, height * 0.8f, + width * 0.1f, height * 0.3f, + width * 0.9f, height * 0.3f); + else if (buttonDirection == 3) + p.addTriangle (width * 0.2f, height * 0.5f, + width * 0.7f, height * 0.1f, + width * 0.7f, height * 0.9f); + + if (isButtonDown) + g.setColour (Colours::white); + else if (isMouseOverButton) + g.setColour (Colours::white.withAlpha (0.7f)); + else + g.setColour (bar.findColour (ScrollBar::thumbColourId).withAlpha (0.5f)); + + g.fillPath (p); + + g.setColour (Colours::black.withAlpha (0.5f)); + g.strokePath (p, PathStrokeType (0.5f)); +} + +void LookAndFeel_V1::drawScrollbar (Graphics& g, ScrollBar& bar, + int x, int y, int width, int height, + bool isScrollbarVertical, int thumbStartPosition, int thumbSize, + bool isMouseOver, bool isMouseDown) +{ + g.fillAll (bar.findColour (ScrollBar::backgroundColourId)); + + g.setColour (bar.findColour (ScrollBar::thumbColourId) + .withAlpha ((isMouseOver || isMouseDown) ? 0.4f : 0.15f)); + + if (thumbSize > 0.0f) + { + Rectangle thumb; + + if (isScrollbarVertical) + { + width -= 2; + g.fillRect (x + roundToInt (width * 0.35f), y, + roundToInt (width * 0.3f), height); + + thumb.setBounds (x + 1, thumbStartPosition, + width - 2, thumbSize); + } + else + { + height -= 2; + g.fillRect (x, y + roundToInt (height * 0.35f), + width, roundToInt (height * 0.3f)); + + thumb.setBounds (thumbStartPosition, y + 1, + thumbSize, height - 2); + } + + g.setColour (bar.findColour (ScrollBar::thumbColourId) + .withAlpha ((isMouseOver || isMouseDown) ? 0.95f : 0.7f)); + + g.fillRect (thumb); + + g.setColour (Colours::black.withAlpha ((isMouseOver || isMouseDown) ? 0.4f : 0.25f)); + g.drawRect (thumb.getX(), thumb.getY(), thumb.getWidth(), thumb.getHeight()); + + if (thumbSize > 16) + { + for (int i = 3; --i >= 0;) + { + const float linePos = thumbStartPosition + thumbSize / 2 + (i - 1) * 4.0f; + g.setColour (Colours::black.withAlpha (0.15f)); + + if (isScrollbarVertical) + { + g.drawLine (x + width * 0.2f, linePos, width * 0.8f, linePos); + g.setColour (Colours::white.withAlpha (0.15f)); + g.drawLine (width * 0.2f, linePos - 1, width * 0.8f, linePos - 1); + } + else + { + g.drawLine (linePos, height * 0.2f, linePos, height * 0.8f); + g.setColour (Colours::white.withAlpha (0.15f)); + g.drawLine (linePos - 1, height * 0.2f, linePos - 1, height * 0.8f); + } + } + } + } +} + +ImageEffectFilter* LookAndFeel_V1::getScrollbarEffect() +{ + return &scrollbarShadow; +} + + +//============================================================================== +void LookAndFeel_V1::drawPopupMenuBackground (Graphics& g, int width, int height) +{ + g.fillAll (findColour (PopupMenu::backgroundColourId)); + + g.setColour (Colours::black.withAlpha (0.6f)); + g.drawRect (0, 0, width, height); +} + +void LookAndFeel_V1::drawMenuBarBackground (Graphics& g, int /*width*/, int /*height*/, bool, MenuBarComponent& menuBar) +{ + g.fillAll (menuBar.findColour (PopupMenu::backgroundColourId)); +} + + +//============================================================================== +void LookAndFeel_V1::drawTextEditorOutline (Graphics& g, int width, int height, TextEditor& textEditor) +{ + if (textEditor.isEnabled()) + { + g.setColour (textEditor.findColour (TextEditor::outlineColourId)); + g.drawRect (0, 0, width, height); + } +} + +//============================================================================== +void LookAndFeel_V1::drawComboBox (Graphics& g, int width, int height, + const bool isButtonDown, + int buttonX, int buttonY, int buttonW, int buttonH, + ComboBox& box) +{ + g.fillAll (box.findColour (ComboBox::backgroundColourId)); + + g.setColour (box.findColour ((isButtonDown) ? ComboBox::buttonColourId + : ComboBox::backgroundColourId)); + g.fillRect (buttonX, buttonY, buttonW, buttonH); + + g.setColour (box.findColour (ComboBox::outlineColourId)); + g.drawRect (0, 0, width, height); + + const float arrowX = 0.2f; + const float arrowH = 0.3f; + + if (box.isEnabled()) + { + Path p; + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.45f - arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.45f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.45f); + + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.55f + arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.55f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.55f); + + g.setColour (box.findColour ((isButtonDown) ? ComboBox::backgroundColourId + : ComboBox::buttonColourId)); + g.fillPath (p); + } +} + +Font LookAndFeel_V1::getComboBoxFont (ComboBox& box) +{ + Font f (jmin (15.0f, box.getHeight() * 0.85f)); + f.setHorizontalScale (0.9f); + return f; +} + +//============================================================================== +static void drawTriangle (Graphics& g, float x1, float y1, float x2, float y2, float x3, float y3, Colour fill, Colour outline) +{ + Path p; + p.addTriangle (x1, y1, x2, y2, x3, y3); + g.setColour (fill); + g.fillPath (p); + + g.setColour (outline); + g.strokePath (p, PathStrokeType (0.3f)); +} + +void LookAndFeel_V1::drawLinearSlider (Graphics& g, + int x, int y, int w, int h, + float sliderPos, float minSliderPos, float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider) +{ + g.fillAll (slider.findColour (Slider::backgroundColourId)); + + if (style == Slider::LinearBar) + { + g.setColour (slider.findColour (Slider::thumbColourId)); + g.fillRect (x, y, (int) sliderPos - x, h); + + g.setColour (slider.findColour (Slider::textBoxTextColourId).withMultipliedAlpha (0.5f)); + g.drawRect (x, y, (int) sliderPos - x, h); + } + else + { + g.setColour (slider.findColour (Slider::trackColourId) + .withMultipliedAlpha (slider.isEnabled() ? 1.0f : 0.3f)); + + if (slider.isHorizontal()) + { + g.fillRect (x, y + roundToInt (h * 0.6f), + w, roundToInt (h * 0.2f)); + } + else + { + g.fillRect (x + roundToInt (w * 0.5f - jmin (3.0f, w * 0.1f)), y, + jmin (4, roundToInt (w * 0.2f)), h); + } + + float alpha = 0.35f; + + if (slider.isEnabled()) + alpha = slider.isMouseOverOrDragging() ? 1.0f : 0.7f; + + const Colour fill (slider.findColour (Slider::thumbColourId).withAlpha (alpha)); + const Colour outline (Colours::black.withAlpha (slider.isEnabled() ? 0.7f : 0.35f)); + + if (style == Slider::TwoValueVertical || style == Slider::ThreeValueVertical) + { + drawTriangle (g, x + w * 0.5f + jmin (4.0f, w * 0.3f), minSliderPos, + x + w * 0.5f - jmin (8.0f, w * 0.4f), minSliderPos - 7.0f, + x + w * 0.5f - jmin (8.0f, w * 0.4f), minSliderPos, + fill, outline); + + drawTriangle (g, x + w * 0.5f + jmin (4.0f, w * 0.3f), maxSliderPos, + x + w * 0.5f - jmin (8.0f, w * 0.4f), maxSliderPos, + x + w * 0.5f - jmin (8.0f, w * 0.4f), maxSliderPos + 7.0f, + fill, outline); + } + else if (style == Slider::TwoValueHorizontal || style == Slider::ThreeValueHorizontal) + { + drawTriangle (g, minSliderPos, y + h * 0.6f - jmin (4.0f, h * 0.3f), + minSliderPos - 7.0f, y + h * 0.9f , + minSliderPos, y + h * 0.9f, + fill, outline); + + drawTriangle (g, maxSliderPos, y + h * 0.6f - jmin (4.0f, h * 0.3f), + maxSliderPos, y + h * 0.9f, + maxSliderPos + 7.0f, y + h * 0.9f, + fill, outline); + } + + if (style == Slider::LinearHorizontal || style == Slider::ThreeValueHorizontal) + { + drawTriangle (g, sliderPos, y + h * 0.9f, + sliderPos - 7.0f, y + h * 0.2f, + sliderPos + 7.0f, y + h * 0.2f, + fill, outline); + } + else if (style == Slider::LinearVertical || style == Slider::ThreeValueVertical) + { + drawTriangle (g, x + w * 0.5f - jmin (4.0f, w * 0.3f), sliderPos, + x + w * 0.5f + jmin (8.0f, w * 0.4f), sliderPos - 7.0f, + x + w * 0.5f + jmin (8.0f, w * 0.4f), sliderPos + 7.0f, + fill, outline); + } + } +} + +Button* LookAndFeel_V1::createSliderButton (const bool isIncrement) +{ + if (isIncrement) + return new ArrowButton ("u", 0.75f, Colours::white.withAlpha (0.8f)); + else + return new ArrowButton ("d", 0.25f, Colours::white.withAlpha (0.8f)); +} + +ImageEffectFilter* LookAndFeel_V1::getSliderEffect() +{ + return &scrollbarShadow; +} + +int LookAndFeel_V1::getSliderThumbRadius (Slider&) +{ + return 8; +} + +//============================================================================== +void LookAndFeel_V1::drawCornerResizer (Graphics& g, int w, int h, bool isMouseOver, bool isMouseDragging) +{ + g.setColour ((isMouseOver || isMouseDragging) ? Colours::lightgrey + : Colours::darkgrey); + + const float lineThickness = jmin (w, h) * 0.1f; + + for (float i = 0.0f; i < 1.0f; i += 0.3f) + { + g.drawLine (w * i, + h + 1.0f, + w + 1.0f, + h * i, + lineThickness); + } +} + +//============================================================================== +Button* LookAndFeel_V1::createDocumentWindowButton (int buttonType) +{ + Path shape; + + if (buttonType == DocumentWindow::closeButton) + { + shape.addLineSegment (Line (0.0f, 0.0f, 1.0f, 1.0f), 0.35f); + shape.addLineSegment (Line (1.0f, 0.0f, 0.0f, 1.0f), 0.35f); + + ShapeButton* const b = new ShapeButton ("close", + Colour (0x7fff3333), + Colour (0xd7ff3333), + Colour (0xf7ff3333)); + + b->setShape (shape, true, true, true); + return b; + } + else if (buttonType == DocumentWindow::minimiseButton) + { + shape.addLineSegment (Line (0.0f, 0.5f, 1.0f, 0.5f), 0.25f); + + DrawableButton* b = new DrawableButton ("minimise", DrawableButton::ImageFitted); + DrawablePath dp; + dp.setPath (shape); + dp.setFill (Colours::black.withAlpha (0.3f)); + b->setImages (&dp); + return b; + } + else if (buttonType == DocumentWindow::maximiseButton) + { + shape.addLineSegment (Line (0.5f, 0.0f, 0.5f, 1.0f), 0.25f); + shape.addLineSegment (Line (0.0f, 0.5f, 1.0f, 0.5f), 0.25f); + + DrawableButton* b = new DrawableButton ("maximise", DrawableButton::ImageFitted); + DrawablePath dp; + dp.setPath (shape); + dp.setFill (Colours::black.withAlpha (0.3f)); + b->setImages (&dp); + return b; + } + + jassertfalse; + return nullptr; +} + +void LookAndFeel_V1::positionDocumentWindowButtons (DocumentWindow&, + int titleBarX, int titleBarY, int titleBarW, int titleBarH, + Button* minimiseButton, + Button* maximiseButton, + Button* closeButton, + bool positionTitleBarButtonsOnLeft) +{ + titleBarY += titleBarH / 8; + titleBarH -= titleBarH / 4; + + const int buttonW = titleBarH; + + int x = positionTitleBarButtonsOnLeft ? titleBarX + 4 + : titleBarX + titleBarW - buttonW - 4; + + if (closeButton != nullptr) + { + closeButton->setBounds (x, titleBarY, buttonW, titleBarH); + x += positionTitleBarButtonsOnLeft ? buttonW + buttonW / 5 + : -(buttonW + buttonW / 5); + } + + if (positionTitleBarButtonsOnLeft) + std::swap (minimiseButton, maximiseButton); + + if (maximiseButton != nullptr) + { + maximiseButton->setBounds (x, titleBarY - 2, buttonW, titleBarH); + x += positionTitleBarButtonsOnLeft ? buttonW : -buttonW; + } + + if (minimiseButton != nullptr) + minimiseButton->setBounds (x, titleBarY - 2, buttonW, titleBarH); +} diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h new file mode 100644 index 000000000..41261d89d --- /dev/null +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h @@ -0,0 +1,101 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_LOOKANDFEEL_V1_H_INCLUDED +#define JUCE_LOOKANDFEEL_V1_H_INCLUDED + +//============================================================================== +/** + The original JUCE look-and-feel, as used back from 2002 to about 2007ish. + @see LookAndFeel, LookAndFeel_V2, LookAndFeel_V3 +*/ +class JUCE_API LookAndFeel_V1 : public LookAndFeel_V2 +{ +public: + LookAndFeel_V1(); + ~LookAndFeel_V1(); + + //============================================================================== + void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour, + bool isMouseOverButton, bool isButtonDown) override; + + void drawToggleButton (Graphics&, ToggleButton&, bool isMouseOverButton, bool isButtonDown) override; + + void drawTickBox (Graphics&, Component&, float x, float y, float w, float h, + bool ticked, bool isEnabled, bool isMouseOverButton, bool isButtonDown) override; + + void drawProgressBar (Graphics&, ProgressBar&, int width, int height, + double progress, const String& textToShow) override; + + //============================================================================== + void drawScrollbarButton (Graphics&, ScrollBar&, int width, int height, + int buttonDirection, bool isScrollbarVertical, + bool isMouseOverButton, bool isButtonDown) override; + + void drawScrollbar (Graphics&, ScrollBar&, int x, int y, int width, int height, + bool isScrollbarVertical, int thumbStartPosition, int thumbSize, + bool isMouseOver, bool isMouseDown) override; + + ImageEffectFilter* getScrollbarEffect() override; + + //============================================================================== + void drawTextEditorOutline (Graphics&, int width, int height, TextEditor&) override; + + //============================================================================== + void drawPopupMenuBackground (Graphics&, int width, int height) override; + void drawMenuBarBackground (Graphics&, int width, int height, bool isMouseOverBar, MenuBarComponent&) override; + + //============================================================================== + void drawComboBox (Graphics&, int width, int height, bool isButtonDown, + int buttonX, int buttonY, int buttonW, int buttonH, ComboBox&) override; + + Font getComboBoxFont (ComboBox&) override; + + //============================================================================== + void drawLinearSlider (Graphics&, int x, int y, int width, int height, + float sliderPos, float minSliderPos, float maxSliderPos, + const Slider::SliderStyle, Slider&) override; + + int getSliderThumbRadius (Slider&) override; + Button* createSliderButton (bool isIncrement) override; + ImageEffectFilter* getSliderEffect() override; + + //============================================================================== + void drawCornerResizer (Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) override; + + Button* createDocumentWindowButton (int buttonType) override; + + void positionDocumentWindowButtons (DocumentWindow&, + int titleBarX, int titleBarY, int titleBarW, int titleBarH, + Button* minimiseButton, Button* maximiseButton, Button* closeButton, + bool positionTitleBarButtonsOnLeft) override; + +private: + DropShadowEffect scrollbarShadow; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeel_V1) +}; + + +#endif // JUCE_LOOKANDFEEL_H_INCLUDED diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp new file mode 100644 index 000000000..0afbea328 --- /dev/null +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -0,0 +1,2947 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +namespace LookAndFeelHelpers +{ + static Colour createBaseColour (Colour buttonColour, + bool hasKeyboardFocus, + bool isMouseOverButton, + bool isButtonDown) noexcept + { + const float sat = hasKeyboardFocus ? 1.3f : 0.9f; + const Colour baseColour (buttonColour.withMultipliedSaturation (sat)); + + if (isButtonDown) return baseColour.contrasting (0.2f); + if (isMouseOverButton) return baseColour.contrasting (0.1f); + + return baseColour; + } + + static TextLayout layoutTooltipText (const String& text, Colour colour) noexcept + { + const float tooltipFontSize = 13.0f; + const int maxToolTipWidth = 400; + + AttributedString s; + s.setJustification (Justification::centred); + s.append (text, Font (tooltipFontSize, Font::bold), colour); + + TextLayout tl; + tl.createLayoutWithBalancedLineLengths (s, (float) maxToolTipWidth); + return tl; + } +} + +//============================================================================== +LookAndFeel_V2::LookAndFeel_V2() +{ + // initialise the standard set of colours.. + const uint32 textButtonColour = 0xffbbbbff; + const uint32 textHighlightColour = 0x401111ee; + const uint32 standardOutlineColour = 0xb2808080; + + static const uint32 standardColours[] = + { + TextButton::buttonColourId, textButtonColour, + TextButton::buttonOnColourId, 0xff4444ff, + TextButton::textColourOnId, 0xff000000, + TextButton::textColourOffId, 0xff000000, + + ToggleButton::textColourId, 0xff000000, + + TextEditor::backgroundColourId, 0xffffffff, + TextEditor::textColourId, 0xff000000, + TextEditor::highlightColourId, textHighlightColour, + TextEditor::highlightedTextColourId, 0xff000000, + TextEditor::outlineColourId, 0x00000000, + TextEditor::focusedOutlineColourId, textButtonColour, + TextEditor::shadowColourId, 0x38000000, + + CaretComponent::caretColourId, 0xff000000, + + Label::backgroundColourId, 0x00000000, + Label::textColourId, 0xff000000, + Label::outlineColourId, 0x00000000, + + ScrollBar::backgroundColourId, 0x00000000, + ScrollBar::thumbColourId, 0xffffffff, + + TreeView::linesColourId, 0x4c000000, + TreeView::backgroundColourId, 0x00000000, + TreeView::dragAndDropIndicatorColourId, 0x80ff0000, + TreeView::selectedItemBackgroundColourId, 0x00000000, + + PopupMenu::backgroundColourId, 0xffffffff, + PopupMenu::textColourId, 0xff000000, + PopupMenu::headerTextColourId, 0xff000000, + PopupMenu::highlightedTextColourId, 0xffffffff, + PopupMenu::highlightedBackgroundColourId, 0x991111aa, + + ComboBox::buttonColourId, 0xffbbbbff, + ComboBox::outlineColourId, standardOutlineColour, + ComboBox::textColourId, 0xff000000, + ComboBox::backgroundColourId, 0xffffffff, + ComboBox::arrowColourId, 0x99000000, + + TextPropertyComponent::backgroundColourId, 0xffffffff, + TextPropertyComponent::textColourId, 0xff000000, + TextPropertyComponent::outlineColourId, standardOutlineColour, + + ListBox::backgroundColourId, 0xffffffff, + ListBox::outlineColourId, standardOutlineColour, + ListBox::textColourId, 0xff000000, + + Slider::backgroundColourId, 0x00000000, + Slider::thumbColourId, textButtonColour, + Slider::trackColourId, 0x7fffffff, + Slider::rotarySliderFillColourId, 0x7f0000ff, + Slider::rotarySliderOutlineColourId, 0x66000000, + Slider::textBoxTextColourId, 0xff000000, + Slider::textBoxBackgroundColourId, 0xffffffff, + Slider::textBoxHighlightColourId, textHighlightColour, + Slider::textBoxOutlineColourId, standardOutlineColour, + + ResizableWindow::backgroundColourId, 0xff777777, + //DocumentWindow::textColourId, 0xff000000, // (this is deliberately not set) + + AlertWindow::backgroundColourId, 0xffededed, + AlertWindow::textColourId, 0xff000000, + AlertWindow::outlineColourId, 0xff666666, + + ProgressBar::backgroundColourId, 0xffeeeeee, + ProgressBar::foregroundColourId, 0xffaaaaee, + + TooltipWindow::backgroundColourId, 0xffeeeebb, + TooltipWindow::textColourId, 0xff000000, + TooltipWindow::outlineColourId, 0x4c000000, + + TabbedComponent::backgroundColourId, 0x00000000, + TabbedComponent::outlineColourId, 0xff777777, + TabbedButtonBar::tabOutlineColourId, 0x80000000, + TabbedButtonBar::frontOutlineColourId, 0x90000000, + + Toolbar::backgroundColourId, 0xfff6f8f9, + Toolbar::separatorColourId, 0x4c000000, + Toolbar::buttonMouseOverBackgroundColourId, 0x4c0000ff, + Toolbar::buttonMouseDownBackgroundColourId, 0x800000ff, + Toolbar::labelTextColourId, 0xff000000, + Toolbar::editingModeOutlineColourId, 0xffff0000, + + DrawableButton::textColourId, 0xff000000, + DrawableButton::textColourOnId, 0xff000000, + DrawableButton::backgroundColourId, 0x00000000, + DrawableButton::backgroundOnColourId, 0xaabbbbff, + + HyperlinkButton::textColourId, 0xcc1111ee, + + GroupComponent::outlineColourId, 0x66000000, + GroupComponent::textColourId, 0xff000000, + + BubbleComponent::backgroundColourId, 0xeeeeeebb, + BubbleComponent::outlineColourId, 0x77000000, + + DirectoryContentsDisplayComponent::highlightColourId, textHighlightColour, + DirectoryContentsDisplayComponent::textColourId, 0xff000000, + + 0x1000440, /*LassoComponent::lassoFillColourId*/ 0x66dddddd, + 0x1000441, /*LassoComponent::lassoOutlineColourId*/ 0x99111111, + + 0x1005000, /*MidiKeyboardComponent::whiteNoteColourId*/ 0xffffffff, + 0x1005001, /*MidiKeyboardComponent::blackNoteColourId*/ 0xff000000, + 0x1005002, /*MidiKeyboardComponent::keySeparatorLineColourId*/ 0x66000000, + 0x1005003, /*MidiKeyboardComponent::mouseOverKeyOverlayColourId*/ 0x80ffff00, + 0x1005004, /*MidiKeyboardComponent::keyDownOverlayColourId*/ 0xffb6b600, + 0x1005005, /*MidiKeyboardComponent::textLabelColourId*/ 0xff000000, + 0x1005006, /*MidiKeyboardComponent::upDownButtonBackgroundColourId*/ 0xffd3d3d3, + 0x1005007, /*MidiKeyboardComponent::upDownButtonArrowColourId*/ 0xff000000, + + 0x1004500, /*CodeEditorComponent::backgroundColourId*/ 0xffffffff, + 0x1004502, /*CodeEditorComponent::highlightColourId*/ textHighlightColour, + 0x1004503, /*CodeEditorComponent::defaultTextColourId*/ 0xff000000, + 0x1004504, /*CodeEditorComponent::lineNumberBackgroundId*/ 0x44999999, + 0x1004505, /*CodeEditorComponent::lineNumberTextId*/ 0x44000000, + + 0x1007000, /*ColourSelector::backgroundColourId*/ 0xffe5e5e5, + 0x1007001, /*ColourSelector::labelTextColourId*/ 0xff000000, + + 0x100ad00, /*KeyMappingEditorComponent::backgroundColourId*/ 0x00000000, + 0x100ad01, /*KeyMappingEditorComponent::textColourId*/ 0xff000000, + + FileSearchPathListComponent::backgroundColourId, 0xffffffff, + + FileChooserDialogBox::titleTextColourId, 0xff000000, + }; + + for (int i = 0; i < numElementsInArray (standardColours); i += 2) + setColour ((int) standardColours [i], Colour ((uint32) standardColours [i + 1])); +} + +LookAndFeel_V2::~LookAndFeel_V2() {} + +//============================================================================== +void LookAndFeel_V2::drawButtonBackground (Graphics& g, + Button& button, + const Colour& backgroundColour, + bool isMouseOverButton, + bool isButtonDown) +{ + const int width = button.getWidth(); + const int height = button.getHeight(); + + const float outlineThickness = button.isEnabled() ? ((isButtonDown || isMouseOverButton) ? 1.2f : 0.7f) : 0.4f; + const float halfThickness = outlineThickness * 0.5f; + + const float indentL = button.isConnectedOnLeft() ? 0.1f : halfThickness; + const float indentR = button.isConnectedOnRight() ? 0.1f : halfThickness; + const float indentT = button.isConnectedOnTop() ? 0.1f : halfThickness; + const float indentB = button.isConnectedOnBottom() ? 0.1f : halfThickness; + + const Colour baseColour (LookAndFeelHelpers::createBaseColour (backgroundColour, + button.hasKeyboardFocus (true), + isMouseOverButton, isButtonDown) + .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f)); + + drawGlassLozenge (g, + indentL, + indentT, + width - indentL - indentR, + height - indentT - indentB, + baseColour, outlineThickness, -1.0f, + button.isConnectedOnLeft(), + button.isConnectedOnRight(), + button.isConnectedOnTop(), + button.isConnectedOnBottom()); +} + +Font LookAndFeel_V2::getTextButtonFont (TextButton& button) +{ + return button.getFont(); +} + +void LookAndFeel_V2::drawButtonText (Graphics& g, TextButton& button, bool /*isMouseOverButton*/, bool /*isButtonDown*/) +{ + Font font (getTextButtonFont (button)); + g.setFont (font); + g.setColour (button.findColour (button.getToggleState() ? TextButton::textColourOnId + : TextButton::textColourOffId) + .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f)); + + const int yIndent = jmin (4, button.proportionOfHeight (0.3f)); + const int cornerSize = jmin (button.getHeight(), button.getWidth()) / 2; + + const int fontHeight = roundToInt (font.getHeight() * 0.6f); + const int leftIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnLeft() ? 4 : 2)); + const int rightIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnRight() ? 4 : 2)); + + g.drawFittedText (button.getButtonText(), + leftIndent, + yIndent, + button.getWidth() - leftIndent - rightIndent, + button.getHeight() - yIndent * 2, + Justification::centred, 2); +} + +void LookAndFeel_V2::drawTickBox (Graphics& g, Component& component, + float x, float y, float w, float h, + const bool ticked, + const bool isEnabled, + const bool isMouseOverButton, + const bool isButtonDown) +{ + const float boxSize = w * 0.7f; + + drawGlassSphere (g, x, y + (h - boxSize) * 0.5f, boxSize, + LookAndFeelHelpers::createBaseColour (component.findColour (TextButton::buttonColourId) + .withMultipliedAlpha (isEnabled ? 1.0f : 0.5f), + true, isMouseOverButton, isButtonDown), + isEnabled ? ((isButtonDown || isMouseOverButton) ? 1.1f : 0.5f) : 0.3f); + + if (ticked) + { + Path tick; + tick.startNewSubPath (1.5f, 3.0f); + tick.lineTo (3.0f, 6.0f); + tick.lineTo (6.0f, 0.0f); + + g.setColour (isEnabled ? Colours::black : Colours::grey); + + const AffineTransform trans (AffineTransform::scale (w / 9.0f, h / 9.0f) + .translated (x, y)); + + g.strokePath (tick, PathStrokeType (2.5f), trans); + } +} + +void LookAndFeel_V2::drawToggleButton (Graphics& g, ToggleButton& button, + bool isMouseOverButton, bool isButtonDown) +{ + if (button.hasKeyboardFocus (true)) + { + g.setColour (button.findColour (TextEditor::focusedOutlineColourId)); + g.drawRect (0, 0, button.getWidth(), button.getHeight()); + } + + float fontSize = jmin (15.0f, button.getHeight() * 0.75f); + const float tickWidth = fontSize * 1.1f; + + drawTickBox (g, button, 4.0f, (button.getHeight() - tickWidth) * 0.5f, + tickWidth, tickWidth, + button.getToggleState(), + button.isEnabled(), + isMouseOverButton, + isButtonDown); + + g.setColour (button.findColour (ToggleButton::textColourId)); + g.setFont (fontSize); + + 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(), + Justification::centredLeft, 10); +} + +void LookAndFeel_V2::changeToggleButtonWidthToFitText (ToggleButton& button) +{ + Font font (jmin (15.0f, button.getHeight() * 0.6f)); + + const int tickWidth = jmin (24, button.getHeight()); + + button.setSize (font.getStringWidth (button.getButtonText()) + tickWidth + 8, + button.getHeight()); +} + +void LookAndFeel_V2::drawDrawableButton (Graphics& g, DrawableButton& button, + bool /*isMouseOverButton*/, bool /*isButtonDown*/) +{ + bool toggleState = button.getToggleState(); + + g.fillAll (button.findColour (toggleState ? DrawableButton::backgroundOnColourId + : DrawableButton::backgroundColourId)); + + const int textH = (button.getStyle() == DrawableButton::ImageAboveTextLabel) + ? jmin (16, button.proportionOfHeight (0.25f)) + : 0; + + if (textH > 0) + { + g.setFont ((float) textH); + + g.setColour (button.findColour (toggleState ? DrawableButton::textColourOnId + : DrawableButton::textColourId) + .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.4f)); + + g.drawFittedText (button.getButtonText(), + 2, button.getHeight() - textH - 1, + button.getWidth() - 4, textH, + Justification::centred, 1); + } +} + +//============================================================================== +AlertWindow* LookAndFeel_V2::createAlertWindow (const String& title, + const String& message, + const String& button1, + const String& button2, + const String& button3, + AlertWindow::AlertIconType iconType, + int numButtons, + Component* associatedComponent) +{ + AlertWindow* aw = new AlertWindow (title, message, iconType, associatedComponent); + + if (numButtons == 1) + { + aw->addButton (button1, 0, + KeyPress (KeyPress::escapeKey), + KeyPress (KeyPress::returnKey)); + } + else + { + const KeyPress button1ShortCut ((int) CharacterFunctions::toLowerCase (button1[0]), 0, 0); + KeyPress button2ShortCut ((int) CharacterFunctions::toLowerCase (button2[0]), 0, 0); + if (button1ShortCut == button2ShortCut) + button2ShortCut = KeyPress(); + + if (numButtons == 2) + { + aw->addButton (button1, 1, KeyPress (KeyPress::returnKey), button1ShortCut); + aw->addButton (button2, 0, KeyPress (KeyPress::escapeKey), button2ShortCut); + } + else if (numButtons == 3) + { + aw->addButton (button1, 1, button1ShortCut); + aw->addButton (button2, 2, button2ShortCut); + aw->addButton (button3, 0, KeyPress (KeyPress::escapeKey)); + } + } + + return aw; +} + +void LookAndFeel_V2::drawAlertBox (Graphics& g, + AlertWindow& alert, + const Rectangle& textArea, + TextLayout& textLayout) +{ + g.fillAll (alert.findColour (AlertWindow::backgroundColourId)); + + int iconSpaceUsed = 0; + + const int iconWidth = 80; + int iconSize = jmin (iconWidth + 50, alert.getHeight() + 20); + + if (alert.containsAnyExtraComponents() || alert.getNumButtons() > 2) + iconSize = jmin (iconSize, textArea.getHeight() + 50); + + const Rectangle iconRect (iconSize / -10, iconSize / -10, + iconSize, iconSize); + + if (alert.getAlertType() != AlertWindow::NoIcon) + { + Path icon; + uint32 colour; + char character; + + if (alert.getAlertType() == AlertWindow::WarningIcon) + { + colour = 0x55ff5555; + character = '!'; + + icon.addTriangle (iconRect.getX() + iconRect.getWidth() * 0.5f, (float) iconRect.getY(), + (float) iconRect.getRight(), (float) iconRect.getBottom(), + (float) iconRect.getX(), (float) iconRect.getBottom()); + + icon = icon.createPathWithRoundedCorners (5.0f); + } + else + { + colour = alert.getAlertType() == AlertWindow::InfoIcon ? (uint32) 0x605555ff : (uint32) 0x40b69900; + character = alert.getAlertType() == AlertWindow::InfoIcon ? 'i' : '?'; + + icon.addEllipse ((float) iconRect.getX(), (float) iconRect.getY(), + (float) iconRect.getWidth(), (float) iconRect.getHeight()); + } + + GlyphArrangement ga; + ga.addFittedText (Font (iconRect.getHeight() * 0.9f, Font::bold), + String::charToString ((juce_wchar) (uint8) character), + (float) iconRect.getX(), (float) iconRect.getY(), + (float) iconRect.getWidth(), (float) iconRect.getHeight(), + Justification::centred, false); + ga.createPath (icon); + + icon.setUsingNonZeroWinding (false); + g.setColour (Colour (colour)); + g.fillPath (icon); + + iconSpaceUsed = iconWidth; + } + + g.setColour (alert.findColour (AlertWindow::textColourId)); + + textLayout.draw (g, Rectangle (textArea.getX() + iconSpaceUsed, + textArea.getY(), + textArea.getWidth() - iconSpaceUsed, + textArea.getHeight()).toFloat()); + + g.setColour (alert.findColour (AlertWindow::outlineColourId)); + g.drawRect (0, 0, alert.getWidth(), alert.getHeight()); +} + +int LookAndFeel_V2::getAlertBoxWindowFlags() +{ + return ComponentPeer::windowAppearsOnTaskbar + | ComponentPeer::windowHasDropShadow; +} + +int LookAndFeel_V2::getAlertWindowButtonHeight() +{ + return 28; +} + +Font LookAndFeel_V2::getAlertWindowMessageFont() +{ + return Font (15.0f); +} + +Font LookAndFeel_V2::getAlertWindowFont() +{ + return Font (12.0f); +} + +//============================================================================== +void LookAndFeel_V2::drawProgressBar (Graphics& g, ProgressBar& progressBar, + int width, int height, + double progress, const String& textToShow) +{ + const Colour background (progressBar.findColour (ProgressBar::backgroundColourId)); + const Colour foreground (progressBar.findColour (ProgressBar::foregroundColourId)); + + g.fillAll (background); + + if (progress >= 0.0f && progress < 1.0f) + { + drawGlassLozenge (g, 1.0f, 1.0f, + (float) jlimit (0.0, width - 2.0, progress * (width - 2.0)), + (float) (height - 2), + foreground, + 0.5f, 0.0f, + true, true, true, true); + } + else + { + // spinning bar.. + g.setColour (foreground); + + const int stripeWidth = height * 2; + const int position = (int) (Time::getMillisecondCounter() / 15) % stripeWidth; + + Path p; + + for (float x = (float) (- position); x < width + stripeWidth; x += stripeWidth) + p.addQuadrilateral (x, 0.0f, + x + stripeWidth * 0.5f, 0.0f, + x, (float) height, + x - stripeWidth * 0.5f, (float) height); + + Image im (Image::ARGB, width, height, true); + + { + Graphics g2 (im); + drawGlassLozenge (g2, 1.0f, 1.0f, + (float) (width - 2), + (float) (height - 2), + foreground, + 0.5f, 0.0f, + true, true, true, true); + } + + g.setTiledImageFill (im, 0, 0, 0.85f); + g.fillPath (p); + } + + if (textToShow.isNotEmpty()) + { + g.setColour (Colour::contrasting (background, foreground)); + g.setFont (height * 0.6f); + + g.drawText (textToShow, 0, 0, width, height, Justification::centred, false); + } +} + +void LookAndFeel_V2::drawSpinningWaitAnimation (Graphics& g, const Colour& colour, int x, int y, int w, int h) +{ + const float radius = jmin (w, h) * 0.4f; + const float thickness = radius * 0.15f; + Path p; + p.addRoundedRectangle (radius * 0.4f, thickness * -0.5f, + radius * 0.6f, thickness, + thickness * 0.5f); + + const float cx = x + w * 0.5f; + const float cy = y + h * 0.5f; + + const uint32 animationIndex = (Time::getMillisecondCounter() / (1000 / 10)) % 12; + + for (uint32 i = 0; i < 12; ++i) + { + const uint32 n = (i + 12 - animationIndex) % 12; + g.setColour (colour.withMultipliedAlpha ((n + 1) / 12.0f)); + + g.fillPath (p, AffineTransform::rotation (i * (float_Pi / 6.0f)) + .translated (cx, cy)); + } +} + +bool LookAndFeel_V2::areScrollbarButtonsVisible() +{ + return true; +} + +void LookAndFeel_V2::drawScrollbarButton (Graphics& g, + ScrollBar& scrollbar, + int width, int height, + int buttonDirection, + bool /*isScrollbarVertical*/, + bool /*isMouseOverButton*/, + bool isButtonDown) +{ + Path p; + + if (buttonDirection == 0) + p.addTriangle (width * 0.5f, height * 0.2f, + width * 0.1f, height * 0.7f, + width * 0.9f, height * 0.7f); + else if (buttonDirection == 1) + p.addTriangle (width * 0.8f, height * 0.5f, + width * 0.3f, height * 0.1f, + width * 0.3f, height * 0.9f); + else if (buttonDirection == 2) + p.addTriangle (width * 0.5f, height * 0.8f, + width * 0.1f, height * 0.3f, + width * 0.9f, height * 0.3f); + else if (buttonDirection == 3) + p.addTriangle (width * 0.2f, height * 0.5f, + width * 0.7f, height * 0.1f, + width * 0.7f, height * 0.9f); + + if (isButtonDown) + g.setColour (scrollbar.findColour (ScrollBar::thumbColourId).contrasting (0.2f)); + else + g.setColour (scrollbar.findColour (ScrollBar::thumbColourId)); + + g.fillPath (p); + + g.setColour (Colour (0x80000000)); + g.strokePath (p, PathStrokeType (0.5f)); +} + +void LookAndFeel_V2::drawScrollbar (Graphics& g, + ScrollBar& scrollbar, + int x, int y, + int width, int height, + bool isScrollbarVertical, + int thumbStartPosition, + int thumbSize, + bool /*isMouseOver*/, + bool /*isMouseDown*/) +{ + g.fillAll (scrollbar.findColour (ScrollBar::backgroundColourId)); + + Path slotPath, thumbPath; + + const float slotIndent = jmin (width, height) > 15 ? 1.0f : 0.0f; + const float slotIndentx2 = slotIndent * 2.0f; + const float thumbIndent = slotIndent + 1.0f; + const float thumbIndentx2 = thumbIndent * 2.0f; + + float gx1 = 0.0f, gy1 = 0.0f, gx2 = 0.0f, gy2 = 0.0f; + + if (isScrollbarVertical) + { + slotPath.addRoundedRectangle (x + slotIndent, + y + slotIndent, + width - slotIndentx2, + height - slotIndentx2, + (width - slotIndentx2) * 0.5f); + + if (thumbSize > 0) + thumbPath.addRoundedRectangle (x + thumbIndent, + thumbStartPosition + thumbIndent, + width - thumbIndentx2, + thumbSize - thumbIndentx2, + (width - thumbIndentx2) * 0.5f); + gx1 = (float) x; + gx2 = x + width * 0.7f; + } + else + { + slotPath.addRoundedRectangle (x + slotIndent, + y + slotIndent, + width - slotIndentx2, + height - slotIndentx2, + (height - slotIndentx2) * 0.5f); + + if (thumbSize > 0) + thumbPath.addRoundedRectangle (thumbStartPosition + thumbIndent, + y + thumbIndent, + thumbSize - thumbIndentx2, + height - thumbIndentx2, + (height - thumbIndentx2) * 0.5f); + gy1 = (float) y; + gy2 = y + height * 0.7f; + } + + const Colour thumbColour (scrollbar.findColour (ScrollBar::thumbColourId)); + Colour trackColour1, trackColour2; + + if (scrollbar.isColourSpecified (ScrollBar::trackColourId) + || isColourSpecified (ScrollBar::trackColourId)) + { + trackColour1 = trackColour2 = scrollbar.findColour (ScrollBar::trackColourId); + } + else + { + trackColour1 = thumbColour.overlaidWith (Colour (0x44000000)); + trackColour2 = thumbColour.overlaidWith (Colour (0x19000000)); + } + + g.setGradientFill (ColourGradient (trackColour1, gx1, gy1, + trackColour2, gx2, gy2, false)); + g.fillPath (slotPath); + + if (isScrollbarVertical) + { + gx1 = x + width * 0.6f; + gx2 = (float) x + width; + } + else + { + gy1 = y + height * 0.6f; + gy2 = (float) y + height; + } + + g.setGradientFill (ColourGradient (Colours::transparentBlack,gx1, gy1, + Colour (0x19000000), gx2, gy2, false)); + g.fillPath (slotPath); + + g.setColour (thumbColour); + g.fillPath (thumbPath); + + g.setGradientFill (ColourGradient (Colour (0x10000000), gx1, gy1, + Colours::transparentBlack, gx2, gy2, false)); + + g.saveState(); + + if (isScrollbarVertical) + g.reduceClipRegion (x + width / 2, y, width, height); + else + g.reduceClipRegion (x, y + height / 2, width, height); + + g.fillPath (thumbPath); + g.restoreState(); + + g.setColour (Colour (0x4c000000)); + g.strokePath (thumbPath, PathStrokeType (0.4f)); +} + +ImageEffectFilter* LookAndFeel_V2::getScrollbarEffect() +{ + return nullptr; +} + +int LookAndFeel_V2::getMinimumScrollbarThumbSize (ScrollBar& scrollbar) +{ + return jmin (scrollbar.getWidth(), scrollbar.getHeight()) * 2; +} + +int LookAndFeel_V2::getDefaultScrollbarWidth() +{ + return 18; +} + +int LookAndFeel_V2::getScrollbarButtonSize (ScrollBar& scrollbar) +{ + return 2 + (scrollbar.isVertical() ? scrollbar.getWidth() + : scrollbar.getHeight()); +} + +//============================================================================== +Path LookAndFeel_V2::getTickShape (const float height) +{ + static const unsigned char tickShapeData[] = + { + 109,0,224,168,68,0,0,119,67,108,0,224,172,68,0,128,146,67,113,0,192,148,68,0,0,219,67,0,96,110,68,0,224,56,68,113,0,64,51,68,0,32,130,68,0,64,20,68,0,224, + 162,68,108,0,128,3,68,0,128,168,68,113,0,128,221,67,0,192,175,68,0,0,207,67,0,32,179,68,113,0,0,201,67,0,224,173,68,0,0,181,67,0,224,161,68,108,0,128,168,67, + 0,128,154,68,113,0,128,141,67,0,192,138,68,0,128,108,67,0,64,131,68,113,0,0,62,67,0,128,119,68,0,0,5,67,0,128,114,68,113,0,0,102,67,0,192,88,68,0,128,155, + 67,0,192,88,68,113,0,0,190,67,0,192,88,68,0,128,232,67,0,224,131,68,108,0,128,246,67,0,192,139,68,113,0,64,33,68,0,128,87,68,0,0,93,68,0,224,26,68,113,0, + 96,140,68,0,128,188,67,0,224,168,68,0,0,119,67,99,101 + }; + + Path p; + p.loadPathFromData (tickShapeData, sizeof (tickShapeData)); + p.scaleToFit (0, 0, height * 2.0f, height, true); + return p; +} + +Path LookAndFeel_V2::getCrossShape (const float height) +{ + static const unsigned char crossShapeData[] = + { + 109,0,0,17,68,0,96,145,68,108,0,192,13,68,0,192,147,68,113,0,0,213,67,0,64,174,68,0,0,168,67,0,64,174,68,113,0,0,104,67,0,64,174,68,0,0,5,67,0,64, + 153,68,113,0,0,18,67,0,64,153,68,0,0,24,67,0,64,153,68,113,0,0,135,67,0,64,153,68,0,128,207,67,0,224,130,68,108,0,0,220,67,0,0,126,68,108,0,0,204,67, + 0,128,117,68,113,0,0,138,67,0,64,82,68,0,0,138,67,0,192,57,68,113,0,0,138,67,0,192,37,68,0,128,210,67,0,64,10,68,113,0,128,220,67,0,64,45,68,0,0,8, + 68,0,128,78,68,108,0,192,14,68,0,0,87,68,108,0,64,20,68,0,0,80,68,113,0,192,57,68,0,0,32,68,0,128,88,68,0,0,32,68,113,0,64,112,68,0,0,32,68,0, + 128,124,68,0,64,68,68,113,0,0,121,68,0,192,67,68,0,128,119,68,0,192,67,68,113,0,192,108,68,0,192,67,68,0,32,89,68,0,96,82,68,113,0,128,69,68,0,0,97,68, + 0,0,56,68,0,64,115,68,108,0,64,49,68,0,128,124,68,108,0,192,55,68,0,96,129,68,113,0,0,92,68,0,224,146,68,0,192,129,68,0,224,146,68,113,0,64,110,68,0,64, + 168,68,0,64,87,68,0,64,168,68,113,0,128,66,68,0,64,168,68,0,64,27,68,0,32,150,68,99,101 + }; + + Path p; + p.loadPathFromData (crossShapeData, sizeof (crossShapeData)); + p.scaleToFit (0, 0, height * 2.0f, height, true); + return p; +} + +//============================================================================== +void LookAndFeel_V2::drawTreeviewPlusMinusBox (Graphics& g, const Rectangle& area, + Colour /*backgroundColour*/, bool isOpen, bool /*isMouseOver*/) +{ + const int boxSize = roundToInt (jmin (16.0f, area.getWidth(), area.getHeight()) * 0.7f) | 1; + + 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; + + g.setColour (Colour (0xe5ffffff)); + g.fillRect (x, y, w, h); + + g.setColour (Colour (0x80000000)); + g.drawRect (x, y, w, h); + + 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); + + if (! isOpen) + g.fillRect (x + centre, y + (h - size) * 0.5f, 1.0f, size); +} + +bool LookAndFeel_V2::areLinesDrawnForTreeView (TreeView&) +{ + return true; +} + +int LookAndFeel_V2::getTreeViewIndentSize (TreeView&) +{ + return 24; +} + +//============================================================================== +void LookAndFeel_V2::drawBubble (Graphics& g, BubbleComponent& comp, + const Point& tip, const Rectangle& body) +{ + Path p; + p.addBubble (body, body.getUnion (Rectangle (tip.x, tip.y, 1.0f, 1.0f)), + tip, 5.0f, jmin (15.0f, body.getWidth() * 0.2f, body.getHeight() * 0.2f)); + + g.setColour (comp.findColour (BubbleComponent::backgroundColourId)); + g.fillPath (p); + + g.setColour (comp.findColour (BubbleComponent::outlineColourId)); + g.strokePath (p, PathStrokeType (1.33f)); +} + + +//============================================================================== +Font LookAndFeel_V2::getPopupMenuFont() +{ + return Font (17.0f); +} + +void LookAndFeel_V2::getIdealPopupMenuItemSize (const String& text, + const bool isSeparator, + int standardMenuItemHeight, + int& idealWidth, + int& idealHeight) +{ + if (isSeparator) + { + idealWidth = 50; + idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight / 2 : 10; + } + else + { + Font font (getPopupMenuFont()); + + if (standardMenuItemHeight > 0 && font.getHeight() > standardMenuItemHeight / 1.3f) + font.setHeight (standardMenuItemHeight / 1.3f); + + idealHeight = standardMenuItemHeight > 0 ? standardMenuItemHeight : roundToInt (font.getHeight() * 1.3f); + idealWidth = font.getStringWidth (text) + idealHeight * 2; + } +} + +void LookAndFeel_V2::drawPopupMenuBackground (Graphics& g, int width, int height) +{ + const Colour background (findColour (PopupMenu::backgroundColourId)); + + g.fillAll (background); + g.setColour (background.overlaidWith (Colour (0x2badd8e6))); + + for (int i = 0; i < height; i += 3) + g.fillRect (0, i, width, 1); + +#if ! JUCE_MAC + g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.6f)); + g.drawRect (0, 0, width, height); +#endif +} + +void LookAndFeel_V2::drawPopupMenuUpDownArrow (Graphics& g, + int width, int height, + bool isScrollUpArrow) +{ + const Colour background (findColour (PopupMenu::backgroundColourId)); + + g.setGradientFill (ColourGradient (background, 0.0f, height * 0.5f, + background.withAlpha (0.0f), + 0.0f, isScrollUpArrow ? ((float) height) : 0.0f, + false)); + + g.fillRect (1, 1, width - 2, height - 2); + + const float hw = width * 0.5f; + const float arrowW = height * 0.3f; + const float y1 = height * (isScrollUpArrow ? 0.6f : 0.3f); + const float y2 = height * (isScrollUpArrow ? 0.3f : 0.6f); + + Path p; + p.addTriangle (hw - arrowW, y1, + hw + arrowW, y1, + hw, y2); + + g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.5f)); + g.fillPath (p); +} + +void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, + int width, int height, + const bool isSeparator, + const bool isActive, + const bool isHighlighted, + const bool isTicked, + const bool hasSubMenu, + const String& text, + const String& shortcutKeyText, + Image* image, + const Colour* const textColourToUse) +{ + const float halfH = height * 0.5f; + + if (isSeparator) + { + const float separatorIndent = 5.5f; + + g.setColour (Colour (0x33000000)); + g.drawLine (separatorIndent, halfH, width - separatorIndent, halfH); + + g.setColour (Colour (0x66ffffff)); + g.drawLine (separatorIndent, halfH + 1.0f, width - separatorIndent, halfH + 1.0f); + } + else + { + Colour textColour (findColour (PopupMenu::textColourId)); + + if (textColourToUse != nullptr) + textColour = *textColourToUse; + + if (isHighlighted) + { + g.setColour (findColour (PopupMenu::highlightedBackgroundColourId)); + g.fillRect (1, 1, width - 2, height - 2); + + g.setColour (findColour (PopupMenu::highlightedTextColourId)); + } + else + { + g.setColour (textColour); + } + + if (! isActive) + g.setOpacity (0.3f); + + Font font (getPopupMenuFont()); + + if (font.getHeight() > height / 1.3f) + font.setHeight (height / 1.3f); + + g.setFont (font); + + const int leftBorder = (height * 5) / 4; + const int rightBorder = 4; + + if (image != nullptr) + { + g.drawImageWithin (*image, + 2, 1, leftBorder - 4, height - 2, + RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); + } + else if (isTicked) + { + const Path tick (getTickShape (1.0f)); + const float th = font.getAscent(); + const float ty = halfH - th * 0.5f; + + g.fillPath (tick, tick.getTransformToScaleToFit (2.0f, ty, (float) (leftBorder - 4), + th, true)); + } + + g.drawFittedText (text, + leftBorder, 0, width - (leftBorder + rightBorder), height, + Justification::centredLeft, 1); + + if (shortcutKeyText.isNotEmpty()) + { + Font f2 (font); + f2.setHeight (f2.getHeight() * 0.75f); + f2.setHorizontalScale (0.95f); + g.setFont (f2); + + g.drawText (shortcutKeyText, + leftBorder, 0, width - (leftBorder + rightBorder + 4), height, + Justification::centredRight, + true); + } + + if (hasSubMenu) + { + const float arrowH = 0.6f * getPopupMenuFont().getAscent(); + const float x = width - height * 0.6f; + + Path p; + p.addTriangle (x, halfH - arrowH * 0.5f, + x, halfH + arrowH * 0.5f, + x + arrowH * 0.6f, halfH); + + g.fillPath (p); + } + } +} + +//============================================================================== +int LookAndFeel_V2::getMenuWindowFlags() +{ + return ComponentPeer::windowHasDropShadow; +} + +void LookAndFeel_V2::drawMenuBarBackground (Graphics& g, int width, int height, + bool, MenuBarComponent& menuBar) +{ + const Colour baseColour (LookAndFeelHelpers::createBaseColour (menuBar.findColour (PopupMenu::backgroundColourId), false, false, false)); + + if (menuBar.isEnabled()) + { + drawShinyButtonShape (g, + -4.0f, 0.0f, + width + 8.0f, (float) height, + 0.0f, + baseColour, + 0.4f, + true, true, true, true); + } + else + { + g.fillAll (baseColour); + } +} + +Font LookAndFeel_V2::getMenuBarFont (MenuBarComponent& menuBar, int /*itemIndex*/, const String& /*itemText*/) +{ + return Font (menuBar.getHeight() * 0.7f); +} + +int LookAndFeel_V2::getMenuBarItemWidth (MenuBarComponent& menuBar, int itemIndex, const String& itemText) +{ + return getMenuBarFont (menuBar, itemIndex, itemText) + .getStringWidth (itemText) + menuBar.getHeight(); +} + +void LookAndFeel_V2::drawMenuBarItem (Graphics& g, + int width, int height, + int itemIndex, + const String& itemText, + bool isMouseOverItem, + bool isMenuOpen, + bool /*isMouseOverBar*/, + MenuBarComponent& menuBar) +{ + if (! menuBar.isEnabled()) + { + g.setColour (menuBar.findColour (PopupMenu::textColourId) + .withMultipliedAlpha (0.5f)); + } + else if (isMenuOpen || isMouseOverItem) + { + g.fillAll (menuBar.findColour (PopupMenu::highlightedBackgroundColourId)); + g.setColour (menuBar.findColour (PopupMenu::highlightedTextColourId)); + } + else + { + g.setColour (menuBar.findColour (PopupMenu::textColourId)); + } + + g.setFont (getMenuBarFont (menuBar, itemIndex, itemText)); + g.drawFittedText (itemText, 0, 0, width, height, Justification::centred, 1); +} + +//============================================================================== +void LookAndFeel_V2::fillTextEditorBackground (Graphics& g, int /*width*/, int /*height*/, + TextEditor& textEditor) +{ + g.fillAll (textEditor.findColour (TextEditor::backgroundColourId)); +} + +void LookAndFeel_V2::drawTextEditorOutline (Graphics& g, int width, int height, TextEditor& textEditor) +{ + if (textEditor.isEnabled()) + { + if (textEditor.hasKeyboardFocus (true) && ! textEditor.isReadOnly()) + { + const int border = 2; + + g.setColour (textEditor.findColour (TextEditor::focusedOutlineColourId)); + g.drawRect (0, 0, width, height, border); + + g.setOpacity (1.0f); + const Colour shadowColour (textEditor.findColour (TextEditor::shadowColourId).withMultipliedAlpha (0.75f)); + drawBevel (g, 0, 0, width, height + 2, border + 2, shadowColour, shadowColour); + } + else + { + g.setColour (textEditor.findColour (TextEditor::outlineColourId)); + g.drawRect (0, 0, width, height); + + g.setOpacity (1.0f); + const Colour shadowColour (textEditor.findColour (TextEditor::shadowColourId)); + drawBevel (g, 0, 0, width, height + 2, 3, shadowColour, shadowColour); + } + } +} + +CaretComponent* LookAndFeel_V2::createCaretComponent (Component* keyFocusOwner) +{ + return new CaretComponent (keyFocusOwner); +} + +//============================================================================== +void LookAndFeel_V2::drawComboBox (Graphics& g, int width, int height, + const bool isButtonDown, + int buttonX, int buttonY, + int buttonW, int buttonH, + ComboBox& box) +{ + g.fillAll (box.findColour (ComboBox::backgroundColourId)); + + if (box.isEnabled() && box.hasKeyboardFocus (false)) + { + g.setColour (box.findColour (ComboBox::buttonColourId)); + g.drawRect (0, 0, width, height, 2); + } + else + { + g.setColour (box.findColour (ComboBox::outlineColourId)); + g.drawRect (0, 0, width, height); + } + + const float outlineThickness = box.isEnabled() ? (isButtonDown ? 1.2f : 0.5f) : 0.3f; + + const Colour baseColour (LookAndFeelHelpers::createBaseColour (box.findColour (ComboBox::buttonColourId), + box.hasKeyboardFocus (true), + false, isButtonDown) + .withMultipliedAlpha (box.isEnabled() ? 1.0f : 0.5f)); + + drawGlassLozenge (g, + buttonX + outlineThickness, buttonY + outlineThickness, + buttonW - outlineThickness * 2.0f, buttonH - outlineThickness * 2.0f, + baseColour, outlineThickness, -1.0f, + true, true, true, true); + + if (box.isEnabled()) + { + const float arrowX = 0.3f; + const float arrowH = 0.2f; + + Path p; + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.45f - arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.45f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.45f); + + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.55f + arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.55f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.55f); + + g.setColour (box.findColour (ComboBox::arrowColourId)); + g.fillPath (p); + } +} + +Font LookAndFeel_V2::getComboBoxFont (ComboBox& box) +{ + return Font (jmin (15.0f, box.getHeight() * 0.85f)); +} + +Label* LookAndFeel_V2::createComboBoxTextBox (ComboBox&) +{ + return new Label (String::empty, String::empty); +} + +void LookAndFeel_V2::positionComboBoxText (ComboBox& box, Label& label) +{ + label.setBounds (1, 1, + box.getWidth() + 3 - box.getHeight(), + box.getHeight() - 2); + + label.setFont (getComboBoxFont (box)); +} + +//============================================================================== +Font LookAndFeel_V2::getLabelFont (Label& label) +{ + return label.getFont(); +} + +void LookAndFeel_V2::drawLabel (Graphics& g, Label& label) +{ + g.fillAll (label.findColour (Label::backgroundColourId)); + + if (! label.isBeingEdited()) + { + const float alpha = label.isEnabled() ? 1.0f : 0.5f; + const Font font (getLabelFont (label)); + + g.setColour (label.findColour (Label::textColourId).withMultipliedAlpha (alpha)); + g.setFont (font); + g.drawFittedText (label.getText(), + label.getHorizontalBorderSize(), + label.getVerticalBorderSize(), + label.getWidth() - 2 * label.getHorizontalBorderSize(), + label.getHeight() - 2 * label.getVerticalBorderSize(), + label.getJustificationType(), + jmax (1, (int) (label.getHeight() / font.getHeight())), + label.getMinimumHorizontalScale()); + + g.setColour (label.findColour (Label::outlineColourId).withMultipliedAlpha (alpha)); + } + else if (label.isEnabled()) + { + g.setColour (label.findColour (Label::outlineColourId)); + } + + g.drawRect (label.getLocalBounds()); +} + +//============================================================================== +void LookAndFeel_V2::drawLinearSliderBackground (Graphics& g, + int x, int y, + int width, int height, + float /*sliderPos*/, + float /*minSliderPos*/, + float /*maxSliderPos*/, + const Slider::SliderStyle /*style*/, + Slider& slider) +{ + const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); + + const Colour trackColour (slider.findColour (Slider::trackColourId)); + const Colour gradCol1 (trackColour.overlaidWith (Colours::black.withAlpha (slider.isEnabled() ? 0.25f : 0.13f))); + const Colour gradCol2 (trackColour.overlaidWith (Colour (0x14000000))); + Path indent; + + if (slider.isHorizontal()) + { + const float iy = y + height * 0.5f - sliderRadius * 0.5f; + const float ih = sliderRadius; + + g.setGradientFill (ColourGradient (gradCol1, 0.0f, iy, + gradCol2, 0.0f, iy + ih, false)); + + indent.addRoundedRectangle (x - sliderRadius * 0.5f, iy, + width + sliderRadius, ih, + 5.0f); + } + else + { + const float ix = x + width * 0.5f - sliderRadius * 0.5f; + const float iw = sliderRadius; + + g.setGradientFill (ColourGradient (gradCol1, ix, 0.0f, + gradCol2, ix + iw, 0.0f, false)); + + indent.addRoundedRectangle (ix, y - sliderRadius * 0.5f, + iw, height + sliderRadius, + 5.0f); + } + + g.fillPath (indent); + + g.setColour (Colour (0x4c000000)); + g.strokePath (indent, PathStrokeType (0.5f)); +} + +void LookAndFeel_V2::drawLinearSliderThumb (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider) +{ + const float sliderRadius = (float) (getSliderThumbRadius (slider) - 2); + + Colour knobColour (LookAndFeelHelpers::createBaseColour (slider.findColour (Slider::thumbColourId), + slider.hasKeyboardFocus (false) && slider.isEnabled(), + slider.isMouseOverOrDragging() && slider.isEnabled(), + slider.isMouseButtonDown() && slider.isEnabled())); + + const float outlineThickness = slider.isEnabled() ? 0.8f : 0.3f; + + if (style == Slider::LinearHorizontal || style == Slider::LinearVertical) + { + float kx, ky; + + if (style == Slider::LinearVertical) + { + kx = x + width * 0.5f; + ky = sliderPos; + } + else + { + kx = sliderPos; + ky = y + height * 0.5f; + } + + drawGlassSphere (g, + kx - sliderRadius, + ky - sliderRadius, + sliderRadius * 2.0f, + knobColour, outlineThickness); + } + else + { + if (style == Slider::ThreeValueVertical) + { + drawGlassSphere (g, x + width * 0.5f - sliderRadius, + sliderPos - sliderRadius, + sliderRadius * 2.0f, + knobColour, outlineThickness); + } + else if (style == Slider::ThreeValueHorizontal) + { + drawGlassSphere (g,sliderPos - sliderRadius, + y + height * 0.5f - sliderRadius, + sliderRadius * 2.0f, + knobColour, outlineThickness); + } + + if (style == Slider::TwoValueVertical || style == Slider::ThreeValueVertical) + { + const float sr = jmin (sliderRadius, width * 0.4f); + + drawGlassPointer (g, jmax (0.0f, x + width * 0.5f - sliderRadius * 2.0f), + minSliderPos - sliderRadius, + sliderRadius * 2.0f, knobColour, outlineThickness, 1); + + drawGlassPointer (g, jmin (x + width - sliderRadius * 2.0f, x + width * 0.5f), maxSliderPos - sr, + sliderRadius * 2.0f, knobColour, outlineThickness, 3); + } + else if (style == Slider::TwoValueHorizontal || style == Slider::ThreeValueHorizontal) + { + const float sr = jmin (sliderRadius, height * 0.4f); + + drawGlassPointer (g, minSliderPos - sr, + jmax (0.0f, y + height * 0.5f - sliderRadius * 2.0f), + sliderRadius * 2.0f, knobColour, outlineThickness, 2); + + drawGlassPointer (g, maxSliderPos - sliderRadius, + jmin (y + height - sliderRadius * 2.0f, y + height * 0.5f), + sliderRadius * 2.0f, knobColour, outlineThickness, 4); + } + } +} + +void LookAndFeel_V2::drawLinearSlider (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider& slider) +{ + g.fillAll (slider.findColour (Slider::backgroundColourId)); + + if (style == Slider::LinearBar || style == Slider::LinearBarVertical) + { + const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); + + Colour baseColour (LookAndFeelHelpers::createBaseColour (slider.findColour (Slider::thumbColourId) + .withMultipliedSaturation (slider.isEnabled() ? 1.0f : 0.5f), + false, isMouseOver, + isMouseOver || slider.isMouseButtonDown())); + + drawShinyButtonShape (g, + (float) x, + style == Slider::LinearBarVertical ? sliderPos + : (float) y, + style == Slider::LinearBarVertical ? (float) width + : (sliderPos - x), + style == Slider::LinearBarVertical ? (height - sliderPos) + : (float) height, 0.0f, + baseColour, + slider.isEnabled() ? 0.9f : 0.3f, + true, true, true, true); + } + else + { + drawLinearSliderBackground (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider); + drawLinearSliderThumb (g, x, y, width, height, sliderPos, minSliderPos, maxSliderPos, style, slider); + } +} + +int LookAndFeel_V2::getSliderThumbRadius (Slider& slider) +{ + return jmin (7, + slider.getHeight() / 2, + slider.getWidth() / 2) + 2; +} + +void LookAndFeel_V2::drawRotarySlider (Graphics& g, + int x, int y, + int width, int height, + float sliderPos, + const float rotaryStartAngle, + const float rotaryEndAngle, + Slider& slider) +{ + const float radius = jmin (width / 2, height / 2) - 2.0f; + const float centreX = x + width * 0.5f; + const float centreY = y + height * 0.5f; + const float rx = centreX - radius; + const float ry = centreY - radius; + const float rw = radius * 2.0f; + const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle); + const bool isMouseOver = slider.isMouseOverOrDragging() && slider.isEnabled(); + + if (radius > 12.0f) + { + if (slider.isEnabled()) + g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f)); + else + g.setColour (Colour (0x80808080)); + + const float thickness = 0.7f; + + { + Path filledArc; + filledArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, angle, thickness); + g.fillPath (filledArc); + } + + if (thickness > 0) + { + const float innerRadius = radius * 0.2f; + Path p; + p.addTriangle (-innerRadius, 0.0f, + 0.0f, -radius * thickness * 1.1f, + innerRadius, 0.0f); + + p.addEllipse (-innerRadius, -innerRadius, innerRadius * 2.0f, innerRadius * 2.0f); + + g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY)); + } + + if (slider.isEnabled()) + g.setColour (slider.findColour (Slider::rotarySliderOutlineColourId)); + else + g.setColour (Colour (0x80808080)); + + Path outlineArc; + outlineArc.addPieSegment (rx, ry, rw, rw, rotaryStartAngle, rotaryEndAngle, thickness); + outlineArc.closeSubPath(); + + g.strokePath (outlineArc, PathStrokeType (slider.isEnabled() ? (isMouseOver ? 2.0f : 1.2f) : 0.3f)); + } + else + { + if (slider.isEnabled()) + g.setColour (slider.findColour (Slider::rotarySliderFillColourId).withAlpha (isMouseOver ? 1.0f : 0.7f)); + else + g.setColour (Colour (0x80808080)); + + Path p; + p.addEllipse (-0.4f * rw, -0.4f * rw, rw * 0.8f, rw * 0.8f); + PathStrokeType (rw * 0.1f).createStrokedPath (p, p); + + p.addLineSegment (Line (0.0f, 0.0f, 0.0f, -radius), rw * 0.2f); + + g.fillPath (p, AffineTransform::rotation (angle).translated (centreX, centreY)); + } +} + +Button* LookAndFeel_V2::createSliderButton (const bool isIncrement) +{ + return new TextButton (isIncrement ? "+" : "-", String::empty); +} + +class LookAndFeel_V2::SliderLabelComp : public Label +{ +public: + SliderLabelComp() : Label (String::empty, String::empty) {} + + void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) {} +}; + +Label* LookAndFeel_V2::createSliderTextBox (Slider& slider) +{ + Label* const l = new SliderLabelComp(); + + l->setJustificationType (Justification::centred); + + l->setColour (Label::textColourId, slider.findColour (Slider::textBoxTextColourId)); + + l->setColour (Label::backgroundColourId, + (slider.getSliderStyle() == Slider::LinearBar || slider.getSliderStyle() == Slider::LinearBarVertical) + ? Colours::transparentBlack + : slider.findColour (Slider::textBoxBackgroundColourId)); + l->setColour (Label::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); + + l->setColour (TextEditor::textColourId, slider.findColour (Slider::textBoxTextColourId)); + + l->setColour (TextEditor::backgroundColourId, + slider.findColour (Slider::textBoxBackgroundColourId) + .withAlpha ((slider.getSliderStyle() == Slider::LinearBar || slider.getSliderStyle() == Slider::LinearBarVertical) + ? 0.7f : 1.0f)); + + l->setColour (TextEditor::outlineColourId, slider.findColour (Slider::textBoxOutlineColourId)); + + return l; +} + +ImageEffectFilter* LookAndFeel_V2::getSliderEffect() +{ + return nullptr; +} + +Font LookAndFeel_V2::getSliderPopupFont() +{ + return Font (15.0f, Font::bold); +} + +int LookAndFeel_V2::getSliderPopupPlacement() +{ + return BubbleComponent::above + | BubbleComponent::below + | BubbleComponent::left + | BubbleComponent::right; +} + +//============================================================================== +void LookAndFeel_V2::getTooltipSize (const String& tipText, int& width, int& height) +{ + const TextLayout tl (LookAndFeelHelpers::layoutTooltipText (tipText, Colours::black)); + + width = (int) (tl.getWidth() + 14.0f); + height = (int) (tl.getHeight() + 6.0f); +} + +void LookAndFeel_V2::drawTooltip (Graphics& g, const String& text, int width, int height) +{ + g.fillAll (findColour (TooltipWindow::backgroundColourId)); + + #if ! JUCE_MAC // The mac windows already have a non-optional 1 pix outline, so don't double it here.. + g.setColour (findColour (TooltipWindow::outlineColourId)); + g.drawRect (0, 0, width, height, 1); + #endif + + LookAndFeelHelpers::layoutTooltipText (text, findColour (TooltipWindow::textColourId)) + .draw (g, Rectangle ((float) width, (float) height)); +} + +//============================================================================== +Button* LookAndFeel_V2::createFilenameComponentBrowseButton (const String& text) +{ + return new TextButton (text, TRANS("click to browse for a different file")); +} + +void LookAndFeel_V2::layoutFilenameComponent (FilenameComponent& filenameComp, + ComboBox* filenameBox, + Button* browseButton) +{ + browseButton->setSize (80, filenameComp.getHeight()); + + if (TextButton* const tb = dynamic_cast (browseButton)) + tb->changeWidthToFitText(); + + browseButton->setTopRightPosition (filenameComp.getWidth(), 0); + + filenameBox->setBounds (0, 0, browseButton->getX(), filenameComp.getHeight()); +} + +//============================================================================== +void LookAndFeel_V2::drawConcertinaPanelHeader (Graphics& g, const Rectangle& area, + bool isMouseOver, bool /*isMouseDown*/, + ConcertinaPanel&, Component& panel) +{ + g.fillAll (Colours::grey.withAlpha (isMouseOver ? 0.9f : 0.7f)); + g.setColour (Colours::black.withAlpha (0.5f)); + g.drawRect (area); + + g.setColour (Colours::white); + g.setFont (Font (area.getHeight() * 0.7f).boldened()); + g.drawFittedText (panel.getName(), 4, 0, area.getWidth() - 6, area.getHeight(), Justification::centredLeft, 1); +} + +//============================================================================== +void LookAndFeel_V2::drawImageButton (Graphics& g, Image* image, + int imageX, int imageY, int imageW, int imageH, + const Colour& overlayColour, + float imageOpacity, + ImageButton& button) +{ + if (! button.isEnabled()) + imageOpacity *= 0.3f; + + AffineTransform t = RectanglePlacement (RectanglePlacement::stretchToFit) + .getTransformToFit (image->getBounds().toFloat(), + Rectangle (imageX, imageY, imageW, imageH).toFloat()); + + if (! overlayColour.isOpaque()) + { + g.setOpacity (imageOpacity); + g.drawImageTransformed (*image, t, false); + } + + if (! overlayColour.isTransparent()) + { + g.setColour (overlayColour); + g.drawImageTransformed (*image, t, true); + } +} + +//============================================================================== +void LookAndFeel_V2::drawCornerResizer (Graphics& g, + int w, int h, + bool /*isMouseOver*/, + bool /*isMouseDragging*/) +{ + const float lineThickness = jmin (w, h) * 0.075f; + + for (float i = 0.0f; i < 1.0f; i += 0.3f) + { + g.setColour (Colours::lightgrey); + + g.drawLine (w * i, + h + 1.0f, + w + 1.0f, + h * i, + lineThickness); + + g.setColour (Colours::darkgrey); + + g.drawLine (w * i + lineThickness, + h + 1.0f, + w + 1.0f, + h * i + lineThickness, + lineThickness); + } +} + +void LookAndFeel_V2::drawResizableFrame (Graphics& g, int w, int h, const BorderSize& border) +{ + if (! border.isEmpty()) + { + const Rectangle fullSize (0, 0, w, h); + const Rectangle centreArea (border.subtractedFrom (fullSize)); + + g.saveState(); + + g.excludeClipRegion (centreArea); + + g.setColour (Colour (0x50000000)); + g.drawRect (fullSize); + + g.setColour (Colour (0x19000000)); + g.drawRect (centreArea.expanded (1, 1)); + + g.restoreState(); + } +} + +//============================================================================== +void LookAndFeel_V2::fillResizableWindowBackground (Graphics& g, int /*w*/, int /*h*/, + const BorderSize& /*border*/, ResizableWindow& window) +{ + g.fillAll (window.getBackgroundColour()); +} + +void LookAndFeel_V2::drawResizableWindowBorder (Graphics&, int /*w*/, int /*h*/, + const BorderSize& /*border*/, ResizableWindow&) +{ +} + +void LookAndFeel_V2::drawDocumentWindowTitleBar (DocumentWindow& window, + Graphics& g, int w, int h, + int titleSpaceX, int titleSpaceW, + const Image* icon, + bool drawTitleTextOnLeft) +{ + const bool isActive = window.isActiveWindow(); + + g.setGradientFill (ColourGradient (window.getBackgroundColour(), + 0.0f, 0.0f, + window.getBackgroundColour().contrasting (isActive ? 0.15f : 0.05f), + 0.0f, (float) h, false)); + g.fillAll(); + + Font font (h * 0.65f, Font::bold); + g.setFont (font); + + int textW = font.getStringWidth (window.getName()); + int iconW = 0; + int iconH = 0; + + if (icon != nullptr) + { + iconH = (int) font.getHeight(); + iconW = icon->getWidth() * iconH / icon->getHeight() + 4; + } + + textW = jmin (titleSpaceW, textW + iconW); + int textX = drawTitleTextOnLeft ? titleSpaceX + : jmax (titleSpaceX, (w - textW) / 2); + + if (textX + textW > titleSpaceX + titleSpaceW) + textX = titleSpaceX + titleSpaceW - textW; + + if (icon != nullptr) + { + g.setOpacity (isActive ? 1.0f : 0.6f); + g.drawImageWithin (*icon, textX, (h - iconH) / 2, iconW, iconH, + RectanglePlacement::centred, false); + textX += iconW; + textW -= iconW; + } + + if (window.isColourSpecified (DocumentWindow::textColourId) || isColourSpecified (DocumentWindow::textColourId)) + g.setColour (window.findColour (DocumentWindow::textColourId)); + else + g.setColour (window.getBackgroundColour().contrasting (isActive ? 0.7f : 0.4f)); + + g.drawText (window.getName(), textX, 0, textW, h, Justification::centredLeft, true); +} + +//============================================================================== +class LookAndFeel_V2::GlassWindowButton : public Button +{ +public: + GlassWindowButton (const String& name, Colour col, + const Path& normalShape_, + const Path& toggledShape_) noexcept + : Button (name), + colour (col), + normalShape (normalShape_), + toggledShape (toggledShape_) + { + } + + //============================================================================== + void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override + { + float alpha = isMouseOverButton ? (isButtonDown ? 1.0f : 0.8f) : 0.55f; + + if (! isEnabled()) + alpha *= 0.5f; + + float x = 0, y = 0, diam; + + if (getWidth() < getHeight()) + { + diam = (float) getWidth(); + y = (getHeight() - getWidth()) * 0.5f; + } + else + { + diam = (float) getHeight(); + y = (getWidth() - getHeight()) * 0.5f; + } + + x += diam * 0.05f; + y += diam * 0.05f; + diam *= 0.9f; + + g.setGradientFill (ColourGradient (Colour::greyLevel (0.9f).withAlpha (alpha), 0, y + diam, + Colour::greyLevel (0.6f).withAlpha (alpha), 0, y, false)); + g.fillEllipse (x, y, diam, diam); + + x += 2.0f; + y += 2.0f; + diam -= 4.0f; + + LookAndFeel_V2::drawGlassSphere (g, x, y, diam, colour.withAlpha (alpha), 1.0f); + + Path& p = getToggleState() ? toggledShape : normalShape; + + const AffineTransform t (p.getTransformToScaleToFit (x + diam * 0.3f, y + diam * 0.3f, + diam * 0.4f, diam * 0.4f, true)); + + g.setColour (Colours::black.withAlpha (alpha * 0.6f)); + g.fillPath (p, t); + } + +private: + Colour colour; + Path normalShape, toggledShape; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlassWindowButton) +}; + +Button* LookAndFeel_V2::createDocumentWindowButton (int buttonType) +{ + Path shape; + const float crossThickness = 0.25f; + + if (buttonType == DocumentWindow::closeButton) + { + shape.addLineSegment (Line (0.0f, 0.0f, 1.0f, 1.0f), crossThickness * 1.4f); + shape.addLineSegment (Line (1.0f, 0.0f, 0.0f, 1.0f), crossThickness * 1.4f); + + return new GlassWindowButton ("close", Colour (0xffdd1100), shape, shape); + } + + if (buttonType == DocumentWindow::minimiseButton) + { + shape.addLineSegment (Line (0.0f, 0.5f, 1.0f, 0.5f), crossThickness); + + return new GlassWindowButton ("minimise", Colour (0xffaa8811), shape, shape); + } + + if (buttonType == DocumentWindow::maximiseButton) + { + shape.addLineSegment (Line (0.5f, 0.0f, 0.5f, 1.0f), crossThickness); + shape.addLineSegment (Line (0.0f, 0.5f, 1.0f, 0.5f), crossThickness); + + Path fullscreenShape; + fullscreenShape.startNewSubPath (45.0f, 100.0f); + fullscreenShape.lineTo (0.0f, 100.0f); + fullscreenShape.lineTo (0.0f, 0.0f); + fullscreenShape.lineTo (100.0f, 0.0f); + fullscreenShape.lineTo (100.0f, 45.0f); + fullscreenShape.addRectangle (45.0f, 45.0f, 100.0f, 100.0f); + PathStrokeType (30.0f).createStrokedPath (fullscreenShape, fullscreenShape); + + return new GlassWindowButton ("maximise", Colour (0xff119911), shape, fullscreenShape); + } + + jassertfalse; + return nullptr; +} + +void LookAndFeel_V2::positionDocumentWindowButtons (DocumentWindow&, + int titleBarX, + int titleBarY, + int titleBarW, + int titleBarH, + Button* minimiseButton, + Button* maximiseButton, + Button* closeButton, + bool positionTitleBarButtonsOnLeft) +{ + const int buttonW = titleBarH - titleBarH / 8; + + int x = positionTitleBarButtonsOnLeft ? titleBarX + 4 + : titleBarX + titleBarW - buttonW - buttonW / 4; + + if (closeButton != nullptr) + { + closeButton->setBounds (x, titleBarY, buttonW, titleBarH); + x += positionTitleBarButtonsOnLeft ? buttonW : -(buttonW + buttonW / 4); + } + + if (positionTitleBarButtonsOnLeft) + std::swap (minimiseButton, maximiseButton); + + if (maximiseButton != nullptr) + { + maximiseButton->setBounds (x, titleBarY, buttonW, titleBarH); + x += positionTitleBarButtonsOnLeft ? buttonW : -buttonW; + } + + if (minimiseButton != nullptr) + minimiseButton->setBounds (x, titleBarY, buttonW, titleBarH); +} + +int LookAndFeel_V2::getDefaultMenuBarHeight() +{ + return 24; +} + +//============================================================================== +DropShadower* LookAndFeel_V2::createDropShadowerForComponent (Component*) +{ + return new DropShadower (DropShadow (Colours::black.withAlpha (0.4f), 10, Point (0, 2))); +} + +//============================================================================== +void LookAndFeel_V2::drawStretchableLayoutResizerBar (Graphics& g, + int w, int h, + bool /*isVerticalBar*/, + bool isMouseOver, + bool isMouseDragging) +{ + float alpha = 0.5f; + + if (isMouseOver || isMouseDragging) + { + g.fillAll (Colour (0x190000ff)); + alpha = 1.0f; + } + + const float cx = w * 0.5f; + const float cy = h * 0.5f; + const float cr = jmin (w, h) * 0.4f; + + g.setGradientFill (ColourGradient (Colours::white.withAlpha (alpha), cx + cr * 0.1f, cy + cr, + Colours::black.withAlpha (alpha), cx, cy - cr * 4.0f, + true)); + + g.fillEllipse (cx - cr, cy - cr, cr * 2.0f, cr * 2.0f); +} + +//============================================================================== +void LookAndFeel_V2::drawGroupComponentOutline (Graphics& g, int width, int height, + const String& text, + const Justification& position, + GroupComponent& group) +{ + const float textH = 15.0f; + const float indent = 3.0f; + const float textEdgeGap = 4.0f; + float cs = 5.0f; + + Font f (textH); + + Path p; + float x = indent; + float y = f.getAscent() - 3.0f; + float w = jmax (0.0f, width - x * 2.0f); + float h = jmax (0.0f, height - y - indent); + cs = jmin (cs, w * 0.5f, h * 0.5f); + const float cs2 = 2.0f * cs; + + float textW = text.isEmpty() ? 0 : jlimit (0.0f, jmax (0.0f, w - cs2 - textEdgeGap * 2), f.getStringWidth (text) + textEdgeGap * 2.0f); + float textX = cs + textEdgeGap; + + if (position.testFlags (Justification::horizontallyCentred)) + textX = cs + (w - cs2 - textW) * 0.5f; + else if (position.testFlags (Justification::right)) + textX = w - cs - textW - textEdgeGap; + + p.startNewSubPath (x + textX + textW, y); + p.lineTo (x + w - cs, y); + + p.addArc (x + w - cs2, y, cs2, cs2, 0, float_Pi * 0.5f); + p.lineTo (x + w, y + h - cs); + + p.addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi); + p.lineTo (x + cs, y + h); + + p.addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f); + p.lineTo (x, y + cs); + + p.addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f); + p.lineTo (x + textX, y); + + const float alpha = group.isEnabled() ? 1.0f : 0.5f; + + g.setColour (group.findColour (GroupComponent::outlineColourId) + .withMultipliedAlpha (alpha)); + + g.strokePath (p, PathStrokeType (2.0f)); + + g.setColour (group.findColour (GroupComponent::textColourId) + .withMultipliedAlpha (alpha)); + g.setFont (f); + g.drawText (text, + roundToInt (x + textX), 0, + roundToInt (textW), + roundToInt (textH), + Justification::centred, true); +} + +//============================================================================== +int LookAndFeel_V2::getTabButtonOverlap (int tabDepth) +{ + return 1 + tabDepth / 3; +} + +int LookAndFeel_V2::getTabButtonSpaceAroundImage() +{ + return 4; +} + +int LookAndFeel_V2::getTabButtonBestWidth (TabBarButton& button, int tabDepth) +{ + int width = Font (tabDepth * 0.6f).getStringWidth (button.getButtonText().trim()) + + getTabButtonOverlap (tabDepth) * 2; + + if (Component* const extraComponent = button.getExtraComponent()) + width += button.getTabbedButtonBar().isVertical() ? extraComponent->getHeight() + : extraComponent->getWidth(); + + return jlimit (tabDepth * 2, tabDepth * 8, width); +} + +Rectangle LookAndFeel_V2::getTabButtonExtraComponentBounds (const TabBarButton& button, Rectangle& textArea, Component& comp) +{ + Rectangle extraComp; + + const TabbedButtonBar::Orientation orientation = button.getTabbedButtonBar().getOrientation(); + + if (button.getExtraComponentPlacement() == TabBarButton::beforeText) + { + switch (orientation) + { + case TabbedButtonBar::TabsAtBottom: + case TabbedButtonBar::TabsAtTop: extraComp = textArea.removeFromLeft (comp.getWidth()); break; + case TabbedButtonBar::TabsAtLeft: extraComp = textArea.removeFromBottom (comp.getHeight()); break; + case TabbedButtonBar::TabsAtRight: extraComp = textArea.removeFromTop (comp.getHeight()); break; + default: jassertfalse; break; + } + } + else + { + switch (orientation) + { + case TabbedButtonBar::TabsAtBottom: + case TabbedButtonBar::TabsAtTop: extraComp = textArea.removeFromRight (comp.getWidth()); break; + case TabbedButtonBar::TabsAtLeft: extraComp = textArea.removeFromTop (comp.getHeight()); break; + case TabbedButtonBar::TabsAtRight: extraComp = textArea.removeFromBottom (comp.getHeight()); break; + default: jassertfalse; break; + } + } + + return extraComp; +} + +void LookAndFeel_V2::createTabButtonShape (TabBarButton& button, Path& p, bool /*isMouseOver*/, bool /*isMouseDown*/) +{ + const Rectangle activeArea (button.getActiveArea()); + const float w = (float) activeArea.getWidth(); + const float h = (float) activeArea.getHeight(); + + float length = w; + float depth = h; + + if (button.getTabbedButtonBar().isVertical()) + std::swap (length, depth); + + const float indent = (float) getTabButtonOverlap ((int) depth); + const float overhang = 4.0f; + + switch (button.getTabbedButtonBar().getOrientation()) + { + case TabbedButtonBar::TabsAtLeft: + p.startNewSubPath (w, 0.0f); + p.lineTo (0.0f, indent); + p.lineTo (0.0f, h - indent); + p.lineTo (w, h); + p.lineTo (w + overhang, h + overhang); + p.lineTo (w + overhang, -overhang); + break; + + case TabbedButtonBar::TabsAtRight: + p.startNewSubPath (0.0f, 0.0f); + p.lineTo (w, indent); + p.lineTo (w, h - indent); + p.lineTo (0.0f, h); + p.lineTo (-overhang, h + overhang); + p.lineTo (-overhang, -overhang); + break; + + case TabbedButtonBar::TabsAtBottom: + p.startNewSubPath (0.0f, 0.0f); + p.lineTo (indent, h); + p.lineTo (w - indent, h); + p.lineTo (w, 0.0f); + p.lineTo (w + overhang, -overhang); + p.lineTo (-overhang, -overhang); + break; + + default: + p.startNewSubPath (0.0f, h); + p.lineTo (indent, 0.0f); + p.lineTo (w - indent, 0.0f); + p.lineTo (w, h); + p.lineTo (w + overhang, h + overhang); + p.lineTo (-overhang, h + overhang); + break; + } + + p.closeSubPath(); + + p = p.createPathWithRoundedCorners (3.0f); +} + +void LookAndFeel_V2::fillTabButtonShape (TabBarButton& button, Graphics& g, const Path& path, bool /*isMouseOver*/, bool /*isMouseDown*/) +{ + const Colour tabBackground (button.getTabBackgroundColour()); + const bool isFrontTab = button.isFrontTab(); + + g.setColour (isFrontTab ? tabBackground + : tabBackground.withMultipliedAlpha (0.9f)); + + g.fillPath (path); + + g.setColour (button.findColour (isFrontTab ? TabbedButtonBar::frontOutlineColourId + : TabbedButtonBar::tabOutlineColourId, false) + .withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f)); + + g.strokePath (path, PathStrokeType (isFrontTab ? 1.0f : 0.5f)); +} + +void LookAndFeel_V2::drawTabButtonText (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown) +{ + const Rectangle area (button.getTextArea().toFloat()); + + float length = area.getWidth(); + float depth = area.getHeight(); + + if (button.getTabbedButtonBar().isVertical()) + std::swap (length, depth); + + Font font (depth * 0.6f); + font.setUnderline (button.hasKeyboardFocus (false)); + + AffineTransform t; + + switch (button.getTabbedButtonBar().getOrientation()) + { + case TabbedButtonBar::TabsAtLeft: t = t.rotated (float_Pi * -0.5f).translated (area.getX(), area.getBottom()); break; + case TabbedButtonBar::TabsAtRight: t = t.rotated (float_Pi * 0.5f).translated (area.getRight(), area.getY()); break; + case TabbedButtonBar::TabsAtTop: + case TabbedButtonBar::TabsAtBottom: t = t.translated (area.getX(), area.getY()); break; + default: jassertfalse; break; + } + + Colour col; + + if (button.isFrontTab() && (button.isColourSpecified (TabbedButtonBar::frontTextColourId) + || isColourSpecified (TabbedButtonBar::frontTextColourId))) + col = findColour (TabbedButtonBar::frontTextColourId); + else if (button.isColourSpecified (TabbedButtonBar::tabTextColourId) + || isColourSpecified (TabbedButtonBar::tabTextColourId)) + col = findColour (TabbedButtonBar::tabTextColourId); + else + col = button.getTabBackgroundColour().contrasting(); + + const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f; + + g.setColour (col.withMultipliedAlpha (alpha)); + g.setFont (font); + g.addTransform (t); + + g.drawFittedText (button.getButtonText().trim(), + 0, 0, (int) length, (int) depth, + Justification::centred, + jmax (1, ((int) depth) / 12)); +} + +void LookAndFeel_V2::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown) +{ + Path tabShape; + createTabButtonShape (button, tabShape, isMouseOver, isMouseDown); + + const Rectangle activeArea (button.getActiveArea()); + tabShape.applyTransform (AffineTransform::translation ((float) activeArea.getX(), + (float) activeArea.getY())); + + DropShadow (Colours::black.withAlpha (0.5f), 2, Point (0, 1)).drawForPath (g, tabShape); + + fillTabButtonShape (button, g, tabShape, isMouseOver, isMouseDown); + drawTabButtonText (button, g, isMouseOver, isMouseDown); +} + +void LookAndFeel_V2::drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) {} + +void LookAndFeel_V2::drawTabAreaBehindFrontButton (TabbedButtonBar& bar, Graphics& g, const int w, const int h) +{ + const float shadowSize = 0.2f; + + Rectangle shadowRect, line; + ColourGradient gradient (Colours::black.withAlpha (bar.isEnabled() ? 0.3f : 0.15f), 0, 0, + Colours::transparentBlack, 0, 0, false); + + switch (bar.getOrientation()) + { + case TabbedButtonBar::TabsAtLeft: + gradient.point1.x = (float) w; + gradient.point2.x = w * (1.0f - shadowSize); + shadowRect.setBounds ((int) gradient.point2.x, 0, w - (int) gradient.point2.x, h); + line.setBounds (w - 1, 0, 1, h); + break; + + case TabbedButtonBar::TabsAtRight: + gradient.point2.x = w * shadowSize; + shadowRect.setBounds (0, 0, (int) gradient.point2.x, h); + line.setBounds (0, 0, 1, h); + break; + + case TabbedButtonBar::TabsAtTop: + gradient.point1.y = (float) h; + gradient.point2.y = h * (1.0f - shadowSize); + shadowRect.setBounds (0, (int) gradient.point2.y, w, h - (int) gradient.point2.y); + line.setBounds (0, h - 1, w, 1); + break; + + case TabbedButtonBar::TabsAtBottom: + gradient.point2.y = h * shadowSize; + shadowRect.setBounds (0, 0, w, (int) gradient.point2.y); + line.setBounds (0, 0, w, 1); + break; + + default: break; + } + + g.setGradientFill (gradient); + g.fillRect (shadowRect.expanded (2, 2)); + + g.setColour (Colour (0x80000000)); + g.fillRect (line); +} + +Button* LookAndFeel_V2::createTabBarExtrasButton() +{ + const float thickness = 7.0f; + const float indent = 22.0f; + + Path p; + p.addEllipse (-10.0f, -10.0f, 120.0f, 120.0f); + + DrawablePath ellipse; + ellipse.setPath (p); + ellipse.setFill (Colour (0x99ffffff)); + + p.clear(); + p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f); + p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f); + p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness); + p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness); + p.setUsingNonZeroWinding (false); + + DrawablePath dp; + dp.setPath (p); + dp.setFill (Colour (0x59000000)); + + DrawableComposite normalImage; + normalImage.addAndMakeVisible (ellipse.createCopy()); + normalImage.addAndMakeVisible (dp.createCopy()); + + dp.setFill (Colour (0xcc000000)); + + DrawableComposite overImage; + overImage.addAndMakeVisible (ellipse.createCopy()); + overImage.addAndMakeVisible (dp.createCopy()); + + DrawableButton* db = new DrawableButton ("tabs", DrawableButton::ImageFitted); + db->setImages (&normalImage, &overImage, nullptr); + return db; +} + + +//============================================================================== +void LookAndFeel_V2::drawTableHeaderBackground (Graphics& g, TableHeaderComponent& header) +{ + g.fillAll (Colours::white); + + Rectangle 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(), + false)); + g.fillRect (area); + + g.setColour (Colour (0x33000000)); + 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*/, + int width, int height, + bool isMouseOver, bool isMouseDown, + int columnFlags) +{ + if (isMouseDown) + g.fillAll (Colour (0x8899aadd)); + else if (isMouseOver) + g.fillAll (Colour (0x5599aadd)); + + Rectangle area (width, height); + area.reduce (4, 0); + + if ((columnFlags & (TableHeaderComponent::sortedForwards | TableHeaderComponent::sortedBackwards)) != 0) + { + Path sortArrow; + sortArrow.addTriangle (0.0f, 0.0f, + 0.5f, (columnFlags & TableHeaderComponent::sortedForwards) != 0 ? -0.8f : 0.8f, + 1.0f, 0.0f); + + g.setColour (Colour (0x99000000)); + g.fillPath (sortArrow, RectanglePlacement (RectanglePlacement::centred) + .getTransformToFit (sortArrow.getBounds(), + area.removeFromRight (height / 2).reduced (2).toFloat())); + } + + g.setColour (Colours::black); + g.setFont (Font (height * 0.5f, Font::bold)); + g.drawFittedText (columnName, area, Justification::centredLeft, 1); +} + +//============================================================================== +void LookAndFeel_V2::drawLasso (Graphics& g, Component& lassoComp) +{ + const int outlineThickness = 1; + + g.fillAll (lassoComp.findColour (0x1000440 /*lassoFillColourId*/)); + + g.setColour (lassoComp.findColour (0x1000441 /*lassoOutlineColourId*/)); + g.drawRect (lassoComp.getLocalBounds(), outlineThickness); +} + +//============================================================================== +void LookAndFeel_V2::paintToolbarBackground (Graphics& g, int w, int h, Toolbar& toolbar) +{ + const Colour background (toolbar.findColour (Toolbar::backgroundColourId)); + + g.setGradientFill (ColourGradient (background, 0.0f, 0.0f, + background.darker (0.1f), + toolbar.isVertical() ? w - 1.0f : 0.0f, + toolbar.isVertical() ? 0.0f : h - 1.0f, + false)); + g.fillAll(); +} + +Button* LookAndFeel_V2::createToolbarMissingItemsButton (Toolbar& /*toolbar*/) +{ + return createTabBarExtrasButton(); +} + +void LookAndFeel_V2::paintToolbarButtonBackground (Graphics& g, int /*width*/, int /*height*/, + bool isMouseOver, bool isMouseDown, + ToolbarItemComponent& component) +{ + if (isMouseDown) + g.fillAll (component.findColour (Toolbar::buttonMouseDownBackgroundColourId, true)); + else if (isMouseOver) + g.fillAll (component.findColour (Toolbar::buttonMouseOverBackgroundColourId, true)); +} + +void LookAndFeel_V2::paintToolbarButtonLabel (Graphics& g, int x, int y, int width, int height, + const String& text, ToolbarItemComponent& component) +{ + g.setColour (component.findColour (Toolbar::labelTextColourId, true) + .withAlpha (component.isEnabled() ? 1.0f : 0.25f)); + + const float fontHeight = jmin (14.0f, height * 0.85f); + g.setFont (fontHeight); + + g.drawFittedText (text, + x, y, width, height, + Justification::centred, + jmax (1, height / (int) fontHeight)); +} + +//============================================================================== +void LookAndFeel_V2::drawPropertyPanelSectionHeader (Graphics& g, const String& name, + bool isOpen, int width, int height) +{ + const float buttonSize = height * 0.75f; + const float buttonIndent = (height - buttonSize) * 0.5f; + + drawTreeviewPlusMinusBox (g, Rectangle (buttonIndent, buttonIndent, buttonSize, buttonSize), Colours::white, isOpen, false); + + const int textX = (int) (buttonIndent * 2.0f + buttonSize + 2.0f); + + g.setColour (Colours::black); + g.setFont (Font (height * 0.7f, Font::bold)); + g.drawText (name, textX, 0, width - textX - 4, height, Justification::centredLeft, true); +} + +void LookAndFeel_V2::drawPropertyComponentBackground (Graphics& g, int width, int height, + PropertyComponent&) +{ + g.setColour (Colour (0x66ffffff)); + g.fillRect (0, 0, width, height - 1); +} + +void LookAndFeel_V2::drawPropertyComponentLabel (Graphics& g, int, int height, + PropertyComponent& component) +{ + g.setColour (Colours::black); + + if (! component.isEnabled()) + g.setOpacity (0.6f); + + g.setFont (jmin (height, 24) * 0.65f); + + const Rectangle r (getPropertyComponentContentPosition (component)); + + g.drawFittedText (component.getName(), + 3, r.getY(), r.getX() - 5, r.getHeight(), + Justification::centredLeft, 2); +} + +Rectangle LookAndFeel_V2::getPropertyComponentContentPosition (PropertyComponent& component) +{ + const int textW = jmin (200, component.getWidth() / 3); + return Rectangle (textW, 1, component.getWidth() - textW - 1, component.getHeight() - 3); +} + +//============================================================================== +void LookAndFeel_V2::drawCallOutBoxBackground (CallOutBox& box, Graphics& g, + const Path& path, Image& cachedImage) +{ + if (cachedImage.isNull()) + { + cachedImage = Image (Image::ARGB, box.getWidth(), box.getHeight(), true); + Graphics g2 (cachedImage); + + DropShadow (Colours::black.withAlpha (0.7f), 8, Point (0, 2)).drawForPath (g2, path); + } + + g.setColour (Colours::black); + g.drawImageAt (cachedImage, 0, 0); + + g.setColour (Colour::greyLevel (0.23f).withAlpha (0.9f)); + g.fillPath (path); + + g.setColour (Colours::white.withAlpha (0.8f)); + g.strokePath (path, PathStrokeType (2.0f)); +} + + +//============================================================================== +AttributedString LookAndFeel_V2::createFileChooserHeaderText (const String& title, + const String& instructions) +{ + AttributedString s; + s.setJustification (Justification::centred); + + const Colour colour (findColour (FileChooserDialogBox::titleTextColourId)); + s.append (title + "\n\n", Font (17.0f, Font::bold), colour); + s.append (instructions, Font (14.0f), colour); + + return s; +} + +void LookAndFeel_V2::drawFileBrowserRow (Graphics& g, int width, int height, + const String& filename, Image* icon, + const String& fileSizeDescription, + const String& fileTimeDescription, + const bool isDirectory, + const bool isItemSelected, + const int /*itemIndex*/, + DirectoryContentsDisplayComponent& dcc) +{ + Component* const fileListComp = dynamic_cast (&dcc); + + if (isItemSelected) + g.fillAll (fileListComp != nullptr ? fileListComp->findColour (DirectoryContentsDisplayComponent::highlightColourId) + : findColour (DirectoryContentsDisplayComponent::highlightColourId)); + + const int x = 32; + g.setColour (Colours::black); + + if (icon != nullptr && icon->isValid()) + { + g.drawImageWithin (*icon, 2, 2, x - 4, height - 4, + RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, + false); + } + else + { + if (const Drawable* d = isDirectory ? getDefaultFolderImage() + : getDefaultDocumentFileImage()) + d->drawWithin (g, Rectangle (2.0f, 2.0f, x - 4.0f, height - 4.0f), + RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, 1.0f); + } + + g.setColour (fileListComp != nullptr ? fileListComp->findColour (DirectoryContentsDisplayComponent::textColourId) + : findColour (DirectoryContentsDisplayComponent::textColourId)); + g.setFont (height * 0.7f); + + if (width > 450 && ! isDirectory) + { + const int sizeX = roundToInt (width * 0.7f); + const int dateX = roundToInt (width * 0.8f); + + g.drawFittedText (filename, + x, 0, sizeX - x, height, + Justification::centredLeft, 1); + + g.setFont (height * 0.5f); + g.setColour (Colours::darkgrey); + + if (! isDirectory) + { + g.drawFittedText (fileSizeDescription, + sizeX, 0, dateX - sizeX - 8, height, + Justification::centredRight, 1); + + g.drawFittedText (fileTimeDescription, + dateX, 0, width - 8 - dateX, height, + Justification::centredRight, 1); + } + } + else + { + g.drawFittedText (filename, + x, 0, width - x, height, + Justification::centredLeft, 1); + + } +} + +Button* LookAndFeel_V2::createFileBrowserGoUpButton() +{ + DrawableButton* goUpButton = new DrawableButton ("up", DrawableButton::ImageOnButtonBackground); + + Path arrowPath; + arrowPath.addArrow (Line (50.0f, 100.0f, 50.0f, 0.0f), 40.0f, 100.0f, 50.0f); + + DrawablePath arrowImage; + arrowImage.setFill (Colours::black.withAlpha (0.4f)); + arrowImage.setPath (arrowPath); + + goUpButton->setImages (&arrowImage); + + return goUpButton; +} + +void LookAndFeel_V2::layoutFileBrowserComponent (FileBrowserComponent& browserComp, + DirectoryContentsDisplayComponent* fileListComponent, + FilePreviewComponent* previewComp, + ComboBox* currentPathBox, + TextEditor* filenameBox, + Button* goUpButton) +{ + const int x = 8; + int w = browserComp.getWidth() - x - x; + + if (previewComp != nullptr) + { + const int previewWidth = w / 3; + previewComp->setBounds (x + w - previewWidth, 0, previewWidth, browserComp.getHeight()); + + w -= previewWidth + 4; + } + + int y = 4; + + const int controlsHeight = 22; + const int bottomSectionHeight = controlsHeight + 8; + const int upButtonWidth = 50; + + currentPathBox->setBounds (x, y, w - upButtonWidth - 6, controlsHeight); + goUpButton->setBounds (x + w - upButtonWidth, y, upButtonWidth, controlsHeight); + + y += controlsHeight + 4; + + if (Component* const listAsComp = dynamic_cast (fileListComponent)) + { + listAsComp->setBounds (x, y, w, browserComp.getHeight() - y - bottomSectionHeight); + y = listAsComp->getBottom() + 4; + } + + filenameBox->setBounds (x + 50, y, w - 50, controlsHeight); +} + +// Pulls a drawable out of compressed valuetree data.. +static Drawable* loadDrawableFromData (const void* data, size_t numBytes) +{ + MemoryInputStream m (data, numBytes, false); + GZIPDecompressorInputStream gz (m); + ValueTree drawable (ValueTree::readFromStream (gz)); + return Drawable::createFromValueTree (drawable.getChild (0), nullptr); +} + +const Drawable* LookAndFeel_V2::getDefaultFolderImage() +{ + if (folderImage == nullptr) + { + static const unsigned char drawableData[] = + { 120,218,197,86,77,111,27,55,16,229,182,161,237,6,61,39,233,77,63,192,38,56,195,225,215,209,105,210,2,141,13,20,201,193,109,111,178,181,178,183,145,181,130,180,110,145,127,159,199,93,73,137,87,53,218,91,109,192,160,151,179,156,55,111,222,188,229,155,247, + 231,87,231,175,47,222,170,234,155,229,244,190,86,213,115,253,102,61,253,123,122,189,168,85,51,83,213,119,250,238,221,47,231,151,175,223,169,170,250,121,221,62,172,84,245,172,60,63,209,243,118,49,171,215,170,107,87,23,245,188,83,213,145,182,167,19,91, + 254,127,223,220,222,117,37,68,82,40,143,174,219,174,107,239,135,168,147,18,37,108,85,245,237,46,207,70,33,249,175,211,238,78,85,186,28,253,76,175,73,109,186,117,251,177,190,106,102,229,241,247,58,24,103,203,15,101,245,103,219,44,187,15,221,39,0,172,142, + 245,125,211,1,196,205,116,181,125,114,164,175,31,186,78,45,219,229,31,245,186,189,106,150,179,102,121,139,100,154,240,231,167,102,177,64,72,247,105,213,23,122,187,158,206,154,122,217,169,85,57,18,1,47,53,101,107,18,135,204,167,147,192,201,216,20,114, + 244,195,62,171,234,7,125,198,100,136,216,145,149,211,9,57,103,40,249,72,219,8,167,170,87,250,140,162,199,123,226,3,34,82,202,134,131,13,172,74,170,233,162,0,177,234,166,93,180,15,235,141,170,206,180,157,204,231,150,156,159,207,39,195,50,214,88,18,150, + 245,205,124,250,104,169,212,135,158,19,144,53,20,112,172,55,237,2,132,13,199,149,130,230,115,145,112,147,147,82,61,157,32,238,178,253,11,145,213,138,10,52,138,38,103,111,99,164,211,137,139,198,35,177,35,167,212,143,15,215,205,13,160,109,163,172,225,152, + 16,232,17,149,140,103,144,158,146,90,113,217,12,6,197,167,236,3,54,5,181,101,73,54,138,90,245,165,227,120,18,252,150,77,15,242,188,228,204,81,169,139,102,249,5,68,192,145,14,244,112,1,145,29,94,137,96,235,49,136,151,58,246,32,88,192,161,88,176,76,226, + 36,247,24,176,7,232,62,16,83,42,155,201,160,30,222,65,72,98,82,76,33,198,254,197,96,124,10,150,243,8,130,48,228,36,94,124,6,4,43,38,0,142,205,99,30,4,221,13,33,230,220,71,177,65,49,142,243,150,7,1,51,20,2,5,96,96,84,225,56,217,188,3,33,46,24,228,112, + 69,69,12,68,228,108,242,99,16,165,118,208,28,51,200,98,87,42,74,62,209,24,4,206,48,22,153,125,132,220,196,56,15,234,99,216,130,0,141,38,74,162,130,48,35,163,141,94,196,245,32,94,104,7,154,132,209,40,108,162,165,232,153,165,17,4,138,201,176,135,58,49, + 165,130,122,108,114,54,28,240,64,17,89,188,79,177,116,149,10,4,246,91,30,94,104,112,96,226,144,131,144,142,98,78,177,7,128,81,242,224,140,36,249,80,208,145,196,12,202,15,16,60,161,200,69,187,169,213,86,198,123,87,224,255,199,21,94,105,134,72,40,177,245, + 14,182,32,232,54,196,231,100,111,11,189,168,201,39,177,84,102,38,139,177,168,74,210,87,174,64,20,138,160,67,111,10,4,98,196,97,60,158,118,133,25,111,173,224,171,37,97,185,119,133,221,242,63,184,194,140,71,174,240,252,145,43,72,32,147,146,147,4,104,104, + 117,134,10,18,12,107,212,40,72,148,57,6,71,69,135,222,248,16,160,168,3,169,144,55,201,69,41,147,137,134,99,50,97,8,178,85,43,217,140,201,151,192,152,10,242,190,24,11,59,183,29,25,42,115,236,98,14,229,252,32,80,66,0,162,17,136,72,6,67,5,45,242,224,10, + 193,102,71,50,6,17,129,212,18,115,105,150,80,169,45,123,222,141,76,178,70,32,55,24,90,217,132,71,73,200,57,238,204,3,136,49,144,185,55,183,190,20,137,52,246,47,113,232,158,69,35,49,145,208,129,193,56,178,77,135,230,145,113,22,140,69,74,20,146,2,120,218, + 155,135,48,32,10,89,30,156,165,204,254,222,193,160,12,19,49,6,210,59,11,70,62,4,31,15,64,196,2,157,98,33,58,1,104,32,152,50,31,128,64,148,183,197,108,209,89,107,240,41,75,36,123,16,208,108,180,44,236,250,182,227,27,20,137,118,76,60,165,137,221,92,94, + 78,215,31,235,245,230,183,242,229,30,214,251,251,195,145,94,148,15,253,170,221,52,93,211,46,7,109,171,81,208,177,94,247,119,132,47,81,186,92,22,246,7,255,254,15,7,107,141,171,197,191,156,123,162,135,187,198,227,131,113,219,80,159,1,4,239,223,231,0,0 }; + + folderImage = loadDrawableFromData (drawableData, sizeof (drawableData)); + } + + return folderImage; +} + +const Drawable* LookAndFeel_V2::getDefaultDocumentFileImage() +{ + if (documentImage == nullptr) + { + static const unsigned char drawableData[] = + { 120,218,213,88,77,115,219,54,16,37,147,208,246,228,214,75,155,246,164,123,29,12,176,216,197,199,49,105,218,94,156,153,78,114,72,219,155,108,75,137,26,89,212,200,116,59,233,175,239,3,105,201,164,68,50,158,166,233,76,196,11,69,60,173,128,197,123,139,183, + 124,241,234,217,155,103,207,207,126,204,242,7,171,233,213,44,203,31,23,47,54,211,191,166,231,203,89,182,184,204,242,147,226,195,165,219,252,125,150,229,249,207,155,242,102,157,229,143,210,227,199,197,101,121,113,115,53,91,85,89,85,174,207,102,243,42, + 203,143,10,125,58,209,233,251,171,197,219,119,85,250,173,97,151,30,157,151,85,85,94,53,168,147,132,50,226,179,252,225,246,143,174,179,44,63,254,101,90,189,203,242,34,5,127,84,172,77,118,93,109,202,247,179,55,139,203,244,248,97,161,179,63,202,197,170, + 122,93,125,192,196,242,227,226,106,81,205,54,217,197,116,125,251,228,168,56,191,169,170,108,85,174,126,159,109,202,55,139,213,229,98,245,182,249,97,254,240,167,197,114,137,5,86,31,214,245,111,175,203,37,254,230,162,92,150,55,155,180,148,249,237,39,203, + 94,215,127,58,10,213,245,39,203,234,249,102,249,87,47,203,63,129,204,49,227,252,73,225,149,145,104,131,245,254,116,34,202,82,164,16,153,179,236,108,177,234,7,49,41,237,130,144,167,17,144,15,42,104,239,93,12,35,32,99,68,9,187,24,125,7,244,77,23,36,164, + 40,56,226,61,12,107,229,130,215,100,105,24,227,89,17,246,211,105,55,140,49,218,43,207,100,245,72,28,195,70,17,230,201,118,8,243,164,139,233,95,88,23,52,152,162,54,104,48,217,237,105,15,111,91,107,253,131,160,118,34,239,69,128,54,232,135,101,121,61,203, + 110,169,181,147,2,253,159,82,48,180,229,247,167,74,193,41,141,188,35,93,241,116,18,148,113,214,120,207,113,47,19,109,16,51,182,153,193,5,59,2,10,90,69,114,218,135,48,2,50,198,43,171,189,152,81,144,88,108,85,136,78,246,64,54,42,163,35,69,30,3,121,82,38, + 98,81,98,70,64,70,139,34,111,163,167,49,144,13,202,138,179,58,220,23,52,180,186,54,104,48,79,109,208,96,198,219,19,31,220,187,118,10,6,65,237,100,222,139,5,109,80,191,30,236,151,162,135,147,142,30,68,105,182,58,6,22,84,43,229,124,148,116,97,145,55,231, + 139,11,76,228,16,37,14,48,205,145,77,134,34,176,55,152,182,200,57,99,93,204,144,145,253,65,97,229,132,72,104,63,62,71,21,140,54,186,41,226,59,84,19,63,130,15,222,235,224,185,59,104,27,226,68,101,153,241,227,177,248,29,20,136,26,8,252,178,183,241,219, + 131,137,160,209,107,109,92,79,124,16,211,184,104,93,77,130,110,124,2,65,172,67,201,60,157,88,163,2,91,99,92,216,198,55,78,69,75,190,150,119,84,98,200,71,150,109,124,36,204,227,52,8,33,229,223,68,167,173,167,131,248,137,212,226,141,19,233,160,154,248, + 144,142,195,140,137,185,59,104,15,247,119,40,126,23,69,81,200,242,110,254,123,20,49,94,112,110,245,199,111,241,167,87,36,252,101,138,132,149,22,22,38,65,134,29,182,139,24,230,192,31,144,184,133,130,72,44,131,210,142,111,147,216,30,76,123,30,113,206,242, + 150,196,157,65,129,130,76,180,194,61,34,225,160,5,228,233,160,118,34,137,26,202,115,212,29,108,72,134,243,223,90,114,226,199,226,119,80,6,245,152,197,122,217,146,184,53,24,140,210,30,21,59,80,79,124,182,202,71,207,218,112,159,72,80,53,140,109,68,2,191, + 227,217,210,78,36,94,137,88,231,82,157,8,176,61,0,122,191,19,137,3,255,13,39,183,228,20,193,151,144,119,166,79,36,40,253,156,138,72,11,181,19,137,14,46,176,217,27,180,135,251,219,31,255,235,61,148,165,96,72,122,118,23,229,81,52,135,24,250,163,183,216, + 211,43,17,217,151,136,253,116,137,28,53,188,127,92,188,221,76,47,23,169,59,90,167,144,141,239,197,86,104,141,189,60,157,80,84,142,140,4,31,154,241,122,105,132,41,107,13,201,39,86,120,24,82,114,206,198,6,96,27,227,172,36,232,168,201,36,219,24,113,62,163, + 154,101,233,143,166,203,102,26,141,206,174,179,252,89,161,39,243,249,197,121,186,38,233,246,146,211,53,1,123,56,194,231,122,143,103,179,217,60,204,167,19,147,110,41,93,173,219,123,72,89,248,35,173,16,220,50,179,111,60,181,24,88,103,156,235,7,78,248,14, + 4,119,78,162,93,60,112,35,109,16,124,126,12,17,71,67,24,1,165,142,1,181,215,248,56,6,66,235,193,137,167,61,22,30,5,3,27,101,71,64,169,25,112,216,2,63,22,169,110,43,18,200,140,129,208,160,88,44,220,208,125,65,67,171,107,131,6,243,212,6,13,102,188,61,241, + 225,189,107,165,96,16,212,78,230,189,88,208,6,245,235,214,237,235,150,62,167,110,155,106,170,53,133,192,117,193,20,84,78,74,174,98,39,92,156,8,112,21,46,80,106,12,209,207,225,228,16,113,59,225,126,87,60,133,25,209,34,36,2,99,242,52,197,48,30,75,244,247, + 212,238,246,182,173,221,185,78,215,127,167,221,162,163,221,250,152,217,146,196,222,145,100,223,235,105,108,28,250,149,212,74,224,86,2,213,118,110,119,204,224,144,208,38,214,131,200,14,214,223,120,189,230,53,1,193,70,133,154,131,56,223,16,229,48,188,14, + 201,205,213,121,71,233,68,89,15,124,103,37,53,26,11,118,176,127,169,88,166,158,219,178,117,173,83,108,75,95,55,68,186,193,53,246,146,206,127,6,63,53,78,58,228,204,155,224,113,74,91,232,221,195,240,105,215,34,29,138,64,128,183,8,130,233,71,173,56,54,101, + 99,75,186,111,65,58,28,229,145,82,19,152,12,99,180,81,130,131,75,234,229,220,247,53,231,154,79,205,185,185,155,199,249,172,38,85,253,204,76,68,95,92,204,207,255,221,75,178,227,14,187,224,224,97,202,172,173,219,12,167,130,133,9,54,135,245,92,176,29,134, + 165,110,139,141,18,16,223,29,188,183,65,207,144,106,144,151,143,128,224,176,168,110,140,32,62,56,110,219,195,54,235,20,68,209,216,34,232,21,6,41,234,157,39,211,201,107,160,230,66,225,56,153,9,101,21,37,237,150,204,14,115,208,22,221,54,216,230,33,116, + 14,65,14,44,19,8,236,73,71,246,182,110,125,224,75,132,195,214,247,163,36,51,252,84,76,124,37,212,100,88,62,183,179,76,67,217,218,242,244,229,116,243,126,182,185,254,21,105,126,208,220,239,94,229,30,21,203,244,202,117,93,94,47,170,69,185,106,246,60,219, + 3,29,23,155,250,109,237,29,170,72,175,109,119,129,127,235,9,92,20,85,185,254,72,220,147,162,121,235,219,13,44,144,225,63,241,244,165,51,0,0 }; + + documentImage = loadDrawableFromData (drawableData, sizeof (drawableData)); + } + + return documentImage; +} + +//============================================================================== +void LookAndFeel_V2::drawLevelMeter (Graphics& g, int width, int height, float level) +{ + g.setColour (Colours::white.withAlpha (0.7f)); + g.fillRoundedRectangle (0.0f, 0.0f, (float) width, (float) height, 3.0f); + g.setColour (Colours::black.withAlpha (0.2f)); + g.drawRoundedRectangle (1.0f, 1.0f, width - 2.0f, height - 2.0f, 3.0f, 1.0f); + + const int totalBlocks = 7; + const int numBlocks = roundToInt (totalBlocks * level); + const float w = (width - 6.0f) / (float) totalBlocks; + + for (int i = 0; i < totalBlocks; ++i) + { + if (i >= numBlocks) + g.setColour (Colours::lightblue.withAlpha (0.6f)); + else + g.setColour (i < totalBlocks - 1 ? Colours::blue.withAlpha (0.5f) + : Colours::red); + + g.fillRoundedRectangle (3.0f + i * w + w * 0.1f, 3.0f, w * 0.8f, height - 6.0f, w * 0.4f); + } +} + +//============================================================================== +void LookAndFeel_V2::drawKeymapChangeButton (Graphics& g, int width, int height, Button& button, const String& keyDescription) +{ + const Colour textColour (button.findColour (0x100ad01 /*KeyMappingEditorComponent::textColourId*/, true)); + + if (keyDescription.isNotEmpty()) + { + if (button.isEnabled()) + { + const float alpha = button.isDown() ? 0.3f : (button.isOver() ? 0.15f : 0.08f); + g.fillAll (textColour.withAlpha (alpha)); + + g.setOpacity (0.3f); + drawBevel (g, 0, 0, width, height, 2); + } + + g.setColour (textColour); + g.setFont (height * 0.6f); + g.drawFittedText (keyDescription, + 3, 0, width - 6, height, + Justification::centred, 1); + } + else + { + const float thickness = 7.0f; + const float indent = 22.0f; + + Path p; + p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f); + p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f); + p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness); + p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness); + p.setUsingNonZeroWinding (false); + + g.setColour (textColour.withAlpha (button.isDown() ? 0.7f : (button.isOver() ? 0.5f : 0.3f))); + g.fillPath (p, p.getTransformToScaleToFit (2.0f, 2.0f, width - 4.0f, height - 4.0f, true)); + } + + if (button.hasKeyboardFocus (false)) + { + g.setColour (textColour.withAlpha (0.4f)); + g.drawRect (0, 0, width, height); + } +} + +//============================================================================== +void LookAndFeel_V2::drawBevel (Graphics& g, const int x, const int y, const int width, const int height, + const int bevelThickness, const Colour& topLeftColour, const Colour& bottomRightColour, + const bool useGradient, const bool sharpEdgeOnOutside) +{ + if (g.clipRegionIntersects (Rectangle (x, y, width, height))) + { + LowLevelGraphicsContext& context = g.getInternalContext(); + context.saveState(); + + for (int i = bevelThickness; --i >= 0;) + { + const float op = useGradient ? (sharpEdgeOnOutside ? bevelThickness - i : i) / (float) bevelThickness + : 1.0f; + + context.setFill (topLeftColour.withMultipliedAlpha (op)); + context.fillRect (Rectangle (x + i, y + i, width - i * 2, 1), false); + context.setFill (topLeftColour.withMultipliedAlpha (op * 0.75f)); + context.fillRect (Rectangle (x + i, y + i + 1, 1, height - i * 2 - 2), false); + context.setFill (bottomRightColour.withMultipliedAlpha (op)); + context.fillRect (Rectangle (x + i, y + height - i - 1, width - i * 2, 1), false); + context.setFill (bottomRightColour.withMultipliedAlpha (op * 0.75f)); + context.fillRect (Rectangle (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2), false); + } + + context.restoreState(); + } +} + +//============================================================================== +void LookAndFeel_V2::drawShinyButtonShape (Graphics& g, + float x, float y, float w, float h, + float maxCornerSize, + const Colour& baseColour, + const float strokeWidth, + const bool flatOnLeft, + const bool flatOnRight, + const bool flatOnTop, + const bool flatOnBottom) noexcept +{ + if (w <= strokeWidth * 1.1f || h <= strokeWidth * 1.1f) + return; + + const float cs = jmin (maxCornerSize, w * 0.5f, h * 0.5f); + + Path outline; + outline.addRoundedRectangle (x, y, w, h, cs, cs, + ! (flatOnLeft || flatOnTop), + ! (flatOnRight || flatOnTop), + ! (flatOnLeft || flatOnBottom), + ! (flatOnRight || flatOnBottom)); + + ColourGradient cg (baseColour, 0.0f, y, + baseColour.overlaidWith (Colour (0x070000ff)), 0.0f, y + h, + false); + + cg.addColour (0.5, baseColour.overlaidWith (Colour (0x33ffffff))); + cg.addColour (0.51, baseColour.overlaidWith (Colour (0x110000ff))); + + g.setGradientFill (cg); + g.fillPath (outline); + + g.setColour (Colour (0x80000000)); + g.strokePath (outline, PathStrokeType (strokeWidth)); +} + +//============================================================================== +void LookAndFeel_V2::drawGlassSphere (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, + const float outlineThickness) noexcept +{ + if (diameter <= outlineThickness) + return; + + Path p; + p.addEllipse (x, y, diameter, diameter); + + { + ColourGradient cg (Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y, + Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y + diameter, false); + + cg.addColour (0.4, Colours::white.overlaidWith (colour)); + + g.setGradientFill (cg); + g.fillPath (p); + } + + g.setGradientFill (ColourGradient (Colours::white, 0, y + diameter * 0.06f, + Colours::transparentWhite, 0, y + diameter * 0.3f, false)); + g.fillEllipse (x + diameter * 0.2f, y + diameter * 0.05f, diameter * 0.6f, diameter * 0.4f); + + ColourGradient cg (Colours::transparentBlack, + x + diameter * 0.5f, y + diameter * 0.5f, + Colours::black.withAlpha (0.5f * outlineThickness * colour.getFloatAlpha()), + x, y + diameter * 0.5f, true); + + cg.addColour (0.7, Colours::transparentBlack); + cg.addColour (0.8, Colours::black.withAlpha (0.1f * outlineThickness)); + + g.setGradientFill (cg); + g.fillPath (p); + + g.setColour (Colours::black.withAlpha (0.5f * colour.getFloatAlpha())); + g.drawEllipse (x, y, diameter, diameter, outlineThickness); +} + +//============================================================================== +void LookAndFeel_V2::drawGlassPointer (Graphics& g, + const float x, const float y, + const float diameter, + const Colour& colour, const float outlineThickness, + const int direction) noexcept +{ + if (diameter <= outlineThickness) + return; + + Path p; + p.startNewSubPath (x + diameter * 0.5f, y); + p.lineTo (x + diameter, y + diameter * 0.6f); + p.lineTo (x + diameter, y + diameter); + p.lineTo (x, y + diameter); + p.lineTo (x, y + diameter * 0.6f); + p.closeSubPath(); + + p.applyTransform (AffineTransform::rotation (direction * (float_Pi * 0.5f), x + diameter * 0.5f, y + diameter * 0.5f)); + + { + ColourGradient cg (Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y, + Colours::white.overlaidWith (colour.withMultipliedAlpha (0.3f)), 0, y + diameter, false); + + cg.addColour (0.4, Colours::white.overlaidWith (colour)); + + g.setGradientFill (cg); + g.fillPath (p); + } + + ColourGradient cg (Colours::transparentBlack, + x + diameter * 0.5f, y + diameter * 0.5f, + Colours::black.withAlpha (0.5f * outlineThickness * colour.getFloatAlpha()), + x - diameter * 0.2f, y + diameter * 0.5f, true); + + cg.addColour (0.5, Colours::transparentBlack); + cg.addColour (0.7, Colours::black.withAlpha (0.07f * outlineThickness)); + + g.setGradientFill (cg); + g.fillPath (p); + + g.setColour (Colours::black.withAlpha (0.5f * colour.getFloatAlpha())); + g.strokePath (p, PathStrokeType (outlineThickness)); +} + +//============================================================================== +void LookAndFeel_V2::drawGlassLozenge (Graphics& g, + const float x, const float y, + const float width, const float height, + const Colour& colour, + const float outlineThickness, + const float cornerSize, + const bool flatOnLeft, + const bool flatOnRight, + const bool flatOnTop, + const bool flatOnBottom) noexcept +{ + if (width <= outlineThickness || height <= outlineThickness) + return; + + const int intX = (int) x; + const int intY = (int) y; + const int intW = (int) width; + const int intH = (int) height; + + const float cs = cornerSize < 0 ? jmin (width * 0.5f, height * 0.5f) : cornerSize; + const float edgeBlurRadius = height * 0.75f + (height - cs * 2.0f); + const int intEdge = (int) edgeBlurRadius; + + Path outline; + outline.addRoundedRectangle (x, y, width, height, cs, cs, + ! (flatOnLeft || flatOnTop), + ! (flatOnRight || flatOnTop), + ! (flatOnLeft || flatOnBottom), + ! (flatOnRight || flatOnBottom)); + + { + ColourGradient cg (colour.darker (0.2f), 0, y, + colour.darker (0.2f), 0, y + height, false); + + cg.addColour (0.03, colour.withMultipliedAlpha (0.3f)); + cg.addColour (0.4, colour); + cg.addColour (0.97, colour.withMultipliedAlpha (0.3f)); + + g.setGradientFill (cg); + g.fillPath (outline); + } + + ColourGradient cg (Colours::transparentBlack, x + edgeBlurRadius, y + height * 0.5f, + colour.darker (0.2f), x, y + height * 0.5f, true); + + cg.addColour (jlimit (0.0, 1.0, 1.0 - (cs * 0.5f) / edgeBlurRadius), Colours::transparentBlack); + cg.addColour (jlimit (0.0, 1.0, 1.0 - (cs * 0.25f) / edgeBlurRadius), colour.darker (0.2f).withMultipliedAlpha (0.3f)); + + if (! (flatOnLeft || flatOnTop || flatOnBottom)) + { + g.saveState(); + g.setGradientFill (cg); + g.reduceClipRegion (intX, intY, intEdge, intH); + g.fillPath (outline); + g.restoreState(); + } + + if (! (flatOnRight || flatOnTop || flatOnBottom)) + { + cg.point1.setX (x + width - edgeBlurRadius); + cg.point2.setX (x + width); + + g.saveState(); + g.setGradientFill (cg); + g.reduceClipRegion (intX + intW - intEdge, intY, 2 + intEdge, intH); + g.fillPath (outline); + g.restoreState(); + } + + { + const float leftIndent = flatOnTop || flatOnLeft ? 0.0f : cs * 0.4f; + const float rightIndent = flatOnTop || flatOnRight ? 0.0f : cs * 0.4f; + + Path highlight; + highlight.addRoundedRectangle (x + leftIndent, + y + cs * 0.1f, + width - (leftIndent + rightIndent), + height * 0.4f, + cs * 0.4f, + cs * 0.4f, + ! (flatOnLeft || flatOnTop), + ! (flatOnRight || flatOnTop), + ! (flatOnLeft || flatOnBottom), + ! (flatOnRight || flatOnBottom)); + + g.setGradientFill (ColourGradient (colour.brighter (10.0f), 0, y + height * 0.06f, + Colours::transparentWhite, 0, y + height * 0.4f, false)); + g.fillPath (highlight); + } + + g.setColour (colour.darker().withMultipliedAlpha (1.5f)); + g.strokePath (outline, PathStrokeType (outlineThickness)); +} diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h new file mode 100644 index 000000000..d0d3119b4 --- /dev/null +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h @@ -0,0 +1,347 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_LOOKANDFEEL_V2_H_INCLUDED +#define JUCE_LOOKANDFEEL_V2_H_INCLUDED + + +//============================================================================== +/** + This LookAndFeel subclass implements the juce style from around 2008-12. + + @see LookAndFeel, LookAndFeel_V1, LookAndFeel_V3 +*/ +class JUCE_API LookAndFeel_V2 : public LookAndFeel +{ +public: + LookAndFeel_V2(); + ~LookAndFeel_V2(); + + //============================================================================== + void drawButtonBackground (Graphics&, Button& button, const Colour& backgroundColour, + bool isMouseOverButton, bool isButtonDown) override; + + Font getTextButtonFont (TextButton&) override; + + void drawButtonText (Graphics&, TextButton& button, + bool isMouseOverButton, bool isButtonDown) override; + + void drawToggleButton (Graphics&, ToggleButton& button, bool isMouseOverButton, bool isButtonDown) override; + + void changeToggleButtonWidthToFitText (ToggleButton&) override; + + void drawTickBox (Graphics&, Component&, + float x, float y, float w, float h, + bool ticked, bool isEnabled, bool isMouseOverButton, bool isButtonDown) override; + + void drawDrawableButton (Graphics&, DrawableButton&, bool isMouseOverButton, bool isButtonDown) override; + + //============================================================================== + AlertWindow* createAlertWindow (const String& title, const String& message, + const String& button1, + const String& button2, + const String& button3, + AlertWindow::AlertIconType iconType, + int numButtons, Component* associatedComponent) override; + + void drawAlertBox (Graphics&, AlertWindow&, const Rectangle& textArea, TextLayout&) override; + int getAlertBoxWindowFlags() override; + int getAlertWindowButtonHeight() override; + Font getAlertWindowMessageFont() override; + Font getAlertWindowFont() override; + + //============================================================================== + void drawProgressBar (Graphics&, ProgressBar&, int width, int height, double progress, const String& textToShow) override; + void drawSpinningWaitAnimation (Graphics&, const Colour& colour, int x, int y, int w, int h) override; + + //============================================================================== + bool areScrollbarButtonsVisible() override; + void drawScrollbarButton (Graphics& g, ScrollBar&, int width, int height, int buttonDirection, + bool isScrollbarVertical, bool isMouseOverButton, bool isButtonDown) override; + + void drawScrollbar (Graphics& g, ScrollBar&, int x, int y, int width, int height, + bool isScrollbarVertical, int thumbStartPosition, int thumbSize, + bool isMouseOver, bool isMouseDown) override; + + ImageEffectFilter* getScrollbarEffect() override; + int getMinimumScrollbarThumbSize (ScrollBar&) override; + int getDefaultScrollbarWidth() override; + int getScrollbarButtonSize (ScrollBar& scrollbar) override; + + //============================================================================== + Path getTickShape (float height) override; + Path getCrossShape (float height) override; + + //============================================================================== + void drawTreeviewPlusMinusBox (Graphics&, const Rectangle& area, + Colour backgroundColour, bool isOpen, bool isMouseOver) override; + bool areLinesDrawnForTreeView (TreeView&) override; + int getTreeViewIndentSize (TreeView&) override; + + //============================================================================== + void fillTextEditorBackground (Graphics&, int width, int height, TextEditor&) override; + void drawTextEditorOutline (Graphics&, int width, int height, TextEditor&) override; + CaretComponent* createCaretComponent (Component* keyFocusOwner) override; + + //============================================================================== + const Drawable* getDefaultFolderImage() override; + const Drawable* getDefaultDocumentFileImage() override; + + AttributedString createFileChooserHeaderText (const String& title, const String& instructions) override; + + void drawFileBrowserRow (Graphics&, int width, int height, + const String& filename, Image* icon, + const String& fileSizeDescription, const String& fileTimeDescription, + bool isDirectory, bool isItemSelected, int itemIndex, + DirectoryContentsDisplayComponent&) override; + + Button* createFileBrowserGoUpButton() override; + + void layoutFileBrowserComponent (FileBrowserComponent&, + DirectoryContentsDisplayComponent*, + FilePreviewComponent*, + ComboBox* currentPathBox, + TextEditor* filenameBox, + Button* goUpButton) override; + + //============================================================================== + void drawBubble (Graphics&, BubbleComponent&, const Point& tip, const Rectangle& body) override; + + void drawLasso (Graphics&, Component&) override; + + //============================================================================== + void drawPopupMenuBackground (Graphics&, int width, int height) override; + + void drawPopupMenuItem (Graphics&, int width, int height, + bool isSeparator, bool isActive, bool isHighlighted, bool isTicked, bool hasSubMenu, + const String& text, const String& shortcutKeyText, + Image* image, const Colour* textColour) override; + + Font getPopupMenuFont() override; + + void drawPopupMenuUpDownArrow (Graphics&, int width, int height, bool isScrollUpArrow) override; + + void getIdealPopupMenuItemSize (const String& text, bool isSeparator, int standardMenuItemHeight, + int& idealWidth, int& idealHeight) override; + int getMenuWindowFlags() override; + + void drawMenuBarBackground (Graphics&, int width, int height, bool isMouseOverBar, MenuBarComponent&) override; + int getMenuBarItemWidth (MenuBarComponent&, int itemIndex, const String& itemText) override; + Font getMenuBarFont (MenuBarComponent&, int itemIndex, const String& itemText) override; + int getDefaultMenuBarHeight() override; + + void drawMenuBarItem (Graphics&, int width, int height, + int itemIndex, const String& itemText, + bool isMouseOverItem, bool isMenuOpen, bool isMouseOverBar, + MenuBarComponent&) override; + + //============================================================================== + void drawComboBox (Graphics&, int width, int height, bool isButtonDown, + int buttonX, int buttonY, int buttonW, int buttonH, + ComboBox&) override; + Font getComboBoxFont (ComboBox&) override; + Label* createComboBoxTextBox (ComboBox&) override; + void positionComboBoxText (ComboBox&, Label&) override; + + //============================================================================== + void drawLabel (Graphics&, Label&) override; + Font getLabelFont (Label&) override; + + //============================================================================== + void drawLinearSlider (Graphics&, int x, int y, int width, int height, + float sliderPos, float minSliderPos, float maxSliderPos, + const Slider::SliderStyle, Slider&) override; + + void drawLinearSliderBackground (Graphics&, int x, int y, int width, int height, + float sliderPos, float minSliderPos, float maxSliderPos, + const Slider::SliderStyle, Slider&) override; + + void drawLinearSliderThumb (Graphics&, int x, int y, int width, int height, + float sliderPos, float minSliderPos, float maxSliderPos, + const Slider::SliderStyle, Slider&) override; + + void drawRotarySlider (Graphics&, int x, int y, int width, int height, + float sliderPosProportional, float rotaryStartAngle, float rotaryEndAngle, + Slider&) override; + + int getSliderThumbRadius (Slider&) override; + Button* createSliderButton (bool isIncrement) override; + Label* createSliderTextBox (Slider&) override; + ImageEffectFilter* getSliderEffect() override; + Font getSliderPopupFont() override; + int getSliderPopupPlacement() override; + + //============================================================================== + void getTooltipSize (const String& tipText, int& width, int& height) override; + void drawTooltip (Graphics&, const String& text, int width, int height) override; + + //============================================================================== + Button* createFilenameComponentBrowseButton (const String& text) override; + void layoutFilenameComponent (FilenameComponent& filenameComp, ComboBox* filenameBox, Button* browseButton) override; + + //============================================================================== + void drawConcertinaPanelHeader (Graphics&, const Rectangle& area, + bool isMouseOver, bool isMouseDown, + ConcertinaPanel&, Component& panel) override; + + //============================================================================== + void drawCornerResizer (Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) override; + void drawResizableFrame (Graphics&, int w, int h, const BorderSize&) override; + + //============================================================================== + void fillResizableWindowBackground (Graphics&, int w, int h, const BorderSize&, ResizableWindow&) override; + void drawResizableWindowBorder (Graphics&, int w, int h, const BorderSize& border, ResizableWindow&) override; + + //============================================================================== + void drawDocumentWindowTitleBar (DocumentWindow&, Graphics&, int w, int h, + int titleSpaceX, int titleSpaceW, + const Image* icon, bool drawTitleTextOnLeft) override; + + Button* createDocumentWindowButton (int buttonType) override; + + void positionDocumentWindowButtons (DocumentWindow&, + int titleBarX, int titleBarY, int titleBarW, int titleBarH, + Button* minimiseButton, + Button* maximiseButton, + Button* closeButton, + bool positionTitleBarButtonsOnLeft) override; + + //============================================================================== + DropShadower* createDropShadowerForComponent (Component*) override; + + //============================================================================== + void drawStretchableLayoutResizerBar (Graphics&, int w, int h, bool isVerticalBar, + bool isMouseOver, bool isMouseDragging) override; + + //============================================================================== + void drawGroupComponentOutline (Graphics&, int w, int h, const String& text, + const Justification&, GroupComponent&) override; + + //============================================================================== + int getTabButtonSpaceAroundImage() override; + int getTabButtonOverlap (int tabDepth) override; + int getTabButtonBestWidth (TabBarButton&, int tabDepth) override; + Rectangle getTabButtonExtraComponentBounds (const TabBarButton&, Rectangle& textArea, Component& extraComp) override; + + void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) override; + void drawTabButtonText (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) override; + void drawTabbedButtonBarBackground (TabbedButtonBar&, Graphics&) override; + void drawTabAreaBehindFrontButton (TabbedButtonBar&, Graphics&, int w, int h) override; + + void createTabButtonShape (TabBarButton&, Path& path, bool isMouseOver, bool isMouseDown) override; + void fillTabButtonShape (TabBarButton&, Graphics&, const Path& path, bool isMouseOver, bool isMouseDown) override; + + Button* createTabBarExtrasButton() override; + + //============================================================================== + void drawImageButton (Graphics&, Image*, + int imageX, int imageY, int imageW, int imageH, + const Colour& overlayColour, float imageOpacity, ImageButton&) override; + + //============================================================================== + 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 paintToolbarBackground (Graphics&, int width, int height, Toolbar&) override; + + Button* createToolbarMissingItemsButton (Toolbar&) override; + + void paintToolbarButtonBackground (Graphics&, int width, int height, + bool isMouseOver, bool isMouseDown, + ToolbarItemComponent&) override; + + void paintToolbarButtonLabel (Graphics&, int x, int y, int width, int height, + const String& text, ToolbarItemComponent&) override; + + //============================================================================== + void drawPropertyPanelSectionHeader (Graphics&, const String& name, bool isOpen, int width, int height) override; + void drawPropertyComponentBackground (Graphics&, int width, int height, PropertyComponent&) override; + void drawPropertyComponentLabel (Graphics&, int width, int height, PropertyComponent&) override; + Rectangle getPropertyComponentContentPosition (PropertyComponent&) override; + + //============================================================================== + void drawCallOutBoxBackground (CallOutBox&, Graphics&, const Path& path, Image& cachedImage) override; + + //============================================================================== + void drawLevelMeter (Graphics&, int width, int height, float level) override; + + void drawKeymapChangeButton (Graphics&, int width, int height, Button& button, const String& keyDescription) override; + + //============================================================================== + /** Draws a 3D raised (or indented) bevel using two colours. + + The bevel is drawn inside the given rectangle, and greater bevel thicknesses + extend inwards. + + The top-left colour is used for the top- and left-hand edges of the + bevel; the bottom-right colour is used for the bottom- and right-hand + edges. + + If useGradient is true, then the bevel fades out to make it look more curved + and less angular. If sharpEdgeOnOutside is true, the outside of the bevel is + sharp, and it fades towards the centre; if sharpEdgeOnOutside is false, then + the centre edges are sharp and it fades towards the outside. + */ + static void drawBevel (Graphics&, + int x, int y, int width, int height, + int bevelThickness, + const Colour& topLeftColour = Colours::white, + const Colour& bottomRightColour = Colours::black, + bool useGradient = true, + bool sharpEdgeOnOutside = true); + + /** Utility function to draw a shiny, glassy circle (for round LED-type buttons). */ + static void drawGlassSphere (Graphics&, float x, float y, float diameter, + const Colour& colour, float outlineThickness) noexcept; + + static void drawGlassPointer (Graphics&, float x, float y, float diameter, + const Colour& colour, float outlineThickness, int direction) noexcept; + + /** Utility function to draw a shiny, glassy oblong (for text buttons). */ + static void drawGlassLozenge (Graphics&, + float x, float y, float width, float height, + const Colour& colour, float outlineThickness, float cornerSize, + bool flatOnLeft, bool flatOnRight, bool flatOnTop, bool flatOnBottom) noexcept; + +private: + //============================================================================== + ScopedPointer folderImage, documentImage; + + void drawShinyButtonShape (Graphics&, + float x, float y, float w, float h, float maxCornerSize, + const Colour& baseColour, float strokeWidth, + bool flatOnLeft, bool flatOnRight, bool flatOnTop, bool flatOnBottom) noexcept; + + class GlassWindowButton; + class SliderLabelComp; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LookAndFeel_V2) +}; + + +#endif // JUCE_LOOKANDFEEL_H_INCLUDED diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp new file mode 100644 index 000000000..ac573ae59 --- /dev/null +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp @@ -0,0 +1,361 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +LookAndFeel_V3::LookAndFeel_V3() +{ + setColour (TreeView::selectedItemBackgroundColourId, Colour (0x301111ee)); + + const Colour textButtonColour (0xffeeeeff); + setColour (TextButton::buttonColourId, textButtonColour); + setColour (ComboBox::buttonColourId, textButtonColour); + + setColour (ScrollBar::thumbColourId, Colour::greyLevel (0.8f).contrasting().withAlpha (0.13f)); +} + +LookAndFeel_V3::~LookAndFeel_V3() {} + +bool LookAndFeel_V3::areScrollbarButtonsVisible() { return false; } + +void LookAndFeel_V3::drawStretchableLayoutResizerBar (Graphics& g, int /*w*/, int /*h*/, bool /*isVerticalBar*/, + bool isMouseOver, bool isMouseDragging) +{ + if (isMouseOver || isMouseDragging) + g.fillAll (Colours::yellow.withAlpha (0.4f)); +} + +void LookAndFeel_V3::drawScrollbar (Graphics& g, ScrollBar& scrollbar, int x, int y, int width, int height, + bool isScrollbarVertical, int thumbStartPosition, int thumbSize, bool isMouseOver, bool isMouseDown) +{ + Path thumbPath; + + if (thumbSize > 0) + { + const float thumbIndent = (isScrollbarVertical ? width : height) * 0.25f; + const float thumbIndentx2 = thumbIndent * 2.0f; + + if (isScrollbarVertical) + thumbPath.addRoundedRectangle (x + thumbIndent, thumbStartPosition + thumbIndent, + width - thumbIndentx2, thumbSize - thumbIndentx2, (width - thumbIndentx2) * 0.5f); + else + thumbPath.addRoundedRectangle (thumbStartPosition + thumbIndent, y + thumbIndent, + thumbSize - thumbIndentx2, height - thumbIndentx2, (height - thumbIndentx2) * 0.5f); + } + + Colour thumbCol (scrollbar.findColour (ScrollBar::thumbColourId, true)); + + if (isMouseOver || isMouseDown) + thumbCol = thumbCol.withMultipliedAlpha (2.0f); + + g.setColour (thumbCol); + g.fillPath (thumbPath); + + g.setColour (thumbCol.contrasting ((isMouseOver || isMouseDown) ? 0.2f : 0.1f)); + g.strokePath (thumbPath, PathStrokeType (1.0f)); +} + +void LookAndFeel_V3::drawConcertinaPanelHeader (Graphics& g, const Rectangle& area, + bool isMouseOver, bool /*isMouseDown*/, + ConcertinaPanel&, Component& panel) +{ + const Colour bkg (Colours::grey); + + g.setGradientFill (ColourGradient (Colours::white.withAlpha (isMouseOver ? 0.4f : 0.2f), 0, (float) area.getY(), + Colours::darkgrey.withAlpha (0.2f), 0, (float) area.getBottom(), false)); + g.fillAll(); + + g.setColour (bkg.contrasting().withAlpha (0.04f)); + g.fillRect (area.withHeight (1)); + g.fillRect (area.withTop (area.getBottom() - 1)); + + g.setColour (bkg.contrasting()); + g.setFont (Font (area.getHeight() * 0.6f).boldened()); + g.drawFittedText (panel.getName(), 4, 0, area.getWidth() - 6, area.getHeight(), Justification::centredLeft, 1); +} + +static void drawButtonShape (Graphics& g, const Path& outline, Colour baseColour, float height) +{ + const float mainBrightness = baseColour.getBrightness(); + const float mainAlpha = baseColour.getFloatAlpha(); + + g.setGradientFill (ColourGradient (baseColour.brighter (0.2f), 0.0f, 0.0f, + baseColour.darker (0.25f), 0.0f, height, false)); + g.fillPath (outline); + + g.setColour (Colours::white.withAlpha (0.4f * mainAlpha * mainBrightness * mainBrightness)); + g.strokePath (outline, PathStrokeType (1.0f), AffineTransform::translation (0.0f, 1.0f) + .scaled (1.0f, (height - 1.6f) / height)); + + g.setColour (Colours::black.withAlpha (0.4f * mainAlpha)); + g.strokePath (outline, PathStrokeType (1.0f)); +} + +void LookAndFeel_V3::drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour, + bool isMouseOverButton, bool isButtonDown) +{ + Colour baseColour (backgroundColour.withMultipliedSaturation (button.hasKeyboardFocus (true) ? 1.3f : 0.9f) + .withMultipliedAlpha (button.isEnabled() ? 0.9f : 0.5f)); + + if (isButtonDown || isMouseOverButton) + baseColour = baseColour.contrasting (isButtonDown ? 0.2f : 0.1f); + + const bool flatOnLeft = button.isConnectedOnLeft(); + const bool flatOnRight = button.isConnectedOnRight(); + const bool flatOnTop = button.isConnectedOnTop(); + const bool flatOnBottom = button.isConnectedOnBottom(); + + const float width = button.getWidth() - 1.0f; + const float height = button.getHeight() - 1.0f; + const float cornerSize = 4.0f; + + Path outline; + outline.addRoundedRectangle (0.5f, 0.5f, width, height, cornerSize, cornerSize, + ! (flatOnLeft || flatOnTop), + ! (flatOnRight || flatOnTop), + ! (flatOnLeft || flatOnBottom), + ! (flatOnRight || flatOnBottom)); + + drawButtonShape (g, outline, baseColour, height); +} + +void LookAndFeel_V3::drawTableHeaderBackground (Graphics& g, TableHeaderComponent& header) +{ + Rectangle r (header.getLocalBounds()); + + g.setColour (Colours::black.withAlpha (0.5f)); + g.fillRect (r.removeFromBottom (1)); + + g.setColour (Colours::white.withAlpha (0.6f)); + g.fillRect (r); + + g.setColour (Colours::black.withAlpha (0.5f)); + + for (int i = header.getNumColumns (true); --i >= 0;) + g.fillRect (header.getColumnPosition (i).removeFromRight (1)); +} + +int LookAndFeel_V3::getTabButtonOverlap (int /*tabDepth*/) { return -1; } +int LookAndFeel_V3::getTabButtonSpaceAroundImage() { return 1; } + +void LookAndFeel_V3::createTabTextLayout (const TabBarButton& button, float length, float depth, + Colour colour, TextLayout& textLayout) +{ + Font font (depth * 0.5f); + font.setUnderline (button.hasKeyboardFocus (false)); + + AttributedString s; + s.setJustification (Justification::centred); + s.append (button.getButtonText().trim(), font, colour); + + textLayout.createLayout (s, length); +} + +void LookAndFeel_V3::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown) +{ + const Rectangle activeArea (button.getActiveArea()); + + const TabbedButtonBar::Orientation o = button.getTabbedButtonBar().getOrientation(); + + const Colour bkg (button.getTabBackgroundColour()); + + if (button.getToggleState()) + { + g.setColour (bkg); + g.fillRect (activeArea); + } + else + { + Point p1, p2; + + switch (o) + { + case TabbedButtonBar::TabsAtBottom: p1 = activeArea.getBottomLeft(); p2 = activeArea.getTopLeft(); break; + case TabbedButtonBar::TabsAtTop: p1 = activeArea.getTopLeft(); p2 = activeArea.getBottomLeft(); break; + case TabbedButtonBar::TabsAtRight: p1 = activeArea.getTopRight(); p2 = activeArea.getTopLeft(); break; + case TabbedButtonBar::TabsAtLeft: p1 = activeArea.getTopLeft(); p2 = activeArea.getTopRight(); break; + default: jassertfalse; break; + } + + g.setGradientFill (ColourGradient (bkg.brighter (0.2f), (float) p1.x, (float) p1.y, + bkg.darker (0.1f), (float) p2.x, (float) p2.y, false)); + g.fillRect (activeArea); + } + + g.setColour (bkg.contrasting (0.3f)); + Rectangle r (activeArea); + + if (o != TabbedButtonBar::TabsAtBottom) g.fillRect (r.removeFromTop (1)); + if (o != TabbedButtonBar::TabsAtTop) g.fillRect (r.removeFromBottom (1)); + if (o != TabbedButtonBar::TabsAtRight) g.fillRect (r.removeFromLeft (1)); + if (o != TabbedButtonBar::TabsAtLeft) g.fillRect (r.removeFromRight (1)); + + const float alpha = button.isEnabled() ? ((isMouseOver || isMouseDown) ? 1.0f : 0.8f) : 0.3f; + const Colour col (bkg.contrasting().withMultipliedAlpha (alpha)); + + const Rectangle area (button.getTextArea().toFloat()); + + float length = area.getWidth(); + float depth = area.getHeight(); + + if (button.getTabbedButtonBar().isVertical()) + std::swap (length, depth); + + TextLayout textLayout; + createTabTextLayout (button, length, depth, col, textLayout); + + AffineTransform t; + + switch (o) + { + case TabbedButtonBar::TabsAtLeft: t = t.rotated (float_Pi * -0.5f).translated (area.getX(), area.getBottom()); break; + case TabbedButtonBar::TabsAtRight: t = t.rotated (float_Pi * 0.5f).translated (area.getRight(), area.getY()); break; + case TabbedButtonBar::TabsAtTop: + case TabbedButtonBar::TabsAtBottom: t = t.translated (area.getX(), area.getY()); break; + default: jassertfalse; break; + } + + g.addTransform (t); + textLayout.draw (g, Rectangle (length, depth)); +} + +void LookAndFeel_V3::drawTreeviewPlusMinusBox (Graphics& g, const Rectangle& area, + Colour backgroundColour, bool isOpen, bool isMouseOver) +{ + Path p; + p.addTriangle (0.0f, 0.0f, 1.0f, isOpen ? 0.0f : 0.5f, isOpen ? 0.5f : 0.0f, 1.0f); + + g.setColour (backgroundColour.contrasting().withAlpha (isMouseOver ? 0.5f : 0.3f)); + g.fillPath (p, p.getTransformToScaleToFit (area.reduced (2, area.getHeight() / 4), true)); +} + +bool LookAndFeel_V3::areLinesDrawnForTreeView (TreeView&) +{ + return false; +} + +int LookAndFeel_V3::getTreeViewIndentSize (TreeView&) +{ + return 20; +} + +void LookAndFeel_V3::drawComboBox (Graphics& g, int width, int height, const bool isButtonDown, + int buttonX, int buttonY, int buttonW, int buttonH, ComboBox& box) +{ + g.fillAll (box.findColour (ComboBox::backgroundColourId)); + + const Colour buttonColour (box.findColour (ComboBox::buttonColourId)); + + if (box.isEnabled() && box.hasKeyboardFocus (false)) + { + g.setColour (buttonColour); + g.drawRect (0, 0, width, height, 2); + } + else + { + g.setColour (box.findColour (ComboBox::outlineColourId)); + g.drawRect (0, 0, width, height); + } + + const float outlineThickness = box.isEnabled() ? (isButtonDown ? 1.2f : 0.5f) : 0.3f; + + Path buttonShape; + buttonShape.addRectangle (buttonX + outlineThickness, + buttonY + outlineThickness, + buttonW - outlineThickness * 2.0f, + buttonH - outlineThickness * 2.0f); + + drawButtonShape (g, buttonShape, + buttonColour.withMultipliedSaturation (box.hasKeyboardFocus (true) ? 1.3f : 0.9f) + .withMultipliedAlpha (box.isEnabled() ? 0.9f : 0.5f), + (float) height); + + if (box.isEnabled()) + { + const float arrowX = 0.3f; + const float arrowH = 0.2f; + + Path p; + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.45f - arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.45f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.45f); + + p.addTriangle (buttonX + buttonW * 0.5f, buttonY + buttonH * (0.55f + arrowH), + buttonX + buttonW * (1.0f - arrowX), buttonY + buttonH * 0.55f, + buttonX + buttonW * arrowX, buttonY + buttonH * 0.55f); + + g.setColour (box.findColour (ComboBox::arrowColourId)); + g.fillPath (p); + } +} + +void LookAndFeel_V3::drawPopupMenuBackground (Graphics& g, int width, int height) +{ + g.fillAll (findColour (PopupMenu::backgroundColourId)); + (void) width; (void) height; + + #if ! JUCE_MAC + g.setColour (findColour (PopupMenu::textColourId).withAlpha (0.6f)); + g.drawRect (0, 0, width, height); + #endif +} + +void LookAndFeel_V3::drawKeymapChangeButton (Graphics& g, int width, int height, + Button& button, const String& keyDescription) +{ + const Colour textColour (button.findColour (0x100ad01 /*KeyMappingEditorComponent::textColourId*/, true)); + + if (keyDescription.isNotEmpty()) + { + if (button.isEnabled()) + { + g.setColour (textColour.withAlpha (button.isDown() ? 0.4f : (button.isOver() ? 0.2f : 0.1f))); + g.fillRoundedRectangle (button.getLocalBounds().toFloat(), 4.0f); + g.drawRoundedRectangle (button.getLocalBounds().toFloat(), 4.0f, 1.0f); + } + + g.setColour (textColour); + g.setFont (height * 0.6f); + g.drawFittedText (keyDescription, 4, 0, width - 8, height, Justification::centred, 1); + } + else + { + const float thickness = 7.0f; + const float indent = 22.0f; + + Path p; + p.addEllipse (0.0f, 0.0f, 100.0f, 100.0f); + p.addRectangle (indent, 50.0f - thickness, 100.0f - indent * 2.0f, thickness * 2.0f); + p.addRectangle (50.0f - thickness, indent, thickness * 2.0f, 50.0f - indent - thickness); + p.addRectangle (50.0f - thickness, 50.0f + thickness, thickness * 2.0f, 50.0f - indent - thickness); + p.setUsingNonZeroWinding (false); + + g.setColour (textColour.darker(0.1f).withAlpha (button.isDown() ? 0.7f : (button.isOver() ? 0.5f : 0.3f))); + g.fillPath (p, p.getTransformToScaleToFit (2.0f, 2.0f, width - 4.0f, height - 4.0f, true)); + } + + if (button.hasKeyboardFocus (false)) + { + g.setColour (textColour.withAlpha (0.4f)); + g.drawRect (0, 0, width, height); + } +} diff --git a/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.h b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.h new file mode 100644 index 000000000..b761cc81b --- /dev/null +++ b/source/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.h @@ -0,0 +1,79 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_LOOKANDFEEL_V3_H_INCLUDED +#define JUCE_LOOKANDFEEL_V3_H_INCLUDED + +//============================================================================== +/** + The latest JUCE look-and-feel style, as introduced in 2013. + @see LookAndFeel, LookAndFeel_V1, LookAndFeel_V2 +*/ +class JUCE_API LookAndFeel_V3 : public LookAndFeel_V2 +{ +public: + LookAndFeel_V3(); + ~LookAndFeel_V3(); + + //============================================================================== + void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour, + bool isMouseOverButton, bool isButtonDown) override; + + void drawTableHeaderBackground (Graphics&, TableHeaderComponent&) override; + + void drawTreeviewPlusMinusBox (Graphics&, const Rectangle& area, + Colour backgroundColour, bool isOpen, bool isMouseOver) override; + bool areLinesDrawnForTreeView (TreeView&) override; + int getTreeViewIndentSize (TreeView&) override; + + void drawComboBox (Graphics& g, int width, int height, bool isButtonDown, + int buttonX, int buttonY, int buttonW, int buttonH, ComboBox& box) override; + + void drawKeymapChangeButton (Graphics& g, int width, int height, Button& button, const String& keyDescription) override; + + void drawPopupMenuBackground (Graphics& g, int width, int height) override; + + int getTabButtonOverlap (int tabDepth) override; + int getTabButtonSpaceAroundImage() override; + void drawTabButton (TabBarButton&, Graphics&, bool isMouseOver, bool isMouseDown) override; + + void drawStretchableLayoutResizerBar (Graphics&, int w, int h, bool isVerticalBar, bool isMouseOver, bool isMouseDragging) override; + + bool areScrollbarButtonsVisible() override; + + void drawScrollbar (Graphics&, ScrollBar&, int x, int y, int width, int height, bool isScrollbarVertical, + int thumbStartPosition, int thumbSize, bool isMouseOver, bool isMouseDown) override; + + void drawConcertinaPanelHeader (Graphics&, const Rectangle& area, bool isMouseOver, bool isMouseDown, + ConcertinaPanel&, Component&) override; + + static void createTabTextLayout (const TabBarButton& button, float length, float depth, Colour colour, TextLayout&); + +private: + Image backgroundTexture; + Colour backgroundTextureBaseColour; +}; + + +#endif // JUCE_LOOKANDFEEL_H_INCLUDED diff --git a/source/modules/juce_gui_basics/menus/juce_PopupMenu.h b/source/modules/juce_gui_basics/menus/juce_PopupMenu.h index 3a2033913..e4622d6a8 100644 --- a/source/modules/juce_gui_basics/menus/juce_PopupMenu.h +++ b/source/modules/juce_gui_basics/menus/juce_PopupMenu.h @@ -480,6 +480,62 @@ public: void addCustomItem (int itemResultID, CustomComponent* customComponent, const PopupMenu* optionalSubMenu = nullptr); + + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + menu drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + /** Fills the background of a popup menu component. */ + virtual void drawPopupMenuBackground (Graphics&, int width, int height) = 0; + + /** Draws one of the items in a popup menu. */ + virtual void drawPopupMenuItem (Graphics&, int width, int height, + bool isSeparator, bool isActive, bool isHighlighted, + bool isTicked, bool hasSubMenu, + const String& text, + const String& shortcutKeyText, + Image* icon, + const Colour* textColour) = 0; + + /** Returns the size and style of font to use in popup menus. */ + virtual Font getPopupMenuFont() = 0; + + virtual void drawPopupMenuUpDownArrow (Graphics&, + int width, int height, + bool isScrollUpArrow) = 0; + + /** Finds the best size for an item in a popup menu. */ + virtual void getIdealPopupMenuItemSize (const String& text, + bool isSeparator, + int standardMenuItemHeight, + int& idealWidth, + int& idealHeight) = 0; + + virtual int getMenuWindowFlags() = 0; + + virtual void drawMenuBarBackground (Graphics&, int width, int height, + bool isMouseOverBar, + MenuBarComponent&) = 0; + + virtual int getDefaultMenuBarHeight() = 0; + + virtual int getMenuBarItemWidth (MenuBarComponent&, int itemIndex, const String& itemText) = 0; + + virtual Font getMenuBarFont (MenuBarComponent&, int itemIndex, const String& itemText) = 0; + + virtual void drawMenuBarItem (Graphics&, int width, int height, + int itemIndex, + const String& itemText, + bool isMouseOverItem, + bool isMenuOpen, + bool isMouseOverBar, + MenuBarComponent&) = 0; + }; + private: //============================================================================== JUCE_PUBLIC_IN_DLL_BUILD (class Item) diff --git a/source/modules/juce_gui_basics/misc/juce_BubbleComponent.h b/source/modules/juce_gui_basics/misc/juce_BubbleComponent.h index 11c312042..200d27220 100644 --- a/source/modules/juce_gui_basics/misc/juce_BubbleComponent.h +++ b/source/modules/juce_gui_basics/misc/juce_BubbleComponent.h @@ -136,6 +136,19 @@ public: outlineColourId = 0x1000af1 /**< The colour to use for an outline around the bubble. */ }; + + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawBubble (Graphics&, BubbleComponent&, + const Point& positionOfTip, + const Rectangle& body) = 0; + }; + protected: //============================================================================== /** Subclasses should override this to return the size of the content they diff --git a/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp index fed413e8d..c7478d5c9 100644 --- a/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp @@ -315,26 +315,39 @@ public: } //============================================================================== - void handleMouseDownCallback (int index, float x, float y, int64 time) + void handleMouseDownCallback (int index, Point pos, int64 time) { - lastMousePos.setXY ((int) x, (int) y); - currentModifiers = currentModifiers.withoutMouseButtons(); - handleMouseEvent (index, lastMousePos, currentModifiers, time); - currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier); - handleMouseEvent (index, lastMousePos, currentModifiers, time); + lastMousePos = pos; + + // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before. + handleMouseEvent (index, pos.toInt(), currentModifiers.withoutMouseButtons(), time); + + if (isValidPeer (this)) + handleMouseDragCallback (index, pos, time); } - void handleMouseDragCallback (int index, float x, float y, int64 time) + void handleMouseDragCallback (int index, Point pos, int64 time) { - lastMousePos.setXY ((int) x, (int) y); - handleMouseEvent (index, lastMousePos, currentModifiers, time); + lastMousePos = pos; + + jassert (index < 64); + touchesDown = (touchesDown | (1 << (index & 63))); + currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier); + handleMouseEvent (index, pos.toInt(), currentModifiers.withoutMouseButtons() + .withFlags (ModifierKeys::leftButtonModifier), time); } - void handleMouseUpCallback (int index, float x, float y, int64 time) + void handleMouseUpCallback (int index, Point pos, int64 time) { - lastMousePos.setXY ((int) x, (int) y); - currentModifiers = currentModifiers.withoutMouseButtons(); - handleMouseEvent (index, lastMousePos, currentModifiers, time); + lastMousePos = pos; + + jassert (index < 64); + touchesDown = (touchesDown & ~(1 << (index & 63))); + + if (touchesDown == 0) + currentModifiers = currentModifiers.withoutMouseButtons(); + + handleMouseEvent (index, pos.toInt(), currentModifiers.withoutMouseButtons(), time); } void handleKeyDownCallback (int k, int kc) @@ -461,7 +474,8 @@ public: //============================================================================== static ModifierKeys currentModifiers; - static Point lastMousePos; + static Point lastMousePos; + static int64 touchesDown; private: //============================================================================== @@ -526,7 +540,8 @@ private: }; ModifierKeys AndroidComponentPeer::currentModifiers = 0; -Point AndroidComponentPeer::lastMousePos; +Point AndroidComponentPeer::lastMousePos; +int64 AndroidComponentPeer::touchesDown = 0; //============================================================================== #define JUCE_VIEW_CALLBACK(returnType, javaMethodName, params, juceMethodInvocation) \ @@ -537,9 +552,9 @@ Point AndroidComponentPeer::lastMousePos; } JUCE_VIEW_CALLBACK (void, handlePaint, (JNIEnv* env, jobject view, jlong host, jobject canvas), handlePaintCallback (env, canvas)) -JUCE_VIEW_CALLBACK (void, handleMouseDown, (JNIEnv* env, jobject view, jlong host, jint i, jfloat x, jfloat y, jlong time), handleMouseDownCallback (i, (float) x, (float) y, (int64) time)) -JUCE_VIEW_CALLBACK (void, handleMouseDrag, (JNIEnv* env, jobject view, jlong host, jint i, jfloat x, jfloat y, jlong time), handleMouseDragCallback (i, (float) x, (float) y, (int64) time)) -JUCE_VIEW_CALLBACK (void, handleMouseUp, (JNIEnv* env, jobject view, jlong host, jint i, jfloat x, jfloat y, jlong time), handleMouseUpCallback (i, (float) x, (float) y, (int64) time)) +JUCE_VIEW_CALLBACK (void, handleMouseDown, (JNIEnv* env, jobject view, jlong host, jint i, jfloat x, jfloat y, jlong time), handleMouseDownCallback (i, Point ((float) x, (float) y), (int64) time)) +JUCE_VIEW_CALLBACK (void, handleMouseDrag, (JNIEnv* env, jobject view, jlong host, jint i, jfloat x, jfloat y, jlong time), handleMouseDragCallback (i, Point ((float) x, (float) y), (int64) time)) +JUCE_VIEW_CALLBACK (void, handleMouseUp, (JNIEnv* env, jobject view, jlong host, jint i, jfloat x, jfloat y, jlong time), handleMouseUpCallback (i, Point ((float) x, (float) y), (int64) time)) JUCE_VIEW_CALLBACK (void, viewSizeChanged, (JNIEnv* env, jobject view, jlong host), handleMovedOrResized()) JUCE_VIEW_CALLBACK (void, focusChanged, (JNIEnv* env, jobject view, jlong host, jboolean hasFocus), handleFocusChangeCallback (hasFocus)) JUCE_VIEW_CALLBACK (void, handleKeyDown, (JNIEnv* env, jobject view, jlong host, jint k, jint kc), handleKeyDownCallback ((int) k, (int) kc)) @@ -582,7 +597,7 @@ bool MouseInputSource::SourceList::addSource() Point MouseInputSource::getCurrentRawMousePosition() { - return AndroidComponentPeer::lastMousePos; + return AndroidComponentPeer::lastMousePos.toInt(); } void MouseInputSource::setRawMousePosition (Point) diff --git a/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm b/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm index 3b27b6109..40cd3a8b4 100644 --- a/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm +++ b/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm @@ -499,6 +499,7 @@ UIViewComponentPeer::UIViewComponentPeer (Component& comp, const int windowStyle r.origin.y = [UIScreen mainScreen].bounds.size.height - (r.origin.y + r.size.height); window = [[JuceUIWindow alloc] init]; + window.hidden = true; window.autoresizesSubviews = NO; window.transform = CGAffineTransformIdentity; window.frame = r; diff --git a/source/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm b/source/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm index 5686bfc17..b09fcc47d 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm @@ -62,7 +62,6 @@ namespace MouseCursorHelpers return CustomMouseCursorInfo (im, (int) (hx * im.getWidth()), (int) (hy * im.getHeight())).create(); - jassertfalse; return nullptr; } } @@ -95,7 +94,16 @@ void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType ty case LeftEdgeResizeCursor: c = [NSCursor resizeLeftCursor]; break; case RightEdgeResizeCursor: c = [NSCursor resizeRightCursor]; break; case CrosshairCursor: c = [NSCursor crosshairCursor]; break; - case CopyingCursor: return MouseCursorHelpers::fromWebKitFile ("copyCursor.png", 0, 0); + + case CopyingCursor: + { + #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 + if (void* m = MouseCursorHelpers::fromWebKitFile ("copyCursor.png", 0, 0)) + return m; + #endif + c = [NSCursor dragCopyCursor]; // added in 10.6 + break; + } case UpDownResizeCursor: case TopEdgeResizeCursor: diff --git a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index ebda571bd..127851797 100644 --- a/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -1686,7 +1686,7 @@ struct JuceNSWindowClass : public ObjCClass addMethod (@selector (canBecomeKeyWindow), canBecomeKeyWindow, "c@:"); addMethod (@selector (becomeKeyWindow), becomeKeyWindow, "v@:"); addMethod (@selector (windowShouldClose:), windowShouldClose, "c@:@"); - addMethod (@selector (constrainFrameRect:toScreen:), constrainFrameRect, @encode (NSRect), "@:", @encode (NSRect*), "@"); + addMethod (@selector (constrainFrameRect:toScreen:), constrainFrameRect, @encode (NSRect), "@:", @encode (NSRect), "@"); addMethod (@selector (windowWillResize:toSize:), windowWillResize, @encode (NSSize), "@:@", @encode (NSSize)); addMethod (@selector (zoom:), zoom, "v@:@"); addMethod (@selector (windowWillMove:), windowWillMove, "v@:@"); diff --git a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp index 83559438d..aa3e0963e 100644 --- a/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp +++ b/source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp @@ -1807,13 +1807,10 @@ private: { const DWORD flags = inputInfo[i].dwFlags; - if ((flags & TOUCHEVENTF_PRIMARY) == 0 // primary events are handled by WM_LBUTTON etc - && (flags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE | TOUCHEVENTF_UP)) != 0) - { - if (! handleTouchInput (inputInfo[i], (flags & TOUCHEVENTF_DOWN) != 0, - (flags & TOUCHEVENTF_UP) != 0)) + if ((flags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE | TOUCHEVENTF_UP)) != 0) + if (! handleTouchInput (inputInfo[i], (flags & TOUCHEVENTF_PRIMARY) != 0, + (flags & TOUCHEVENTF_DOWN) != 0, (flags & TOUCHEVENTF_UP) != 0)) return 0; // abandon method if this window was deleted by the callback - } } } @@ -1821,7 +1818,7 @@ private: return 0; } - bool handleTouchInput (const TOUCHINPUT& touch, const bool isDown, const bool isUp) + bool handleTouchInput (const TOUCHINPUT& touch, const bool isPrimary, const bool isDown, const bool isUp) { bool isCancel = false; const int touchIndex = currentTouches.getIndexOfTouch (touch.dwID); @@ -1835,10 +1832,13 @@ private: currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier); modsToSend = currentModifiers; - // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before. - handleMouseEvent (touchIndex + 1, pos, modsToSend.withoutMouseButtons(), time); - if (! isValidPeer (this)) // (in case this component was deleted by the event) - return false; + if (! isPrimary) + { + // this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before. + handleMouseEvent (touchIndex, pos, modsToSend.withoutMouseButtons(), time); + if (! isValidPeer (this)) // (in case this component was deleted by the event) + return false; + } } else if (isUp) { @@ -1848,6 +1848,10 @@ private: if (! currentTouches.areAnyTouchesActive()) isCancel = true; } + else + { + modsToSend = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier); + } if (isCancel) { @@ -1855,13 +1859,16 @@ private: currentModifiers = currentModifiers.withoutMouseButtons(); } - handleMouseEvent (touchIndex + 1, pos, modsToSend, time); - if (! isValidPeer (this)) // (in case this component was deleted by the event) - return false; + if (! isPrimary) + { + handleMouseEvent (touchIndex, pos, modsToSend, time); + if (! isValidPeer (this)) // (in case this component was deleted by the event) + return false; + } - if (isUp || isCancel) + if ((isUp || isCancel) && ! isPrimary) { - handleMouseEvent (touchIndex + 1, Point (-10, -10), currentModifiers, time); + handleMouseEvent (touchIndex, Point (-10, -10), currentModifiers, time); if (! isValidPeer (this)) return false; } diff --git a/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h b/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h index 9914ea5cc..de45f3296 100644 --- a/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h +++ b/source/modules/juce_gui_basics/properties/juce_PropertyComponent.h @@ -102,6 +102,18 @@ public: /** By default, this just repaints the component. */ void enablementChanged() override; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawPropertyPanelSectionHeader (Graphics&, const String& name, bool isOpen, int width, int height) = 0; + virtual void drawPropertyComponentBackground (Graphics&, int width, int height, PropertyComponent&) = 0; + virtual void drawPropertyComponentLabel (Graphics&, int width, int height, PropertyComponent&) = 0; + virtual Rectangle getPropertyComponentContentPosition (PropertyComponent&) = 0; + }; + protected: /** Used by the PropertyPanel to determine how high this component needs to be. A subclass can update this value in its constructor but shouldn't alter it later diff --git a/source/modules/juce_gui_basics/widgets/juce_ComboBox.h b/source/modules/juce_gui_basics/widgets/juce_ComboBox.h index f03579e45..b258adff0 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ComboBox.h +++ b/source/modules/juce_gui_basics/widgets/juce_ComboBox.h @@ -333,6 +333,25 @@ public: arrowColourId = 0x1000e00, /**< The colour for the arrow shape that pops up the menu */ }; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + ComboBox functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawComboBox (Graphics&, int width, int height, bool isButtonDown, + int buttonX, int buttonY, int buttonW, int buttonH, + ComboBox&) = 0; + + virtual Font getComboBoxFont (ComboBox&) = 0; + + virtual Label* createComboBoxTextBox (ComboBox&) = 0; + + virtual void positionComboBoxText (ComboBox&, Label& labelToPosition) = 0; + }; + //============================================================================== /** @internal */ void labelTextChanged (Label*) override; diff --git a/source/modules/juce_gui_basics/widgets/juce_Label.h b/source/modules/juce_gui_basics/widgets/juce_Label.h index fae999aed..909474624 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Label.h +++ b/source/modules/juce_gui_basics/widgets/juce_Label.h @@ -249,6 +249,18 @@ public: /** Returns the currently-visible text editor, or nullptr if none is open. */ TextEditor* getCurrentTextEditor() const noexcept; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + label drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawLabel (Graphics&, Label&) = 0; + virtual Font getLabelFont (Label&) = 0; + }; + protected: //============================================================================== /** Creates the TextEditor component that will be used when the user has clicked on the label. diff --git a/source/modules/juce_gui_basics/widgets/juce_ProgressBar.h b/source/modules/juce_gui_basics/widgets/juce_ProgressBar.h index da7db4642..eae21cc98 100644 --- a/source/modules/juce_gui_basics/widgets/juce_ProgressBar.h +++ b/source/modules/juce_gui_basics/widgets/juce_ProgressBar.h @@ -90,6 +90,24 @@ public: classes will probably use variations on this colour. */ }; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + /** Draws a progress bar. + + If the progress value is less than 0 or greater than 1.0, this should draw a spinning + bar that fills the whole space (i.e. to say that the app is still busy but the progress + isn't known). It can use the current time as a basis for playing an animation. + + (Used by progress bars in AlertWindow). + */ + virtual void drawProgressBar (Graphics&, ProgressBar&, int width, int height, + double progress, const String& textToShow) = 0; + }; + protected: //============================================================================== /** @internal */ diff --git a/source/modules/juce_gui_basics/widgets/juce_Slider.h b/source/modules/juce_gui_basics/widgets/juce_Slider.h index a6bb200d1..cec0e3518 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Slider.h +++ b/source/modules/juce_gui_basics/widgets/juce_Slider.h @@ -762,6 +762,57 @@ public: textBoxOutlineColourId = 0x1001700 /**< The colour to use for a border around the text-editor box. */ }; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + slider drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + //============================================================================== + virtual void drawLinearSlider (Graphics&, + int x, int y, int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle, + Slider&) = 0; + + virtual void drawLinearSliderBackground (Graphics&, + int x, int y, int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle style, + Slider&) = 0; + + virtual void drawLinearSliderThumb (Graphics&, + int x, int y, int width, int height, + float sliderPos, + float minSliderPos, + float maxSliderPos, + const Slider::SliderStyle, + Slider&) = 0; + + virtual int getSliderThumbRadius (Slider&) = 0; + + virtual void drawRotarySlider (Graphics&, + int x, int y, int width, int height, + float sliderPosProportional, + float rotaryStartAngle, + float rotaryEndAngle, + Slider&) = 0; + + virtual Button* createSliderButton (bool isIncrement) = 0; + virtual Label* createSliderTextBox (Slider&) = 0; + + virtual ImageEffectFilter* getSliderEffect() = 0; + + virtual Font getSliderPopupFont() = 0; + virtual int getSliderPopupPlacement() = 0; + }; + protected: //============================================================================== /** @internal */ diff --git a/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h b/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h index 845dec453..b9eba92d4 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h +++ b/source/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.h @@ -366,6 +366,19 @@ public: */ virtual void reactToMenuItem (int menuReturnId, int columnIdClicked); + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawTableHeaderBackground (Graphics&, TableHeaderComponent&) = 0; + + virtual void drawTableHeaderColumn (Graphics&, const String& columnName, int columnId, + int width, int height, + bool isMouseOver, bool isMouseDown, int columnFlags) = 0; + }; + //============================================================================== /** @internal */ void paint (Graphics&) override; diff --git a/source/modules/juce_gui_basics/widgets/juce_TextEditor.h b/source/modules/juce_gui_basics/widgets/juce_TextEditor.h index f77f7f819..f4a9784ba 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TextEditor.h +++ b/source/modules/juce_gui_basics/widgets/juce_TextEditor.h @@ -580,6 +580,20 @@ public: void setInputRestrictions (int maxTextLength, const String& allowedCharacters = String::empty); + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + TextEditor drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void fillTextEditorBackground (Graphics&, int width, int height, TextEditor&) = 0; + virtual void drawTextEditorOutline (Graphics&, int width, int height, TextEditor&) = 0; + + virtual CaretComponent* createCaretComponent (Component* keyFocusOwner) = 0; + }; + //============================================================================== /** @internal */ void paint (Graphics&) override; diff --git a/source/modules/juce_gui_basics/widgets/juce_Toolbar.h b/source/modules/juce_gui_basics/widgets/juce_Toolbar.h index d9505812f..1a85726e5 100644 --- a/source/modules/juce_gui_basics/widgets/juce_Toolbar.h +++ b/source/modules/juce_gui_basics/widgets/juce_Toolbar.h @@ -267,6 +267,24 @@ public: bool restoreFromString (ToolbarItemFactory& factoryToUse, const String& savedVersion); + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void paintToolbarBackground (Graphics&, int width, int height, Toolbar&) = 0; + + virtual Button* createToolbarMissingItemsButton (Toolbar&) = 0; + + virtual void paintToolbarButtonBackground (Graphics&, int width, int height, + bool isMouseOver, bool isMouseDown, + ToolbarItemComponent&) = 0; + + virtual void paintToolbarButtonLabel (Graphics&, int x, int y, int width, int height, + const String& text, ToolbarItemComponent&) = 0; + }; + //============================================================================== /** @internal */ void paint (Graphics&) override; diff --git a/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp index c7672e0cb..3a49df3ec 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/source/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -439,7 +439,7 @@ TreeView::TreeView (const String& name) : Component (name), viewport (new TreeViewport()), rootItem (nullptr), - indentSize (24), + indentSize (-1), defaultOpenness (false), needsRecalculating (true), rootItemVisible (true), @@ -522,6 +522,12 @@ void TreeView::setIndentSize (const int newIndentSize) } } +int TreeView::getIndentSize() noexcept +{ + return indentSize >= 0 ? indentSize + : getLookAndFeel().getTreeViewIndentSize (*this); +} + void TreeView::setDefaultOpenness (const bool isOpenByDefault) { if (defaultOpenness != isOpenByDefault) @@ -860,7 +866,7 @@ void TreeView::recalculateIfNeeded() //============================================================================== struct TreeView::InsertPoint { - InsertPoint (const TreeView& view, const StringArray& files, + InsertPoint (TreeView& view, const StringArray& files, const DragAndDropTarget::SourceDetails& dragSourceDetails) : pos (dragSourceDetails.localPosition), item (view.getItemAt (dragSourceDetails.localPosition.y)), @@ -1125,7 +1131,8 @@ TreeViewItem::TreeViewItem() totalWidth (0), selected (false), redrawNeeded (true), - drawLinesInside (true), + drawLinesInside (false), + drawLinesSet (false), drawsInLeftMargin (false), openness (opennessDefault) { @@ -1317,10 +1324,10 @@ void TreeViewItem::paintItem (Graphics&, int, int) { } -void TreeViewItem::paintOpenCloseButton (Graphics& g, int width, int height, bool isMouseOver) +void TreeViewItem::paintOpenCloseButton (Graphics& g, const Rectangle& area, Colour backgroundColour, bool isMouseOver) { - ownerView->getLookAndFeel() - .drawTreeviewPlusMinusBox (g, 0, 0, width, height, ! isOpen(), isMouseOver); + getOwnerView()->getLookAndFeel() + .drawTreeviewPlusMinusBox (g, area, backgroundColour, isOpen(), isMouseOver); } void TreeViewItem::paintHorizontalConnectingLine (Graphics& g, const Line& line) @@ -1495,6 +1502,12 @@ namespace TreeViewHelpers } } +bool TreeViewItem::areLinesDrawn() const +{ + return drawLinesSet ? drawLinesInside + : (ownerView != nullptr && ownerView->getLookAndFeel().areLinesDrawnForTreeView (*ownerView)); +} + void TreeViewItem::paintRecursively (Graphics& g, int width) { jassert (ownerView != nullptr); @@ -1510,7 +1523,12 @@ void TreeViewItem::paintRecursively (Graphics& g, int width) if (g.reduceClipRegion (drawsInLeftMargin ? -indent : 0, 0, drawsInLeftMargin ? itemW + indent : itemW, itemHeight)) + { + if (isSelected()) + g.fillAll (ownerView->findColour (TreeView::selectedItemBackgroundColourId)); + paintItem (g, itemW, itemHeight); + } } const float halfH = itemHeight * 0.5f; @@ -1521,11 +1539,12 @@ void TreeViewItem::paintRecursively (Graphics& g, int width) { float x = (depth + 0.5f) * indentWidth; - if (parentItem != nullptr && parentItem->drawLinesInside) + const bool parentLinesDrawn = parentItem != nullptr && parentItem->areLinesDrawn(); + + if (parentLinesDrawn) paintVerticalConnectingLine (g, Line (x, 0, x, isLastOfSiblings() ? halfH : (float) itemHeight)); - if ((parentItem != nullptr && parentItem->drawLinesInside) - || (parentItem == nullptr && drawLinesInside)) + if (parentLinesDrawn || (parentItem == nullptr && areLinesDrawn())) paintHorizontalConnectingLine (g, Line (x, halfH, x + indentWidth / 2, halfH)); { @@ -1536,8 +1555,7 @@ void TreeViewItem::paintRecursively (Graphics& g, int width) { x -= (float) indentWidth; - if ((p->parentItem == nullptr || p->parentItem->drawLinesInside) - && ! p->isLastOfSiblings()) + if ((p->parentItem == nullptr || p->parentItem->areLinesDrawn()) && ! p->isLastOfSiblings()) p->paintVerticalConnectingLine (g, Line (x, 0, x, (float) itemHeight)); p = p->parentItem; @@ -1545,15 +1563,9 @@ void TreeViewItem::paintRecursively (Graphics& g, int width) } if (mightContainSubItems()) - { - Graphics::ScopedSaveState ss (g); - - g.setOrigin (depth * indentWidth, 0); - g.reduceClipRegion (0, 0, indentWidth, itemHeight); - - paintOpenCloseButton (g, indentWidth, itemHeight, + paintOpenCloseButton (g, Rectangle ((float) (depth * indentWidth), 0, (float) indentWidth, (float) itemHeight), + Colours::white, ownerView->viewport->getContentComp()->isMouseOverButton (this)); - } } if (isOpen()) @@ -1731,6 +1743,7 @@ int TreeViewItem::getRowNumberInTree() const noexcept void TreeViewItem::setLinesDrawnForSubItems (const bool drawLines) noexcept { drawLinesInside = drawLines; + drawLinesSet = true; } TreeViewItem* TreeViewItem::getNextVisibleItem (const bool recurse) const noexcept @@ -1840,8 +1853,8 @@ XmlElement* TreeViewItem::getOpennessState (const bool canReturnNull) const e = new XmlElement ("OPEN"); - for (int i = 0; i < subItems.size(); ++i) - e->addChildElement (subItems.getUnchecked(i)->getOpennessState (true)); + for (int i = subItems.size(); --i >= 0;) + e->prependChildElement (subItems.getUnchecked(i)->getOpennessState (true)); } else { diff --git a/source/modules/juce_gui_basics/widgets/juce_TreeView.h b/source/modules/juce_gui_basics/widgets/juce_TreeView.h index 5e7504666..2b18c6728 100644 --- a/source/modules/juce_gui_basics/widgets/juce_TreeView.h +++ b/source/modules/juce_gui_basics/widgets/juce_TreeView.h @@ -83,6 +83,20 @@ public: */ void addSubItem (TreeViewItem* newItem, int insertPosition = -1); + /** Adds a sub-item with a sort-comparator, assuming that the existing items are already sorted. + + @param comparator the comparator object for sorting - see sortSubItems() for details about + the methods this class must provide. + @param newItem the object to add to the item's sub-item list. Once added, these can be + found using getSubItem(). When the items are later removed with + removeSubItem() (or when this item is deleted), they will be deleted. + */ + template + void addSubItemSorted (ElementComparator& comparator, TreeViewItem* newItem) + { + addSubItem (newItem, findInsertIndexInSortedArray (comparator, subItems.begin(), newItem, 0, subItems.size())); + } + /** Removes one of the sub-items. @param index the item to remove @@ -178,7 +192,7 @@ public: bool areAllParentsOpen() const noexcept; /** Changes whether lines are drawn to connect any sub-items to this item. - By default, line-drawing is turned on. + By default, line-drawing is turned on according to LookAndFeel::areLinesDrawnForTreeView(). */ void setLinesDrawnForSubItems (bool shouldDrawLines) noexcept; @@ -299,11 +313,14 @@ public: /** Draws the item's open/close button. - If you don't implement this method, the default behaviour is to - call LookAndFeel::drawTreeviewPlusMinusBox(), but you can override - it for custom effects. + If you don't implement this method, the default behaviour is to call + LookAndFeel::drawTreeviewPlusMinusBox(), but you can override it for custom + effects. You may want to override it and call the base-class implementation + with a different backgroundColour parameter, if your implementation has a + background colour other than the default (white). */ - virtual void paintOpenCloseButton (Graphics&, int width, int height, bool isMouseOver); + virtual void paintOpenCloseButton (Graphics&, const Rectangle& area, + Colour backgroundColour, bool isMouseOver); /** Draws the line that connects this item to the vertical line extending below its parent. */ virtual void paintHorizontalConnectingLine (Graphics&, const Line& line); @@ -519,12 +536,13 @@ private: //============================================================================== TreeView* ownerView; TreeViewItem* parentItem; - OwnedArray subItems; + OwnedArray subItems; int y, itemHeight, totalHeight, itemWidth, totalWidth; int uid; bool selected : 1; bool redrawNeeded : 1; bool drawLinesInside : 1; + bool drawLinesSet : 1; bool drawsInLeftMargin : 1; unsigned int openness : 2; @@ -549,6 +567,7 @@ private: XmlElement* getOpennessState (bool canReturnNull) const; bool removeSubItemFromList (int index, bool deleteItem); void removeAllSubItemsFromList(); + bool areLinesDrawn() const; #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // The parameters for these methods have changed - please update your code! @@ -721,7 +740,7 @@ public: /** Returns the number of pixels by which each nested level of the tree is indented. @see setIndentSize */ - int getIndentSize() const noexcept { return indentSize; } + int getIndentSize() noexcept; /** Changes the distance by which each nested level of the tree is indented. @see getIndentSize @@ -779,9 +798,25 @@ public: */ enum ColourIds { - backgroundColourId = 0x1000500, /**< A background colour to fill the component with. */ - linesColourId = 0x1000501, /**< The colour to draw the lines with.*/ - dragAndDropIndicatorColourId = 0x1000502 /**< The colour to use for the drag-and-drop target position indicator. */ + backgroundColourId = 0x1000500, /**< A background colour to fill the component with. */ + linesColourId = 0x1000501, /**< The colour to draw the lines with.*/ + dragAndDropIndicatorColourId = 0x1000502, /**< The colour to use for the drag-and-drop target position indicator. */ + selectedItemBackgroundColourId = 0x1000503 /**< The colour to use to fill the background of any selected items. */ + }; + + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + treeview drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawTreeviewPlusMinusBox (Graphics&, const Rectangle& area, + Colour backgroundColour, bool isItemOpen, bool isMouseOver) = 0; + + virtual bool areLinesDrawnForTreeView (TreeView&) = 0; + virtual int getTreeViewIndentSize (TreeView&) = 0; }; //============================================================================== @@ -849,6 +884,11 @@ private: void moveIntoSelectedItem(); void moveByPages (int numPages); + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE + // this method has been deprecated - see the new version.. + virtual int paintOpenCloseButton (Graphics&, int, int, bool) { return 0; } + #endif + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeView) }; diff --git a/source/modules/juce_gui_basics/windows/juce_AlertWindow.h b/source/modules/juce_gui_basics/windows/juce_AlertWindow.h index 7180ab7ee..2d3601354 100644 --- a/source/modules/juce_gui_basics/windows/juce_AlertWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_AlertWindow.h @@ -417,6 +417,32 @@ public: outlineColourId = 0x1001820 /**< An optional colour to use to draw a border around the window. */ }; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + alert-window drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual AlertWindow* createAlertWindow (const String& title, const String& message, + const String& button1, + const String& button2, + const String& button3, + AlertWindow::AlertIconType iconType, + int numButtons, + Component* associatedComponent) = 0; + + virtual void drawAlertBox (Graphics&, AlertWindow&, const Rectangle& textArea, TextLayout&) = 0; + + virtual int getAlertBoxWindowFlags() = 0; + + virtual int getAlertWindowButtonHeight() = 0; + + virtual Font getAlertWindowMessageFont() = 0; + virtual Font getAlertWindowFont() = 0; + }; + protected: //============================================================================== /** @internal */ diff --git a/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp b/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp index 63d7459d7..f2fa91ba1 100644 --- a/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp +++ b/source/modules/juce_gui_basics/windows/juce_CallOutBox.cpp @@ -48,8 +48,11 @@ CallOutBox::~CallOutBox() { } +enum { callOutBoxDismissCommandId = 0x4f83a04b }; + //============================================================================== -class CallOutBoxCallback : public ModalComponentManager::Callback +class CallOutBoxCallback : public ModalComponentManager::Callback, + private Timer { public: CallOutBoxCallback (Component* c, const Rectangle& area, Component* parent) @@ -57,9 +60,16 @@ public: { callout.setVisible (true); callout.enterModalState (true, this); + startTimer (200); } - void modalStateFinished (int) {} + void modalStateFinished (int) override {} + + void timerCallback() override + { + if (! Process::isForegroundProcess()) + callout.postCommandMessage (callOutBoxDismissCommandId); + } ScopedPointer content; CallOutBox callout; @@ -110,8 +120,6 @@ bool CallOutBox::hitTest (int x, int y) return outline.contains ((float) x, (float) y); } -enum { callOutBoxDismissCommandId = 0x4f83a04b }; - void CallOutBox::inputAttemptWhenModal() { const Point mousePos (getMouseXYRelative() + getBounds().getPosition()); diff --git a/source/modules/juce_gui_basics/windows/juce_CallOutBox.h b/source/modules/juce_gui_basics/windows/juce_CallOutBox.h index 22a9612b6..fab2e6b5f 100644 --- a/source/modules/juce_gui_basics/windows/juce_CallOutBox.h +++ b/source/modules/juce_gui_basics/windows/juce_CallOutBox.h @@ -118,6 +118,15 @@ public: const Rectangle& areaToPointTo, Component* parentComponent); + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawCallOutBoxBackground (CallOutBox&, Graphics&, const Path&, Image& cachedImage) = 0; + }; + //============================================================================== /** @internal */ void paint (Graphics&) override; diff --git a/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp b/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp index fddc0408f..c51c47925 100644 --- a/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp +++ b/source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp @@ -298,15 +298,11 @@ void ComponentPeer::handleMovedOrResized() Rectangle newBounds (Component::ComponentHelpers::rawPeerPositionToLocal (component, getBounds())); Rectangle oldBounds (component.getBounds()); -// oldBounds = Component::ComponentHelpers::localPositionToRawPeerPos (component, oldBounds); - const bool wasMoved = (oldBounds.getPosition() != newBounds.getPosition()); const bool wasResized = (oldBounds.getWidth() != newBounds.getWidth() || oldBounds.getHeight() != newBounds.getHeight()); if (wasMoved || wasResized) { -// newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, newBounds); - component.bounds = newBounds; if (wasResized) diff --git a/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h b/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h index 0bf7cc8a9..17457bdcc 100644 --- a/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_DocumentWindow.h @@ -223,6 +223,30 @@ public: and feel class how this is used. */ }; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + window drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void drawDocumentWindowTitleBar (DocumentWindow&, + Graphics&, int w, int h, + int titleSpaceX, int titleSpaceW, + const Image* icon, + bool drawTitleTextOnLeft) = 0; + + virtual Button* createDocumentWindowButton (int buttonType) = 0; + + virtual void positionDocumentWindowButtons (DocumentWindow&, + int titleBarX, int titleBarY, int titleBarW, int titleBarH, + Button* minimiseButton, + Button* maximiseButton, + Button* closeButton, + bool positionTitleBarButtonsOnLeft) = 0; + }; + //============================================================================== #ifndef DOXYGEN /** @internal */ diff --git a/source/modules/juce_gui_basics/windows/juce_ResizableWindow.h b/source/modules/juce_gui_basics/windows/juce_ResizableWindow.h index e2076d033..7a7ffd74a 100644 --- a/source/modules/juce_gui_basics/windows/juce_ResizableWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_ResizableWindow.h @@ -306,8 +306,23 @@ public: bool resizeToFit = false)); using TopLevelWindow::addToDesktop; -protected: //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + window drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + //============================================================================== + virtual void drawCornerResizer (Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) = 0; + virtual void drawResizableFrame (Graphics&, int w, int h, const BorderSize&) = 0; + + virtual void fillResizableWindowBackground (Graphics&, int w, int h, const BorderSize&, ResizableWindow&) = 0; + virtual void drawResizableWindowBorder (Graphics&, int w, int h, const BorderSize& border, ResizableWindow&) = 0; + }; + +protected: /** @internal */ void paint (Graphics&) override; /** (if overriding this, make sure you call ResizableWindow::moved() in your subclass) */ diff --git a/source/modules/juce_gui_basics/windows/juce_TooltipWindow.h b/source/modules/juce_gui_basics/windows/juce_TooltipWindow.h index 63564be36..e6515ee2f 100644 --- a/source/modules/juce_gui_basics/windows/juce_TooltipWindow.h +++ b/source/modules/juce_gui_basics/windows/juce_TooltipWindow.h @@ -96,6 +96,17 @@ public: outlineColourId = 0x1001c10 /**< The colour to use to draw an outline around the tooltip. */ }; + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + window drawing functionality. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual void getTooltipSize (const String& tipText, int& width, int& height) = 0; + virtual void drawTooltip (Graphics&, const String& text, int width, int height) = 0; + }; private: //==============================================================================