• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 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 "base/strings/utf_string_conversions.h"
6 #include "ui/base/hit_test.h"
7 #include "ui/views/bubble/bubble_border.h"
8 #include "ui/views/bubble/bubble_frame_view.h"
9 #include "ui/views/controls/button/checkbox.h"
10 #include "ui/views/controls/button/label_button.h"
11 #include "ui/views/test/views_test_base.h"
12 #include "ui/views/widget/widget.h"
13 #include "ui/views/window/dialog_client_view.h"
14 #include "ui/views/window/dialog_delegate.h"
15 
16 namespace views {
17 
18 namespace {
19 
20 class TestDialog : public DialogDelegateView, public ButtonListener {
21  public:
TestDialog()22   TestDialog()
23       : canceled_(false),
24         accepted_(false),
25         closeable_(false),
26         last_pressed_button_(NULL) {}
~TestDialog()27   virtual ~TestDialog() {}
28 
29   // DialogDelegateView overrides:
Cancel()30   virtual bool Cancel() OVERRIDE {
31     canceled_ = true;
32     return closeable_;
33   }
Accept()34   virtual bool Accept() OVERRIDE {
35     accepted_ = true;
36     return closeable_;
37   }
38 
39   // DialogDelegateView overrides:
GetPreferredSize()40   virtual gfx::Size GetPreferredSize() OVERRIDE { return gfx::Size(200, 200); }
GetWindowTitle() const41   virtual string16 GetWindowTitle() const OVERRIDE { return title_; }
42 
43   // ButtonListener override:
ButtonPressed(Button * sender,const ui::Event & event)44   virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE {
45     last_pressed_button_ = sender;
46   }
47 
last_pressed_button() const48   Button* last_pressed_button() const { return last_pressed_button_; }
49 
PressEnterAndCheckStates(Button * button)50   void PressEnterAndCheckStates(Button* button) {
51     ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0, false);
52     GetFocusManager()->OnKeyEvent(key_event);
53     const DialogClientView* client_view = GetDialogClientView();
54     EXPECT_EQ(canceled_, client_view->cancel_button()->is_default());
55     EXPECT_EQ(accepted_, client_view->ok_button()->is_default());
56     // This view does not listen for ok or cancel clicks, DialogClientView does.
57     CheckAndResetStates(button == client_view->cancel_button(),
58                         button == client_view->ok_button(),
59                         (canceled_ || accepted_ ) ? NULL : button);
60   }
61 
CheckAndResetStates(bool canceled,bool accepted,Button * last_pressed)62   void CheckAndResetStates(bool canceled, bool accepted, Button* last_pressed) {
63     EXPECT_EQ(canceled, canceled_);
64     canceled_ = false;
65     EXPECT_EQ(accepted, accepted_);
66     accepted_ = false;
67     EXPECT_EQ(last_pressed, last_pressed_button_);
68     last_pressed_button_ = NULL;
69   }
70 
TearDown()71   void TearDown() {
72     closeable_ = true;
73     GetWidget()->Close();
74   }
75 
set_title(const string16 & title)76   void set_title(const string16& title) { title_ = title; }
77 
78  private:
79   bool canceled_;
80   bool accepted_;
81   // Prevent the dialog from closing, for repeated ok and cancel button clicks.
82   bool closeable_;
83   Button* last_pressed_button_;
84   string16 title_;
85 
86   DISALLOW_COPY_AND_ASSIGN(TestDialog);
87 };
88 
89 class DialogTest : public ViewsTestBase {
90  public:
DialogTest()91   DialogTest() : dialog_(NULL) {}
~DialogTest()92   virtual ~DialogTest() {}
93 
SetUp()94   virtual void SetUp() OVERRIDE {
95     ViewsTestBase::SetUp();
96     dialog_ = new TestDialog();
97     DialogDelegate::CreateDialogWidget(dialog_, GetContext(), NULL)->Show();
98   }
99 
TearDown()100   virtual void TearDown() OVERRIDE {
101     dialog_->TearDown();
102     ViewsTestBase::TearDown();
103   }
104 
dialog() const105   TestDialog* dialog() const { return dialog_; }
106 
107  private:
108   TestDialog* dialog_;
109 
110   DISALLOW_COPY_AND_ASSIGN(DialogTest);
111 };
112 
113 }  // namespace
114 
TEST_F(DialogTest,DefaultButtons)115 TEST_F(DialogTest, DefaultButtons) {
116   DialogClientView* client_view = dialog()->GetDialogClientView();
117   LabelButton* ok_button = client_view->ok_button();
118 
119   // DialogDelegate's default button (ok) should be default (and handle enter).
120   EXPECT_EQ(ui::DIALOG_BUTTON_OK, dialog()->GetDefaultDialogButton());
121   dialog()->PressEnterAndCheckStates(ok_button);
122 
123   // Focus another button in the dialog, it should become the default.
124   LabelButton* button_1 = new LabelButton(dialog(), string16());
125   client_view->AddChildView(button_1);
126   client_view->OnWillChangeFocus(ok_button, button_1);
127   EXPECT_TRUE(button_1->is_default());
128   dialog()->PressEnterAndCheckStates(button_1);
129 
130   // Focus a Checkbox (not a push button), OK should become the default again.
131   Checkbox* checkbox = new Checkbox(string16());
132   client_view->AddChildView(checkbox);
133   client_view->OnWillChangeFocus(button_1, checkbox);
134   EXPECT_FALSE(button_1->is_default());
135   dialog()->PressEnterAndCheckStates(ok_button);
136 
137   // Focus yet another button in the dialog, it should become the default.
138   LabelButton* button_2 = new LabelButton(dialog(), string16());
139   client_view->AddChildView(button_2);
140   client_view->OnWillChangeFocus(checkbox, button_2);
141   EXPECT_FALSE(button_1->is_default());
142   EXPECT_TRUE(button_2->is_default());
143   dialog()->PressEnterAndCheckStates(button_2);
144 
145   // Focus nothing, OK should become the default again.
146   client_view->OnWillChangeFocus(button_2, NULL);
147   EXPECT_FALSE(button_1->is_default());
148   EXPECT_FALSE(button_2->is_default());
149   dialog()->PressEnterAndCheckStates(ok_button);
150 }
151 
TEST_F(DialogTest,AcceptAndCancel)152 TEST_F(DialogTest, AcceptAndCancel) {
153   DialogClientView* client_view = dialog()->GetDialogClientView();
154   LabelButton* ok_button = client_view->ok_button();
155   LabelButton* cancel_button = client_view->cancel_button();
156 
157   // Check that return/escape accelerators accept/cancel dialogs.
158   const ui::KeyEvent return_key(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0, false);
159   dialog()->GetFocusManager()->OnKeyEvent(return_key);
160   dialog()->CheckAndResetStates(false, true, NULL);
161   const ui::KeyEvent escape_key(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0, false);
162   dialog()->GetFocusManager()->OnKeyEvent(escape_key);
163   dialog()->CheckAndResetStates(true, false, NULL);
164 
165   // Check ok and cancel button behavior on a directed return key events.
166   ok_button->OnKeyPressed(return_key);
167   dialog()->CheckAndResetStates(false, true, NULL);
168   cancel_button->OnKeyPressed(return_key);
169   dialog()->CheckAndResetStates(true, false, NULL);
170 
171   // Check that return accelerators cancel dialogs if cancel is focused.
172   cancel_button->RequestFocus();
173   dialog()->GetFocusManager()->OnKeyEvent(return_key);
174   dialog()->CheckAndResetStates(true, false, NULL);
175 }
176 
TEST_F(DialogTest,RemoveDefaultButton)177 TEST_F(DialogTest, RemoveDefaultButton) {
178   // Removing buttons from the dialog here should not cause a crash on close.
179   delete dialog()->GetDialogClientView()->ok_button();
180   delete dialog()->GetDialogClientView()->cancel_button();
181 }
182 
TEST_F(DialogTest,HitTest)183 TEST_F(DialogTest, HitTest) {
184   // Ensure that the new style's BubbleFrameView hit-tests as expected.
185   const NonClientView* view = dialog()->GetWidget()->non_client_view();
186   BubbleFrameView* frame = static_cast<BubbleFrameView*>(view->frame_view());
187   const int border = frame->bubble_border()->GetBorderThickness();
188 
189   struct {
190     const int point;
191     const int hit;
192   } cases[] = {
193     { border,      HTSYSMENU },
194     { border + 10, HTSYSMENU },
195     { border + 20, HTCAPTION },
196     { border + 40, HTCLIENT  },
197     { border + 50, HTCLIENT  },
198     { 1000,        HTNOWHERE },
199   };
200 
201   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
202     gfx::Point point(cases[i].point, cases[i].point);
203     EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point))
204         << " with border: " << border << ", at point " << cases[i].point;
205   }
206 }
207 
TEST_F(DialogTest,InitialBoundsAccommodateTitle)208 TEST_F(DialogTest, InitialBoundsAccommodateTitle) {
209   TestDialog* titled_dialog(new TestDialog());
210   titled_dialog->set_title(ASCIIToUTF16("Title"));
211   DialogDelegate::CreateDialogWidget(titled_dialog, GetContext(), NULL);
212 
213   // Titled dialogs have taller initial frame bounds than untitled dialogs.
214   EXPECT_GT(titled_dialog->GetWidget()->GetWindowBoundsInScreen().height(),
215             dialog()->GetWidget()->GetWindowBoundsInScreen().height());
216 
217   // Giving the default test dialog a title will make the bounds the same.
218   dialog()->set_title(ASCIIToUTF16("Title"));
219   dialog()->GetWidget()->UpdateWindowTitle();
220   View* frame = dialog()->GetWidget()->non_client_view()->frame_view();
221   EXPECT_EQ(titled_dialog->GetWidget()->GetWindowBoundsInScreen().height(),
222             frame->GetPreferredSize().height());
223 }
224 
225 }  // namespace views
226