OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef UI_GFX_RENDER_TEXT_H_ | 5 #ifndef UI_GFX_RENDER_TEXT_H_ |
6 #define UI_GFX_RENDER_TEXT_H_ | 6 #define UI_GFX_RENDER_TEXT_H_ |
7 #pragma once | 7 #pragma once |
8 | 8 |
9 #include <algorithm> | |
9 #include <vector> | 10 #include <vector> |
10 | 11 |
11 #include "base/gtest_prod_util.h" | 12 #include "base/gtest_prod_util.h" |
12 #include "base/i18n/rtl.h" | 13 #include "base/i18n/rtl.h" |
13 #include "base/string16.h" | 14 #include "base/string16.h" |
14 #include "third_party/skia/include/core/SkColor.h" | 15 #include "third_party/skia/include/core/SkColor.h" |
15 #include "ui/base/range/range.h" | 16 #include "ui/base/range/range.h" |
16 #include "ui/gfx/font.h" | 17 #include "ui/gfx/font.h" |
17 #include "ui/gfx/rect.h" | 18 #include "ui/gfx/rect.h" |
18 #include "ui/gfx/point.h" | 19 #include "ui/gfx/point.h" |
19 | 20 |
20 namespace { | 21 namespace gfx { |
21 | 22 |
22 // Strike line width. | 23 // Strike line width. |
23 const int kStrikeWidth = 2; | 24 const int kStrikeWidth = 2; |
24 | 25 |
25 // Color settings for text, backgrounds and cursor. | 26 // Color settings for text, backgrounds and cursor. |
26 // These are tentative, and should be derived from theme, system | 27 // These are tentative, and should be derived from theme, system |
27 // settings and current settings. | 28 // settings and current settings. |
28 // TODO(oshima): Change this to match the standard chrome | 29 // TODO(oshima): Change this to match the standard chrome |
29 // before dogfooding textfield views. | 30 // before dogfooding textfield views. |
30 const SkColor kSelectedTextColor = SK_ColorWHITE; | 31 const SkColor kSelectedTextColor = SK_ColorWHITE; |
31 const SkColor kFocusedSelectionColor = SK_ColorCYAN; | 32 const SkColor kFocusedSelectionColor = SK_ColorCYAN; |
32 const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY; | 33 const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY; |
33 const SkColor kCursorColor = SK_ColorBLACK; | 34 const SkColor kCursorColor = SK_ColorBLACK; |
34 | 35 |
35 } // namespace | |
36 | |
37 namespace gfx { | |
38 | |
39 class Canvas; | 36 class Canvas; |
40 class RenderTextTest; | 37 class RenderTextTest; |
41 | 38 |
42 // A visual style applicable to a range of text. | 39 // A visual style applicable to a range of text. |
43 struct UI_API StyleRange { | 40 struct UI_API StyleRange { |
44 StyleRange(); | 41 StyleRange(); |
45 | 42 |
46 Font font; | 43 Font font; |
47 SkColor foreground; | 44 SkColor foreground; |
48 bool strike; | 45 bool strike; |
49 bool underline; | 46 bool underline; |
50 ui::Range range; | 47 ui::Range range; |
51 }; | 48 }; |
52 | 49 |
53 typedef std::vector<StyleRange> StyleRanges; | 50 typedef std::vector<StyleRange> StyleRanges; |
54 | 51 |
55 // TODO(msw): Distinguish between logical character and glyph? | 52 // TODO(msw): Distinguish between logical character and glyph? |
56 enum BreakType { | 53 enum BreakType { |
57 CHARACTER_BREAK, | 54 CHARACTER_BREAK, |
58 WORD_BREAK, | 55 WORD_BREAK, |
59 LINE_BREAK, | 56 LINE_BREAK, |
60 }; | 57 }; |
61 | 58 |
59 // TODO(xji): publish bidi-editing guide line and replace the place holder. | |
60 // SelectionModel is used to represent the logical selection and visual | |
61 // position of cursor. | |
62 // | |
63 // For bi-directional text, the mapping between visual position and logical | |
64 // position is not one-to-one. For example, logical text "abcDEF" where capital | |
65 // letters stand for Hebrew, the visual display is "abcFED". According to the | |
66 // bidi editing guide (http://bidi-editing-guideline): | |
67 // 1. If pointing to the right half of the cell of a LTR character, the current | |
68 // position must be set after this character and the caret must be displayed | |
69 // after this character. | |
70 // 2. If pointing to the right half of the cell of a RTL character, the current | |
71 // position must be set before this character and the caret must be displayed | |
72 // before this character. | |
73 // | |
74 // Pointing to the right half of 'c' and pointing to the right half of 'D' both | |
75 // set the logical cursor position to 3. But the cursor displayed visually at | |
76 // different places: | |
77 // Pointing to the right half of 'c' displays the cursor right of 'c' as | |
78 // "abc|FED". | |
79 // Pointing to the right half of 'D' displays the cursor right of 'D' as | |
80 // "abcFED|". | |
81 // So, besides the logical selection start point and end point, we need extra | |
82 // information to specify to which character and on which edge of the character | |
83 // the visual cursor is bound to. For example, the visual cursor is bound to | |
84 // the trailing side of the 2nd character 'c' when pointing to right half of | |
85 // 'c'. And it is bound to the leading edge of the 3rd character 'D' when | |
86 // pointing to right of 'D'. | |
87 class UI_API SelectionModel { | |
88 public: | |
89 enum CaretPlacement { | |
90 // PREVIOUS_GRAPHEME_TRAILING means cursor is visually attached to the | |
91 // trailing edge of previous grapheme. | |
92 PREVIOUS_GRAPHEME_TRAILING, | |
93 LEADING, | |
94 TRAILING, | |
95 }; | |
96 | |
97 SelectionModel(); | |
98 explicit SelectionModel(size_t pos); | |
99 SelectionModel(size_t end, size_t pos, CaretPlacement status); | |
100 SelectionModel(size_t start, size_t end, size_t pos, CaretPlacement status); | |
101 | |
102 virtual ~SelectionModel(); | |
103 | |
104 size_t selection_start() const { return selection_start_; } | |
105 void set_selection_start(size_t pos) { selection_start_ = pos; } | |
106 | |
107 size_t selection_end() const { return selection_end_; } | |
108 void set_selection_end(size_t pos) { selection_end_ = pos; } | |
109 | |
110 size_t caret_pos() const { return caret_pos_; } | |
111 void set_caret_pos(size_t pos) { caret_pos_ = pos; } | |
112 | |
113 CaretPlacement caret_placement() const { return caret_placement_; } | |
114 void set_caret_placement(CaretPlacement placement) { | |
115 caret_placement_ = placement; | |
116 } | |
117 | |
118 bool Equals(const SelectionModel& sel) const; | |
119 | |
120 private: | |
121 void Init(size_t start, size_t end, size_t pos, CaretPlacement status); | |
122 | |
123 // Logical selection start. If there is non-empty selection, the selection | |
124 // always starts visually at the leading edge of the selection_start. So, we | |
125 // do not need extra information for visual selection bounding. | |
126 size_t selection_start_; | |
127 | |
128 // The logical cursor position that next character will be inserted into. | |
129 // It is also the end of the selection. | |
130 size_t selection_end_; | |
131 | |
132 // The following two fields are used to guide cursor visual position. | |
133 // The index of the character that cursor is visually attached to. | |
134 size_t caret_pos_; | |
135 // The visual placement of the cursor, relative to its associated character. | |
136 CaretPlacement caret_placement_; | |
137 }; | |
138 | |
62 // TODO(msw): Implement RenderText[Win|Linux] for Uniscribe/Pango BiDi... | 139 // TODO(msw): Implement RenderText[Win|Linux] for Uniscribe/Pango BiDi... |
63 | 140 |
64 // RenderText represents an abstract model of styled text and its corresponding | 141 // RenderText represents an abstract model of styled text and its corresponding |
65 // visual layout. Support is built in for a cursor, a selection, simple styling, | 142 // visual layout. Support is built in for a cursor, a selection, simple styling, |
66 // complex scripts, and bi-directional text. Implementations provide mechanisms | 143 // complex scripts, and bi-directional text. Implementations provide mechanisms |
67 // for rendering and translation between logical and visual data. | 144 // for rendering and translation between logical and visual data. |
68 class UI_API RenderText { | 145 class UI_API RenderText { |
69 | |
70 public: | 146 public: |
71 virtual ~RenderText(); | 147 virtual ~RenderText(); |
72 | 148 |
73 // Creates a platform-specific RenderText instance. | 149 // Creates a platform-specific RenderText instance. |
74 static RenderText* CreateRenderText(); | 150 static RenderText* CreateRenderText(); |
75 | 151 |
76 const string16& text() const { return text_; } | 152 const string16& text() const { return text_; } |
77 virtual void SetText(const string16& text); | 153 virtual void SetText(const string16& text); |
78 | 154 |
155 const SelectionModel& selection_model() const { return selection_model_; } | |
156 void SetSelectionModel(const SelectionModel& sel); | |
157 | |
79 bool cursor_visible() const { return cursor_visible_; } | 158 bool cursor_visible() const { return cursor_visible_; } |
80 void set_cursor_visible(bool visible) { cursor_visible_ = visible; } | 159 void set_cursor_visible(bool visible) { cursor_visible_ = visible; } |
81 | 160 |
82 bool insert_mode() const { return insert_mode_; } | 161 bool insert_mode() const { return insert_mode_; } |
83 void toggle_insert_mode() { insert_mode_ = !insert_mode_; } | 162 void toggle_insert_mode() { insert_mode_ = !insert_mode_; } |
84 | 163 |
85 bool focused() const { return focused_; } | 164 bool focused() const { return focused_; } |
86 void set_focused(bool focused) { focused_ = focused; } | 165 void set_focused(bool focused) { focused_ = focused; } |
87 | 166 |
88 const StyleRange& default_style() const { return default_style_; } | 167 const StyleRange& default_style() const { return default_style_; } |
89 void set_default_style(StyleRange style) { default_style_ = style; } | 168 void set_default_style(StyleRange style) { default_style_ = style; } |
90 | 169 |
91 const Rect& display_rect() const { return display_rect_; } | 170 const Rect& display_rect() const { return display_rect_; } |
92 void set_display_rect(const Rect& r) { display_rect_ = r; } | 171 virtual void set_display_rect(const Rect& r) { display_rect_ = r; } |
93 | 172 |
94 const gfx::Point& display_offset() const { return display_offset_; } | 173 const gfx::Point& display_offset() const { return display_offset_; } |
95 | 174 |
175 // This cursor position corresponds to SelectionModel::selection_end. In | |
176 // addition to representing the selection end, it's also where logical text | |
177 // edits take place, and doesn't necessarily correspond to | |
178 // SelectionModel::caret_pos. | |
96 size_t GetCursorPosition() const; | 179 size_t GetCursorPosition() const; |
97 void SetCursorPosition(const size_t position); | 180 void SetCursorPosition(const size_t position); |
98 | 181 |
182 void SetCaretPlacement(SelectionModel::CaretPlacement placement) { | |
183 selection_model_.set_caret_placement(placement); | |
184 } | |
185 | |
99 // Moves the cursor left or right. Cursor movement is visual, meaning that | 186 // Moves the cursor left or right. Cursor movement is visual, meaning that |
100 // left and right are relative to screen, not the directionality of the text. | 187 // left and right are relative to screen, not the directionality of the text. |
101 // If |select| is false, the selection range is emptied at the new position. | 188 // If |select| is false, the selection range is emptied at the new position. |
102 // If |break_type| is CHARACTER_BREAK, move to the neighboring character. | 189 // If |break_type| is CHARACTER_BREAK, move to the neighboring character. |
103 // If |break_type| is WORD_BREAK, move to the nearest word boundary. | 190 // If |break_type| is WORD_BREAK, move to the nearest word boundary. |
104 // If |break_type| is LINE_BREAK, move to text edge as shown on screen. | 191 // If |break_type| is LINE_BREAK, move to text edge as shown on screen. |
105 void MoveCursorLeft(BreakType break_type, bool select); | 192 void MoveCursorLeft(BreakType break_type, bool select); |
106 void MoveCursorRight(BreakType break_type, bool select); | 193 void MoveCursorRight(BreakType break_type, bool select); |
107 | 194 |
108 // Moves the cursor to the specified logical |position|. | 195 // Set the selection_model_ to the value of |selection|. |
109 // If |select| is false, the selection range is emptied at the new position. | |
110 // Returns true if the cursor position or selection range changed. | 196 // Returns true if the cursor position or selection range changed. |
111 bool MoveCursorTo(size_t position, bool select); | 197 bool MoveCursorTo(const SelectionModel& selection); |
112 | 198 |
113 // Move the cursor to the position associated with the clicked point. | 199 // Move the cursor to the position associated with the clicked point. |
114 // If |select| is false, the selection range is emptied at the new position. | 200 // If |select| is false, the selection range is emptied at the new position. |
115 bool MoveCursorTo(const Point& point, bool select); | 201 bool MoveCursorTo(const Point& point, bool select); |
116 | 202 |
117 const ui::Range& GetSelection() const; | 203 size_t GetSelectionStart() const { |
118 void SetSelection(const ui::Range& range); | 204 return selection_model_.selection_start(); |
205 } | |
206 size_t MinOfSelection() const { | |
207 return std::min(GetSelectionStart(), GetCursorPosition()); | |
208 } | |
209 size_t MaxOfSelection() const { | |
210 return std::max(GetSelectionStart(), GetCursorPosition()); | |
211 } | |
212 bool EmptySelection() const { | |
213 return GetSelectionStart() == GetCursorPosition(); | |
214 } | |
119 | 215 |
120 // Returns true if the local point is over selected text. | 216 // Returns true if the local point is over selected text. |
121 bool IsPointInSelection(const Point& point) const; | 217 bool IsPointInSelection(const Point& point); |
122 | 218 |
123 // Selects no text, all text, or the word at the current cursor position. | 219 // Selects no text, all text, or the word at the current cursor position. |
124 void ClearSelection(); | 220 void ClearSelection(); |
125 void SelectAll(); | 221 void SelectAll(); |
126 void SelectWord(); | 222 void SelectWord(); |
127 | 223 |
128 const ui::Range& GetCompositionRange() const; | 224 const ui::Range& GetCompositionRange() const; |
129 void SetCompositionRange(const ui::Range& composition_range); | 225 void SetCompositionRange(const ui::Range& composition_range); |
130 | 226 |
131 // Apply |style_range| to the internal style model. | 227 // Apply |style_range| to the internal style model. |
132 virtual void ApplyStyleRange(StyleRange style_range); | 228 virtual void ApplyStyleRange(StyleRange style_range); |
133 | 229 |
134 // Apply |default_style_| over the entire text range. | 230 // Apply |default_style_| over the entire text range. |
135 virtual void ApplyDefaultStyle(); | 231 virtual void ApplyDefaultStyle(); |
136 | 232 |
137 base::i18n::TextDirection GetTextDirection() const; | 233 base::i18n::TextDirection GetTextDirection() const; |
138 | 234 |
139 // Get the width of the entire string. | 235 // Get the width of the entire string. |
140 int GetStringWidth() const; | 236 virtual int GetStringWidth(); |
141 | 237 |
142 virtual void Draw(Canvas* canvas); | 238 virtual void Draw(Canvas* canvas); |
143 | 239 |
144 // TODO(msw): Deprecate this function. Logical and visual cursors are not | 240 // Gets the SelectionModel from a visual point in local coordinates. |
145 // mapped one-to-one. See the selection_range_ TODO for more information. | 241 virtual SelectionModel FindCursorPosition(const Point& point); |
146 // Get the logical cursor position from a visual point in local coordinates. | |
147 virtual size_t FindCursorPosition(const Point& point) const; | |
148 | 242 |
149 // Get the visual bounds containing the logical substring within |range|. | 243 // Get the visual bounds containing the logical substring within |from| to |
150 // These bounds could be visually discontiguous if the logical selection range | 244 // |to|. These bounds could be visually discontinuous if the logical |
151 // is split by an odd number of LTR/RTL level change. | 245 // selection range is split by an odd number of LTR/RTL level change. |
152 virtual std::vector<Rect> GetSubstringBounds( | 246 virtual std::vector<Rect> GetSubstringBounds( |
153 const ui::Range& range) const; | 247 size_t from, size_t to) const; |
154 | 248 |
155 // Get the visual bounds describing the cursor at |position|. These bounds | 249 // Get the visual bounds describing the cursor at |selection|. These bounds |
156 // typically represent a vertical line, but if |insert_mode| is true they | 250 // typically represent a vertical line, but if |insert_mode| is true they |
157 // contain the bounds of the associated glyph. | 251 // contain the bounds of the associated glyph. |
158 virtual Rect GetCursorBounds(size_t position, bool insert_mode) const; | 252 virtual Rect GetCursorBounds(const SelectionModel& selection, |
253 bool insert_mode); | |
254 | |
255 // Compute cursor_bounds_ and update display_offset_ when necessary. Cache | |
256 // the values for later use and return cursor_bounds_. | |
257 Rect CursorBounds(); | |
msw
2011/08/03 23:44:38
This should return a const ref.
xji
2011/08/04 06:26:50
Done.
| |
159 | 258 |
160 protected: | 259 protected: |
161 RenderText(); | 260 RenderText(); |
162 | 261 |
262 void set_cursor_bounds_valid(bool valid) { cursor_bounds_valid_ = valid; } | |
263 | |
163 const StyleRanges& style_ranges() const { return style_ranges_; } | 264 const StyleRanges& style_ranges() const { return style_ranges_; } |
164 | 265 |
165 // Get the cursor position that visually neighbors |position|. | 266 // Get the cursor position that visually neighbors |position|. |
166 // If |move_by_word| is true, return the neighboring word delimiter position. | 267 // If |move_by_word| is true, return the neighboring word delimiter position. |
167 virtual size_t GetLeftCursorPosition(size_t position, | 268 virtual SelectionModel GetLeftCursorPosition(const SelectionModel& current, |
168 bool move_by_word) const; | 269 bool move_by_word); |
169 virtual size_t GetRightCursorPosition(size_t position, | 270 virtual SelectionModel GetRightCursorPosition(const SelectionModel& current, |
170 bool move_by_word) const; | 271 bool move_by_word); |
272 | |
273 // Apply composition style (underline) to composition range and selection | |
274 // style (foreground) to selection range. | |
275 void ApplyCompositionAndSelectionStyles(StyleRanges* style_ranges) const; | |
171 | 276 |
172 private: | 277 private: |
173 friend class RenderTextTest; | 278 friend class RenderTextTest; |
174 | 279 |
175 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, DefaultStyle); | 280 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, DefaultStyle); |
176 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, CustomDefaultStyle); | 281 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, CustomDefaultStyle); |
177 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyleRange); | 282 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, ApplyStyleRange); |
178 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StyleRangesAdjust); | 283 FRIEND_TEST_ALL_PREFIXES(RenderTextTest, StyleRangesAdjust); |
179 | 284 |
180 // Clear out |style_ranges_|. | 285 // Clear out |style_ranges_|. |
181 void ClearStyleRanges(); | 286 void ClearStyleRanges(); |
182 | 287 |
183 bool IsPositionAtWordSelectionBoundary(size_t pos); | 288 bool IsPositionAtWordSelectionBoundary(size_t pos); |
184 | 289 |
290 void UpdateCursorBoundsAndDisplayOffset(); | |
291 | |
185 // Logical UTF-16 string data to be drawn. | 292 // Logical UTF-16 string data to be drawn. |
186 string16 text_; | 293 string16 text_; |
187 | 294 |
188 // TODO(msw): A single logical cursor position doesn't support two potential | 295 // Logical selection range and visual cursor position. |
189 // visual cursor positions. For example, clicking right of 'c' & 'D' yeilds: | 296 SelectionModel selection_model_; |
190 // (visually: 'abc|FEDghi' and 'abcFED|ghi', both logically: 'abc|DEFghi'). | 297 |
191 // Similarly, one visual position may have two associated logical positions. | 298 // The cached cursor bounds. |
192 // For example, clicking the right side of 'D' and left side of 'g' yields: | 299 Rect cursor_bounds_; |
193 // (both visually: 'abcFED|ghi', logically: 'abc|DEFghi' and 'abcDEF|ghi'). | 300 // cursor_bounds_ is computed when needed and cached afterwards. And it is |
194 // Update the cursor model with a leading/trailing flag, a level association, | 301 // invalidated in operations such as SetCursorPosition, SetSelection, Font |
195 // or a disjoint visual position to satisfy the proposed visual behavior. | 302 // related style change, and other operations that trigger re-layout. |
196 // Logical selection range; the range end is also the logical cursor position. | 303 bool cursor_bounds_valid_; |
197 ui::Range selection_range_; | |
198 | 304 |
199 // The cursor visibility and insert mode. | 305 // The cursor visibility and insert mode. |
200 bool cursor_visible_; | 306 bool cursor_visible_; |
201 bool insert_mode_; | 307 bool insert_mode_; |
202 | 308 |
203 // The focus state of the text. | 309 // The focus state of the text. |
204 bool focused_; | 310 bool focused_; |
205 | 311 |
206 // Composition text range. | 312 // Composition text range. |
207 ui::Range composition_range_; | 313 ui::Range composition_range_; |
208 | 314 |
209 // List of style ranges. Elements in the list never overlap each other. | 315 // List of style ranges. Elements in the list never overlap each other. |
210 StyleRanges style_ranges_; | 316 StyleRanges style_ranges_; |
211 // The default text style. | 317 // The default text style. |
212 StyleRange default_style_; | 318 StyleRange default_style_; |
213 | 319 |
214 // The local display area for rendering the text. | 320 // The local display area for rendering the text. |
215 Rect display_rect_; | 321 Rect display_rect_; |
216 // The offset for the text to be drawn, relative to the display area. | 322 // The offset for the text to be drawn, relative to the display area. |
217 Point display_offset_; | 323 Point display_offset_; |
218 | 324 |
219 DISALLOW_COPY_AND_ASSIGN(RenderText); | 325 DISALLOW_COPY_AND_ASSIGN(RenderText); |
220 }; | 326 }; |
221 | 327 |
222 } // namespace gfx | 328 } // namespace gfx |
223 | 329 |
224 #endif // UI_GFX_RENDER_TEXT_H_ | 330 #endif // UI_GFX_RENDER_TEXT_H_ |
OLD | NEW |