forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tab_strip.cc
216 lines (184 loc) · 7.17 KB
/
tab_strip.cc
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
// Copyright 2020 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 "ash/hud_display/tab_strip.h"
#include <cmath>
#include "ash/hud_display/hud_display.h"
#include "ash/hud_display/hud_properties.h"
#include "base/bind.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/text_constants.h"
#include "ui/views/layout/layout_manager.h"
#include "ui/views/metadata/metadata_impl_macros.h"
namespace ash {
namespace hud_display {
namespace {
// The width in pixels of overlaying adjacent tabs. Must be even number.
constexpr int kTabOverlayWidth = 2 * kTabOverlayCornerRadius / 3;
// Border around tab text (tab overlay width will be added to this).
constexpr int kTabTitleBorder = 3;
class HUDTabStripLayout : public views::LayoutManager {
public:
HUDTabStripLayout() = default;
HUDTabStripLayout(const HUDTabStripLayout&) = delete;
HUDTabStripLayout& operator=(const HUDTabStripLayout&) = delete;
~HUDTabStripLayout() override = default;
// views::LayoutManager:
void Layout(views::View* host) override;
gfx::Size GetPreferredSize(const views::View* host) const override;
};
gfx::Size HUDTabStripLayout::GetPreferredSize(const views::View* host) const {
gfx::Size result;
for (const auto* child : host->children()) {
const gfx::Size child_preferred = child->GetPreferredSize();
// Tab strip is always horizontal.
result.set_width(result.width() + child_preferred.width() -
kTabOverlayWidth);
result.set_height(std::max(result.height(), child_preferred.height()));
}
// Assume all children have equal left and right border, which is used to
// overlay the tabs. Add one overlay width to compensate one edge.
if (host->children().size())
result.set_width(result.width() + kTabOverlayWidth);
// Add right padding equal to the padding of the settings icon.
result.set_width(result.width() + kSettingsIconBorder);
return result;
}
void HUDTabStripLayout::Layout(views::View* host) {
// Assume all children have equal left and right border, which is used to
// overlay the tabs.
int left_offset = 0;
for (auto* child : host->children()) {
const gfx::Size preferred = child->GetPreferredSize();
const gfx::Size child_size({preferred.width(), host->height()});
child->SetSize(child_size);
child->SetPosition({left_offset, 0});
left_offset += child_size.width() - kTabOverlayWidth;
}
}
} // namespace
BEGIN_METADATA(HUDTabButton, views::LabelButton)
END_METADATA
HUDTabButton::HUDTabButton(Style style,
const DisplayMode display_mode,
const base::string16& text)
: views::LabelButton(views::Button::PressedCallback(), text),
style_(style),
display_mode_(display_mode) {
SetHorizontalAlignment(gfx::ALIGN_CENTER);
SetEnabledTextColors(kHUDDefaultColor);
SetProperty(kHUDClickHandler, HTCLIENT);
SetBorder(views::CreateEmptyBorder(
kSettingsIconBorder, kTabOverlayWidth + kTabTitleBorder,
kSettingsIconBorder, kTabOverlayWidth + kTabTitleBorder));
SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
}
void HUDTabButton::SetStyle(Style style) {
if (style_ == style)
return;
style_ = style;
SchedulePaint();
}
void HUDTabButton::PaintButtonContents(gfx::Canvas* canvas) {
// Horizontal offset from tab {0,0} where two tab arcs cross.
constexpr int kTabArcCrossedX = kTabOverlayWidth / 2;
// Reduce kTabArcCrossedX by one pixel when calculating partial arc so that
// the pixels along kTabArcCrossedX vertical line are drawn by full arc only.
static const float kTabPartialArcAngle =
90 - 180 *
asinf((kTabOverlayCornerRadius - kTabArcCrossedX + 1) /
(float)kTabOverlayCornerRadius) /
M_PI;
const int kCircleSize = kTabOverlayCornerRadius * 2;
const int right_edge = width();
const int bottom_edge = height();
SkPath path;
// Draw left vertical line and arc
if (style_ == Style::RIGHT) {
/* |true| - move to the start of the arc */
path.arcTo({0, 0, kCircleSize, kCircleSize}, -90 - kTabPartialArcAngle,
kTabPartialArcAngle, true);
} else {
if (style_ == Style::LEFT) {
// Draw bottom line from the right edge. Adjust for 2 pixels crossing the
// right vertical line.
path.moveTo(right_edge - kTabOverlayWidth / 2 - 2, bottom_edge);
path.lineTo(0, bottom_edge);
} else {
// No bottom line. Just move to the start of the vertical line.
path.moveTo(0, bottom_edge);
}
/* |false| will draw straight line to the start of the arc */
path.arcTo({0, 0, kCircleSize - 1, kCircleSize}, -180, 90, false);
}
// Draw top line, right arc and right vertical line
if (style_ == Style::LEFT) {
/* |false| will draw straight line to the start of the arc */
path.arcTo({right_edge - kCircleSize, 0, right_edge, kCircleSize}, -90,
kTabPartialArcAngle, false);
} else {
/* |false| will draw straight line to the start of the arc */
path.arcTo({right_edge - kCircleSize, 0, right_edge, kCircleSize}, -90, 90,
false);
path.lineTo(right_edge, bottom_edge);
if (style_ == Style::RIGHT) {
// Draw bottom line to the left edge. Adjust for 2 pixels crossing the
// left vertical line.
path.lineTo(kTabOverlayWidth / 2 + 2, bottom_edge);
}
}
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setBlendMode(SkBlendMode::kSrc);
flags.setStyle(cc::PaintFlags::kStroke_Style);
flags.setStrokeWidth(1);
flags.setColor(kHUDDefaultColor);
canvas->DrawPath(path, flags);
}
BEGIN_METADATA(HUDTabStrip, views::View)
END_METADATA
HUDTabStrip::HUDTabStrip(HUDDisplayView* hud) : hud_(hud) {
SetLayoutManager(std::make_unique<HUDTabStripLayout>());
}
HUDTabStrip::~HUDTabStrip() = default;
HUDTabButton* HUDTabStrip::AddTabButton(const DisplayMode display_mode,
const base::string16& label) {
CHECK_NE(static_cast<int>(display_mode), 0);
// Make first tab active by default.
HUDTabButton* tab_button = AddChildView(std::make_unique<HUDTabButton>(
tabs_.size() ? HUDTabButton::Style::RIGHT : HUDTabButton::Style::ACTIVE,
display_mode, label));
tab_button->SetCallback(base::BindRepeating(
[](HUDTabButton* sender, HUDTabStrip* tab_strip) {
for (const auto* tab : tab_strip->tabs_) {
if (tab == sender) {
tab_strip->hud_->SetDisplayMode(tab->display_mode());
return;
}
}
NOTREACHED();
},
base::Unretained(tab_button), base::Unretained(this)));
tabs_.push_back(tab_button);
return tab_button;
}
void HUDTabStrip::ActivateTab(const DisplayMode mode) {
// True if we find given active tab.
bool found = false;
for (HUDTabButton* tab : tabs_) {
if (found) {
tab->SetStyle(HUDTabButton::Style::RIGHT);
continue;
}
if (tab->display_mode() == mode) {
found = true;
tab->SetStyle(HUDTabButton::Style::ACTIVE);
continue;
}
tab->SetStyle(HUDTabButton::Style::LEFT);
}
DCHECK(found);
}
} // namespace hud_display
} // namespace ash