| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UI_GFX_RENDER_TEXT_H_ |
| #define UI_GFX_RENDER_TEXT_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <array> |
| #include <cstring> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/i18n/rtl.h" |
| #include "base/macros.h" |
| #include "build/build_config.h" |
| #include "cc/paint/paint_canvas.h" |
| #include "cc/paint/paint_flags.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "third_party/skia/include/core/SkFont.h" |
| #include "third_party/skia/include/core/SkRefCnt.h" |
| #include "ui/gfx/break_list.h" |
| #include "ui/gfx/color_palette.h" |
| #include "ui/gfx/font_list.h" |
| #include "ui/gfx/font_render_params.h" |
| #include "ui/gfx/geometry/point.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/geometry/size_f.h" |
| #include "ui/gfx/geometry/vector2d.h" |
| #include "ui/gfx/range/range.h" |
| #include "ui/gfx/range/range_f.h" |
| #include "ui/gfx/selection_model.h" |
| #include "ui/gfx/shadow_value.h" |
| #include "ui/gfx/text_constants.h" |
| |
| class SkDrawLooper; |
| struct SkPoint; |
| class SkTypeface; |
| |
| namespace gfx { |
| namespace test { |
| class RenderTextTestApi; |
| } |
| |
| class Canvas; |
| struct DecoratedText; |
| class Font; |
| |
| namespace internal { |
| |
| class TextRunList; |
| |
| // Internal helper class used by derived classes to draw text through Skia. |
| class GFX_EXPORT SkiaTextRenderer { |
| public: |
| explicit SkiaTextRenderer(Canvas* canvas); |
| SkiaTextRenderer(const SkiaTextRenderer&) = delete; |
| SkiaTextRenderer& operator=(const SkiaTextRenderer&) = delete; |
| virtual ~SkiaTextRenderer(); |
| |
| void SetDrawLooper(sk_sp<SkDrawLooper> draw_looper); |
| void SetFontRenderParams(const FontRenderParams& params, |
| bool subpixel_rendering_suppressed); |
| void SetTypeface(sk_sp<SkTypeface> typeface); |
| void SetTextSize(SkScalar size); |
| void SetForegroundColor(SkColor foreground); |
| void SetShader(sk_sp<cc::PaintShader> shader); |
| // TODO(vmpstr): Change this API to mimic SkCanvas::drawTextBlob instead. |
| virtual void DrawPosText(const SkPoint* pos, |
| const uint16_t* glyphs, |
| size_t glyph_count); |
| void DrawUnderline(int x, int y, int width, SkScalar thickness_factor = 1.0); |
| void DrawStrike(int x, int y, int width, SkScalar thickness_factor); |
| |
| private: |
| friend class test::RenderTextTestApi; |
| |
| Canvas* canvas_; |
| cc::PaintCanvas* canvas_skia_; |
| cc::PaintFlags flags_; |
| SkFont font_; |
| }; |
| |
| struct TextToDisplayIndex { |
| size_t text_index = 0; |
| size_t display_index = 0; |
| }; |
| using TextToDisplaySequence = std::vector<TextToDisplayIndex>; |
| using GraphemeIterator = TextToDisplaySequence::const_iterator; |
| using StyleArray = std::array<BreakList<bool>, TEXT_STYLE_COUNT>; |
| |
| // Internal helper class used to iterate colors, baselines, and styles. |
| class StyleIterator { |
| public: |
| StyleIterator(const BreakList<SkColor>* colors, |
| const BreakList<BaselineStyle>* baselines, |
| const BreakList<int>* font_size_overrides, |
| const BreakList<Font::Weight>* weights, |
| const StyleArray* styles); |
| StyleIterator(const StyleIterator& style); |
| ~StyleIterator(); |
| StyleIterator& operator=(const StyleIterator& style); |
| |
| // Get the colors and styles at the current iterator position. |
| SkColor color() const { return color_->second; } |
| BaselineStyle baseline() const { return baseline_->second; } |
| int font_size_override() const { return font_size_override_->second; } |
| bool style(TextStyle s) const { return style_[s]->second; } |
| Font::Weight weight() const { return weight_->second; } |
| |
| // Get the intersecting range of the current iterator set. |
| Range GetRange() const; |
| |
| // Get the intersecting range of the current iterator set for attributes that |
| // can break text (e.g. not color). |
| Range GetTextBreakingRange() const; |
| |
| // Update the iterator to point to colors and styles applicable at |position|. |
| void IncrementToPosition(size_t position); |
| |
| private: |
| // Pointers to the breaklists to iterate through. These pointers can't be |
| // nullptr and the breaklists must outlive this object. |
| const BreakList<SkColor>* colors_; |
| const BreakList<BaselineStyle>* baselines_; |
| const BreakList<int>* font_size_overrides_; |
| const BreakList<Font::Weight>* weights_; |
| const StyleArray* styles_; |
| |
| BreakList<SkColor>::const_iterator color_; |
| BreakList<BaselineStyle>::const_iterator baseline_; |
| BreakList<int>::const_iterator font_size_override_; |
| BreakList<Font::Weight>::const_iterator weight_; |
| std::array<BreakList<bool>::const_iterator, TEXT_STYLE_COUNT> style_; |
| }; |
| |
| // Line segments are slices of the display text to be rendered on a single line. |
| struct LineSegment { |
| LineSegment(); |
| ~LineSegment(); |
| |
| // X coordinates of this line segment in text space. |
| RangeF x_range; |
| |
| // The character range this segment corresponds to. |
| Range char_range; |
| |
| // Index of the text run that generated this segment. |
| size_t run; |
| |
| // Returns the width of this line segment in text space. |
| float width() const { return x_range.length(); } |
| }; |
| |
| // A line of display text, comprised of a line segment list and some metrics. |
| struct Line { |
| Line(); |
| Line(const Line& other); |
| ~Line(); |
| |
| // Segments that make up this line in visual order. |
| std::vector<LineSegment> segments; |
| |
| // The sum of segment widths and the maximum of segment heights. |
| SizeF size; |
| |
| // Sum of preceding lines' heights. |
| int preceding_heights; |
| |
| // Maximum baseline of all segments on this line. |
| int baseline; |
| |
| // The text index of this line in |text_|. |
| int display_text_index = 0; |
| }; |
| |
| // Internal class that contains the results of the text layout and shaping. |
| class ShapedText { |
| public: |
| explicit ShapedText(std::vector<Line> lines); |
| ~ShapedText(); |
| |
| const std::vector<Line>& lines() const { return lines_; } |
| |
| private: |
| std::vector<Line> lines_; |
| }; |
| |
| // Creates an SkTypeface from a font, |italic| and a desired |weight|. |
| // May return null. |
| sk_sp<SkTypeface> CreateSkiaTypeface(const Font& font, |
| bool italic, |
| Font::Weight weight); |
| |
| // Applies the given FontRenderParams to the SkFont. |
| void ApplyRenderParams(const FontRenderParams& params, |
| bool subpixel_rendering_suppressed, |
| SkFont* font); |
| |
| } // namespace internal |
| |
| // RenderText represents an abstract model of styled text and its corresponding |
| // visual layout. Support is built in for a cursor, selections, simple styling, |
| // complex scripts, and bi-directional text. Implementations provide mechanisms |
| // for rendering and translation between logical and visual data. |
| class GFX_EXPORT RenderText { |
| public: |
| #if defined(OS_APPLE) |
| // On Mac, while selecting text if the cursor is outside the vertical text |
| // bounds, drag to the end of the text. |
| static constexpr bool kDragToEndIfOutsideVerticalBounds = true; |
| // Mac supports a selection that is "undirected". When undirected, the cursor |
| // doesn't know which end of the selection it's at until it first moves. |
| static constexpr bool kSelectionIsAlwaysDirected = false; |
| #else |
| static constexpr bool kDragToEndIfOutsideVerticalBounds = false; |
| static constexpr bool kSelectionIsAlwaysDirected = true; |
| #endif |
| |
| // Invalid value of baseline. Assigning this value to |baseline_| causes |
| // re-calculation of baseline. |
| static constexpr int kInvalidBaseline = INT_MAX; |
| |
| // Default fraction of the text size to use for a strike-through or underline. |
| static constexpr SkScalar kLineThicknessFactor = (SK_Scalar1 / 18); |
| |
| // The character used for displaying obscured text. Use a bullet character. |
| // TODO(pbos): This is highly font dependent, consider replacing the character |
| // with a vector glyph. |
| static constexpr char16_t kPasswordReplacementChar = 0x2022; |
| |
| RenderText(const RenderText&) = delete; |
| RenderText& operator=(const RenderText&) = delete; |
| virtual ~RenderText(); |
| |
| // Creates a RenderText instance. |
| static std::unique_ptr<RenderText> CreateRenderText(); |
| |
| // Like above but copies all style settings too. |
| std::unique_ptr<RenderText> CreateInstanceOfSameStyle( |
| const std::u16string& text) const; |
| |
| const std::u16string& text() const { return text_; } |
| void SetText(const std::u16string& text); |
| void AppendText(const std::u16string& text); |
| |
| HorizontalAlignment horizontal_alignment() const { |
| return horizontal_alignment_; |
| } |
| void SetHorizontalAlignment(HorizontalAlignment alignment); |
| |
| VerticalAlignment vertical_alignment() const { return vertical_alignment_; } |
| void SetVerticalAlignment(VerticalAlignment alignment); |
| |
| const FontList& font_list() const { return font_list_; } |
| void SetFontList(const FontList& font_list); |
| |
| bool cursor_enabled() const { return cursor_enabled_; } |
| void SetCursorEnabled(bool cursor_enabled); |
| |
| SkColor selection_color() const { return selection_color_; } |
| void set_selection_color(SkColor color) { selection_color_ = color; } |
| |
| SkColor selection_background_focused_color() const { |
| return selection_background_focused_color_; |
| } |
| void set_selection_background_focused_color(SkColor color) { |
| selection_background_focused_color_ = color; |
| } |
| |
| bool symmetric_selection_visual_bounds() const { |
| return symmetric_selection_visual_bounds_; |
| } |
| void set_symmetric_selection_visual_bounds(bool symmetric) { |
| symmetric_selection_visual_bounds_ = symmetric; |
| } |
| |
| bool focused() const { return focused_; } |
| void set_focused(bool focused) { focused_ = focused; } |
| |
| bool clip_to_display_rect() const { return clip_to_display_rect_; } |
| void set_clip_to_display_rect(bool clip) { clip_to_display_rect_ = clip; } |
| |
| // In an obscured (password) field, all text is drawn as bullets. |
| bool obscured() const { return obscured_; } |
| void SetObscured(bool obscured); |
| |
| // Makes a char in obscured text at |index| to be revealed. |index| should be |
| // a UTF16 text index. If there is a previous revealed index, the previous one |
| // is cleared and only the last set index will be revealed. If |index| is -1 |
| // or out of range, no char will be revealed. The revealed index is also |
| // cleared when SetText or SetObscured is called. |
| void SetObscuredRevealIndex(int index); |
| |
| // For obscured (password) fields, the extra spacing between glyphs. |
| int obscured_glyph_spacing() const { return obscured_glyph_spacing_; } |
| void SetObscuredGlyphSpacing(int spacing); |
| |
| bool multiline() const { return multiline_; } |
| void SetMultiline(bool multiline); |
| |
| // If multiline, a non-zero value will cap the number of lines rendered, |
| // and elide the rest (currently only ELIDE_TAIL supported.) |
| void SetMaxLines(size_t max_lines); |
| size_t max_lines() const { return max_lines_; } |
| |
| // Returns the actual number of lines, broken by |lines_|. |
| size_t GetNumLines(); |
| |
| // Returns the text index of the given line |line|. Returns the text length |
| // for any |line| above the number of lines. |
| size_t GetTextIndexOfLine(size_t line); |
| |
| // TODO(mukai): ELIDE_LONG_WORDS is not supported. |
| WordWrapBehavior word_wrap_behavior() const { return word_wrap_behavior_; } |
| void SetWordWrapBehavior(WordWrapBehavior behavior); |
| |
| // TODO(ckocagil): Add vertical alignment and line spacing support instead. |
| int min_line_height() const { return min_line_height_; } |
| void SetMinLineHeight(int line_height); |
| |
| // Set the maximum length of the layout text, not the actual text. |
| // A |length| of 0 forgoes a hard limit, but does not guarantee proper |
| // functionality of very long strings. Applies to subsequent SetText calls. |
| // WARNING: Only use this for system limits, it lacks complex text support. |
| void set_truncate_length(size_t length) { truncate_length_ = length; } |
| |
| // The display text will be elided to fit |display_rect| using this behavior. |
| void SetElideBehavior(ElideBehavior elide_behavior); |
| ElideBehavior elide_behavior() const { return elide_behavior_; } |
| |
| // When display text is elided, determines how whitespace is handled. |
| // If absl::nullopt is specified, the default elision for the current elide |
| // behavior will be applied. |
| void SetWhitespaceElision(absl::optional<bool> elide_whitespace); |
| absl::optional<bool> whitespace_elision() const { |
| return whitespace_elision_; |
| } |
| |
| const Rect& display_rect() const { return display_rect_; } |
| void SetDisplayRect(const Rect& r); |
| |
| bool subpixel_rendering_suppressed() const { |
| return subpixel_rendering_suppressed_; |
| } |
| void set_subpixel_rendering_suppressed(bool suppressed) { |
| subpixel_rendering_suppressed_ = suppressed; |
| } |
| |
| const SelectionModel& selection_model() const { return selection_model_; } |
| const Range& selection() const { return selection_model_.selection(); } |
| size_t cursor_position() const { return selection_model_.caret_pos(); } |
| const std::vector<Range>& secondary_selections() const { |
| return selection_model_.secondary_selections(); |
| } |
| const std::vector<Range> GetAllSelections() const; |
| |
| // Set the cursor to |position|, with the caret affinity trailing the previous |
| // grapheme, or if there is no previous grapheme, leading the cursor position. |
| // See SelectionModel::caret_affinity_ for details. |
| void SetCursorPosition(size_t position); |
| |
| // Moves the cursor left or right. Cursor movement is visual, meaning that |
| // left and right are relative to screen, not the directionality of the text. |
| // |selection_behavior| determines whether a selection is to be made and it's |
| // behavior. |
| void MoveCursor(BreakType break_type, |
| VisualCursorDirection direction, |
| SelectionBehavior selection_behavior); |
| |
| // Set the selection_model_ to the value of |selection|. |
| // The selection range is clamped to text().length() if out of range. |
| // Returns true if the cursor position or selection range changed. |
| // If any index in |selection_model| is not a cursorable position (not on a |
| // grapheme boundary), it is a no-op and returns false. |
| bool SetSelection(const SelectionModel& selection); |
| |
| // Moves the cursor to the text index corresponding to |point|. If |select| is |
| // true, a selection is made with the current selection start index. If the |
| // resultant text indices do not lie on valid grapheme boundaries, it is a no- |
| // op and returns false. If this move is happening because of a drag causing a |
| // selection change, and |drag_origin| is not the zero point, then |
| // |drag_origin| overrides the default origin for a select-to-drag |
| // (usually the existing text insertion cursor). |
| bool MoveCursorToPoint(const gfx::Point& point, |
| bool select, |
| const gfx::Point& drag_origin = gfx::Point()); |
| |
| // Set the |selection_model_| based on |range|. If the |range| start or end is |
| // greater than text length, it is modified to be the text length. If the |
| // |range| start or end is not a cursorable position (not on grapheme |
| // boundary), it is a NO-OP and returns false. Otherwise, returns true. If |
| // |primary| is true, secondary selections are cleared; otherwise, the range |
| // will be added as a secondary selection not associated with the cursor. In |
| // the latter case, |range| should not overlap with existing selections. |
| bool SelectRange(const Range& range, bool primary = true); |
| |
| // Returns true if the local point is over selected text. |
| bool IsPointInSelection(const Point& point); |
| |
| // Selects no text, keeping the current cursor position and caret affinity. |
| void ClearSelection(); |
| |
| // Select the entire text range. If |reversed| is true, the range will end at |
| // the logical beginning of the text; this generally shows the leading portion |
| // of text that overflows its display area. |
| void SelectAll(bool reversed); |
| |
| // Selects the word at the current cursor position. If there is a non-empty |
| // selection, the selection bounds are extended to their nearest word |
| // boundaries. |
| void SelectWord(); |
| |
| void SetCompositionRange(const Range& composition_range); |
| |
| // Set the text color over the entire text or a logical character range. |
| // The |range| should be valid, non-reversed, and within [0, text().length()]. |
| void SetColor(SkColor value); |
| void ApplyColor(SkColor value, const Range& range); |
| |
| // DEPRECATED. |
| // Set the baseline style over the entire text or a logical character range. |
| // The |range| should be valid, non-reversed, and within [0, text().length()]. |
| // TODO(tapted): Remove this. The only client is moving to |
| // ApplyFontSizeOverride. |
| void SetBaselineStyle(BaselineStyle value); |
| void ApplyBaselineStyle(BaselineStyle value, const Range& range); |
| |
| // Alters the font size in |range|. |
| void ApplyFontSizeOverride(int font_size_override, const Range& range); |
| |
| // Set various text styles over the entire text or a logical character range. |
| // The respective |style| is applied if |value| is true, or removed if false. |
| // The |range| should be valid, non-reversed, and within [0, text().length()]. |
| void SetStyle(TextStyle style, bool value); |
| void ApplyStyle(TextStyle style, bool value, const Range& range); |
| |
| void SetWeight(Font::Weight weight); |
| void ApplyWeight(Font::Weight weight, const Range& range); |
| |
| // Returns whether this style is enabled consistently across the entire |
| // RenderText. |
| bool GetStyle(TextStyle style) const; |
| |
| // Set or get the text directionality mode and get the text direction yielded. |
| void SetDirectionalityMode(DirectionalityMode mode); |
| DirectionalityMode directionality_mode() const { |
| return directionality_mode_; |
| } |
| |
| base::i18n::TextDirection GetTextDirection() const; |
| base::i18n::TextDirection GetDisplayTextDirection(); |
| |
| // Returns the visual movement direction corresponding to the logical |
| // end/beginning of the text, considering only the dominant direction returned |
| // by |GetDisplayTextDirection()|, not the direction of a particular run. |
| VisualCursorDirection GetVisualDirectionOfLogicalEnd(); |
| VisualCursorDirection GetVisualDirectionOfLogicalBeginning(); |
| |
| // Returns the text used to display, which may be obscured, truncated or |
| // elided. The subclass may compute elided text on the fly, or use |
| // precomputed the elided text. |
| virtual const std::u16string& GetDisplayText() = 0; |
| |
| // Returns the size required to display the current string (which is the |
| // wrapped size in multiline mode). The returned size does not include space |
| // reserved for the cursor or the offset text shadows. |
| Size GetStringSize(); |
| |
| // This is same as GetStringSize except that fractional size is returned. |
| // The default implementation is same as GetStringSize. Certain platforms that |
| // compute the text size as floating-point values, like Mac, will override |
| // this method. |
| // See comment in Canvas::GetStringWidthF for its usage. |
| virtual SizeF GetStringSizeF() = 0; |
| |
| // Returns the size of the line containing |caret|. |
| virtual SizeF GetLineSizeF(const SelectionModel& caret) = 0; |
| |
| // Returns the sum of all the line widths. |
| float TotalLineWidth(); |
| |
| // Returns the width of the content (which is the wrapped width in multiline |
| // mode). Reserves room for the cursor if |cursor_enabled_| is true. |
| float GetContentWidthF(); |
| |
| // Same as GetContentWidthF with the width rounded up. |
| int GetContentWidth(); |
| |
| // Returns the common baseline of the text. The return value is the vertical |
| // offset from the top of |display_rect_| to the text baseline, in pixels. |
| // The baseline is determined from the font list and display rect, and does |
| // not depend on the text. |
| int GetBaseline(); |
| |
| // If |select_all| is true, draws as focused with all text selected. |
| void Draw(Canvas* canvas, bool select_all = false); |
| |
| // Gets the SelectionModel from a visual point in local coordinates. If |
| // |drag_origin| is nonzero, it is used as the baseline for |
| // out-of-vertical-bounds drags on platforms that have them, instead of the |
| // default origin (the insertion cursor's position). |
| SelectionModel FindCursorPosition(const Point& point, |
| const Point& drag_origin = gfx::Point()); |
| |
| // Returns true if the position is a valid logical index into text(). Indices |
| // amid multi-character graphemes are allowed here, unlike IsValidCursorIndex. |
| bool IsValidLogicalIndex(size_t index) const; |
| |
| // Returns true if the position is a valid logical index into text(), and is |
| // also a valid grapheme boundary, which may be used as a cursor position. |
| bool IsValidCursorIndex(size_t index) const; |
| |
| // Get the visual bounds of a cursor at |caret|. These bounds typically |
| // represent a vertical line if |insert_mode| is true. Pass false for |
| // |insert_mode| to retrieve the bounds of the associated glyph. These bounds |
| // are in local coordinates, but may be outside the visible region if the text |
| // is longer than the textfield. Subsequent text, cursor, or bounds changes |
| // may invalidate returned values. Note that |caret| must be placed at |
| // grapheme boundary, i.e. caret.caret_pos() must be a cursorable position. |
| // TODO(crbug.com/248597): Add multiline support. |
| Rect GetCursorBounds(const SelectionModel& caret, bool insert_mode); |
| |
| // Compute the current cursor bounds, panning the text to show the cursor in |
| // the display rect if necessary. These bounds are in local coordinates. |
| // Subsequent text, cursor, or bounds changes may invalidate returned values. |
| const Rect& GetUpdatedCursorBounds(); |
| |
| // Returns a grapheme iterator that contains the codepoint at |index|. |
| internal::GraphemeIterator GetGraphemeIteratorAtTextIndex(size_t index) const; |
| internal::GraphemeIterator GetGraphemeIteratorAtDisplayTextIndex( |
| size_t index) const; |
| |
| // For a given grapheme iterator, returns its index. |
| size_t GetTextIndex(internal::GraphemeIterator iter) const; |
| size_t GetDisplayTextIndex(internal::GraphemeIterator iter) const; |
| |
| // Returns true of the current index is at the start of a grapheme. |
| bool IsGraphemeBoundary(size_t index) const; |
| |
| // Given an |index| in text(), return the next or previous grapheme boundary |
| // in logical order (i.e. the nearest cursorable index). The return value is |
| // in the range 0 to text().length() inclusive (the input is clamped if it is |
| // out of that range). Always moves by at least one character index unless the |
| // supplied index is already at the boundary of the string. |
| size_t IndexOfAdjacentGrapheme(size_t index, |
| LogicalCursorDirection direction) const; |
| |
| // Return a SelectionModel with the cursor at the current selection's start. |
| // The returned value represents a cursor/caret position without a selection. |
| SelectionModel GetSelectionModelForSelectionStart() const; |
| |
| // Sets shadows to drawn with text. |
| void set_shadows(const ShadowValues& shadows) { shadows_ = shadows; } |
| const ShadowValues& shadows() const { return shadows_; } |
| |
| // Get the visual bounds containing the logical substring within the |range|. |
| // If |range| is empty, the result is empty. This method rounds internally so |
| // the returned bounds may be slightly larger than the |range|, but are |
| // guaranteed not to be smaller. These bounds could be visually discontinuous |
| // if the substring is split by a LTR/RTL level change. These bounds are in |
| // local coordinates, but may be outside the visible region if the text is |
| // larger than the available space. Subsequent text, cursor, or bounds changes |
| // may invalidate returned values. |
| virtual std::vector<Rect> GetSubstringBounds(const Range& range) = 0; |
| |
| // Gets the horizontal span (relative to the left of the text, not the view) |
| // of the sequence of glyphs in |text_range|, over which the cursor will |
| // jump when breaking by characters. If the glyphs are RTL then the returned |
| // Range will have is_reversed() true. (This does not return a Rect because a |
| // Rect can't have a negative width.) |
| virtual RangeF GetCursorSpan(const Range& text_range) = 0; |
| |
| const Vector2d& GetUpdatedDisplayOffset(); |
| void SetDisplayOffset(int horizontal_offset); |
| void SetDisplayOffset(Vector2d offset); |
| |
| // Returns the line offset from the origin after applying the text alignment |
| // and the display offset. |
| Vector2d GetLineOffset(size_t line_number); |
| |
| // Retrieves the word displayed at the given |point| along with its styling |
| // information. |point| is in the view's coordinates. If no word is displayed |
| // at the point, returns a nearby word. |baseline_point| should correspond to |
| // the baseline point of the leftmost glyph of the |word| in the view's |
| // coordinates. Returns false, if no word can be retrieved. |
| bool GetWordLookupDataAtPoint(const Point& point, |
| DecoratedText* decorated_word, |
| Point* baseline_point); |
| |
| // Retrieves the text at |range| along with its styling information. |
| // |baseline_point| should correspond to the baseline point of |
| // the leftmost glyph of the text in the view's coordinates. If the text |
| // spans multiple lines, |baseline_point| will correspond with the leftmost |
| // glyph on the first line in the range. Returns false, if no text can be |
| // retrieved. |
| bool GetLookupDataForRange(const Range& range, |
| DecoratedText* decorated_text, |
| Point* baseline_point); |
| |
| // Retrieves the text in the given |range|. |
| std::u16string GetTextFromRange(const Range& range) const; |
| |
| void set_strike_thickness_factor(SkScalar f) { strike_thickness_factor_ = f; } |
| |
| // Return the line index that contains the argument; or the index of the last |
| // line if the |caret| exceeds the text length. |
| virtual size_t GetLineContainingCaret(const SelectionModel& caret) = 0; |
| |
| // Expands |range| to its nearest grapheme boundaries and returns the |
| // resulting range. Maintains directionality of |range|. |
| Range ExpandRangeToGraphemeBoundary(const Range& range) const; |
| |
| // Specify the width/height of a glyph for test. The width/height of glyphs is |
| // very platform-dependent and environment-dependent. Otherwise multiline text |
| // will become really flaky. |
| void set_glyph_width_for_test(float width) { glyph_width_for_test_ = width; } |
| void set_glyph_height_for_test(float height) { |
| glyph_height_for_test_ = height; |
| } |
| |
| protected: |
| RenderText(); |
| |
| // Whether |segment| corresponds to the newline character. This uses |text_| |
| // to look up the corresponding character. |
| bool IsNewlineSegment(const internal::LineSegment& segment) const; |
| |
| // Whether |segment| corresponds to the newline character inside |text|. |
| bool IsNewlineSegment(const std::u16string& text, |
| const internal::LineSegment& segment) const; |
| |
| // Returns the character range of segments in |line| excluding the trailing |
| // newline segment. |
| Range GetLineRange(const std::u16string& text, |
| const internal::Line& line) const; |
| |
| // Returns the text used for layout (e.g. after rewriting, eliding and |
| // obscuring characters). |
| const std::u16string& GetLayoutText() const; |
| |
| // NOTE: The value of these accessors may be stale. Please make sure |
| // that these fields are up to date before accessing them. |
| const std::u16string& display_text() const { return display_text_; } |
| bool text_elided() const { return text_elided_; } |
| |
| // Returns an iterator over the |text_| attributes. |
| internal::StyleIterator GetTextStyleIterator() const; |
| // Returns an iterator over the |layout_text_| attributes. |
| internal::StyleIterator GetLayoutTextStyleIterator() const; |
| |
| const BreakList<SkColor>& colors() const { return colors_; } |
| const BreakList<BaselineStyle>& baselines() const { return baselines_; } |
| const BreakList<int>& font_size_overrides() const { |
| return font_size_overrides_; |
| } |
| const BreakList<Font::Weight>& weights() const { return weights_; } |
| const internal::StyleArray& styles() const { return styles_; } |
| SkScalar strike_thickness_factor() const { return strike_thickness_factor_; } |
| |
| const BreakList<SkColor>& layout_colors() const { return layout_colors_; } |
| |
| // Whether all the BreakLists have only one break. |
| bool IsHomogeneous() const; |
| |
| // Returns the shaped text structure. The shaped text contains the visual |
| // positions of glyphs required to render the text. |
| bool has_shaped_text() const { return shaped_text_ != nullptr; } |
| internal::ShapedText* GetShapedText(); |
| void set_shaped_text(std::unique_ptr<internal::ShapedText> shaped_text) { |
| shaped_text_ = std::move(shaped_text); |
| } |
| |
| // Returns the baseline of the current text. The return value depends on |
| // the text and its layout while the return value of GetBaseline() doesn't. |
| // GetAlignmentOffset() takes into account the difference between them. |
| // |
| // We'd like a RenderText to show the text always on the same baseline |
| // regardless of the text, so the text does not jump up or down depending |
| // on the text. However, underlying layout engines return different baselines |
| // depending on the text. In general, layout engines determine the minimum |
| // bounding box for the text and return the baseline from the top of the |
| // bounding box. So the baseline changes depending on font metrics used to |
| // layout the text. |
| // |
| // For example, suppose there are FontA and FontB and the baseline of FontA |
| // is smaller than the one of FontB. If the text is laid out only with FontA, |
| // then the baseline of FontA may be returned. If the text includes some |
| // characters which are laid out with FontB, then the baseline of FontB may |
| // be returned. |
| // |
| // GetBaseline() returns the fixed baseline regardless of the text. |
| // GetDisplayTextBaseline() returns the baseline determined by the underlying |
| // layout engine, and it changes depending on the text. GetAlignmentOffset() |
| // returns the difference between them. |
| int GetDisplayTextBaseline(); |
| |
| // Get the selection model that visually neighbors |position| by |break_type|. |
| // The returned value represents a cursor/caret position without a selection. |
| SelectionModel GetAdjacentSelectionModel(const SelectionModel& current, |
| BreakType break_type, |
| VisualCursorDirection direction); |
| |
| // Get the selection model visually left/right of |selection| by one grapheme. |
| // The returned value represents a cursor/caret position without a selection. |
| virtual SelectionModel AdjacentCharSelectionModel( |
| const SelectionModel& selection, |
| VisualCursorDirection direction) = 0; |
| |
| // Get the selection model visually left/right of |selection| by one word. |
| // The returned value represents a cursor/caret position without a selection. |
| virtual SelectionModel AdjacentWordSelectionModel( |
| const SelectionModel& selection, |
| VisualCursorDirection direction) = 0; |
| |
| // Get the selection model visually above/below |selection| by one line. |
| // The returned value represents a cursor/caret position without a selection. |
| virtual SelectionModel AdjacentLineSelectionModel( |
| const SelectionModel& selection, |
| VisualCursorDirection direction) = 0; |
| |
| // Get the selection model corresponding to visual text ends. |
| // The returned value represents a cursor/caret position without a selection. |
| SelectionModel EdgeSelectionModel(VisualCursorDirection direction); |
| |
| // Get the selection model corresponding to visual text ends for |line_index|. |
| // The returned value represents a cursor/caret position without a selection. |
| SelectionModel LineSelectionModel(size_t line_index, |
| gfx::VisualCursorDirection direction); |
| |
| // Sets the selection model. |model| should be valid. |
| void SetSelectionModel(const SelectionModel& model); |
| // Adds a secondary selection. |selection| should not overlap with existing |
| // selections. |
| void AddSecondarySelection(const Range selection); |
| |
| // Convert between indices into |text_| and indices into |
| // GetDisplayText(), which differ when the text is obscured, |
| // truncated or elided. |
| size_t TextIndexToDisplayIndex(size_t index) const; |
| size_t DisplayIndexToTextIndex(size_t index) const; |
| |
| // Notifies that layout text, or attributes that affect the layout text |
| // shape have changed. |text_changed| is true if the content of the |
| // |layout_text_| has changed, not just attributes. |
| virtual void OnLayoutTextAttributeChanged(bool text_changed); |
| |
| // Notifies that attributes that affect the display text shape have changed. |
| virtual void OnDisplayTextAttributeChanged() = 0; |
| |
| // Ensure the text is laid out, lines are computed, and |lines_| is valid. |
| virtual void EnsureLayout() = 0; |
| |
| // Draw all text and make the given ranges appear selected. |
| virtual void DrawVisualText(internal::SkiaTextRenderer* renderer, |
| const std::vector<Range>& selections) = 0; |
| |
| // Update the display text. |
| void UpdateDisplayText(float text_width); |
| |
| // Returns display text positions that are suitable for breaking lines. |
| const BreakList<size_t>& GetLineBreaks(); |
| |
| // Convert points from the text space to the view space. Handles the display |
| // area, display offset, application LTR/RTL mode and multiline. |line| is the |
| // index of the line in which |point| is found, and is required to be passed |
| // because by the time |point| is in text space, the information to account |
| // for certain zero-width characters (such as empty lines) is lost. |
| Point ToViewPoint(const PointF& point, size_t line); |
| |
| // Get the alignment, resolving ALIGN_TO_HEAD with the current text direction. |
| HorizontalAlignment GetCurrentHorizontalAlignment(); |
| |
| // Returns the line offset from the origin, accounts for text alignment only. |
| Vector2d GetAlignmentOffset(size_t line_number); |
| |
| // Applies fade effects to |renderer|. |
| void ApplyFadeEffects(internal::SkiaTextRenderer* renderer); |
| |
| // Applies text shadows to |renderer|. |
| void ApplyTextShadows(internal::SkiaTextRenderer* renderer); |
| |
| // Get the text direction for the current directionality mode and given |
| // |text|. |
| base::i18n::TextDirection GetTextDirectionForGivenText( |
| const std::u16string& text) const; |
| |
| // Adjust ranged styles to accommodate a new |text_| length. |
| void UpdateStyleLengths(); |
| |
| // Adjust ranged styles to accommodate a new |layout_text_| length. |
| void UpdateLayoutStyleLengths(size_t max_length) const; |
| |
| // Returns the line index for the given argument. |text_y| is relative to |
| // the text bounds. Returns -1 if |text_y| is above the text and |
| // lines().size() if |text_y| is below it. |
| int GetLineContainingYCoord(float text_y); |
| |
| // A convenience function to check whether the glyph attached to the caret |
| // is within the given range. |
| static bool RangeContainsCaret(const Range& range, |
| size_t caret_pos, |
| LogicalCursorDirection caret_affinity); |
| |
| // Returns the baseline, with which the text best appears vertically centered. |
| static int DetermineBaselineCenteringText(const int display_height, |
| const FontList& font_list); |
| |
| // Returns an expanded version of |rect| that is vertically symmetric with |
| // respect to the center of |display_rect|. |
| static gfx::Rect ExpandToBeVerticallySymmetric(const gfx::Rect& rect, |
| const gfx::Rect& display_rect); |
| |
| // Given |rects|, sort them along the x-axis and merge intersecting rects |
| // using union. Expects all selections in the text to be from the same line. |
| static void MergeIntersectingRects(std::vector<Rect>& rects); |
| |
| // Resets |cached_cursor_x_| to null. When non-null, CURSOR_UP, CURSOR_DOWN |
| // movements use this value instead of the current cursor x position to |
| // determine the next cursor x position. |
| void reset_cached_cursor_x() { cached_cursor_x_.reset(); } |
| |
| void set_cached_cursor_x(int x) { cached_cursor_x_ = x; } |
| absl::optional<int> cached_cursor_x() const { return cached_cursor_x_; } |
| |
| // Fixed width of glyphs. This should only be set in test environments. |
| float glyph_width_for_test_ = 0; |
| // Fixed height of glyphs. This should only be set in test environments. |
| float glyph_height_for_test_ = 0; |
| |
| private: |
| friend class test::RenderTextTestApi; |
| |
| // Resets |layout_text_| and |display_text_| and marks them dirty. |
| void OnTextAttributeChanged(); |
| |
| // Computes the |layout_text_| by rewriting it from |text_|, if needed. |
| // Computes the layout break lists, if needed. |
| void EnsureLayoutTextUpdated() const; |
| |
| // Elides |text| as needed to fit in the |available_width| using |behavior|. |
| // |text_width| is the pre-calculated width of the text shaped by this render |
| // text, or pass 0 if the width is unknown. |
| std::u16string Elide(const std::u16string& text, |
| float text_width, |
| float available_width, |
| ElideBehavior behavior); |
| |
| // Elides |email| as needed to fit the |available_width|. |
| std::u16string ElideEmail(const std::u16string& email, float available_width); |
| |
| // Update the cached bounds and display offset to ensure that the current |
| // cursor is within the visible display area. |
| void UpdateCachedBoundsAndOffset(); |
| |
| // Draws the specified ranges of text with a selected appearance. |
| void DrawSelections(Canvas* canvas, const std::vector<Range>& selections); |
| |
| // Returns a grapheme iterator that contains the codepoint at |index|. |
| internal::GraphemeIterator GetGraphemeIteratorAtIndex( |
| const std::u16string& text, |
| const size_t internal::TextToDisplayIndex::*field, |
| size_t index) const; |
| |
| // Returns the nearest word start boundary for |index|. First searches in the |
| // CURSOR_BACKWARD direction, then in the CURSOR_FORWARD direction. Returns |
| // the text length if no valid boundary is found. |
| size_t GetNearestWordStartBoundary(size_t index) const; |
| |
| // Expands |range| to its nearest word boundaries and returns the resulting |
| // range. Maintains directionality of |range|. |
| Range ExpandRangeToWordBoundary(const Range& range) const; |
| |
| // Returns an implementation-specific run list, if implemented. |
| virtual internal::TextRunList* GetRunList() = 0; |
| virtual const internal::TextRunList* GetRunList() const = 0; |
| |
| // Returns the decorated text corresponding to |range|. Returns false if the |
| // text cannot be retrieved, e.g. if the text is obscured. |
| virtual bool GetDecoratedTextForRange(const Range& range, |
| DecoratedText* decorated_text) = 0; |
| |
| // Logical UTF-16 string data to be drawn. |
| std::u16string text_; |
| |
| // Horizontal alignment of the text with respect to |display_rect_|. The |
| // default is to align left if the application UI is LTR and right if RTL. |
| HorizontalAlignment horizontal_alignment_{base::i18n::IsRTL() ? ALIGN_RIGHT |
| : ALIGN_LEFT}; |
| |
| // Vertical alignment of the text with respect to |display_rect_|. Only |
| // applicable when |multiline_| is true. The default is to align center. |
| VerticalAlignment vertical_alignment_ = ALIGN_MIDDLE; |
| |
| // The text directionality mode, defaults to DIRECTIONALITY_FROM_TEXT. |
| DirectionalityMode directionality_mode_ = DIRECTIONALITY_FROM_TEXT; |
| |
| // The cached text direction, potentially computed from the text or UI locale. |
| // Use GetTextDirection(), do not use this potentially invalid value directly! |
| mutable base::i18n::TextDirection text_direction_ = |
| base::i18n::UNKNOWN_DIRECTION; |
| mutable base::i18n::TextDirection display_text_direction_ = |
| base::i18n::UNKNOWN_DIRECTION; |
| |
| // A list of fonts used to render |text_|. |
| FontList font_list_; |
| |
| // Logical selection ranges and visual cursor position. |
| SelectionModel selection_model_; |
| |
| // The cached cursor bounds; get these bounds with GetUpdatedCursorBounds. |
| Rect cursor_bounds_; |
| |
| // Specifies whether the cursor is enabled. If disabled, no space is reserved |
| // for the cursor when positioning text. |
| bool cursor_enabled_ = true; |
| |
| // Whether the current selection has a known direction. That is, whether a |
| // directional input (e.g. arrow key) has been received for the current |
| // selection to indicate which end of the selection has the caret. When true, |
| // directed inputs preserve (rather than replace) the selection affinity. |
| bool has_directed_selection_ = kSelectionIsAlwaysDirected; |
| |
| // The color used for drawing selected text. |
| SkColor selection_color_ = kPlaceholderColor; |
| |
| // The background color used for drawing the selection when focused. |
| SkColor selection_background_focused_color_ = kPlaceholderColor; |
| |
| // Whether the selection visual bounds should be expanded vertically to be |
| // vertically symmetric with respect to the display rect. Note this flag has |
| // no effect on multi-line text. |
| bool symmetric_selection_visual_bounds_ = false; |
| |
| // The focus state of the text. |
| bool focused_ = false; |
| |
| // Composition text range. |
| Range composition_range_ = Range::InvalidRange(); |
| |
| // Color, baseline, and style breaks, used to modify ranges of text. |
| // BreakList positions are stored with text indices, not display indices. |
| // TODO(msw): Expand to support cursor, selection, background, etc. colors. |
| BreakList<SkColor> colors_{kPlaceholderColor}; |
| BreakList<BaselineStyle> baselines_{NORMAL_BASELINE}; |
| BreakList<int> font_size_overrides_{0}; |
| BreakList<Font::Weight> weights_{Font::Weight::NORMAL}; |
| internal::StyleArray styles_; |
| |
| mutable BreakList<SkColor> layout_colors_; |
| mutable BreakList<BaselineStyle> layout_baselines_; |
| mutable BreakList<int> layout_font_size_overrides_; |
| mutable BreakList<Font::Weight> layout_weights_; |
| mutable internal::StyleArray layout_styles_; |
| |
| // A mapping from text to display text indices for each grapheme. The vector |
| // contains an ordered sequence of indice pairs. Both sequence |text_index| |
| // and |display_index| are sorted. |
| mutable internal::TextToDisplaySequence text_to_display_indices_; |
| |
| // A flag to obscure actual text with asterisks for password fields. |
| bool obscured_ = false; |
| // The index at which the char should be revealed in the obscured text. |
| int obscured_reveal_index_ = -1; |
| |
| // The maximum length of text to display, 0 forgoes a hard limit. |
| size_t truncate_length_ = 0; |
| |
| // The obscured and/or truncated text used to layout the text to display. |
| mutable std::u16string layout_text_; |
| |
| // The elided text displayed visually. This is empty if the text |
| // does not have to be elided, or became empty as a result of eliding. |
| // TODO(oshima): When the text is elided, painting can be done only with |
| // display text info, so it should be able to clear the |layout_text_| and |
| // associated information. |
| mutable std::u16string display_text_; |
| |
| // The behavior for eliding, fading, or truncating. |
| ElideBehavior elide_behavior_ = NO_ELIDE; |
| |
| // The behavior for eliding whitespace when eliding or truncating. |
| absl::optional<bool> whitespace_elision_; |
| |
| // True if the text is elided given the current behavior and display area. |
| bool text_elided_ = false; |
| |
| // The minimum height a line should have. |
| int min_line_height_ = 0; |
| |
| // Whether the text should be broken into multiple lines. Uses the width of |
| // |display_rect_| as the width cap. |
| bool multiline_ = false; |
| |
| // If multiple lines, the maximum number of lines to render, or 0. |
| size_t max_lines_ = 0; |
| |
| // The wrap behavior when the text is broken into lines. Do nothing unless |
| // |multiline_| is set. The default value is IGNORE_LONG_WORDS. |
| WordWrapBehavior word_wrap_behavior_ = IGNORE_LONG_WORDS; |
| |
| // Set to true to suppress subpixel rendering due to non-font reasons (eg. |
| // if the background is transparent). The default value is false. |
| bool subpixel_rendering_suppressed_ = false; |
| |
| // The local display area for rendering the text. |
| Rect display_rect_; |
| |
| // Flag to work around a Skia bug with the PDF path (http://crbug.com/133548) |
| // that results in incorrect clipping when drawing to the document margins. |
| // This field allows disabling clipping to work around the issue. |
| // TODO(asvitkine): Remove this when the underlying Skia bug is fixed. |
| bool clip_to_display_rect_ = true; |
| |
| // The offset for the text to be drawn, relative to the display area. |
| // Get this point with GetUpdatedDisplayOffset (or risk using a stale value). |
| Vector2d display_offset_; |
| |
| // The baseline of the text. This is determined from the height of the |
| // display area and the cap height of the font list so the text is vertically |
| // centered. |
| int baseline_ = kInvalidBaseline; |
| |
| // The cached bounds and offset are invalidated by changes to the cursor, |
| // selection, font, and other operations that adjust the visible text bounds. |
| bool cached_bounds_and_offset_valid_ = false; |
| |
| // Text shadows to be drawn. |
| ShadowValues shadows_; |
| |
| // A list of valid display text line break positions. |
| BreakList<size_t> line_breaks_; |
| |
| // Text shaping computed by EnsureLayout. This should be invalidated upon |
| // OnLayoutTextAttributeChanged and OnDisplayTextAttributeChanged calls. |
| std::unique_ptr<internal::ShapedText> shaped_text_; |
| |
| // The ratio of strike-through line thickness to text height. |
| SkScalar strike_thickness_factor_ = kLineThicknessFactor; |
| |
| // Extra spacing placed between glyphs; used only for obscured text styling. |
| int obscured_glyph_spacing_ = 0; |
| |
| // The cursor position in view space, used to traverse lines of varied widths. |
| absl::optional<int> cached_cursor_x_; |
| |
| // Tell whether or not the |layout_text_| needs an update or is up to date. |
| mutable bool layout_text_up_to_date_ = false; |
| }; |
| |
| } // namespace gfx |
| |
| #endif // UI_GFX_RENDER_TEXT_H_ |