Commit 105ce465 authored by Brett Wilson's avatar Brett Wilson Committed by Commit Bot

Add basic grouing features.

Adds simple opener-based grouping to the experimental tab controller.

Clicks are not handled for activation. Tabs are rendered overlapped
properly.

Bug: 778461
Change-Id: Ic93cb109875d815018a6197e35b10b358959ac02
Reviewed-on: https://chromium-review.googlesource.com/777735Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: Brett Wilson <brettw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517913}
parent 597b2a11
...@@ -58,10 +58,11 @@ TabDataExperimental::~TabDataExperimental() = default; ...@@ -58,10 +58,11 @@ TabDataExperimental::~TabDataExperimental() = default;
TabDataExperimental& TabDataExperimental::operator=(TabDataExperimental&&) = TabDataExperimental& TabDataExperimental::operator=(TabDataExperimental&&) =
default; default;
const base::string16& TabDataExperimental::GetTitle() const { base::string16 TabDataExperimental::GetTitle() const {
DCHECK(type() == Type::kSingle);
// TODO(brettw) this will need to use TabUIHelper. // TODO(brettw) this will need to use TabUIHelper.
if (contents_)
return contents_->GetTitle(); return contents_->GetTitle();
return base::string16();
} }
bool TabDataExperimental::CountsAsViewIndex() const { bool TabDataExperimental::CountsAsViewIndex() const {
......
...@@ -49,12 +49,13 @@ class TabDataExperimental { ...@@ -49,12 +49,13 @@ class TabDataExperimental {
TabDataExperimental* parent() { return parent_; } TabDataExperimental* parent() { return parent_; }
Type type() const { return type_; } Type type() const { return type_; }
void set_type(Type t) { type_ = t; }
bool expanded() const { return expanded_; } bool expanded() const { return expanded_; }
content::WebContents* contents() { return contents_; } content::WebContents* contents() { return contents_; }
// Valid when type() == kSingle or kHubAndSpoke. base::string16 GetTitle() const;
const base::string16& GetTitle() const;
// Returns true if this tab data itself is counted as a enumerable item when // Returns true if this tab data itself is counted as a enumerable item when
// going through the view. // going through the view.
......
...@@ -379,12 +379,30 @@ void TabStripModelExperimental::InsertWebContentsAt( ...@@ -379,12 +379,30 @@ void TabStripModelExperimental::InsertWebContentsAt(
delegate_->WillAddWebContents(contents); delegate_->WillAddWebContents(contents);
bool active = (add_types & ADD_ACTIVE) != 0; bool active = (add_types & ADD_ACTIVE) != 0;
TabDataExperimental* data = nullptr;
if ((add_types & ADD_INHERIT_GROUP) && active_index() >= 0 &&
active_index() < count()) {
// Add as a child following opener.
TabDataExperimental* parent = GetDataForViewIndex(active_index());
if (parent->type() == TabDataExperimental::Type::kSingle) {
// Promote parent to hub-and-spoke.
parent->set_type(TabDataExperimental::Type::kHubAndSpoke);
for (auto& observer : exp_observers_)
observer.TabChanged(parent);
}
// Always insert tabs at the end for now (so parent is always null). parent->children_.push_back(std::make_unique<TabDataExperimental>(
TabDataExperimental* parent = nullptr;
tabs_.emplace_back(std::make_unique<TabDataExperimental>(
parent, TabDataExperimental::Type::kSingle, contents, this)); parent, TabDataExperimental::Type::kSingle, contents, this));
const TabDataExperimental* data = tabs_.back().get(); data = parent->children_.back().get();
} else {
// Add at toplevel.
tabs_.push_back(std::make_unique<TabDataExperimental>(
nullptr, TabDataExperimental::Type::kSingle, contents, this));
data = tabs_.back().get();
}
UpdateViewCount(); UpdateViewCount();
index = tab_view_count_ - 1; index = tab_view_count_ - 1;
...@@ -407,7 +425,6 @@ bool TabStripModelExperimental::CloseWebContentsAt(int view_index, ...@@ -407,7 +425,6 @@ bool TabStripModelExperimental::CloseWebContentsAt(int view_index,
uint32_t close_types) { uint32_t close_types) {
ViewIterator found = FindViewIndex(view_index); ViewIterator found = FindViewIndex(view_index);
DCHECK(found != end()); DCHECK(found != end());
DCHECK(found->type() == TabDataExperimental::Type::kSingle);
content::WebContents* closing = found->contents_; content::WebContents* closing = found->contents_;
if (closing) if (closing)
InternalCloseTabs(base::span<content::WebContents*>(&closing, 1)); InternalCloseTabs(base::span<content::WebContents*>(&closing, 1));
...@@ -527,8 +544,21 @@ TabDataExperimental* TabStripModelExperimental::GetDataForViewIndex( ...@@ -527,8 +544,21 @@ TabDataExperimental* TabStripModelExperimental::GetDataForViewIndex(
return &*found; return &*found;
} }
int TabStripModelExperimental::GetViewIndexForData(
const TabDataExperimental* data) const {
int view_index = 0;
for (const auto& cur : *this) {
if (&cur == data)
return view_index;
++view_index;
}
return kNoTab;
}
void TabStripModelExperimental::ActivateTabAt(int index, bool user_gesture) { void TabStripModelExperimental::ActivateTabAt(int index, bool user_gesture) {
DCHECK(ContainsIndex(index)); if (!ContainsIndex(index))
return;
ui::ListSelectionModel new_model = selection_model_; ui::ListSelectionModel new_model = selection_model_;
new_model.SetSelectedIndex(index); new_model.SetSelectedIndex(index);
SetSelection(std::move(new_model), SetSelection(std::move(new_model),
...@@ -649,7 +679,6 @@ bool TabStripModelExperimental::IsTabPinned(int index) const { ...@@ -649,7 +679,6 @@ bool TabStripModelExperimental::IsTabPinned(int index) const {
} }
bool TabStripModelExperimental::IsTabBlocked(int index) const { bool TabStripModelExperimental::IsTabBlocked(int index) const {
NOTIMPLEMENTED();
return false; return false;
} }
...@@ -689,6 +718,10 @@ void TabStripModelExperimental::AddWebContents(content::WebContents* contents, ...@@ -689,6 +718,10 @@ void TabStripModelExperimental::AddWebContents(content::WebContents* contents,
int index, int index,
ui::PageTransition transition, ui::PageTransition transition,
int add_types) { int add_types) {
// Force group inheritance for link click transitions.
if (ui::PageTransitionTypeIncludingQualifiersIs(transition,
ui::PAGE_TRANSITION_LINK))
add_types |= ADD_INHERIT_GROUP;
InsertWebContentsAt(index, contents, add_types); InsertWebContentsAt(index, contents, add_types);
} }
...@@ -769,6 +802,7 @@ void TabStripModelExperimental::DetachWebContents( ...@@ -769,6 +802,7 @@ void TabStripModelExperimental::DetachWebContents(
NOTREACHED(); // WebContents not found in this model. NOTREACHED(); // WebContents not found in this model.
return; return;
} }
TabDataExperimental* data = &*found;
bool was_selected; bool was_selected;
if (view_index == kNoTab) if (view_index == kNoTab)
...@@ -777,12 +811,31 @@ void TabStripModelExperimental::DetachWebContents( ...@@ -777,12 +811,31 @@ void TabStripModelExperimental::DetachWebContents(
was_selected = IsTabSelected(view_index); was_selected = IsTabSelected(view_index);
int next_selected_index = view_index; int next_selected_index = view_index;
if (found->parent_) { if (data->parent_) {
// Erase in parent. TabDataExperimental* parent = data->parent_;
found->parent_->children_.erase(found->parent_->children_.begin() + // Erase out of the parent.
found.inner_index_); parent->children_.erase(parent->children_.begin() + found.inner_index_);
// TODO(brettw) remove the parent if it's empty! if (parent->children_.empty()) {
if (parent->type() == TabDataExperimental::Type::kHubAndSpoke) {
// Erasing the last child of a hub and spoke one converts it back to
// a single.
parent->set_type(TabDataExperimental::Type::kSingle);
for (auto& observer : exp_observers_)
observer.TabChanged(parent);
} else {
DCHECK(parent->type() == TabDataExperimental::Type::kGroup);
// TODO(brettw) remove group. Notifications might be tricky.
}
}
} else if (data->type() == TabDataExperimental::Type::kHubAndSpoke) {
// Removing the "hub" from a hub and spoke converts to a group.
data->set_type(TabDataExperimental::Type::kGroup);
data->contents_ =
nullptr; // TODO(brettw) does this delete things properly?
for (auto& observer : exp_observers_)
observer.TabChanged(data);
} else { } else {
// Just remove from tabs. // Just remove from tabs.
tabs_.erase(tabs_.begin() + found.toplevel_index_); tabs_.erase(tabs_.begin() + found.toplevel_index_);
......
...@@ -196,6 +196,9 @@ class TabStripModelExperimental : public TabStripModel { ...@@ -196,6 +196,9 @@ class TabStripModelExperimental : public TabStripModel {
const TabDataExperimental* GetDataForViewIndex(int view_index) const; const TabDataExperimental* GetDataForViewIndex(int view_index) const;
TabDataExperimental* GetDataForViewIndex(int view_index); TabDataExperimental* GetDataForViewIndex(int view_index);
// Returns kNoTab if there isn't a view index for this data.
int GetViewIndexForData(const TabDataExperimental* data) const;
void AddExperimentalObserver(TabStripModelExperimentalObserver* observer); void AddExperimentalObserver(TabStripModelExperimentalObserver* observer);
void RemoveExperimentalObserver(TabStripModelExperimentalObserver* observer); void RemoveExperimentalObserver(TabStripModelExperimentalObserver* observer);
......
...@@ -264,8 +264,7 @@ class Tab::TabCloseButton : public views::ImageButton, ...@@ -264,8 +264,7 @@ class Tab::TabCloseButton : public views::ImageButton,
explicit TabCloseButton(Tab* tab) explicit TabCloseButton(Tab* tab)
: views::ImageButton(tab), : views::ImageButton(tab),
tab_(tab) { tab_(tab) {
SetEventTargeter( SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
} }
~TabCloseButton() override {} ~TabCloseButton() override {}
...@@ -461,8 +460,7 @@ Tab::Tab(TabController* controller, gfx::AnimationContainer* container) ...@@ -461,8 +460,7 @@ Tab::Tab(TabController* controller, gfx::AnimationContainer* container)
title_->SetText(CoreTabHelper::GetDefaultTitle()); title_->SetText(CoreTabHelper::GetDefaultTitle());
AddChildView(title_); AddChildView(title_);
SetEventTargeter( SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
throbber_ = new ThrobberView(this); throbber_ = new ThrobberView(this);
throbber_->SetVisible(false); throbber_->SetVisible(false);
......
...@@ -6,11 +6,13 @@ ...@@ -6,11 +6,13 @@
#include "chrome/browser/ui/layout_constants.h" #include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/tab_contents/core_tab_helper.h" #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
#include "chrome/browser/ui/tabs/tab_data_experimental.h" #include "chrome/browser/ui/tabs/tab_strip_model_experimental.h"
#include "chrome/browser/ui/view_ids.h" #include "chrome/browser/ui/view_ids.h"
#include "components/grit/components_scaled_resources.h" #include "components/grit/components_scaled_resources.h"
#include "ui/views/border.h" #include "ui/views/border.h"
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
#include "ui/views/masked_targeter_delegate.h"
#include "ui/views/widget/widget.h"
namespace { namespace {
...@@ -27,9 +29,12 @@ float GetTabEndcapWidth() { ...@@ -27,9 +29,12 @@ float GetTabEndcapWidth() {
} // namespace } // namespace
TabExperimental::TabExperimental(const TabDataExperimental* data) TabExperimental::TabExperimental(TabStripModelExperimental* model,
const TabDataExperimental* data)
: views::View(), : views::View(),
model_(model),
data_(data), data_(data),
type_(data->type()),
title_(new views::Label), title_(new views::Label),
hover_controller_(this), hover_controller_(this),
paint_(this) { paint_(this) {
...@@ -40,6 +45,8 @@ TabExperimental::TabExperimental(const TabDataExperimental* data) ...@@ -40,6 +45,8 @@ TabExperimental::TabExperimental(const TabDataExperimental* data)
title_->SetText(CoreTabHelper::GetDefaultTitle()); title_->SetText(CoreTabHelper::GetDefaultTitle());
AddChildView(title_); AddChildView(title_);
SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
// So we get don't get enter/exit on children and don't prematurely stop the // So we get don't get enter/exit on children and don't prematurely stop the
// hover. // hover.
set_notify_enter_exit_on_child(true); set_notify_enter_exit_on_child(true);
...@@ -73,23 +80,84 @@ void TabExperimental::SetSelected(bool selected) { ...@@ -73,23 +80,84 @@ void TabExperimental::SetSelected(bool selected) {
} }
void TabExperimental::DataUpdated() { void TabExperimental::DataUpdated() {
type_ = data_->type();
title_->SetText(data_->GetTitle()); title_->SetText(data_->GetTitle());
} }
void TabExperimental::SetGroupLayoutParams(int first_child_begin_x) {
first_child_begin_x_ = first_child_begin_x;
}
int TabExperimental::GetOverlap() { int TabExperimental::GetOverlap() {
// We want to overlap the endcap portions entirely. // We want to overlap the endcap portions entirely.
return gfx::ToCeiledInt(GetTabEndcapWidth()); return gfx::ToCeiledInt(GetTabEndcapWidth());
} }
bool TabExperimental::GetHitTestMask(gfx::Path* mask) const {
// When the window is maximized we don't want to shave off the edges or top
// shadow of the tab, such that the user can click anywhere along the top
// edge of the screen to select a tab. Ditto for immersive fullscreen.
const views::Widget* widget = GetWidget();
*mask = paint_.GetBorderPath(
GetWidget()->GetCompositor()->device_scale_factor(), true,
widget && (widget->IsMaximized() || widget->IsFullscreen()),
GetTabEndcapWidth(), size());
return true;
}
void TabExperimental::OnPaint(gfx::Canvas* canvas) { void TabExperimental::OnPaint(gfx::Canvas* canvas) {
if (type_ == TabDataExperimental::Type::kSingle)
paint_.PaintTabBackground(canvas, active_, 0, 0, nullptr); paint_.PaintTabBackground(canvas, active_, 0, 0, nullptr);
else
paint_.PaintGroupBackground(canvas, active_);
} }
void TabExperimental::Layout() { void TabExperimental::Layout() {
// Space between the favicon and title.
constexpr int kTitleSpacing = 6; constexpr int kTitleSpacing = 6;
const gfx::Rect bounds = GetContentsBounds(); const gfx::Rect bounds = GetContentsBounds();
title_->SetBoundsRect(gfx::Rect(bounds.x() + kTitleSpacing, bounds.y(), int title_left = bounds.x() + kTitleSpacing;
bounds.width() - (kTitleSpacing * 2), int title_right;
bounds.height())); if (first_child_begin_x_ >= 0)
title_right = first_child_begin_x_;
else
title_right = bounds.width() - kTitleSpacing;
title_->SetBoundsRect(gfx::Rect(title_left, bounds.y(),
title_right - title_left, bounds.height()));
}
bool TabExperimental::OnMousePressed(const ui::MouseEvent& event) {
// TODO(brettw) the non-experimental one has some stuff about touch and
// multi-selection here.
if (event.IsOnlyLeftMouseButton())
model_->ActivateTabAt(model_->GetViewIndexForData(data_), true);
return true;
}
void TabExperimental::OnMouseReleased(const ui::MouseEvent& event) {
// Close tab on middle click, but only if the button is released over the tab
// (normal windows behavior is to discard presses of a UI element where the
// releases happen off the element).
if (event.IsMiddleMouseButton()) {
if (HitTestPoint(event.location())) {
// TODO(brettw) old one did PrepareForCloseAt which does some animation
// stuff.
model_->CloseWebContentsAt(
model_->GetViewIndexForData(data_),
TabStripModel::CLOSE_USER_GESTURE |
TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
} else if (closing_) {
// We're animating closed and a middle mouse button was pushed on us but
// we don't contain the mouse anymore. We assume the user is clicking
// quicker than the animation and we should close the tab that falls under
// the mouse.
/* TODO(brettw) fast closing.
Tab* closest_tab = controller_->GetTabAt(this, event.location());
if (closest_tab)
controller_->CloseTab(closest_tab, CLOSE_TAB_FROM_MOUSE);
*/
}
}
} }
...@@ -6,24 +6,33 @@ ...@@ -6,24 +6,33 @@
#define CHROME_BROWSER_UI_VIEWS_TABS_TAB_EXPERIMENTAL_H_ #define CHROME_BROWSER_UI_VIEWS_TABS_TAB_EXPERIMENTAL_H_
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/ui/tabs/tab_data_experimental.h"
#include "chrome/browser/ui/views/tabs/tab_experimental_paint.h" #include "chrome/browser/ui/views/tabs/tab_experimental_paint.h"
#include "ui/views/controls/glow_hover_controller.h" #include "ui/views/controls/glow_hover_controller.h"
#include "ui/views/masked_targeter_delegate.h"
#include "ui/views/view.h" #include "ui/views/view.h"
class TabDataExperimental; class TabDataExperimental;
class TabStripModelExperimental;
namespace views { namespace views {
class Label; class Label;
} }
class TabExperimental : public views::View { class TabExperimental : public views::MaskedTargeterDelegate,
public views::View {
public: public:
explicit TabExperimental(const TabDataExperimental* data); explicit TabExperimental(TabStripModelExperimental* model,
const TabDataExperimental* data);
~TabExperimental() override; ~TabExperimental() override;
// Will be null when closing. // Will be null when closing.
const TabDataExperimental* data() const { return data_; } const TabDataExperimental* data() const { return data_; }
// Cached stuff from data_ that can be used regardless of whether the data
// pointer is null or not.
TabDataExperimental::Type type() const { return type_; }
// The view order is stored on a tab for the use of TabStrip layout and // The view order is stored on a tab for the use of TabStrip layout and
// computatations. It is not used directly by this class. It represents this // computatations. It is not used directly by this class. It represents this
// tab's position in the current layout. // tab's position in the current layout.
...@@ -52,17 +61,30 @@ class TabExperimental : public views::View { ...@@ -52,17 +61,30 @@ class TabExperimental : public views::View {
// to redraw everything. // to redraw everything.
void DataUpdated(); void DataUpdated();
// Called for group types when layout is done to set the bounds of the
// first tab. This is used to determine some painting parameters.
void SetGroupLayoutParams(int first_child_begin_x);
// Returns the overlap between adjacent tabs. // Returns the overlap between adjacent tabs.
static int GetOverlap(); static int GetOverlap();
private: private:
// views::MaskedTargeterDelegate:
bool GetHitTestMask(gfx::Path* mask) const override;
// views::View: // views::View:
void OnPaint(gfx::Canvas* canvas) override; void OnPaint(gfx::Canvas* canvas) override;
void Layout() override; void Layout() override;
bool OnMousePressed(const ui::MouseEvent& event) override;
void OnMouseReleased(const ui::MouseEvent& event) override;
TabStripModelExperimental* model_;
// Will be null when closing. // Will be null when closing.
const TabDataExperimental* data_; const TabDataExperimental* data_;
TabDataExperimental::Type type_;
size_t view_order_ = static_cast<size_t>(-1); size_t view_order_ = static_cast<size_t>(-1);
gfx::Rect ideal_bounds_; gfx::Rect ideal_bounds_;
...@@ -72,6 +94,9 @@ class TabExperimental : public views::View { ...@@ -72,6 +94,9 @@ class TabExperimental : public views::View {
views::Label* title_; // Non-owning (owned by View hierarchy). views::Label* title_; // Non-owning (owned by View hierarchy).
// Location of the first child tab. Negative indicates unused.
int first_child_begin_x_ = -1;
views::GlowHoverController hover_controller_; views::GlowHoverController hover_controller_;
TabExperimentalPaint paint_; TabExperimentalPaint paint_;
......
...@@ -26,12 +26,26 @@ class TabExperimentalPaint { ...@@ -26,12 +26,26 @@ class TabExperimentalPaint {
explicit TabExperimentalPaint(views::View* view); explicit TabExperimentalPaint(views::View* view);
~TabExperimentalPaint(); ~TabExperimentalPaint();
// Returns a path corresponding to the tab's outer border for a given tab
// |size|, |scale|, and |endcap_width|. If |unscale_at_end| is true, this
// path will be normalized to a 1x scale by scaling by 1/scale before
// returning. If |extend_to_top| is true, the path is extended vertically to
// the top of the tab bounds. The caller uses this for Fitts' Law purposes
// in maximized/fullscreen mode.
gfx::Path GetBorderPath(float scale,
bool unscale_at_end,
bool extend_to_top,
float endcap_width,
const gfx::Size& size) const;
void PaintTabBackground(gfx::Canvas* canvas, void PaintTabBackground(gfx::Canvas* canvas,
bool active, bool active,
int fill_id, int fill_id,
int y_offset, int y_offset,
gfx::Path* clip); gfx::Path* clip);
void PaintGroupBackground(gfx::Canvas* canvas, bool active);
private: private:
class BackgroundCache { class BackgroundCache {
public: public:
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
#include "base/containers/flat_set.h" #include "base/containers/flat_set.h"
#include "base/containers/span.h"
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
...@@ -423,6 +424,12 @@ class TabStripExperimental : public TabStrip, ...@@ -423,6 +424,12 @@ class TabStripExperimental : public TabStrip,
// Ensures that view_order_ corresponds to the latest tabs_ structure. // Ensures that view_order_ corresponds to the latest tabs_ structure.
void EnsureViewOrderUpToDate() const; void EnsureViewOrderUpToDate() const;
// Recursive backend for EnsureViewOrderUpToDate.
void ComputeViewOrder(
base::span<const std::unique_ptr<TabDataExperimental>> tabs,
const TabDataExperimental* selected,
std::vector<TabExperimental*>* output) const;
// -- Member Variables ------------------------------------------------------ // -- Member Variables ------------------------------------------------------
TabStripModelExperimental* model_; TabStripModelExperimental* model_;
...@@ -437,8 +444,9 @@ class TabStripExperimental : public TabStrip, ...@@ -437,8 +444,9 @@ class TabStripExperimental : public TabStrip,
// this will cause them to be painted in order of their memory address! // this will cause them to be painted in order of their memory address!
base::flat_set<TabExperimental*> closing_tabs_; base::flat_set<TabExperimental*> closing_tabs_;
// Cached ordered vector for painting the tabs. This will include everything // Cached ordered vector for painting the tabs. The topmost view is at the
// in the tabs_ map and also the items in closing_tabs_. // front(). This will include everything in the tabs_ map and also the items
// in closing_tabs_.
// //
// This is computed by EnsureViewOrderUpToDate(). // This is computed by EnsureViewOrderUpToDate().
mutable std::vector<TabExperimental*> view_order_; mutable std::vector<TabExperimental*> view_order_;
......
...@@ -1934,10 +1934,10 @@ void TabStripImpl::SetTabBoundsForDrag( ...@@ -1934,10 +1934,10 @@ void TabStripImpl::SetTabBoundsForDrag(
void TabStripImpl::AddMessageLoopObserver() { void TabStripImpl::AddMessageLoopObserver() {
if (!mouse_watcher_.get()) { if (!mouse_watcher_.get()) {
mouse_watcher_.reset(new views::MouseWatcher( mouse_watcher_ = std::make_unique<views::MouseWatcher>(
new views::MouseWatcherViewHost( new views::MouseWatcherViewHost(
this, gfx::Insets(0, 0, kTabStripAnimationVSlop, 0)), this, gfx::Insets(0, 0, kTabStripAnimationVSlop, 0)),
this)); this);
} }
mouse_watcher_->Start(); mouse_watcher_->Start();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment