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

Side by Side Diff: chrome/browser/chromeos/text_input/candidate_window.cc

Issue 579026: Move candidate_window from third_party/chromeos_text_input to chrome/browser/chromeos/text_input. (Closed)
Patch Set: Created 10 years, 10 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
« no previous file with comments | « build/all.gyp ('k') | chrome/browser/chromeos/text_input/text_input.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009 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 // TODO(satorux):
6 // - Implement a horizontal candidate window.
7 // - Implement a scroll bar or an indicator showing where you are in the
8 // candidate window.
9
10 #include <algorithm>
11 #include <string>
12 #include <vector>
13
14 #include "app/app_paths.h"
15 #include "app/gfx/canvas.h"
16 #include "app/gfx/font.h"
17 #include "app/resource_bundle.h"
18 #include "base/at_exit.h"
19 #include "base/file_path.h"
20 #include "base/observer_list.h"
21 #include "base/path_service.h"
22 #include "base/process_util.h"
23 #include "base/scoped_ptr.h"
24 #include "base/string_util.h"
25 #include "chrome/browser/chromeos/cros/cros_library.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "third_party/cros/chromeos_cros_api.h"
28 #include "third_party/cros/chromeos_ime.h"
29 #include "views/controls/label.h"
30 #include "views/controls/textfield/textfield.h"
31 #include "views/event.h"
32 #include "views/fill_layout.h"
33 #include "views/focus/accelerator_handler.h"
34 #include "views/grid_layout.h"
35 #include "views/screen.h"
36 #include "views/widget/root_view.h"
37 #include "views/widget/widget.h"
38 #include "views/widget/widget_gtk.h"
39 #include "views/window/non_client_view.h"
40 #include "views/window/window.h"
41 #include "views/window/window_delegate.h"
42
43 namespace {
44
45 // Colors used in the candidate window UI.
46 const SkColor kFrameColor = SkColorSetRGB(0x96, 0x96, 0x96);
47 const SkColor kShortcutBackgroundColor = SkColorSetARGB(0x10, 0x3, 0x4, 0xf);
48 const SkColor kSelectedRowBackgroundColor = SkColorSetRGB(0xd1, 0xea, 0xff);
49 const SkColor kDefaultBackgroundColor = SkColorSetRGB(0xff, 0xff, 0xff);
50 const SkColor kSelectedRowFrameColor = SkColorSetRGB(0x7f, 0xac, 0xdd);
51 const SkColor kFooterTopColor = SkColorSetRGB(0xff, 0xff, 0xff);
52 const SkColor kFooterBottomColor = SkColorSetRGB(0xee, 0xee, 0xee);
53 const SkColor kShortcutColor = SkColorSetRGB(0x61, 0x61, 0x61);
54 const SkColor kDisabledShortcutColor = SkColorSetRGB(0xcc, 0xcc, 0xcc);
55
56 // The minimum width of candidate labels in the vertical candidate
57 // window. We use this value to prevent the candidate window from being
58 // too narrow when all candidates are short.
59 const int kMinCandidateLabelWidth = 100;
60
61 // Wraps the given view with some padding, and returns it.
62 views::View* WrapWithPadding(views::View* view, const gfx::Insets& insets) {
63 views::View* wrapper = new views::View;
64 // Use GridLayout to give some insets inside.
65 views::GridLayout* layout = new views::GridLayout(wrapper);
66 wrapper->SetLayoutManager(layout); // |wrapper| owns |layout|.
67 layout->SetInsets(insets);
68
69 views::ColumnSet* column_set = layout->AddColumnSet(0);
70 column_set->AddColumn(
71 views::GridLayout::FILL, views::GridLayout::FILL,
72 1, views::GridLayout::USE_PREF, 0, 0);
73 layout->StartRow(0, 0);
74
75 // Add the view contents.
76 layout->AddView(view); // |view| is owned by |wraper|, not |layout|.
77 return wrapper;
78 }
79
80 } // namespace
81
82 namespace chromeos {
83
84 class CandidateView;
85
86 // CandidateWindowView is the main container of the candidate window UI.
87 class CandidateWindowView : public views::View {
88 public:
89 // Should we show candidates vertically or horizontally?
90 enum Orientation {
91 kVertical,
92 kHorizontal,
93 };
94
95 // The object can be monitored by the observer.
96 class Observer {
97 public:
98 virtual ~Observer() {}
99 // The function is called when a candidate is committed.
100 // See comments at NotifyCandidateClicke() in chromeos_ime.h for
101 // details about the parameters.
102 virtual void OnCandidateCommitted(int index, int button, int flag) = 0;
103 };
104
105 explicit CandidateWindowView(views::Widget* parent_frame);
106 virtual ~CandidateWindowView() {}
107 void Init();
108
109 // Adds the given observer. The ownership is not transferred.
110 void AddObserver(Observer* observer) {
111 observers_.AddObserver(observer);
112 }
113
114 // Removes the given observer.
115 void RemoveObserver(Observer* observer) {
116 observers_.RemoveObserver(observer);
117 }
118
119 // Selects the candidate specified by the index in the current page
120 // (zero-origin). Changes the appearance of the selected candidate,
121 // updates the information in the candidate window as needed.
122 void SelectCandidateAt(int index_in_page);
123
124 // The function is called when a candidate is being dragged. From the
125 // given point, locates the candidate under the mouse cursor, and
126 // selects it.
127 void OnCandidateDragged(const gfx::Point& point);
128
129 // Commits the candidate currently being selected.
130 void CommitCandidate();
131
132 // Hides the auxiliary text.
133 void HideAuxiliaryText();
134
135 // Shows the auxiliary text.
136 void ShowAuxiliaryText();
137
138 // Updates the auxiliary text.
139 void UpdateAuxiliaryText(const std::string& utf8_text);
140
141 // Updates candidates of the candidate window from |lookup_table|.
142 void UpdateCandidates(const ImeLookupTable& lookup_table);
143
144 private:
145 // Initializes the candidate views if needed.
146 void MaybeInitializeCandidateViews(int num_views,
147 Orientation orientation);
148
149 // Creates the footer area, where we show status information.
150 // For instance, we show a cursor position like 2/19.
151 views::View* CreateFooterArea();
152
153 // Creates the header area, where we show auxiliary text.
154 views::View* CreateHeaderArea();
155
156 // Resizes the parent frame and schedules painting. This needs to be
157 // called when the visible contents of the candidate window are
158 // modified.
159 void ResizeAndSchedulePaint();
160
161 // The orientation of the candidate window.
162 Orientation orientation_;
163
164 // The lookup table (candidates).
165 ImeLookupTable lookup_table_;
166 // The index in the current page of the candidate currently being selected.
167 int selected_candidate_index_in_page_;
168 // The observers of the object.
169 ObserverList<Observer> observers_;
170
171 // The parent frame.
172 views::Widget* parent_frame_;
173
174 // Views created in the class will be part of tree of |this|, so these
175 // child views will be deleted when |this| is deleted.
176
177 // The candidate area is where candidates are rendered.
178 views::View* candidate_area_;
179 // The footer area is where some status messages are shown if needed.
180 views::View* footer_area_;
181 // We use this when we show something in the footer area.
182 scoped_ptr<views::View> footer_area_contents_;
183 // We use this when we show nothing in the footer area.
184 scoped_ptr<views::View> footer_area_place_holder_;
185 // The header area is where the auxiliary text is shown, if the
186 // auxiliary text is provided. If it is not provided, we show nothing.
187 // For instance, we show pinyin text like "zhong'guo", but we show
188 // nothing with Japanese IME.
189 views::View* header_area_;
190 // We use this when we show something in the header area.
191 scoped_ptr<views::View> header_area_contents_;
192 // We use this when we show nothing in the header area.
193 scoped_ptr<views::View> header_area_place_holder_;
194 // The candidate views are used for rendering candidates.
195 std::vector<CandidateView*> candidate_views_;
196 // The auxiliary text label is shown in the auxiliary text area.
197 views::Label* auxiliary_text_label_;
198 // The footer label is shown in the footer area.
199 views::Label* footer_label_;
200 };
201
202 // CandidateRow renderes a row of a candidate.
203 class CandidateView : public views::View {
204 public:
205 CandidateView(CandidateWindowView* parent_candidate_window,
206 int index_in_page,
207 CandidateWindowView::Orientation orientation);
208 virtual ~CandidateView() {}
209 void Init();
210
211 // Sets candidate text with the given text.
212 void SetCandidateText(const std::wstring& text);
213
214 // Selects the candidate row. Changes the appearance to make it look
215 // like a selected candidate.
216 void Select();
217
218 // Unselects the candidate row. Changes the appearance to make it look
219 // like an unselected candidate.
220 void Unselect();
221
222 // Disables the candidate row. Changes the appearance to make it look
223 // like unclickable area.
224 void Disable();
225
226 private:
227 // View::OnMousePressed() implementation.
228 virtual bool OnMousePressed(const views::MouseEvent& event);
229
230 // View::OnMouseDragged() implementation.
231 virtual bool OnMouseDragged(const views::MouseEvent& event);
232
233 // View::OnMouseReleased() implementation.
234 virtual void OnMouseReleased(const views::MouseEvent& event,
235 bool canceled);
236
237 // Zero-origin index in the current page.
238 int index_in_page_;
239
240 // The orientation of the candidate view.
241 CandidateWindowView::Orientation orientation_;
242
243 // The parent candidate window that contains this view.
244 CandidateWindowView* parent_candidate_window_;
245
246 // Views created in the class will be part of tree of |this|, so these
247 // child views will be deleted when |this| is deleted.
248
249 // The shortcut label renders shortcut numbers like 1, 2, and 3.
250 views::Label* shortcut_label_;
251 // The candidate label renders candidates.
252 views::Label* candidate_label_;
253 };
254
255 // VerticalCandidateLabel is used for rendering candidate text in
256 // the vertical candidate window.
257 class VerticalCandidateLabel : public views::Label {
258 virtual ~VerticalCandidateLabel() {}
259
260 // Returns the preferred size, but guarantees that the width has at
261 // least kMinCandidateLabelWidth pixels.
262 virtual gfx::Size GetPreferredSize() {
263 gfx::Size size = Label::GetPreferredSize();
264 // Hack. +2 is needed to prevent labels from getting elided like
265 // "abc..." in some cases. TODO(satorux): Figure out why it's
266 // necessary.
267 size.set_width(size.width() + 2);
268 if (size.width() < kMinCandidateLabelWidth) {
269 size.set_width(kMinCandidateLabelWidth);
270 }
271 return size;
272 }
273 };
274
275 // CandidateWindowController controls the CandidateWindow.
276 class CandidateWindowController : public CandidateWindowView::Observer {
277 public:
278 CandidateWindowController();
279 virtual ~CandidateWindowController();
280 void Init();
281
282 // Returns the work area of the monitor nearest the candidate window.
283 gfx::Rect GetMonitorWorkAreaNearestWindow();
284
285 // CandidateWindowView::Observer implementation.
286 virtual void OnCandidateCommitted(int index,
287 int button,
288 int flags);
289
290 private:
291 // Creates the candidate window view.
292 void CreateView();
293
294 // The function is called when |HideAuxiliaryText| signal is received in
295 // libcros. |ime_library| is a void pointer to this object.
296 static void OnHideAuxiliaryText(void* ime_library);
297
298 // The function is called when |HideLookupTable| signal is received in
299 // libcros. |ime_library| is a void pointer to this object.
300 static void OnHideLookupTable(void* ime_library);
301
302 // The function is called when |SetCursorLocation| signal is received
303 // in libcros. |ime_library| is a void pointer to this object.
304 static void OnSetCursorLocation(void* ime_library,
305 int x,
306 int y,
307 int width,
308 int height);
309
310 // The function is called when |UpdateAuxiliaryText| signal is received
311 // in libcros. |ime_library| is a void pointer to this object.
312 static void OnUpdateAuxiliaryText(void* ime_library,
313 const std::string& utf8_text,
314 bool visible);
315
316 // The function is called when |UpdateLookupTable| signal is received
317 // in libcros. |ime_library| is a void pointer to this object.
318 static void OnUpdateLookupTable(void* ime_library,
319 const ImeLookupTable& lookup_table);
320
321 // The connection is used for communicating with IME code in libcros.
322 ImeStatusConnection* ime_status_connection_;
323
324 // The candidate window view.
325 CandidateWindowView* candidate_window_;
326
327 // This is the outer frame of the candidate window view. The frame will
328 // own |candidate_window_|.
329 scoped_ptr<views::Widget> frame_;
330 };
331
332 CandidateView::CandidateView(
333 CandidateWindowView* parent_candidate_window,
334 int index_in_page,
335 CandidateWindowView::Orientation orientation)
336 : index_in_page_(index_in_page),
337 orientation_(orientation),
338 parent_candidate_window_(parent_candidate_window),
339 shortcut_label_(NULL),
340 candidate_label_(NULL) {
341 }
342
343 void CandidateView::Init() {
344 views::GridLayout* layout = new views::GridLayout(this);
345 SetLayoutManager(layout); // |this| owns |layout|.
346
347 // Choose the character used for the shortcut label.
348 const wchar_t kShortcutCharacters[] = L"1234567890ABCDEF";
349 // The default character should not be used but just in case.
350 wchar_t shortcut_character = L'?';
351 if (index_in_page_ < static_cast<int>(arraysize(kShortcutCharacters) - 1)) {
352 shortcut_character = kShortcutCharacters[index_in_page_];
353 }
354
355 // Create the shortcut label. The label will eventually be part of the
356 // tree of |this| via |wrapped_shortcut_label|, hence it's deleted when
357 // |this| is deleted.
358 shortcut_label_ = new views::Label(
359 StringPrintf(L"%lc", shortcut_character));
360
361 // Wrap it with padding.
362 const gfx::Insets kVerticalShortcutLabelInsets(1, 6, 1, 6);
363 const gfx::Insets kHorizontalShortcutLabelInsets(1, 1, 1, 1);
364 const gfx::Insets insets = (orientation_ == CandidateWindowView::kVertical ?
365 kVerticalShortcutLabelInsets :
366 kHorizontalShortcutLabelInsets);
367 views::View* wrapped_shortcut_label =
368 WrapWithPadding(shortcut_label_, insets);
369 // Make the font bold.
370 gfx::Font font = shortcut_label_->GetFont();
371 gfx::Font bold_font = font.DeriveFont(0, gfx::Font::BOLD);
372 shortcut_label_->SetFont(bold_font);
373 // TODO(satorux): Maybe we need to use language specific fonts for
374 // candidate_label, like Chinese font for Chinese IME?
375
376 // Add decoration based on the orientation.
377 if (orientation_ == CandidateWindowView::kVertical) {
378 // Set the background color.
379 wrapped_shortcut_label->set_background(
380 views::Background::CreateSolidBackground(
381 kShortcutBackgroundColor));
382 }
383 shortcut_label_->SetColor(kShortcutColor);
384
385 // Create the candidate label. The label will be added to |this| as a
386 // child view, hence it's deleted when |this| is deleted.
387 if (orientation_ == CandidateWindowView::kVertical) {
388 candidate_label_ = new VerticalCandidateLabel;
389 } else {
390 candidate_label_ = new views::Label;
391 }
392 candidate_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
393
394 // Initialize the column set with two columns.
395 views::ColumnSet* column_set = layout->AddColumnSet(0);
396 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
397 0, views::GridLayout::USE_PREF, 0, 0);
398 column_set->AddPaddingColumn(0, 4);
399 column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
400 0, views::GridLayout::USE_PREF, 0, 0);
401 column_set->AddPaddingColumn(0, 4);
402
403 // Add the shortcut label and the candidate label.
404 layout->StartRow(0, 0);
405 // |wrapped_shortcut_label| and |candidate_label_| will be owned by |this|.
406 layout->AddView(wrapped_shortcut_label);
407 layout->AddView(candidate_label_);
408 }
409
410 void CandidateView::SetCandidateText(const std::wstring& text) {
411 shortcut_label_->SetColor(kShortcutColor);
412 candidate_label_->SetText(text);
413 }
414
415 void CandidateView::Select() {
416 set_background(
417 views::Background::CreateSolidBackground(kSelectedRowBackgroundColor));
418 set_border(views::Border::CreateSolidBorder(1, kSelectedRowFrameColor));
419 }
420
421 void CandidateView::Unselect() {
422 set_background(NULL);
423 set_border(NULL);
424 }
425
426 void CandidateView::Disable() {
427 shortcut_label_->SetColor(kDisabledShortcutColor);
428 candidate_label_->SetText(L"");
429 }
430
431 bool CandidateView::OnMousePressed(const views::MouseEvent& event) {
432 // Select the candidate. We'll commit the candidate when the mouse
433 // button is released.
434 parent_candidate_window_->SelectCandidateAt(index_in_page_);
435 // Request MouseDraggged and MouseReleased events.
436 return true;
437 }
438
439 bool CandidateView::OnMouseDragged(const views::MouseEvent& event) {
440 gfx::Point location_in_candidate_window = event.location();
441 views::View::ConvertPointToView(this, parent_candidate_window_,
442 &location_in_candidate_window);
443 // Notify the candidate window that a candidate is now being dragged.
444 parent_candidate_window_->OnCandidateDragged(location_in_candidate_window);
445 // Request MouseReleased event.
446 return true;
447 }
448
449 void CandidateView::OnMouseReleased(const views::MouseEvent& event,
450 bool canceled) {
451 // Commit the current candidate unless it's canceled.
452 if (!canceled) {
453 parent_candidate_window_->CommitCandidate();
454 }
455 }
456
457 CandidateWindowView::CandidateWindowView(
458 views::Widget* parent_frame)
459 : orientation_(kVertical),
460 selected_candidate_index_in_page_(0),
461 parent_frame_(parent_frame),
462 candidate_area_(NULL),
463 footer_area_(NULL),
464 header_area_(NULL),
465 auxiliary_text_label_(NULL),
466 footer_label_(NULL) {
467 }
468
469 void CandidateWindowView::Init() {
470 // Set the background and the border of the view.
471 set_background(
472 views::Background::CreateSolidBackground(kDefaultBackgroundColor));
473 set_border(views::Border::CreateSolidBorder(1, kFrameColor));
474
475 // Create the header area.
476 header_area_ = CreateHeaderArea();
477 // Create the candidate area.
478 candidate_area_ = new views::View;
479 // Create the footer area.
480 footer_area_ = CreateFooterArea();
481
482 // Set the window layout of the view
483 views::GridLayout* layout = new views::GridLayout(this);
484 SetLayoutManager(layout); // |this| owns layout|.
485 views::ColumnSet* column_set = layout->AddColumnSet(0);
486 column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
487 0, views::GridLayout::USE_PREF, 0, 0);
488
489 // Add the header area.
490 layout->StartRow(0, 0);
491 layout->AddView(header_area_); // |header_area_| is owned by |this|.
492
493 // Add the candidate area.
494 layout->StartRow(0, 0);
495 layout->AddView(candidate_area_); // |candidate_area_| is owned by |this|.
496
497 // Add the footer area.
498 layout->StartRow(0, 0);
499 layout->AddView(footer_area_); // |footer_area_| is owned by |this|.
500 }
501
502 void CandidateWindowView::HideAuxiliaryText() {
503 // Put the place holder to the header area.
504 header_area_->RemoveAllChildViews(false); // Don't delete child views.
505 header_area_->AddChildView(header_area_place_holder_.get());
506 ResizeAndSchedulePaint();
507 }
508
509 void CandidateWindowView::ShowAuxiliaryText() {
510 // Put contents to the header area.
511 header_area_->RemoveAllChildViews(false); // Don't delete child views.
512 header_area_->AddChildView(header_area_contents_.get());
513 ResizeAndSchedulePaint();
514 }
515
516 void CandidateWindowView::UpdateAuxiliaryText(const std::string& utf8_text) {
517 auxiliary_text_label_->SetText(UTF8ToWide(utf8_text));
518 }
519
520 void CandidateWindowView::UpdateCandidates(
521 const ImeLookupTable& lookup_table) {
522 // HACK: ibus-pinyin sets page_size to 5. For now, we use the magic
523 // number here to determine the orientation.
524 // TODO(satorux): We should get the orientation information from
525 // lookup_table.
526 Orientation orientation = kVertical;
527 if (lookup_table.page_size == 5) {
528 orientation = kHorizontal;
529 }
530 // Initialize candidate views if necessary.
531 MaybeInitializeCandidateViews(lookup_table.page_size,
532 orientation);
533 lookup_table_ = lookup_table;
534 orientation_ = orientation;
535
536 // Update the candidates in the current page.
537 const int start_from = (lookup_table_.current_page_index *
538 lookup_table_.page_size);
539 for (size_t i = 0; i < candidate_views_.size(); ++i) {
540 const size_t index_in_page = i;
541 const size_t candidate_index = start_from + index_in_page;
542 if (candidate_index < lookup_table_.candidates.size()) {
543 candidate_views_[index_in_page]->SetCandidateText(
544 UTF8ToWide(lookup_table_.candidates[candidate_index]));
545 } else {
546 // Disable the empty row.
547 candidate_views_[index_in_page]->Disable();
548 }
549 }
550
551 // Select the current candidate per the lookup table.
552 // TODO(satorux): Rename cursor_row_index to cursor_index_in_page.
553 SelectCandidateAt(lookup_table_.cursor_row_index);
554 }
555
556 void CandidateWindowView::MaybeInitializeCandidateViews(
557 int num_views,
558 Orientation orientation) {
559 // If the requested number of views matches the number of current views,
560 // just reuse these.
561 if (num_views == static_cast<int>(candidate_views_.size()) &&
562 orientation == orientation_) {
563 return;
564 }
565
566 // Clear the existing candidate_views if any.
567 for (size_t i = 0; i < candidate_views_.size(); ++i) {
568 candidate_area_->RemoveChildView(candidate_views_[i]);
569 }
570 candidate_views_.clear();
571
572 views::GridLayout* layout = new views::GridLayout(candidate_area_);
573 // |candidate_area_| owns |layout|.
574 candidate_area_->SetLayoutManager(layout);
575 // Initialize the column set.
576 views::ColumnSet* column_set = layout->AddColumnSet(0);
577 if (orientation == kVertical) {
578 column_set->AddColumn(views::GridLayout::FILL,
579 views::GridLayout::FILL,
580 0, views::GridLayout::USE_PREF, 0, 0);
581 } else {
582 for (int i = 0; i < num_views; ++i) {
583 column_set->AddColumn(views::GridLayout::FILL,
584 views::GridLayout::FILL,
585 0, views::GridLayout::USE_PREF, 0, 0);
586 }
587 }
588
589 // Set insets so the border of the selected candidate is drawn inside of
590 // the border of the main candidate window, but we don't have the inset
591 // at the top and the bottom as we have the borders of the header and
592 // footer areas.
593 const gfx::Insets kCandidateAreaInsets(0, 1, 0, 1);
594 layout->SetInsets(kCandidateAreaInsets.top(),
595 kCandidateAreaInsets.left(),
596 kCandidateAreaInsets.bottom(),
597 kCandidateAreaInsets.right());
598
599 // Add views to the candidate area.
600 if (orientation == kHorizontal) {
601 layout->StartRow(0, 0);
602 }
603 for (int i = 0; i < num_views; ++i) {
604 CandidateView* candidate_row = new CandidateView(this, i, orientation);
605 candidate_row->Init();
606 candidate_views_.push_back(candidate_row);
607 if (orientation == kVertical) {
608 layout->StartRow(0, 0);
609 }
610 // |candidate_row| will be owned by candidate_area_|.
611 layout->AddView(candidate_row);
612 }
613 }
614
615 views::View* CandidateWindowView::CreateHeaderArea() {
616 // |header_area_place_holder_| will not be owned by another view.
617 // This will be deleted by scoped_ptr.
618 //
619 // This is because we swap the contents of |header_area_| between
620 // |header_area_place_holder_| (to show nothing) and
621 // |header_area_contents_| (to show something). In other words,
622 // |header_area_| only contains one of the two views hence cannot own
623 // the two views at the same time.
624 header_area_place_holder_.reset(new views::View);
625 header_area_place_holder_->set_parent_owned(false); // Won't be owened.
626
627 // |auxiliary_text_label_| will be owned by |header_area_contents_|.
628 auxiliary_text_label_ = new views::Label;
629 auxiliary_text_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
630
631 const gfx::Insets kHeaderInsets(2, 2, 2, 4);
632 // |header_area_contents_| will not be owned by another view.
633 // See a comment at |header_area_place_holder_| for why.
634 header_area_contents_.reset(
635 WrapWithPadding(auxiliary_text_label_, kHeaderInsets));
636 header_area_contents_->set_parent_owned(false); // Won't be owened.
637 header_area_contents_->set_border(
638 views::Border::CreateSolidBorder(1, kFrameColor));
639 header_area_contents_->set_background(
640 views::Background::CreateVerticalGradientBackground(
641 kFooterTopColor,
642 kFooterBottomColor));
643
644 views::View* header_area = new views::View;
645 header_area->SetLayoutManager(new views::FillLayout);
646 // Initialize the header area with the place holder (i.e. show nothing).
647 header_area->AddChildView(header_area_place_holder_.get());
648 return header_area;
649 }
650
651 views::View* CandidateWindowView::CreateFooterArea() {
652 // |footer_area_place_holder_| will not be owned by another view.
653 // See also the comment about |header_area_place_holder_| in
654 // CreateHeaderArea().
655 footer_area_place_holder_.reset(new views::View);
656 footer_area_place_holder_->set_parent_owned(false); // Won't be owened.
657
658 footer_label_ = new views::Label();
659 footer_label_->SetHorizontalAlignment(views::Label::ALIGN_RIGHT);
660
661 const gfx::Insets kFooterInsets(2, 2, 2, 4);
662 footer_area_contents_.reset(
663 WrapWithPadding(footer_label_, kFooterInsets));
664 footer_area_contents_->set_parent_owned(false); // Won't be owened.
665 footer_area_contents_->set_border(
666 views::Border::CreateSolidBorder(1, kFrameColor));
667 footer_area_contents_->set_background(
668 views::Background::CreateVerticalGradientBackground(
669 kFooterTopColor,
670 kFooterBottomColor));
671
672 views::View* footer_area = new views::View;
673 footer_area->SetLayoutManager(new views::FillLayout);
674 // Initialize the footer area with the place holder (i.e. show nothing).
675 footer_area->AddChildView(footer_area_place_holder_.get());
676 return footer_area;
677 }
678
679 void CandidateWindowView::SelectCandidateAt(int index_in_page) {
680 // Ignore click on out of range views.
681 if (index_in_page >= lookup_table_.num_candidates_in_current_page) {
682 return;
683 }
684
685 // Remember the currently selected candidate index in the current page.
686 selected_candidate_index_in_page_ = index_in_page;
687
688 // Unselect all the candidate first. Theoretically, we could remember
689 // the lastly selected candidate and only unselect it, but unselecting
690 // everything is simpler.
691 for (size_t i = 0; i < candidate_views_.size(); ++i) {
692 candidate_views_[i]->Unselect();
693 }
694 // Select the candidate specified by index_in_page.
695 candidate_views_[index_in_page]->Select();
696
697 // Update the cursor indexes in the model.
698 lookup_table_.cursor_row_index = index_in_page;
699 lookup_table_.cursor_absolute_index =
700 (lookup_table_.page_size * lookup_table_.current_page_index +
701 index_in_page);
702
703 // Update the footer area.
704 footer_area_->RemoveAllChildViews(false); // Don't delete child views.
705 if (orientation_ == kVertical) {
706 // Show information about the cursor and the page in the footer area.
707 footer_label_->SetText(
708 StringPrintf(L"%d/%d",
709 lookup_table_.cursor_absolute_index + 1,
710 lookup_table_.candidates.size()));
711 footer_area_->AddChildView(footer_area_contents_.get());
712 } else {
713 // Show nothing in the footer area if the orientation is horizontal.
714 footer_area_->AddChildView(footer_area_place_holder_.get());
715 }
716
717 ResizeAndSchedulePaint();
718 }
719
720 void CandidateWindowView::OnCandidateDragged(
721 const gfx::Point& location) {
722 for (size_t i = 0; i < candidate_views_.size(); ++i) {
723 gfx::Point converted_location = location;
724 views::View::ConvertPointToView(this, candidate_views_[i],
725 &converted_location);
726 if (candidate_views_[i]->HitTest(converted_location)) {
727 SelectCandidateAt(i);
728 break;
729 }
730 }
731 }
732
733 void CandidateWindowView::CommitCandidate() {
734 // For now, we don't distinguish left and right clicks.
735 const int button = 1; // Left button.
736 const int key_modifilers = 0;
737 FOR_EACH_OBSERVER(Observer, observers_,
738 OnCandidateCommitted(selected_candidate_index_in_page_,
739 button,
740 key_modifilers));
741 }
742
743 void CandidateWindowView::ResizeAndSchedulePaint() {
744 // Resize the parent frame, with the current candidate window size.
745 gfx::Size size = GetPreferredSize();
746 gfx::Rect bounds;
747 parent_frame_->GetBounds(&bounds, false);
748 bounds.set_width(size.width());
749 bounds.set_height(size.height());
750 parent_frame_->SetBounds(bounds);
751
752 SchedulePaint();
753 }
754
755 void CandidateWindowController::Init() {
756 // Initialize the IME status connection.
757 ImeStatusMonitorFunctions functions;
758 functions.hide_auxiliary_text =
759 &CandidateWindowController::OnHideAuxiliaryText;
760 functions.hide_lookup_table =
761 &CandidateWindowController::OnHideLookupTable;
762 functions.set_cursor_location =
763 &CandidateWindowController::OnSetCursorLocation;
764 functions.update_auxiliary_text =
765 &CandidateWindowController::OnUpdateAuxiliaryText;
766 functions.update_lookup_table =
767 &CandidateWindowController::OnUpdateLookupTable;
768 ime_status_connection_ = MonitorImeStatus(functions, this);
769 CHECK(ime_status_connection_)
770 << "MonitorImeStatus() failed.";
771
772 // Create the candidate window view.
773 CreateView();
774 }
775
776 void CandidateWindowController::CreateView() {
777 // Create a non-decorated frame.
778 frame_.reset(views::Widget::CreatePopupWidget(
779 views::Widget::NotTransparent,
780 views::Widget::AcceptEvents,
781 views::Widget::DeleteOnDestroy));
782 // The size is initially zero.
783 frame_->Init(NULL, gfx::Rect(0, 0));
784
785 // Create the candidate window.
786 candidate_window_ = new CandidateWindowView(frame_.get());
787 candidate_window_->Init();
788 candidate_window_->AddObserver(this);
789
790 // Put the candidate window view on the frame. The frame is resized
791 // later when the candidate window is shown.
792 views::RootView* root_view = frame_->GetRootView();
793 // |root_view| owns the |candidate_window_|, thus |frame_| effectively
794 // owns |candidate_window_|.
795 root_view->SetContentsView(candidate_window_);
796 }
797
798 CandidateWindowController::CandidateWindowController()
799 : ime_status_connection_(NULL),
800 frame_(NULL) {
801 }
802
803 CandidateWindowController::~CandidateWindowController() {
804 candidate_window_->RemoveObserver(this);
805 chromeos::DisconnectImeStatus(ime_status_connection_);
806 }
807
808 gfx::Rect CandidateWindowController::GetMonitorWorkAreaNearestWindow() {
809 return views::Screen::GetMonitorWorkAreaNearestWindow(
810 frame_->GetNativeView());
811 }
812
813 void CandidateWindowController::OnHideAuxiliaryText(void* ime_library) {
814 CandidateWindowController* controller =
815 static_cast<CandidateWindowController*>(ime_library);
816
817 controller->candidate_window_->HideAuxiliaryText();
818 }
819
820 void CandidateWindowController::OnHideLookupTable(void* ime_library) {
821 CandidateWindowController* controller =
822 static_cast<CandidateWindowController*>(ime_library);
823
824 controller->frame_->Hide();
825 }
826
827 void CandidateWindowController::OnSetCursorLocation(void* ime_library,
828 int x,
829 int y,
830 int width,
831 int height) {
832 CandidateWindowController* controller =
833 static_cast<CandidateWindowController*>(ime_library);
834
835 // TODO(satorux): This has to be computed runtime.
836 const int kHorizontalOffset = 30;
837
838 gfx::Rect frame_bounds;
839 controller->frame_->GetBounds(&frame_bounds, false);
840
841 gfx::Rect screen_bounds =
842 controller->GetMonitorWorkAreaNearestWindow();
843
844 // The default position.
845 frame_bounds.set_x(x - kHorizontalOffset);
846 frame_bounds.set_y(y + height);
847
848 // Handle overflow at the left and the top.
849 frame_bounds.set_x(std::max(frame_bounds.x(), screen_bounds.x()));
850 frame_bounds.set_y(std::max(frame_bounds.y(), screen_bounds.y()));
851
852 // Handle overflow at the right.
853 const int right_overflow = frame_bounds.right() - screen_bounds.right();
854 if (right_overflow > 0) {
855 frame_bounds.set_x(frame_bounds.x() - right_overflow);
856 }
857
858 // Handle overflow at the bottom.
859 const int bottom_overflow = frame_bounds.bottom() - screen_bounds.bottom();
860 if (bottom_overflow > 0) {
861 frame_bounds.set_y(frame_bounds.y() - height - frame_bounds.height());
862 }
863
864 controller->frame_->SetBounds(frame_bounds);
865 }
866
867 void CandidateWindowController::OnUpdateAuxiliaryText(
868 void* ime_library,
869 const std::string& utf8_text,
870 bool visible) {
871 CandidateWindowController* controller =
872 static_cast<CandidateWindowController*>(ime_library);
873 // HACK for ibus-anthy: ibus-anthy sends us page information like
874 // "( 1 / 19 )" as auxiliary text. We should ignore this as we show the
875 // same information in the footer area (i.e. don't want to show the same
876 // information in two places).
877 //
878 // TODO(satorux): Remove this once we remove ibus-anthy from Chromium OS.
879 if (utf8_text.size() >= 2 &&
880 utf8_text[0] == '(' && utf8_text[utf8_text.size() -1] == ')') {
881 // Hide the auxiliary text in case something is shown already.
882 controller->candidate_window_->HideAuxiliaryText();
883 return; // Ignore the given auxiliary text.
884 }
885
886 // If it's not visible, hide the auxiliary text and return.
887 if (!visible) {
888 controller->candidate_window_->HideAuxiliaryText();
889 return;
890 }
891 controller->candidate_window_->UpdateAuxiliaryText(utf8_text);
892 controller->candidate_window_->ShowAuxiliaryText();
893 }
894
895 void CandidateWindowController::OnUpdateLookupTable(
896 void* ime_library,
897 const ImeLookupTable& lookup_table) {
898 CandidateWindowController* controller =
899 static_cast<CandidateWindowController*>(ime_library);
900
901 // If it's not visible, hide the window and return.
902 if (!lookup_table.visible) {
903 controller->frame_->Hide();
904 return;
905 }
906
907 controller->candidate_window_->UpdateCandidates(lookup_table);
908 controller->frame_->Show();
909 }
910
911 void CandidateWindowController::OnCandidateCommitted(int index,
912 int button,
913 int flags) {
914 NotifyCandidateClicked(ime_status_connection_, index, button, flags);
915 }
916
917 } // namespace chromeos
918
919 int main(int argc, char** argv) {
920 // Initialize gtk stuff.
921 g_thread_init(NULL);
922 g_type_init();
923 gtk_init(&argc, &argv);
924
925 // Initialize Chrome stuff.
926 base::AtExitManager exit_manager;
927 base::EnableTerminationOnHeapCorruption();
928 app::RegisterPathProvider();
929 ResourceBundle::InitSharedInstance(L"en-US");
930
931 // Load libcros.
932 chrome::RegisterPathProvider(); // for libcros.so.
933 CHECK(chromeos::CrosLibrary::EnsureLoaded())
934 << "Failed to load libcros";
935
936 // Create the main message loop.
937 MessageLoop main_message_loop(MessageLoop::TYPE_UI);
938
939 // Create the candidate window controller.
940 chromeos::CandidateWindowController controller;
941 controller.Init();
942
943 // Start the main loop.
944 views::AcceleratorHandler accelerator_handler;
945 MessageLoopForUI::current()->Run(&accelerator_handler);
946
947 return 0;
948 }
OLDNEW
« no previous file with comments | « build/all.gyp ('k') | chrome/browser/chromeos/text_input/text_input.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698