From 72bcfc775d748a460876d7ff04cfacad07b4496b Mon Sep 17 00:00:00 2001 From: "tfarina@chromium.org" Date: Fri, 25 Nov 2011 20:20:35 +0000 Subject: views: Move tabbed_pane directory to ui/views/controls/. BUG=104039 R=maruel@chromium.org TBR=ben@chromium.org Review URL: http://codereview.chromium.org/8658009 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111612 0039d316-1c4b-4281-b951-d872f2087c98 --- .../controls/tabbed_pane/native_tabbed_pane_gtk.cc | 233 ++++++++++++ .../controls/tabbed_pane/native_tabbed_pane_gtk.h | 69 ++++ .../controls/tabbed_pane/native_tabbed_pane_win.cc | 407 +++++++++++++++++++++ .../controls/tabbed_pane/native_tabbed_pane_win.h | 92 +++++ .../tabbed_pane/native_tabbed_pane_wrapper.h | 79 ++++ ui/views/controls/tabbed_pane/tabbed_pane.cc | 142 +++++++ ui/views/controls/tabbed_pane/tabbed_pane.h | 105 ++++++ .../controls/tabbed_pane/tabbed_pane_listener.h | 24 ++ .../controls/tabbed_pane/tabbed_pane_unittest.cc | 135 +++++++ ui/views/examples/examples_main.cc | 2 +- ui/views/examples/tabbed_pane_example.cc | 2 +- ui/views/examples/tabbed_pane_example.h | 2 +- ui/views/focus/focus_manager_unittest.cc | 4 +- ui/views/focus/focus_traversal_unittest.cc | 2 +- 14 files changed, 1292 insertions(+), 6 deletions(-) create mode 100644 ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.cc create mode 100644 ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h create mode 100644 ui/views/controls/tabbed_pane/native_tabbed_pane_win.cc create mode 100644 ui/views/controls/tabbed_pane/native_tabbed_pane_win.h create mode 100644 ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h create mode 100644 ui/views/controls/tabbed_pane/tabbed_pane.cc create mode 100644 ui/views/controls/tabbed_pane/tabbed_pane.h create mode 100644 ui/views/controls/tabbed_pane/tabbed_pane_listener.h create mode 100644 ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc (limited to 'ui') diff --git a/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.cc b/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.cc new file mode 100644 index 0000000..34e32ad --- /dev/null +++ b/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.cc @@ -0,0 +1,233 @@ +// Copyright (c) 2011 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 "ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h" + +#include + +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/utf_string_conversions.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/font.h" +#include "ui/gfx/skia_utils_gtk.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/widget/native_widget.h" +#include "ui/views/widget/widget.h" +#include "views/background.h" + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneGtk, public: + +NativeTabbedPaneGtk::NativeTabbedPaneGtk(TabbedPane* tabbed_pane) + : NativeControlGtk(), + tabbed_pane_(tabbed_pane) { + set_focus_view(tabbed_pane); +} + +NativeTabbedPaneGtk::~NativeTabbedPaneGtk() { +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneGtk, NativeControlGtk implementation: + +void NativeTabbedPaneGtk::CreateNativeControl() { + GtkWidget* widget = gtk_notebook_new(); + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(widget), GTK_POS_TOP); + g_signal_connect(widget, "switch-page", + G_CALLBACK(CallSwitchPage), this); + NativeControlCreated(widget); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneGtk, NativeTabbedPaneWrapper implementation: + +void NativeTabbedPaneGtk::AddTab(const string16& title, View* contents) { + AddTabAtIndex(GetTabCount(), title, contents, true); +} + +void NativeTabbedPaneGtk::AddTabAtIndex(int index, + const string16& title, + View* contents, + bool select_if_first_tab) { + DCHECK(native_view()); + DoAddTabAtIndex(index, title, contents, select_if_first_tab); +} + +View* NativeTabbedPaneGtk::RemoveTabAtIndex(int index) { + int tab_count = GetTabCount(); + 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 { + // last tab. nothing to select. + } + } + + GtkWidget* page = + gtk_notebook_get_nth_page(GTK_NOTEBOOK(native_view()), index); + Widget* widget = Widget::GetWidgetForNativeView(page); + + // detach the content view from widget so that we can delete widget + // without destroying the content view. + View* removed_tab = GetTabViewAt(index); + widget->GetRootView()->RemoveChildView(removed_tab); + + // widget delete itself when native_view is deleted. + gtk_notebook_remove_page(GTK_NOTEBOOK(native_view()), index); + + // Removing a tab might change the size of the tabbed pane. + if (GetWidget()) + GetWidget()->GetRootView()->Layout(); + + return removed_tab; +} + +void NativeTabbedPaneGtk::SelectTabAt(int index) { + DCHECK((index >= 0) && (index < GetTabCount())); + gtk_notebook_set_current_page(GTK_NOTEBOOK(native_view()), index); +} + +int NativeTabbedPaneGtk::GetTabCount() { + return gtk_notebook_get_n_pages(GTK_NOTEBOOK(native_view())); +} + +int NativeTabbedPaneGtk::GetSelectedTabIndex() { + return gtk_notebook_get_current_page(GTK_NOTEBOOK(native_view())); +} + +View* NativeTabbedPaneGtk::GetSelectedTab() { + return GetTabViewAt(GetSelectedTabIndex()); +} + +View* NativeTabbedPaneGtk::GetView() { + return this; +} + +void NativeTabbedPaneGtk::SetFocus() { + OnFocus(); +} + +gfx::Size NativeTabbedPaneGtk::GetPreferredSize() { + if (!native_view()) + return gfx::Size(); + + // For some strange reason (or maybe it's a bug), the requisition is not + // returned in the passed requisition parameter, but instead written to the + // widget's requisition field. + GtkRequisition requisition = { 0, 0 }; + gtk_widget_size_request(GTK_WIDGET(native_view()), &requisition); + GtkRequisition& size(GTK_WIDGET(native_view())->requisition); + return gfx::Size(size.width, size.height); +} + +gfx::NativeView NativeTabbedPaneGtk::GetTestingHandle() const { + return native_view(); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneGtk, View implementation: + +FocusTraversable* NativeTabbedPaneGtk::GetFocusTraversable() { + return GetWidgetAt(GetSelectedTabIndex()); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneGtk, private: +void NativeTabbedPaneGtk::DoAddTabAtIndex(int index, + const string16& title, + View* contents, + bool select_if_first_tab) { + int tab_count = GetTabCount(); + DCHECK(index <= tab_count); + + Widget* page_container = new Widget; + page_container->Init( + Widget::InitParams(Widget::InitParams::TYPE_CONTROL)); + page_container->SetContentsView(contents); + page_container->SetFocusTraversableParent(GetWidget()->GetFocusTraversable()); + page_container->SetFocusTraversableParentView(this); + page_container->Show(); + + if (!contents->background()) { + GtkStyle* window_style = + gtk_widget_get_style(page_container->GetNativeView()); + contents->set_background( + Background::CreateSolidBackground( + gfx::GdkColorToSkColor(window_style->bg[GTK_STATE_NORMAL]))); + } + + GtkWidget* page = page_container->GetNativeView(); + // increment ref count not to delete on remove below + g_object_ref(page); + // detach parent from the page so that we can add it to notebook + GtkWidget* parent = gtk_widget_get_parent(page); + gtk_container_remove(GTK_CONTAINER(parent), page); + + GtkWidget* label = gtk_label_new(UTF16ToUTF8(title).c_str()); + gtk_widget_show(label); + gtk_notebook_insert_page(GTK_NOTEBOOK(native_view()), + page, + label, + index); + g_object_unref(page); + + if (tab_count == 0 && select_if_first_tab) + gtk_notebook_set_current_page(GTK_NOTEBOOK(native_view()), 0); + + // Relayout the hierarchy, since the added tab might require more space. + if (GetWidget()) + GetWidget()->GetRootView()->Layout(); +} + +Widget* NativeTabbedPaneGtk::GetWidgetAt(int index) { + DCHECK(index <= GetTabCount()); + GtkWidget* page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(native_view()), + index); + Widget* widget = Widget::GetWidgetForNativeView(page); + DCHECK(widget); + return widget; +} + +View* NativeTabbedPaneGtk::GetTabViewAt(int index) { + Widget* widget = GetWidgetAt(index); + DCHECK(widget); + DCHECK_EQ(1, widget->GetRootView()->child_count()); + return widget->GetRootView()->child_at(0); +} + +void NativeTabbedPaneGtk::OnSwitchPage(int selected_tab_index) { + TabbedPaneListener* listener = tabbed_pane_->listener(); + if (listener != NULL) + listener->TabSelectedAt(selected_tab_index); +} + +// static +void NativeTabbedPaneGtk::CallSwitchPage(GtkNotebook* widget, + GtkNotebookPage* page, + guint selected_tab_index, + NativeTabbedPaneGtk* tabbed_pane) { + tabbed_pane->OnSwitchPage(selected_tab_index); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneWrapper, public: + +// static +NativeTabbedPaneWrapper* NativeTabbedPaneWrapper::CreateNativeWrapper( + TabbedPane* tabbed_pane) { + return new NativeTabbedPaneGtk(tabbed_pane); +} + +} // namespace views diff --git a/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h b/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h new file mode 100644 index 0000000..c37af6c --- /dev/null +++ b/ui/views/controls/tabbed_pane/native_tabbed_pane_gtk.h @@ -0,0 +1,69 @@ +// Copyright (c) 2011 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 UI_VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_GTK_H_ +#define UI_VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_GTK_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" +#include "views/controls/native_control_gtk.h" + +namespace views { + +class NativeTabbedPaneGtk : public NativeControlGtk, + public NativeTabbedPaneWrapper { + public: + explicit NativeTabbedPaneGtk(TabbedPane* tabbed_pane); + virtual ~NativeTabbedPaneGtk(); + + // NativeControlGtk: + virtual void CreateNativeControl() OVERRIDE; + + // NativeTabbedPaneWrapper: + virtual void AddTab(const string16& title, View* contents) OVERRIDE; + virtual void AddTabAtIndex(int index, + const string16& title, + View* contents, + bool select_if_first_tab) OVERRIDE; + virtual View* RemoveTabAtIndex(int index) OVERRIDE; + virtual void SelectTabAt(int index) OVERRIDE; + virtual int GetTabCount() OVERRIDE; + virtual int GetSelectedTabIndex() OVERRIDE; + virtual View* GetSelectedTab() OVERRIDE; + virtual View* GetView() OVERRIDE; + virtual void SetFocus() OVERRIDE; + virtual gfx::Size GetPreferredSize() OVERRIDE; + virtual gfx::NativeView GetTestingHandle() const OVERRIDE; + + // View: + virtual FocusTraversable* GetFocusTraversable() OVERRIDE; + + private: + void DoAddTabAtIndex(int index, + const string16& title, + View* contents, + bool select_if_first_tab); + + // Returns the Widget containing the tab contents at |index|. + Widget* GetWidgetAt(int index); + + View* GetTabViewAt(int index); + void OnSwitchPage(int selected_tab_index); + + static void CallSwitchPage(GtkNotebook* widget, + GtkNotebookPage* page, + guint selected_tab_index, + NativeTabbedPaneGtk* tabbed_pane); + + // The tabbed-pane we are bound to. + TabbedPane* tabbed_pane_; + + DISALLOW_COPY_AND_ASSIGN(NativeTabbedPaneGtk); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_GTK_H_ diff --git a/ui/views/controls/tabbed_pane/native_tabbed_pane_win.cc b/ui/views/controls/tabbed_pane/native_tabbed_pane_win.cc new file mode 100644 index 0000000..3648e1f --- /dev/null +++ b/ui/views/controls/tabbed_pane/native_tabbed_pane_win.cc @@ -0,0 +1,407 @@ +// Copyright (c) 2011 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 "ui/views/controls/tabbed_pane/native_tabbed_pane_win.h" + +#include + +#include "base/logging.h" +#include "base/stl_util.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/base/win/hwnd_util.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/font.h" +#include "ui/gfx/native_theme_win.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/widget/root_view.h" +#include "ui/views/widget/widget.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::NativeThemeWin::instance()->GetThemeColorWithDefault( + gfx::NativeThemeWin::TAB, TABP_BODY, 0, TMT_FILLCOLORHINT, + COLOR_3DFACE); + SetNativeControlColor(tab_page_color); + } + virtual ~TabBackground() {} + + virtual void Paint(gfx::Canvas* canvas, View* view) const { + gfx::Rect r(0, 0, view->width(), view->height()); + gfx::NativeTheme::ExtraParams extra; + gfx::NativeTheme::instance()->Paint( + canvas->GetSkCanvas(), gfx::NativeTheme::kTabPanelBackground, + gfx::NativeTheme::kNormal, r, extra); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TabBackground); +}; + +// Custom layout manager that takes care of sizing and displaying the tab pages. +class TabLayout : public LayoutManager { + public: + TabLayout() {} + + // Switches to the tab page identified by the given index. + void SwitchToPage(View* host, View* page) { + for (int i = 0; i < host->child_count(); ++i) { + View* child = host->child_at(i); + // The child might not have been laid out yet. + if (child == page) + child->SetBoundsRect(host->GetContentsBounds()); + child->SetVisible(child == page); + } + + FocusManager* focus_manager = page->GetFocusManager(); + DCHECK(focus_manager); + const View* focused_view = focus_manager->GetFocusedView(); + if (focused_view && host->Contains(focused_view) && + !page->Contains(focused_view)) + focus_manager->SetFocusedView(page); + } + + private: + // LayoutManager overrides: + virtual void Layout(View* host) { + gfx::Rect bounds(host->GetContentsBounds()); + for (int i = 0; i < host->child_count(); ++i) { + View* child = host->child_at(i); + // We only layout visible children, since it may be expensive. + if (child->IsVisible() && child->bounds() != bounds) + child->SetBoundsRect(bounds); + } + } + + virtual gfx::Size GetPreferredSize(View* host) { + // First, query the preferred sizes to determine a good width. + int width = 0; + for (int i = 0; i < host->child_count(); ++i) { + View* page = host->child_at(i); + width = std::max(width, page->GetPreferredSize().width()); + } + // After we know the width, decide on the height. + return gfx::Size(width, GetPreferredHeightForWidth(host, width)); + } + + virtual int GetPreferredHeightForWidth(View* host, int width) { + int height = 0; + for (int i = 0; i < host->child_count(); ++i) { + View* page = host->child_at(i); + height = std::max(height, page->GetHeightForWidth(width)); + } + return height; + } + + DISALLOW_COPY_AND_ASSIGN(TabLayout); +}; + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneWin, public: + +NativeTabbedPaneWin::NativeTabbedPaneWin(TabbedPane* tabbed_pane) + : NativeControlWin(), + tabbed_pane_(tabbed_pane), + tab_layout_manager_(NULL), + content_window_(NULL), + selected_index_(-1) { + // 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 string16& title, View* contents) { + AddTabAtIndex(static_cast(tab_views_.size()), title, contents, true); +} + +void NativeTabbedPaneWin::AddTabAtIndex(int index, + const string16& title, + View* contents, + bool select_if_first_tab) { + DCHECK(index <= static_cast(tab_views_.size())); + contents->set_parent_owned(false); + contents->SetVisible(false); + tab_views_.insert(tab_views_.begin() + index, contents); + tab_titles_.insert(tab_titles_.begin() + index, title); + + if (!contents->background()) + contents->set_background(new TabBackground); + + if (tab_views_.size() == 1 && select_if_first_tab) + selected_index_ = 0; + + // Add native tab only if the native control is alreay created. + if (content_window_) { + AddNativeTab(index, title); + // The newly added tab may have made the contents window smaller. + ResizeContents(); + + View* content_root = content_window_->GetRootView(); + content_root->AddChildView(contents); + // Switch to the newly added tab if requested; + if (tab_views_.size() == 1 && select_if_first_tab) + tab_layout_manager_->SwitchToPage(content_root, contents); + } +} + +void NativeTabbedPaneWin::AddNativeTab(int index, const string16& title) { + 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 (base::i18n::IsRTL()) { + tcitem.mask |= TCIF_RTLREADING; + } + + tcitem.pszText = const_cast(title.c_str()); + int result = TabCtrl_InsertItem(native_view(), index, &tcitem); + DCHECK(result != -1); +} + +View* NativeTabbedPaneWin::RemoveTabAtIndex(int index) { + int tab_count = static_cast(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 if (content_window_) { + // 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. + if (content_window_) + ResizeContents(); + + std::vector::iterator iter = tab_views_.begin() + index; + View* removed_tab = *iter; + if (content_window_) + content_window_->GetRootView()->RemoveChildView(removed_tab); + tab_views_.erase(iter); + tab_titles_.erase(tab_titles_.begin() + index); + + return removed_tab; +} + +void NativeTabbedPaneWin::SelectTabAt(int index) { + DCHECK((index >= 0) && (index < static_cast(tab_views_.size()))); + if (native_view()) + TabCtrl_SetCurSel(native_view(), index); + DoSelectTabAt(index, true); +} + +int NativeTabbedPaneWin::GetTabCount() { + return TabCtrl_GetItemCount(native_view()); +} + +int NativeTabbedPaneWin::GetSelectedTabIndex() { + return TabCtrl_GetCurSel(native_view()); +} + +View* NativeTabbedPaneWin::GetSelectedTab() { + if (selected_index_ < 0) + return NULL; + return tab_views_[selected_index_]; +} + +View* NativeTabbedPaneWin::GetView() { + return this; +} + +void NativeTabbedPaneWin::SetFocus() { + // Focus the associated HWND. + OnFocus(); +} + +gfx::Size NativeTabbedPaneWin::GetPreferredSize() { + if (!native_view()) + return gfx::Size(); + + gfx::Rect contentSize(content_window_->GetRootView()->GetPreferredSize()); + RECT paneSize = { 0, 0, contentSize.width(), contentSize.height() }; + TabCtrl_AdjustRect(native_view(), TRUE, &paneSize); + return gfx::Size(paneSize.right - paneSize.left, + paneSize.bottom - paneSize.top); +} + +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 WS_EX_LAYOUTRTL 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). + DWORD style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | WS_CLIPCHILDREN; + HWND tab_control = ::CreateWindowEx(0, + WC_TABCONTROL, + L"", + style, + 0, 0, width(), height(), + GetWidget()->GetNativeView(), NULL, NULL, + NULL); + ui::CheckWindowCreated(tab_control); + + HFONT font = ResourceBundle::GetSharedInstance(). + GetFont(ResourceBundle::BaseFont).GetNativeFont(); + SendMessage(tab_control, WM_SETFONT, reinterpret_cast(font), FALSE); + + // Create the view container which is a child of the TabControl. + content_window_ = new Widget; + Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); + params.parent = tab_control; + content_window_->Init(params); + + // Explicitly setting the WS_EX_LAYOUTRTL property for the HWND (see above + // for why we waited until |content_window_| is created before we set this + // property for the tabbed pane's HWND). + if (base::i18n::IsRTL()) + l10n_util::HWNDSetRTLLayout(tab_control); + + View* root_view = content_window_->GetRootView(); + tab_layout_manager_ = new TabLayout(); + root_view->SetLayoutManager(tab_layout_manager_); + 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); + + NativeControlCreated(tab_control); + + // Add tabs that are already added if any. + if (!tab_views_.empty()) { + InitializeTabs(); + if (selected_index_ >= 0) + DoSelectTabAt(selected_index_, false); + } + + ResizeContents(); +} + +bool NativeTabbedPaneWin::ProcessMessage(UINT message, + WPARAM w_param, + LPARAM l_param, + LRESULT* result) { + if (message == WM_NOTIFY && + reinterpret_cast(l_param)->code == TCN_SELCHANGE) { + int selected_tab = TabCtrl_GetCurSel(native_view()); + DCHECK(selected_tab != -1); + DoSelectTabAt(selected_tab, true); + 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( + GetWidget()->GetFocusTraversable()); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneWin, private: + +void NativeTabbedPaneWin::InitializeTabs() { + for (size_t i = 0; i < tab_titles_.size(); ++i) + AddNativeTab(i, tab_titles_[i]); + + View* content_root = content_window_->GetRootView(); + for (std::vector::iterator tab(tab_views_.begin()); + tab != tab_views_.end(); ++tab) + content_root->AddChildView(*tab); +} + +void NativeTabbedPaneWin::DoSelectTabAt(int index, boolean invoke_listener) { + selected_index_ = index; + if (content_window_) { + View* content_root = content_window_->GetRootView(); + tab_layout_manager_->SwitchToPage(content_root, tab_views_[index]); + } + if (invoke_listener && tabbed_pane_->listener()) + tabbed_pane_->listener()->TabSelectedAt(index); +} + +void NativeTabbedPaneWin::ResizeContents() { + RECT content_bounds; + if (!GetClientRect(native_view(), &content_bounds)) + return; + TabCtrl_AdjustRect(native_view(), FALSE, &content_bounds); + content_window_->SetBounds(gfx::Rect(content_bounds)); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeTabbedPaneWrapper, public: + +// static +NativeTabbedPaneWrapper* NativeTabbedPaneWrapper::CreateNativeWrapper( + TabbedPane* tabbed_pane) { + return new NativeTabbedPaneWin(tabbed_pane); +} + +} // namespace views diff --git a/ui/views/controls/tabbed_pane/native_tabbed_pane_win.h b/ui/views/controls/tabbed_pane/native_tabbed_pane_win.h new file mode 100644 index 0000000..8935c1a --- /dev/null +++ b/ui/views/controls/tabbed_pane/native_tabbed_pane_win.h @@ -0,0 +1,92 @@ +// Copyright (c) 2011 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 UI_VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WIN_H_ +#define UI_VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WIN_H_ +#pragma once + +#include + +#include "ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" +#include "views/controls/native_control_win.h" + +namespace views { + +class Widget; +class TabLayout; + +class NativeTabbedPaneWin : public NativeControlWin, + public NativeTabbedPaneWrapper { + public: + explicit NativeTabbedPaneWin(TabbedPane* tabbed_pane); + virtual ~NativeTabbedPaneWin(); + + // NativeTabbedPaneWrapper implementation: + virtual void AddTab(const string16& title, View* contents); + virtual void AddTabAtIndex(int index, + const string16& 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::Size GetPreferredSize(); + 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: + // Called upon creation of native control to initialize tabs that are added + // before the native control is created. + void InitializeTabs(); + + // Adds a tab with the given content to native control at the given index. + void AddNativeTab(int index, const string16& title); + + // Changes the contents view to the view associated with the tab at |index|. + // |invoke_listener| controls if this methold should invoke the + // Listener::TabSelectedAt callback. + void DoSelectTabAt(int index, boolean invoke_listener); + + // 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 layout manager we use for managing our tabs. + TabLayout* tab_layout_manager_; + + // The views associated with the different tabs. + std::vector tab_views_; + + // The tab's title strings. + std::vector tab_titles_; + + // The index of the selected tab. + int selected_index_; + + // The window displayed in the tab. + Widget* content_window_; + + DISALLOW_COPY_AND_ASSIGN(NativeTabbedPaneWin); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WIN_H_ diff --git a/ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h b/ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h new file mode 100644 index 0000000..90a651f --- /dev/null +++ b/ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h @@ -0,0 +1,79 @@ +// Copyright (c) 2011 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 UI_VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WRAPPER_H_ +#define UI_VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WRAPPER_H_ +#pragma once + +#include "base/string16.h" +#include "ui/gfx/native_widget_types.h" + +namespace gfx { +class Size; +} + +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 string16& 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 string16& 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; + + // Gets the preferred size of the tabbed pane. + virtual gfx::Size GetPreferredSize() = 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 // UI_VIEWS_CONTROLS_TABBED_PANE_NATIVE_TABBED_PANE_WRAPPER_H_ diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc new file mode 100644 index 0000000..1f797df --- /dev/null +++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc @@ -0,0 +1,142 @@ +// Copyright (c) 2011 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 "ui/views/controls/tabbed_pane/tabbed_pane.h" + +#include "base/logging.h" +#include "ui/base/accessibility/accessible_view_state.h" +#include "ui/base/keycodes/keyboard_codes.h" +#include "ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" +#include "ui/views/widget/widget.h" +#include "views/controls/native/native_view_host.h" + +namespace views { + +// static +const char TabbedPane::kViewClassName[] = "views/TabbedPane"; + +TabbedPane::TabbedPane() : native_tabbed_pane_(NULL), listener_(NULL) { + set_focusable(true); +} + +TabbedPane::~TabbedPane() { +} + +int TabbedPane::GetTabCount() { + return native_tabbed_pane_->GetTabCount(); +} + +int TabbedPane::GetSelectedTabIndex() { + return native_tabbed_pane_->GetSelectedTabIndex(); +} + +View* TabbedPane::GetSelectedTab() { + return native_tabbed_pane_->GetSelectedTab(); +} + +void TabbedPane::AddTab(const string16& title, View* contents) { + native_tabbed_pane_->AddTab(title, contents); + PreferredSizeChanged(); +} + +void TabbedPane::AddTabAtIndex(int index, + const string16& title, + View* contents, + bool select_if_first_tab) { + native_tabbed_pane_->AddTabAtIndex(index, title, contents, + select_if_first_tab); + PreferredSizeChanged(); +} + +View* TabbedPane::RemoveTabAtIndex(int index) { + View* tab = native_tabbed_pane_->RemoveTabAtIndex(index); + PreferredSizeChanged(); + return tab; +} + +void TabbedPane::SelectTabAt(int index) { + native_tabbed_pane_->SelectTabAt(index); +} + +void TabbedPane::SetAccessibleName(const string16& name) { + accessible_name_ = name; +} + +gfx::Size TabbedPane::GetPreferredSize() { + return native_tabbed_pane_ ? + native_tabbed_pane_->GetPreferredSize() : gfx::Size(); +} + +void TabbedPane::LoadAccelerators() { + // Ctrl+Shift+Tab + AddAccelerator(ui::Accelerator(ui::VKEY_TAB, true, true, false)); + // Ctrl+Tab + AddAccelerator(ui::Accelerator(ui::VKEY_TAB, false, true, false)); +} + +void TabbedPane::Layout() { + if (native_tabbed_pane_) + native_tabbed_pane_->GetView()->SetBounds(0, 0, width(), height()); +} + +void TabbedPane::ViewHierarchyChanged(bool is_add, View* parent, View* child) { + if (is_add && !native_tabbed_pane_) { + // The native wrapper's lifetime will be managed by the view hierarchy after + // we call AddChildView. + native_tabbed_pane_ = NativeTabbedPaneWrapper::CreateNativeWrapper(this); + AddChildView(native_tabbed_pane_->GetView()); + LoadAccelerators(); + } +} + +bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { + // We only accept Ctrl+Tab keyboard events. + DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); + + int tab_count = GetTabCount(); + if (tab_count <= 1) + return false; + int selected_tab_index = GetSelectedTabIndex(); + int next_tab_index = accelerator.IsShiftDown() ? + (selected_tab_index - 1) % tab_count : + (selected_tab_index + 1) % tab_count; + // Wrap around. + if (next_tab_index < 0) + next_tab_index += tab_count; + SelectTabAt(next_tab_index); + return true; +} + +std::string TabbedPane::GetClassName() const { + return kViewClassName; +} + +void TabbedPane::OnFocus() { + // Forward the focus to the wrapper. + if (native_tabbed_pane_) { + native_tabbed_pane_->SetFocus(); + + View* selected_tab = GetSelectedTab(); + if (selected_tab) { + selected_tab->GetWidget()->NotifyAccessibilityEvent( + selected_tab, ui::AccessibilityTypes::EVENT_FOCUS, true); + } + } else { + View::OnFocus(); // Will focus the RootView window (so we still get + // keyboard messages). + } +} + +void TabbedPane::OnPaintFocusBorder(gfx::Canvas* canvas) { + if (NativeViewHost::kRenderNativeControlFocus) + View::OnPaintFocusBorder(canvas); +} + +void TabbedPane::GetAccessibleState(ui::AccessibleViewState* state) { + state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST; + state->name = accessible_name_; +} + +} // namespace views diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.h b/ui/views/controls/tabbed_pane/tabbed_pane.h new file mode 100644 index 0000000..97a9d63 --- /dev/null +++ b/ui/views/controls/tabbed_pane/tabbed_pane.h @@ -0,0 +1,105 @@ +// Copyright (c) 2011 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 UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_H_ +#define UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/string16.h" +#include "views/view.h" + +namespace views { + +class NativeTabbedPaneWrapper; +class TabbedPaneListener; + +// TabbedPane is a view that shows tabs. When the user clicks on a tab, the +// associated view is displayed. +class VIEWS_EXPORT TabbedPane : public View { + public: + TabbedPane(); + virtual ~TabbedPane(); + + TabbedPaneListener* listener() const { return listener_; } + void set_listener(TabbedPaneListener* listener) { listener_ = listener; } + + NativeTabbedPaneWrapper* native_wrapper() const { + return native_tabbed_pane_; + } + + // 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 string16& title, View* contents); + + // Adds a new tab at |index| with |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 string16& title, + View* contents, + bool select_if_first_tab); + + // Removes the tab at |index| and returns the associated content view. + // The caller becomes the owner of the returned view. + View* RemoveTabAtIndex(int index); + + // Selects the tab at |index|, which must be valid. + void SelectTabAt(int index); + + void SetAccessibleName(const string16& name); + + // View: + virtual gfx::Size GetPreferredSize() OVERRIDE; + + 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[]; + + // We support Ctrl+Tab and Ctrl+Shift+Tab to navigate tabbed option pages. + void LoadAccelerators(); + + // View: + virtual void Layout() OVERRIDE; + virtual void ViewHierarchyChanged(bool is_add, + View* parent, + View* child) OVERRIDE; + // Handles Ctrl+Tab and Ctrl+Shift+Tab navigation of pages. + virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; + virtual std::string GetClassName() const OVERRIDE; + virtual void OnFocus() OVERRIDE; + virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE; + virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; + + // The listener we notify about tab selection changes. + TabbedPaneListener* listener_; + + // The accessible name of this view. + string16 accessible_name_; + + DISALLOW_COPY_AND_ASSIGN(TabbedPane); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_H_ diff --git a/ui/views/controls/tabbed_pane/tabbed_pane_listener.h b/ui/views/controls/tabbed_pane/tabbed_pane_listener.h new file mode 100644 index 0000000..05df022 --- /dev/null +++ b/ui/views/controls/tabbed_pane/tabbed_pane_listener.h @@ -0,0 +1,24 @@ +// Copyright (c) 2011 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 UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_LISTENER_H_ +#define UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_LISTENER_H_ +#pragma once + +namespace views { + +// An interface implemented by an object to let it know that a tabbed pane was +// selected by the user at the specified index. +class TabbedPaneListener { + public: + // Called when the tab at |index| is selected by the user. + virtual void TabSelectedAt(int index) = 0; + + protected: + virtual ~TabbedPaneListener() {} +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_TABBED_PANE_TABBED_PANE_LISTENER_H_ diff --git a/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc new file mode 100644 index 0000000..e209c8b --- /dev/null +++ b/ui/views/controls/tabbed_pane/tabbed_pane_unittest.cc @@ -0,0 +1,135 @@ +// Copyright (c) 2011 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 "base/message_loop.h" +#include "base/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane.h" +#include "ui/views/test/views_test_base.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace views { + +// A view for testing that takes a fixed preferred size upon construction. +class FixedSizeView : public View { + public: + FixedSizeView(const gfx::Size& size) + : size_(size) {} + + virtual gfx::Size GetPreferredSize() { + return size_; + } + + private: + const gfx::Size size_; + + DISALLOW_COPY_AND_ASSIGN(FixedSizeView); +}; + +class TabbedPaneTest : public ViewsTestBase, + public WidgetDelegate { + public: + TabbedPaneTest() {} + + TabbedPane* tabbed_pane_; + + private: + virtual void SetUp() OVERRIDE { + ViewsTestBase::SetUp(); + tabbed_pane_ = new TabbedPane(); + window_ = Widget::CreateWindowWithBounds(this, gfx::Rect(0, 0, 100, 100)); + window_->Show(); + } + + virtual void TearDown() OVERRIDE { + window_->Close(); + ViewsTestBase::TearDown(); + } + + virtual views::View* GetContentsView() OVERRIDE { + return tabbed_pane_; + } + virtual views::Widget* GetWidget() OVERRIDE { + return tabbed_pane_->GetWidget(); + } + virtual const views::Widget* GetWidget() const OVERRIDE { + return tabbed_pane_->GetWidget(); + } + + Widget* window_; + + DISALLOW_COPY_AND_ASSIGN(TabbedPaneTest); +}; + +// Tests that TabbedPane::GetPreferredSize() and TabbedPane::Layout(). +TEST_F(TabbedPaneTest, SizeAndLayout) { + View* child1 = new FixedSizeView(gfx::Size(20, 10)); + tabbed_pane_->AddTab(ASCIIToUTF16("tab1"), child1); + View* child2 = new FixedSizeView(gfx::Size(5, 5)); + tabbed_pane_->AddTab(ASCIIToUTF16("tab2"), child2); + tabbed_pane_->SelectTabAt(0); + + // Check that the preferred size is larger than the largest child. + gfx::Size pref(tabbed_pane_->GetPreferredSize()); + EXPECT_GT(pref.width(), 20); + EXPECT_GT(pref.height(), 10); + + // The bounds of our children should be smaller than the tabbed pane's bounds. + tabbed_pane_->SetBounds(0, 0, 100, 200); + RunPendingMessages(); + gfx::Rect bounds(child1->bounds()); + EXPECT_GT(bounds.width(), 0); + EXPECT_LT(bounds.width(), 100); + EXPECT_GT(bounds.height(), 0); + EXPECT_LT(bounds.height(), 200); + + // If we switch to the other tab, it should get assigned the same bounds. + tabbed_pane_->SelectTabAt(1); + EXPECT_EQ(bounds, child2->bounds()); +} + +TEST_F(TabbedPaneTest, AddRemove) { + View* tab0 = new View; + tabbed_pane_->AddTab(ASCIIToUTF16("tab0"), tab0); + EXPECT_EQ(tab0, tabbed_pane_->GetSelectedTab()); + EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); + + // Add more 3 tabs. + tabbed_pane_->AddTab(ASCIIToUTF16("tab1"), new View); + tabbed_pane_->AddTab(ASCIIToUTF16("tab2"), new View); + tabbed_pane_->AddTab(ASCIIToUTF16("tab3"), new View); + EXPECT_EQ(4, tabbed_pane_->GetTabCount()); + + // Note: AddTab() doesn't select a tab if the tabbed pane isn't empty. + + // Select the last one. + tabbed_pane_->SelectTabAt(tabbed_pane_->GetTabCount() - 1); + EXPECT_EQ(3, tabbed_pane_->GetSelectedTabIndex()); + + // Remove the last one. + delete tabbed_pane_->RemoveTabAtIndex(3); + EXPECT_EQ(3, tabbed_pane_->GetTabCount()); + + // After removing the last tab, check if the tabbed pane selected the previous + // tab. + EXPECT_EQ(2, tabbed_pane_->GetSelectedTabIndex()); + + tabbed_pane_->AddTabAtIndex(0, ASCIIToUTF16("tab4"), new View, true); + + // Assert that even adding a new tab, the tabbed pane doesn't change the + // selection, i.e., it doesn't select the new one. + // The last tab should remains selected, instead of the tab added at index 0. + EXPECT_EQ(3, tabbed_pane_->GetSelectedTabIndex()); + + // Now change the selected tab. + tabbed_pane_->SelectTabAt(1); + EXPECT_EQ(1, tabbed_pane_->GetSelectedTabIndex()); + + // Remove the first one. + delete tabbed_pane_->RemoveTabAtIndex(0); + EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); +} + +} // namespace views diff --git a/ui/views/examples/examples_main.cc b/ui/views/examples/examples_main.cc index f687d28..b776c60 100644 --- a/ui/views/examples/examples_main.cc +++ b/ui/views/examples/examples_main.cc @@ -12,6 +12,7 @@ #include "base/utf_string_conversions.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_paths.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane.h" #include "ui/views/examples/bubble_example.h" #include "ui/views/examples/button_example.h" #include "ui/views/examples/combobox_example.h" @@ -37,7 +38,6 @@ #include "ui/views/widget/widget.h" #include "views/controls/button/text_button.h" #include "views/controls/label.h" -#include "views/controls/tabbed_pane/tabbed_pane.h" #if defined(OS_WIN) // TableView is not yet ported to Linux. diff --git a/ui/views/examples/tabbed_pane_example.cc b/ui/views/examples/tabbed_pane_example.cc index 03a7e32..52338f5 100644 --- a/ui/views/examples/tabbed_pane_example.cc +++ b/ui/views/examples/tabbed_pane_example.cc @@ -5,8 +5,8 @@ #include "ui/views/examples/tabbed_pane_example.h" #include "base/utf_string_conversions.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane.h" #include "ui/views/layout/grid_layout.h" -#include "views/controls/tabbed_pane/tabbed_pane.h" namespace examples { diff --git a/ui/views/examples/tabbed_pane_example.h b/ui/views/examples/tabbed_pane_example.h index 1564ac6..d5192f2 100644 --- a/ui/views/examples/tabbed_pane_example.h +++ b/ui/views/examples/tabbed_pane_example.h @@ -8,9 +8,9 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h" #include "ui/views/examples/example_base.h" #include "views/controls/button/text_button.h" -#include "views/controls/tabbed_pane/tabbed_pane_listener.h" namespace views { class TabbedPane; diff --git a/ui/views/focus/focus_manager_unittest.cc b/ui/views/focus/focus_manager_unittest.cc index c55e4e0..af4ea62 100644 --- a/ui/views/focus/focus_manager_unittest.cc +++ b/ui/views/focus/focus_manager_unittest.cc @@ -13,8 +13,8 @@ #include "views/controls/textfield/textfield.h" #if !defined(USE_AURA) -#include "views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" -#include "views/controls/tabbed_pane/tabbed_pane.h" +#include "ui/views/controls/tabbed_pane/native_tabbed_pane_wrapper.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane.h" #endif #if defined(OS_LINUX) diff --git a/ui/views/focus/focus_traversal_unittest.cc b/ui/views/focus/focus_traversal_unittest.cc index e886a95..6eb4416 100644 --- a/ui/views/focus/focus_traversal_unittest.cc +++ b/ui/views/focus/focus_traversal_unittest.cc @@ -21,7 +21,7 @@ #include "views/controls/textfield/textfield.h" #if !defined(USE_AURA) -#include "views/controls/tabbed_pane/tabbed_pane.h" +#include "ui/views/controls/tabbed_pane/tabbed_pane.h" #endif namespace views { -- cgit v1.1