1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
|
// 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 "chrome/browser/views/tabs/tab.h"
#include "app/gfx/canvas.h"
#include "app/gfx/font.h"
#include "app/gfx/path.h"
#include "app/l10n_util.h"
#include "app/menus/simple_menu_model.h"
#include "app/resource_bundle.h"
#include "base/compiler_specific.h"
#include "base/gfx/size.h"
#include "chrome/browser/tab_menu_model.h"
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/browser/views/tabs/tab_strip.h"
#include "grit/generated_resources.h"
#include "views/controls/menu/menu_2.h"
#include "views/widget/tooltip_manager.h"
#include "views/widget/widget.h"
const std::string Tab::kTabClassName = "browser/tabs/Tab";
static const SkScalar kTabCapWidth = 15;
static const SkScalar kTabTopCurveWidth = 4;
static const SkScalar kTabBottomCurveWidth = 3;
class Tab::TabContextMenuContents : public menus::SimpleMenuModel::Delegate {
public:
explicit TabContextMenuContents(Tab* tab)
: ALLOW_THIS_IN_INITIALIZER_LIST(model_(this)),
tab_(tab),
last_command_(TabStripModel::CommandFirst) {
Build();
}
virtual ~TabContextMenuContents() {
menu_->CancelMenu();
tab_->delegate()->StopAllHighlighting();
}
void RunMenuAt(const gfx::Point& point) {
// Save a pointer to delegate before we call RunMenuAt, because it runs a
// nested message loop that may not return until after we are deleted.
Tab::TabDelegate* delegate = tab_->delegate();
menu_->RunMenuAt(point, views::Menu2::ALIGN_TOPLEFT);
// We could be gone now. Assume |this| is junk!
if (delegate)
delegate->StopAllHighlighting();
}
// Overridden from menus::SimpleMenuModel::Delegate:
virtual bool IsCommandIdChecked(int command_id) const {
if (!tab_ || command_id != TabStripModel::CommandTogglePinned)
return false;
return tab_->delegate()->IsTabPinned(tab_);
}
virtual bool IsCommandIdEnabled(int command_id) const {
return tab_ && tab_->delegate()->IsCommandEnabledForTab(
static_cast<TabStripModel::ContextMenuCommand>(command_id),
tab_);
}
virtual bool GetAcceleratorForCommandId(
int command_id,
menus::Accelerator* accelerator) {
return tab_->GetWidget()->GetAccelerator(command_id, accelerator);
}
virtual void CommandIdHighlighted(int command_id) {
if (!tab_)
return;
tab_->delegate()->StopHighlightTabsForCommand(last_command_, tab_);
last_command_ = static_cast<TabStripModel::ContextMenuCommand>(command_id);
tab_->delegate()->StartHighlightTabsForCommand(last_command_, tab_);
}
virtual void ExecuteCommand(int command_id) {
if (!tab_)
return;
tab_->delegate()->ExecuteCommandForTab(
static_cast<TabStripModel::ContextMenuCommand>(command_id),
tab_);
}
private:
void Build() {
menu_.reset(new views::Menu2(&model_));
}
TabMenuModel model_;
scoped_ptr<views::Menu2> menu_;
// The Tab the context menu was brought up for. Set to NULL when the menu
// is canceled.
Tab* tab_;
// The last command that was selected, so that we can start/stop highlighting
// appropriately as the user moves through the menu.
TabStripModel::ContextMenuCommand last_command_;
DISALLOW_COPY_AND_ASSIGN(TabContextMenuContents);
};
///////////////////////////////////////////////////////////////////////////////
// Tab, public:
Tab::Tab(TabDelegate* delegate)
: TabRenderer(),
delegate_(delegate),
closing_(false) {
close_button()->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_CLOSE));
close_button()->SetAnimationDuration(0);
SetContextMenuController(this);
}
Tab::~Tab() {
}
///////////////////////////////////////////////////////////////////////////////
// Tab, TabRenderer overrides:
bool Tab::IsSelected() const {
return delegate_->IsTabSelected(this);
}
///////////////////////////////////////////////////////////////////////////////
// Tab, views::View overrides:
bool Tab::HasHitTestMask() const {
return true;
}
void Tab::GetHitTestMask(gfx::Path* mask) const {
MakePathForTab(mask);
}
bool Tab::OnMousePressed(const views::MouseEvent& event) {
if (event.IsOnlyLeftMouseButton()) {
// Store whether or not we were selected just now... we only want to be
// able to drag foreground tabs, so we don't start dragging the tab if
// it was in the background.
bool just_selected = !IsSelected();
if (just_selected) {
delegate_->SelectTab(this);
}
delegate_->MaybeStartDrag(this, event);
}
return true;
}
bool Tab::OnMouseDragged(const views::MouseEvent& event) {
delegate_->ContinueDrag(event);
return true;
}
void Tab::OnMouseReleased(const views::MouseEvent& event, bool canceled) {
// Notify the drag helper that we're done with any potential drag operations.
// Clean up the drag helper, which is re-created on the next mouse press.
// In some cases, ending the drag will schedule the tab for destruction; if
// so, bail immediately, since our members are already dead and we shouldn't
// do anything else except drop the tab where it is.
if (delegate_->EndDrag(canceled))
return;
// 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() && HitTest(event.location()))
delegate_->CloseTab(this);
}
bool Tab::GetTooltipText(int x, int y, std::wstring* tooltip) {
std::wstring title = GetTitle();
if (!title.empty()) {
// Only show the tooltip if the title is truncated.
gfx::Font font;
if (font.GetStringWidth(title) > title_bounds().width()) {
*tooltip = title;
return true;
}
}
return false;
}
bool Tab::GetTooltipTextOrigin(int x, int y, gfx::Point* origin) {
gfx::Font font;
origin->set_x(title_bounds().x() + 10);
origin->set_y(-views::TooltipManager::GetTooltipHeight() - 4);
return true;
}
bool Tab::GetAccessibleRole(AccessibilityTypes::Role* role) {
DCHECK(role);
*role = AccessibilityTypes::ROLE_PAGETAB;
return true;
}
bool Tab::GetAccessibleName(std::wstring* name) {
*name = GetTitle();
return !name->empty();
}
///////////////////////////////////////////////////////////////////////////////
// Tab, views::ContextMenuController implementation:
void Tab::ShowContextMenu(views::View* source, int x, int y,
bool is_mouse_gesture) {
if (!context_menu_contents_.get())
context_menu_contents_.reset(new TabContextMenuContents(this));
context_menu_contents_->RunMenuAt(gfx::Point(x, y));
}
///////////////////////////////////////////////////////////////////////////////
// views::ButtonListener implementation:
void Tab::ButtonPressed(views::Button* sender, const views::Event& event) {
if (sender == close_button())
delegate_->CloseTab(this);
}
///////////////////////////////////////////////////////////////////////////////
// Tab, private:
void Tab::MakePathForTab(gfx::Path* path) const {
DCHECK(path);
SkScalar h = SkIntToScalar(height());
SkScalar w = SkIntToScalar(width());
path->moveTo(0, h);
// Left end cap.
path->lineTo(kTabBottomCurveWidth, h - kTabBottomCurveWidth);
path->lineTo(kTabCapWidth - kTabTopCurveWidth, kTabTopCurveWidth);
path->lineTo(kTabCapWidth, 0);
// Connect to the right cap.
path->lineTo(w - kTabCapWidth, 0);
// Right end cap.
path->lineTo(w - kTabCapWidth + kTabTopCurveWidth, kTabTopCurveWidth);
path->lineTo(w - kTabBottomCurveWidth, h - kTabBottomCurveWidth);
path->lineTo(w, h);
// Close out the path.
path->lineTo(0, h);
path->close();
}
|