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 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
157 } | 159 } |
158 | 160 |
159 ///////////////////////////////////////////////////////////////// | 161 ///////////////////////////////////////////////////////////////// |
160 // NativeTextfieldViews, NativeTextifieldWrapper overrides: | 162 // NativeTextfieldViews, NativeTextifieldWrapper overrides: |
161 | 163 |
162 string16 NativeTextfieldViews::GetText() const { | 164 string16 NativeTextfieldViews::GetText() const { |
163 return model_->text(); | 165 return model_->text(); |
164 } | 166 } |
165 | 167 |
166 void NativeTextfieldViews::UpdateText() { | 168 void NativeTextfieldViews::UpdateText() { |
167 bool changed = model_->SetText(textfield_->text()); | 169 model_->SetText(textfield_->text()); |
168 UpdateCursorBoundsAndTextOffset(); | 170 UpdateCursorBoundsAndTextOffset(); |
169 SchedulePaint(); | 171 SchedulePaint(); |
170 if (changed) { | |
171 TextfieldController* controller = textfield_->GetController(); | |
172 if (controller) | |
173 controller->ContentsChanged(textfield_, GetText()); | |
174 } | |
175 } | 172 } |
176 | 173 |
177 void NativeTextfieldViews::AppendText(const string16& text) { | 174 void NativeTextfieldViews::AppendText(const string16& text) { |
178 if (text.empty()) | 175 if (text.empty()) |
179 return; | 176 return; |
180 model_->Append(text); | 177 model_->Append(text); |
181 UpdateCursorBoundsAndTextOffset(); | 178 UpdateCursorBoundsAndTextOffset(); |
182 SchedulePaint(); | 179 SchedulePaint(); |
183 | |
184 TextfieldController* controller = textfield_->GetController(); | |
185 if (controller) | |
186 controller->ContentsChanged(textfield_, GetText()); | |
187 } | 180 } |
188 | 181 |
189 string16 NativeTextfieldViews::GetSelectedText() const { | 182 string16 NativeTextfieldViews::GetSelectedText() const { |
190 return model_->GetSelectedText(); | 183 return model_->GetSelectedText(); |
191 } | 184 } |
192 | 185 |
193 void NativeTextfieldViews::SelectAll() { | 186 void NativeTextfieldViews::SelectAll() { |
194 model_->SelectAll(); | 187 model_->SelectAll(); |
195 SchedulePaint(); | 188 SchedulePaint(); |
196 } | 189 } |
(...skipping 20 matching lines...) Expand all Loading... | |
217 | 210 |
218 void NativeTextfieldViews::UpdateBackgroundColor() { | 211 void NativeTextfieldViews::UpdateBackgroundColor() { |
219 // TODO(oshima): Background has to match the border's shape. | 212 // TODO(oshima): Background has to match the border's shape. |
220 set_background( | 213 set_background( |
221 Background::CreateSolidBackground(textfield_->background_color())); | 214 Background::CreateSolidBackground(textfield_->background_color())); |
222 SchedulePaint(); | 215 SchedulePaint(); |
223 } | 216 } |
224 | 217 |
225 void NativeTextfieldViews::UpdateReadOnly() { | 218 void NativeTextfieldViews::UpdateReadOnly() { |
226 SchedulePaint(); | 219 SchedulePaint(); |
220 OnTextInputTypeChanged(); | |
227 } | 221 } |
228 | 222 |
229 void NativeTextfieldViews::UpdateFont() { | 223 void NativeTextfieldViews::UpdateFont() { |
230 UpdateCursorBoundsAndTextOffset(); | 224 UpdateCursorBoundsAndTextOffset(); |
231 } | 225 } |
232 | 226 |
233 void NativeTextfieldViews::UpdateIsPassword() { | 227 void NativeTextfieldViews::UpdateIsPassword() { |
234 model_->set_is_password(textfield_->IsPassword()); | 228 model_->set_is_password(textfield_->IsPassword()); |
235 UpdateCursorBoundsAndTextOffset(); | 229 UpdateCursorBoundsAndTextOffset(); |
236 SchedulePaint(); | 230 SchedulePaint(); |
231 OnTextInputTypeChanged(); | |
237 } | 232 } |
238 | 233 |
239 void NativeTextfieldViews::UpdateEnabled() { | 234 void NativeTextfieldViews::UpdateEnabled() { |
240 SetEnabled(textfield_->IsEnabled()); | 235 SetEnabled(textfield_->IsEnabled()); |
241 SchedulePaint(); | 236 SchedulePaint(); |
237 OnTextInputTypeChanged(); | |
242 } | 238 } |
243 | 239 |
244 gfx::Insets NativeTextfieldViews::CalculateInsets() { | 240 gfx::Insets NativeTextfieldViews::CalculateInsets() { |
245 return GetInsets(); | 241 return GetInsets(); |
246 } | 242 } |
247 | 243 |
248 void NativeTextfieldViews::UpdateHorizontalMargins() { | 244 void NativeTextfieldViews::UpdateHorizontalMargins() { |
249 int left, right; | 245 int left, right; |
250 if (!textfield_->GetHorizontalMargins(&left, &right)) | 246 if (!textfield_->GetHorizontalMargins(&left, &right)) |
251 return; | 247 return; |
(...skipping 20 matching lines...) Expand all Loading... | |
272 View* NativeTextfieldViews::GetView() { | 268 View* NativeTextfieldViews::GetView() { |
273 return this; | 269 return this; |
274 } | 270 } |
275 | 271 |
276 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { | 272 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { |
277 NOTREACHED(); | 273 NOTREACHED(); |
278 return NULL; | 274 return NULL; |
279 } | 275 } |
280 | 276 |
281 bool NativeTextfieldViews::IsIMEComposing() const { | 277 bool NativeTextfieldViews::IsIMEComposing() const { |
282 return false; | 278 return model_->HasCompositionText(); |
283 } | 279 } |
284 | 280 |
285 void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { | 281 void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { |
286 model_->GetSelectedRange(range); | 282 model_->GetSelectedRange(range); |
287 } | 283 } |
288 | 284 |
289 void NativeTextfieldViews::SelectRange(const ui::Range& range) { | 285 void NativeTextfieldViews::SelectRange(const ui::Range& range) { |
290 model_->SelectRange(range); | 286 model_->SelectRange(range); |
291 UpdateCursorBoundsAndTextOffset(); | 287 UpdateCursorBoundsAndTextOffset(); |
292 SchedulePaint(); | 288 SchedulePaint(); |
(...skipping 11 matching lines...) Expand all Loading... | |
304 return handled || HandleKeyEvent(e); | 300 return handled || HandleKeyEvent(e); |
305 } | 301 } |
306 | 302 |
307 bool NativeTextfieldViews::HandleKeyReleased(const views::KeyEvent& e) { | 303 bool NativeTextfieldViews::HandleKeyReleased(const views::KeyEvent& e) { |
308 return true; | 304 return true; |
309 } | 305 } |
310 | 306 |
311 void NativeTextfieldViews::HandleFocus() { | 307 void NativeTextfieldViews::HandleFocus() { |
312 is_cursor_visible_ = true; | 308 is_cursor_visible_ = true; |
313 SchedulePaint(); | 309 SchedulePaint(); |
310 OnCaretBoundsChanged(); | |
314 // Start blinking cursor. | 311 // Start blinking cursor. |
315 MessageLoop::current()->PostDelayedTask( | 312 MessageLoop::current()->PostDelayedTask( |
316 FROM_HERE, | 313 FROM_HERE, |
317 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), | 314 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), |
318 kCursorVisibleTimeMs); | 315 kCursorVisibleTimeMs); |
319 } | 316 } |
320 | 317 |
321 void NativeTextfieldViews::HandleBlur() { | 318 void NativeTextfieldViews::HandleBlur() { |
322 // Stop blinking cursor. | 319 // Stop blinking cursor. |
323 cursor_timer_.RevokeAll(); | 320 cursor_timer_.RevokeAll(); |
324 if (is_cursor_visible_) { | 321 if (is_cursor_visible_) { |
325 is_cursor_visible_ = false; | 322 is_cursor_visible_ = false; |
326 RepaintCursor(); | 323 RepaintCursor(); |
327 } | 324 } |
328 } | 325 } |
329 | 326 |
327 TextInputClient* NativeTextfieldViews::GetTextInputClient() { | |
328 return textfield_->read_only() ? NULL : this; | |
329 } | |
330 | |
330 ///////////////////////////////////////////////////////////////// | 331 ///////////////////////////////////////////////////////////////// |
331 // NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides: | 332 // NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides: |
332 | 333 |
333 bool NativeTextfieldViews::IsCommandIdChecked(int command_id) const { | 334 bool NativeTextfieldViews::IsCommandIdChecked(int command_id) const { |
334 return true; | 335 return true; |
335 } | 336 } |
336 | 337 |
337 bool NativeTextfieldViews::IsCommandIdEnabled(int command_id) const { | 338 bool NativeTextfieldViews::IsCommandIdEnabled(int command_id) const { |
338 bool editable = !textfield_->read_only(); | 339 bool editable = !textfield_->read_only(); |
339 string16 result; | 340 string16 result; |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
409 void NativeTextfieldViews::SetEnableTextfieldViews(bool enabled) { | 410 void NativeTextfieldViews::SetEnableTextfieldViews(bool enabled) { |
410 textfield_view_enabled = enabled; | 411 textfield_view_enabled = enabled; |
411 } | 412 } |
412 | 413 |
413 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { | 414 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
414 UpdateCursorBoundsAndTextOffset(); | 415 UpdateCursorBoundsAndTextOffset(); |
415 } | 416 } |
416 | 417 |
417 | 418 |
418 /////////////////////////////////////////////////////////////////////////////// | 419 /////////////////////////////////////////////////////////////////////////////// |
419 // NativeTextfieldViews private: | 420 // NativeTextfieldViews, TextInputClient implementation, private: |
421 | |
422 void NativeTextfieldViews::SetCompositionText( | |
423 const ui::CompositionText& composition) { | |
424 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) | |
425 return; | |
426 | |
427 OnBeforeUserAction(); | |
428 skip_input_method_cancel_composition_ = true; | |
429 model_->SetCompositionText(composition); | |
430 skip_input_method_cancel_composition_ = false; | |
431 UpdateAfterChange(true, true); | |
432 OnAfterUserAction(); | |
433 } | |
434 | |
435 void NativeTextfieldViews::ConfirmCompositionText() { | |
436 if (!model_->HasCompositionText()) | |
437 return; | |
438 | |
439 OnBeforeUserAction(); | |
440 skip_input_method_cancel_composition_ = true; | |
441 model_->ConfirmCompositionText(); | |
442 skip_input_method_cancel_composition_ = false; | |
443 UpdateAfterChange(true, true); | |
444 OnAfterUserAction(); | |
445 } | |
446 | |
447 void NativeTextfieldViews::ClearCompositionText() { | |
448 if (!model_->HasCompositionText()) | |
449 return; | |
450 | |
451 OnBeforeUserAction(); | |
452 skip_input_method_cancel_composition_ = true; | |
453 model_->ClearCompositionText(); | |
454 skip_input_method_cancel_composition_ = false; | |
455 UpdateAfterChange(true, true); | |
456 OnAfterUserAction(); | |
457 } | |
458 | |
459 void NativeTextfieldViews::InsertText(const string16& text) { | |
460 // TODO(suzhe): Filter invalid characters. | |
461 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) | |
462 return; | |
463 | |
464 OnBeforeUserAction(); | |
465 skip_input_method_cancel_composition_ = true; | |
466 if (insert_) | |
467 model_->InsertText(text); | |
468 else | |
469 model_->ReplaceText(text); | |
470 skip_input_method_cancel_composition_ = false; | |
471 UpdateAfterChange(true, true); | |
472 OnAfterUserAction(); | |
473 } | |
474 | |
475 void NativeTextfieldViews::InsertChar(char16 ch, int flags) { | |
476 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || | |
477 !ShouldInsertChar(ch, flags)) { | |
478 return; | |
479 } | |
480 | |
481 OnBeforeUserAction(); | |
482 skip_input_method_cancel_composition_ = true; | |
483 if (insert_) | |
484 model_->InsertChar(ch); | |
485 else | |
486 model_->ReplaceChar(ch); | |
487 skip_input_method_cancel_composition_ = false; | |
488 UpdateAfterChange(true, true); | |
489 OnAfterUserAction(); | |
490 } | |
491 | |
492 ui::TextInputType NativeTextfieldViews::GetTextInputType() { | |
493 if (textfield_->read_only() || !textfield_->IsEnabled()) | |
494 return ui::TEXT_INPUT_TYPE_NONE; | |
495 else if (textfield_->IsPassword()) | |
496 return ui::TEXT_INPUT_TYPE_PASSWORD; | |
497 return ui::TEXT_INPUT_TYPE_TEXT; | |
498 } | |
499 | |
500 gfx::Rect NativeTextfieldViews::GetCaretBounds() { | |
501 return cursor_bounds_; | |
502 } | |
503 | |
504 bool NativeTextfieldViews::HasCompositionText() { | |
505 return model_->HasCompositionText(); | |
506 } | |
507 | |
508 bool NativeTextfieldViews::GetTextRange(ui::Range* range) { | |
509 // We don't allow the input method to retrieve or delete content from a | |
510 // password box. | |
511 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) | |
512 return false; | |
513 | |
514 model_->GetTextRange(range); | |
515 return true; | |
516 } | |
517 | |
518 bool NativeTextfieldViews::GetCompositionTextRange(ui::Range* range) { | |
519 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) | |
520 return false; | |
521 | |
522 model_->GetCompositionTextRange(range); | |
523 return true; | |
524 } | |
525 | |
526 bool NativeTextfieldViews::GetSelectionRange(ui::Range* range) { | |
527 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) | |
528 return false; | |
529 | |
530 model_->GetSelectedRange(range); | |
531 return true; | |
532 } | |
533 | |
534 bool NativeTextfieldViews::SetSelectionRange(const ui::Range& range) { | |
535 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT || !range.IsValid()) | |
536 return false; | |
537 | |
538 OnBeforeUserAction(); | |
539 SelectRange(range); | |
540 OnAfterUserAction(); | |
541 return true; | |
542 } | |
543 | |
544 bool NativeTextfieldViews::DeleteRange(const ui::Range& range) { | |
545 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT || range.is_empty()) | |
546 return false; | |
547 | |
548 OnBeforeUserAction(); | |
549 model_->SelectRange(range); | |
550 if (model_->HasSelection()) { | |
551 model_->DeleteSelection(); | |
552 UpdateAfterChange(true, true); | |
553 } | |
554 OnAfterUserAction(); | |
555 return true; | |
556 } | |
557 | |
558 bool NativeTextfieldViews::GetTextFromRange( | |
559 const ui::Range& range, | |
560 const base::Callback<void(const string16&)>& callback) { | |
561 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT || range.is_empty()) | |
562 return false; | |
563 | |
564 callback.Run(model_->GetTextFromRange(range)); | |
565 return true; | |
566 } | |
567 | |
568 void NativeTextfieldViews::OnInputMethodChanged() { | |
569 NOTIMPLEMENTED(); | |
570 } | |
571 | |
572 bool NativeTextfieldViews::ChangeTextDirectionAndLayoutAlignment( | |
573 base::i18n::TextDirection direction) { | |
574 NOTIMPLEMENTED(); | |
575 return false; | |
576 } | |
577 | |
578 void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { | |
579 if (skip_input_method_cancel_composition_ || !textfield_->HasFocus()) | |
580 return; | |
581 | |
582 InputMethod* input_method = textfield_->GetInputMethod(); | |
583 if (input_method) | |
584 input_method->CancelComposition(textfield_); | |
585 } | |
420 | 586 |
421 const gfx::Font& NativeTextfieldViews::GetFont() const { | 587 const gfx::Font& NativeTextfieldViews::GetFont() const { |
422 return textfield_->font(); | 588 return textfield_->font(); |
423 } | 589 } |
424 | 590 |
425 SkColor NativeTextfieldViews::GetTextColor() const { | 591 SkColor NativeTextfieldViews::GetTextColor() const { |
426 return textfield_->text_color(); | 592 return textfield_->text_color(); |
427 } | 593 } |
428 | 594 |
429 void NativeTextfieldViews::UpdateCursor() { | 595 void NativeTextfieldViews::UpdateCursor() { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
472 text_offset_ = -cursor_bounds_.x(); | 638 text_offset_ = -cursor_bounds_.x(); |
473 } else if(full_width > width && text_offset_ + full_width < width) { | 639 } else if(full_width > width && text_offset_ + full_width < width) { |
474 // when the cursor moves within the textfield with the text | 640 // when the cursor moves within the textfield with the text |
475 // longer than the field. | 641 // longer than the field. |
476 text_offset_ = width - full_width; | 642 text_offset_ = width - full_width; |
477 } else { | 643 } else { |
478 // move cursor freely. | 644 // move cursor freely. |
479 } | 645 } |
480 // shift cursor bounds to fit insets. | 646 // shift cursor bounds to fit insets. |
481 cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + insets.left()); | 647 cursor_bounds_.set_x(cursor_bounds_.x() + text_offset_ + insets.left()); |
648 | |
649 OnCaretBoundsChanged(); | |
482 } | 650 } |
483 | 651 |
484 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { | 652 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { |
485 gfx::Insets insets = GetInsets(); | 653 gfx::Insets insets = GetInsets(); |
486 | 654 |
487 canvas->Save(); | 655 canvas->Save(); |
488 canvas->ClipRectInt(insets.left(), insets.top(), | 656 canvas->ClipRectInt(insets.left(), insets.top(), |
489 width() - insets.width(), height() - insets.height()); | 657 width() - insets.width(), height() - insets.height()); |
490 | 658 |
491 // TODO(oshima): bidi support | 659 // TODO(oshima): bidi support |
492 // TODO(varunjain): re-implement this so only that dirty text is painted. | 660 // TODO(varunjain): re-implement this so only that dirty text is painted. |
493 TextfieldViewsModel::TextFragments fragments; | 661 TextfieldViewsModel::TextFragments fragments; |
494 model_->GetFragments(&fragments); | 662 model_->GetFragments(&fragments); |
495 int x_offset = text_offset_ + insets.left(); | 663 int x_offset = text_offset_ + insets.left(); |
496 int y = insets.top(); | 664 int y = insets.top(); |
497 int text_height = height() - insets.height(); | 665 int text_height = height() - insets.height(); |
498 SkColor selection_color = | 666 SkColor selection_color = |
499 textfield_->HasFocus() ? | 667 textfield_->HasFocus() ? |
500 kFocusedSelectionColor : kUnfocusedSelectionColor; | 668 kFocusedSelectionColor : kUnfocusedSelectionColor; |
501 SkColor text_color = | 669 SkColor text_color = |
502 textfield_->read_only() ? kReadonlyTextColor : GetTextColor(); | 670 textfield_->read_only() ? kReadonlyTextColor : GetTextColor(); |
503 | 671 |
504 for (TextfieldViewsModel::TextFragments::const_iterator iter = | 672 for (TextfieldViewsModel::TextFragments::const_iterator iter = |
505 fragments.begin(); | 673 fragments.begin(); |
506 iter != fragments.end(); | 674 iter != fragments.end(); |
507 iter++) { | 675 iter++) { |
508 string16 text = model_->GetVisibleText((*iter).begin, (*iter).end); | 676 string16 text = model_->GetVisibleText(iter->start, iter->end); |
677 | |
678 gfx::Font font = GetFont(); | |
679 if (iter->underline) | |
680 font = font.DeriveFont(0, font.GetStyle() | gfx::Font::UNDERLINED); | |
681 | |
509 // TODO(oshima): This does not give the accurate position due to | 682 // TODO(oshima): This does not give the accurate position due to |
510 // kerning. Figure out how webkit does this with skia. | 683 // kerning. Figure out how webkit does this with skia. |
511 int width = GetFont().GetStringWidth(text); | 684 int width = font.GetStringWidth(text); |
512 | 685 |
513 if ((*iter).selected) { | 686 if (iter->selected) { |
514 canvas->FillRectInt(selection_color, x_offset, y, width, text_height); | 687 canvas->FillRectInt(selection_color, x_offset, y, width, text_height); |
515 canvas->DrawStringInt(text, GetFont(), kSelectedTextColor, | 688 canvas->DrawStringInt(text, font, kSelectedTextColor, |
516 x_offset, y, width, text_height); | 689 x_offset, y, width, text_height); |
517 } else { | 690 } else { |
518 canvas->DrawStringInt(text, GetFont(), text_color, | 691 canvas->DrawStringInt(text, font, text_color, |
519 x_offset, y, width, text_height); | 692 x_offset, y, width, text_height); |
520 } | 693 } |
521 x_offset += width; | 694 x_offset += width; |
522 } | 695 } |
523 canvas->Restore(); | 696 canvas->Restore(); |
524 | 697 |
525 if (textfield_->IsEnabled() && is_cursor_visible_ && | 698 if (textfield_->IsEnabled() && is_cursor_visible_ && |
526 !model_->HasSelection()) { | 699 !model_->HasSelection()) { |
527 // Paint Cursor. Replace cursor is drawn as rectangle for now. | 700 // Paint Cursor. Replace cursor is drawn as rectangle for now. |
528 canvas->DrawRectInt(kCursorColor, | 701 canvas->DrawRectInt(kCursorColor, |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
574 case ui::VKEY_LEFT: | 747 case ui::VKEY_LEFT: |
575 control ? model_->MoveCursorToPreviousWord(selection) | 748 control ? model_->MoveCursorToPreviousWord(selection) |
576 : model_->MoveCursorLeft(selection); | 749 : model_->MoveCursorLeft(selection); |
577 cursor_changed = true; | 750 cursor_changed = true; |
578 break; | 751 break; |
579 case ui::VKEY_END: | 752 case ui::VKEY_END: |
580 model_->MoveCursorToEnd(selection); | 753 model_->MoveCursorToEnd(selection); |
581 cursor_changed = true; | 754 cursor_changed = true; |
582 break; | 755 break; |
583 case ui::VKEY_HOME: | 756 case ui::VKEY_HOME: |
584 model_->MoveCursorToStart(selection); | 757 model_->MoveCursorToHome(selection); |
585 cursor_changed = true; | 758 cursor_changed = true; |
586 break; | 759 break; |
587 case ui::VKEY_BACK: | 760 case ui::VKEY_BACK: |
588 if (!editable) | 761 if (!editable) |
589 break; | 762 break; |
590 if (!model_->HasSelection()) { | 763 if (!model_->HasSelection()) { |
591 if (selection && control) { | 764 if (selection && control) { |
592 // If both shift and control are pressed, then erase upto the | 765 // If both shift and control are pressed, then erase upto the |
593 // beginning of the buffer in ChromeOS. In windows, do nothing. | 766 // beginning of the buffer in ChromeOS. In windows, do nothing. |
594 #if defined(OS_WIN) | 767 #if defined(OS_WIN) |
595 break; | 768 break; |
596 #else | 769 #else |
597 model_->MoveCursorToStart(true); | 770 model_->MoveCursorToHome(true); |
598 #endif | 771 #endif |
599 } else if (control) { | 772 } else if (control) { |
600 // If only control is pressed, then erase the previous word. | 773 // If only control is pressed, then erase the previous word. |
601 model_->MoveCursorToPreviousWord(true); | 774 model_->MoveCursorToPreviousWord(true); |
602 } | 775 } |
603 } | 776 } |
604 text_changed = model_->Backspace(); | 777 text_changed = model_->Backspace(); |
605 cursor_changed = true; | 778 cursor_changed = true; |
606 break; | 779 break; |
607 case ui::VKEY_DELETE: | 780 case ui::VKEY_DELETE: |
(...skipping 15 matching lines...) Expand all Loading... | |
623 } | 796 } |
624 cursor_changed = text_changed = model_->Delete(); | 797 cursor_changed = text_changed = model_->Delete(); |
625 break; | 798 break; |
626 case ui::VKEY_INSERT: | 799 case ui::VKEY_INSERT: |
627 insert_ = !insert_; | 800 insert_ = !insert_; |
628 cursor_changed = true; | 801 cursor_changed = true; |
629 break; | 802 break; |
630 default: | 803 default: |
631 break; | 804 break; |
632 } | 805 } |
633 char16 ch = key_event.GetCharacter(); | 806 |
634 if (editable && ShouldInsertChar(ch, key_event.flags())) { | 807 // Only handle text input by ourselves if there is no input method. |
635 if (insert_) | 808 if (!textfield_->GetInputMethod()) { |
636 model_->Insert(ch); | 809 char16 ch = key_event.GetCharacter(); |
637 else | 810 if (editable && ShouldInsertChar(ch, key_event.flags())) { |
638 model_->Replace(ch); | 811 if (insert_) |
639 text_changed = true; | 812 model_->InsertChar(ch); |
813 else | |
814 model_->ReplaceChar(ch); | |
815 text_changed = true; | |
816 } | |
640 } | 817 } |
641 | 818 |
642 UpdateAfterChange(text_changed, cursor_changed); | 819 UpdateAfterChange(text_changed, cursor_changed); |
643 OnAfterUserAction(); | 820 OnAfterUserAction(); |
644 return (text_changed || cursor_changed); | 821 return (text_changed || cursor_changed); |
645 } | 822 } |
646 return false; | 823 return false; |
647 } | 824 } |
648 | 825 |
649 size_t NativeTextfieldViews::FindCursorPosition(const gfx::Point& point) const { | 826 size_t NativeTextfieldViews::FindCursorPosition(const gfx::Point& point) const { |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
714 | 891 |
715 void NativeTextfieldViews::SetCursorForMouseClick(const views::MouseEvent& e) { | 892 void NativeTextfieldViews::SetCursorForMouseClick(const views::MouseEvent& e) { |
716 size_t pos = FindCursorPosition(e.location()); | 893 size_t pos = FindCursorPosition(e.location()); |
717 if (model_->MoveCursorTo(pos, false)) { | 894 if (model_->MoveCursorTo(pos, false)) { |
718 UpdateCursorBoundsAndTextOffset(); | 895 UpdateCursorBoundsAndTextOffset(); |
719 } | 896 } |
720 } | 897 } |
721 | 898 |
722 void NativeTextfieldViews::PropagateTextChange() { | 899 void NativeTextfieldViews::PropagateTextChange() { |
723 textfield_->SyncText(); | 900 textfield_->SyncText(); |
724 TextfieldController* controller = textfield_->GetController(); | |
725 if (controller) | |
726 controller->ContentsChanged(textfield_, GetText()); | |
727 } | 901 } |
728 | 902 |
729 void NativeTextfieldViews::UpdateAfterChange(bool text_changed, | 903 void NativeTextfieldViews::UpdateAfterChange(bool text_changed, |
730 bool cursor_changed) { | 904 bool cursor_changed) { |
731 if (text_changed) | 905 if (text_changed) |
732 PropagateTextChange(); | 906 PropagateTextChange(); |
733 if (cursor_changed) { | 907 if (cursor_changed) { |
734 is_cursor_visible_ = true; | 908 is_cursor_visible_ = true; |
735 RepaintCursor(); | 909 RepaintCursor(); |
736 } | 910 } |
(...skipping 10 matching lines...) Expand all Loading... | |
747 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); | 921 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); |
748 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); | 922 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); |
749 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); | 923 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); |
750 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); | 924 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); |
751 context_menu_contents_->AddSeparator(); | 925 context_menu_contents_->AddSeparator(); |
752 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, | 926 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, |
753 IDS_APP_SELECT_ALL); | 927 IDS_APP_SELECT_ALL); |
754 context_menu_menu_.reset(new Menu2(context_menu_contents_.get())); | 928 context_menu_menu_.reset(new Menu2(context_menu_contents_.get())); |
755 } | 929 } |
756 | 930 |
931 void NativeTextfieldViews::OnTextInputTypeChanged() { | |
932 if (!textfield_->HasFocus()) | |
933 return; | |
oshima
2011/03/29 19:07:55
can we check this in input_method? is it bad idea?
James Su
2011/03/29 19:21:51
InputMethod implementations also do this check. Th
oshima
2011/03/30 21:06:34
The code that has no effect or speculative code ma
Peng
2011/03/30 21:28:27
We have several input_method implementations (Ibus
James Su
2011/03/30 21:30:32
Existing InputMethod implementations simply return
James Su
2011/03/30 21:30:32
Yes. I updated comments of these methods to clarif
oshima
2011/03/30 21:51:25
Thanks. Just for clarification, I was fine with ei
| |
934 | |
935 InputMethod* input_method = textfield_->GetInputMethod(); | |
oshima
2011/03/29 19:07:55
when input_method can be NULL?
James Su
2011/03/29 19:21:51
Right now, it's only possible in test code, where
oshima
2011/03/30 21:06:34
If it shouldn't be null for regular case, I'd pref
James Su
2011/03/30 21:30:32
I'd prefer to keep this check, otherwise we need s
oshima
2011/03/30 21:51:25
Altering the code for unittest to pass sounds wron
James Su
2011/03/30 22:46:38
Sounds reasonable. I'll remove the check.
| |
936 if (input_method) | |
937 input_method->OnTextInputTypeChanged(textfield_); | |
938 } | |
939 | |
940 void NativeTextfieldViews::OnCaretBoundsChanged() { | |
941 if (!textfield_->HasFocus()) | |
942 return; | |
943 | |
944 InputMethod* input_method = textfield_->GetInputMethod(); | |
945 if (input_method) | |
946 input_method->OnCaretBoundsChanged(textfield_); | |
947 } | |
948 | |
757 void NativeTextfieldViews::OnBeforeUserAction() { | 949 void NativeTextfieldViews::OnBeforeUserAction() { |
758 TextfieldController* controller = textfield_->GetController(); | 950 TextfieldController* controller = textfield_->GetController(); |
759 if (controller) | 951 if (controller) |
760 controller->OnBeforeUserAction(textfield_); | 952 controller->OnBeforeUserAction(textfield_); |
761 } | 953 } |
762 | 954 |
763 void NativeTextfieldViews::OnAfterUserAction() { | 955 void NativeTextfieldViews::OnAfterUserAction() { |
764 TextfieldController* controller = textfield_->GetController(); | 956 TextfieldController* controller = textfield_->GetController(); |
765 if (controller) | 957 if (controller) |
766 controller->OnAfterUserAction(textfield_); | 958 controller->OnAfterUserAction(textfield_); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
825 } | 1017 } |
826 | 1018 |
827 void NativeTextfieldViews::TextfieldBorder::SetInsets(int top, | 1019 void NativeTextfieldViews::TextfieldBorder::SetInsets(int top, |
828 int left, | 1020 int left, |
829 int bottom, | 1021 int bottom, |
830 int right) { | 1022 int right) { |
831 insets_.Set(top, left, bottom, right); | 1023 insets_.Set(top, left, bottom, right); |
832 } | 1024 } |
833 | 1025 |
834 } // namespace views | 1026 } // namespace views |
OLD | NEW |