Commit 6f655324 authored by estade@chromium.org's avatar estade@chromium.org

ntp4 info bubble

displays a bubble pointing at "Most Visited"; this is dismissed after 10 displays (try opening the ntp 11 times), or after the user clicks a navigation dot. See bug for more background.

BUG=92434
TEST=manual


Review URL: http://codereview.chromium.org/7461160

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96680 0039d316-1c4b-4281-b951-d872f2087c98
parent c903db11
......@@ -342,6 +342,9 @@ be available for now. -->
desc="Title for the hard-coded thumbnail that represents the Google Chrome Welcome page. This is used on the NTP when there aren't enough thumbnails to show.">
Welcome to Chromium
</message>
<message name="IDS_NTP4_INTRO_MESSAGE" desc="Message explaning the navigation bar at the bottom of the new NTP.">
Chromium's New Tab page has been remodeled. Switch between sections by clicking these labels. Chrome will remember your preference for next time.
</message>
<if expr="not pp_ifdef('chromeos')">
<message name="IDS_EXTERNAL_PROTOCOL_INFORMATION" desc="General information about what Chrome is trying to do when opening this external protocol">
Chromium needs to launch an external application to handle <ph name="SCHEME">$1<ex>acrobat:</ex></ph> links. The link requested is <ph name="PROTOLINK">$2<ex>acrobat:yourpdf</ex></ph>.
......
......@@ -320,6 +320,9 @@ Chrome supports. -->
desc="Title for the hard-coded thumbnail that represents the Google Chrome Welcome page. This is used on the NTP when there aren't enough thumbnails to show.">
Welcome to Google Chrome
</message>
<message name="IDS_NTP4_INTRO_MESSAGE" desc="Message explaning the navigation bar at the bottom of the new NTP.">
Chrome's New Tab page has been remodeled. Switch between sections by clicking these labels. Chrome will remember your preference for next time.
</message>
<if expr="not pp_ifdef('chromeos')">
<message name="IDS_EXTERNAL_PROTOCOL_INFORMATION" desc="General information about what Chrome is trying to do when opening this external protocol">
Google Chrome needs to launch an external application to handle <ph name="SCHEME">$1<ex>acrobat:</ex></ph> links. The link requested is <ph name="PROTOLINK">$2<ex>acrobat:yourpdf</ex></ph>.
......
......@@ -96,6 +96,8 @@ cr.define('ntp4', function() {
// handling in onInputMouseDown_.
if (this.ownerDocument.activeElement != this.input_)
this.focus();
chrome.send('navigationDotUsed');
e.stopPropagation();
},
......
......@@ -24,6 +24,7 @@ document.documentElement.setAttribute('touchui', true);
</script>
</if>
<link rel="stylesheet" href="../shared/css/bubble.css">
<link rel="stylesheet" href="../shared/css/menu.css">
<link rel="stylesheet" href="apps_page.css">
<link rel="stylesheet" href="bookmarks_page.css">
......@@ -40,6 +41,7 @@ document.documentElement.setAttribute('touchui', true);
<script src="../shared/js/cr.js"></script>
<script src="../shared/js/cr/ui.js"></script>
<script src="../shared/js/cr/ui/bubble.js"></script>
<script src="../shared/js/cr/ui/menu.js"></script>
<script src="../shared/js/cr/ui/menu_item.js"></script>
<script src="../shared/js/cr/ui/position_util.js"></script>
......
......@@ -109,6 +109,14 @@ cr.define('ntp4', function() {
*/
var highlightAppId = null;
/**
* If non-null, an info bubble for showing messages to the user. It points at
* the Most Visited label, and is used to draw more attention to the
* navigation dot UI.
* @type {!Element|undefined}
*/
var infoBubble;
/**
* The time in milliseconds for most transitions. This should match what's
* in new_tab.css. Unfortunately there's no better way to try to time
......@@ -187,6 +195,15 @@ cr.define('ntp4', function() {
appendTilePage(mostVisitedPage, localStrings.getString('mostvisited'));
chrome.send('getMostVisited');
if (localStrings.getString('ntp4_intro_message')) {
infoBubble = new cr.ui.Bubble;
infoBubble.anchorNode = mostVisitedPage.navigationDot;
infoBubble.text = localStrings.getString('ntp4_intro_message');
infoBubble.show();
chrome.send('introMessageSeen');
}
bookmarksPage = new ntp4.BookmarksPage();
appendTilePage(bookmarksPage, localStrings.getString('bookmarksPage'));
chrome.send('getBookmarks');
......@@ -406,6 +423,8 @@ cr.define('ntp4', function() {
dotList.appendChild(newDot);
page.navigationDot = newDot;
if (infoBubble)
window.setTimeout(infoBubble.reposition.bind(infoBubble), 0);
eventTracker.add(page, 'pagelayout', onPageLayout);
}
......@@ -594,9 +613,6 @@ cr.define('ntp4', function() {
} else if (page.classList.contains('bookmarks-page')) {
shownPage = templateData['bookmarks_page_id'];
shownPageIndex = 0;
} else if (page.classList.contains('bookmarks-page')) {
shownPage = templateData['bookmarks_page_id'];
shownPageIndex = 0;
} else {
console.error('unknown page selected');
}
......
/* Copyright (c) 2011 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.
*/
.bubble {
/* These margins correlate to the difference between the tip of the arrow
* and the bottom left of the bubble. */
margin-left: -30px;
margin-top: -8px;
position: absolute;
/* Height is dynamic, width fixed. */
width: 200px;
z-index: 9999;
}
.bubble-contents {
left: 1px;
padding: 10px;
position: relative;
text-align: left;
top: 1px;
width: 198px;
z-index: 3;
}
.bubble-shadow {
bottom: -2px;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}
.bubble-arrow {
/* The tip of the arrow. */
border-bottom-right-radius: 1px;
/* No border on the right or top (inner sides of the rotated square) because
* it would overlap/darken the content shadow. */
border-right: none;
border-top: none;
height: 15px;
left: 22px;
position: absolute;
bottom: -9px;
width: 15px;
z-index: 2;
-webkit-transform: rotate(45deg);
}
.bubble-contents,
.bubble-arrow {
background: white;
}
.bubble-shadow,
.bubble-arrow {
border: 1px solid rgba(0, 0, 0, 0.3);
-webkit-box-shadow: -2px 2px 4px rgba(0, 0, 0, 0.3);
}
.bubble-shadow,
.bubble-contents {
border-radius: 6px;
box-sizing: border-box;
}
// Copyright (c) 2011 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.
// require: event_tracker.js
cr.define('cr.ui', function() {
/**
* Bubble is a free-floating informational bubble with a triangular arrow
* that points at a place of interest on the page. Currently the arrow is
* always positioned at the bottom left and points down.
*/
var Bubble = cr.ui.define('div');
Bubble.prototype = {
__proto__: HTMLDivElement.prototype,
decorate: function() {
this.className = 'bubble';
this.innerHTML =
'<div class=\"bubble-contents\"></div>' +
'<div class=\"bubble-shadow\"></div>' +
'<div class=\"bubble-arrow\"></div>';
this.hidden = true;
},
/**
* Sets the text message within the bubble.
* @param {String} text The message string.
*/
set text(text) {
this.querySelector('.bubble-contents').textContent = text;
},
/**
* Sets the anchor node, i.e. the node that this bubble points at.
* @param {HTMLElement} node The new anchor node.
*/
set anchorNode(node) {
this.anchorNode_ = node;
if (!this.hidden)
reposition();
},
/**
* Updates the position of the bubble. This is automatically called when
* the window is resized, but should also be called any time the layout
* may have changed.
*/
reposition: function() {
var node = this.anchorNode_;
var clientRect = node.getBoundingClientRect();
this.style.left = (clientRect.left + clientRect.right) / 2 + 'px';
this.style.top = (clientRect.top - this.clientHeight) + 'px';
},
/**
* Starts showing the bubble. The bubble will grab input and show until the
* user clicks away.
*/
show: function() {
if (!this.hidden)
return;
document.body.appendChild(this);
this.hidden = false;
this.reposition();
this.eventTracker_ = new EventTracker;
this.eventTracker_.add(window, 'resize', this.reposition.bind(this));
var doc = this.ownerDocument;
this.eventTracker_.add(doc, 'keydown', this, true);
this.eventTracker_.add(doc, 'mousedown', this, true);
},
/**
* Hides the bubble from view.
*/
hide: function() {
this.hidden = true;
this.eventTracker_.removeAll();
this.parentNode.removeChild(this);
},
/**
* Handles keydown and mousedown events, dismissing the bubble if
* necessary.
* @param {Event} e The event.
*/
handleEvent: function(e) {
switch (e.type) {
case 'keydown':
if (e.keyCode == 27) // Esc
this.hide();
break;
case 'mousedown':
if (!this.contains(e.target))
this.hide();
break;
}
e.stopPropagation();
e.preventDefault();
return;
},
};
return {
Bubble: Bubble
};
});
......@@ -11,6 +11,10 @@
#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
#include "chrome/common/chrome_notification_types.h"
#include "content/common/notification_service.h"
#include "grit/chromium_strings.h"
#include "ui/base/l10n/l10n_util.h"
static const int kIntroDisplayMax = 10;
void NewTabPageHandler::RegisterMessages() {
web_ui_->RegisterMessageCallback("closePromo", NewCallback(
......@@ -19,6 +23,10 @@ void NewTabPageHandler::RegisterMessages() {
this, &NewTabPageHandler::HandleCloseSyncNotification));
web_ui_->RegisterMessageCallback("pageSelected", NewCallback(
this, &NewTabPageHandler::HandlePageSelected));
web_ui_->RegisterMessageCallback("navigationDotUsed", NewCallback(
this, &NewTabPageHandler::HandleNavDotUsed));
web_ui_->RegisterMessageCallback("introMessageSeen", NewCallback(
this, &NewTabPageHandler::HandleIntroMessageSeen));
}
void NewTabPageHandler::HandleClosePromo(const ListValue* args) {
......@@ -50,11 +58,24 @@ void NewTabPageHandler::HandlePageSelected(const ListValue* args) {
prefs->SetInteger(prefs::kNTPShownPage, page_id | index);
}
void NewTabPageHandler::HandleNavDotUsed(const ListValue* args) {
PrefService* prefs = Profile::FromWebUI(web_ui_)->GetPrefs();
prefs->SetInteger(prefs::kNTP4IntroDisplayCount, kIntroDisplayMax + 1);
}
void NewTabPageHandler::HandleIntroMessageSeen(const ListValue* args) {
PrefService* prefs = Profile::FromWebUI(web_ui_)->GetPrefs();
int intro_displays = prefs->GetInteger(prefs::kNTP4IntroDisplayCount);
prefs->SetInteger(prefs::kNTP4IntroDisplayCount, intro_displays + 1);
}
// static
void NewTabPageHandler::RegisterUserPrefs(PrefService* prefs) {
// TODO(estade): should be syncable.
prefs->RegisterIntegerPref(prefs::kNTPShownPage, APPS_PAGE_ID,
PrefService::UNSYNCABLE_PREF);
prefs->RegisterIntegerPref(prefs::kNTP4IntroDisplayCount, 0,
PrefService::UNSYNCABLE_PREF);
}
// static
......@@ -71,4 +92,10 @@ void NewTabPageHandler::GetLocalizedValues(Profile* profile,
int shown_page = prefs->GetInteger(prefs::kNTPShownPage);
values->SetInteger("shown_page_type", shown_page & ~INDEX_MASK);
values->SetInteger("shown_page_index", shown_page & INDEX_MASK);
int intro_displays = prefs->GetInteger(prefs::kNTP4IntroDisplayCount);
if (intro_displays <= kIntroDisplayMax) {
values->SetString("ntp4_intro_message",
l10n_util::GetStringUTF16(IDS_NTP4_INTRO_MESSAGE));
}
}
......@@ -30,6 +30,15 @@ class NewTabPageHandler : public WebUIMessageHandler {
// Callback for "pageSelected".
void HandlePageSelected(const ListValue* args);
// Callback for "navigationDotUsed". This is called when a navigation dot is
// clicked, whereas pageSelected might be called after any page-switching user
// action.
void HandleNavDotUsed(const ListValue* args);
// Callback for "handleIntroMessageSeen". No arguments. Called when the intro
// message is displayed.
void HandleIntroMessageSeen(const ListValue* args);
// Register NTP preferences.
static void RegisterUserPrefs(PrefService* prefs);
......
......@@ -182,6 +182,7 @@ NTPResourceCache::NTPResourceCache(Profile* profile) : profile_(profile) {
pref_change_registrar_.Add(prefs::kHomePageIsNewTabPage, this);
pref_change_registrar_.Add(prefs::kNTPShownSections, this);
pref_change_registrar_.Add(prefs::kNTPShownPage, this);
pref_change_registrar_.Add(prefs::kNTP4IntroDisplayCount, this);
}
NTPResourceCache::~NTPResourceCache() {}
......
......@@ -1121,6 +1121,9 @@ const char kExtensionBlacklistUpdateVersion[] =
const char kExtensionSidebarWidth[] = "extensions.sidebar.width";
// Number of times the NTP4 informational bubble has been shown.
const char kNTP4IntroDisplayCount[] = "ntp.intro_display_count";
// New Tab Page URLs that should not be shown as most visited thumbnails.
const char kNTPMostVisitedURLsBlacklist[] = "ntp.most_visited_blacklist";
......
......@@ -394,6 +394,7 @@ extern const char kExtensionSidebarWidth[];
extern const char kNTPTipsResourceServer[];
extern const char kNTP4IntroDisplayCount[];
extern const char kNTPMostVisitedURLsBlacklist[];
extern const char kNTPMostVisitedPinnedURLs[];
extern const char kNTPPromoResourceCache[];
......
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