Home | History | Annotate | Download | only in window
      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:
     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, 0, false);
     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 
     92 class 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 
    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 
    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(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0, false);
    162   dialog()->GetFocusManager()->OnKeyEvent(return_key);
    163   dialog()->CheckAndResetStates(false, true, NULL);
    164   const ui::KeyEvent escape_key(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0, false);
    165   dialog()->GetFocusManager()->OnKeyEvent(escape_key);
    166   dialog()->CheckAndResetStates(true, false, NULL);
    167 
    168   // Check ok and cancel button behavior on a directed return key events.
    169   ok_button->OnKeyPressed(return_key);
    170   dialog()->CheckAndResetStates(false, true, NULL);
    171   cancel_button->OnKeyPressed(return_key);
    172   dialog()->CheckAndResetStates(true, false, NULL);
    173 
    174   // Check that return accelerators cancel dialogs if cancel is focused.
    175   cancel_button->RequestFocus();
    176   dialog()->GetFocusManager()->OnKeyEvent(return_key);
    177   dialog()->CheckAndResetStates(true, false, NULL);
    178 }
    179 
    180 TEST_F(DialogTest, RemoveDefaultButton) {
    181   // Removing buttons from the dialog here should not cause a crash on close.
    182   delete dialog()->GetDialogClientView()->ok_button();
    183   delete dialog()->GetDialogClientView()->cancel_button();
    184 }
    185 
    186 TEST_F(DialogTest, HitTest) {
    187   // Ensure that the new style's BubbleFrameView hit-tests as expected.
    188   const NonClientView* view = dialog()->GetWidget()->non_client_view();
    189   BubbleFrameView* frame = static_cast<BubbleFrameView*>(view->frame_view());
    190   const int border = frame->bubble_border()->GetBorderThickness();
    191 
    192   struct {
    193     const int point;
    194     const int hit;
    195   } cases[] = {
    196     { border,      HTSYSMENU },
    197     { border + 10, HTSYSMENU },
    198     { border + 20, HTCAPTION },
    199     { border + 40, HTCLIENT  },
    200     { border + 50, HTCLIENT  },
    201     { 1000,        HTNOWHERE },
    202   };
    203 
    204   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    205     gfx::Point point(cases[i].point, cases[i].point);
    206     EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point))
    207         << " with border: " << border << ", at point " << cases[i].point;
    208   }
    209 }
    210 
    211 TEST_F(DialogTest, BoundsAccommodateTitle) {
    212   TestDialog* dialog2(new TestDialog());
    213   dialog2->set_title(base::ASCIIToUTF16("Title"));
    214   DialogDelegate::CreateDialogWidget(dialog2, GetContext(), NULL);
    215 
    216   // Titled dialogs have taller initial frame bounds than untitled dialogs.
    217   View* frame1 = dialog()->GetWidget()->non_client_view()->frame_view();
    218   View* frame2 = dialog2->GetWidget()->non_client_view()->frame_view();
    219   EXPECT_LT(frame1->GetPreferredSize().height(),
    220             frame2->GetPreferredSize().height());
    221 
    222   // Giving the default test dialog a title will yield the same bounds.
    223   dialog()->set_title(base::ASCIIToUTF16("Title"));
    224   dialog()->GetWidget()->UpdateWindowTitle();
    225   EXPECT_EQ(frame1->GetPreferredSize().height(),
    226             frame2->GetPreferredSize().height());
    227 }
    228 
    229 }  // namespace views
    230