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

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

Powered by Google App Engine
This is Rietveld 408576698