| // Copyright 2013 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/autofill/autofill_tooltip_controller.h" |
| |
| #include "base/mac/foundation_util.h" |
| #import "chrome/browser/ui/cocoa/autofill/autofill_bubble_controller.h" |
| #import "ui/base/cocoa/base_view.h" |
| #import "ui/base/cocoa/hover_image_button.h" |
| |
| // Delay time before tooltip shows/hides. |
| const NSTimeInterval kTooltipDelay = 0.1; |
| |
| // How far to inset tooltip contents. |
| CGFloat kTooltipInset = 10; |
| |
| #pragma mark AutofillTooltipController - private methods |
| |
| @interface AutofillTooltipController () |
| |
| // Sets hover state for "mouse over InfoBubble". |
| - (void)setHoveringOnBubble:(BOOL)hoveringOnBubble; |
| |
| // Update the combined hover state - if either button or bubble is hovered, |
| // the combined state is considered "hovered". Notifies delegate if the state |
| // changed. |
| - (void)updateTooltipDisplayState; |
| |
| @end |
| |
| #pragma mark AutofillTooltip |
| |
| // The actual tooltip control - based on HoverButton, which comes with free |
| // hover handling. |
| @interface AutofillTooltip : HoverButton { |
| @private |
| // Not owned - |tooltipController_| owns this object. |
| AutofillTooltipController* tooltipController_; |
| } |
| |
| @property(assign, nonatomic) AutofillTooltipController* tooltipController; |
| |
| @end |
| |
| |
| @implementation AutofillTooltip |
| |
| @synthesize tooltipController = tooltipController_; |
| |
| - (void)drawRect:(NSRect)rect { |
| [[self image] drawInRect:rect |
| fromRect:NSZeroRect |
| operation:NSCompositeSourceOver |
| fraction:1.0 |
| respectFlipped:YES |
| hints:nil]; |
| } |
| |
| - (void)setHoverState:(HoverState)state { |
| [super setHoverState:state]; |
| [tooltipController_ updateTooltipDisplayState]; |
| } |
| |
| - (BOOL)acceptsFirstResponder { |
| return NO; |
| } |
| |
| @end |
| |
| #pragma mark AutofillTrackingView |
| |
| // A very basic view that only tracks mouseEntered:/mouseExited: and forwards |
| // them to |tooltipController_|. |
| @interface AutofillTrackingView : BaseView { |
| @private |
| // Not owned - tooltip controller owns tracking view and tooltip. |
| AutofillTooltipController* tooltipController_; |
| } |
| |
| @property(assign, nonatomic) AutofillTooltipController* tooltipController; |
| |
| @end |
| |
| @implementation AutofillTrackingView |
| |
| @synthesize tooltipController = tooltipController_; |
| |
| - (void)mouseEntered:(NSEvent*)theEvent { |
| [tooltipController_ setHoveringOnBubble:YES]; |
| } |
| |
| - (void)mouseExited:(NSEvent*)theEvent { |
| [tooltipController_ setHoveringOnBubble:NO]; |
| } |
| |
| @end |
| |
| #pragma mark AutofillTooltipController |
| |
| @implementation AutofillTooltipController |
| |
| @synthesize message = message_; |
| |
| - (id)initWithArrowLocation:(info_bubble::BubbleArrowLocation)arrowLocation { |
| if ((self = [super init])) { |
| arrowLocation_ = arrowLocation; |
| view_.reset([[AutofillTooltip alloc] init]); |
| [self setView:view_]; |
| [view_ setTooltipController:self]; |
| } |
| return self; |
| } |
| |
| - (void)dealloc { |
| [view_ setTooltipController:nil]; |
| [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| [[NSNotificationCenter defaultCenter] |
| removeObserver:self |
| name:NSWindowWillCloseNotification |
| object:[bubbleController_ window]]; |
| [super dealloc]; |
| } |
| |
| - (void)setImage:(NSImage*)image { |
| [view_ setImage:image]; |
| [view_ setFrameSize:[image size]]; |
| } |
| |
| - (void)tooltipWindowWillClose:(NSNotification*)notification { |
| bubbleController_ = nil; |
| } |
| |
| - (void)displayHover { |
| [bubbleController_ close]; |
| bubbleController_ = |
| [[AutofillBubbleController alloc] |
| initWithParentWindow:[[self view] window] |
| message:[self message] |
| inset:NSMakeSize(kTooltipInset, kTooltipInset) |
| arrowLocation:arrowLocation_]; |
| [bubbleController_ setShouldCloseOnResignKey:NO]; |
| |
| // Handle bubble self-deleting. |
| NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
| [center addObserver:self |
| selector:@selector(tooltipWindowWillClose:) |
| name:NSWindowWillCloseNotification |
| object:[bubbleController_ window]]; |
| |
| // Inject a tracking view so controller can track hover events for the bubble. |
| base::scoped_nsobject<NSView> oldContentView( |
| [[[bubbleController_ window] contentView] retain]); |
| base::scoped_nsobject<AutofillTrackingView> trackingView( |
| [[AutofillTrackingView alloc] initWithFrame:[oldContentView frame]]); |
| [trackingView setTooltipController:self]; |
| [trackingView setAutoresizesSubviews:YES]; |
| [oldContentView setFrame:[trackingView bounds]]; |
| [oldContentView |
| setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; |
| [[bubbleController_ window] setContentView:trackingView]; |
| [trackingView setSubviews:@[ oldContentView ]]; |
| |
| // Compute anchor point (in window coords - views might be flipped). |
| NSRect viewRect = [view_ convertRect:[view_ bounds] toView:nil]; |
| NSPoint anchorPoint = NSMakePoint(NSMidX(viewRect), NSMinY(viewRect)); |
| [bubbleController_ setAnchorPoint: |
| [[[self view] window] convertBaseToScreen:anchorPoint]]; |
| [bubbleController_ showWindow:self]; |
| } |
| |
| - (void)hideHover { |
| [bubbleController_ close]; |
| } |
| |
| - (void)setHoveringOnBubble:(BOOL)hoveringOnBubble { |
| isHoveringOnBubble_ = hoveringOnBubble; |
| [self updateTooltipDisplayState]; |
| } |
| |
| - (void)updateTooltipDisplayState { |
| BOOL newDisplayState = |
| ([view_ hoverState] != kHoverStateNone || isHoveringOnBubble_); |
| |
| if (newDisplayState != shouldDisplayTooltip_) { |
| shouldDisplayTooltip_ = newDisplayState; |
| |
| // Cancel any pending visibility changes. |
| [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| |
| // If the desired visibility disagrees with current visibility, start a |
| // timer to change visibility. (Uses '!!' to force bool values) |
| if (!!bubbleController_ ^ !!shouldDisplayTooltip_) { |
| SEL sel = shouldDisplayTooltip_ ? @selector(displayHover) |
| : @selector(hideHover); |
| [self performSelector:sel withObject:nil afterDelay:kTooltipDelay]; |
| } |
| } |
| } |
| |
| @end |