OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "views/controls/textfield/textfield_view.h" | |
6 | |
7 namespace views { | |
8 | |
9 const char TextfieldView::kViewClassName[] = "views/TextfieldView"; | |
10 | |
11 TextfieldView::TextfieldView() { | |
12 Init(L""); | |
13 } | |
14 | |
15 TextfieldView::TextfieldView(const std::wstring& text) { | |
16 Init(text); | |
17 } | |
18 | |
19 TextfieldView::TextfieldView(TextfieldModel* model) { | |
20 Init(L""); | |
21 model_.reset(model); | |
22 } | |
23 | |
24 void TextfieldView::Init(const std::wstring& text) { | |
25 controller_.reset(NULL); | |
26 selection_on_ = false; | |
27 model_.reset(new TextfieldModel(text)); | |
28 set_background(Background::CreateSolidBackground(255, 255, 255, 255)); | |
29 SetHorizontalAlignment(ALIGN_LEFT); | |
30 this->SetFocusable(true); | |
31 RequestFocus(); | |
32 onscreen_cursor_pos_ = font().GetStringWidth(text); | |
33 } | |
34 | |
35 void TextfieldView::SetController(TextfieldController* controller) { | |
36 controller_.reset(controller); | |
37 } | |
38 | |
39 //////////////////////////////////////////////////////////////////////////////// | |
40 // View overrides: | |
41 | |
42 bool TextfieldView::SkipDefaultKeyEventProcessing(const views::KeyEvent& e) { | |
43 return true; | |
44 } | |
45 | |
46 // TODO(varunjain): implement mouse and focus events related methods. | |
47 bool TextfieldView::OnMousePressed(const views::MouseEvent& e) { | |
48 return true; | |
49 } | |
50 | |
51 bool TextfieldView::OnMouseDragged(const views::MouseEvent& e) { | |
52 return true; | |
53 } | |
54 | |
55 void TextfieldView::OnMouseReleased(const views::MouseEvent& e, bool canceled) { | |
56 RequestFocus(); | |
57 } | |
58 | |
59 bool TextfieldView::OnKeyPressed(const views::KeyEvent& e) { | |
60 bool handled = HandleKeyEvent(e); | |
61 this->SetText(model_->text()); | |
62 SchedulePaint(); | |
63 return handled; | |
64 } | |
65 | |
66 bool TextfieldView::OnKeyReleased(const views::KeyEvent& e) { | |
67 return true; | |
68 } | |
69 | |
70 void TextfieldView::WillGainFocus() { | |
71 } | |
72 | |
73 void TextfieldView::DidGainFocus() { | |
74 } | |
75 | |
76 void TextfieldView::WillLoseFocus() { | |
77 } | |
78 | |
79 std::wstring TextfieldView::GetText() { | |
80 return model_->text(); | |
81 } | |
82 | |
83 gfx::Rect TextfieldView::GetTextBounds() { | |
84 gfx::Insets insets = View::GetInsets(); | |
85 int text_height = font().GetHeight(); | |
86 int text_y = std::max(0, (bounds().height() - text_height)) / 2; | |
87 gfx::Rect text_bounds(insets.left(), text_y, | |
88 bounds().width() - insets.width(), text_height); | |
89 return text_bounds; | |
90 } | |
91 | |
92 void TextfieldView::PaintTextAndCursor(gfx::Canvas* canvas) { | |
93 // TODO(varunjain): re-implement this so that only dirty text is painted. | |
94 std::wstring str = model_->text(); | |
95 gfx::Rect text_bounds = GetTextBounds(); | |
96 int onscreen_cursor_pos = onscreen_cursor_pos_ + View::GetInsets().left(); | |
97 | |
98 // Get string to the left of cursor. | |
99 std::wstring left_string = GetStringToLeftOfCursor(str, | |
100 model_->GetCurrentCursorPos(), onscreen_cursor_pos, text_bounds); | |
101 int left_str_width = font().GetStringWidth(left_string); | |
102 | |
103 // Get string to the right of cursor. | |
104 std::wstring right_string = GetStringToRightOfCursor(str, | |
105 model_->GetCurrentCursorPos(), onscreen_cursor_pos, text_bounds); | |
106 | |
107 // Paint the complete string. | |
108 visible_text_ = left_string + right_string; | |
109 canvas->DrawStringInt(visible_text_, font(), GetColor(), text_bounds.x(), | |
110 text_bounds.y(), text_bounds.width(), text_bounds.height(), 0); | |
111 | |
112 // Paint Cursor. | |
113 if (HasFocus()) { | |
114 canvas->DrawLineInt(SK_ColorBLUE, text_bounds.x() + left_str_width, 0, | |
115 text_bounds.x() + left_str_width, bounds().height()); | |
116 } | |
117 } | |
118 | |
119 void TextfieldView::Paint(gfx::Canvas* canvas) { | |
120 set_border(HasFocus() ? Border::CreateSolidBorder(2, SK_ColorGRAY) | |
121 : Border::CreateSolidBorder(1, SK_ColorBLACK)); | |
122 PaintBackground(canvas); | |
123 PaintTextAndCursor(canvas); | |
124 PaintBorder(canvas); | |
125 } | |
126 | |
127 std::wstring TextfieldView::GetStringToLeftOfCursor(std::wstring text, | |
128 int cursor_pos, int onscreen_cursor_pos, gfx::Rect text_bounds) { | |
129 std::wstring str = L""; | |
tfarina
2010/11/18 11:55:31
I think when you do: std::wstring str; it is alrea
| |
130 int available_size = onscreen_cursor_pos - text_bounds.x(); | |
131 for (int pos = cursor_pos; pos > 0; pos--) { | |
tfarina
2010/11/18 11:55:31
--pos
| |
132 if (font().GetStringWidth(str + text[pos - 1]) <= available_size) { | |
133 str += text[pos - 1]; | |
134 } else { | |
135 break; | |
136 } | |
137 } | |
138 for (unsigned i = 0; i < str.length() / 2.0; i++) { | |
tfarina
2010/11/18 11:55:31
++i
| |
139 wchar_t temp = str[i]; | |
140 str[i] = str[str.length() - i - 1]; | |
141 str[str.length() - i - 1] = temp; | |
142 } | |
143 return str; | |
144 } | |
145 | |
146 std::wstring TextfieldView::GetStringToRightOfCursor(std::wstring text, | |
147 int cursor_pos, int onscreen_cursor_pos, gfx::Rect text_bounds) { | |
148 std::wstring str; | |
149 int available_size = text_bounds.x() + text_bounds.width() | |
150 - onscreen_cursor_pos; | |
151 for (unsigned pos = cursor_pos; pos < text.length(); pos++) { | |
tfarina
2010/11/18 11:55:31
++pos
| |
152 if (font().GetStringWidth(str + text[pos]) <= available_size) { | |
153 str += text[pos]; | |
154 } else { | |
155 break; | |
156 } | |
157 } | |
158 return str; | |
159 } | |
160 | |
161 bool TextfieldView::HandleKeyEvent(const KeyEvent& key_event) { | |
162 int current_cursor_pos = model_->GetCurrentCursorPos(); | |
163 std::wstring removed_str = L""; | |
164 bool need_cursor_repositioning = true; | |
165 bool text_changed = false; | |
166 if (key_event.GetType() == views::Event::ET_KEY_PRESSED) { | |
167 app::KeyboardCode key_code = key_event.GetKeyCode(); | |
168 if (key_code == app::VKEY_TAB) { | |
169 return false; | |
170 } | |
171 switch (key_code) { | |
172 case app::VKEY_RIGHT: | |
173 key_event.IsControlDown() ? model_->MoveCursorToNextWord() | |
174 : model_->MoveCursorRight(); | |
175 break; | |
176 case app::VKEY_LEFT: | |
177 key_event.IsControlDown() ? model_->MoveCursorToPreviousWord() | |
178 : model_->MoveCursorLeft(); | |
179 break; | |
180 case app::VKEY_END: | |
181 model_->MoveCursorToEnd(); | |
182 break; | |
183 case app::VKEY_HOME: | |
184 model_->MoveCursorToStart(); | |
185 break; | |
186 case app::VKEY_BACK: | |
187 removed_str += model_->InsertBackspace(); | |
188 if (font().GetStringWidth(model_->text()) | |
189 <= bounds().width() - View::GetInsets().width()) { | |
190 SafeDecCursorPos(font().GetStringWidth(removed_str)); | |
191 } | |
192 need_cursor_repositioning = false; | |
193 break; | |
194 case app::VKEY_DELETE: | |
195 removed_str += model_->InsertDelete(); | |
196 default: | |
197 break; | |
198 } | |
199 if (IsWritable(key_code)) { | |
200 model_->Insert(GetWritableChar(key_event)); | |
201 text_changed = true; | |
202 } | |
203 if (key_event.IsShiftDown() && !selection_on_ && | |
204 IsDirectionalKey(key_code)) { | |
205 selection_on_ = true; | |
206 selection_start_ = model_->GetCurrentCursorPos(); | |
207 } | |
208 if (removed_str.length() > 0) { | |
209 text_changed = true; | |
210 } | |
211 | |
212 // Move the onscreen cursor to new position if required. | |
213 if (need_cursor_repositioning) { | |
214 std::wstring text = model_->text(); | |
215 int new_cursor_pos = model_->GetCurrentCursorPos(); | |
216 std::wstring str_diff; | |
217 if (new_cursor_pos > current_cursor_pos) { | |
218 for (int i = current_cursor_pos; i < new_cursor_pos; i++) { | |
219 str_diff += text[i]; | |
220 } | |
221 SafeIncCursorPos(font().GetStringWidth(str_diff)); | |
222 } else if (new_cursor_pos < current_cursor_pos) { | |
223 for (int i = new_cursor_pos; i < current_cursor_pos; i++) { | |
224 str_diff += text[i]; | |
225 } | |
226 SafeDecCursorPos(font().GetStringWidth(str_diff)); | |
227 } | |
228 } | |
229 | |
230 // Send notification to the controller if text has changed. | |
231 if (controller_.get() && text_changed) { | |
232 controller_->TextChanged(model_->text()); | |
233 } | |
234 } | |
235 return true; | |
236 } | |
237 | |
238 void TextfieldView::SafeIncCursorPos(int inc) { | |
239 if (onscreen_cursor_pos_ + inc | |
240 < bounds().width() - View::GetInsets().width()) { | |
tfarina
2010/11/18 11:55:31
put this < in the line above?
| |
241 onscreen_cursor_pos_ += inc; | |
242 } else { | |
243 onscreen_cursor_pos_ = bounds().width() - View::GetInsets().width(); | |
244 } | |
245 } | |
246 | |
247 void TextfieldView::SafeDecCursorPos(int dec) { | |
248 if (onscreen_cursor_pos_ - dec > 0) { | |
249 onscreen_cursor_pos_ -= dec; | |
250 } else { | |
251 onscreen_cursor_pos_ = 0; | |
252 } | |
253 } | |
254 | |
255 bool TextfieldView::IsDirectionalKey(app::KeyboardCode key_code) { | |
256 switch (key_code) { | |
257 case app::VKEY_RIGHT: | |
258 case app::VKEY_LEFT: | |
259 case app::VKEY_END: | |
260 case app::VKEY_HOME: | |
261 return true; | |
262 break; | |
tfarina
2010/11/18 11:55:31
I think the break is not necessary here, it won't
| |
263 default: | |
264 return false; | |
265 } | |
266 } | |
267 | |
268 bool TextfieldView::IsWritable(app::KeyboardCode key_code) { | |
269 return (key_code >= app::VKEY_0 && key_code <= app::VKEY_Z) | |
270 || (key_code >= app::VKEY_NUMPAD0 && key_code <= app::VKEY_DIVIDE) | |
tfarina
2010/11/18 11:55:31
Please, fix all the occurrences in this file. This
| |
271 || (key_code >= app::VKEY_OEM_1 && key_code <= app::VKEY_OEM_8) | |
272 || key_code == app::VKEY_SPACE; | |
273 } | |
274 | |
275 wchar_t TextfieldView::GetWritableChar(const KeyEvent& key_event) { | |
276 app::KeyboardCode key_code = key_event.GetKeyCode(); | |
277 bool shift = false; | |
278 if ((key_code >= app::VKEY_0 && key_code <= app::VKEY_9) | |
279 || (key_code >= app::VKEY_OEM_1 && key_code <= app::VKEY_OEM_8)) { | |
280 shift = key_event.IsShiftDown(); | |
281 } else if (key_code >= app::VKEY_A && key_code <= app::VKEY_Z) { | |
282 shift = (key_event.IsLockDown() && !key_event.IsShiftDown()) | |
283 || (!key_event.IsLockDown() && key_event.IsShiftDown()); | |
284 } | |
285 return GetWritableChar(key_code, shift); | |
286 } | |
287 | |
288 wchar_t TextfieldView::GetWritableChar(app::KeyboardCode key_code, | |
289 bool shift) { | |
tfarina
2010/11/18 11:55:31
You can align shift with app::KeyboardCode. Also p
| |
290 switch (key_code) { | |
291 case app::VKEY_NUMPAD0: | |
292 return '0'; | |
293 case app::VKEY_NUMPAD1: | |
294 return '1'; | |
295 case app::VKEY_NUMPAD2: | |
296 return '2'; | |
297 case app::VKEY_NUMPAD3: | |
298 return '3'; | |
299 case app::VKEY_NUMPAD4: | |
300 return '4'; | |
301 case app::VKEY_NUMPAD5: | |
302 return '5'; | |
303 case app::VKEY_NUMPAD6: | |
304 return '6'; | |
305 case app::VKEY_NUMPAD7: | |
306 return '7'; | |
307 case app::VKEY_NUMPAD8: | |
308 return '8'; | |
309 case app::VKEY_NUMPAD9: | |
310 return '9'; | |
311 case app::VKEY_MULTIPLY: | |
312 return '*'; | |
313 case app::VKEY_ADD: | |
314 return '+'; | |
315 case app::VKEY_SUBTRACT: | |
316 return '-'; | |
317 case app::VKEY_DECIMAL: | |
318 return '.'; | |
319 case app::VKEY_DIVIDE: | |
320 return '/'; | |
321 | |
322 // case app::VKEY_BACK: | |
tfarina
2010/11/18 11:55:31
Why this big block of comment? If it isn't used. P
| |
323 // return GDK_BackSpace; | |
324 // case app::VKEY_TAB: | |
325 // return shift ? GDK_ISO_Left_Tab : GDK_Tab; | |
326 // case app::VKEY_CLEAR: | |
327 // return GDK_Clear; | |
328 // case app::VKEY_RETURN: | |
329 // return GDK_Return; | |
330 // case app::VKEY_SHIFT: | |
331 // return GDK_Shift_L; | |
332 // case app::VKEY_CONTROL: | |
333 // return GDK_Control_L; | |
334 // case app::VKEY_MENU: | |
335 // return GDK_Alt_L; | |
336 // case app::VKEY_APPS: | |
337 // return GDK_Menu; | |
338 // | |
339 // case app::VKEY_PAUSE: | |
340 // return GDK_Pause; | |
341 // case app::VKEY_CAPITAL: | |
342 // return GDK_Caps_Lock; | |
343 // case app::VKEY_KANA: | |
344 // return GDK_Kana_Lock; | |
345 // case app::VKEY_HANJA: | |
346 // return GDK_Hangul_Hanja; | |
347 // case app::VKEY_ESCAPE: | |
348 // return GDK_Escape; | |
349 case app::VKEY_SPACE: | |
350 return ' '; | |
351 // case app::VKEY_PRIOR: | |
352 // return GDK_Page_Up; | |
353 // case app::VKEY_NEXT: | |
354 // return GDK_Page_Down; | |
355 // case app::VKEY_END: | |
356 // return GDK_End; | |
357 // case app::VKEY_HOME: | |
358 // return GDK_Home; | |
359 // case app::VKEY_LEFT: | |
360 // return GDK_Left; | |
361 // case app::VKEY_UP: | |
362 // return GDK_Up; | |
363 // case app::VKEY_RIGHT: | |
364 // return GDK_Right; | |
365 // case app::VKEY_DOWN: | |
366 // return GDK_Down; | |
367 // case app::VKEY_SELECT: | |
368 // return GDK_Select; | |
369 // case app::VKEY_PRINT: | |
370 // return GDK_Print; | |
371 // case app::VKEY_EXECUTE: | |
372 // return GDK_Execute; | |
373 // case app::VKEY_INSERT: | |
374 // return GDK_Insert; | |
375 // case app::VKEY_DELETE: | |
376 // return GDK_Delete; | |
377 // case app::VKEY_HELP: | |
378 // return GDK_Help; | |
379 case app::VKEY_0: | |
380 return shift ? ')' : '0'; | |
381 case app::VKEY_1: | |
382 return shift ? '!' : '1'; | |
383 case app::VKEY_2: | |
384 return shift ? '@' : '2'; | |
385 case app::VKEY_3: | |
386 return shift ? '#' : '3'; | |
387 case app::VKEY_4: | |
388 return shift ? '$' : '4'; | |
389 case app::VKEY_5: | |
390 return shift ? '%' : '5'; | |
391 case app::VKEY_6: | |
392 return shift ? '^' : '6'; | |
393 case app::VKEY_7: | |
394 return shift ? '&' : '7'; | |
395 case app::VKEY_8: | |
396 return shift ? '*' : '8'; | |
397 case app::VKEY_9: | |
398 return shift ? '(' : '9'; | |
399 | |
400 case app::VKEY_A: | |
401 case app::VKEY_B: | |
402 case app::VKEY_C: | |
403 case app::VKEY_D: | |
404 case app::VKEY_E: | |
405 case app::VKEY_F: | |
406 case app::VKEY_G: | |
407 case app::VKEY_H: | |
408 case app::VKEY_I: | |
409 case app::VKEY_J: | |
410 case app::VKEY_K: | |
411 case app::VKEY_L: | |
412 case app::VKEY_M: | |
413 case app::VKEY_N: | |
414 case app::VKEY_O: | |
415 case app::VKEY_P: | |
416 case app::VKEY_Q: | |
417 case app::VKEY_R: | |
418 case app::VKEY_S: | |
419 case app::VKEY_T: | |
420 case app::VKEY_U: | |
421 case app::VKEY_V: | |
422 case app::VKEY_W: | |
423 case app::VKEY_X: | |
424 case app::VKEY_Y: | |
425 case app::VKEY_Z: | |
426 return (shift ? 'A' : 'a') + (key_code - app::VKEY_A); | |
427 | |
428 // case app::VKEY_LWIN: | |
429 // return GDK_Meta_L; | |
430 // case app::VKEY_RWIN: | |
431 // return GDK_Meta_R; | |
432 // | |
433 // case app::VKEY_NUMLOCK: | |
434 // return GDK_Num_Lock; | |
435 // | |
436 // case app::VKEY_SCROLL: | |
437 // return GDK_Scroll_Lock; | |
438 // | |
439 case app::VKEY_OEM_1: | |
440 return shift ? ':' : ';'; | |
441 case app::VKEY_OEM_PLUS: | |
442 return shift ? '+' : '='; | |
443 case app::VKEY_OEM_COMMA: | |
444 return shift ? '<' : ','; | |
445 case app::VKEY_OEM_MINUS: | |
446 return shift ? '_' : '-'; | |
447 case app::VKEY_OEM_PERIOD: | |
448 return shift ? '>' : '.'; | |
449 case app::VKEY_OEM_2: | |
450 return shift ? '?' : '/'; | |
451 case app::VKEY_OEM_3: | |
452 return shift ? '~' : '`'; | |
453 case app::VKEY_OEM_4: | |
454 return shift ? '}' : ']'; | |
455 case app::VKEY_OEM_5: | |
456 return shift ? '|' : '\\'; | |
457 case app::VKEY_OEM_6: | |
458 return shift ? '{' : '['; | |
459 case app::VKEY_OEM_7: | |
460 return shift ? '"' : '\''; | |
461 // | |
462 // case app::VKEY_F1: | |
463 // case app::VKEY_F2: | |
464 // case app::VKEY_F3: | |
465 // case app::VKEY_F4: | |
466 // case app::VKEY_F5: | |
467 // case app::VKEY_F6: | |
468 // case app::VKEY_F7: | |
469 // case app::VKEY_F8: | |
470 // case app::VKEY_F9: | |
471 // case app::VKEY_F10: | |
472 // case app::VKEY_F11: | |
473 // case app::VKEY_F12: | |
474 // case app::VKEY_F13: | |
475 // case app::VKEY_F14: | |
476 // case app::VKEY_F15: | |
477 // case app::VKEY_F16: | |
478 // case app::VKEY_F17: | |
479 // case app::VKEY_F18: | |
480 // case app::VKEY_F19: | |
481 // case app::VKEY_F20: | |
482 // case app::VKEY_F21: | |
483 // case app::VKEY_F22: | |
484 // case app::VKEY_F23: | |
485 // case app::VKEY_F24: | |
486 // return GDK_F1 + (keycode - app::VKEY_F1); | |
487 | |
488 default: | |
489 return 0; | |
490 } | |
491 } | |
492 } | |
OLD | NEW |