diff options
author | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-15 18:29:45 +0000 |
---|---|---|
committer | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-15 18:29:45 +0000 |
commit | 1b5d23dfa22d8858dc9ab35d373fcf328aadbcba (patch) | |
tree | c0e3b61de8b3a7037ec3b135211a2a974879d134 | |
parent | 055a9406124ab9642e444d07fad1b98b1485bf86 (diff) | |
download | chromium_src-1b5d23dfa22d8858dc9ab35d373fcf328aadbcba.zip chromium_src-1b5d23dfa22d8858dc9ab35d373fcf328aadbcba.tar.gz chromium_src-1b5d23dfa22d8858dc9ab35d373fcf328aadbcba.tar.bz2 |
Make tooltips work correctly, allowing for multiple tooltips w/out the mouse leaving the view (which is all NSView can handle by itself). Adds some Camino code to do so.
BUG=13995
TEST=tooltips in web pages, pages with multiple tooltips, expose and spaces.
Review URL: http://codereview.chromium.org/125133
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18408 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_mac.h | 6 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_widget_host_view_mac.mm | 40 | ||||
-rw-r--r-- | chrome/chrome.gyp | 4 | ||||
-rw-r--r-- | third_party/mozilla/include/NSScreen+Utils.h | 45 | ||||
-rw-r--r-- | third_party/mozilla/include/NSScreen+Utils.m | 59 | ||||
-rw-r--r-- | third_party/mozilla/include/ToolTip.h | 57 | ||||
-rw-r--r-- | third_party/mozilla/include/ToolTip.mm | 209 |
7 files changed, 413 insertions, 7 deletions
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.h b/chrome/browser/renderer_host/render_widget_host_view_mac.h index b663cf4..ab97232 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.h +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.h @@ -7,6 +7,7 @@ #import <Cocoa/Cocoa.h> +#include "base/scoped_nsobject.h" #include "base/task.h" #include "base/time.h" #include "chrome/browser/cocoa/base_view.h" @@ -14,6 +15,7 @@ #include "webkit/glue/webcursor.h" class RenderWidgetHostViewMac; +@class ToolTip; // This is the view that lives in the Cocoa view hierarchy. In Windows-land, // RenderWidgetHostViewWin is both the view and the delegate. We split the roles @@ -133,6 +135,10 @@ class RenderWidgetHostViewMac : public RenderWidgetHostView { // Tooltips // The text to be shown in the tooltip, supplied by the renderer. std::wstring tooltip_text_; + // Used to display tooltips. We can't use the [NSView -setToolTip:] methods + // because we need to be able to show and hide the tooltip without the mouse + // leaving a region and NSView isn't set up for that to happen. + scoped_nsobject<ToolTip> tooltip_; // Factory used to safely scope delayed calls to ShutdownHost(). ScopedRunnableMethodFactory<RenderWidgetHostViewMac> shutdown_factory_; diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm index 073fb73..23fe34c 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm +++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm @@ -12,6 +12,7 @@ #include "chrome/browser/renderer_host/render_widget_host.h" #include "chrome/common/native_web_keyboard_event.h" #include "skia/ext/platform_canvas.h" +#import "third_party/mozilla/include/ToolTip.h" #include "webkit/api/public/mac/WebInputEventFactory.h" #include "webkit/api/public/WebInputEvent.h" #include "webkit/glue/webmenurunner_mac.h" @@ -51,6 +52,7 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget) parent_view_(NULL) { cocoa_view_ = [[[RenderWidgetHostViewCocoa alloc] initWithRenderWidgetHostViewMac:this] autorelease]; + tooltip_.reset([[ToolTip alloc] init]); render_widget_host_->set_view(this); } @@ -251,17 +253,41 @@ void RenderWidgetHostViewMac::Destroy() { [cocoa_view_ autorelease]; } +// Called from the renderer to tell us what the tooltip text should be. It +// calls us frequently so we need to cache the value to prevent doing a lot +// of repeat work. We cannot simply use [-NSView setToolTip:] because NSView +// can't handle the case where the tooltip text changes while the mouse is +// still inside the view. Since the page elements that get tooltips are all +// contained within this view (and are unknown to the NSView system), we +// are forced to implement our own tooltips with child windows. +// TODO(pinkerton): Do we want these tooltips to time out after a certain time? +// Gecko does this automatically in the back-end, hence the ToolTip class not +// needing that functionality. We can either modify ToolTip or add this +// functionality here with a timer. void RenderWidgetHostViewMac::SetTooltipText(const std::wstring& tooltip_text) { if (tooltip_text != tooltip_text_) { tooltip_text_ = tooltip_text; // Clamp the tooltip length to kMaxTooltipLength. It's a DOS issue on - // Windows; we're just trying to be polite. + // Windows; we're just trying to be polite. Don't persist the trimmed + // string, as then the comparison above will always fail and we'll try to + // set it again every single time the mouse moves. + std::wstring display_text = tooltip_text_; if (tooltip_text_.length() > kMaxTooltipLength) - tooltip_text_ = tooltip_text_.substr(0, kMaxTooltipLength); - - NSString* tooltip_nsstring = base::SysWideToNSString(tooltip_text_); - [cocoa_view_ setToolTip:tooltip_nsstring]; + display_text = tooltip_text_.substr(0, kMaxTooltipLength); + + NSString* tooltip_nsstring = base::SysWideToNSString(display_text); + if ([tooltip_nsstring length] == 0) { + [tooltip_ closeToolTip]; + } else { + // Get the current mouse location in the window's coordinate system and + // use that as the point for displaying the tooltip. + NSPoint event_point = + [[cocoa_view_ window] mouseLocationOutsideOfEventStream]; + [tooltip_ showToolTipAtPoint:event_point + withString:tooltip_nsstring + overWindow:[cocoa_view_ window]]; + } } } @@ -459,7 +485,7 @@ void RenderWidgetHostViewMac::ShutdownHost() { // We're dead, so becoming first responder is probably a bad idea. return NO; } - + renderWidgetHostView_->render_widget_host_->Focus(); return YES; } @@ -470,7 +496,7 @@ void RenderWidgetHostViewMac::ShutdownHost() { // idea. return YES; } - + if (closeOnDeactivate_) renderWidgetHostView_->KillSelf(); diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index bed25c9..4977443 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1722,8 +1722,12 @@ '../third_party/GTM/AppKit/GTMUILocalizer.h', '../third_party/GTM/AppKit/GTMUILocalizer.m', # Build necessary Mozilla sources + '../third_party/mozilla/include/NSScreen+Utils.h', + '../third_party/mozilla/include/NSScreen+Utils.m', '../third_party/mozilla/include/NSWorkspace+Utils.h', '../third_party/mozilla/include/NSWorkspace+Utils.m', + '../third_party/mozilla/include/ToolTip.h', + '../third_party/mozilla/include/ToolTip.mm', ], 'include_dirs': [ '../third_party/GTM', diff --git a/third_party/mozilla/include/NSScreen+Utils.h b/third_party/mozilla/include/NSScreen+Utils.h new file mode 100644 index 0000000..d8dc23c --- /dev/null +++ b/third_party/mozilla/include/NSScreen+Utils.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#import <Cocoa/Cocoa.h> + + +@interface NSScreen (CHScreenAdditions) + ++ (NSScreen*)screenForPoint:(NSPoint)point; + +@end diff --git a/third_party/mozilla/include/NSScreen+Utils.m b/third_party/mozilla/include/NSScreen+Utils.m new file mode 100644 index 0000000..8d3318d --- /dev/null +++ b/third_party/mozilla/include/NSScreen+Utils.m @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +#import "NSScreen+Utils.h" + + +@implementation NSScreen (CHScreenAdditions) + ++ (NSScreen*)screenForPoint:(NSPoint)point +{ + NSArray* screens = [NSScreen screens]; + NSEnumerator* screenEnum = [screens objectEnumerator]; + NSScreen* screen; + + while ( (screen = [screenEnum nextObject]) ) { + NSRect frame = [screen frame]; + if (NSPointInRect(point, frame)) + break; + } + + return screen; +} + +@end diff --git a/third_party/mozilla/include/ToolTip.h b/third_party/mozilla/include/ToolTip.h new file mode 100644 index 0000000..add3491 --- /dev/null +++ b/third_party/mozilla/include/ToolTip.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Richard Schreyer + * Josh Aas <josh@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#import <Cocoa/Cocoa.h> + +@class ToolTipView; + +@interface ToolTip : NSObject +{ + NSWindow* mTooltipWindow; + NSTextField* mTextField; + NSTextView* mTextView; +} + +// Display a tooltip at |point| (in window coordinates) in |inWindow|. +- (void)showToolTipAtPoint:(NSPoint)point + withString:(NSString*)string + overWindow:(NSWindow*)inWindow; +- (void)closeToolTip; + +@end diff --git a/third_party/mozilla/include/ToolTip.mm b/third_party/mozilla/include/ToolTip.mm new file mode 100644 index 0000000..2f45be1 --- /dev/null +++ b/third_party/mozilla/include/ToolTip.mm @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Richard Schreyer + * Josh Aas <josh@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#import "ToolTip.h" +#import "NSScreen+Utils.h" + +@interface ToolTip (ToolTipPrivateMethods) + +- (void)parentWindowDidResignKey:(NSNotification*)inNotification; + +@end + +const float kBorderPadding = 2.0; +const float kMaxTextFieldWidth = 250.0; +const float kVOffset = 20.0; + +@implementation ToolTip + +- (id)init +{ + if ((self = [super init])) + { + // the ref from -alloc is balanced by the -release in dealloc + mTooltipWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0.0, 0.0, kMaxTextFieldWidth, 0.0) + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:YES]; + + // we don't want closing the window to release it, because we aren't always in control + // of the close (AppKit may do it on quit). + [mTooltipWindow setReleasedWhenClosed:NO]; + + // Create a textfield as the content of our new window. + // Field occupies all but the top 2 and bottom 2 pixels of the panel (bug 149635) + mTextView = [[NSTextView alloc] initWithFrame:NSMakeRect(0.0, kBorderPadding, kMaxTextFieldWidth, 0.0)]; + [[mTooltipWindow contentView] addSubview:mTextView]; + [mTextView release]; // window holds ref + + // set up the panel + [mTooltipWindow setHasShadow:YES]; + [mTooltipWindow setBackgroundColor:[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:0.81 alpha:1.0]]; + + + // set up the text view + [mTextView setDrawsBackground:NO]; + [mTextView setEditable:NO]; + [mTextView setSelectable:NO]; + [mTextView setFont:[NSFont toolTipsFontOfSize:[NSFont smallSystemFontSize]]]; + [mTextView setMinSize:NSMakeSize(0.0, 0.0)]; + [mTextView setHorizontallyResizable:YES]; + [mTextView setVerticallyResizable:YES]; + } + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [mTooltipWindow close]; // we set the window not to release on -close + [mTooltipWindow release]; + + [super dealloc]; +} + +- (void)showToolTipAtPoint:(NSPoint)windowPoint + withString:(NSString*)string + overWindow:(NSWindow*)inWindow +{ + if ([string length] == 0) + return; + + NSPoint point = [inWindow convertBaseToScreen:windowPoint]; + NSScreen* screen = [NSScreen screenForPoint:point]; + if (!screen) + screen = [NSScreen mainScreen]; + + if (!screen) + return; + + // register for window losing key status notifications, so we can hide the tooltip + // on window deactivation + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(parentWindowDidResignKey:) + name:NSWindowDidResignKeyNotification + object:inWindow]; + + NSRect screenFrame = [screen visibleFrame]; + NSSize screenSize = screenFrame.size; + + // for some reason, text views suffer from hysteresis; the answer you get this time + // depends on what you had in there before. so clear state first. + [mTextView setString:@""]; + [mTextView setFrame:NSMakeRect(0, kBorderPadding, 0, 0)]; + + // -sizeToFit sucks. For some reason it likes to wrap short words, so + // we measure the text by hand and set that as the min width. + NSSize stringSize = [string sizeWithAttributes:[NSDictionary dictionaryWithObject:[NSFont toolTipsFontOfSize:[NSFont smallSystemFontSize]] forKey:NSFontAttributeName]]; + float textViewWidth = ceil(stringSize.width); + if (textViewWidth > kMaxTextFieldWidth) + textViewWidth = kMaxTextFieldWidth; + + textViewWidth += 2.0 * 5.0; // magic numbers required to make the text not wrap. No, this isn't -textContainerInset. + + // set up the text view + [mTextView setMaxSize:NSMakeSize(kMaxTextFieldWidth, screenSize.height - 2 * kBorderPadding)]; // do this here since we know screen size + [mTextView setString:string]; // do this after setting max size, before setting constrained frame size, + // reset to max width - it will not grow horizontally when resizing, only vertically + [mTextView setConstrainedFrameSize:NSMakeSize(kMaxTextFieldWidth, 0.0)]; + // to avoid wrapping when we don't want it, set the min width + [mTextView setMinSize:NSMakeSize(textViewWidth, 0.0)]; + + // finally, do the buggy sizeToFit + [mTextView sizeToFit]; + // The first time we sizeToFit a text field on Leopard, it decides that + // 0 would be a good height. We disagree, so make it try again. + NSRect textViewFrame = [mTextView frame]; + if (textViewFrame.size.height < 1.0) { + [mTextView sizeToFit]; + textViewFrame = [mTextView frame]; + } + + // set the origin back where its supposed to be + [mTextView setFrameOrigin:NSMakePoint(0, kBorderPadding)]; + + // size the panel correctly, taking border into account + NSSize textSize = textViewFrame.size; + textSize.height += kBorderPadding + kBorderPadding; + [mTooltipWindow setContentSize:textSize]; + + // We try to put the top left point right below the cursor. If that doesn't fit + // on screen, put the bottom left point above the cursor. + if (point.y - kVOffset - textSize.height > NSMinY(screenFrame)) { + point.y -= kVOffset; + [mTooltipWindow setFrameTopLeftPoint:point]; + } + else { + point.y += kVOffset / 2.5; + [mTooltipWindow setFrameOrigin:point]; + } + + // if it doesn't fit on screen horizontally, adjust so that it does + float amountOffScreenX = NSMaxX(screenFrame) - NSMaxX([mTooltipWindow frame]); + if (amountOffScreenX < 0) { + NSRect movedFrame = [mTooltipWindow frame]; + movedFrame.origin.x += amountOffScreenX; + [mTooltipWindow setFrame:movedFrame display:NO]; + } + + // add as a child window + [inWindow addChildWindow:mTooltipWindow ordered:NSWindowAbove]; + // show the panel + [mTooltipWindow orderFront:nil]; +} + +- (void)closeToolTip +{ + // we can get -closeToolTip even if we didn't show it + if ([mTooltipWindow isVisible]) + { + [[mTooltipWindow parentWindow] removeChildWindow:mTooltipWindow]; + [mTooltipWindow orderOut:nil]; + } + + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)parentWindowDidResignKey:(NSNotification*)inNotification +{ + [self closeToolTip]; +} + +@end |