Chromium Code Reviews| Index: ui/views/controls/tabbed_pane/native_tabbed_pane_views.cc |
| =================================================================== |
| --- ui/views/controls/tabbed_pane/native_tabbed_pane_views.cc (revision 0) |
| +++ ui/views/controls/tabbed_pane/native_tabbed_pane_views.cc (revision 0) |
| @@ -0,0 +1,372 @@ |
| +// 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_views.h" |
| + |
| +#include "base/logging.h" |
| +#include "base/stl_util.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| +#include "ui/gfx/canvas_skia.h" |
| +#include "ui/gfx/font.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/widget.h" |
| + |
| +namespace views { |
| + |
| +class TabStrip; |
| + |
| +class Tab : public View { |
| + public: |
| + Tab(TabStrip* tab_strip, const string16& title) |
| + : tab_strip_(tab_strip), |
| + title_(title) {} |
| + virtual ~Tab() {} |
| + |
| + static int GetMinimumTabHeight() { |
| + return gfx::Font().GetHeight() + 10; |
| + } |
| + |
| + // Overridden from View: |
| + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { |
| + canvas->FillRect(GetTabColor(), GetLocalBounds()); |
| + canvas->DrawStringInt(title_, gfx::Font(), SK_ColorBLACK, GetLocalBounds()); |
| + } |
| + virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE; |
| + virtual gfx::Size GetPreferredSize() OVERRIDE { |
| + gfx::Size ps(gfx::Font().GetStringWidth(title_), GetMinimumTabHeight()); |
| + ps.Enlarge(10, 0); |
| + return ps; |
| + } |
| + |
| + private: |
| + SkColor GetTabColor() const; |
| + |
| + TabStrip* tab_strip_; |
| + string16 title_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Tab); |
| +}; |
| + |
| +class TabStrip : public View { |
| + public: |
| + explicit TabStrip(NativeTabbedPaneViews* owner) |
| + : owner_(owner), |
| + selected_tab_(NULL) { |
| + } |
| + virtual ~TabStrip() {} |
| + |
| + void SelectTab(View* tab) { |
| + if (tab == selected_tab_) |
| + return; |
| + if (selected_tab_) |
| + selected_tab_->SchedulePaint(); |
| + selected_tab_ = tab; |
| + selected_tab_->SchedulePaint(); |
| + owner_->TabSelectionChanged(GetIndexOf(tab)); |
| + } |
| + |
| + bool IsTabSelected(const View* tab) const { |
| + return tab == selected_tab_; |
| + } |
| + |
| + int GetSelectedIndex() const { |
| + return GetIndexOf(selected_tab_); |
| + } |
| + |
| + void RemoveTabAt(int index) { |
| + delete child_at(index); |
| + } |
| + |
| + // Overridden from View: |
| + virtual gfx::Size GetPreferredSize() OVERRIDE { |
| + return gfx::Size(50, Tab::GetMinimumTabHeight()); |
| + } |
| + virtual void Layout() OVERRIDE { |
| + int x = 0; |
| + for (int i = 0; i < child_count(); ++i) { |
| + gfx::Size ps = child_at(i)->GetPreferredSize(); |
| + child_at(i)->SetBounds(x, 0, ps.width(), ps.height()); |
| + x = child_at(i)->bounds().right(); |
| + } |
| + } |
| + |
| + private: |
| + NativeTabbedPaneViews* owner_; |
| + View* selected_tab_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TabStrip); |
| +}; |
| + |
| +bool Tab::OnMousePressed(const MouseEvent& event) { |
| + tab_strip_->SelectTab(this); |
| + return true; |
| +} |
| + |
| +SkColor Tab::GetTabColor() const { |
| + return tab_strip_->IsTabSelected(this) ? SK_ColorRED : SK_ColorBLUE; |
| +} |
| + |
| +// 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. |
|
sky
2011/11/28 23:46:22
update comment (no 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) { |
|
sky
2011/11/28 23:46:22
OVERRIDE
|
| + 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); |
| +}; |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// NativeTabbedPaneViews, public: |
| + |
| +NativeTabbedPaneViews::NativeTabbedPaneViews(TabbedPane* tabbed_pane) |
| + : tabbed_pane_(tabbed_pane), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(tab_strip_(new TabStrip(this))), |
|
sky
2011/11/28 23:46:22
make order match header.
|
| + tab_layout_manager_(new TabLayout), |
| + content_window_(NULL), |
| + selected_index_(-1) { |
| + AddChildView(tab_strip_); |
| +} |
| + |
| +NativeTabbedPaneViews::~NativeTabbedPaneViews() { |
| + // We own the tab views, let's delete them. |
| + STLDeleteContainerPointers(tab_views_.begin(), tab_views_.end()); |
| +} |
| + |
| +void NativeTabbedPaneViews::TabSelectionChanged(int index) { |
| + DoSelectTabAt(index, true); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// NativeTabbedPaneViews, NativeTabbedPaneWrapper implementation: |
| + |
| +void NativeTabbedPaneViews::AddTab(const string16& title, View* contents) { |
| + AddTabAtIndex(static_cast<int>(tab_views_.size()), title, contents, true); |
| +} |
| + |
| +void NativeTabbedPaneViews::AddTabAtIndex(int index, |
| + const string16& title, |
| + View* contents, |
| + bool select_if_first_tab) { |
| + DCHECK(index <= static_cast<int>(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 (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); |
| + |
| + 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 NativeTabbedPaneViews::AddNativeTab(int index, const string16& title) { |
| + tab_strip_->AddChildViewAt(new Tab(tab_strip_, title), index); |
| +} |
| + |
| +View* NativeTabbedPaneViews::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 if (content_window_) { |
| + // That was the last tab. Remove the contents. |
| + content_window_->GetRootView()->RemoveAllChildViews(false); |
| + } |
| + } |
| + tab_strip_->RemoveTabAt(index); |
| + |
| + std::vector<View*>::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 NativeTabbedPaneViews::SelectTabAt(int index) { |
| + DCHECK((index >= 0) && (index < static_cast<int>(tab_views_.size()))); |
| + tab_strip_->SelectTab(tab_strip_->child_at(index)); |
| +} |
| + |
| +int NativeTabbedPaneViews::GetTabCount() { |
| + return tab_strip_->child_count(); |
| +} |
| + |
| +int NativeTabbedPaneViews::GetSelectedTabIndex() { |
| + return tab_strip_->GetSelectedIndex(); |
| +} |
| + |
| +View* NativeTabbedPaneViews::GetSelectedTab() { |
| + if (selected_index_ < 0) |
| + return NULL; |
| + return tab_views_[selected_index_]; |
| +} |
| + |
| +View* NativeTabbedPaneViews::GetView() { |
| + return this; |
| +} |
| + |
| +void NativeTabbedPaneViews::SetFocus() { |
| + // Focus the associated HWND. |
| + OnFocus(); |
| +} |
| + |
| +gfx::Size NativeTabbedPaneViews::GetPreferredSize() { |
| + return gfx::Size(0, tab_strip_->GetPreferredSize().height()); |
| +} |
| + |
| +gfx::NativeView NativeTabbedPaneViews::GetTestingHandle() const { |
| + return NULL; |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// NativeTabbedPaneViews, View overrides: |
| + |
| +void NativeTabbedPaneViews::Layout() { |
| + gfx::Size ps = tab_strip_->GetPreferredSize(); |
| + tab_strip_->SetBounds(0, 0, width(), ps.height()); |
| + |
| + gfx::Point child_origin(0, tab_strip_->bounds().bottom()); |
| + View::ConvertPointToWidget(this, &child_origin); |
| + |
| + if (content_window_) { |
| + content_window_->SetBounds(gfx::Rect(child_origin.x(), |
| + child_origin.y(), |
| + width(), |
| + std::max(0, height() - ps.height()))); |
| + } |
| +} |
| + |
| +FocusTraversable* NativeTabbedPaneViews::GetFocusTraversable() { |
| + return content_window_; |
| +} |
| + |
| +void NativeTabbedPaneViews::ViewHierarchyChanged(bool is_add, |
| + View *parent, |
| + View *child) { |
| + if (is_add && child == this) |
| + InitControl(); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// NativeTabbedPaneViews, private: |
| + |
| +void NativeTabbedPaneViews::InitControl() { |
| + content_window_ = new Widget; |
| + Widget::InitParams params(Widget::InitParams::TYPE_CONTROL); |
| + params.parent = GetWidget()->GetNativeView(); |
| + content_window_->Init(params); |
| + content_window_->GetRootView()->SetLayoutManager(tab_layout_manager_); |
| + content_window_->GetRootView()->set_background( |
| + Background::CreateSolidBackground(SK_ColorLTGRAY)); |
| + content_window_->SetFocusTraversableParentView(this); |
| + content_window_->SetFocusTraversableParent( |
| + GetWidget()->GetFocusTraversable()); |
| + |
| + // Add tabs that are already added if any. |
| + if (!tab_views_.empty()) { |
| + InitializeTabs(); |
| + if (selected_index_ >= 0) |
| + DoSelectTabAt(selected_index_, false); |
| + } |
| +} |
| + |
| +void NativeTabbedPaneViews::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<View*>::iterator tab(tab_views_.begin()); |
| + tab != tab_views_.end(); ++tab) |
| + content_root->AddChildView(*tab); |
| +} |
| + |
| +void NativeTabbedPaneViews::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); |
| +} |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| +// NativeTabbedPaneWrapper, public: |
| + |
| +// static |
| +NativeTabbedPaneWrapper* NativeTabbedPaneWrapper::CreateNativeWrapper( |
| + TabbedPane* tabbed_pane) { |
| + return new NativeTabbedPaneViews(tabbed_pane); |
| +} |
| + |
| +} // namespace views |
| Property changes on: ui\views\controls\tabbed_pane\native_tabbed_pane_views.cc |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |