1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License 11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 12 13 End User License Agreement: www.juce.com/juce-6-licence 14 Privacy Policy: www.juce.com/juce-privacy-policy 15 16 Or: You may also use this code under the terms of the GPL v3 (see 17 www.gnu.org/licenses). 18 19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 21 DISCLAIMED. 22 23 ============================================================================== 24 */ 25 26 namespace juce 27 { 28 29 //============================================================================== 30 /** 31 A glyph from a particular font, with a particular size, style, 32 typeface and position. 33 34 You should rarely need to use this class directly - for most purposes, the 35 GlyphArrangement class will do what you need for text layout. 36 37 @see GlyphArrangement, Font 38 39 @tags{Graphics} 40 */ 41 class JUCE_API PositionedGlyph final 42 { 43 public: 44 //============================================================================== 45 PositionedGlyph() noexcept; 46 47 PositionedGlyph (const Font& font, juce_wchar character, int glyphNumber, 48 float anchorX, float baselineY, float width, bool isWhitespace); 49 50 PositionedGlyph (const PositionedGlyph&) = default; 51 PositionedGlyph& operator= (const PositionedGlyph&) = default; 52 PositionedGlyph (PositionedGlyph&&) noexcept = default; 53 PositionedGlyph& operator= (PositionedGlyph&&) noexcept = default; 54 55 ~PositionedGlyph(); 56 57 /** Returns the character the glyph represents. */ getCharacter()58 juce_wchar getCharacter() const noexcept { return character; } 59 /** Checks whether the glyph is actually empty. */ isWhitespace()60 bool isWhitespace() const noexcept { return whitespace; } 61 62 /** Returns the position of the glyph's left-hand edge. */ getLeft()63 float getLeft() const noexcept { return x; } 64 /** Returns the position of the glyph's right-hand edge. */ getRight()65 float getRight() const noexcept { return x + w; } 66 /** Returns the y position of the glyph's baseline. */ getBaselineY()67 float getBaselineY() const noexcept { return y; } 68 /** Returns the y position of the top of the glyph. */ getTop()69 float getTop() const { return y - font.getAscent(); } 70 /** Returns the y position of the bottom of the glyph. */ getBottom()71 float getBottom() const { return y + font.getDescent(); } 72 /** Returns the bounds of the glyph. */ getBounds()73 Rectangle<float> getBounds() const { return { x, getTop(), w, font.getHeight() }; } 74 75 //============================================================================== 76 /** Shifts the glyph's position by a relative amount. */ 77 void moveBy (float deltaX, float deltaY); 78 79 //============================================================================== 80 /** Draws the glyph into a graphics context. 81 (Note that this may change the context's currently selected font). 82 */ 83 void draw (Graphics& g) const; 84 85 /** Draws the glyph into a graphics context, with an extra transform applied to it. 86 (Note that this may change the context's currently selected font). 87 */ 88 void draw (Graphics& g, AffineTransform transform) const; 89 90 /** Returns the path for this glyph. 91 @param path the glyph's outline will be appended to this path 92 */ 93 void createPath (Path& path) const; 94 95 /** Checks to see if a point lies within this glyph. */ 96 bool hitTest (float x, float y) const; 97 98 private: 99 //============================================================================== 100 friend class GlyphArrangement; 101 Font font; 102 juce_wchar character; 103 int glyph; 104 float x, y, w; 105 bool whitespace; 106 107 JUCE_LEAK_DETECTOR (PositionedGlyph) 108 }; 109 110 111 //============================================================================== 112 /** 113 A set of glyphs, each with a position. 114 115 You can create a GlyphArrangement, text to it and then draw it onto a 116 graphics context. It's used internally by the text methods in the 117 Graphics class, but can be used directly if more control is needed. 118 119 @see Font, PositionedGlyph 120 121 @tags{Graphics} 122 */ 123 class JUCE_API GlyphArrangement final 124 { 125 public: 126 //============================================================================== 127 /** Creates an empty arrangement. */ 128 GlyphArrangement(); 129 130 GlyphArrangement (const GlyphArrangement&) = default; 131 GlyphArrangement& operator= (const GlyphArrangement&) = default; 132 GlyphArrangement (GlyphArrangement&&) = default; 133 GlyphArrangement& operator= (GlyphArrangement&&) = default; 134 135 /** Destructor. */ 136 ~GlyphArrangement() = default; 137 138 //============================================================================== 139 /** Returns the total number of glyphs in the arrangement. */ getNumGlyphs()140 int getNumGlyphs() const noexcept { return glyphs.size(); } 141 142 /** Returns one of the glyphs from the arrangement. 143 144 @param index the glyph's index, from 0 to (getNumGlyphs() - 1). Be 145 careful not to pass an out-of-range index here, as it 146 doesn't do any bounds-checking. 147 */ 148 PositionedGlyph& getGlyph (int index) noexcept; 149 begin()150 const PositionedGlyph* begin() const { return glyphs.begin(); } end()151 const PositionedGlyph* end() const { return glyphs.end(); } 152 153 //============================================================================== 154 /** Clears all text from the arrangement and resets it. */ 155 void clear(); 156 157 /** Appends a line of text to the arrangement. 158 159 This will add the text as a single line, where x is the left-hand edge of the 160 first character, and y is the position for the text's baseline. 161 162 If the text contains new-lines or carriage-returns, this will ignore them - use 163 addJustifiedText() to add multi-line arrangements. 164 */ 165 void addLineOfText (const Font& font, 166 const String& text, 167 float x, float y); 168 169 /** Adds a line of text, truncating it if it's wider than a specified size. 170 171 This is the same as addLineOfText(), but if the line's width exceeds the value 172 specified in maxWidthPixels, it will be truncated using either ellipsis (i.e. dots: "..."), 173 if useEllipsis is true, or if this is false, it will just drop any subsequent characters. 174 */ 175 void addCurtailedLineOfText (const Font& font, 176 const String& text, 177 float x, float y, 178 float maxWidthPixels, 179 bool useEllipsis); 180 181 /** Adds some multi-line text, breaking lines at word-boundaries if they are too wide. 182 183 This will add text to the arrangement, breaking it into new lines either where there 184 is a new-line or carriage-return character in the text, or where a line's width 185 exceeds the value set in maxLineWidth. 186 187 Each line that is added will be laid out using the flags set in horizontalLayout, so 188 the lines can be left- or right-justified, or centred horizontally in the space 189 between x and (x + maxLineWidth). 190 191 The y coordinate is the position of the baseline of the first line of text - subsequent 192 lines will be placed below it, separated by a distance of font.getHeight() + leading. 193 */ 194 void addJustifiedText (const Font& font, 195 const String& text, 196 float x, float y, 197 float maxLineWidth, 198 Justification horizontalLayout, 199 float leading = 0.0f); 200 201 /** Tries to fit some text within a given space. 202 203 This does its best to make the given text readable within the specified rectangle, 204 so it's useful for labelling things. 205 206 If the text is too big, it'll be squashed horizontally or broken over multiple lines 207 if the maximumLinesToUse value allows this. If the text just won't fit into the space, 208 it'll cram as much as possible in there, and put some ellipsis at the end to show that 209 it's been truncated. 210 211 A Justification parameter lets you specify how the text is laid out within the rectangle, 212 both horizontally and vertically. 213 214 The minimumHorizontalScale parameter specifies how much the text can be squashed horizontally 215 to try to squeeze it into the space. If you don't want any horizontal scaling to occur, you 216 can set this value to 1.0f. Pass 0 if you want it to use the default value. 217 218 @see Graphics::drawFittedText 219 */ 220 void addFittedText (const Font& font, 221 const String& text, 222 float x, float y, float width, float height, 223 Justification layout, 224 int maximumLinesToUse, 225 float minimumHorizontalScale = 0.0f); 226 227 /** Appends another glyph arrangement to this one. */ 228 void addGlyphArrangement (const GlyphArrangement&); 229 230 /** Appends a custom glyph to the arrangement. */ 231 void addGlyph (const PositionedGlyph&); 232 233 //============================================================================== 234 /** Draws this glyph arrangement to a graphics context. 235 236 This uses cached bitmaps so is much faster than the draw (Graphics&, AffineTransform) 237 method, which renders the glyphs as filled vectors. 238 */ 239 void draw (const Graphics&) const; 240 241 /** Draws this glyph arrangement to a graphics context. 242 243 This renders the paths as filled vectors, so is far slower than the draw (Graphics&) 244 method for non-transformed arrangements. 245 */ 246 void draw (const Graphics&, AffineTransform) const; 247 248 /** Converts the set of glyphs into a path. 249 @param path the glyphs' outlines will be appended to this path 250 */ 251 void createPath (Path& path) const; 252 253 /** Looks for a glyph that contains the given coordinate. 254 @returns the index of the glyph, or -1 if none were found. 255 */ 256 int findGlyphIndexAt (float x, float y) const; 257 258 //============================================================================== 259 /** Finds the smallest rectangle that will enclose a subset of the glyphs. 260 261 262 @param startIndex the first glyph to test 263 @param numGlyphs the number of glyphs to include; if this is < 0, all glyphs after 264 startIndex will be included 265 @param includeWhitespace if true, the extent of any whitespace characters will also 266 be taken into account 267 */ 268 Rectangle<float> getBoundingBox (int startIndex, int numGlyphs, bool includeWhitespace) const; 269 270 /** Shifts a set of glyphs by a given amount. 271 272 @param startIndex the first glyph to transform 273 @param numGlyphs the number of glyphs to move; if this is < 0, all glyphs after 274 startIndex will be used 275 @param deltaX the amount to add to their x-positions 276 @param deltaY the amount to add to their y-positions 277 */ 278 void moveRangeOfGlyphs (int startIndex, int numGlyphs, 279 float deltaX, float deltaY); 280 281 /** Removes a set of glyphs from the arrangement. 282 283 @param startIndex the first glyph to remove 284 @param numGlyphs the number of glyphs to remove; if this is < 0, all glyphs after 285 startIndex will be deleted 286 */ 287 void removeRangeOfGlyphs (int startIndex, int numGlyphs); 288 289 /** Expands or compresses a set of glyphs horizontally. 290 291 @param startIndex the first glyph to transform 292 @param numGlyphs the number of glyphs to stretch; if this is < 0, all glyphs after 293 startIndex will be used 294 @param horizontalScaleFactor how much to scale their horizontal width by 295 */ 296 void stretchRangeOfGlyphs (int startIndex, int numGlyphs, 297 float horizontalScaleFactor); 298 299 /** Justifies a set of glyphs within a given space. 300 301 This moves the glyphs as a block so that the whole thing is located within the 302 given rectangle with the specified layout. 303 304 If the Justification::horizontallyJustified flag is specified, each line will 305 be stretched out to fill the specified width. 306 */ 307 void justifyGlyphs (int startIndex, int numGlyphs, 308 float x, float y, float width, float height, 309 Justification justification); 310 311 312 private: 313 //============================================================================== 314 Array<PositionedGlyph> glyphs; 315 316 int insertEllipsis (const Font&, float maxXPos, int startIndex, int endIndex); 317 int fitLineIntoSpace (int start, int numGlyphs, float x, float y, float w, float h, const Font&, 318 Justification, float minimumHorizontalScale); 319 void spreadOutLine (int start, int numGlyphs, float targetWidth); 320 void splitLines (const String&, Font, int start, float x, float y, float w, float h, int maxLines, 321 float lineWidth, Justification, float minimumHorizontalScale); 322 void addLinesWithLineBreaks (const String&, const Font&, float x, float y, float width, float height, Justification); 323 void drawGlyphUnderline (const Graphics&, const PositionedGlyph&, int, AffineTransform) const; 324 325 JUCE_LEAK_DETECTOR (GlyphArrangement) 326 }; 327 328 } // namespace juce 329