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/app_strings.h" | 13 #include "grit/app_strings.h" |
14 #include "ui/base/clipboard/clipboard.h" | 14 #include "ui/base/clipboard/clipboard.h" |
15 #include "ui/base/range/range.h" | 15 #include "ui/base/range/range.h" |
16 #include "ui/gfx/canvas.h" | 16 #include "ui/gfx/canvas.h" |
17 #include "ui/gfx/canvas_skia.h" | 17 #include "ui/gfx/canvas_skia.h" |
18 #include "ui/gfx/insets.h" | 18 #include "ui/gfx/insets.h" |
19 #include "views/background.h" | 19 #include "views/background.h" |
20 #include "views/border.h" | 20 #include "views/border.h" |
21 #include "views/controls/menu/menu_2.h" | 21 #include "views/controls/menu/menu_2.h" |
22 #include "views/controls/textfield/textfield.h" | 22 #include "views/controls/textfield/textfield.h" |
23 #include "views/controls/textfield/textfield_controller.h" | 23 #include "views/controls/textfield/textfield_controller.h" |
24 #include "views/controls/textfield/textfield_views_model.h" | 24 #include "views/controls/textfield/textfield_views_model.h" |
25 #include "views/events/event.h" | 25 #include "views/events/event.h" |
| 26 #include "views/ime/input_method.h" |
26 #include "views/metrics.h" | 27 #include "views/metrics.h" |
27 #include "views/views_delegate.h" | 28 #include "views/views_delegate.h" |
28 | 29 |
29 #if defined(OS_LINUX) | 30 #if defined(OS_LINUX) |
30 #include "ui/gfx/gtk_util.h" | 31 #include "ui/gfx/gtk_util.h" |
31 #endif | 32 #endif |
32 | 33 |
33 namespace { | 34 namespace { |
34 | 35 |
35 // A global flag to switch the Textfield wrapper to TextfieldViews. | 36 // A global flag to switch the Textfield wrapper to TextfieldViews. |
(...skipping 18 matching lines...) Expand all Loading... |
54 const char kEnableViewsBasedTextfieldSwitch[] = "enable-textfield-views"; | 55 const char kEnableViewsBasedTextfieldSwitch[] = "enable-textfield-views"; |
55 } // namespace | 56 } // namespace |
56 | 57 |
57 namespace views { | 58 namespace views { |
58 | 59 |
59 const char NativeTextfieldViews::kViewClassName[] = | 60 const char NativeTextfieldViews::kViewClassName[] = |
60 "views/NativeTextfieldViews"; | 61 "views/NativeTextfieldViews"; |
61 | 62 |
62 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) | 63 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) |
63 : textfield_(parent), | 64 : textfield_(parent), |
64 model_(new TextfieldViewsModel()), | 65 ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))), |
65 text_border_(new TextfieldBorder()), | 66 text_border_(new TextfieldBorder()), |
66 text_offset_(0), | 67 text_offset_(0), |
67 insert_(true), | 68 insert_(true), |
68 is_cursor_visible_(false), | 69 is_cursor_visible_(false), |
| 70 skip_input_method_cancel_composition_(false), |
69 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), | 71 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), |
70 last_mouse_press_time_(base::Time::FromInternalValue(0)), | 72 last_mouse_press_time_(base::Time::FromInternalValue(0)), |
71 click_state_(NONE) { | 73 click_state_(NONE) { |
72 set_border(text_border_); | 74 set_border(text_border_); |
73 | 75 |
74 // Multiline is not supported. | 76 // Multiline is not supported. |
75 DCHECK_NE(parent->style(), Textfield::STYLE_MULTILINE); | 77 DCHECK_NE(parent->style(), Textfield::STYLE_MULTILINE); |
76 // Lowercase is not supported. | 78 // Lowercase is not supported. |
77 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE); | 79 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE); |
78 | 80 |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 } | 155 } |
154 | 156 |
155 ///////////////////////////////////////////////////////////////// | 157 ///////////////////////////////////////////////////////////////// |
156 // NativeTextfieldViews, NativeTextifieldWrapper overrides: | 158 // NativeTextfieldViews, NativeTextifieldWrapper overrides: |
157 | 159 |
158 string16 NativeTextfieldViews::GetText() const { | 160 string16 NativeTextfieldViews::GetText() const { |
159 return model_->text(); | 161 return model_->text(); |
160 } | 162 } |
161 | 163 |
162 void NativeTextfieldViews::UpdateText() { | 164 void NativeTextfieldViews::UpdateText() { |
163 bool changed = model_->SetText(textfield_->text()); | 165 model_->SetText(textfield_->text()); |
164 UpdateCursorBoundsAndTextOffset(); | 166 UpdateCursorBoundsAndTextOffset(); |
165 SchedulePaint(); | 167 SchedulePaint(); |
166 if (changed) { | |
167 TextfieldController* controller = textfield_->GetController(); | |
168 if (controller) | |
169 controller->ContentsChanged(textfield_, GetText()); | |
170 } | |
171 } | 168 } |
172 | 169 |
173 void NativeTextfieldViews::AppendText(const string16& text) { | 170 void NativeTextfieldViews::AppendText(const string16& text) { |
174 if (text.empty()) | 171 if (text.empty()) |
175 return; | 172 return; |
176 model_->Append(text); | 173 model_->Append(text); |
177 UpdateCursorBoundsAndTextOffset(); | 174 UpdateCursorBoundsAndTextOffset(); |
178 SchedulePaint(); | 175 SchedulePaint(); |
179 | |
180 TextfieldController* controller = textfield_->GetController(); | |
181 if (controller) | |
182 controller->ContentsChanged(textfield_, GetText()); | |
183 } | 176 } |
184 | 177 |
185 string16 NativeTextfieldViews::GetSelectedText() const { | 178 string16 NativeTextfieldViews::GetSelectedText() const { |
186 return model_->GetSelectedText(); | 179 return model_->GetSelectedText(); |
187 } | 180 } |
188 | 181 |
189 void NativeTextfieldViews::SelectAll() { | 182 void NativeTextfieldViews::SelectAll() { |
190 model_->SelectAll(); | 183 model_->SelectAll(); |
191 SchedulePaint(); | 184 SchedulePaint(); |
192 } | 185 } |
(...skipping 20 matching lines...) Expand all Loading... |
213 | 206 |
214 void NativeTextfieldViews::UpdateBackgroundColor() { | 207 void NativeTextfieldViews::UpdateBackgroundColor() { |
215 // TODO(oshima): Background has to match the border's shape. | 208 // TODO(oshima): Background has to match the border's shape. |
216 set_background( | 209 set_background( |
217 Background::CreateSolidBackground(textfield_->background_color())); | 210 Background::CreateSolidBackground(textfield_->background_color())); |
218 SchedulePaint(); | 211 SchedulePaint(); |
219 } | 212 } |
220 | 213 |
221 void NativeTextfieldViews::UpdateReadOnly() { | 214 void NativeTextfieldViews::UpdateReadOnly() { |
222 SchedulePaint(); | 215 SchedulePaint(); |
| 216 OnTextInputTypeChanged(); |
223 } | 217 } |
224 | 218 |
225 void NativeTextfieldViews::UpdateFont() { | 219 void NativeTextfieldViews::UpdateFont() { |
226 UpdateCursorBoundsAndTextOffset(); | 220 UpdateCursorBoundsAndTextOffset(); |
227 } | 221 } |
228 | 222 |
229 void NativeTextfieldViews::UpdateIsPassword() { | 223 void NativeTextfieldViews::UpdateIsPassword() { |
230 model_->set_is_password(textfield_->IsPassword()); | 224 model_->set_is_password(textfield_->IsPassword()); |
231 UpdateCursorBoundsAndTextOffset(); | 225 UpdateCursorBoundsAndTextOffset(); |
232 SchedulePaint(); | 226 SchedulePaint(); |
| 227 OnTextInputTypeChanged(); |
233 } | 228 } |
234 | 229 |
235 void NativeTextfieldViews::UpdateEnabled() { | 230 void NativeTextfieldViews::UpdateEnabled() { |
236 SetEnabled(textfield_->IsEnabled()); | 231 SetEnabled(textfield_->IsEnabled()); |
237 SchedulePaint(); | 232 SchedulePaint(); |
| 233 OnTextInputTypeChanged(); |
238 } | 234 } |
239 | 235 |
240 gfx::Insets NativeTextfieldViews::CalculateInsets() { | 236 gfx::Insets NativeTextfieldViews::CalculateInsets() { |
241 return GetInsets(); | 237 return GetInsets(); |
242 } | 238 } |
243 | 239 |
244 void NativeTextfieldViews::UpdateHorizontalMargins() { | 240 void NativeTextfieldViews::UpdateHorizontalMargins() { |
245 int left, right; | 241 int left, right; |
246 if (!textfield_->GetHorizontalMargins(&left, &right)) | 242 if (!textfield_->GetHorizontalMargins(&left, &right)) |
247 return; | 243 return; |
(...skipping 20 matching lines...) Expand all Loading... |
268 View* NativeTextfieldViews::GetView() { | 264 View* NativeTextfieldViews::GetView() { |
269 return this; | 265 return this; |
270 } | 266 } |
271 | 267 |
272 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { | 268 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { |
273 NOTREACHED(); | 269 NOTREACHED(); |
274 return NULL; | 270 return NULL; |
275 } | 271 } |
276 | 272 |
277 bool NativeTextfieldViews::IsIMEComposing() const { | 273 bool NativeTextfieldViews::IsIMEComposing() const { |
278 return false; | 274 return model_->HasCompositionText(); |
279 } | 275 } |
280 | 276 |
281 void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { | 277 void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { |
282 model_->GetSelectedRange(range); | 278 model_->GetSelectedRange(range); |
283 } | 279 } |
284 | 280 |
285 void NativeTextfieldViews::SelectRange(const ui::Range& range) { | 281 void NativeTextfieldViews::SelectRange(const ui::Range& range) { |
286 model_->SelectRange(range); | 282 model_->SelectRange(range); |
287 UpdateCursorBoundsAndTextOffset(); | 283 UpdateCursorBoundsAndTextOffset(); |
288 SchedulePaint(); | 284 SchedulePaint(); |
(...skipping 11 matching lines...) Expand all Loading... |
300 return handled || HandleKeyEvent(e); | 296 return handled || HandleKeyEvent(e); |
301 } | 297 } |
302 | 298 |
303 bool NativeTextfieldViews::HandleKeyReleased(const views::KeyEvent& e) { | 299 bool NativeTextfieldViews::HandleKeyReleased(const views::KeyEvent& e) { |
304 return true; | 300 return true; |
305 } | 301 } |
306 | 302 |
307 void NativeTextfieldViews::HandleFocus() { | 303 void NativeTextfieldViews::HandleFocus() { |
308 is_cursor_visible_ = true; | 304 is_cursor_visible_ = true; |
309 SchedulePaint(); | 305 SchedulePaint(); |
| 306 OnCaretBoundsChanged(); |
310 // Start blinking cursor. | 307 // Start blinking cursor. |
311 MessageLoop::current()->PostDelayedTask( | 308 MessageLoop::current()->PostDelayedTask( |
312 FROM_HERE, | 309 FROM_HERE, |
313 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), | 310 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), |
314 kCursorVisibleTimeMs); | 311 kCursorVisibleTimeMs); |
315 } | 312 } |
316 | 313 |
317 void NativeTextfieldViews::HandleBlur() { | 314 void NativeTextfieldViews::HandleBlur() { |
318 // Stop blinking cursor. | 315 // Stop blinking cursor. |
319 cursor_timer_.RevokeAll(); | 316 cursor_timer_.RevokeAll(); |
320 if (is_cursor_visible_) { | 317 if (is_cursor_visible_) { |
321 is_cursor_visible_ = false; | 318 is_cursor_visible_ = false; |
322 RepaintCursor(); | 319 RepaintCursor(); |
323 } | 320 } |
324 } | 321 } |
325 | 322 |
| 323 TextInputClient* NativeTextfieldViews::GetTextInputClient() { |
| 324 return textfield_->read_only() ? NULL : this; |
| 325 } |
| 326 |
326 ///////////////////////////////////////////////////////////////// | 327 ///////////////////////////////////////////////////////////////// |
327 // NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides: | 328 // NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides: |
328 | 329 |
329 bool NativeTextfieldViews::IsCommandIdChecked(int command_id) const { | 330 bool NativeTextfieldViews::IsCommandIdChecked(int command_id) const { |
330 return true; | 331 return true; |
331 } | 332 } |
332 | 333 |
333 bool NativeTextfieldViews::IsCommandIdEnabled(int command_id) const { | 334 bool NativeTextfieldViews::IsCommandIdEnabled(int command_id) const { |
334 bool editable = !textfield_->read_only(); | 335 bool editable = !textfield_->read_only(); |
335 string16 result; | 336 string16 result; |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 void NativeTextfieldViews::SetEnableTextfieldViews(bool enabled) { | 406 void NativeTextfieldViews::SetEnableTextfieldViews(bool enabled) { |
406 textfield_view_enabled = enabled; | 407 textfield_view_enabled = enabled; |
407 } | 408 } |
408 | 409 |
409 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { | 410 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
410 UpdateCursorBoundsAndTextOffset(); | 411 UpdateCursorBoundsAndTextOffset(); |
411 } | 412 } |
412 | 413 |
413 | 414 |
414 /////////////////////////////////////////////////////////////////////////////// | 415 /////////////////////////////////////////////////////////////////////////////// |
415 // NativeTextfieldViews private: | 416 // NativeTextfieldViews, TextInputClient implementation, private: |
| 417 |
| 418 void NativeTextfieldViews::SetCompositionText( |
| 419 const ui::CompositionText& composition) { |
| 420 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| 421 return; |
| 422 |
| 423 OnBeforeUserAction(); |
| 424 skip_input_method_cancel_composition_ = true; |
| 425 model_->SetCompositionText(composition); |
| 426 skip_input_method_cancel_composition_ = false; |
| 427 UpdateAfterChange(true, true); |
| 428 OnAfterUserAction(); |
| 429 } |
| 430 |
| 431 void NativeTextfieldViews::ConfirmCompositionText() { |
| 432 if (!model_->HasCompositionText()) |
| 433 return; |
| 434 |
| 435 OnBeforeUserAction(); |
| 436 skip_input_method_cancel_composition_ = true; |
| 437 model_->ConfirmCompositionText(); |
| 438 skip_input_method_cancel_composition_ = false; |
| 439 UpdateAfterChange(true, true); |
| 440 OnAfterUserAction(); |
| 441 } |
| 442 |
| 443 void NativeTextfieldViews::ClearCompositionText() { |
| 444 if (!model_->HasCompositionText()) |
| 445 return; |
| 446 |
| 447 OnBeforeUserAction(); |
| 448 skip_input_method_cancel_composition_ = true; |
| 449 model_->ClearCompositionText(); |
| 450 skip_input_method_cancel_composition_ = false; |
| 451 UpdateAfterChange(true, true); |
| 452 OnAfterUserAction(); |
| 453 } |
| 454 |
| 455 void NativeTextfieldViews::InsertText(const string16& text) { |
| 456 // TODO(suzhe): Filter invalid characters. |
| 457 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) |
| 458 return; |
| 459 |
| 460 OnBeforeUserAction(); |
| 461 skip_input_method_cancel_composition_ = true; |
| 462 if (insert_) |
| 463 model_->InsertText(text); |
| 464 else |
| 465 model_->ReplaceText(text); |
| 466 skip_input_method_cancel_composition_ = false; |
| 467 UpdateAfterChange(true, true); |
| 468 OnAfterUserAction(); |
| 469 } |
| 470 |
| 471 void NativeTextfieldViews::InsertChar(char16 ch, int flags) { |
| 472 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || |
| 473 !ShouldInsertChar(ch, flags)) { |
| 474 return; |
| 475 } |
| 476 |
| 477 OnBeforeUserAction(); |
| 478 skip_input_method_cancel_composition_ = true; |
| 479 if (insert_) |
| 480 model_->InsertChar(ch); |
| 481 else |
| 482 model_->ReplaceChar(ch); |
| 483 skip_input_method_cancel_composition_ = false; |
| 484 UpdateAfterChange(true, true); |
| 485 OnAfterUserAction(); |
| 486 } |
| 487 |
| 488 ui::TextInputType NativeTextfieldViews::GetTextInputType() { |
| 489 if (textfield_->read_only() || !textfield_->IsEnabled()) |
| 490 return ui::TEXT_INPUT_TYPE_NONE; |
| 491 else if (textfield_->IsPassword()) |
| 492 return ui::TEXT_INPUT_TYPE_PASSWORD; |
| 493 return ui::TEXT_INPUT_TYPE_TEXT; |
| 494 } |
| 495 |
| 496 gfx::Rect NativeTextfieldViews::GetCaretBounds() { |
| 497 return cursor_bounds_; |
| 498 } |
| 499 |
| 500 bool NativeTextfieldViews::HasCompositionText() { |
| 501 return model_->HasCompositionText(); |
| 502 } |
| 503 |
| 504 bool NativeTextfieldViews::GetTextRange(ui::Range* range) { |
| 505 // We don't allow the input method to retrieve or delete content from a |
| 506 // password box. |
| 507 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) |
| 508 return false; |
| 509 |
| 510 model_->GetTextRange(range); |
| 511 return true; |
| 512 } |
| 513 |
| 514 bool NativeTextfieldViews::GetCompositionTextRange(ui::Range* range) { |
| 515 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) |
| 516 return false; |
| 517 |
| 518 model_->GetCompositionTextRange(range); |
| 519 return true; |
| 520 } |
| 521 |
| 522 bool NativeTextfieldViews::GetSelectionRange(ui::Range* range) { |
| 523 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) |
| 524 return false; |
| 525 |
| 526 model_->GetSelectedRange(range); |
| 527 return true; |
| 528 } |
| 529 |
| 530 bool NativeTextfieldViews::SetSelectionRange(const ui::Range& range) { |
| 531 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT || !range.IsValid()) |
| 532 return false; |
| 533 |
| 534 OnBeforeUserAction(); |
| 535 SelectRange(range); |
| 536 OnAfterUserAction(); |
| 537 return true; |
| 538 } |
| 539 |
| 540 bool NativeTextfieldViews::DeleteRange(const ui::Range& range) { |
| 541 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT || range.is_empty()) |
| 542 return false; |
| 543 |
| 544 OnBeforeUserAction(); |
| 545 model_->SelectRange(range); |
| 546 if (model_->HasSelection()) { |
| 547 model_->DeleteSelection(); |
| 548 UpdateAfterChange(true, true); |
| 549 } |
| 550 OnAfterUserAction(); |
| 551 return true; |
| 552 } |
| 553 |
| 554 bool NativeTextfieldViews::GetTextFromRange( |
| 555 const ui::Range& range, |
| 556 const base::Callback<void(const string16&)>& callback) { |
| 557 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT || range.is_empty()) |
| 558 return false; |
| 559 |
| 560 callback.Run(model_->GetTextFromRange(range)); |
| 561 return true; |
| 562 } |
| 563 |
| 564 void NativeTextfieldViews::OnInputMethodChanged() { |
| 565 NOTIMPLEMENTED(); |
| 566 } |
| 567 |
| 568 bool NativeTextfieldViews::ChangeTextDirectionAndLayoutAlignment( |
| 569 base::i18n::TextDirection direction) { |
| 570 NOTIMPLEMENTED(); |
| 571 return false; |
| 572 } |
| 573 |
| 574 View* NativeTextfieldViews::GetOwnerViewOfTextInputClient() { |
| 575 return textfield_; |
| 576 } |
| 577 |
| 578 void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { |
| 579 if (skip_input_method_cancel_composition_) |
| 580 return; |
| 581 DCHECK(textfield_->GetInputMethod()); |
| 582 textfield_->GetInputMethod()->CancelComposition(textfield_); |
| 583 } |
416 | 584 |
417 const gfx::Font& NativeTextfieldViews::GetFont() const { | 585 const gfx::Font& NativeTextfieldViews::GetFont() const { |
418 return textfield_->font(); | 586 return textfield_->font(); |
419 } | 587 } |
420 | 588 |
421 SkColor NativeTextfieldViews::GetTextColor() const { | 589 SkColor NativeTextfieldViews::GetTextColor() const { |
422 return textfield_->text_color(); | 590 return textfield_->text_color(); |
423 } | 591 } |
424 | 592 |
425 void NativeTextfieldViews::UpdateCursor() { | 593 void NativeTextfieldViews::UpdateCursor() { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
468 text_offset_ = -cursor_bounds_.x(); | 636 text_offset_ = -cursor_bounds_.x(); |
469 } else if (full_width > width && text_offset_ + full_width < width) { | 637 } else if (full_width > width && text_offset_ + full_width < width) { |
470 // when the cursor moves within the textfield with the text | 638 // when the cursor moves within the textfield with the text |
471 // longer than the field. | 639 // longer than the field. |
472 text_offset_ = width - full_width; | 640 text_offset_ = width - full_width; |
473 } else { | 641 } else { |
474 // move cursor freely. | 642 // move cursor freely. |
475 } | 643 } |
476 // shift cursor bounds to fit insets. | 644 // shift cursor bounds to fit insets. |
477 cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + insets.left()); | 645 cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + insets.left()); |
| 646 |
| 647 OnCaretBoundsChanged(); |
478 } | 648 } |
479 | 649 |
480 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { | 650 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { |
481 gfx::Insets insets = GetInsets(); | 651 gfx::Insets insets = GetInsets(); |
482 | 652 |
483 canvas->Save(); | 653 canvas->Save(); |
484 canvas->ClipRectInt(insets.left(), insets.top(), | 654 canvas->ClipRectInt(insets.left(), insets.top(), |
485 width() - insets.width(), height() - insets.height()); | 655 width() - insets.width(), height() - insets.height()); |
486 | 656 |
487 // TODO(oshima): bidi support | 657 // TODO(oshima): bidi support |
488 // TODO(varunjain): re-implement this so only that dirty text is painted. | 658 // TODO(varunjain): re-implement this so only that dirty text is painted. |
489 TextfieldViewsModel::TextFragments fragments; | 659 TextfieldViewsModel::TextFragments fragments; |
490 model_->GetFragments(&fragments); | 660 model_->GetFragments(&fragments); |
491 int x_offset = text_offset_ + insets.left(); | 661 int x_offset = text_offset_ + insets.left(); |
492 int y = insets.top(); | 662 int y = insets.top(); |
493 int text_height = height() - insets.height(); | 663 int text_height = height() - insets.height(); |
494 SkColor selection_color = | 664 SkColor selection_color = |
495 textfield_->HasFocus() ? | 665 textfield_->HasFocus() ? |
496 kFocusedSelectionColor : kUnfocusedSelectionColor; | 666 kFocusedSelectionColor : kUnfocusedSelectionColor; |
497 SkColor text_color = | 667 SkColor text_color = |
498 textfield_->read_only() ? kReadonlyTextColor : GetTextColor(); | 668 textfield_->read_only() ? kReadonlyTextColor : GetTextColor(); |
499 | 669 |
500 for (TextfieldViewsModel::TextFragments::const_iterator iter = | 670 for (TextfieldViewsModel::TextFragments::const_iterator iter = |
501 fragments.begin(); | 671 fragments.begin(); |
502 iter != fragments.end(); | 672 iter != fragments.end(); |
503 iter++) { | 673 iter++) { |
504 string16 text = model_->GetVisibleText((*iter).begin, (*iter).end); | 674 string16 text = model_->GetVisibleText(iter->start, iter->end); |
| 675 |
| 676 gfx::Font font = GetFont(); |
| 677 if (iter->underline) |
| 678 font = font.DeriveFont(0, font.GetStyle() | gfx::Font::UNDERLINED); |
| 679 |
505 // TODO(oshima): This does not give the accurate position due to | 680 // TODO(oshima): This does not give the accurate position due to |
506 // kerning. Figure out how webkit does this with skia. | 681 // kerning. Figure out how webkit does this with skia. |
507 int width = GetFont().GetStringWidth(text); | 682 int width = font.GetStringWidth(text); |
508 | 683 |
509 if ((*iter).selected) { | 684 if (iter->selected) { |
510 canvas->FillRectInt(selection_color, x_offset, y, width, text_height); | 685 canvas->FillRectInt(selection_color, x_offset, y, width, text_height); |
511 canvas->DrawStringInt(text, GetFont(), kSelectedTextColor, | 686 canvas->DrawStringInt(text, font, kSelectedTextColor, |
512 x_offset, y, width, text_height); | 687 x_offset, y, width, text_height); |
513 } else { | 688 } else { |
514 canvas->DrawStringInt(text, GetFont(), text_color, | 689 canvas->DrawStringInt(text, font, text_color, |
515 x_offset, y, width, text_height); | 690 x_offset, y, width, text_height); |
516 } | 691 } |
517 x_offset += width; | 692 x_offset += width; |
518 } | 693 } |
519 canvas->Restore(); | 694 canvas->Restore(); |
520 | 695 |
521 if (textfield_->IsEnabled() && is_cursor_visible_ && | 696 if (textfield_->IsEnabled() && is_cursor_visible_ && |
522 !model_->HasSelection()) { | 697 !model_->HasSelection()) { |
523 // Paint Cursor. Replace cursor is drawn as rectangle for now. | 698 // Paint Cursor. Replace cursor is drawn as rectangle for now. |
524 canvas->DrawRectInt(kCursorColor, | 699 canvas->DrawRectInt(kCursorColor, |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
570 case ui::VKEY_LEFT: | 745 case ui::VKEY_LEFT: |
571 control ? model_->MoveCursorToPreviousWord(selection) | 746 control ? model_->MoveCursorToPreviousWord(selection) |
572 : model_->MoveCursorLeft(selection); | 747 : model_->MoveCursorLeft(selection); |
573 cursor_changed = true; | 748 cursor_changed = true; |
574 break; | 749 break; |
575 case ui::VKEY_END: | 750 case ui::VKEY_END: |
576 model_->MoveCursorToEnd(selection); | 751 model_->MoveCursorToEnd(selection); |
577 cursor_changed = true; | 752 cursor_changed = true; |
578 break; | 753 break; |
579 case ui::VKEY_HOME: | 754 case ui::VKEY_HOME: |
580 model_->MoveCursorToStart(selection); | 755 model_->MoveCursorToHome(selection); |
581 cursor_changed = true; | 756 cursor_changed = true; |
582 break; | 757 break; |
583 case ui::VKEY_BACK: | 758 case ui::VKEY_BACK: |
584 if (!editable) | 759 if (!editable) |
585 break; | 760 break; |
586 if (!model_->HasSelection()) { | 761 if (!model_->HasSelection()) { |
587 if (selection && control) { | 762 if (selection && control) { |
588 // If both shift and control are pressed, then erase upto the | 763 // If both shift and control are pressed, then erase upto the |
589 // beginning of the buffer in ChromeOS. In windows, do nothing. | 764 // beginning of the buffer in ChromeOS. In windows, do nothing. |
590 #if defined(OS_WIN) | 765 #if defined(OS_WIN) |
591 break; | 766 break; |
592 #else | 767 #else |
593 model_->MoveCursorToStart(true); | 768 model_->MoveCursorToHome(true); |
594 #endif | 769 #endif |
595 } else if (control) { | 770 } else if (control) { |
596 // If only control is pressed, then erase the previous word. | 771 // If only control is pressed, then erase the previous word. |
597 model_->MoveCursorToPreviousWord(true); | 772 model_->MoveCursorToPreviousWord(true); |
598 } | 773 } |
599 } | 774 } |
600 text_changed = model_->Backspace(); | 775 text_changed = model_->Backspace(); |
601 cursor_changed = true; | 776 cursor_changed = true; |
602 break; | 777 break; |
603 case ui::VKEY_DELETE: | 778 case ui::VKEY_DELETE: |
(...skipping 15 matching lines...) Expand all Loading... |
619 } | 794 } |
620 cursor_changed = text_changed = model_->Delete(); | 795 cursor_changed = text_changed = model_->Delete(); |
621 break; | 796 break; |
622 case ui::VKEY_INSERT: | 797 case ui::VKEY_INSERT: |
623 insert_ = !insert_; | 798 insert_ = !insert_; |
624 cursor_changed = true; | 799 cursor_changed = true; |
625 break; | 800 break; |
626 default: | 801 default: |
627 break; | 802 break; |
628 } | 803 } |
629 char16 ch = key_event.GetCharacter(); | 804 |
630 if (editable && ShouldInsertChar(ch, key_event.flags())) { | 805 // We must have input method in order to support text input. |
631 if (insert_) | 806 DCHECK(textfield_->GetInputMethod()); |
632 model_->Insert(ch); | |
633 else | |
634 model_->Replace(ch); | |
635 text_changed = true; | |
636 } | |
637 | 807 |
638 UpdateAfterChange(text_changed, cursor_changed); | 808 UpdateAfterChange(text_changed, cursor_changed); |
639 OnAfterUserAction(); | 809 OnAfterUserAction(); |
640 return (text_changed || cursor_changed); | 810 return (text_changed || cursor_changed); |
641 } | 811 } |
642 return false; | 812 return false; |
643 } | 813 } |
644 | 814 |
645 size_t NativeTextfieldViews::FindCursorPosition(const gfx::Point& point) const { | 815 size_t NativeTextfieldViews::FindCursorPosition(const gfx::Point& point) const { |
646 // TODO(oshima): BIDI/i18n support. | 816 // TODO(oshima): BIDI/i18n support. |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
710 | 880 |
711 void NativeTextfieldViews::SetCursorForMouseClick(const views::MouseEvent& e) { | 881 void NativeTextfieldViews::SetCursorForMouseClick(const views::MouseEvent& e) { |
712 size_t pos = FindCursorPosition(e.location()); | 882 size_t pos = FindCursorPosition(e.location()); |
713 if (model_->MoveCursorTo(pos, false)) { | 883 if (model_->MoveCursorTo(pos, false)) { |
714 UpdateCursorBoundsAndTextOffset(); | 884 UpdateCursorBoundsAndTextOffset(); |
715 } | 885 } |
716 } | 886 } |
717 | 887 |
718 void NativeTextfieldViews::PropagateTextChange() { | 888 void NativeTextfieldViews::PropagateTextChange() { |
719 textfield_->SyncText(); | 889 textfield_->SyncText(); |
720 TextfieldController* controller = textfield_->GetController(); | |
721 if (controller) | |
722 controller->ContentsChanged(textfield_, GetText()); | |
723 } | 890 } |
724 | 891 |
725 void NativeTextfieldViews::UpdateAfterChange(bool text_changed, | 892 void NativeTextfieldViews::UpdateAfterChange(bool text_changed, |
726 bool cursor_changed) { | 893 bool cursor_changed) { |
727 if (text_changed) | 894 if (text_changed) |
728 PropagateTextChange(); | 895 PropagateTextChange(); |
729 if (cursor_changed) { | 896 if (cursor_changed) { |
730 is_cursor_visible_ = true; | 897 is_cursor_visible_ = true; |
731 RepaintCursor(); | 898 RepaintCursor(); |
732 } | 899 } |
(...skipping 10 matching lines...) Expand all Loading... |
743 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); | 910 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); |
744 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); | 911 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); |
745 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); | 912 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); |
746 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); | 913 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); |
747 context_menu_contents_->AddSeparator(); | 914 context_menu_contents_->AddSeparator(); |
748 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, | 915 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, |
749 IDS_APP_SELECT_ALL); | 916 IDS_APP_SELECT_ALL); |
750 context_menu_menu_.reset(new Menu2(context_menu_contents_.get())); | 917 context_menu_menu_.reset(new Menu2(context_menu_contents_.get())); |
751 } | 918 } |
752 | 919 |
| 920 void NativeTextfieldViews::OnTextInputTypeChanged() { |
| 921 DCHECK(textfield_->GetInputMethod()); |
| 922 textfield_->GetInputMethod()->OnTextInputTypeChanged(textfield_); |
| 923 } |
| 924 |
| 925 void NativeTextfieldViews::OnCaretBoundsChanged() { |
| 926 DCHECK(textfield_->GetInputMethod()); |
| 927 textfield_->GetInputMethod()->OnCaretBoundsChanged(textfield_); |
| 928 } |
| 929 |
753 void NativeTextfieldViews::OnBeforeUserAction() { | 930 void NativeTextfieldViews::OnBeforeUserAction() { |
754 TextfieldController* controller = textfield_->GetController(); | 931 TextfieldController* controller = textfield_->GetController(); |
755 if (controller) | 932 if (controller) |
756 controller->OnBeforeUserAction(textfield_); | 933 controller->OnBeforeUserAction(textfield_); |
757 } | 934 } |
758 | 935 |
759 void NativeTextfieldViews::OnAfterUserAction() { | 936 void NativeTextfieldViews::OnAfterUserAction() { |
760 TextfieldController* controller = textfield_->GetController(); | 937 TextfieldController* controller = textfield_->GetController(); |
761 if (controller) | 938 if (controller) |
762 controller->OnAfterUserAction(textfield_); | 939 controller->OnAfterUserAction(textfield_); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
821 } | 998 } |
822 | 999 |
823 void NativeTextfieldViews::TextfieldBorder::SetInsets(int top, | 1000 void NativeTextfieldViews::TextfieldBorder::SetInsets(int top, |
824 int left, | 1001 int left, |
825 int bottom, | 1002 int bottom, |
826 int right) { | 1003 int right) { |
827 insets_.Set(top, left, bottom, right); | 1004 insets_.Set(top, left, bottom, right); |
828 } | 1005 } |
829 | 1006 |
830 } // namespace views | 1007 } // namespace views |
OLD | NEW |