diff options
author | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-21 17:57:46 +0000 |
---|---|---|
committer | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-21 17:57:46 +0000 |
commit | 35f83a463a8cb26f2862fc1d051fac6e18eb582a (patch) | |
tree | 7f0c891bdfbc31abaf5be7f5dbe6db926787605b /views/controls/tabbed_pane | |
parent | 914550a5a50f81f3e3717a3ff82af56b30a57ce8 (diff) | |
download | chromium_src-35f83a463a8cb26f2862fc1d051fac6e18eb582a.zip chromium_src-35f83a463a8cb26f2862fc1d051fac6e18eb582a.tar.gz chromium_src-35f83a463a8cb26f2862fc1d051fac6e18eb582a.tar.bz2 |
Refactoring of tabbed-pane component so it can be ported to Linux toolkit_view.
BUG=None
TEST=Make sure the Options dialog still works as expected (tab selection, focus traversal...)
Review URL: http://codereview.chromium.org/155668
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21189 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/controls/tabbed_pane')
-rw-r--r-- | views/controls/tabbed_pane/native_tabbed_pane_win.cc | 300 | ||||
-rw-r--r-- | views/controls/tabbed_pane/native_tabbed_pane_win.h | 69 | ||||
-rw-r--r-- | views/controls/tabbed_pane/native_tabbed_pane_wrapper.h | 68 | ||||
-rw-r--r-- | views/controls/tabbed_pane/tabbed_pane.cc | 89 | ||||
-rw-r--r-- | views/controls/tabbed_pane/tabbed_pane.h | 92 |
5 files changed, 618 insertions, 0 deletions
diff --git a/views/controls/tabbed_pane/native_tabbed_pane_win.cc b/views/controls/tabbed_pane/native_tabbed_pane_win.cc new file mode 100644 index 0000000..8ae054c --- /dev/null +++ b/views/controls/tabbed_pane/native_tabbed_pane_win.cc @@ -0,0 +1,300 @@ +// 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 "views/controls/tabbed_pane/native_tabbed_pane_win.h" + +#include <vssym32.h> + +#include "app/gfx/canvas.h" +#include "app/gfx/font.h" +#include "app/l10n_util_win.h" +#include "app/resource_bundle.h" +#include "base/gfx/native_theme.h" +#include "base/logging.h" +#include "base/stl_util-inl.h" +#include "views/controls/tabbed_pane/tabbed_pane.h" +#include "views/fill_layout.h" +#include "views/widget/root_view.h" +#include "views/widget/widget_win.h" + +namespace views { + +// A background object that paints the tab panel background which may be +// rendered by the system visual styles system. +class TabBackground : public Background { + public: + explicit TabBackground() { + // TMT_FILLCOLORHINT returns a color value that supposedly + // approximates the texture drawn by PaintTabPanelBackground. + SkColor tab_page_color = + gfx::NativeTheme::instance()->GetThemeColorWithDefault( + gfx::NativeTheme::TAB, TABP_BODY, 0, TMT_FILLCOLORHINT, + COLOR_3DFACE); + SetNativeControlColor(tab_page_color); + } + virtual ~TabBackground() {} + + virtual void Paint(gfx::Canvas* canvas, View* view) const { + HDC dc = canvas->beginPlatformPaint(); + RECT r = {0, 0, view->width(), view->height()}; + gfx::NativeTheme::instance()->PaintTabPanelBackground(dc, &r); + canvas->endPlatformPaint(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TabBackground); +}; + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneWin, public: + +NativeTabbedPaneWin::NativeTabbedPaneWin(TabbedPane* tabbed_pane) + : NativeControlWin(), + tabbed_pane_(tabbed_pane), + content_window_(NULL) { + // Associates the actual HWND with the tabbed-pane so the tabbed-pane is + // the one considered as having the focus (not the wrapper) when the HWND is + // focused directly (with a click for example). + set_focus_view(tabbed_pane); +} + +NativeTabbedPaneWin::~NativeTabbedPaneWin() { + // We own the tab views, let's delete them. + STLDeleteContainerPointers(tab_views_.begin(), tab_views_.end()); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneWin, NativeTabbedPaneWrapper implementation: + +void NativeTabbedPaneWin::AddTab(const std::wstring& title, View* contents) { + AddTabAtIndex(static_cast<int>(tab_views_.size()), title, contents, true); +} + +void NativeTabbedPaneWin::AddTabAtIndex(int index, const std::wstring& title, + View* contents, + bool select_if_first_tab) { + DCHECK(index <= static_cast<int>(tab_views_.size())); + contents->SetParentOwned(false); + tab_views_.insert(tab_views_.begin() + index, contents); + + TCITEM tcitem; + tcitem.mask = TCIF_TEXT; + + // If the locale is RTL, we set the TCIF_RTLREADING so that BiDi text is + // rendered properly on the tabs. + if (UILayoutIsRightToLeft()) { + tcitem.mask |= TCIF_RTLREADING; + } + + tcitem.pszText = const_cast<wchar_t*>(title.c_str()); + int result = TabCtrl_InsertItem(native_view(), index, &tcitem); + DCHECK(result != -1); + + if (!contents->background()) + contents->set_background(new TabBackground); + + if (tab_views_.size() == 1 && select_if_first_tab) { + // If this is the only tab displayed, make sure the contents is set. + content_window_->GetRootView()->AddChildView(contents); + } + + // The newly added tab may have made the contents window smaller. + ResizeContents(); +} + +View* NativeTabbedPaneWin::RemoveTabAtIndex(int index) { + int tab_count = static_cast<int>(tab_views_.size()); + DCHECK(index >= 0 && index < tab_count); + + if (index < (tab_count - 1)) { + // Select the next tab. + SelectTabAt(index + 1); + } else { + // We are the last tab, select the previous one. + if (index > 0) { + SelectTabAt(index - 1); + } else { + // That was the last tab. Remove the contents. + content_window_->GetRootView()->RemoveAllChildViews(false); + } + } + TabCtrl_DeleteItem(native_view(), index); + + // The removed tab may have made the contents window bigger. + ResizeContents(); + + std::vector<View*>::iterator iter = tab_views_.begin() + index; + View* removed_tab = *iter; + tab_views_.erase(iter); + + return removed_tab; +} + +void NativeTabbedPaneWin::SelectTabAt(int index) { + DCHECK((index >= 0) && (index < static_cast<int>(tab_views_.size()))); + TabCtrl_SetCurSel(native_view(), index); + DoSelectTabAt(index); +} + +int NativeTabbedPaneWin::GetTabCount() { + return TabCtrl_GetItemCount(native_view()); +} + +int NativeTabbedPaneWin::GetSelectedTabIndex() { + return TabCtrl_GetCurSel(native_view()); +} + +View* NativeTabbedPaneWin::GetSelectedTab() { + return content_window_->GetRootView()->GetChildViewAt(0); +} + +View* NativeTabbedPaneWin::GetView() { + return this; +} + +void NativeTabbedPaneWin::SetFocus() { + // Focus the associated HWND. + Focus(); +} + +gfx::NativeView NativeTabbedPaneWin::GetTestingHandle() const { + return native_view(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneWin, NativeControlWin override: + +void NativeTabbedPaneWin::CreateNativeControl() { + // Create the tab control. + // + // Note that we don't follow the common convention for NativeControl + // subclasses and we don't pass the value returned from + // NativeControl::GetAdditionalExStyle() as the dwExStyle parameter. Here is + // why: on RTL locales, if we pass NativeControl::GetAdditionalExStyle() when + // we basically tell Windows to create our HWND with the WS_EX_LAYOUTRTL. If + // we do that, then the HWND we create for |content_window_| below will + // inherit the WS_EX_LAYOUTRTL property and this will result in the contents + // being flipped, which is not what we want (because we handle mirroring in + // views without the use of Windows' support for mirroring). Therefore, + // we initially create our HWND without the aforementioned property and we + // explicitly set this property our child is created. This way, on RTL + // locales, our tabs will be nicely rendered from right to left (by virtue of + // Windows doing the right thing with the TabbedPane HWND) and each tab + // contents will use an RTL layout correctly (by virtue of the mirroring + // infrastructure in views doing the right thing with each View we put + // in the tab). + HWND tab_control = ::CreateWindowEx(0, + WC_TABCONTROL, + L"", + WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, + 0, 0, width(), height(), + GetWidget()->GetNativeView(), NULL, NULL, + NULL); + + HFONT font = ResourceBundle::GetSharedInstance(). + GetFont(ResourceBundle::BaseFont).hfont(); + SendMessage(tab_control, WM_SETFONT, reinterpret_cast<WPARAM>(font), FALSE); + + // Create the view container which is a child of the TabControl. + content_window_ = new WidgetWin(); + content_window_->Init(tab_control, gfx::Rect()); + + // Explicitly setting the WS_EX_LAYOUTRTL property for the HWND (see above + // for a thorough explanation regarding why we waited until |content_window_| + // if created before we set this property for the tabbed pane's HWND). + if (UILayoutIsRightToLeft()) + l10n_util::HWNDSetRTLLayout(tab_control); + + RootView* root_view = content_window_->GetRootView(); + root_view->SetLayoutManager(new FillLayout()); + DWORD sys_color = ::GetSysColor(COLOR_3DHILIGHT); + SkColor color = SkColorSetRGB(GetRValue(sys_color), GetGValue(sys_color), + GetBValue(sys_color)); + root_view->set_background(Background::CreateSolidBackground(color)); + + content_window_->SetFocusTraversableParentView(this); + ResizeContents(); + + NativeControlCreated(tab_control); +} + +bool NativeTabbedPaneWin::ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) { + if (message == WM_NOTIFY && + reinterpret_cast<LPNMHDR>(l_param)->code == TCN_SELCHANGE) { + int selected_tab = TabCtrl_GetCurSel(native_view()); + DCHECK(selected_tab != -1); + DoSelectTabAt(selected_tab); + return TRUE; + } + return NativeControlWin::ProcessMessage(message, w_param, l_param, result); +} + +//////////////////////////////////////////////////////////////////////////////// +// View override: + +void NativeTabbedPaneWin::Layout() { + NativeControlWin::Layout(); + ResizeContents(); +} + +FocusTraversable* NativeTabbedPaneWin::GetFocusTraversable() { + return content_window_; +} + +void NativeTabbedPaneWin::ViewHierarchyChanged(bool is_add, + View *parent, + View *child) { + NativeControlWin::ViewHierarchyChanged(is_add, parent, child); + + if (is_add && (child == this) && content_window_) { + // We have been added to a view hierarchy, update the FocusTraversable + // parent. + content_window_->SetFocusTraversableParent(GetRootView()); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneWin, private: + +void NativeTabbedPaneWin::DoSelectTabAt(int index) { + RootView* content_root = content_window_->GetRootView(); + + // Clear the focus if the focused view was on the tab. + FocusManager* focus_manager = GetFocusManager(); + DCHECK(focus_manager); + View* focused_view = focus_manager->GetFocusedView(); + if (focused_view && content_root->IsParentOf(focused_view)) + focus_manager->ClearFocus(); + + content_root->RemoveAllChildViews(false); + content_root->AddChildView(tab_views_[index]); + content_root->Layout(); + + if (tabbed_pane_->listener()) + tabbed_pane_->listener()->TabSelectedAt(index); +} + +void NativeTabbedPaneWin::ResizeContents() { + CRect content_bounds; + if (!GetClientRect(native_view(), &content_bounds)) + return; + TabCtrl_AdjustRect(native_view(), FALSE, &content_bounds); + content_window_->MoveWindow(content_bounds.left, content_bounds.top, + content_bounds.Width(), content_bounds.Height(), + TRUE); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneWrapper, public: + +// static +NativeTabbedPaneWrapper* NativeTabbedPaneWrapper::CreateNativeWrapper( + TabbedPane* tabbed_pane) { + return new NativeTabbedPaneWin(tabbed_pane); +} + +} // namespace views diff --git a/views/controls/tabbed_pane/native_tabbed_pane_win.h b/views/controls/tabbed_pane/native_tabbed_pane_win.h new file mode 100644 index 0000000..be55d15 --- /dev/null +++ b/views/controls/tabbed_pane/native_tabbed_pane_win.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 VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WIN_H_ +#define VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WIN_H_ + +#include "views/controls/native_control_win.h" +#include "views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" + +namespace views { + +class WidgetWin; + +class NativeTabbedPaneWin : public NativeControlWin, + public NativeTabbedPaneWrapper { + public: + explicit NativeTabbedPaneWin(TabbedPane* tabbed_pane); + virtual ~NativeTabbedPaneWin(); + + // NativeTabbedPaneWrapper implementation: + virtual void AddTab(const std::wstring& title, View* contents); + virtual void AddTabAtIndex(int index, + const std::wstring& title, + View* contents, + bool select_if_first_tab); + virtual View* RemoveTabAtIndex(int index); + virtual void SelectTabAt(int index); + virtual int GetTabCount(); + virtual int GetSelectedTabIndex(); + virtual View* GetSelectedTab(); + virtual View* GetView(); + virtual void SetFocus(); + virtual gfx::NativeView GetTestingHandle() const; + + // NativeControlWin overrides. + virtual void CreateNativeControl(); + virtual bool ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result); + + // View overrides: + virtual void Layout(); + virtual FocusTraversable* GetFocusTraversable(); + virtual void ViewHierarchyChanged(bool is_add, View *parent, View *child); + + private: + // Changes the contents view to the view associated with the tab at |index|. + void DoSelectTabAt(int index); + + // Resizes the HWND control to macth the size of the containing view. + void ResizeContents(); + + // The tabbed-pane we are bound to. + TabbedPane* tabbed_pane_; + + // The views associated with the different tabs. + std::vector<View*> tab_views_; + + // The window displayed in the tab. + WidgetWin* content_window_; + + DISALLOW_COPY_AND_ASSIGN(NativeTabbedPaneWin); +}; + +} // namespace views + +#endif // VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WIN_H_ diff --git a/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h b/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h new file mode 100644 index 0000000..a051898 --- /dev/null +++ b/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h @@ -0,0 +1,68 @@ +// 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 VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WRAPPER_H_ +#define VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WRAPPER_H_ + +namespace views { + +class TabbedPane; +class View; + +// An interface implemented by an object that provides a platform-native +// tabbed-pane. +class NativeTabbedPaneWrapper { + public: + // The TabbedPane calls this when it is destroyed to clean up the wrapper + // object. + virtual ~NativeTabbedPaneWrapper() { } + + // Adds a new tab at the end of this TabbedPane with the specified |title|. + // |contents| is the view displayed when the tab is selected and is owned by + // the TabbedPane. + virtual void AddTab(const std::wstring& title, View* contents) = 0; + + // Adds a new tab at the specified |index| with the specified |title|. + // |contents| is the view displayed when the tab is selected and is owned by + // the TabbedPane. If |select_if_first_tab| is true and the tabbed pane is + // currently empty, the new tab is selected. If you pass in false for + // |select_if_first_tab| you need to explicitly invoke SelectTabAt, otherwise + // the tabbed pane will not have a valid selection. + virtual void AddTabAtIndex(int index, + const std::wstring& title, + View* contents, + bool select_if_first_tab) = 0; + + // Removes the tab at the specified |index| and returns the associated content + // view. The caller becomes the owner of the returned view. + virtual View* RemoveTabAtIndex(int index) = 0; + + // Selects the tab at the specified |index|, which must be valid. + virtual void SelectTabAt(int index) = 0; + + // Returns the number of tabs. + virtual int GetTabCount() = 0; + + // Returns the index of the selected tab. + virtual int GetSelectedTabIndex() = 0; + + // Returns the contents of the selected tab. + virtual View* GetSelectedTab() = 0; + + // Retrieves the views::View that hosts the native control. + virtual View* GetView() = 0; + + // Sets the focus to the tabbed pane native view. + virtual void SetFocus() = 0; + + // Returns a handle to the underlying native view for testing. + virtual gfx::NativeView GetTestingHandle() const = 0; + + // Creates an appropriate NativeTabbedPaneWrapper for the platform. + static NativeTabbedPaneWrapper* CreateNativeWrapper(TabbedPane* tabbed_pane); +}; + +} // namespace views + +#endif // VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WRAPPER_H_ diff --git a/views/controls/tabbed_pane/tabbed_pane.cc b/views/controls/tabbed_pane/tabbed_pane.cc new file mode 100644 index 0000000..20a2e7a --- /dev/null +++ b/views/controls/tabbed_pane/tabbed_pane.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2006-2008 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 "views/controls/tabbed_pane/tabbed_pane.h" + +#include "views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" + +namespace views { + +// static +const char TabbedPane::kViewClassName[] = "views/TabbedPane"; + +TabbedPane::TabbedPane() : native_tabbed_pane_(NULL), listener_(NULL) { + SetFocusable(true); +} + +TabbedPane::~TabbedPane() { +} + +void TabbedPane::SetListener(Listener* listener) { + listener_ = listener; +} + +void TabbedPane::AddTab(const std::wstring& title, View* contents) { + native_tabbed_pane_->AddTab(title, contents); +} + +void TabbedPane::AddTabAtIndex(int index, + const std::wstring& title, + View* contents, + bool select_if_first_tab) { + native_tabbed_pane_->AddTabAtIndex(index, title, contents, + select_if_first_tab); +} + +int TabbedPane::GetSelectedTabIndex() { + return native_tabbed_pane_->GetSelectedTabIndex(); +} + +View* TabbedPane::GetSelectedTab() { + return native_tabbed_pane_->GetSelectedTab(); +} + +View* TabbedPane::RemoveTabAtIndex(int index) { + return native_tabbed_pane_->RemoveTabAtIndex(index); +} + +void TabbedPane::SelectTabAt(int index) { + native_tabbed_pane_->SelectTabAt(index); +} + +int TabbedPane::GetTabCount() { + return native_tabbed_pane_->GetTabCount(); +} + +void TabbedPane::CreateWrapper() { + native_tabbed_pane_ = NativeTabbedPaneWrapper::CreateNativeWrapper(this); +} + +// View overrides: +std::string TabbedPane::GetClassName() const { + return kViewClassName; +} + +void TabbedPane::ViewHierarchyChanged(bool is_add, View* parent, View* child) { + if (is_add && !native_tabbed_pane_ && GetWidget()) { + CreateWrapper(); + AddChildView(native_tabbed_pane_->GetView()); + } +} + +void TabbedPane::Layout() { + if (native_tabbed_pane_) { + native_tabbed_pane_->GetView()->SetBounds(0, 0, width(), height()); + native_tabbed_pane_->GetView()->Layout(); + } +} + +void TabbedPane::Focus() { + // Forward the focus to the wrapper. + if (native_tabbed_pane_) + native_tabbed_pane_->SetFocus(); + else + View::Focus(); // Will focus the RootView window (so we still get keyboard + // messages). +} + +} // namespace views diff --git a/views/controls/tabbed_pane/tabbed_pane.h b/views/controls/tabbed_pane/tabbed_pane.h new file mode 100644 index 0000000..f0eb5a5 --- /dev/null +++ b/views/controls/tabbed_pane/tabbed_pane.h @@ -0,0 +1,92 @@ +// 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 VIEWS_CONTROLS_TABBED_PANE_H_ +#define VIEWS_CONTROLS_TABBED_PANE_H_ + +#include "views/view.h" + +namespace views { + +class NativeTabbedPaneWrapper; + +// The TabbedPane class is a view that shows tabs. When the user clicks on a +// tab, the associated view is displayed. +// TODO (jcampan): implement GetPreferredSize(). + +class TabbedPane : public View { + public: + TabbedPane(); + virtual ~TabbedPane(); + + // An interface an object can implement to be notified about events within + // the TabbedPane. + class Listener { + public: + // Called when the tab at the specified |index| is selected by the user. + virtual void TabSelectedAt(int index) = 0; + }; + void SetListener(Listener* listener); + + // Returns the number of tabs. + int GetTabCount(); + + // Returns the index of the selected tab. + int GetSelectedTabIndex(); + + // Returns the contents of the selected tab. + View* GetSelectedTab(); + + // Adds a new tab at the end of this TabbedPane with the specified |title|. + // |contents| is the view displayed when the tab is selected and is owned by + // the TabbedPane. + void AddTab(const std::wstring& title, View* contents); + + // Adds a new tab at the specified |index| with the specified |title|. + // |contents| is the view displayed when the tab is selected and is owned by + // the TabbedPane. If |select_if_first_tab| is true and the tabbed pane is + // currently empty, the new tab is selected. If you pass in false for + // |select_if_first_tab| you need to explicitly invoke SelectTabAt, otherwise + // the tabbed pane will not have a valid selection. + void AddTabAtIndex(int index, + const std::wstring& title, + View* contents, + bool select_if_first_tab); + + // Removes the tab at the specified |index| and returns the associated content + // view. The caller becomes the owner of the returned view. + View* RemoveTabAtIndex(int index); + + // Selects the tab at the specified |index|, which must be valid. + void SelectTabAt(int index); + + Listener* listener() const { return listener_; } + + // View overrides: + virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child); + virtual std::string GetClassName() const; + virtual void Layout(); + virtual void Focus(); + + protected: + // The object that actually implements the tabbed-pane. + // Protected for tests access. + NativeTabbedPaneWrapper* native_tabbed_pane_; + + private: + // The tabbed-pane's class name. + static const char kViewClassName[]; + + // Creates the native wrapper. + void CreateWrapper(); + + // The listener we notify about tab selection changes. + Listener* listener_; + + DISALLOW_COPY_AND_ASSIGN(TabbedPane); +}; + +} // namespace views + +#endif // VIEWS_CONTROLS_TABBED_PANE_H_ |