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 #include "views/controls/textfield/native_textfield_views.h" | 5 #include "views/controls/textfield/native_textfield_views.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/message_loop.h" | 11 #include "base/message_loop.h" |
12 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
13 #include "grit/ui_strings.h" | 13 #include "grit/ui_strings.h" |
14 #include "ui/base/clipboard/clipboard.h" | 14 #include "ui/base/clipboard/clipboard.h" |
15 #include "ui/base/dragdrop/drag_drop_types.h" | 15 #include "ui/base/dragdrop/drag_drop_types.h" |
16 #include "ui/base/range/range.h" | 16 #include "ui/base/range/range.h" |
17 #include "ui/gfx/canvas.h" | 17 #include "ui/gfx/canvas.h" |
18 #include "ui/gfx/insets.h" | 18 #include "ui/gfx/insets.h" |
| 19 #include "ui/gfx/render_text.h" |
19 #include "views/background.h" | 20 #include "views/background.h" |
20 #include "views/border.h" | 21 #include "views/border.h" |
21 #include "views/controls/focusable_border.h" | 22 #include "views/controls/focusable_border.h" |
22 #include "views/controls/menu/menu_item_view.h" | 23 #include "views/controls/menu/menu_item_view.h" |
23 #include "views/controls/menu/menu_model_adapter.h" | 24 #include "views/controls/menu/menu_model_adapter.h" |
24 #include "views/controls/textfield/text_style.h" | |
25 #include "views/controls/textfield/textfield.h" | 25 #include "views/controls/textfield/textfield.h" |
26 #include "views/controls/textfield/textfield_controller.h" | 26 #include "views/controls/textfield/textfield_controller.h" |
27 #include "views/controls/textfield/textfield_views_model.h" | 27 #include "views/controls/textfield/textfield_views_model.h" |
28 #include "views/events/event.h" | 28 #include "views/events/event.h" |
29 #include "views/ime/input_method.h" | 29 #include "views/ime/input_method.h" |
30 #include "views/metrics.h" | 30 #include "views/metrics.h" |
31 #include "views/views_delegate.h" | 31 #include "views/views_delegate.h" |
32 #include "views/widget/widget.h" | 32 #include "views/widget/widget.h" |
33 | 33 |
34 #if defined(OS_LINUX) | 34 #if defined(OS_LINUX) |
35 #include "ui/gfx/gtk_util.h" | 35 #include "ui/gfx/gtk_util.h" |
36 #endif | 36 #endif |
37 | 37 |
38 namespace { | 38 namespace { |
39 | 39 |
40 // Color settings for text, backgrounds and cursor. | 40 // Text color for read only. |
41 // These are tentative, and should be derived from theme, system | 41 const SkColor kReadonlyTextColor = SK_ColorDKGRAY; |
42 // settings and current settings. | |
43 // TODO(oshima): Change this to match the standard chrome | |
44 // before dogfooding textfield views. | |
45 const SkColor kSelectedTextColor = SK_ColorWHITE; | |
46 const SkColor kFocusedSelectionColor = SK_ColorCYAN; | |
47 const SkColor kUnfocusedSelectionColor = SK_ColorLTGRAY; | |
48 const SkColor kCursorColor = SK_ColorBLACK; | |
49 | 42 |
50 // Parameters to control cursor blinking. | 43 // Parameters to control cursor blinking. |
51 const int kCursorVisibleTimeMs = 800; | 44 const int kCursorVisibleTimeMs = 800; |
52 const int kCursorInvisibleTimeMs = 500; | 45 const int kCursorInvisibleTimeMs = 500; |
53 | 46 |
54 } // namespace | 47 } // namespace |
55 | 48 |
56 namespace views { | 49 namespace views { |
57 | 50 |
58 const char NativeTextfieldViews::kViewClassName[] = | 51 const char NativeTextfieldViews::kViewClassName[] = |
59 "views/NativeTextfieldViews"; | 52 "views/NativeTextfieldViews"; |
60 | 53 |
61 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) | 54 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) |
62 : textfield_(parent), | 55 : textfield_(parent), |
63 ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))), | 56 ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))), |
64 text_border_(new FocusableBorder()), | 57 text_border_(new FocusableBorder()), |
65 text_offset_(0), | |
66 insert_(true), | |
67 is_cursor_visible_(false), | 58 is_cursor_visible_(false), |
| 59 is_drop_cursor_visible_(false), |
68 skip_input_method_cancel_composition_(false), | 60 skip_input_method_cancel_composition_(false), |
69 initiating_drag_(false), | 61 initiating_drag_(false), |
70 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), | 62 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), |
71 aggregated_clicks_(0), | 63 aggregated_clicks_(0), |
72 last_click_time_(base::Time::FromInternalValue(0)), | 64 last_click_time_(base::Time::FromInternalValue(0)), |
73 last_click_location_(0, 0) { | 65 last_click_location_(0, 0) { |
74 set_border(text_border_); | 66 set_border(text_border_); |
75 | 67 |
76 // Lowercase is not supported. | 68 // Lowercase is not supported. |
77 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE); | 69 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE); |
78 | 70 |
| 71 // Set the default text style. |
| 72 gfx::StyleRange default_style; |
| 73 default_style.font = textfield_->font(); |
| 74 default_style.foreground = textfield_->text_color(); |
| 75 GetRenderText()->set_default_style(default_style); |
| 76 |
79 set_context_menu_controller(this); | 77 set_context_menu_controller(this); |
80 set_drag_controller(this); | 78 set_drag_controller(this); |
81 } | 79 } |
82 | 80 |
83 NativeTextfieldViews::~NativeTextfieldViews() { | 81 NativeTextfieldViews::~NativeTextfieldViews() { |
84 } | 82 } |
85 | 83 |
86 //////////////////////////////////////////////////////////////////////////////// | 84 //////////////////////////////////////////////////////////////////////////////// |
87 // NativeTextfieldViews, View overrides: | 85 // NativeTextfieldViews, View overrides: |
88 | 86 |
89 bool NativeTextfieldViews::OnMousePressed(const MouseEvent& event) { | 87 bool NativeTextfieldViews::OnMousePressed(const MouseEvent& event) { |
90 OnBeforeUserAction(); | 88 OnBeforeUserAction(); |
91 textfield_->RequestFocus(); | 89 textfield_->RequestFocus(); |
92 | 90 |
93 if (event.IsOnlyLeftMouseButton()) { | 91 if (event.IsOnlyLeftMouseButton()) { |
94 base::TimeDelta time_delta = event.time_stamp() - last_click_time_; | 92 base::TimeDelta time_delta = event.time_stamp() - last_click_time_; |
95 gfx::Point location_delta = event.location().Subtract(last_click_location_); | 93 gfx::Point location_delta = event.location().Subtract(last_click_location_); |
96 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() && | 94 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() && |
97 !ExceededDragThreshold(location_delta.x(), location_delta.y())) { | 95 !ExceededDragThreshold(location_delta.x(), location_delta.y())) { |
98 aggregated_clicks_ = (aggregated_clicks_ + 1) % 3; | 96 aggregated_clicks_ = (aggregated_clicks_ + 1) % 3; |
99 } else { | 97 } else { |
100 aggregated_clicks_ = 0; | 98 aggregated_clicks_ = 0; |
101 } | 99 } |
102 last_click_time_ = event.time_stamp(); | 100 last_click_time_ = event.time_stamp(); |
103 last_click_location_ = event.location(); | 101 last_click_location_ = event.location(); |
104 | 102 |
105 initiating_drag_ = false; | 103 initiating_drag_ = false; |
106 switch(aggregated_clicks_) { | 104 switch(aggregated_clicks_) { |
107 case 0: | 105 case 0: |
108 if (!IsPointInSelection(event.location())) | 106 if (!GetRenderText()->IsPointInSelection(event.location())) |
109 MoveCursorTo(event.location(), event.IsShiftDown()); | 107 MoveCursorTo(event.location(), event.IsShiftDown()); |
110 else | 108 else |
111 initiating_drag_ = true; | 109 initiating_drag_ = true; |
112 break; | 110 break; |
113 case 1: | 111 case 1: |
114 model_->SelectWord(); | 112 model_->SelectWord(); |
115 break; | 113 break; |
116 case 2: | 114 case 2: |
117 model_->SelectAll(); | 115 model_->SelectAll(); |
118 break; | 116 break; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 return true; | 167 return true; |
170 } | 168 } |
171 | 169 |
172 bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) { | 170 bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) { |
173 return textfield_->IsEnabled() && !textfield_->read_only() && | 171 return textfield_->IsEnabled() && !textfield_->read_only() && |
174 data.HasString(); | 172 data.HasString(); |
175 } | 173 } |
176 | 174 |
177 int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) { | 175 int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) { |
178 DCHECK(CanDrop(event.data())); | 176 DCHECK(CanDrop(event.data())); |
179 bool is_point_in_selection = IsPointInSelection(event.location()); | 177 bool in_selection = GetRenderText()->IsPointInSelection(event.location()); |
180 is_drop_cursor_visible_ = !is_point_in_selection; | 178 is_drop_cursor_visible_ = !in_selection; |
181 // TODO(msw): Pan over text when the user drags to the visible text edge. | 179 // TODO(msw): Pan over text when the user drags to the visible text edge. |
182 UpdateCursorBoundsAndTextOffset(FindCursorPosition(event.location()), true); | 180 OnCaretBoundsChanged(); |
183 SchedulePaint(); | 181 SchedulePaint(); |
184 | 182 |
185 if (initiating_drag_) { | 183 if (initiating_drag_) { |
186 if (is_point_in_selection) | 184 if (in_selection) |
187 return ui::DragDropTypes::DRAG_NONE; | 185 return ui::DragDropTypes::DRAG_NONE; |
188 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : | 186 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : |
189 ui::DragDropTypes::DRAG_MOVE; | 187 ui::DragDropTypes::DRAG_MOVE; |
190 } | 188 } |
191 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; | 189 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; |
192 } | 190 } |
193 | 191 |
194 int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) { | 192 int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) { |
195 DCHECK(CanDrop(event.data())); | 193 DCHECK(CanDrop(event.data())); |
196 DCHECK(!initiating_drag_ || !IsPointInSelection(event.location())); | 194 DCHECK(!initiating_drag_ || |
| 195 !GetRenderText()->IsPointInSelection(event.location())); |
197 OnBeforeUserAction(); | 196 OnBeforeUserAction(); |
198 skip_input_method_cancel_composition_ = true; | 197 skip_input_method_cancel_composition_ = true; |
199 | 198 |
200 size_t drop_destination = FindCursorPosition(event.location()); | 199 // TODO(msw): Remove final reference to FindCursorPosition. |
| 200 size_t drop_destination = |
| 201 GetRenderText()->FindCursorPosition(event.location()); |
201 string16 text; | 202 string16 text; |
202 event.data().GetString(&text); | 203 event.data().GetString(&text); |
203 | 204 |
204 // We'll delete the current selection for a drag and drop within this view. | 205 // We'll delete the current selection for a drag and drop within this view. |
205 bool move = initiating_drag_ && !event.IsControlDown() && | 206 bool move = initiating_drag_ && !event.IsControlDown() && |
206 event.source_operations() & ui::DragDropTypes::DRAG_MOVE; | 207 event.source_operations() & ui::DragDropTypes::DRAG_MOVE; |
207 if (move) { | 208 if (move) { |
208 ui::Range selected_range; | 209 ui::Range selected_range; |
209 model_->GetSelectedRange(&selected_range); | 210 model_->GetSelectedRange(&selected_range); |
210 // Adjust the drop destination if it is on or after the current selection. | 211 // Adjust the drop destination if it is on or after the current selection. |
211 if (selected_range.GetMax() <= drop_destination) | 212 if (selected_range.GetMax() <= drop_destination) |
212 drop_destination -= selected_range.length(); | 213 drop_destination -= selected_range.length(); |
213 else if (selected_range.GetMin() <= drop_destination) | 214 else if (selected_range.GetMin() <= drop_destination) |
214 drop_destination = selected_range.GetMin(); | 215 drop_destination = selected_range.GetMin(); |
215 model_->DeleteSelectionAndInsertTextAt(text, drop_destination); | 216 model_->DeleteSelectionAndInsertTextAt(text, drop_destination); |
216 } else { | 217 } else { |
217 model_->MoveCursorTo(drop_destination, false); | 218 model_->MoveCursorTo(drop_destination, false); |
218 // Drop always inserts a text even if insert_ == false. | 219 // Drop always inserts text even if the textfield is not in insert mode. |
219 model_->InsertText(text); | 220 model_->InsertText(text); |
220 } | 221 } |
221 skip_input_method_cancel_composition_ = false; | 222 skip_input_method_cancel_composition_ = false; |
222 UpdateAfterChange(true, true); | 223 UpdateAfterChange(true, true); |
223 OnAfterUserAction(); | 224 OnAfterUserAction(); |
224 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; | 225 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; |
225 } | 226 } |
226 | 227 |
227 void NativeTextfieldViews::OnDragDone() { | 228 void NativeTextfieldViews::OnDragDone() { |
228 initiating_drag_ = false; | 229 initiating_drag_ = false; |
(...skipping 10 matching lines...) Expand all Loading... |
239 | 240 |
240 void NativeTextfieldViews::OnFocus() { | 241 void NativeTextfieldViews::OnFocus() { |
241 NOTREACHED(); | 242 NOTREACHED(); |
242 } | 243 } |
243 | 244 |
244 void NativeTextfieldViews::OnBlur() { | 245 void NativeTextfieldViews::OnBlur() { |
245 NOTREACHED(); | 246 NOTREACHED(); |
246 } | 247 } |
247 | 248 |
248 gfx::NativeCursor NativeTextfieldViews::GetCursor(const MouseEvent& event) { | 249 gfx::NativeCursor NativeTextfieldViews::GetCursor(const MouseEvent& event) { |
249 bool text = !initiating_drag_ && (event.type() == ui::ET_MOUSE_DRAGGED || | 250 bool in_selection = GetRenderText()->IsPointInSelection(event.location()); |
250 !IsPointInSelection(event.location())); | 251 bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED; |
| 252 bool text_cursor = !initiating_drag_ && (drag_event || !in_selection); |
251 #if defined(OS_WIN) | 253 #if defined(OS_WIN) |
252 static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM); | 254 static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM); |
253 static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); | 255 static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); |
254 return text ? ibeam : arrow; | 256 return text_cursor ? ibeam : arrow; |
255 #else | 257 #else |
256 return text ? gfx::GetCursor(GDK_XTERM) : NULL; | 258 return text_cursor ? gfx::GetCursor(GDK_XTERM) : NULL; |
257 #endif | 259 #endif |
258 } | 260 } |
259 | 261 |
260 ///////////////////////////////////////////////////////////////// | 262 ///////////////////////////////////////////////////////////////// |
261 // NativeTextfieldViews, ContextMenuController overrides: | 263 // NativeTextfieldViews, ContextMenuController overrides: |
262 void NativeTextfieldViews::ShowContextMenuForView(View* source, | 264 void NativeTextfieldViews::ShowContextMenuForView(View* source, |
263 const gfx::Point& p, | 265 const gfx::Point& p, |
264 bool is_mouse_gesture) { | 266 bool is_mouse_gesture) { |
265 UpdateContextMenu(); | 267 UpdateContextMenu(); |
266 context_menu_menu_->RunMenuAt(GetWidget()->GetNativeWindow(), | 268 context_menu_menu_->RunMenuAt(GetWidget()->GetNativeWindow(), |
267 NULL, | 269 NULL, |
268 gfx::Rect(p, gfx::Size()), | 270 gfx::Rect(p, gfx::Size()), |
269 views::MenuItemView::TOPLEFT, | 271 views::MenuItemView::TOPLEFT, |
270 true); | 272 true); |
271 } | 273 } |
272 | 274 |
273 ///////////////////////////////////////////////////////////////// | 275 ///////////////////////////////////////////////////////////////// |
274 // NativeTextfieldViews, views::DragController overrides: | 276 // NativeTextfieldViews, views::DragController overrides: |
275 void NativeTextfieldViews::WriteDragDataForView(views::View* sender, | 277 void NativeTextfieldViews::WriteDragDataForView(views::View* sender, |
276 const gfx::Point& press_pt, | 278 const gfx::Point& press_pt, |
277 OSExchangeData* data) { | 279 OSExchangeData* data) { |
278 DCHECK_NE(ui::DragDropTypes::DRAG_NONE, | 280 DCHECK_NE(ui::DragDropTypes::DRAG_NONE, |
279 GetDragOperationsForView(sender, press_pt)); | 281 GetDragOperationsForView(sender, press_pt)); |
280 data->SetString(GetSelectedText()); | 282 data->SetString(GetSelectedText()); |
281 } | 283 } |
282 | 284 |
283 int NativeTextfieldViews::GetDragOperationsForView(views::View* sender, | 285 int NativeTextfieldViews::GetDragOperationsForView(views::View* sender, |
284 const gfx::Point& p) { | 286 const gfx::Point& p) { |
285 if (!textfield_->IsEnabled() || !IsPointInSelection(p)) | 287 if (!textfield_->IsEnabled() || !GetRenderText()->IsPointInSelection(p)) |
286 return ui::DragDropTypes::DRAG_NONE; | 288 return ui::DragDropTypes::DRAG_NONE; |
287 if (sender == this && !textfield_->read_only()) | 289 if (sender == this && !textfield_->read_only()) |
288 return ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY; | 290 return ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY; |
289 return ui::DragDropTypes::DRAG_COPY; | 291 return ui::DragDropTypes::DRAG_COPY; |
290 } | 292 } |
291 | 293 |
292 bool NativeTextfieldViews::CanStartDragForView(View* sender, | 294 bool NativeTextfieldViews::CanStartDragForView(View* sender, |
293 const gfx::Point& press_pt, | 295 const gfx::Point& press_pt, |
294 const gfx::Point& p) { | 296 const gfx::Point& p) { |
295 return IsPointInSelection(press_pt); | 297 return GetRenderText()->IsPointInSelection(press_pt); |
296 } | 298 } |
297 | 299 |
298 ///////////////////////////////////////////////////////////////// | 300 ///////////////////////////////////////////////////////////////// |
299 // NativeTextfieldViews, NativeTextifieldWrapper overrides: | 301 // NativeTextfieldViews, NativeTextifieldWrapper overrides: |
300 | 302 |
301 string16 NativeTextfieldViews::GetText() const { | 303 string16 NativeTextfieldViews::GetText() const { |
302 return model_->text(); | 304 return model_->GetText(); |
303 } | 305 } |
304 | 306 |
305 void NativeTextfieldViews::UpdateText() { | 307 void NativeTextfieldViews::UpdateText() { |
306 model_->SetText(textfield_->text()); | 308 model_->SetText(textfield_->text()); |
307 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 309 OnCaretBoundsChanged(); |
308 SchedulePaint(); | 310 SchedulePaint(); |
309 } | 311 } |
310 | 312 |
311 void NativeTextfieldViews::AppendText(const string16& text) { | 313 void NativeTextfieldViews::AppendText(const string16& text) { |
312 if (text.empty()) | 314 if (text.empty()) |
313 return; | 315 return; |
314 model_->Append(text); | 316 model_->Append(text); |
315 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 317 OnCaretBoundsChanged(); |
316 SchedulePaint(); | 318 SchedulePaint(); |
317 } | 319 } |
318 | 320 |
319 string16 NativeTextfieldViews::GetSelectedText() const { | 321 string16 NativeTextfieldViews::GetSelectedText() const { |
320 return model_->GetSelectedText(); | 322 return model_->GetSelectedText(); |
321 } | 323 } |
322 | 324 |
323 void NativeTextfieldViews::SelectAll() { | 325 void NativeTextfieldViews::SelectAll() { |
324 model_->SelectAll(); | 326 model_->SelectAll(); |
325 SchedulePaint(); | 327 SchedulePaint(); |
(...skipping 20 matching lines...) Expand all Loading... |
346 } | 348 } |
347 | 349 |
348 void NativeTextfieldViews::UpdateBackgroundColor() { | 350 void NativeTextfieldViews::UpdateBackgroundColor() { |
349 // TODO(oshima): Background has to match the border's shape. | 351 // TODO(oshima): Background has to match the border's shape. |
350 set_background( | 352 set_background( |
351 Background::CreateSolidBackground(textfield_->background_color())); | 353 Background::CreateSolidBackground(textfield_->background_color())); |
352 SchedulePaint(); | 354 SchedulePaint(); |
353 } | 355 } |
354 | 356 |
355 void NativeTextfieldViews::UpdateReadOnly() { | 357 void NativeTextfieldViews::UpdateReadOnly() { |
| 358 // Update the default text style. |
| 359 gfx::StyleRange default_style(GetRenderText()->get_default_style()); |
| 360 default_style.foreground = textfield_->read_only() ? kReadonlyTextColor : |
| 361 textfield_->text_color(); |
| 362 GetRenderText()->set_default_style(default_style); |
| 363 GetRenderText()->ApplyDefaultStyle(); |
| 364 |
356 SchedulePaint(); | 365 SchedulePaint(); |
357 OnTextInputTypeChanged(); | 366 OnTextInputTypeChanged(); |
358 } | 367 } |
359 | 368 |
360 void NativeTextfieldViews::UpdateFont() { | 369 void NativeTextfieldViews::UpdateFont() { |
361 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 370 OnCaretBoundsChanged(); |
362 } | 371 } |
363 | 372 |
364 void NativeTextfieldViews::UpdateIsPassword() { | 373 void NativeTextfieldViews::UpdateIsPassword() { |
365 model_->set_is_password(textfield_->IsPassword()); | 374 model_->set_is_password(textfield_->IsPassword()); |
366 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 375 OnCaretBoundsChanged(); |
367 SchedulePaint(); | 376 SchedulePaint(); |
368 OnTextInputTypeChanged(); | 377 OnTextInputTypeChanged(); |
369 } | 378 } |
370 | 379 |
371 void NativeTextfieldViews::UpdateEnabled() { | 380 void NativeTextfieldViews::UpdateEnabled() { |
372 SetEnabled(textfield_->IsEnabled()); | 381 SetEnabled(textfield_->IsEnabled()); |
373 SchedulePaint(); | 382 SchedulePaint(); |
374 OnTextInputTypeChanged(); | 383 OnTextInputTypeChanged(); |
375 } | 384 } |
376 | 385 |
377 gfx::Insets NativeTextfieldViews::CalculateInsets() { | 386 gfx::Insets NativeTextfieldViews::CalculateInsets() { |
378 return GetInsets(); | 387 return GetInsets(); |
379 } | 388 } |
380 | 389 |
381 void NativeTextfieldViews::UpdateHorizontalMargins() { | 390 void NativeTextfieldViews::UpdateHorizontalMargins() { |
382 int left, right; | 391 int left, right; |
383 if (!textfield_->GetHorizontalMargins(&left, &right)) | 392 if (!textfield_->GetHorizontalMargins(&left, &right)) |
384 return; | 393 return; |
385 gfx::Insets inset = GetInsets(); | 394 gfx::Insets inset = GetInsets(); |
386 | 395 |
387 text_border_->SetInsets(inset.top(), left, inset.bottom(), right); | 396 text_border_->SetInsets(inset.top(), left, inset.bottom(), right); |
388 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 397 OnCaretBoundsChanged(); |
389 } | 398 } |
390 | 399 |
391 void NativeTextfieldViews::UpdateVerticalMargins() { | 400 void NativeTextfieldViews::UpdateVerticalMargins() { |
392 int top, bottom; | 401 int top, bottom; |
393 if (!textfield_->GetVerticalMargins(&top, &bottom)) | 402 if (!textfield_->GetVerticalMargins(&top, &bottom)) |
394 return; | 403 return; |
395 gfx::Insets inset = GetInsets(); | 404 gfx::Insets inset = GetInsets(); |
396 | |
397 text_border_->SetInsets(top, inset.left(), bottom, inset.right()); | 405 text_border_->SetInsets(top, inset.left(), bottom, inset.right()); |
398 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 406 OnCaretBoundsChanged(); |
399 } | 407 } |
400 | 408 |
401 bool NativeTextfieldViews::SetFocus() { | 409 bool NativeTextfieldViews::SetFocus() { |
402 return false; | 410 return false; |
403 } | 411 } |
404 | 412 |
405 View* NativeTextfieldViews::GetView() { | 413 View* NativeTextfieldViews::GetView() { |
406 return this; | 414 return this; |
407 } | 415 } |
408 | 416 |
409 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { | 417 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { |
410 NOTREACHED(); | 418 NOTREACHED(); |
411 return NULL; | 419 return NULL; |
412 } | 420 } |
413 | 421 |
414 bool NativeTextfieldViews::IsIMEComposing() const { | 422 bool NativeTextfieldViews::IsIMEComposing() const { |
415 return model_->HasCompositionText(); | 423 return model_->HasCompositionText(); |
416 } | 424 } |
417 | 425 |
418 void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { | 426 void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { |
419 model_->GetSelectedRange(range); | 427 model_->GetSelectedRange(range); |
420 } | 428 } |
421 | 429 |
422 void NativeTextfieldViews::SelectRange(const ui::Range& range) { | 430 void NativeTextfieldViews::SelectRange(const ui::Range& range) { |
423 model_->SelectRange(range); | 431 model_->SelectRange(range); |
424 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 432 OnCaretBoundsChanged(); |
425 SchedulePaint(); | 433 SchedulePaint(); |
426 } | 434 } |
427 | 435 |
428 size_t NativeTextfieldViews::GetCursorPosition() const { | 436 size_t NativeTextfieldViews::GetCursorPosition() const { |
429 return model_->cursor_pos(); | 437 return model_->GetCursorPosition(); |
430 } | 438 } |
431 | 439 |
432 bool NativeTextfieldViews::HandleKeyPressed(const KeyEvent& e) { | 440 bool NativeTextfieldViews::HandleKeyPressed(const KeyEvent& e) { |
433 TextfieldController* controller = textfield_->GetController(); | 441 TextfieldController* controller = textfield_->GetController(); |
434 bool handled = false; | 442 bool handled = false; |
435 if (controller) | 443 if (controller) |
436 handled = controller->HandleKeyEvent(textfield_, e); | 444 handled = controller->HandleKeyEvent(textfield_, e); |
437 return handled || HandleKeyEvent(e); | 445 return handled || HandleKeyEvent(e); |
438 } | 446 } |
439 | 447 |
440 bool NativeTextfieldViews::HandleKeyReleased(const KeyEvent& e) { | 448 bool NativeTextfieldViews::HandleKeyReleased(const KeyEvent& e) { |
441 return true; | 449 return true; |
442 } | 450 } |
443 | 451 |
444 void NativeTextfieldViews::HandleFocus() { | 452 void NativeTextfieldViews::HandleFocus() { |
| 453 GetRenderText()->set_focused(true); |
445 is_cursor_visible_ = true; | 454 is_cursor_visible_ = true; |
446 SchedulePaint(); | 455 SchedulePaint(); |
447 OnCaretBoundsChanged(); | 456 OnCaretBoundsChanged(); |
448 // Start blinking cursor. | 457 // Start blinking cursor. |
449 MessageLoop::current()->PostDelayedTask( | 458 MessageLoop::current()->PostDelayedTask( |
450 FROM_HERE, | 459 FROM_HERE, |
451 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), | 460 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), |
452 kCursorVisibleTimeMs); | 461 kCursorVisibleTimeMs); |
453 } | 462 } |
454 | 463 |
455 void NativeTextfieldViews::HandleBlur() { | 464 void NativeTextfieldViews::HandleBlur() { |
| 465 GetRenderText()->set_focused(false); |
456 // Stop blinking cursor. | 466 // Stop blinking cursor. |
457 cursor_timer_.RevokeAll(); | 467 cursor_timer_.RevokeAll(); |
458 if (is_cursor_visible_) { | 468 if (is_cursor_visible_) { |
459 is_cursor_visible_ = false; | 469 is_cursor_visible_ = false; |
460 RepaintCursor(); | 470 RepaintCursor(); |
461 } | 471 } |
462 } | 472 } |
463 | 473 |
464 TextInputClient* NativeTextfieldViews::GetTextInputClient() { | 474 TextInputClient* NativeTextfieldViews::GetTextInputClient() { |
465 return textfield_->read_only() ? NULL : this; | 475 return textfield_->read_only() ? NULL : this; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
529 default: | 539 default: |
530 NOTREACHED() << "unknown command: " << command_id; | 540 NOTREACHED() << "unknown command: " << command_id; |
531 break; | 541 break; |
532 } | 542 } |
533 | 543 |
534 // The cursor must have changed if text changed during cut/paste/delete. | 544 // The cursor must have changed if text changed during cut/paste/delete. |
535 UpdateAfterChange(text_changed, text_changed); | 545 UpdateAfterChange(text_changed, text_changed); |
536 OnAfterUserAction(); | 546 OnAfterUserAction(); |
537 } | 547 } |
538 | 548 |
539 TextStyle* NativeTextfieldViews::CreateTextStyle() { | 549 void NativeTextfieldViews::ApplyStyleRange(const gfx::StyleRange& style) { |
540 return model_->CreateTextStyle(); | 550 GetRenderText()->ApplyStyleRange(style); |
541 } | |
542 | |
543 void NativeTextfieldViews::ApplyTextStyle(const TextStyle* style, | |
544 const ui::Range& range) { | |
545 model_->ApplyTextStyle(style, range); | |
546 SchedulePaint(); | 551 SchedulePaint(); |
547 } | 552 } |
548 | 553 |
549 void NativeTextfieldViews::ClearAllTextStyles() { | 554 void NativeTextfieldViews::ApplyDefaultStyle() { |
550 model_->ClearAllTextStyles(); | 555 GetRenderText()->ApplyDefaultStyle(); |
551 SchedulePaint(); | 556 SchedulePaint(); |
552 } | 557 } |
553 | 558 |
554 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { | 559 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
555 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 560 // Set the RenderText display area. |
| 561 gfx::Insets insets = GetInsets(); |
| 562 gfx::Rect display_rect(insets.left(), |
| 563 insets.top(), |
| 564 width() - insets.width(), |
| 565 height() - insets.height()); |
| 566 GetRenderText()->set_display_rect(display_rect); |
| 567 OnCaretBoundsChanged(); |
556 } | 568 } |
557 | 569 |
558 /////////////////////////////////////////////////////////////////////////////// | 570 /////////////////////////////////////////////////////////////////////////////// |
559 // NativeTextfieldViews, TextInputClient implementation, private: | 571 // NativeTextfieldViews, TextInputClient implementation, private: |
560 | 572 |
561 void NativeTextfieldViews::SetCompositionText( | 573 void NativeTextfieldViews::SetCompositionText( |
562 const ui::CompositionText& composition) { | 574 const ui::CompositionText& composition) { |
563 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) | 575 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
564 return; | 576 return; |
565 | 577 |
(...skipping 29 matching lines...) Expand all Loading... |
595 OnAfterUserAction(); | 607 OnAfterUserAction(); |
596 } | 608 } |
597 | 609 |
598 void NativeTextfieldViews::InsertText(const string16& text) { | 610 void NativeTextfieldViews::InsertText(const string16& text) { |
599 // TODO(suzhe): Filter invalid characters. | 611 // TODO(suzhe): Filter invalid characters. |
600 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) | 612 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) |
601 return; | 613 return; |
602 | 614 |
603 OnBeforeUserAction(); | 615 OnBeforeUserAction(); |
604 skip_input_method_cancel_composition_ = true; | 616 skip_input_method_cancel_composition_ = true; |
605 if (insert_) | 617 if (GetRenderText()->get_insert_mode()) |
606 model_->InsertText(text); | 618 model_->InsertText(text); |
607 else | 619 else |
608 model_->ReplaceText(text); | 620 model_->ReplaceText(text); |
609 skip_input_method_cancel_composition_ = false; | 621 skip_input_method_cancel_composition_ = false; |
610 UpdateAfterChange(true, true); | 622 UpdateAfterChange(true, true); |
611 OnAfterUserAction(); | 623 OnAfterUserAction(); |
612 } | 624 } |
613 | 625 |
614 void NativeTextfieldViews::InsertChar(char16 ch, int flags) { | 626 void NativeTextfieldViews::InsertChar(char16 ch, int flags) { |
615 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || | 627 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || |
616 !ShouldInsertChar(ch, flags)) { | 628 !ShouldInsertChar(ch, flags)) { |
617 return; | 629 return; |
618 } | 630 } |
619 | 631 |
620 OnBeforeUserAction(); | 632 OnBeforeUserAction(); |
621 skip_input_method_cancel_composition_ = true; | 633 skip_input_method_cancel_composition_ = true; |
622 if (insert_) | 634 if (GetRenderText()->get_insert_mode()) |
623 model_->InsertChar(ch); | 635 model_->InsertChar(ch); |
624 else | 636 else |
625 model_->ReplaceChar(ch); | 637 model_->ReplaceChar(ch); |
626 skip_input_method_cancel_composition_ = false; | 638 skip_input_method_cancel_composition_ = false; |
627 UpdateAfterChange(true, true); | 639 UpdateAfterChange(true, true); |
628 OnAfterUserAction(); | 640 OnAfterUserAction(); |
629 } | 641 } |
630 | 642 |
631 ui::TextInputType NativeTextfieldViews::GetTextInputType() { | 643 ui::TextInputType NativeTextfieldViews::GetTextInputType() { |
632 if (textfield_->read_only() || !textfield_->IsEnabled()) | 644 if (textfield_->read_only() || !textfield_->IsEnabled()) |
633 return ui::TEXT_INPUT_TYPE_NONE; | 645 return ui::TEXT_INPUT_TYPE_NONE; |
634 else if (textfield_->IsPassword()) | 646 else if (textfield_->IsPassword()) |
635 return ui::TEXT_INPUT_TYPE_PASSWORD; | 647 return ui::TEXT_INPUT_TYPE_PASSWORD; |
636 return ui::TEXT_INPUT_TYPE_TEXT; | 648 return ui::TEXT_INPUT_TYPE_TEXT; |
637 } | 649 } |
638 | 650 |
639 gfx::Rect NativeTextfieldViews::GetCaretBounds() { | 651 gfx::Rect NativeTextfieldViews::GetCaretBounds() { |
640 return cursor_bounds_; | 652 gfx::RenderText* render_text = GetRenderText(); |
| 653 return render_text->GetCursorBounds(render_text->GetCursor(), |
| 654 render_text->get_insert_mode()); |
641 } | 655 } |
642 | 656 |
643 bool NativeTextfieldViews::HasCompositionText() { | 657 bool NativeTextfieldViews::HasCompositionText() { |
644 return model_->HasCompositionText(); | 658 return model_->HasCompositionText(); |
645 } | 659 } |
646 | 660 |
647 bool NativeTextfieldViews::GetTextRange(ui::Range* range) { | 661 bool NativeTextfieldViews::GetTextRange(ui::Range* range) { |
648 // We don't allow the input method to retrieve or delete content from a | 662 // We don't allow the input method to retrieve or delete content from a |
649 // password box. | 663 // password box. |
650 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) | 664 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
718 return textfield_; | 732 return textfield_; |
719 } | 733 } |
720 | 734 |
721 void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { | 735 void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { |
722 if (skip_input_method_cancel_composition_) | 736 if (skip_input_method_cancel_composition_) |
723 return; | 737 return; |
724 DCHECK(textfield_->GetInputMethod()); | 738 DCHECK(textfield_->GetInputMethod()); |
725 textfield_->GetInputMethod()->CancelComposition(textfield_); | 739 textfield_->GetInputMethod()->CancelComposition(textfield_); |
726 } | 740 } |
727 | 741 |
728 const gfx::Font& NativeTextfieldViews::GetFont() const { | 742 gfx::RenderText* NativeTextfieldViews::GetRenderText() const { |
729 return textfield_->font(); | 743 return model_->get_render_text(); |
730 } | |
731 | |
732 SkColor NativeTextfieldViews::GetTextColor() const { | |
733 return textfield_->text_color(); | |
734 } | 744 } |
735 | 745 |
736 void NativeTextfieldViews::UpdateCursor() { | 746 void NativeTextfieldViews::UpdateCursor() { |
737 is_cursor_visible_ = !is_cursor_visible_; | 747 is_cursor_visible_ = !is_cursor_visible_; |
738 RepaintCursor(); | 748 RepaintCursor(); |
739 MessageLoop::current()->PostDelayedTask( | 749 MessageLoop::current()->PostDelayedTask( |
740 FROM_HERE, | 750 FROM_HERE, |
741 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), | 751 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), |
742 is_cursor_visible_ ? kCursorVisibleTimeMs : kCursorInvisibleTimeMs); | 752 is_cursor_visible_ ? kCursorVisibleTimeMs : kCursorInvisibleTimeMs); |
743 } | 753 } |
744 | 754 |
745 void NativeTextfieldViews::RepaintCursor() { | 755 void NativeTextfieldViews::RepaintCursor() { |
746 gfx::Rect r = cursor_bounds_; | 756 gfx::Rect r(GetCaretBounds()); |
747 r.Inset(-1, -1, -1, -1); | 757 r.Inset(-1, -1, -1, -1); |
748 SchedulePaintInRect(r); | 758 SchedulePaintInRect(r); |
749 } | 759 } |
750 | 760 |
751 gfx::Rect NativeTextfieldViews::GetCursorBounds(size_t cursor_pos, | |
752 bool insert_mode) const { | |
753 string16 text = model_->GetVisibleText(); | |
754 const gfx::Font& font = GetFont(); | |
755 int x = font.GetStringWidth(text.substr(0U, cursor_pos)); | |
756 DCHECK_GE(x, 0); | |
757 int h = std::min(height() - GetInsets().height(), font.GetHeight()); | |
758 gfx::Rect bounds(x, (height() - h) / 2, 0, h); | |
759 if (!insert_mode && text.length() != cursor_pos) | |
760 bounds.set_width(font.GetStringWidth(text.substr(0, cursor_pos + 1)) - x); | |
761 return bounds; | |
762 } | |
763 | |
764 | |
765 void NativeTextfieldViews::UpdateCursorBoundsAndTextOffset(size_t cursor_pos, | |
766 bool insert_mode) { | |
767 if (bounds().IsEmpty()) | |
768 return; | |
769 | |
770 // TODO(oshima): bidi | |
771 int width = bounds().width() - GetInsets().width(); | |
772 int full_width = GetFont().GetStringWidth(model_->GetVisibleText()); | |
773 cursor_bounds_ = GetCursorBounds(cursor_pos, insert_mode); | |
774 | |
775 if (full_width < width) { | |
776 // Show all text whenever the text fits to the size. | |
777 text_offset_ = 0; | |
778 } else if ((text_offset_ + cursor_bounds_.right()) > width) { | |
779 // when the cursor overflows to the right | |
780 text_offset_ = width - cursor_bounds_.right(); | |
781 } else if ((text_offset_ + cursor_bounds_.x()) < 0) { | |
782 // when the cursor overflows to the left | |
783 text_offset_ = -cursor_bounds_.x(); | |
784 } else if (full_width > width && text_offset_ + full_width < width) { | |
785 // when the cursor moves within the textfield with the text | |
786 // longer than the field. | |
787 text_offset_ = width - full_width; | |
788 } else { | |
789 // move cursor freely. | |
790 } | |
791 // shift cursor bounds to fit insets. | |
792 cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + GetInsets().left()); | |
793 | |
794 OnCaretBoundsChanged(); | |
795 } | |
796 | |
797 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { | 761 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { |
798 gfx::Insets insets = GetInsets(); | |
799 | |
800 canvas->Save(); | 762 canvas->Save(); |
801 canvas->ClipRectInt(insets.left(), insets.top(), | 763 GetRenderText()->set_cursor_visible(is_drop_cursor_visible_ || |
802 width() - insets.width(), height() - insets.height()); | 764 (is_cursor_visible_ && !model_->HasSelection())); |
803 | 765 // Draw the text, cursor, and selection. |
804 // TODO(oshima): bidi support | 766 GetRenderText()->Draw(canvas); |
805 // TODO(varunjain): re-implement this so only that dirty text is painted. | |
806 TextfieldViewsModel::TextFragments fragments; | |
807 model_->GetFragments(&fragments); | |
808 int x_offset = text_offset_ + insets.left(); | |
809 int y = insets.top(); | |
810 int text_height = height() - insets.height(); | |
811 SkColor selection_color = | |
812 textfield_->HasFocus() ? | |
813 kFocusedSelectionColor : kUnfocusedSelectionColor; | |
814 gfx::Font font = GetFont(); | |
815 gfx::Rect selection_bounds = model_->GetSelectionBounds(font); | |
816 | |
817 if (!selection_bounds.IsEmpty()) { | |
818 canvas->FillRectInt(selection_color, | |
819 x_offset + selection_bounds.x(), | |
820 (height() - selection_bounds.height()) / 2, | |
821 selection_bounds.width(), | |
822 selection_bounds.height()); | |
823 } | |
824 | |
825 for (TextfieldViewsModel::TextFragments::const_iterator iter = | |
826 fragments.begin(); | |
827 iter != fragments.end(); | |
828 iter++) { | |
829 string16 text = model_->GetVisibleText(iter->range.start(), | |
830 iter->range.end()); | |
831 // TODO(oshima): This does not give the accurate position due to | |
832 // kerning. Figure out how to do. | |
833 int width = font.GetStringWidth(text); | |
834 iter->style->DrawString(canvas, text, font, textfield_->read_only(), | |
835 x_offset, y, width, text_height); | |
836 x_offset += width; | |
837 } | |
838 canvas->Restore(); | 767 canvas->Restore(); |
839 | |
840 // Paint cursor. Replace cursor is drawn as rectangle for now. | |
841 if (textfield_->IsEnabled() && (is_drop_cursor_visible_ || | |
842 (is_cursor_visible_ && !model_->HasSelection()))) | |
843 canvas->DrawRectInt(kCursorColor, | |
844 cursor_bounds_.x(), | |
845 cursor_bounds_.y(), | |
846 cursor_bounds_.width(), | |
847 cursor_bounds_.height()); | |
848 } | 768 } |
849 | 769 |
850 bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { | 770 bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { |
851 // TODO(oshima): Refactor and consolidate with ExecuteCommand. | 771 // TODO(oshima): Refactor and consolidate with ExecuteCommand. |
852 if (key_event.type() == ui::ET_KEY_PRESSED) { | 772 if (key_event.type() == ui::ET_KEY_PRESSED) { |
853 ui::KeyboardCode key_code = key_event.key_code(); | 773 ui::KeyboardCode key_code = key_event.key_code(); |
854 // TODO(oshima): shift-tab does not work. Figure out why and fix. | 774 // TODO(oshima): shift-tab does not work. Figure out why and fix. |
855 if (key_code == ui::VKEY_TAB) | 775 if (key_code == ui::VKEY_TAB) |
856 return false; | 776 return false; |
857 | 777 |
(...skipping 24 matching lines...) Expand all Loading... |
882 break; | 802 break; |
883 case ui::VKEY_C: | 803 case ui::VKEY_C: |
884 if (control) | 804 if (control) |
885 model_->Copy(); | 805 model_->Copy(); |
886 break; | 806 break; |
887 case ui::VKEY_V: | 807 case ui::VKEY_V: |
888 if (control && editable) | 808 if (control && editable) |
889 cursor_changed = text_changed = Paste(); | 809 cursor_changed = text_changed = Paste(); |
890 break; | 810 break; |
891 case ui::VKEY_RIGHT: | 811 case ui::VKEY_RIGHT: |
892 control ? model_->MoveCursorToNextWord(selection) | 812 model_->MoveCursorRight( |
893 : model_->MoveCursorRight(selection); | 813 control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, selection); |
894 cursor_changed = true; | 814 cursor_changed = true; |
895 break; | 815 break; |
896 case ui::VKEY_LEFT: | 816 case ui::VKEY_LEFT: |
897 control ? model_->MoveCursorToPreviousWord(selection) | 817 model_->MoveCursorLeft( |
898 : model_->MoveCursorLeft(selection); | 818 control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, selection); |
899 cursor_changed = true; | 819 cursor_changed = true; |
900 break; | 820 break; |
901 case ui::VKEY_END: | 821 case ui::VKEY_END: |
902 model_->MoveCursorToEnd(selection); | 822 model_->MoveCursorRight(gfx::LINE_BREAK, selection); |
903 cursor_changed = true; | 823 cursor_changed = true; |
904 break; | 824 break; |
905 case ui::VKEY_HOME: | 825 case ui::VKEY_HOME: |
906 model_->MoveCursorToHome(selection); | 826 model_->MoveCursorLeft(gfx::LINE_BREAK, selection); |
907 cursor_changed = true; | 827 cursor_changed = true; |
908 break; | 828 break; |
909 case ui::VKEY_BACK: | 829 case ui::VKEY_BACK: |
910 if (!editable) | 830 if (!editable) |
911 break; | 831 break; |
912 if (!model_->HasSelection()) { | 832 if (!model_->HasSelection()) { |
913 if (selection && control) { | 833 if (selection && control) { |
914 // If both shift and control are pressed, then erase upto the | 834 // If both shift and control are pressed, then erase upto the |
915 // beginning of the buffer in ChromeOS. In windows, do nothing. | 835 // beginning of the buffer in ChromeOS. In windows, do nothing. |
916 #if defined(OS_WIN) | 836 #if defined(OS_WIN) |
917 break; | 837 break; |
918 #else | 838 #else |
919 model_->MoveCursorToHome(true); | 839 model_->MoveCursorLeft(gfx::LINE_BREAK, true); |
920 #endif | 840 #endif |
921 } else if (control) { | 841 } else if (control) { |
922 // If only control is pressed, then erase the previous word. | 842 // If only control is pressed, then erase the previous word. |
923 model_->MoveCursorToPreviousWord(true); | 843 model_->MoveCursorLeft(gfx::WORD_BREAK, true); |
924 } | 844 } |
925 } | 845 } |
926 text_changed = model_->Backspace(); | 846 text_changed = model_->Backspace(); |
927 cursor_changed = true; | 847 cursor_changed = true; |
928 break; | 848 break; |
929 case ui::VKEY_DELETE: | 849 case ui::VKEY_DELETE: |
930 if (!editable) | 850 if (!editable) |
931 break; | 851 break; |
932 if (!model_->HasSelection()) { | 852 if (!model_->HasSelection()) { |
933 if (selection && control) { | 853 if (selection && control) { |
934 // If both shift and control are pressed, then erase upto the | 854 // If both shift and control are pressed, then erase upto the |
935 // end of the buffer in ChromeOS. In windows, do nothing. | 855 // end of the buffer in ChromeOS. In windows, do nothing. |
936 #if defined(OS_WIN) | 856 #if defined(OS_WIN) |
937 break; | 857 break; |
938 #else | 858 #else |
939 model_->MoveCursorToEnd(true); | 859 model_->MoveCursorRight(gfx::LINE_BREAK, true); |
940 #endif | 860 #endif |
941 } else if (control) { | 861 } else if (control) { |
942 // If only control is pressed, then erase the next word. | 862 // If only control is pressed, then erase the next word. |
943 model_->MoveCursorToNextWord(true); | 863 model_->MoveCursorRight(gfx::WORD_BREAK, true); |
944 } | 864 } |
945 } | 865 } |
946 cursor_changed = text_changed = model_->Delete(); | 866 cursor_changed = text_changed = model_->Delete(); |
947 break; | 867 break; |
948 case ui::VKEY_INSERT: | 868 case ui::VKEY_INSERT: |
949 insert_ = !insert_; | 869 GetRenderText()->toggle_insert_mode(); |
950 cursor_changed = true; | 870 cursor_changed = true; |
951 break; | 871 break; |
952 default: | 872 default: |
953 break; | 873 break; |
954 } | 874 } |
955 | 875 |
956 // We must have input method in order to support text input. | 876 // We must have input method in order to support text input. |
957 DCHECK(textfield_->GetInputMethod()); | 877 DCHECK(textfield_->GetInputMethod()); |
958 | 878 |
959 UpdateAfterChange(text_changed, cursor_changed); | 879 UpdateAfterChange(text_changed, cursor_changed); |
960 OnAfterUserAction(); | 880 OnAfterUserAction(); |
961 return (text_changed || cursor_changed); | 881 return (text_changed || cursor_changed); |
962 } | 882 } |
963 return false; | 883 return false; |
964 } | 884 } |
965 | 885 |
966 size_t NativeTextfieldViews::FindCursorPosition(const gfx::Point& point) const { | |
967 // TODO(oshima): BIDI/i18n support. | |
968 gfx::Font font = GetFont(); | |
969 gfx::Insets insets = GetInsets(); | |
970 string16 text = model_->GetVisibleText(); | |
971 int left = 0; | |
972 int left_pos = 0; | |
973 int right = font.GetStringWidth(text); | |
974 int right_pos = text.length(); | |
975 | |
976 int x = point.x() - insets.left() - text_offset_; | |
977 if (x <= left) return left_pos; | |
978 if (x >= right) return right_pos; | |
979 // binary searching the cursor position. | |
980 // TODO(oshima): use the center of character instead of edge. | |
981 // Binary search may not work for language like arabic. | |
982 while (std::abs(static_cast<long>(right_pos - left_pos) > 1)) { | |
983 int pivot_pos = left_pos + (right_pos - left_pos) / 2; | |
984 int pivot = font.GetStringWidth(text.substr(0, pivot_pos)); | |
985 if (pivot < x) { | |
986 left = pivot; | |
987 left_pos = pivot_pos; | |
988 } else if (pivot == x) { | |
989 return pivot_pos; | |
990 } else { | |
991 right = pivot; | |
992 right_pos = pivot_pos; | |
993 } | |
994 } | |
995 return left_pos; | |
996 } | |
997 | |
998 bool NativeTextfieldViews::IsPointInSelection(const gfx::Point& point) const { | |
999 ui::Range range; | |
1000 GetSelectedRange(&range); | |
1001 size_t pos = FindCursorPosition(point); | |
1002 return (pos >= range.GetMin() && pos < range.GetMax()); | |
1003 } | |
1004 | |
1005 bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) { | 886 bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) { |
1006 size_t pos = FindCursorPosition(point); | 887 if (!model_->MoveCursorTo(point, select)) |
1007 if (model_->MoveCursorTo(pos, select)) { | 888 return false; |
1008 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 889 OnCaretBoundsChanged(); |
1009 return true; | 890 return true; |
1010 } | |
1011 return false; | |
1012 } | 891 } |
1013 | 892 |
1014 void NativeTextfieldViews::PropagateTextChange() { | 893 void NativeTextfieldViews::PropagateTextChange() { |
1015 textfield_->SyncText(); | 894 textfield_->SyncText(); |
1016 } | 895 } |
1017 | 896 |
1018 void NativeTextfieldViews::UpdateAfterChange(bool text_changed, | 897 void NativeTextfieldViews::UpdateAfterChange(bool text_changed, |
1019 bool cursor_changed) { | 898 bool cursor_changed) { |
1020 if (text_changed) | 899 if (text_changed) |
1021 PropagateTextChange(); | 900 PropagateTextChange(); |
1022 if (cursor_changed) { | 901 if (cursor_changed) { |
1023 is_cursor_visible_ = true; | 902 is_cursor_visible_ = true; |
1024 RepaintCursor(); | 903 RepaintCursor(); |
1025 } | 904 } |
1026 if (text_changed || cursor_changed) { | 905 if (text_changed || cursor_changed) { |
1027 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); | 906 OnCaretBoundsChanged(); |
1028 SchedulePaint(); | 907 SchedulePaint(); |
1029 } | 908 } |
1030 } | 909 } |
1031 | 910 |
1032 void NativeTextfieldViews::UpdateContextMenu() { | 911 void NativeTextfieldViews::UpdateContextMenu() { |
1033 if (!context_menu_contents_.get()) { | 912 if (!context_menu_contents_.get()) { |
1034 context_menu_contents_.reset(new ui::SimpleMenuModel(this)); | 913 context_menu_contents_.reset(new ui::SimpleMenuModel(this)); |
1035 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); | 914 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); |
1036 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); | 915 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); |
1037 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); | 916 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1071 TextfieldController* controller = textfield_->GetController(); | 950 TextfieldController* controller = textfield_->GetController(); |
1072 if (controller) | 951 if (controller) |
1073 controller->OnAfterUserAction(textfield_); | 952 controller->OnAfterUserAction(textfield_); |
1074 } | 953 } |
1075 | 954 |
1076 bool NativeTextfieldViews::Paste() { | 955 bool NativeTextfieldViews::Paste() { |
1077 const bool success = model_->Paste(); | 956 const bool success = model_->Paste(); |
1078 | 957 |
1079 // Calls TextfieldController::ContentsChanged() explicitly if the paste action | 958 // Calls TextfieldController::ContentsChanged() explicitly if the paste action |
1080 // did not change the content at all. See http://crbug.com/79002 | 959 // did not change the content at all. See http://crbug.com/79002 |
1081 if (success && model_->text() == textfield_->text()) { | 960 if (success && GetText() == textfield_->text()) { |
1082 TextfieldController* controller = textfield_->GetController(); | 961 TextfieldController* controller = textfield_->GetController(); |
1083 if (controller) | 962 if (controller) |
1084 controller->ContentsChanged(textfield_, textfield_->text()); | 963 controller->ContentsChanged(textfield_, textfield_->text()); |
1085 } | 964 } |
1086 return success; | 965 return success; |
1087 } | 966 } |
1088 | 967 |
1089 // static | 968 // static |
1090 bool NativeTextfieldViews::ShouldInsertChar(char16 ch, int flags) { | 969 bool NativeTextfieldViews::ShouldInsertChar(char16 ch, int flags) { |
1091 // Filter out all control characters, including tab and new line characters, | 970 // Filter out all control characters, including tab and new line characters, |
1092 // and all characters with Alt modifier. But we need to allow characters with | 971 // and all characters with Alt modifier. But we need to allow characters with |
1093 // AltGr modifier. | 972 // AltGr modifier. |
1094 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different | 973 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different |
1095 // flag that we don't care about. | 974 // flag that we don't care about. |
1096 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && | 975 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && |
1097 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN; | 976 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN; |
1098 } | 977 } |
1099 | 978 |
1100 } // namespace views | 979 } // namespace views |
OLD | NEW |