Commit 3f0e79e9 authored by thakis@chromium.org's avatar thakis@chromium.org

mac: Makeshift UI for scroll feedback.

Heavily based on code by Alexei Svitkine <asvitkine@chromium.org>

BUG=90228
TEST=two-finger history scroll. get some ui feedback.

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@97022 0039d316-1c4b-4281-b951-d872f2087c98
parent da1bd185
......@@ -25,6 +25,7 @@
#include "chrome/browser/spellchecker/spellchecker_platform_engine.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#import "chrome/browser/ui/cocoa/history_overlay_controller.h"
#import "chrome/browser/ui/cocoa/rwhvm_editcommand_helper.h"
#import "chrome/browser/ui/cocoa/view_id_util.h"
#include "chrome/common/render_messages.h"
......@@ -1587,15 +1588,25 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
// that's actually done in the renderer
// (ChromeClientImpl::shouldRubberBand()), when it decides if it should
// rubberband or send back an event unhandled.
// TODO(thakis): Is this true with the UI up?
(isPinnedLeft_ && !isRightScroll) ||
(isPinnedRight_ && isRightScroll))) {
// The way this api works: gestureAmount is between -1 and 1 (float). If
// Released by the tracking handler once the gesture is complete.
HistoryOverlayController* historyOverlay =
[[HistoryOverlayController alloc]
initForMode:goForward ? kHistoryOverlayModeForward :
kHistoryOverlayModeBack];
// The way this api works: gestureAmount is between -1 and 1 (float). If
// the user does the gesture for more than about 25% (i.e. < -0.25 or >
// 0.25) and then lets go, it is accepted, we get a NSEventPhaseEnded,
// and after that the block is called with amounts animating towards 1
// (or -1, depending on the direction). If the user lets go below that
// threshold, we get NSEventPhaseCancelled, and the amount animates
// toward 0.
// toward 0. When gestureAmount has reaches its final value, i.e. the
// track animation is done, the handler is called with |isComplete| set
// to |YES|.
[theEvent trackSwipeEventWithOptions:0
dampenAmountThresholdMin:-1
max:1
......@@ -1603,9 +1614,13 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
NSEventPhase phase,
BOOL isComplete,
BOOL *stop) {
if (phase == NSEventPhaseBegan) {
[historyOverlay showPanelForWindow:[self window]];
return;
}
// |gestureAmount| obeys -[NSEvent isDirectionInvertedFromDevice]
// automatically.
// TODO(thakis): UI.
Browser* browser = BrowserList::GetLastActive();
if (phase == NSEventPhaseEnded && browser) {
if (goForward)
......@@ -1613,6 +1628,12 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
else
browser->GoBack(CURRENT_TAB);
}
[historyOverlay setProgress:gestureAmount];
if (isComplete) {
[historyOverlay dismiss];
[historyOverlay release];
}
}];
return YES;
}
......
// 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.
#ifndef CHROME_BROWSER_UI_COCOA_OVERLAY_PANEL_CONTROLLER_H_
#define CHROME_BROWSER_UI_COCOA_OVERLAY_PANEL_CONTROLLER_H_
#pragma once
#import <Cocoa/Cocoa.h>
#import "base/mac/cocoa_protocols.h"
#include "base/memory/scoped_nsobject.h"
@class OverlayFrameView;
enum HistoryOverlayMode {
kHistoryOverlayModeBack,
kHistoryOverlayModeForward
};
// The HistoryOverlayController manages the overlay HUD panel which displays
// navigation gesture icons within a browser window.
@interface HistoryOverlayController : NSWindowController<NSWindowDelegate> {
@private
HistoryOverlayMode mode_;
// The content view of the window that this controller manages.
OverlayFrameView* contentView_; // Weak, owned by the window.
scoped_nsobject<NSWindow> parent_;
}
// Designated initializer.
- (id)initForMode:(HistoryOverlayMode)mode;
// Shows the panel.
- (void)showPanelForWindow:(NSWindow*)window;
// Updates the appearance of the overlay based on track gesture progress.
- (void)setProgress:(CGFloat)gestureAmount;
// Schedules a fade-out animation and then closes the window,
// which will release the controller.
- (void)dismiss;
@end
#endif // CHROME_BROWSER_UI_COCOA_OVERLAY_PANEL_CONTROLLER_H_
// 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.
#import "chrome/browser/ui/cocoa/history_overlay_controller.h"
#import <QuartzCore/QuartzCore.h>
#include <cmath>
// OverlayFrameView ////////////////////////////////////////////////////////////
// The content view of the window that draws a custom frame.
@interface OverlayFrameView : NSView {
@private
NSTextField* message_; // Weak, owned by the view hierarchy.
}
- (void)setMessageText:(NSString*)text;
@end
// The content view of the window that draws a custom frame.
@implementation OverlayFrameView
- (id)initWithFrame:(NSRect)frameRect {
if ((self = [super initWithFrame:frameRect])) {
scoped_nsobject<NSTextField> message(
// The frame will be fixed up when |-setMessageText:| is called.
[[NSTextField alloc] initWithFrame:NSZeroRect]);
message_ = message.get();
[message_ setEditable:NO];
[message_ setSelectable:NO];
[message_ setBezeled:NO];
[message_ setDrawsBackground:NO];
[message_ setFont:[NSFont boldSystemFontOfSize:72]];
[message_ setTextColor:[NSColor whiteColor]];
[self addSubview:message_];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
const CGFloat kCornerRadius = 5.0;
NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:[self bounds]
xRadius:kCornerRadius
yRadius:kCornerRadius];
NSColor* fillColor = [NSColor colorWithCalibratedWhite:0.2 alpha:0.85];
[fillColor set];
[path fill];
}
- (void)setMessageText:(NSString*)text {
const CGFloat kHorizontalPadding = 30; // In view coordinates.
// Style the string.
scoped_nsobject<NSMutableAttributedString> attrString(
[[NSMutableAttributedString alloc] initWithString:text]);
scoped_nsobject<NSShadow> textShadow([[NSShadow alloc] init]);
[textShadow.get() setShadowColor:[NSColor colorWithCalibratedWhite:0
alpha:0.6]];
[textShadow.get() setShadowOffset:NSMakeSize(0, -1)];
[textShadow setShadowBlurRadius:1.0];
[attrString addAttribute:NSShadowAttributeName
value:textShadow
range:NSMakeRange(0, [text length])];
[message_ setAttributedStringValue:attrString];
// Fix up the frame of the string.
[message_ sizeToFit];
NSRect messageFrame = [message_ frame];
NSRect frameInViewSpace =
[message_ convertRect:[[self window] frame] fromView:nil];
if (NSWidth(messageFrame) > NSWidth(frameInViewSpace))
frameInViewSpace.size.width = NSWidth(messageFrame) + kHorizontalPadding;
messageFrame.origin.x =
(NSWidth(frameInViewSpace) - NSWidth(messageFrame)) / 2;
messageFrame.origin.y =
(NSHeight(frameInViewSpace) - NSHeight(messageFrame)) / 2;
[[self window] setFrame:[message_ convertRect:frameInViewSpace toView:nil]
display:YES];
[message_ setFrame:messageFrame];
}
@end
// HistoryOverlayController ////////////////////////////////////////////////////
@implementation HistoryOverlayController
- (id)initForMode:(HistoryOverlayMode)mode {
const NSRect kWindowFrame = NSMakeRect(0, 0, 120, 70);
scoped_nsobject<NSWindow> window(
[[NSWindow alloc] initWithContentRect:kWindowFrame
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO]);
if ((self = [super initWithWindow:window])) {
mode_ = mode;
[window setDelegate:self];
[window setBackgroundColor:[NSColor clearColor]];
[window setOpaque:NO];
[window setHasShadow:NO];
// Create the content view. Take the frame from the existing content view.
NSRect frame = [[window contentView] frame];
scoped_nsobject<OverlayFrameView> frameView(
[[OverlayFrameView alloc] initWithFrame:frame]);
contentView_ = frameView.get();
[window setContentView:contentView_];
const unichar kBackArrowCharacter = 0x2190;
const unichar kForwardArrowCharacter = 0x2192;
unichar commandChar = mode_ == kHistoryOverlayModeForward ?
kForwardArrowCharacter : kBackArrowCharacter;
NSString* text =
[NSString stringWithCharacters:&commandChar length:1];
[contentView_ setMessageText:text];
}
return self;
}
- (void)setProgress:(CGFloat)gestureAmount {
const CGFloat kVerticalPositionRatio = 0.65;
NSRect windowFrame = [parent_ frame];
CGFloat minX = NSMinX(windowFrame);
CGFloat maxX = NSMaxX(windowFrame) - NSWidth([[self window] frame]);
CGFloat x = 0;
if (mode_ == kHistoryOverlayModeForward)
x = maxX + gestureAmount * (maxX - minX);
else if (mode_ == kHistoryOverlayModeBack)
x = minX + gestureAmount * (maxX - minX);
NSPoint p = [parent_ frame].origin;
p.x = x;
p.y += (NSHeight(windowFrame) - NSHeight([[self window] frame])) *
kVerticalPositionRatio;
[[self window] setFrameOrigin:p];
CGFloat alpha =
-(std::abs(gestureAmount) - 1) * (std::abs(gestureAmount) - 1) + 1;
[[self window] setAlphaValue:alpha];
}
- (void)dismiss {
const CGFloat kFadeOutDurationSeconds = 0.2;
NSWindow* overlayWindow = [self window];
scoped_nsobject<CAAnimation> animation(
[[overlayWindow animationForKey:@"alphaValue"] copy]);
[animation setDelegate:self];
[animation setDuration:kFadeOutDurationSeconds];
NSMutableDictionary* dictionary =
[NSMutableDictionary dictionaryWithCapacity:1];
[dictionary setObject:animation forKey:@"alphaValue"];
[overlayWindow setAnimations:dictionary];
[[overlayWindow animator] setAlphaValue:0.0];
}
- (void)windowWillClose:(NSNotification*)notification {
// Release all animations because CAAnimation retains its delegate (self),
// which will cause a retain cycle. Break it!
[[self window] setAnimations:[NSDictionary dictionary]];
}
- (void)showPanelForWindow:(NSWindow*)window {
parent_.reset([window retain]);
[self setProgress:0]; // Set initial window position.
[self showWindow:self];
}
- (void)animationDidStop:(CAAnimation*)theAnimation finished:(BOOL)finished {
[self close];
}
@end
......@@ -2510,6 +2510,8 @@
'browser/ui/cocoa/history_menu_bridge.mm',
'browser/ui/cocoa/history_menu_cocoa_controller.h',
'browser/ui/cocoa/history_menu_cocoa_controller.mm',
'browser/ui/cocoa/history_overlay_controller.h',
'browser/ui/cocoa/history_overlay_controller.mm',
'browser/ui/cocoa/hover_button.h',
'browser/ui/cocoa/hover_button.mm',
'browser/ui/cocoa/hover_close_button.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