Commit 38c60c13 authored by Christopher Lam's avatar Christopher Lam Committed by Commit Bot

[desktop-pwas] Render 'App name by example.com' string in window title.

This CL adds an extra string to the Desktop PWA window title that
denotes which app the window belongs to and the domain of the start URL.
The window title is painted such that the page title will elide first,
followed by the app name then the domain. Further work is required to
maintain this property for all languages.

Bug: 762401
Change-Id: Ibcc697b02fa643df9c2618ca0ef0249c6f887b64
Reviewed-on: https://chromium-review.googlesource.com/768601
Commit-Queue: calamity <calamity@chromium.org>
Reviewed-by: default avatarTrent Apted <tapted@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517782}
parent 735b892f
......@@ -64,14 +64,6 @@ void TileRoundRect(gfx::Canvas* canvas,
canvas->DrawPath(path, flags);
}
// Returns the FontList to use for the title.
const gfx::FontList& GetTitleFontList() {
static const gfx::FontList* title_font_list =
new gfx::FontList(views::NativeWidgetAura::GetWindowTitleFontList());
ANNOTATE_LEAKING_OBJECT_PTR(title_font_list);
return *title_font_list;
}
} // namespace
namespace ash {
......@@ -79,43 +71,38 @@ namespace ash {
///////////////////////////////////////////////////////////////////////////////
// DefaultFrameHeader, public:
DefaultFrameHeader::DefaultFrameHeader(mojom::WindowStyle window_style)
DefaultFrameHeader::DefaultFrameHeader(
views::Widget* frame,
views::View* header_view,
FrameCaptionButtonContainerView* caption_button_container,
FrameCaptionButton* back_button,
mojom::WindowStyle window_style)
: window_style_(window_style),
frame_(nullptr),
view_(nullptr),
back_button_(nullptr),
frame_(frame),
view_(header_view),
back_button_(back_button),
left_header_view_(nullptr),
active_frame_color_(kDefaultFrameColor),
inactive_frame_color_(kDefaultFrameColor),
caption_button_container_(nullptr),
caption_button_container_(caption_button_container),
painted_height_(0),
mode_(MODE_INACTIVE),
initial_paint_(true),
activation_animation_(new gfx::SlideAnimation(this)) {}
DefaultFrameHeader::~DefaultFrameHeader() {}
void DefaultFrameHeader::Init(
views::Widget* frame,
views::View* header_view,
FrameCaptionButtonContainerView* caption_button_container,
FrameCaptionButton* back_button) {
activation_animation_(new gfx::SlideAnimation(this)) {
DCHECK(frame);
DCHECK(header_view);
DCHECK(caption_button_container);
frame_ = frame;
view_ = header_view;
caption_button_container_ = caption_button_container;
caption_button_container_->SetButtonSize(
GetAshLayoutSize(AshLayoutSize::NON_BROWSER_CAPTION_BUTTON));
UpdateAllButtonImages();
UpdateBackButton(back_button);
}
DefaultFrameHeader::~DefaultFrameHeader() = default;
int DefaultFrameHeader::GetMinimumHeaderWidth() const {
// Ensure we have enough space for the window icon and buttons. We allow
// the title string to collapse to zero width.
return GetTitleBounds().x() +
return GetAvailableTitleBounds().x() +
caption_button_container_->GetMinimumSize().width();
}
......@@ -220,7 +207,7 @@ void DefaultFrameHeader::SetHeaderHeightForPainting(int height) {
}
void DefaultFrameHeader::SchedulePaintForTitle() {
view_->SchedulePaintInRect(GetTitleBounds());
view_->SchedulePaintInRect(GetAvailableTitleBounds());
}
void DefaultFrameHeader::SetPaintAsActive(bool paint_as_active) {
......@@ -253,6 +240,14 @@ bool DefaultFrameHeader::ShouldUseLightImages() const {
: active_frame_color_);
}
// static
const gfx::FontList& DefaultFrameHeader::GetTitleFontList() {
static const gfx::FontList* title_font_list =
new gfx::FontList(views::NativeWidgetAura::GetWindowTitleFontList());
ANNOTATE_LEAKING_OBJECT_PTR(title_font_list);
return *title_font_list;
}
void DefaultFrameHeader::UpdateLeftHeaderView(views::View* left_header_view) {
left_header_view_ = left_header_view;
}
......@@ -301,11 +296,10 @@ void DefaultFrameHeader::PaintHighlightForInactiveRestoredWindow(
void DefaultFrameHeader::PaintTitleBar(gfx::Canvas* canvas) {
// The window icon is painted by its own views::View.
gfx::Rect title_bounds = GetTitleBounds();
gfx::Rect title_bounds = GetAvailableTitleBounds();
title_bounds.set_x(view_->GetMirroredXForRect(title_bounds));
canvas->DrawStringRectWithFlags(
frame_->widget_delegate()->GetWindowTitle(), GetTitleFontList(),
GetTitleColor(), title_bounds, gfx::Canvas::NO_SUBPIXEL_RENDERING);
canvas->DrawStringRect(frame_->widget_delegate()->GetWindowTitle(),
GetTitleFontList(), GetTitleColor(), title_bounds);
}
void DefaultFrameHeader::PaintHeaderContentSeparator(gfx::Canvas* canvas) {
......@@ -347,10 +341,10 @@ gfx::Rect DefaultFrameHeader::GetLocalBounds() const {
return gfx::Rect(view_->width(), painted_height_);
}
gfx::Rect DefaultFrameHeader::GetTitleBounds() const {
gfx::Rect DefaultFrameHeader::GetAvailableTitleBounds() const {
views::View* left_view = left_header_view_ ? left_header_view_ : back_button_;
return FrameHeaderUtil::GetTitleBounds(left_view, caption_button_container_,
GetTitleFontList());
return FrameHeaderUtil::GetAvailableTitleBounds(
left_view, caption_button_container_, GetTitleFontList());
}
bool DefaultFrameHeader::UsesCustomFrameColors() const {
......
......@@ -17,9 +17,11 @@
#include "ui/gfx/animation/animation_delegate.h"
namespace gfx {
class FontList;
class Rect;
class SlideAnimation;
} // namespace gfx
namespace views {
class View;
class Widget;
......@@ -33,15 +35,14 @@ class FrameCaptionButtonContainerView;
class ASH_EXPORT DefaultFrameHeader : public FrameHeader,
public gfx::AnimationDelegate {
public:
explicit DefaultFrameHeader(
mojom::WindowStyle window_style = mojom::WindowStyle::DEFAULT);
~DefaultFrameHeader() override;
// DefaultFrameHeader does not take ownership of any of the parameters.
void Init(views::Widget* frame,
DefaultFrameHeader(
views::Widget* frame,
views::View* header_view,
FrameCaptionButtonContainerView* caption_button_container,
FrameCaptionButton* back_button);
FrameCaptionButton* back_button,
mojom::WindowStyle window_style = mojom::WindowStyle::DEFAULT);
~DefaultFrameHeader() override;
// FrameHeader overrides:
int GetMinimumHeaderWidth() const override;
......@@ -73,6 +74,18 @@ class ASH_EXPORT DefaultFrameHeader : public FrameHeader,
// background of the frame is dark.
bool ShouldUseLightImages() const;
protected:
// Returns the FontList to use for the title.
static const gfx::FontList& GetTitleFontList();
// Paints the title bar, primarily the title string.
virtual void PaintTitleBar(gfx::Canvas* canvas);
// Returns the bounds for the title.
gfx::Rect GetAvailableTitleBounds() const;
views::View* view() { return view_; }
private:
FRIEND_TEST_ALL_PREFIXES(DefaultFrameHeaderTest, BackButtonAlignment);
FRIEND_TEST_ALL_PREFIXES(DefaultFrameHeaderTest, TitleIconAlignment);
......@@ -85,9 +98,6 @@ class ASH_EXPORT DefaultFrameHeader : public FrameHeader,
// windows.
void PaintHighlightForInactiveRestoredWindow(gfx::Canvas* canvas);
// Paints the title bar, primarily the title string.
void PaintTitleBar(gfx::Canvas* canvas);
// Paints the header/content separator.
void PaintHeaderContentSeparator(gfx::Canvas* canvas);
......@@ -102,9 +112,6 @@ class ASH_EXPORT DefaultFrameHeader : public FrameHeader,
// same width as |view_|.
gfx::Rect GetLocalBounds() const;
// Returns the bounds for the title.
gfx::Rect GetTitleBounds() const;
// Returns whether the frame uses custom frame coloring.
bool UsesCustomFrameColors() const;
......
......@@ -14,7 +14,6 @@
#include "ui/views/widget/widget.h"
#include "ui/views/window/non_client_view.h"
using ash::FrameHeader;
using views::NonClientFrameView;
using views::Widget;
......@@ -26,18 +25,17 @@ using DefaultFrameHeaderTest = AshTestBase;
TEST_F(DefaultFrameHeaderTest, TitleIconAlignment) {
std::unique_ptr<Widget> w = CreateTestWidget(
nullptr, kShellWindowId_DefaultContainer, gfx::Rect(1, 2, 3, 4));
ash::FrameCaptionButtonContainerView container(w.get());
FrameCaptionButtonContainerView container(w.get());
views::StaticSizedView window_icon(gfx::Size(16, 16));
window_icon.SetBounds(0, 0, 16, 16);
w->SetBounds(gfx::Rect(0, 0, 500, 500));
w->Show();
DefaultFrameHeader frame_header;
frame_header.Init(w.get(), w->non_client_view()->frame_view(), &container,
nullptr);
DefaultFrameHeader frame_header(w.get(), w->non_client_view()->frame_view(),
&container, nullptr);
frame_header.UpdateLeftHeaderView(&window_icon);
frame_header.LayoutHeader();
gfx::Rect title_bounds = frame_header.GetTitleBounds();
gfx::Rect title_bounds = frame_header.GetAvailableTitleBounds();
EXPECT_EQ(window_icon.bounds().CenterPoint().y(),
title_bounds.CenterPoint().y());
}
......@@ -45,15 +43,14 @@ TEST_F(DefaultFrameHeaderTest, TitleIconAlignment) {
TEST_F(DefaultFrameHeaderTest, BackButtonAlignment) {
std::unique_ptr<Widget> w = CreateTestWidget(
nullptr, kShellWindowId_DefaultContainer, gfx::Rect(1, 2, 3, 4));
ash::FrameCaptionButtonContainerView container(w.get());
ash::FrameBackButton back;
FrameCaptionButtonContainerView container(w.get());
FrameBackButton back;
DefaultFrameHeader frame_header;
frame_header.Init(w.get(), w->non_client_view()->frame_view(), &container,
nullptr);
DefaultFrameHeader frame_header(w.get(), w->non_client_view()->frame_view(),
&container, nullptr);
frame_header.UpdateBackButton(&back);
frame_header.LayoutHeader();
gfx::Rect title_bounds = frame_header.GetTitleBounds();
gfx::Rect title_bounds = frame_header.GetAvailableTitleBounds();
// The back button should be positioned at the left edge, and
// vertically centered.
EXPECT_EQ(back.bounds().CenterPoint().y(), title_bounds.CenterPoint().y());
......@@ -64,15 +61,14 @@ TEST_F(DefaultFrameHeaderTest, BackButtonAlignment) {
TEST_F(DefaultFrameHeaderTest, LightIcons) {
std::unique_ptr<Widget> w = CreateTestWidget(
nullptr, kShellWindowId_DefaultContainer, gfx::Rect(1, 2, 3, 4));
ash::FrameCaptionButtonContainerView container(w.get());
FrameCaptionButtonContainerView container(w.get());
views::StaticSizedView window_icon(gfx::Size(16, 16));
window_icon.SetBounds(0, 0, 16, 16);
w->SetBounds(gfx::Rect(0, 0, 500, 500));
w->Show();
DefaultFrameHeader frame_header;
frame_header.Init(w.get(), w->non_client_view()->frame_view(), &container,
nullptr);
DefaultFrameHeader frame_header(w.get(), w->non_client_view()->frame_view(),
&container, nullptr);
// Check by default light icons are not used.
frame_header.mode_ = FrameHeader::MODE_ACTIVE;
......
......@@ -59,7 +59,7 @@ int FrameHeaderUtil::GetThemeBackgroundXInset() {
}
// static
gfx::Rect FrameHeaderUtil::GetTitleBounds(
gfx::Rect FrameHeaderUtil::GetAvailableTitleBounds(
const views::View* left_view,
const views::View* right_view,
const gfx::FontList& title_font_list) {
......
......@@ -34,10 +34,11 @@ class ASH_EXPORT FrameHeaderUtil {
// the window.
static int GetThemeBackgroundXInset();
// Returns the bounds for the header's title given the views to the left and
// right of the title, and the font used.
// |left_view| should be NULL if there is no view to the left of the title.
static gfx::Rect GetTitleBounds(const views::View* left_view,
// Returns the available bounds for the header's title given the views to the
// left and right of the title, and the font used. |left_view| should be null
// if there is no view to the left of the title.
static gfx::Rect GetAvailableTitleBounds(
const views::View* left_view,
const views::View* right_view,
const gfx::FontList& title_font_list);
......
......@@ -21,7 +21,6 @@ namespace ash {
HeaderView::HeaderView(views::Widget* target_widget,
mojom::WindowStyle window_style)
: target_widget_(target_widget),
frame_header_(std::make_unique<DefaultFrameHeader>(window_style)),
avatar_icon_(nullptr),
caption_button_container_(nullptr),
fullscreen_visible_fraction_(0),
......@@ -31,7 +30,8 @@ HeaderView::HeaderView(views::Widget* target_widget,
caption_button_container_->UpdateSizeButtonVisibility();
AddChildView(caption_button_container_);
frame_header_->Init(target_widget_, this, caption_button_container_, nullptr);
frame_header_ = std::make_unique<DefaultFrameHeader>(
target_widget_, this, caption_button_container_, nullptr, window_style);
Shell::Get()->tablet_mode_controller()->AddObserver(this);
}
......
......@@ -48,14 +48,13 @@ const char* PanelFrameView::GetClassName() const {
}
void PanelFrameView::InitFrameHeader() {
frame_header_.reset(new DefaultFrameHeader);
GetWidgetWindow()->SetProperty(aura::client::kTopViewColor,
frame_header_->GetInactiveFrameColor());
caption_button_container_ = new FrameCaptionButtonContainerView(frame_);
AddChildView(caption_button_container_);
frame_header_->Init(frame_, this, caption_button_container_, nullptr);
frame_header_ = std::make_unique<DefaultFrameHeader>(
frame_, this, caption_button_container_, nullptr);
GetWidgetWindow()->SetProperty(aura::client::kTopViewColor,
frame_header_->GetInactiveFrameColor());
if (frame_->widget_delegate()->ShouldShowWindowIcon()) {
window_icon_ = new views::ImageView();
......
......@@ -4571,6 +4571,10 @@ Keep your key file in a safe place. You will need it to create new versions of y
</message>
</if>
<message name="IDS_HOSTED_APP_NAME_AND_DOMAIN" desc="The string that is always displayed in the window title in Hosted App app windows.">
<ph name="APP_NAME">$1<ex>GMail</ex></ph> by <ph name="SITE_ORIGIN">$2<ex>google.com</ex></ph>
</message>
<!-- Components -->
<message name="IDS_COMPONENTS_TITLE" desc="Title for the chrome://components page.">
Components
......
......@@ -1302,6 +1302,8 @@ split_static_library("ui") {
"views/frame/browser_frame_header_ash.h",
"views/frame/browser_non_client_frame_view_ash.cc",
"views/frame/browser_non_client_frame_view_ash.h",
"views/frame/hosted_app_frame_header_ash.cc",
"views/frame/hosted_app_frame_header_ash.h",
"views/frame/immersive_context_mus.cc",
"views/frame/immersive_context_mus.h",
"views/frame/immersive_handler_factory_mus.cc",
......
......@@ -20,6 +20,7 @@
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "ui/gfx/favicon_size.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
......@@ -78,9 +79,7 @@ HostedAppBrowserController::HostedAppBrowserController(Browser* browser)
HostedAppBrowserController::~HostedAppBrowserController() {}
bool HostedAppBrowserController::ShouldShowLocationBar() const {
const Extension* extension =
ExtensionRegistry::Get(browser_->profile())->GetExtensionById(
extension_id_, ExtensionRegistry::EVERYTHING);
const Extension* extension = GetExtension();
const content::WebContents* web_contents =
browser_->tab_strip_model()->GetActiveWebContents();
......@@ -154,11 +153,29 @@ base::Optional<SkColor> HostedAppBrowserController::GetThemeColor() const {
}
base::string16 HostedAppBrowserController::GetTitle() const {
content::NavigationEntry* entry = browser_->tab_strip_model()
->GetActiveWebContents()
->GetController()
.GetVisibleEntry();
content::WebContents* web_contents =
browser_->tab_strip_model()->GetActiveWebContents();
if (!web_contents)
return base::string16();
content::NavigationEntry* entry =
web_contents->GetController().GetVisibleEntry();
return entry ? entry->GetTitle() : base::string16();
}
const Extension* HostedAppBrowserController::GetExtension() const {
return ExtensionRegistry::Get(browser_->profile())
->GetExtensionById(extension_id_, ExtensionRegistry::EVERYTHING);
}
std::string HostedAppBrowserController::GetAppShortName() const {
return GetExtension()->short_name();
}
std::string HostedAppBrowserController::GetDomainAndRegistry() const {
return net::registry_controlled_domains::GetDomainAndRegistry(
AppLaunchInfo::GetLaunchWebURL(GetExtension()),
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
}
} // namespace extensions
......@@ -19,6 +19,8 @@ class ImageSkia;
namespace extensions {
class Extension;
// Class to encapsulate logic to control the browser UI for hosted apps.
class HostedAppBrowserController {
public:
......@@ -52,7 +54,16 @@ class HostedAppBrowserController {
// Returns the title to be displayed in the window title bar.
base::string16 GetTitle() const;
// Gets the short name of the app.
std::string GetAppShortName() const;
// Gets the domain and registry of the app start url (e.g example.com.au).
std::string GetDomainAndRegistry() const;
private:
// Gets the extension for this controller.
const Extension* GetExtension() const;
Browser* browser_;
const std::string extension_id_;
......
......@@ -311,6 +311,6 @@ gfx::Rect BrowserFrameHeaderAsh::GetPaintedBounds() const {
gfx::Rect BrowserFrameHeaderAsh::GetTitleBounds() const {
views::View* left_view = window_icon_ ? window_icon_ : back_button_;
return ash::FrameHeaderUtil::GetTitleBounds(
return ash::FrameHeaderUtil::GetAvailableTitleBounds(
left_view, caption_button_container_, BrowserFrame::GetTitleFontList());
}
......@@ -28,6 +28,7 @@
#include "chrome/browser/ui/views/frame/browser_frame_header_ash.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/hosted_app_button_container.h"
#include "chrome/browser/ui/views/frame/hosted_app_frame_header_ash.h"
#include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
#include "chrome/browser/ui/views/profiles/profile_indicator_icon.h"
#include "chrome/browser/ui/views/tab_icon_view.h"
......@@ -108,43 +109,7 @@ void BrowserNonClientFrameViewAsh::Init() {
// TODO(oshima): Update the back button state.
}
if (UsePackagedAppHeaderStyle()) {
ash::DefaultFrameHeader* frame_header = new ash::DefaultFrameHeader;
frame_header_.reset(frame_header);
frame_header->Init(frame(), this, caption_button_container_, back_button_);
if (window_icon_)
frame_header->UpdateLeftHeaderView(window_icon_);
extensions::HostedAppBrowserController* app_controller =
browser->hosted_app_controller();
if (app_controller) {
// Hosted apps apply a theme color if specified by the extension.
base::Optional<SkColor> theme_color = app_controller->GetThemeColor();
if (theme_color) {
SkColor opaque_theme_color =
SkColorSetA(theme_color.value(), SK_AlphaOPAQUE);
frame_header->SetFrameColors(opaque_theme_color, opaque_theme_color);
}
if (extensions::HostedAppBrowserController::
IsForExperimentalHostedAppBrowser(browser)) {
SkColor text_color = frame_header->GetTitleColor();
hosted_app_button_container_ = new HostedAppButtonContainer(
browser_view(), text_color,
SkColorSetA(text_color,
255 * ash::kInactiveFrameButtonIconAlphaRatio));
caption_button_container_->AddChildViewAt(hosted_app_button_container_,
0);
}
} else if (!browser->is_app()) {
// For non app (i.e. WebUI) windows (e.g. Settings) use MD frame color.
frame_header->SetFrameColors(kMdWebUIFrameColor, kMdWebUIFrameColor);
}
} else {
BrowserFrameHeaderAsh* frame_header = new BrowserFrameHeaderAsh;
frame_header_.reset(frame_header);
frame_header->Init(frame(), browser_view(), this, window_icon_,
caption_button_container_, back_button_);
}
frame_header_ = CreateFrameHeader();
if (browser->is_app()) {
frame()->GetNativeWindow()->SetProperty(
......@@ -529,3 +494,42 @@ void BrowserNonClientFrameViewAsh::OnOverviewModeChanged(bool in_overview) {
// Schedule a paint to show or hide the header.
SchedulePaint();
}
std::unique_ptr<ash::FrameHeader>
BrowserNonClientFrameViewAsh::CreateFrameHeader() {
Browser* browser = browser_view()->browser();
if (!UsePackagedAppHeaderStyle()) {
auto browser_frame_header = std::make_unique<BrowserFrameHeaderAsh>();
browser_frame_header->Init(frame(), browser_view(), this, window_icon_,
caption_button_container_, back_button_);
return browser_frame_header;
}
std::unique_ptr<ash::DefaultFrameHeader> default_frame_header = nullptr;
if (extensions::HostedAppBrowserController::IsForExperimentalHostedAppBrowser(
browser)) {
default_frame_header = std::make_unique<HostedAppFrameHeaderAsh>(
browser->hosted_app_controller(), frame(), this,
caption_button_container_, back_button_);
// Add the container for extra hosted app buttons (e.g app menu button).
SkColor text_color = default_frame_header->GetTitleColor();
hosted_app_button_container_ = new HostedAppButtonContainer(
browser_view(), text_color,
SkColorSetA(text_color, 255 * ash::kInactiveFrameButtonIconAlphaRatio));
caption_button_container_->AddChildViewAt(hosted_app_button_container_, 0);
} else {
default_frame_header = std::make_unique<ash::DefaultFrameHeader>(
frame(), this, caption_button_container_, back_button_);
if (!browser->is_app()) {
// For non app (i.e. WebUI) windows (e.g. Settings) use MD frame color.
default_frame_header->SetFrameColors(kMdWebUIFrameColor,
kMdWebUIFrameColor);
}
}
if (window_icon_)
default_frame_header->UpdateLeftHeaderView(window_icon_);
return default_frame_header;
}
......@@ -115,6 +115,9 @@ class BrowserNonClientFrameViewAsh : public BrowserNonClientFrameView,
// ends.
void OnOverviewModeChanged(bool in_overview);
// Creates the frame header for the browser window.
std::unique_ptr<ash::FrameHeader> CreateFrameHeader();
// View which contains the window controls.
ash::FrameCaptionButtonContainerView* caption_button_container_;
......
......@@ -7,7 +7,6 @@
#include "ash/ash_constants.h"
#include "ash/frame/caption_buttons/frame_caption_button.h"
#include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
#include "ash/frame/default_frame_header.h"
#include "ash/frame/frame_header.h"
#include "ash/public/cpp/ash_switches.h"
#include "ash/public/cpp/immersive/immersive_fullscreen_controller_test_api.h"
......@@ -35,6 +34,7 @@
#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/hosted_app_button_container.h"
#include "chrome/browser/ui/views/frame/hosted_app_frame_header_ash.h"
#include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
#include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
#include "chrome/browser/ui/views/profiles/profile_indicator_icon.h"
......@@ -480,22 +480,45 @@ IN_PROC_BROWSER_TEST_F(HostedAppNonClientFrameViewAshTest, HostedAppFrame) {
static_cast<BrowserNonClientFrameViewAsh*>(
browser_view->frame()->GetFrameView());
EXPECT_TRUE(frame_view->hosted_app_button_container_->visible());
HostedAppButtonContainer* button_container =
frame_view->hosted_app_button_container_;
EXPECT_TRUE(button_container->visible());
// Ensure the theme color is set.
auto* frame_header =
static_cast<ash::DefaultFrameHeader*>(frame_view->frame_header_.get());
static_cast<HostedAppFrameHeaderAsh*>(frame_view->frame_header_.get());
EXPECT_EQ(SK_ColorBLUE, frame_header->GetActiveFrameColor());
EXPECT_EQ(SK_ColorBLUE, frame_header->GetInactiveFrameColor());
EXPECT_EQ(SK_ColorWHITE, button_container->active_icon_color_);
// Show the menu.
HostedAppButtonContainer::AppMenuButton* menu_button =
frame_view->hosted_app_button_container_->app_menu_button_;
button_container->app_menu_button_;
ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
menu_button->OnMousePressed(e);
EXPECT_TRUE(menu_button->menu()->IsShowing());
// The app and domain should render next to the window title.
frame_header->LayoutRenderTexts(gfx::Rect(300, 30), 100, 100);
EXPECT_EQ(gfx::Rect(100, 30),
frame_header->title_render_text_->display_rect());
EXPECT_EQ(gfx::Rect(100, 0, 100, 30),
frame_header->app_and_domain_render_text_->display_rect());
// The title should prefer truncating the window title.
frame_header->LayoutRenderTexts(gfx::Rect(300, 30), 250, 100);
EXPECT_EQ(gfx::Rect(200, 30),
frame_header->title_render_text_->display_rect());
EXPECT_EQ(gfx::Rect(200, 0, 100, 30),
frame_header->app_and_domain_render_text_->display_rect());
// The app and domain should be clipped to the available title bounds.
frame_header->LayoutRenderTexts(gfx::Rect(60, 30), 250, 100);
EXPECT_EQ(gfx::Rect(0, 30), frame_header->title_render_text_->display_rect());
EXPECT_EQ(gfx::Rect(60, 30),
frame_header->app_and_domain_render_text_->display_rect());
}
namespace {
......
// Copyright 2017 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/ui/views/frame/hosted_app_frame_header_ash.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/render_text.h"
#include "ui/views/view.h"
HostedAppFrameHeaderAsh::HostedAppFrameHeaderAsh(
extensions::HostedAppBrowserController* app_controller,
views::Widget* frame,
views::View* header_view,
ash::FrameCaptionButtonContainerView* caption_button_container,
ash::FrameCaptionButton* back_button)
: DefaultFrameHeader(frame,
header_view,
caption_button_container,
back_button),
app_controller_(app_controller),
app_name_(base::UTF8ToUTF16(app_controller->GetAppShortName())),
app_and_domain_(l10n_util::GetStringFUTF16(
IDS_HOSTED_APP_NAME_AND_DOMAIN,
app_name_,
base::UTF8ToUTF16(app_controller->GetDomainAndRegistry()))) {
// Hosted apps apply a theme color if specified by the extension.
base::Optional<SkColor> theme_color = app_controller->GetThemeColor();
if (theme_color) {
SkColor opaque_theme_color =
SkColorSetA(theme_color.value(), SK_AlphaOPAQUE);
SetFrameColors(opaque_theme_color, opaque_theme_color);
}
title_render_text_ = CreateRenderText();
app_and_domain_render_text_ = CreateRenderText();
app_and_domain_render_text_->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
}
HostedAppFrameHeaderAsh::~HostedAppFrameHeaderAsh() {}
std::unique_ptr<gfx::RenderText> HostedAppFrameHeaderAsh::CreateRenderText() {
std::unique_ptr<gfx::RenderText> render_text(
gfx::RenderText::CreateInstance());
render_text->SetFontList(GetTitleFontList());
render_text->SetCursorEnabled(false);
render_text->SetColor(GetTitleColor());
render_text->SetElideBehavior(gfx::FADE_TAIL);
return render_text;
}
void HostedAppFrameHeaderAsh::UpdateRenderTexts() {
// TODO(calamity): Investigate localization implications of using a separator
// like this.
constexpr char kSeparator[] = " | ";
// If the title matches the app name, don't render the title.
base::string16 title = app_controller_->GetTitle();
if (title == app_name_)
title = base::string16();
// Add a separator if the title isn't empty.
app_and_domain_render_text_->SetText(
title.empty() ? app_and_domain_
: base::ASCIIToUTF16(kSeparator) + app_and_domain_);
title_render_text_->SetText(title);
}
void HostedAppFrameHeaderAsh::LayoutRenderTexts(
const gfx::Rect& available_title_bounds,
int title_width,
int app_and_domain_width) {
// The title is either its own width if it fits, or the space remaining
// after rendering the app and domain (which may be negative, but gets clamped
// to 0).
gfx::Rect title_bounds = available_title_bounds;
title_bounds.set_width(
std::min(title_bounds.width() - app_and_domain_width, title_width));
title_bounds.set_x(view()->GetMirroredXForRect(title_bounds));
title_render_text_->SetDisplayRect(title_bounds);
// The app and domain are placed to the right of the title and clipped to the
// original title bounds. This string is given full width whenever possible.
gfx::Rect app_and_domain_bounds = available_title_bounds;
app_and_domain_bounds.set_x(title_bounds.right());
app_and_domain_bounds.set_width(app_and_domain_width);
app_and_domain_bounds.Intersect(available_title_bounds);
app_and_domain_bounds.set_x(
view()->GetMirroredXForRect(app_and_domain_bounds));
app_and_domain_render_text_->SetDisplayRect(app_and_domain_bounds);
}
void HostedAppFrameHeaderAsh::PaintTitleBar(gfx::Canvas* canvas) {
title_render_text_->Draw(canvas);
app_and_domain_render_text_->Draw(canvas);
}
// TODO(calamity): Make this elide behavior more sophisticated in handling other
// possible translations and languages (the domain should always render).
void HostedAppFrameHeaderAsh::LayoutHeader() {
DefaultFrameHeader::LayoutHeader();
// We only need to recalculate the strings when the window title changes, and
// that causes the NonClientView to Layout(), which calls into us here.
UpdateRenderTexts();
LayoutRenderTexts(GetAvailableTitleBounds(),
title_render_text_->GetStringSize().width(),
app_and_domain_render_text_->GetStringSize().width());
}
// Copyright 2017 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.
#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_HOSTED_APP_FRAME_HEADER_ASH_H_
#define CHROME_BROWSER_UI_VIEWS_FRAME_HOSTED_APP_FRAME_HEADER_ASH_H_
#include "ash/frame/default_frame_header.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
namespace gfx {
class Canvas;
class RenderText;
} // namespace gfx
namespace extensions {
class HostedAppBrowserController;
} // namespace extensions
// Helper class for managing the hosted app window header.
class HostedAppFrameHeaderAsh : public ash::DefaultFrameHeader {
public:
HostedAppFrameHeaderAsh(
extensions::HostedAppBrowserController* app_controller,
views::Widget* frame,
views::View* header_view,
ash::FrameCaptionButtonContainerView* caption_button_container,
ash::FrameCaptionButton* back_button);
~HostedAppFrameHeaderAsh() override;
private:
FRIEND_TEST_ALL_PREFIXES(HostedAppNonClientFrameViewAshTest, HostedAppFrame);
// Create a render text for this header.
std::unique_ptr<gfx::RenderText> CreateRenderText();
// Refresh the text inside the render texts.
void UpdateRenderTexts();
// Render the app and domain to the right of the window title, truncating the
// the window title first if the combination doesn't fit.
void LayoutRenderTexts(const gfx::Rect& available_title_bounds,
int title_width,
int app_and_domain_width);
// ash::DefaultFrameHeader:
void PaintTitleBar(gfx::Canvas* canvas) override;
void LayoutHeader() override;
extensions::HostedAppBrowserController* app_controller_; // Weak.
// The render text for the window title.
std::unique_ptr<gfx::RenderText> title_render_text_;
// The render text for the app and domain in the title bar.
std::unique_ptr<gfx::RenderText> app_and_domain_render_text_;
// The name of the app.
const base::string16 app_name_;
// The app and domain string to display in the title bar.
const base::string16 app_and_domain_;
DISALLOW_COPY_AND_ASSIGN(HostedAppFrameHeaderAsh);
};
#endif // CHROME_BROWSER_UI_VIEWS_FRAME_HOSTED_APP_FRAME_HEADER_ASH_H_
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