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 16namespace views { 17 18namespace { 19 20class TestDialog : public DialogDelegateView, public ButtonListener { 21 public: 22 TestDialog() 23 : canceled_(false), 24 accepted_(false), 25 closeable_(false), 26 last_pressed_button_(NULL) {} 27 virtual ~TestDialog() {} 28 29 // DialogDelegateView overrides: 30 virtual bool Cancel() OVERRIDE { 31 canceled_ = true; 32 return closeable_; 33 } 34 virtual bool Accept() OVERRIDE { 35 accepted_ = true; 36 return closeable_; 37 } 38 39 // DialogDelegateView overrides: 40 virtual gfx::Size GetPreferredSize() const OVERRIDE { 41 return gfx::Size(200, 200); 42 } 43 virtual base::string16 GetWindowTitle() const OVERRIDE { return title_; } 44 virtual bool UseNewStyleForThisDialog() const OVERRIDE { return true; } 45 46 // ButtonListener override: 47 virtual void ButtonPressed(Button* sender, const ui::Event& event) OVERRIDE { 48 last_pressed_button_ = sender; 49 } 50 51 Button* last_pressed_button() const { return last_pressed_button_; } 52 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 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 74 void TearDown() { 75 closeable_ = true; 76 GetWidget()->Close(); 77 } 78 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 92class DialogTest : public ViewsTestBase { 93 public: 94 DialogTest() : dialog_(NULL) {} 95 virtual ~DialogTest() {} 96 97 virtual void SetUp() OVERRIDE { 98 ViewsTestBase::SetUp(); 99 dialog_ = new TestDialog(); 100 DialogDelegate::CreateDialogWidget(dialog_, GetContext(), NULL)->Show(); 101 } 102 103 virtual void TearDown() OVERRIDE { 104 dialog_->TearDown(); 105 ViewsTestBase::TearDown(); 106 } 107 108 TestDialog* dialog() const { return dialog_; } 109 110 private: 111 TestDialog* dialog_; 112 113 DISALLOW_COPY_AND_ASSIGN(DialogTest); 114}; 115 116} // namespace 117 118TEST_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 155TEST_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 182TEST_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 188TEST_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 213TEST_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