From 3ddf6031fbb932577ed9b979d59f2c30f5d5df5e Mon Sep 17 00:00:00 2001 From: Antoine Mercadal Date: Tue, 26 Apr 2011 14:46:30 +0200 Subject: [PATCH] Support for tooltips on CPControls --- AppKit/AppKit.j | 1 + AppKit/CPAttachedWindow.j | 394 ++++++++++++++++++ AppKit/CPControl.j | 102 ++++- AppKit/CPToolTip.j | 117 ++++++ AppKit/CPToolbar.j | 4 + AppKit/CPWindow/CPWindow.j | 1 + AppKit/CPWindow/_CPAttachedWindowView.j | 241 +++++++++++ ...tached-window-button-close-highlighted.png | Bin 0 -> 1377 bytes .../attached-window-button-close.png | Bin 0 -> 1375 bytes Tests/Manual/CPToolTip/AppController.j | 67 +++ Tests/Manual/CPToolTip/Info.plist | 12 + Tests/Manual/CPToolTip/Jakefile | 93 +++++ Tests/Manual/CPToolTip/Resources/spinner.gif | Bin 0 -> 1849 bytes Tests/Manual/CPToolTip/Resources/test.png | Bin 0 -> 2472 bytes Tests/Manual/CPToolTip/index-debug.html | 103 +++++ Tests/Manual/CPToolTip/index.html | 78 ++++ Tests/Manual/CPToolTip/main.j | 18 + 17 files changed, 1221 insertions(+), 10 deletions(-) create mode 100644 AppKit/CPAttachedWindow.j create mode 100644 AppKit/CPToolTip.j create mode 100644 AppKit/CPWindow/_CPAttachedWindowView.j create mode 100644 AppKit/Resources/CPAttachedWindow/attached-window-button-close-highlighted.png create mode 100644 AppKit/Resources/CPAttachedWindow/attached-window-button-close.png create mode 100644 Tests/Manual/CPToolTip/AppController.j create mode 100644 Tests/Manual/CPToolTip/Info.plist create mode 100644 Tests/Manual/CPToolTip/Jakefile create mode 100644 Tests/Manual/CPToolTip/Resources/spinner.gif create mode 100644 Tests/Manual/CPToolTip/Resources/test.png create mode 100644 Tests/Manual/CPToolTip/index-debug.html create mode 100644 Tests/Manual/CPToolTip/index.html create mode 100644 Tests/Manual/CPToolTip/main.j diff --git a/AppKit/AppKit.j b/AppKit/AppKit.j index e8feb4dd0..56ba2227a 100644 --- a/AppKit/AppKit.j +++ b/AppKit/AppKit.j @@ -87,6 +87,7 @@ @import "CPTokenField.j" @import "CPToolbar.j" @import "CPToolbarItem.j" +@import "CPToolTip.j" @import "CPTreeNode.j" @import "CPView.j" @import "CPViewAnimation.j" diff --git a/AppKit/CPAttachedWindow.j b/AppKit/CPAttachedWindow.j new file mode 100644 index 000000000..b08833c4a --- /dev/null +++ b/AppKit/CPAttachedWindow.j @@ -0,0 +1,394 @@ +/* + * CPAttachedWindow.j + * AppKit + * + * Created by Antoine Mercadal + * Copyright 2009, Antoine Mercadal + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +@import "CPButton.j" +@import "CPColor.j" +@import "CPImage.j" +@import "CPImageView.j" +@import "CPView.j" +@import "CPWindow.j" + + +CPClosableOnBlurWindowMask = 1 << 4; +CPAttachedWhiteWindowMask = 1 << 25; +CPAttachedBlackWindowMask = 1 << 26; + +/*! @ingroup appkit + This is a simple attached window like the one that pops up + when you double click on a meeting in iCal +*/ +@implementation CPAttachedWindow : CPWindow +{ + id _targetView @accessors(property=targetView); + BOOL _isClosed; + BOOL _closeOnBlur; + + CPButton _closeButton; +} + +/*! override default windowView class loader + @param aStyleMask the window mask + @return the windowView class +*/ + ++ (Class)_windowViewClassForStyleMask:(unsigned)aStyleMask +{ + if (aStyleMask & CPAttachedWhiteWindowMask) + return _CPAttachedWindowViewWhite; + + return _CPAttachedWindowViewBlack; +} + + +#pragma mark - +#pragma mark Initialization + +/*! create and init a CPAttachedWindow with given size of and view + @param aSize the size of the attached window + @param aView the target view + @return ready to use CPAttachedWindow +*/ ++ (id)attachedWindowWithSize:(CGSize)aSize forView:(CPView)aView +{ + return [CPAttachedWindow attachedWindowWithSize:aSize forView:aView styleMask:nil]; +} + +/*! create and init a CPAttachedWindow with given size of and view + @param aSize the size of the attached window + @param aView the target view + @return ready to use CPAttachedWindow + @param styleMask the window style mask (combine CPClosableWindowMask, CPAttachedWhiteWindowMask, CPAttachedBlackWindowMask and CPClosableOnBlurWindowMask) +*/ ++ (id)attachedWindowWithSize:(CGSize)aSize forView:(CPView)aView styleMask:(int)aMask +{ + var attachedWindow = [[CPAttachedWindow alloc] initWithContentRect:CPRectMake(0.0, 0.0, aSize.width, aSize.height) styleMask:aMask]; + + [attachedWindow attachToView:aView]; + + return attachedWindow; +} + +/*! create and init a CPAttachedWindow with given frame + @param aFrame the frame of the attached window + @return ready to use CPAttachedWindow +*/ +- (id)initWithContentRect:(CGRect)aFrame +{ + self = [self initWithContentRect:aFrame styleMask:CPAttachedWhiteWindowMask] + return self; +} + +/*! create and init a CPAttachedWindow with given frame + @param aFrame the frame of the attached window + @param styleMask the window style mask (combine CPClosableWindowMask, CPAttachedWhiteWindowMask, CPAttachedBlackWindowMask and CPClosableOnBlurWindowMask) + @return ready to use CPAttachedWindow +*/ +- (id)initWithContentRect:(CGRect)aFrame styleMask:(unsigned)aStyleMask +{ + if (self = [super initWithContentRect:aFrame styleMask:aStyleMask]) + { + _isClosed = NO; + + var bundle = [CPBundle bundleForClass:[self class]], + buttonClose = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAttachedWindow/attached-window-button-close.png"] size:CPSizeMake(15.0, 15.0)], + buttonClosePressed = [[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPAttachedWindow/attached-window-button-close-highlighted.png"] size:CPSizeMake(15.0, 15.0)]; + + if (aStyleMask & CPClosableWindowMask) + { + _closeButton = [[CPButton alloc] initWithFrame:CPRectMake(8.0, 1.0, 14.0, 14.0)]; + [_closeButton setImageScaling:CPScaleProportionally]; + [_closeButton setBordered:NO]; + [_closeButton setImage:buttonClose]; // this avoid the blinking.. + [_closeButton setValue:buttonClose forThemeAttribute:@"image"]; + [_closeButton setValue:buttonClosePressed forThemeAttribute:@"image" inState:CPThemeStateHighlighted]; + [_closeButton setTarget:self]; + [_closeButton setAction:@selector(close:)]; + [[self contentView] addSubview:_closeButton]; + } + + _closeOnBlur = (aStyleMask & CPClosableOnBlurWindowMask); + + [self setLevel:CPStatusWindowLevel]; + [self setMovableByWindowBackground:YES]; + [self setHasShadow:NO]; + + [_windowView setNeedsDisplay:YES]; + } + + return self; +} + +#pragma mark - +#pragma mark Window actions + +/*! called when the window is loowing focus and close the window + if CPClosableOnBlurWindowMask is setted +*/ +- (void)resignMainWindow +{ + if (_closeOnBlur && !_isClosed) + { + // set a close flag to avoid infinite loop + _isClosed = YES; + [self close]; + + if (_delegate && [_delegate respondsToSelector:@selector(didAttachedWindowClose:)]) + [_delegate didAttachedWindowClose:self]; + } +} + +#pragma mark - +#pragma mark Utilities + +- (CPPoint)computeOrigin:(CPView)aView gravity:(int)gravity +{ + var frameView = [aView frame], + currentView = aView, + origin = [aView frameOrigin], + nativeRect = [[[CPApp mainWindow] platformWindow] nativeContentRect], + lastView; + + // if somebody succeed to use the conversion function of CPView + // to get this working, please do. + while (currentView = [currentView superview]) + { + origin.x += [currentView frameOrigin].x; + origin.y += [currentView frameOrigin].y; + lastView = currentView; + } + + origin.x += [[lastView window] frame].origin.x; + origin.y += [[lastView window] frame].origin.y; + + // take care of the scrolling point + if ([aView enclosingScrollView]) + { + var offsetPoint = [[[aView enclosingScrollView] contentView] boundsOrigin]; + origin.x -= offsetPoint.x; + origin.y -= offsetPoint.y; + } + + var originLeft = CPPointCreateCopy(origin), + originRight = CPPointCreateCopy(origin), + originTop = CPPointCreateCopy(origin), + originBottom = CPPointCreateCopy(origin); + + // CPAttachedWindowGravityRight + originRight.x += CPRectGetWidth(frameView); + originRight.y += (CPRectGetHeight(frameView) / 2.0) - (CPRectGetHeight([self frame]) / 2.0) + + // CPAttachedWindowGravityLeft + originLeft.x -= CPRectGetWidth([self frame]); + originLeft.y += (CPRectGetHeight(frameView) / 2.0) - (CPRectGetHeight([self frame]) / 2.0) + + // CPAttachedWindowGravityBottom + originBottom.x += CPRectGetWidth(frameView) / 2.0 - CPRectGetWidth([self frame]) / 2.0; + originBottom.y += CPRectGetHeight(frameView); + + // CPAttachedWindowGravityTop + originTop.x += CPRectGetWidth(frameView) / 2.0 - CPRectGetWidth([self frame]) / 2.0; + originTop.y -= CPRectGetHeight([self frame]); + + + if (gravity === CPAttachedWindowGravityAuto) + { + var frameCopy = CPRectCreateCopy([self frame]); + + nativeRect.origin.x = 0.0; + nativeRect.origin.y = 0.0; + + var tests = [originRight, originLeft, originTop, originBottom]; + + gravity = CPAttachedWindowGravityRight; + for (var i = 0; i < tests.length; i++) + { + frameCopy.origin = tests[i]; + + if (CPRectContainsRect(nativeRect, frameCopy)) + { + if (CPPointEqualToPoint(tests[i], originRight)) + { + gravity = CPAttachedWindowGravityRight + break; + } + else if (CPPointEqualToPoint(tests[i], originLeft)) + { + gravity = CPAttachedWindowGravityLeft + break; + } + else if (CPPointEqualToPoint(tests[i], originTop)) + { + gravity = CPAttachedWindowGravityUp + break; + } + else if (CPPointEqualToPoint(tests[i], originBottom)) + { + gravity = CPAttachedWindowGravityDown + break; + } + } + } + } + + var originToBeReturned; + switch (gravity) + { + case CPAttachedWindowGravityRight: + originToBeReturned = originRight; + break; + case CPAttachedWindowGravityLeft: + originToBeReturned = originLeft; + break; + case CPAttachedWindowGravityDown: + originToBeReturned = originBottom; + break; + case CPAttachedWindowGravityUp: + originToBeReturned = originTop; + break; + } + + [_windowView setGravity:gravity]; + + var o = originToBeReturned; + if (o.x < 0) + { + [_windowView setGravity:nil]; + o.x = 0; + } + if (o.x + CPRectGetWidth([self frame]) > nativeRect.size.width) + { + [_windowView setGravity:nil]; + o.x = nativeRect.size.width - CPRectGetWidth([self frame]); + } + if (o.y < 0) + { + [_windowView setGravity:nil]; + o.y = 0; + } + if (o.y + CPRectGetHeight([self frame]) > nativeRect.size.height) + { + [_windowView setGravity:nil]; + o.y = nativeRect.size.height - CPRectGetHeight([self frame]); + } + + return originToBeReturned; +} + + +#pragma mark - +#pragma mark Notification handlers + +- (void)_attachedWindowDidMove:(CPNotification)aNotification +{ + if ([_windowView isMouseDownPressed]) + { + [_windowView hideCursor]; + [self setLevel:CPNormalWindowLevel]; + [_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)]; + [[CPNotificationCenter defaultCenter] removeObserver:self name:CPWindowDidMoveNotification object:self]; + } +} + + +#pragma mark - +#pragma mark Utilities + +/*! compute the frame needed to be placed to the given view + and position the attached window according to this view (gravity will be CPAttachedWindowGravityAuto) + @param aView the view where CPAttachedWindow must be attached +*/ +- (void)positionRelativeToView:(CPView)aView +{ + [self positionRelativeToView:aView gravity:CPAttachedWindowGravityAuto]; +} + +/*! compute the frame needed to be placed to the given view + and position the attached window according to this view + @param aView the view where CPAttachedWindow must be attached + @param aGravity the gravity to use +*/ +- (void)positionRelativeToView:(CPView)aView gravity:(int)aGravity +{ + var frameView = [aView frame], + posX = frameView.origin.x + CPRectGetWidth(frameView), + posY = frameView.origin.y + (CPRectGetHeight(frameView) / 2.0) - (CPRectGetHeight([self frame]) / 2.0), + point = [self computeOrigin:aView gravity:aGravity]; + + point.y = MAX(point.y, 0); + + [self setFrameOrigin:point]; + [_windowView showCursor]; + [self setLevel:CPStatusWindowLevel]; + [_closeButton setFrameOrigin:CPPointMake(1.0, 1.0)]; + [_windowView setNeedsDisplay:YES]; + [self makeKeyAndOrderFront:nil]; +} + +/*! set the _targetView and attach the CPAttachedWindow to it + @param aView the view where CPAttachedWindow must be attached +*/ +- (void)attachToView:(CPView)aView +{ + _targetView = aView; + [self positionRelativeToView:_targetView]; + + [_targetView addObserver:self forKeyPath:@"window.frame" options:nil context:nil]; +} + + +#pragma mark - +#pragma mark Actions + +/*! closes the CPAttachedWindow + @param sender the sender of the action +*/ +- (IBAction)close:(id)aSender +{ + [self close]; + + [_targetView removeObserver:self forKeyPath:@"window.frame"]; + + if (_delegate && [_delegate respondsToSelector:@selector(didAttachedWindowClose:)]) + [_delegate didAttachedWindowClose:self]; +} + +/*! order front the window as usual and add listener for CPWindowDidMoveNotification + @param sender the sender of the action +*/ +- (IBAction)makeKeyAndOrderFront:(is)aSender +{ + [super makeKeyAndOrderFront:aSender]; + [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_attachedWindowDidMove:) name:CPWindowDidMoveNotification object:self]; +} + +/*! update the CPAttachedWindow frame if a resize event is observed + +*/ +- (void)observeValueForKeyPath:(CPString)aPath ofObject:(id)anObject change:(CPDictionary)theChange context:(void)aContext +{ + if ([aPath isEqual:@"window.frame"]) + { + [self positionRelativeToView:_targetView]; + } +} + +@end \ No newline at end of file diff --git a/AppKit/CPControl.j b/AppKit/CPControl.j index a4d48cb8e..46c84c3c4 100644 --- a/AppKit/CPControl.j +++ b/AppKit/CPControl.j @@ -25,7 +25,6 @@ @import "CPView.j" @import "CPKeyValueBinding.j" - CPLeftTextAlignment = 0; CPRightTextAlignment = 1; CPCenterTextAlignment = 2; @@ -72,7 +71,9 @@ CPControlTextDidBeginEditingNotification = "CPControlTextDidBeginEditingNotif CPControlTextDidChangeNotification = "CPControlTextDidChangeNotification"; CPControlTextDidEndEditingNotification = "CPControlTextDidEndEditingNotification"; -var CPControlBlackColor = [CPColor blackColor]; +var CPControlBlackColor = [CPColor blackColor], + CPCurrentToolTip, + CPCurrentToolTipTimer; /*! @ingroup appkit @@ -244,28 +245,109 @@ var CPControlBlackColor = [CPColor blackColor]; @param aToolTip the tooltip */ -/* --(void)setToolTip:(CPString)aToolTip +- (void)setToolTip:(CPString)aToolTip { if (_toolTip == aToolTip) return; _toolTip = aToolTip; -#if PLATFORM(DOM) - _DOMElement.title = aToolTip; -#endif + if (!_DOMElement) + return; + + var fIn = function(e){ + [self _fireToolTip]; + }; + fOut = function(e){ + [self _invalidateToolTip]; + }; + + + if (_toolTip) + { + if (_DOMElement.addEventListener) + { + _DOMElement.addEventListener("mouseover", fIn, NO); + _DOMElement.addEventListener("keypress", fOut, NO); + _DOMElement.addEventListener("mouseout", fOut, NO); + } + else if (_DOMElement.attachEvent) + { + _DOMElement.attachEvent("onmouseover", fIn); + _DOMElement.attachEvent("onkeypress", fOut); + _DOMElement.attachEvent("onmouseout", fOut); + } + } + else + { + if (_DOMElement.removeEventListener) + { + _DOMElement.removeEventListener("mouseover", fIn, NO); + _DOMElement.removeEventListener("keypress", fOut, NO); + _DOMElement.removeEventListener("mouseout", fOut, NO); + } + else if (_DOMElement.detachEvent) + { + _DOMElement.detachEvent("onmouseover", fIn); + _DOMElement.detachEvent("onkeypress", fOut); + _DOMElement.detachEvent("onmouseout", fOut); + } + } } -*/ + /*! Returns the receiver's tooltip */ -/* --(CPString)toolTip +- (CPString)toolTip { return _toolTip; } + +/*! @ignore + starts the tooltip timer */ +- (void)_fireToolTip +{ + if (CPCurrentToolTipTimer) + { + [CPCurrentToolTipTimer invalidate]; + if (CPCurrentToolTip) + [CPCurrentToolTip close:nil]; + CPCurrentToolTip = nil; + } + + if (_toolTip) + CPCurrentToolTipTimer = [CPTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(_showToolTip:) userInfo:nil repeats:NO]; +} + +/*! @ignore + Stop the tooltip timer if any +*/ +- (void)_invalidateToolTip +{ + if (CPCurrentToolTipTimer) + { + [CPCurrentToolTipTimer invalidate]; + CPCurrentToolTipTimer = nil; + } + + if (CPCurrentToolTip) + { + [CPCurrentToolTip close:nil]; + CPCurrentToolTip = nil; + } +} + +/*! @ignore + Actually shows the tooltip if any +*/ +- (void)_showToolTip:(CPTimer)aTimer +{ + if (CPCurrentToolTip) + [CPCurrentToolTip close:nil]; + CPCurrentToolTip = [CPToolTip toolTipWithString:_toolTip forView:self]; +} + /*! Returns whether the control can continuously send its action messages. diff --git a/AppKit/CPToolTip.j b/AppKit/CPToolTip.j new file mode 100644 index 000000000..85abf0ca2 --- /dev/null +++ b/AppKit/CPToolTip.j @@ -0,0 +1,117 @@ +/* + * CPStepper.j + * AppKit + * + * Created by Antoine Mercadal + * Copyright 2009, Antoine Mercadal + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +@import "CPTextField.j" +@import "CPView.j" + +@import "CPAttachedWindow.j" + + +CPToolTipDefaultColorMask = CPAttachedBlackWindowMask; + +/*! @ingroup appkit + subclass of CPAttachedWindow in order to build quick tooltip +*/ +@implementation CPToolTip : CPAttachedWindow +{ + CPTextField _content; +} + +#pragma mark - +#pragma mark Class Methods + +/*! returns an initialized CPToolTip with string and attach it to given view + @param aString the content of the tooltip + @param aView the view where the tooltip will be attached +*/ ++ (CPToolTip)toolTipWithString:(CPString)aString forView:(CPView)aView +{ + var tooltip = [[CPToolTip alloc] initWithString:aString styleMask:CPToolTipDefaultColorMask]; + + [tooltip setAlphaValue:0.9]; + [tooltip attachToView:aView]; + [tooltip resignMainWindow]; + + return tooltip; +} + +/*! compute a cool size for the given string + @param aToolTipSize the original wanted tool tip size + @param aText the wanted text + @return CPArray containing the computer toolTipSize and textFrameSize +*/ ++ (CPSize)computeCorrectSize:(CPSize)aToolTipSize text:(CPString)aText +{ + var font = [CPFont systemFontOfSize:12.0], + textFrameSize = [aText sizeWithFont:font inWidth:(aToolTipSize.width - 10)]; + + if (textFrameSize.height < 100) + { + aToolTipSize.height = textFrameSize.height + 10; + return [aToolTipSize, textFrameSize]; + } + + var newWidth = aToolTipSize.width + ((parseInt(textFrameSize.height - 100) / 30) * 30); + textFrameSize = [aText sizeWithFont:font inWidth:newWidth - 10]; + aToolTipSize.width = newWidth + 5; + aToolTipSize.height = textFrameSize.height + 10; + + return [aToolTipSize, textFrameSize]; +} + + +#pragma mark - +#pragma mark Initialization + +/*! returns an initialized CPToolTip with string + @param aString the content of the tooltip +*/ +- (id)initWithString:(CPString)aString styleMask:(unsigned)aStyleMask +{ + var toolTipFrame = CPRectMake(0.0, 0.0, 250.0, 30.0), + layout = [CPToolTip computeCorrectSize:toolTipFrame.size text:aString], + textFrameSize = layout[1]; + + toolTipFrame.size = layout[0]; + + if (self = [super initWithContentRect:toolTipFrame styleMask:aStyleMask]) + { + textFrameSize.height += 4; + + _content = [CPTextField labelWithTitle:aString]; + [_content setLineBreakMode:CPLineBreakByCharWrapping]; + [_content setAlignment:CPJustifiedTextAlignment]; + [_content setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + [_content setFrameOrigin:CPPointMake(5.0, 5.0)]; + [_content setFrameSize:textFrameSize]; + [_content setTextShadowOffset:CGSizeMake(0.0, 1.0)]; + [_content setTextColor:(aStyleMask & CPAttachedWhiteWindowMask) ? [CPColor blackColor] : [CPColor whiteColor]]; + [_content setValue:((aStyleMask & CPAttachedWhiteWindowMask) ? [CPColor whiteColor] : [CPColor blackColor]) forThemeAttribute:@"text-shadow-color"]; + + [[self contentView] addSubview:_content]; + [self setMovableByWindowBackground:NO]; + } + + return self; +} + +@end \ No newline at end of file diff --git a/AppKit/CPToolbar.j b/AppKit/CPToolbar.j index 5fd6e6bad..eebd8a168 100644 --- a/AppKit/CPToolbar.j +++ b/AppKit/CPToolbar.j @@ -849,6 +849,10 @@ var _CPToolbarItemInfoMake = function(anIndex, aView, aLabel, aMinWidth) view = [[_CPToolbarItemView alloc] initWithToolbarItem:item toolbar:self]; _viewsForToolbarItems[[item UID]] = view; + + if ([item toolTip] && [view respondsToSelector:@selector(setToolTip:)]) + [view setToolTip:[item toolTip]]; + [self addSubview:view]; _minWidth += [view minSize].width + TOOLBAR_ITEM_MARGIN; diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index 8a2f6e43f..c740f0bac 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -2910,6 +2910,7 @@ CPCustomWindowShadowStyle = 3; @import "_CPWindowView.j" @import "_CPStandardWindowView.j" @import "_CPDocModalWindowView.j" +@import "_CPAttachedWindowView.j" @import "_CPHUDWindowView.j" @import "_CPBorderlessWindowView.j" @import "_CPBorderlessBridgeWindowView.j" diff --git a/AppKit/CPWindow/_CPAttachedWindowView.j b/AppKit/CPWindow/_CPAttachedWindowView.j new file mode 100644 index 000000000..b0b0b1d1b --- /dev/null +++ b/AppKit/CPWindow/_CPAttachedWindowView.j @@ -0,0 +1,241 @@ +/* + * _CPAttachedWindowView.j + * AppKit + * + * Created by Antoine Mercadal + * Copyright 2011 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +@import "_CPWindowView.j" + +CPAttachedWindowGravityUp = 0; +CPAttachedWindowGravityDown = 1; +CPAttachedWindowGravityLeft = 2; +CPAttachedWindowGravityRight = 3; +CPAttachedWindowGravityAuto = 4; + +/*! a custom CPWindowView that manage border and cursor +*/ +@implementation _CPAttachedWindowView : _CPWindowView +{ + BOOL _mouseDownPressed @accessors(getter=isMouseDownPressed, setter=setMouseDownPressed:); + unsigned _gravity @accessors(property=gravity); + + BOOL _useGlowingEffect; + CPColor _backgroundTopColor; + CPColor _backgroundBottomColor; + CPColor _strokeColor; + CPImage _cursorBackgroundBottom; + CPImage _cursorBackgroundLeft; + CPImage _cursorBackgroundRight; + CPImage _cursorBackgroundTop; + CPSize _cursorSize; +} + +/*! compute the contentView frame from a given window frame + @param aFrameRect the window frame +*/ ++ (CGRect)contentRectForFrameRect:(CGRect)aFrameRect +{ + var contentRect = CGRectMakeCopy(aFrameRect); + + // @todo change border art and remove this pixel perfect adaptation + // return CGRectInset(contentRect, 20, 20); + + contentRect.origin.x += 18; + contentRect.origin.y += 17; + contentRect.size.width -= 35; + contentRect.size.height -= 37; + + return contentRect; +} + +/*! compute the window frame from a given contentView frame + @param aContentRect the contentView frame +*/ ++ (CGRect)frameRectForContentRect:(CGRect)aContentRect +{ + var frameRect = CGRectMakeCopy(aContentRect); + + // @todo change border art and remove this pixel perfect adaptation + //return CGRectOffset(frameRect, 20, 20); + + frameRect.origin.x -= 18; + frameRect.origin.y -= 17; + frameRect.size.width += 35; + frameRect.size.height += 37; + return frameRect; +} + +- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask +{ + if (self = [super initWithFrame:aFrame styleMask:aStyleMask]) + { + _cursorSize = CPSizeMake(15, 10); + } + + return self; +} + +- (void)hideCursor +{ + _cursorSize = CPSizeMakeZero(); + [self setNeedsDisplay:YES]; +} + +- (void)showCursor +{ + _cursorSize = CPSizeMake(15, 10); + [self setNeedsDisplay:YES]; + _mouseDownPressed = NO; +} + + +- (void)drawRect:(CGRect)aRect +{ + [super drawRect:aRect]; + + var context = [[CPGraphicsContext currentContext] graphicsPort], + gradientColor = [[_backgroundTopColor redComponent], [_backgroundTopColor greenComponent], [_backgroundTopColor blueComponent],1.0, [_backgroundBottomColor redComponent], [_backgroundBottomColor greenComponent], [_backgroundBottomColor blueComponent],1.0], + gradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), gradientColor, [0,1], 2), + radius = 5, + arrowWidth = _cursorSize.width, + arrowHeight = _cursorSize.height, + strokeWidth = 2; + + CGContextSetStrokeColor(context, _strokeColor); + CGContextSetLineWidth(context, strokeWidth); + CGContextBeginPath(context); + + aRect.origin.x += strokeWidth; + aRect.origin.y += strokeWidth; + aRect.size.width -= strokeWidth * 2; + aRect.size.height -= strokeWidth * 2; + + if (_useGlowingEffect) + { + var shadowColor = [[CPColor blackColor] colorWithAlphaComponent:.1], + shadowSize = CGSizeMake(0, 0), + shadowBlur = 5; + + //compensate for the shadow blur + aRect.origin.x += shadowBlur; + aRect.origin.y += shadowBlur; + aRect.size.width -= shadowBlur * 2; + aRect.size.height -= shadowBlur * 2; + + //set the shadow + CGContextSetShadow(context, CGSizeMake(0,0), 20); + CGContextSetShadowWithColor(context, shadowSize, shadowBlur, shadowColor); + } + + //Remodulate size and origin + aRect.size.width -= 10; + aRect.origin.x += 5; + aRect.size.height -= 10; + aRect.origin.y += 5; + + CGContextAddPath(context, CGPathWithRoundedRectangleInRect(aRect, radius, radius, YES, YES, YES, YES)); + CGContextDrawLinearGradient(context, gradient, CGPointMake(CPRectGetMidX(aRect), 0.0), CGPointMake(CPRectGetMidX(aRect), aRect.size.height), 0); + CGContextClosePath(context); + + //Start the arrow + switch (_gravity) + { + case CPAttachedWindowGravityLeft: + CGContextMoveToPoint(context, aRect.size.width + aRect.origin.x, (aRect.size.height / 2 - (arrowWidth / 2)) + aRect.origin.y); + CGContextAddLineToPoint(context, aRect.size.width + arrowHeight + aRect.origin.x, (aRect.size.height / 2) + aRect.origin.y); + CGContextAddLineToPoint(context, aRect.size.width + aRect.origin.x, (aRect.size.height / 2 + (arrowWidth / 2)) + aRect.origin.y); + break; + + case CPAttachedWindowGravityRight: + CGContextMoveToPoint(context, aRect.origin.x, (aRect.size.height / 2 - (arrowWidth / 2)) + aRect.origin.y); + CGContextAddLineToPoint(context, aRect.origin.x - arrowHeight, (aRect.size.height / 2) + aRect.origin.y); + CGContextAddLineToPoint(context, aRect.origin.x, (aRect.size.height / 2 + (arrowWidth / 2) + aRect.origin.y)); + break; + + case CPAttachedWindowGravityDown: + CGContextMoveToPoint(context, (aRect.size.width / 2 - (arrowWidth / 2)) + aRect.origin.x, aRect.origin.y); + CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x, aRect.origin.y - arrowHeight); + CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x , aRect.origin.y); + break; + + case CPAttachedWindowGravityUp: + CGContextMoveToPoint(context, (aRect.size.width / 2 - (arrowWidth / 2)) + aRect.origin.x, aRect.size.height + aRect.origin.y); + CGContextAddLineToPoint(context, (aRect.size.width / 2) + aRect.origin.x, aRect.size.height + aRect.origin.y + arrowHeight); + CGContextAddLineToPoint(context, (aRect.size.width / 2) + (arrowWidth / 2) + aRect.origin.x , aRect.size.height + aRect.origin.y); + break; + } + + //Draw it + CGContextStrokePath(context); + CGContextFillPath(context); +} + + +- (void)mouseDown:(CPEvent)anEvent +{ + _mouseDownPressed = YES; + [super mouseDown:anEvent]; +} + +- (void)mouseUp:(CPEvent)anEvent +{ + _mouseDownPressed = NO; + [super mouseUp:anEvent]; +} + +@end + + + +@implementation _CPAttachedWindowViewBlack : _CPAttachedWindowView + +- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask +{ + if (self = [super initWithFrame:aFrame styleMask:aStyleMask]) + { + _useGlowingEffect = YES; + _strokeColor = [CPColor colorWithHexString:@"131313"]; + _backgroundTopColor = [CPColor colorWithHexString:@"363636"]; + _backgroundBottomColor = [CPColor colorWithHexString:@"212121"]; + _cursorSize = CPSizeMake(15, 10); + } + + return self; +} + +@end + + +@implementation _CPAttachedWindowViewWhite : _CPAttachedWindowView + +- (id)initWithFrame:(CPRect)aFrame styleMask:(unsigned)aStyleMask +{ + if (self = [super initWithFrame:aFrame styleMask:aStyleMask]) + { + _useGlowingEffect = YES; + _strokeColor = [CPColor colorWithHexString:@"ADEDFF"]; + _backgroundTopColor = [CPColor colorWithHexString:@"ffffff"]; + _backgroundBottomColor = [CPColor colorWithHexString:@"ebebeb"]; + _cursorSize = CPSizeMake(15, 10); + } + + return self; +} + +@end \ No newline at end of file diff --git a/AppKit/Resources/CPAttachedWindow/attached-window-button-close-highlighted.png b/AppKit/Resources/CPAttachedWindow/attached-window-button-close-highlighted.png new file mode 100644 index 0000000000000000000000000000000000000000..2d3bcda481290602506d708144b08a7fdbb909ba GIT binary patch literal 1377 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%xRe+5hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|85Px?R(JoBXH0dq_dFl)Ro3@&3}V4Udb;uunKD`t|d z2UDWN@%MY5{92+X6;*mcan`2E1s&65yrLE@Tx%I6)!f$hZP^0DjJ)ikfEb;f)>2&) zWJ)qQR<6_6KD+y${~!M&;&pqz+u8kpe%hVYaY3uQrfE|)w@!oPtoH{*En4$8zn11w z+*I(knezj0-AiFc=Le!Phwi`QQj9wEow0&l{sDW5h73bZg6y zyfU+N#=gY~!99^_86K7^5|+p4t(R!KC?<0}QdHVIcf%syP80TdKb-?shR6l23|SPQ zvBq;(ijkzabo2IzgKMT#9n#28GVv|entnP}_jFg+g>7%E)F$uzVDrB7X}|YL!EaZd z=r(t~*d_OE`APGfptI@kr>H)bQ&*SV8nt$t^ij>JUZIuCFTbpiRTt})4b+gV4UFG> zejAIx#MisNYrK!y({=Qb)i0k7XZYSnAN`oO`Fw+Ncun@7|4e-TeVl!VStoprdMEh1 zcJ^Z987wE8XWyGTSyS)Q%B$Ng*_vaUvi`5jDw)B4IbqTv@edp_Y=7+QnRpmD+-K=e T_p{psD)c;E{an^LB{Ts5x>w@k literal 0 HcmV?d00001 diff --git a/AppKit/Resources/CPAttachedWindow/attached-window-button-close.png b/AppKit/Resources/CPAttachedWindow/attached-window-button-close.png new file mode 100644 index 0000000000000000000000000000000000000000..a39c6ac46f2409c7fde217d7e29a5cfd16f6b284 GIT binary patch literal 1375 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%xRe+5hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|85Px?R(JoBXH0dq_dFl+od+w+fsfwAAy#WAGfmd~Zr zUWXk-*d8qDlv;Qzsdv%VxC5@{PL3D3j!u={U7+~(UaR(e$1koiKNubzE1#ez(h~4u zk%x}sVliKl_gg>y|LSw{z;&PWb0_DV>5+4txNO$C*Uc3<2bs>C+Q7{>N8#IE@zp{T zk35j|IAC1kb%ue*S)5Ps{THE$TYCPP-EowTdk`)3JAuP3k*RM%no?)8=#Hjnj?}I4 zo+b|j=RFWz6_6gOTG5bg7kDsp_0?P6YLjI@+2qep4&vN@y}eY}zIc<6y3+mcuXo1e zoh_WhtT+8^nzzhkty`bYPM&kS)kx8OB#&BuguiIItH{EJW zT=D+$jps9RByLN;{Bg6T`lp{RN7LgFtoeF%?WV+W3I4 z#&f^?nH$~}^Wy%TN=<3H{?>VtspqCAw^+0EA9ZSF#CY{?Ul-&4)#z<_10zHJ-X;70 Sak4xF6?vYnelF{r5}E+Z1?$QH literal 0 HcmV?d00001 diff --git a/Tests/Manual/CPToolTip/AppController.j b/Tests/Manual/CPToolTip/AppController.j new file mode 100644 index 000000000..f8e33e175 --- /dev/null +++ b/Tests/Manual/CPToolTip/AppController.j @@ -0,0 +1,67 @@ +/* + * AppController.j + * tooltips + * + * Created by You on April 26, 2011. + * Copyright 2011, Your Company All rights reserved. + */ + +@import +@import + + +@implementation AppController : CPObject +{ + CPArray _toolbarItems; +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], + contentView = [theWindow contentView]; + + var b = [CPButton buttonWithTitle:@"Small tooltip"]; + [b setFrameOrigin:CPPointMake(10.0, 30.0)]; + [b setToolTip:@"Lorem ipsum dolor sit amet."]; + [contentView addSubview:b]; + + var b = [CPButton buttonWithTitle:@"Medium tooltip"]; + [b setFrameOrigin:CPPointMake(10.0, 80.0)]; + [b setToolTip:@"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi."]; + [contentView addSubview:b]; + + var b = [CPButton buttonWithTitle:@"Big tooltip"]; + [b setFrameOrigin:CPPointMake(10.0, 130.0)]; + [b setToolTip:@"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue. Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna. Aliquam convallis sollicitudin purus. Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet. Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet."]; + [contentView addSubview:b]; + [theWindow orderFront:self]; + + // Uncomment the following line to turn on the standard menu bar. + [CPMenu setMenuBarVisible:YES]; + + var tbItem1 = [[CPToolbarItem alloc] initWithItemIdentifier:@"1"]; + [tbItem1 setLabel:@"Mouse over me"]; + [tbItem1 setImage:[[CPImage alloc] initWithContentsOfFile:[[CPBundle mainBundle] pathForResource:@"test.png"] size:CPSizeMake(35, 32)]]; + [tbItem1 setToolTip:@"Hey guys, I'm a toolbar tooltip! Isn't it cool?"]; + _toolbarItems = [tbItem1]; + + var tb = [[CPToolbar alloc] init]; + [tb setDelegate:self]; + [theWindow setToolbar:tb]; +} + +- (CPArray)toolbarDefaultItemIdentifiers:(CPToolbar)toolbar +{ + return _toolbarItems; +} + +- (CPArray)toolbarAllowedItemIdentifiers:(CPToolbar)toolbar +{ + return _toolbarItems; +} + +- (CPToolbarItem)toolbar:(CPToolbar)toolbar itemForItemIdentifier:(CPString)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag +{ + return _toolbarItems[0]; +} +@end diff --git a/Tests/Manual/CPToolTip/Info.plist b/Tests/Manual/CPToolTip/Info.plist new file mode 100644 index 000000000..f12e98f7f --- /dev/null +++ b/Tests/Manual/CPToolTip/Info.plist @@ -0,0 +1,12 @@ + + + + + CPApplicationDelegateClass + AppController + CPBundleName + tooltips + CPPrincipalClass + CPApplication + + diff --git a/Tests/Manual/CPToolTip/Jakefile b/Tests/Manual/CPToolTip/Jakefile new file mode 100644 index 000000000..8f62ca5cd --- /dev/null +++ b/Tests/Manual/CPToolTip/Jakefile @@ -0,0 +1,93 @@ +/* + * Jakefile + * tooltips + * + * Created by You on April 26, 2011. + * Copyright 2011, Your Company All rights reserved. + */ + +var ENV = require("system").env, + FILE = require("file"), + JAKE = require("jake"), + task = JAKE.task, + FileList = JAKE.FileList, + app = require("cappuccino/jake").app, + configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug", + OS = require("os"); + +app ("tooltips", function(task) +{ + task.setBuildIntermediatesPath(FILE.join("Build", "tooltips.build", configuration)); + task.setBuildPath(FILE.join("Build", configuration)); + + task.setProductName("tooltips"); + task.setIdentifier("com.yourcompany.tooltips"); + task.setVersion("1.0"); + task.setAuthor("Your Company"); + task.setEmail("feedback @nospam@ yourcompany.com"); + task.setSummary("tooltips"); + task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**"))); + task.setResources(new FileList("Resources/**")); + task.setIndexFilePath("index.html"); + task.setInfoPlistPath("Info.plist"); + + if (configuration === "Debug") + task.setCompilerFlags("-DDEBUG -g"); + else + task.setCompilerFlags("-O"); +}); + +task ("default", ["tooltips"], function() +{ + printResults(configuration); +}); + +task ("build", ["default"]); + +task ("debug", function() +{ + ENV["CONFIGURATION"] = "Debug"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("release", function() +{ + ENV["CONFIGURATION"] = "Release"; + JAKE.subjake(["."], "build", ENV); +}); + +task ("run", ["debug"], function() +{ + OS.system(["open", FILE.join("Build", "Debug", "tooltips", "index.html")]); +}); + +task ("run-release", ["release"], function() +{ + OS.system(["open", FILE.join("Build", "Release", "tooltips", "index.html")]); +}); + +task ("deploy", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Deployment", "tooltips")); + OS.system(["press", "-f", FILE.join("Build", "Release", "tooltips"), FILE.join("Build", "Deployment", "tooltips")]); + printResults("Deployment") +}); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "tooltips")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "tooltips"), FILE.join("Build", "Desktop", "tooltips", "tooltips.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "tooltips", "tooltips.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "tooltips")); + print("----------------------------"); +} diff --git a/Tests/Manual/CPToolTip/Resources/spinner.gif b/Tests/Manual/CPToolTip/Resources/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..06dbc2bc21dddcf0e09b566d5b211aee89570f52 GIT binary patch literal 1849 zcma*odr(tX9tZHtz31lM+(&Y`A`Ou`NeG&R#DrIfV%?hng21vs6v>ooRQQFbk7E_K!V( z{?5!fpZWgIZ%*d6t%i*j24bL}AZUJm9)h6R*;%L4IWse3G#VWaN1#$zSSXcBhlYlH zJ|D}n{r&w2f+Qp)SS*&n?*G4}{}!h?td_mjU6Kad-rW*QwWYCUk7d^e+sjpZAn6kT z5rM;`{~_}-wm+L@%+E;JphLm}C3WzQAQD0wBoY|rOTQX{(@pp-WAA8Iw0HKl|15j) zhGh(SFWu#5zUCw^T!}C~eC}~?{xG-r7ugQC>taC+|L^xr&a%{#cgUOZXIM&v?)k^2 z1i5%0!U00wq)r4vlAwXBA_m5J5WNeuBm-XJQ5pG4GD%vc|K){+TJ{AkHqN@SJKczX zU}QRd9n*_bf^h94)6Ctv{CUfz_;qs#RDuQ$DR%y1&5G-a6rhf*4Y zl+UOD1*GE{GQ7vv;c-tTIMkz0ovO>9j%b5hpa@a*CBJ#W!0@ndSwF|%hMkdFtXiU) z`BEp+yhkt8ZY>tg&uyH@%^%K4jDrbA;et&vnnzZ3z22@e6<27z2Vk13kb7OtISyxv z(XoO-LNQCZe3lfzc%*pNqU<9T+p~teThOEB+~e&U0mC|zS{Q)dJZ)!^1f7L(zg*L> z*vSW%?dR~@@0(}x6-V^A`~ImD;)NaBUnP8;RH=bS2@pk`wp>>8r&jGjmGPy%1BW}v zoPo}ca$~bze@efc3kapuEVW1!%teOZT3j2Tw5=hjiAf02d}7dL0oFC%@=RXp5Ow#% z@a>+AM|YWt$n&e`>sB-32gBcuTi>R>*|8@lp`y6thc7ydyqDsuUn~YzZf|{-R@-3y zL#wx{Ii}xxL_csiW*LBn0-A$>zp4WOmkf6=ilrjlW?-l9+bCd7|i?*b5NTmy<<6NZ0T8$SN@mwdC2f z6jqK=N@bS@!=YSj%>h0}*4p+%0HbTIrE$w7UMT6+AZZ&DAo*qZAAi(OtNbIfl#Dw^ zJWeiCp~zi#&t6x}m9)O^eR4HiLV3QA<<0>HZ8%$^lrSE94Wb}=+MM^!b>n#5&-JQR zkr-CEu9C;_F*7DqDisulV6Pmg$nFL0TPn%~*m^-`Z3^BgU(sNpnx%nW(!eV9A&FvI zHL3X3lw8Wji^6=8KbQDE-e%b?svdFxxdh$8r97@7$ojeI`6AL8Ob6QC$qh&>ZWVDX1F{0vJnT%%mE;GveKWR%C} zM&4d;Jf3s9|HAA)yVUPo`Aq;0do#)uHSXi5*QF*)x@MVVHr+cN)uMZ__F|&Ta#p8d z53TOKtce!PJUuie8UWol-S(`c2nH?UGqJP{!4RR4u$LCfn)q-hj0^f=h(VYy)T6eN zhRO!ja-aDBTcgeyP(8Ua4IdiOog^*CQa?R(cP#9AgL9`j>EX-6Yf1lzX(!~``M1XC zNmM<4<6d~wWZ$Xrk0K}UteTrq@LBBk#MsjkK;pbuVhe)NI7(7Pf(l?lxC7=1Z7Pzl zMbS;nV4NI5_N{1$P)&XC)huOGQ+h^zpYUZfb*1n6>!`#*b3y52LGmi+<4sY5jyD#- zw&$d}DTguLAfnRtjrM*JfqtHqUu6rQoU=g%1E9xk%;)TDm^2<8n-0IhTsQFaek)MY}>PK4;nBk=ow4pF>vzkO& gna%Org-kgQCf=+BeMi^RbuY;YE~rTjend;_cdi8t>i_@% literal 0 HcmV?d00001 diff --git a/Tests/Manual/CPToolTip/Resources/test.png b/Tests/Manual/CPToolTip/Resources/test.png new file mode 100644 index 0000000000000000000000000000000000000000..8ebf637898b8c2068c0de261b06fec204869da31 GIT binary patch literal 2472 zcmV;Z30L-sP)&JTi z_9k``=S7^*H130{K`Ct{qNuG%g;bP6p$ZC$5>X*lzz;-Kel$YrgMuKHf}oHlBDAQL zN+_hDCMdK@MVp5p&dc%IvAy1>_ciZ3Gn_k_HLkJao!Lz!j&ywQ%%(*r;w+hN_ypqbu<0>WT)f@mtkj^r{-^ElSGfR9@VdoR&aR$)9oT zYsg2T4&)k?W&RJS-N-*&9czj8b+$k#6aY~%K-3IKi6*Fd35aO{T~ulSoZkll)(1g8 z2>r1ze0GH&ilVeFJ~h4VTB5KY=Xw_T&}{`}8b+nN68U#)ds_$lx}so!4>FPtrRgGS z!?b}A#BXA2&ax680%P$H;sOra0Ox}y!4E5zbilH%=26Ba=`0FF;<@AzjqaTv_OgwL>__Z~5f%pAX6-XhBjtdP!~d6pcB-%szXPU0Gsbv$tTjeUthkS z4&vhjbqDp-&rj`K-rcglE!qrYXl?~jLjg><6-3Z)ClH+2z(W@<;KlgV&m8|cRyR;z z|LKW!ra^w$7Hxre4gr-_Fsd{+g9;cQgM2mzk=7{0mh}S5vWt$B4bxsUYMOVi?`Kb+ zy?6*6a1bAjEH>7+*blz?^50m4kR$6?^#;>ua=D@mnoewCtKrd$7hz*mg}qy2b{iiW zs(P%d>5$2bkW(!fSh=(X<)|vuGmB*uJ74(Ao>hI_zu+USFqJ9c%DFSSje!(w|6D)t zJP(?t!Mmr2;f&JZY7UNLp{K18im4>TCsI!wdFG2pZkAEp6x8EC{u7TQNABFTp=UBH zfr1X~mY(^1BgDGeQ7{e&`bjFCh3DVTyPhW$bnVII zy*)kCxR1z+YHy^jg$ac~5Crh~e6}z7`G9R&Zb_7IFBCZF?uf1)pGxn*TW@*_6llZW z6q=eLl`n&?>z*Q6uB1XwCo^LK^x80p>v`xpOrsoakqD5n@!g(4J^JkLdjkT0FJ?X< z%j$ymyBJO8L>O?ASvp4o6IenFPjiWe0!9THzcCSR|AVH2UbiZp*rb!~yOmyPipKMK)h@BY(gFz@`z{!@C zn&x9n24yl(Q>4iisLgv`VNt!TrqyODw#I2DI+cf}#s;{a%-1|0jiPj+aa-VA<$k9Q zJZ)gaLne0kt(C8*3pesW1XqvwbPaRQo0!nTKBem=qUjNOe79;mFpZ^4HmFJ5qiWR@ z-5Lyy`lViU4T=!;D$(V;X!WR;=tt>VMurQcX&6r9o@dks%Fp;wQ*`fCM_hx_HT9~l zA}EusDO%a)Je07x9z->zsTxfVuDA!K$fxB}{wuyvv}Tj3D`B=T6?32|(g&_VDaG`= z#p&jkBjHv&<(i)TiuzQO@^bVr zbh&g$%BMH_!finJEHB#H^XMjcZ0n$X^1TPMljepHymX<5&5_Xv$|%K&E{Ta?4xqTMyA_6e4&IqU{6suJbex9^O&y3KY^4!^yM9-|$>` zDNhU)RH^WiG@Z0nZ#rh^hDTCmTT662ZLen~*K<${q2tBe6lh}p1tm98uBAj~WzHQx z0BqoEQGZ~7^#xrgxIa343Ijv{9^AAN-aj)8&%JgMqp9J_>^RKIjdBhOsp}t2o;vcI znyX2aqU*PA4zw|Ew%xb0D!X;_fk4WA{Pyz(d06xX z#-5Az4X_rPhN`M{)fi2KXh30288Y!9-BiloEu24gu;re|m1_4&#_cy>q;SL9hrYyy zBL^a}RgG4l85HG49ikR+^qmwCtr3DejakTWij=dv#Y-pNYaaZvG*fu__`j90x_$F) zymNLf_!EoIwsYwD%4OP3@qYemgR{1y}JMJM04iEjI>YX5u3)JGxgLKX)GA zrXWod|YT>xP3_{-_!w?U$6_G)KnrXO#AfT4A23fnQmps?X+e5LO;U^YcUn?!-=A}reS4ootN054cX8*!?wY2<0@I`p96RK$n_P + + + + + + + + + + + + + + tooltips + + + + + + + + + + + + + + +
+
+ + + +
+
+ + + diff --git a/Tests/Manual/CPToolTip/index.html b/Tests/Manual/CPToolTip/index.html new file mode 100644 index 000000000..224a5034e --- /dev/null +++ b/Tests/Manual/CPToolTip/index.html @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + tooltips + + + + + + + + + + + + +
+
+ + + +
+
+ + + + diff --git a/Tests/Manual/CPToolTip/main.j b/Tests/Manual/CPToolTip/main.j new file mode 100644 index 000000000..9e6a15286 --- /dev/null +++ b/Tests/Manual/CPToolTip/main.j @@ -0,0 +1,18 @@ +/* + * AppController.j + * tooltips + * + * Created by You on April 26, 2011. + * Copyright 2011, Your Company All rights reserved. + */ + +@import +@import + +@import "AppController.j" + + +function main(args, namedArgs) +{ + CPApplicationMain(args, namedArgs); +}