Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(312)

Side by Side Diff: views/controls/textfield/native_textfield_views.cc

Issue 7265011: RenderText API Outline. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Almost at parity with the current implementation. Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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/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.
41 // These are tentative, and should be derived from theme, system
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
50 // Parameters to control cursor blinking. 40 // Parameters to control cursor blinking.
51 const int kCursorVisibleTimeMs = 800; 41 const int kCursorVisibleTimeMs = 800;
52 const int kCursorInvisibleTimeMs = 500; 42 const int kCursorInvisibleTimeMs = 500;
53 43
54 } // namespace 44 } // namespace
55 45
56 namespace views { 46 namespace views {
57 47
58 const char NativeTextfieldViews::kViewClassName[] = 48 const char NativeTextfieldViews::kViewClassName[] =
59 "views/NativeTextfieldViews"; 49 "views/NativeTextfieldViews";
60 50
61 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) 51 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent)
62 : textfield_(parent), 52 : textfield_(parent),
63 ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))), 53 ALLOW_THIS_IN_INITIALIZER_LIST(model_(new TextfieldViewsModel(this))),
64 text_border_(new FocusableBorder()), 54 text_border_(new FocusableBorder()),
65 text_offset_(0),
66 insert_(true),
67 is_cursor_visible_(false), 55 is_cursor_visible_(false),
68 skip_input_method_cancel_composition_(false), 56 skip_input_method_cancel_composition_(false),
69 initiating_drag_(false), 57 initiating_drag_(false),
70 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), 58 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)),
71 aggregated_clicks_(0), 59 aggregated_clicks_(0),
72 last_click_time_(base::Time::FromInternalValue(0)), 60 last_click_time_(base::Time::FromInternalValue(0)),
73 last_click_location_(0, 0) { 61 last_click_location_(0, 0) {
74 set_border(text_border_); 62 set_border(text_border_);
75 63
76 // Lowercase is not supported. 64 // Lowercase is not supported.
77 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE); 65 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE);
78 66
67 // Set the default text style.
68 gfx::StyleRange default_style;
69 default_style.font = textfield_->font();
70 default_style.foreground = textfield_->text_color();
71 GetRenderText()->set_default_style(default_style);
72
79 set_context_menu_controller(this); 73 set_context_menu_controller(this);
80 set_drag_controller(this); 74 set_drag_controller(this);
81 } 75 }
82 76
83 NativeTextfieldViews::~NativeTextfieldViews() { 77 NativeTextfieldViews::~NativeTextfieldViews() {
84 } 78 }
85 79
86 //////////////////////////////////////////////////////////////////////////////// 80 ////////////////////////////////////////////////////////////////////////////////
87 // NativeTextfieldViews, View overrides: 81 // NativeTextfieldViews, View overrides:
88 82
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) { 166 bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) {
173 return textfield_->IsEnabled() && !textfield_->read_only() && 167 return textfield_->IsEnabled() && !textfield_->read_only() &&
174 data.HasString(); 168 data.HasString();
175 } 169 }
176 170
177 int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) { 171 int NativeTextfieldViews::OnDragUpdated(const DropTargetEvent& event) {
178 DCHECK(CanDrop(event.data())); 172 DCHECK(CanDrop(event.data()));
179 bool is_point_in_selection = IsPointInSelection(event.location()); 173 bool is_point_in_selection = IsPointInSelection(event.location());
180 is_drop_cursor_visible_ = !is_point_in_selection; 174 is_drop_cursor_visible_ = !is_point_in_selection;
181 // TODO(msw): Pan over text when the user drags to the visible text edge. 175 // TODO(msw): Pan over text when the user drags to the visible text edge.
182 UpdateCursorBoundsAndTextOffset(FindCursorPosition(event.location()), true); 176 size_t cursor_pos = GetRenderText()->FindCursorPosition(event.location());
177 OnCaretBoundsChanged();
183 SchedulePaint(); 178 SchedulePaint();
184 179
185 if (initiating_drag_) { 180 if (initiating_drag_) {
186 if (is_point_in_selection) 181 if (is_point_in_selection)
187 return ui::DragDropTypes::DRAG_NONE; 182 return ui::DragDropTypes::DRAG_NONE;
188 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : 183 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY :
189 ui::DragDropTypes::DRAG_MOVE; 184 ui::DragDropTypes::DRAG_MOVE;
190 } 185 }
191 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; 186 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE;
192 } 187 }
193 188
194 int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) { 189 int NativeTextfieldViews::OnPerformDrop(const DropTargetEvent& event) {
195 DCHECK(CanDrop(event.data())); 190 DCHECK(CanDrop(event.data()));
196 DCHECK(!initiating_drag_ || !IsPointInSelection(event.location())); 191 DCHECK(!initiating_drag_ || !IsPointInSelection(event.location()));
197 OnBeforeUserAction(); 192 OnBeforeUserAction();
198 skip_input_method_cancel_composition_ = true; 193 skip_input_method_cancel_composition_ = true;
199 194
200 size_t drop_destination = FindCursorPosition(event.location()); 195 size_t drop_destination =
196 GetRenderText()->FindCursorPosition(event.location());
201 string16 text; 197 string16 text;
202 event.data().GetString(&text); 198 event.data().GetString(&text);
203 199
204 // We'll delete the current selection for a drag and drop within this view. 200 // We'll delete the current selection for a drag and drop within this view.
205 bool move = initiating_drag_ && !event.IsControlDown() && 201 bool move = initiating_drag_ && !event.IsControlDown() &&
206 event.source_operations() & ui::DragDropTypes::DRAG_MOVE; 202 event.source_operations() & ui::DragDropTypes::DRAG_MOVE;
207 if (move) { 203 if (move) {
208 ui::Range selected_range; 204 ui::Range selected_range;
209 model_->GetSelectedRange(&selected_range); 205 model_->GetSelectedRange(&selected_range);
210 // Adjust the drop destination if it is on or after the current selection. 206 // Adjust the drop destination if it is on or after the current selection.
211 if (selected_range.GetMax() <= drop_destination) 207 if (selected_range.GetMax() <= drop_destination)
212 drop_destination -= selected_range.length(); 208 drop_destination -= selected_range.length();
213 else if (selected_range.GetMin() <= drop_destination) 209 else if (selected_range.GetMin() <= drop_destination)
214 drop_destination = selected_range.GetMin(); 210 drop_destination = selected_range.GetMin();
215 model_->DeleteSelectionAndInsertTextAt(text, drop_destination); 211 model_->DeleteSelectionAndInsertTextAt(text, drop_destination);
216 } else { 212 } else {
217 model_->MoveCursorTo(drop_destination, false); 213 model_->MoveCursorTo(drop_destination, false);
218 // Drop always inserts a text even if insert_ == false. 214 // Drop always inserts text even if the textfield is not in insert mode.
219 model_->InsertText(text); 215 model_->InsertText(text);
220 } 216 }
221 skip_input_method_cancel_composition_ = false; 217 skip_input_method_cancel_composition_ = false;
222 UpdateAfterChange(true, true); 218 UpdateAfterChange(true, true);
223 OnAfterUserAction(); 219 OnAfterUserAction();
224 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; 220 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY;
225 } 221 }
226 222
227 void NativeTextfieldViews::OnDragDone() { 223 void NativeTextfieldViews::OnDragDone() {
228 initiating_drag_ = false; 224 initiating_drag_ = false;
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 bool NativeTextfieldViews::CanStartDragForView(View* sender, 288 bool NativeTextfieldViews::CanStartDragForView(View* sender,
293 const gfx::Point& press_pt, 289 const gfx::Point& press_pt,
294 const gfx::Point& p) { 290 const gfx::Point& p) {
295 return IsPointInSelection(press_pt); 291 return IsPointInSelection(press_pt);
296 } 292 }
297 293
298 ///////////////////////////////////////////////////////////////// 294 /////////////////////////////////////////////////////////////////
299 // NativeTextfieldViews, NativeTextifieldWrapper overrides: 295 // NativeTextfieldViews, NativeTextifieldWrapper overrides:
300 296
301 string16 NativeTextfieldViews::GetText() const { 297 string16 NativeTextfieldViews::GetText() const {
302 return model_->text(); 298 return model_->GetText();
303 } 299 }
304 300
305 void NativeTextfieldViews::UpdateText() { 301 void NativeTextfieldViews::UpdateText() {
306 model_->SetText(textfield_->text()); 302 model_->SetText(textfield_->text());
307 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 303 OnCaretBoundsChanged();
308 SchedulePaint(); 304 SchedulePaint();
309 } 305 }
310 306
311 void NativeTextfieldViews::AppendText(const string16& text) { 307 void NativeTextfieldViews::AppendText(const string16& text) {
312 if (text.empty()) 308 if (text.empty())
313 return; 309 return;
314 model_->Append(text); 310 model_->Append(text);
315 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 311 OnCaretBoundsChanged();
316 SchedulePaint(); 312 SchedulePaint();
317 } 313 }
318 314
319 string16 NativeTextfieldViews::GetSelectedText() const { 315 string16 NativeTextfieldViews::GetSelectedText() const {
320 return model_->GetSelectedText(); 316 return model_->GetSelectedText();
321 } 317 }
322 318
323 void NativeTextfieldViews::SelectAll() { 319 void NativeTextfieldViews::SelectAll() {
324 model_->SelectAll(); 320 model_->SelectAll();
325 SchedulePaint(); 321 SchedulePaint();
(...skipping 25 matching lines...) Expand all
351 Background::CreateSolidBackground(textfield_->background_color())); 347 Background::CreateSolidBackground(textfield_->background_color()));
352 SchedulePaint(); 348 SchedulePaint();
353 } 349 }
354 350
355 void NativeTextfieldViews::UpdateReadOnly() { 351 void NativeTextfieldViews::UpdateReadOnly() {
356 SchedulePaint(); 352 SchedulePaint();
357 OnTextInputTypeChanged(); 353 OnTextInputTypeChanged();
358 } 354 }
359 355
360 void NativeTextfieldViews::UpdateFont() { 356 void NativeTextfieldViews::UpdateFont() {
361 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 357 OnCaretBoundsChanged();
362 } 358 }
363 359
364 void NativeTextfieldViews::UpdateIsPassword() { 360 void NativeTextfieldViews::UpdateIsPassword() {
365 model_->set_is_password(textfield_->IsPassword()); 361 model_->set_is_password(textfield_->IsPassword());
366 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 362 OnCaretBoundsChanged();
367 SchedulePaint(); 363 SchedulePaint();
368 OnTextInputTypeChanged(); 364 OnTextInputTypeChanged();
369 } 365 }
370 366
371 void NativeTextfieldViews::UpdateEnabled() { 367 void NativeTextfieldViews::UpdateEnabled() {
372 SetEnabled(textfield_->IsEnabled()); 368 SetEnabled(textfield_->IsEnabled());
373 SchedulePaint(); 369 SchedulePaint();
374 OnTextInputTypeChanged(); 370 OnTextInputTypeChanged();
375 } 371 }
376 372
377 gfx::Insets NativeTextfieldViews::CalculateInsets() { 373 gfx::Insets NativeTextfieldViews::CalculateInsets() {
378 return GetInsets(); 374 return GetInsets();
379 } 375 }
380 376
381 void NativeTextfieldViews::UpdateHorizontalMargins() { 377 void NativeTextfieldViews::UpdateHorizontalMargins() {
382 int left, right; 378 int left, right;
383 if (!textfield_->GetHorizontalMargins(&left, &right)) 379 if (!textfield_->GetHorizontalMargins(&left, &right))
384 return; 380 return;
385 gfx::Insets inset = GetInsets(); 381 gfx::Insets inset = GetInsets();
386 382
387 text_border_->SetInsets(inset.top(), left, inset.bottom(), right); 383 text_border_->SetInsets(inset.top(), left, inset.bottom(), right);
388 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 384 OnCaretBoundsChanged();
389 } 385 }
390 386
391 void NativeTextfieldViews::UpdateVerticalMargins() { 387 void NativeTextfieldViews::UpdateVerticalMargins() {
392 int top, bottom; 388 int top, bottom;
393 if (!textfield_->GetVerticalMargins(&top, &bottom)) 389 if (!textfield_->GetVerticalMargins(&top, &bottom))
394 return; 390 return;
395 gfx::Insets inset = GetInsets(); 391 gfx::Insets inset = GetInsets();
396
397 text_border_->SetInsets(top, inset.left(), bottom, inset.right()); 392 text_border_->SetInsets(top, inset.left(), bottom, inset.right());
398 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 393 OnCaretBoundsChanged();
399 } 394 }
400 395
401 bool NativeTextfieldViews::SetFocus() { 396 bool NativeTextfieldViews::SetFocus() {
402 return false; 397 return false;
403 } 398 }
404 399
405 View* NativeTextfieldViews::GetView() { 400 View* NativeTextfieldViews::GetView() {
406 return this; 401 return this;
407 } 402 }
408 403
409 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const { 404 gfx::NativeView NativeTextfieldViews::GetTestingHandle() const {
410 NOTREACHED(); 405 NOTREACHED();
411 return NULL; 406 return NULL;
412 } 407 }
413 408
414 bool NativeTextfieldViews::IsIMEComposing() const { 409 bool NativeTextfieldViews::IsIMEComposing() const {
415 return model_->HasCompositionText(); 410 return model_->HasCompositionText();
416 } 411 }
417 412
418 void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const { 413 void NativeTextfieldViews::GetSelectedRange(ui::Range* range) const {
419 model_->GetSelectedRange(range); 414 model_->GetSelectedRange(range);
420 } 415 }
421 416
422 void NativeTextfieldViews::SelectRange(const ui::Range& range) { 417 void NativeTextfieldViews::SelectRange(const ui::Range& range) {
423 model_->SelectRange(range); 418 model_->SelectRange(range);
424 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 419 OnCaretBoundsChanged();
425 SchedulePaint(); 420 SchedulePaint();
426 } 421 }
427 422
428 size_t NativeTextfieldViews::GetCursorPosition() const { 423 size_t NativeTextfieldViews::GetCursorPosition() const {
429 return model_->cursor_pos(); 424 return model_->GetCursorPosition();
430 } 425 }
431 426
432 bool NativeTextfieldViews::HandleKeyPressed(const KeyEvent& e) { 427 bool NativeTextfieldViews::HandleKeyPressed(const KeyEvent& e) {
433 TextfieldController* controller = textfield_->GetController(); 428 TextfieldController* controller = textfield_->GetController();
434 bool handled = false; 429 bool handled = false;
435 if (controller) 430 if (controller)
436 handled = controller->HandleKeyEvent(textfield_, e); 431 handled = controller->HandleKeyEvent(textfield_, e);
437 return handled || HandleKeyEvent(e); 432 return handled || HandleKeyEvent(e);
438 } 433 }
439 434
440 bool NativeTextfieldViews::HandleKeyReleased(const KeyEvent& e) { 435 bool NativeTextfieldViews::HandleKeyReleased(const KeyEvent& e) {
441 return true; 436 return true;
442 } 437 }
443 438
444 void NativeTextfieldViews::HandleFocus() { 439 void NativeTextfieldViews::HandleFocus() {
440 GetRenderText()->set_focused(true);
445 is_cursor_visible_ = true; 441 is_cursor_visible_ = true;
446 SchedulePaint(); 442 SchedulePaint();
447 OnCaretBoundsChanged(); 443 OnCaretBoundsChanged();
448 // Start blinking cursor. 444 // Start blinking cursor.
449 MessageLoop::current()->PostDelayedTask( 445 MessageLoop::current()->PostDelayedTask(
450 FROM_HERE, 446 FROM_HERE,
451 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), 447 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor),
452 kCursorVisibleTimeMs); 448 kCursorVisibleTimeMs);
453 } 449 }
454 450
455 void NativeTextfieldViews::HandleBlur() { 451 void NativeTextfieldViews::HandleBlur() {
452 GetRenderText()->set_focused(false);
456 // Stop blinking cursor. 453 // Stop blinking cursor.
457 cursor_timer_.RevokeAll(); 454 cursor_timer_.RevokeAll();
458 if (is_cursor_visible_) { 455 if (is_cursor_visible_) {
459 is_cursor_visible_ = false; 456 is_cursor_visible_ = false;
460 RepaintCursor(); 457 RepaintCursor();
461 } 458 }
462 } 459 }
463 460
464 TextInputClient* NativeTextfieldViews::GetTextInputClient() { 461 TextInputClient* NativeTextfieldViews::GetTextInputClient() {
465 return textfield_->read_only() ? NULL : this; 462 return textfield_->read_only() ? NULL : this;
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
529 default: 526 default:
530 NOTREACHED() << "unknown command: " << command_id; 527 NOTREACHED() << "unknown command: " << command_id;
531 break; 528 break;
532 } 529 }
533 530
534 // The cursor must have changed if text changed during cut/paste/delete. 531 // The cursor must have changed if text changed during cut/paste/delete.
535 UpdateAfterChange(text_changed, text_changed); 532 UpdateAfterChange(text_changed, text_changed);
536 OnAfterUserAction(); 533 OnAfterUserAction();
537 } 534 }
538 535
539 TextStyle* NativeTextfieldViews::CreateTextStyle() { 536 void NativeTextfieldViews::ApplyStyleRange(const gfx::StyleRange& style) {
540 return model_->CreateTextStyle(); 537 GetRenderText()->ApplyStyleRange(style);
541 }
542
543 void NativeTextfieldViews::ApplyTextStyle(const TextStyle* style,
544 const ui::Range& range) {
545 model_->ApplyTextStyle(style, range);
546 SchedulePaint(); 538 SchedulePaint();
547 } 539 }
548 540
549 void NativeTextfieldViews::ClearAllTextStyles() { 541 void NativeTextfieldViews::ApplyDefaultStyle() {
550 model_->ClearAllTextStyles(); 542 GetRenderText()->ApplyDefaultStyle();
551 SchedulePaint(); 543 SchedulePaint();
552 } 544 }
553 545
554 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { 546 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) {
555 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 547 // Set the RenderText display area.
548 gfx::Insets insets = GetInsets();
549 gfx::Rect display_rect(insets.left(),
550 insets.top(),
551 width() - insets.width(),
552 height() - insets.height());
553 GetRenderText()->set_display_rect(display_rect);
554 OnCaretBoundsChanged();
556 } 555 }
557 556
558 /////////////////////////////////////////////////////////////////////////////// 557 ///////////////////////////////////////////////////////////////////////////////
559 // NativeTextfieldViews, TextInputClient implementation, private: 558 // NativeTextfieldViews, TextInputClient implementation, private:
560 559
561 void NativeTextfieldViews::SetCompositionText( 560 void NativeTextfieldViews::SetCompositionText(
562 const ui::CompositionText& composition) { 561 const ui::CompositionText& composition) {
563 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) 562 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
564 return; 563 return;
565 564
(...skipping 29 matching lines...) Expand all
595 OnAfterUserAction(); 594 OnAfterUserAction();
596 } 595 }
597 596
598 void NativeTextfieldViews::InsertText(const string16& text) { 597 void NativeTextfieldViews::InsertText(const string16& text) {
599 // TODO(suzhe): Filter invalid characters. 598 // TODO(suzhe): Filter invalid characters.
600 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) 599 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty())
601 return; 600 return;
602 601
603 OnBeforeUserAction(); 602 OnBeforeUserAction();
604 skip_input_method_cancel_composition_ = true; 603 skip_input_method_cancel_composition_ = true;
605 if (insert_) 604 if (GetRenderText()->get_insert_mode())
606 model_->InsertText(text); 605 model_->InsertText(text);
607 else 606 else
608 model_->ReplaceText(text); 607 model_->ReplaceText(text);
609 skip_input_method_cancel_composition_ = false; 608 skip_input_method_cancel_composition_ = false;
610 UpdateAfterChange(true, true); 609 UpdateAfterChange(true, true);
611 OnAfterUserAction(); 610 OnAfterUserAction();
612 } 611 }
613 612
614 void NativeTextfieldViews::InsertChar(char16 ch, int flags) { 613 void NativeTextfieldViews::InsertChar(char16 ch, int flags) {
615 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || 614 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE ||
616 !ShouldInsertChar(ch, flags)) { 615 !ShouldInsertChar(ch, flags)) {
617 return; 616 return;
618 } 617 }
619 618
620 OnBeforeUserAction(); 619 OnBeforeUserAction();
621 skip_input_method_cancel_composition_ = true; 620 skip_input_method_cancel_composition_ = true;
622 if (insert_) 621 if (GetRenderText()->get_insert_mode())
623 model_->InsertChar(ch); 622 model_->InsertChar(ch);
624 else 623 else
625 model_->ReplaceChar(ch); 624 model_->ReplaceChar(ch);
626 skip_input_method_cancel_composition_ = false; 625 skip_input_method_cancel_composition_ = false;
627 UpdateAfterChange(true, true); 626 UpdateAfterChange(true, true);
628 OnAfterUserAction(); 627 OnAfterUserAction();
629 } 628 }
630 629
631 ui::TextInputType NativeTextfieldViews::GetTextInputType() { 630 ui::TextInputType NativeTextfieldViews::GetTextInputType() {
632 if (textfield_->read_only() || !textfield_->IsEnabled()) 631 if (textfield_->read_only() || !textfield_->IsEnabled())
633 return ui::TEXT_INPUT_TYPE_NONE; 632 return ui::TEXT_INPUT_TYPE_NONE;
634 else if (textfield_->IsPassword()) 633 else if (textfield_->IsPassword())
635 return ui::TEXT_INPUT_TYPE_PASSWORD; 634 return ui::TEXT_INPUT_TYPE_PASSWORD;
636 return ui::TEXT_INPUT_TYPE_TEXT; 635 return ui::TEXT_INPUT_TYPE_TEXT;
637 } 636 }
638 637
639 gfx::Rect NativeTextfieldViews::GetCaretBounds() { 638 gfx::Rect NativeTextfieldViews::GetCaretBounds() {
640 return cursor_bounds_; 639 gfx::RenderText* render_text = GetRenderText();
640 return render_text->GetCursorBounds(render_text->GetCursor(),
641 render_text->get_insert_mode());
641 } 642 }
642 643
643 bool NativeTextfieldViews::HasCompositionText() { 644 bool NativeTextfieldViews::HasCompositionText() {
644 return model_->HasCompositionText(); 645 return model_->HasCompositionText();
645 } 646 }
646 647
647 bool NativeTextfieldViews::GetTextRange(ui::Range* range) { 648 bool NativeTextfieldViews::GetTextRange(ui::Range* range) {
648 // We don't allow the input method to retrieve or delete content from a 649 // We don't allow the input method to retrieve or delete content from a
649 // password box. 650 // password box.
650 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT) 651 if (GetTextInputType() != ui::TEXT_INPUT_TYPE_TEXT)
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
718 return textfield_; 719 return textfield_;
719 } 720 }
720 721
721 void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { 722 void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() {
722 if (skip_input_method_cancel_composition_) 723 if (skip_input_method_cancel_composition_)
723 return; 724 return;
724 DCHECK(textfield_->GetInputMethod()); 725 DCHECK(textfield_->GetInputMethod());
725 textfield_->GetInputMethod()->CancelComposition(textfield_); 726 textfield_->GetInputMethod()->CancelComposition(textfield_);
726 } 727 }
727 728
728 const gfx::Font& NativeTextfieldViews::GetFont() const { 729 gfx::RenderText* NativeTextfieldViews::GetRenderText() const {
729 return textfield_->font(); 730 return model_->get_render_text();
730 }
731
732 SkColor NativeTextfieldViews::GetTextColor() const {
733 return textfield_->text_color();
734 } 731 }
735 732
736 void NativeTextfieldViews::UpdateCursor() { 733 void NativeTextfieldViews::UpdateCursor() {
737 is_cursor_visible_ = !is_cursor_visible_; 734 is_cursor_visible_ = !is_cursor_visible_;
738 RepaintCursor(); 735 RepaintCursor();
739 MessageLoop::current()->PostDelayedTask( 736 MessageLoop::current()->PostDelayedTask(
740 FROM_HERE, 737 FROM_HERE,
741 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor), 738 cursor_timer_.NewRunnableMethod(&NativeTextfieldViews::UpdateCursor),
742 is_cursor_visible_ ? kCursorVisibleTimeMs : kCursorInvisibleTimeMs); 739 is_cursor_visible_ ? kCursorVisibleTimeMs : kCursorInvisibleTimeMs);
743 } 740 }
744 741
745 void NativeTextfieldViews::RepaintCursor() { 742 void NativeTextfieldViews::RepaintCursor() {
746 gfx::Rect r = cursor_bounds_; 743 gfx::Rect r(GetCaretBounds());
747 r.Inset(-1, -1, -1, -1); 744 r.Inset(-1, -1, -1, -1);
748 SchedulePaintInRect(r); 745 SchedulePaintInRect(r);
749 } 746 }
750 747
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) { 748 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) {
798 gfx::Insets insets = GetInsets();
799
800 canvas->Save(); 749 canvas->Save();
801 canvas->ClipRectInt(insets.left(), insets.top(), 750 GetRenderText()->set_cursor_visible(is_drop_cursor_visible_ ||
802 width() - insets.width(), height() - insets.height()); 751 (is_cursor_visible_ && !model_->HasSelection()));
803 752 // Draw the text, cursor, and selection.
804 // TODO(oshima): bidi support 753 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(); 754 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 } 755 }
849 756
850 bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) { 757 bool NativeTextfieldViews::HandleKeyEvent(const KeyEvent& key_event) {
851 // TODO(oshima): Refactor and consolidate with ExecuteCommand. 758 // TODO(oshima): Refactor and consolidate with ExecuteCommand.
852 if (key_event.type() == ui::ET_KEY_PRESSED) { 759 if (key_event.type() == ui::ET_KEY_PRESSED) {
853 ui::KeyboardCode key_code = key_event.key_code(); 760 ui::KeyboardCode key_code = key_event.key_code();
854 // TODO(oshima): shift-tab does not work. Figure out why and fix. 761 // TODO(oshima): shift-tab does not work. Figure out why and fix.
855 if (key_code == ui::VKEY_TAB) 762 if (key_code == ui::VKEY_TAB)
856 return false; 763 return false;
857 764
(...skipping 24 matching lines...) Expand all
882 break; 789 break;
883 case ui::VKEY_C: 790 case ui::VKEY_C:
884 if (control) 791 if (control)
885 model_->Copy(); 792 model_->Copy();
886 break; 793 break;
887 case ui::VKEY_V: 794 case ui::VKEY_V:
888 if (control && editable) 795 if (control && editable)
889 cursor_changed = text_changed = Paste(); 796 cursor_changed = text_changed = Paste();
890 break; 797 break;
891 case ui::VKEY_RIGHT: 798 case ui::VKEY_RIGHT:
892 control ? model_->MoveCursorToNextWord(selection) 799 control ? model_->MoveCursorRightByWord(selection)
893 : model_->MoveCursorRight(selection); 800 : model_->MoveCursorRight(selection);
894 cursor_changed = true; 801 cursor_changed = true;
895 break; 802 break;
896 case ui::VKEY_LEFT: 803 case ui::VKEY_LEFT:
897 control ? model_->MoveCursorToPreviousWord(selection) 804 control ? model_->MoveCursorLeftByWord(selection)
898 : model_->MoveCursorLeft(selection); 805 : model_->MoveCursorLeft(selection);
899 cursor_changed = true; 806 cursor_changed = true;
900 break; 807 break;
901 case ui::VKEY_END: 808 case ui::VKEY_END:
902 model_->MoveCursorToEnd(selection); 809 model_->MoveCursorToRightEnd(selection);
903 cursor_changed = true; 810 cursor_changed = true;
904 break; 811 break;
905 case ui::VKEY_HOME: 812 case ui::VKEY_HOME:
906 model_->MoveCursorToHome(selection); 813 model_->MoveCursorToLeftEnd(selection);
907 cursor_changed = true; 814 cursor_changed = true;
908 break; 815 break;
909 case ui::VKEY_BACK: 816 case ui::VKEY_BACK:
910 if (!editable) 817 if (!editable)
911 break; 818 break;
912 if (!model_->HasSelection()) { 819 if (!model_->HasSelection()) {
913 if (selection && control) { 820 if (selection && control) {
914 // If both shift and control are pressed, then erase upto the 821 // If both shift and control are pressed, then erase upto the
915 // beginning of the buffer in ChromeOS. In windows, do nothing. 822 // beginning of the buffer in ChromeOS. In windows, do nothing.
916 #if defined(OS_WIN) 823 #if defined(OS_WIN)
917 break; 824 break;
918 #else 825 #else
919 model_->MoveCursorToHome(true); 826 model_->MoveCursorToLeftEnd(true);
920 #endif 827 #endif
921 } else if (control) { 828 } else if (control) {
922 // If only control is pressed, then erase the previous word. 829 // If only control is pressed, then erase the previous word.
923 model_->MoveCursorToPreviousWord(true); 830 model_->MoveCursorLeftByWord(true);
924 } 831 }
925 } 832 }
926 text_changed = model_->Backspace(); 833 text_changed = model_->Backspace();
927 cursor_changed = true; 834 cursor_changed = true;
928 break; 835 break;
929 case ui::VKEY_DELETE: 836 case ui::VKEY_DELETE:
930 if (!editable) 837 if (!editable)
931 break; 838 break;
932 if (!model_->HasSelection()) { 839 if (!model_->HasSelection()) {
933 if (selection && control) { 840 if (selection && control) {
934 // If both shift and control are pressed, then erase upto the 841 // If both shift and control are pressed, then erase upto the
935 // end of the buffer in ChromeOS. In windows, do nothing. 842 // end of the buffer in ChromeOS. In windows, do nothing.
936 #if defined(OS_WIN) 843 #if defined(OS_WIN)
937 break; 844 break;
938 #else 845 #else
939 model_->MoveCursorToEnd(true); 846 model_->MoveCursorToRightEnd(true);
940 #endif 847 #endif
941 } else if (control) { 848 } else if (control) {
942 // If only control is pressed, then erase the next word. 849 // If only control is pressed, then erase the next word.
943 model_->MoveCursorToNextWord(true); 850 model_->MoveCursorRightByWord(true);
944 } 851 }
945 } 852 }
946 cursor_changed = text_changed = model_->Delete(); 853 cursor_changed = text_changed = model_->Delete();
947 break; 854 break;
948 case ui::VKEY_INSERT: 855 case ui::VKEY_INSERT:
949 insert_ = !insert_; 856 GetRenderText()->set_insert_mode(!GetRenderText()->get_insert_mode());
oshima 2011/07/15 21:21:36 may be flip_insert_mode() is simpler.
msw 2011/07/19 07:11:22 Done.
950 cursor_changed = true; 857 cursor_changed = true;
951 break; 858 break;
952 default: 859 default:
953 break; 860 break;
954 } 861 }
955 862
956 // We must have input method in order to support text input. 863 // We must have input method in order to support text input.
957 DCHECK(textfield_->GetInputMethod()); 864 DCHECK(textfield_->GetInputMethod());
958 865
959 UpdateAfterChange(text_changed, cursor_changed); 866 UpdateAfterChange(text_changed, cursor_changed);
960 OnAfterUserAction(); 867 OnAfterUserAction();
961 return (text_changed || cursor_changed); 868 return (text_changed || cursor_changed);
962 } 869 }
963 return false; 870 return false;
964 } 871 }
965 872
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 { 873 bool NativeTextfieldViews::IsPointInSelection(const gfx::Point& point) const {
999 ui::Range range; 874 ui::Range range;
1000 GetSelectedRange(&range); 875 GetSelectedRange(&range);
1001 size_t pos = FindCursorPosition(point); 876 size_t pos = GetRenderText()->FindCursorPosition(point);
1002 return (pos >= range.GetMin() && pos < range.GetMax()); 877 return (pos >= range.GetMin() && pos < range.GetMax());
1003 } 878 }
1004 879
1005 bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) { 880 bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) {
1006 size_t pos = FindCursorPosition(point); 881 size_t pos = GetRenderText()->FindCursorPosition(point);
1007 if (model_->MoveCursorTo(pos, select)) { 882 if (model_->MoveCursorTo(pos, select)) {
1008 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 883 OnCaretBoundsChanged();
1009 return true; 884 return true;
1010 } 885 }
1011 return false; 886 return false;
1012 } 887 }
1013 888
1014 void NativeTextfieldViews::PropagateTextChange() { 889 void NativeTextfieldViews::PropagateTextChange() {
1015 textfield_->SyncText(); 890 textfield_->SyncText();
1016 } 891 }
1017 892
1018 void NativeTextfieldViews::UpdateAfterChange(bool text_changed, 893 void NativeTextfieldViews::UpdateAfterChange(bool text_changed,
1019 bool cursor_changed) { 894 bool cursor_changed) {
1020 if (text_changed) 895 if (text_changed)
1021 PropagateTextChange(); 896 PropagateTextChange();
1022 if (cursor_changed) { 897 if (cursor_changed) {
1023 is_cursor_visible_ = true; 898 is_cursor_visible_ = true;
1024 RepaintCursor(); 899 RepaintCursor();
1025 } 900 }
1026 if (text_changed || cursor_changed) { 901 if (text_changed || cursor_changed) {
1027 UpdateCursorBoundsAndTextOffset(model_->cursor_pos(), insert_); 902 OnCaretBoundsChanged();
1028 SchedulePaint(); 903 SchedulePaint();
1029 } 904 }
1030 } 905 }
1031 906
1032 void NativeTextfieldViews::UpdateContextMenu() { 907 void NativeTextfieldViews::UpdateContextMenu() {
1033 if (!context_menu_contents_.get()) { 908 if (!context_menu_contents_.get()) {
1034 context_menu_contents_.reset(new ui::SimpleMenuModel(this)); 909 context_menu_contents_.reset(new ui::SimpleMenuModel(this));
1035 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); 910 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT);
1036 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); 911 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY);
1037 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); 912 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE);
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1071 TextfieldController* controller = textfield_->GetController(); 946 TextfieldController* controller = textfield_->GetController();
1072 if (controller) 947 if (controller)
1073 controller->OnAfterUserAction(textfield_); 948 controller->OnAfterUserAction(textfield_);
1074 } 949 }
1075 950
1076 bool NativeTextfieldViews::Paste() { 951 bool NativeTextfieldViews::Paste() {
1077 const bool success = model_->Paste(); 952 const bool success = model_->Paste();
1078 953
1079 // Calls TextfieldController::ContentsChanged() explicitly if the paste action 954 // Calls TextfieldController::ContentsChanged() explicitly if the paste action
1080 // did not change the content at all. See http://crbug.com/79002 955 // did not change the content at all. See http://crbug.com/79002
1081 if (success && model_->text() == textfield_->text()) { 956 if (success && GetText() == textfield_->text()) {
1082 TextfieldController* controller = textfield_->GetController(); 957 TextfieldController* controller = textfield_->GetController();
1083 if (controller) 958 if (controller)
1084 controller->ContentsChanged(textfield_, textfield_->text()); 959 controller->ContentsChanged(textfield_, textfield_->text());
1085 } 960 }
1086 return success; 961 return success;
1087 } 962 }
1088 963
1089 // static 964 // static
1090 bool NativeTextfieldViews::ShouldInsertChar(char16 ch, int flags) { 965 bool NativeTextfieldViews::ShouldInsertChar(char16 ch, int flags) {
1091 // Filter out all control characters, including tab and new line characters, 966 // 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 967 // and all characters with Alt modifier. But we need to allow characters with
1093 // AltGr modifier. 968 // AltGr modifier.
1094 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different 969 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different
1095 // flag that we don't care about. 970 // flag that we don't care about.
1096 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && 971 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) &&
1097 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN; 972 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN;
1098 } 973 }
1099 974
1100 } // namespace views 975 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698