| @@ -567,7 +567,7 @@ static void createTabTextLayout (const TabBarButton& button, const Rectangle<int | |||
| textLayout.createLayout (s, (float) textArea.getWidth()); | |||
| } | |||
| static Colour getTabBackgroundColour (TabBarButton& button) | |||
| Colour IntrojucerLookAndFeel::getTabBackgroundColour (TabBarButton& button) | |||
| { | |||
| const Colour bkg (button.findColour (mainBackgroundColourId).contrasting (0.15f)); | |||
| @@ -580,6 +580,7 @@ static Colour getTabBackgroundColour (TabBarButton& button) | |||
| void IntrojucerLookAndFeel::drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown) | |||
| { | |||
| const Rectangle<int> activeArea (button.getActiveArea()); | |||
| const Colour bkg (getTabBackgroundColour (button)); | |||
| g.setGradientFill (ColourGradient (bkg.brighter (0.1f), 0, (float) activeArea.getY(), | |||
| @@ -89,6 +89,7 @@ public: | |||
| int getTabButtonOverlap (int tabDepth); | |||
| int getTabButtonSpaceAroundImage(); | |||
| int getTabButtonBestWidth (TabBarButton& button, int tabDepth); | |||
| static Colour getTabBackgroundColour (TabBarButton& button); | |||
| void drawTabButton (TabBarButton& button, Graphics& g, bool isMouseOver, bool isMouseDown); | |||
| Rectangle<int> getTabButtonExtraComponentBounds (const TabBarButton& button, Rectangle<int>& textArea, Component& comp); | |||
| @@ -46,8 +46,7 @@ CodeDocument& SourceCodeDocument::getCodeDocument() | |||
| Component* SourceCodeDocument::createEditor() | |||
| { | |||
| SourceCodeEditor* e = new SourceCodeEditor (this); | |||
| e->createEditor (getCodeDocument()); | |||
| SourceCodeEditor* e = new SourceCodeEditor (this, getCodeDocument()); | |||
| applyLastState (*(e->editor)); | |||
| return e; | |||
| } | |||
| @@ -114,9 +113,21 @@ void SourceCodeDocument::applyLastState (CodeEditorComponent& editor) const | |||
| } | |||
| //============================================================================== | |||
| SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* doc) | |||
| SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* doc, CodeDocument& codeDocument) | |||
| : DocumentEditorComponent (doc) | |||
| { | |||
| setOpaque (true); | |||
| if (document->getFile().hasFileExtension (sourceOrHeaderFileExtensions)) | |||
| setEditor (new CppCodeEditorComponent (document->getFile(), codeDocument)); | |||
| else | |||
| setEditor (new GenericCodeEditorComponent (document->getFile(), codeDocument, nullptr)); | |||
| } | |||
| SourceCodeEditor::SourceCodeEditor (OpenDocumentManager::Document* doc, CodeEditorComponent* ed) | |||
| : DocumentEditorComponent (doc) | |||
| { | |||
| setEditor (ed); | |||
| } | |||
| SourceCodeEditor::~SourceCodeEditor() | |||
| @@ -130,14 +141,6 @@ SourceCodeEditor::~SourceCodeEditor() | |||
| doc->updateLastState (*editor); | |||
| } | |||
| void SourceCodeEditor::createEditor (CodeDocument& codeDocument) | |||
| { | |||
| if (document->getFile().hasFileExtension (sourceOrHeaderFileExtensions)) | |||
| setEditor (new CppCodeEditorComponent (document->getFile(), codeDocument)); | |||
| else | |||
| setEditor (new GenericCodeEditorComponent (document->getFile(), codeDocument, nullptr)); | |||
| } | |||
| void SourceCodeEditor::setEditor (CodeEditorComponent* newEditor) | |||
| { | |||
| if (editor != nullptr) | |||
| @@ -141,12 +141,10 @@ class SourceCodeEditor : public DocumentEditorComponent, | |||
| private CodeDocument::Listener | |||
| { | |||
| public: | |||
| SourceCodeEditor (OpenDocumentManager::Document* document); | |||
| SourceCodeEditor (OpenDocumentManager::Document* document, CodeDocument&); | |||
| SourceCodeEditor (OpenDocumentManager::Document* document, CodeEditorComponent*); | |||
| ~SourceCodeEditor(); | |||
| void createEditor (CodeDocument& codeDocument); | |||
| void setEditor (CodeEditorComponent*); | |||
| void scrollToKeepRangeOnScreen (Range<int> range); | |||
| void highlight (Range<int> range, bool cursorAtStart); | |||
| @@ -165,6 +163,7 @@ private: | |||
| void codeDocumentTextInserted (const String&, int); | |||
| void codeDocumentTextDeleted (int, int); | |||
| void setEditor (CodeEditorComponent*); | |||
| void updateColourScheme(); | |||
| void checkSaveState(); | |||
| @@ -329,9 +329,9 @@ JucerDocumentEditor::JucerDocumentEditor (JucerDocument* const doc) | |||
| tabbedComponent.addTab ("Resources", tabColour, new ResourceEditorPanel (*document), true); | |||
| SourceCodeEditor* codeEditor = new SourceCodeEditor (&document->getCppDocument()); | |||
| codeEditor->setEditor (new CppCodeEditorComponent (document->getCppFile(), | |||
| document->getCppDocument().getCodeDocument())); | |||
| SourceCodeEditor* codeEditor = new SourceCodeEditor (&document->getCppDocument(), | |||
| new CppCodeEditorComponent (document->getCppFile(), | |||
| document->getCppDocument().getCodeDocument())); | |||
| tabbedComponent.addTab ("Code", tabColour, codeEditor, true); | |||
| @@ -39,6 +39,8 @@ extern "C" | |||
| } | |||
| #endif | |||
| static CTFontRef getCTFontFromTypeface (const Font& f); | |||
| namespace CoreTextTypeLayout | |||
| { | |||
| static String findBestAvailableStyle (const Font& font, CGAffineTransform& requiredTransform) | |||
| @@ -187,6 +189,18 @@ namespace CoreTextTypeLayout | |||
| HeapBlock<CGPoint> local; | |||
| }; | |||
| static CTFontRef getOrCreateFont (const Font& f) | |||
| { | |||
| if (CTFontRef ctf = getCTFontFromTypeface (f)) | |||
| { | |||
| CFRetain (ctf); | |||
| return ctf; | |||
| } | |||
| CGAffineTransform transform; | |||
| return createCTFont (f, referenceFontSize, transform); | |||
| } | |||
| //============================================================================== | |||
| static CFAttributedStringRef createCFAttributedString (const AttributedString& text) | |||
| { | |||
| @@ -213,13 +227,14 @@ namespace CoreTextTypeLayout | |||
| if (const Font* const f = attr->getFont()) | |||
| { | |||
| CGAffineTransform transform; | |||
| CTFontRef ctFontRef = createCTFont (*f, referenceFontSize, transform); | |||
| ctFontRef = getFontWithPointSize (ctFontRef, f->getHeight() * getHeightToPointsFactor (ctFontRef)); | |||
| if (CTFontRef ctFontRef = getOrCreateFont (*f)) | |||
| { | |||
| ctFontRef = getFontWithPointSize (ctFontRef, f->getHeight() * getHeightToPointsFactor (ctFontRef)); | |||
| CFAttributedStringSetAttribute (attribString, CFRangeMake (range.getStart(), range.getLength()), | |||
| kCTFontAttributeName, ctFontRef); | |||
| CFRelease (ctFontRef); | |||
| CFAttributedStringSetAttribute (attribString, CFRangeMake (range.getStart(), range.getLength()), | |||
| kCTFontAttributeName, ctFontRef); | |||
| CFRelease (ctFontRef); | |||
| } | |||
| } | |||
| if (const Colour* const col = attr->getColour()) | |||
| @@ -416,11 +431,11 @@ class OSXTypeface : public Typeface | |||
| public: | |||
| OSXTypeface (const Font& font) | |||
| : Typeface (font.getTypefaceName(), | |||
| font.getTypefaceStyle()), | |||
| font.getTypefaceStyle()), | |||
| fontRef (nullptr), | |||
| ctFontRef (nullptr), | |||
| fontHeightToPointsFactor (1.0f), | |||
| renderingTransform (CGAffineTransformIdentity), | |||
| ctFontRef (nullptr), | |||
| attributedStringAtts (nullptr), | |||
| ascent (0.0f), | |||
| unitsToHeightScaleFactor (0.0f) | |||
| @@ -566,12 +581,12 @@ public: | |||
| //============================================================================== | |||
| CGFontRef fontRef; | |||
| CTFontRef ctFontRef; | |||
| float fontHeightToPointsFactor; | |||
| CGAffineTransform renderingTransform; | |||
| private: | |||
| CTFontRef ctFontRef; | |||
| CFDictionaryRef attributedStringAtts; | |||
| float ascent, unitsToHeightScaleFactor; | |||
| AffineTransform pathTransform; | |||
| @@ -598,6 +613,15 @@ private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface) | |||
| }; | |||
| CTFontRef getCTFontFromTypeface (const Font& f) | |||
| { | |||
| if (OSXTypeface* tf = dynamic_cast <OSXTypeface*> (f.getTypeface())) | |||
| return tf->ctFontRef; | |||
| return 0; | |||
| } | |||
| StringArray Font::findAllTypefaceNames() | |||
| { | |||
| StringArray names; | |||
| @@ -158,28 +158,21 @@ void DrawableText::recalculateCoordinates (Expression::Scope* scope) | |||
| repaint(); | |||
| } | |||
| const AffineTransform DrawableText::getArrangementAndTransform (GlyphArrangement& glyphs) const | |||
| { | |||
| const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength(); | |||
| const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength(); | |||
| glyphs.addFittedText (scaledFont, text, 0, 0, w, h, justification, 0x100000); | |||
| return AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].x, resolvedPoints[0].y, | |||
| w, 0, resolvedPoints[1].x, resolvedPoints[1].y, | |||
| 0, h, resolvedPoints[2].x, resolvedPoints[2].y); | |||
| } | |||
| //============================================================================== | |||
| void DrawableText::paint (Graphics& g) | |||
| { | |||
| transformContextToCorrectOrigin (g); | |||
| const float w = Line<float> (resolvedPoints[0], resolvedPoints[1]).getLength(); | |||
| const float h = Line<float> (resolvedPoints[0], resolvedPoints[2]).getLength(); | |||
| g.addTransform (AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].x, resolvedPoints[0].y, | |||
| w, 0, resolvedPoints[1].x, resolvedPoints[1].y, | |||
| 0, h, resolvedPoints[2].x, resolvedPoints[2].y)); | |||
| g.setFont (scaledFont); | |||
| g.setColour (colour); | |||
| GlyphArrangement ga; | |||
| const AffineTransform transform (getArrangementAndTransform (ga)); | |||
| ga.draw (g, transform); | |||
| g.drawFittedText (text, Rectangle<int> (w, h), justification, 0x100000); | |||
| } | |||
| Rectangle<float> DrawableText::getDrawableBounds() const | |||
| @@ -149,7 +149,6 @@ private: | |||
| bool registerCoordinates (RelativeCoordinatePositionerBase&); | |||
| void recalculateCoordinates (Expression::Scope*); | |||
| void refreshBounds(); | |||
| const AffineTransform getArrangementAndTransform (GlyphArrangement& glyphs) const; | |||
| DrawableText& operator= (const DrawableText&); | |||
| JUCE_LEAK_DETECTOR (DrawableText) | |||
| @@ -81,8 +81,7 @@ public: | |||
| } | |||
| void draw (CodeEditorComponent& owner, Graphics& g, const Font& fontToUse, | |||
| const float leftClip, const float rightClip, | |||
| const float x, const int y, const int baselineOffset, | |||
| const float rightClip, const float x, const int y, | |||
| const int lineH, const float characterWidth, | |||
| const Colour highlightColour) const | |||
| { | |||
| @@ -93,9 +92,11 @@ public: | |||
| roundToInt ((highlightColumnEnd - highlightColumnStart) * characterWidth), lineH); | |||
| } | |||
| const float baselineY = (float) (y + baselineOffset); | |||
| Colour lastColour (0x00000001); | |||
| GlyphArrangement ga; | |||
| AttributedString as; | |||
| as.setJustification (Justification::centredLeft); | |||
| int column = 0; | |||
| for (int i = 0; i < tokens.size(); ++i) | |||
| @@ -104,26 +105,12 @@ public: | |||
| if (tokenX > rightClip) | |||
| break; | |||
| SyntaxToken& token = tokens.getReference(i); | |||
| const Colour newColour (owner.getColourForTokenType (token.tokenType)); | |||
| if (lastColour != newColour) | |||
| { | |||
| ga.draw (g); | |||
| ga.clear(); | |||
| lastColour = newColour; | |||
| g.setColour (newColour); | |||
| } | |||
| const SyntaxToken& token = tokens.getReference(i); | |||
| as.append (token.text, fontToUse, owner.getColourForTokenType (token.tokenType)); | |||
| column += token.length; | |||
| if (x + column * characterWidth >= leftClip) | |||
| ga.addCurtailedLineOfText (fontToUse, token.text, tokenX, baselineY, | |||
| (rightClip - tokenX) + characterWidth, false); | |||
| } | |||
| ga.draw (g); | |||
| as.draw (g, Rectangle<int> (x, y, 10000, lineH).toFloat()); | |||
| } | |||
| private: | |||
| @@ -484,20 +471,18 @@ void CodeEditorComponent::paint (Graphics& g) | |||
| g.reduceClipRegion (gutterSize, 0, verticalScrollBar.getX() - gutterSize, horizontalScrollBar.getY()); | |||
| g.setFont (font); | |||
| const int baselineOffset = (int) font.getAscent(); | |||
| const Colour highlightColour (findColour (CodeEditorComponent::highlightColourId)); | |||
| const Rectangle<int> clip (g.getClipBounds()); | |||
| const int firstLineToDraw = jmax (0, clip.getY() / lineHeight); | |||
| const int lastLineToDraw = jmin (lines.size(), clip.getBottom() / lineHeight + 1); | |||
| const float x = (float) (gutterSize - xOffset * charWidth); | |||
| const float leftClip = (float) clip.getX(); | |||
| const float rightClip = (float) clip.getRight(); | |||
| for (int i = firstLineToDraw; i < lastLineToDraw; ++i) | |||
| lines.getUnchecked(i)->draw (*this, g, font, leftClip, rightClip, | |||
| x, lineHeight * i, baselineOffset, | |||
| lineHeight, charWidth, highlightColour); | |||
| lines.getUnchecked(i)->draw (*this, g, font, rightClip, | |||
| x, lineHeight * i, lineHeight, | |||
| charWidth, highlightColour); | |||
| } | |||
| void CodeEditorComponent::setScrollbarThickness (const int thickness) | |||