summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/views/client_view.cc6
-rw-r--r--chrome/views/client_view.h4
-rw-r--r--chrome/views/dialog_client_view.cc73
-rw-r--r--chrome/views/dialog_client_view.h14
-rw-r--r--chrome/views/focus_manager.cc16
-rw-r--r--chrome/views/focus_manager.h4
-rw-r--r--chrome/views/native_button.cc18
-rw-r--r--chrome/views/native_button.h10
-rw-r--r--chrome/views/view.cc27
-rw-r--r--chrome/views/view.h3
-rw-r--r--chrome/views/view_unittest.cc75
-rw-r--r--chrome/views/window.cc4
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();