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