Commit fccdfe27 authored by Ahmed Fakhry's avatar Ahmed Fakhry Committed by Commit Bot

Touchable Chrome: New Tab Button

This CL implements the touch-optimized design of the New Tab
Button in both incognito and normal modes. Button is flat
with no highlight, and has an ink drop ripple effect.

Demo is posted on the bug.

BUG=805245
TEST=manually

Change-Id: I33a2e5e213193318b70e1c01ee330a67d6af21f0
Reviewed-on: https://chromium-review.googlesource.com/903375
Commit-Queue: Ahmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarEvan Stade <estade@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#537851}
parent 645fc981
......@@ -66,6 +66,8 @@ aggregate_vector_icons("chrome_vector_icons") {
"navigate_home.icon",
"navigate_stop.1x.icon",
"navigate_stop.icon",
"new_tab_button_incognito.icon",
"new_tab_button_plus.icon",
"overflow_chevron.1x.icon",
"overflow_chevron.icon",
"paintbrush.icon",
......
// Copyright 2018 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.
CANVAS_DIMENSIONS, 12,
MOVE_TO, 4.51f, 0.03f,
LINE_TO, 6, 1,
LINE_TO, 7.48f, 0.03f,
R_CUBIC_TO, 0.36f, -0.1f, 0.76f, 0.06f, 0.89f, 0.36f,
LINE_TO, 10, 4.97f,
H_LINE_TO, 2,
LINE_TO, 3.62f, 0.39f,
R_CUBIC_TO, 0.14f, -0.3f, 0.53f, -0.46f, 0.89f, -0.36f,
CLOSE,
MOVE_TO, 9, 12,
R_CUBIC_TO, -1, 0, -2, -0.5f, -2, -1.5f,
R_CUBIC_TO, -0.5f, -0.66f, -1.5f, -0.66f, -2, 0,
R_CUBIC_TO, 0, 1, -1, 1.5f, -2, 1.5f,
R_CUBIC_TO, -1.5f, 0, -2, -1, -2, -2.01f,
R_CUBIC_TO, 0, -1, 0.5f, -2.01f, 2, -2.01f,
R_CUBIC_TO, 1, 0, 2, 0.52f, 2, 1.52f,
R_CUBIC_TO, 0.5f, -0.51f, 1.5f, -0.51f, 2, 0,
R_CUBIC_TO, 0, -1, 1, -1.52f, 2, -1.52f,
R_CUBIC_TO, 1.5f, 0, 2, 1.02f, 2, 2.02f,
R_CUBIC_TO, 0, 1, -0.5f, 2, -2, 2,
CLOSE,
R_MOVE_TO, 3, -5.02f,
H_LINE_TO, 0,
V_LINE_TO, 5.97f,
R_H_LINE_TO, 12,
R_V_LINE_TO, 1,
CLOSE,
R_MOVE_TO, -9, 4.02f,
R_CUBIC_TO, 0.55f, 0, 1, -0.45f, 1, -1,
R_CUBIC_TO, 0, -0.56f, -0.45f, -1, -1, -1,
R_CUBIC_TO, -0.55f, 0, -1, 0.45f, -1, 1,
R_CUBIC_TO, 0, 0.56f, 0.45f, 1, 1, 1,
CLOSE,
R_MOVE_TO, 6, 0,
R_CUBIC_TO, 0.55f, 0, 1, -0.45f, 1, -1,
R_CUBIC_TO, 0, -0.56f, -0.45f, -1, -1, -1,
R_CUBIC_TO, -0.55f, 0, -1, 0.45f, -1, 1,
R_CUBIC_TO, 0, 0.56f, 0.45f, 1, 1, 1,
CLOSE,
END
\ No newline at end of file
// Copyright 2018 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.
CANVAS_DIMENSIONS, 12,
MOVE_TO, 7, 5,
R_H_LINE_TO, 5,
R_V_LINE_TO, 2,
H_LINE_TO, 7,
R_V_LINE_TO, 5,
H_LINE_TO, 5,
V_LINE_TO, 7,
H_LINE_TO, 0,
V_LINE_TO, 5,
R_H_LINE_TO, 5,
V_LINE_TO, 0,
R_H_LINE_TO, 2,
CLOSE,
END
\ No newline at end of file
......@@ -9,11 +9,10 @@
#include "ui/base/material_design/material_design_controller.h"
int GetLayoutConstant(LayoutConstant constant) {
const bool hybrid = ui::MaterialDesignController::GetMode() ==
ui::MaterialDesignController::MATERIAL_HYBRID;
const int mode = ui::MaterialDesignController::GetMode();
const bool hybrid = mode == ui::MaterialDesignController::MATERIAL_HYBRID;
const bool touch_optimized_material =
ui::MaterialDesignController::IsTouchOptimizedUiEnabled();
mode == ui::MaterialDesignController::MATERIAL_TOUCH_OPTIMIZED;
switch (constant) {
case LOCATION_BAR_BUBBLE_VERTICAL_PADDING:
return hybrid ? 1 : 3;
......@@ -38,11 +37,19 @@ int GetLayoutConstant(LayoutConstant constant) {
return 16;
case LOCATION_BAR_ICON_INTERIOR_PADDING:
return 4;
case TABSTRIP_NEW_TAB_BUTTON_OVERLAP:
return hybrid ? 6 : 5;
case TABSTRIP_NEW_TAB_BUTTON_SPACING: {
// In non-touch optimized UI, we make the new tab button overlap with the
// last tab in the tabstrip (i.e negative spacing). However, in
// touch-optimized UI, we actually want to push the new tab button
// further away from the tab. The distance is 8 DIP from the point at
// which the last tab's endcap intersects with the tabstrip separator,
// which is actually 6 DIP from the last tab's right point.
constexpr int kSpacing[] = {-5, -6, 6};
return kSpacing[mode];
}
case TAB_HEIGHT: {
constexpr int kTabHeight[] = {29, 33, 41};
return kTabHeight[ui::MaterialDesignController::GetMode()];
return kTabHeight[mode];
}
case TOOLBAR_ELEMENT_PADDING:
return hybrid ? 8 : 0;
......@@ -78,12 +85,14 @@ gfx::Insets GetLayoutInsets(LayoutInset inset) {
return gfx::Insets();
}
gfx::Size GetLayoutSize(LayoutSize size) {
const bool hybrid = ui::MaterialDesignController::GetMode() ==
ui::MaterialDesignController::MATERIAL_HYBRID;
gfx::Size GetLayoutSize(LayoutSize size, bool is_incognito) {
const int mode = ui::MaterialDesignController::GetMode();
switch (size) {
case NEW_TAB_BUTTON:
return hybrid ? gfx::Size(39, 21) : gfx::Size(36, 18);
case NEW_TAB_BUTTON: {
const gfx::Size sizes[] = {
{36, 18}, {39, 21}, {(is_incognito ? 42 : 24), 24}};
return sizes[mode];
}
}
NOTREACHED();
return gfx::Size();
......
......@@ -46,8 +46,8 @@ enum LayoutConstant {
// of the icon view (e.g. does not highlight on hover).
LOCATION_BAR_ICON_INTERIOR_PADDING,
// The amount of overlap between the last tab and the new tab button.
TABSTRIP_NEW_TAB_BUTTON_OVERLAP,
// The amount of spacing between the last tab and the new tab button.
TABSTRIP_NEW_TAB_BUTTON_SPACING,
// The height of a tab, including outer strokes. In non-100% scales this is
// slightly larger than the apparent height of the tab, as the top stroke is
......@@ -93,12 +93,14 @@ enum LayoutInset {
enum LayoutSize {
// The visible size of the new tab button; does not include any Fitts' Law
// extensions.
// extensions. Note that in touch-optimized UI mode, the new tab button's
// width is larger when the browser is in incognito mode. The height remains
// the same whether incognito or not.
NEW_TAB_BUTTON,
};
int GetLayoutConstant(LayoutConstant constant);
gfx::Insets GetLayoutInsets(LayoutInset inset);
gfx::Size GetLayoutSize(LayoutSize size);
gfx::Size GetLayoutSize(LayoutSize size, bool is_incognito);
#endif // CHROME_BROWSER_UI_LAYOUT_CONSTANTS_H_
......@@ -41,6 +41,7 @@ class TestLayoutDelegate : public OpaqueBrowserFrameViewLayoutDelegate {
~TestLayoutDelegate() override {}
// OpaqueBrowserFrameViewLayoutDelegate:
bool IsIncognito() const override { return false; }
bool ShouldShowWindowIcon() const override { return false; }
bool ShouldShowWindowTitle() const override { return false; }
base::string16 GetWindowTitle() const override { return base::string16(); }
......
......@@ -151,9 +151,13 @@ gfx::Rect GlassBrowserFrameView::GetBoundsForTabStrip(
// In non-tablet mode, allow the new tab button to slide completely
// under the profile switcher button.
if (!IsMaximized()) {
end_x = std::min(end_x + GetLayoutSize(NEW_TAB_BUTTON).width() +
kNewTabCaptionRestoredSpacing,
old_end_x);
const int new_tab_button_width =
GetLayoutSize(NEW_TAB_BUTTON,
browser_view()->tabstrip()->IsIncognito())
.width();
end_x = std::min(
end_x + new_tab_button_width + kNewTabCaptionRestoredSpacing,
old_end_x);
}
}
}
......
......@@ -352,6 +352,10 @@ gfx::ImageSkia OpaqueBrowserFrameView::GetFaviconForTabIconView() {
///////////////////////////////////////////////////////////////////////////////
// OpaqueBrowserFrameView, OpaqueBrowserFrameViewLayoutDelegate implementation:
bool OpaqueBrowserFrameView::IsIncognito() const {
return browser_view()->tabstrip()->IsIncognito();
}
bool OpaqueBrowserFrameView::ShouldShowWindowIcon() const {
views::WidgetDelegate* delegate = frame()->widget_delegate();
return ShouldShowWindowTitleBar() && delegate &&
......
......@@ -86,6 +86,7 @@ class OpaqueBrowserFrameView : public BrowserNonClientFrameView,
gfx::ImageSkia GetFaviconForTabIconView() override;
// OpaqueBrowserFrameViewLayoutDelegate implementation:
bool IsIncognito() const override;
bool ShouldShowWindowIcon() const override;
bool ShouldShowWindowTitle() const override;
base::string16 GetWindowTitle() const override;
......
......@@ -291,7 +291,8 @@ void OpaqueBrowserFrameViewLayout::LayoutNewStyleAvatar(views::View* host) {
// the avatar button.
if (!IsTitleBarCondensed()) {
trailing_button_start_ -=
GetLayoutSize(NEW_TAB_BUTTON).width() + kCaptionSpacing;
GetLayoutSize(NEW_TAB_BUTTON, delegate_->IsIncognito()).width() +
kCaptionSpacing;
}
new_avatar_button_->SetBounds(button_x, button_y, button_width,
......
......@@ -14,6 +14,9 @@ class Size;
// Browser{,Frame,View}.
class OpaqueBrowserFrameViewLayoutDelegate {
public:
// Returns true if the browser is in incognito mode.
virtual bool IsIncognito() const = 0;
// Controls the visual placement of the window icon/title in non-tabstrip
// mode.
virtual bool ShouldShowWindowIcon() const = 0;
......
......@@ -49,6 +49,7 @@ class TestLayoutDelegate : public OpaqueBrowserFrameViewLayoutDelegate {
void set_maximized(bool maximized) { maximized_ = maximized; }
// OpaqueBrowserFrameViewLayoutDelegate:
bool IsIncognito() const override { return false; }
bool ShouldShowWindowIcon() const override { return !window_title_.empty(); }
bool ShouldShowWindowTitle() const override { return !window_title_.empty(); }
base::string16 GetWindowTitle() const override { return window_title_; }
......@@ -237,7 +238,8 @@ class OpaqueBrowserFrameViewLayoutTest : public views::ViewsTestBase {
caption_buttons_width +=
avatar_button_->GetPreferredSize().width() +
(maximized ? OBFVL::kCaptionSpacing
: -GetLayoutSize(NEW_TAB_BUTTON).width());
: -GetLayoutSize(NEW_TAB_BUTTON, delegate_->IsIncognito())
.width());
}
int tabstrip_x = OpaqueBrowserFrameView::kAvatarIconPadding;
if (show_caption_buttons && caption_buttons_on_left) {
......
......@@ -60,7 +60,15 @@ class NewTabButton : public views::ImageButton,
void OnMouseReleased(const ui::MouseEvent& event) override;
#endif
void OnGestureEvent(ui::GestureEvent* event) override;
void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override;
std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
void NotifyClick(const ui::Event& event) override;
std::unique_ptr<views::InkDrop> CreateInkDrop() override;
std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
void PaintButtonContents(gfx::Canvas* canvas) override;
void Layout() override;
void OnThemeChanged() override;
// views::MaskedTargeterDelegate:
bool GetHitTestMask(gfx::Path* mask) const override;
......@@ -72,7 +80,7 @@ class NewTabButton : public views::ImageButton,
// Note: This is different than the rect around the entire New Tab Button as
// it extends to the top of the tabstrip for Fitts' Law interaction in a
// maximized window. Used for anchoring the NewTabPromo.
gfx::Rect GetVisibleBounds();
gfx::Rect GetVisibleBounds() const;
// Computes a path corresponding to the button's outer border for a given
// |scale| and stores it in |path|. |button_y| is used as the y-coordinate
......@@ -91,22 +99,52 @@ class NewTabButton : public views::ImageButton,
const SkPath& fill,
gfx::Canvas* canvas) const;
SkColor GetButtonFillColor() const;
// In the touch-optimized UI, initializes the needed button icons.
void InitButtonIcons();
// Returns the path for the touch-optimized new tab button for the given
// |scale|. |button_y| is the button's top y-cordinate. If |for_fill| is true,
// the path will be shrunk by 1px from all sides to allow room for the stroke
// to show up.
SkPath GetTouchOptimizedButtonPath(float button_y,
float scale,
bool for_fill) const;
void UpdateInkDropBaseColor();
// Tab strip that contains this button.
TabStrip* tab_strip_;
// Promotional UI that appears next to the NewTabButton and encourages its
// use. Owned by its NativeWidget.
NewTabPromoBubbleView* new_tab_promo_;
NewTabPromoBubbleView* new_tab_promo_ = nullptr;
// The offset used to paint the background image.
gfx::Point background_offset_;
// Whether this new tab button belongs to a tabstrip that is part of an
// incognito mode browser or not. Note that you can't drag a tab from one
// incognito browser to another non-incognito browser or vice versa.
const bool is_incognito_;
// In the touch-optimized UI, the new tab button has a plus icon, and an
// incognito icon if is in incognito mode.
gfx::ImageSkia plus_icon_;
gfx::ImageSkia incognito_icon_;
// In touch-optimized UI, this view holds the ink drop layer so that it's
// shifted down to the correct top offset of the button, since the actual
// button's y-coordinate is 0 due to Fitt's Law needs.
views::InkDropContainerView* ink_drop_container_ = nullptr;
// were we destroyed?
bool* destroyed_;
bool* destroyed_ = nullptr;
// Observes the NewTabPromo's Widget. Used to tell whether the promo is
// open and get called back when it closes.
ScopedObserver<views::Widget, WidgetObserver> new_tab_promo_observer_;
ScopedObserver<views::Widget, WidgetObserver> new_tab_promo_observer_{this};
DISALLOW_COPY_AND_ASSIGN(NewTabButton);
};
......
......@@ -117,9 +117,9 @@ const int kPinnedToNonPinnedOffset = 3;
#endif
// Returns the width needed for the new tab button (and padding).
int GetNewTabButtonWidth() {
return GetLayoutSize(NEW_TAB_BUTTON).width() -
GetLayoutConstant(TABSTRIP_NEW_TAB_BUTTON_OVERLAP);
int GetNewTabButtonWidth(bool is_incognito) {
return GetLayoutSize(NEW_TAB_BUTTON, is_incognito).width() +
GetLayoutConstant(TABSTRIP_NEW_TAB_BUTTON_SPACING);
}
// Animation delegate used for any automatic tab movement. Hides the tab if it
......@@ -847,7 +847,8 @@ bool TabStrip::IsTabPinned(const Tab* tab) const {
}
bool TabStrip::IsIncognito() const {
return controller_->IsIncognito();
// There may be no controller in tests.
return controller_ && controller_->IsIncognito();
}
void TabStrip::MaybeStartDrag(
......@@ -1205,7 +1206,7 @@ gfx::Size TabStrip::CalculatePreferredSize() const {
needed_tab_width = std::min(std::max(needed_tab_width, min_selected_width),
largest_min_tab_width);
}
return gfx::Size(needed_tab_width + GetNewTabButtonWidth(),
return gfx::Size(needed_tab_width + GetNewTabButtonWidth(IsIncognito()),
Tab::GetMinimumInactiveSize().height());
}
......@@ -1313,7 +1314,7 @@ void TabStrip::Init() {
// So we get enter/exit on children to switch stacked layout on and off.
set_notify_enter_exit_on_child(true);
new_tab_button_bounds_.set_size(GetLayoutSize(NEW_TAB_BUTTON));
new_tab_button_bounds_.set_size(GetLayoutSize(NEW_TAB_BUTTON, IsIncognito()));
new_tab_button_bounds_.Inset(0, 0, 0, -NewTabButton::GetTopOffset());
new_tab_button_ = new NewTabButton(this, this);
new_tab_button_->SetTooltipText(
......@@ -2120,8 +2121,8 @@ void TabStrip::GenerateIdealBounds() {
// tabstrip. Constrain the x-coordinate of the new tab button so that it is
// always visible.
const int new_tab_x = std::min(
max_new_tab_x, tabs_.ideal_bounds(tabs_.view_size() - 1).right() -
GetLayoutConstant(TABSTRIP_NEW_TAB_BUTTON_OVERLAP));
max_new_tab_x, tabs_.ideal_bounds(tabs_.view_size() - 1).right() +
GetLayoutConstant(TABSTRIP_NEW_TAB_BUTTON_SPACING));
const int old_max_x = new_tab_button_bounds_.right();
new_tab_button_bounds_.set_origin(gfx::Point(new_tab_x, 0));
if (new_tab_button_bounds_.right() != old_max_x) {
......@@ -2150,7 +2151,7 @@ int TabStrip::GenerateIdealBoundsForPinnedTabs(int* first_non_pinned_index) {
}
int TabStrip::GetTabAreaWidth() const {
return width() - GetNewTabButtonWidth();
return width() - GetNewTabButtonWidth(IsIncognito());
}
void TabStrip::StartResizeLayoutAnimation() {
......@@ -2194,8 +2195,8 @@ void TabStrip::StartMouseInitiatedRemoveTabAnimation(int model_index) {
// the new tab button should stay where it is.
new_tab_button_bounds_.set_x(
std::min(width() - new_tab_button_bounds_.width(),
ideal_bounds(tab_count() - 1).right() -
GetLayoutConstant(TABSTRIP_NEW_TAB_BUTTON_OVERLAP)));
ideal_bounds(tab_count() - 1).right() +
GetLayoutConstant(TABSTRIP_NEW_TAB_BUTTON_SPACING)));
PrepareForAnimation();
......
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