From 49b47cc866c324498632b521d35b0d911331df28 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Wed, 28 Apr 2010 14:51:11 +0100 Subject: [PATCH] Minor additions to Rectangle and CodeEditorComponent. Jucer development. --- .../Source/model/jucer_CodeGenerator.cpp | 117 ++++++++++++-- .../Source/model/jucer_CodeGenerator.h | 50 +++++- .../Source/model/jucer_ComponentDocument.cpp | 26 ++-- .../Source/model/jucer_ComponentDocument.h | 8 +- .../jucer_ComponentEditor.cpp | 144 ++++++++++++++++++ juce_amalgamated.cpp | 98 +++++++----- juce_amalgamated.h | 14 ++ .../code_editor/juce_CodeDocument.cpp | 1 + .../code_editor/juce_CodeEditorComponent.cpp | 54 +++++-- .../code_editor/juce_CodeEditorComponent.h | 10 ++ src/gui/graphics/geometry/juce_Rectangle.h | 12 ++ .../mac/juce_iphone_UIViewComponentPeer.mm | 18 ++- .../mac/juce_mac_NSViewComponentPeer.mm | 26 ++-- 13 files changed, 479 insertions(+), 99 deletions(-) diff --git a/extras/Jucer (experimental)/Source/model/jucer_CodeGenerator.cpp b/extras/Jucer (experimental)/Source/model/jucer_CodeGenerator.cpp index 007b902a68..fe7e937913 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_CodeGenerator.cpp +++ b/extras/Jucer (experimental)/Source/model/jucer_CodeGenerator.cpp @@ -304,15 +304,79 @@ void CodeGenerator::applyToCode (String& code, const File& targetFile, //============================================================================== -CodeGenerator::CustomisedCodeSnippets::CustomisedCodeSnippets() +CodeGenerator::CustomCodeList::Iterator::Iterator (const String& documentText, CustomCodeList& customCode_) + : customCode (customCode_), i (0), codeDocument (0) { + lines.addLines (documentText); + } -CodeGenerator::CustomisedCodeSnippets::~CustomisedCodeSnippets() +CodeGenerator::CustomCodeList::Iterator::~Iterator() { } -void CodeGenerator::CustomisedCodeSnippets::reloadFrom (const String& fileContent) +bool CodeGenerator::CustomCodeList::Iterator::next() +{ + textBefore = String::empty; + textAfter = String::empty; + + while (i < lines.size()) + { + textBefore += lines[i] + "\n"; + + if (lines[i].trimStart().startsWith ("//[")) + { + String tag (lines[i].trimStart().substring (3)); + tag = tag.upToFirstOccurrenceOf ("]", false, false).trim(); + + if (! (tag.isEmpty() || tag.startsWithChar ('/'))) + { + const int endLine = indexOfLineStartingWith (lines, "//[/" + tag + "]", i + 1); + + if (endLine > i) + { + sectionName = tag; + codeDocument = customCode.getDocumentFor (tag, true); + i = endLine; + + bool isLastTag = true; + for (int j = i + 1; j < lines.size(); ++j) + { + if (lines[j].trimStart().startsWith ("//[")) + { + isLastTag = false; + break; + } + } + + if (isLastTag) + { + textAfter = lines.joinIntoString (newLine, i, lines.size() - i); + i = lines.size(); + } + + return true; + } + } + } + + ++i; + } + + return false; +} + + +//============================================================================== +CodeGenerator::CustomCodeList::CustomCodeList() +{ +} + +CodeGenerator::CustomCodeList::~CustomCodeList() +{ +} + +void CodeGenerator::CustomCodeList::reloadFrom (const String& fileContent) { sectionNames.clear(); sectionContent.clear(); @@ -339,19 +403,23 @@ void CodeGenerator::CustomisedCodeSnippets::reloadFrom (const String& fileConten sectionNames.add (tag); - CodeDocument* doc = new CodeDocument(); + CodeDocumentRef::Ptr doc (new CodeDocumentRef (new CodeDocument())); sectionContent.add (doc); - doc->replaceAllContent (content); + doc->getDocument().replaceAllContent (content); + doc->getDocument().clearUndoHistory(); + doc->getDocument().setSavePoint(); i = endLine; } } } } + + sendSynchronousChangeMessage (this); } -void CodeGenerator::CustomisedCodeSnippets::applyTo (String& fileContent) const +void CodeGenerator::CustomCodeList::applyTo (String& fileContent) const { StringArray lines; lines.addLines (fileContent); @@ -400,12 +468,31 @@ void CodeGenerator::CustomisedCodeSnippets::applyTo (String& fileContent) const fileContent = lines.joinIntoString (newLine); } -bool CodeGenerator::CustomisedCodeSnippets::areAnySnippetsUnsaved() const +bool CodeGenerator::CustomCodeList::needsSaving() const +{ + for (int i = sectionContent.size(); --i >= 0;) + if (sectionContent.getUnchecked(i)->getDocument().hasChangedSinceSavePoint()) + return true; + + return false; +} + +int CodeGenerator::CustomCodeList::getNumSections() const +{ + return sectionNames.size(); +} + +const String CodeGenerator::CustomCodeList::getSectionName (int index) const +{ + return sectionNames [index]; +} + +const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr CodeGenerator::CustomCodeList::getDocument (int index) const { - return true; //xxx + return sectionContent [index]; } -CodeDocument* CodeGenerator::CustomisedCodeSnippets::getDocumentFor (const String& sectionName, bool createIfNotFound) +const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr CodeGenerator::CustomCodeList::getDocumentFor (const String& sectionName, bool createIfNotFound) { const int index = sectionNames.indexOf (sectionName); @@ -415,24 +502,26 @@ CodeDocument* CodeGenerator::CustomisedCodeSnippets::getDocumentFor (const Strin if (createIfNotFound) { sectionNames.add (sectionName); - sectionContent.add (new CodeDocument()); - return sectionContent.getLast(); + + const CodeDocumentRef::Ptr doc (new CodeDocumentRef (new CodeDocument())); + sectionContent.add (doc); + return doc; } return 0; } -const String CodeGenerator::CustomisedCodeSnippets::getSectionContent (const String& sectionName) const +const String CodeGenerator::CustomCodeList::getSectionContent (const String& sectionName) const { const int index = sectionNames.indexOf (sectionName); if (index >= 0) - return sectionContent[index]->getAllContent(); + return sectionContent[index]->getDocument().getAllContent(); return String::empty; } -void CodeGenerator::CustomisedCodeSnippets::removeSection (const String& sectionName) +void CodeGenerator::CustomCodeList::removeSection (const String& sectionName) { const int index = sectionNames.indexOf (sectionName); diff --git a/extras/Jucer (experimental)/Source/model/jucer_CodeGenerator.h b/extras/Jucer (experimental)/Source/model/jucer_CodeGenerator.h index 99f5d754df..d46ecad288 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_CodeGenerator.h +++ b/extras/Jucer (experimental)/Source/model/jucer_CodeGenerator.h @@ -75,23 +75,61 @@ public: //============================================================================== // An object to load and store all the user-defined bits of code as documents. - class CustomisedCodeSnippets + class CustomCodeList : public ChangeBroadcaster { public: - CustomisedCodeSnippets(); - ~CustomisedCodeSnippets(); + CustomCodeList(); + ~CustomCodeList(); void reloadFrom (const String& fileContent); void applyTo (String& fileContent) const; - bool areAnySnippetsUnsaved() const; + bool needsSaving() const; - CodeDocument* getDocumentFor (const String& sectionName, bool createIfNotFound); + //============================================================================== + // Ref-counted wrapper for a code document.. + class CodeDocumentRef : public ReferenceCountedObject + { + public: + CodeDocumentRef (CodeDocument* doc_) : doc (doc_) {} + + CodeDocument& getDocument() const throw() { return *doc; } + + typedef ReferenceCountedObjectPtr Ptr; + + private: + CodeDocument* doc; + }; + + //============================================================================== + int getNumSections() const; + const String getSectionName (int index) const; + + const CodeDocumentRef::Ptr getDocument (int index) const; + + const CodeDocumentRef::Ptr getDocumentFor (const String& sectionName, bool createIfNotFound); const String getSectionContent (const String& sectionName) const; void removeSection (const String& sectionName); + class Iterator + { + public: + Iterator (const String& documentText, CustomCodeList& customCode); + ~Iterator(); + + bool next(); + + String textBefore, textAfter, sectionName; + CodeDocumentRef::Ptr codeDocument; + + private: + CustomCodeList& customCode; + StringArray lines; + int i; + }; + private: StringArray sectionNames; - OwnedArray sectionContent; + ReferenceCountedArray sectionContent; }; //============================================================================== diff --git a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp index 045d90e944..bf31f2af18 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp +++ b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.cpp @@ -80,19 +80,24 @@ void ComponentDocument::beginNewTransaction() undoManager.beginNewTransaction(); } -void ComponentDocument::valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property) +void ComponentDocument::changed() { changedSinceSaved = true; } +void ComponentDocument::valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const var::identifier& property) +{ + changed(); +} + void ComponentDocument::valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { - changedSinceSaved = true; + changed(); } void ComponentDocument::valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) { - changedSinceSaved = true; + changed(); } bool ComponentDocument::isComponentFile (const File& file) @@ -114,6 +119,9 @@ bool ComponentDocument::isComponentFile (const File& file) return false; } +const String ComponentDocument::getCppTemplate() const { return String (BinaryData::jucer_ComponentTemplate_cpp); } +const String ComponentDocument::getHeaderTemplate() const { return String (BinaryData::jucer_ComponentTemplate_h); } + void ComponentDocument::writeCode (OutputStream& cpp, OutputStream& header) { CodeGenerator codeGen; @@ -128,20 +136,20 @@ void ComponentDocument::writeCode (OutputStream& cpp, OutputStream& header) } { - String code (BinaryData::jucer_ComponentTemplate_cpp); + String code (getCppTemplate()); String oldContent; codeGen.applyToCode (code, cppFile, false, project); - customisedCodeSnippets.applyTo (code); + customCode.applyTo (code); cpp << code; } { - String code (BinaryData::jucer_ComponentTemplate_h); + String code (getHeaderTemplate()); String oldContent; codeGen.applyToCode (code, cppFile.withFileExtension (".h"), false, project); - customisedCodeSnippets.applyTo (code); + customCode.applyTo (code); header << code; } } @@ -224,7 +232,7 @@ bool ComponentDocument::reload() undoManager.clearUndoHistory(); changedSinceSaved = false; - customisedCodeSnippets.reloadFrom (cppFile.loadFileAsString()); + customCode.reloadFrom (cppFile.loadFileAsString()); return true; } } @@ -234,7 +242,7 @@ bool ComponentDocument::reload() bool ComponentDocument::hasChangedSinceLastSave() { - return changedSinceSaved; + return changedSinceSaved || customCode.needsSaving(); } void ComponentDocument::createSubTreeIfNotThere (const String& name) diff --git a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h index 728f7d62c3..0af094740f 100644 --- a/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h +++ b/extras/Jucer (experimental)/Source/model/jucer_ComponentDocument.h @@ -47,6 +47,7 @@ public: bool save(); bool reload(); bool hasChangedSinceLastSave(); + void changed(); const File getCppFile() const { return cppFile; } //============================================================================== @@ -136,7 +137,10 @@ public: void endDrag (const MouseEvent& e); //============================================================================== - CodeGenerator::CustomisedCodeSnippets& getCustomisedCodeSnippets() throw() { return customisedCodeSnippets; } + CodeGenerator::CustomCodeList& getCustomCodeList() throw() { return customCode; } + + const String getCppTemplate() const; + const String getHeaderTemplate() const; //============================================================================== ValueTree& getRoot() { return root; } @@ -164,7 +168,7 @@ private: File cppFile; ValueTree root; ScopedPointer markersX, markersY; - CodeGenerator::CustomisedCodeSnippets customisedCodeSnippets; + CodeGenerator::CustomCodeList customCode; mutable UndoManager undoManager; bool changedSinceSaved; diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp index 911575eb40..48aed649d6 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditor.cpp @@ -536,14 +536,158 @@ public: CodeEditorHolder (ComponentEditor& editor_) : editor (editor_) { + addAndMakeVisible (viewport = new Viewport()); + viewport->setScrollBarsShown (true, false); + viewport->setViewedComponent (new ContentHolder (editor)); } ~CodeEditorHolder() { } + void resized() + { + viewport->setBounds (getLocalBounds()); + + int visWidth = viewport->getMaximumVisibleWidth(); + dynamic_cast (viewport->getViewedComponent())->updateSize (visWidth); + + if (viewport->getMaximumVisibleWidth() != visWidth) + dynamic_cast (viewport->getViewedComponent())->updateSize (viewport->getMaximumVisibleWidth()); + } + private: ComponentEditor& editor; + enum { updateCommandId = 0x23427fa1 }; + + class EditorHolder : public Component, + public CodeDocument::Listener + { + public: + EditorHolder (const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr doc, + const String& name, const String& textBefore, const String& textAfter) + : Component (name), document (doc), cppTokeniser(), codeEditor (doc->getDocument(), &cppTokeniser) + { + linesBefore.addLines (textBefore); + linesAfter.addLines (textAfter); + + addAndMakeVisible (&codeEditor); + doc->getDocument().addListener (this); + } + + ~EditorHolder() + { + document->getDocument().removeListener (this); + } + + void paint (Graphics& g) + { + g.setFont (codeEditor.getFont()); + g.setColour (Colours::darkgrey); + + const int fontHeight = codeEditor.getLineHeight(); + const int fontAscent = (int) codeEditor.getFont().getAscent(); + const int textX = 5; + + int i; + for (i = 0; i < linesBefore.size(); ++i) + g.drawSingleLineText (linesBefore[i], textX, i * fontHeight + fontAscent); + + for (i = 0; i < linesAfter.size(); ++i) + g.drawSingleLineText (linesAfter[i], textX, codeEditor.getBottom() + i * fontHeight + fontAscent); + } + + void updateSize (int width) + { + const int fontHeight = codeEditor.getLineHeight(); + + codeEditor.setBounds (0, fontHeight * linesBefore.size() + 1, + width, 2 + codeEditor.getScrollbarThickness() + + fontHeight * jmax (1, document->getDocument().getNumLines())); + + setSize (width, (linesBefore.size() + linesAfter.size()) * fontHeight + codeEditor.getHeight()); + } + + void codeDocumentChanged (const CodeDocument::Position&, const CodeDocument::Position&) + { + int oldHeight = getHeight(); + updateSize (getWidth()); + if (getHeight() != oldHeight) + getParentComponent()->handleCommandMessage (updateCommandId); + } + + const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr document; + CPlusPlusCodeTokeniser cppTokeniser; + CodeEditorComponent codeEditor; + StringArray linesBefore, linesAfter; + }; + + class ContentHolder : public Component, + public ChangeListener + { + public: + ContentHolder (ComponentEditor& editor_) + : editor (editor_) + { + setOpaque (true); + editor.getDocument().getCustomCodeList().addChangeListener (this); + changeListenerCallback (0); + } + + ~ContentHolder() + { + editor.getDocument().getCustomCodeList().removeChangeListener (this); + } + + void paint (Graphics& g) + { + g.fillAll (Colours::lightgrey); + } + + void updateSize (int width) + { + int y = 2; + + for (int i = 0; i < editors.size(); ++i) + { + editors.getUnchecked(i)->updateSize (width - 8); + editors.getUnchecked(i)->setTopLeftPosition (4, y + 1); + y = editors.getUnchecked(i)->getBottom() + 1; + } + + setSize (width, y + 2); + } + + void changeListenerCallback (void*) + { + editors.clear(); + + CodeGenerator::CustomCodeList::Iterator iter (editor.getDocument().getCppTemplate(), + editor.getDocument().getCustomCodeList()); + + while (iter.next()) + { + EditorHolder* ed = new EditorHolder (iter.codeDocument, iter.sectionName, iter.textBefore, iter.textAfter); + editors.add (ed); + addAndMakeVisible (ed); + } + + updateSize (getWidth()); + } + + void handleCommandMessage (int commandId) + { + if (commandId == updateCommandId) + updateSize (getWidth()); + else + Component::handleCommandMessage (commandId); + } + + OwnedArray editors; + ComponentEditor& editor; + }; + + Viewport* viewport; }; //============================================================================== diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 8f2ec02f56..eb4e637563 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -43436,6 +43436,7 @@ void CodeDocument::Position::setPositionMaintained (const bool isMaintained) thr } else { + // If this happens, you may have deleted the document while there are Position objects that are still using it... jassert (owner->positionsToMaintain.contains (this)); owner->positionsToMaintain.removeValue (this); } @@ -43949,7 +43950,8 @@ class CodeEditorComponent::CaretComponent : public Component, public Timer { public: - CaretComponent() + CaretComponent (CodeEditorComponent& owner_) + : owner (owner_) { setAlwaysOnTop (true); setInterceptsMouseClicks (false, false); @@ -43961,27 +43963,29 @@ public: void paint (Graphics& g) { - if (getParentComponent()->hasKeyboardFocus (true)) - g.fillAll (findColour (CodeEditorComponent::caretColourId)); + g.fillAll (findColour (CodeEditorComponent::caretColourId)); } void timerCallback() { - setVisible (! isVisible()); + setVisible (shouldBeShown() && ! isVisible()); } - void updatePosition (CodeEditorComponent& owner) + void updatePosition() { startTimer (400); - setVisible (true); + setVisible (shouldBeShown()); - const Rectangle pos (owner.getCharacterBounds (owner.getCaretPos())); - setBounds (pos.getX(), pos.getY(), 2, pos.getHeight()); + setBounds (owner.getCharacterBounds (owner.getCaretPos()).withWidth (2)); } private: + CodeEditorComponent& owner; + CaretComponent (const CaretComponent&); CaretComponent& operator= (const CaretComponent&); + + bool shouldBeShown() const { return owner.hasKeyboardFocus (true); } }; class CodeEditorComponent::CodeEditorLine @@ -44224,7 +44228,7 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& document_, addAndMakeVisible (horizontalScrollBar = new ScrollBar (false)); horizontalScrollBar->setSingleStepSize (1.0); - addAndMakeVisible (caret = new CaretComponent()); + addAndMakeVisible (caret = new CaretComponent (*this)); Font f (12.0f); f.setTypefaceName (Font::getDefaultMonospacedFontName()); @@ -44262,7 +44266,7 @@ void CodeEditorComponent::codeDocumentChanged (const CodeDocument::Position& aff triggerAsyncUpdate(); - caret->updatePosition (*this); + caret->updatePosition(); columnToTryToMaintain = -1; if (affectedTextEnd.getPosition() >= selectionStart.getPosition() @@ -44282,7 +44286,7 @@ void CodeEditorComponent::resized() columnsOnScreen = (int) ((getWidth() - scrollbarThickness) / charWidth); lines.clear(); rebuildLineTokens(); - caret->updatePosition (*this); + caret->updatePosition(); verticalScrollBar->setBounds (getWidth() - scrollbarThickness, 0, scrollbarThickness, getHeight() - scrollbarThickness); horizontalScrollBar->setBounds (gutter, getHeight() - scrollbarThickness, getWidth() - scrollbarThickness - gutter, scrollbarThickness); @@ -44424,7 +44428,7 @@ void CodeEditorComponent::moveCaretTo (const CodeDocument::Position& newPos, con deselectAll(); } - caret->updatePosition (*this); + caret->updatePosition(); scrollToKeepCaretOnScreen(); updateScrollBars(); } @@ -44455,7 +44459,7 @@ void CodeEditorComponent::scrollToLineInternal (int newFirstLineOnScreen) if (newFirstLineOnScreen != firstLineOnScreen) { firstLineOnScreen = newFirstLineOnScreen; - caret->updatePosition (*this); + caret->updatePosition(); updateCachedIterators (firstLineOnScreen); triggerAsyncUpdate(); @@ -44469,7 +44473,7 @@ void CodeEditorComponent::scrollToColumnInternal (double column) if (xOffset != newOffset) { xOffset = newOffset; - caret->updatePosition (*this); + caret->updatePosition(); repaint(); } } @@ -44967,8 +44971,16 @@ void CodeEditorComponent::mouseDoubleClick (const MouseEvent& e) void CodeEditorComponent::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) { - verticalScrollBar->mouseWheelMove (e, 0, wheelIncrementY); - horizontalScrollBar->mouseWheelMove (e, wheelIncrementX, 0); + if ((verticalScrollBar->isVisible() && wheelIncrementY != 0) + || (horizontalScrollBar->isVisible() && wheelIncrementX != 0)) + { + verticalScrollBar->mouseWheelMove (e, 0, wheelIncrementY); + horizontalScrollBar->mouseWheelMove (e, wheelIncrementX, 0); + } + else + { + Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY); + } } void CodeEditorComponent::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart) @@ -44979,6 +44991,16 @@ void CodeEditorComponent::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, doub scrollToColumnInternal (newRangeStart); } +void CodeEditorComponent::focusGained (FocusChangeType cause) +{ + caret->updatePosition(); +} + +void CodeEditorComponent::focusLost (FocusChangeType cause) +{ + caret->updatePosition(); +} + void CodeEditorComponent::setTabSize (const int numSpaces, const bool insertSpaces) throw() { useSpacesForTabs = insertSpaces; @@ -240061,15 +240083,19 @@ void UIViewComponentPeer::toFront (bool makeActiveWindow) void UIViewComponentPeer::toBehind (ComponentPeer* other) { - UIViewComponentPeer* o = (UIViewComponentPeer*) other; + UIViewComponentPeer* const otherPeer = dynamic_cast (other); + jassert (otherPeer != 0); // wrong type of window? - if (isSharedWindow) + if (otherPeer != 0) { - [[view superview] insertSubview: view belowSubview: o->view]; - } - else - { - jassertfalse // don't know how to do this + if (isSharedWindow) + { + [[view superview] insertSubview: view belowSubview: otherPeer->view]; + } + else + { + jassertfalse // don't know how to do this + } } } @@ -245320,19 +245346,23 @@ void NSViewComponentPeer::toFront (bool makeActiveWindow) void NSViewComponentPeer::toBehind (ComponentPeer* other) { - NSViewComponentPeer* o = (NSViewComponentPeer*) other; + NSViewComponentPeer* const otherPeer = dynamic_cast (other); + jassert (otherPeer != 0); // wrong type of window? - if (isSharedWindow) + if (otherPeer != 0) { - [[view superview] addSubview: view - positioned: NSWindowBelow - relativeTo: o->view]; - } - else - { - [window orderWindow: NSWindowBelow - relativeTo: o->window != 0 ? [o->window windowNumber] - : nil ]; + if (isSharedWindow) + { + [[view superview] addSubview: view + positioned: NSWindowBelow + relativeTo: otherPeer->view]; + } + else + { + [window orderWindow: NSWindowBelow + relativeTo: otherPeer->window != 0 ? [otherPeer->window windowNumber] + : nil ]; + } } } diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 7af60cc928..e05a6855a8 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -10313,6 +10313,14 @@ public: void setHeight (const ValueType newHeight) throw() { h = newHeight; } + const Rectangle withX (const ValueType newX) const throw() { return Rectangle (newX, y, w, h); } + + const Rectangle withY (const ValueType newY) const throw() { return Rectangle (x, newY, w, h); } + + const Rectangle withWidth (const ValueType newWidth) const throw() { return Rectangle (x, y, newWidth, h); } + + const Rectangle withHeight (const ValueType newHeight) const throw() { return Rectangle (x, y, w, newHeight); } + void setLeft (const ValueType newLeft) throw() { w = jmax (ValueType(), x + w - newLeft); @@ -21156,6 +21164,8 @@ public: void setFont (const Font& newFont); + const Font& getFont() const throw() { return font; } + void resetToDefaultColours(); void setColourForTokenType (int tokenType, const Colour& colour); @@ -21174,6 +21184,8 @@ public: void setScrollbarThickness (int thickness) throw(); + int getScrollbarThickness() const throw() { return scrollbarThickness; } + void resized(); void paint (Graphics& g); bool keyPressed (const KeyPress& key); @@ -21182,6 +21194,8 @@ public: void mouseUp (const MouseEvent& e); void mouseDoubleClick (const MouseEvent& e); void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); + void focusGained (FocusChangeType cause); + void focusLost (FocusChangeType cause); void timerCallback(); void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart); void handleAsyncUpdate(); diff --git a/src/gui/components/code_editor/juce_CodeDocument.cpp b/src/gui/components/code_editor/juce_CodeDocument.cpp index 320938f415..156f7fb7ed 100644 --- a/src/gui/components/code_editor/juce_CodeDocument.cpp +++ b/src/gui/components/code_editor/juce_CodeDocument.cpp @@ -431,6 +431,7 @@ void CodeDocument::Position::setPositionMaintained (const bool isMaintained) thr } else { + // If this happens, you may have deleted the document while there are Position objects that are still using it... jassert (owner->positionsToMaintain.contains (this)); owner->positionsToMaintain.removeValue (this); } diff --git a/src/gui/components/code_editor/juce_CodeEditorComponent.cpp b/src/gui/components/code_editor/juce_CodeEditorComponent.cpp index 3d6fb5f5e7..b2abf7c1b4 100644 --- a/src/gui/components/code_editor/juce_CodeEditorComponent.cpp +++ b/src/gui/components/code_editor/juce_CodeEditorComponent.cpp @@ -37,7 +37,8 @@ class CodeEditorComponent::CaretComponent : public Component, public Timer { public: - CaretComponent() + CaretComponent (CodeEditorComponent& owner_) + : owner (owner_) { setAlwaysOnTop (true); setInterceptsMouseClicks (false, false); @@ -49,27 +50,29 @@ public: void paint (Graphics& g) { - if (getParentComponent()->hasKeyboardFocus (true)) - g.fillAll (findColour (CodeEditorComponent::caretColourId)); + g.fillAll (findColour (CodeEditorComponent::caretColourId)); } void timerCallback() { - setVisible (! isVisible()); + setVisible (shouldBeShown() && ! isVisible()); } - void updatePosition (CodeEditorComponent& owner) + void updatePosition() { startTimer (400); - setVisible (true); + setVisible (shouldBeShown()); - const Rectangle pos (owner.getCharacterBounds (owner.getCaretPos())); - setBounds (pos.getX(), pos.getY(), 2, pos.getHeight()); + setBounds (owner.getCharacterBounds (owner.getCaretPos()).withWidth (2)); } private: + CodeEditorComponent& owner; + CaretComponent (const CaretComponent&); CaretComponent& operator= (const CaretComponent&); + + bool shouldBeShown() const { return owner.hasKeyboardFocus (true); } }; //============================================================================== @@ -314,7 +317,7 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& document_, addAndMakeVisible (horizontalScrollBar = new ScrollBar (false)); horizontalScrollBar->setSingleStepSize (1.0); - addAndMakeVisible (caret = new CaretComponent()); + addAndMakeVisible (caret = new CaretComponent (*this)); Font f (12.0f); f.setTypefaceName (Font::getDefaultMonospacedFontName()); @@ -353,7 +356,7 @@ void CodeEditorComponent::codeDocumentChanged (const CodeDocument::Position& aff triggerAsyncUpdate(); - caret->updatePosition (*this); + caret->updatePosition(); columnToTryToMaintain = -1; if (affectedTextEnd.getPosition() >= selectionStart.getPosition() @@ -373,7 +376,7 @@ void CodeEditorComponent::resized() columnsOnScreen = (int) ((getWidth() - scrollbarThickness) / charWidth); lines.clear(); rebuildLineTokens(); - caret->updatePosition (*this); + caret->updatePosition(); verticalScrollBar->setBounds (getWidth() - scrollbarThickness, 0, scrollbarThickness, getHeight() - scrollbarThickness); horizontalScrollBar->setBounds (gutter, getHeight() - scrollbarThickness, getWidth() - scrollbarThickness - gutter, scrollbarThickness); @@ -516,7 +519,7 @@ void CodeEditorComponent::moveCaretTo (const CodeDocument::Position& newPos, con deselectAll(); } - caret->updatePosition (*this); + caret->updatePosition(); scrollToKeepCaretOnScreen(); updateScrollBars(); } @@ -547,7 +550,7 @@ void CodeEditorComponent::scrollToLineInternal (int newFirstLineOnScreen) if (newFirstLineOnScreen != firstLineOnScreen) { firstLineOnScreen = newFirstLineOnScreen; - caret->updatePosition (*this); + caret->updatePosition(); updateCachedIterators (firstLineOnScreen); triggerAsyncUpdate(); @@ -561,7 +564,7 @@ void CodeEditorComponent::scrollToColumnInternal (double column) if (xOffset != newOffset) { xOffset = newOffset; - caret->updatePosition (*this); + caret->updatePosition(); repaint(); } } @@ -1071,8 +1074,16 @@ void CodeEditorComponent::mouseDoubleClick (const MouseEvent& e) void CodeEditorComponent::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) { - verticalScrollBar->mouseWheelMove (e, 0, wheelIncrementY); - horizontalScrollBar->mouseWheelMove (e, wheelIncrementX, 0); + if ((verticalScrollBar->isVisible() && wheelIncrementY != 0) + || (horizontalScrollBar->isVisible() && wheelIncrementX != 0)) + { + verticalScrollBar->mouseWheelMove (e, 0, wheelIncrementY); + horizontalScrollBar->mouseWheelMove (e, wheelIncrementX, 0); + } + else + { + Component::mouseWheelMove (e, wheelIncrementX, wheelIncrementY); + } } void CodeEditorComponent::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart) @@ -1083,6 +1094,17 @@ void CodeEditorComponent::scrollBarMoved (ScrollBar* scrollBarThatHasMoved, doub scrollToColumnInternal (newRangeStart); } +//============================================================================== +void CodeEditorComponent::focusGained (FocusChangeType cause) +{ + caret->updatePosition(); +} + +void CodeEditorComponent::focusLost (FocusChangeType cause) +{ + caret->updatePosition(); +} + //============================================================================== void CodeEditorComponent::setTabSize (const int numSpaces, const bool insertSpaces) throw() { diff --git a/src/gui/components/code_editor/juce_CodeEditorComponent.h b/src/gui/components/code_editor/juce_CodeEditorComponent.h index 463f234f55..5e680c048a 100644 --- a/src/gui/components/code_editor/juce_CodeEditorComponent.h +++ b/src/gui/components/code_editor/juce_CodeEditorComponent.h @@ -175,6 +175,9 @@ public: */ void setFont (const Font& newFont); + /** Returns the font that the editor is using. */ + const Font& getFont() const throw() { return font; } + /** Resets the syntax highlighting colours to the default ones provided by the code tokeniser. @see CodeTokeniser::getDefaultColour @@ -217,6 +220,9 @@ public: /** Changes the size of the scrollbars. */ void setScrollbarThickness (int thickness) throw(); + /** Returns the thickness of the scrollbars. */ + int getScrollbarThickness() const throw() { return scrollbarThickness; } + //============================================================================== /** @internal */ void resized(); @@ -235,6 +241,10 @@ public: /** @internal */ void mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY); /** @internal */ + void focusGained (FocusChangeType cause); + /** @internal */ + void focusLost (FocusChangeType cause); + /** @internal */ void timerCallback(); /** @internal */ void scrollBarMoved (ScrollBar* scrollBarThatHasMoved, double newRangeStart); diff --git a/src/gui/graphics/geometry/juce_Rectangle.h b/src/gui/graphics/geometry/juce_Rectangle.h index 60e5cd6e4f..031931a8d8 100644 --- a/src/gui/graphics/geometry/juce_Rectangle.h +++ b/src/gui/graphics/geometry/juce_Rectangle.h @@ -161,6 +161,18 @@ public: /** Changes the rectangle's height */ void setHeight (const ValueType newHeight) throw() { h = newHeight; } + /** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */ + const Rectangle withX (const ValueType newX) const throw() { return Rectangle (newX, y, w, h); } + + /** Returns a rectangle which has the same size and x-position as this one, but with a different y-position. */ + const Rectangle withY (const ValueType newY) const throw() { return Rectangle (x, newY, w, h); } + + /** Returns a rectangle which has the same position and height as this one, but with a different width. */ + const Rectangle withWidth (const ValueType newWidth) const throw() { return Rectangle (x, y, newWidth, h); } + + /** Returns a rectangle which has the same position and width as this one, but with a different height. */ + const Rectangle withHeight (const ValueType newHeight) const throw() { return Rectangle (x, y, w, newHeight); } + /** Moves the x position, adjusting the width so that the right-hand edge remains in the same place. If the x is moved to be on the right of the current right-hand edge, the width will be set to zero. */ diff --git a/src/native/mac/juce_iphone_UIViewComponentPeer.mm b/src/native/mac/juce_iphone_UIViewComponentPeer.mm index 68807a3f65..e3f9f077b1 100644 --- a/src/native/mac/juce_iphone_UIViewComponentPeer.mm +++ b/src/native/mac/juce_iphone_UIViewComponentPeer.mm @@ -592,15 +592,19 @@ void UIViewComponentPeer::toFront (bool makeActiveWindow) void UIViewComponentPeer::toBehind (ComponentPeer* other) { - UIViewComponentPeer* o = (UIViewComponentPeer*) other; + UIViewComponentPeer* const otherPeer = dynamic_cast (other); + jassert (otherPeer != 0); // wrong type of window? - if (isSharedWindow) + if (otherPeer != 0) { - [[view superview] insertSubview: view belowSubview: o->view]; - } - else - { - jassertfalse // don't know how to do this + if (isSharedWindow) + { + [[view superview] insertSubview: view belowSubview: otherPeer->view]; + } + else + { + jassertfalse // don't know how to do this + } } } diff --git a/src/native/mac/juce_mac_NSViewComponentPeer.mm b/src/native/mac/juce_mac_NSViewComponentPeer.mm index 94921a462e..0930d8c27c 100644 --- a/src/native/mac/juce_mac_NSViewComponentPeer.mm +++ b/src/native/mac/juce_mac_NSViewComponentPeer.mm @@ -1178,19 +1178,23 @@ void NSViewComponentPeer::toFront (bool makeActiveWindow) void NSViewComponentPeer::toBehind (ComponentPeer* other) { - NSViewComponentPeer* o = (NSViewComponentPeer*) other; + NSViewComponentPeer* const otherPeer = dynamic_cast (other); + jassert (otherPeer != 0); // wrong type of window? - if (isSharedWindow) - { - [[view superview] addSubview: view - positioned: NSWindowBelow - relativeTo: o->view]; - } - else + if (otherPeer != 0) { - [window orderWindow: NSWindowBelow - relativeTo: o->window != 0 ? [o->window windowNumber] - : nil ]; + if (isSharedWindow) + { + [[view superview] addSubview: view + positioned: NSWindowBelow + relativeTo: otherPeer->view]; + } + else + { + [window orderWindow: NSWindowBelow + relativeTo: otherPeer->window != 0 ? [otherPeer->window windowNumber] + : nil ]; + } } }