diff options
-rw-r--r-- | chrome/views/client_view.cc | 6 | ||||
-rw-r--r-- | chrome/views/client_view.h | 4 | ||||
-rw-r--r-- | chrome/views/dialog_client_view.cc | 73 | ||||
-rw-r--r-- | chrome/views/dialog_client_view.h | 14 | ||||
-rw-r--r-- | chrome/views/focus_manager.cc | 16 | ||||
-rw-r--r-- | chrome/views/focus_manager.h | 4 | ||||
-rw-r--r-- | chrome/views/native_button.cc | 18 | ||||
-rw-r--r-- | chrome/views/native_button.h | 10 | ||||
-rw-r--r-- | chrome/views/view.cc | 27 | ||||
-rw-r--r-- | chrome/views/view.h | 3 | ||||
-rw-r--r-- | chrome/views/view_unittest.cc | 75 | ||||
-rw-r--r-- | chrome/views/window.cc | 4 |
12 files changed, 241 insertions, 13 deletions
diff --git a/chrome/views/client_view.cc b/chrome/views/client_view.cc index dd9ef5b..ba3f6e4 100644 --- a/chrome/views/client_view.cc +++ b/chrome/views/client_view.cc @@ -4,6 +4,8 @@ #include "base/logging.h" #include "chrome/views/client_view.h" +#include "chrome/views/window.h" +#include "chrome/views/window_delegate.h" namespace views { @@ -19,6 +21,10 @@ int ClientView::NonClientHitTest(const gfx::Point& point) { return bounds().Contains(point) ? HTCLIENT : HTNOWHERE; } +void ClientView::WindowClosing() { + window_->window_delegate()->WindowClosing(); +} + /////////////////////////////////////////////////////////////////////////////// // ClientView, View overrides: diff --git a/chrome/views/client_view.h b/chrome/views/client_view.h index 692e0ba..919f661 100644 --- a/chrome/views/client_view.h +++ b/chrome/views/client_view.h @@ -38,6 +38,10 @@ class ClientView : public View { // dialog, etc. virtual bool CanClose() const { return true; } + // Notification that the window is closing. The default implementation + // forwards the notification to the delegate. + virtual void WindowClosing(); + // Tests to see if the specified point (in view coordinates) is within the // bounds of this view. If so, it returns HTCLIENT in this default // implementation. If it is outside the bounds of this view, this must return diff --git a/chrome/views/dialog_client_view.cc b/chrome/views/dialog_client_view.cc index 024331f..5fc1dd2 100644 --- a/chrome/views/dialog_client_view.cc +++ b/chrome/views/dialog_client_view.cc @@ -43,7 +43,7 @@ void FillViewWithSysColor(ChromeCanvas* canvas, View* view, COLORREF color) { // DialogButton ---------------------------------------------------------------- // DialogButtons is used for the ok/cancel buttons of the window. DialogButton -// forwrds AcceleratorPressed to the delegate. +// forwards AcceleratorPressed to the delegate. class DialogButton : public NativeButton { public: @@ -90,7 +90,8 @@ DialogClientView::DialogClientView(Window* owner, View* contents_view) ok_button_(NULL), cancel_button_(NULL), extra_view_(NULL), - accepted_(false) { + accepted_(false), + default_button_(NULL) { InitClass(); } @@ -105,12 +106,14 @@ void DialogClientView::ShowDialogButtons() { dd->GetDialogButtonLabel(DialogDelegate::DIALOGBUTTON_OK); if (label.empty()) label = l10n_util::GetString(IDS_OK); - ok_button_ = new DialogButton( - window(), DialogDelegate::DIALOGBUTTON_OK, - label, - (dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_OK) != 0); + bool is_default_button = + (dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_OK) != 0; + ok_button_ = new DialogButton(window(), DialogDelegate::DIALOGBUTTON_OK, + label, is_default_button); ok_button_->SetListener(this); ok_button_->SetGroup(kButtonGroup); + if (is_default_button) + default_button_ = ok_button_; if (!cancel_button_) ok_button_->AddAccelerator(Accelerator(VK_ESCAPE, false, false, false)); AddChildView(ok_button_); @@ -125,14 +128,17 @@ void DialogClientView::ShowDialogButtons() { label = l10n_util::GetString(IDS_CLOSE); } } - cancel_button_ = new DialogButton( - window(), DialogDelegate::DIALOGBUTTON_CANCEL, - label, + bool is_default_button = (dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_CANCEL) - != 0); + != 0; + cancel_button_ = new DialogButton(window(), + DialogDelegate::DIALOGBUTTON_CANCEL, + label, is_default_button); cancel_button_->SetListener(this); cancel_button_->SetGroup(kButtonGroup); cancel_button_->AddAccelerator(Accelerator(VK_ESCAPE, false, false, false)); + if (is_default_button) + default_button_ = ok_button_; AddChildView(cancel_button_); } if (!buttons) { @@ -142,6 +148,37 @@ void DialogClientView::ShowDialogButtons() { } } +void DialogClientView::SetDefaultButton(NativeButton* new_default_button) { + if (default_button_ && default_button_ != new_default_button) { + default_button_->SetDefaultButton(false); + default_button_ = NULL; + } + + if (new_default_button) { + default_button_ = new_default_button; + default_button_->SetDefaultButton(true); + } +} + +void DialogClientView::FocusWillChange(View* focused_before, + View* focused_now) { + NativeButton* new_default_button = NULL; + if (focused_now && + focused_now->GetClassName() == NativeButton::kViewClassName) { + new_default_button = static_cast<NativeButton*>(focused_now); + } else { + // The focused view is not a button, get the default button from the + // delegate. + DialogDelegate* dd = GetDialogDelegate(); + if ((dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_OK) != 0) + new_default_button = ok_button_; + if ((dd->GetDefaultDialogButton() & DialogDelegate::DIALOGBUTTON_CANCEL) + != 0) + new_default_button = cancel_button_; + } + SetDefaultButton(new_default_button); +} + // Changing dialog labels will change button widths. void DialogClientView::UpdateDialogButtons() { DialogDelegate* dd = GetDialogDelegate(); @@ -190,6 +227,14 @@ bool DialogClientView::CanClose() const { return true; } +void DialogClientView::WindowClosing() { + FocusManager* focus_manager = GetFocusManager(); + DCHECK(focus_manager); + if (focus_manager) + focus_manager->RemoveFocusChangeListener(this); + ClientView::WindowClosing(); +} + int DialogClientView::NonClientHitTest(const gfx::Point& point) { if (size_box_bounds_.Contains(point.x() - x(), point.y() - y())) return HTBOTTOMRIGHT; @@ -223,6 +268,14 @@ void DialogClientView::ViewHierarchyChanged(bool is_add, View* parent, // Container's HWND. ShowDialogButtons(); ClientView::ViewHierarchyChanged(is_add, parent, child); + + FocusManager* focus_manager = GetFocusManager(); + // Listen for focus change events so we can update the default button. + DCHECK(focus_manager); // bug #1291225: crash reports seem to indicate it + // can be NULL. + if (focus_manager) + focus_manager->AddFocusChangeListener(this); + // The "extra view" must be created and installed after the contents view // has been inserted into the view hierarchy. CreateExtraView(); diff --git a/chrome/views/dialog_client_view.h b/chrome/views/dialog_client_view.h index f8bcac1..32713d0 100644 --- a/chrome/views/dialog_client_view.h +++ b/chrome/views/dialog_client_view.h @@ -7,6 +7,7 @@ #include "chrome/common/gfx/chrome_font.h" #include "chrome/views/client_view.h" +#include "chrome/views/focus_manager.h" #include "chrome/views/native_button.h" namespace views { @@ -24,7 +25,8 @@ class Window; // buttons. // class DialogClientView : public ClientView, - public NativeButton::Listener { + public NativeButton::Listener, + public FocusChangeListener { public: DialogClientView(Window* window, View* contents_view); virtual ~DialogClientView(); @@ -49,9 +51,13 @@ class DialogClientView : public ClientView, // Overridden from ClientView: virtual bool CanClose() const; + virtual void WindowClosing(); virtual int NonClientHitTest(const gfx::Point& point); virtual DialogClientView* AsDialogClientView() { return this; } + // FocusChangeListener implementation: + virtual void FocusWillChange(View* focused_before, View* focused_now); + protected: // View overrides: virtual void Paint(ChromeCanvas* canvas); @@ -77,6 +83,9 @@ class DialogClientView : public ClientView, void LayoutDialogButtons(); void LayoutContentsView(); + // Makes the specified button the default button. + void SetDefaultButton(NativeButton* button); + bool has_dialog_buttons() const { return ok_button_ || cancel_button_; } // Create and add the extra view, if supplied by the delegate. @@ -88,6 +97,9 @@ class DialogClientView : public ClientView, // The dialog buttons. NativeButton* ok_button_; NativeButton* cancel_button_; + + // The button that is currently the default button if any. + NativeButton* default_button_; // The button-level extra view, NULL unless the dialog delegate supplies one. View* extra_view_; diff --git a/chrome/views/focus_manager.cc b/chrome/views/focus_manager.cc index 4ea004c..4164cb8 100644 --- a/chrome/views/focus_manager.cc +++ b/chrome/views/focus_manager.cc @@ -697,6 +697,22 @@ AcceleratorTarget* FocusManager::RegisterAccelerator( return previous_target; } +void FocusManager::UnregisterAccelerator(const Accelerator& accelerator, + AcceleratorTarget* target) { + AcceleratorMap::iterator iter = accelerators_.find(accelerator); + if (iter == accelerators_.end()) { + NOTREACHED() << "Unregistering non-existing accelerator"; + return; + } + + if (iter->second != target) { + NOTREACHED() << "Unregistering accelerator for wrong target"; + return; + } + + accelerators_.erase(iter); +} + void FocusManager::UnregisterAccelerators(AcceleratorTarget* target) { for (AcceleratorMap::iterator iter = accelerators_.begin(); iter != accelerators_.end();) { diff --git a/chrome/views/focus_manager.h b/chrome/views/focus_manager.h index 767edf7..835b745 100644 --- a/chrome/views/focus_manager.h +++ b/chrome/views/focus_manager.h @@ -239,6 +239,10 @@ class FocusManager : public NotificationObserver { AcceleratorTarget* RegisterAccelerator(const Accelerator& accelerator, AcceleratorTarget* target); + // Unregister the specified keyboard accelerator for the specified target. + void UnregisterAccelerator(const Accelerator& accelerator, + AcceleratorTarget* target); + // Unregister all keyboard accelerator for the specified target. void UnregisterAccelerators(AcceleratorTarget* target); diff --git a/chrome/views/native_button.cc b/chrome/views/native_button.cc index bbee9e9..00aea52 100644 --- a/chrome/views/native_button.cc +++ b/chrome/views/native_button.cc @@ -12,6 +12,8 @@ namespace views { +const char NativeButton::kViewClassName[] = "chrome/views/NativeButton"; + NativeButton::NativeButton(const std::wstring& label) : enforce_dlu_min_size_(true) { Init(label, false); @@ -25,6 +27,10 @@ NativeButton::NativeButton(const std::wstring& label, bool is_default) NativeButton::~NativeButton() { } +std::string NativeButton::GetClassName() const { + return kViewClassName; +} + void NativeButton::SetListener(Listener *l) { listener_ = l; } @@ -116,6 +122,18 @@ void NativeButton::ConfigureNativeButton(HWND hwnd) { ::SetWindowText(hwnd, label_.c_str()); } +void NativeButton::SetDefaultButton(bool is_default_button) { + if (is_default_button == is_default_) + return; + is_default_ = is_default_button; + if (is_default_button) + AddAccelerator(Accelerator(VK_RETURN, false, false, false)); + else + RemoveAccelerator(Accelerator(VK_RETURN, false, false, false)); + SendMessage(GetNativeControlHWND(), BM_SETSTYLE, + is_default_button ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON, true); +} + bool NativeButton::AcceleratorPressed(const Accelerator& accelerator) { if (enabled_) { Clicked(); diff --git a/chrome/views/native_button.h b/chrome/views/native_button.h index 03b03b0..655c49c 100644 --- a/chrome/views/native_button.h +++ b/chrome/views/native_button.h @@ -34,6 +34,8 @@ class NativeButton : public NativeControl { void SetLabel(const std::wstring& l); const std::wstring GetLabel() const; + std::string GetClassName() const; + class Listener { public: // @@ -53,6 +55,11 @@ class NativeButton : public NativeControl { // on both sides of the button for each directions void SetPadding(CSize size); + // Sets/unsets this button as the default button. The default button is the + // one triggered when enter is pressed. It is displayed with a blue border. + void SetDefaultButton(bool is_default_button); + bool IsDefaultButton() const { return is_default_; } + virtual LRESULT OnNotify(int w_param, LPNMHDR l_param); virtual LRESULT OnCommand(UINT code, int id, HWND source); @@ -83,6 +90,9 @@ class NativeButton : public NativeControl { // guidelines. Default is true. Set this to false if you want slim buttons. void set_enforce_dlu_min_size(bool value) { enforce_dlu_min_size_ = value; } + // The view class name. + static const char kViewClassName[]; + protected: virtual HWND CreateNativeControl(HWND parent_container); diff --git a/chrome/views/view.cc b/chrome/views/view.cc index 9fccd77..1dbe9f8 100644 --- a/chrome/views/view.cc +++ b/chrome/views/view.cc @@ -988,6 +988,33 @@ void View::AddAccelerator(const Accelerator& accelerator) { RegisterAccelerators(); } +void View::RemoveAccelerator(const Accelerator& accelerator) { + std::vector<Accelerator>::iterator iter; + if (!accelerators_.get() || + ((iter = std::find(accelerators_->begin(), accelerators_->end(), + accelerator)) == accelerators_->end())) { + NOTREACHED() << "Removing non-existing accelerator"; + return; + } + + accelerators_->erase(iter); + RootView* root_view = GetRootView(); + if (!root_view) { + // We are not part of a view hierarchy, so there is nothing to do as we + // removed ourselves from accelerators_, we won't be registered when added + // to one. + return; + } + + FocusManager* focus_manager = GetFocusManager(); + if (focus_manager) { + // We may not have a FocusManager if the window containing us is being + // closed, in which case the FocusManager is being deleted so there is + // nothing to unregister. + focus_manager->UnregisterAccelerator(accelerator, this); + } +} + void View::ResetAccelerators() { if (accelerators_.get()) { UnregisterAccelerators(); diff --git a/chrome/views/view.h b/chrome/views/view.h index 0a6ad31..d8bf716 100644 --- a/chrome/views/view.h +++ b/chrome/views/view.h @@ -504,6 +504,9 @@ class View : public AcceleratorTarget { // method several times. virtual void AddAccelerator(const Accelerator& accelerator); + // Removes the specified accelerator for this view. + virtual void RemoveAccelerator(const Accelerator& accelerator); + // Removes all the keyboard accelerators for this view. virtual void ResetAccelerators(); diff --git a/chrome/views/view_unittest.cc b/chrome/views/view_unittest.cc index ca6d026..e7196a0 100644 --- a/chrome/views/view_unittest.cc +++ b/chrome/views/view_unittest.cc @@ -5,6 +5,8 @@ #include "chrome/common/gfx/chrome_canvas.h" #include "chrome/common/gfx/path.h" #include "chrome/views/background.h" +#include "chrome/views/checkbox.h" +#include "chrome/views/dialog_delegate.h" #include "chrome/views/event.h" #include "chrome/views/root_view.h" #include "chrome/views/view.h" @@ -580,3 +582,76 @@ TEST_F(ViewTest, HitTestMasks) { EXPECT_EQ(v1, root_view->GetViewForPoint(v1_origin)); EXPECT_EQ(root_view, root_view->GetViewForPoint(v2_origin)); } + +class TestDialogView : public views::View, + public views::DialogDelegate { + public: + TestDialogView() { + } + + // views::DialogDelegate implementation: + virtual int GetDialogButtons() const { + return DIALOGBUTTON_OK | DIALOGBUTTON_CANCEL; + } + + virtual int GetDefaultDialogButton() const { + return DIALOGBUTTON_OK; + } + + virtual View* GetContentsView() { + views::View* container = new views::View(); + button1_ = new views::NativeButton(L"Button1"); + button2_ = new views::NativeButton(L"Button2"); + checkbox_ = new views::CheckBox(L"My checkbox"); + container->AddChildView(button1_); + container->AddChildView(button2_); + container->AddChildView(checkbox_); + return container; + } + views::NativeButton* button1_; + views::NativeButton* button2_; + views::NativeButton* checkbox_; +}; + +TEST_F(ViewTest, DialogDefaultButtonTest) { + TestDialogView* dialog_view_ = new TestDialogView(); + views::Window* window = + views::Window::CreateChromeWindow(NULL, gfx::Rect(0, 0, 100, 100), + dialog_view_); + views::DialogClientView* client_view = + static_cast<views::DialogClientView*>(window->client_view()); + views::NativeButton* ok_button = client_view->ok_button(); + views::NativeButton* cancel_button = client_view->cancel_button(); + + EXPECT_TRUE(ok_button->IsDefaultButton()); + + // Simualte focusing another button, it should become the default button. + client_view->FocusWillChange(ok_button, dialog_view_->button1_); + EXPECT_FALSE(ok_button->IsDefaultButton()); + EXPECT_TRUE(dialog_view_->button1_->IsDefaultButton()); + + // Now select something that is not a button, the OK should become the default + // button again. + client_view->FocusWillChange(dialog_view_->button1_, dialog_view_->checkbox_); + EXPECT_TRUE(ok_button->IsDefaultButton()); + EXPECT_FALSE(dialog_view_->button1_->IsDefaultButton()); + + // Select yet another button. + client_view->FocusWillChange(dialog_view_->checkbox_, dialog_view_->button2_); + EXPECT_FALSE(ok_button->IsDefaultButton()); + EXPECT_FALSE(dialog_view_->button1_->IsDefaultButton()); + EXPECT_TRUE(dialog_view_->button2_->IsDefaultButton()); + + // Focus nothing. + client_view->FocusWillChange(dialog_view_->button2_, NULL); + EXPECT_TRUE(ok_button->IsDefaultButton()); + EXPECT_FALSE(dialog_view_->button1_->IsDefaultButton()); + EXPECT_FALSE(dialog_view_->button2_->IsDefaultButton()); + + // Focus the cancel button. + client_view->FocusWillChange(NULL, cancel_button); + EXPECT_FALSE(ok_button->IsDefaultButton()); + EXPECT_TRUE(cancel_button->IsDefaultButton()); + EXPECT_FALSE(dialog_view_->button1_->IsDefaultButton()); + EXPECT_FALSE(dialog_view_->button2_->IsDefaultButton()); +}
\ No newline at end of file diff --git a/chrome/views/window.cc b/chrome/views/window.cc index 1566584..2f7b4f6 100644 --- a/chrome/views/window.cc +++ b/chrome/views/window.cc @@ -378,8 +378,8 @@ void Window::OnCommand(UINT notification_code, int command_id, HWND window) { } void Window::OnDestroy() { - if (window_delegate_) { - window_delegate_->WindowClosing(); + if (client_view_) { + client_view_->WindowClosing(); window_delegate_ = NULL; } RestoreEnabledIfNecessary(); |