migrating to the latest JUCE version
This commit is contained in:
@ -1,74 +1,74 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
CPlusPlusCodeTokeniser::CPlusPlusCodeTokeniser() {}
|
||||
CPlusPlusCodeTokeniser::~CPlusPlusCodeTokeniser() {}
|
||||
|
||||
int CPlusPlusCodeTokeniser::readNextToken (CodeDocument::Iterator& source)
|
||||
{
|
||||
return CppTokeniserFunctions::readNextToken (source);
|
||||
}
|
||||
|
||||
CodeEditorComponent::ColourScheme CPlusPlusCodeTokeniser::getDefaultColourScheme()
|
||||
{
|
||||
struct Type
|
||||
{
|
||||
const char* name;
|
||||
uint32 colour;
|
||||
};
|
||||
|
||||
const Type types[] =
|
||||
{
|
||||
{ "Error", 0xffcc0000 },
|
||||
{ "Comment", 0xff00aa00 },
|
||||
{ "Keyword", 0xff0000cc },
|
||||
{ "Operator", 0xff225500 },
|
||||
{ "Identifier", 0xff000000 },
|
||||
{ "Integer", 0xff880000 },
|
||||
{ "Float", 0xff885500 },
|
||||
{ "String", 0xff990099 },
|
||||
{ "Bracket", 0xff000055 },
|
||||
{ "Punctuation", 0xff004400 },
|
||||
{ "Preprocessor Text", 0xff660000 }
|
||||
};
|
||||
|
||||
CodeEditorComponent::ColourScheme cs;
|
||||
|
||||
for (auto& t : types)
|
||||
cs.set (t.name, Colour (t.colour));
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
bool CPlusPlusCodeTokeniser::isReservedKeyword (const String& token) noexcept
|
||||
{
|
||||
return CppTokeniserFunctions::isReservedKeyword (token.getCharPointer(), token.length());
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
CPlusPlusCodeTokeniser::CPlusPlusCodeTokeniser() {}
|
||||
CPlusPlusCodeTokeniser::~CPlusPlusCodeTokeniser() {}
|
||||
|
||||
int CPlusPlusCodeTokeniser::readNextToken (CodeDocument::Iterator& source)
|
||||
{
|
||||
return CppTokeniserFunctions::readNextToken (source);
|
||||
}
|
||||
|
||||
CodeEditorComponent::ColourScheme CPlusPlusCodeTokeniser::getDefaultColourScheme()
|
||||
{
|
||||
struct Type
|
||||
{
|
||||
const char* name;
|
||||
uint32 colour;
|
||||
};
|
||||
|
||||
const Type types[] =
|
||||
{
|
||||
{ "Error", 0xffcc0000 },
|
||||
{ "Comment", 0xff00aa00 },
|
||||
{ "Keyword", 0xff0000cc },
|
||||
{ "Operator", 0xff225500 },
|
||||
{ "Identifier", 0xff000000 },
|
||||
{ "Integer", 0xff880000 },
|
||||
{ "Float", 0xff885500 },
|
||||
{ "String", 0xff990099 },
|
||||
{ "Bracket", 0xff000055 },
|
||||
{ "Punctuation", 0xff004400 },
|
||||
{ "Preprocessor Text", 0xff660000 }
|
||||
};
|
||||
|
||||
CodeEditorComponent::ColourScheme cs;
|
||||
|
||||
for (auto& t : types)
|
||||
cs.set (t.name, Colour (t.colour));
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
bool CPlusPlusCodeTokeniser::isReservedKeyword (const String& token) noexcept
|
||||
{
|
||||
return CppTokeniserFunctions::isReservedKeyword (token.getCharPointer(), token.length());
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,72 +1,72 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple lexical analyser for syntax colouring of C++ code.
|
||||
|
||||
@see CodeEditorComponent, CodeDocument
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CPlusPlusCodeTokeniser : public CodeTokeniser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
CPlusPlusCodeTokeniser();
|
||||
~CPlusPlusCodeTokeniser() override;
|
||||
|
||||
//==============================================================================
|
||||
int readNextToken (CodeDocument::Iterator&) override;
|
||||
CodeEditorComponent::ColourScheme getDefaultColourScheme() override;
|
||||
|
||||
/** This is a handy method for checking whether a string is a c++ reserved keyword. */
|
||||
static bool isReservedKeyword (const String& token) noexcept;
|
||||
|
||||
/** The token values returned by this tokeniser. */
|
||||
enum TokenType
|
||||
{
|
||||
tokenType_error = 0,
|
||||
tokenType_comment,
|
||||
tokenType_keyword,
|
||||
tokenType_operator,
|
||||
tokenType_identifier,
|
||||
tokenType_integer,
|
||||
tokenType_float,
|
||||
tokenType_string,
|
||||
tokenType_bracket,
|
||||
tokenType_punctuation,
|
||||
tokenType_preprocessor
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (CPlusPlusCodeTokeniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple lexical analyser for syntax colouring of C++ code.
|
||||
|
||||
@see CodeEditorComponent, CodeDocument
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CPlusPlusCodeTokeniser : public CodeTokeniser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
CPlusPlusCodeTokeniser();
|
||||
~CPlusPlusCodeTokeniser() override;
|
||||
|
||||
//==============================================================================
|
||||
int readNextToken (CodeDocument::Iterator&) override;
|
||||
CodeEditorComponent::ColourScheme getDefaultColourScheme() override;
|
||||
|
||||
/** This is a handy method for checking whether a string is a c++ reserved keyword. */
|
||||
static bool isReservedKeyword (const String& token) noexcept;
|
||||
|
||||
/** The token values returned by this tokeniser. */
|
||||
enum TokenType
|
||||
{
|
||||
tokenType_error = 0,
|
||||
tokenType_comment,
|
||||
tokenType_keyword,
|
||||
tokenType_operator,
|
||||
tokenType_identifier,
|
||||
tokenType_integer,
|
||||
tokenType_float,
|
||||
tokenType_string,
|
||||
tokenType_bracket,
|
||||
tokenType_punctuation,
|
||||
tokenType_preprocessor
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_LEAK_DETECTOR (CPlusPlusCodeTokeniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,450 +1,450 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class CodeDocumentLine;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for storing and manipulating a source code file.
|
||||
|
||||
When using a CodeEditorComponent, it takes one of these as its source object.
|
||||
|
||||
The CodeDocument stores its content as an array of lines, which makes it
|
||||
quick to insert and delete.
|
||||
|
||||
@see CodeEditorComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CodeDocument
|
||||
{
|
||||
public:
|
||||
/** Creates a new, empty document. */
|
||||
CodeDocument();
|
||||
|
||||
/** Destructor. */
|
||||
~CodeDocument();
|
||||
|
||||
//==============================================================================
|
||||
/** A position in a code document.
|
||||
|
||||
Using this class you can find a position in a code document and quickly get its
|
||||
character position, line, and index. By calling setPositionMaintained (true), the
|
||||
position is automatically updated when text is inserted or deleted in the document,
|
||||
so that it maintains its original place in the text.
|
||||
*/
|
||||
class JUCE_API Position
|
||||
{
|
||||
public:
|
||||
/** Creates an uninitialised position.
|
||||
Don't attempt to call any methods on this until you've given it an owner document
|
||||
to refer to!
|
||||
*/
|
||||
Position() noexcept;
|
||||
|
||||
/** Creates a position based on a line and index in a document.
|
||||
|
||||
Note that this index is NOT the column number, it's the number of characters from the
|
||||
start of the line. The "column" number isn't quite the same, because if the line
|
||||
contains any tab characters, the relationship of the index to its visual column depends on
|
||||
the number of spaces per tab being used!
|
||||
|
||||
Lines are numbered from zero, and if the line or index are beyond the bounds of the document,
|
||||
they will be adjusted to keep them within its limits.
|
||||
*/
|
||||
Position (const CodeDocument& ownerDocument,
|
||||
int line, int indexInLine) noexcept;
|
||||
|
||||
/** Creates a position based on a character index in a document.
|
||||
This position is placed at the specified number of characters from the start of the
|
||||
document. The line and column are auto-calculated.
|
||||
|
||||
If the position is beyond the range of the document, it'll be adjusted to keep it
|
||||
inside.
|
||||
*/
|
||||
Position (const CodeDocument& ownerDocument,
|
||||
int charactersFromStartOfDocument) noexcept;
|
||||
|
||||
/** Creates a copy of another position.
|
||||
|
||||
This will copy the position, but the new object will not be set to maintain its position,
|
||||
even if the source object was set to do so.
|
||||
*/
|
||||
Position (const Position&) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~Position();
|
||||
|
||||
Position& operator= (const Position&);
|
||||
|
||||
bool operator== (const Position&) const noexcept;
|
||||
bool operator!= (const Position&) const noexcept;
|
||||
|
||||
/** Points this object at a new position within the document.
|
||||
|
||||
If the position is beyond the range of the document, it'll be adjusted to keep it
|
||||
inside.
|
||||
@see getPosition, setLineAndIndex
|
||||
*/
|
||||
void setPosition (int charactersFromStartOfDocument);
|
||||
|
||||
/** Returns the position as the number of characters from the start of the document.
|
||||
@see setPosition, getLineNumber, getIndexInLine
|
||||
*/
|
||||
int getPosition() const noexcept { return characterPos; }
|
||||
|
||||
/** Moves the position to a new line and index within the line.
|
||||
|
||||
Note that the index is NOT the column at which the position appears in an editor.
|
||||
If the line contains any tab characters, the relationship of the index to its
|
||||
visual position depends on the number of spaces per tab being used!
|
||||
|
||||
Lines are numbered from zero, and if the line or index are beyond the bounds of the document,
|
||||
they will be adjusted to keep them within its limits.
|
||||
*/
|
||||
void setLineAndIndex (int newLineNumber, int newIndexInLine);
|
||||
|
||||
/** Returns the line number of this position.
|
||||
The first line in the document is numbered zero, not one!
|
||||
*/
|
||||
int getLineNumber() const noexcept { return line; }
|
||||
|
||||
/** Returns the number of characters from the start of the line.
|
||||
|
||||
Note that this value is NOT the column at which the position appears in an editor.
|
||||
If the line contains any tab characters, the relationship of the index to its
|
||||
visual position depends on the number of spaces per tab being used!
|
||||
*/
|
||||
int getIndexInLine() const noexcept { return indexInLine; }
|
||||
|
||||
/** Allows the position to be automatically updated when the document changes.
|
||||
|
||||
If this is set to true, the position will register with its document so that
|
||||
when the document has text inserted or deleted, this position will be automatically
|
||||
moved to keep it at the same position in the text.
|
||||
*/
|
||||
void setPositionMaintained (bool isMaintained);
|
||||
|
||||
//==============================================================================
|
||||
/** Moves the position forwards or backwards by the specified number of characters.
|
||||
@see movedBy
|
||||
*/
|
||||
void moveBy (int characterDelta);
|
||||
|
||||
/** Returns a position which is the same as this one, moved by the specified number of
|
||||
characters.
|
||||
@see moveBy
|
||||
*/
|
||||
Position movedBy (int characterDelta) const;
|
||||
|
||||
/** Returns a position which is the same as this one, moved up or down by the specified
|
||||
number of lines.
|
||||
@see movedBy
|
||||
*/
|
||||
Position movedByLines (int deltaLines) const;
|
||||
|
||||
/** Returns the character in the document at this position.
|
||||
@see getLineText
|
||||
*/
|
||||
juce_wchar getCharacter() const;
|
||||
|
||||
/** Returns the line from the document that this position is within.
|
||||
@see getCharacter, getLineNumber
|
||||
*/
|
||||
String getLineText() const;
|
||||
|
||||
private:
|
||||
CodeDocument* owner = nullptr;
|
||||
int characterPos = 0, line = 0, indexInLine = 0;
|
||||
bool positionMaintained = false;
|
||||
|
||||
friend class CodeDocument;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the full text of the document. */
|
||||
String getAllContent() const;
|
||||
|
||||
/** Returns a section of the document's text. */
|
||||
String getTextBetween (const Position& start, const Position& end) const;
|
||||
|
||||
/** Returns a line from the document. */
|
||||
String getLine (int lineIndex) const noexcept;
|
||||
|
||||
/** Returns the number of characters in the document. */
|
||||
int getNumCharacters() const noexcept;
|
||||
|
||||
/** Returns the number of lines in the document. */
|
||||
int getNumLines() const noexcept { return lines.size(); }
|
||||
|
||||
/** Returns the number of characters in the longest line of the document. */
|
||||
int getMaximumLineLength() noexcept;
|
||||
|
||||
/** Deletes a section of the text.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void deleteSection (const Position& startPosition, const Position& endPosition);
|
||||
|
||||
/** Deletes a section of the text.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void deleteSection (int startIndex, int endIndex);
|
||||
|
||||
/** Inserts some text into the document at a given position.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void insertText (const Position& position, const String& text);
|
||||
|
||||
/** Inserts some text into the document at a given position.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void insertText (int insertIndex, const String& text);
|
||||
|
||||
/** Replaces a section of the text with a new string.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void replaceSection (int startIndex, int endIndex, const String& newText);
|
||||
|
||||
/** Clears the document and replaces it with some new text.
|
||||
|
||||
This operation is undoable - if you're trying to completely reset the document, you
|
||||
might want to also call clearUndoHistory() and setSavePoint() after using this method.
|
||||
*/
|
||||
void replaceAllContent (const String& newContent);
|
||||
|
||||
/** Analyses the changes between the current content and some new text, and applies
|
||||
those changes.
|
||||
*/
|
||||
void applyChanges (const String& newContent);
|
||||
|
||||
/** Replaces the editor's contents with the contents of a stream.
|
||||
This will also reset the undo history and save point marker.
|
||||
*/
|
||||
bool loadFromStream (InputStream& stream);
|
||||
|
||||
/** Writes the editor's current contents to a stream. */
|
||||
bool writeToStream (OutputStream& stream);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the preferred new-line characters for the document.
|
||||
This will be either "\\n", "\\r\\n", or (rarely) "\\r".
|
||||
@see setNewLineCharacters
|
||||
*/
|
||||
String getNewLineCharacters() const noexcept { return newLineChars; }
|
||||
|
||||
/** Sets the new-line characters that the document should use.
|
||||
The string must be either "\\n", "\\r\\n", or (rarely) "\\r".
|
||||
@see getNewLineCharacters
|
||||
*/
|
||||
void setNewLineCharacters (const String& newLineCharacters) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Begins a new undo transaction.
|
||||
|
||||
The document itself will not call this internally, so relies on whatever is using the
|
||||
document to periodically call this to break up the undo sequence into sensible chunks.
|
||||
@see UndoManager::beginNewTransaction
|
||||
*/
|
||||
void newTransaction();
|
||||
|
||||
/** Undo the last operation.
|
||||
@see UndoManager::undo
|
||||
*/
|
||||
void undo();
|
||||
|
||||
/** Redo the last operation.
|
||||
@see UndoManager::redo
|
||||
*/
|
||||
void redo();
|
||||
|
||||
/** Clears the undo history.
|
||||
@see UndoManager::clearUndoHistory
|
||||
*/
|
||||
void clearUndoHistory();
|
||||
|
||||
/** Returns the document's UndoManager */
|
||||
UndoManager& getUndoManager() noexcept { return undoManager; }
|
||||
|
||||
//==============================================================================
|
||||
/** Makes a note that the document's current state matches the one that is saved.
|
||||
|
||||
After this has been called, hasChangedSinceSavePoint() will return false until
|
||||
the document has been altered, and then it'll start returning true. If the document is
|
||||
altered, but then undone until it gets back to this state, hasChangedSinceSavePoint()
|
||||
will again return false.
|
||||
|
||||
@see hasChangedSinceSavePoint
|
||||
*/
|
||||
void setSavePoint() noexcept;
|
||||
|
||||
/** Returns true if the state of the document differs from the state it was in when
|
||||
setSavePoint() was last called.
|
||||
|
||||
@see setSavePoint
|
||||
*/
|
||||
bool hasChangedSinceSavePoint() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Searches for a word-break. */
|
||||
Position findWordBreakAfter (const Position& position) const noexcept;
|
||||
/** Searches for a word-break. */
|
||||
Position findWordBreakBefore (const Position& position) const noexcept;
|
||||
/** Finds the token that contains the given position. */
|
||||
void findTokenContaining (const Position& pos, Position& start, Position& end) const noexcept;
|
||||
/** Finds the line that contains the given position. */
|
||||
void findLineContaining (const Position& pos, Position& start, Position& end) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** An object that receives callbacks from the CodeDocument when its text changes.
|
||||
@see CodeDocument::addListener, CodeDocument::removeListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
Listener() = default;
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called by a CodeDocument when text is added. */
|
||||
virtual void codeDocumentTextInserted (const String& newText, int insertIndex) = 0;
|
||||
|
||||
/** Called by a CodeDocument when text is deleted. */
|
||||
virtual void codeDocumentTextDeleted (int startIndex, int endIndex) = 0;
|
||||
};
|
||||
|
||||
/** Registers a listener object to receive callbacks when the document changes.
|
||||
If the listener is already registered, this method has no effect.
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Deregisters a listener.
|
||||
@see addListener
|
||||
*/
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Iterates the text in a CodeDocument.
|
||||
|
||||
This class lets you read characters from a CodeDocument. It's designed to be used
|
||||
by a CodeTokeniser object.
|
||||
|
||||
@see CodeDocument
|
||||
*/
|
||||
class JUCE_API Iterator
|
||||
{
|
||||
public:
|
||||
/** Creates an uninitialised iterator.
|
||||
Don't attempt to call any methods on this until you've given it an
|
||||
owner document to refer to!
|
||||
*/
|
||||
Iterator() noexcept;
|
||||
|
||||
Iterator (const CodeDocument& document) noexcept;
|
||||
Iterator (CodeDocument::Position) noexcept;
|
||||
~Iterator() noexcept;
|
||||
|
||||
Iterator (const Iterator&) = default;
|
||||
Iterator& operator= (const Iterator&) = default;
|
||||
|
||||
/** Reads the next character and returns it. Returns 0 if you try to
|
||||
read past the document's end.
|
||||
@see peekNextChar, previousChar
|
||||
*/
|
||||
juce_wchar nextChar() noexcept;
|
||||
|
||||
/** Reads the next character without moving the current position. */
|
||||
juce_wchar peekNextChar() const noexcept;
|
||||
|
||||
/** Reads the previous character and returns it. Returns 0 if you try to
|
||||
read past the document's start.
|
||||
@see isSOF, peekPreviousChar, nextChar
|
||||
*/
|
||||
juce_wchar previousChar() noexcept;
|
||||
|
||||
/** Reads the next character without moving the current position. */
|
||||
juce_wchar peekPreviousChar() const noexcept;
|
||||
|
||||
/** Advances the position by one character. */
|
||||
void skip() noexcept;
|
||||
|
||||
/** Returns the position as the number of characters from the start of the document. */
|
||||
int getPosition() const noexcept { return position; }
|
||||
|
||||
/** Skips over any whitespace characters until the next character is non-whitespace. */
|
||||
void skipWhitespace() noexcept;
|
||||
|
||||
/** Skips forward until the next character will be the first character on the next line */
|
||||
void skipToEndOfLine() noexcept;
|
||||
|
||||
/** Skips backward until the next character will be the first character on this line */
|
||||
void skipToStartOfLine() noexcept;
|
||||
|
||||
/** Returns the line number of the next character. */
|
||||
int getLine() const noexcept { return line; }
|
||||
|
||||
/** Returns true if the iterator has reached the end of the document. */
|
||||
bool isEOF() const noexcept;
|
||||
|
||||
/** Returns true if the iterator is at the start of the document. */
|
||||
bool isSOF() const noexcept;
|
||||
|
||||
/** Convert this iterator to a CodeDocument::Position. */
|
||||
CodeDocument::Position toPosition() const;
|
||||
|
||||
private:
|
||||
bool reinitialiseCharPtr() const;
|
||||
|
||||
const CodeDocument* document;
|
||||
mutable String::CharPointerType charPointer { nullptr };
|
||||
int line = 0, position = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct InsertAction;
|
||||
struct DeleteAction;
|
||||
friend class Iterator;
|
||||
friend class Position;
|
||||
|
||||
OwnedArray<CodeDocumentLine> lines;
|
||||
Array<Position*> positionsToMaintain;
|
||||
UndoManager undoManager;
|
||||
int currentActionIndex = 0, indexOfSavedState = -1;
|
||||
int maximumLineLength = -1;
|
||||
ListenerList<Listener> listeners;
|
||||
String newLineChars { "\r\n" };
|
||||
|
||||
void insert (const String& text, int insertPos, bool undoable);
|
||||
void remove (int startPos, int endPos, bool undoable);
|
||||
void checkLastLineStatus();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeDocument)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class CodeDocumentLine;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for storing and manipulating a source code file.
|
||||
|
||||
When using a CodeEditorComponent, it takes one of these as its source object.
|
||||
|
||||
The CodeDocument stores its content as an array of lines, which makes it
|
||||
quick to insert and delete.
|
||||
|
||||
@see CodeEditorComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CodeDocument
|
||||
{
|
||||
public:
|
||||
/** Creates a new, empty document. */
|
||||
CodeDocument();
|
||||
|
||||
/** Destructor. */
|
||||
~CodeDocument();
|
||||
|
||||
//==============================================================================
|
||||
/** A position in a code document.
|
||||
|
||||
Using this class you can find a position in a code document and quickly get its
|
||||
character position, line, and index. By calling setPositionMaintained (true), the
|
||||
position is automatically updated when text is inserted or deleted in the document,
|
||||
so that it maintains its original place in the text.
|
||||
*/
|
||||
class JUCE_API Position
|
||||
{
|
||||
public:
|
||||
/** Creates an uninitialised position.
|
||||
Don't attempt to call any methods on this until you've given it an owner document
|
||||
to refer to!
|
||||
*/
|
||||
Position() noexcept;
|
||||
|
||||
/** Creates a position based on a line and index in a document.
|
||||
|
||||
Note that this index is NOT the column number, it's the number of characters from the
|
||||
start of the line. The "column" number isn't quite the same, because if the line
|
||||
contains any tab characters, the relationship of the index to its visual column depends on
|
||||
the number of spaces per tab being used!
|
||||
|
||||
Lines are numbered from zero, and if the line or index are beyond the bounds of the document,
|
||||
they will be adjusted to keep them within its limits.
|
||||
*/
|
||||
Position (const CodeDocument& ownerDocument,
|
||||
int line, int indexInLine) noexcept;
|
||||
|
||||
/** Creates a position based on a character index in a document.
|
||||
This position is placed at the specified number of characters from the start of the
|
||||
document. The line and column are auto-calculated.
|
||||
|
||||
If the position is beyond the range of the document, it'll be adjusted to keep it
|
||||
inside.
|
||||
*/
|
||||
Position (const CodeDocument& ownerDocument,
|
||||
int charactersFromStartOfDocument) noexcept;
|
||||
|
||||
/** Creates a copy of another position.
|
||||
|
||||
This will copy the position, but the new object will not be set to maintain its position,
|
||||
even if the source object was set to do so.
|
||||
*/
|
||||
Position (const Position&) noexcept;
|
||||
|
||||
/** Destructor. */
|
||||
~Position();
|
||||
|
||||
Position& operator= (const Position&);
|
||||
|
||||
bool operator== (const Position&) const noexcept;
|
||||
bool operator!= (const Position&) const noexcept;
|
||||
|
||||
/** Points this object at a new position within the document.
|
||||
|
||||
If the position is beyond the range of the document, it'll be adjusted to keep it
|
||||
inside.
|
||||
@see getPosition, setLineAndIndex
|
||||
*/
|
||||
void setPosition (int charactersFromStartOfDocument);
|
||||
|
||||
/** Returns the position as the number of characters from the start of the document.
|
||||
@see setPosition, getLineNumber, getIndexInLine
|
||||
*/
|
||||
int getPosition() const noexcept { return characterPos; }
|
||||
|
||||
/** Moves the position to a new line and index within the line.
|
||||
|
||||
Note that the index is NOT the column at which the position appears in an editor.
|
||||
If the line contains any tab characters, the relationship of the index to its
|
||||
visual position depends on the number of spaces per tab being used!
|
||||
|
||||
Lines are numbered from zero, and if the line or index are beyond the bounds of the document,
|
||||
they will be adjusted to keep them within its limits.
|
||||
*/
|
||||
void setLineAndIndex (int newLineNumber, int newIndexInLine);
|
||||
|
||||
/** Returns the line number of this position.
|
||||
The first line in the document is numbered zero, not one!
|
||||
*/
|
||||
int getLineNumber() const noexcept { return line; }
|
||||
|
||||
/** Returns the number of characters from the start of the line.
|
||||
|
||||
Note that this value is NOT the column at which the position appears in an editor.
|
||||
If the line contains any tab characters, the relationship of the index to its
|
||||
visual position depends on the number of spaces per tab being used!
|
||||
*/
|
||||
int getIndexInLine() const noexcept { return indexInLine; }
|
||||
|
||||
/** Allows the position to be automatically updated when the document changes.
|
||||
|
||||
If this is set to true, the position will register with its document so that
|
||||
when the document has text inserted or deleted, this position will be automatically
|
||||
moved to keep it at the same position in the text.
|
||||
*/
|
||||
void setPositionMaintained (bool isMaintained);
|
||||
|
||||
//==============================================================================
|
||||
/** Moves the position forwards or backwards by the specified number of characters.
|
||||
@see movedBy
|
||||
*/
|
||||
void moveBy (int characterDelta);
|
||||
|
||||
/** Returns a position which is the same as this one, moved by the specified number of
|
||||
characters.
|
||||
@see moveBy
|
||||
*/
|
||||
Position movedBy (int characterDelta) const;
|
||||
|
||||
/** Returns a position which is the same as this one, moved up or down by the specified
|
||||
number of lines.
|
||||
@see movedBy
|
||||
*/
|
||||
Position movedByLines (int deltaLines) const;
|
||||
|
||||
/** Returns the character in the document at this position.
|
||||
@see getLineText
|
||||
*/
|
||||
juce_wchar getCharacter() const;
|
||||
|
||||
/** Returns the line from the document that this position is within.
|
||||
@see getCharacter, getLineNumber
|
||||
*/
|
||||
String getLineText() const;
|
||||
|
||||
private:
|
||||
CodeDocument* owner = nullptr;
|
||||
int characterPos = 0, line = 0, indexInLine = 0;
|
||||
bool positionMaintained = false;
|
||||
|
||||
friend class CodeDocument;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the full text of the document. */
|
||||
String getAllContent() const;
|
||||
|
||||
/** Returns a section of the document's text. */
|
||||
String getTextBetween (const Position& start, const Position& end) const;
|
||||
|
||||
/** Returns a line from the document. */
|
||||
String getLine (int lineIndex) const noexcept;
|
||||
|
||||
/** Returns the number of characters in the document. */
|
||||
int getNumCharacters() const noexcept;
|
||||
|
||||
/** Returns the number of lines in the document. */
|
||||
int getNumLines() const noexcept { return lines.size(); }
|
||||
|
||||
/** Returns the number of characters in the longest line of the document. */
|
||||
int getMaximumLineLength() noexcept;
|
||||
|
||||
/** Deletes a section of the text.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void deleteSection (const Position& startPosition, const Position& endPosition);
|
||||
|
||||
/** Deletes a section of the text.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void deleteSection (int startIndex, int endIndex);
|
||||
|
||||
/** Inserts some text into the document at a given position.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void insertText (const Position& position, const String& text);
|
||||
|
||||
/** Inserts some text into the document at a given position.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void insertText (int insertIndex, const String& text);
|
||||
|
||||
/** Replaces a section of the text with a new string.
|
||||
This operation is undoable.
|
||||
*/
|
||||
void replaceSection (int startIndex, int endIndex, const String& newText);
|
||||
|
||||
/** Clears the document and replaces it with some new text.
|
||||
|
||||
This operation is undoable - if you're trying to completely reset the document, you
|
||||
might want to also call clearUndoHistory() and setSavePoint() after using this method.
|
||||
*/
|
||||
void replaceAllContent (const String& newContent);
|
||||
|
||||
/** Analyses the changes between the current content and some new text, and applies
|
||||
those changes.
|
||||
*/
|
||||
void applyChanges (const String& newContent);
|
||||
|
||||
/** Replaces the editor's contents with the contents of a stream.
|
||||
This will also reset the undo history and save point marker.
|
||||
*/
|
||||
bool loadFromStream (InputStream& stream);
|
||||
|
||||
/** Writes the editor's current contents to a stream. */
|
||||
bool writeToStream (OutputStream& stream);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the preferred new-line characters for the document.
|
||||
This will be either "\\n", "\\r\\n", or (rarely) "\\r".
|
||||
@see setNewLineCharacters
|
||||
*/
|
||||
String getNewLineCharacters() const noexcept { return newLineChars; }
|
||||
|
||||
/** Sets the new-line characters that the document should use.
|
||||
The string must be either "\\n", "\\r\\n", or (rarely) "\\r".
|
||||
@see getNewLineCharacters
|
||||
*/
|
||||
void setNewLineCharacters (const String& newLineCharacters) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Begins a new undo transaction.
|
||||
|
||||
The document itself will not call this internally, so relies on whatever is using the
|
||||
document to periodically call this to break up the undo sequence into sensible chunks.
|
||||
@see UndoManager::beginNewTransaction
|
||||
*/
|
||||
void newTransaction();
|
||||
|
||||
/** Undo the last operation.
|
||||
@see UndoManager::undo
|
||||
*/
|
||||
void undo();
|
||||
|
||||
/** Redo the last operation.
|
||||
@see UndoManager::redo
|
||||
*/
|
||||
void redo();
|
||||
|
||||
/** Clears the undo history.
|
||||
@see UndoManager::clearUndoHistory
|
||||
*/
|
||||
void clearUndoHistory();
|
||||
|
||||
/** Returns the document's UndoManager */
|
||||
UndoManager& getUndoManager() noexcept { return undoManager; }
|
||||
|
||||
//==============================================================================
|
||||
/** Makes a note that the document's current state matches the one that is saved.
|
||||
|
||||
After this has been called, hasChangedSinceSavePoint() will return false until
|
||||
the document has been altered, and then it'll start returning true. If the document is
|
||||
altered, but then undone until it gets back to this state, hasChangedSinceSavePoint()
|
||||
will again return false.
|
||||
|
||||
@see hasChangedSinceSavePoint
|
||||
*/
|
||||
void setSavePoint() noexcept;
|
||||
|
||||
/** Returns true if the state of the document differs from the state it was in when
|
||||
setSavePoint() was last called.
|
||||
|
||||
@see setSavePoint
|
||||
*/
|
||||
bool hasChangedSinceSavePoint() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Searches for a word-break. */
|
||||
Position findWordBreakAfter (const Position& position) const noexcept;
|
||||
/** Searches for a word-break. */
|
||||
Position findWordBreakBefore (const Position& position) const noexcept;
|
||||
/** Finds the token that contains the given position. */
|
||||
void findTokenContaining (const Position& pos, Position& start, Position& end) const noexcept;
|
||||
/** Finds the line that contains the given position. */
|
||||
void findLineContaining (const Position& pos, Position& start, Position& end) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** An object that receives callbacks from the CodeDocument when its text changes.
|
||||
@see CodeDocument::addListener, CodeDocument::removeListener
|
||||
*/
|
||||
class JUCE_API Listener
|
||||
{
|
||||
public:
|
||||
Listener() = default;
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called by a CodeDocument when text is added. */
|
||||
virtual void codeDocumentTextInserted (const String& newText, int insertIndex) = 0;
|
||||
|
||||
/** Called by a CodeDocument when text is deleted. */
|
||||
virtual void codeDocumentTextDeleted (int startIndex, int endIndex) = 0;
|
||||
};
|
||||
|
||||
/** Registers a listener object to receive callbacks when the document changes.
|
||||
If the listener is already registered, this method has no effect.
|
||||
@see removeListener
|
||||
*/
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Deregisters a listener.
|
||||
@see addListener
|
||||
*/
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Iterates the text in a CodeDocument.
|
||||
|
||||
This class lets you read characters from a CodeDocument. It's designed to be used
|
||||
by a CodeTokeniser object.
|
||||
|
||||
@see CodeDocument
|
||||
*/
|
||||
class JUCE_API Iterator
|
||||
{
|
||||
public:
|
||||
/** Creates an uninitialised iterator.
|
||||
Don't attempt to call any methods on this until you've given it an
|
||||
owner document to refer to!
|
||||
*/
|
||||
Iterator() noexcept;
|
||||
|
||||
Iterator (const CodeDocument& document) noexcept;
|
||||
Iterator (CodeDocument::Position) noexcept;
|
||||
~Iterator() noexcept;
|
||||
|
||||
Iterator (const Iterator&) = default;
|
||||
Iterator& operator= (const Iterator&) = default;
|
||||
|
||||
/** Reads the next character and returns it. Returns 0 if you try to
|
||||
read past the document's end.
|
||||
@see peekNextChar, previousChar
|
||||
*/
|
||||
juce_wchar nextChar() noexcept;
|
||||
|
||||
/** Reads the next character without moving the current position. */
|
||||
juce_wchar peekNextChar() const noexcept;
|
||||
|
||||
/** Reads the previous character and returns it. Returns 0 if you try to
|
||||
read past the document's start.
|
||||
@see isSOF, peekPreviousChar, nextChar
|
||||
*/
|
||||
juce_wchar previousChar() noexcept;
|
||||
|
||||
/** Reads the next character without moving the current position. */
|
||||
juce_wchar peekPreviousChar() const noexcept;
|
||||
|
||||
/** Advances the position by one character. */
|
||||
void skip() noexcept;
|
||||
|
||||
/** Returns the position as the number of characters from the start of the document. */
|
||||
int getPosition() const noexcept { return position; }
|
||||
|
||||
/** Skips over any whitespace characters until the next character is non-whitespace. */
|
||||
void skipWhitespace() noexcept;
|
||||
|
||||
/** Skips forward until the next character will be the first character on the next line */
|
||||
void skipToEndOfLine() noexcept;
|
||||
|
||||
/** Skips backward until the next character will be the first character on this line */
|
||||
void skipToStartOfLine() noexcept;
|
||||
|
||||
/** Returns the line number of the next character. */
|
||||
int getLine() const noexcept { return line; }
|
||||
|
||||
/** Returns true if the iterator has reached the end of the document. */
|
||||
bool isEOF() const noexcept;
|
||||
|
||||
/** Returns true if the iterator is at the start of the document. */
|
||||
bool isSOF() const noexcept;
|
||||
|
||||
/** Convert this iterator to a CodeDocument::Position. */
|
||||
CodeDocument::Position toPosition() const;
|
||||
|
||||
private:
|
||||
bool reinitialiseCharPtr() const;
|
||||
|
||||
const CodeDocument* document;
|
||||
mutable String::CharPointerType charPointer { nullptr };
|
||||
int line = 0, position = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct InsertAction;
|
||||
struct DeleteAction;
|
||||
friend class Iterator;
|
||||
friend class Position;
|
||||
|
||||
OwnedArray<CodeDocumentLine> lines;
|
||||
Array<Position*> positionsToMaintain;
|
||||
UndoManager undoManager;
|
||||
int currentActionIndex = 0, indexOfSavedState = -1;
|
||||
int maximumLineLength = -1;
|
||||
ListenerList<Listener> listeners;
|
||||
String newLineChars { "\r\n" };
|
||||
|
||||
void insert (const String& text, int insertPos, bool undoable);
|
||||
void remove (int startPos, int endPos, bool undoable);
|
||||
void checkLastLineStatus();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeDocument)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,456 +1,476 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class CodeTokeniser;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A text editor component designed specifically for source code.
|
||||
|
||||
This is designed to handle syntax highlighting and fast editing of very large
|
||||
files.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CodeEditorComponent : public Component,
|
||||
public ApplicationCommandTarget,
|
||||
public TextInputTarget
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an editor for a document.
|
||||
|
||||
The tokeniser object is optional - pass nullptr to disable syntax highlighting.
|
||||
The object that you pass in is not owned or deleted by the editor - you must
|
||||
make sure that it doesn't get deleted while this component is still using it.
|
||||
|
||||
@see CodeDocument
|
||||
*/
|
||||
CodeEditorComponent (CodeDocument& document,
|
||||
CodeTokeniser* codeTokeniser);
|
||||
|
||||
/** Destructor. */
|
||||
~CodeEditorComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the code document that this component is editing. */
|
||||
CodeDocument& getDocument() const noexcept { return document; }
|
||||
|
||||
/** Loads the given content into the document.
|
||||
This will completely reset the CodeDocument object, clear its undo history,
|
||||
and fill it with this text.
|
||||
*/
|
||||
void loadContent (const String& newContent);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the standard character width. */
|
||||
float getCharWidth() const noexcept { return charWidth; }
|
||||
|
||||
/** Returns the height of a line of text, in pixels. */
|
||||
int getLineHeight() const noexcept { return lineHeight; }
|
||||
|
||||
/** Returns the number of whole lines visible on the screen,
|
||||
This doesn't include a cut-off line that might be visible at the bottom if the
|
||||
component's height isn't an exact multiple of the line-height.
|
||||
*/
|
||||
int getNumLinesOnScreen() const noexcept { return linesOnScreen; }
|
||||
|
||||
/** Returns the index of the first line that's visible at the top of the editor. */
|
||||
int getFirstLineOnScreen() const noexcept { return firstLineOnScreen; }
|
||||
|
||||
/** Returns the number of whole columns visible on the screen.
|
||||
This doesn't include any cut-off columns at the right-hand edge.
|
||||
*/
|
||||
int getNumColumnsOnScreen() const noexcept { return columnsOnScreen; }
|
||||
|
||||
/** Returns the current caret position. */
|
||||
CodeDocument::Position getCaretPos() const { return caretPos; }
|
||||
|
||||
/** Returns the position of the caret, relative to the editor's origin. */
|
||||
Rectangle<int> getCaretRectangle() override;
|
||||
|
||||
/** Moves the caret.
|
||||
If selecting is true, the section of the document between the current
|
||||
caret position and the new one will become selected. If false, any currently
|
||||
selected region will be deselected.
|
||||
*/
|
||||
void moveCaretTo (const CodeDocument::Position& newPos, bool selecting);
|
||||
|
||||
/** Returns the on-screen position of a character in the document.
|
||||
The rectangle returned is relative to this component's top-left origin.
|
||||
*/
|
||||
Rectangle<int> getCharacterBounds (const CodeDocument::Position& pos) const;
|
||||
|
||||
/** Finds the character at a given on-screen position.
|
||||
The coordinates are relative to this component's top-left origin.
|
||||
*/
|
||||
CodeDocument::Position getPositionAt (int x, int y) const;
|
||||
|
||||
/** Returns the start of the selection as a position. */
|
||||
CodeDocument::Position getSelectionStart() const { return selectionStart; }
|
||||
|
||||
/** Returns the end of the selection as a position. */
|
||||
CodeDocument::Position getSelectionEnd() const { return selectionEnd; }
|
||||
|
||||
/** Enables or disables the line-number display in the gutter. */
|
||||
void setLineNumbersShown (bool shouldBeShown);
|
||||
|
||||
//==============================================================================
|
||||
bool moveCaretLeft (bool moveInWholeWordSteps, bool selecting);
|
||||
bool moveCaretRight (bool moveInWholeWordSteps, bool selecting);
|
||||
bool moveCaretUp (bool selecting);
|
||||
bool moveCaretDown (bool selecting);
|
||||
bool scrollDown();
|
||||
bool scrollUp();
|
||||
bool pageUp (bool selecting);
|
||||
bool pageDown (bool selecting);
|
||||
bool moveCaretToTop (bool selecting);
|
||||
bool moveCaretToStartOfLine (bool selecting);
|
||||
bool moveCaretToEnd (bool selecting);
|
||||
bool moveCaretToEndOfLine (bool selecting);
|
||||
bool deleteBackwards (bool moveInWholeWordSteps);
|
||||
bool deleteForwards (bool moveInWholeWordSteps);
|
||||
bool deleteWhitespaceBackwardsToTabStop();
|
||||
virtual bool copyToClipboard();
|
||||
virtual bool cutToClipboard();
|
||||
virtual bool pasteFromClipboard();
|
||||
bool undo();
|
||||
bool redo();
|
||||
|
||||
void selectRegion (const CodeDocument::Position& start, const CodeDocument::Position& end);
|
||||
bool selectAll();
|
||||
void deselectAll();
|
||||
|
||||
void scrollToLine (int newFirstLineOnScreen);
|
||||
void scrollBy (int deltaLines);
|
||||
void scrollToColumn (int newFirstColumnOnScreen);
|
||||
void scrollToKeepCaretOnScreen();
|
||||
void scrollToKeepLinesOnScreen (Range<int> linesToShow);
|
||||
|
||||
void insertTextAtCaret (const String& textToInsert) override;
|
||||
void insertTabAtCaret();
|
||||
|
||||
void indentSelection();
|
||||
void unindentSelection();
|
||||
|
||||
//==============================================================================
|
||||
Range<int> getHighlightedRegion() const override;
|
||||
bool isHighlightActive() const noexcept;
|
||||
void setHighlightedRegion (const Range<int>& newRange) override;
|
||||
String getTextInRange (const Range<int>& range) const override;
|
||||
|
||||
//==============================================================================
|
||||
/** Can be used to save and restore the editor's caret position, selection state, etc. */
|
||||
struct State
|
||||
{
|
||||
/** Creates an object containing the state of the given editor. */
|
||||
State (const CodeEditorComponent&);
|
||||
/** Creates a state object from a string that was previously created with toString(). */
|
||||
State (const String& stringifiedVersion);
|
||||
State (const State&) noexcept;
|
||||
|
||||
/** Updates the given editor with this saved state. */
|
||||
void restoreState (CodeEditorComponent&) const;
|
||||
|
||||
/** Returns a stringified version of this state that can be used to recreate it later. */
|
||||
String toString() const;
|
||||
|
||||
private:
|
||||
int lastTopLine, lastCaretPos, lastSelectionEnd;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current tab settings.
|
||||
This lets you change the tab size and whether pressing the tab key inserts a
|
||||
tab character, or its equivalent number of spaces.
|
||||
*/
|
||||
void setTabSize (int numSpacesPerTab, bool insertSpacesInsteadOfTabCharacters);
|
||||
|
||||
/** Returns the current number of spaces per tab.
|
||||
@see setTabSize
|
||||
*/
|
||||
int getTabSize() const noexcept { return spacesPerTab; }
|
||||
|
||||
/** Returns true if the tab key will insert spaces instead of actual tab characters.
|
||||
@see setTabSize
|
||||
*/
|
||||
bool areSpacesInsertedForTabs() const { return useSpacesForTabs; }
|
||||
|
||||
/** Returns a string containing spaces or tab characters to generate the given number of spaces. */
|
||||
String getTabString (int numSpaces) const;
|
||||
|
||||
/** Changes the font.
|
||||
Make sure you only use a fixed-width font, or this component will look pretty nasty!
|
||||
*/
|
||||
void setFont (const Font& newFont);
|
||||
|
||||
/** Returns the font that the editor is using. */
|
||||
const Font& getFont() const noexcept { return font; }
|
||||
|
||||
/** Makes the editor read-only. */
|
||||
void setReadOnly (bool shouldBeReadOnly) noexcept;
|
||||
|
||||
/** Returns true if the editor is set to be read-only. */
|
||||
bool isReadOnly() const noexcept { return readOnly; }
|
||||
|
||||
//==============================================================================
|
||||
/** Defines a syntax highlighting colour scheme */
|
||||
struct JUCE_API ColourScheme
|
||||
{
|
||||
/** Defines a colour for a token type */
|
||||
struct TokenType
|
||||
{
|
||||
String name;
|
||||
Colour colour;
|
||||
};
|
||||
|
||||
Array<TokenType> types;
|
||||
|
||||
void set (const String& name, Colour colour);
|
||||
};
|
||||
|
||||
/** Changes the syntax highlighting scheme.
|
||||
The token type values are dependent on the tokeniser being used - use
|
||||
CodeTokeniser::getTokenTypes() to get a list of the token types.
|
||||
@see getColourForTokenType
|
||||
*/
|
||||
void setColourScheme (const ColourScheme& scheme);
|
||||
|
||||
/** Returns the current syntax highlighting colour scheme. */
|
||||
const ColourScheme& getColourScheme() const noexcept { return colourScheme; }
|
||||
|
||||
/** Returns one the syntax highlighting colour for the given token.
|
||||
The token type values are dependent on the tokeniser being used.
|
||||
@see setColourScheme
|
||||
*/
|
||||
Colour getColourForTokenType (int tokenType) const;
|
||||
|
||||
/** Rebuilds the syntax highlighting for a section of text.
|
||||
|
||||
This happens automatically any time the CodeDocument is edited, but this
|
||||
method lets you change text colours even when the CodeDocument hasn't changed.
|
||||
|
||||
For example, you could use this to highlight tokens as the cursor moves.
|
||||
To do so you'll need to tell your custom CodeTokeniser where the token you
|
||||
want to highlight is, and make it return a special type of token. Then you
|
||||
should call this method supplying the range of the highlighted text.
|
||||
@see CodeTokeniser
|
||||
*/
|
||||
void retokenise (int startIndex, int endIndex);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the editor.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1004500, /**< A colour to use to fill the editor's background. */
|
||||
highlightColourId = 0x1004502, /**< The colour to use for the highlighted background under selected text. */
|
||||
defaultTextColourId = 0x1004503, /**< The colour to use for text when no syntax colouring is enabled. */
|
||||
lineNumberBackgroundId = 0x1004504, /**< The colour to use for filling the background of the line-number gutter. */
|
||||
lineNumberTextId = 0x1004505, /**< The colour to use for drawing the line numbers. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the size of the scrollbars. */
|
||||
void setScrollbarThickness (int thickness);
|
||||
|
||||
/** Returns the thickness of the scrollbars. */
|
||||
int getScrollbarThickness() const noexcept { return scrollbarThickness; }
|
||||
|
||||
//==============================================================================
|
||||
/** Called when the return key is pressed - this can be overridden for custom behaviour. */
|
||||
virtual void handleReturnKey();
|
||||
/** Called when the tab key is pressed - this can be overridden for custom behaviour. */
|
||||
virtual void handleTabKey();
|
||||
/** Called when the escape key is pressed - this can be overridden for custom behaviour. */
|
||||
virtual void handleEscapeKey();
|
||||
|
||||
/** Called when the view position is scrolled horizontally or vertically. */
|
||||
virtual void editorViewportPositionChanged();
|
||||
|
||||
/** Called when the caret position moves. */
|
||||
virtual void caretPositionMoved();
|
||||
|
||||
//==============================================================================
|
||||
/** This adds the items to the popup menu.
|
||||
|
||||
By default it adds the cut/copy/paste items, but you can override this if
|
||||
you need to replace these with your own items.
|
||||
|
||||
If you want to add your own items to the existing ones, you can override this,
|
||||
call the base class's addPopupMenuItems() method, then append your own items.
|
||||
|
||||
When the menu has been shown, performPopupMenuAction() will be called to
|
||||
perform the item that the user has chosen.
|
||||
|
||||
If this was triggered by a mouse-click, the mouseClickEvent parameter will be
|
||||
a pointer to the info about it, or may be null if the menu is being triggered
|
||||
by some other means.
|
||||
|
||||
@see performPopupMenuAction, setPopupMenuEnabled, isPopupMenuEnabled
|
||||
*/
|
||||
virtual void addPopupMenuItems (PopupMenu& menuToAddTo,
|
||||
const MouseEvent* mouseClickEvent);
|
||||
|
||||
/** This is called to perform one of the items that was shown on the popup menu.
|
||||
|
||||
If you've overridden addPopupMenuItems(), you should also override this
|
||||
to perform the actions that you've added.
|
||||
|
||||
If you've overridden addPopupMenuItems() but have still left the default items
|
||||
on the menu, remember to call the superclass's performPopupMenuAction()
|
||||
so that it can perform the default actions if that's what the user clicked on.
|
||||
|
||||
@see addPopupMenuItems, setPopupMenuEnabled, isPopupMenuEnabled
|
||||
*/
|
||||
virtual void performPopupMenuAction (int menuItemID);
|
||||
|
||||
/** Specifies a command-manager which the editor will notify whenever the state
|
||||
of any of its commands changes.
|
||||
If you're making use of the editor's ApplicationCommandTarget interface, then
|
||||
you should also use this to tell it which command manager it should use. Make
|
||||
sure that the manager does not go out of scope while the editor is using it. You
|
||||
can pass a nullptr here to disable this.
|
||||
*/
|
||||
void setCommandManager (ApplicationCommandManager* newManager) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDoubleClick (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
|
||||
/** @internal */
|
||||
void focusGained (FocusChangeType) override;
|
||||
/** @internal */
|
||||
void focusLost (FocusChangeType) override;
|
||||
/** @internal */
|
||||
bool isTextInputActive() const override;
|
||||
/** @internal */
|
||||
void setTemporaryUnderlining (const Array<Range<int>>&) override;
|
||||
/** @internal */
|
||||
ApplicationCommandTarget* getNextCommandTarget() override;
|
||||
/** @internal */
|
||||
void getAllCommands (Array<CommandID>&) override;
|
||||
/** @internal */
|
||||
void getCommandInfo (CommandID, ApplicationCommandInfo&) override;
|
||||
/** @internal */
|
||||
bool perform (const InvocationInfo&) override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
CodeDocument& document;
|
||||
|
||||
Font font;
|
||||
int firstLineOnScreen = 0, spacesPerTab = 4;
|
||||
float charWidth = 0;
|
||||
int lineHeight = 0, linesOnScreen = 0, columnsOnScreen = 0;
|
||||
int scrollbarThickness = 16, columnToTryToMaintain = -1;
|
||||
bool readOnly = false, useSpacesForTabs = true, showLineNumbers = false, shouldFollowDocumentChanges = false;
|
||||
double xOffset = 0;
|
||||
CodeDocument::Position caretPos, selectionStart, selectionEnd;
|
||||
|
||||
std::unique_ptr<CaretComponent> caret;
|
||||
ScrollBar verticalScrollBar { true }, horizontalScrollBar { false };
|
||||
ApplicationCommandManager* appCommandManager = nullptr;
|
||||
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
class GutterComponent;
|
||||
std::unique_ptr<GutterComponent> gutter;
|
||||
|
||||
class CodeEditorAccessibilityHandler;
|
||||
|
||||
enum DragType
|
||||
{
|
||||
notDragging,
|
||||
draggingSelectionStart,
|
||||
draggingSelectionEnd
|
||||
};
|
||||
|
||||
DragType dragType = notDragging;
|
||||
|
||||
//==============================================================================
|
||||
CodeTokeniser* codeTokeniser;
|
||||
ColourScheme colourScheme;
|
||||
|
||||
class CodeEditorLine;
|
||||
OwnedArray<CodeEditorLine> lines;
|
||||
void rebuildLineTokens();
|
||||
void rebuildLineTokensAsync();
|
||||
void codeDocumentChanged (int start, int end);
|
||||
|
||||
Array<CodeDocument::Iterator> cachedIterators;
|
||||
void clearCachedIterators (int firstLineToBeInvalid);
|
||||
void updateCachedIterators (int maxLineNum);
|
||||
void getIteratorForPosition (int position, CodeDocument::Iterator&);
|
||||
|
||||
void moveLineDelta (int delta, bool selecting);
|
||||
int getGutterSize() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void insertText (const String&);
|
||||
virtual void updateCaretPosition();
|
||||
void updateScrollBars();
|
||||
void scrollToLineInternal (int line);
|
||||
void scrollToColumnInternal (double column);
|
||||
void newTransaction();
|
||||
void cut();
|
||||
void indentSelectedLines (int spacesToAdd);
|
||||
bool skipBackwardsToPreviousTab();
|
||||
bool performCommand (CommandID);
|
||||
void setSelection (CodeDocument::Position, CodeDocument::Position);
|
||||
|
||||
int indexToColumn (int line, int index) const noexcept;
|
||||
int columnToIndex (int line, int column) const noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeEditorComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class CodeTokeniser;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A text editor component designed specifically for source code.
|
||||
|
||||
This is designed to handle syntax highlighting and fast editing of very large
|
||||
files.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CodeEditorComponent : public Component,
|
||||
public ApplicationCommandTarget,
|
||||
public TextInputTarget
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an editor for a document.
|
||||
|
||||
The tokeniser object is optional - pass nullptr to disable syntax highlighting.
|
||||
The object that you pass in is not owned or deleted by the editor - you must
|
||||
make sure that it doesn't get deleted while this component is still using it.
|
||||
|
||||
@see CodeDocument
|
||||
*/
|
||||
CodeEditorComponent (CodeDocument& document,
|
||||
CodeTokeniser* codeTokeniser);
|
||||
|
||||
/** Destructor. */
|
||||
~CodeEditorComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the code document that this component is editing. */
|
||||
CodeDocument& getDocument() const noexcept { return document; }
|
||||
|
||||
/** Loads the given content into the document.
|
||||
This will completely reset the CodeDocument object, clear its undo history,
|
||||
and fill it with this text.
|
||||
*/
|
||||
void loadContent (const String& newContent);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the standard character width. */
|
||||
float getCharWidth() const noexcept { return charWidth; }
|
||||
|
||||
/** Returns the height of a line of text, in pixels. */
|
||||
int getLineHeight() const noexcept { return lineHeight; }
|
||||
|
||||
/** Returns the number of whole lines visible on the screen,
|
||||
This doesn't include a cut-off line that might be visible at the bottom if the
|
||||
component's height isn't an exact multiple of the line-height.
|
||||
*/
|
||||
int getNumLinesOnScreen() const noexcept { return linesOnScreen; }
|
||||
|
||||
/** Returns the index of the first line that's visible at the top of the editor. */
|
||||
int getFirstLineOnScreen() const noexcept { return firstLineOnScreen; }
|
||||
|
||||
/** Returns the number of whole columns visible on the screen.
|
||||
This doesn't include any cut-off columns at the right-hand edge.
|
||||
*/
|
||||
int getNumColumnsOnScreen() const noexcept { return columnsOnScreen; }
|
||||
|
||||
/** Returns the current caret position. */
|
||||
CodeDocument::Position getCaretPos() const { return caretPos; }
|
||||
|
||||
/** Returns the total number of codepoints in the string. */
|
||||
int getTotalNumChars() const override { return document.getNumCharacters(); }
|
||||
|
||||
/** Moves the caret.
|
||||
If selecting is true, the section of the document between the current
|
||||
caret position and the new one will become selected. If false, any currently
|
||||
selected region will be deselected.
|
||||
*/
|
||||
void moveCaretTo (const CodeDocument::Position& newPos, bool selecting);
|
||||
|
||||
/** Returns the on-screen position of a character in the document.
|
||||
The rectangle returned is relative to this component's top-left origin.
|
||||
*/
|
||||
Rectangle<int> getCharacterBounds (const CodeDocument::Position& pos) const;
|
||||
|
||||
/** Finds the character at a given on-screen position.
|
||||
The coordinates are relative to this component's top-left origin.
|
||||
*/
|
||||
CodeDocument::Position getPositionAt (int x, int y) const;
|
||||
|
||||
/** Returns the start of the selection as a position. */
|
||||
CodeDocument::Position getSelectionStart() const { return selectionStart; }
|
||||
|
||||
/** Returns the end of the selection as a position. */
|
||||
CodeDocument::Position getSelectionEnd() const { return selectionEnd; }
|
||||
|
||||
/** Enables or disables the line-number display in the gutter. */
|
||||
void setLineNumbersShown (bool shouldBeShown);
|
||||
|
||||
/** Returns the number of characters from the beginning of the document to the caret. */
|
||||
int getCaretPosition() const override { return getCaretPos().getPosition(); }
|
||||
|
||||
/** @see getPositionAt */
|
||||
int getCharIndexForPoint (Point<int> point) const override;
|
||||
|
||||
/** Returns the bounds of the caret at a particular location in the text. */
|
||||
Rectangle<int> getCaretRectangleForCharIndex (int index) const override
|
||||
{
|
||||
return getCharacterBounds ({ document, index });
|
||||
}
|
||||
|
||||
/** Returns the bounding box for a range of text in the editor. As the range may span
|
||||
multiple lines, this method returns a RectangleList.
|
||||
|
||||
The bounds are relative to the component's top-left and may extend beyond the bounds
|
||||
of the component if the text is long and word wrapping is disabled.
|
||||
*/
|
||||
RectangleList<int> getTextBounds (Range<int> textRange) const override;
|
||||
|
||||
//==============================================================================
|
||||
bool moveCaretLeft (bool moveInWholeWordSteps, bool selecting);
|
||||
bool moveCaretRight (bool moveInWholeWordSteps, bool selecting);
|
||||
bool moveCaretUp (bool selecting);
|
||||
bool moveCaretDown (bool selecting);
|
||||
bool scrollDown();
|
||||
bool scrollUp();
|
||||
bool pageUp (bool selecting);
|
||||
bool pageDown (bool selecting);
|
||||
bool moveCaretToTop (bool selecting);
|
||||
bool moveCaretToStartOfLine (bool selecting);
|
||||
bool moveCaretToEnd (bool selecting);
|
||||
bool moveCaretToEndOfLine (bool selecting);
|
||||
bool deleteBackwards (bool moveInWholeWordSteps);
|
||||
bool deleteForwards (bool moveInWholeWordSteps);
|
||||
bool deleteWhitespaceBackwardsToTabStop();
|
||||
virtual bool copyToClipboard();
|
||||
virtual bool cutToClipboard();
|
||||
virtual bool pasteFromClipboard();
|
||||
bool undo();
|
||||
bool redo();
|
||||
|
||||
void selectRegion (const CodeDocument::Position& start, const CodeDocument::Position& end);
|
||||
bool selectAll();
|
||||
void deselectAll();
|
||||
|
||||
void scrollToLine (int newFirstLineOnScreen);
|
||||
void scrollBy (int deltaLines);
|
||||
void scrollToColumn (int newFirstColumnOnScreen);
|
||||
void scrollToKeepCaretOnScreen();
|
||||
void scrollToKeepLinesOnScreen (Range<int> linesToShow);
|
||||
|
||||
void insertTextAtCaret (const String& textToInsert) override;
|
||||
void insertTabAtCaret();
|
||||
|
||||
void indentSelection();
|
||||
void unindentSelection();
|
||||
|
||||
//==============================================================================
|
||||
Range<int> getHighlightedRegion() const override;
|
||||
bool isHighlightActive() const noexcept;
|
||||
void setHighlightedRegion (const Range<int>& newRange) override;
|
||||
String getTextInRange (const Range<int>& range) const override;
|
||||
|
||||
//==============================================================================
|
||||
/** Can be used to save and restore the editor's caret position, selection state, etc. */
|
||||
struct State
|
||||
{
|
||||
/** Creates an object containing the state of the given editor. */
|
||||
State (const CodeEditorComponent&);
|
||||
/** Creates a state object from a string that was previously created with toString(). */
|
||||
State (const String& stringifiedVersion);
|
||||
State (const State&) noexcept;
|
||||
|
||||
/** Updates the given editor with this saved state. */
|
||||
void restoreState (CodeEditorComponent&) const;
|
||||
|
||||
/** Returns a stringified version of this state that can be used to recreate it later. */
|
||||
String toString() const;
|
||||
|
||||
private:
|
||||
int lastTopLine, lastCaretPos, lastSelectionEnd;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current tab settings.
|
||||
This lets you change the tab size and whether pressing the tab key inserts a
|
||||
tab character, or its equivalent number of spaces.
|
||||
*/
|
||||
void setTabSize (int numSpacesPerTab, bool insertSpacesInsteadOfTabCharacters);
|
||||
|
||||
/** Returns the current number of spaces per tab.
|
||||
@see setTabSize
|
||||
*/
|
||||
int getTabSize() const noexcept { return spacesPerTab; }
|
||||
|
||||
/** Returns true if the tab key will insert spaces instead of actual tab characters.
|
||||
@see setTabSize
|
||||
*/
|
||||
bool areSpacesInsertedForTabs() const { return useSpacesForTabs; }
|
||||
|
||||
/** Returns a string containing spaces or tab characters to generate the given number of spaces. */
|
||||
String getTabString (int numSpaces) const;
|
||||
|
||||
/** Changes the font.
|
||||
Make sure you only use a fixed-width font, or this component will look pretty nasty!
|
||||
*/
|
||||
void setFont (const Font& newFont);
|
||||
|
||||
/** Returns the font that the editor is using. */
|
||||
const Font& getFont() const noexcept { return font; }
|
||||
|
||||
/** Makes the editor read-only. */
|
||||
void setReadOnly (bool shouldBeReadOnly) noexcept;
|
||||
|
||||
/** Returns true if the editor is set to be read-only. */
|
||||
bool isReadOnly() const noexcept { return readOnly; }
|
||||
|
||||
//==============================================================================
|
||||
/** Defines a syntax highlighting colour scheme */
|
||||
struct JUCE_API ColourScheme
|
||||
{
|
||||
/** Defines a colour for a token type */
|
||||
struct TokenType
|
||||
{
|
||||
String name;
|
||||
Colour colour;
|
||||
};
|
||||
|
||||
Array<TokenType> types;
|
||||
|
||||
void set (const String& name, Colour colour);
|
||||
};
|
||||
|
||||
/** Changes the syntax highlighting scheme.
|
||||
The token type values are dependent on the tokeniser being used - use
|
||||
CodeTokeniser::getTokenTypes() to get a list of the token types.
|
||||
@see getColourForTokenType
|
||||
*/
|
||||
void setColourScheme (const ColourScheme& scheme);
|
||||
|
||||
/** Returns the current syntax highlighting colour scheme. */
|
||||
const ColourScheme& getColourScheme() const noexcept { return colourScheme; }
|
||||
|
||||
/** Returns one the syntax highlighting colour for the given token.
|
||||
The token type values are dependent on the tokeniser being used.
|
||||
@see setColourScheme
|
||||
*/
|
||||
Colour getColourForTokenType (int tokenType) const;
|
||||
|
||||
/** Rebuilds the syntax highlighting for a section of text.
|
||||
|
||||
This happens automatically any time the CodeDocument is edited, but this
|
||||
method lets you change text colours even when the CodeDocument hasn't changed.
|
||||
|
||||
For example, you could use this to highlight tokens as the cursor moves.
|
||||
To do so you'll need to tell your custom CodeTokeniser where the token you
|
||||
want to highlight is, and make it return a special type of token. Then you
|
||||
should call this method supplying the range of the highlighted text.
|
||||
@see CodeTokeniser
|
||||
*/
|
||||
void retokenise (int startIndex, int endIndex);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the editor.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1004500, /**< A colour to use to fill the editor's background. */
|
||||
highlightColourId = 0x1004502, /**< The colour to use for the highlighted background under selected text. */
|
||||
defaultTextColourId = 0x1004503, /**< The colour to use for text when no syntax colouring is enabled. */
|
||||
lineNumberBackgroundId = 0x1004504, /**< The colour to use for filling the background of the line-number gutter. */
|
||||
lineNumberTextId = 0x1004505, /**< The colour to use for drawing the line numbers. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the size of the scrollbars. */
|
||||
void setScrollbarThickness (int thickness);
|
||||
|
||||
/** Returns the thickness of the scrollbars. */
|
||||
int getScrollbarThickness() const noexcept { return scrollbarThickness; }
|
||||
|
||||
//==============================================================================
|
||||
/** Called when the return key is pressed - this can be overridden for custom behaviour. */
|
||||
virtual void handleReturnKey();
|
||||
/** Called when the tab key is pressed - this can be overridden for custom behaviour. */
|
||||
virtual void handleTabKey();
|
||||
/** Called when the escape key is pressed - this can be overridden for custom behaviour. */
|
||||
virtual void handleEscapeKey();
|
||||
|
||||
/** Called when the view position is scrolled horizontally or vertically. */
|
||||
virtual void editorViewportPositionChanged();
|
||||
|
||||
/** Called when the caret position moves. */
|
||||
virtual void caretPositionMoved();
|
||||
|
||||
//==============================================================================
|
||||
/** This adds the items to the popup menu.
|
||||
|
||||
By default it adds the cut/copy/paste items, but you can override this if
|
||||
you need to replace these with your own items.
|
||||
|
||||
If you want to add your own items to the existing ones, you can override this,
|
||||
call the base class's addPopupMenuItems() method, then append your own items.
|
||||
|
||||
When the menu has been shown, performPopupMenuAction() will be called to
|
||||
perform the item that the user has chosen.
|
||||
|
||||
If this was triggered by a mouse-click, the mouseClickEvent parameter will be
|
||||
a pointer to the info about it, or may be null if the menu is being triggered
|
||||
by some other means.
|
||||
|
||||
@see performPopupMenuAction, setPopupMenuEnabled, isPopupMenuEnabled
|
||||
*/
|
||||
virtual void addPopupMenuItems (PopupMenu& menuToAddTo,
|
||||
const MouseEvent* mouseClickEvent);
|
||||
|
||||
/** This is called to perform one of the items that was shown on the popup menu.
|
||||
|
||||
If you've overridden addPopupMenuItems(), you should also override this
|
||||
to perform the actions that you've added.
|
||||
|
||||
If you've overridden addPopupMenuItems() but have still left the default items
|
||||
on the menu, remember to call the superclass's performPopupMenuAction()
|
||||
so that it can perform the default actions if that's what the user clicked on.
|
||||
|
||||
@see addPopupMenuItems, setPopupMenuEnabled, isPopupMenuEnabled
|
||||
*/
|
||||
virtual void performPopupMenuAction (int menuItemID);
|
||||
|
||||
/** Specifies a command-manager which the editor will notify whenever the state
|
||||
of any of its commands changes.
|
||||
If you're making use of the editor's ApplicationCommandTarget interface, then
|
||||
you should also use this to tell it which command manager it should use. Make
|
||||
sure that the manager does not go out of scope while the editor is using it. You
|
||||
can pass a nullptr here to disable this.
|
||||
*/
|
||||
void setCommandManager (ApplicationCommandManager* newManager) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDoubleClick (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
|
||||
/** @internal */
|
||||
void focusGained (FocusChangeType) override;
|
||||
/** @internal */
|
||||
void focusLost (FocusChangeType) override;
|
||||
/** @internal */
|
||||
bool isTextInputActive() const override;
|
||||
/** @internal */
|
||||
void setTemporaryUnderlining (const Array<Range<int>>&) override;
|
||||
/** @internal */
|
||||
ApplicationCommandTarget* getNextCommandTarget() override;
|
||||
/** @internal */
|
||||
void getAllCommands (Array<CommandID>&) override;
|
||||
/** @internal */
|
||||
void getCommandInfo (CommandID, ApplicationCommandInfo&) override;
|
||||
/** @internal */
|
||||
bool perform (const InvocationInfo&) override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
CodeDocument& document;
|
||||
|
||||
Font font;
|
||||
int firstLineOnScreen = 0, spacesPerTab = 4;
|
||||
float charWidth = 0;
|
||||
int lineHeight = 0, linesOnScreen = 0, columnsOnScreen = 0;
|
||||
int scrollbarThickness = 16, columnToTryToMaintain = -1;
|
||||
bool readOnly = false, useSpacesForTabs = true, showLineNumbers = false, shouldFollowDocumentChanges = false;
|
||||
double xOffset = 0;
|
||||
CodeDocument::Position caretPos, selectionStart, selectionEnd;
|
||||
|
||||
std::unique_ptr<CaretComponent> caret;
|
||||
ScrollBar verticalScrollBar { true }, horizontalScrollBar { false };
|
||||
ApplicationCommandManager* appCommandManager = nullptr;
|
||||
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
class GutterComponent;
|
||||
std::unique_ptr<GutterComponent> gutter;
|
||||
|
||||
class CodeEditorAccessibilityHandler;
|
||||
|
||||
enum DragType
|
||||
{
|
||||
notDragging,
|
||||
draggingSelectionStart,
|
||||
draggingSelectionEnd
|
||||
};
|
||||
|
||||
DragType dragType = notDragging;
|
||||
|
||||
//==============================================================================
|
||||
CodeTokeniser* codeTokeniser;
|
||||
ColourScheme colourScheme;
|
||||
|
||||
class CodeEditorLine;
|
||||
OwnedArray<CodeEditorLine> lines;
|
||||
void rebuildLineTokens();
|
||||
void rebuildLineTokensAsync();
|
||||
void codeDocumentChanged (int start, int end);
|
||||
|
||||
Array<CodeDocument::Iterator> cachedIterators;
|
||||
void clearCachedIterators (int firstLineToBeInvalid);
|
||||
void updateCachedIterators (int maxLineNum);
|
||||
void getIteratorForPosition (int position, CodeDocument::Iterator&);
|
||||
|
||||
void moveLineDelta (int delta, bool selecting);
|
||||
int getGutterSize() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void insertText (const String&);
|
||||
virtual void updateCaretPosition();
|
||||
void updateScrollBars();
|
||||
void scrollToLineInternal (int line);
|
||||
void scrollToColumnInternal (double column);
|
||||
void newTransaction();
|
||||
void cut();
|
||||
void indentSelectedLines (int spacesToAdd);
|
||||
bool skipBackwardsToPreviousTab();
|
||||
bool performCommand (CommandID);
|
||||
void setSelection (CodeDocument::Position, CodeDocument::Position);
|
||||
|
||||
int indexToColumn (int line, int index) const noexcept;
|
||||
int columnToIndex (int line, int column) const noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CodeEditorComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,59 +1,59 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for tokenising code so that the syntax can be displayed in a
|
||||
code editor.
|
||||
|
||||
@see CodeDocument, CodeEditorComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CodeTokeniser
|
||||
{
|
||||
public:
|
||||
CodeTokeniser() = default;
|
||||
virtual ~CodeTokeniser() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Reads the next token from the source and returns its token type.
|
||||
|
||||
This must leave the source pointing to the first character in the
|
||||
next token.
|
||||
*/
|
||||
virtual int readNextToken (CodeDocument::Iterator& source) = 0;
|
||||
|
||||
/** Returns a suggested syntax highlighting colour scheme. */
|
||||
virtual CodeEditorComponent::ColourScheme getDefaultColourScheme() = 0;
|
||||
|
||||
private:
|
||||
JUCE_LEAK_DETECTOR (CodeTokeniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for tokenising code so that the syntax can be displayed in a
|
||||
code editor.
|
||||
|
||||
@see CodeDocument, CodeEditorComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CodeTokeniser
|
||||
{
|
||||
public:
|
||||
CodeTokeniser() = default;
|
||||
virtual ~CodeTokeniser() = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Reads the next token from the source and returns its token type.
|
||||
|
||||
This must leave the source pointing to the first character in the
|
||||
next token.
|
||||
*/
|
||||
virtual int readNextToken (CodeDocument::Iterator& source) = 0;
|
||||
|
||||
/** Returns a suggested syntax highlighting colour scheme. */
|
||||
virtual CodeEditorComponent::ColourScheme getDefaultColourScheme() = 0;
|
||||
|
||||
private:
|
||||
JUCE_LEAK_DETECTOR (CodeTokeniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,239 +1,239 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct LuaTokeniserFunctions
|
||||
{
|
||||
static bool isReservedKeyword (String::CharPointerType token, const int tokenLength) noexcept
|
||||
{
|
||||
static const char* const keywords2Char[] =
|
||||
{ "if", "or", "in", "do", nullptr };
|
||||
|
||||
static const char* const keywords3Char[] =
|
||||
{ "and", "end", "for", "nil", "not", nullptr };
|
||||
|
||||
static const char* const keywords4Char[] =
|
||||
{ "then", "true", "else", nullptr };
|
||||
|
||||
static const char* const keywords5Char[] =
|
||||
{ "false", "local", "until", "while", "break", nullptr };
|
||||
|
||||
static const char* const keywords6Char[] =
|
||||
{ "repeat", "return", "elseif", nullptr};
|
||||
|
||||
static const char* const keywordsOther[] =
|
||||
{ "function", "@interface", "@end", "@synthesize", "@dynamic", "@public",
|
||||
"@private", "@property", "@protected", "@class", nullptr };
|
||||
|
||||
const char* const* k;
|
||||
|
||||
switch (tokenLength)
|
||||
{
|
||||
case 2: k = keywords2Char; break;
|
||||
case 3: k = keywords3Char; break;
|
||||
case 4: k = keywords4Char; break;
|
||||
case 5: k = keywords5Char; break;
|
||||
case 6: k = keywords6Char; break;
|
||||
|
||||
default:
|
||||
if (tokenLength < 2 || tokenLength > 16)
|
||||
return false;
|
||||
|
||||
k = keywordsOther;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; k[i] != nullptr; ++i)
|
||||
if (token.compare (CharPointer_ASCII (k[i])) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static int parseIdentifier (Iterator& source) noexcept
|
||||
{
|
||||
int tokenLength = 0;
|
||||
String::CharPointerType::CharType possibleIdentifier[100];
|
||||
String::CharPointerType possible (possibleIdentifier);
|
||||
|
||||
while (CppTokeniserFunctions::isIdentifierBody (source.peekNextChar()))
|
||||
{
|
||||
auto c = source.nextChar();
|
||||
|
||||
if (tokenLength < 20)
|
||||
possible.write (c);
|
||||
|
||||
++tokenLength;
|
||||
}
|
||||
|
||||
if (tokenLength > 1 && tokenLength <= 16)
|
||||
{
|
||||
possible.writeNull();
|
||||
|
||||
if (isReservedKeyword (String::CharPointerType (possibleIdentifier), tokenLength))
|
||||
return LuaTokeniser::tokenType_keyword;
|
||||
}
|
||||
|
||||
return LuaTokeniser::tokenType_identifier;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static int readNextToken (Iterator& source)
|
||||
{
|
||||
source.skipWhitespace();
|
||||
|
||||
auto firstChar = source.peekNextChar();
|
||||
|
||||
switch (firstChar)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case '.':
|
||||
{
|
||||
auto result = CppTokeniserFunctions::parseNumber (source);
|
||||
|
||||
if (result == LuaTokeniser::tokenType_error)
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (firstChar == '.')
|
||||
return LuaTokeniser::tokenType_punctuation;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case ',':
|
||||
case ';':
|
||||
case ':':
|
||||
source.skip();
|
||||
return LuaTokeniser::tokenType_punctuation;
|
||||
|
||||
case '(': case ')':
|
||||
case '{': case '}':
|
||||
case '[': case ']':
|
||||
source.skip();
|
||||
return LuaTokeniser::tokenType_bracket;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
CppTokeniserFunctions::skipQuotedString (source);
|
||||
return LuaTokeniser::tokenType_string;
|
||||
|
||||
case '+':
|
||||
source.skip();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '+', '=');
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
|
||||
case '-':
|
||||
{
|
||||
source.skip();
|
||||
auto result = CppTokeniserFunctions::parseNumber (source);
|
||||
|
||||
if (source.peekNextChar() == '-')
|
||||
{
|
||||
source.skipToEndOfLine();
|
||||
return LuaTokeniser::tokenType_comment;
|
||||
}
|
||||
|
||||
if (result == LuaTokeniser::tokenType_error)
|
||||
{
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '-', '=');
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case '*': case '%':
|
||||
case '=': case '!':
|
||||
source.skip();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '=');
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
|
||||
case '?':
|
||||
case '~':
|
||||
source.skip();
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
|
||||
case '<': case '>':
|
||||
case '|': case '&': case '^':
|
||||
source.skip();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, firstChar);
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '=');
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
|
||||
default:
|
||||
if (CppTokeniserFunctions::isIdentifierStart (firstChar))
|
||||
return parseIdentifier (source);
|
||||
|
||||
source.skip();
|
||||
break;
|
||||
}
|
||||
|
||||
return LuaTokeniser::tokenType_error;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
LuaTokeniser::LuaTokeniser() {}
|
||||
LuaTokeniser::~LuaTokeniser() {}
|
||||
|
||||
int LuaTokeniser::readNextToken (CodeDocument::Iterator& source)
|
||||
{
|
||||
return LuaTokeniserFunctions::readNextToken (source);
|
||||
}
|
||||
|
||||
CodeEditorComponent::ColourScheme LuaTokeniser::getDefaultColourScheme()
|
||||
{
|
||||
static const CodeEditorComponent::ColourScheme::TokenType types[] =
|
||||
{
|
||||
{ "Error", Colour (0xffcc0000) },
|
||||
{ "Comment", Colour (0xff3c3c3c) },
|
||||
{ "Keyword", Colour (0xff0000cc) },
|
||||
{ "Operator", Colour (0xff225500) },
|
||||
{ "Identifier", Colour (0xff000000) },
|
||||
{ "Integer", Colour (0xff880000) },
|
||||
{ "Float", Colour (0xff885500) },
|
||||
{ "String", Colour (0xff990099) },
|
||||
{ "Bracket", Colour (0xff000055) },
|
||||
{ "Punctuation", Colour (0xff004400) }
|
||||
};
|
||||
|
||||
CodeEditorComponent::ColourScheme cs;
|
||||
|
||||
for (auto& t : types)
|
||||
cs.set (t.name, Colour (t.colour));
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct LuaTokeniserFunctions
|
||||
{
|
||||
static bool isReservedKeyword (String::CharPointerType token, const int tokenLength) noexcept
|
||||
{
|
||||
static const char* const keywords2Char[] =
|
||||
{ "if", "or", "in", "do", nullptr };
|
||||
|
||||
static const char* const keywords3Char[] =
|
||||
{ "and", "end", "for", "nil", "not", nullptr };
|
||||
|
||||
static const char* const keywords4Char[] =
|
||||
{ "then", "true", "else", nullptr };
|
||||
|
||||
static const char* const keywords5Char[] =
|
||||
{ "false", "local", "until", "while", "break", nullptr };
|
||||
|
||||
static const char* const keywords6Char[] =
|
||||
{ "repeat", "return", "elseif", nullptr};
|
||||
|
||||
static const char* const keywordsOther[] =
|
||||
{ "function", "@interface", "@end", "@synthesize", "@dynamic", "@public",
|
||||
"@private", "@property", "@protected", "@class", nullptr };
|
||||
|
||||
const char* const* k;
|
||||
|
||||
switch (tokenLength)
|
||||
{
|
||||
case 2: k = keywords2Char; break;
|
||||
case 3: k = keywords3Char; break;
|
||||
case 4: k = keywords4Char; break;
|
||||
case 5: k = keywords5Char; break;
|
||||
case 6: k = keywords6Char; break;
|
||||
|
||||
default:
|
||||
if (tokenLength < 2 || tokenLength > 16)
|
||||
return false;
|
||||
|
||||
k = keywordsOther;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; k[i] != nullptr; ++i)
|
||||
if (token.compare (CharPointer_ASCII (k[i])) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static int parseIdentifier (Iterator& source) noexcept
|
||||
{
|
||||
int tokenLength = 0;
|
||||
String::CharPointerType::CharType possibleIdentifier[100] = {};
|
||||
String::CharPointerType possible (possibleIdentifier);
|
||||
|
||||
while (CppTokeniserFunctions::isIdentifierBody (source.peekNextChar()))
|
||||
{
|
||||
auto c = source.nextChar();
|
||||
|
||||
if (tokenLength < 20)
|
||||
possible.write (c);
|
||||
|
||||
++tokenLength;
|
||||
}
|
||||
|
||||
if (tokenLength > 1 && tokenLength <= 16)
|
||||
{
|
||||
possible.writeNull();
|
||||
|
||||
if (isReservedKeyword (String::CharPointerType (possibleIdentifier), tokenLength))
|
||||
return LuaTokeniser::tokenType_keyword;
|
||||
}
|
||||
|
||||
return LuaTokeniser::tokenType_identifier;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static int readNextToken (Iterator& source)
|
||||
{
|
||||
source.skipWhitespace();
|
||||
|
||||
auto firstChar = source.peekNextChar();
|
||||
|
||||
switch (firstChar)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case '.':
|
||||
{
|
||||
auto result = CppTokeniserFunctions::parseNumber (source);
|
||||
|
||||
if (result == LuaTokeniser::tokenType_error)
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (firstChar == '.')
|
||||
return LuaTokeniser::tokenType_punctuation;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case ',':
|
||||
case ';':
|
||||
case ':':
|
||||
source.skip();
|
||||
return LuaTokeniser::tokenType_punctuation;
|
||||
|
||||
case '(': case ')':
|
||||
case '{': case '}':
|
||||
case '[': case ']':
|
||||
source.skip();
|
||||
return LuaTokeniser::tokenType_bracket;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
CppTokeniserFunctions::skipQuotedString (source);
|
||||
return LuaTokeniser::tokenType_string;
|
||||
|
||||
case '+':
|
||||
source.skip();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '+', '=');
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
|
||||
case '-':
|
||||
{
|
||||
source.skip();
|
||||
auto result = CppTokeniserFunctions::parseNumber (source);
|
||||
|
||||
if (source.peekNextChar() == '-')
|
||||
{
|
||||
source.skipToEndOfLine();
|
||||
return LuaTokeniser::tokenType_comment;
|
||||
}
|
||||
|
||||
if (result == LuaTokeniser::tokenType_error)
|
||||
{
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '-', '=');
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case '*': case '%':
|
||||
case '=': case '!':
|
||||
source.skip();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '=');
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
|
||||
case '?':
|
||||
case '~':
|
||||
source.skip();
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
|
||||
case '<': case '>':
|
||||
case '|': case '&': case '^':
|
||||
source.skip();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, firstChar);
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '=');
|
||||
return LuaTokeniser::tokenType_operator;
|
||||
|
||||
default:
|
||||
if (CppTokeniserFunctions::isIdentifierStart (firstChar))
|
||||
return parseIdentifier (source);
|
||||
|
||||
source.skip();
|
||||
break;
|
||||
}
|
||||
|
||||
return LuaTokeniser::tokenType_error;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
LuaTokeniser::LuaTokeniser() {}
|
||||
LuaTokeniser::~LuaTokeniser() {}
|
||||
|
||||
int LuaTokeniser::readNextToken (CodeDocument::Iterator& source)
|
||||
{
|
||||
return LuaTokeniserFunctions::readNextToken (source);
|
||||
}
|
||||
|
||||
CodeEditorComponent::ColourScheme LuaTokeniser::getDefaultColourScheme()
|
||||
{
|
||||
static const CodeEditorComponent::ColourScheme::TokenType types[] =
|
||||
{
|
||||
{ "Error", Colour (0xffcc0000) },
|
||||
{ "Comment", Colour (0xff3c3c3c) },
|
||||
{ "Keyword", Colour (0xff0000cc) },
|
||||
{ "Operator", Colour (0xff225500) },
|
||||
{ "Identifier", Colour (0xff000000) },
|
||||
{ "Integer", Colour (0xff880000) },
|
||||
{ "Float", Colour (0xff885500) },
|
||||
{ "String", Colour (0xff990099) },
|
||||
{ "Bracket", Colour (0xff000055) },
|
||||
{ "Punctuation", Colour (0xff004400) }
|
||||
};
|
||||
|
||||
CodeEditorComponent::ColourScheme cs;
|
||||
|
||||
for (auto& t : types)
|
||||
cs.set (t.name, Colour (t.colour));
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,65 +1,65 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API LuaTokeniser : public CodeTokeniser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
LuaTokeniser();
|
||||
~LuaTokeniser() override;
|
||||
|
||||
//==============================================================================
|
||||
int readNextToken (CodeDocument::Iterator&) override;
|
||||
CodeEditorComponent::ColourScheme getDefaultColourScheme() override;
|
||||
|
||||
/** The token values returned by this tokeniser. */
|
||||
enum TokenType
|
||||
{
|
||||
tokenType_error = 0,
|
||||
tokenType_comment,
|
||||
tokenType_keyword,
|
||||
tokenType_operator,
|
||||
tokenType_identifier,
|
||||
tokenType_integer,
|
||||
tokenType_float,
|
||||
tokenType_string,
|
||||
tokenType_bracket,
|
||||
tokenType_punctuation
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LuaTokeniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API LuaTokeniser : public CodeTokeniser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
LuaTokeniser();
|
||||
~LuaTokeniser() override;
|
||||
|
||||
//==============================================================================
|
||||
int readNextToken (CodeDocument::Iterator&) override;
|
||||
CodeEditorComponent::ColourScheme getDefaultColourScheme() override;
|
||||
|
||||
/** The token values returned by this tokeniser. */
|
||||
enum TokenType
|
||||
{
|
||||
tokenType_error = 0,
|
||||
tokenType_comment,
|
||||
tokenType_keyword,
|
||||
tokenType_operator,
|
||||
tokenType_identifier,
|
||||
tokenType_integer,
|
||||
tokenType_float,
|
||||
tokenType_string,
|
||||
tokenType_bracket,
|
||||
tokenType_punctuation
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LuaTokeniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,172 +1,172 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
XmlTokeniser::XmlTokeniser() {}
|
||||
XmlTokeniser::~XmlTokeniser() {}
|
||||
|
||||
CodeEditorComponent::ColourScheme XmlTokeniser::getDefaultColourScheme()
|
||||
{
|
||||
struct Type
|
||||
{
|
||||
const char* name;
|
||||
uint32 colour;
|
||||
};
|
||||
|
||||
const Type types[] =
|
||||
{
|
||||
{ "Error", 0xffcc0000 },
|
||||
{ "Comment", 0xff00aa00 },
|
||||
{ "Keyword", 0xff0000cc },
|
||||
{ "Operator", 0xff225500 },
|
||||
{ "Identifier", 0xff000000 },
|
||||
{ "String", 0xff990099 },
|
||||
{ "Bracket", 0xff000055 },
|
||||
{ "Punctuation", 0xff004400 },
|
||||
{ "Preprocessor Text", 0xff660000 }
|
||||
};
|
||||
|
||||
CodeEditorComponent::ColourScheme cs;
|
||||
|
||||
for (auto& t : types)
|
||||
cs.set (t.name, Colour (t.colour));
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void skipToEndOfXmlDTD (Iterator& source) noexcept
|
||||
{
|
||||
bool lastWasQuestionMark = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto c = source.nextChar();
|
||||
|
||||
if (c == 0 || (c == '>' && lastWasQuestionMark))
|
||||
break;
|
||||
|
||||
lastWasQuestionMark = (c == '?');
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void skipToEndOfXmlComment (Iterator& source) noexcept
|
||||
{
|
||||
juce_wchar last[2] = {};
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto c = source.nextChar();
|
||||
|
||||
if (c == 0 || (c == '>' && last[0] == '-' && last[1] == '-'))
|
||||
break;
|
||||
|
||||
last[1] = last[0];
|
||||
last[0] = c;
|
||||
}
|
||||
}
|
||||
|
||||
int XmlTokeniser::readNextToken (CodeDocument::Iterator& source)
|
||||
{
|
||||
source.skipWhitespace();
|
||||
auto firstChar = source.peekNextChar();
|
||||
|
||||
switch (firstChar)
|
||||
{
|
||||
case 0: break;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
CppTokeniserFunctions::skipQuotedString (source);
|
||||
return tokenType_string;
|
||||
|
||||
case '<':
|
||||
{
|
||||
source.skip();
|
||||
source.skipWhitespace();
|
||||
auto nextChar = source.peekNextChar();
|
||||
|
||||
if (nextChar == '?')
|
||||
{
|
||||
source.skip();
|
||||
skipToEndOfXmlDTD (source);
|
||||
return tokenType_preprocessor;
|
||||
}
|
||||
|
||||
if (nextChar == '!')
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '-')
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '-')
|
||||
{
|
||||
skipToEndOfXmlComment (source);
|
||||
return tokenType_comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '/');
|
||||
CppTokeniserFunctions::parseIdentifier (source);
|
||||
source.skipWhitespace();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '/');
|
||||
source.skipWhitespace();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '>');
|
||||
return tokenType_keyword;
|
||||
}
|
||||
|
||||
case '>':
|
||||
source.skip();
|
||||
return tokenType_keyword;
|
||||
|
||||
case '/':
|
||||
source.skip();
|
||||
source.skipWhitespace();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '>');
|
||||
return tokenType_keyword;
|
||||
|
||||
case '=':
|
||||
case ':':
|
||||
source.skip();
|
||||
return tokenType_operator;
|
||||
|
||||
default:
|
||||
if (CppTokeniserFunctions::isIdentifierStart (firstChar))
|
||||
CppTokeniserFunctions::parseIdentifier (source);
|
||||
|
||||
source.skip();
|
||||
break;
|
||||
};
|
||||
|
||||
return tokenType_identifier;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
XmlTokeniser::XmlTokeniser() {}
|
||||
XmlTokeniser::~XmlTokeniser() {}
|
||||
|
||||
CodeEditorComponent::ColourScheme XmlTokeniser::getDefaultColourScheme()
|
||||
{
|
||||
struct Type
|
||||
{
|
||||
const char* name;
|
||||
uint32 colour;
|
||||
};
|
||||
|
||||
const Type types[] =
|
||||
{
|
||||
{ "Error", 0xffcc0000 },
|
||||
{ "Comment", 0xff00aa00 },
|
||||
{ "Keyword", 0xff0000cc },
|
||||
{ "Operator", 0xff225500 },
|
||||
{ "Identifier", 0xff000000 },
|
||||
{ "String", 0xff990099 },
|
||||
{ "Bracket", 0xff000055 },
|
||||
{ "Punctuation", 0xff004400 },
|
||||
{ "Preprocessor Text", 0xff660000 }
|
||||
};
|
||||
|
||||
CodeEditorComponent::ColourScheme cs;
|
||||
|
||||
for (auto& t : types)
|
||||
cs.set (t.name, Colour (t.colour));
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void skipToEndOfXmlDTD (Iterator& source) noexcept
|
||||
{
|
||||
bool lastWasQuestionMark = false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto c = source.nextChar();
|
||||
|
||||
if (c == 0 || (c == '>' && lastWasQuestionMark))
|
||||
break;
|
||||
|
||||
lastWasQuestionMark = (c == '?');
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
static void skipToEndOfXmlComment (Iterator& source) noexcept
|
||||
{
|
||||
juce_wchar last[2] = {};
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto c = source.nextChar();
|
||||
|
||||
if (c == 0 || (c == '>' && last[0] == '-' && last[1] == '-'))
|
||||
break;
|
||||
|
||||
last[1] = last[0];
|
||||
last[0] = c;
|
||||
}
|
||||
}
|
||||
|
||||
int XmlTokeniser::readNextToken (CodeDocument::Iterator& source)
|
||||
{
|
||||
source.skipWhitespace();
|
||||
auto firstChar = source.peekNextChar();
|
||||
|
||||
switch (firstChar)
|
||||
{
|
||||
case 0: break;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
CppTokeniserFunctions::skipQuotedString (source);
|
||||
return tokenType_string;
|
||||
|
||||
case '<':
|
||||
{
|
||||
source.skip();
|
||||
source.skipWhitespace();
|
||||
auto nextChar = source.peekNextChar();
|
||||
|
||||
if (nextChar == '?')
|
||||
{
|
||||
source.skip();
|
||||
skipToEndOfXmlDTD (source);
|
||||
return tokenType_preprocessor;
|
||||
}
|
||||
|
||||
if (nextChar == '!')
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '-')
|
||||
{
|
||||
source.skip();
|
||||
|
||||
if (source.peekNextChar() == '-')
|
||||
{
|
||||
skipToEndOfXmlComment (source);
|
||||
return tokenType_comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '/');
|
||||
CppTokeniserFunctions::parseIdentifier (source);
|
||||
source.skipWhitespace();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '/');
|
||||
source.skipWhitespace();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '>');
|
||||
return tokenType_keyword;
|
||||
}
|
||||
|
||||
case '>':
|
||||
source.skip();
|
||||
return tokenType_keyword;
|
||||
|
||||
case '/':
|
||||
source.skip();
|
||||
source.skipWhitespace();
|
||||
CppTokeniserFunctions::skipIfNextCharMatches (source, '>');
|
||||
return tokenType_keyword;
|
||||
|
||||
case '=':
|
||||
case ':':
|
||||
source.skip();
|
||||
return tokenType_operator;
|
||||
|
||||
default:
|
||||
if (CppTokeniserFunctions::isIdentifierStart (firstChar))
|
||||
CppTokeniserFunctions::parseIdentifier (source);
|
||||
|
||||
source.skip();
|
||||
break;
|
||||
};
|
||||
|
||||
return tokenType_identifier;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,63 +1,63 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API XmlTokeniser : public CodeTokeniser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
XmlTokeniser();
|
||||
~XmlTokeniser() override;
|
||||
|
||||
//==============================================================================
|
||||
int readNextToken (CodeDocument::Iterator&) override;
|
||||
CodeEditorComponent::ColourScheme getDefaultColourScheme() override;
|
||||
|
||||
/** The token values returned by this tokeniser. */
|
||||
enum TokenType
|
||||
{
|
||||
tokenType_error = 0,
|
||||
tokenType_comment,
|
||||
tokenType_keyword,
|
||||
tokenType_operator,
|
||||
tokenType_identifier,
|
||||
tokenType_string,
|
||||
tokenType_bracket,
|
||||
tokenType_punctuation,
|
||||
tokenType_preprocessor
|
||||
};
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlTokeniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API XmlTokeniser : public CodeTokeniser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
XmlTokeniser();
|
||||
~XmlTokeniser() override;
|
||||
|
||||
//==============================================================================
|
||||
int readNextToken (CodeDocument::Iterator&) override;
|
||||
CodeEditorComponent::ColourScheme getDefaultColourScheme() override;
|
||||
|
||||
/** The token values returned by this tokeniser. */
|
||||
enum TokenType
|
||||
{
|
||||
tokenType_error = 0,
|
||||
tokenType_comment,
|
||||
tokenType_keyword,
|
||||
tokenType_operator,
|
||||
tokenType_identifier,
|
||||
tokenType_string,
|
||||
tokenType_bracket,
|
||||
tokenType_punctuation,
|
||||
tokenType_preprocessor
|
||||
};
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlTokeniser)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,431 +1,431 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class to take care of the logic involved with the loading/saving of some kind
|
||||
of document.
|
||||
|
||||
There's quite a lot of tedious logic involved in writing all the load/save/save-as
|
||||
functions you need for documents that get saved to a file, so this class attempts
|
||||
to abstract most of the boring stuff.
|
||||
|
||||
Your subclass should just implement all the pure virtual methods, and you can
|
||||
then use the higher-level public methods to do the load/save dialogs, to warn the user
|
||||
about overwriting files, etc.
|
||||
|
||||
The document object keeps track of whether it has changed since it was last saved or
|
||||
loaded, so when you change something, call its changed() method. This will set a
|
||||
flag so it knows it needs saving, and will also broadcast a change message using the
|
||||
ChangeBroadcaster base class.
|
||||
|
||||
@see ChangeBroadcaster
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FileBasedDocument : public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
/** Creates a FileBasedDocument.
|
||||
|
||||
@param fileExtension the extension to use when loading/saving files, e.g. ".doc"
|
||||
@param fileWildCard the wildcard to use in file dialogs, e.g. "*.doc"
|
||||
@param openFileDialogTitle the title to show on an open-file dialog, e.g. "Choose a file to open.."
|
||||
@param saveFileDialogTitle the title to show on an save-file dialog, e.g. "Choose a file to save as.."
|
||||
*/
|
||||
FileBasedDocument (const String& fileExtension,
|
||||
const String& fileWildCard,
|
||||
const String& openFileDialogTitle,
|
||||
const String& saveFileDialogTitle);
|
||||
|
||||
/** Destructor. */
|
||||
~FileBasedDocument() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the changed() method has been called since the file was
|
||||
last saved or loaded.
|
||||
|
||||
@see setChangedFlag, changed
|
||||
*/
|
||||
bool hasChangedSinceSaved() const;
|
||||
|
||||
/** Called to indicate that the document has changed and needs saving.
|
||||
|
||||
This method will also trigger a change message to be sent out using the
|
||||
ChangeBroadcaster base class.
|
||||
|
||||
After calling the method, the hasChangedSinceSaved() method will return true, until
|
||||
it is reset either by saving to a file or using the setChangedFlag() method.
|
||||
|
||||
@see hasChangedSinceSaved, setChangedFlag
|
||||
*/
|
||||
virtual void changed();
|
||||
|
||||
/** Sets the state of the 'changed' flag.
|
||||
|
||||
The 'changed' flag is set to true when the changed() method is called - use this method
|
||||
to reset it or to set it without also broadcasting a change message.
|
||||
|
||||
@see changed, hasChangedSinceSaved
|
||||
*/
|
||||
void setChangedFlag (bool hasChanged);
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to open a file.
|
||||
|
||||
If the file opens correctly the document's file (see the getFile() method) is set
|
||||
to this new one; if it fails, the document's file is left unchanged, and optionally
|
||||
a message box is shown telling the user there was an error.
|
||||
|
||||
@returns A result indicating whether the new file loaded successfully, or the error
|
||||
message if it failed.
|
||||
@see loadDocument, loadFromUserSpecifiedFile
|
||||
*/
|
||||
Result loadFrom (const File& fileToLoadFrom,
|
||||
bool showMessageOnFailure,
|
||||
bool showWaitCursor = true);
|
||||
|
||||
/** Tries to open a file.
|
||||
|
||||
The callback is called with the result indicating whether the new file loaded
|
||||
successfully, or the error message if it failed.
|
||||
|
||||
If the file opens correctly the document's file (see the getFile() method) is set
|
||||
to this new one; if it fails, the document's file is left unchanged, and optionally
|
||||
a message box is shown telling the user there was an error.
|
||||
|
||||
@see loadDocumentAsync, loadFromUserSpecifiedFileAsync
|
||||
*/
|
||||
void loadFromAsync (const File& fileToLoadFrom,
|
||||
bool showMessageOnFailure,
|
||||
std::function<void (Result)> callback);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Asks the user for a file and tries to load it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the loadFrom() method is used to
|
||||
try to load it, optionally showing a message if it fails.
|
||||
|
||||
@returns a result indicating success; This will be a failure message if the user
|
||||
cancelled or if they picked a file which failed to load correctly
|
||||
@see loadFrom
|
||||
*/
|
||||
Result loadFromUserSpecifiedFile (bool showMessageOnFailure);
|
||||
#endif
|
||||
|
||||
/** Asks the user for a file and tries to load it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the loadFrom() method is used to
|
||||
try to load it, optionally showing a message if it fails. The result
|
||||
of the operation is provided in the callback function.
|
||||
|
||||
@see loadFrom
|
||||
*/
|
||||
void loadFromUserSpecifiedFileAsync (bool showMessageOnFailure, std::function<void (Result)> callback);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of possible outcomes of one of the save() methods
|
||||
*/
|
||||
enum SaveResult
|
||||
{
|
||||
savedOk = 0, /**< indicates that a file was saved successfully. */
|
||||
userCancelledSave, /**< indicates that the user aborted the save operation. */
|
||||
failedToWriteToFile /**< indicates that it tried to write to a file but this failed. */
|
||||
};
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Tries to save the document to the last file it was saved or loaded from.
|
||||
|
||||
This will always try to write to the file, even if the document isn't flagged as
|
||||
having changed.
|
||||
|
||||
@param askUserForFileIfNotSpecified if there's no file currently specified and this is
|
||||
true, it will prompt the user to pick a file, as if
|
||||
saveAsInteractive() was called.
|
||||
@param showMessageOnFailure if true it will show a warning message when if the
|
||||
save operation fails
|
||||
@see saveIfNeededAndUserAgrees, saveAs, saveAsInteractive
|
||||
*/
|
||||
SaveResult save (bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure);
|
||||
#endif
|
||||
|
||||
/** Tries to save the document to the last file it was saved or loaded from.
|
||||
|
||||
This will always try to write to the file, even if the document isn't flagged as
|
||||
having changed.
|
||||
|
||||
@param askUserForFileIfNotSpecified if there's no file currently specified and this is
|
||||
true, it will prompt the user to pick a file, as if
|
||||
saveAsInteractive() was called.
|
||||
@param showMessageOnFailure if true it will show a warning message when if the
|
||||
save operation fails
|
||||
@param callback called after the save operation with the result
|
||||
@see saveIfNeededAndUserAgrees, saveAs, saveAsInteractive
|
||||
*/
|
||||
void saveAsync (bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure,
|
||||
std::function<void (SaveResult)> callback);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** If the file needs saving, it'll ask the user if that's what they want to do, and save
|
||||
it if they say yes.
|
||||
|
||||
If you've got a document open and want to close it (e.g. to quit the app), this is the
|
||||
method to call.
|
||||
|
||||
If the document doesn't need saving it'll return the value savedOk so
|
||||
you can go ahead and delete the document.
|
||||
|
||||
If it does need saving it'll prompt the user, and if they say "discard changes" it'll
|
||||
return savedOk, so again, you can safely delete the document.
|
||||
|
||||
If the user clicks "cancel", it'll return userCancelledSave, so if you can abort the
|
||||
close-document operation.
|
||||
|
||||
And if they click "save changes", it'll try to save and either return savedOk, or
|
||||
failedToWriteToFile if there was a problem.
|
||||
|
||||
@see save, saveAs, saveAsInteractive
|
||||
*/
|
||||
SaveResult saveIfNeededAndUserAgrees();
|
||||
#endif
|
||||
|
||||
/** If the file needs saving, it'll ask the user if that's what they want to do, and save
|
||||
it if they say yes.
|
||||
|
||||
If you've got a document open and want to close it (e.g. to quit the app), this is the
|
||||
method to call.
|
||||
|
||||
If the document doesn't need saving the callback will be called with the value savedOk
|
||||
so you can go ahead and delete the document.
|
||||
|
||||
If it does need saving it'll prompt the user, and if they say "discard changes" the
|
||||
callback will be called with savedOk, so again, you can safely delete the document.
|
||||
|
||||
If the user clicks "cancel", the callback will be aclled with userCancelledSave, so
|
||||
you can abort the close-document operation.
|
||||
|
||||
And if they click "save changes", it'll try to save and the callback will be called
|
||||
with either savedOk, or failedToWriteToFile if there was a problem.
|
||||
|
||||
@see saveAsync, saveAsAsync, saveAsInteractiveAsync
|
||||
*/
|
||||
void saveIfNeededAndUserAgreesAsync (std::function<void (SaveResult)> callback);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Tries to save the document to a specified file.
|
||||
|
||||
If this succeeds, it'll also change the document's internal file (as returned by
|
||||
the getFile() method). If it fails, the file will be left unchanged.
|
||||
|
||||
@param newFile the file to try to write to
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||||
the user first if they want to overwrite it
|
||||
@param askUserForFileIfNotSpecified if the file is non-existent and this is true, it'll
|
||||
use the saveAsInteractive() method to ask the user for a
|
||||
filename
|
||||
@param showMessageOnFailure if true and the write operation fails, it'll show
|
||||
a message box to warn the user
|
||||
@param showWaitCursor if true, the 'wait' mouse cursor will be showin during
|
||||
saving
|
||||
@see saveIfNeededAndUserAgrees, save, saveAsInteractive
|
||||
*/
|
||||
SaveResult saveAs (const File& newFile,
|
||||
bool warnAboutOverwritingExistingFiles,
|
||||
bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure,
|
||||
bool showWaitCursor = true);
|
||||
#endif
|
||||
|
||||
/** Tries to save the document to a specified file.
|
||||
|
||||
If this succeeds, it'll also change the document's internal file (as returned by
|
||||
the getFile() method). If it fails, the file will be left unchanged.
|
||||
|
||||
@param newFile the file to try to write to
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask the user
|
||||
first if they want to overwrite it
|
||||
@param askUserForFileIfNotSpecified if the file is non-existent and this is true, it'll
|
||||
use the saveAsInteractive() method to ask the user
|
||||
for a filename
|
||||
@param showMessageOnFailure if true and the write operation fails, it'll show
|
||||
a message box to warn the user
|
||||
@param callback called with the result of the save operation
|
||||
|
||||
@see saveIfNeededAndUserAgreesAsync, saveAsync, saveAsInteractiveAsync
|
||||
*/
|
||||
void saveAsAsync (const File& newFile,
|
||||
bool warnAboutOverwritingExistingFiles,
|
||||
bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure,
|
||||
std::function<void (SaveResult)> callback);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Prompts the user for a filename and tries to save to it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the saveAs() method is used to try to save
|
||||
to this file.
|
||||
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||||
the user first if they want to overwrite it
|
||||
@see saveIfNeededAndUserAgrees, save, saveAs
|
||||
*/
|
||||
SaveResult saveAsInteractive (bool warnAboutOverwritingExistingFiles);
|
||||
#endif
|
||||
|
||||
/** Prompts the user for a filename and tries to save to it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the saveAs() method is used to try to save
|
||||
to this file.
|
||||
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||||
the user first if they want to overwrite it
|
||||
@param callback called with the result of the save operation
|
||||
@see saveIfNeededAndUserAgreesAsync, saveAsync, saveAsAsync
|
||||
*/
|
||||
void saveAsInteractiveAsync (bool warnAboutOverwritingExistingFiles,
|
||||
std::function<void (SaveResult)> callback);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the file that this document was last successfully saved or loaded from.
|
||||
|
||||
When the document object is created, this will be set to File().
|
||||
|
||||
It is changed when one of the load or save methods is used, or when setFile()
|
||||
is used to explicitly set it.
|
||||
*/
|
||||
const File& getFile() const;
|
||||
|
||||
/** Sets the file that this document thinks it was loaded from.
|
||||
|
||||
This won't actually load anything - it just changes the file stored internally.
|
||||
|
||||
@see getFile
|
||||
*/
|
||||
void setFile (const File& newFile);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Overload this to return the title of the document.
|
||||
|
||||
This is used in message boxes, filenames and file choosers, so it should be
|
||||
something sensible.
|
||||
*/
|
||||
virtual String getDocumentTitle() = 0;
|
||||
|
||||
/** This method should try to load your document from the given file.
|
||||
@returns a Result object to indicate the whether there was an error.
|
||||
*/
|
||||
virtual Result loadDocument (const File& file) = 0;
|
||||
|
||||
/** This method should try to load your document from the given file, then
|
||||
call the provided callback on the message thread, passing the result of the load.
|
||||
|
||||
By default, this will synchronously call through to loadDocument.
|
||||
|
||||
For longer-running load operations, you may wish to override this function to
|
||||
run the load on a background thread, and then to call the callback later on the
|
||||
message thread to signal that the load has completed.
|
||||
*/
|
||||
virtual void loadDocumentAsync (const File& file, std::function<void (Result)> callback);
|
||||
|
||||
/** This method should try to write your document to the given file.
|
||||
@returns a Result object to indicate the whether there was an error.
|
||||
*/
|
||||
virtual Result saveDocument (const File& file) = 0;
|
||||
|
||||
/** This method should try to write your document to the given file, then
|
||||
call the provided callback on the message thread, passing the result of the write.
|
||||
|
||||
By default, this will synchronously call through to saveDocument.
|
||||
|
||||
For longer-running save operations, you may wish to override this function to
|
||||
run the save on a background thread, and then to call the callback later on the
|
||||
message thread to signal that the save has completed.
|
||||
*/
|
||||
virtual void saveDocumentAsync (const File& file, std::function<void (Result)> callback);
|
||||
|
||||
/** This is used for dialog boxes to make them open at the last folder you
|
||||
were using.
|
||||
|
||||
getLastDocumentOpened() and setLastDocumentOpened() are used to store
|
||||
the last document that was used - you might want to store this value
|
||||
in a static variable, or even in your application's properties. It should
|
||||
be a global setting rather than a property of this object.
|
||||
|
||||
This method works very well in conjunction with a RecentlyOpenedFilesList
|
||||
object to manage your recent-files list.
|
||||
|
||||
As a default value, it's ok to return File(), and the document object will
|
||||
use a sensible one instead.
|
||||
|
||||
@see RecentlyOpenedFilesList
|
||||
*/
|
||||
virtual File getLastDocumentOpened() = 0;
|
||||
|
||||
/** This is used for dialog boxes to make them open at the last folder you
|
||||
were using.
|
||||
|
||||
getLastDocumentOpened() and setLastDocumentOpened() are used to store
|
||||
the last document that was used - you might want to store this value
|
||||
in a static variable, or even in your application's properties. It should
|
||||
be a global setting rather than a property of this object.
|
||||
|
||||
This method works very well in conjunction with a RecentlyOpenedFilesList
|
||||
object to manage your recent-files list.
|
||||
|
||||
@see RecentlyOpenedFilesList
|
||||
*/
|
||||
virtual void setLastDocumentOpened (const File& file) = 0;
|
||||
|
||||
/** This is called by saveAsInteractiveAsync() to allow you to optionally customise the
|
||||
filename that the user is presented with in the save dialog.
|
||||
The defaultFile parameter is an initial suggestion based on what the class knows
|
||||
about the current document - you can return a variation on this file with a different
|
||||
extension, etc, or just return something completely different.
|
||||
*/
|
||||
virtual File getSuggestedSaveAsFile (const File& defaultFile);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBasedDocument)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class to take care of the logic involved with the loading/saving of some kind
|
||||
of document.
|
||||
|
||||
There's quite a lot of tedious logic involved in writing all the load/save/save-as
|
||||
functions you need for documents that get saved to a file, so this class attempts
|
||||
to abstract most of the boring stuff.
|
||||
|
||||
Your subclass should just implement all the pure virtual methods, and you can
|
||||
then use the higher-level public methods to do the load/save dialogs, to warn the user
|
||||
about overwriting files, etc.
|
||||
|
||||
The document object keeps track of whether it has changed since it was last saved or
|
||||
loaded, so when you change something, call its changed() method. This will set a
|
||||
flag so it knows it needs saving, and will also broadcast a change message using the
|
||||
ChangeBroadcaster base class.
|
||||
|
||||
@see ChangeBroadcaster
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API FileBasedDocument : public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
/** Creates a FileBasedDocument.
|
||||
|
||||
@param fileExtension the extension to use when loading/saving files, e.g. ".doc"
|
||||
@param fileWildCard the wildcard to use in file dialogs, e.g. "*.doc"
|
||||
@param openFileDialogTitle the title to show on an open-file dialog, e.g. "Choose a file to open.."
|
||||
@param saveFileDialogTitle the title to show on an save-file dialog, e.g. "Choose a file to save as.."
|
||||
*/
|
||||
FileBasedDocument (const String& fileExtension,
|
||||
const String& fileWildCard,
|
||||
const String& openFileDialogTitle,
|
||||
const String& saveFileDialogTitle);
|
||||
|
||||
/** Destructor. */
|
||||
~FileBasedDocument() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the changed() method has been called since the file was
|
||||
last saved or loaded.
|
||||
|
||||
@see setChangedFlag, changed
|
||||
*/
|
||||
bool hasChangedSinceSaved() const;
|
||||
|
||||
/** Called to indicate that the document has changed and needs saving.
|
||||
|
||||
This method will also trigger a change message to be sent out using the
|
||||
ChangeBroadcaster base class.
|
||||
|
||||
After calling the method, the hasChangedSinceSaved() method will return true, until
|
||||
it is reset either by saving to a file or using the setChangedFlag() method.
|
||||
|
||||
@see hasChangedSinceSaved, setChangedFlag
|
||||
*/
|
||||
virtual void changed();
|
||||
|
||||
/** Sets the state of the 'changed' flag.
|
||||
|
||||
The 'changed' flag is set to true when the changed() method is called - use this method
|
||||
to reset it or to set it without also broadcasting a change message.
|
||||
|
||||
@see changed, hasChangedSinceSaved
|
||||
*/
|
||||
void setChangedFlag (bool hasChanged);
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to open a file.
|
||||
|
||||
If the file opens correctly the document's file (see the getFile() method) is set
|
||||
to this new one; if it fails, the document's file is left unchanged, and optionally
|
||||
a message box is shown telling the user there was an error.
|
||||
|
||||
@returns A result indicating whether the new file loaded successfully, or the error
|
||||
message if it failed.
|
||||
@see loadDocument, loadFromUserSpecifiedFile
|
||||
*/
|
||||
Result loadFrom (const File& fileToLoadFrom,
|
||||
bool showMessageOnFailure,
|
||||
bool showWaitCursor = true);
|
||||
|
||||
/** Tries to open a file.
|
||||
|
||||
The callback is called with the result indicating whether the new file loaded
|
||||
successfully, or the error message if it failed.
|
||||
|
||||
If the file opens correctly the document's file (see the getFile() method) is set
|
||||
to this new one; if it fails, the document's file is left unchanged, and optionally
|
||||
a message box is shown telling the user there was an error.
|
||||
|
||||
@see loadDocumentAsync, loadFromUserSpecifiedFileAsync
|
||||
*/
|
||||
void loadFromAsync (const File& fileToLoadFrom,
|
||||
bool showMessageOnFailure,
|
||||
std::function<void (Result)> callback);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Asks the user for a file and tries to load it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the loadFrom() method is used to
|
||||
try to load it, optionally showing a message if it fails.
|
||||
|
||||
@returns a result indicating success; This will be a failure message if the user
|
||||
cancelled or if they picked a file which failed to load correctly
|
||||
@see loadFrom
|
||||
*/
|
||||
Result loadFromUserSpecifiedFile (bool showMessageOnFailure);
|
||||
#endif
|
||||
|
||||
/** Asks the user for a file and tries to load it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the loadFrom() method is used to
|
||||
try to load it, optionally showing a message if it fails. The result
|
||||
of the operation is provided in the callback function.
|
||||
|
||||
@see loadFrom
|
||||
*/
|
||||
void loadFromUserSpecifiedFileAsync (bool showMessageOnFailure, std::function<void (Result)> callback);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of possible outcomes of one of the save() methods
|
||||
*/
|
||||
enum SaveResult
|
||||
{
|
||||
savedOk = 0, /**< indicates that a file was saved successfully. */
|
||||
userCancelledSave, /**< indicates that the user aborted the save operation. */
|
||||
failedToWriteToFile /**< indicates that it tried to write to a file but this failed. */
|
||||
};
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Tries to save the document to the last file it was saved or loaded from.
|
||||
|
||||
This will always try to write to the file, even if the document isn't flagged as
|
||||
having changed.
|
||||
|
||||
@param askUserForFileIfNotSpecified if there's no file currently specified and this is
|
||||
true, it will prompt the user to pick a file, as if
|
||||
saveAsInteractive() was called.
|
||||
@param showMessageOnFailure if true it will show a warning message when if the
|
||||
save operation fails
|
||||
@see saveIfNeededAndUserAgrees, saveAs, saveAsInteractive
|
||||
*/
|
||||
SaveResult save (bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure);
|
||||
#endif
|
||||
|
||||
/** Tries to save the document to the last file it was saved or loaded from.
|
||||
|
||||
This will always try to write to the file, even if the document isn't flagged as
|
||||
having changed.
|
||||
|
||||
@param askUserForFileIfNotSpecified if there's no file currently specified and this is
|
||||
true, it will prompt the user to pick a file, as if
|
||||
saveAsInteractive() was called.
|
||||
@param showMessageOnFailure if true it will show a warning message when if the
|
||||
save operation fails
|
||||
@param callback called after the save operation with the result
|
||||
@see saveIfNeededAndUserAgrees, saveAs, saveAsInteractive
|
||||
*/
|
||||
void saveAsync (bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure,
|
||||
std::function<void (SaveResult)> callback);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** If the file needs saving, it'll ask the user if that's what they want to do, and save
|
||||
it if they say yes.
|
||||
|
||||
If you've got a document open and want to close it (e.g. to quit the app), this is the
|
||||
method to call.
|
||||
|
||||
If the document doesn't need saving it'll return the value savedOk so
|
||||
you can go ahead and delete the document.
|
||||
|
||||
If it does need saving it'll prompt the user, and if they say "discard changes" it'll
|
||||
return savedOk, so again, you can safely delete the document.
|
||||
|
||||
If the user clicks "cancel", it'll return userCancelledSave, so if you can abort the
|
||||
close-document operation.
|
||||
|
||||
And if they click "save changes", it'll try to save and either return savedOk, or
|
||||
failedToWriteToFile if there was a problem.
|
||||
|
||||
@see save, saveAs, saveAsInteractive
|
||||
*/
|
||||
SaveResult saveIfNeededAndUserAgrees();
|
||||
#endif
|
||||
|
||||
/** If the file needs saving, it'll ask the user if that's what they want to do, and save
|
||||
it if they say yes.
|
||||
|
||||
If you've got a document open and want to close it (e.g. to quit the app), this is the
|
||||
method to call.
|
||||
|
||||
If the document doesn't need saving the callback will be called with the value savedOk
|
||||
so you can go ahead and delete the document.
|
||||
|
||||
If it does need saving it'll prompt the user, and if they say "discard changes" the
|
||||
callback will be called with savedOk, so again, you can safely delete the document.
|
||||
|
||||
If the user clicks "cancel", the callback will be called with userCancelledSave, so
|
||||
you can abort the close-document operation.
|
||||
|
||||
And if they click "save changes", it'll try to save and the callback will be called
|
||||
with either savedOk, or failedToWriteToFile if there was a problem.
|
||||
|
||||
@see saveAsync, saveAsAsync, saveAsInteractiveAsync
|
||||
*/
|
||||
void saveIfNeededAndUserAgreesAsync (std::function<void (SaveResult)> callback);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Tries to save the document to a specified file.
|
||||
|
||||
If this succeeds, it'll also change the document's internal file (as returned by
|
||||
the getFile() method). If it fails, the file will be left unchanged.
|
||||
|
||||
@param newFile the file to try to write to
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||||
the user first if they want to overwrite it
|
||||
@param askUserForFileIfNotSpecified if the file is non-existent and this is true, it'll
|
||||
use the saveAsInteractive() method to ask the user for a
|
||||
filename
|
||||
@param showMessageOnFailure if true and the write operation fails, it'll show
|
||||
a message box to warn the user
|
||||
@param showWaitCursor if true, the 'wait' mouse cursor will be showin during
|
||||
saving
|
||||
@see saveIfNeededAndUserAgrees, save, saveAsInteractive
|
||||
*/
|
||||
SaveResult saveAs (const File& newFile,
|
||||
bool warnAboutOverwritingExistingFiles,
|
||||
bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure,
|
||||
bool showWaitCursor = true);
|
||||
#endif
|
||||
|
||||
/** Tries to save the document to a specified file.
|
||||
|
||||
If this succeeds, it'll also change the document's internal file (as returned by
|
||||
the getFile() method). If it fails, the file will be left unchanged.
|
||||
|
||||
@param newFile the file to try to write to
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask the user
|
||||
first if they want to overwrite it
|
||||
@param askUserForFileIfNotSpecified if the file is non-existent and this is true, it'll
|
||||
use the saveAsInteractive() method to ask the user
|
||||
for a filename
|
||||
@param showMessageOnFailure if true and the write operation fails, it'll show
|
||||
a message box to warn the user
|
||||
@param callback called with the result of the save operation
|
||||
|
||||
@see saveIfNeededAndUserAgreesAsync, saveAsync, saveAsInteractiveAsync
|
||||
*/
|
||||
void saveAsAsync (const File& newFile,
|
||||
bool warnAboutOverwritingExistingFiles,
|
||||
bool askUserForFileIfNotSpecified,
|
||||
bool showMessageOnFailure,
|
||||
std::function<void (SaveResult)> callback);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Prompts the user for a filename and tries to save to it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the saveAs() method is used to try to save
|
||||
to this file.
|
||||
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||||
the user first if they want to overwrite it
|
||||
@see saveIfNeededAndUserAgrees, save, saveAs
|
||||
*/
|
||||
SaveResult saveAsInteractive (bool warnAboutOverwritingExistingFiles);
|
||||
#endif
|
||||
|
||||
/** Prompts the user for a filename and tries to save to it.
|
||||
|
||||
This will pop up a dialog box using the title, file extension and
|
||||
wildcard specified in the document's constructor, and asks the user
|
||||
for a file. If they pick one, the saveAs() method is used to try to save
|
||||
to this file.
|
||||
|
||||
@param warnAboutOverwritingExistingFiles if true and the file exists, it'll ask
|
||||
the user first if they want to overwrite it
|
||||
@param callback called with the result of the save operation
|
||||
@see saveIfNeededAndUserAgreesAsync, saveAsync, saveAsAsync
|
||||
*/
|
||||
void saveAsInteractiveAsync (bool warnAboutOverwritingExistingFiles,
|
||||
std::function<void (SaveResult)> callback);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the file that this document was last successfully saved or loaded from.
|
||||
|
||||
When the document object is created, this will be set to File().
|
||||
|
||||
It is changed when one of the load or save methods is used, or when setFile()
|
||||
is used to explicitly set it.
|
||||
*/
|
||||
const File& getFile() const;
|
||||
|
||||
/** Sets the file that this document thinks it was loaded from.
|
||||
|
||||
This won't actually load anything - it just changes the file stored internally.
|
||||
|
||||
@see getFile
|
||||
*/
|
||||
void setFile (const File& newFile);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** Overload this to return the title of the document.
|
||||
|
||||
This is used in message boxes, filenames and file choosers, so it should be
|
||||
something sensible.
|
||||
*/
|
||||
virtual String getDocumentTitle() = 0;
|
||||
|
||||
/** This method should try to load your document from the given file.
|
||||
@returns a Result object to indicate the whether there was an error.
|
||||
*/
|
||||
virtual Result loadDocument (const File& file) = 0;
|
||||
|
||||
/** This method should try to load your document from the given file, then
|
||||
call the provided callback on the message thread, passing the result of the load.
|
||||
|
||||
By default, this will synchronously call through to loadDocument.
|
||||
|
||||
For longer-running load operations, you may wish to override this function to
|
||||
run the load on a background thread, and then to call the callback later on the
|
||||
message thread to signal that the load has completed.
|
||||
*/
|
||||
virtual void loadDocumentAsync (const File& file, std::function<void (Result)> callback);
|
||||
|
||||
/** This method should try to write your document to the given file.
|
||||
@returns a Result object to indicate the whether there was an error.
|
||||
*/
|
||||
virtual Result saveDocument (const File& file) = 0;
|
||||
|
||||
/** This method should try to write your document to the given file, then
|
||||
call the provided callback on the message thread, passing the result of the write.
|
||||
|
||||
By default, this will synchronously call through to saveDocument.
|
||||
|
||||
For longer-running save operations, you may wish to override this function to
|
||||
run the save on a background thread, and then to call the callback later on the
|
||||
message thread to signal that the save has completed.
|
||||
*/
|
||||
virtual void saveDocumentAsync (const File& file, std::function<void (Result)> callback);
|
||||
|
||||
/** This is used for dialog boxes to make them open at the last folder you
|
||||
were using.
|
||||
|
||||
getLastDocumentOpened() and setLastDocumentOpened() are used to store
|
||||
the last document that was used - you might want to store this value
|
||||
in a static variable, or even in your application's properties. It should
|
||||
be a global setting rather than a property of this object.
|
||||
|
||||
This method works very well in conjunction with a RecentlyOpenedFilesList
|
||||
object to manage your recent-files list.
|
||||
|
||||
As a default value, it's ok to return File(), and the document object will
|
||||
use a sensible one instead.
|
||||
|
||||
@see RecentlyOpenedFilesList
|
||||
*/
|
||||
virtual File getLastDocumentOpened() = 0;
|
||||
|
||||
/** This is used for dialog boxes to make them open at the last folder you
|
||||
were using.
|
||||
|
||||
getLastDocumentOpened() and setLastDocumentOpened() are used to store
|
||||
the last document that was used - you might want to store this value
|
||||
in a static variable, or even in your application's properties. It should
|
||||
be a global setting rather than a property of this object.
|
||||
|
||||
This method works very well in conjunction with a RecentlyOpenedFilesList
|
||||
object to manage your recent-files list.
|
||||
|
||||
@see RecentlyOpenedFilesList
|
||||
*/
|
||||
virtual void setLastDocumentOpened (const File& file) = 0;
|
||||
|
||||
/** This is called by saveAsInteractiveAsync() to allow you to optionally customise the
|
||||
filename that the user is presented with in the save dialog.
|
||||
The defaultFile parameter is an initial suggestion based on what the class knows
|
||||
about the current document - you can return a variation on this file with a different
|
||||
extension, etc, or just return something completely different.
|
||||
*/
|
||||
virtual File getSuggestedSaveAsFile (const File& defaultFile);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBasedDocument)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,128 +1,128 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WINDOWS || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Windows-specific class that can create and embed an ActiveX control inside
|
||||
itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use createControl() to instantiate an ActiveX control. The control
|
||||
will then be moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the control is a heavyweight window, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ActiveXControlComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
ActiveXControlComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~ActiveXControlComponent() override;
|
||||
|
||||
/** Tries to create an ActiveX control and embed it in this peer.
|
||||
|
||||
The peer controlIID is a pointer to an IID structure - it's treated
|
||||
as a void* because when including the JUCE headers, you might not always
|
||||
have included windows.h first, in which case IID wouldn't be defined.
|
||||
|
||||
e.g. @code
|
||||
const IID myIID = __uuidof (QTControl);
|
||||
myControlComp->createControl (&myIID);
|
||||
@endcode
|
||||
*/
|
||||
bool createControl (const void* controlIID);
|
||||
|
||||
/** Deletes the ActiveX control, if one has been created.
|
||||
*/
|
||||
void deleteControl();
|
||||
|
||||
/** Returns true if a control is currently in use. */
|
||||
bool isControlOpen() const noexcept { return control != nullptr; }
|
||||
|
||||
/** Does a QueryInterface call on the embedded control object.
|
||||
|
||||
This allows you to cast the control to whatever type of COM object you need.
|
||||
|
||||
The iid parameter is a pointer to an IID structure - it's treated
|
||||
as a void* because when including the JUCE headers, you might not always
|
||||
have included windows.h first, in which case IID wouldn't be defined, but
|
||||
you should just pass a pointer to an IID.
|
||||
|
||||
e.g. @code
|
||||
const IID iid = __uuidof (IOleWindow);
|
||||
|
||||
IOleWindow* oleWindow = (IOleWindow*) myControlComp->queryInterface (&iid);
|
||||
|
||||
if (oleWindow != nullptr)
|
||||
{
|
||||
HWND hwnd;
|
||||
oleWindow->GetWindow (&hwnd);
|
||||
|
||||
...
|
||||
|
||||
oleWindow->Release();
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
void* queryInterface (const void* iid) const;
|
||||
|
||||
/** Set this to false to stop mouse events being allowed through to the control.
|
||||
*/
|
||||
void setMouseEventsAllowed (bool eventsCanReachControl);
|
||||
|
||||
/** Returns true if mouse events are allowed to get through to the control.
|
||||
*/
|
||||
bool areMouseEventsAllowed() const noexcept { return mouseEventsAllowed; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
/** @internal */
|
||||
intptr_t offerEventToActiveXControl (void*);
|
||||
static intptr_t offerEventToActiveXControlStatic (void*);
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> control;
|
||||
bool mouseEventsAllowed = true;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveXControlComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WINDOWS || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Windows-specific class that can create and embed an ActiveX control inside
|
||||
itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use createControl() to instantiate an ActiveX control. The control
|
||||
will then be moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the control is a heavyweight window, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ActiveXControlComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
ActiveXControlComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~ActiveXControlComponent() override;
|
||||
|
||||
/** Tries to create an ActiveX control and embed it in this peer.
|
||||
|
||||
The peer controlIID is a pointer to an IID structure - it's treated
|
||||
as a void* because when including the JUCE headers, you might not always
|
||||
have included windows.h first, in which case IID wouldn't be defined.
|
||||
|
||||
e.g. @code
|
||||
const IID myIID = __uuidof (QTControl);
|
||||
myControlComp->createControl (&myIID);
|
||||
@endcode
|
||||
*/
|
||||
bool createControl (const void* controlIID);
|
||||
|
||||
/** Deletes the ActiveX control, if one has been created.
|
||||
*/
|
||||
void deleteControl();
|
||||
|
||||
/** Returns true if a control is currently in use. */
|
||||
bool isControlOpen() const noexcept { return control != nullptr; }
|
||||
|
||||
/** Does a QueryInterface call on the embedded control object.
|
||||
|
||||
This allows you to cast the control to whatever type of COM object you need.
|
||||
|
||||
The iid parameter is a pointer to an IID structure - it's treated
|
||||
as a void* because when including the JUCE headers, you might not always
|
||||
have included windows.h first, in which case IID wouldn't be defined, but
|
||||
you should just pass a pointer to an IID.
|
||||
|
||||
e.g. @code
|
||||
const IID iid = __uuidof (IOleWindow);
|
||||
|
||||
IOleWindow* oleWindow = (IOleWindow*) myControlComp->queryInterface (&iid);
|
||||
|
||||
if (oleWindow != nullptr)
|
||||
{
|
||||
HWND hwnd;
|
||||
oleWindow->GetWindow (&hwnd);
|
||||
|
||||
...
|
||||
|
||||
oleWindow->Release();
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
void* queryInterface (const void* iid) const;
|
||||
|
||||
/** Set this to false to stop mouse events being allowed through to the control.
|
||||
*/
|
||||
void setMouseEventsAllowed (bool eventsCanReachControl);
|
||||
|
||||
/** Returns true if mouse events are allowed to get through to the control.
|
||||
*/
|
||||
bool areMouseEventsAllowed() const noexcept { return mouseEventsAllowed; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
/** @internal */
|
||||
intptr_t offerEventToActiveXControl (void*);
|
||||
static intptr_t offerEventToActiveXControlStatic (void*);
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> control;
|
||||
bool mouseEventsAllowed = true;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveXControlComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,80 +1,80 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_ANDROID || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An Android-specific class that can create and embed a View inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setView() to assign a View to it. The view will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the view is a native object, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API AndroidViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container */
|
||||
AndroidViewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~AndroidViewComponent() override;
|
||||
|
||||
/** Assigns a View to this peer.
|
||||
|
||||
The view will be retained and released by this component for as long as
|
||||
it is needed. To remove the current view, just call setView (nullptr).
|
||||
*/
|
||||
void setView (void* uiView);
|
||||
|
||||
/** Returns the current View. */
|
||||
void* getView() const;
|
||||
|
||||
/** Resizes this component to fit the view that it contains. */
|
||||
void resizeToFitView();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidViewComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_ANDROID || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An Android-specific class that can create and embed a View inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setView() to assign a View to it. The view will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the view is a native object, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API AndroidViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container */
|
||||
AndroidViewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~AndroidViewComponent() override;
|
||||
|
||||
/** Assigns a View to this peer.
|
||||
|
||||
The view will be retained and released by this component for as long as
|
||||
it is needed. To remove the current view, just call setView (nullptr).
|
||||
*/
|
||||
void setView (void* uiView);
|
||||
|
||||
/** Returns the current View. */
|
||||
void* getView() const;
|
||||
|
||||
/** Resizes this component to fit the view that it contains. */
|
||||
void resizeToFitView();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidViewComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,86 +1,89 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WINDOWS || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Windows-specific class that can create and embed a HWND inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setHWND() to assign a HWND to it. The window will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the window is a native object, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API HWNDComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
HWNDComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~HWNDComponent() override;
|
||||
|
||||
/** Assigns a HWND to this peer.
|
||||
|
||||
The window will be retained and released by this component for as long as
|
||||
it is needed. To remove the current HWND, just call setHWND (nullptr).
|
||||
|
||||
Note: A void* is used here to avoid including the Windows headers as
|
||||
part of JuceHeader.h, but the method expects a HWND.
|
||||
*/
|
||||
void setHWND (void* hwnd);
|
||||
|
||||
/** Returns the current HWND.
|
||||
|
||||
Note: A void* is returned here to avoid the needing to include the Windows
|
||||
headers, so you should just cast the return value to a HWND.
|
||||
*/
|
||||
void* getHWND() const;
|
||||
|
||||
/** Resizes this component to fit the HWND that it contains. */
|
||||
void resizeToFit();
|
||||
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WINDOWS || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Windows-specific class that can create and embed a HWND inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setHWND() to assign a HWND to it. The window will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the window is a native object, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API HWNDComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
HWNDComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~HWNDComponent() override;
|
||||
|
||||
/** Assigns a HWND to this peer.
|
||||
|
||||
The window will be retained and released by this component for as long as
|
||||
it is needed. To remove the current HWND, just call setHWND (nullptr).
|
||||
|
||||
Note: A void* is used here to avoid including the Windows headers as
|
||||
part of JuceHeader.h, but the method expects a HWND.
|
||||
*/
|
||||
void setHWND (void* hwnd);
|
||||
|
||||
/** Returns the current HWND.
|
||||
|
||||
Note: A void* is returned here to avoid the needing to include the Windows
|
||||
headers, so you should just cast the return value to a HWND.
|
||||
*/
|
||||
void* getHWND() const;
|
||||
|
||||
/** Resizes this component to fit the HWND that it contains. */
|
||||
void resizeToFit();
|
||||
|
||||
/** Forces the bounds of the HWND to match the bounds of this component. */
|
||||
void updateHWNDBounds();
|
||||
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HWNDComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,90 +1,90 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_MAC || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Mac-specific class that can create and embed an NSView inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setView() to assign an NSView to it. The view will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the view is a native object, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API NSViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
NSViewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~NSViewComponent() override;
|
||||
|
||||
/** Assigns an NSView to this peer.
|
||||
|
||||
The view will be retained and released by this component for as long as
|
||||
it is needed. To remove the current view, just call setView (nullptr).
|
||||
|
||||
Note: A void* is used here to avoid including the cocoa headers as
|
||||
part of JuceHeader.h, but the method expects an NSView*.
|
||||
*/
|
||||
void setView (void* nsView);
|
||||
|
||||
/** Returns the current NSView.
|
||||
|
||||
Note: A void* is returned here to avoid the needing to include the cocoa
|
||||
headers, so you should just cast the return value to an NSView*.
|
||||
*/
|
||||
void* getView() const;
|
||||
|
||||
/** Resizes this component to fit the view that it contains. */
|
||||
void resizeToFitView();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void alphaChanged() override;
|
||||
/** @internal */
|
||||
static ReferenceCountedObject* attachViewToComponent (Component&, void*);
|
||||
|
||||
private:
|
||||
ReferenceCountedObjectPtr<ReferenceCountedObject> attachment;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_MAC || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Mac-specific class that can create and embed an NSView inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setView() to assign an NSView to it. The view will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the view is a native object, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API NSViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
NSViewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~NSViewComponent() override;
|
||||
|
||||
/** Assigns an NSView to this peer.
|
||||
|
||||
The view will be retained and released by this component for as long as
|
||||
it is needed. To remove the current view, just call setView (nullptr).
|
||||
|
||||
Note: A void* is used here to avoid including the cocoa headers as
|
||||
part of JuceHeader.h, but the method expects an NSView*.
|
||||
*/
|
||||
void setView (void* nsView);
|
||||
|
||||
/** Returns the current NSView.
|
||||
|
||||
Note: A void* is returned here to avoid the needing to include the cocoa
|
||||
headers, so you should just cast the return value to an NSView*.
|
||||
*/
|
||||
void* getView() const;
|
||||
|
||||
/** Resizes this component to fit the view that it contains. */
|
||||
void resizeToFitView();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void alphaChanged() override;
|
||||
/** @internal */
|
||||
static ReferenceCountedObject* attachViewToComponent (Component&, void*);
|
||||
|
||||
private:
|
||||
ReferenceCountedObjectPtr<ReferenceCountedObject> attachment;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NSViewComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,88 +1,88 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_IOS || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An iOS-specific class that can create and embed an UIView inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setView() to assign a UIView to it. The view will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the view is a native object, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API UIViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
UIViewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~UIViewComponent() override;
|
||||
|
||||
/** Assigns an UIView to this peer.
|
||||
|
||||
The view will be retained and released by this component for as long as
|
||||
it is needed. To remove the current view, just call setView (nullptr).
|
||||
|
||||
Note: A void* is used here to avoid including the cocoa headers as
|
||||
part of JuceHeader.h, but the method expects an UIView*.
|
||||
*/
|
||||
void setView (void* uiView);
|
||||
|
||||
/** Returns the current UIView.
|
||||
|
||||
Note: A void* is returned here to avoid the needing to include the cocoa
|
||||
headers, so you should just cast the return value to an UIView*.
|
||||
*/
|
||||
void* getView() const;
|
||||
|
||||
/** Resizes this component to fit the view that it contains. */
|
||||
void resizeToFitView();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_IOS || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
An iOS-specific class that can create and embed an UIView inside itself.
|
||||
|
||||
To use it, create one of these, put it in place and make sure it's visible in a
|
||||
window, then use setView() to assign a UIView to it. The view will then be
|
||||
moved and resized to follow the movements of this component.
|
||||
|
||||
Of course, since the view is a native object, it'll obliterate any
|
||||
JUCE components that may overlap this component, but that's life.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API UIViewComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Create an initially-empty container. */
|
||||
UIViewComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~UIViewComponent() override;
|
||||
|
||||
/** Assigns an UIView to this peer.
|
||||
|
||||
The view will be retained and released by this component for as long as
|
||||
it is needed. To remove the current view, just call setView (nullptr).
|
||||
|
||||
Note: A void* is used here to avoid including the cocoa headers as
|
||||
part of JuceHeader.h, but the method expects an UIView*.
|
||||
*/
|
||||
void setView (void* uiView);
|
||||
|
||||
/** Returns the current UIView.
|
||||
|
||||
Note: A void* is returned here to avoid the needing to include the cocoa
|
||||
headers, so you should just cast the return value to an UIView*.
|
||||
*/
|
||||
void* getView() const;
|
||||
|
||||
/** Resizes this component to fit the view that it contains. */
|
||||
void resizeToFitView();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
|
||||
private:
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIViewComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,117 +1,120 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/** @internal */
|
||||
bool juce_handleXEmbedEvent (ComponentPeer*, void*);
|
||||
/** @internal */
|
||||
unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Linux-specific class that can embed a foreign X11 widget.
|
||||
|
||||
Use this class to embed a foreign X11 widget from other toolkits such as
|
||||
GTK+ or QT.
|
||||
|
||||
There are two ways to initiate the Xembed protocol. Either the client creates
|
||||
a window and passes this to the host (client initiated) or the host
|
||||
creates a window in which the client can reparent it's client widget
|
||||
(host initiated). XEmbedComponent supports both protocol types.
|
||||
|
||||
This is how you embed a GTK+ widget: if you are using the client
|
||||
initiated version of the protocol, then create a new gtk widget with
|
||||
gtk_plug_new (0). Then query the window id of the plug via gtk_plug_get_id().
|
||||
Pass this id to the constructor of this class.
|
||||
|
||||
If you are using the host initiated version of the protocol, then first create
|
||||
the XEmbedComponent using the default constructor. Use getHostWindowID to get
|
||||
the window id of the host, use this to construct your gtk plug via gtk_plug_new.
|
||||
|
||||
A similar approach can be used to embed QT widgets via QT's QX11EmbedWidget
|
||||
class.
|
||||
|
||||
Other toolkits or raw X11 widgets should follow the X11 embed protocol:
|
||||
https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class XEmbedComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
|
||||
/** Creates a JUCE component wrapping a foreign widget
|
||||
|
||||
Use this constructor if you are using the host initiated version
|
||||
of the XEmbedProtocol. When using this version of the protocol
|
||||
you must call getHostWindowID() and pass this id to the foreign toolkit.
|
||||
*/
|
||||
XEmbedComponent (bool wantsKeyboardFocus = true,
|
||||
bool allowForeignWidgetToResizeComponent = false);
|
||||
|
||||
/** Create a JUCE component wrapping the foreign widget with id wID
|
||||
|
||||
Use this constructor if you are using the client initiated version
|
||||
of the XEmbedProtocol.
|
||||
*/
|
||||
XEmbedComponent (unsigned long wID, bool wantsKeyboardFocus = true,
|
||||
bool allowForeignWidgetToResizeComponent = false);
|
||||
|
||||
|
||||
/** Destructor. */
|
||||
~XEmbedComponent() override;
|
||||
|
||||
/** Use this method to retrieve the host's window id when using the
|
||||
host initiated version of the XEmbedProtocol
|
||||
*/
|
||||
unsigned long getHostWindowID();
|
||||
|
||||
/** Removes the client window from the host. */
|
||||
void removeClient();
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
void focusGained (FocusChangeType) override;
|
||||
void focusLost (FocusChangeType) override;
|
||||
void broughtToFront() override;
|
||||
|
||||
private:
|
||||
friend bool juce::juce_handleXEmbedEvent (ComponentPeer*, void*);
|
||||
friend unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
|
||||
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/** @internal */
|
||||
bool juce_handleXEmbedEvent (ComponentPeer*, void*);
|
||||
/** @internal */
|
||||
unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A Linux-specific class that can embed a foreign X11 widget.
|
||||
|
||||
Use this class to embed a foreign X11 widget from other toolkits such as
|
||||
GTK+ or QT.
|
||||
|
||||
There are two ways to initiate the Xembed protocol. Either the client creates
|
||||
a window and passes this to the host (client initiated) or the host
|
||||
creates a window in which the client can reparent it's client widget
|
||||
(host initiated). XEmbedComponent supports both protocol types.
|
||||
|
||||
This is how you embed a GTK+ widget: if you are using the client
|
||||
initiated version of the protocol, then create a new gtk widget with
|
||||
gtk_plug_new (0). Then query the window id of the plug via gtk_plug_get_id().
|
||||
Pass this id to the constructor of this class.
|
||||
|
||||
If you are using the host initiated version of the protocol, then first create
|
||||
the XEmbedComponent using the default constructor. Use getHostWindowID to get
|
||||
the window id of the host, use this to construct your gtk plug via gtk_plug_new.
|
||||
|
||||
A similar approach can be used to embed QT widgets via QT's QX11EmbedWidget
|
||||
class.
|
||||
|
||||
Other toolkits or raw X11 widgets should follow the X11 embed protocol:
|
||||
https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class XEmbedComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
|
||||
/** Creates a JUCE component wrapping a foreign widget
|
||||
|
||||
Use this constructor if you are using the host initiated version
|
||||
of the XEmbedProtocol. When using this version of the protocol
|
||||
you must call getHostWindowID() and pass this id to the foreign toolkit.
|
||||
*/
|
||||
XEmbedComponent (bool wantsKeyboardFocus = true,
|
||||
bool allowForeignWidgetToResizeComponent = false);
|
||||
|
||||
/** Create a JUCE component wrapping the foreign widget with id wID
|
||||
|
||||
Use this constructor if you are using the client initiated version
|
||||
of the XEmbedProtocol.
|
||||
*/
|
||||
XEmbedComponent (unsigned long wID, bool wantsKeyboardFocus = true,
|
||||
bool allowForeignWidgetToResizeComponent = false);
|
||||
|
||||
|
||||
/** Destructor. */
|
||||
~XEmbedComponent() override;
|
||||
|
||||
/** Use this method to retrieve the host's window id when using the
|
||||
host initiated version of the XEmbedProtocol
|
||||
*/
|
||||
unsigned long getHostWindowID();
|
||||
|
||||
/** Removes the client window from the host. */
|
||||
void removeClient();
|
||||
|
||||
/** Forces the embedded window to match the current size of this component. */
|
||||
void updateEmbeddedBounds();
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
void focusGained (FocusChangeType) override;
|
||||
void focusLost (FocusChangeType) override;
|
||||
void broughtToFront() override;
|
||||
|
||||
private:
|
||||
friend bool juce::juce_handleXEmbedEvent (ComponentPeer*, void*);
|
||||
friend unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
|
||||
|
||||
class Pimpl;
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
392
deps/juce/modules/juce_gui_extra/juce_gui_extra.cpp
vendored
392
deps/juce/modules/juce_gui_extra/juce_gui_extra.cpp
vendored
@ -1,197 +1,195 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_GUI_EXTRA_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
|
||||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
|
||||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1
|
||||
#define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1
|
||||
#define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1
|
||||
#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1
|
||||
|
||||
#ifndef JUCE_PUSH_NOTIFICATIONS
|
||||
#define JUCE_PUSH_NOTIFICATIONS 0
|
||||
#endif
|
||||
|
||||
#include "juce_gui_extra.h"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
#import <WebKit/WebKit.h>
|
||||
#import <IOKit/IOKitLib.h>
|
||||
#import <IOKit/IOCFPlugIn.h>
|
||||
#import <IOKit/hid/IOHIDLib.h>
|
||||
#import <IOKit/hid/IOHIDKeys.h>
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
#import <Foundation/NSUserNotification.h>
|
||||
|
||||
#include "native/juce_mac_PushNotifications.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_IOS
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
#if defined (__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#endif
|
||||
|
||||
#include "native/juce_ios_PushNotifications.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
#include "native/juce_android_PushNotifications.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#include <windowsx.h>
|
||||
#include <vfw.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include <exdisp.h>
|
||||
#include <exdispid.h>
|
||||
|
||||
#if JUCE_USE_WIN_WEBVIEW2
|
||||
#include <windows.foundation.h>
|
||||
#include <windows.foundation.collections.h>
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4265)
|
||||
#include <wrl.h>
|
||||
#include <wrl/wrappers/corewrappers.h>
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
#include "WebView2.h"
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4458)
|
||||
#include "WebView2EnvironmentOptions.h"
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif (JUCE_LINUX || JUCE_BSD) && JUCE_WEB_BROWSER
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant", "-Wparentheses", "-Wdeprecated-declarations")
|
||||
|
||||
// If you're missing this header, you need to install the webkit2gtk-4.0 package
|
||||
#include <gtk/gtk.h>
|
||||
#include <gtk/gtkx.h>
|
||||
#include <glib-unix.h>
|
||||
#include <webkit2/webkit2.h>
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#include "documents/juce_FileBasedDocument.cpp"
|
||||
#include "code_editor/juce_CodeDocument.cpp"
|
||||
#include "code_editor/juce_CodeEditorComponent.cpp"
|
||||
#include "code_editor/juce_CPlusPlusCodeTokeniser.cpp"
|
||||
#include "code_editor/juce_XMLCodeTokeniser.cpp"
|
||||
#include "code_editor/juce_LuaCodeTokeniser.cpp"
|
||||
#include "misc/juce_BubbleMessageComponent.cpp"
|
||||
#include "misc/juce_ColourSelector.cpp"
|
||||
#include "misc/juce_KeyMappingEditorComponent.cpp"
|
||||
#include "misc/juce_PreferencesPanel.cpp"
|
||||
#include "misc/juce_PushNotifications.cpp"
|
||||
#include "misc/juce_RecentlyOpenedFilesList.cpp"
|
||||
#include "misc/juce_SplashScreen.cpp"
|
||||
#include "misc/juce_SystemTrayIconComponent.cpp"
|
||||
#include "misc/juce_LiveConstantEditor.cpp"
|
||||
#include "misc/juce_AnimatedAppComponent.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
|
||||
#if JUCE_MAC
|
||||
#include "native/juce_mac_NSViewComponent.mm"
|
||||
#include "native/juce_mac_AppleRemote.mm"
|
||||
#include "native/juce_mac_SystemTrayIcon.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
#include "native/juce_ios_UIViewComponent.mm"
|
||||
#endif
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_mac_WebBrowserComponent.mm"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#include "native/juce_win32_ActiveXComponent.cpp"
|
||||
#include "native/juce_win32_HWNDComponent.cpp"
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_win32_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
#include "native/juce_win32_SystemTrayIcon.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant")
|
||||
|
||||
#include "native/juce_linux_XEmbedComponent.cpp"
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_linux_X11_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#include "native/juce_linux_X11_SystemTrayIcon.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
#include "native/juce_AndroidViewComponent.cpp"
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_android_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_WINDOWS && JUCE_WEB_BROWSER
|
||||
juce::WebBrowserComponent::WebBrowserComponent (ConstructWithoutPimpl) {}
|
||||
juce::WindowsWebView2WebBrowserComponent::WindowsWebView2WebBrowserComponent (bool unloadWhenHidden,
|
||||
const WebView2Preferences&)
|
||||
: WebBrowserComponent (unloadWhenHidden) {}
|
||||
#endif
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifdef JUCE_GUI_EXTRA_H_INCLUDED
|
||||
/* When you add this cpp file to your project, you mustn't include it in a file where you've
|
||||
already included any other headers - just put it inside a file on its own, possibly with your config
|
||||
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
|
||||
header files that the compiler may be using.
|
||||
*/
|
||||
#error "Incorrect use of JUCE cpp file"
|
||||
#endif
|
||||
|
||||
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
|
||||
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
|
||||
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
|
||||
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1
|
||||
#define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1
|
||||
#define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1
|
||||
#define JUCE_GUI_BASICS_INCLUDE_SCOPED_THREAD_DPI_AWARENESS_SETTER 1
|
||||
|
||||
#ifndef JUCE_PUSH_NOTIFICATIONS
|
||||
#define JUCE_PUSH_NOTIFICATIONS 0
|
||||
#endif
|
||||
|
||||
#include "juce_gui_extra.h"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC
|
||||
#import <WebKit/WebKit.h>
|
||||
#import <IOKit/IOKitLib.h>
|
||||
#import <IOKit/IOCFPlugIn.h>
|
||||
#import <IOKit/hid/IOHIDLib.h>
|
||||
#import <IOKit/hid/IOHIDKeys.h>
|
||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
#import <Foundation/NSUserNotification.h>
|
||||
|
||||
#include "native/juce_mac_PushNotifications.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_IOS
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#include "native/juce_ios_PushNotifications.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
#include "native/juce_android_PushNotifications.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#include <windowsx.h>
|
||||
#include <vfw.h>
|
||||
#include <commdlg.h>
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include <exdisp.h>
|
||||
#include <exdispid.h>
|
||||
|
||||
#if JUCE_USE_WIN_WEBVIEW2
|
||||
#include <windows.foundation.h>
|
||||
#include <windows.foundation.collections.h>
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4265)
|
||||
#include <wrl.h>
|
||||
#include <wrl/wrappers/corewrappers.h>
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
#include "WebView2.h"
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4458)
|
||||
#include "WebView2EnvironmentOptions.h"
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif (JUCE_LINUX || JUCE_BSD) && JUCE_WEB_BROWSER
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant", "-Wparentheses", "-Wdeprecated-declarations")
|
||||
|
||||
// If you're missing this header, you need to install the webkit2gtk-4.0 package
|
||||
#include <gtk/gtk.h>
|
||||
#include <gtk/gtkx.h>
|
||||
#include <glib-unix.h>
|
||||
#include <webkit2/webkit2.h>
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#include "documents/juce_FileBasedDocument.cpp"
|
||||
#include "code_editor/juce_CodeDocument.cpp"
|
||||
#include "code_editor/juce_CodeEditorComponent.cpp"
|
||||
#include "code_editor/juce_CPlusPlusCodeTokeniser.cpp"
|
||||
#include "code_editor/juce_XMLCodeTokeniser.cpp"
|
||||
#include "code_editor/juce_LuaCodeTokeniser.cpp"
|
||||
#include "misc/juce_BubbleMessageComponent.cpp"
|
||||
#include "misc/juce_ColourSelector.cpp"
|
||||
#include "misc/juce_KeyMappingEditorComponent.cpp"
|
||||
#include "misc/juce_PreferencesPanel.cpp"
|
||||
#include "misc/juce_PushNotifications.cpp"
|
||||
#include "misc/juce_RecentlyOpenedFilesList.cpp"
|
||||
#include "misc/juce_SplashScreen.cpp"
|
||||
#include "misc/juce_SystemTrayIconComponent.cpp"
|
||||
#include "misc/juce_LiveConstantEditor.cpp"
|
||||
#include "misc/juce_AnimatedAppComponent.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
|
||||
#if JUCE_MAC
|
||||
#include "native/juce_mac_NSViewFrameWatcher.h"
|
||||
#include "native/juce_mac_NSViewComponent.mm"
|
||||
#include "native/juce_mac_AppleRemote.mm"
|
||||
#include "native/juce_mac_SystemTrayIcon.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
#include "native/juce_ios_UIViewComponent.mm"
|
||||
#endif
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_mac_WebBrowserComponent.mm"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#include "native/juce_win32_ActiveXComponent.cpp"
|
||||
#include "native/juce_win32_HWNDComponent.cpp"
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_win32_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
#include "native/juce_win32_SystemTrayIcon.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_LINUX || JUCE_BSD
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wzero-as-null-pointer-constant")
|
||||
|
||||
#include "native/juce_linux_XEmbedComponent.cpp"
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_linux_X11_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
#include "native/juce_linux_X11_SystemTrayIcon.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
#include "native/juce_AndroidViewComponent.cpp"
|
||||
|
||||
#if JUCE_WEB_BROWSER
|
||||
#include "native/juce_android_WebBrowserComponent.cpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_WINDOWS && JUCE_WEB_BROWSER
|
||||
juce::WebBrowserComponent::WebBrowserComponent (ConstructWithoutPimpl) {}
|
||||
juce::WindowsWebView2WebBrowserComponent::WindowsWebView2WebBrowserComponent (bool unloadWhenHidden,
|
||||
const WebView2Preferences&)
|
||||
: WebBrowserComponent (unloadWhenHidden) {}
|
||||
#endif
|
||||
|
244
deps/juce/modules/juce_gui_extra/juce_gui_extra.h
vendored
244
deps/juce/modules/juce_gui_extra/juce_gui_extra.h
vendored
@ -1,121 +1,123 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_gui_extra
|
||||
vendor: juce
|
||||
version: 6.1.2
|
||||
name: JUCE extended GUI classes
|
||||
description: Miscellaneous GUI classes for specialised tasks.
|
||||
website: http://www.juce.com/juce
|
||||
license: GPL/Commercial
|
||||
minimumCppStandard: 14
|
||||
|
||||
dependencies: juce_gui_basics
|
||||
OSXFrameworks: WebKit
|
||||
iOSFrameworks: WebKit
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#define JUCE_GUI_EXTRA_H_INCLUDED
|
||||
|
||||
#include <juce_gui_basics/juce_gui_basics.h>
|
||||
|
||||
//==============================================================================
|
||||
/** Config: JUCE_WEB_BROWSER
|
||||
This lets you disable the WebBrowserComponent class.
|
||||
If you're not using any embedded web-pages, turning this off may reduce your code size.
|
||||
*/
|
||||
#ifndef JUCE_WEB_BROWSER
|
||||
#define JUCE_WEB_BROWSER 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_WIN_WEBVIEW2
|
||||
Enables the use of the Microsoft Edge (Chromium) WebView2 browser on Windows,
|
||||
currently in developer preview.
|
||||
|
||||
If using the Projucer, the Microsoft.Web.WebView2 package will be added to the
|
||||
project solution if this flag is enabled. If you are building using CMake you
|
||||
will need to manually add the package via the Visual Studio package manager.
|
||||
|
||||
In addition to enabling this macro, you will need to use the
|
||||
WindowsWebView2WebBrowserComponent wrapper - see the documentation of that
|
||||
class for more details.
|
||||
*/
|
||||
#ifndef JUCE_USE_WIN_WEBVIEW2
|
||||
#define JUCE_USE_WIN_WEBVIEW2 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_ENABLE_LIVE_CONSTANT_EDITOR
|
||||
This lets you turn on the JUCE_ENABLE_LIVE_CONSTANT_EDITOR support (desktop only). By default
|
||||
this will be enabled for debug builds and disabled for release builds. See the documentation
|
||||
for that macro for more details.
|
||||
*/
|
||||
#ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR
|
||||
#if JUCE_DEBUG && ! (JUCE_IOS || JUCE_ANDROID)
|
||||
#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#include "documents/juce_FileBasedDocument.h"
|
||||
#include "code_editor/juce_CodeDocument.h"
|
||||
#include "code_editor/juce_CodeEditorComponent.h"
|
||||
#include "code_editor/juce_CodeTokeniser.h"
|
||||
#include "code_editor/juce_CPlusPlusCodeTokeniser.h"
|
||||
#include "code_editor/juce_CPlusPlusCodeTokeniserFunctions.h"
|
||||
#include "code_editor/juce_XMLCodeTokeniser.h"
|
||||
#include "code_editor/juce_LuaCodeTokeniser.h"
|
||||
#include "embedding/juce_ActiveXControlComponent.h"
|
||||
#include "embedding/juce_AndroidViewComponent.h"
|
||||
#include "embedding/juce_NSViewComponent.h"
|
||||
#include "embedding/juce_UIViewComponent.h"
|
||||
#include "embedding/juce_XEmbedComponent.h"
|
||||
#include "embedding/juce_HWNDComponent.h"
|
||||
#include "misc/juce_AppleRemote.h"
|
||||
#include "misc/juce_BubbleMessageComponent.h"
|
||||
#include "misc/juce_ColourSelector.h"
|
||||
#include "misc/juce_KeyMappingEditorComponent.h"
|
||||
#include "misc/juce_PreferencesPanel.h"
|
||||
#include "misc/juce_PushNotifications.h"
|
||||
#include "misc/juce_RecentlyOpenedFilesList.h"
|
||||
#include "misc/juce_SplashScreen.h"
|
||||
#include "misc/juce_SystemTrayIconComponent.h"
|
||||
#include "misc/juce_WebBrowserComponent.h"
|
||||
#include "misc/juce_LiveConstantEditor.h"
|
||||
#include "misc/juce_AnimatedAppComponent.h"
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
The block below describes the properties of this module, and is read by
|
||||
the Projucer to automatically generate project code that uses it.
|
||||
For details about the syntax and how to create or use a module, see the
|
||||
JUCE Module Format.md file.
|
||||
|
||||
|
||||
BEGIN_JUCE_MODULE_DECLARATION
|
||||
|
||||
ID: juce_gui_extra
|
||||
vendor: juce
|
||||
version: 7.0.2
|
||||
name: JUCE extended GUI classes
|
||||
description: Miscellaneous GUI classes for specialised tasks.
|
||||
website: http://www.juce.com/juce
|
||||
license: GPL/Commercial
|
||||
minimumCppStandard: 14
|
||||
|
||||
dependencies: juce_gui_basics
|
||||
OSXFrameworks: WebKit
|
||||
iOSFrameworks: WebKit
|
||||
WeakiOSFrameworks: UserNotifications
|
||||
WeakMacOSFrameworks: UserNotifications
|
||||
|
||||
END_JUCE_MODULE_DECLARATION
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
#define JUCE_GUI_EXTRA_H_INCLUDED
|
||||
|
||||
#include <juce_gui_basics/juce_gui_basics.h>
|
||||
|
||||
//==============================================================================
|
||||
/** Config: JUCE_WEB_BROWSER
|
||||
This lets you disable the WebBrowserComponent class.
|
||||
If you're not using any embedded web-pages, turning this off may reduce your code size.
|
||||
*/
|
||||
#ifndef JUCE_WEB_BROWSER
|
||||
#define JUCE_WEB_BROWSER 1
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_WIN_WEBVIEW2
|
||||
Enables the use of the Microsoft Edge (Chromium) WebView2 browser on Windows,
|
||||
currently in developer preview.
|
||||
|
||||
If using the Projucer, the Microsoft.Web.WebView2 package will be added to the
|
||||
project solution if this flag is enabled. If you are building using CMake you
|
||||
will need to manually add the package via the Visual Studio package manager.
|
||||
|
||||
In addition to enabling this macro, you will need to use the
|
||||
WindowsWebView2WebBrowserComponent wrapper - see the documentation of that
|
||||
class for more details.
|
||||
*/
|
||||
#ifndef JUCE_USE_WIN_WEBVIEW2
|
||||
#define JUCE_USE_WIN_WEBVIEW2 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_ENABLE_LIVE_CONSTANT_EDITOR
|
||||
This lets you turn on the JUCE_ENABLE_LIVE_CONSTANT_EDITOR support (desktop only). By default
|
||||
this will be enabled for debug builds and disabled for release builds. See the documentation
|
||||
for that macro for more details.
|
||||
*/
|
||||
#ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR
|
||||
#if JUCE_DEBUG && ! (JUCE_IOS || JUCE_ANDROID)
|
||||
#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#include "documents/juce_FileBasedDocument.h"
|
||||
#include "code_editor/juce_CodeDocument.h"
|
||||
#include "code_editor/juce_CodeEditorComponent.h"
|
||||
#include "code_editor/juce_CodeTokeniser.h"
|
||||
#include "code_editor/juce_CPlusPlusCodeTokeniser.h"
|
||||
#include "code_editor/juce_CPlusPlusCodeTokeniserFunctions.h"
|
||||
#include "code_editor/juce_XMLCodeTokeniser.h"
|
||||
#include "code_editor/juce_LuaCodeTokeniser.h"
|
||||
#include "embedding/juce_ActiveXControlComponent.h"
|
||||
#include "embedding/juce_AndroidViewComponent.h"
|
||||
#include "embedding/juce_NSViewComponent.h"
|
||||
#include "embedding/juce_UIViewComponent.h"
|
||||
#include "embedding/juce_XEmbedComponent.h"
|
||||
#include "embedding/juce_HWNDComponent.h"
|
||||
#include "misc/juce_AppleRemote.h"
|
||||
#include "misc/juce_BubbleMessageComponent.h"
|
||||
#include "misc/juce_ColourSelector.h"
|
||||
#include "misc/juce_KeyMappingEditorComponent.h"
|
||||
#include "misc/juce_PreferencesPanel.h"
|
||||
#include "misc/juce_PushNotifications.h"
|
||||
#include "misc/juce_RecentlyOpenedFilesList.h"
|
||||
#include "misc/juce_SplashScreen.h"
|
||||
#include "misc/juce_SystemTrayIconComponent.h"
|
||||
#include "misc/juce_WebBrowserComponent.h"
|
||||
#include "misc/juce_LiveConstantEditor.h"
|
||||
#include "misc/juce_AnimatedAppComponent.h"
|
||||
|
@ -2,15 +2,15 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
|
@ -1,54 +1,54 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
AnimatedAppComponent::AnimatedAppComponent()
|
||||
: lastUpdateTime (Time::getCurrentTime()), totalUpdates (0)
|
||||
{
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
void AnimatedAppComponent::setFramesPerSecond (int framesPerSecond)
|
||||
{
|
||||
jassert (framesPerSecond > 0 && framesPerSecond < 1000);
|
||||
startTimerHz (framesPerSecond);
|
||||
}
|
||||
|
||||
int AnimatedAppComponent::getMillisecondsSinceLastUpdate() const noexcept
|
||||
{
|
||||
return (int) (Time::getCurrentTime() - lastUpdateTime).inMilliseconds();
|
||||
}
|
||||
|
||||
void AnimatedAppComponent::timerCallback()
|
||||
{
|
||||
++totalUpdates;
|
||||
update();
|
||||
repaint();
|
||||
lastUpdateTime = Time::getCurrentTime();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
AnimatedAppComponent::AnimatedAppComponent()
|
||||
: lastUpdateTime (Time::getCurrentTime()), totalUpdates (0)
|
||||
{
|
||||
setOpaque (true);
|
||||
}
|
||||
|
||||
void AnimatedAppComponent::setFramesPerSecond (int framesPerSecond)
|
||||
{
|
||||
jassert (framesPerSecond > 0 && framesPerSecond < 1000);
|
||||
startTimerHz (framesPerSecond);
|
||||
}
|
||||
|
||||
int AnimatedAppComponent::getMillisecondsSinceLastUpdate() const noexcept
|
||||
{
|
||||
return (int) (Time::getCurrentTime() - lastUpdateTime).inMilliseconds();
|
||||
}
|
||||
|
||||
void AnimatedAppComponent::timerCallback()
|
||||
{
|
||||
++totalUpdates;
|
||||
update();
|
||||
repaint();
|
||||
lastUpdateTime = Time::getCurrentTime();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,77 +1,77 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for writing simple one-page graphical apps.
|
||||
|
||||
A subclass can inherit from this and implement just a few methods such as
|
||||
paint() and mouse-handling. The base class provides some simple abstractions
|
||||
to take care of continuously repainting itself.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class AnimatedAppComponent : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
AnimatedAppComponent();
|
||||
|
||||
/** Your subclass can call this to start a timer running which will
|
||||
call update() and repaint the component at the given frequency.
|
||||
*/
|
||||
void setFramesPerSecond (int framesPerSecond);
|
||||
|
||||
/** Called periodically, at the frequency specified by setFramesPerSecond().
|
||||
This is a the best place to do things like advancing animation parameters,
|
||||
checking the mouse position, etc.
|
||||
*/
|
||||
virtual void update() = 0;
|
||||
|
||||
/** Returns the number of times that update() has been called since the component
|
||||
started running.
|
||||
*/
|
||||
int getFrameCounter() const noexcept { return totalUpdates; }
|
||||
|
||||
/** When called from update(), this returns the number of milliseconds since the
|
||||
last update call.
|
||||
This might be useful for accurately timing animations, etc.
|
||||
*/
|
||||
int getMillisecondsSinceLastUpdate() const noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Time lastUpdateTime;
|
||||
int totalUpdates;
|
||||
|
||||
void timerCallback() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimatedAppComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for writing simple one-page graphical apps.
|
||||
|
||||
A subclass can inherit from this and implement just a few methods such as
|
||||
paint() and mouse-handling. The base class provides some simple abstractions
|
||||
to take care of continuously repainting itself.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class AnimatedAppComponent : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
AnimatedAppComponent();
|
||||
|
||||
/** Your subclass can call this to start a timer running which will
|
||||
call update() and repaint the component at the given frequency.
|
||||
*/
|
||||
void setFramesPerSecond (int framesPerSecond);
|
||||
|
||||
/** Called periodically, at the frequency specified by setFramesPerSecond().
|
||||
This is a the best place to do things like advancing animation parameters,
|
||||
checking the mouse position, etc.
|
||||
*/
|
||||
virtual void update() = 0;
|
||||
|
||||
/** Returns the number of times that update() has been called since the component
|
||||
started running.
|
||||
*/
|
||||
int getFrameCounter() const noexcept { return totalUpdates; }
|
||||
|
||||
/** When called from update(), this returns the number of milliseconds since the
|
||||
last update call.
|
||||
This might be useful for accurately timing animations, etc.
|
||||
*/
|
||||
int getMillisecondsSinceLastUpdate() const noexcept;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Time lastUpdateTime;
|
||||
int totalUpdates;
|
||||
|
||||
void timerCallback() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimatedAppComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,117 +1,117 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || DOXYGEN
|
||||
/**
|
||||
Receives events from an Apple IR remote control device (Only available in OSX!).
|
||||
|
||||
To use it, just create a subclass of this class, implementing the buttonPressed()
|
||||
callback, then call start() and stop() to start or stop receiving events.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API AppleRemoteDevice
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
AppleRemoteDevice();
|
||||
virtual ~AppleRemoteDevice();
|
||||
|
||||
//==============================================================================
|
||||
/** The set of buttons that may be pressed.
|
||||
@see buttonPressed
|
||||
*/
|
||||
enum ButtonType
|
||||
{
|
||||
menuButton = 0, /**< The menu button (if it's held for a short time). */
|
||||
playButton, /**< The play button. */
|
||||
plusButton, /**< The plus or volume-up button. */
|
||||
minusButton, /**< The minus or volume-down button. */
|
||||
rightButton, /**< The right button (if it's held for a short time). */
|
||||
leftButton, /**< The left button (if it's held for a short time). */
|
||||
rightButton_Long, /**< The right button (if it's held for a long time). */
|
||||
leftButton_Long, /**< The menu button (if it's held for a long time). */
|
||||
menuButton_Long, /**< The menu button (if it's held for a long time). */
|
||||
playButtonSleepMode,
|
||||
switched
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Override this method to receive the callback about a button press.
|
||||
|
||||
The callback will happen on the application's message thread.
|
||||
|
||||
Some buttons trigger matching up and down events, in which the isDown parameter
|
||||
will be true and then false. Others only send a single event when the
|
||||
button is pressed.
|
||||
*/
|
||||
virtual void buttonPressed (ButtonType buttonId, bool isDown) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts the device running and responding to events.
|
||||
|
||||
Returns true if it managed to open the device.
|
||||
|
||||
@param inExclusiveMode if true, the remote will be grabbed exclusively for this app,
|
||||
and will not be available to any other part of the system. If
|
||||
false, it will be shared with other apps.
|
||||
@see stop
|
||||
*/
|
||||
bool start (bool inExclusiveMode);
|
||||
|
||||
/** Stops the device running.
|
||||
@see start
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/** Returns true if the device has been started successfully.
|
||||
*/
|
||||
bool isActive() const;
|
||||
|
||||
/** Returns the ID number of the remote, if it has sent one.
|
||||
*/
|
||||
int getRemoteId() const { return remoteId; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void handleCallbackInternal();
|
||||
|
||||
private:
|
||||
void* device;
|
||||
void* queue;
|
||||
int remoteId;
|
||||
|
||||
bool open (bool openInExclusiveMode);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AppleRemoteDevice)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || DOXYGEN
|
||||
/**
|
||||
Receives events from an Apple IR remote control device (Only available in OSX!).
|
||||
|
||||
To use it, just create a subclass of this class, implementing the buttonPressed()
|
||||
callback, then call start() and stop() to start or stop receiving events.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API AppleRemoteDevice
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
AppleRemoteDevice();
|
||||
virtual ~AppleRemoteDevice();
|
||||
|
||||
//==============================================================================
|
||||
/** The set of buttons that may be pressed.
|
||||
@see buttonPressed
|
||||
*/
|
||||
enum ButtonType
|
||||
{
|
||||
menuButton = 0, /**< The menu button (if it's held for a short time). */
|
||||
playButton, /**< The play button. */
|
||||
plusButton, /**< The plus or volume-up button. */
|
||||
minusButton, /**< The minus or volume-down button. */
|
||||
rightButton, /**< The right button (if it's held for a short time). */
|
||||
leftButton, /**< The left button (if it's held for a short time). */
|
||||
rightButton_Long, /**< The right button (if it's held for a long time). */
|
||||
leftButton_Long, /**< The menu button (if it's held for a long time). */
|
||||
menuButton_Long, /**< The menu button (if it's held for a long time). */
|
||||
playButtonSleepMode,
|
||||
switched
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Override this method to receive the callback about a button press.
|
||||
|
||||
The callback will happen on the application's message thread.
|
||||
|
||||
Some buttons trigger matching up and down events, in which the isDown parameter
|
||||
will be true and then false. Others only send a single event when the
|
||||
button is pressed.
|
||||
*/
|
||||
virtual void buttonPressed (ButtonType buttonId, bool isDown) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Starts the device running and responding to events.
|
||||
|
||||
Returns true if it managed to open the device.
|
||||
|
||||
@param inExclusiveMode if true, the remote will be grabbed exclusively for this app,
|
||||
and will not be available to any other part of the system. If
|
||||
false, it will be shared with other apps.
|
||||
@see stop
|
||||
*/
|
||||
bool start (bool inExclusiveMode);
|
||||
|
||||
/** Stops the device running.
|
||||
@see start
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/** Returns true if the device has been started successfully.
|
||||
*/
|
||||
bool isActive() const;
|
||||
|
||||
/** Returns the ID number of the remote, if it has sent one.
|
||||
*/
|
||||
int getRemoteId() const { return remoteId; }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void handleCallbackInternal();
|
||||
|
||||
private:
|
||||
void* device;
|
||||
void* queue;
|
||||
int remoteId;
|
||||
|
||||
bool open (bool openInExclusiveMode);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AppleRemoteDevice)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,124 +1,124 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
BubbleMessageComponent::BubbleMessageComponent (int fadeOutLengthMs)
|
||||
: fadeOutLength (fadeOutLengthMs), mouseClickCounter (0),
|
||||
expiryTime (0), deleteAfterUse (false)
|
||||
{
|
||||
}
|
||||
|
||||
BubbleMessageComponent::~BubbleMessageComponent()
|
||||
{
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::showAt (const Rectangle<int>& pos,
|
||||
const AttributedString& text,
|
||||
const int numMillisecondsBeforeRemoving,
|
||||
const bool removeWhenMouseClicked,
|
||||
const bool deleteSelfAfterUse)
|
||||
{
|
||||
createLayout (text);
|
||||
setPosition (pos);
|
||||
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::showAt (Component* const component,
|
||||
const AttributedString& text,
|
||||
const int numMillisecondsBeforeRemoving,
|
||||
const bool removeWhenMouseClicked,
|
||||
const bool deleteSelfAfterUse)
|
||||
{
|
||||
createLayout (text);
|
||||
setPosition (component);
|
||||
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::createLayout (const AttributedString& text)
|
||||
{
|
||||
textLayout.createLayoutWithBalancedLineLengths (text, 256);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::init (const int numMillisecondsBeforeRemoving,
|
||||
const bool removeWhenMouseClicked,
|
||||
const bool deleteSelfAfterUse)
|
||||
{
|
||||
setAlpha (1.0f);
|
||||
setVisible (true);
|
||||
deleteAfterUse = deleteSelfAfterUse;
|
||||
|
||||
expiryTime = numMillisecondsBeforeRemoving > 0
|
||||
? (Time::getMillisecondCounter() + (uint32) numMillisecondsBeforeRemoving) : 0;
|
||||
|
||||
mouseClickCounter = Desktop::getInstance().getMouseButtonClickCounter();
|
||||
|
||||
if (! (removeWhenMouseClicked && isShowing()))
|
||||
mouseClickCounter += 0xfffff;
|
||||
|
||||
startTimer (77);
|
||||
repaint();
|
||||
}
|
||||
|
||||
const float bubblePaddingX = 20.0f;
|
||||
const float bubblePaddingY = 14.0f;
|
||||
|
||||
void BubbleMessageComponent::getContentSize (int& w, int& h)
|
||||
{
|
||||
w = (int) (bubblePaddingX + textLayout.getWidth());
|
||||
h = (int) (bubblePaddingY + textLayout.getHeight());
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::paintContent (Graphics& g, int w, int h)
|
||||
{
|
||||
g.setColour (findColour (TooltipWindow::textColourId));
|
||||
|
||||
textLayout.draw (g, Rectangle<float> (bubblePaddingX / 2.0f, bubblePaddingY / 2.0f,
|
||||
(float) w - bubblePaddingX, (float) h - bubblePaddingY));
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::timerCallback()
|
||||
{
|
||||
if (Desktop::getInstance().getMouseButtonClickCounter() > mouseClickCounter)
|
||||
hide (false);
|
||||
else if (expiryTime != 0 && Time::getMillisecondCounter() > expiryTime)
|
||||
hide (true);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::hide (const bool fadeOut)
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
if (fadeOut)
|
||||
Desktop::getInstance().getAnimator().fadeOut (this, fadeOutLength);
|
||||
else
|
||||
setVisible (false);
|
||||
|
||||
if (deleteAfterUse)
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
BubbleMessageComponent::BubbleMessageComponent (int fadeOutLengthMs)
|
||||
: fadeOutLength (fadeOutLengthMs), mouseClickCounter (0),
|
||||
expiryTime (0), deleteAfterUse (false)
|
||||
{
|
||||
}
|
||||
|
||||
BubbleMessageComponent::~BubbleMessageComponent()
|
||||
{
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::showAt (const Rectangle<int>& pos,
|
||||
const AttributedString& text,
|
||||
const int numMillisecondsBeforeRemoving,
|
||||
const bool removeWhenMouseClicked,
|
||||
const bool deleteSelfAfterUse)
|
||||
{
|
||||
createLayout (text);
|
||||
setPosition (pos);
|
||||
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::showAt (Component* const component,
|
||||
const AttributedString& text,
|
||||
const int numMillisecondsBeforeRemoving,
|
||||
const bool removeWhenMouseClicked,
|
||||
const bool deleteSelfAfterUse)
|
||||
{
|
||||
createLayout (text);
|
||||
setPosition (component);
|
||||
init (numMillisecondsBeforeRemoving, removeWhenMouseClicked, deleteSelfAfterUse);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::createLayout (const AttributedString& text)
|
||||
{
|
||||
textLayout.createLayoutWithBalancedLineLengths (text, 256);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::init (const int numMillisecondsBeforeRemoving,
|
||||
const bool removeWhenMouseClicked,
|
||||
const bool deleteSelfAfterUse)
|
||||
{
|
||||
setAlpha (1.0f);
|
||||
setVisible (true);
|
||||
deleteAfterUse = deleteSelfAfterUse;
|
||||
|
||||
expiryTime = numMillisecondsBeforeRemoving > 0
|
||||
? (Time::getMillisecondCounter() + (uint32) numMillisecondsBeforeRemoving) : 0;
|
||||
|
||||
mouseClickCounter = Desktop::getInstance().getMouseButtonClickCounter();
|
||||
|
||||
if (! (removeWhenMouseClicked && isShowing()))
|
||||
mouseClickCounter += 0xfffff;
|
||||
|
||||
startTimer (77);
|
||||
repaint();
|
||||
}
|
||||
|
||||
const float bubblePaddingX = 20.0f;
|
||||
const float bubblePaddingY = 14.0f;
|
||||
|
||||
void BubbleMessageComponent::getContentSize (int& w, int& h)
|
||||
{
|
||||
w = (int) (bubblePaddingX + textLayout.getWidth());
|
||||
h = (int) (bubblePaddingY + textLayout.getHeight());
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::paintContent (Graphics& g, int w, int h)
|
||||
{
|
||||
g.setColour (findColour (TooltipWindow::textColourId));
|
||||
|
||||
textLayout.draw (g, Rectangle<float> (bubblePaddingX / 2.0f, bubblePaddingY / 2.0f,
|
||||
(float) w - bubblePaddingX, (float) h - bubblePaddingY));
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::timerCallback()
|
||||
{
|
||||
if (Desktop::getInstance().getMouseButtonClickCounter() > mouseClickCounter)
|
||||
hide (false);
|
||||
else if (expiryTime != 0 && Time::getMillisecondCounter() > expiryTime)
|
||||
hide (true);
|
||||
}
|
||||
|
||||
void BubbleMessageComponent::hide (const bool fadeOut)
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
if (fadeOut)
|
||||
Desktop::getInstance().getAnimator().fadeOut (this, fadeOutLength);
|
||||
else
|
||||
setVisible (false);
|
||||
|
||||
if (deleteAfterUse)
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,125 +1,125 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A speech-bubble component that displays a short message.
|
||||
|
||||
This can be used to show a message with the tail of the speech bubble
|
||||
pointing to a particular component or location on the screen.
|
||||
|
||||
@see BubbleComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API BubbleMessageComponent : public BubbleComponent,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a bubble component.
|
||||
|
||||
After creating one a BubbleComponent, do the following:
|
||||
- add it to an appropriate parent component, or put it on the
|
||||
desktop with Component::addToDesktop (0).
|
||||
- use the showAt() method to show a message.
|
||||
- it will make itself invisible after it times-out (and can optionally
|
||||
also delete itself), or you can reuse it somewhere else by calling
|
||||
showAt() again.
|
||||
*/
|
||||
BubbleMessageComponent (int fadeOutLengthMs = 150);
|
||||
|
||||
/** Destructor. */
|
||||
~BubbleMessageComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Shows a message bubble at a particular position.
|
||||
|
||||
This shows the bubble with its stem pointing to the given location
|
||||
(coordinates being relative to its parent component).
|
||||
|
||||
@param position the coords of the object to point to
|
||||
@param message the text to display
|
||||
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
|
||||
from its parent component. If this is 0 or less, it
|
||||
will stay there until manually removed.
|
||||
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
|
||||
mouse button is pressed (anywhere on the screen)
|
||||
@param deleteSelfAfterUse if true, then the component will delete itself after
|
||||
it becomes invisible
|
||||
*/
|
||||
void showAt (const Rectangle<int>& position,
|
||||
const AttributedString& message,
|
||||
int numMillisecondsBeforeRemoving,
|
||||
bool removeWhenMouseClicked = true,
|
||||
bool deleteSelfAfterUse = false);
|
||||
|
||||
/** Shows a message bubble next to a particular component.
|
||||
|
||||
This shows the bubble with its stem pointing at the given component.
|
||||
|
||||
@param component the component that you want to point at
|
||||
@param message the text to display
|
||||
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
|
||||
from its parent component. If this is 0 or less, it
|
||||
will stay there until manually removed.
|
||||
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
|
||||
mouse button is pressed (anywhere on the screen)
|
||||
@param deleteSelfAfterUse if true, then the component will delete itself after
|
||||
it becomes invisible
|
||||
*/
|
||||
void showAt (Component* component,
|
||||
const AttributedString& message,
|
||||
int numMillisecondsBeforeRemoving,
|
||||
bool removeWhenMouseClicked = true,
|
||||
bool deleteSelfAfterUse = false);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void getContentSize (int& w, int& h) override;
|
||||
/** @internal */
|
||||
void paintContent (Graphics& g, int w, int h) override;
|
||||
/** @internal */
|
||||
void timerCallback() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int fadeOutLength, mouseClickCounter;
|
||||
TextLayout textLayout;
|
||||
int64 expiryTime;
|
||||
bool deleteAfterUse;
|
||||
|
||||
void createLayout (const AttributedString&);
|
||||
void init (int, bool, bool);
|
||||
void hide (bool fadeOut);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleMessageComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A speech-bubble component that displays a short message.
|
||||
|
||||
This can be used to show a message with the tail of the speech bubble
|
||||
pointing to a particular component or location on the screen.
|
||||
|
||||
@see BubbleComponent
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API BubbleMessageComponent : public BubbleComponent,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a bubble component.
|
||||
|
||||
After creating one a BubbleComponent, do the following:
|
||||
- add it to an appropriate parent component, or put it on the
|
||||
desktop with Component::addToDesktop (0).
|
||||
- use the showAt() method to show a message.
|
||||
- it will make itself invisible after it times-out (and can optionally
|
||||
also delete itself), or you can reuse it somewhere else by calling
|
||||
showAt() again.
|
||||
*/
|
||||
BubbleMessageComponent (int fadeOutLengthMs = 150);
|
||||
|
||||
/** Destructor. */
|
||||
~BubbleMessageComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Shows a message bubble at a particular position.
|
||||
|
||||
This shows the bubble with its stem pointing to the given location
|
||||
(coordinates being relative to its parent component).
|
||||
|
||||
@param position the coords of the object to point to
|
||||
@param message the text to display
|
||||
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
|
||||
from its parent component. If this is 0 or less, it
|
||||
will stay there until manually removed.
|
||||
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
|
||||
mouse button is pressed (anywhere on the screen)
|
||||
@param deleteSelfAfterUse if true, then the component will delete itself after
|
||||
it becomes invisible
|
||||
*/
|
||||
void showAt (const Rectangle<int>& position,
|
||||
const AttributedString& message,
|
||||
int numMillisecondsBeforeRemoving,
|
||||
bool removeWhenMouseClicked = true,
|
||||
bool deleteSelfAfterUse = false);
|
||||
|
||||
/** Shows a message bubble next to a particular component.
|
||||
|
||||
This shows the bubble with its stem pointing at the given component.
|
||||
|
||||
@param component the component that you want to point at
|
||||
@param message the text to display
|
||||
@param numMillisecondsBeforeRemoving how long to leave it on the screen before removing itself
|
||||
from its parent component. If this is 0 or less, it
|
||||
will stay there until manually removed.
|
||||
@param removeWhenMouseClicked if this is true, the bubble will disappear as soon as a
|
||||
mouse button is pressed (anywhere on the screen)
|
||||
@param deleteSelfAfterUse if true, then the component will delete itself after
|
||||
it becomes invisible
|
||||
*/
|
||||
void showAt (Component* component,
|
||||
const AttributedString& message,
|
||||
int numMillisecondsBeforeRemoving,
|
||||
bool removeWhenMouseClicked = true,
|
||||
bool deleteSelfAfterUse = false);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void getContentSize (int& w, int& h) override;
|
||||
/** @internal */
|
||||
void paintContent (Graphics& g, int w, int h) override;
|
||||
/** @internal */
|
||||
void timerCallback() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int fadeOutLength, mouseClickCounter;
|
||||
TextLayout textLayout;
|
||||
int64 expiryTime;
|
||||
bool deleteAfterUse;
|
||||
|
||||
void createLayout (const AttributedString&);
|
||||
void init (int, bool, bool);
|
||||
void hide (bool fadeOut);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BubbleMessageComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,167 +1,167 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that lets the user choose a colour.
|
||||
|
||||
This shows RGB sliders and a colourspace that the user can pick colours from.
|
||||
|
||||
This class is also a ChangeBroadcaster, so listeners can register to be told
|
||||
when the colour changes.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ColourSelector : public Component,
|
||||
public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Options for the type of selector to show. These are passed into the constructor. */
|
||||
enum ColourSelectorOptions
|
||||
{
|
||||
showAlphaChannel = 1 << 0, /**< if set, the colour's alpha channel can be changed as well as its RGB. */
|
||||
|
||||
showColourAtTop = 1 << 1, /**< if set, a swatch of the colour is shown at the top of the component. */
|
||||
editableColour = 1 << 2, /**< if set, the colour shows at the top of the component is editable. */
|
||||
showSliders = 1 << 3, /**< if set, RGB sliders are shown at the bottom of the component. */
|
||||
showColourspace = 1 << 4 /**< if set, a big HSV selector is shown. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a ColourSelector object.
|
||||
|
||||
The flags are a combination of values from the ColourSelectorOptions enum, specifying
|
||||
which of the selector's features should be visible.
|
||||
|
||||
The edgeGap value specifies the amount of space to leave around the edge.
|
||||
|
||||
gapAroundColourSpaceComponent indicates how much of a gap to put around the
|
||||
colourspace and hue selector components.
|
||||
*/
|
||||
ColourSelector (int flags = (showAlphaChannel | showColourAtTop | showSliders | showColourspace),
|
||||
int edgeGap = 4,
|
||||
int gapAroundColourSpaceComponent = 7);
|
||||
|
||||
/** Destructor. */
|
||||
~ColourSelector() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the colour that the user has currently selected.
|
||||
|
||||
The ColourSelector class is also a ChangeBroadcaster, so listeners can
|
||||
register to be told when the colour changes.
|
||||
|
||||
@see setCurrentColour
|
||||
*/
|
||||
Colour getCurrentColour() const;
|
||||
|
||||
/** Changes the colour that is currently being shown.
|
||||
|
||||
@param newColour the new colour to show
|
||||
@param notificationType whether to send a notification of the change to listeners.
|
||||
A notification will only be sent if the colour has changed.
|
||||
*/
|
||||
void setCurrentColour (Colour newColour, NotificationType notificationType = sendNotification);
|
||||
|
||||
//==============================================================================
|
||||
/** Tells the selector how many preset colour swatches you want to have on the component.
|
||||
|
||||
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
|
||||
setSwatchColour(), to return the number of colours you want, and to set and retrieve
|
||||
their values.
|
||||
*/
|
||||
virtual int getNumSwatches() const;
|
||||
|
||||
/** Called by the selector to find out the colour of one of the swatches.
|
||||
|
||||
Your subclass should return the colour of the swatch with the given index.
|
||||
|
||||
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
|
||||
setSwatchColour(), to return the number of colours you want, and to set and retrieve
|
||||
their values.
|
||||
*/
|
||||
virtual Colour getSwatchColour (int index) const;
|
||||
|
||||
/** Called by the selector when the user puts a new colour into one of the swatches.
|
||||
|
||||
Your subclass should change the colour of the swatch with the given index.
|
||||
|
||||
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
|
||||
setSwatchColour(), to return the number of colours you want, and to set and retrieve
|
||||
their values.
|
||||
*/
|
||||
virtual void setSwatchColour (int index, const Colour& newColour);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the keyboard.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1007000, /**< the colour used to fill the component's background. */
|
||||
labelTextColourId = 0x1007001 /**< the colour used for the labels next to the sliders. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// These need to be public otherwise the Projucer's live-build engine will complain
|
||||
class ColourSpaceView;
|
||||
class HueSelectorComp;
|
||||
class ColourPreviewComp;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class SwatchComponent;
|
||||
|
||||
Colour colour;
|
||||
float h, s, v;
|
||||
std::unique_ptr<Slider> sliders[4];
|
||||
std::unique_ptr<ColourSpaceView> colourSpace;
|
||||
std::unique_ptr<HueSelectorComp> hueSelector;
|
||||
std::unique_ptr<ColourPreviewComp> previewComponent;
|
||||
OwnedArray<SwatchComponent> swatchComponents;
|
||||
const int flags;
|
||||
int edgeGap;
|
||||
|
||||
void setHue (float newH);
|
||||
void setSV (float newS, float newV);
|
||||
void updateHSV();
|
||||
void update (NotificationType);
|
||||
void changeColour();
|
||||
void paint (Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourSelector)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that lets the user choose a colour.
|
||||
|
||||
This shows RGB sliders and a colourspace that the user can pick colours from.
|
||||
|
||||
This class is also a ChangeBroadcaster, so listeners can register to be told
|
||||
when the colour changes.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ColourSelector : public Component,
|
||||
public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Options for the type of selector to show. These are passed into the constructor. */
|
||||
enum ColourSelectorOptions
|
||||
{
|
||||
showAlphaChannel = 1 << 0, /**< if set, the colour's alpha channel can be changed as well as its RGB. */
|
||||
|
||||
showColourAtTop = 1 << 1, /**< if set, a swatch of the colour is shown at the top of the component. */
|
||||
editableColour = 1 << 2, /**< if set, the colour shows at the top of the component is editable. */
|
||||
showSliders = 1 << 3, /**< if set, RGB sliders are shown at the bottom of the component. */
|
||||
showColourspace = 1 << 4 /**< if set, a big HSV selector is shown. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a ColourSelector object.
|
||||
|
||||
The flags are a combination of values from the ColourSelectorOptions enum, specifying
|
||||
which of the selector's features should be visible.
|
||||
|
||||
The edgeGap value specifies the amount of space to leave around the edge.
|
||||
|
||||
gapAroundColourSpaceComponent indicates how much of a gap to put around the
|
||||
colourspace and hue selector components.
|
||||
*/
|
||||
ColourSelector (int flags = (showAlphaChannel | showColourAtTop | showSliders | showColourspace),
|
||||
int edgeGap = 4,
|
||||
int gapAroundColourSpaceComponent = 7);
|
||||
|
||||
/** Destructor. */
|
||||
~ColourSelector() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the colour that the user has currently selected.
|
||||
|
||||
The ColourSelector class is also a ChangeBroadcaster, so listeners can
|
||||
register to be told when the colour changes.
|
||||
|
||||
@see setCurrentColour
|
||||
*/
|
||||
Colour getCurrentColour() const;
|
||||
|
||||
/** Changes the colour that is currently being shown.
|
||||
|
||||
@param newColour the new colour to show
|
||||
@param notificationType whether to send a notification of the change to listeners.
|
||||
A notification will only be sent if the colour has changed.
|
||||
*/
|
||||
void setCurrentColour (Colour newColour, NotificationType notificationType = sendNotification);
|
||||
|
||||
//==============================================================================
|
||||
/** Tells the selector how many preset colour swatches you want to have on the component.
|
||||
|
||||
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
|
||||
setSwatchColour(), to return the number of colours you want, and to set and retrieve
|
||||
their values.
|
||||
*/
|
||||
virtual int getNumSwatches() const;
|
||||
|
||||
/** Called by the selector to find out the colour of one of the swatches.
|
||||
|
||||
Your subclass should return the colour of the swatch with the given index.
|
||||
|
||||
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
|
||||
setSwatchColour(), to return the number of colours you want, and to set and retrieve
|
||||
their values.
|
||||
*/
|
||||
virtual Colour getSwatchColour (int index) const;
|
||||
|
||||
/** Called by the selector when the user puts a new colour into one of the swatches.
|
||||
|
||||
Your subclass should change the colour of the swatch with the given index.
|
||||
|
||||
To enable swatches, you'll need to override getNumSwatches(), getSwatchColour(), and
|
||||
setSwatchColour(), to return the number of colours you want, and to set and retrieve
|
||||
their values.
|
||||
*/
|
||||
virtual void setSwatchColour (int index, const Colour& newColour);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the keyboard.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1007000, /**< the colour used to fill the component's background. */
|
||||
labelTextColourId = 0x1007001 /**< the colour used for the labels next to the sliders. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// These need to be public otherwise the Projucer's live-build engine will complain
|
||||
class ColourSpaceView;
|
||||
class HueSelectorComp;
|
||||
class ColourPreviewComp;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class SwatchComponent;
|
||||
|
||||
Colour colour;
|
||||
float h, s, v;
|
||||
std::unique_ptr<Slider> sliders[4];
|
||||
std::unique_ptr<ColourSpaceView> colourSpace;
|
||||
std::unique_ptr<HueSelectorComp> hueSelector;
|
||||
std::unique_ptr<ColourPreviewComp> previewComponent;
|
||||
OwnedArray<SwatchComponent> swatchComponents;
|
||||
const int flags;
|
||||
int edgeGap;
|
||||
|
||||
void setHue (float newH);
|
||||
void setSV (float newS, float newV);
|
||||
void updateHSV();
|
||||
void update (NotificationType);
|
||||
void changeColour();
|
||||
void paint (Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourSelector)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,480 +1,480 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class KeyMappingEditorComponent::ChangeKeyButton : public Button
|
||||
{
|
||||
public:
|
||||
ChangeKeyButton (KeyMappingEditorComponent& kec, CommandID command,
|
||||
const String& keyName, int keyIndex)
|
||||
: Button (keyName),
|
||||
owner (kec),
|
||||
commandID (command),
|
||||
keyNum (keyIndex)
|
||||
{
|
||||
setWantsKeyboardFocus (false);
|
||||
setTriggeredOnMouseDown (keyNum >= 0);
|
||||
|
||||
setTooltip (keyIndex < 0 ? TRANS("Adds a new key-mapping")
|
||||
: TRANS("Click to change this key-mapping"));
|
||||
}
|
||||
|
||||
void paintButton (Graphics& g, bool /*isOver*/, bool /*isDown*/) override
|
||||
{
|
||||
getLookAndFeel().drawKeymapChangeButton (g, getWidth(), getHeight(), *this,
|
||||
keyNum >= 0 ? getName() : String());
|
||||
}
|
||||
|
||||
void clicked() override
|
||||
{
|
||||
if (keyNum >= 0)
|
||||
{
|
||||
Component::SafePointer<ChangeKeyButton> button (this);
|
||||
PopupMenu m;
|
||||
|
||||
m.addItem (TRANS("Change this key-mapping"),
|
||||
[button]
|
||||
{
|
||||
if (button != nullptr)
|
||||
button.getComponent()->assignNewKey();
|
||||
});
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
m.addItem (TRANS("Remove this key-mapping"),
|
||||
[button]
|
||||
{
|
||||
if (button != nullptr)
|
||||
button->owner.getMappings().removeKeyPress (button->commandID,
|
||||
button->keyNum);
|
||||
});
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options().withTargetComponent (this));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignNewKey(); // + button pressed..
|
||||
}
|
||||
}
|
||||
|
||||
using Button::clicked;
|
||||
|
||||
void fitToContent (const int h) noexcept
|
||||
{
|
||||
if (keyNum < 0)
|
||||
setSize (h, h);
|
||||
else
|
||||
setSize (jlimit (h * 4, h * 8, 6 + Font ((float) h * 0.6f).getStringWidth (getName())), h);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class KeyEntryWindow : public AlertWindow
|
||||
{
|
||||
public:
|
||||
KeyEntryWindow (KeyMappingEditorComponent& kec)
|
||||
: AlertWindow (TRANS("New key-mapping"),
|
||||
TRANS("Please press a key combination now..."),
|
||||
MessageBoxIconType::NoIcon),
|
||||
owner (kec)
|
||||
{
|
||||
addButton (TRANS("OK"), 1);
|
||||
addButton (TRANS("Cancel"), 0);
|
||||
|
||||
// (avoid return + escape keys getting processed by the buttons..)
|
||||
for (auto* child : getChildren())
|
||||
child->setWantsKeyboardFocus (false);
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
grabKeyboardFocus();
|
||||
}
|
||||
|
||||
bool keyPressed (const KeyPress& key) override
|
||||
{
|
||||
lastPress = key;
|
||||
String message (TRANS("Key") + ": " + owner.getDescriptionForKeyPress (key));
|
||||
|
||||
auto previousCommand = owner.getMappings().findCommandForKeyPress (key);
|
||||
|
||||
if (previousCommand != 0)
|
||||
message << "\n\n("
|
||||
<< TRANS("Currently assigned to \"CMDN\"")
|
||||
.replace ("CMDN", TRANS (owner.getCommandManager().getNameOfCommand (previousCommand)))
|
||||
<< ')';
|
||||
|
||||
setMessage (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool keyStateChanged (bool) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
KeyPress lastPress;
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow)
|
||||
};
|
||||
|
||||
static void assignNewKeyCallback (int result, ChangeKeyButton* button, KeyPress newKey)
|
||||
{
|
||||
if (result != 0 && button != nullptr)
|
||||
button->setNewKey (newKey, true);
|
||||
}
|
||||
|
||||
void setNewKey (const KeyPress& newKey, bool dontAskUser)
|
||||
{
|
||||
if (newKey.isValid())
|
||||
{
|
||||
auto previousCommand = owner.getMappings().findCommandForKeyPress (newKey);
|
||||
|
||||
if (previousCommand == 0 || dontAskUser)
|
||||
{
|
||||
owner.getMappings().removeKeyPress (newKey);
|
||||
|
||||
if (keyNum >= 0)
|
||||
owner.getMappings().removeKeyPress (commandID, keyNum);
|
||||
|
||||
owner.getMappings().addKeyPress (commandID, newKey, keyNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
|
||||
TRANS("Change key-mapping"),
|
||||
TRANS("This key is already assigned to the command \"CMDN\"")
|
||||
.replace ("CMDN", owner.getCommandManager().getNameOfCommand (previousCommand))
|
||||
+ "\n\n"
|
||||
+ TRANS("Do you want to re-assign it to this new command instead?"),
|
||||
TRANS("Re-assign"),
|
||||
TRANS("Cancel"),
|
||||
this,
|
||||
ModalCallbackFunction::forComponent (assignNewKeyCallback,
|
||||
this, KeyPress (newKey)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void keyChosen (int result, ChangeKeyButton* button)
|
||||
{
|
||||
if (button != nullptr && button->currentKeyEntryWindow != nullptr)
|
||||
{
|
||||
if (result != 0)
|
||||
{
|
||||
button->currentKeyEntryWindow->setVisible (false);
|
||||
button->setNewKey (button->currentKeyEntryWindow->lastPress, false);
|
||||
}
|
||||
|
||||
button->currentKeyEntryWindow.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void assignNewKey()
|
||||
{
|
||||
currentKeyEntryWindow.reset (new KeyEntryWindow (owner));
|
||||
currentKeyEntryWindow->enterModalState (true, ModalCallbackFunction::forComponent (keyChosen, this));
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
const CommandID commandID;
|
||||
const int keyNum;
|
||||
std::unique_ptr<KeyEntryWindow> currentKeyEntryWindow;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::ItemComponent : public Component
|
||||
{
|
||||
public:
|
||||
ItemComponent (KeyMappingEditorComponent& kec, CommandID command)
|
||||
: owner (kec), commandID (command)
|
||||
{
|
||||
setInterceptsMouseClicks (false, true);
|
||||
|
||||
const bool isReadOnly = owner.isCommandReadOnly (commandID);
|
||||
|
||||
auto keyPresses = owner.getMappings().getKeyPressesAssignedToCommand (commandID);
|
||||
|
||||
for (int i = 0; i < jmin ((int) maxNumAssignments, keyPresses.size()); ++i)
|
||||
addKeyPressButton (owner.getDescriptionForKeyPress (keyPresses.getReference (i)), i, isReadOnly);
|
||||
|
||||
addKeyPressButton ("Change Key Mapping", -1, isReadOnly);
|
||||
}
|
||||
|
||||
void addKeyPressButton (const String& desc, const int index, const bool isReadOnly)
|
||||
{
|
||||
auto* b = new ChangeKeyButton (owner, commandID, desc, index);
|
||||
keyChangeButtons.add (b);
|
||||
|
||||
b->setEnabled (! isReadOnly);
|
||||
b->setVisible (keyChangeButtons.size() <= (int) maxNumAssignments);
|
||||
addChildComponent (b);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.setFont ((float) getHeight() * 0.7f);
|
||||
g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
|
||||
|
||||
g.drawFittedText (TRANS (owner.getCommandManager().getNameOfCommand (commandID)),
|
||||
4, 0, jmax (40, getChildComponent (0)->getX() - 5), getHeight(),
|
||||
Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
int x = getWidth() - 4;
|
||||
|
||||
for (int i = keyChangeButtons.size(); --i >= 0;)
|
||||
{
|
||||
auto* b = keyChangeButtons.getUnchecked(i);
|
||||
|
||||
b->fitToContent (getHeight() - 2);
|
||||
b->setTopRightPosition (x, 1);
|
||||
x = b->getX() - 5;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return createIgnoredAccessibilityHandler (*this);
|
||||
}
|
||||
|
||||
KeyMappingEditorComponent& owner;
|
||||
OwnedArray<ChangeKeyButton> keyChangeButtons;
|
||||
const CommandID commandID;
|
||||
|
||||
enum { maxNumAssignments = 3 };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::MappingItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
MappingItem (KeyMappingEditorComponent& kec, CommandID command)
|
||||
: owner (kec), commandID (command)
|
||||
{}
|
||||
|
||||
String getUniqueName() const override { return String ((int) commandID) + "_id"; }
|
||||
bool mightContainSubItems() override { return false; }
|
||||
int getItemHeight() const override { return 20; }
|
||||
std::unique_ptr<Component> createItemComponent() override { return std::make_unique<ItemComponent> (owner, commandID); }
|
||||
String getAccessibilityName() override { return TRANS (owner.getCommandManager().getNameOfCommand (commandID)); }
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
const CommandID commandID;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MappingItem)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::CategoryItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
CategoryItem (KeyMappingEditorComponent& kec, const String& name)
|
||||
: owner (kec), categoryName (name)
|
||||
{}
|
||||
|
||||
String getUniqueName() const override { return categoryName + "_cat"; }
|
||||
bool mightContainSubItems() override { return true; }
|
||||
int getItemHeight() const override { return 22; }
|
||||
String getAccessibilityName() override { return categoryName; }
|
||||
|
||||
void paintItem (Graphics& g, int width, int height) override
|
||||
{
|
||||
g.setFont (Font ((float) height * 0.7f, Font::bold));
|
||||
g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
|
||||
|
||||
g.drawText (TRANS (categoryName), 2, 0, width - 2, height, Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen) override
|
||||
{
|
||||
if (isNowOpen)
|
||||
{
|
||||
if (getNumSubItems() == 0)
|
||||
for (auto command : owner.getCommandManager().getCommandsInCategory (categoryName))
|
||||
if (owner.shouldCommandBeIncluded (command))
|
||||
addSubItem (new MappingItem (owner, command));
|
||||
}
|
||||
else
|
||||
{
|
||||
clearSubItems();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
String categoryName;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CategoryItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::TopLevelItem : public TreeViewItem,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
TopLevelItem (KeyMappingEditorComponent& kec) : owner (kec)
|
||||
{
|
||||
setLinesDrawnForSubItems (false);
|
||||
owner.getMappings().addChangeListener (this);
|
||||
}
|
||||
|
||||
~TopLevelItem() override
|
||||
{
|
||||
owner.getMappings().removeChangeListener (this);
|
||||
}
|
||||
|
||||
bool mightContainSubItems() override { return true; }
|
||||
String getUniqueName() const override { return "keys"; }
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
const OpennessRestorer opennessRestorer (*this);
|
||||
clearSubItems();
|
||||
|
||||
for (auto category : owner.getCommandManager().getCommandCategories())
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (auto command : owner.getCommandManager().getCommandsInCategory (category))
|
||||
if (owner.shouldCommandBeIncluded (command))
|
||||
++count;
|
||||
|
||||
if (count > 0)
|
||||
addSubItem (new CategoryItem (owner, category));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
};
|
||||
|
||||
static void resetKeyMappingsToDefaultsCallback (int result, KeyMappingEditorComponent* owner)
|
||||
{
|
||||
if (result != 0 && owner != nullptr)
|
||||
owner->getMappings().resetToDefaultMappings();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappingManager,
|
||||
const bool showResetToDefaultButton)
|
||||
: mappings (mappingManager),
|
||||
resetButton (TRANS ("reset to defaults"))
|
||||
{
|
||||
treeItem.reset (new TopLevelItem (*this));
|
||||
|
||||
if (showResetToDefaultButton)
|
||||
{
|
||||
addAndMakeVisible (resetButton);
|
||||
|
||||
resetButton.onClick = [this]
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::QuestionIcon,
|
||||
TRANS("Reset to defaults"),
|
||||
TRANS("Are you sure you want to reset all the key-mappings to their default state?"),
|
||||
TRANS("Reset"),
|
||||
{}, this,
|
||||
ModalCallbackFunction::forComponent (resetKeyMappingsToDefaultsCallback, this));
|
||||
};
|
||||
}
|
||||
|
||||
addAndMakeVisible (tree);
|
||||
tree.setTitle ("Key Mappings");
|
||||
tree.setColour (TreeView::backgroundColourId, findColour (backgroundColourId));
|
||||
tree.setRootItemVisible (false);
|
||||
tree.setDefaultOpenness (true);
|
||||
tree.setRootItem (treeItem.get());
|
||||
tree.setIndentSize (12);
|
||||
}
|
||||
|
||||
KeyMappingEditorComponent::~KeyMappingEditorComponent()
|
||||
{
|
||||
tree.setRootItem (nullptr);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void KeyMappingEditorComponent::setColours (Colour mainBackground,
|
||||
Colour textColour)
|
||||
{
|
||||
setColour (backgroundColourId, mainBackground);
|
||||
setColour (textColourId, textColour);
|
||||
tree.setColour (TreeView::backgroundColourId, mainBackground);
|
||||
}
|
||||
|
||||
void KeyMappingEditorComponent::parentHierarchyChanged()
|
||||
{
|
||||
treeItem->changeListenerCallback (nullptr);
|
||||
}
|
||||
|
||||
void KeyMappingEditorComponent::resized()
|
||||
{
|
||||
int h = getHeight();
|
||||
|
||||
if (resetButton.isVisible())
|
||||
{
|
||||
const int buttonHeight = 20;
|
||||
h -= buttonHeight + 8;
|
||||
int x = getWidth() - 8;
|
||||
|
||||
resetButton.changeWidthToFitText (buttonHeight);
|
||||
resetButton.setTopRightPosition (x, h + 6);
|
||||
}
|
||||
|
||||
tree.setBounds (0, 0, getWidth(), h);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyMappingEditorComponent::shouldCommandBeIncluded (const CommandID commandID)
|
||||
{
|
||||
auto* ci = mappings.getCommandManager().getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr && (ci->flags & ApplicationCommandInfo::hiddenFromKeyEditor) == 0;
|
||||
}
|
||||
|
||||
bool KeyMappingEditorComponent::isCommandReadOnly (const CommandID commandID)
|
||||
{
|
||||
auto* ci = mappings.getCommandManager().getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr && (ci->flags & ApplicationCommandInfo::readOnlyInKeyEditor) != 0;
|
||||
}
|
||||
|
||||
String KeyMappingEditorComponent::getDescriptionForKeyPress (const KeyPress& key)
|
||||
{
|
||||
return key.getTextDescription();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class KeyMappingEditorComponent::ChangeKeyButton : public Button
|
||||
{
|
||||
public:
|
||||
ChangeKeyButton (KeyMappingEditorComponent& kec, CommandID command,
|
||||
const String& keyName, int keyIndex)
|
||||
: Button (keyName),
|
||||
owner (kec),
|
||||
commandID (command),
|
||||
keyNum (keyIndex)
|
||||
{
|
||||
setWantsKeyboardFocus (false);
|
||||
setTriggeredOnMouseDown (keyNum >= 0);
|
||||
|
||||
setTooltip (keyIndex < 0 ? TRANS("Adds a new key-mapping")
|
||||
: TRANS("Click to change this key-mapping"));
|
||||
}
|
||||
|
||||
void paintButton (Graphics& g, bool /*isOver*/, bool /*isDown*/) override
|
||||
{
|
||||
getLookAndFeel().drawKeymapChangeButton (g, getWidth(), getHeight(), *this,
|
||||
keyNum >= 0 ? getName() : String());
|
||||
}
|
||||
|
||||
void clicked() override
|
||||
{
|
||||
if (keyNum >= 0)
|
||||
{
|
||||
Component::SafePointer<ChangeKeyButton> button (this);
|
||||
PopupMenu m;
|
||||
|
||||
m.addItem (TRANS("Change this key-mapping"),
|
||||
[button]
|
||||
{
|
||||
if (button != nullptr)
|
||||
button.getComponent()->assignNewKey();
|
||||
});
|
||||
|
||||
m.addSeparator();
|
||||
|
||||
m.addItem (TRANS("Remove this key-mapping"),
|
||||
[button]
|
||||
{
|
||||
if (button != nullptr)
|
||||
button->owner.getMappings().removeKeyPress (button->commandID,
|
||||
button->keyNum);
|
||||
});
|
||||
|
||||
m.showMenuAsync (PopupMenu::Options().withTargetComponent (this));
|
||||
}
|
||||
else
|
||||
{
|
||||
assignNewKey(); // + button pressed..
|
||||
}
|
||||
}
|
||||
|
||||
using Button::clicked;
|
||||
|
||||
void fitToContent (const int h) noexcept
|
||||
{
|
||||
if (keyNum < 0)
|
||||
setSize (h, h);
|
||||
else
|
||||
setSize (jlimit (h * 4, h * 8, 6 + Font ((float) h * 0.6f).getStringWidth (getName())), h);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class KeyEntryWindow : public AlertWindow
|
||||
{
|
||||
public:
|
||||
KeyEntryWindow (KeyMappingEditorComponent& kec)
|
||||
: AlertWindow (TRANS("New key-mapping"),
|
||||
TRANS("Please press a key combination now..."),
|
||||
MessageBoxIconType::NoIcon),
|
||||
owner (kec)
|
||||
{
|
||||
addButton (TRANS("OK"), 1);
|
||||
addButton (TRANS("Cancel"), 0);
|
||||
|
||||
// (avoid return + escape keys getting processed by the buttons..)
|
||||
for (auto* child : getChildren())
|
||||
child->setWantsKeyboardFocus (false);
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
grabKeyboardFocus();
|
||||
}
|
||||
|
||||
bool keyPressed (const KeyPress& key) override
|
||||
{
|
||||
lastPress = key;
|
||||
String message (TRANS("Key") + ": " + owner.getDescriptionForKeyPress (key));
|
||||
|
||||
auto previousCommand = owner.getMappings().findCommandForKeyPress (key);
|
||||
|
||||
if (previousCommand != 0)
|
||||
message << "\n\n("
|
||||
<< TRANS("Currently assigned to \"CMDN\"")
|
||||
.replace ("CMDN", TRANS (owner.getCommandManager().getNameOfCommand (previousCommand)))
|
||||
<< ')';
|
||||
|
||||
setMessage (message);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool keyStateChanged (bool) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
KeyPress lastPress;
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow)
|
||||
};
|
||||
|
||||
static void assignNewKeyCallback (int result, ChangeKeyButton* button, KeyPress newKey)
|
||||
{
|
||||
if (result != 0 && button != nullptr)
|
||||
button->setNewKey (newKey, true);
|
||||
}
|
||||
|
||||
void setNewKey (const KeyPress& newKey, bool dontAskUser)
|
||||
{
|
||||
if (newKey.isValid())
|
||||
{
|
||||
auto previousCommand = owner.getMappings().findCommandForKeyPress (newKey);
|
||||
|
||||
if (previousCommand == 0 || dontAskUser)
|
||||
{
|
||||
owner.getMappings().removeKeyPress (newKey);
|
||||
|
||||
if (keyNum >= 0)
|
||||
owner.getMappings().removeKeyPress (commandID, keyNum);
|
||||
|
||||
owner.getMappings().addKeyPress (commandID, newKey, keyNum);
|
||||
}
|
||||
else
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::WarningIcon,
|
||||
TRANS("Change key-mapping"),
|
||||
TRANS("This key is already assigned to the command \"CMDN\"")
|
||||
.replace ("CMDN", owner.getCommandManager().getNameOfCommand (previousCommand))
|
||||
+ "\n\n"
|
||||
+ TRANS("Do you want to re-assign it to this new command instead?"),
|
||||
TRANS("Re-assign"),
|
||||
TRANS("Cancel"),
|
||||
this,
|
||||
ModalCallbackFunction::forComponent (assignNewKeyCallback,
|
||||
this, KeyPress (newKey)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void keyChosen (int result, ChangeKeyButton* button)
|
||||
{
|
||||
if (button != nullptr && button->currentKeyEntryWindow != nullptr)
|
||||
{
|
||||
if (result != 0)
|
||||
{
|
||||
button->currentKeyEntryWindow->setVisible (false);
|
||||
button->setNewKey (button->currentKeyEntryWindow->lastPress, false);
|
||||
}
|
||||
|
||||
button->currentKeyEntryWindow.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void assignNewKey()
|
||||
{
|
||||
currentKeyEntryWindow.reset (new KeyEntryWindow (owner));
|
||||
currentKeyEntryWindow->enterModalState (true, ModalCallbackFunction::forComponent (keyChosen, this));
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
const CommandID commandID;
|
||||
const int keyNum;
|
||||
std::unique_ptr<KeyEntryWindow> currentKeyEntryWindow;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::ItemComponent : public Component
|
||||
{
|
||||
public:
|
||||
ItemComponent (KeyMappingEditorComponent& kec, CommandID command)
|
||||
: owner (kec), commandID (command)
|
||||
{
|
||||
setInterceptsMouseClicks (false, true);
|
||||
|
||||
const bool isReadOnly = owner.isCommandReadOnly (commandID);
|
||||
|
||||
auto keyPresses = owner.getMappings().getKeyPressesAssignedToCommand (commandID);
|
||||
|
||||
for (int i = 0; i < jmin ((int) maxNumAssignments, keyPresses.size()); ++i)
|
||||
addKeyPressButton (owner.getDescriptionForKeyPress (keyPresses.getReference (i)), i, isReadOnly);
|
||||
|
||||
addKeyPressButton ("Change Key Mapping", -1, isReadOnly);
|
||||
}
|
||||
|
||||
void addKeyPressButton (const String& desc, const int index, const bool isReadOnly)
|
||||
{
|
||||
auto* b = new ChangeKeyButton (owner, commandID, desc, index);
|
||||
keyChangeButtons.add (b);
|
||||
|
||||
b->setEnabled (! isReadOnly);
|
||||
b->setVisible (keyChangeButtons.size() <= (int) maxNumAssignments);
|
||||
addChildComponent (b);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.setFont ((float) getHeight() * 0.7f);
|
||||
g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
|
||||
|
||||
g.drawFittedText (TRANS (owner.getCommandManager().getNameOfCommand (commandID)),
|
||||
4, 0, jmax (40, getChildComponent (0)->getX() - 5), getHeight(),
|
||||
Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
int x = getWidth() - 4;
|
||||
|
||||
for (int i = keyChangeButtons.size(); --i >= 0;)
|
||||
{
|
||||
auto* b = keyChangeButtons.getUnchecked(i);
|
||||
|
||||
b->fitToContent (getHeight() - 2);
|
||||
b->setTopRightPosition (x, 1);
|
||||
x = b->getX() - 5;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return createIgnoredAccessibilityHandler (*this);
|
||||
}
|
||||
|
||||
KeyMappingEditorComponent& owner;
|
||||
OwnedArray<ChangeKeyButton> keyChangeButtons;
|
||||
const CommandID commandID;
|
||||
|
||||
enum { maxNumAssignments = 3 };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::MappingItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
MappingItem (KeyMappingEditorComponent& kec, CommandID command)
|
||||
: owner (kec), commandID (command)
|
||||
{}
|
||||
|
||||
String getUniqueName() const override { return String ((int) commandID) + "_id"; }
|
||||
bool mightContainSubItems() override { return false; }
|
||||
int getItemHeight() const override { return 20; }
|
||||
std::unique_ptr<Component> createItemComponent() override { return std::make_unique<ItemComponent> (owner, commandID); }
|
||||
String getAccessibilityName() override { return TRANS (owner.getCommandManager().getNameOfCommand (commandID)); }
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
const CommandID commandID;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MappingItem)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::CategoryItem : public TreeViewItem
|
||||
{
|
||||
public:
|
||||
CategoryItem (KeyMappingEditorComponent& kec, const String& name)
|
||||
: owner (kec), categoryName (name)
|
||||
{}
|
||||
|
||||
String getUniqueName() const override { return categoryName + "_cat"; }
|
||||
bool mightContainSubItems() override { return true; }
|
||||
int getItemHeight() const override { return 22; }
|
||||
String getAccessibilityName() override { return categoryName; }
|
||||
|
||||
void paintItem (Graphics& g, int width, int height) override
|
||||
{
|
||||
g.setFont (Font ((float) height * 0.7f, Font::bold));
|
||||
g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
|
||||
|
||||
g.drawText (TRANS (categoryName), 2, 0, width - 2, height, Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
void itemOpennessChanged (bool isNowOpen) override
|
||||
{
|
||||
if (isNowOpen)
|
||||
{
|
||||
if (getNumSubItems() == 0)
|
||||
for (auto command : owner.getCommandManager().getCommandsInCategory (categoryName))
|
||||
if (owner.shouldCommandBeIncluded (command))
|
||||
addSubItem (new MappingItem (owner, command));
|
||||
}
|
||||
else
|
||||
{
|
||||
clearSubItems();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
String categoryName;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CategoryItem)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class KeyMappingEditorComponent::TopLevelItem : public TreeViewItem,
|
||||
private ChangeListener
|
||||
{
|
||||
public:
|
||||
TopLevelItem (KeyMappingEditorComponent& kec) : owner (kec)
|
||||
{
|
||||
setLinesDrawnForSubItems (false);
|
||||
owner.getMappings().addChangeListener (this);
|
||||
}
|
||||
|
||||
~TopLevelItem() override
|
||||
{
|
||||
owner.getMappings().removeChangeListener (this);
|
||||
}
|
||||
|
||||
bool mightContainSubItems() override { return true; }
|
||||
String getUniqueName() const override { return "keys"; }
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster*) override
|
||||
{
|
||||
const OpennessRestorer opennessRestorer (*this);
|
||||
clearSubItems();
|
||||
|
||||
for (auto category : owner.getCommandManager().getCommandCategories())
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (auto command : owner.getCommandManager().getCommandsInCategory (category))
|
||||
if (owner.shouldCommandBeIncluded (command))
|
||||
++count;
|
||||
|
||||
if (count > 0)
|
||||
addSubItem (new CategoryItem (owner, category));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
KeyMappingEditorComponent& owner;
|
||||
};
|
||||
|
||||
static void resetKeyMappingsToDefaultsCallback (int result, KeyMappingEditorComponent* owner)
|
||||
{
|
||||
if (result != 0 && owner != nullptr)
|
||||
owner->getMappings().resetToDefaultMappings();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappingManager,
|
||||
const bool showResetToDefaultButton)
|
||||
: mappings (mappingManager),
|
||||
resetButton (TRANS ("reset to defaults"))
|
||||
{
|
||||
treeItem.reset (new TopLevelItem (*this));
|
||||
|
||||
if (showResetToDefaultButton)
|
||||
{
|
||||
addAndMakeVisible (resetButton);
|
||||
|
||||
resetButton.onClick = [this]
|
||||
{
|
||||
AlertWindow::showOkCancelBox (MessageBoxIconType::QuestionIcon,
|
||||
TRANS("Reset to defaults"),
|
||||
TRANS("Are you sure you want to reset all the key-mappings to their default state?"),
|
||||
TRANS("Reset"),
|
||||
{}, this,
|
||||
ModalCallbackFunction::forComponent (resetKeyMappingsToDefaultsCallback, this));
|
||||
};
|
||||
}
|
||||
|
||||
addAndMakeVisible (tree);
|
||||
tree.setTitle ("Key Mappings");
|
||||
tree.setColour (TreeView::backgroundColourId, findColour (backgroundColourId));
|
||||
tree.setRootItemVisible (false);
|
||||
tree.setDefaultOpenness (true);
|
||||
tree.setRootItem (treeItem.get());
|
||||
tree.setIndentSize (12);
|
||||
}
|
||||
|
||||
KeyMappingEditorComponent::~KeyMappingEditorComponent()
|
||||
{
|
||||
tree.setRootItem (nullptr);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void KeyMappingEditorComponent::setColours (Colour mainBackground,
|
||||
Colour textColour)
|
||||
{
|
||||
setColour (backgroundColourId, mainBackground);
|
||||
setColour (textColourId, textColour);
|
||||
tree.setColour (TreeView::backgroundColourId, mainBackground);
|
||||
}
|
||||
|
||||
void KeyMappingEditorComponent::parentHierarchyChanged()
|
||||
{
|
||||
treeItem->changeListenerCallback (nullptr);
|
||||
}
|
||||
|
||||
void KeyMappingEditorComponent::resized()
|
||||
{
|
||||
int h = getHeight();
|
||||
|
||||
if (resetButton.isVisible())
|
||||
{
|
||||
const int buttonHeight = 20;
|
||||
h -= buttonHeight + 8;
|
||||
int x = getWidth() - 8;
|
||||
|
||||
resetButton.changeWidthToFitText (buttonHeight);
|
||||
resetButton.setTopRightPosition (x, h + 6);
|
||||
}
|
||||
|
||||
tree.setBounds (0, 0, getWidth(), h);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyMappingEditorComponent::shouldCommandBeIncluded (const CommandID commandID)
|
||||
{
|
||||
auto* ci = mappings.getCommandManager().getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr && (ci->flags & ApplicationCommandInfo::hiddenFromKeyEditor) == 0;
|
||||
}
|
||||
|
||||
bool KeyMappingEditorComponent::isCommandReadOnly (const CommandID commandID)
|
||||
{
|
||||
auto* ci = mappings.getCommandManager().getCommandForID (commandID);
|
||||
|
||||
return ci != nullptr && (ci->flags & ApplicationCommandInfo::readOnlyInKeyEditor) != 0;
|
||||
}
|
||||
|
||||
String KeyMappingEditorComponent::getDescriptionForKeyPress (const KeyPress& key)
|
||||
{
|
||||
return key.getTextDescription();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,132 +1,132 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component to allow editing of the keymaps stored by a KeyPressMappingSet
|
||||
object.
|
||||
|
||||
@see KeyPressMappingSet
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API KeyMappingEditorComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a KeyMappingEditorComponent.
|
||||
|
||||
@param mappingSet this is the set of mappings to display and edit. Make sure the
|
||||
mappings object is not deleted before this component!
|
||||
@param showResetToDefaultButton if true, then at the bottom of the list, the
|
||||
component will include a 'reset to defaults' button.
|
||||
*/
|
||||
KeyMappingEditorComponent (KeyPressMappingSet& mappingSet,
|
||||
bool showResetToDefaultButton);
|
||||
|
||||
/** Destructor. */
|
||||
~KeyMappingEditorComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets up the colours to use for parts of the component.
|
||||
|
||||
@param mainBackground colour to use for most of the background
|
||||
@param textColour colour to use for the text
|
||||
*/
|
||||
void setColours (Colour mainBackground,
|
||||
Colour textColour);
|
||||
|
||||
/** Returns the KeyPressMappingSet that this component is acting upon. */
|
||||
KeyPressMappingSet& getMappings() const noexcept { return mappings; }
|
||||
|
||||
/** Returns the ApplicationCommandManager that this component is connected to. */
|
||||
ApplicationCommandManager& getCommandManager() const noexcept { return mappings.getCommandManager(); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Can be overridden if some commands need to be excluded from the list.
|
||||
|
||||
By default this will use the KeyPressMappingSet's shouldCommandBeVisibleInEditor()
|
||||
method to decide what to return, but you can override it to handle special cases.
|
||||
*/
|
||||
virtual bool shouldCommandBeIncluded (CommandID commandID);
|
||||
|
||||
/** Can be overridden to indicate that some commands are shown as read-only.
|
||||
|
||||
By default this will use the KeyPressMappingSet's shouldCommandBeReadOnlyInEditor()
|
||||
method to decide what to return, but you can override it to handle special cases.
|
||||
*/
|
||||
virtual bool isCommandReadOnly (CommandID commandID);
|
||||
|
||||
/** This can be overridden to let you change the format of the string used
|
||||
to describe a keypress.
|
||||
|
||||
This is handy if you're using non-standard KeyPress objects, e.g. for custom
|
||||
keys that are triggered by something else externally. If you override the
|
||||
method, be sure to let the base class's method handle keys you're not
|
||||
interested in.
|
||||
*/
|
||||
virtual String getDescriptionForKeyPress (const KeyPress& key);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the editor.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x100ad00, /**< The background colour to fill the editor background. */
|
||||
textColourId = 0x100ad01, /**< The colour for the text. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
KeyPressMappingSet& mappings;
|
||||
TreeView tree;
|
||||
TextButton resetButton;
|
||||
|
||||
class TopLevelItem;
|
||||
class ChangeKeyButton;
|
||||
class MappingItem;
|
||||
class CategoryItem;
|
||||
class ItemComponent;
|
||||
std::unique_ptr<TopLevelItem> treeItem;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingEditorComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component to allow editing of the keymaps stored by a KeyPressMappingSet
|
||||
object.
|
||||
|
||||
@see KeyPressMappingSet
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API KeyMappingEditorComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a KeyMappingEditorComponent.
|
||||
|
||||
@param mappingSet this is the set of mappings to display and edit. Make sure the
|
||||
mappings object is not deleted before this component!
|
||||
@param showResetToDefaultButton if true, then at the bottom of the list, the
|
||||
component will include a 'reset to defaults' button.
|
||||
*/
|
||||
KeyMappingEditorComponent (KeyPressMappingSet& mappingSet,
|
||||
bool showResetToDefaultButton);
|
||||
|
||||
/** Destructor. */
|
||||
~KeyMappingEditorComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets up the colours to use for parts of the component.
|
||||
|
||||
@param mainBackground colour to use for most of the background
|
||||
@param textColour colour to use for the text
|
||||
*/
|
||||
void setColours (Colour mainBackground,
|
||||
Colour textColour);
|
||||
|
||||
/** Returns the KeyPressMappingSet that this component is acting upon. */
|
||||
KeyPressMappingSet& getMappings() const noexcept { return mappings; }
|
||||
|
||||
/** Returns the ApplicationCommandManager that this component is connected to. */
|
||||
ApplicationCommandManager& getCommandManager() const noexcept { return mappings.getCommandManager(); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Can be overridden if some commands need to be excluded from the list.
|
||||
|
||||
By default this will use the KeyPressMappingSet's shouldCommandBeVisibleInEditor()
|
||||
method to decide what to return, but you can override it to handle special cases.
|
||||
*/
|
||||
virtual bool shouldCommandBeIncluded (CommandID commandID);
|
||||
|
||||
/** Can be overridden to indicate that some commands are shown as read-only.
|
||||
|
||||
By default this will use the KeyPressMappingSet's shouldCommandBeReadOnlyInEditor()
|
||||
method to decide what to return, but you can override it to handle special cases.
|
||||
*/
|
||||
virtual bool isCommandReadOnly (CommandID commandID);
|
||||
|
||||
/** This can be overridden to let you change the format of the string used
|
||||
to describe a keypress.
|
||||
|
||||
This is handy if you're using non-standard KeyPress objects, e.g. for custom
|
||||
keys that are triggered by something else externally. If you override the
|
||||
method, be sure to let the base class's method handle keys you're not
|
||||
interested in.
|
||||
*/
|
||||
virtual String getDescriptionForKeyPress (const KeyPress& key);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the editor.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x100ad00, /**< The background colour to fill the editor background. */
|
||||
textColourId = 0x100ad01, /**< The colour for the text. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
KeyPressMappingSet& mappings;
|
||||
TreeView tree;
|
||||
TextButton resetButton;
|
||||
|
||||
class TopLevelItem;
|
||||
class ChangeKeyButton;
|
||||
class MappingItem;
|
||||
class CategoryItem;
|
||||
class ItemComponent;
|
||||
std::unique_ptr<TopLevelItem> treeItem;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyMappingEditorComponent)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,497 +1,497 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR
|
||||
|
||||
namespace LiveConstantEditor
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class AllComponentRepainter : private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
AllComponentRepainter() {}
|
||||
~AllComponentRepainter() override { clearSingletonInstance(); }
|
||||
|
||||
JUCE_DECLARE_SINGLETON (AllComponentRepainter, false)
|
||||
|
||||
void trigger()
|
||||
{
|
||||
if (! isTimerRunning())
|
||||
startTimer (100);
|
||||
}
|
||||
|
||||
private:
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
Array<Component*> alreadyDone;
|
||||
|
||||
for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;)
|
||||
if (auto* c = TopLevelWindow::getTopLevelWindow(i))
|
||||
repaintAndResizeAllComps (c, alreadyDone);
|
||||
|
||||
auto& desktop = Desktop::getInstance();
|
||||
|
||||
for (int i = desktop.getNumComponents(); --i >= 0;)
|
||||
if (auto* c = desktop.getComponent(i))
|
||||
repaintAndResizeAllComps (c, alreadyDone);
|
||||
}
|
||||
|
||||
static void repaintAndResizeAllComps (Component::SafePointer<Component> c,
|
||||
Array<Component*>& alreadyDone)
|
||||
{
|
||||
if (c->isVisible() && ! alreadyDone.contains (c))
|
||||
{
|
||||
c->repaint();
|
||||
c->resized();
|
||||
|
||||
for (int i = c->getNumChildComponents(); --i >= 0;)
|
||||
{
|
||||
if (auto* child = c->getChildComponent(i))
|
||||
{
|
||||
repaintAndResizeAllComps (child, alreadyDone);
|
||||
alreadyDone.add (child);
|
||||
}
|
||||
|
||||
if (c == nullptr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (AllComponentRepainter)
|
||||
JUCE_IMPLEMENT_SINGLETON (ValueList)
|
||||
|
||||
//==============================================================================
|
||||
int64 parseInt (String s)
|
||||
{
|
||||
s = s.trimStart();
|
||||
|
||||
if (s.startsWithChar ('-'))
|
||||
return -parseInt (s.substring (1));
|
||||
|
||||
if (s.startsWith ("0x"))
|
||||
return s.substring(2).getHexValue64();
|
||||
|
||||
return s.getLargeIntValue();
|
||||
}
|
||||
|
||||
double parseDouble (const String& s)
|
||||
{
|
||||
return s.retainCharacters ("0123456789.eE-").getDoubleValue();
|
||||
}
|
||||
|
||||
String intToString (int v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); }
|
||||
String intToString (int64 v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); }
|
||||
|
||||
//==============================================================================
|
||||
LiveValueBase::LiveValueBase (const char* file, int line)
|
||||
: sourceFile (file), sourceLine (line)
|
||||
{
|
||||
name = File (sourceFile).getFileName() + " : " + String (sourceLine);
|
||||
}
|
||||
|
||||
LiveValueBase::~LiveValueBase()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LivePropertyEditorBase::LivePropertyEditorBase (LiveValueBase& v, CodeDocument& d)
|
||||
: value (v), document (d), sourceEditor (document, &tokeniser)
|
||||
{
|
||||
setSize (600, 100);
|
||||
|
||||
addAndMakeVisible (name);
|
||||
addAndMakeVisible (resetButton);
|
||||
addAndMakeVisible (valueEditor);
|
||||
addAndMakeVisible (sourceEditor);
|
||||
|
||||
findOriginalValueInCode();
|
||||
selectOriginalValue();
|
||||
|
||||
name.setFont (13.0f);
|
||||
name.setText (v.name, dontSendNotification);
|
||||
valueEditor.setMultiLine (v.isString());
|
||||
valueEditor.setReturnKeyStartsNewLine (v.isString());
|
||||
valueEditor.setText (v.getStringValue (wasHex), dontSendNotification);
|
||||
valueEditor.onTextChange = [this] { applyNewValue (valueEditor.getText()); };
|
||||
sourceEditor.setReadOnly (true);
|
||||
sourceEditor.setFont (sourceEditor.getFont().withHeight (13.0f));
|
||||
resetButton.onClick = [this] { applyNewValue (value.getOriginalStringValue (wasHex)); };
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::white);
|
||||
g.fillRect (getLocalBounds().removeFromBottom (1));
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::resized()
|
||||
{
|
||||
auto r = getLocalBounds().reduced (0, 3).withTrimmedBottom (1);
|
||||
|
||||
auto left = r.removeFromLeft (jmax (200, r.getWidth() / 3));
|
||||
|
||||
auto top = left.removeFromTop (25);
|
||||
resetButton.setBounds (top.removeFromRight (35).reduced (0, 3));
|
||||
name.setBounds (top);
|
||||
|
||||
if (customComp != nullptr)
|
||||
{
|
||||
valueEditor.setBounds (left.removeFromTop (25));
|
||||
left.removeFromTop (2);
|
||||
customComp->setBounds (left);
|
||||
}
|
||||
else
|
||||
{
|
||||
valueEditor.setBounds (left);
|
||||
}
|
||||
|
||||
r.removeFromLeft (4);
|
||||
sourceEditor.setBounds (r);
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::applyNewValue (const String& s)
|
||||
{
|
||||
value.setStringValue (s);
|
||||
|
||||
document.replaceSection (valueStart.getPosition(), valueEnd.getPosition(), value.getCodeValue (wasHex));
|
||||
document.clearUndoHistory();
|
||||
selectOriginalValue();
|
||||
|
||||
valueEditor.setText (s, dontSendNotification);
|
||||
AllComponentRepainter::getInstance()->trigger();
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::selectOriginalValue()
|
||||
{
|
||||
sourceEditor.selectRegion (valueStart, valueEnd);
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::findOriginalValueInCode()
|
||||
{
|
||||
CodeDocument::Position pos (document, value.sourceLine, 0);
|
||||
auto line = pos.getLineText();
|
||||
auto p = line.getCharPointer();
|
||||
|
||||
p = CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT"));
|
||||
|
||||
if (p.isEmpty())
|
||||
{
|
||||
// Not sure how this would happen - some kind of mix-up between source code and line numbers..
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
p += (int) (sizeof ("JUCE_LIVE_CONSTANT") - 1);
|
||||
p.incrementToEndOfWhitespace();
|
||||
|
||||
if (! CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")).isEmpty())
|
||||
{
|
||||
// Aargh! You've added two JUCE_LIVE_CONSTANT macros on the same line!
|
||||
// They're identified by their line number, so you must make sure each
|
||||
// one goes on a separate line!
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
if (p.getAndAdvance() == '(')
|
||||
{
|
||||
auto start = p, end = p;
|
||||
|
||||
int depth = 1;
|
||||
|
||||
while (! end.isEmpty())
|
||||
{
|
||||
auto c = end.getAndAdvance();
|
||||
|
||||
if (c == '(') ++depth;
|
||||
if (c == ')') --depth;
|
||||
|
||||
if (depth == 0)
|
||||
{
|
||||
--end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (end > start)
|
||||
{
|
||||
valueStart = CodeDocument::Position (document, value.sourceLine, (int) (start - line.getCharPointer()));
|
||||
valueEnd = CodeDocument::Position (document, value.sourceLine, (int) (end - line.getCharPointer()));
|
||||
|
||||
valueStart.setPositionMaintained (true);
|
||||
valueEnd.setPositionMaintained (true);
|
||||
|
||||
wasHex = String (start, end).containsIgnoreCase ("0x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ValueListHolderComponent : public Component
|
||||
{
|
||||
public:
|
||||
ValueListHolderComponent (ValueList& l) : valueList (l)
|
||||
{
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
void addItem (int width, LiveValueBase& v, CodeDocument& doc)
|
||||
{
|
||||
addAndMakeVisible (editors.add (v.createPropertyComponent (doc)));
|
||||
layout (width);
|
||||
}
|
||||
|
||||
void layout (int width)
|
||||
{
|
||||
setSize (width, editors.size() * itemHeight);
|
||||
resized();
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds().reduced (2, 0);
|
||||
|
||||
for (int i = 0; i < editors.size(); ++i)
|
||||
editors.getUnchecked(i)->setBounds (r.removeFromTop (itemHeight));
|
||||
}
|
||||
|
||||
enum { itemHeight = 120 };
|
||||
|
||||
ValueList& valueList;
|
||||
OwnedArray<LivePropertyEditorBase> editors;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ValueList::EditorWindow : public DocumentWindow,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
EditorWindow (ValueList& list)
|
||||
: DocumentWindow ("Live Values", Colours::lightgrey, DocumentWindow::closeButton)
|
||||
{
|
||||
setLookAndFeel (&lookAndFeel);
|
||||
setUsingNativeTitleBar (true);
|
||||
|
||||
viewport.setViewedComponent (new ValueListHolderComponent (list), true);
|
||||
viewport.setSize (700, 600);
|
||||
viewport.setScrollBarsShown (true, false);
|
||||
|
||||
setContentNonOwned (&viewport, true);
|
||||
setResizable (true, false);
|
||||
setResizeLimits (500, 400, 10000, 10000);
|
||||
centreWithSize (getWidth(), getHeight());
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
~EditorWindow() override
|
||||
{
|
||||
setLookAndFeel (nullptr);
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
setVisible (false);
|
||||
}
|
||||
|
||||
void updateItems (ValueList& list)
|
||||
{
|
||||
if (auto* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
|
||||
{
|
||||
while (l->getNumChildComponents() < list.values.size())
|
||||
{
|
||||
if (auto* v = list.values [l->getNumChildComponents()])
|
||||
l->addItem (viewport.getMaximumVisibleWidth(), *v, list.getDocument (v->sourceFile));
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
setVisible (true);
|
||||
}
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
DocumentWindow::resized();
|
||||
|
||||
if (auto* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
|
||||
l->layout (viewport.getMaximumVisibleWidth());
|
||||
}
|
||||
|
||||
Viewport viewport;
|
||||
LookAndFeel_V3 lookAndFeel;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ValueList::ValueList() {}
|
||||
ValueList::~ValueList() { clearSingletonInstance(); }
|
||||
|
||||
void ValueList::addValue (LiveValueBase* v)
|
||||
{
|
||||
values.add (v);
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void ValueList::handleAsyncUpdate()
|
||||
{
|
||||
if (editorWindow == nullptr)
|
||||
editorWindow = new EditorWindow (*this);
|
||||
|
||||
editorWindow->updateItems (*this);
|
||||
}
|
||||
|
||||
CodeDocument& ValueList::getDocument (const File& file)
|
||||
{
|
||||
const int index = documentFiles.indexOf (file.getFullPathName());
|
||||
|
||||
if (index >= 0)
|
||||
return *documents.getUnchecked (index);
|
||||
|
||||
auto* doc = documents.add (new CodeDocument());
|
||||
documentFiles.add (file);
|
||||
doc->replaceAllContent (file.loadFileAsString());
|
||||
doc->clearUndoHistory();
|
||||
return *doc;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct ColourEditorComp : public Component,
|
||||
private ChangeListener
|
||||
{
|
||||
ColourEditorComp (LivePropertyEditorBase& e) : editor (e)
|
||||
{
|
||||
setMouseCursor (MouseCursor::PointingHandCursor);
|
||||
}
|
||||
|
||||
Colour getColour() const
|
||||
{
|
||||
return Colour ((uint32) parseInt (editor.value.getStringValue (false)));
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillCheckerBoard (getLocalBounds().toFloat(), 6.0f, 6.0f,
|
||||
Colour (0xffdddddd).overlaidWith (getColour()),
|
||||
Colour (0xffffffff).overlaidWith (getColour()));
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent&) override
|
||||
{
|
||||
auto colourSelector = std::make_unique<ColourSelector>();
|
||||
colourSelector->setName ("Colour");
|
||||
colourSelector->setCurrentColour (getColour());
|
||||
colourSelector->addChangeListener (this);
|
||||
colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
|
||||
colourSelector->setSize (300, 400);
|
||||
|
||||
CallOutBox::launchAsynchronously (std::move (colourSelector), getScreenBounds(), nullptr);
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster* source) override
|
||||
{
|
||||
if (auto* cs = dynamic_cast<ColourSelector*> (source))
|
||||
editor.applyNewValue (getAsString (cs->getCurrentColour(), true));
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
LivePropertyEditorBase& editor;
|
||||
};
|
||||
|
||||
Component* createColourEditor (LivePropertyEditorBase& editor)
|
||||
{
|
||||
return new ColourEditorComp (editor);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct SliderComp : public Component
|
||||
{
|
||||
SliderComp (LivePropertyEditorBase& e, bool useFloat)
|
||||
: editor (e), isFloat (useFloat)
|
||||
{
|
||||
slider.setTextBoxStyle (Slider::NoTextBox, true, 0, 0);
|
||||
addAndMakeVisible (slider);
|
||||
updateRange();
|
||||
slider.onDragEnd = [this] { updateRange(); };
|
||||
slider.onValueChange = [this]
|
||||
{
|
||||
editor.applyNewValue (isFloat ? getAsString ((double) slider.getValue(), editor.wasHex)
|
||||
: getAsString ((int64) slider.getValue(), editor.wasHex));
|
||||
};
|
||||
}
|
||||
|
||||
virtual void updateRange()
|
||||
{
|
||||
double v = isFloat ? parseDouble (editor.value.getStringValue (false))
|
||||
: (double) parseInt (editor.value.getStringValue (false));
|
||||
|
||||
double range = isFloat ? 10 : 100;
|
||||
|
||||
slider.setRange (v - range, v + range);
|
||||
slider.setValue (v, dontSendNotification);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
slider.setBounds (getLocalBounds().removeFromTop (25));
|
||||
}
|
||||
|
||||
LivePropertyEditorBase& editor;
|
||||
Slider slider;
|
||||
bool isFloat;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct BoolSliderComp : public SliderComp
|
||||
{
|
||||
BoolSliderComp (LivePropertyEditorBase& e)
|
||||
: SliderComp (e, false)
|
||||
{
|
||||
slider.onValueChange = [this] { editor.applyNewValue (slider.getValue() > 0.5 ? "true" : "false"); };
|
||||
}
|
||||
|
||||
void updateRange() override
|
||||
{
|
||||
slider.setRange (0.0, 1.0, dontSendNotification);
|
||||
slider.setValue (editor.value.getStringValue (false) == "true", dontSendNotification);
|
||||
}
|
||||
};
|
||||
|
||||
Component* createIntegerSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, false); }
|
||||
Component* createFloatSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, true); }
|
||||
Component* createBoolSlider (LivePropertyEditorBase& editor) { return new BoolSliderComp (editor); }
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR
|
||||
|
||||
namespace LiveConstantEditor
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class AllComponentRepainter : private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
AllComponentRepainter() {}
|
||||
~AllComponentRepainter() override { clearSingletonInstance(); }
|
||||
|
||||
JUCE_DECLARE_SINGLETON (AllComponentRepainter, false)
|
||||
|
||||
void trigger()
|
||||
{
|
||||
if (! isTimerRunning())
|
||||
startTimer (100);
|
||||
}
|
||||
|
||||
private:
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
Array<Component*> alreadyDone;
|
||||
|
||||
for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;)
|
||||
if (auto* c = TopLevelWindow::getTopLevelWindow(i))
|
||||
repaintAndResizeAllComps (c, alreadyDone);
|
||||
|
||||
auto& desktop = Desktop::getInstance();
|
||||
|
||||
for (int i = desktop.getNumComponents(); --i >= 0;)
|
||||
if (auto* c = desktop.getComponent(i))
|
||||
repaintAndResizeAllComps (c, alreadyDone);
|
||||
}
|
||||
|
||||
static void repaintAndResizeAllComps (Component::SafePointer<Component> c,
|
||||
Array<Component*>& alreadyDone)
|
||||
{
|
||||
if (c->isVisible() && ! alreadyDone.contains (c))
|
||||
{
|
||||
c->repaint();
|
||||
c->resized();
|
||||
|
||||
for (int i = c->getNumChildComponents(); --i >= 0;)
|
||||
{
|
||||
if (auto* child = c->getChildComponent(i))
|
||||
{
|
||||
repaintAndResizeAllComps (child, alreadyDone);
|
||||
alreadyDone.add (child);
|
||||
}
|
||||
|
||||
if (c == nullptr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (AllComponentRepainter)
|
||||
JUCE_IMPLEMENT_SINGLETON (ValueList)
|
||||
|
||||
//==============================================================================
|
||||
int64 parseInt (String s)
|
||||
{
|
||||
s = s.trimStart();
|
||||
|
||||
if (s.startsWithChar ('-'))
|
||||
return -parseInt (s.substring (1));
|
||||
|
||||
if (s.startsWith ("0x"))
|
||||
return s.substring(2).getHexValue64();
|
||||
|
||||
return s.getLargeIntValue();
|
||||
}
|
||||
|
||||
double parseDouble (const String& s)
|
||||
{
|
||||
return s.retainCharacters ("0123456789.eE-").getDoubleValue();
|
||||
}
|
||||
|
||||
String intToString (int v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); }
|
||||
String intToString (int64 v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); }
|
||||
|
||||
//==============================================================================
|
||||
LiveValueBase::LiveValueBase (const char* file, int line)
|
||||
: sourceFile (file), sourceLine (line)
|
||||
{
|
||||
name = File (sourceFile).getFileName() + " : " + String (sourceLine);
|
||||
}
|
||||
|
||||
LiveValueBase::~LiveValueBase()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LivePropertyEditorBase::LivePropertyEditorBase (LiveValueBase& v, CodeDocument& d)
|
||||
: value (v), document (d), sourceEditor (document, &tokeniser)
|
||||
{
|
||||
setSize (600, 100);
|
||||
|
||||
addAndMakeVisible (name);
|
||||
addAndMakeVisible (resetButton);
|
||||
addAndMakeVisible (valueEditor);
|
||||
addAndMakeVisible (sourceEditor);
|
||||
|
||||
findOriginalValueInCode();
|
||||
selectOriginalValue();
|
||||
|
||||
name.setFont (13.0f);
|
||||
name.setText (v.name, dontSendNotification);
|
||||
valueEditor.setMultiLine (v.isString());
|
||||
valueEditor.setReturnKeyStartsNewLine (v.isString());
|
||||
valueEditor.setText (v.getStringValue (wasHex), dontSendNotification);
|
||||
valueEditor.onTextChange = [this] { applyNewValue (valueEditor.getText()); };
|
||||
sourceEditor.setReadOnly (true);
|
||||
sourceEditor.setFont (sourceEditor.getFont().withHeight (13.0f));
|
||||
resetButton.onClick = [this] { applyNewValue (value.getOriginalStringValue (wasHex)); };
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::white);
|
||||
g.fillRect (getLocalBounds().removeFromBottom (1));
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::resized()
|
||||
{
|
||||
auto r = getLocalBounds().reduced (0, 3).withTrimmedBottom (1);
|
||||
|
||||
auto left = r.removeFromLeft (jmax (200, r.getWidth() / 3));
|
||||
|
||||
auto top = left.removeFromTop (25);
|
||||
resetButton.setBounds (top.removeFromRight (35).reduced (0, 3));
|
||||
name.setBounds (top);
|
||||
|
||||
if (customComp != nullptr)
|
||||
{
|
||||
valueEditor.setBounds (left.removeFromTop (25));
|
||||
left.removeFromTop (2);
|
||||
customComp->setBounds (left);
|
||||
}
|
||||
else
|
||||
{
|
||||
valueEditor.setBounds (left);
|
||||
}
|
||||
|
||||
r.removeFromLeft (4);
|
||||
sourceEditor.setBounds (r);
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::applyNewValue (const String& s)
|
||||
{
|
||||
value.setStringValue (s);
|
||||
|
||||
document.replaceSection (valueStart.getPosition(), valueEnd.getPosition(), value.getCodeValue (wasHex));
|
||||
document.clearUndoHistory();
|
||||
selectOriginalValue();
|
||||
|
||||
valueEditor.setText (s, dontSendNotification);
|
||||
AllComponentRepainter::getInstance()->trigger();
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::selectOriginalValue()
|
||||
{
|
||||
sourceEditor.selectRegion (valueStart, valueEnd);
|
||||
}
|
||||
|
||||
void LivePropertyEditorBase::findOriginalValueInCode()
|
||||
{
|
||||
CodeDocument::Position pos (document, value.sourceLine, 0);
|
||||
auto line = pos.getLineText();
|
||||
auto p = line.getCharPointer();
|
||||
|
||||
p = CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT"));
|
||||
|
||||
if (p.isEmpty())
|
||||
{
|
||||
// Not sure how this would happen - some kind of mix-up between source code and line numbers..
|
||||
jassertfalse;
|
||||
return;
|
||||
}
|
||||
|
||||
p += (int) (sizeof ("JUCE_LIVE_CONSTANT") - 1);
|
||||
p.incrementToEndOfWhitespace();
|
||||
|
||||
if (! CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")).isEmpty())
|
||||
{
|
||||
// Aargh! You've added two JUCE_LIVE_CONSTANT macros on the same line!
|
||||
// They're identified by their line number, so you must make sure each
|
||||
// one goes on a separate line!
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
if (p.getAndAdvance() == '(')
|
||||
{
|
||||
auto start = p, end = p;
|
||||
|
||||
int depth = 1;
|
||||
|
||||
while (! end.isEmpty())
|
||||
{
|
||||
auto c = end.getAndAdvance();
|
||||
|
||||
if (c == '(') ++depth;
|
||||
if (c == ')') --depth;
|
||||
|
||||
if (depth == 0)
|
||||
{
|
||||
--end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (end > start)
|
||||
{
|
||||
valueStart = CodeDocument::Position (document, value.sourceLine, (int) (start - line.getCharPointer()));
|
||||
valueEnd = CodeDocument::Position (document, value.sourceLine, (int) (end - line.getCharPointer()));
|
||||
|
||||
valueStart.setPositionMaintained (true);
|
||||
valueEnd.setPositionMaintained (true);
|
||||
|
||||
wasHex = String (start, end).containsIgnoreCase ("0x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ValueListHolderComponent : public Component
|
||||
{
|
||||
public:
|
||||
ValueListHolderComponent (ValueList& l) : valueList (l)
|
||||
{
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
void addItem (int width, LiveValueBase& v, CodeDocument& doc)
|
||||
{
|
||||
addAndMakeVisible (editors.add (v.createPropertyComponent (doc)));
|
||||
layout (width);
|
||||
}
|
||||
|
||||
void layout (int width)
|
||||
{
|
||||
setSize (width, editors.size() * itemHeight);
|
||||
resized();
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto r = getLocalBounds().reduced (2, 0);
|
||||
|
||||
for (int i = 0; i < editors.size(); ++i)
|
||||
editors.getUnchecked(i)->setBounds (r.removeFromTop (itemHeight));
|
||||
}
|
||||
|
||||
enum { itemHeight = 120 };
|
||||
|
||||
ValueList& valueList;
|
||||
OwnedArray<LivePropertyEditorBase> editors;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class ValueList::EditorWindow : public DocumentWindow,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
EditorWindow (ValueList& list)
|
||||
: DocumentWindow ("Live Values", Colours::lightgrey, DocumentWindow::closeButton)
|
||||
{
|
||||
setLookAndFeel (&lookAndFeel);
|
||||
setUsingNativeTitleBar (true);
|
||||
|
||||
viewport.setViewedComponent (new ValueListHolderComponent (list), true);
|
||||
viewport.setSize (700, 600);
|
||||
viewport.setScrollBarsShown (true, false);
|
||||
|
||||
setContentNonOwned (&viewport, true);
|
||||
setResizable (true, false);
|
||||
setResizeLimits (500, 400, 10000, 10000);
|
||||
centreWithSize (getWidth(), getHeight());
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
~EditorWindow() override
|
||||
{
|
||||
setLookAndFeel (nullptr);
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
setVisible (false);
|
||||
}
|
||||
|
||||
void updateItems (ValueList& list)
|
||||
{
|
||||
if (auto* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
|
||||
{
|
||||
while (l->getNumChildComponents() < list.values.size())
|
||||
{
|
||||
if (auto* v = list.values [l->getNumChildComponents()])
|
||||
l->addItem (viewport.getMaximumVisibleWidth(), *v, list.getDocument (v->sourceFile));
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
setVisible (true);
|
||||
}
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
DocumentWindow::resized();
|
||||
|
||||
if (auto* l = dynamic_cast<ValueListHolderComponent*> (viewport.getViewedComponent()))
|
||||
l->layout (viewport.getMaximumVisibleWidth());
|
||||
}
|
||||
|
||||
Viewport viewport;
|
||||
LookAndFeel_V3 lookAndFeel;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ValueList::ValueList() {}
|
||||
ValueList::~ValueList() { clearSingletonInstance(); }
|
||||
|
||||
void ValueList::addValue (LiveValueBase* v)
|
||||
{
|
||||
values.add (v);
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void ValueList::handleAsyncUpdate()
|
||||
{
|
||||
if (editorWindow == nullptr)
|
||||
editorWindow = new EditorWindow (*this);
|
||||
|
||||
editorWindow->updateItems (*this);
|
||||
}
|
||||
|
||||
CodeDocument& ValueList::getDocument (const File& file)
|
||||
{
|
||||
const int index = documentFiles.indexOf (file.getFullPathName());
|
||||
|
||||
if (index >= 0)
|
||||
return *documents.getUnchecked (index);
|
||||
|
||||
auto* doc = documents.add (new CodeDocument());
|
||||
documentFiles.add (file);
|
||||
doc->replaceAllContent (file.loadFileAsString());
|
||||
doc->clearUndoHistory();
|
||||
return *doc;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct ColourEditorComp : public Component,
|
||||
private ChangeListener
|
||||
{
|
||||
ColourEditorComp (LivePropertyEditorBase& e) : editor (e)
|
||||
{
|
||||
setMouseCursor (MouseCursor::PointingHandCursor);
|
||||
}
|
||||
|
||||
Colour getColour() const
|
||||
{
|
||||
return Colour ((uint32) parseInt (editor.value.getStringValue (false)));
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
g.fillCheckerBoard (getLocalBounds().toFloat(), 6.0f, 6.0f,
|
||||
Colour (0xffdddddd).overlaidWith (getColour()),
|
||||
Colour (0xffffffff).overlaidWith (getColour()));
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent&) override
|
||||
{
|
||||
auto colourSelector = std::make_unique<ColourSelector>();
|
||||
colourSelector->setName ("Colour");
|
||||
colourSelector->setCurrentColour (getColour());
|
||||
colourSelector->addChangeListener (this);
|
||||
colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack);
|
||||
colourSelector->setSize (300, 400);
|
||||
|
||||
CallOutBox::launchAsynchronously (std::move (colourSelector), getScreenBounds(), nullptr);
|
||||
}
|
||||
|
||||
void changeListenerCallback (ChangeBroadcaster* source) override
|
||||
{
|
||||
if (auto* cs = dynamic_cast<ColourSelector*> (source))
|
||||
editor.applyNewValue (getAsString (cs->getCurrentColour(), true));
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
LivePropertyEditorBase& editor;
|
||||
};
|
||||
|
||||
Component* createColourEditor (LivePropertyEditorBase& editor)
|
||||
{
|
||||
return new ColourEditorComp (editor);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct SliderComp : public Component
|
||||
{
|
||||
SliderComp (LivePropertyEditorBase& e, bool useFloat)
|
||||
: editor (e), isFloat (useFloat)
|
||||
{
|
||||
slider.setTextBoxStyle (Slider::NoTextBox, true, 0, 0);
|
||||
addAndMakeVisible (slider);
|
||||
updateRange();
|
||||
slider.onDragEnd = [this] { updateRange(); };
|
||||
slider.onValueChange = [this]
|
||||
{
|
||||
editor.applyNewValue (isFloat ? getAsString ((double) slider.getValue(), editor.wasHex)
|
||||
: getAsString ((int64) slider.getValue(), editor.wasHex));
|
||||
};
|
||||
}
|
||||
|
||||
virtual void updateRange()
|
||||
{
|
||||
double v = isFloat ? parseDouble (editor.value.getStringValue (false))
|
||||
: (double) parseInt (editor.value.getStringValue (false));
|
||||
|
||||
double range = isFloat ? 10 : 100;
|
||||
|
||||
slider.setRange (v - range, v + range);
|
||||
slider.setValue (v, dontSendNotification);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
slider.setBounds (getLocalBounds().removeFromTop (25));
|
||||
}
|
||||
|
||||
LivePropertyEditorBase& editor;
|
||||
Slider slider;
|
||||
bool isFloat;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct BoolSliderComp : public SliderComp
|
||||
{
|
||||
BoolSliderComp (LivePropertyEditorBase& e)
|
||||
: SliderComp (e, false)
|
||||
{
|
||||
slider.onValueChange = [this] { editor.applyNewValue (slider.getValue() > 0.5 ? "true" : "false"); };
|
||||
}
|
||||
|
||||
void updateRange() override
|
||||
{
|
||||
slider.setRange (0.0, 1.0, dontSendNotification);
|
||||
slider.setValue (editor.value.getStringValue (false) == "true", dontSendNotification);
|
||||
}
|
||||
};
|
||||
|
||||
Component* createIntegerSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, false); }
|
||||
Component* createFloatSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, true); }
|
||||
Component* createBoolSlider (LivePropertyEditorBase& editor) { return new BoolSliderComp (editor); }
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,305 +1,305 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR && ! defined (DOXYGEN)
|
||||
|
||||
//==============================================================================
|
||||
/** You can safely ignore all the stuff in this namespace - it's a bunch of boilerplate
|
||||
code used to implement the JUCE_LIVE_CONSTANT functionality.
|
||||
*/
|
||||
namespace LiveConstantEditor
|
||||
{
|
||||
int64 parseInt (String);
|
||||
double parseDouble (const String&);
|
||||
String intToString (int, bool preferHex);
|
||||
String intToString (int64, bool preferHex);
|
||||
|
||||
template <typename Type>
|
||||
static void setFromString (Type& v, const String& s) { v = static_cast<Type> (s); }
|
||||
inline void setFromString (char& v, const String& s) { v = (char) parseInt (s); }
|
||||
inline void setFromString (unsigned char& v, const String& s) { v = (unsigned char) parseInt (s); }
|
||||
inline void setFromString (short& v, const String& s) { v = (short) parseInt (s); }
|
||||
inline void setFromString (unsigned short& v, const String& s) { v = (unsigned short) parseInt (s); }
|
||||
inline void setFromString (int& v, const String& s) { v = (int) parseInt (s); }
|
||||
inline void setFromString (unsigned int& v, const String& s) { v = (unsigned int) parseInt (s); }
|
||||
inline void setFromString (long& v, const String& s) { v = (long) parseInt (s); }
|
||||
inline void setFromString (unsigned long& v, const String& s) { v = (unsigned long) parseInt (s); }
|
||||
inline void setFromString (int64& v, const String& s) { v = (int64) parseInt (s); }
|
||||
inline void setFromString (uint64& v, const String& s) { v = (uint64) parseInt (s); }
|
||||
inline void setFromString (double& v, const String& s) { v = parseDouble (s); }
|
||||
inline void setFromString (float& v, const String& s) { v = (float) parseDouble (s); }
|
||||
inline void setFromString (bool& v, const String& s) { v = (s == "true"); }
|
||||
inline void setFromString (String& v, const String& s) { v = s; }
|
||||
inline void setFromString (Colour& v, const String& s) { v = Colour ((uint32) parseInt (s)); }
|
||||
|
||||
template <typename Type>
|
||||
inline String getAsString (const Type& v, bool) { return String (v); }
|
||||
inline String getAsString (char v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (unsigned char v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (short v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (unsigned short v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (int v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (unsigned int v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (bool v, bool) { return v ? "true" : "false"; }
|
||||
inline String getAsString (int64 v, bool preferHex) { return intToString ((int64) v, preferHex); }
|
||||
inline String getAsString (uint64 v, bool preferHex) { return intToString ((int64) v, preferHex); }
|
||||
inline String getAsString (Colour v, bool) { return intToString ((int) v.getARGB(), true); }
|
||||
|
||||
template <typename Type> struct isStringType { enum { value = 0 }; };
|
||||
template <> struct isStringType<String> { enum { value = 1 }; };
|
||||
|
||||
template <typename Type>
|
||||
inline String getAsCode (Type& v, bool preferHex) { return getAsString (v, preferHex); }
|
||||
inline String getAsCode (Colour v, bool) { return "Colour (0x" + String::toHexString ((int) v.getARGB()).paddedLeft ('0', 8) + ")"; }
|
||||
inline String getAsCode (const String& v, bool) { return CppTokeniserFunctions::addEscapeChars(v).quoted(); }
|
||||
inline String getAsCode (const char* v, bool) { return getAsCode (String (v), false); }
|
||||
|
||||
template <typename Type>
|
||||
inline const char* castToCharPointer (const Type&) { return ""; }
|
||||
inline const char* castToCharPointer (const String& s) { return s.toRawUTF8(); }
|
||||
|
||||
struct LivePropertyEditorBase;
|
||||
|
||||
//==============================================================================
|
||||
struct JUCE_API LiveValueBase
|
||||
{
|
||||
LiveValueBase (const char* file, int line);
|
||||
virtual ~LiveValueBase();
|
||||
|
||||
virtual LivePropertyEditorBase* createPropertyComponent (CodeDocument&) = 0;
|
||||
virtual String getStringValue (bool preferHex) const = 0;
|
||||
virtual String getCodeValue (bool preferHex) const = 0;
|
||||
virtual void setStringValue (const String&) = 0;
|
||||
virtual String getOriginalStringValue (bool preferHex) const = 0;
|
||||
virtual bool isString() const = 0;
|
||||
|
||||
String name, sourceFile;
|
||||
int sourceLine;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LiveValueBase)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JUCE_API LivePropertyEditorBase : public Component
|
||||
{
|
||||
LivePropertyEditorBase (LiveValueBase&, CodeDocument&);
|
||||
|
||||
void paint (Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
void applyNewValue (const String&);
|
||||
void selectOriginalValue();
|
||||
void findOriginalValueInCode();
|
||||
|
||||
LiveValueBase& value;
|
||||
Label name;
|
||||
TextEditor valueEditor;
|
||||
TextButton resetButton { "reset" };
|
||||
CodeDocument& document;
|
||||
CPlusPlusCodeTokeniser tokeniser;
|
||||
CodeEditorComponent sourceEditor;
|
||||
CodeDocument::Position valueStart, valueEnd;
|
||||
std::unique_ptr<Component> customComp;
|
||||
bool wasHex = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LivePropertyEditorBase)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Component* createColourEditor (LivePropertyEditorBase&);
|
||||
Component* createIntegerSlider (LivePropertyEditorBase&);
|
||||
Component* createFloatSlider (LivePropertyEditorBase&);
|
||||
Component* createBoolSlider (LivePropertyEditorBase&);
|
||||
|
||||
template <typename Type> struct CustomEditor { static Component* create (LivePropertyEditorBase&) { return nullptr; } };
|
||||
template <> struct CustomEditor<char> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<unsigned char> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<int> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<unsigned int> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<short> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<unsigned short> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<int64> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<uint64> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<float> { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } };
|
||||
template <> struct CustomEditor<double> { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } };
|
||||
template <> struct CustomEditor<Colour> { static Component* create (LivePropertyEditorBase& e) { return createColourEditor (e); } };
|
||||
template <> struct CustomEditor<bool> { static Component* create (LivePropertyEditorBase& e) { return createBoolSlider (e); } };
|
||||
|
||||
template <typename Type>
|
||||
struct LivePropertyEditor : public LivePropertyEditorBase
|
||||
{
|
||||
template <typename ValueType>
|
||||
LivePropertyEditor (ValueType& v, CodeDocument& d) : LivePropertyEditorBase (v, d)
|
||||
{
|
||||
customComp.reset (CustomEditor<Type>::create (*this));
|
||||
addAndMakeVisible (customComp.get());
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
struct LiveValue : public LiveValueBase
|
||||
{
|
||||
LiveValue (const char* file, int line, const Type& initialValue)
|
||||
: LiveValueBase (file, line), value (initialValue), originalValue (initialValue)
|
||||
{}
|
||||
|
||||
operator Type() const noexcept { return value; }
|
||||
Type get() const noexcept { return value; }
|
||||
operator const char*() const { return castToCharPointer (value); }
|
||||
|
||||
LivePropertyEditorBase* createPropertyComponent (CodeDocument& doc) override
|
||||
{
|
||||
return new LivePropertyEditor<Type> (*this, doc);
|
||||
}
|
||||
|
||||
String getStringValue (bool preferHex) const override { return getAsString (value, preferHex); }
|
||||
String getCodeValue (bool preferHex) const override { return getAsCode (value, preferHex); }
|
||||
String getOriginalStringValue (bool preferHex) const override { return getAsString (originalValue, preferHex); }
|
||||
void setStringValue (const String& s) override { setFromString (value, s); }
|
||||
bool isString() const override { return isStringType<Type>::value; }
|
||||
|
||||
Type value, originalValue;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LiveValue)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class JUCE_API ValueList : private AsyncUpdater,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
ValueList();
|
||||
~ValueList() override;
|
||||
|
||||
JUCE_DECLARE_SINGLETON (ValueList, false)
|
||||
|
||||
template <typename Type>
|
||||
LiveValue<Type>& getValue (const char* file, int line, const Type& initialValue)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
using ValueType = LiveValue<Type>;
|
||||
|
||||
for (auto* v : values)
|
||||
if (v->sourceLine == line && v->sourceFile == file)
|
||||
return *static_cast<ValueType*> (v);
|
||||
|
||||
auto v = new ValueType (file, line, initialValue);
|
||||
addValue (v);
|
||||
return *v;
|
||||
}
|
||||
|
||||
private:
|
||||
OwnedArray<LiveValueBase> values;
|
||||
OwnedArray<CodeDocument> documents;
|
||||
Array<File> documentFiles;
|
||||
class EditorWindow;
|
||||
Component::SafePointer<EditorWindow> editorWindow;
|
||||
CriticalSection lock;
|
||||
|
||||
CodeDocument& getDocument (const File&);
|
||||
void addValue (LiveValueBase*);
|
||||
void handleAsyncUpdate() override;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
inline LiveValue<Type>& getValue (const char* file, int line, const Type& initialValue)
|
||||
{
|
||||
// If you hit this assertion then the __FILE__ macro is providing a
|
||||
// relative path instead of an absolute path. On Windows this will be
|
||||
// a path relative to the build directory rather than the currently
|
||||
// running application. To fix this you must compile with the /FC flag.
|
||||
jassert (File::isAbsolutePath (file));
|
||||
|
||||
return ValueList::getInstance()->getValue (file, line, initialValue);
|
||||
}
|
||||
|
||||
inline LiveValue<String>& getValue (const char* file, int line, const char* initialValue)
|
||||
{
|
||||
return getValue (file, line, String (initialValue));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR || DOXYGEN
|
||||
/**
|
||||
This macro wraps a primitive constant value in some cunning boilerplate code that allows
|
||||
its value to be interactively tweaked in a popup window while your application is running.
|
||||
|
||||
In a release build, this macro disappears and is replaced by only the constant that it
|
||||
wraps, but if JUCE_ENABLE_LIVE_CONSTANT_EDITOR is enabled, it injects a class wrapper
|
||||
that automatically pops-up a window containing an editor that allows the value to be
|
||||
tweaked at run-time. The editor window will also force all visible components to be
|
||||
resized and repainted whenever a value is changed, so that if you use this to wrap
|
||||
a colour or layout parameter, you'll be able to immediately see the effects of changing it.
|
||||
|
||||
The editor will also load the original source-file that contains each JUCE_LIVE_CONSTANT
|
||||
macro, and will display a preview of the modified source code as you adjust the values.
|
||||
|
||||
Things to note:
|
||||
|
||||
- Only one of these per line! The __FILE__ and __LINE__ macros are used to identify
|
||||
the value, so things will get confused if you have more than one per line
|
||||
- Obviously because it needs to load the source code based on the __FILE__ macro,
|
||||
it'll only work if the source files are stored locally in the same location as they
|
||||
were when you compiled the program.
|
||||
- It's only designed to cope with simple types: primitives, string literals, and
|
||||
the Colour class, so if you try using it for other classes or complex expressions,
|
||||
good luck!
|
||||
- The editor window will get popped up whenever a new value is used for the first
|
||||
time. You can close the window, but there's no way to get it back without restarting
|
||||
the app!
|
||||
|
||||
e.g. in this example the colours, font size, and text used in the paint method can
|
||||
all be adjusted live:
|
||||
@code
|
||||
void MyComp::paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (JUCE_LIVE_CONSTANT (Colour (0xffddddff)));
|
||||
|
||||
Colour fontColour = JUCE_LIVE_CONSTANT (Colour (0xff005500));
|
||||
float fontSize = JUCE_LIVE_CONSTANT (16.0f);
|
||||
|
||||
g.setColour (fontColour);
|
||||
g.setFont (fontSize);
|
||||
|
||||
g.drawFittedText (JUCE_LIVE_CONSTANT ("Hello world!"),
|
||||
getLocalBounds(), Justification::centred, 2);
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
#define JUCE_LIVE_CONSTANT(initialValue) \
|
||||
(juce::LiveConstantEditor::getValue (__FILE__, __LINE__ - 1, initialValue).get())
|
||||
#else
|
||||
#define JUCE_LIVE_CONSTANT(initialValue) \
|
||||
(initialValue)
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR && ! defined (DOXYGEN)
|
||||
|
||||
//==============================================================================
|
||||
/** You can safely ignore all the stuff in this namespace - it's a bunch of boilerplate
|
||||
code used to implement the JUCE_LIVE_CONSTANT functionality.
|
||||
*/
|
||||
namespace LiveConstantEditor
|
||||
{
|
||||
int64 parseInt (String);
|
||||
double parseDouble (const String&);
|
||||
String intToString (int, bool preferHex);
|
||||
String intToString (int64, bool preferHex);
|
||||
|
||||
template <typename Type>
|
||||
static void setFromString (Type& v, const String& s) { v = static_cast<Type> (s); }
|
||||
inline void setFromString (char& v, const String& s) { v = (char) parseInt (s); }
|
||||
inline void setFromString (unsigned char& v, const String& s) { v = (unsigned char) parseInt (s); }
|
||||
inline void setFromString (short& v, const String& s) { v = (short) parseInt (s); }
|
||||
inline void setFromString (unsigned short& v, const String& s) { v = (unsigned short) parseInt (s); }
|
||||
inline void setFromString (int& v, const String& s) { v = (int) parseInt (s); }
|
||||
inline void setFromString (unsigned int& v, const String& s) { v = (unsigned int) parseInt (s); }
|
||||
inline void setFromString (long& v, const String& s) { v = (long) parseInt (s); }
|
||||
inline void setFromString (unsigned long& v, const String& s) { v = (unsigned long) parseInt (s); }
|
||||
inline void setFromString (int64& v, const String& s) { v = (int64) parseInt (s); }
|
||||
inline void setFromString (uint64& v, const String& s) { v = (uint64) parseInt (s); }
|
||||
inline void setFromString (double& v, const String& s) { v = parseDouble (s); }
|
||||
inline void setFromString (float& v, const String& s) { v = (float) parseDouble (s); }
|
||||
inline void setFromString (bool& v, const String& s) { v = (s == "true"); }
|
||||
inline void setFromString (String& v, const String& s) { v = s; }
|
||||
inline void setFromString (Colour& v, const String& s) { v = Colour ((uint32) parseInt (s)); }
|
||||
|
||||
template <typename Type>
|
||||
inline String getAsString (const Type& v, bool) { return String (v); }
|
||||
inline String getAsString (char v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (unsigned char v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (short v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (unsigned short v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (int v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (unsigned int v, bool preferHex) { return intToString ((int) v, preferHex); }
|
||||
inline String getAsString (bool v, bool) { return v ? "true" : "false"; }
|
||||
inline String getAsString (int64 v, bool preferHex) { return intToString ((int64) v, preferHex); }
|
||||
inline String getAsString (uint64 v, bool preferHex) { return intToString ((int64) v, preferHex); }
|
||||
inline String getAsString (Colour v, bool) { return intToString ((int) v.getARGB(), true); }
|
||||
|
||||
template <typename Type> struct isStringType { enum { value = 0 }; };
|
||||
template <> struct isStringType<String> { enum { value = 1 }; };
|
||||
|
||||
template <typename Type>
|
||||
inline String getAsCode (Type& v, bool preferHex) { return getAsString (v, preferHex); }
|
||||
inline String getAsCode (Colour v, bool) { return "Colour (0x" + String::toHexString ((int) v.getARGB()).paddedLeft ('0', 8) + ")"; }
|
||||
inline String getAsCode (const String& v, bool) { return CppTokeniserFunctions::addEscapeChars(v).quoted(); }
|
||||
inline String getAsCode (const char* v, bool) { return getAsCode (String (v), false); }
|
||||
|
||||
template <typename Type>
|
||||
inline const char* castToCharPointer (const Type&) { return ""; }
|
||||
inline const char* castToCharPointer (const String& s) { return s.toRawUTF8(); }
|
||||
|
||||
struct LivePropertyEditorBase;
|
||||
|
||||
//==============================================================================
|
||||
struct JUCE_API LiveValueBase
|
||||
{
|
||||
LiveValueBase (const char* file, int line);
|
||||
virtual ~LiveValueBase();
|
||||
|
||||
virtual LivePropertyEditorBase* createPropertyComponent (CodeDocument&) = 0;
|
||||
virtual String getStringValue (bool preferHex) const = 0;
|
||||
virtual String getCodeValue (bool preferHex) const = 0;
|
||||
virtual void setStringValue (const String&) = 0;
|
||||
virtual String getOriginalStringValue (bool preferHex) const = 0;
|
||||
virtual bool isString() const = 0;
|
||||
|
||||
String name, sourceFile;
|
||||
int sourceLine;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LiveValueBase)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JUCE_API LivePropertyEditorBase : public Component
|
||||
{
|
||||
LivePropertyEditorBase (LiveValueBase&, CodeDocument&);
|
||||
|
||||
void paint (Graphics&) override;
|
||||
void resized() override;
|
||||
|
||||
void applyNewValue (const String&);
|
||||
void selectOriginalValue();
|
||||
void findOriginalValueInCode();
|
||||
|
||||
LiveValueBase& value;
|
||||
Label name;
|
||||
TextEditor valueEditor;
|
||||
TextButton resetButton { "reset" };
|
||||
CodeDocument& document;
|
||||
CPlusPlusCodeTokeniser tokeniser;
|
||||
CodeEditorComponent sourceEditor;
|
||||
CodeDocument::Position valueStart, valueEnd;
|
||||
std::unique_ptr<Component> customComp;
|
||||
bool wasHex = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LivePropertyEditorBase)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Component* createColourEditor (LivePropertyEditorBase&);
|
||||
Component* createIntegerSlider (LivePropertyEditorBase&);
|
||||
Component* createFloatSlider (LivePropertyEditorBase&);
|
||||
Component* createBoolSlider (LivePropertyEditorBase&);
|
||||
|
||||
template <typename Type> struct CustomEditor { static Component* create (LivePropertyEditorBase&) { return nullptr; } };
|
||||
template <> struct CustomEditor<char> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<unsigned char> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<int> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<unsigned int> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<short> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<unsigned short> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<int64> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<uint64> { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } };
|
||||
template <> struct CustomEditor<float> { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } };
|
||||
template <> struct CustomEditor<double> { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } };
|
||||
template <> struct CustomEditor<Colour> { static Component* create (LivePropertyEditorBase& e) { return createColourEditor (e); } };
|
||||
template <> struct CustomEditor<bool> { static Component* create (LivePropertyEditorBase& e) { return createBoolSlider (e); } };
|
||||
|
||||
template <typename Type>
|
||||
struct LivePropertyEditor : public LivePropertyEditorBase
|
||||
{
|
||||
template <typename ValueType>
|
||||
LivePropertyEditor (ValueType& v, CodeDocument& d) : LivePropertyEditorBase (v, d)
|
||||
{
|
||||
customComp.reset (CustomEditor<Type>::create (*this));
|
||||
addAndMakeVisible (customComp.get());
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
struct LiveValue : public LiveValueBase
|
||||
{
|
||||
LiveValue (const char* file, int line, const Type& initialValue)
|
||||
: LiveValueBase (file, line), value (initialValue), originalValue (initialValue)
|
||||
{}
|
||||
|
||||
operator Type() const noexcept { return value; }
|
||||
Type get() const noexcept { return value; }
|
||||
operator const char*() const { return castToCharPointer (value); }
|
||||
|
||||
LivePropertyEditorBase* createPropertyComponent (CodeDocument& doc) override
|
||||
{
|
||||
return new LivePropertyEditor<Type> (*this, doc);
|
||||
}
|
||||
|
||||
String getStringValue (bool preferHex) const override { return getAsString (value, preferHex); }
|
||||
String getCodeValue (bool preferHex) const override { return getAsCode (value, preferHex); }
|
||||
String getOriginalStringValue (bool preferHex) const override { return getAsString (originalValue, preferHex); }
|
||||
void setStringValue (const String& s) override { setFromString (value, s); }
|
||||
bool isString() const override { return isStringType<Type>::value; }
|
||||
|
||||
Type value, originalValue;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LiveValue)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class JUCE_API ValueList : private AsyncUpdater,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
ValueList();
|
||||
~ValueList() override;
|
||||
|
||||
JUCE_DECLARE_SINGLETON (ValueList, false)
|
||||
|
||||
template <typename Type>
|
||||
LiveValue<Type>& getValue (const char* file, int line, const Type& initialValue)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
using ValueType = LiveValue<Type>;
|
||||
|
||||
for (auto* v : values)
|
||||
if (v->sourceLine == line && v->sourceFile == file)
|
||||
return *static_cast<ValueType*> (v);
|
||||
|
||||
auto v = new ValueType (file, line, initialValue);
|
||||
addValue (v);
|
||||
return *v;
|
||||
}
|
||||
|
||||
private:
|
||||
OwnedArray<LiveValueBase> values;
|
||||
OwnedArray<CodeDocument> documents;
|
||||
Array<File> documentFiles;
|
||||
class EditorWindow;
|
||||
Component::SafePointer<EditorWindow> editorWindow;
|
||||
CriticalSection lock;
|
||||
|
||||
CodeDocument& getDocument (const File&);
|
||||
void addValue (LiveValueBase*);
|
||||
void handleAsyncUpdate() override;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
inline LiveValue<Type>& getValue (const char* file, int line, const Type& initialValue)
|
||||
{
|
||||
// If you hit this assertion then the __FILE__ macro is providing a
|
||||
// relative path instead of an absolute path. On Windows this will be
|
||||
// a path relative to the build directory rather than the currently
|
||||
// running application. To fix this you must compile with the /FC flag.
|
||||
jassert (File::isAbsolutePath (file));
|
||||
|
||||
return ValueList::getInstance()->getValue (file, line, initialValue);
|
||||
}
|
||||
|
||||
inline LiveValue<String>& getValue (const char* file, int line, const char* initialValue)
|
||||
{
|
||||
return getValue (file, line, String (initialValue));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR || DOXYGEN
|
||||
/**
|
||||
This macro wraps a primitive constant value in some cunning boilerplate code that allows
|
||||
its value to be interactively tweaked in a popup window while your application is running.
|
||||
|
||||
In a release build, this macro disappears and is replaced by only the constant that it
|
||||
wraps, but if JUCE_ENABLE_LIVE_CONSTANT_EDITOR is enabled, it injects a class wrapper
|
||||
that automatically pops-up a window containing an editor that allows the value to be
|
||||
tweaked at run-time. The editor window will also force all visible components to be
|
||||
resized and repainted whenever a value is changed, so that if you use this to wrap
|
||||
a colour or layout parameter, you'll be able to immediately see the effects of changing it.
|
||||
|
||||
The editor will also load the original source-file that contains each JUCE_LIVE_CONSTANT
|
||||
macro, and will display a preview of the modified source code as you adjust the values.
|
||||
|
||||
Things to note:
|
||||
|
||||
- Only one of these per line! The __FILE__ and __LINE__ macros are used to identify
|
||||
the value, so things will get confused if you have more than one per line
|
||||
- Obviously because it needs to load the source code based on the __FILE__ macro,
|
||||
it'll only work if the source files are stored locally in the same location as they
|
||||
were when you compiled the program.
|
||||
- It's only designed to cope with simple types: primitives, string literals, and
|
||||
the Colour class, so if you try using it for other classes or complex expressions,
|
||||
good luck!
|
||||
- The editor window will get popped up whenever a new value is used for the first
|
||||
time. You can close the window, but there's no way to get it back without restarting
|
||||
the app!
|
||||
|
||||
e.g. in this example the colours, font size, and text used in the paint method can
|
||||
all be adjusted live:
|
||||
@code
|
||||
void MyComp::paint (Graphics& g) override
|
||||
{
|
||||
g.fillAll (JUCE_LIVE_CONSTANT (Colour (0xffddddff)));
|
||||
|
||||
Colour fontColour = JUCE_LIVE_CONSTANT (Colour (0xff005500));
|
||||
float fontSize = JUCE_LIVE_CONSTANT (16.0f);
|
||||
|
||||
g.setColour (fontColour);
|
||||
g.setFont (fontSize);
|
||||
|
||||
g.drawFittedText (JUCE_LIVE_CONSTANT ("Hello world!"),
|
||||
getLocalBounds(), Justification::centred, 2);
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
#define JUCE_LIVE_CONSTANT(initialValue) \
|
||||
(juce::LiveConstantEditor::getValue (__FILE__, __LINE__ - 1, initialValue).get())
|
||||
#else
|
||||
#define JUCE_LIVE_CONSTANT(initialValue) \
|
||||
(initialValue)
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,156 +1,156 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
PreferencesPanel::PreferencesPanel()
|
||||
: buttonSize (70)
|
||||
{
|
||||
}
|
||||
|
||||
PreferencesPanel::~PreferencesPanel()
|
||||
{
|
||||
}
|
||||
|
||||
int PreferencesPanel::getButtonSize() const noexcept
|
||||
{
|
||||
return buttonSize;
|
||||
}
|
||||
|
||||
void PreferencesPanel::setButtonSize (int newSize)
|
||||
{
|
||||
buttonSize = newSize;
|
||||
resized();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PreferencesPanel::addSettingsPage (const String& title,
|
||||
const Drawable* icon,
|
||||
const Drawable* overIcon,
|
||||
const Drawable* downIcon)
|
||||
{
|
||||
auto* button = new DrawableButton (title, DrawableButton::ImageAboveTextLabel);
|
||||
buttons.add (button);
|
||||
|
||||
button->setImages (icon, overIcon, downIcon);
|
||||
button->setRadioGroupId (1);
|
||||
button->onClick = [this] { clickedPage(); };
|
||||
button->setClickingTogglesState (true);
|
||||
button->setWantsKeyboardFocus (false);
|
||||
addAndMakeVisible (button);
|
||||
|
||||
resized();
|
||||
|
||||
if (currentPage == nullptr)
|
||||
setCurrentPage (title);
|
||||
}
|
||||
|
||||
void PreferencesPanel::addSettingsPage (const String& title, const void* imageData, int imageDataSize)
|
||||
{
|
||||
DrawableImage icon, iconOver, iconDown;
|
||||
icon.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
|
||||
|
||||
iconOver.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
|
||||
iconOver.setOverlayColour (Colours::black.withAlpha (0.12f));
|
||||
|
||||
iconDown.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
|
||||
iconDown.setOverlayColour (Colours::black.withAlpha (0.25f));
|
||||
|
||||
addSettingsPage (title, &icon, &iconOver, &iconDown);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PreferencesPanel::showInDialogBox (const String& dialogTitle, int dialogWidth, int dialogHeight, Colour backgroundColour)
|
||||
{
|
||||
setSize (dialogWidth, dialogHeight);
|
||||
|
||||
DialogWindow::LaunchOptions o;
|
||||
o.content.setNonOwned (this);
|
||||
o.dialogTitle = dialogTitle;
|
||||
o.dialogBackgroundColour = backgroundColour;
|
||||
o.escapeKeyTriggersCloseButton = false;
|
||||
o.useNativeTitleBar = false;
|
||||
o.resizable = false;
|
||||
|
||||
o.launchAsync();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PreferencesPanel::resized()
|
||||
{
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
buttons.getUnchecked(i)->setBounds (i * buttonSize, 0, buttonSize, buttonSize);
|
||||
|
||||
if (currentPage != nullptr)
|
||||
currentPage->setBounds (getLocalBounds().withTop (buttonSize + 5));
|
||||
}
|
||||
|
||||
void PreferencesPanel::paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::grey);
|
||||
g.fillRect (0, buttonSize + 2, getWidth(), 1);
|
||||
}
|
||||
|
||||
void PreferencesPanel::setCurrentPage (const String& pageName)
|
||||
{
|
||||
if (currentPageName != pageName)
|
||||
{
|
||||
currentPageName = pageName;
|
||||
|
||||
currentPage.reset();
|
||||
currentPage.reset (createComponentForPage (pageName));
|
||||
|
||||
if (currentPage != nullptr)
|
||||
{
|
||||
addAndMakeVisible (currentPage.get());
|
||||
currentPage->toBack();
|
||||
resized();
|
||||
}
|
||||
|
||||
for (auto* b : buttons)
|
||||
{
|
||||
if (b->getName() == pageName)
|
||||
{
|
||||
b->setToggleState (true, dontSendNotification);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreferencesPanel::clickedPage()
|
||||
{
|
||||
for (auto* b : buttons)
|
||||
{
|
||||
if (b->getToggleState())
|
||||
{
|
||||
setCurrentPage (b->getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
PreferencesPanel::PreferencesPanel()
|
||||
: buttonSize (70)
|
||||
{
|
||||
}
|
||||
|
||||
PreferencesPanel::~PreferencesPanel()
|
||||
{
|
||||
}
|
||||
|
||||
int PreferencesPanel::getButtonSize() const noexcept
|
||||
{
|
||||
return buttonSize;
|
||||
}
|
||||
|
||||
void PreferencesPanel::setButtonSize (int newSize)
|
||||
{
|
||||
buttonSize = newSize;
|
||||
resized();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PreferencesPanel::addSettingsPage (const String& title,
|
||||
const Drawable* icon,
|
||||
const Drawable* overIcon,
|
||||
const Drawable* downIcon)
|
||||
{
|
||||
auto* button = new DrawableButton (title, DrawableButton::ImageAboveTextLabel);
|
||||
buttons.add (button);
|
||||
|
||||
button->setImages (icon, overIcon, downIcon);
|
||||
button->setRadioGroupId (1);
|
||||
button->onClick = [this] { clickedPage(); };
|
||||
button->setClickingTogglesState (true);
|
||||
button->setWantsKeyboardFocus (false);
|
||||
addAndMakeVisible (button);
|
||||
|
||||
resized();
|
||||
|
||||
if (currentPage == nullptr)
|
||||
setCurrentPage (title);
|
||||
}
|
||||
|
||||
void PreferencesPanel::addSettingsPage (const String& title, const void* imageData, int imageDataSize)
|
||||
{
|
||||
DrawableImage icon, iconOver, iconDown;
|
||||
icon.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
|
||||
|
||||
iconOver.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
|
||||
iconOver.setOverlayColour (Colours::black.withAlpha (0.12f));
|
||||
|
||||
iconDown.setImage (ImageCache::getFromMemory (imageData, imageDataSize));
|
||||
iconDown.setOverlayColour (Colours::black.withAlpha (0.25f));
|
||||
|
||||
addSettingsPage (title, &icon, &iconOver, &iconDown);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PreferencesPanel::showInDialogBox (const String& dialogTitle, int dialogWidth, int dialogHeight, Colour backgroundColour)
|
||||
{
|
||||
setSize (dialogWidth, dialogHeight);
|
||||
|
||||
DialogWindow::LaunchOptions o;
|
||||
o.content.setNonOwned (this);
|
||||
o.dialogTitle = dialogTitle;
|
||||
o.dialogBackgroundColour = backgroundColour;
|
||||
o.escapeKeyTriggersCloseButton = false;
|
||||
o.useNativeTitleBar = false;
|
||||
o.resizable = false;
|
||||
|
||||
o.launchAsync();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void PreferencesPanel::resized()
|
||||
{
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
buttons.getUnchecked(i)->setBounds (i * buttonSize, 0, buttonSize, buttonSize);
|
||||
|
||||
if (currentPage != nullptr)
|
||||
currentPage->setBounds (getLocalBounds().withTop (buttonSize + 5));
|
||||
}
|
||||
|
||||
void PreferencesPanel::paint (Graphics& g)
|
||||
{
|
||||
g.setColour (Colours::grey);
|
||||
g.fillRect (0, buttonSize + 2, getWidth(), 1);
|
||||
}
|
||||
|
||||
void PreferencesPanel::setCurrentPage (const String& pageName)
|
||||
{
|
||||
if (currentPageName != pageName)
|
||||
{
|
||||
currentPageName = pageName;
|
||||
|
||||
currentPage.reset();
|
||||
currentPage.reset (createComponentForPage (pageName));
|
||||
|
||||
if (currentPage != nullptr)
|
||||
{
|
||||
addAndMakeVisible (currentPage.get());
|
||||
currentPage->toBack();
|
||||
resized();
|
||||
}
|
||||
|
||||
for (auto* b : buttons)
|
||||
{
|
||||
if (b->getName() == pageName)
|
||||
{
|
||||
b->setToggleState (true, dontSendNotification);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreferencesPanel::clickedPage()
|
||||
{
|
||||
for (auto* b : buttons)
|
||||
{
|
||||
if (b->getToggleState())
|
||||
{
|
||||
setCurrentPage (b->getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,147 +1,147 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component with a set of buttons at the top for changing between pages of
|
||||
preferences.
|
||||
|
||||
This is just a handy way of writing a Mac-style preferences panel where you
|
||||
have a row of buttons along the top for the different preference categories,
|
||||
each button having an icon above its name. Clicking these will show an
|
||||
appropriate prefs page below it.
|
||||
|
||||
You can either put one of these inside your own component, or just use the
|
||||
showInDialogBox() method to show it in a window and run it modally.
|
||||
|
||||
To use it, just add a set of named pages with the addSettingsPage() method,
|
||||
and implement the createComponentForPage() method to create suitable components
|
||||
for each of these pages.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API PreferencesPanel : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty panel.
|
||||
|
||||
Use addSettingsPage() to add some pages to it in your constructor.
|
||||
*/
|
||||
PreferencesPanel();
|
||||
|
||||
/** Destructor. */
|
||||
~PreferencesPanel() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a page using a set of drawables to define the page's icon.
|
||||
|
||||
Note that the other version of this method is much easier if you're using
|
||||
an image instead of a custom drawable.
|
||||
|
||||
@param pageTitle the name of this preferences page - you'll need to
|
||||
make sure your createComponentForPage() method creates
|
||||
a suitable component when it is passed this name
|
||||
@param normalIcon the drawable to display in the page's button normally
|
||||
@param overIcon the drawable to display in the page's button when the mouse is over
|
||||
@param downIcon the drawable to display in the page's button when the button is down
|
||||
@see DrawableButton
|
||||
*/
|
||||
void addSettingsPage (const String& pageTitle,
|
||||
const Drawable* normalIcon,
|
||||
const Drawable* overIcon,
|
||||
const Drawable* downIcon);
|
||||
|
||||
/** Creates a page using a set of drawables to define the page's icon.
|
||||
|
||||
The other version of this method gives you more control over the icon, but this
|
||||
one is much easier if you're just loading it from a file.
|
||||
|
||||
@param pageTitle the name of this preferences page - you'll need to
|
||||
make sure your createComponentForPage() method creates
|
||||
a suitable component when it is passed this name
|
||||
@param imageData a block of data containing an image file, e.g. a jpeg, png or gif.
|
||||
For this to look good, you'll probably want to use a nice
|
||||
transparent png file.
|
||||
@param imageDataSize the size of the image data, in bytes
|
||||
*/
|
||||
void addSettingsPage (const String& pageTitle,
|
||||
const void* imageData,
|
||||
int imageDataSize);
|
||||
|
||||
/** Utility method to display this panel in a DialogWindow.
|
||||
|
||||
Calling this will create a DialogWindow containing this panel with the
|
||||
given size and title, and will run it modally, returning when the user
|
||||
closes the dialog box.
|
||||
*/
|
||||
void showInDialogBox (const String& dialogTitle,
|
||||
int dialogWidth,
|
||||
int dialogHeight,
|
||||
Colour backgroundColour = Colours::white);
|
||||
|
||||
//==============================================================================
|
||||
/** Subclasses must override this to return a component for each preferences page.
|
||||
|
||||
The subclass should return a pointer to a new component representing the named
|
||||
page, which the panel will then display.
|
||||
|
||||
The panel will delete the component later when the user goes to another page
|
||||
or deletes the panel.
|
||||
*/
|
||||
virtual Component* createComponentForPage (const String& pageName) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current page being displayed. */
|
||||
void setCurrentPage (const String& pageName);
|
||||
|
||||
/** Returns the size of the buttons shown along the top. */
|
||||
int getButtonSize() const noexcept;
|
||||
|
||||
/** Changes the size of the buttons shown along the top. */
|
||||
void setButtonSize (int newSize);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String currentPageName;
|
||||
std::unique_ptr<Component> currentPage;
|
||||
OwnedArray<DrawableButton> buttons;
|
||||
int buttonSize;
|
||||
|
||||
void clickedPage();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreferencesPanel)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component with a set of buttons at the top for changing between pages of
|
||||
preferences.
|
||||
|
||||
This is just a handy way of writing a Mac-style preferences panel where you
|
||||
have a row of buttons along the top for the different preference categories,
|
||||
each button having an icon above its name. Clicking these will show an
|
||||
appropriate prefs page below it.
|
||||
|
||||
You can either put one of these inside your own component, or just use the
|
||||
showInDialogBox() method to show it in a window and run it modally.
|
||||
|
||||
To use it, just add a set of named pages with the addSettingsPage() method,
|
||||
and implement the createComponentForPage() method to create suitable components
|
||||
for each of these pages.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API PreferencesPanel : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty panel.
|
||||
|
||||
Use addSettingsPage() to add some pages to it in your constructor.
|
||||
*/
|
||||
PreferencesPanel();
|
||||
|
||||
/** Destructor. */
|
||||
~PreferencesPanel() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a page using a set of drawables to define the page's icon.
|
||||
|
||||
Note that the other version of this method is much easier if you're using
|
||||
an image instead of a custom drawable.
|
||||
|
||||
@param pageTitle the name of this preferences page - you'll need to
|
||||
make sure your createComponentForPage() method creates
|
||||
a suitable component when it is passed this name
|
||||
@param normalIcon the drawable to display in the page's button normally
|
||||
@param overIcon the drawable to display in the page's button when the mouse is over
|
||||
@param downIcon the drawable to display in the page's button when the button is down
|
||||
@see DrawableButton
|
||||
*/
|
||||
void addSettingsPage (const String& pageTitle,
|
||||
const Drawable* normalIcon,
|
||||
const Drawable* overIcon,
|
||||
const Drawable* downIcon);
|
||||
|
||||
/** Creates a page using a set of drawables to define the page's icon.
|
||||
|
||||
The other version of this method gives you more control over the icon, but this
|
||||
one is much easier if you're just loading it from a file.
|
||||
|
||||
@param pageTitle the name of this preferences page - you'll need to
|
||||
make sure your createComponentForPage() method creates
|
||||
a suitable component when it is passed this name
|
||||
@param imageData a block of data containing an image file, e.g. a jpeg, png or gif.
|
||||
For this to look good, you'll probably want to use a nice
|
||||
transparent png file.
|
||||
@param imageDataSize the size of the image data, in bytes
|
||||
*/
|
||||
void addSettingsPage (const String& pageTitle,
|
||||
const void* imageData,
|
||||
int imageDataSize);
|
||||
|
||||
/** Utility method to display this panel in a DialogWindow.
|
||||
|
||||
Calling this will create a DialogWindow containing this panel with the
|
||||
given size and title, and will run it modally, returning when the user
|
||||
closes the dialog box.
|
||||
*/
|
||||
void showInDialogBox (const String& dialogTitle,
|
||||
int dialogWidth,
|
||||
int dialogHeight,
|
||||
Colour backgroundColour = Colours::white);
|
||||
|
||||
//==============================================================================
|
||||
/** Subclasses must override this to return a component for each preferences page.
|
||||
|
||||
The subclass should return a pointer to a new component representing the named
|
||||
page, which the panel will then display.
|
||||
|
||||
The panel will delete the component later when the user goes to another page
|
||||
or deletes the panel.
|
||||
*/
|
||||
virtual Component* createComponentForPage (const String& pageName) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current page being displayed. */
|
||||
void setCurrentPage (const String& pageName);
|
||||
|
||||
/** Returns the size of the buttons shown along the top. */
|
||||
int getButtonSize() const noexcept;
|
||||
|
||||
/** Changes the size of the buttons shown along the top. */
|
||||
void setButtonSize (int newSize);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String currentPageName;
|
||||
std::unique_ptr<Component> currentPage;
|
||||
OwnedArray<DrawableButton> buttons;
|
||||
int buttonSize;
|
||||
|
||||
void clickedPage();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreferencesPanel)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,229 +1,229 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_ANDROID && ! JUCE_IOS && ! JUCE_MAC
|
||||
bool PushNotifications::Notification::isValid() const noexcept { return true; }
|
||||
#endif
|
||||
|
||||
PushNotifications::Notification::Notification (const Notification& other)
|
||||
: identifier (other.identifier),
|
||||
title (other.title),
|
||||
body (other.body),
|
||||
subtitle (other.subtitle),
|
||||
groupId (other.groupId),
|
||||
badgeNumber (other.badgeNumber),
|
||||
soundToPlay (other.soundToPlay),
|
||||
properties (other.properties),
|
||||
category (other.category),
|
||||
triggerIntervalSec (other.triggerIntervalSec),
|
||||
repeat (other.repeat),
|
||||
icon (other.icon),
|
||||
channelId (other.channelId),
|
||||
largeIcon (other.largeIcon),
|
||||
tickerText (other.tickerText),
|
||||
actions (other.actions),
|
||||
progress (other.progress),
|
||||
person (other.person),
|
||||
type (other.type),
|
||||
priority (other.priority),
|
||||
lockScreenAppearance (other.lockScreenAppearance),
|
||||
publicVersion (other.publicVersion.get() != nullptr ? new Notification (*other.publicVersion) : nullptr),
|
||||
groupSortKey (other.groupSortKey),
|
||||
groupSummary (other.groupSummary),
|
||||
accentColour (other.accentColour),
|
||||
ledColour (other.ledColour),
|
||||
ledBlinkPattern (other.ledBlinkPattern),
|
||||
vibrationPattern (other.vibrationPattern),
|
||||
shouldAutoCancel (other.shouldAutoCancel),
|
||||
localOnly (other.localOnly),
|
||||
ongoing (other.ongoing),
|
||||
alertOnlyOnce (other.alertOnlyOnce),
|
||||
timestampVisibility (other.timestampVisibility),
|
||||
badgeIconType (other.badgeIconType),
|
||||
groupAlertBehaviour (other.groupAlertBehaviour),
|
||||
timeoutAfterMs (other.timeoutAfterMs)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_IMPLEMENT_SINGLETON (PushNotifications)
|
||||
|
||||
PushNotifications::PushNotifications()
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
: pimpl (new Pimpl (*this))
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
PushNotifications::~PushNotifications() { clearSingletonInstance(); }
|
||||
|
||||
void PushNotifications::addListener (Listener* l) { listeners.add (l); }
|
||||
void PushNotifications::removeListener (Listener* l) { listeners.remove (l); }
|
||||
|
||||
void PushNotifications::requestPermissionsWithSettings (const PushNotifications::Settings& settings)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
|
||||
pimpl->requestPermissionsWithSettings (settings);
|
||||
#else
|
||||
ignoreUnused (settings);
|
||||
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::requestSettingsUsed()
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
|
||||
pimpl->requestSettingsUsed();
|
||||
#else
|
||||
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PushNotifications::areNotificationsEnabled() const
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
return pimpl->areNotificationsEnabled();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::getDeliveredNotifications() const
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->getDeliveredNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::removeAllDeliveredNotifications()
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->removeAllDeliveredNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
String PushNotifications::getDeviceToken() const
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
return pimpl->getDeviceToken();
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::setupChannels (const Array<ChannelGroup>& groups, const Array<Channel>& channels)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->setupChannels (groups, channels);
|
||||
#else
|
||||
ignoreUnused (groups, channels);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::getPendingLocalNotifications() const
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->getPendingLocalNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::removeAllPendingLocalNotifications()
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->removeAllPendingLocalNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::subscribeToTopic (const String& topic)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->subscribeToTopic (topic);
|
||||
#else
|
||||
ignoreUnused (topic);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::unsubscribeFromTopic (const String& topic)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->unsubscribeFromTopic (topic);
|
||||
#else
|
||||
ignoreUnused (topic);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PushNotifications::sendLocalNotification (const Notification& n)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->sendLocalNotification (n);
|
||||
#else
|
||||
ignoreUnused (n);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::removeDeliveredNotification (const String& identifier)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->removeDeliveredNotification (identifier);
|
||||
#else
|
||||
ignoreUnused (identifier);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::removePendingLocalNotification (const String& identifier)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->removePendingLocalNotification (identifier);
|
||||
#else
|
||||
ignoreUnused (identifier);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::sendUpstreamMessage (const String& serverSenderId,
|
||||
const String& collapseKey,
|
||||
const String& messageId,
|
||||
const String& messageType,
|
||||
int timeToLive,
|
||||
const StringPairArray& additionalData)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->sendUpstreamMessage (serverSenderId,
|
||||
collapseKey,
|
||||
messageId,
|
||||
messageType,
|
||||
timeToLive,
|
||||
additionalData);
|
||||
#else
|
||||
ignoreUnused (serverSenderId, collapseKey, messageId, messageType);
|
||||
ignoreUnused (timeToLive, additionalData);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_ANDROID && ! JUCE_IOS && ! JUCE_MAC
|
||||
bool PushNotifications::Notification::isValid() const noexcept { return true; }
|
||||
#endif
|
||||
|
||||
PushNotifications::Notification::Notification (const Notification& other)
|
||||
: identifier (other.identifier),
|
||||
title (other.title),
|
||||
body (other.body),
|
||||
subtitle (other.subtitle),
|
||||
groupId (other.groupId),
|
||||
badgeNumber (other.badgeNumber),
|
||||
soundToPlay (other.soundToPlay),
|
||||
properties (other.properties),
|
||||
category (other.category),
|
||||
triggerIntervalSec (other.triggerIntervalSec),
|
||||
repeat (other.repeat),
|
||||
icon (other.icon),
|
||||
channelId (other.channelId),
|
||||
largeIcon (other.largeIcon),
|
||||
tickerText (other.tickerText),
|
||||
actions (other.actions),
|
||||
progress (other.progress),
|
||||
person (other.person),
|
||||
type (other.type),
|
||||
priority (other.priority),
|
||||
lockScreenAppearance (other.lockScreenAppearance),
|
||||
publicVersion (other.publicVersion.get() != nullptr ? new Notification (*other.publicVersion) : nullptr),
|
||||
groupSortKey (other.groupSortKey),
|
||||
groupSummary (other.groupSummary),
|
||||
accentColour (other.accentColour),
|
||||
ledColour (other.ledColour),
|
||||
ledBlinkPattern (other.ledBlinkPattern),
|
||||
vibrationPattern (other.vibrationPattern),
|
||||
shouldAutoCancel (other.shouldAutoCancel),
|
||||
localOnly (other.localOnly),
|
||||
ongoing (other.ongoing),
|
||||
alertOnlyOnce (other.alertOnlyOnce),
|
||||
timestampVisibility (other.timestampVisibility),
|
||||
badgeIconType (other.badgeIconType),
|
||||
groupAlertBehaviour (other.groupAlertBehaviour),
|
||||
timeoutAfterMs (other.timeoutAfterMs)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_IMPLEMENT_SINGLETON (PushNotifications)
|
||||
|
||||
PushNotifications::PushNotifications()
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
: pimpl (new Pimpl (*this))
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
PushNotifications::~PushNotifications() { clearSingletonInstance(); }
|
||||
|
||||
void PushNotifications::addListener (Listener* l) { listeners.add (l); }
|
||||
void PushNotifications::removeListener (Listener* l) { listeners.remove (l); }
|
||||
|
||||
void PushNotifications::requestPermissionsWithSettings (const PushNotifications::Settings& settings)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
|
||||
pimpl->requestPermissionsWithSettings (settings);
|
||||
#else
|
||||
ignoreUnused (settings);
|
||||
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::requestSettingsUsed()
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS && (JUCE_IOS || JUCE_MAC)
|
||||
pimpl->requestSettingsUsed();
|
||||
#else
|
||||
listeners.call ([] (Listener& l) { l.notificationSettingsReceived ({}); });
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PushNotifications::areNotificationsEnabled() const
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
return pimpl->areNotificationsEnabled();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::getDeliveredNotifications() const
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->getDeliveredNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::removeAllDeliveredNotifications()
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->removeAllDeliveredNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
String PushNotifications::getDeviceToken() const
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
return pimpl->getDeviceToken();
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::setupChannels (const Array<ChannelGroup>& groups, const Array<Channel>& channels)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->setupChannels (groups, channels);
|
||||
#else
|
||||
ignoreUnused (groups, channels);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::getPendingLocalNotifications() const
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->getPendingLocalNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::removeAllPendingLocalNotifications()
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->removeAllPendingLocalNotifications();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::subscribeToTopic (const String& topic)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->subscribeToTopic (topic);
|
||||
#else
|
||||
ignoreUnused (topic);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::unsubscribeFromTopic (const String& topic)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->unsubscribeFromTopic (topic);
|
||||
#else
|
||||
ignoreUnused (topic);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void PushNotifications::sendLocalNotification (const Notification& n)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->sendLocalNotification (n);
|
||||
#else
|
||||
ignoreUnused (n);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::removeDeliveredNotification (const String& identifier)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->removeDeliveredNotification (identifier);
|
||||
#else
|
||||
ignoreUnused (identifier);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::removePendingLocalNotification (const String& identifier)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->removePendingLocalNotification (identifier);
|
||||
#else
|
||||
ignoreUnused (identifier);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushNotifications::sendUpstreamMessage (const String& serverSenderId,
|
||||
const String& collapseKey,
|
||||
const String& messageId,
|
||||
const String& messageType,
|
||||
int timeToLive,
|
||||
const StringPairArray& additionalData)
|
||||
{
|
||||
#if JUCE_PUSH_NOTIFICATIONS
|
||||
pimpl->sendUpstreamMessage (serverSenderId,
|
||||
collapseKey,
|
||||
messageId,
|
||||
messageType,
|
||||
timeToLive,
|
||||
additionalData);
|
||||
#else
|
||||
ignoreUnused (serverSenderId, collapseKey, messageId, messageType);
|
||||
ignoreUnused (timeToLive, additionalData);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,188 +1,184 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
RecentlyOpenedFilesList::RecentlyOpenedFilesList()
|
||||
: maxNumberOfItems (10)
|
||||
{
|
||||
}
|
||||
|
||||
RecentlyOpenedFilesList::~RecentlyOpenedFilesList()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void RecentlyOpenedFilesList::setMaxNumberOfItems (const int newMaxNumber)
|
||||
{
|
||||
maxNumberOfItems = jmax (1, newMaxNumber);
|
||||
|
||||
files.removeRange (maxNumberOfItems, getNumFiles());
|
||||
}
|
||||
|
||||
int RecentlyOpenedFilesList::getNumFiles() const
|
||||
{
|
||||
return files.size();
|
||||
}
|
||||
|
||||
File RecentlyOpenedFilesList::getFile (const int index) const
|
||||
{
|
||||
return File (files [index]);
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::clear()
|
||||
{
|
||||
files.clear();
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::addFile (const File& file)
|
||||
{
|
||||
removeFile (file);
|
||||
files.insert (0, file.getFullPathName());
|
||||
|
||||
setMaxNumberOfItems (maxNumberOfItems);
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::removeFile (const File& file)
|
||||
{
|
||||
files.removeString (file.getFullPathName());
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::removeNonExistentFiles()
|
||||
{
|
||||
for (int i = getNumFiles(); --i >= 0;)
|
||||
if (! getFile(i).exists())
|
||||
files.remove (i);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int RecentlyOpenedFilesList::createPopupMenuItems (PopupMenu& menuToAddTo,
|
||||
const int baseItemId,
|
||||
const bool showFullPaths,
|
||||
const bool dontAddNonExistentFiles,
|
||||
const File** filesToAvoid)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
for (int i = 0; i < getNumFiles(); ++i)
|
||||
{
|
||||
const File f (getFile(i));
|
||||
|
||||
if ((! dontAddNonExistentFiles) || f.exists())
|
||||
{
|
||||
bool needsAvoiding = false;
|
||||
|
||||
if (filesToAvoid != nullptr)
|
||||
{
|
||||
for (const File** avoid = filesToAvoid; *avoid != nullptr; ++avoid)
|
||||
{
|
||||
if (f == **avoid)
|
||||
{
|
||||
needsAvoiding = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! needsAvoiding)
|
||||
{
|
||||
menuToAddTo.addItem (baseItemId + i,
|
||||
showFullPaths ? f.getFullPathName()
|
||||
: f.getFileName());
|
||||
++num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String RecentlyOpenedFilesList::toString() const
|
||||
{
|
||||
return files.joinIntoString ("\n");
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::restoreFromString (const String& stringifiedVersion)
|
||||
{
|
||||
clear();
|
||||
files.addLines (stringifiedVersion);
|
||||
|
||||
setMaxNumberOfItems (maxNumberOfItems);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void RecentlyOpenedFilesList::registerRecentFileNatively (const File& file)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL: createNSURLFromFile (file)];
|
||||
}
|
||||
#else
|
||||
ignoreUnused (file);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::forgetRecentFileNatively (const File& file)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
// for some reason, OSX doesn't provide a method to just remove a single file
|
||||
// from the recent list, so we clear them all and add them back excluding
|
||||
// the specified file
|
||||
|
||||
auto sharedDocController = [NSDocumentController sharedDocumentController];
|
||||
auto recentDocumentURLs = [sharedDocController recentDocumentURLs];
|
||||
|
||||
[sharedDocController clearRecentDocuments: nil];
|
||||
|
||||
auto* nsFile = createNSURLFromFile (file);
|
||||
|
||||
auto reverseEnumerator = [recentDocumentURLs reverseObjectEnumerator];
|
||||
|
||||
for (NSURL* url : reverseEnumerator)
|
||||
if (! [url isEqual:nsFile])
|
||||
[sharedDocController noteNewRecentDocumentURL:url];
|
||||
}
|
||||
#else
|
||||
ignoreUnused (file);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::clearRecentFilesNatively()
|
||||
{
|
||||
#if JUCE_MAC
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSDocumentController sharedDocumentController] clearRecentDocuments: nil];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
RecentlyOpenedFilesList::RecentlyOpenedFilesList()
|
||||
: maxNumberOfItems (10)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void RecentlyOpenedFilesList::setMaxNumberOfItems (const int newMaxNumber)
|
||||
{
|
||||
maxNumberOfItems = jmax (1, newMaxNumber);
|
||||
|
||||
files.removeRange (maxNumberOfItems, getNumFiles());
|
||||
}
|
||||
|
||||
int RecentlyOpenedFilesList::getNumFiles() const
|
||||
{
|
||||
return files.size();
|
||||
}
|
||||
|
||||
File RecentlyOpenedFilesList::getFile (const int index) const
|
||||
{
|
||||
return File (files [index]);
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::clear()
|
||||
{
|
||||
files.clear();
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::addFile (const File& file)
|
||||
{
|
||||
removeFile (file);
|
||||
files.insert (0, file.getFullPathName());
|
||||
|
||||
setMaxNumberOfItems (maxNumberOfItems);
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::removeFile (const File& file)
|
||||
{
|
||||
files.removeString (file.getFullPathName());
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::removeNonExistentFiles()
|
||||
{
|
||||
for (int i = getNumFiles(); --i >= 0;)
|
||||
if (! getFile(i).exists())
|
||||
files.remove (i);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int RecentlyOpenedFilesList::createPopupMenuItems (PopupMenu& menuToAddTo,
|
||||
const int baseItemId,
|
||||
const bool showFullPaths,
|
||||
const bool dontAddNonExistentFiles,
|
||||
const File** filesToAvoid)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
for (int i = 0; i < getNumFiles(); ++i)
|
||||
{
|
||||
const File f (getFile(i));
|
||||
|
||||
if ((! dontAddNonExistentFiles) || f.exists())
|
||||
{
|
||||
bool needsAvoiding = false;
|
||||
|
||||
if (filesToAvoid != nullptr)
|
||||
{
|
||||
for (const File** avoid = filesToAvoid; *avoid != nullptr; ++avoid)
|
||||
{
|
||||
if (f == **avoid)
|
||||
{
|
||||
needsAvoiding = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! needsAvoiding)
|
||||
{
|
||||
menuToAddTo.addItem (baseItemId + i,
|
||||
showFullPaths ? f.getFullPathName()
|
||||
: f.getFileName());
|
||||
++num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String RecentlyOpenedFilesList::toString() const
|
||||
{
|
||||
return files.joinIntoString ("\n");
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::restoreFromString (const String& stringifiedVersion)
|
||||
{
|
||||
clear();
|
||||
files.addLines (stringifiedVersion);
|
||||
|
||||
setMaxNumberOfItems (maxNumberOfItems);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void RecentlyOpenedFilesList::registerRecentFileNatively (const File& file)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL: createNSURLFromFile (file)];
|
||||
}
|
||||
#else
|
||||
ignoreUnused (file);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::forgetRecentFileNatively (const File& file)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
// for some reason, OSX doesn't provide a method to just remove a single file
|
||||
// from the recent list, so we clear them all and add them back excluding
|
||||
// the specified file
|
||||
|
||||
auto sharedDocController = [NSDocumentController sharedDocumentController];
|
||||
auto recentDocumentURLs = [sharedDocController recentDocumentURLs];
|
||||
|
||||
[sharedDocController clearRecentDocuments: nil];
|
||||
|
||||
auto* nsFile = createNSURLFromFile (file);
|
||||
|
||||
auto reverseEnumerator = [recentDocumentURLs reverseObjectEnumerator];
|
||||
|
||||
for (NSURL* url : reverseEnumerator)
|
||||
if (! [url isEqual:nsFile])
|
||||
[sharedDocController noteNewRecentDocumentURL:url];
|
||||
}
|
||||
#else
|
||||
ignoreUnused (file);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RecentlyOpenedFilesList::clearRecentFilesNatively()
|
||||
{
|
||||
#if JUCE_MAC
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
[[NSDocumentController sharedDocumentController] clearRecentDocuments: nil];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,180 +1,177 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages a set of files for use as a list of recently-opened documents.
|
||||
|
||||
This is a handy class for holding your list of recently-opened documents, with
|
||||
helpful methods for things like purging any non-existent files, automatically
|
||||
adding them to a menu, and making persistence easy.
|
||||
|
||||
@see File, FileBasedDocument
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API RecentlyOpenedFilesList
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty list.
|
||||
*/
|
||||
RecentlyOpenedFilesList();
|
||||
|
||||
/** Destructor. */
|
||||
~RecentlyOpenedFilesList();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a limit for the number of files that will be stored in the list.
|
||||
|
||||
When addFile() is called, then if there is no more space in the list, the
|
||||
least-recently added file will be dropped.
|
||||
|
||||
@see getMaxNumberOfItems
|
||||
*/
|
||||
void setMaxNumberOfItems (int newMaxNumber);
|
||||
|
||||
/** Returns the number of items that this list will store.
|
||||
@see setMaxNumberOfItems
|
||||
*/
|
||||
int getMaxNumberOfItems() const noexcept { return maxNumberOfItems; }
|
||||
|
||||
/** Returns the number of files in the list.
|
||||
|
||||
The most recently added file is always at index 0.
|
||||
*/
|
||||
int getNumFiles() const;
|
||||
|
||||
/** Returns one of the files in the list.
|
||||
|
||||
The most recently added file is always at index 0.
|
||||
*/
|
||||
File getFile (int index) const;
|
||||
|
||||
/** Returns an array of all the absolute pathnames in the list.
|
||||
*/
|
||||
const StringArray& getAllFilenames() const noexcept { return files; }
|
||||
|
||||
/** Clears all the files from the list. */
|
||||
void clear();
|
||||
|
||||
/** Adds a file to the list.
|
||||
|
||||
The file will be added at index 0. If this file is already in the list, it will
|
||||
be moved up to index 0, but a file can only appear once in the list.
|
||||
|
||||
If the list already contains the maximum number of items that is permitted, the
|
||||
least-recently added file will be dropped from the end.
|
||||
*/
|
||||
void addFile (const File& file);
|
||||
|
||||
/** Removes a file from the list. */
|
||||
void removeFile (const File& file);
|
||||
|
||||
/** Checks each of the files in the list, removing any that don't exist.
|
||||
|
||||
You might want to call this after reloading a list of files, or before putting them
|
||||
on a menu.
|
||||
*/
|
||||
void removeNonExistentFiles();
|
||||
|
||||
/** Tells the OS to add a file to the OS-managed list of recent documents for this app.
|
||||
|
||||
Not all OSes maintain a list of recent files for an application, so this
|
||||
function will have no effect on some OSes. Currently it's just implemented for OSX.
|
||||
*/
|
||||
static void registerRecentFileNatively (const File& file);
|
||||
|
||||
/** Tells the OS to remove a file from the OS-managed list of recent documents for this app.
|
||||
|
||||
Not all OSes maintain a list of recent files for an application, so this
|
||||
function will have no effect on some OSes. Currently it's just implemented for OSX.
|
||||
*/
|
||||
static void forgetRecentFileNatively (const File& file);
|
||||
|
||||
/** Tells the OS to clear the OS-managed list of recent documents for this app.
|
||||
|
||||
Not all OSes maintain a list of recent files for an application, so this
|
||||
function will have no effect on some OSes. Currently it's just implemented for OSX.
|
||||
*/
|
||||
static void clearRecentFilesNatively();
|
||||
|
||||
//==============================================================================
|
||||
/** Adds entries to a menu, representing each of the files in the list.
|
||||
|
||||
This is handy for creating an "open recent file..." menu in your app. The
|
||||
menu items are numbered consecutively starting with the baseItemId value,
|
||||
and can either be added as complete pathnames, or just the last part of the
|
||||
filename.
|
||||
|
||||
If dontAddNonExistentFiles is true, then each file will be checked and only those
|
||||
that exist will be added.
|
||||
|
||||
If filesToAvoid is not a nullptr, then it is considered to be a zero-terminated array
|
||||
of pointers to file objects. Any files that appear in this list will not be added to
|
||||
the menu - the reason for this is that you might have a number of files already open,
|
||||
so might not want these to be shown in the menu.
|
||||
|
||||
It returns the number of items that were added.
|
||||
*/
|
||||
int createPopupMenuItems (PopupMenu& menuToAddItemsTo,
|
||||
int baseItemId,
|
||||
bool showFullPaths,
|
||||
bool dontAddNonExistentFiles,
|
||||
const File** filesToAvoid = nullptr);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a string that encapsulates all the files in the list.
|
||||
|
||||
The string that is returned can later be passed into restoreFromString() in
|
||||
order to recreate the list. This is handy for persisting your list, e.g. in
|
||||
a PropertiesFile object.
|
||||
|
||||
@see restoreFromString
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
/** Restores the list from a previously stringified version of the list.
|
||||
|
||||
Pass in a stringified version created with toString() in order to persist/restore
|
||||
your list.
|
||||
|
||||
@see toString
|
||||
*/
|
||||
void restoreFromString (const String& stringifiedVersion);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
StringArray files;
|
||||
int maxNumberOfItems;
|
||||
|
||||
JUCE_LEAK_DETECTOR (RecentlyOpenedFilesList)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages a set of files for use as a list of recently-opened documents.
|
||||
|
||||
This is a handy class for holding your list of recently-opened documents, with
|
||||
helpful methods for things like purging any non-existent files, automatically
|
||||
adding them to a menu, and making persistence easy.
|
||||
|
||||
@see File, FileBasedDocument
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API RecentlyOpenedFilesList
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an empty list.
|
||||
*/
|
||||
RecentlyOpenedFilesList();
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a limit for the number of files that will be stored in the list.
|
||||
|
||||
When addFile() is called, then if there is no more space in the list, the
|
||||
least-recently added file will be dropped.
|
||||
|
||||
@see getMaxNumberOfItems
|
||||
*/
|
||||
void setMaxNumberOfItems (int newMaxNumber);
|
||||
|
||||
/** Returns the number of items that this list will store.
|
||||
@see setMaxNumberOfItems
|
||||
*/
|
||||
int getMaxNumberOfItems() const noexcept { return maxNumberOfItems; }
|
||||
|
||||
/** Returns the number of files in the list.
|
||||
|
||||
The most recently added file is always at index 0.
|
||||
*/
|
||||
int getNumFiles() const;
|
||||
|
||||
/** Returns one of the files in the list.
|
||||
|
||||
The most recently added file is always at index 0.
|
||||
*/
|
||||
File getFile (int index) const;
|
||||
|
||||
/** Returns an array of all the absolute pathnames in the list.
|
||||
*/
|
||||
const StringArray& getAllFilenames() const noexcept { return files; }
|
||||
|
||||
/** Clears all the files from the list. */
|
||||
void clear();
|
||||
|
||||
/** Adds a file to the list.
|
||||
|
||||
The file will be added at index 0. If this file is already in the list, it will
|
||||
be moved up to index 0, but a file can only appear once in the list.
|
||||
|
||||
If the list already contains the maximum number of items that is permitted, the
|
||||
least-recently added file will be dropped from the end.
|
||||
*/
|
||||
void addFile (const File& file);
|
||||
|
||||
/** Removes a file from the list. */
|
||||
void removeFile (const File& file);
|
||||
|
||||
/** Checks each of the files in the list, removing any that don't exist.
|
||||
|
||||
You might want to call this after reloading a list of files, or before putting them
|
||||
on a menu.
|
||||
*/
|
||||
void removeNonExistentFiles();
|
||||
|
||||
/** Tells the OS to add a file to the OS-managed list of recent documents for this app.
|
||||
|
||||
Not all OSes maintain a list of recent files for an application, so this
|
||||
function will have no effect on some OSes. Currently it's just implemented for OSX.
|
||||
*/
|
||||
static void registerRecentFileNatively (const File& file);
|
||||
|
||||
/** Tells the OS to remove a file from the OS-managed list of recent documents for this app.
|
||||
|
||||
Not all OSes maintain a list of recent files for an application, so this
|
||||
function will have no effect on some OSes. Currently it's just implemented for OSX.
|
||||
*/
|
||||
static void forgetRecentFileNatively (const File& file);
|
||||
|
||||
/** Tells the OS to clear the OS-managed list of recent documents for this app.
|
||||
|
||||
Not all OSes maintain a list of recent files for an application, so this
|
||||
function will have no effect on some OSes. Currently it's just implemented for OSX.
|
||||
*/
|
||||
static void clearRecentFilesNatively();
|
||||
|
||||
//==============================================================================
|
||||
/** Adds entries to a menu, representing each of the files in the list.
|
||||
|
||||
This is handy for creating an "open recent file..." menu in your app. The
|
||||
menu items are numbered consecutively starting with the baseItemId value,
|
||||
and can either be added as complete pathnames, or just the last part of the
|
||||
filename.
|
||||
|
||||
If dontAddNonExistentFiles is true, then each file will be checked and only those
|
||||
that exist will be added.
|
||||
|
||||
If filesToAvoid is not a nullptr, then it is considered to be a zero-terminated array
|
||||
of pointers to file objects. Any files that appear in this list will not be added to
|
||||
the menu - the reason for this is that you might have a number of files already open,
|
||||
so might not want these to be shown in the menu.
|
||||
|
||||
It returns the number of items that were added.
|
||||
*/
|
||||
int createPopupMenuItems (PopupMenu& menuToAddItemsTo,
|
||||
int baseItemId,
|
||||
bool showFullPaths,
|
||||
bool dontAddNonExistentFiles,
|
||||
const File** filesToAvoid = nullptr);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a string that encapsulates all the files in the list.
|
||||
|
||||
The string that is returned can later be passed into restoreFromString() in
|
||||
order to recreate the list. This is handy for persisting your list, e.g. in
|
||||
a PropertiesFile object.
|
||||
|
||||
@see restoreFromString
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
/** Restores the list from a previously stringified version of the list.
|
||||
|
||||
Pass in a stringified version created with toString() in order to persist/restore
|
||||
your list.
|
||||
|
||||
@see toString
|
||||
*/
|
||||
void restoreFromString (const String& stringifiedVersion);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
StringArray files;
|
||||
int maxNumberOfItems;
|
||||
|
||||
JUCE_LEAK_DETECTOR (RecentlyOpenedFilesList)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,101 +1,101 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
SplashScreen::SplashScreen (const String& title, const Image& image, bool useDropShadow)
|
||||
: Component (title),
|
||||
backgroundImage (image),
|
||||
clickCountToDelete (0)
|
||||
{
|
||||
// You must supply a valid image here!
|
||||
jassert (backgroundImage.isValid());
|
||||
|
||||
setOpaque (! backgroundImage.hasAlphaChannel());
|
||||
|
||||
#if JUCE_IOS || JUCE_ANDROID
|
||||
const bool useFullScreen = true;
|
||||
#else
|
||||
const bool useFullScreen = false;
|
||||
#endif
|
||||
|
||||
makeVisible (image.getWidth(), image.getHeight(), useDropShadow, useFullScreen);
|
||||
}
|
||||
|
||||
SplashScreen::SplashScreen (const String& title, int width, int height, bool useDropShadow)
|
||||
: Component (title),
|
||||
clickCountToDelete (0)
|
||||
{
|
||||
makeVisible (width, height, useDropShadow, false);
|
||||
}
|
||||
|
||||
void SplashScreen::makeVisible (int w, int h, bool useDropShadow, bool fullscreen)
|
||||
{
|
||||
clickCountToDelete = Desktop::getInstance().getMouseButtonClickCounter();
|
||||
creationTime = Time::getCurrentTime();
|
||||
|
||||
const Rectangle<int> screenSize = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
|
||||
const int width = (fullscreen ? screenSize.getWidth() : w);
|
||||
const int height = (fullscreen ? screenSize.getHeight() : h);
|
||||
|
||||
setAlwaysOnTop (true);
|
||||
setVisible (true);
|
||||
centreWithSize (width, height);
|
||||
addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0);
|
||||
|
||||
if (fullscreen)
|
||||
getPeer()->setFullScreen (true);
|
||||
|
||||
toFront (false);
|
||||
}
|
||||
|
||||
SplashScreen::~SplashScreen() {}
|
||||
|
||||
void SplashScreen::deleteAfterDelay (RelativeTime timeout, bool removeOnMouseClick)
|
||||
{
|
||||
// Note that this method must be safe to call from non-GUI threads
|
||||
if (! removeOnMouseClick)
|
||||
clickCountToDelete = std::numeric_limits<int>::max();
|
||||
|
||||
minimumVisibleTime = timeout;
|
||||
|
||||
startTimer (50);
|
||||
}
|
||||
|
||||
void SplashScreen::paint (Graphics& g)
|
||||
{
|
||||
g.setOpacity (1.0f);
|
||||
g.drawImage (backgroundImage, getLocalBounds().toFloat(), RectanglePlacement (RectanglePlacement::fillDestination));
|
||||
}
|
||||
|
||||
void SplashScreen::timerCallback()
|
||||
{
|
||||
if (Time::getCurrentTime() > creationTime + minimumVisibleTime
|
||||
|| Desktop::getInstance().getMouseButtonClickCounter() > clickCountToDelete)
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
SplashScreen::SplashScreen (const String& title, const Image& image, bool useDropShadow)
|
||||
: Component (title),
|
||||
backgroundImage (image),
|
||||
clickCountToDelete (0)
|
||||
{
|
||||
// You must supply a valid image here!
|
||||
jassert (backgroundImage.isValid());
|
||||
|
||||
setOpaque (! backgroundImage.hasAlphaChannel());
|
||||
|
||||
#if JUCE_IOS || JUCE_ANDROID
|
||||
const bool useFullScreen = true;
|
||||
#else
|
||||
const bool useFullScreen = false;
|
||||
#endif
|
||||
|
||||
makeVisible (image.getWidth(), image.getHeight(), useDropShadow, useFullScreen);
|
||||
}
|
||||
|
||||
SplashScreen::SplashScreen (const String& title, int width, int height, bool useDropShadow)
|
||||
: Component (title),
|
||||
clickCountToDelete (0)
|
||||
{
|
||||
makeVisible (width, height, useDropShadow, false);
|
||||
}
|
||||
|
||||
void SplashScreen::makeVisible (int w, int h, bool useDropShadow, bool fullscreen)
|
||||
{
|
||||
clickCountToDelete = Desktop::getInstance().getMouseButtonClickCounter();
|
||||
creationTime = Time::getCurrentTime();
|
||||
|
||||
const Rectangle<int> screenSize = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
|
||||
const int width = (fullscreen ? screenSize.getWidth() : w);
|
||||
const int height = (fullscreen ? screenSize.getHeight() : h);
|
||||
|
||||
setAlwaysOnTop (true);
|
||||
setVisible (true);
|
||||
centreWithSize (width, height);
|
||||
addToDesktop (useDropShadow ? ComponentPeer::windowHasDropShadow : 0);
|
||||
|
||||
if (fullscreen)
|
||||
getPeer()->setFullScreen (true);
|
||||
|
||||
toFront (false);
|
||||
}
|
||||
|
||||
SplashScreen::~SplashScreen() {}
|
||||
|
||||
void SplashScreen::deleteAfterDelay (RelativeTime timeout, bool removeOnMouseClick)
|
||||
{
|
||||
// Note that this method must be safe to call from non-GUI threads
|
||||
if (! removeOnMouseClick)
|
||||
clickCountToDelete = std::numeric_limits<int>::max();
|
||||
|
||||
minimumVisibleTime = timeout;
|
||||
|
||||
startTimer (50);
|
||||
}
|
||||
|
||||
void SplashScreen::paint (Graphics& g)
|
||||
{
|
||||
g.setOpacity (1.0f);
|
||||
g.drawImage (backgroundImage, getLocalBounds().toFloat(), RectanglePlacement (RectanglePlacement::fillDestination));
|
||||
}
|
||||
|
||||
void SplashScreen::timerCallback()
|
||||
{
|
||||
if (Time::getCurrentTime() > creationTime + minimumVisibleTime
|
||||
|| Desktop::getInstance().getMouseButtonClickCounter() > clickCountToDelete)
|
||||
delete this;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,156 +1,156 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** A component for showing a splash screen while your app starts up.
|
||||
|
||||
This will automatically position itself, and can be told to delete itself after
|
||||
being on-screen for a minimum length of time.
|
||||
|
||||
To use it, just create one of these in your JUCEApplicationBase::initialise() method,
|
||||
and when your initialisation tasks have finished running, call its deleteAfterDelay()
|
||||
method to make it automatically get rid of itself.
|
||||
|
||||
Note that although you could call deleteAfterDelay() as soon as you create the
|
||||
SplashScreen object, if you've got a long initialisation procedure, you probably
|
||||
don't want the splash to time-out and disappear before the initialisation has
|
||||
finished, which is why it makes sense to not call this method until the end of
|
||||
your init tasks.
|
||||
|
||||
E.g. @code
|
||||
|
||||
void MyApp::initialise (const String& commandLine)
|
||||
{
|
||||
splash = new SplashScreen ("Welcome to my app!",
|
||||
ImageFileFormat::loadFrom (File ("/foobar/splash.jpg")),
|
||||
true);
|
||||
|
||||
// now kick off your initialisation work on some kind of thread or task, and
|
||||
launchBackgroundInitialisationThread();
|
||||
}
|
||||
|
||||
void MyApp::myInitialisationWorkFinished()
|
||||
{
|
||||
// ..assuming this is some kind of callback method that is triggered when
|
||||
// your background initialisation threads have finished, and it's time to open
|
||||
// your main window, etc..
|
||||
|
||||
splash->deleteAfterDelay (RelativeTime::seconds (4), false);
|
||||
|
||||
...etc...
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API SplashScreen : public Component,
|
||||
private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a SplashScreen object.
|
||||
|
||||
When called, the constructor will position the SplashScreen in the centre of the
|
||||
display, and after the time specified, it will automatically delete itself.
|
||||
|
||||
Bear in mind that if you call this during your JUCEApplicationBase::initialise()
|
||||
method and then block the message thread by performing some kind of task, then
|
||||
obviously neither your splash screen nor any other GUI will appear until you
|
||||
allow the message thread to resume and do its work. So if you have time-consuming
|
||||
tasks to do during startup, use a background thread for them.
|
||||
|
||||
After creating one of these (or your subclass of it), you should do your app's
|
||||
initialisation work, and then call the deleteAfterDelay() method to tell this object
|
||||
to delete itself after the user has had chance to get a good look at it.
|
||||
|
||||
If you're writing a custom splash screen class, there's another protected constructor
|
||||
that your subclass can call, which doesn't take an image.
|
||||
|
||||
@param title the name to give the component
|
||||
@param backgroundImage an image to draw on the component. The component's size
|
||||
will be set to the size of this image, and if the image is
|
||||
semi-transparent, the component will be made non-opaque
|
||||
@param useDropShadow if true, the window will have a drop shadow
|
||||
|
||||
*/
|
||||
SplashScreen (const String& title,
|
||||
const Image& backgroundImage,
|
||||
bool useDropShadow);
|
||||
|
||||
/** Destructor. */
|
||||
~SplashScreen() override;
|
||||
|
||||
/** Tells the component to auto-delete itself after a timeout period, or when the
|
||||
mouse is clicked.
|
||||
|
||||
You should call this after finishing your app's initialisation work.
|
||||
|
||||
Note that although you could call deleteAfterDelay() as soon as you create the
|
||||
SplashScreen object, if you've got a long initialisation procedure, you probably
|
||||
don't want the splash to time-out and disappear before your initialisation has
|
||||
finished, which is why it makes sense to not call this method and start the
|
||||
self-delete timer until you're ready.
|
||||
|
||||
It's safe to call this method from a non-GUI thread as long as there's no danger that
|
||||
the object may be being deleted at the same time.
|
||||
|
||||
@param minimumTotalTimeToDisplayFor how long the splash screen should stay visible for.
|
||||
Note that this time is measured from the construction-time of this
|
||||
object, not from the time that the deleteAfterDelay() method is
|
||||
called, so if you call this method after a long initialisation
|
||||
period, it may be deleted without any further delay.
|
||||
@param removeOnMouseClick if true, the window will be deleted as soon as the user clicks
|
||||
the mouse (anywhere)
|
||||
*/
|
||||
void deleteAfterDelay (RelativeTime minimumTotalTimeToDisplayFor,
|
||||
bool removeOnMouseClick);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** This constructor is for use by custom sub-classes that don't want to provide an image. */
|
||||
SplashScreen (const String& title, int width, int height, bool useDropShadow);
|
||||
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Image backgroundImage;
|
||||
Time creationTime;
|
||||
RelativeTime minimumVisibleTime;
|
||||
int clickCountToDelete;
|
||||
|
||||
void timerCallback() override;
|
||||
void makeVisible (int w, int h, bool shadow, bool fullscreen);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SplashScreen)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** A component for showing a splash screen while your app starts up.
|
||||
|
||||
This will automatically position itself, and can be told to delete itself after
|
||||
being on-screen for a minimum length of time.
|
||||
|
||||
To use it, just create one of these in your JUCEApplicationBase::initialise() method,
|
||||
and when your initialisation tasks have finished running, call its deleteAfterDelay()
|
||||
method to make it automatically get rid of itself.
|
||||
|
||||
Note that although you could call deleteAfterDelay() as soon as you create the
|
||||
SplashScreen object, if you've got a long initialisation procedure, you probably
|
||||
don't want the splash to time-out and disappear before the initialisation has
|
||||
finished, which is why it makes sense to not call this method until the end of
|
||||
your init tasks.
|
||||
|
||||
E.g. @code
|
||||
|
||||
void MyApp::initialise (const String& commandLine)
|
||||
{
|
||||
splash = new SplashScreen ("Welcome to my app!",
|
||||
ImageFileFormat::loadFrom (File ("/foobar/splash.jpg")),
|
||||
true);
|
||||
|
||||
// now kick off your initialisation work on some kind of thread or task, and
|
||||
launchBackgroundInitialisationThread();
|
||||
}
|
||||
|
||||
void MyApp::myInitialisationWorkFinished()
|
||||
{
|
||||
// ..assuming this is some kind of callback method that is triggered when
|
||||
// your background initialisation threads have finished, and it's time to open
|
||||
// your main window, etc..
|
||||
|
||||
splash->deleteAfterDelay (RelativeTime::seconds (4), false);
|
||||
|
||||
...etc...
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API SplashScreen : public Component,
|
||||
private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a SplashScreen object.
|
||||
|
||||
When called, the constructor will position the SplashScreen in the centre of the
|
||||
display, and after the time specified, it will automatically delete itself.
|
||||
|
||||
Bear in mind that if you call this during your JUCEApplicationBase::initialise()
|
||||
method and then block the message thread by performing some kind of task, then
|
||||
obviously neither your splash screen nor any other GUI will appear until you
|
||||
allow the message thread to resume and do its work. So if you have time-consuming
|
||||
tasks to do during startup, use a background thread for them.
|
||||
|
||||
After creating one of these (or your subclass of it), you should do your app's
|
||||
initialisation work, and then call the deleteAfterDelay() method to tell this object
|
||||
to delete itself after the user has had chance to get a good look at it.
|
||||
|
||||
If you're writing a custom splash screen class, there's another protected constructor
|
||||
that your subclass can call, which doesn't take an image.
|
||||
|
||||
@param title the name to give the component
|
||||
@param backgroundImage an image to draw on the component. The component's size
|
||||
will be set to the size of this image, and if the image is
|
||||
semi-transparent, the component will be made non-opaque
|
||||
@param useDropShadow if true, the window will have a drop shadow
|
||||
|
||||
*/
|
||||
SplashScreen (const String& title,
|
||||
const Image& backgroundImage,
|
||||
bool useDropShadow);
|
||||
|
||||
/** Destructor. */
|
||||
~SplashScreen() override;
|
||||
|
||||
/** Tells the component to auto-delete itself after a timeout period, or when the
|
||||
mouse is clicked.
|
||||
|
||||
You should call this after finishing your app's initialisation work.
|
||||
|
||||
Note that although you could call deleteAfterDelay() as soon as you create the
|
||||
SplashScreen object, if you've got a long initialisation procedure, you probably
|
||||
don't want the splash to time-out and disappear before your initialisation has
|
||||
finished, which is why it makes sense to not call this method and start the
|
||||
self-delete timer until you're ready.
|
||||
|
||||
It's safe to call this method from a non-GUI thread as long as there's no danger that
|
||||
the object may be being deleted at the same time.
|
||||
|
||||
@param minimumTotalTimeToDisplayFor how long the splash screen should stay visible for.
|
||||
Note that this time is measured from the construction-time of this
|
||||
object, not from the time that the deleteAfterDelay() method is
|
||||
called, so if you call this method after a long initialisation
|
||||
period, it may be deleted without any further delay.
|
||||
@param removeOnMouseClick if true, the window will be deleted as soon as the user clicks
|
||||
the mouse (anywhere)
|
||||
*/
|
||||
void deleteAfterDelay (RelativeTime minimumTotalTimeToDisplayFor,
|
||||
bool removeOnMouseClick);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** This constructor is for use by custom sub-classes that don't want to provide an image. */
|
||||
SplashScreen (const String& title, int width, int height, bool useDropShadow);
|
||||
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Image backgroundImage;
|
||||
Time creationTime;
|
||||
RelativeTime minimumVisibleTime;
|
||||
int clickCountToDelete;
|
||||
|
||||
void timerCallback() override;
|
||||
void makeVisible (int w, int h, bool shadow, bool fullscreen);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SplashScreen)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,42 +1,42 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_MAC
|
||||
|
||||
SystemTrayIconComponent::SystemTrayIconComponent()
|
||||
{
|
||||
addToDesktop (0);
|
||||
}
|
||||
|
||||
SystemTrayIconComponent::~SystemTrayIconComponent()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_MAC
|
||||
|
||||
SystemTrayIconComponent::SystemTrayIconComponent()
|
||||
{
|
||||
addToDesktop (0);
|
||||
}
|
||||
|
||||
SystemTrayIconComponent::~SystemTrayIconComponent()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,116 +1,116 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_MAC || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This component sits in the taskbar tray as a small icon.
|
||||
|
||||
(NB: The exact behaviour of this class will differ between OSes, and it
|
||||
isn't fully implemented for all OSes)
|
||||
|
||||
To use it, just create one of these components, but don't attempt to make it
|
||||
visible, add it to a parent, or put it on the desktop.
|
||||
|
||||
You can then call setIconImage() to create an icon for it in the taskbar.
|
||||
|
||||
To change the icon's tooltip, you can use setIconTooltip().
|
||||
|
||||
To respond to mouse-events, you can override the normal mouseDown(),
|
||||
mouseUp(), mouseDoubleClick() and mouseMove() methods, and although the x, y
|
||||
position will not be valid, you can use this to respond to clicks. Traditionally
|
||||
you'd use a left-click to show your application's window, and a right-click
|
||||
to show a pop-up menu.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API SystemTrayIconComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
SystemTrayIconComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~SystemTrayIconComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the image shown in the taskbar.
|
||||
|
||||
On Windows and Linux a full colour Image is used as an icon.
|
||||
On macOS a template image is used, where all non-transparent regions will be
|
||||
rendered in a monochrome colour selected dynamically by the operating system.
|
||||
|
||||
@param colourImage An colour image to use as an icon on Windows and Linux
|
||||
@param templateImage A template image to use as an icon on macOS
|
||||
*/
|
||||
void setIconImage (const Image& colourImage, const Image& templateImage);
|
||||
|
||||
/** Changes the icon's tooltip (if the current OS supports this). */
|
||||
void setIconTooltip (const String& tooltip);
|
||||
|
||||
/** Highlights the icon (if the current OS supports this). */
|
||||
void setHighlighted (bool);
|
||||
|
||||
/** Shows a floating text bubble pointing to the icon (if the current OS supports this). */
|
||||
void showInfoBubble (const String& title, const String& content);
|
||||
|
||||
/** Hides the icon's floating text bubble (if the current OS supports this). */
|
||||
void hideInfoBubble();
|
||||
|
||||
/** Returns the raw handle to whatever kind of internal OS structure is
|
||||
involved in showing this icon.
|
||||
@see ComponentPeer::getNativeHandle()
|
||||
*/
|
||||
void* getNativeHandle() const;
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
#endif
|
||||
|
||||
#if JUCE_MAC
|
||||
/** Shows a menu attached to the OSX menu bar icon. */
|
||||
void showDropdownMenu (const PopupMenu& menu);
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_PUBLIC_IN_DLL_BUILD (class Pimpl)
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
[[deprecated ("The new setIconImage function signature requires different images for macOS and the other platforms.")]]
|
||||
void setIconImage (const Image& newImage);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemTrayIconComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WINDOWS || JUCE_LINUX || JUCE_BSD || JUCE_MAC || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This component sits in the taskbar tray as a small icon.
|
||||
|
||||
(NB: The exact behaviour of this class will differ between OSes, and it
|
||||
isn't fully implemented for all OSes)
|
||||
|
||||
To use it, just create one of these components, but don't attempt to make it
|
||||
visible, add it to a parent, or put it on the desktop.
|
||||
|
||||
You can then call setIconImage() to create an icon for it in the taskbar.
|
||||
|
||||
To change the icon's tooltip, you can use setIconTooltip().
|
||||
|
||||
To respond to mouse-events, you can override the normal mouseDown(),
|
||||
mouseUp(), mouseDoubleClick() and mouseMove() methods, and although the x, y
|
||||
position will not be valid, you can use this to respond to clicks. Traditionally
|
||||
you'd use a left-click to show your application's window, and a right-click
|
||||
to show a pop-up menu.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API SystemTrayIconComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Constructor. */
|
||||
SystemTrayIconComponent();
|
||||
|
||||
/** Destructor. */
|
||||
~SystemTrayIconComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the image shown in the taskbar.
|
||||
|
||||
On Windows and Linux a full colour Image is used as an icon.
|
||||
On macOS a template image is used, where all non-transparent regions will be
|
||||
rendered in a monochrome colour selected dynamically by the operating system.
|
||||
|
||||
@param colourImage An colour image to use as an icon on Windows and Linux
|
||||
@param templateImage A template image to use as an icon on macOS
|
||||
*/
|
||||
void setIconImage (const Image& colourImage, const Image& templateImage);
|
||||
|
||||
/** Changes the icon's tooltip (if the current OS supports this). */
|
||||
void setIconTooltip (const String& tooltip);
|
||||
|
||||
/** Highlights the icon (if the current OS supports this). */
|
||||
void setHighlighted (bool);
|
||||
|
||||
/** Shows a floating text bubble pointing to the icon (if the current OS supports this). */
|
||||
void showInfoBubble (const String& title, const String& content);
|
||||
|
||||
/** Hides the icon's floating text bubble (if the current OS supports this). */
|
||||
void hideInfoBubble();
|
||||
|
||||
/** Returns the raw handle to whatever kind of internal OS structure is
|
||||
involved in showing this icon.
|
||||
@see ComponentPeer::getNativeHandle()
|
||||
*/
|
||||
void* getNativeHandle() const;
|
||||
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
#endif
|
||||
|
||||
#if JUCE_MAC
|
||||
/** Shows a menu attached to the OSX menu bar icon. */
|
||||
void showDropdownMenu (const PopupMenu& menu);
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_PUBLIC_IN_DLL_BUILD (class Pimpl)
|
||||
std::unique_ptr<Pimpl> pimpl;
|
||||
|
||||
[[deprecated ("The new setIconImage function signature requires different images for macOS and the other platforms.")]]
|
||||
void setIconImage (const Image& newImage);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemTrayIconComponent)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,287 +1,287 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WEB_BROWSER || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that displays an embedded web browser.
|
||||
|
||||
The browser itself will be platform-dependent. On Mac and iOS it will be
|
||||
WebKit, on Android it will be Chrome, and on Linux it will be WebKit.
|
||||
|
||||
On Windows it will be IE, but if JUCE_USE_WIN_WEBVIEW2 is enabled then using
|
||||
the WindowsWebView2WebBrowserComponent wrapper instead of this class directly
|
||||
will attempt to use the Microsoft Edge (Chromium) WebView2. See the documentation
|
||||
of that class for more information about its requirements.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API WebBrowserComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a WebBrowserComponent.
|
||||
|
||||
Once it's created and visible, send the browser to a URL using goToURL().
|
||||
|
||||
@param unloadPageWhenBrowserIsHidden if this is true, then when the browser
|
||||
component is taken offscreen, it'll clear the current page
|
||||
and replace it with a blank page - this can be handy to stop
|
||||
the browser using resources in the background when it's not
|
||||
actually being used.
|
||||
*/
|
||||
explicit WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true);
|
||||
|
||||
/** Destructor. */
|
||||
~WebBrowserComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sends the browser to a particular URL.
|
||||
|
||||
@param url the URL to go to.
|
||||
@param headers an optional set of parameters to put in the HTTP header. If
|
||||
you supply this, it should be a set of string in the form
|
||||
"HeaderKey: HeaderValue"
|
||||
@param postData an optional block of data that will be attached to the HTTP
|
||||
POST request
|
||||
*/
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers = nullptr,
|
||||
const MemoryBlock* postData = nullptr);
|
||||
|
||||
/** Stops the current page loading. */
|
||||
void stop();
|
||||
|
||||
/** Sends the browser back one page. */
|
||||
void goBack();
|
||||
|
||||
/** Sends the browser forward one page. */
|
||||
void goForward();
|
||||
|
||||
/** Refreshes the browser. */
|
||||
void refresh();
|
||||
|
||||
/** Clear cookies that the OS has stored for the WebComponents of this application */
|
||||
static void clearCookies();
|
||||
|
||||
//==============================================================================
|
||||
/** This callback is called when the browser is about to navigate
|
||||
to a new location.
|
||||
|
||||
You can override this method to perform some action when the user
|
||||
tries to go to a particular URL. To allow the operation to carry on,
|
||||
return true, or return false to stop the navigation happening.
|
||||
*/
|
||||
virtual bool pageAboutToLoad (const String& newURL) { ignoreUnused (newURL); return true; }
|
||||
|
||||
/** This callback happens when the browser has finished loading a page. */
|
||||
virtual void pageFinishedLoading (const String& url) { ignoreUnused (url); }
|
||||
|
||||
/** This callback happens when a network error was encountered while
|
||||
trying to load a page.
|
||||
|
||||
You can override this method to show some other error page by calling
|
||||
goToURL. Return true to allow the browser to carry on to the internal
|
||||
browser error page.
|
||||
|
||||
The errorInfo contains some platform dependent string describing the
|
||||
error.
|
||||
*/
|
||||
virtual bool pageLoadHadNetworkError (const String& errorInfo) { ignoreUnused (errorInfo); return true; }
|
||||
|
||||
/** This callback occurs when a script or other activity in the browser asks for
|
||||
the window to be closed.
|
||||
*/
|
||||
virtual void windowCloseRequest() {}
|
||||
|
||||
/** This callback occurs when the browser attempts to load a URL in a new window.
|
||||
This won't actually load the window but gives you a chance to either launch a
|
||||
new window yourself or just load the URL into the current window with goToURL().
|
||||
*/
|
||||
virtual void newWindowAttemptingToLoad (const String& newURL) { ignoreUnused (newURL); }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void visibilityChanged() override;
|
||||
/** @internal */
|
||||
void focusGained (FocusChangeType) override;
|
||||
|
||||
/** @internal */
|
||||
class Pimpl;
|
||||
|
||||
protected:
|
||||
friend class WindowsWebView2WebBrowserComponent;
|
||||
|
||||
/** @internal */
|
||||
struct ConstructWithoutPimpl
|
||||
{
|
||||
explicit ConstructWithoutPimpl (bool unloadOnHide) : unloadWhenHidden (unloadOnHide) {}
|
||||
const bool unloadWhenHidden;
|
||||
};
|
||||
explicit WebBrowserComponent (ConstructWithoutPimpl);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<Pimpl> browser;
|
||||
bool blankPageShown = false, unloadPageWhenHidden;
|
||||
String lastURL;
|
||||
StringArray lastHeaders;
|
||||
MemoryBlock lastPostData;
|
||||
|
||||
void reloadLastURL();
|
||||
void checkWindowAssociation();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Class used to create a set of preferences to pass to the WindowsWebView2WebBrowserComponent
|
||||
wrapper constructor to modify aspects of its behaviour and settings.
|
||||
|
||||
You can chain together a series of calls to this class's methods to create a set of whatever
|
||||
preferences you want to specify.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API WebView2Preferences
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Sets a custom location for the WebView2Loader.dll that is not a part of the
|
||||
standard system DLL search paths.
|
||||
*/
|
||||
WebView2Preferences withDLLLocation (const File& location) const { return with (&WebView2Preferences::dllLocation, location); }
|
||||
|
||||
/** Sets a non-default location for storing user data for the browser instance. */
|
||||
WebView2Preferences withUserDataFolder (const File& folder) const { return with (&WebView2Preferences::userDataFolder, folder); }
|
||||
|
||||
/** If this is set, the status bar usually displayed in the lower-left of the webview
|
||||
will be disabled.
|
||||
*/
|
||||
WebView2Preferences withStatusBarDisabled() const { return with (&WebView2Preferences::disableStatusBar, true); }
|
||||
|
||||
/** If this is set, a blank page will be displayed on error instead of the default
|
||||
built-in error page.
|
||||
*/
|
||||
WebView2Preferences withBuiltInErrorPageDisabled() const { return with (&WebView2Preferences::disableBuiltInErrorPage, true); }
|
||||
|
||||
/** Sets the background colour that WebView2 renders underneath all web content.
|
||||
|
||||
This colour must either be fully opaque or transparent. On Windows 7 this
|
||||
colour must be opaque.
|
||||
*/
|
||||
WebView2Preferences withBackgroundColour (const Colour& colour) const
|
||||
{
|
||||
// the background colour must be either fully opaque or transparent!
|
||||
jassert (colour.isOpaque() || colour.isTransparent());
|
||||
|
||||
return with (&WebView2Preferences::backgroundColour, colour);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File getDLLLocation() const { return dllLocation; }
|
||||
File getUserDataFolder() const { return userDataFolder; }
|
||||
bool getIsStatusBarDisabled() const noexcept { return disableStatusBar; }
|
||||
bool getIsBuiltInErrorPageDisabled() const noexcept { return disableBuiltInErrorPage; }
|
||||
Colour getBackgroundColour() const { return backgroundColour; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename Member, typename Item>
|
||||
WebView2Preferences with (Member&& member, Item&& item) const
|
||||
{
|
||||
auto options = *this;
|
||||
options.*member = std::forward<Item> (item);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
File dllLocation, userDataFolder;
|
||||
bool disableStatusBar = false, disableBuiltInErrorPage = false;
|
||||
Colour backgroundColour = Colours::white;
|
||||
};
|
||||
|
||||
/**
|
||||
If you have enabled the JUCE_USE_WIN_WEBVIEW2 flag then this wrapper will attempt to
|
||||
use the Microsoft Edge (Chromium) WebView2 control instead of IE on Windows. It will
|
||||
behave the same as WebBrowserComponent on all other platforms and will fall back to
|
||||
IE on Windows if the WebView2 requirements are not met.
|
||||
|
||||
This requires Microsoft Edge (minimum version 82.0.488.0) to be installed at runtime.
|
||||
|
||||
Currently this also requires that WebView2Loader.dll, which can be found in the
|
||||
Microsoft.Web.WebView package, is installed at runtime. As this is not a standard
|
||||
system DLL, we can't rely on it being found via the normal system DLL search paths.
|
||||
Therefore in order to use WebView2 you need to ensure that WebView2Loader.dll is
|
||||
installed either to a location covered by the Windows DLL system search paths or
|
||||
to the folder specified in the WebView2Preferences.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class WindowsWebView2WebBrowserComponent : public WebBrowserComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a WebBrowserComponent that is compatible with the WebView2 control
|
||||
on Windows.
|
||||
|
||||
@param unloadPageWhenBrowserIsHidden if this is true, then when the browser
|
||||
component is taken offscreen, it'll clear the current page
|
||||
and replace it with a blank page - this can be handy to stop
|
||||
the browser using resources in the background when it's not
|
||||
actually being used.
|
||||
@param preferences a set of preferences used to control aspects of the webview's
|
||||
behaviour.
|
||||
|
||||
@see WebView2Preferences
|
||||
*/
|
||||
WindowsWebView2WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true,
|
||||
const WebView2Preferences& preferences = {});
|
||||
|
||||
// This constructor has been deprecated. Use the new constructor that takes a
|
||||
// WebView2Preferences instead.
|
||||
explicit WindowsWebView2WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true,
|
||||
const File& dllLocation = {},
|
||||
const File& userDataFolder = {})
|
||||
: WindowsWebView2WebBrowserComponent (unloadPageWhenBrowserIsHidden,
|
||||
WebView2Preferences().withDLLLocation (dllLocation)
|
||||
.withUserDataFolder (userDataFolder))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_WEB_BROWSER || DOXYGEN
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A component that displays an embedded web browser.
|
||||
|
||||
The browser itself will be platform-dependent. On Mac and iOS it will be
|
||||
WebKit, on Android it will be Chrome, and on Linux it will be WebKit.
|
||||
|
||||
On Windows it will be IE, but if JUCE_USE_WIN_WEBVIEW2 is enabled then using
|
||||
the WindowsWebView2WebBrowserComponent wrapper instead of this class directly
|
||||
will attempt to use the Microsoft Edge (Chromium) WebView2. See the documentation
|
||||
of that class for more information about its requirements.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API WebBrowserComponent : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a WebBrowserComponent.
|
||||
|
||||
Once it's created and visible, send the browser to a URL using goToURL().
|
||||
|
||||
@param unloadPageWhenBrowserIsHidden if this is true, then when the browser
|
||||
component is taken offscreen, it'll clear the current page
|
||||
and replace it with a blank page - this can be handy to stop
|
||||
the browser using resources in the background when it's not
|
||||
actually being used.
|
||||
*/
|
||||
explicit WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true);
|
||||
|
||||
/** Destructor. */
|
||||
~WebBrowserComponent() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sends the browser to a particular URL.
|
||||
|
||||
@param url the URL to go to.
|
||||
@param headers an optional set of parameters to put in the HTTP header. If
|
||||
you supply this, it should be a set of string in the form
|
||||
"HeaderKey: HeaderValue"
|
||||
@param postData an optional block of data that will be attached to the HTTP
|
||||
POST request
|
||||
*/
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers = nullptr,
|
||||
const MemoryBlock* postData = nullptr);
|
||||
|
||||
/** Stops the current page loading. */
|
||||
void stop();
|
||||
|
||||
/** Sends the browser back one page. */
|
||||
void goBack();
|
||||
|
||||
/** Sends the browser forward one page. */
|
||||
void goForward();
|
||||
|
||||
/** Refreshes the browser. */
|
||||
void refresh();
|
||||
|
||||
/** Clear cookies that the OS has stored for the WebComponents of this application */
|
||||
static void clearCookies();
|
||||
|
||||
//==============================================================================
|
||||
/** This callback is called when the browser is about to navigate
|
||||
to a new location.
|
||||
|
||||
You can override this method to perform some action when the user
|
||||
tries to go to a particular URL. To allow the operation to carry on,
|
||||
return true, or return false to stop the navigation happening.
|
||||
*/
|
||||
virtual bool pageAboutToLoad (const String& newURL) { ignoreUnused (newURL); return true; }
|
||||
|
||||
/** This callback happens when the browser has finished loading a page. */
|
||||
virtual void pageFinishedLoading (const String& url) { ignoreUnused (url); }
|
||||
|
||||
/** This callback happens when a network error was encountered while
|
||||
trying to load a page.
|
||||
|
||||
You can override this method to show some other error page by calling
|
||||
goToURL. Return true to allow the browser to carry on to the internal
|
||||
browser error page.
|
||||
|
||||
The errorInfo contains some platform dependent string describing the
|
||||
error.
|
||||
*/
|
||||
virtual bool pageLoadHadNetworkError (const String& errorInfo) { ignoreUnused (errorInfo); return true; }
|
||||
|
||||
/** This callback occurs when a script or other activity in the browser asks for
|
||||
the window to be closed.
|
||||
*/
|
||||
virtual void windowCloseRequest() {}
|
||||
|
||||
/** This callback occurs when the browser attempts to load a URL in a new window.
|
||||
This won't actually load the window but gives you a chance to either launch a
|
||||
new window yourself or just load the URL into the current window with goToURL().
|
||||
*/
|
||||
virtual void newWindowAttemptingToLoad (const String& newURL) { ignoreUnused (newURL); }
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
void visibilityChanged() override;
|
||||
/** @internal */
|
||||
void focusGained (FocusChangeType) override;
|
||||
|
||||
/** @internal */
|
||||
class Pimpl;
|
||||
|
||||
protected:
|
||||
friend class WindowsWebView2WebBrowserComponent;
|
||||
|
||||
/** @internal */
|
||||
struct ConstructWithoutPimpl
|
||||
{
|
||||
explicit ConstructWithoutPimpl (bool unloadOnHide) : unloadWhenHidden (unloadOnHide) {}
|
||||
const bool unloadWhenHidden;
|
||||
};
|
||||
explicit WebBrowserComponent (ConstructWithoutPimpl);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::unique_ptr<Pimpl> browser;
|
||||
bool blankPageShown = false, unloadPageWhenHidden;
|
||||
String lastURL;
|
||||
StringArray lastHeaders;
|
||||
MemoryBlock lastPostData;
|
||||
|
||||
void reloadLastURL();
|
||||
void checkWindowAssociation();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebBrowserComponent)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Class used to create a set of preferences to pass to the WindowsWebView2WebBrowserComponent
|
||||
wrapper constructor to modify aspects of its behaviour and settings.
|
||||
|
||||
You can chain together a series of calls to this class's methods to create a set of whatever
|
||||
preferences you want to specify.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API WebView2Preferences
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Sets a custom location for the WebView2Loader.dll that is not a part of the
|
||||
standard system DLL search paths.
|
||||
*/
|
||||
JUCE_NODISCARD WebView2Preferences withDLLLocation (const File& location) const { return with (&WebView2Preferences::dllLocation, location); }
|
||||
|
||||
/** Sets a non-default location for storing user data for the browser instance. */
|
||||
WebView2Preferences withUserDataFolder (const File& folder) const { return with (&WebView2Preferences::userDataFolder, folder); }
|
||||
|
||||
/** If this is set, the status bar usually displayed in the lower-left of the webview
|
||||
will be disabled.
|
||||
*/
|
||||
JUCE_NODISCARD WebView2Preferences withStatusBarDisabled() const { return with (&WebView2Preferences::disableStatusBar, true); }
|
||||
|
||||
/** If this is set, a blank page will be displayed on error instead of the default
|
||||
built-in error page.
|
||||
*/
|
||||
JUCE_NODISCARD WebView2Preferences withBuiltInErrorPageDisabled() const { return with (&WebView2Preferences::disableBuiltInErrorPage, true); }
|
||||
|
||||
/** Sets the background colour that WebView2 renders underneath all web content.
|
||||
|
||||
This colour must either be fully opaque or transparent. On Windows 7 this
|
||||
colour must be opaque.
|
||||
*/
|
||||
JUCE_NODISCARD WebView2Preferences withBackgroundColour (const Colour& colour) const
|
||||
{
|
||||
// the background colour must be either fully opaque or transparent!
|
||||
jassert (colour.isOpaque() || colour.isTransparent());
|
||||
|
||||
return with (&WebView2Preferences::backgroundColour, colour);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File getDLLLocation() const { return dllLocation; }
|
||||
File getUserDataFolder() const { return userDataFolder; }
|
||||
bool getIsStatusBarDisabled() const noexcept { return disableStatusBar; }
|
||||
bool getIsBuiltInErrorPageDisabled() const noexcept { return disableBuiltInErrorPage; }
|
||||
Colour getBackgroundColour() const { return backgroundColour; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename Member, typename Item>
|
||||
WebView2Preferences with (Member&& member, Item&& item) const
|
||||
{
|
||||
auto options = *this;
|
||||
options.*member = std::forward<Item> (item);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
File dllLocation, userDataFolder;
|
||||
bool disableStatusBar = false, disableBuiltInErrorPage = false;
|
||||
Colour backgroundColour = Colours::white;
|
||||
};
|
||||
|
||||
/**
|
||||
If you have enabled the JUCE_USE_WIN_WEBVIEW2 flag then this wrapper will attempt to
|
||||
use the Microsoft Edge (Chromium) WebView2 control instead of IE on Windows. It will
|
||||
behave the same as WebBrowserComponent on all other platforms and will fall back to
|
||||
IE on Windows if the WebView2 requirements are not met.
|
||||
|
||||
This requires Microsoft Edge (minimum version 82.0.488.0) to be installed at runtime.
|
||||
|
||||
Currently this also requires that WebView2Loader.dll, which can be found in the
|
||||
Microsoft.Web.WebView package, is installed at runtime. As this is not a standard
|
||||
system DLL, we can't rely on it being found via the normal system DLL search paths.
|
||||
Therefore in order to use WebView2 you need to ensure that WebView2Loader.dll is
|
||||
installed either to a location covered by the Windows DLL system search paths or
|
||||
to the folder specified in the WebView2Preferences.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class WindowsWebView2WebBrowserComponent : public WebBrowserComponent
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a WebBrowserComponent that is compatible with the WebView2 control
|
||||
on Windows.
|
||||
|
||||
@param unloadPageWhenBrowserIsHidden if this is true, then when the browser
|
||||
component is taken offscreen, it'll clear the current page
|
||||
and replace it with a blank page - this can be handy to stop
|
||||
the browser using resources in the background when it's not
|
||||
actually being used.
|
||||
@param preferences a set of preferences used to control aspects of the webview's
|
||||
behaviour.
|
||||
|
||||
@see WebView2Preferences
|
||||
*/
|
||||
WindowsWebView2WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true,
|
||||
const WebView2Preferences& preferences = {});
|
||||
|
||||
// This constructor has been deprecated. Use the new constructor that takes a
|
||||
// WebView2Preferences instead.
|
||||
explicit WindowsWebView2WebBrowserComponent (bool unloadPageWhenBrowserIsHidden = true,
|
||||
const File& dllLocation = {},
|
||||
const File& userDataFolder = {})
|
||||
: WindowsWebView2WebBrowserComponent (unloadPageWhenBrowserIsHidden,
|
||||
WebView2Preferences().withDLLLocation (dllLocation)
|
||||
.withUserDataFolder (userDataFolder))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -2,15 +2,15 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
|
@ -2,15 +2,15 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
|
@ -2,15 +2,15 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
|
@ -2,15 +2,15 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
|
@ -1,176 +1,176 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class AndroidViewComponent::Pimpl : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
Pimpl (const LocalRef<jobject>& v, Component& comp)
|
||||
: ComponentMovementWatcher (&comp),
|
||||
view (v),
|
||||
owner (comp)
|
||||
{
|
||||
if (owner.isShowing())
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
~Pimpl() override
|
||||
{
|
||||
removeFromParent();
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
|
||||
{
|
||||
auto* topComp = owner.getTopLevelComponent();
|
||||
|
||||
if (topComp->getPeer() != nullptr)
|
||||
{
|
||||
auto pos = topComp->getLocalPoint (&owner, Point<int>());
|
||||
|
||||
Rectangle<int> r (pos.x, pos.y, owner.getWidth(), owner.getHeight());
|
||||
r *= Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale;
|
||||
|
||||
getEnv()->CallVoidMethod (view, AndroidView.layout, r.getX(), r.getY(),
|
||||
r.getRight(), r.getBottom());
|
||||
}
|
||||
}
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
auto* peer = owner.getPeer();
|
||||
|
||||
if (currentPeer != peer)
|
||||
{
|
||||
removeFromParent();
|
||||
currentPeer = peer;
|
||||
|
||||
addToParent();
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
VISIBLE = 0,
|
||||
INVISIBLE = 4
|
||||
};
|
||||
|
||||
getEnv()->CallVoidMethod (view, AndroidView.setVisibility, owner.isShowing() ? VISIBLE : INVISIBLE);
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
void componentBroughtToFront (Component& comp) override
|
||||
{
|
||||
ComponentMovementWatcher::componentBroughtToFront (comp);
|
||||
}
|
||||
|
||||
Rectangle<int> getViewBounds() const
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
int width = env->CallIntMethod (view, AndroidView.getWidth);
|
||||
int height = env->CallIntMethod (view, AndroidView.getHeight);
|
||||
|
||||
return Rectangle<int> (width, height);
|
||||
}
|
||||
|
||||
GlobalRef view;
|
||||
|
||||
private:
|
||||
void addToParent()
|
||||
{
|
||||
if (currentPeer != nullptr)
|
||||
{
|
||||
jobject peerView = (jobject) currentPeer->getNativeHandle();
|
||||
|
||||
// NB: Assuming a parent is always of ViewGroup type
|
||||
auto* env = getEnv();
|
||||
|
||||
env->CallVoidMethod (peerView, AndroidViewGroup.addView, view.get());
|
||||
componentMovedOrResized (false, false);
|
||||
}
|
||||
}
|
||||
|
||||
void removeFromParent()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto parentView = env->CallObjectMethod (view, AndroidView.getParent);
|
||||
|
||||
if (parentView != nullptr)
|
||||
{
|
||||
// Assuming a parent is always of ViewGroup type
|
||||
env->CallVoidMethod (parentView, AndroidViewGroup.removeView, view.get());
|
||||
}
|
||||
}
|
||||
|
||||
Component& owner;
|
||||
ComponentPeer* currentPeer = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AndroidViewComponent::AndroidViewComponent()
|
||||
{
|
||||
}
|
||||
|
||||
AndroidViewComponent::~AndroidViewComponent() {}
|
||||
|
||||
void AndroidViewComponent::setView (void* view)
|
||||
{
|
||||
if (view != getView())
|
||||
{
|
||||
pimpl.reset();
|
||||
|
||||
if (view != nullptr)
|
||||
{
|
||||
// explicitly create a new local ref here so that we don't
|
||||
// delete the users pointer
|
||||
auto* env = getEnv();
|
||||
auto localref = LocalRef<jobject>(env->NewLocalRef((jobject) view));
|
||||
|
||||
pimpl.reset (new Pimpl (localref, *this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* AndroidViewComponent::getView() const
|
||||
{
|
||||
return pimpl == nullptr ? nullptr : (void*) pimpl->view;
|
||||
}
|
||||
|
||||
void AndroidViewComponent::resizeToFitView()
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
setBounds (pimpl->getViewBounds());
|
||||
}
|
||||
|
||||
void AndroidViewComponent::paint (Graphics&) {}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class AndroidViewComponent::Pimpl : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
Pimpl (const LocalRef<jobject>& v, Component& comp)
|
||||
: ComponentMovementWatcher (&comp),
|
||||
view (v),
|
||||
owner (comp)
|
||||
{
|
||||
if (owner.isShowing())
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
~Pimpl() override
|
||||
{
|
||||
removeFromParent();
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
|
||||
{
|
||||
auto* topComp = owner.getTopLevelComponent();
|
||||
|
||||
if (topComp->getPeer() != nullptr)
|
||||
{
|
||||
auto pos = topComp->getLocalPoint (&owner, Point<int>());
|
||||
|
||||
Rectangle<int> r (pos.x, pos.y, owner.getWidth(), owner.getHeight());
|
||||
r *= Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale;
|
||||
|
||||
getEnv()->CallVoidMethod (view, AndroidView.layout, r.getX(), r.getY(),
|
||||
r.getRight(), r.getBottom());
|
||||
}
|
||||
}
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
auto* peer = owner.getPeer();
|
||||
|
||||
if (currentPeer != peer)
|
||||
{
|
||||
removeFromParent();
|
||||
currentPeer = peer;
|
||||
|
||||
addToParent();
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
VISIBLE = 0,
|
||||
INVISIBLE = 4
|
||||
};
|
||||
|
||||
getEnv()->CallVoidMethod (view, AndroidView.setVisibility, owner.isShowing() ? VISIBLE : INVISIBLE);
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
void componentBroughtToFront (Component& comp) override
|
||||
{
|
||||
ComponentMovementWatcher::componentBroughtToFront (comp);
|
||||
}
|
||||
|
||||
Rectangle<int> getViewBounds() const
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
int width = env->CallIntMethod (view, AndroidView.getWidth);
|
||||
int height = env->CallIntMethod (view, AndroidView.getHeight);
|
||||
|
||||
return Rectangle<int> (width, height);
|
||||
}
|
||||
|
||||
GlobalRef view;
|
||||
|
||||
private:
|
||||
void addToParent()
|
||||
{
|
||||
if (currentPeer != nullptr)
|
||||
{
|
||||
jobject peerView = (jobject) currentPeer->getNativeHandle();
|
||||
|
||||
// NB: Assuming a parent is always of ViewGroup type
|
||||
auto* env = getEnv();
|
||||
|
||||
env->CallVoidMethod (peerView, AndroidViewGroup.addView, view.get());
|
||||
componentMovedOrResized (false, false);
|
||||
}
|
||||
}
|
||||
|
||||
void removeFromParent()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto parentView = env->CallObjectMethod (view, AndroidView.getParent);
|
||||
|
||||
if (parentView != nullptr)
|
||||
{
|
||||
// Assuming a parent is always of ViewGroup type
|
||||
env->CallVoidMethod (parentView, AndroidViewGroup.removeView, view.get());
|
||||
}
|
||||
}
|
||||
|
||||
Component& owner;
|
||||
ComponentPeer* currentPeer = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AndroidViewComponent::AndroidViewComponent()
|
||||
{
|
||||
}
|
||||
|
||||
AndroidViewComponent::~AndroidViewComponent() {}
|
||||
|
||||
void AndroidViewComponent::setView (void* view)
|
||||
{
|
||||
if (view != getView())
|
||||
{
|
||||
pimpl.reset();
|
||||
|
||||
if (view != nullptr)
|
||||
{
|
||||
// explicitly create a new local ref here so that we don't
|
||||
// delete the users pointer
|
||||
auto* env = getEnv();
|
||||
auto localref = LocalRef<jobject>(env->NewLocalRef((jobject) view));
|
||||
|
||||
pimpl.reset (new Pimpl (localref, *this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* AndroidViewComponent::getView() const
|
||||
{
|
||||
return pimpl == nullptr ? nullptr : (void*) pimpl->view;
|
||||
}
|
||||
|
||||
void AndroidViewComponent::resizeToFitView()
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
setBounds (pimpl->getViewBounds());
|
||||
}
|
||||
|
||||
void AndroidViewComponent::paint (Graphics&) {}
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -2,15 +2,15 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
|
@ -1,151 +1,151 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class SystemTrayIconComponent::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (const Image& im, Window windowH) : image (im)
|
||||
{
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
|
||||
auto* display = XWindowSystem::getInstance()->getDisplay();
|
||||
|
||||
auto* screen = X11Symbols::getInstance()->xDefaultScreenOfDisplay (display);
|
||||
auto screenNumber = X11Symbols::getInstance()->xScreenNumberOfScreen (screen);
|
||||
|
||||
String screenAtom ("_NET_SYSTEM_TRAY_S");
|
||||
screenAtom << screenNumber;
|
||||
Atom selectionAtom = XWindowSystemUtilities::Atoms::getCreating (display, screenAtom.toUTF8());
|
||||
|
||||
X11Symbols::getInstance()->xGrabServer (display);
|
||||
auto managerWin = X11Symbols::getInstance()->xGetSelectionOwner (display, selectionAtom);
|
||||
|
||||
if (managerWin != None)
|
||||
X11Symbols::getInstance()->xSelectInput (display, managerWin, StructureNotifyMask);
|
||||
|
||||
X11Symbols::getInstance()->xUngrabServer (display);
|
||||
X11Symbols::getInstance()->xFlush (display);
|
||||
|
||||
if (managerWin != None)
|
||||
{
|
||||
XEvent ev = { 0 };
|
||||
ev.xclient.type = ClientMessage;
|
||||
ev.xclient.window = managerWin;
|
||||
ev.xclient.message_type = XWindowSystemUtilities::Atoms::getCreating (display, "_NET_SYSTEM_TRAY_OPCODE");
|
||||
ev.xclient.format = 32;
|
||||
ev.xclient.data.l[0] = CurrentTime;
|
||||
ev.xclient.data.l[1] = 0 /*SYSTEM_TRAY_REQUEST_DOCK*/;
|
||||
ev.xclient.data.l[2] = (long) windowH;
|
||||
ev.xclient.data.l[3] = 0;
|
||||
ev.xclient.data.l[4] = 0;
|
||||
|
||||
X11Symbols::getInstance()->xSendEvent (display, managerWin, False, NoEventMask, &ev);
|
||||
X11Symbols::getInstance()->xSync (display, False);
|
||||
}
|
||||
|
||||
// For older KDE's ...
|
||||
long atomData = 1;
|
||||
Atom trayAtom = XWindowSystemUtilities::Atoms::getCreating (display, "KWM_DOCKWINDOW");
|
||||
X11Symbols::getInstance()->xChangeProperty (display, windowH, trayAtom, trayAtom,
|
||||
32, PropModeReplace, (unsigned char*) &atomData, 1);
|
||||
|
||||
// For more recent KDE's...
|
||||
trayAtom = XWindowSystemUtilities::Atoms::getCreating (display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR");
|
||||
X11Symbols::getInstance()->xChangeProperty (display, windowH, trayAtom, XA_WINDOW,
|
||||
32, PropModeReplace, (unsigned char*) &windowH, 1);
|
||||
|
||||
// A minimum size must be specified for GNOME and Xfce, otherwise the icon is displayed with a width of 1
|
||||
if (auto* hints = X11Symbols::getInstance()->xAllocSizeHints())
|
||||
{
|
||||
hints->flags = PMinSize;
|
||||
hints->min_width = 22;
|
||||
hints->min_height = 22;
|
||||
X11Symbols::getInstance()->xSetWMNormalHints (display, windowH, hints);
|
||||
X11Symbols::getInstance()->xFree (hints);
|
||||
}
|
||||
}
|
||||
|
||||
Image image;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void SystemTrayIconComponent::setIconImage (const Image& colourImage, const Image&)
|
||||
{
|
||||
pimpl.reset();
|
||||
|
||||
if (colourImage.isValid())
|
||||
{
|
||||
if (! isOnDesktop())
|
||||
addToDesktop (0);
|
||||
|
||||
pimpl.reset (new Pimpl (colourImage, (Window) getWindowHandle()));
|
||||
|
||||
setVisible (true);
|
||||
toFront (false);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::paint (Graphics& g)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
g.drawImage (pimpl->image, getLocalBounds().toFloat(),
|
||||
RectanglePlacement::xLeft | RectanglePlacement::yTop | RectanglePlacement::onlyReduceInSize);
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setIconTooltip (const String& /*tooltip*/)
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setHighlighted (bool)
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::showInfoBubble (const String& /*title*/, const String& /*content*/)
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::hideInfoBubble()
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void* SystemTrayIconComponent::getNativeHandle() const
|
||||
{
|
||||
return getWindowHandle();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class SystemTrayIconComponent::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (const Image& im, Window windowH) : image (im)
|
||||
{
|
||||
XWindowSystemUtilities::ScopedXLock xLock;
|
||||
|
||||
auto* display = XWindowSystem::getInstance()->getDisplay();
|
||||
|
||||
auto* screen = X11Symbols::getInstance()->xDefaultScreenOfDisplay (display);
|
||||
auto screenNumber = X11Symbols::getInstance()->xScreenNumberOfScreen (screen);
|
||||
|
||||
String screenAtom ("_NET_SYSTEM_TRAY_S");
|
||||
screenAtom << screenNumber;
|
||||
Atom selectionAtom = XWindowSystemUtilities::Atoms::getCreating (display, screenAtom.toUTF8());
|
||||
|
||||
X11Symbols::getInstance()->xGrabServer (display);
|
||||
auto managerWin = X11Symbols::getInstance()->xGetSelectionOwner (display, selectionAtom);
|
||||
|
||||
if (managerWin != None)
|
||||
X11Symbols::getInstance()->xSelectInput (display, managerWin, StructureNotifyMask);
|
||||
|
||||
X11Symbols::getInstance()->xUngrabServer (display);
|
||||
X11Symbols::getInstance()->xFlush (display);
|
||||
|
||||
if (managerWin != None)
|
||||
{
|
||||
XEvent ev = { 0 };
|
||||
ev.xclient.type = ClientMessage;
|
||||
ev.xclient.window = managerWin;
|
||||
ev.xclient.message_type = XWindowSystemUtilities::Atoms::getCreating (display, "_NET_SYSTEM_TRAY_OPCODE");
|
||||
ev.xclient.format = 32;
|
||||
ev.xclient.data.l[0] = CurrentTime;
|
||||
ev.xclient.data.l[1] = 0 /*SYSTEM_TRAY_REQUEST_DOCK*/;
|
||||
ev.xclient.data.l[2] = (long) windowH;
|
||||
ev.xclient.data.l[3] = 0;
|
||||
ev.xclient.data.l[4] = 0;
|
||||
|
||||
X11Symbols::getInstance()->xSendEvent (display, managerWin, False, NoEventMask, &ev);
|
||||
X11Symbols::getInstance()->xSync (display, False);
|
||||
}
|
||||
|
||||
// For older KDE's ...
|
||||
long atomData = 1;
|
||||
Atom trayAtom = XWindowSystemUtilities::Atoms::getCreating (display, "KWM_DOCKWINDOW");
|
||||
X11Symbols::getInstance()->xChangeProperty (display, windowH, trayAtom, trayAtom,
|
||||
32, PropModeReplace, (unsigned char*) &atomData, 1);
|
||||
|
||||
// For more recent KDE's...
|
||||
trayAtom = XWindowSystemUtilities::Atoms::getCreating (display, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR");
|
||||
X11Symbols::getInstance()->xChangeProperty (display, windowH, trayAtom, XA_WINDOW,
|
||||
32, PropModeReplace, (unsigned char*) &windowH, 1);
|
||||
|
||||
// A minimum size must be specified for GNOME and Xfce, otherwise the icon is displayed with a width of 1
|
||||
if (auto* hints = X11Symbols::getInstance()->xAllocSizeHints())
|
||||
{
|
||||
hints->flags = PMinSize;
|
||||
hints->min_width = 22;
|
||||
hints->min_height = 22;
|
||||
X11Symbols::getInstance()->xSetWMNormalHints (display, windowH, hints);
|
||||
X11Symbols::getInstance()->xFree (hints);
|
||||
}
|
||||
}
|
||||
|
||||
Image image;
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void SystemTrayIconComponent::setIconImage (const Image& colourImage, const Image&)
|
||||
{
|
||||
pimpl.reset();
|
||||
|
||||
if (colourImage.isValid())
|
||||
{
|
||||
if (! isOnDesktop())
|
||||
addToDesktop (0);
|
||||
|
||||
pimpl.reset (new Pimpl (colourImage, (Window) getWindowHandle()));
|
||||
|
||||
setVisible (true);
|
||||
toFront (false);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::paint (Graphics& g)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
g.drawImage (pimpl->image, getLocalBounds().toFloat(),
|
||||
RectanglePlacement::xLeft | RectanglePlacement::yTop | RectanglePlacement::onlyReduceInSize);
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setIconTooltip (const String& /*tooltip*/)
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setHighlighted (bool)
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::showInfoBubble (const String& /*title*/, const String& /*content*/)
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::hideInfoBubble()
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void* SystemTrayIconComponent::getNativeHandle() const
|
||||
{
|
||||
return getWindowHandle();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -2,15 +2,15 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
@ -47,12 +47,17 @@ namespace
|
||||
io_iterator_t iter = 0;
|
||||
io_object_t iod = 0;
|
||||
|
||||
const auto defaultPort =
|
||||
#if defined (MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0
|
||||
kIOMainPortDefault;
|
||||
#else
|
||||
kIOMasterPortDefault;
|
||||
#endif
|
||||
const auto defaultPort = []
|
||||
{
|
||||
#if defined (MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0
|
||||
if (@available (macOS 12.0, *))
|
||||
return kIOMainPortDefault;
|
||||
#endif
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
||||
return kIOMasterPortDefault;
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}();
|
||||
|
||||
if (IOServiceGetMatchingServices (defaultPort, dict, &iter) == kIOReturnSuccess
|
||||
&& iter != 0)
|
||||
|
@ -1,346 +0,0 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Creates a floating carbon window that can be used to hold a carbon UI.
|
||||
|
||||
This is a handy class that's designed to be inlined where needed, e.g.
|
||||
in the audio plugin hosting code.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class CarbonViewWrapperComponent : public Component,
|
||||
public ComponentMovementWatcher,
|
||||
public Timer
|
||||
{
|
||||
public:
|
||||
CarbonViewWrapperComponent()
|
||||
: ComponentMovementWatcher (this),
|
||||
carbonWindow (nil),
|
||||
keepPluginWindowWhenHidden (false),
|
||||
wrapperWindow (nil),
|
||||
embeddedView (0),
|
||||
recursiveResize (false),
|
||||
repaintChildOnCreation (true)
|
||||
{
|
||||
}
|
||||
|
||||
~CarbonViewWrapperComponent()
|
||||
{
|
||||
jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor!
|
||||
}
|
||||
|
||||
virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0;
|
||||
virtual void removeView (HIViewRef embeddedView) = 0;
|
||||
virtual void handleMouseDown (int, int) {}
|
||||
virtual void handlePaint() {}
|
||||
|
||||
virtual bool getEmbeddedViewSize (int& w, int& h)
|
||||
{
|
||||
if (embeddedView == 0)
|
||||
return false;
|
||||
|
||||
HIRect bounds;
|
||||
HIViewGetBounds (embeddedView, &bounds);
|
||||
w = jmax (1, roundToInt (bounds.size.width));
|
||||
h = jmax (1, roundToInt (bounds.size.height));
|
||||
return true;
|
||||
}
|
||||
|
||||
void createWindow()
|
||||
{
|
||||
if (wrapperWindow == nil)
|
||||
{
|
||||
Rect r;
|
||||
r.left = (short) getScreenX();
|
||||
r.top = (short) getScreenY();
|
||||
r.right = (short) (r.left + getWidth());
|
||||
r.bottom = (short) (r.top + getHeight());
|
||||
|
||||
CreateNewWindow (kDocumentWindowClass,
|
||||
(WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute
|
||||
| kWindowNoShadowAttribute | kWindowNoTitleBarAttribute),
|
||||
&r, &wrapperWindow);
|
||||
|
||||
jassert (wrapperWindow != 0);
|
||||
if (wrapperWindow == 0)
|
||||
return;
|
||||
|
||||
carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow];
|
||||
|
||||
[getOwnerWindow() addChildWindow: carbonWindow
|
||||
ordered: NSWindowAbove];
|
||||
|
||||
embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow));
|
||||
|
||||
// Check for the plugin creating its own floating window, and if there is one,
|
||||
// we need to reparent it to make it visible..
|
||||
if (carbonWindow.childWindows.count > 0)
|
||||
if (NSWindow* floatingChildWindow = [[carbonWindow childWindows] objectAtIndex: 0])
|
||||
[getOwnerWindow() addChildWindow: floatingChildWindow
|
||||
ordered: NSWindowAbove];
|
||||
|
||||
EventTypeSpec windowEventTypes[] =
|
||||
{
|
||||
{ kEventClassWindow, kEventWindowGetClickActivation },
|
||||
{ kEventClassWindow, kEventWindowHandleDeactivate },
|
||||
{ kEventClassWindow, kEventWindowBoundsChanging },
|
||||
{ kEventClassMouse, kEventMouseDown },
|
||||
{ kEventClassMouse, kEventMouseMoved },
|
||||
{ kEventClassMouse, kEventMouseDragged },
|
||||
{ kEventClassMouse, kEventMouseUp },
|
||||
{ kEventClassWindow, kEventWindowDrawContent },
|
||||
{ kEventClassWindow, kEventWindowShown },
|
||||
{ kEventClassWindow, kEventWindowHidden }
|
||||
};
|
||||
|
||||
EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback);
|
||||
InstallWindowEventHandler (wrapperWindow, upp,
|
||||
sizeof (windowEventTypes) / sizeof (EventTypeSpec),
|
||||
windowEventTypes, this, &eventHandlerRef);
|
||||
|
||||
setOurSizeToEmbeddedViewSize();
|
||||
setEmbeddedWindowToOurSize();
|
||||
|
||||
creationTime = Time::getCurrentTime();
|
||||
}
|
||||
}
|
||||
|
||||
void deleteWindow()
|
||||
{
|
||||
removeView (embeddedView);
|
||||
embeddedView = 0;
|
||||
|
||||
if (wrapperWindow != nil)
|
||||
{
|
||||
NSWindow* ownerWindow = getOwnerWindow();
|
||||
|
||||
if ([[ownerWindow childWindows] count] > 0)
|
||||
{
|
||||
[ownerWindow removeChildWindow: carbonWindow];
|
||||
[carbonWindow close];
|
||||
}
|
||||
|
||||
RemoveEventHandler (eventHandlerRef);
|
||||
DisposeWindow (wrapperWindow);
|
||||
wrapperWindow = nil;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setOurSizeToEmbeddedViewSize()
|
||||
{
|
||||
int w, h;
|
||||
if (getEmbeddedViewSize (w, h))
|
||||
{
|
||||
if (w != getWidth() || h != getHeight())
|
||||
{
|
||||
startTimer (50);
|
||||
setSize (w, h);
|
||||
|
||||
if (Component* p = getParentComponent())
|
||||
p->setSize (w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
startTimer (jlimit (50, 500, getTimerInterval() + 20));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void setEmbeddedWindowToOurSize()
|
||||
{
|
||||
if (! recursiveResize)
|
||||
{
|
||||
recursiveResize = true;
|
||||
|
||||
if (embeddedView != 0)
|
||||
{
|
||||
HIRect r;
|
||||
r.origin.x = 0;
|
||||
r.origin.y = 0;
|
||||
r.size.width = (float) getWidth();
|
||||
r.size.height = (float) getHeight();
|
||||
HIViewSetFrame (embeddedView, &r);
|
||||
}
|
||||
|
||||
if (wrapperWindow != nil)
|
||||
{
|
||||
jassert (getTopLevelComponent()->getDesktopScaleFactor() == 1.0f);
|
||||
Rectangle<int> screenBounds (getScreenBounds() * Desktop::getInstance().getGlobalScaleFactor());
|
||||
|
||||
Rect wr;
|
||||
wr.left = (short) screenBounds.getX();
|
||||
wr.top = (short) screenBounds.getY();
|
||||
wr.right = (short) screenBounds.getRight();
|
||||
wr.bottom = (short) screenBounds.getBottom();
|
||||
|
||||
SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr);
|
||||
|
||||
// This group stuff is mainly a workaround for Mackie plugins like FinalMix..
|
||||
WindowGroupRef group = GetWindowGroup (wrapperWindow);
|
||||
WindowRef attachedWindow;
|
||||
|
||||
if (GetIndexedWindow (group, 2, kWindowGroupContentsReturnWindows, &attachedWindow) == noErr)
|
||||
{
|
||||
SelectWindow (attachedWindow);
|
||||
ActivateWindow (attachedWindow, TRUE);
|
||||
HideWindow (wrapperWindow);
|
||||
}
|
||||
|
||||
ShowWindow (wrapperWindow);
|
||||
}
|
||||
|
||||
recursiveResize = false;
|
||||
}
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
|
||||
{
|
||||
setEmbeddedWindowToOurSize();
|
||||
}
|
||||
|
||||
// (overridden to intercept movements of the top-level window)
|
||||
void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) override
|
||||
{
|
||||
ComponentMovementWatcher::componentMovedOrResized (component, wasMoved, wasResized);
|
||||
|
||||
if (&component == getTopLevelComponent())
|
||||
setEmbeddedWindowToOurSize();
|
||||
}
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
deleteWindow();
|
||||
createWindow();
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
if (isShowing())
|
||||
createWindow();
|
||||
else if (! keepPluginWindowWhenHidden)
|
||||
deleteWindow();
|
||||
|
||||
setEmbeddedWindowToOurSize();
|
||||
}
|
||||
|
||||
static void recursiveHIViewRepaint (HIViewRef view)
|
||||
{
|
||||
HIViewSetNeedsDisplay (view, true);
|
||||
HIViewRef child = HIViewGetFirstSubview (view);
|
||||
|
||||
while (child != 0)
|
||||
{
|
||||
recursiveHIViewRepaint (child);
|
||||
child = HIViewGetNextView (child);
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (isShowing())
|
||||
{
|
||||
setOurSizeToEmbeddedViewSize();
|
||||
|
||||
// To avoid strange overpainting problems when the UI is first opened, we'll
|
||||
// repaint it a few times during the first second that it's on-screen..
|
||||
if (repaintChildOnCreation && (Time::getCurrentTime() - creationTime).inMilliseconds() < 1000)
|
||||
recursiveHIViewRepaint (HIViewGetRoot (wrapperWindow));
|
||||
}
|
||||
}
|
||||
|
||||
void setRepaintsChildHIViewWhenCreated (bool b) noexcept
|
||||
{
|
||||
repaintChildOnCreation = b;
|
||||
}
|
||||
|
||||
OSStatus carbonEventHandler (EventHandlerCallRef /*nextHandlerRef*/, EventRef event)
|
||||
{
|
||||
switch (GetEventKind (event))
|
||||
{
|
||||
case kEventWindowHandleDeactivate:
|
||||
ActivateWindow (wrapperWindow, TRUE);
|
||||
return noErr;
|
||||
|
||||
case kEventWindowGetClickActivation:
|
||||
{
|
||||
getTopLevelComponent()->toFront (false);
|
||||
[carbonWindow makeKeyAndOrderFront: nil];
|
||||
|
||||
ClickActivationResult howToHandleClick = kActivateAndHandleClick;
|
||||
|
||||
SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult,
|
||||
sizeof (ClickActivationResult), &howToHandleClick);
|
||||
|
||||
if (embeddedView != 0)
|
||||
HIViewSetNeedsDisplay (embeddedView, true);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
}
|
||||
|
||||
return eventNotHandledErr;
|
||||
}
|
||||
|
||||
static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, EventRef event, void* userData)
|
||||
{
|
||||
return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event);
|
||||
}
|
||||
|
||||
NSWindow* carbonWindow;
|
||||
bool keepPluginWindowWhenHidden;
|
||||
|
||||
protected:
|
||||
WindowRef wrapperWindow;
|
||||
HIViewRef embeddedView;
|
||||
bool recursiveResize, repaintChildOnCreation;
|
||||
Time creationTime;
|
||||
|
||||
EventHandlerRef eventHandlerRef;
|
||||
|
||||
NSWindow* getOwnerWindow() const { return [((NSView*) getWindowHandle()) window]; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Non-public utility function that hosts can use if they need to get hold of the
|
||||
// internals of a carbon wrapper window..
|
||||
void* getCarbonWindow (Component* possibleCarbonComponent)
|
||||
{
|
||||
if (CarbonViewWrapperComponent* cv = dynamic_cast<CarbonViewWrapperComponent*> (possibleCarbonComponent))
|
||||
return cv->carbonWindow;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace juce
|
@ -2,15 +2,15 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
@ -26,85 +26,6 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
static const auto nsViewFrameChangedSelector = @selector (frameChanged:);
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
struct NSViewCallbackInterface
|
||||
{
|
||||
virtual ~NSViewCallbackInterface() = default;
|
||||
virtual void frameChanged() = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct NSViewFrameChangeCallbackClass : public ObjCClass<NSObject>
|
||||
{
|
||||
NSViewFrameChangeCallbackClass()
|
||||
: ObjCClass ("JUCE_NSViewCallback_")
|
||||
{
|
||||
addIvar<NSViewCallbackInterface*> ("target");
|
||||
|
||||
addMethod (nsViewFrameChangedSelector, frameChanged, "v@:@");
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static void setTarget (id self, NSViewCallbackInterface* c)
|
||||
{
|
||||
object_setInstanceVariable (self, "target", c);
|
||||
}
|
||||
|
||||
private:
|
||||
static void frameChanged (id self, SEL, NSNotification*)
|
||||
{
|
||||
if (auto* target = getIvar<NSViewCallbackInterface*> (self, "target"))
|
||||
target->frameChanged();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (NSViewFrameChangeCallbackClass)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class NSViewFrameWatcher : private NSViewCallbackInterface
|
||||
{
|
||||
public:
|
||||
NSViewFrameWatcher (NSView* viewToWatch, std::function<void()> viewResizedIn)
|
||||
: viewResized (std::move (viewResizedIn)), callback (makeCallbackForView (viewToWatch))
|
||||
{
|
||||
}
|
||||
|
||||
~NSViewFrameWatcher() override
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: callback];
|
||||
[callback release];
|
||||
callback = nil;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (NSViewFrameWatcher)
|
||||
JUCE_DECLARE_NON_MOVEABLE (NSViewFrameWatcher)
|
||||
|
||||
private:
|
||||
id makeCallbackForView (NSView* view)
|
||||
{
|
||||
static NSViewFrameChangeCallbackClass cls;
|
||||
auto* result = [cls.createInstance() init];
|
||||
NSViewFrameChangeCallbackClass::setTarget (result, this);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver: result
|
||||
selector: nsViewFrameChangedSelector
|
||||
name: NSViewFrameDidChangeNotification
|
||||
object: view];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void frameChanged() override { viewResized(); }
|
||||
|
||||
std::function<void()> viewResized;
|
||||
id callback;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class NSViewAttachment : public ReferenceCountedObject,
|
||||
public ComponentMovementWatcher
|
||||
{
|
||||
|
111
deps/juce/modules/juce_gui_extra/native/juce_mac_NSViewFrameWatcher.h
vendored
Normal file
111
deps/juce/modules/juce_gui_extra/native/juce_mac_NSViewFrameWatcher.h
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_MAC
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
const auto nsViewFrameChangedSelector = @selector (frameChanged:);
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
struct NSViewCallbackInterface
|
||||
{
|
||||
virtual ~NSViewCallbackInterface() = default;
|
||||
virtual void frameChanged() = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct NSViewFrameChangeCallbackClass : public ObjCClass<NSObject>
|
||||
{
|
||||
NSViewFrameChangeCallbackClass()
|
||||
: ObjCClass ("JUCE_NSViewCallback_")
|
||||
{
|
||||
addIvar<NSViewCallbackInterface*> ("target");
|
||||
|
||||
addMethod (nsViewFrameChangedSelector, frameChanged);
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static void setTarget (id self, NSViewCallbackInterface* c)
|
||||
{
|
||||
object_setInstanceVariable (self, "target", c);
|
||||
}
|
||||
|
||||
private:
|
||||
static void frameChanged (id self, SEL, NSNotification*)
|
||||
{
|
||||
if (auto* target = getIvar<NSViewCallbackInterface*> (self, "target"))
|
||||
target->frameChanged();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (NSViewFrameChangeCallbackClass)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class NSViewFrameWatcher : private NSViewCallbackInterface
|
||||
{
|
||||
public:
|
||||
NSViewFrameWatcher (NSView* viewToWatch, std::function<void()> viewResizedIn)
|
||||
: viewResized (std::move (viewResizedIn)), callback (makeCallbackForView (viewToWatch))
|
||||
{
|
||||
}
|
||||
|
||||
~NSViewFrameWatcher() override
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: callback];
|
||||
[callback release];
|
||||
callback = nil;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (NSViewFrameWatcher)
|
||||
JUCE_DECLARE_NON_MOVEABLE (NSViewFrameWatcher)
|
||||
|
||||
private:
|
||||
id makeCallbackForView (NSView* view)
|
||||
{
|
||||
static NSViewFrameChangeCallbackClass cls;
|
||||
auto* result = [cls.createInstance() init];
|
||||
NSViewFrameChangeCallbackClass::setTarget (result, this);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver: result
|
||||
selector: nsViewFrameChangedSelector
|
||||
name: NSViewFrameDidChangeNotification
|
||||
object: view];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void frameChanged() override { viewResized(); }
|
||||
|
||||
std::function<void()> viewResized;
|
||||
id callback;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,444 +1,445 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wdeprecated-declarations")
|
||||
|
||||
extern NSMenu* createNSMenu (const PopupMenu&, const String& name, int topLevelMenuId,
|
||||
int topLevelIndex, bool addDelegate);
|
||||
|
||||
//==============================================================================
|
||||
struct StatusItemContainer : public Timer
|
||||
{
|
||||
//==============================================================================
|
||||
StatusItemContainer (SystemTrayIconComponent& iconComp, const Image& im)
|
||||
: owner (iconComp), statusIcon (imageToNSImage (im))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void configureIcon() = 0;
|
||||
virtual void setHighlighted (bool shouldHighlight) = 0;
|
||||
|
||||
//==============================================================================
|
||||
void setIconSize()
|
||||
{
|
||||
[statusIcon.get() setSize: NSMakeSize (20.0f, 20.0f)];
|
||||
}
|
||||
|
||||
void updateIcon (const Image& newImage)
|
||||
{
|
||||
statusIcon.reset (imageToNSImage (newImage));
|
||||
setIconSize();
|
||||
configureIcon();
|
||||
}
|
||||
|
||||
void showMenu (const PopupMenu& menu)
|
||||
{
|
||||
if (NSMenu* m = createNSMenu (menu, "MenuBarItem", -2, -3, true))
|
||||
{
|
||||
setHighlighted (true);
|
||||
stopTimer();
|
||||
|
||||
// There's currently no good alternative to this.
|
||||
[statusItem.get() popUpStatusItemMenu: m];
|
||||
|
||||
startTimer (1);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
setHighlighted (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
SystemTrayIconComponent& owner;
|
||||
|
||||
NSUniquePtr<NSStatusItem> statusItem;
|
||||
NSUniquePtr<NSImage> statusIcon;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StatusItemContainer)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ButtonBasedStatusItem : public StatusItemContainer
|
||||
{
|
||||
//==============================================================================
|
||||
ButtonBasedStatusItem (SystemTrayIconComponent& iconComp, const Image& im)
|
||||
: StatusItemContainer (iconComp, im)
|
||||
{
|
||||
static ButtonEventForwarderClass cls;
|
||||
eventForwarder.reset ([cls.createInstance() init]);
|
||||
ButtonEventForwarderClass::setOwner (eventForwarder.get(), this);
|
||||
|
||||
setIconSize();
|
||||
configureIcon();
|
||||
|
||||
statusItem.reset ([[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain]);
|
||||
auto button = [statusItem.get() button];
|
||||
button.image = statusIcon.get();
|
||||
button.target = eventForwarder.get();
|
||||
button.action = @selector (handleEvent:);
|
||||
#if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
|
||||
[button sendActionOn: NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown | NSEventMaskScrollWheel];
|
||||
#else
|
||||
[button sendActionOn: NSLeftMouseDownMask | NSRightMouseDownMask | NSScrollWheelMask];
|
||||
#endif
|
||||
}
|
||||
|
||||
void configureIcon() override
|
||||
{
|
||||
[statusIcon.get() setTemplate: true];
|
||||
[statusItem.get() button].image = statusIcon.get();
|
||||
}
|
||||
|
||||
void setHighlighted (bool shouldHighlight) override
|
||||
{
|
||||
[[statusItem.get() button] setHighlighted: shouldHighlight];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleEvent()
|
||||
{
|
||||
auto e = [NSApp currentEvent];
|
||||
NSEventType type = [e type];
|
||||
|
||||
const bool isLeft = (type == NSEventTypeLeftMouseDown);
|
||||
const bool isRight = (type == NSEventTypeRightMouseDown);
|
||||
|
||||
if (owner.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (isLeft || isRight)
|
||||
if (auto* current = Component::getCurrentlyModalComponent())
|
||||
current->inputAttemptWhenModal();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto eventMods = ComponentPeer::getCurrentModifiersRealtime();
|
||||
|
||||
if (([e modifierFlags] & NSEventModifierFlagCommand) != 0)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::commandModifier);
|
||||
|
||||
auto now = Time::getCurrentTime();
|
||||
auto mouseSource = Desktop::getInstance().getMainMouseSource();
|
||||
auto pressure = (float) e.pressure;
|
||||
|
||||
if (isLeft || isRight)
|
||||
{
|
||||
owner.mouseDown ({ mouseSource, {},
|
||||
eventMods.withFlags (isLeft ? ModifierKeys::leftButtonModifier
|
||||
: ModifierKeys::rightButtonModifier),
|
||||
pressure,
|
||||
MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
|
||||
MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false });
|
||||
|
||||
owner.mouseUp ({ mouseSource, {},
|
||||
eventMods.withoutMouseButtons(),
|
||||
pressure,
|
||||
MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
|
||||
MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false });
|
||||
}
|
||||
else if (type == NSEventTypeMouseMoved)
|
||||
{
|
||||
owner.mouseMove (MouseEvent (mouseSource, {}, eventMods, pressure,
|
||||
MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
|
||||
MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ButtonEventForwarderClass : public ObjCClass<NSObject>
|
||||
{
|
||||
public:
|
||||
ButtonEventForwarderClass() : ObjCClass<NSObject> ("JUCEButtonEventForwarderClass_")
|
||||
{
|
||||
addIvar<ButtonBasedStatusItem*> ("owner");
|
||||
|
||||
addMethod (@selector (handleEvent:), handleEvent, "v@:@");
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static ButtonBasedStatusItem* getOwner (id self) { return getIvar<ButtonBasedStatusItem*> (self, "owner"); }
|
||||
static void setOwner (id self, ButtonBasedStatusItem* owner) { object_setInstanceVariable (self, "owner", owner); }
|
||||
|
||||
private:
|
||||
static void handleEvent (id self, SEL, id)
|
||||
{
|
||||
if (auto* owner = getOwner (self))
|
||||
owner->handleEvent();
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
NSUniquePtr<NSObject> eventForwarder;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ViewBasedStatusItem : public StatusItemContainer
|
||||
{
|
||||
//==============================================================================
|
||||
ViewBasedStatusItem (SystemTrayIconComponent& iconComp, const Image& im)
|
||||
: StatusItemContainer (iconComp, im)
|
||||
{
|
||||
static SystemTrayViewClass cls;
|
||||
view.reset ([cls.createInstance() init]);
|
||||
SystemTrayViewClass::setOwner (view.get(), this);
|
||||
SystemTrayViewClass::setImage (view.get(), statusIcon.get());
|
||||
|
||||
setIconSize();
|
||||
|
||||
statusItem.reset ([[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain]);
|
||||
[statusItem.get() setView: view.get()];
|
||||
|
||||
SystemTrayViewClass::frameChanged (view.get(), SEL(), nullptr);
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[[NSNotificationCenter defaultCenter] addObserver: view.get()
|
||||
selector: @selector (frameChanged:)
|
||||
name: NSWindowDidMoveNotification
|
||||
object: nil];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
~ViewBasedStatusItem() override
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: view.get()];
|
||||
[[NSStatusBar systemStatusBar] removeStatusItem: statusItem.get()];
|
||||
SystemTrayViewClass::setOwner (view.get(), nullptr);
|
||||
SystemTrayViewClass::setImage (view.get(), nil);
|
||||
}
|
||||
|
||||
void configureIcon() override
|
||||
{
|
||||
SystemTrayViewClass::setImage (view.get(), statusIcon.get());
|
||||
[statusItem.get() setView: view.get()];
|
||||
}
|
||||
|
||||
void setHighlighted (bool shouldHighlight) override
|
||||
{
|
||||
isHighlighted = shouldHighlight;
|
||||
[view.get() setNeedsDisplay: true];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleStatusItemAction (NSEvent* e)
|
||||
{
|
||||
NSEventType type = [e type];
|
||||
|
||||
const bool isLeft = (type == NSEventTypeLeftMouseDown || type == NSEventTypeLeftMouseUp);
|
||||
const bool isRight = (type == NSEventTypeRightMouseDown || type == NSEventTypeRightMouseUp);
|
||||
|
||||
if (owner.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (isLeft || isRight)
|
||||
if (auto* current = Component::getCurrentlyModalComponent())
|
||||
current->inputAttemptWhenModal();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto eventMods = ComponentPeer::getCurrentModifiersRealtime();
|
||||
|
||||
if (([e modifierFlags] & NSEventModifierFlagCommand) != 0)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::commandModifier);
|
||||
|
||||
auto now = Time::getCurrentTime();
|
||||
auto mouseSource = Desktop::getInstance().getMainMouseSource();
|
||||
auto pressure = (float) e.pressure;
|
||||
|
||||
if (isLeft || isRight) // Only mouse up is sent by the OS, so simulate a down/up
|
||||
{
|
||||
setHighlighted (true);
|
||||
startTimer (150);
|
||||
|
||||
owner.mouseDown (MouseEvent (mouseSource, {},
|
||||
eventMods.withFlags (isLeft ? ModifierKeys::leftButtonModifier
|
||||
: ModifierKeys::rightButtonModifier),
|
||||
pressure, MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
|
||||
MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false));
|
||||
|
||||
owner.mouseUp (MouseEvent (mouseSource, {}, eventMods.withoutMouseButtons(), pressure,
|
||||
MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
|
||||
MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false));
|
||||
}
|
||||
else if (type == NSEventTypeMouseMoved)
|
||||
{
|
||||
owner.mouseMove (MouseEvent (mouseSource, {}, eventMods, pressure,
|
||||
MouseInputSource::invalidOrientation, MouseInputSource::invalidRotation,
|
||||
MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct SystemTrayViewClass : public ObjCClass<NSControl>
|
||||
{
|
||||
SystemTrayViewClass() : ObjCClass<NSControl> ("JUCESystemTrayView_")
|
||||
{
|
||||
addIvar<ViewBasedStatusItem*> ("owner");
|
||||
addIvar<NSImage*> ("image");
|
||||
|
||||
addMethod (@selector (mouseDown:), handleEventDown, "v@:@");
|
||||
addMethod (@selector (rightMouseDown:), handleEventDown, "v@:@");
|
||||
addMethod (@selector (drawRect:), drawRect, "v@:@");
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
addMethod (@selector (frameChanged:), frameChanged, "v@:@");
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static ViewBasedStatusItem* getOwner (id self) { return getIvar<ViewBasedStatusItem*> (self, "owner"); }
|
||||
static NSImage* getImage (id self) { return getIvar<NSImage*> (self, "image"); }
|
||||
static void setOwner (id self, ViewBasedStatusItem* owner) { object_setInstanceVariable (self, "owner", owner); }
|
||||
static void setImage (id self, NSImage* image) { object_setInstanceVariable (self, "image", image); }
|
||||
|
||||
static void frameChanged (id self, SEL, NSNotification*)
|
||||
{
|
||||
if (auto* owner = getOwner (self))
|
||||
{
|
||||
NSRect r = [[[owner->statusItem.get() view] window] frame];
|
||||
NSRect sr = [[[NSScreen screens] objectAtIndex: 0] frame];
|
||||
r.origin.y = sr.size.height - r.origin.y - r.size.height;
|
||||
owner->owner.setBounds (convertToRectInt (r));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void handleEventDown (id self, SEL, NSEvent* e)
|
||||
{
|
||||
if (auto* owner = getOwner (self))
|
||||
owner->handleStatusItemAction (e);
|
||||
}
|
||||
|
||||
static void drawRect (id self, SEL, NSRect)
|
||||
{
|
||||
NSRect bounds = [self bounds];
|
||||
|
||||
if (auto* owner = getOwner (self))
|
||||
[owner->statusItem.get() drawStatusBarBackgroundInRect: bounds
|
||||
withHighlight: owner->isHighlighted];
|
||||
|
||||
if (NSImage* const im = getImage (self))
|
||||
{
|
||||
NSSize imageSize = [im size];
|
||||
|
||||
[im drawInRect: NSMakeRect (bounds.origin.x + ((bounds.size.width - imageSize.width) / 2.0f),
|
||||
bounds.origin.y + ((bounds.size.height - imageSize.height) / 2.0f),
|
||||
imageSize.width, imageSize.height)
|
||||
fromRect: NSZeroRect
|
||||
operation: NSCompositingOperationSourceOver
|
||||
fraction: 1.0f];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
NSUniquePtr<NSControl> view;
|
||||
bool isHighlighted = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class SystemTrayIconComponent::Pimpl
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Pimpl (SystemTrayIconComponent& iconComp, const Image& im)
|
||||
{
|
||||
if (std::floor (NSFoundationVersionNumber) > NSFoundationVersionNumber10_10)
|
||||
statusItemHolder = std::make_unique<ButtonBasedStatusItem> (iconComp, im);
|
||||
else
|
||||
statusItemHolder = std::make_unique<ViewBasedStatusItem> (iconComp, im);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<StatusItemContainer> statusItemHolder;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void SystemTrayIconComponent::setIconImage (const Image&, const Image& templateImage)
|
||||
{
|
||||
if (templateImage.isValid())
|
||||
{
|
||||
if (pimpl == nullptr)
|
||||
pimpl.reset (new Pimpl (*this, templateImage));
|
||||
else
|
||||
pimpl->statusItemHolder->updateIcon (templateImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
pimpl.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setIconTooltip (const String&)
|
||||
{
|
||||
// xxx not yet implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setHighlighted (bool shouldHighlight)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->statusItemHolder->setHighlighted (shouldHighlight);
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::showInfoBubble (const String& /*title*/, const String& /*content*/)
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::hideInfoBubble()
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void* SystemTrayIconComponent::getNativeHandle() const
|
||||
{
|
||||
return pimpl != nullptr ? pimpl->statusItemHolder->statusItem.get() : nullptr;
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::showDropdownMenu (const PopupMenu& menu)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->statusItemHolder->showMenu (menu);
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
||||
|
||||
extern NSMenu* createNSMenu (const PopupMenu&, const String& name, int topLevelMenuId,
|
||||
int topLevelIndex, bool addDelegate);
|
||||
|
||||
//==============================================================================
|
||||
struct StatusItemContainer : public Timer
|
||||
{
|
||||
//==============================================================================
|
||||
StatusItemContainer (SystemTrayIconComponent& iconComp, const Image& im)
|
||||
: owner (iconComp), statusIcon (imageToNSImage (ScaledImage (im)))
|
||||
{
|
||||
}
|
||||
|
||||
virtual void configureIcon() = 0;
|
||||
virtual void setHighlighted (bool shouldHighlight) = 0;
|
||||
|
||||
//==============================================================================
|
||||
void setIconSize()
|
||||
{
|
||||
[statusIcon.get() setSize: NSMakeSize (20.0f, 20.0f)];
|
||||
}
|
||||
|
||||
void updateIcon (const Image& newImage)
|
||||
{
|
||||
statusIcon.reset (imageToNSImage (ScaledImage (newImage)));
|
||||
setIconSize();
|
||||
configureIcon();
|
||||
}
|
||||
|
||||
void showMenu (const PopupMenu& menu)
|
||||
{
|
||||
if (NSMenu* m = createNSMenu (menu, "MenuBarItem", -2, -3, true))
|
||||
{
|
||||
setHighlighted (true);
|
||||
stopTimer();
|
||||
|
||||
// There's currently no good alternative to this.
|
||||
[statusItem.get() popUpStatusItemMenu: m];
|
||||
|
||||
startTimer (1);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
setHighlighted (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
SystemTrayIconComponent& owner;
|
||||
|
||||
NSUniquePtr<NSStatusItem> statusItem;
|
||||
NSUniquePtr<NSImage> statusIcon;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StatusItemContainer)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct API_AVAILABLE (macos (10.10)) ButtonBasedStatusItem : public StatusItemContainer
|
||||
{
|
||||
//==============================================================================
|
||||
ButtonBasedStatusItem (SystemTrayIconComponent& iconComp, const Image& im)
|
||||
: StatusItemContainer (iconComp, im)
|
||||
{
|
||||
static ButtonEventForwarderClass cls;
|
||||
eventForwarder.reset ([cls.createInstance() init]);
|
||||
ButtonEventForwarderClass::setOwner (eventForwarder.get(), this);
|
||||
|
||||
setIconSize();
|
||||
configureIcon();
|
||||
|
||||
statusItem.reset ([[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain]);
|
||||
auto button = [statusItem.get() button];
|
||||
button.image = statusIcon.get();
|
||||
button.target = eventForwarder.get();
|
||||
button.action = @selector (handleEvent:);
|
||||
[button sendActionOn: NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown | NSEventMaskScrollWheel];
|
||||
}
|
||||
|
||||
~ButtonBasedStatusItem() override
|
||||
{
|
||||
[statusItem.get() button].image = nullptr;
|
||||
}
|
||||
|
||||
void configureIcon() override
|
||||
{
|
||||
[statusIcon.get() setTemplate: true];
|
||||
[statusItem.get() button].image = statusIcon.get();
|
||||
}
|
||||
|
||||
void setHighlighted (bool shouldHighlight) override
|
||||
{
|
||||
[[statusItem.get() button] setHighlighted: shouldHighlight];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleEvent()
|
||||
{
|
||||
auto e = [NSApp currentEvent];
|
||||
NSEventType type = [e type];
|
||||
|
||||
const bool isLeft = (type == NSEventTypeLeftMouseDown);
|
||||
const bool isRight = (type == NSEventTypeRightMouseDown);
|
||||
|
||||
if (owner.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (isLeft || isRight)
|
||||
if (auto* current = Component::getCurrentlyModalComponent())
|
||||
current->inputAttemptWhenModal();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto eventMods = ComponentPeer::getCurrentModifiersRealtime();
|
||||
|
||||
if (([e modifierFlags] & NSEventModifierFlagCommand) != 0)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::commandModifier);
|
||||
|
||||
auto now = Time::getCurrentTime();
|
||||
auto mouseSource = Desktop::getInstance().getMainMouseSource();
|
||||
auto pressure = (float) e.pressure;
|
||||
|
||||
if (isLeft || isRight)
|
||||
{
|
||||
owner.mouseDown ({ mouseSource, {},
|
||||
eventMods.withFlags (isLeft ? ModifierKeys::leftButtonModifier
|
||||
: ModifierKeys::rightButtonModifier),
|
||||
pressure,
|
||||
MouseInputSource::defaultOrientation, MouseInputSource::defaultRotation,
|
||||
MouseInputSource::defaultTiltX, MouseInputSource::defaultTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false });
|
||||
|
||||
owner.mouseUp ({ mouseSource, {},
|
||||
eventMods.withoutMouseButtons(),
|
||||
pressure,
|
||||
MouseInputSource::defaultOrientation, MouseInputSource::defaultRotation,
|
||||
MouseInputSource::defaultTiltX, MouseInputSource::defaultTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false });
|
||||
}
|
||||
else if (type == NSEventTypeMouseMoved)
|
||||
{
|
||||
owner.mouseMove (MouseEvent (mouseSource, {}, eventMods, pressure,
|
||||
MouseInputSource::defaultOrientation, MouseInputSource::defaultRotation,
|
||||
MouseInputSource::defaultTiltX, MouseInputSource::defaultTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ButtonEventForwarderClass : public ObjCClass<NSObject>
|
||||
{
|
||||
public:
|
||||
ButtonEventForwarderClass() : ObjCClass<NSObject> ("JUCEButtonEventForwarderClass_")
|
||||
{
|
||||
addIvar<ButtonBasedStatusItem*> ("owner");
|
||||
|
||||
addMethod (@selector (handleEvent:), handleEvent);
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static ButtonBasedStatusItem* getOwner (id self) { return getIvar<ButtonBasedStatusItem*> (self, "owner"); }
|
||||
static void setOwner (id self, ButtonBasedStatusItem* owner) { object_setInstanceVariable (self, "owner", owner); }
|
||||
|
||||
private:
|
||||
static void handleEvent (id self, SEL, id)
|
||||
{
|
||||
if (auto* owner = getOwner (self))
|
||||
owner->handleEvent();
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
NSUniquePtr<NSObject> eventForwarder;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct ViewBasedStatusItem : public StatusItemContainer
|
||||
{
|
||||
//==============================================================================
|
||||
ViewBasedStatusItem (SystemTrayIconComponent& iconComp, const Image& im)
|
||||
: StatusItemContainer (iconComp, im)
|
||||
{
|
||||
static SystemTrayViewClass cls;
|
||||
view.reset ([cls.createInstance() init]);
|
||||
SystemTrayViewClass::setOwner (view.get(), this);
|
||||
SystemTrayViewClass::setImage (view.get(), statusIcon.get());
|
||||
|
||||
setIconSize();
|
||||
|
||||
statusItem.reset ([[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain]);
|
||||
[statusItem.get() setView: view.get()];
|
||||
|
||||
SystemTrayViewClass::frameChanged (view.get(), SEL(), nullptr);
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
[[NSNotificationCenter defaultCenter] addObserver: view.get()
|
||||
selector: @selector (frameChanged:)
|
||||
name: NSWindowDidMoveNotification
|
||||
object: nil];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
~ViewBasedStatusItem() override
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver: view.get()];
|
||||
[[NSStatusBar systemStatusBar] removeStatusItem: statusItem.get()];
|
||||
SystemTrayViewClass::setOwner (view.get(), nullptr);
|
||||
SystemTrayViewClass::setImage (view.get(), nil);
|
||||
}
|
||||
|
||||
void configureIcon() override
|
||||
{
|
||||
SystemTrayViewClass::setImage (view.get(), statusIcon.get());
|
||||
[statusItem.get() setView: view.get()];
|
||||
}
|
||||
|
||||
void setHighlighted (bool shouldHighlight) override
|
||||
{
|
||||
isHighlighted = shouldHighlight;
|
||||
[view.get() setNeedsDisplay: true];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleStatusItemAction (NSEvent* e)
|
||||
{
|
||||
NSEventType type = [e type];
|
||||
|
||||
const bool isLeft = (type == NSEventTypeLeftMouseDown || type == NSEventTypeLeftMouseUp);
|
||||
const bool isRight = (type == NSEventTypeRightMouseDown || type == NSEventTypeRightMouseUp);
|
||||
|
||||
if (owner.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (isLeft || isRight)
|
||||
if (auto* current = Component::getCurrentlyModalComponent())
|
||||
current->inputAttemptWhenModal();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto eventMods = ComponentPeer::getCurrentModifiersRealtime();
|
||||
|
||||
if (([e modifierFlags] & NSEventModifierFlagCommand) != 0)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::commandModifier);
|
||||
|
||||
auto now = Time::getCurrentTime();
|
||||
auto mouseSource = Desktop::getInstance().getMainMouseSource();
|
||||
auto pressure = (float) e.pressure;
|
||||
|
||||
if (isLeft || isRight) // Only mouse up is sent by the OS, so simulate a down/up
|
||||
{
|
||||
setHighlighted (true);
|
||||
startTimer (150);
|
||||
|
||||
owner.mouseDown (MouseEvent (mouseSource, {},
|
||||
eventMods.withFlags (isLeft ? ModifierKeys::leftButtonModifier
|
||||
: ModifierKeys::rightButtonModifier),
|
||||
pressure, MouseInputSource::defaultOrientation, MouseInputSource::defaultRotation,
|
||||
MouseInputSource::defaultTiltX, MouseInputSource::defaultTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false));
|
||||
|
||||
owner.mouseUp (MouseEvent (mouseSource, {}, eventMods.withoutMouseButtons(), pressure,
|
||||
MouseInputSource::defaultOrientation, MouseInputSource::defaultRotation,
|
||||
MouseInputSource::defaultTiltX, MouseInputSource::defaultTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false));
|
||||
}
|
||||
else if (type == NSEventTypeMouseMoved)
|
||||
{
|
||||
owner.mouseMove (MouseEvent (mouseSource, {}, eventMods, pressure,
|
||||
MouseInputSource::defaultOrientation, MouseInputSource::defaultRotation,
|
||||
MouseInputSource::defaultTiltX, MouseInputSource::defaultTiltY,
|
||||
&owner, &owner, now, {}, now, 1, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct SystemTrayViewClass : public ObjCClass<NSControl>
|
||||
{
|
||||
SystemTrayViewClass() : ObjCClass<NSControl> ("JUCESystemTrayView_")
|
||||
{
|
||||
addIvar<ViewBasedStatusItem*> ("owner");
|
||||
addIvar<NSImage*> ("image");
|
||||
|
||||
addMethod (@selector (mouseDown:), handleEventDown);
|
||||
addMethod (@selector (rightMouseDown:), handleEventDown);
|
||||
addMethod (@selector (drawRect:), drawRect);
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
addMethod (@selector (frameChanged:), frameChanged);
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static ViewBasedStatusItem* getOwner (id self) { return getIvar<ViewBasedStatusItem*> (self, "owner"); }
|
||||
static NSImage* getImage (id self) { return getIvar<NSImage*> (self, "image"); }
|
||||
static void setOwner (id self, ViewBasedStatusItem* owner) { object_setInstanceVariable (self, "owner", owner); }
|
||||
static void setImage (id self, NSImage* image) { object_setInstanceVariable (self, "image", image); }
|
||||
|
||||
static void frameChanged (id self, SEL, NSNotification*)
|
||||
{
|
||||
if (auto* owner = getOwner (self))
|
||||
{
|
||||
NSRect r = [[[owner->statusItem.get() view] window] frame];
|
||||
NSRect sr = [[[NSScreen screens] objectAtIndex: 0] frame];
|
||||
r.origin.y = sr.size.height - r.origin.y - r.size.height;
|
||||
owner->owner.setBounds (convertToRectInt (r));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void handleEventDown (id self, SEL, NSEvent* e)
|
||||
{
|
||||
if (auto* owner = getOwner (self))
|
||||
owner->handleStatusItemAction (e);
|
||||
}
|
||||
|
||||
static void drawRect (id self, SEL, NSRect)
|
||||
{
|
||||
NSRect bounds = [self bounds];
|
||||
|
||||
if (auto* owner = getOwner (self))
|
||||
[owner->statusItem.get() drawStatusBarBackgroundInRect: bounds
|
||||
withHighlight: owner->isHighlighted];
|
||||
|
||||
if (NSImage* const im = getImage (self))
|
||||
{
|
||||
NSSize imageSize = [im size];
|
||||
|
||||
[im drawInRect: NSMakeRect (bounds.origin.x + ((bounds.size.width - imageSize.width) / 2.0f),
|
||||
bounds.origin.y + ((bounds.size.height - imageSize.height) / 2.0f),
|
||||
imageSize.width, imageSize.height)
|
||||
fromRect: NSZeroRect
|
||||
operation: NSCompositingOperationSourceOver
|
||||
fraction: 1.0f];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
NSUniquePtr<NSControl> view;
|
||||
bool isHighlighted = false;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class SystemTrayIconComponent::Pimpl
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
Pimpl (SystemTrayIconComponent& iconComp, const Image& im)
|
||||
{
|
||||
if (@available (macOS 10.10, *))
|
||||
statusItemHolder = std::make_unique<ButtonBasedStatusItem> (iconComp, im);
|
||||
else
|
||||
statusItemHolder = std::make_unique<ViewBasedStatusItem> (iconComp, im);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<StatusItemContainer> statusItemHolder;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void SystemTrayIconComponent::setIconImage (const Image&, const Image& templateImage)
|
||||
{
|
||||
if (templateImage.isValid())
|
||||
{
|
||||
if (pimpl == nullptr)
|
||||
pimpl.reset (new Pimpl (*this, templateImage));
|
||||
else
|
||||
pimpl->statusItemHolder->updateIcon (templateImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
pimpl.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setIconTooltip (const String&)
|
||||
{
|
||||
// xxx not yet implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setHighlighted (bool shouldHighlight)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->statusItemHolder->setHighlighted (shouldHighlight);
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::showInfoBubble (const String& /*title*/, const String& /*content*/)
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::hideInfoBubble()
|
||||
{
|
||||
// xxx Not implemented!
|
||||
}
|
||||
|
||||
void* SystemTrayIconComponent::getNativeHandle() const
|
||||
{
|
||||
return pimpl != nullptr ? pimpl->statusItemHolder->statusItem.get() : nullptr;
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::showDropdownMenu (const PopupMenu& menu)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->statusItemHolder->showMenu (menu);
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
||||
|
@ -2,15 +2,15 @@
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
@ -26,17 +26,9 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10)
|
||||
#define JUCE_USE_WKWEBVIEW 1
|
||||
#endif
|
||||
|
||||
#if (defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12)
|
||||
#define WKWEBVIEW_OPENPANEL_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
static NSURL* appendParametersToFileURL (const URL& url, NSURL* fileUrl)
|
||||
{
|
||||
if (@available (macOS 10.9, *))
|
||||
if (@available (macOS 10.10, *))
|
||||
{
|
||||
const auto parameterNames = url.getParameterNames();
|
||||
const auto parameterValues = url.getParameterValues();
|
||||
@ -115,295 +107,125 @@ static NSMutableURLRequest* getRequestForURL (const String& url, const StringArr
|
||||
}
|
||||
|
||||
#if JUCE_MAC
|
||||
|
||||
#if JUCE_USE_WKWEBVIEW
|
||||
using WebViewBase = ObjCClass<WKWebView>;
|
||||
#else
|
||||
using WebViewBase = ObjCClass<WebView>;
|
||||
#endif
|
||||
|
||||
struct WebViewKeyEquivalentResponder : public WebViewBase
|
||||
template <class WebViewClass>
|
||||
struct WebViewKeyEquivalentResponder : public ObjCClass<WebViewClass>
|
||||
{
|
||||
WebViewKeyEquivalentResponder()
|
||||
: WebViewBase ("WebViewKeyEquivalentResponder_")
|
||||
: ObjCClass<WebViewClass> ("WebViewKeyEquivalentResponder_")
|
||||
{
|
||||
addMethod (@selector (performKeyEquivalent:), performKeyEquivalent, @encode (BOOL), "@:@");
|
||||
registerClass();
|
||||
}
|
||||
this->addMethod (@selector (performKeyEquivalent:),
|
||||
[] (id self, SEL selector, NSEvent* event)
|
||||
{
|
||||
const auto isCommandDown = [event]
|
||||
{
|
||||
const auto modifierFlags = [event modifierFlags];
|
||||
|
||||
private:
|
||||
static BOOL performKeyEquivalent (id self, SEL selector, NSEvent* event)
|
||||
{
|
||||
NSResponder* first = [[self window] firstResponder];
|
||||
if (@available (macOS 10.12, *))
|
||||
return (modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand;
|
||||
|
||||
#if (defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12)
|
||||
constexpr auto mask = NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||
constexpr auto key = NSEventModifierFlagCommand;
|
||||
#else
|
||||
constexpr auto mask = NSDeviceIndependentModifierFlagsMask;
|
||||
constexpr auto key = NSCommandKeyMask;
|
||||
#endif
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
||||
return (modifierFlags & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask;
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}();
|
||||
|
||||
if (([event modifierFlags] & mask) == key)
|
||||
{
|
||||
auto sendAction = [&] (SEL actionSelector) -> BOOL
|
||||
{
|
||||
return [NSApp sendAction: actionSelector
|
||||
to: first
|
||||
from: self];
|
||||
};
|
||||
if (isCommandDown)
|
||||
{
|
||||
auto sendAction = [&] (SEL actionSelector) -> BOOL
|
||||
{
|
||||
return [NSApp sendAction:actionSelector
|
||||
to:[[self window] firstResponder]
|
||||
from:self];
|
||||
};
|
||||
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString: @"x"]) return sendAction (@selector (cut:));
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString: @"c"]) return sendAction (@selector (copy:));
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString: @"v"]) return sendAction (@selector (paste:));
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString: @"a"]) return sendAction (@selector (selectAll:));
|
||||
}
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString:@"x"])
|
||||
return sendAction (@selector (cut:));
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString:@"c"])
|
||||
return sendAction (@selector (copy:));
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString:@"v"])
|
||||
return sendAction (@selector (paste:));
|
||||
if ([[event charactersIgnoringModifiers] isEqualToString:@"a"])
|
||||
return sendAction (@selector (selectAll:));
|
||||
}
|
||||
|
||||
return sendSuperclassMessage<BOOL> (self, selector, event);
|
||||
return ObjCClass<WebViewClass>::template sendSuperclassMessage<BOOL> (self, selector, event);
|
||||
});
|
||||
this->registerClass();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_WKWEBVIEW
|
||||
|
||||
struct WebViewDelegateClass : public ObjCClass<NSObject>
|
||||
{
|
||||
WebViewDelegateClass() : ObjCClass<NSObject> ("JUCEWebViewDelegate_")
|
||||
{
|
||||
addIvar<WebBrowserComponent*> ("owner");
|
||||
|
||||
addMethod (@selector (webView:decidePolicyForNavigationAction:decisionHandler:), decidePolicyForNavigationAction, "v@:@@@");
|
||||
addMethod (@selector (webView:didFinishNavigation:), didFinishNavigation, "v@:@@");
|
||||
addMethod (@selector (webView:didFailNavigation:withError:), didFailNavigation, "v@:@@@");
|
||||
addMethod (@selector (webView:didFailProvisionalNavigation:withError:), didFailProvisionalNavigation, "v@:@@@");
|
||||
addMethod (@selector (webViewDidClose:), webViewDidClose, "v@:@");
|
||||
addMethod (@selector (webView:createWebViewWithConfiguration:forNavigationAction:
|
||||
windowFeatures:), createWebView, "@@:@@@@");
|
||||
|
||||
#if WKWEBVIEW_OPENPANEL_SUPPORTED
|
||||
addMethod (@selector (webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:), runOpenPanel, "v@:@@@@");
|
||||
#endif
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static void setOwner (id self, WebBrowserComponent* owner) { object_setInstanceVariable (self, "owner", owner); }
|
||||
static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); }
|
||||
|
||||
private:
|
||||
static void decidePolicyForNavigationAction (id self, SEL, WKWebView*, WKNavigationAction* navigationAction,
|
||||
void (^decisionHandler)(WKNavigationActionPolicy))
|
||||
{
|
||||
if (getOwner (self)->pageAboutToLoad (nsStringToJuce ([[[navigationAction request] URL] absoluteString])))
|
||||
decisionHandler (WKNavigationActionPolicyAllow);
|
||||
else
|
||||
decisionHandler (WKNavigationActionPolicyCancel);
|
||||
}
|
||||
|
||||
static void didFinishNavigation (id self, SEL, WKWebView* webview, WKNavigation*)
|
||||
{
|
||||
getOwner (self)->pageFinishedLoading (nsStringToJuce ([[webview URL] absoluteString]));
|
||||
}
|
||||
|
||||
static void displayError (WebBrowserComponent* owner, NSError* error)
|
||||
{
|
||||
if ([error code] != NSURLErrorCancelled)
|
||||
{
|
||||
auto errorString = nsStringToJuce ([error localizedDescription]);
|
||||
bool proceedToErrorPage = owner->pageLoadHadNetworkError (errorString);
|
||||
|
||||
// WKWebView doesn't have an internal error page, so make a really simple one ourselves
|
||||
if (proceedToErrorPage)
|
||||
owner->goToURL ("data:text/plain;charset=UTF-8," + errorString);
|
||||
}
|
||||
}
|
||||
|
||||
static void didFailNavigation (id self, SEL, WKWebView*, WKNavigation*, NSError* error)
|
||||
{
|
||||
displayError (getOwner (self), error);
|
||||
}
|
||||
|
||||
static void didFailProvisionalNavigation (id self, SEL, WKWebView*, WKNavigation*, NSError* error)
|
||||
{
|
||||
displayError (getOwner (self), error);
|
||||
}
|
||||
|
||||
static void webViewDidClose (id self, SEL, WKWebView*)
|
||||
{
|
||||
getOwner (self)->windowCloseRequest();
|
||||
}
|
||||
|
||||
static WKWebView* createWebView (id self, SEL, WKWebView*, WKWebViewConfiguration*,
|
||||
WKNavigationAction* navigationAction, WKWindowFeatures*)
|
||||
{
|
||||
getOwner (self)->newWindowAttemptingToLoad (nsStringToJuce ([[[navigationAction request] URL] absoluteString]));
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if WKWEBVIEW_OPENPANEL_SUPPORTED
|
||||
static void runOpenPanel (id, SEL, WKWebView*, WKOpenPanelParameters* parameters, WKFrameInfo*,
|
||||
void (^completionHandler)(NSArray<NSURL*>*))
|
||||
{
|
||||
using CompletionHandlerType = decltype (completionHandler);
|
||||
|
||||
class DeletedFileChooserWrapper : private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
DeletedFileChooserWrapper (std::unique_ptr<FileChooser> fc, CompletionHandlerType h)
|
||||
: chooser (std::move (fc)), handler (h)
|
||||
{
|
||||
[handler retain];
|
||||
}
|
||||
|
||||
~DeletedFileChooserWrapper()
|
||||
{
|
||||
callHandler (nullptr);
|
||||
[handler release];
|
||||
}
|
||||
|
||||
void callHandler (NSArray<NSURL*>* urls)
|
||||
{
|
||||
if (handlerCalled)
|
||||
return;
|
||||
|
||||
handler (urls);
|
||||
handlerCalled = true;
|
||||
}
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
private:
|
||||
CompletionHandlerType handler;
|
||||
bool handlerCalled = false;
|
||||
};
|
||||
|
||||
auto chooser = std::make_unique<FileChooser> (TRANS("Select the file you want to upload..."),
|
||||
File::getSpecialLocation (File::userHomeDirectory), "*");
|
||||
auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), completionHandler);
|
||||
|
||||
auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
|
||||
| ([parameters allowsMultipleSelection] ? FileBrowserComponent::canSelectMultipleItems : 0);
|
||||
|
||||
#if (defined (MAC_OS_X_VERSION_10_14) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14)
|
||||
if ([parameters allowsDirectories])
|
||||
flags |= FileBrowserComponent::canSelectDirectories;
|
||||
#endif
|
||||
|
||||
wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&)
|
||||
{
|
||||
auto results = wrapper->chooser->getResults();
|
||||
auto urls = [NSMutableArray arrayWithCapacity: (NSUInteger) results.size()];
|
||||
|
||||
for (auto& f : results)
|
||||
[urls addObject: [NSURL fileURLWithPath: juceStringToNS (f.getFullPathName())]];
|
||||
|
||||
wrapper->callHandler (urls);
|
||||
delete wrapper;
|
||||
});
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class WebBrowserComponent::Pimpl
|
||||
#if JUCE_MAC
|
||||
: public NSViewComponent
|
||||
#else
|
||||
: public UIViewComponent
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
Pimpl (WebBrowserComponent* owner)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
static WebViewKeyEquivalentResponder webviewClass;
|
||||
webView = (WKWebView*) webviewClass.createInstance();
|
||||
|
||||
webView = [webView initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)];
|
||||
#else
|
||||
webView = [[WKWebView alloc] initWithFrame: CGRectMake (0, 0, 100.0f, 100.0f)];
|
||||
#endif
|
||||
|
||||
static WebViewDelegateClass cls;
|
||||
webViewDelegate = [cls.createInstance() init];
|
||||
WebViewDelegateClass::setOwner (webViewDelegate, owner);
|
||||
|
||||
[webView setNavigationDelegate: webViewDelegate];
|
||||
[webView setUIDelegate: webViewDelegate];
|
||||
|
||||
setView (webView);
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
[webView setNavigationDelegate: nil];
|
||||
[webView setUIDelegate: nil];
|
||||
|
||||
[webViewDelegate release];
|
||||
|
||||
setView (nil);
|
||||
}
|
||||
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
auto trimmed = url.trimStart();
|
||||
|
||||
if (trimmed.startsWithIgnoreCase ("javascript:"))
|
||||
{
|
||||
[webView evaluateJavaScript: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))
|
||||
completionHandler: nil];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
stop();
|
||||
|
||||
if (trimmed.startsWithIgnoreCase ("file:"))
|
||||
{
|
||||
auto file = URL (url).getLocalFile();
|
||||
|
||||
if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())])
|
||||
[webView loadFileURL: appendParametersToFileURL (url, nsUrl) allowingReadAccessToURL: nsUrl];
|
||||
}
|
||||
else if (NSMutableURLRequest* request = getRequestForURL (url, headers, postData))
|
||||
{
|
||||
[webView loadRequest: request];
|
||||
}
|
||||
}
|
||||
|
||||
void goBack() { [webView goBack]; }
|
||||
void goForward() { [webView goForward]; }
|
||||
|
||||
void stop() { [webView stopLoading]; }
|
||||
void refresh() { [webView reload]; }
|
||||
|
||||
private:
|
||||
WKWebView* webView = nil;
|
||||
id webViewDelegate;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#if JUCE_MAC
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
||||
struct DownloadClickDetectorClass : public ObjCClass<NSObject>
|
||||
{
|
||||
DownloadClickDetectorClass() : ObjCClass<NSObject> ("JUCEWebClickDetector_")
|
||||
DownloadClickDetectorClass() : ObjCClass ("JUCEWebClickDetector_")
|
||||
{
|
||||
addIvar<WebBrowserComponent*> ("owner");
|
||||
|
||||
addMethod (@selector (webView:didFailLoadWithError:forFrame:), didFailLoadWithError);
|
||||
addMethod (@selector (webView:didFailProvisionalLoadWithError:forFrame:), didFailLoadWithError);
|
||||
|
||||
addMethod (@selector (webView:decidePolicyForNavigationAction:request:frame:decisionListener:),
|
||||
decidePolicyForNavigationAction, "v@:@@@@@");
|
||||
[] (id self, SEL, WebView*, NSDictionary* actionInformation, NSURLRequest*, WebFrame*, id<WebPolicyDecisionListener> listener)
|
||||
{
|
||||
if (getOwner (self)->pageAboutToLoad (getOriginalURL (actionInformation)))
|
||||
[listener use];
|
||||
else
|
||||
[listener ignore];
|
||||
});
|
||||
|
||||
addMethod (@selector (webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:),
|
||||
decidePolicyForNewWindowAction, "v@:@@@@@");
|
||||
addMethod (@selector (webView:didFinishLoadForFrame:), didFinishLoadForFrame, "v@:@@");
|
||||
addMethod (@selector (webView:didFailLoadWithError:forFrame:), didFailLoadWithError, "v@:@@@");
|
||||
addMethod (@selector (webView:didFailProvisionalLoadWithError:forFrame:), didFailLoadWithError, "v@:@@@");
|
||||
addMethod (@selector (webView:willCloseFrame:), willCloseFrame, "v@:@@");
|
||||
addMethod (@selector (webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:), runOpenPanel, "v@:@@", @encode (BOOL));
|
||||
[] (id self, SEL, WebView*, NSDictionary* actionInformation, NSURLRequest*, NSString*, id<WebPolicyDecisionListener> listener)
|
||||
{
|
||||
getOwner (self)->newWindowAttemptingToLoad (getOriginalURL (actionInformation));
|
||||
[listener ignore];
|
||||
});
|
||||
|
||||
addMethod (@selector (webView:didFinishLoadForFrame:),
|
||||
[] (id self, SEL, WebView* sender, WebFrame* frame)
|
||||
{
|
||||
if ([frame isEqual:[sender mainFrame]])
|
||||
{
|
||||
NSURL* url = [[[frame dataSource] request] URL];
|
||||
getOwner (self)->pageFinishedLoading (nsStringToJuce ([url absoluteString]));
|
||||
}
|
||||
});
|
||||
|
||||
addMethod (@selector (webView:willCloseFrame:),
|
||||
[] (id self, SEL, WebView*, WebFrame*)
|
||||
{
|
||||
getOwner (self)->windowCloseRequest();
|
||||
});
|
||||
|
||||
addMethod (@selector (webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:),
|
||||
[] (id, SEL, WebView*, id<WebOpenPanelResultListener> resultListener, BOOL allowMultipleFiles)
|
||||
{
|
||||
struct DeletedFileChooserWrapper : private DeletedAtShutdown
|
||||
{
|
||||
DeletedFileChooserWrapper (std::unique_ptr<FileChooser> fc, id<WebOpenPanelResultListener> rl)
|
||||
: chooser (std::move (fc)), listener (rl)
|
||||
{
|
||||
[listener.get() retain];
|
||||
}
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
ObjCObjectHandle<id<WebOpenPanelResultListener>> listener;
|
||||
};
|
||||
|
||||
auto chooser = std::make_unique<FileChooser> (TRANS ("Select the file you want to upload..."),
|
||||
File::getSpecialLocation (File::userHomeDirectory),
|
||||
"*");
|
||||
auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), resultListener);
|
||||
|
||||
auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
|
||||
| (allowMultipleFiles ? FileBrowserComponent::canSelectMultipleItems : 0);
|
||||
|
||||
wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&)
|
||||
{
|
||||
for (auto& f : wrapper->chooser->getResults())
|
||||
[wrapper->listener.get() chooseFilename: juceStringToNS (f.getFullPathName())];
|
||||
|
||||
delete wrapper;
|
||||
});
|
||||
});
|
||||
|
||||
registerClass();
|
||||
}
|
||||
@ -420,31 +242,6 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
static void decidePolicyForNavigationAction (id self, SEL, WebView*, NSDictionary* actionInformation,
|
||||
NSURLRequest*, WebFrame*, id<WebPolicyDecisionListener> listener)
|
||||
{
|
||||
if (getOwner (self)->pageAboutToLoad (getOriginalURL (actionInformation)))
|
||||
[listener use];
|
||||
else
|
||||
[listener ignore];
|
||||
}
|
||||
|
||||
static void decidePolicyForNewWindowAction (id self, SEL, WebView*, NSDictionary* actionInformation,
|
||||
NSURLRequest*, NSString*, id<WebPolicyDecisionListener> listener)
|
||||
{
|
||||
getOwner (self)->newWindowAttemptingToLoad (getOriginalURL (actionInformation));
|
||||
[listener ignore];
|
||||
}
|
||||
|
||||
static void didFinishLoadForFrame (id self, SEL, WebView* sender, WebFrame* frame)
|
||||
{
|
||||
if ([frame isEqual: [sender mainFrame]])
|
||||
{
|
||||
NSURL* url = [[[frame dataSource] request] URL];
|
||||
getOwner (self)->pageFinishedLoading (nsStringToJuce ([url absoluteString]));
|
||||
}
|
||||
}
|
||||
|
||||
static void didFailLoadWithError (id self, SEL, WebView* sender, NSError* error, WebFrame* frame)
|
||||
{
|
||||
if ([frame isEqual: [sender mainFrame]] && error != nullptr && [error code] != NSURLErrorCancelled)
|
||||
@ -457,61 +254,123 @@ private:
|
||||
getOwner (self)->goToURL ("data:text/plain;charset=UTF-8," + errorString);
|
||||
}
|
||||
}
|
||||
|
||||
static void willCloseFrame (id self, SEL, WebView*, WebFrame*)
|
||||
{
|
||||
getOwner (self)->windowCloseRequest();
|
||||
}
|
||||
|
||||
static void runOpenPanel (id, SEL, WebView*, id<WebOpenPanelResultListener> resultListener, BOOL allowMultipleFiles)
|
||||
{
|
||||
struct DeletedFileChooserWrapper : private DeletedAtShutdown
|
||||
{
|
||||
DeletedFileChooserWrapper (std::unique_ptr<FileChooser> fc, id<WebOpenPanelResultListener> rl)
|
||||
: chooser (std::move (fc)), listener (rl)
|
||||
{
|
||||
[listener retain];
|
||||
}
|
||||
|
||||
~DeletedFileChooserWrapper()
|
||||
{
|
||||
[listener release];
|
||||
}
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
id<WebOpenPanelResultListener> listener;
|
||||
};
|
||||
|
||||
auto chooser = std::make_unique<FileChooser> (TRANS("Select the file you want to upload..."),
|
||||
File::getSpecialLocation (File::userHomeDirectory), "*");
|
||||
auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), resultListener);
|
||||
|
||||
auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
|
||||
| (allowMultipleFiles ? FileBrowserComponent::canSelectMultipleItems : 0);
|
||||
|
||||
wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&)
|
||||
{
|
||||
for (auto& f : wrapper->chooser->getResults())
|
||||
[wrapper->listener chooseFilename: juceStringToNS (f.getFullPathName())];
|
||||
|
||||
delete wrapper;
|
||||
});
|
||||
}
|
||||
};
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
struct WebViewDelegateClass : public ObjCClass<NSObject>
|
||||
struct API_AVAILABLE (macos (10.10)) WebViewDelegateClass : public ObjCClass<NSObject>
|
||||
{
|
||||
WebViewDelegateClass() : ObjCClass<NSObject> ("JUCEWebViewDelegate_")
|
||||
WebViewDelegateClass() : ObjCClass ("JUCEWebViewDelegate_")
|
||||
{
|
||||
addIvar<WebBrowserComponent*> ("owner");
|
||||
|
||||
addMethod (@selector (gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:),
|
||||
shouldRecognizeSimultaneouslyWithGestureRecognizer, "c@:@@");
|
||||
addMethod (@selector (webView:decidePolicyForNavigationAction:decisionHandler:),
|
||||
[] (id self, SEL, WKWebView*, WKNavigationAction* navigationAction, void (^decisionHandler) (WKNavigationActionPolicy))
|
||||
{
|
||||
if (getOwner (self)->pageAboutToLoad (nsStringToJuce ([[[navigationAction request] URL] absoluteString])))
|
||||
decisionHandler (WKNavigationActionPolicyAllow);
|
||||
else
|
||||
decisionHandler (WKNavigationActionPolicyCancel);
|
||||
});
|
||||
|
||||
addMethod (@selector (webView:shouldStartLoadWithRequest:navigationType:), shouldStartLoadWithRequest, "c@:@@@");
|
||||
addMethod (@selector (webViewDidFinishLoad:), webViewDidFinishLoad, "v@:@");
|
||||
addMethod (@selector (webView:didFinishNavigation:),
|
||||
[] (id self, SEL, WKWebView* webview, WKNavigation*)
|
||||
{
|
||||
getOwner (self)->pageFinishedLoading (nsStringToJuce ([[webview URL] absoluteString]));
|
||||
});
|
||||
|
||||
addMethod (@selector (webView:didFailNavigation:withError:),
|
||||
[] (id self, SEL, WKWebView*, WKNavigation*, NSError* error)
|
||||
{
|
||||
displayError (getOwner (self), error);
|
||||
});
|
||||
|
||||
addMethod (@selector (webView:didFailProvisionalNavigation:withError:),
|
||||
[] (id self, SEL, WKWebView*, WKNavigation*, NSError* error)
|
||||
{
|
||||
displayError (getOwner (self), error);
|
||||
});
|
||||
|
||||
addMethod (@selector (webViewDidClose:),
|
||||
[] (id self, SEL, WKWebView*)
|
||||
{
|
||||
getOwner (self)->windowCloseRequest();
|
||||
});
|
||||
|
||||
addMethod (@selector (webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:),
|
||||
[] (id self, SEL, WKWebView*, WKWebViewConfiguration*, WKNavigationAction* navigationAction, WKWindowFeatures*)
|
||||
{
|
||||
getOwner (self)->newWindowAttemptingToLoad (nsStringToJuce ([[[navigationAction request] URL] absoluteString]));
|
||||
return nil;
|
||||
});
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
if (@available (macOS 10.12, *))
|
||||
{
|
||||
addMethod (@selector (webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:),
|
||||
[] (id, SEL, WKWebView*, WKOpenPanelParameters* parameters, WKFrameInfo*, void (^completionHandler)(NSArray<NSURL*>*))
|
||||
{
|
||||
using CompletionHandlerType = decltype (completionHandler);
|
||||
|
||||
class DeletedFileChooserWrapper : private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
DeletedFileChooserWrapper (std::unique_ptr<FileChooser> fc, CompletionHandlerType h)
|
||||
: chooser (std::move (fc)), handler (h)
|
||||
{
|
||||
[handler.get() retain];
|
||||
}
|
||||
|
||||
~DeletedFileChooserWrapper()
|
||||
{
|
||||
callHandler (nullptr);
|
||||
}
|
||||
|
||||
void callHandler (NSArray<NSURL*>* urls)
|
||||
{
|
||||
if (handlerCalled)
|
||||
return;
|
||||
|
||||
handler.get() (urls);
|
||||
handlerCalled = true;
|
||||
}
|
||||
|
||||
std::unique_ptr<FileChooser> chooser;
|
||||
|
||||
private:
|
||||
ObjCObjectHandle<CompletionHandlerType> handler;
|
||||
bool handlerCalled = false;
|
||||
};
|
||||
|
||||
auto chooser = std::make_unique<FileChooser> (TRANS("Select the file you want to upload..."),
|
||||
File::getSpecialLocation (File::userHomeDirectory), "*");
|
||||
auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), completionHandler);
|
||||
|
||||
auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
|
||||
| ([parameters allowsMultipleSelection] ? FileBrowserComponent::canSelectMultipleItems : 0);
|
||||
|
||||
#if JUCE_MAC
|
||||
if (@available (macOS 10.14, *))
|
||||
{
|
||||
if ([parameters allowsDirectories])
|
||||
flags |= FileBrowserComponent::canSelectDirectories;
|
||||
}
|
||||
#endif
|
||||
|
||||
wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&)
|
||||
{
|
||||
auto results = wrapper->chooser->getResults();
|
||||
auto urls = [NSMutableArray arrayWithCapacity: (NSUInteger) results.size()];
|
||||
|
||||
for (auto& f : results)
|
||||
[urls addObject: [NSURL fileURLWithPath: juceStringToNS (f.getFullPathName())]];
|
||||
|
||||
wrapper->callHandler (urls);
|
||||
delete wrapper;
|
||||
});
|
||||
});
|
||||
}
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
registerClass();
|
||||
}
|
||||
@ -520,86 +379,70 @@ struct WebViewDelegateClass : public ObjCClass<NSObject>
|
||||
static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); }
|
||||
|
||||
private:
|
||||
static BOOL shouldRecognizeSimultaneouslyWithGestureRecognizer (id, SEL, UIGestureRecognizer*, UIGestureRecognizer*)
|
||||
static void displayError (WebBrowserComponent* owner, NSError* error)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
if ([error code] != NSURLErrorCancelled)
|
||||
{
|
||||
auto errorString = nsStringToJuce ([error localizedDescription]);
|
||||
bool proceedToErrorPage = owner->pageLoadHadNetworkError (errorString);
|
||||
|
||||
static BOOL shouldStartLoadWithRequest (id self, SEL, UIWebView*, NSURLRequest* request, UIWebViewNavigationType)
|
||||
{
|
||||
return getOwner (self)->pageAboutToLoad (nsStringToJuce ([[request URL] absoluteString]));
|
||||
}
|
||||
|
||||
static void webViewDidFinishLoad (id self, SEL, UIWebView* webView)
|
||||
{
|
||||
getOwner (self)->pageFinishedLoading (nsStringToJuce ([[[webView request] URL] absoluteString]));
|
||||
// WKWebView doesn't have an internal error page, so make a really simple one ourselves
|
||||
if (proceedToErrorPage)
|
||||
owner->goToURL ("data:text/plain;charset=UTF-8," + errorString);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
class WebBrowserComponent::Pimpl
|
||||
#if JUCE_MAC
|
||||
: public NSViewComponent
|
||||
#else
|
||||
: public UIViewComponent
|
||||
#endif
|
||||
struct WebViewBase
|
||||
{
|
||||
virtual ~WebViewBase() = default;
|
||||
|
||||
virtual void goToURL (const String&, const StringArray*, const MemoryBlock*) = 0;
|
||||
virtual void goBack() = 0;
|
||||
virtual void goForward() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void refresh() = 0;
|
||||
|
||||
virtual id getWebView() = 0;
|
||||
};
|
||||
|
||||
#if JUCE_MAC
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
||||
class WebViewImpl : public WebViewBase
|
||||
{
|
||||
public:
|
||||
Pimpl (WebBrowserComponent* owner)
|
||||
WebViewImpl (WebBrowserComponent* owner)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
static WebViewKeyEquivalentResponder webviewClass;
|
||||
webView = (WebView*) webviewClass.createInstance();
|
||||
static WebViewKeyEquivalentResponder<WebView> webviewClass;
|
||||
|
||||
webView = [webView initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
|
||||
frameName: nsEmptyString()
|
||||
groupName: nsEmptyString()];
|
||||
webView.reset ([webviewClass.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
|
||||
frameName: nsEmptyString()
|
||||
groupName: nsEmptyString()]);
|
||||
|
||||
static DownloadClickDetectorClass cls;
|
||||
clickListener = [cls.createInstance() init];
|
||||
DownloadClickDetectorClass::setOwner (clickListener, owner);
|
||||
clickListener.reset ([cls.createInstance() init]);
|
||||
DownloadClickDetectorClass::setOwner (clickListener.get(), owner);
|
||||
|
||||
[webView setPolicyDelegate: clickListener];
|
||||
[webView setFrameLoadDelegate: clickListener];
|
||||
[webView setUIDelegate: clickListener];
|
||||
#else
|
||||
webView = [[UIWebView alloc] initWithFrame: CGRectMake (0, 0, 1.0f, 1.0f)];
|
||||
|
||||
static WebViewDelegateClass cls;
|
||||
webViewDelegate = [cls.createInstance() init];
|
||||
WebViewDelegateClass::setOwner (webViewDelegate, owner);
|
||||
|
||||
[webView setDelegate: webViewDelegate];
|
||||
#endif
|
||||
|
||||
setView (webView);
|
||||
[webView.get() setPolicyDelegate: clickListener.get()];
|
||||
[webView.get() setFrameLoadDelegate: clickListener.get()];
|
||||
[webView.get() setUIDelegate: clickListener.get()];
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
~WebViewImpl() override
|
||||
{
|
||||
#if JUCE_MAC
|
||||
[webView setPolicyDelegate: nil];
|
||||
[webView setFrameLoadDelegate: nil];
|
||||
[webView setUIDelegate: nil];
|
||||
|
||||
[clickListener release];
|
||||
#else
|
||||
[webView setDelegate: nil];
|
||||
[webViewDelegate release];
|
||||
#endif
|
||||
|
||||
setView (nil);
|
||||
[webView.get() setPolicyDelegate: nil];
|
||||
[webView.get() setFrameLoadDelegate: nil];
|
||||
[webView.get() setUIDelegate: nil];
|
||||
}
|
||||
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
const MemoryBlock* postData) override
|
||||
{
|
||||
if (url.trimStart().startsWithIgnoreCase ("javascript:"))
|
||||
{
|
||||
[webView stringByEvaluatingJavaScriptFromString: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))];
|
||||
[webView.get() stringByEvaluatingJavaScriptFromString: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))];
|
||||
return;
|
||||
}
|
||||
|
||||
@ -623,51 +466,146 @@ public:
|
||||
};
|
||||
|
||||
if (NSMutableURLRequest* request = getRequest())
|
||||
{
|
||||
#if JUCE_MAC
|
||||
[[webView mainFrame] loadRequest: request];
|
||||
#else
|
||||
[webView loadRequest: request];
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
[webView setScalesPageToFit: YES];
|
||||
#endif
|
||||
}
|
||||
[[webView.get() mainFrame] loadRequest: request];
|
||||
}
|
||||
|
||||
void goBack() { [webView goBack]; }
|
||||
void goForward() { [webView goForward]; }
|
||||
void goBack() override { [webView.get() goBack]; }
|
||||
void goForward() override { [webView.get() goForward]; }
|
||||
|
||||
#if JUCE_MAC
|
||||
void stop() { [webView stopLoading: nil]; }
|
||||
void refresh() { [webView reload: nil]; }
|
||||
#else
|
||||
void stop() { [webView stopLoading]; }
|
||||
void refresh() { [webView reload]; }
|
||||
#endif
|
||||
void stop() override { [webView.get() stopLoading: nil]; }
|
||||
void refresh() override { [webView.get() reload: nil]; }
|
||||
|
||||
id getWebView() override { return webView.get(); }
|
||||
|
||||
void mouseMove (const MouseEvent&)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
// WebKit doesn't capture mouse-moves itself, so it seems the only way to make
|
||||
// them work is to push them via this non-public method..
|
||||
if ([webView respondsToSelector: @selector (_updateMouseoverWithFakeEvent)])
|
||||
[webView performSelector: @selector (_updateMouseoverWithFakeEvent)];
|
||||
if ([webView.get() respondsToSelector: @selector (_updateMouseoverWithFakeEvent)])
|
||||
[webView.get() performSelector: @selector (_updateMouseoverWithFakeEvent)];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
private:
|
||||
#if JUCE_MAC
|
||||
WebView* webView = nil;
|
||||
id clickListener;
|
||||
#else
|
||||
UIWebView* webView = nil;
|
||||
id webViewDelegate;
|
||||
#endif
|
||||
ObjCObjectHandle<WebView*> webView;
|
||||
ObjCObjectHandle<id> clickListener;
|
||||
};
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
#endif
|
||||
|
||||
class API_AVAILABLE (macos (10.11)) WKWebViewImpl : public WebViewBase
|
||||
{
|
||||
public:
|
||||
WKWebViewImpl (WebBrowserComponent* owner)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
static WebViewKeyEquivalentResponder<WKWebView> webviewClass;
|
||||
|
||||
webView.reset ([webviewClass.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)]);
|
||||
#else
|
||||
webView.reset ([[WKWebView alloc] initWithFrame: CGRectMake (0, 0, 100.0f, 100.0f)]);
|
||||
#endif
|
||||
|
||||
static WebViewDelegateClass cls;
|
||||
webViewDelegate.reset ([cls.createInstance() init]);
|
||||
WebViewDelegateClass::setOwner (webViewDelegate.get(), owner);
|
||||
|
||||
[webView.get() setNavigationDelegate: webViewDelegate.get()];
|
||||
[webView.get() setUIDelegate: webViewDelegate.get()];
|
||||
}
|
||||
|
||||
~WKWebViewImpl() override
|
||||
{
|
||||
[webView.get() setNavigationDelegate: nil];
|
||||
[webView.get() setUIDelegate: nil];
|
||||
}
|
||||
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData) override
|
||||
{
|
||||
auto trimmed = url.trimStart();
|
||||
|
||||
if (trimmed.startsWithIgnoreCase ("javascript:"))
|
||||
{
|
||||
[webView.get() evaluateJavaScript: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))
|
||||
completionHandler: nil];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
stop();
|
||||
|
||||
if (trimmed.startsWithIgnoreCase ("file:"))
|
||||
{
|
||||
auto file = URL (url).getLocalFile();
|
||||
|
||||
if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())])
|
||||
[webView.get() loadFileURL: appendParametersToFileURL (url, nsUrl) allowingReadAccessToURL: nsUrl];
|
||||
}
|
||||
else if (NSMutableURLRequest* request = getRequestForURL (url, headers, postData))
|
||||
{
|
||||
[webView.get() loadRequest: request];
|
||||
}
|
||||
}
|
||||
|
||||
void goBack() override { [webView.get() goBack]; }
|
||||
void goForward() override { [webView.get() goForward]; }
|
||||
|
||||
void stop() override { [webView.get() stopLoading]; }
|
||||
void refresh() override { [webView.get() reload]; }
|
||||
|
||||
id getWebView() override { return webView.get(); }
|
||||
|
||||
private:
|
||||
ObjCObjectHandle<WKWebView*> webView;
|
||||
ObjCObjectHandle<id> webViewDelegate;
|
||||
};
|
||||
|
||||
#endif
|
||||
//==============================================================================
|
||||
class WebBrowserComponent::Pimpl
|
||||
#if JUCE_MAC
|
||||
: public NSViewComponent
|
||||
#else
|
||||
: public UIViewComponent
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
Pimpl (WebBrowserComponent* owner)
|
||||
{
|
||||
if (@available (macOS 10.11, *))
|
||||
webView = std::make_unique<WKWebViewImpl> (owner);
|
||||
#if JUCE_MAC
|
||||
else
|
||||
webView = std::make_unique<WebViewImpl> (owner);
|
||||
#endif
|
||||
|
||||
setView (webView->getWebView());
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
webView = nullptr;
|
||||
setView (nil);
|
||||
}
|
||||
|
||||
void goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
webView->goToURL (url, headers, postData);
|
||||
}
|
||||
|
||||
void goBack() { webView->goBack(); }
|
||||
void goForward() { webView->goForward(); }
|
||||
|
||||
void stop() { webView->stop(); }
|
||||
void refresh() { webView->refresh(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<WebViewBase> webView;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden)
|
||||
@ -678,9 +616,7 @@ WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden)
|
||||
addAndMakeVisible (browser.get());
|
||||
}
|
||||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
}
|
||||
WebBrowserComponent::~WebBrowserComponent() = default;
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::goToURL (const String& url,
|
||||
|
@ -1,496 +1,497 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
extern int64 getMouseEventTime();
|
||||
|
||||
JUCE_DECLARE_UUID_GETTER (IOleObject, "00000112-0000-0000-C000-000000000046")
|
||||
JUCE_DECLARE_UUID_GETTER (IOleWindow, "00000114-0000-0000-C000-000000000046")
|
||||
JUCE_DECLARE_UUID_GETTER (IOleInPlaceSite, "00000119-0000-0000-C000-000000000046")
|
||||
|
||||
namespace ActiveXHelpers
|
||||
{
|
||||
//==============================================================================
|
||||
struct JuceIStorage : public ComBaseClassHelper<IStorage>
|
||||
{
|
||||
JuceIStorage() {}
|
||||
|
||||
JUCE_COMRESULT CreateStream (const WCHAR*, DWORD, DWORD, DWORD, IStream**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OpenStream (const WCHAR*, void*, DWORD, DWORD, IStream**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT CreateStorage (const WCHAR*, DWORD, DWORD, DWORD, IStorage**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OpenStorage (const WCHAR*, IStorage*, DWORD, SNB, DWORD, IStorage**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT CopyTo (DWORD, IID const*, SNB, IStorage*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT MoveElementTo (const OLECHAR*,IStorage*, const OLECHAR*, DWORD) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT Commit (DWORD) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT Revert() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT EnumElements (DWORD, void*, DWORD, IEnumSTATSTG**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT DestroyElement (const OLECHAR*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT RenameElement (const WCHAR*, const WCHAR*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetElementTimes (const WCHAR*, FILETIME const*, FILETIME const*, FILETIME const*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetClass (REFCLSID) { return S_OK; }
|
||||
JUCE_COMRESULT SetStateBits (DWORD, DWORD) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT Stat (STATSTG*, DWORD) { return E_NOTIMPL; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JuceOleInPlaceFrame : public ComBaseClassHelper<IOleInPlaceFrame>
|
||||
{
|
||||
JuceOleInPlaceFrame (HWND hwnd) : window (hwnd) {}
|
||||
|
||||
JUCE_COMRESULT GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; }
|
||||
JUCE_COMRESULT ContextSensitiveHelp (BOOL) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetBorder (LPRECT) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT RequestBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetActiveObject (IOleInPlaceActiveObject* a, LPCOLESTR) { activeObject = a; return S_OK; }
|
||||
JUCE_COMRESULT InsertMenus (HMENU, LPOLEMENUGROUPWIDTHS) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetMenu (HMENU, HOLEMENU, HWND) { return S_OK; }
|
||||
JUCE_COMRESULT RemoveMenus (HMENU) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetStatusText (LPCOLESTR) { return S_OK; }
|
||||
JUCE_COMRESULT EnableModeless (BOOL) { return S_OK; }
|
||||
JUCE_COMRESULT TranslateAccelerator (LPMSG, WORD) { return E_NOTIMPL; }
|
||||
|
||||
HRESULT OfferKeyTranslation (LPMSG lpmsg)
|
||||
{
|
||||
if (activeObject != nullptr)
|
||||
return activeObject->TranslateAcceleratorW (lpmsg);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HWND window;
|
||||
ComSmartPtr<IOleInPlaceActiveObject> activeObject;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JuceIOleInPlaceSite : public ComBaseClassHelper<IOleInPlaceSite>
|
||||
{
|
||||
JuceIOleInPlaceSite (HWND hwnd)
|
||||
: window (hwnd),
|
||||
frame (new JuceOleInPlaceFrame (window))
|
||||
{}
|
||||
|
||||
~JuceIOleInPlaceSite()
|
||||
{
|
||||
frame->Release();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; }
|
||||
JUCE_COMRESULT ContextSensitiveHelp (BOOL) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT CanInPlaceActivate() { return S_OK; }
|
||||
JUCE_COMRESULT OnInPlaceActivate() { return S_OK; }
|
||||
JUCE_COMRESULT OnUIActivate() { return S_OK; }
|
||||
|
||||
JUCE_COMRESULT GetWindowContext (LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc, LPRECT, LPRECT, LPOLEINPLACEFRAMEINFO lpFrameInfo)
|
||||
{
|
||||
/* Note: If you call AddRef on the frame here, then some types of object (e.g. web browser control) cause leaks..
|
||||
If you don't call AddRef then others crash (e.g. QuickTime).. Bit of a catch-22, so letting it leak is probably preferable.
|
||||
*/
|
||||
if (lplpFrame != nullptr) { frame->AddRef(); *lplpFrame = frame; }
|
||||
if (lplpDoc != nullptr) *lplpDoc = nullptr;
|
||||
lpFrameInfo->fMDIApp = FALSE;
|
||||
lpFrameInfo->hwndFrame = window;
|
||||
lpFrameInfo->haccel = nullptr;
|
||||
lpFrameInfo->cAccelEntries = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Scroll (SIZE) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OnUIDeactivate (BOOL) { return S_OK; }
|
||||
JUCE_COMRESULT OnInPlaceDeactivate() { return S_OK; }
|
||||
JUCE_COMRESULT DiscardUndoState() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT DeactivateAndUndo() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OnPosRectChange (LPCRECT) { return S_OK; }
|
||||
|
||||
LRESULT offerEventToActiveXControl (::MSG& msg)
|
||||
{
|
||||
if (frame != nullptr)
|
||||
return frame->OfferKeyTranslation (&msg);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HWND window;
|
||||
JuceOleInPlaceFrame* frame;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JuceIOleClientSite : public ComBaseClassHelper<IOleClientSite>
|
||||
{
|
||||
JuceIOleClientSite (HWND window) : inplaceSite (new JuceIOleInPlaceSite (window))
|
||||
{}
|
||||
|
||||
~JuceIOleClientSite()
|
||||
{
|
||||
inplaceSite->Release();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT QueryInterface (REFIID type, void** result)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
if (type == __uuidof (IOleInPlaceSite))
|
||||
{
|
||||
inplaceSite->AddRef();
|
||||
*result = static_cast<IOleInPlaceSite*> (inplaceSite);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return ComBaseClassHelper <IOleClientSite>::QueryInterface (type, result);
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
JUCE_COMRESULT SaveObject() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetMoniker (DWORD, DWORD, IMoniker**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetContainer (LPOLECONTAINER* ppContainer) { *ppContainer = nullptr; return E_NOINTERFACE; }
|
||||
JUCE_COMRESULT ShowObject() { return S_OK; }
|
||||
JUCE_COMRESULT OnShowWindow (BOOL) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT RequestNewObjectLayout() { return E_NOTIMPL; }
|
||||
|
||||
LRESULT offerEventToActiveXControl (::MSG& msg)
|
||||
{
|
||||
if (inplaceSite != nullptr)
|
||||
return inplaceSite->offerEventToActiveXControl (msg);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
JuceIOleInPlaceSite* inplaceSite;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static Array<ActiveXControlComponent*> activeXComps;
|
||||
|
||||
static HWND getHWND (const ActiveXControlComponent* const component)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
HWND hwnd = {};
|
||||
const IID iid = __uuidof (IOleWindow);
|
||||
|
||||
if (auto* window = (IOleWindow*) component->queryInterface (&iid))
|
||||
{
|
||||
window->GetWindow (&hwnd);
|
||||
window->Release();
|
||||
}
|
||||
|
||||
return hwnd;
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
static void offerActiveXMouseEventToPeer (ComponentPeer* peer, HWND hwnd, UINT message, LPARAM lParam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
{
|
||||
RECT activeXRect, peerRect;
|
||||
GetWindowRect (hwnd, &activeXRect);
|
||||
GetWindowRect ((HWND) peer->getNativeHandle(), &peerRect);
|
||||
|
||||
peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse,
|
||||
{ (float) (GET_X_LPARAM (lParam) + activeXRect.left - peerRect.left),
|
||||
(float) (GET_Y_LPARAM (lParam) + activeXRect.top - peerRect.top) },
|
||||
ComponentPeer::getCurrentModifiersRealtime(),
|
||||
MouseInputSource::invalidPressure,
|
||||
MouseInputSource::invalidOrientation,
|
||||
getMouseEventTime());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ActiveXControlComponent::Pimpl : public ComponentMovementWatcher,
|
||||
public ComponentPeer::ScaleFactorListener
|
||||
{
|
||||
public:
|
||||
Pimpl (HWND hwnd, ActiveXControlComponent& activeXComp)
|
||||
: ComponentMovementWatcher (&activeXComp),
|
||||
owner (activeXComp),
|
||||
storage (new ActiveXHelpers::JuceIStorage()),
|
||||
clientSite (new ActiveXHelpers::JuceIOleClientSite (hwnd))
|
||||
{
|
||||
}
|
||||
|
||||
~Pimpl() override
|
||||
{
|
||||
if (control != nullptr)
|
||||
{
|
||||
control->Close (OLECLOSE_NOSAVE);
|
||||
control->Release();
|
||||
}
|
||||
|
||||
clientSite->Release();
|
||||
storage->Release();
|
||||
|
||||
if (currentPeer != nullptr)
|
||||
currentPeer->removeScaleFactorListener (this);
|
||||
}
|
||||
|
||||
void setControlBounds (Rectangle<int> newBounds) const
|
||||
{
|
||||
if (controlHWND != nullptr)
|
||||
{
|
||||
if (auto* peer = owner.getTopLevelComponent()->getPeer())
|
||||
newBounds = (newBounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt();
|
||||
|
||||
MoveWindow (controlHWND, newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight(), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void setControlVisible (bool shouldBeVisible) const
|
||||
{
|
||||
if (controlHWND != nullptr)
|
||||
ShowWindow (controlHWND, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
using ComponentMovementWatcher::componentMovedOrResized;
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
|
||||
{
|
||||
if (auto* peer = owner.getTopLevelComponent()->getPeer())
|
||||
setControlBounds (peer->getAreaCoveredBy (owner));
|
||||
}
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
if (currentPeer != nullptr)
|
||||
currentPeer->removeScaleFactorListener (this);
|
||||
|
||||
componentMovedOrResized (true, true);
|
||||
|
||||
currentPeer = owner.getTopLevelComponent()->getPeer();
|
||||
|
||||
if (currentPeer != nullptr)
|
||||
currentPeer->addScaleFactorListener (this);
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentVisibilityChanged;
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
setControlVisible (owner.isShowing());
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
void nativeScaleFactorChanged (double /*newScaleFactor*/) override
|
||||
{
|
||||
componentMovedOrResized (true, true);
|
||||
}
|
||||
|
||||
// intercepts events going to an activeX control, so we can sneakily use the mouse events
|
||||
static LRESULT CALLBACK activeXHookWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
for (auto* ax : ActiveXHelpers::activeXComps)
|
||||
{
|
||||
if (ax->control != nullptr && ax->control->controlHWND == hwnd)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_LBUTTONDBLCLK:
|
||||
case WM_MBUTTONDBLCLK:
|
||||
case WM_RBUTTONDBLCLK:
|
||||
if (ax->isShowing())
|
||||
{
|
||||
if (auto* peer = ax->getPeer())
|
||||
{
|
||||
ActiveXHelpers::offerActiveXMouseEventToPeer (peer, hwnd, message, lParam);
|
||||
|
||||
if (! ax->areMouseEventsAllowed())
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return CallWindowProc (ax->control->originalWndProc, hwnd, message, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc (hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
ActiveXControlComponent& owner;
|
||||
ComponentPeer* currentPeer = nullptr;
|
||||
HWND controlHWND = {};
|
||||
IStorage* storage = nullptr;
|
||||
ActiveXHelpers::JuceIOleClientSite* clientSite = nullptr;
|
||||
IOleObject* control = nullptr;
|
||||
WNDPROC originalWndProc = nullptr;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ActiveXControlComponent::ActiveXControlComponent()
|
||||
{
|
||||
ActiveXHelpers::activeXComps.add (this);
|
||||
}
|
||||
|
||||
ActiveXControlComponent::~ActiveXControlComponent()
|
||||
{
|
||||
deleteControl();
|
||||
ActiveXHelpers::activeXComps.removeFirstMatchingValue (this);
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::paint (Graphics& g)
|
||||
{
|
||||
if (control == nullptr)
|
||||
g.fillAll (Colours::lightgrey);
|
||||
}
|
||||
|
||||
bool ActiveXControlComponent::createControl (const void* controlIID)
|
||||
{
|
||||
deleteControl();
|
||||
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
auto controlBounds = peer->getAreaCoveredBy (*this);
|
||||
auto hwnd = (HWND) peer->getNativeHandle();
|
||||
|
||||
std::unique_ptr<Pimpl> newControl (new Pimpl (hwnd, *this));
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
HRESULT hr = OleCreate (*(const IID*) controlIID, __uuidof (IOleObject), 1 /*OLERENDER_DRAW*/, nullptr,
|
||||
newControl->clientSite, newControl->storage,
|
||||
(void**) &(newControl->control));
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
if (hr == S_OK)
|
||||
{
|
||||
newControl->control->SetHostNames (L"JUCE", nullptr);
|
||||
|
||||
if (OleSetContainedObject (newControl->control, TRUE) == S_OK)
|
||||
{
|
||||
RECT rect;
|
||||
rect.left = controlBounds.getX();
|
||||
rect.top = controlBounds.getY();
|
||||
rect.right = controlBounds.getRight();
|
||||
rect.bottom = controlBounds.getBottom();
|
||||
|
||||
if (newControl->control->DoVerb (OLEIVERB_SHOW, nullptr, newControl->clientSite, 0, hwnd, &rect) == S_OK)
|
||||
{
|
||||
control.reset (newControl.release());
|
||||
control->controlHWND = ActiveXHelpers::getHWND (this);
|
||||
|
||||
if (control->controlHWND != nullptr)
|
||||
{
|
||||
control->setControlBounds (controlBounds);
|
||||
|
||||
control->originalWndProc = (WNDPROC) GetWindowLongPtr ((HWND) control->controlHWND, GWLP_WNDPROC);
|
||||
SetWindowLongPtr ((HWND) control->controlHWND, GWLP_WNDPROC, (LONG_PTR) Pimpl::activeXHookWndProc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the component must have already been added to a real window when you call this!
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::deleteControl()
|
||||
{
|
||||
control = nullptr;
|
||||
}
|
||||
|
||||
void* ActiveXControlComponent::queryInterface (const void* iid) const
|
||||
{
|
||||
void* result = nullptr;
|
||||
|
||||
if (control != nullptr && control->control != nullptr
|
||||
&& SUCCEEDED (control->control->QueryInterface (*(const IID*) iid, &result)))
|
||||
return result;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::setMouseEventsAllowed (const bool eventsCanReachControl)
|
||||
{
|
||||
mouseEventsAllowed = eventsCanReachControl;
|
||||
}
|
||||
|
||||
intptr_t ActiveXControlComponent::offerEventToActiveXControl (void* ptr)
|
||||
{
|
||||
if (control != nullptr && control->clientSite != nullptr)
|
||||
return (intptr_t) control->clientSite->offerEventToActiveXControl (*reinterpret_cast<::MSG*> (ptr));
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
intptr_t ActiveXControlComponent::offerEventToActiveXControlStatic (void* ptr)
|
||||
{
|
||||
for (auto* ax : ActiveXHelpers::activeXComps)
|
||||
{
|
||||
auto result = ax->offerEventToActiveXControl (ptr);
|
||||
|
||||
if (result != S_FALSE)
|
||||
return result;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
LRESULT juce_offerEventToActiveXControl (::MSG& msg)
|
||||
{
|
||||
if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
|
||||
return ActiveXControlComponent::offerEventToActiveXControlStatic (&msg);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
extern int64 getMouseEventTime();
|
||||
|
||||
JUCE_DECLARE_UUID_GETTER (IOleObject, "00000112-0000-0000-C000-000000000046")
|
||||
JUCE_DECLARE_UUID_GETTER (IOleWindow, "00000114-0000-0000-C000-000000000046")
|
||||
JUCE_DECLARE_UUID_GETTER (IOleInPlaceSite, "00000119-0000-0000-C000-000000000046")
|
||||
|
||||
namespace ActiveXHelpers
|
||||
{
|
||||
//==============================================================================
|
||||
struct JuceIStorage : public ComBaseClassHelper<IStorage>
|
||||
{
|
||||
JuceIStorage() {}
|
||||
|
||||
JUCE_COMRESULT CreateStream (const WCHAR*, DWORD, DWORD, DWORD, IStream**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OpenStream (const WCHAR*, void*, DWORD, DWORD, IStream**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT CreateStorage (const WCHAR*, DWORD, DWORD, DWORD, IStorage**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OpenStorage (const WCHAR*, IStorage*, DWORD, SNB, DWORD, IStorage**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT CopyTo (DWORD, IID const*, SNB, IStorage*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT MoveElementTo (const OLECHAR*,IStorage*, const OLECHAR*, DWORD) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT Commit (DWORD) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT Revert() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT EnumElements (DWORD, void*, DWORD, IEnumSTATSTG**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT DestroyElement (const OLECHAR*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT RenameElement (const WCHAR*, const WCHAR*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetElementTimes (const WCHAR*, FILETIME const*, FILETIME const*, FILETIME const*) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetClass (REFCLSID) { return S_OK; }
|
||||
JUCE_COMRESULT SetStateBits (DWORD, DWORD) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT Stat (STATSTG*, DWORD) { return E_NOTIMPL; }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JuceOleInPlaceFrame : public ComBaseClassHelper<IOleInPlaceFrame>
|
||||
{
|
||||
JuceOleInPlaceFrame (HWND hwnd) : window (hwnd) {}
|
||||
|
||||
JUCE_COMRESULT GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; }
|
||||
JUCE_COMRESULT ContextSensitiveHelp (BOOL) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetBorder (LPRECT) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT RequestBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetBorderSpace (LPCBORDERWIDTHS) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetActiveObject (IOleInPlaceActiveObject* a, LPCOLESTR) { activeObject = a; return S_OK; }
|
||||
JUCE_COMRESULT InsertMenus (HMENU, LPOLEMENUGROUPWIDTHS) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetMenu (HMENU, HOLEMENU, HWND) { return S_OK; }
|
||||
JUCE_COMRESULT RemoveMenus (HMENU) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT SetStatusText (LPCOLESTR) { return S_OK; }
|
||||
JUCE_COMRESULT EnableModeless (BOOL) { return S_OK; }
|
||||
JUCE_COMRESULT TranslateAccelerator (LPMSG, WORD) { return E_NOTIMPL; }
|
||||
|
||||
HRESULT OfferKeyTranslation (LPMSG lpmsg)
|
||||
{
|
||||
if (activeObject != nullptr)
|
||||
return activeObject->TranslateAcceleratorW (lpmsg);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HWND window;
|
||||
ComSmartPtr<IOleInPlaceActiveObject> activeObject;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JuceIOleInPlaceSite : public ComBaseClassHelper<IOleInPlaceSite>
|
||||
{
|
||||
JuceIOleInPlaceSite (HWND hwnd)
|
||||
: window (hwnd),
|
||||
frame (new JuceOleInPlaceFrame (window))
|
||||
{}
|
||||
|
||||
~JuceIOleInPlaceSite()
|
||||
{
|
||||
frame->Release();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetWindow (HWND* lphwnd) { *lphwnd = window; return S_OK; }
|
||||
JUCE_COMRESULT ContextSensitiveHelp (BOOL) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT CanInPlaceActivate() { return S_OK; }
|
||||
JUCE_COMRESULT OnInPlaceActivate() { return S_OK; }
|
||||
JUCE_COMRESULT OnUIActivate() { return S_OK; }
|
||||
|
||||
JUCE_COMRESULT GetWindowContext (LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc, LPRECT, LPRECT, LPOLEINPLACEFRAMEINFO lpFrameInfo)
|
||||
{
|
||||
/* Note: If you call AddRef on the frame here, then some types of object (e.g. web browser control) cause leaks..
|
||||
If you don't call AddRef then others crash (e.g. QuickTime).. Bit of a catch-22, so letting it leak is probably preferable.
|
||||
*/
|
||||
if (lplpFrame != nullptr) { frame->AddRef(); *lplpFrame = frame; }
|
||||
if (lplpDoc != nullptr) *lplpDoc = nullptr;
|
||||
lpFrameInfo->fMDIApp = FALSE;
|
||||
lpFrameInfo->hwndFrame = window;
|
||||
lpFrameInfo->haccel = nullptr;
|
||||
lpFrameInfo->cAccelEntries = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Scroll (SIZE) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OnUIDeactivate (BOOL) { return S_OK; }
|
||||
JUCE_COMRESULT OnInPlaceDeactivate() { return S_OK; }
|
||||
JUCE_COMRESULT DiscardUndoState() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT DeactivateAndUndo() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT OnPosRectChange (LPCRECT) { return S_OK; }
|
||||
|
||||
LRESULT offerEventToActiveXControl (::MSG& msg)
|
||||
{
|
||||
if (frame != nullptr)
|
||||
return frame->OfferKeyTranslation (&msg);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
HWND window;
|
||||
JuceOleInPlaceFrame* frame;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct JuceIOleClientSite : public ComBaseClassHelper<IOleClientSite>
|
||||
{
|
||||
JuceIOleClientSite (HWND window) : inplaceSite (new JuceIOleInPlaceSite (window))
|
||||
{}
|
||||
|
||||
~JuceIOleClientSite()
|
||||
{
|
||||
inplaceSite->Release();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT QueryInterface (REFIID type, void** result)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
if (type == __uuidof (IOleInPlaceSite))
|
||||
{
|
||||
inplaceSite->AddRef();
|
||||
*result = static_cast<IOleInPlaceSite*> (inplaceSite);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return ComBaseClassHelper <IOleClientSite>::QueryInterface (type, result);
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
JUCE_COMRESULT SaveObject() { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetMoniker (DWORD, DWORD, IMoniker**) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT GetContainer (LPOLECONTAINER* ppContainer) { *ppContainer = nullptr; return E_NOINTERFACE; }
|
||||
JUCE_COMRESULT ShowObject() { return S_OK; }
|
||||
JUCE_COMRESULT OnShowWindow (BOOL) { return E_NOTIMPL; }
|
||||
JUCE_COMRESULT RequestNewObjectLayout() { return E_NOTIMPL; }
|
||||
|
||||
LRESULT offerEventToActiveXControl (::MSG& msg)
|
||||
{
|
||||
if (inplaceSite != nullptr)
|
||||
return inplaceSite->offerEventToActiveXControl (msg);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
JuceIOleInPlaceSite* inplaceSite;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static Array<ActiveXControlComponent*> activeXComps;
|
||||
|
||||
static HWND getHWND (const ActiveXControlComponent* const component)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
HWND hwnd = {};
|
||||
const IID iid = __uuidof (IOleWindow);
|
||||
|
||||
if (auto* window = (IOleWindow*) component->queryInterface (&iid))
|
||||
{
|
||||
window->GetWindow (&hwnd);
|
||||
window->Release();
|
||||
}
|
||||
|
||||
return hwnd;
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
static void offerActiveXMouseEventToPeer (ComponentPeer* peer, HWND hwnd, UINT message, LPARAM lParam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
{
|
||||
RECT activeXRect, peerRect;
|
||||
GetWindowRect (hwnd, &activeXRect);
|
||||
GetWindowRect ((HWND) peer->getNativeHandle(), &peerRect);
|
||||
|
||||
peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse,
|
||||
{ (float) (GET_X_LPARAM (lParam) + activeXRect.left - peerRect.left),
|
||||
(float) (GET_Y_LPARAM (lParam) + activeXRect.top - peerRect.top) },
|
||||
ComponentPeer::getCurrentModifiersRealtime(),
|
||||
MouseInputSource::defaultPressure,
|
||||
MouseInputSource::defaultOrientation,
|
||||
getMouseEventTime());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ActiveXControlComponent::Pimpl : public ComponentMovementWatcher,
|
||||
public ComponentPeer::ScaleFactorListener
|
||||
{
|
||||
public:
|
||||
Pimpl (HWND hwnd, ActiveXControlComponent& activeXComp)
|
||||
: ComponentMovementWatcher (&activeXComp),
|
||||
owner (activeXComp),
|
||||
storage (new ActiveXHelpers::JuceIStorage()),
|
||||
clientSite (new ActiveXHelpers::JuceIOleClientSite (hwnd))
|
||||
{
|
||||
}
|
||||
|
||||
~Pimpl() override
|
||||
{
|
||||
if (control != nullptr)
|
||||
{
|
||||
control->Close (OLECLOSE_NOSAVE);
|
||||
control->Release();
|
||||
}
|
||||
|
||||
clientSite->Release();
|
||||
storage->Release();
|
||||
|
||||
if (currentPeer != nullptr)
|
||||
currentPeer->removeScaleFactorListener (this);
|
||||
}
|
||||
|
||||
void setControlBounds (Rectangle<int> newBounds) const
|
||||
{
|
||||
if (controlHWND != nullptr)
|
||||
{
|
||||
if (auto* peer = owner.getTopLevelComponent()->getPeer())
|
||||
newBounds = (newBounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt();
|
||||
|
||||
MoveWindow (controlHWND, newBounds.getX(), newBounds.getY(), newBounds.getWidth(), newBounds.getHeight(), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void setControlVisible (bool shouldBeVisible) const
|
||||
{
|
||||
if (controlHWND != nullptr)
|
||||
ShowWindow (controlHWND, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
using ComponentMovementWatcher::componentMovedOrResized;
|
||||
|
||||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
|
||||
{
|
||||
if (auto* peer = owner.getTopLevelComponent()->getPeer())
|
||||
setControlBounds (peer->getAreaCoveredBy (owner));
|
||||
}
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
if (currentPeer != nullptr)
|
||||
currentPeer->removeScaleFactorListener (this);
|
||||
|
||||
componentMovedOrResized (true, true);
|
||||
|
||||
currentPeer = owner.getTopLevelComponent()->getPeer();
|
||||
|
||||
if (currentPeer != nullptr)
|
||||
currentPeer->addScaleFactorListener (this);
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentVisibilityChanged;
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
setControlVisible (owner.isShowing());
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
void nativeScaleFactorChanged (double /*newScaleFactor*/) override
|
||||
{
|
||||
componentMovedOrResized (true, true);
|
||||
}
|
||||
|
||||
// intercepts events going to an activeX control, so we can sneakily use the mouse events
|
||||
static LRESULT CALLBACK activeXHookWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
for (auto* ax : ActiveXHelpers::activeXComps)
|
||||
{
|
||||
if (ax->control != nullptr && ax->control->controlHWND == hwnd)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_MOUSEMOVE:
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_LBUTTONUP:
|
||||
case WM_MBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
case WM_LBUTTONDBLCLK:
|
||||
case WM_MBUTTONDBLCLK:
|
||||
case WM_RBUTTONDBLCLK:
|
||||
if (ax->isShowing())
|
||||
{
|
||||
if (auto* peer = ax->getPeer())
|
||||
{
|
||||
ActiveXHelpers::offerActiveXMouseEventToPeer (peer, hwnd, message, lParam);
|
||||
|
||||
if (! ax->areMouseEventsAllowed())
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return CallWindowProc (ax->control->originalWndProc, hwnd, message, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc (hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
ActiveXControlComponent& owner;
|
||||
ComponentPeer* currentPeer = nullptr;
|
||||
HWND controlHWND = {};
|
||||
IStorage* storage = nullptr;
|
||||
ActiveXHelpers::JuceIOleClientSite* clientSite = nullptr;
|
||||
IOleObject* control = nullptr;
|
||||
WNDPROC originalWndProc = nullptr;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ActiveXControlComponent::ActiveXControlComponent()
|
||||
{
|
||||
ActiveXHelpers::activeXComps.add (this);
|
||||
}
|
||||
|
||||
ActiveXControlComponent::~ActiveXControlComponent()
|
||||
{
|
||||
deleteControl();
|
||||
ActiveXHelpers::activeXComps.removeFirstMatchingValue (this);
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::paint (Graphics& g)
|
||||
{
|
||||
if (control == nullptr)
|
||||
g.fillAll (Colours::lightgrey);
|
||||
}
|
||||
|
||||
bool ActiveXControlComponent::createControl (const void* controlIID)
|
||||
{
|
||||
deleteControl();
|
||||
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
auto controlBounds = peer->getAreaCoveredBy (*this);
|
||||
auto hwnd = (HWND) peer->getNativeHandle();
|
||||
|
||||
std::unique_ptr<Pimpl> newControl (new Pimpl (hwnd, *this));
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
HRESULT hr = OleCreate (*(const IID*) controlIID, __uuidof (IOleObject), 1 /*OLERENDER_DRAW*/, nullptr,
|
||||
newControl->clientSite, newControl->storage,
|
||||
(void**) &(newControl->control));
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
if (hr == S_OK)
|
||||
{
|
||||
newControl->control->SetHostNames (L"JUCE", nullptr);
|
||||
|
||||
if (OleSetContainedObject (newControl->control, TRUE) == S_OK)
|
||||
{
|
||||
RECT rect;
|
||||
rect.left = controlBounds.getX();
|
||||
rect.top = controlBounds.getY();
|
||||
rect.right = controlBounds.getRight();
|
||||
rect.bottom = controlBounds.getBottom();
|
||||
|
||||
if (newControl->control->DoVerb (OLEIVERB_SHOW, nullptr, newControl->clientSite, 0, hwnd, &rect) == S_OK)
|
||||
{
|
||||
control.reset (newControl.release());
|
||||
control->controlHWND = ActiveXHelpers::getHWND (this);
|
||||
|
||||
if (control->controlHWND != nullptr)
|
||||
{
|
||||
control->setControlBounds (controlBounds);
|
||||
|
||||
control->originalWndProc = (WNDPROC) GetWindowLongPtr ((HWND) control->controlHWND, GWLP_WNDPROC);
|
||||
SetWindowLongPtr ((HWND) control->controlHWND, GWLP_WNDPROC, (LONG_PTR) Pimpl::activeXHookWndProc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the component must have already been added to a real window when you call this!
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::deleteControl()
|
||||
{
|
||||
control = nullptr;
|
||||
}
|
||||
|
||||
void* ActiveXControlComponent::queryInterface (const void* iid) const
|
||||
{
|
||||
void* result = nullptr;
|
||||
|
||||
if (control != nullptr && control->control != nullptr
|
||||
&& SUCCEEDED (control->control->QueryInterface (*(const IID*) iid, &result)))
|
||||
return result;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ActiveXControlComponent::setMouseEventsAllowed (const bool eventsCanReachControl)
|
||||
{
|
||||
mouseEventsAllowed = eventsCanReachControl;
|
||||
}
|
||||
|
||||
intptr_t ActiveXControlComponent::offerEventToActiveXControl (void* ptr)
|
||||
{
|
||||
if (control != nullptr && control->clientSite != nullptr)
|
||||
return (intptr_t) control->clientSite->offerEventToActiveXControl (*reinterpret_cast<::MSG*> (ptr));
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
intptr_t ActiveXControlComponent::offerEventToActiveXControlStatic (void* ptr)
|
||||
{
|
||||
for (auto* ax : ActiveXHelpers::activeXComps)
|
||||
{
|
||||
auto result = ax->offerEventToActiveXControl (ptr);
|
||||
|
||||
if (result != S_FALSE)
|
||||
return result;
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
LRESULT juce_offerEventToActiveXControl (::MSG& msg);
|
||||
LRESULT juce_offerEventToActiveXControl (::MSG& msg)
|
||||
{
|
||||
if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
|
||||
return ActiveXControlComponent::offerEventToActiveXControlStatic (&msg);
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,175 +1,181 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class HWNDComponent::Pimpl : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
Pimpl (HWND h, Component& comp)
|
||||
: ComponentMovementWatcher (&comp),
|
||||
hwnd (h),
|
||||
owner (comp)
|
||||
{
|
||||
if (owner.isShowing())
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
~Pimpl() override
|
||||
{
|
||||
removeFromParent();
|
||||
DestroyWindow (hwnd);
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool wasMoved, bool wasResized) override
|
||||
{
|
||||
if (auto* peer = owner.getTopLevelComponent()->getPeer())
|
||||
{
|
||||
auto area = (peer->getAreaCoveredBy (owner).toFloat() * peer->getPlatformScaleFactor()).getSmallestIntegerContainer();
|
||||
|
||||
UINT flagsToSend = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER;
|
||||
|
||||
if (! wasMoved) flagsToSend |= SWP_NOMOVE;
|
||||
if (! wasResized) flagsToSend |= SWP_NOSIZE;
|
||||
|
||||
ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { hwnd };
|
||||
|
||||
SetWindowPos (hwnd, nullptr, area.getX(), area.getY(), area.getWidth(), area.getHeight(), flagsToSend);
|
||||
}
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentMovedOrResized;
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
auto* peer = owner.getPeer();
|
||||
|
||||
if (currentPeer != peer)
|
||||
{
|
||||
removeFromParent();
|
||||
currentPeer = peer;
|
||||
|
||||
addToParent();
|
||||
}
|
||||
|
||||
auto isShowing = owner.isShowing();
|
||||
|
||||
ShowWindow (hwnd, isShowing ? SW_SHOWNA : SW_HIDE);
|
||||
|
||||
if (isShowing)
|
||||
InvalidateRect (hwnd, nullptr, 0);
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentVisibilityChanged;
|
||||
|
||||
void componentBroughtToFront (Component& comp) override
|
||||
{
|
||||
ComponentMovementWatcher::componentBroughtToFront (comp);
|
||||
}
|
||||
|
||||
Rectangle<int> getHWNDBounds() const
|
||||
{
|
||||
if (auto* peer = owner.getPeer())
|
||||
{
|
||||
ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { hwnd };
|
||||
|
||||
RECT r;
|
||||
GetWindowRect (hwnd, &r);
|
||||
Rectangle<int> windowRectangle (r.right - r.left, r.bottom - r.top);
|
||||
|
||||
return (windowRectangle.toFloat() / peer->getPlatformScaleFactor()).toNearestInt();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
HWND hwnd;
|
||||
|
||||
private:
|
||||
void addToParent()
|
||||
{
|
||||
if (currentPeer != nullptr)
|
||||
{
|
||||
auto windowFlags = GetWindowLongPtr (hwnd, -16);
|
||||
|
||||
using FlagType = decltype (windowFlags);
|
||||
|
||||
windowFlags &= ~(FlagType) WS_POPUP;
|
||||
windowFlags |= (FlagType) WS_CHILD;
|
||||
|
||||
SetWindowLongPtr (hwnd, -16, windowFlags);
|
||||
SetParent (hwnd, (HWND) currentPeer->getNativeHandle());
|
||||
|
||||
componentMovedOrResized (true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void removeFromParent()
|
||||
{
|
||||
ShowWindow (hwnd, SW_HIDE);
|
||||
SetParent (hwnd, nullptr);
|
||||
}
|
||||
|
||||
Component& owner;
|
||||
ComponentPeer* currentPeer = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
HWNDComponent::HWNDComponent() {}
|
||||
HWNDComponent::~HWNDComponent() {}
|
||||
|
||||
void HWNDComponent::paint (Graphics&) {}
|
||||
|
||||
void HWNDComponent::setHWND (void* hwnd)
|
||||
{
|
||||
if (hwnd != getHWND())
|
||||
{
|
||||
pimpl.reset();
|
||||
|
||||
if (hwnd != nullptr)
|
||||
pimpl.reset (new Pimpl ((HWND) hwnd, *this));
|
||||
}
|
||||
}
|
||||
|
||||
void* HWNDComponent::getHWND() const
|
||||
{
|
||||
return pimpl == nullptr ? nullptr : (void*) pimpl->hwnd;
|
||||
}
|
||||
|
||||
void HWNDComponent::resizeToFit()
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
setBounds (pimpl->getHWNDBounds());
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class HWNDComponent::Pimpl : public ComponentMovementWatcher
|
||||
{
|
||||
public:
|
||||
Pimpl (HWND h, Component& comp)
|
||||
: ComponentMovementWatcher (&comp),
|
||||
hwnd (h),
|
||||
owner (comp)
|
||||
{
|
||||
if (owner.isShowing())
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
~Pimpl() override
|
||||
{
|
||||
removeFromParent();
|
||||
DestroyWindow (hwnd);
|
||||
}
|
||||
|
||||
void componentMovedOrResized (bool wasMoved, bool wasResized) override
|
||||
{
|
||||
if (auto* peer = owner.getTopLevelComponent()->getPeer())
|
||||
{
|
||||
auto area = (peer->getAreaCoveredBy (owner).toFloat() * peer->getPlatformScaleFactor()).getSmallestIntegerContainer();
|
||||
|
||||
UINT flagsToSend = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER;
|
||||
|
||||
if (! wasMoved) flagsToSend |= SWP_NOMOVE;
|
||||
if (! wasResized) flagsToSend |= SWP_NOSIZE;
|
||||
|
||||
ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { hwnd };
|
||||
|
||||
SetWindowPos (hwnd, nullptr, area.getX(), area.getY(), area.getWidth(), area.getHeight(), flagsToSend);
|
||||
}
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentMovedOrResized;
|
||||
|
||||
void componentPeerChanged() override
|
||||
{
|
||||
auto* peer = owner.getPeer();
|
||||
|
||||
if (currentPeer != peer)
|
||||
{
|
||||
removeFromParent();
|
||||
currentPeer = peer;
|
||||
|
||||
addToParent();
|
||||
}
|
||||
|
||||
auto isShowing = owner.isShowing();
|
||||
|
||||
ShowWindow (hwnd, isShowing ? SW_SHOWNA : SW_HIDE);
|
||||
|
||||
if (isShowing)
|
||||
InvalidateRect (hwnd, nullptr, 0);
|
||||
}
|
||||
|
||||
void componentVisibilityChanged() override
|
||||
{
|
||||
componentPeerChanged();
|
||||
}
|
||||
|
||||
using ComponentMovementWatcher::componentVisibilityChanged;
|
||||
|
||||
void componentBroughtToFront (Component& comp) override
|
||||
{
|
||||
ComponentMovementWatcher::componentBroughtToFront (comp);
|
||||
}
|
||||
|
||||
Rectangle<int> getHWNDBounds() const
|
||||
{
|
||||
if (auto* peer = owner.getPeer())
|
||||
{
|
||||
ScopedThreadDPIAwarenessSetter threadDpiAwarenessSetter { hwnd };
|
||||
|
||||
RECT r;
|
||||
GetWindowRect (hwnd, &r);
|
||||
Rectangle<int> windowRectangle (r.right - r.left, r.bottom - r.top);
|
||||
|
||||
return (windowRectangle.toFloat() / peer->getPlatformScaleFactor()).toNearestInt();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
HWND hwnd;
|
||||
|
||||
private:
|
||||
void addToParent()
|
||||
{
|
||||
if (currentPeer != nullptr)
|
||||
{
|
||||
auto windowFlags = GetWindowLongPtr (hwnd, -16);
|
||||
|
||||
using FlagType = decltype (windowFlags);
|
||||
|
||||
windowFlags &= ~(FlagType) WS_POPUP;
|
||||
windowFlags |= (FlagType) WS_CHILD;
|
||||
|
||||
SetWindowLongPtr (hwnd, -16, windowFlags);
|
||||
SetParent (hwnd, (HWND) currentPeer->getNativeHandle());
|
||||
|
||||
componentMovedOrResized (true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void removeFromParent()
|
||||
{
|
||||
ShowWindow (hwnd, SW_HIDE);
|
||||
SetParent (hwnd, nullptr);
|
||||
}
|
||||
|
||||
Component& owner;
|
||||
ComponentPeer* currentPeer = nullptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
HWNDComponent::HWNDComponent() {}
|
||||
HWNDComponent::~HWNDComponent() {}
|
||||
|
||||
void HWNDComponent::paint (Graphics&) {}
|
||||
|
||||
void HWNDComponent::setHWND (void* hwnd)
|
||||
{
|
||||
if (hwnd != getHWND())
|
||||
{
|
||||
pimpl.reset();
|
||||
|
||||
if (hwnd != nullptr)
|
||||
pimpl.reset (new Pimpl ((HWND) hwnd, *this));
|
||||
}
|
||||
}
|
||||
|
||||
void* HWNDComponent::getHWND() const
|
||||
{
|
||||
return pimpl == nullptr ? nullptr : (void*) pimpl->hwnd;
|
||||
}
|
||||
|
||||
void HWNDComponent::resizeToFit()
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
setBounds (pimpl->getHWNDBounds());
|
||||
}
|
||||
|
||||
void HWNDComponent::updateHWNDBounds()
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->componentMovedOrResized (true, true);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,242 +1,242 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
extern void* getUser32Function (const char*);
|
||||
|
||||
namespace IconConverters
|
||||
{
|
||||
extern HICON createHICONFromImage (const Image&, BOOL isIcon, int hotspotX, int hotspotY);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SystemTrayIconComponent::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (SystemTrayIconComponent& owner_, HICON hicon, HWND hwnd)
|
||||
: owner (owner_),
|
||||
originalWndProc ((WNDPROC) GetWindowLongPtr (hwnd, GWLP_WNDPROC)),
|
||||
taskbarCreatedMessage (RegisterWindowMessage (TEXT ("TaskbarCreated")))
|
||||
{
|
||||
SetWindowLongPtr (hwnd, GWLP_WNDPROC, (LONG_PTR) hookedWndProc);
|
||||
|
||||
zerostruct (iconData);
|
||||
iconData.cbSize = sizeof (iconData);
|
||||
iconData.hWnd = hwnd;
|
||||
iconData.uID = (UINT) (pointer_sized_int) hwnd;
|
||||
iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
iconData.uCallbackMessage = WM_TRAYNOTIFY;
|
||||
iconData.hIcon = hicon;
|
||||
|
||||
notify (NIM_ADD);
|
||||
|
||||
// In order to receive the "TaskbarCreated" message, we need to request that it's not filtered out.
|
||||
// (Need to load dynamically, as ChangeWindowMessageFilter is only available in Vista and later)
|
||||
typedef BOOL (WINAPI* ChangeWindowMessageFilterType) (UINT, DWORD);
|
||||
|
||||
if (ChangeWindowMessageFilterType changeWindowMessageFilter
|
||||
= (ChangeWindowMessageFilterType) getUser32Function ("ChangeWindowMessageFilter"))
|
||||
changeWindowMessageFilter (taskbarCreatedMessage, 1 /* MSGFLT_ADD */);
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
SetWindowLongPtr (iconData.hWnd, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
|
||||
|
||||
iconData.uFlags = 0;
|
||||
notify (NIM_DELETE);
|
||||
DestroyIcon (iconData.hIcon);
|
||||
}
|
||||
|
||||
void updateIcon (HICON hicon)
|
||||
{
|
||||
HICON oldIcon = iconData.hIcon;
|
||||
|
||||
iconData.hIcon = hicon;
|
||||
iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
notify (NIM_MODIFY);
|
||||
|
||||
DestroyIcon (oldIcon);
|
||||
}
|
||||
|
||||
void setToolTip (const String& toolTip)
|
||||
{
|
||||
iconData.uFlags = NIF_TIP;
|
||||
toolTip.copyToUTF16 (iconData.szTip, sizeof (iconData.szTip) - 1);
|
||||
notify (NIM_MODIFY);
|
||||
}
|
||||
|
||||
void handleTaskBarEvent (const LPARAM lParam)
|
||||
{
|
||||
if (owner.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN
|
||||
|| lParam == WM_LBUTTONDBLCLK || lParam == WM_RBUTTONDBLCLK)
|
||||
{
|
||||
if (auto* current = Component::getCurrentlyModalComponent())
|
||||
current->inputAttemptWhenModal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ModifierKeys eventMods (ComponentPeer::getCurrentModifiersRealtime());
|
||||
|
||||
if (lParam == WM_LBUTTONDOWN || lParam == WM_LBUTTONDBLCLK)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::leftButtonModifier);
|
||||
else if (lParam == WM_RBUTTONDOWN || lParam == WM_RBUTTONDBLCLK)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::rightButtonModifier);
|
||||
else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
|
||||
eventMods = eventMods.withoutMouseButtons();
|
||||
|
||||
const Time eventTime (getMouseEventTime());
|
||||
|
||||
const MouseEvent e (Desktop::getInstance().getMainMouseSource(), {}, eventMods,
|
||||
MouseInputSource::invalidPressure, MouseInputSource::invalidOrientation,
|
||||
MouseInputSource::invalidRotation, MouseInputSource::invalidTiltX, MouseInputSource::invalidTiltY,
|
||||
&owner, &owner, eventTime, {}, eventTime, 1, false);
|
||||
|
||||
if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
|
||||
{
|
||||
SetFocus (iconData.hWnd);
|
||||
SetForegroundWindow (iconData.hWnd);
|
||||
owner.mouseDown (e);
|
||||
}
|
||||
else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
|
||||
{
|
||||
owner.mouseUp (e);
|
||||
}
|
||||
else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_RBUTTONDBLCLK)
|
||||
{
|
||||
owner.mouseDoubleClick (e);
|
||||
}
|
||||
else if (lParam == WM_MOUSEMOVE)
|
||||
{
|
||||
owner.mouseMove (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Pimpl* getPimpl (HWND hwnd)
|
||||
{
|
||||
if (JuceWindowIdentifier::isJUCEWindow (hwnd))
|
||||
if (ComponentPeer* peer = (ComponentPeer*) GetWindowLongPtr (hwnd, 8))
|
||||
if (SystemTrayIconComponent* const iconComp = dynamic_cast<SystemTrayIconComponent*> (&(peer->getComponent())))
|
||||
return iconComp->pimpl.get();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK hookedWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (Pimpl* const p = getPimpl (hwnd))
|
||||
return p->windowProc (hwnd, message, wParam, lParam);
|
||||
|
||||
return DefWindowProcW (hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT windowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (message == WM_TRAYNOTIFY)
|
||||
{
|
||||
handleTaskBarEvent (lParam);
|
||||
}
|
||||
else if (message == taskbarCreatedMessage)
|
||||
{
|
||||
iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
notify (NIM_ADD);
|
||||
}
|
||||
|
||||
return CallWindowProc (originalWndProc, hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
void showBubble (const String& title, const String& content)
|
||||
{
|
||||
iconData.uFlags = 0x10 /*NIF_INFO*/;
|
||||
title.copyToUTF16 (iconData.szInfoTitle, sizeof (iconData.szInfoTitle) - 1);
|
||||
content.copyToUTF16 (iconData.szInfo, sizeof (iconData.szInfo) - 1);
|
||||
notify (NIM_MODIFY);
|
||||
}
|
||||
|
||||
SystemTrayIconComponent& owner;
|
||||
NOTIFYICONDATA iconData;
|
||||
|
||||
private:
|
||||
WNDPROC originalWndProc;
|
||||
const DWORD taskbarCreatedMessage;
|
||||
enum { WM_TRAYNOTIFY = WM_USER + 100 };
|
||||
|
||||
void notify (DWORD message) noexcept { Shell_NotifyIcon (message, &iconData); }
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void SystemTrayIconComponent::setIconImage (const Image& colourImage, const Image&)
|
||||
{
|
||||
if (colourImage.isValid())
|
||||
{
|
||||
HICON hicon = IconConverters::createHICONFromImage (colourImage, TRUE, 0, 0);
|
||||
|
||||
if (pimpl == nullptr)
|
||||
pimpl.reset (new Pimpl (*this, hicon, (HWND) getWindowHandle()));
|
||||
else
|
||||
pimpl->updateIcon (hicon);
|
||||
}
|
||||
else
|
||||
{
|
||||
pimpl.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setIconTooltip (const String& tooltip)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->setToolTip (tooltip);
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setHighlighted (bool)
|
||||
{
|
||||
// N/A on Windows.
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::showInfoBubble (const String& title, const String& content)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->showBubble (title, content);
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::hideInfoBubble()
|
||||
{
|
||||
showInfoBubble (String(), String());
|
||||
}
|
||||
|
||||
void* SystemTrayIconComponent::getNativeHandle() const
|
||||
{
|
||||
return pimpl != nullptr ? &(pimpl->iconData) : nullptr;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
extern void* getUser32Function (const char*);
|
||||
|
||||
namespace IconConverters
|
||||
{
|
||||
extern HICON createHICONFromImage (const Image&, BOOL isIcon, int hotspotX, int hotspotY);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SystemTrayIconComponent::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (SystemTrayIconComponent& owner_, HICON hicon, HWND hwnd)
|
||||
: owner (owner_),
|
||||
originalWndProc ((WNDPROC) GetWindowLongPtr (hwnd, GWLP_WNDPROC)),
|
||||
taskbarCreatedMessage (RegisterWindowMessage (TEXT ("TaskbarCreated")))
|
||||
{
|
||||
SetWindowLongPtr (hwnd, GWLP_WNDPROC, (LONG_PTR) hookedWndProc);
|
||||
|
||||
zerostruct (iconData);
|
||||
iconData.cbSize = sizeof (iconData);
|
||||
iconData.hWnd = hwnd;
|
||||
iconData.uID = (UINT) (pointer_sized_int) hwnd;
|
||||
iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
iconData.uCallbackMessage = WM_TRAYNOTIFY;
|
||||
iconData.hIcon = hicon;
|
||||
|
||||
notify (NIM_ADD);
|
||||
|
||||
// In order to receive the "TaskbarCreated" message, we need to request that it's not filtered out.
|
||||
// (Need to load dynamically, as ChangeWindowMessageFilter is only available in Vista and later)
|
||||
typedef BOOL (WINAPI* ChangeWindowMessageFilterType) (UINT, DWORD);
|
||||
|
||||
if (ChangeWindowMessageFilterType changeWindowMessageFilter
|
||||
= (ChangeWindowMessageFilterType) getUser32Function ("ChangeWindowMessageFilter"))
|
||||
changeWindowMessageFilter (taskbarCreatedMessage, 1 /* MSGFLT_ADD */);
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
SetWindowLongPtr (iconData.hWnd, GWLP_WNDPROC, (LONG_PTR) originalWndProc);
|
||||
|
||||
iconData.uFlags = 0;
|
||||
notify (NIM_DELETE);
|
||||
DestroyIcon (iconData.hIcon);
|
||||
}
|
||||
|
||||
void updateIcon (HICON hicon)
|
||||
{
|
||||
HICON oldIcon = iconData.hIcon;
|
||||
|
||||
iconData.hIcon = hicon;
|
||||
iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
notify (NIM_MODIFY);
|
||||
|
||||
DestroyIcon (oldIcon);
|
||||
}
|
||||
|
||||
void setToolTip (const String& toolTip)
|
||||
{
|
||||
iconData.uFlags = NIF_TIP;
|
||||
toolTip.copyToUTF16 (iconData.szTip, sizeof (iconData.szTip) - 1);
|
||||
notify (NIM_MODIFY);
|
||||
}
|
||||
|
||||
void handleTaskBarEvent (const LPARAM lParam)
|
||||
{
|
||||
if (owner.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN
|
||||
|| lParam == WM_LBUTTONDBLCLK || lParam == WM_RBUTTONDBLCLK)
|
||||
{
|
||||
if (auto* current = Component::getCurrentlyModalComponent())
|
||||
current->inputAttemptWhenModal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ModifierKeys eventMods (ComponentPeer::getCurrentModifiersRealtime());
|
||||
|
||||
if (lParam == WM_LBUTTONDOWN || lParam == WM_LBUTTONDBLCLK)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::leftButtonModifier);
|
||||
else if (lParam == WM_RBUTTONDOWN || lParam == WM_RBUTTONDBLCLK)
|
||||
eventMods = eventMods.withFlags (ModifierKeys::rightButtonModifier);
|
||||
else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
|
||||
eventMods = eventMods.withoutMouseButtons();
|
||||
|
||||
const Time eventTime (getMouseEventTime());
|
||||
|
||||
const MouseEvent e (Desktop::getInstance().getMainMouseSource(), {}, eventMods,
|
||||
MouseInputSource::defaultPressure, MouseInputSource::defaultOrientation,
|
||||
MouseInputSource::defaultRotation, MouseInputSource::defaultTiltX, MouseInputSource::defaultTiltY,
|
||||
&owner, &owner, eventTime, {}, eventTime, 1, false);
|
||||
|
||||
if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
|
||||
{
|
||||
SetFocus (iconData.hWnd);
|
||||
SetForegroundWindow (iconData.hWnd);
|
||||
owner.mouseDown (e);
|
||||
}
|
||||
else if (lParam == WM_LBUTTONUP || lParam == WM_RBUTTONUP)
|
||||
{
|
||||
owner.mouseUp (e);
|
||||
}
|
||||
else if (lParam == WM_LBUTTONDBLCLK || lParam == WM_RBUTTONDBLCLK)
|
||||
{
|
||||
owner.mouseDoubleClick (e);
|
||||
}
|
||||
else if (lParam == WM_MOUSEMOVE)
|
||||
{
|
||||
owner.mouseMove (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Pimpl* getPimpl (HWND hwnd)
|
||||
{
|
||||
if (JuceWindowIdentifier::isJUCEWindow (hwnd))
|
||||
if (ComponentPeer* peer = (ComponentPeer*) GetWindowLongPtr (hwnd, 8))
|
||||
if (SystemTrayIconComponent* const iconComp = dynamic_cast<SystemTrayIconComponent*> (&(peer->getComponent())))
|
||||
return iconComp->pimpl.get();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK hookedWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (Pimpl* const p = getPimpl (hwnd))
|
||||
return p->windowProc (hwnd, message, wParam, lParam);
|
||||
|
||||
return DefWindowProcW (hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
LRESULT windowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (message == WM_TRAYNOTIFY)
|
||||
{
|
||||
handleTaskBarEvent (lParam);
|
||||
}
|
||||
else if (message == taskbarCreatedMessage)
|
||||
{
|
||||
iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
notify (NIM_ADD);
|
||||
}
|
||||
|
||||
return CallWindowProc (originalWndProc, hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
void showBubble (const String& title, const String& content)
|
||||
{
|
||||
iconData.uFlags = 0x10 /*NIF_INFO*/;
|
||||
title.copyToUTF16 (iconData.szInfoTitle, sizeof (iconData.szInfoTitle) - 1);
|
||||
content.copyToUTF16 (iconData.szInfo, sizeof (iconData.szInfo) - 1);
|
||||
notify (NIM_MODIFY);
|
||||
}
|
||||
|
||||
SystemTrayIconComponent& owner;
|
||||
NOTIFYICONDATA iconData;
|
||||
|
||||
private:
|
||||
WNDPROC originalWndProc;
|
||||
const DWORD taskbarCreatedMessage;
|
||||
enum { WM_TRAYNOTIFY = WM_USER + 100 };
|
||||
|
||||
void notify (DWORD message) noexcept { Shell_NotifyIcon (message, &iconData); }
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void SystemTrayIconComponent::setIconImage (const Image& colourImage, const Image&)
|
||||
{
|
||||
if (colourImage.isValid())
|
||||
{
|
||||
HICON hicon = IconConverters::createHICONFromImage (colourImage, TRUE, 0, 0);
|
||||
|
||||
if (pimpl == nullptr)
|
||||
pimpl.reset (new Pimpl (*this, hicon, (HWND) getWindowHandle()));
|
||||
else
|
||||
pimpl->updateIcon (hicon);
|
||||
}
|
||||
else
|
||||
{
|
||||
pimpl.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setIconTooltip (const String& tooltip)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->setToolTip (tooltip);
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::setHighlighted (bool)
|
||||
{
|
||||
// N/A on Windows.
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::showInfoBubble (const String& title, const String& content)
|
||||
{
|
||||
if (pimpl != nullptr)
|
||||
pimpl->showBubble (title, content);
|
||||
}
|
||||
|
||||
void SystemTrayIconComponent::hideInfoBubble()
|
||||
{
|
||||
showInfoBubble (String(), String());
|
||||
}
|
||||
|
||||
void* SystemTrayIconComponent::getNativeHandle() const
|
||||
{
|
||||
return pimpl != nullptr ? &(pimpl->iconData) : nullptr;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user