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
ash / hud_display / tab_strip.cc [blame]
// Copyright 2020 The Chromium Authors
// 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/functional/bind.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/text_constants.h"
#include "ui/views/border.h"
namespace ash {
namespace hud_display {
namespace {
// The width in pixels of overlaying adjacent tabs. Must be an even number.
constexpr int kHUDTabOverlayWidth = 2 * kHUDTabOverlayCornerRadius / 3;
// Border around tab text (the tab overlay width will be added to this).
constexpr int kHUDTabTitleBorder = 3;
} // namespace
BEGIN_METADATA(HUDTabButton)
END_METADATA
HUDTabButton::HUDTabButton(Style style,
const HUDDisplayMode display_mode,
const std::u16string& 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(gfx::Insets::VH(
kHUDSettingsIconBorder, kHUDTabOverlayWidth + kHUDTabTitleBorder)));
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 = kHUDTabOverlayWidth / 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((kHUDTabOverlayCornerRadius - kTabArcCrossedX + 1) /
(float)kHUDTabOverlayCornerRadius) /
M_PI;
constexpr SkScalar kCircleSize = kHUDTabOverlayCornerRadius * 2;
const SkScalar right_edge = width();
const SkScalar 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 - kHUDTabOverlayWidth / 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(kHUDTabOverlayWidth / 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)
END_METADATA
HUDTabStrip::HUDTabStrip(HUDDisplayView* hud) : hud_(hud) {
SetBetweenChildSpacing(-kHUDTabOverlayWidth);
SetInsideBorderInsets(gfx::Insets::TLBR(0, 0, 0, kHUDSettingsIconBorder));
}
HUDTabStrip::~HUDTabStrip() = default;
HUDTabButton* HUDTabStrip::AddTabButton(const HUDDisplayMode display_mode,
const std::u16string& 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 ash::hud_display::HUDTabButton* 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 HUDDisplayMode 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