diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-18 20:40:34 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-18 20:40:34 +0000 |
commit | 420949ff13d309c7bbc56173bca33ee4ffafd1b3 (patch) | |
tree | 58b0c60202bddd3e034882a13f2bfe8e61be42b6 /chrome | |
parent | 33f8e3dfcc9c04c4aaa19757d8994161b650e040 (diff) | |
download | chromium_src-420949ff13d309c7bbc56173bca33ee4ffafd1b3.zip chromium_src-420949ff13d309c7bbc56173bca33ee4ffafd1b3.tar.gz chromium_src-420949ff13d309c7bbc56173bca33ee4ffafd1b3.tar.bz2 |
New native control helpers. Built but not run by anyone just yet. See design doc
http://dev.chromium.org/developers/design-documents/native-controls
Review URL: http://codereview.chromium.org/48094
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@12008 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/views/controls/button/native_button_win.cc | 264 | ||||
-rw-r--r-- | chrome/views/controls/button/native_button_win.h | 111 | ||||
-rw-r--r-- | chrome/views/controls/button/native_button_wrapper.h | 69 | ||||
-rw-r--r-- | chrome/views/controls/native_control_win.cc | 175 | ||||
-rw-r--r-- | chrome/views/controls/native_control_win.h | 94 | ||||
-rw-r--r-- | chrome/views/views.vcproj | 20 | ||||
-rw-r--r-- | chrome/views/widget/widget_win.cc | 61 |
7 files changed, 794 insertions, 0 deletions
diff --git a/chrome/views/controls/button/native_button_win.cc b/chrome/views/controls/button/native_button_win.cc new file mode 100644 index 0000000..35472cd --- /dev/null +++ b/chrome/views/controls/button/native_button_win.cc @@ -0,0 +1,264 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#include "chrome/views/controls/button/native_button_win.h" + +#include "chrome/common/l10n_util.h" +#include "chrome/views/widget/widget.h" + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// NativeButtonWin, public: + +NativeButtonWin::NativeButtonWin(NativeButtonWrapperListener* listener) + : NativeControlWin(), + listener_(listener), + is_default_(false), + ignore_minimum_size_(false), + min_dlu_size_(50, 14) { + // The min size in DLUs comes from + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch14e.asp +} + +NativeButtonWin::~NativeButtonWin() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeButtonWin, NativeButtonWrapper implementation: + +void NativeButtonWin::SetLabel(const std::wstring& label) { + label_ = label; + + // Even though we create a flipped HWND for a native button when the locale + // is right-to-left, Windows does not render text for the button using a + // right-to-left context (perhaps because the parent HWND is not flipped). + // The result is that RTL strings containing punctuation marks are not + // displayed properly. For example, the string "...ABC" (where A, B and C are + // Hebrew characters) is displayed as "ABC..." which is incorrect. + // + // In order to overcome this problem, we mark the localized Hebrew strings as + // RTL strings explicitly (using the appropriate Unicode formatting) so that + // Windows displays the text correctly regardless of the HWND hierarchy. + std::wstring localized_label; + if (l10n_util::AdjustStringForLocaleDirection(label_, &localized_label)) + label_ = localized_label; + + // SetLabel can be called before the view is attached to a view hierarchy, so + // we check the HWND before attempting to do anything with it. + if (IsWindow(GetHWND())) + SetWindowText(GetHWND(), label_.c_str()); +} + +std::wstring NativeButtonWin::GetLabel() const { + return label_; +} + +void NativeButtonWin::SetFont(const ChromeFont& font) { + font_ = font; +} + +void NativeButtonWin::SetDefaultButton(bool is_default) { + if (is_default == is_default_) + return; + is_default_ = is_default; + + // SetDefaultButton can be called before the view is attached to a view + // hierarchy, so we check the HWND before attempting to modify its style. + if (IsWindow(GetHWND())) { + SendMessage(GetHWND(), BM_SETSTYLE, + is_default_ ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON, true); + } +} + +bool NativeButtonWin::IsDefaultButton() const { + return is_default_; +} + +void NativeButtonWin::SetMinimumSizeInPlatformUnits( + const gfx::Size& minimum_size) { + min_dlu_size_ = minimum_size; +} + +void NativeButtonWin::SetIgnoreMinimumSize(bool ignore_minimum_size) { + ignore_minimum_size_ = ignore_minimum_size; +} + +View* NativeButtonWin::GetView() { + return this; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeButtonWin, View overrides: + +gfx::Size NativeButtonWin::GetPreferredSize() { + if (!GetHWND()) + return gfx::Size(); + + SIZE sz = {0}; + SendMessage(GetHWND(), BCM_GETIDEALSIZE, 0, reinterpret_cast<LPARAM>(&sz)); + + if (!ignore_minimum_size_) { + if (min_dlu_size_.width()) { + sz.cx = std::max(static_cast<int>(sz.cx), + font_.horizontal_dlus_to_pixels(min_dlu_size_.width())); + } + if (min_dlu_size_.height()) { + sz.cy = std::max(static_cast<int>(sz.cy), + font_.vertical_dlus_to_pixels(min_dlu_size_.height())); + } + } + return gfx::Size(sz.cx, sz.cy); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeButtonWin, NativeControlWin overrides: + +LRESULT NativeButtonWin::ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param) { + if (message == WM_COMMAND && HIWORD(w_param) == BN_CLICKED) { + listener_->ButtonPressed(); + return 0; + } + return NativeControlWin::ProcessMessage(message, w_param, l_param); +} + +bool NativeButtonWin::OnKeyDown(int vkey) { + bool enter_pressed = vkey == VK_RETURN; + if (enter_pressed) + listener_->ButtonPressed(); + return enter_pressed; +} + +bool NativeButtonWin::NotifyOnKeyDown() const { + return true; +} + +void NativeButtonWin::CreateNativeControl() { + DWORD flags = WS_CHILD | BS_PUSHBUTTON; + if (is_default_) + flags |= BS_DEFPUSHBUTTON; + HWND control_hwnd = CreateWindowEx(GetAdditionalExStyle(), L"BUTTON", L"", + flags, 0, 0, width(), height(), + GetWidget()->GetNativeView(), NULL, NULL, + NULL); + NativeControlCreated(control_hwnd); +} + +void NativeButtonWin::NativeControlCreated(HWND control_hwnd) { + NativeControlWin::NativeControlCreated(control_hwnd); + + SendMessage(control_hwnd, WM_SETFONT, reinterpret_cast<WPARAM>(font_.hfont()), + FALSE); + SetLabel(GetLabel()); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeCheckboxWin, public: + +NativeCheckboxWin::NativeCheckboxWin(NativeButtonWrapperListener* listener) + : NativeButtonWin(listener), + selected_(false) { +} + +NativeCheckboxWin::~NativeCheckboxWin() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeCheckboxWin, NativeButtonWrapper implementation: + +void NativeCheckboxWin::SetSelected(bool selected) { + if (selected == selected_) + return; + + selected_ = selected; + if (GetHWND()) { + SendMessage(GetHWND(), BM_SETCHECK, selected ? BST_CHECKED : BST_UNCHECKED, + 0); + } +} + +bool NativeCheckboxWin::IsSelected() const { + return selected_; +} + +void NativeCheckboxWin::SetHighlight(bool highlight) { + SendMessage(GetHWND(), BM_SETSTATE, highlight, 0); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeCheckboxWin, NativeButtonWin overrides: + +LRESULT NativeCheckboxWin::ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param) { + if (message == WM_COMMAND && HIWORD(w_param) == BN_CLICKED) { + SetSelected(!IsSelected()); + // Fall through to the NativeButtonWin's handler, which will send the + // clicked notification to the listener... + } + return NativeButtonWin::ProcessMessage(message, w_param, l_param); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeCheckboxWin, protected: + +void NativeCheckboxWin::CreateNativeControl() { + HWND control_hwnd = CreateWindowEx(WS_EX_TRANSPARENT | GetAdditionalExStyle(), + L"BUTTON", L"", + WS_CHILD | BS_CHECKBOX | WS_VISIBLE, + 0, 0, width(), height(), + GetWidget()->GetNativeView(), NULL, NULL, + NULL); + NativeControlCreated(control_hwnd); +} + +void NativeCheckboxWin::NativeControlCreated(HWND control_hwnd) { + NativeButtonWin::NativeControlCreated(control_hwnd); + SetSelected(IsSelected()); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeRadioButtonWin, public: + +NativeRadioButtonWin::NativeRadioButtonWin( + NativeButtonWrapperListener* listener) + : NativeCheckboxWin(listener) { +} + +NativeRadioButtonWin::~NativeRadioButtonWin() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeRaidoButotnWin, NativeCheckboxWin overrides: + +void NativeRadioButtonWin::CreateNativeControl() { + HWND control_hwnd = CreateWindowEx(GetAdditionalExStyle(), L"BUTTON", + L"", WS_CHILD | BS_RADIOBUTTON, + 0, 0, width(), height(), + GetWidget()->GetNativeView(), NULL, NULL, + NULL); + NativeControlCreated(control_hwnd); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeButtonWrapper, public: + +// static +NativeButtonWrapper* NativeButtonWrapper::CreateNativeButtonWrapper( + NativeButtonWrapperListener* listener, Type type) { + switch (type) { + case TYPE_BUTTON: + return new NativeButtonWin(listener); + case TYPE_CHECKBOX: + return new NativeCheckboxWin(listener); + case TYPE_RADIOBUTTON: + return new NativeRadioButtonWin(listener); + } + NOTREACHED() << "Invalid button type!"; + return NULL; +} + +} // namespace views diff --git a/chrome/views/controls/button/native_button_win.h b/chrome/views/controls/button/native_button_win.h new file mode 100644 index 0000000..b711537 --- /dev/null +++ b/chrome/views/controls/button/native_button_win.h @@ -0,0 +1,111 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#ifndef CHROME_VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WIN_H_ +#define CHROME_VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WIN_H_ + +#include "chrome/common/gfx/chrome_font.h" +#include "chrome/views/controls/native_control_win.h" +#include "chrome/views/controls/button/native_button_wrapper.h" + +namespace views { + +// A View that hosts a native Windows button. +class NativeButtonWin : public NativeControlWin, + public NativeButtonWrapper { + public: + explicit NativeButtonWin(NativeButtonWrapperListener* listener); + virtual ~NativeButtonWin(); + + // Overridden from NativeButtonWrapper: + virtual void SetLabel(const std::wstring& label); + virtual std::wstring GetLabel() const; + virtual void SetFont(const ChromeFont& font); + virtual void SetDefaultButton(bool is_default); + virtual bool IsDefaultButton() const; + virtual void SetMinimumSizeInPlatformUnits(const gfx::Size& minimum_size); + virtual void SetIgnoreMinimumSize(bool ignore_minimum_size); + virtual View* GetView(); + + // Overridden from View: + virtual gfx::Size GetPreferredSize(); + + // Overridden from NativeControlWin: + virtual LRESULT ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param); + virtual bool OnKeyDown(int vkey); + + protected: + virtual bool NotifyOnKeyDown() const; + virtual void CreateNativeControl(); + virtual void NativeControlCreated(HWND control_hwnd); + + private: + // Our listener. + NativeButtonWrapperListener* listener_; + + // The button label. + std::wstring label_; + + // True if the button is the default button in its context. + bool is_default_; + + // The font used to render the button label. + ChromeFont font_; + + // True if the button should ignore the minimum size for the platform. Default + // is false. + bool ignore_minimum_size_; + + // Minimum size, in dlus (see SetMinimumSizeInPlatformUnits). + gfx::Size min_dlu_size_; + + DISALLOW_COPY_AND_ASSIGN(NativeButtonWin); +}; + +// A View that hosts a native Windows checkbox. +class NativeCheckboxWin : public NativeButtonWin { + public: + explicit NativeCheckboxWin(NativeButtonWrapperListener* listener); + virtual ~NativeCheckboxWin(); + + // Overridden from NativeButtonWrapper: + virtual void SetSelected(bool selected); + virtual bool IsSelected() const; + virtual void SetHighlight(bool highlight); + + // Overridden from NativeControlWin: + virtual LRESULT ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param); + + protected: + virtual void CreateNativeControl(); + virtual void NativeControlCreated(HWND control_hwnd); + + private: + // True if this checkbox is checked. + bool selected_; + + DISALLOW_COPY_AND_ASSIGN(NativeCheckboxWin); +}; + +// A View that hosts a native Windows radio button. +class NativeRadioButtonWin : public NativeCheckboxWin { + public: + explicit NativeRadioButtonWin(NativeButtonWrapperListener* listener); + virtual ~NativeRadioButtonWin(); + + protected: + // Overridden from NativeCheckboxWin: + virtual void CreateNativeControl(); + + private: + DISALLOW_COPY_AND_ASSIGN(NativeRadioButtonWin); +}; + +} // namespace views + +#endif // #ifndef CHROME_VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WIN_H_ diff --git a/chrome/views/controls/button/native_button_wrapper.h b/chrome/views/controls/button/native_button_wrapper.h new file mode 100644 index 0000000..f530b83 --- /dev/null +++ b/chrome/views/controls/button/native_button_wrapper.h @@ -0,0 +1,69 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#ifndef CHROME_VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WRAPPER_H_ +#define CHROME_VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WRAPPER_H_ + +class ChromeFont; + +namespace views { + +// An interface implemented by the view that owns the NativeButtonWrapper that +// allows it to be notified when the button is pressed. +class NativeButtonWrapperListener { + public: + virtual void ButtonPressed() = 0; +}; + +// A specialization of NativeControlWrapper that hosts a platform-native button. +class NativeButtonWrapper { + public: + // Sets/Gets the button's label. + virtual void SetLabel(const std::wstring& label) = 0; + virtual std::wstring GetLabel() const = 0; + + // Sets the font used by this button. + virtual void SetFont(const ChromeFont& font) = 0; + + // Sets whether or not the button should have the default appearance and + // action in its setting. + virtual void SetDefaultButton(bool is_default) = 0; + virtual bool IsDefaultButton() const = 0; + + // Sets the minimum size of the button from the specified size in native + // dialog units. The definition of this unit may vary from platform to + // platform. If the width/height is non-zero, the preferred size of the button + // will not be less than this value when the dialog units are converted to + // pixels. + virtual void SetMinimumSizeInPlatformUnits(const gfx::Size& minimum_size) = 0; + + // Call to have the button ignore its minimum size. Use this if you want + // buttons narrower than the defined minimum size. + virtual void SetIgnoreMinimumSize(bool ignore_minimum_size) = 0; + + // Sets/Gets the selected state of button. Valid only for checkboxes and radio + // buttons. + virtual void SetSelected(bool is_selected) {} + virtual bool IsSelected(bool is_selected) { return false; } + + // Shows the hover state for the button if |highlight| is true. + virtual void SetHighlight(bool highlight) {}; + + // Retrieves the views::View that hosts the native control. + virtual View* GetView() = 0; + + enum Type { + TYPE_BUTTON, + TYPE_CHECKBOX, + TYPE_RADIOBUTTON + }; + + // Creates an appropriate NativeButtonWrapper for the platform. + static NativeButtonWrapper* CreateNativeButtonWrapper( + NativeButtonWrapperListener* listener, Type type); +}; + +} // namespace views + +#endif // #ifndef CHROME_VIEWS_CONTROLS_BUTTON_NATIVE_BUTTON_WRAPPER_H_ diff --git a/chrome/views/controls/native_control_win.cc b/chrome/views/controls/native_control_win.cc new file mode 100644 index 0000000..f60b93c --- /dev/null +++ b/chrome/views/controls/native_control_win.cc @@ -0,0 +1,175 @@ +// #ifndef CHROME_VIEWS_NATIVE_CONTROL_WIN_H_// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#include "chrome/views/controls/native_control_win.h" + +#include "base/logging.h" +#include "base/win_util.h" +#include "chrome/common/l10n_util_win.h" + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// NativeControlWin, public: + +// static +const wchar_t* NativeControlWin::kNativeControlWinKey = + L"__NATIVE_CONTROL_WIN__"; + +NativeControlWin::NativeControlWin() : HWNDView() { +} + +NativeControlWin::~NativeControlWin() { +} + +LRESULT NativeControlWin::ProcessMessage(UINT message, WPARAM w_param, + LPARAM l_param) { + switch (message) { + case WM_CONTEXTMENU: + ShowContextMenu(gfx::Point(LOWORD(l_param), HIWORD(l_param))); + break; + case WM_CTLCOLORBTN: + case WM_CTLCOLORSTATIC: + return GetControlColor(message, reinterpret_cast<HDC>(w_param), + GetHWND()); + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeControlWin, View overrides: + +void NativeControlWin::SetEnabled(bool enabled) { + if (IsEnabled() != enabled) { + View::SetEnabled(enabled); + if (GetHWND()) + EnableWindow(GetHWND(), IsEnabled()); + } +} + +void NativeControlWin::ViewHierarchyChanged(bool is_add, View* parent, + View* child) { + // Create the HWND when we're added to a valid Widget. Many controls need a + // parent HWND to function properly. + if (is_add && GetWidget() && !GetHWND()) + CreateNativeControl(); +} + +void NativeControlWin::VisibilityChanged(View* starting_from, bool is_visible) { + if (!is_visible) { + // We destroy the child control HWND when we become invisible because of the + // performance cost of maintaining many HWNDs. + HWND hwnd = GetHWND(); + Detach(); + DestroyWindow(hwnd); + } else if (!GetHWND()) { + CreateNativeControl(); + } +} + +void NativeControlWin::Focus() { + DCHECK(GetHWND()); + SetFocus(GetHWND()); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeControlWin, protected: + +void NativeControlWin::ShowContextMenu(const gfx::Point& location) { + if (!GetContextMenuController()) + return; + + int x = location.x(); + int y = location.y(); + bool is_mouse = true; + if (x == -1 && y == -1) { + gfx::Point point = GetKeyboardContextMenuLocation(); + x = point.x(); + y = point.y(); + is_mouse = false; + } + View::ShowContextMenu(x, y, is_mouse); +} + +void NativeControlWin::NativeControlCreated(HWND native_control) { + // Associate this object with the control's HWND so that WidgetWin can find + // this object when it receives messages from it. + SetProp(native_control, kNativeControlWinKey, this); + + Attach(native_control); + // GetHWND() is now valid. + + // Subclass the window so we can monitor for key presses. + win_util::Subclass(GetHWND(), &NativeControlWin::NativeControlWndProc); + + // Update the newly created HWND with any resident enabled state. + EnableWindow(GetHWND(), IsEnabled()); + + // This message ensures that the focus border is shown. + SendMessage(GetHWND(), WM_CHANGEUISTATE, + MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0); +} + +DWORD NativeControlWin::GetAdditionalExStyle() const { + // If the UI for the view is mirrored, we should make sure we add the + // extended window style for a right-to-left layout so the subclass creates + // a mirrored HWND for the underlying control. + DWORD ex_style = 0; + if (UILayoutIsRightToLeft()) + ex_style |= l10n_util::GetExtendedStyles(); + + return ex_style; +} + +DWORD NativeControlWin::GetAdditionalRTLStyle() const { + // If the UI for the view is mirrored, we should make sure we add the + // extended window style for a right-to-left layout so the subclass creates + // a mirrored HWND for the underlying control. + DWORD ex_style = 0; + if (UILayoutIsRightToLeft()) + ex_style |= l10n_util::GetExtendedTooltipStyles(); + + return ex_style; +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeControlWin, private: + +LRESULT NativeControlWin::GetControlColor(UINT message, HDC dc, HWND sender) { + View *ancestor = this; + while (ancestor) { + const Background* background = ancestor->background(); + if (background) { + HBRUSH brush = background->GetNativeControlBrush(); + if (brush) + return reinterpret_cast<LRESULT>(brush); + } + ancestor = ancestor->GetParent(); + } + + // COLOR_BTNFACE is the default for dialog box backgrounds. + return reinterpret_cast<LRESULT>(GetSysColorBrush(COLOR_BTNFACE)); +} + +// static +LRESULT NativeControlWin::NativeControlWndProc(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param) { + NativeControlWin* native_control = + static_cast<NativeControlWin*>(GetProp(window, kNativeControlWinKey)); + DCHECK(native_control); + + if (message == WM_KEYDOWN && native_control->NotifyOnKeyDown()) { + if (native_control->OnKeyDown(static_cast<int>(w_param))) + return 0; + } else if (message == WM_DESTROY) { + win_util::Unsubclass(window, &NativeControlWin::NativeControlWndProc); + TRACK_HWND_DESTRUCTION(window); + } + + return DefWindowProc(window, message, w_param, l_param); +} + +} // namespace views diff --git a/chrome/views/controls/native_control_win.h b/chrome/views/controls/native_control_win.h new file mode 100644 index 0000000..c2ecb41 --- /dev/null +++ b/chrome/views/controls/native_control_win.h @@ -0,0 +1,94 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#ifndef CHROME_VIEWS_CONTROLS_NATIVE_CONTROL_WIN_H_ +#define CHROME_VIEWS_CONTROLS_NATIVE_CONTROL_WIN_H_ + +#include "chrome/views/controls/hwnd_view.h" + +namespace views { + +// A View that hosts a native Windows control. +class NativeControlWin : public HWNDView { + public: + static const wchar_t* kNativeControlWinKey; + + NativeControlWin(); + virtual ~NativeControlWin(); + + // Called by the containing WidgetWin when a message is received from the HWND + // created by an object derived from NativeControlWin. Derived classes MUST + // call _this_ version of the function if they override it and do not handle + // all of the messages listed in widget_win.cc ProcessNativeControlWinMessage. + virtual LRESULT ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param); + + // Called by our subclassed window procedure when a WM_KEYDOWN message is + // received by the HWND created by an object derived from NativeControlWin. + // Returns true if the key was processed, false otherwise. + virtual bool OnKeyDown(int vkey) { return false; } + + // Overridden from View: + virtual void SetEnabled(bool enabled); + + protected: + virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child); + virtual void VisibilityChanged(View* starting_from, bool is_visible); + virtual void Focus(); + + // Called by the containing WidgetWin when a WM_CONTEXTMENU message is + // received from the HWND created by an object derived from NativeControlWin. + virtual void ShowContextMenu(const gfx::Point& location); + + // Derived classes interested in receiving key down notification should + // override this method and return true. In which case OnKeyDown is called + // when a key down message is sent to the control. + // Note that this method is called at the time of the control creation: the + // behavior will not change if the returned value changes after the control + // has been created. + virtual bool NotifyOnKeyDown() const { return false; } + + // Called when the NativeControlWin is attached to a View hierarchy with a + // valid Widget. The NativeControlWin should use this opportunity to create + // its associated HWND. + virtual void CreateNativeControl() = 0; + + // MUST be called by the subclass implementation of |CreateNativeControl| + // immediately after creating the control HWND, otherwise it won't be attached + // to the HWNDView and will be effectively orphaned. + virtual void NativeControlCreated(HWND native_control); + + // Returns additional extended style flags. When subclasses call + // CreateWindowEx in order to create the underlying control, they must OR the + // ExStyle parameter with the value returned by this function. + // + // We currently use this method in order to add flags such as WS_EX_LAYOUTRTL + // to the HWND for views with right-to-left UI layout. + DWORD GetAdditionalExStyle() const; + + // TODO(xji): we use the following temporary function as we transition the + // various native controls to use the right set of RTL flags. This function + // will go away (and be replaced by GetAdditionalExStyle()) once all the + // controls are properly transitioned. + DWORD GetAdditionalRTLStyle() const; + + private: + // Called by the containing WidgetWin when a message of type WM_CTLCOLORBTN or + // WM_CTLCOLORSTATIC is sent from the HWND created by an object dreived from + // NativeControlWin. + LRESULT GetControlColor(UINT message, HDC dc, HWND sender); + + // Our subclass window procedure for the attached control. + static LRESULT CALLBACK NativeControlWndProc(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param); + + DISALLOW_COPY_AND_ASSIGN(NativeControlWin); +}; + +} // namespace views + +#endif // #ifndef CHROME_VIEWS_CONTROLS_NATIVE_CONTROL_WIN_H_ diff --git a/chrome/views/views.vcproj b/chrome/views/views.vcproj index 784f440..bb0ecad6 100644 --- a/chrome/views/views.vcproj +++ b/chrome/views/views.vcproj @@ -499,6 +499,14 @@ > </File> <File + RelativePath=".\controls\native_control_win.cc" + > + </File> + <File + RelativePath=".\controls\native_control_win.h" + > + </File> + <File RelativePath=".\controls\scroll_view.cc" > </File> @@ -606,6 +614,18 @@ > </File> <File + RelativePath=".\controls\button\native_button_win.cc" + > + </File> + <File + RelativePath=".\controls\button\native_button_win.h" + > + </File> + <File + RelativePath=".\controls\button\native_button_wrapper.h" + > + </File> + <File RelativePath=".\controls\button\radio_button.cc" > </File> diff --git a/chrome/views/widget/widget_win.cc b/chrome/views/widget/widget_win.cc index 047313d..16f510c 100644 --- a/chrome/views/widget/widget_win.cc +++ b/chrome/views/widget/widget_win.cc @@ -12,6 +12,7 @@ #include "chrome/common/notification_service.h" #include "chrome/common/win_util.h" #include "chrome/views/accessibility/view_accessibility.h" +#include "chrome/views/controls/native_control_win.h" #include "chrome/views/widget/aero_tooltip_manager.h" #include "chrome/views/widget/hwnd_notification_source.h" #include "chrome/views/widget/root_view.h" @@ -34,6 +35,11 @@ RootView* GetRootViewForHWND(HWND hwnd) { return reinterpret_cast<RootView*>(::GetProp(hwnd, kRootViewWindowProperty)); } +NativeControlWin* GetNativeControlWinForHWND(HWND hwnd) { + return reinterpret_cast<NativeControlWin*>( + ::GetProp(hwnd, NativeControlWin::kNativeControlWinKey)); +} + // Used to locate the WidgetWin issuing the current Create. Only valid for the // life of Create. // @@ -928,6 +934,51 @@ std::wstring WidgetWin::GetWindowClassName() { return name; } +// Get the source HWND of the specified message. Depending on the message, the +// source HWND is encoded in either the WPARAM or the LPARAM value. +HWND GetControlHWNDForMessage(UINT message, WPARAM w_param, LPARAM l_param) { + // Each of the following messages can be sent by a child HWND and must be + // forwarded to its associated NativeControlWin for handling. + switch (message) { + case WM_NOTIFY: + return reinterpret_cast<NMHDR*>(l_param)->hwndFrom; + case WM_COMMAND: + return reinterpret_cast<HWND>(l_param); + case WM_CONTEXTMENU: + return reinterpret_cast<HWND>(w_param); + case WM_CTLCOLORBTN: + case WM_CTLCOLORSTATIC: + return reinterpret_cast<HWND>(l_param); + } + return NULL; +} + +// Some messages may be sent to us by a child HWND managed by +// NativeControlWin. If this is the case, this function will forward those +// messages on to the object associated with the source HWND and return true, +// in which case the window procedure must not do any further processing of +// the message. If there is no associated NativeControlWin, the return value +// will be false and the WndProc can continue processing the message normally. +// |l_result| contains the result of the message processing by the control and +// must be returned by the WndProc if the return value is true. +bool ProcessNativeControlMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* l_result) { + *l_result = 0; + + HWND control_hwnd = GetControlHWNDForMessage(message, w_param, l_param); + if (IsWindow(control_hwnd)) { + NativeControlWin* wrapper = GetNativeControlWinForHWND(control_hwnd); + if (wrapper) { + *l_result = wrapper->ProcessMessage(message, w_param, l_param); + return true; + } + } + + return false; +} + // static LRESULT CALLBACK WidgetWin::WndProc(HWND window, UINT message, WPARAM w_param, LPARAM l_param) { @@ -939,11 +990,21 @@ LRESULT CALLBACK WidgetWin::WndProc(HWND window, UINT message, widget->hwnd_ = window; return TRUE; } + WidgetWin* widget = reinterpret_cast<WidgetWin*>( win_util::GetWindowUserData(window)); if (!widget) return 0; + LRESULT result = 0; + + // First allow messages sent by child controls to be processed directly by + // their associated views. If such a view is present, it will handle the + // message *instead of* this WidgetWin. + if (ProcessNativeControlMessage(message, w_param, l_param, &result)) + return result; + + // Otherwise we handle everything else. if (!widget->ProcessWindowMessage(window, message, w_param, l_param, result)) result = DefWindowProc(window, message, w_param, l_param); if (message == WM_NCDESTROY) { |