input_window_dialog_win.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
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 "chrome/browser/ui/input_window_dialog.h"
6
7#include "base/compiler_specific.h"
8#include "base/message_loop.h"
9#include "base/task.h"
10#include "views/grid_layout.h"
11#include "views/controls/label.h"
12#include "views/controls/textfield/textfield.h"
13#include "views/standard_layout.h"
14#include "views/window/dialog_delegate.h"
15#include "views/window/window.h"
16#include "grit/generated_resources.h"
17
18// Width to make the text field, in pixels.
19static const int kTextfieldWidth = 200;
20
21class ContentView;
22
23// The Windows implementation of the cross platform input dialog interface.
24class WinInputWindowDialog : public InputWindowDialog {
25 public:
26  WinInputWindowDialog(HWND parent,
27                       const std::wstring& window_title,
28                       const std::wstring& label,
29                       const std::wstring& contents,
30                       Delegate* delegate);
31  virtual ~WinInputWindowDialog();
32
33  virtual void Show();
34  virtual void Close();
35
36  const std::wstring& window_title() const { return window_title_; }
37  const std::wstring& label() const { return label_; }
38  const std::wstring& contents() const { return contents_; }
39
40  InputWindowDialog::Delegate* delegate() { return delegate_.get(); }
41
42 private:
43  // Our chrome views window.
44  views::Window* window_;
45
46  // Strings to feed to the on screen window.
47  std::wstring window_title_;
48  std::wstring label_;
49  std::wstring contents_;
50
51  // Our delegate. Consumes the window's output.
52  scoped_ptr<InputWindowDialog::Delegate> delegate_;
53};
54
55// ContentView, as the name implies, is the content view for the InputWindow.
56// It registers accelerators that accept/cancel the input.
57class ContentView : public views::View,
58                    public views::DialogDelegate,
59                    public views::Textfield::Controller {
60 public:
61  explicit ContentView(WinInputWindowDialog* delegate)
62      : delegate_(delegate),
63        ALLOW_THIS_IN_INITIALIZER_LIST(focus_grabber_factory_(this)) {
64    DCHECK(delegate_);
65  }
66
67  // views::DialogDelegate overrides:
68  virtual bool IsDialogButtonEnabled(
69      MessageBoxFlags::DialogButton button) const;
70  virtual bool Accept();
71  virtual bool Cancel();
72  virtual void DeleteDelegate();
73  virtual std::wstring GetWindowTitle() const;
74  virtual bool IsModal() const { return true; }
75  virtual views::View* GetContentsView();
76
77  // views::Textfield::Controller overrides:
78  virtual void ContentsChanged(views::Textfield* sender,
79                               const std::wstring& new_contents);
80  virtual bool HandleKeystroke(views::Textfield*,
81                               const views::Textfield::Keystroke&) {
82    return false;
83  }
84
85 protected:
86  // views::View overrides:
87  virtual void ViewHierarchyChanged(bool is_add, views::View* parent,
88                                    views::View* child);
89
90 private:
91  // Set up dialog controls and layout.
92  void InitControlLayout();
93
94  // Sets focus to the first focusable element within the dialog.
95  void FocusFirstFocusableControl();
96
97  // The Textfield that the user can type into.
98  views::Textfield* text_field_;
99
100  // The delegate that the ContentView uses to communicate changes to the
101  // caller.
102  WinInputWindowDialog* delegate_;
103
104  // Helps us set focus to the first Textfield in the window.
105  ScopedRunnableMethodFactory<ContentView> focus_grabber_factory_;
106
107  DISALLOW_COPY_AND_ASSIGN(ContentView);
108};
109
110///////////////////////////////////////////////////////////////////////////////
111// ContentView, views::DialogDelegate implementation:
112
113bool ContentView::IsDialogButtonEnabled(
114    MessageBoxFlags::DialogButton button) const {
115  if (button == MessageBoxFlags::DIALOGBUTTON_OK &&
116      !delegate_->delegate()->IsValid(text_field_->text())) {
117    return false;
118  }
119  return true;
120}
121
122bool ContentView::Accept() {
123  delegate_->delegate()->InputAccepted(text_field_->text());
124  return true;
125}
126
127bool ContentView::Cancel() {
128  delegate_->delegate()->InputCanceled();
129  return true;
130}
131
132void ContentView::DeleteDelegate() {
133  delete delegate_;
134}
135
136std::wstring ContentView::GetWindowTitle() const {
137  return delegate_->window_title();
138}
139
140views::View* ContentView::GetContentsView() {
141  return this;
142}
143
144///////////////////////////////////////////////////////////////////////////////
145// ContentView, views::Textfield::Controller implementation:
146
147void ContentView::ContentsChanged(views::Textfield* sender,
148                                  const std::wstring& new_contents) {
149  GetDialogClientView()->UpdateDialogButtons();
150}
151
152///////////////////////////////////////////////////////////////////////////////
153// ContentView, protected:
154
155void ContentView::ViewHierarchyChanged(bool is_add,
156                                       views::View* parent,
157                                       views::View* child) {
158  if (is_add && child == this)
159    InitControlLayout();
160}
161
162///////////////////////////////////////////////////////////////////////////////
163// ContentView, private:
164
165void ContentView::InitControlLayout() {
166  text_field_ = new views::Textfield;
167  text_field_->SetText(delegate_->contents());
168  text_field_->SetController(this);
169
170  using views::ColumnSet;
171  using views::GridLayout;
172
173  // TODO(sky): Vertical alignment should be baseline.
174  GridLayout* layout = CreatePanelGridLayout(this);
175  SetLayoutManager(layout);
176
177  ColumnSet* c1 = layout->AddColumnSet(0);
178  c1->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
179                GridLayout::USE_PREF, 0, 0);
180  c1->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
181  c1->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
182                GridLayout::USE_PREF, kTextfieldWidth, kTextfieldWidth);
183
184  layout->StartRow(0, 0);
185  views::Label* label = new views::Label(delegate_->label());
186  layout->AddView(label);
187  layout->AddView(text_field_);
188
189  MessageLoop::current()->PostTask(FROM_HERE,
190      focus_grabber_factory_.NewRunnableMethod(
191          &ContentView::FocusFirstFocusableControl));
192}
193
194void ContentView::FocusFirstFocusableControl() {
195  text_field_->SelectAll();
196  text_field_->RequestFocus();
197}
198
199WinInputWindowDialog::WinInputWindowDialog(HWND parent,
200                                           const std::wstring& window_title,
201                                           const std::wstring& label,
202                                           const std::wstring& contents,
203                                           Delegate* delegate)
204    : window_title_(window_title),
205      label_(label),
206      contents_(contents),
207      delegate_(delegate) {
208  window_ = views::Window::CreateChromeWindow(parent, gfx::Rect(),
209                                              new ContentView(this));
210  window_->GetClientView()->AsDialogClientView()->UpdateDialogButtons();
211}
212
213WinInputWindowDialog::~WinInputWindowDialog() {
214}
215
216void WinInputWindowDialog::Show() {
217  window_->Show();
218}
219
220void WinInputWindowDialog::Close() {
221  window_->Close();
222}
223
224// static
225InputWindowDialog* InputWindowDialog::Create(HWND parent,
226                                             const std::wstring& window_title,
227                                             const std::wstring& label,
228                                             const std::wstring& contents,
229                                             Delegate* delegate) {
230  return new WinInputWindowDialog(parent,
231                                  window_title,
232                                  label,
233                                  contents,
234                                  delegate);
235}
236