From 2b23374ec70fe3452b17cec285fc22394b67026f Mon Sep 17 00:00:00 2001 From: Aparajita Fishman Date: Mon, 29 Jul 2013 12:51:52 -0400 Subject: [PATCH] New: CPAlert supports custom didEnd selector or block. Previously, unless you were using a sheet alert, there was no way to set a custom dismissal handler for an alert. In Cocoa alerts are synchronous, in Cappuccino they are asynchronous, which meant that a single dismissal handler had to handle all possible non-sheet alerts. This commit adds several methods which add two ways to specify a custom dismissal handler: via delegate/selector, and via a block (function). The new methods are: setDidEndSelector: runModalWithDidEndBlock: beginSheetModalForWindow:didEndBlock: Note that didEnd blocks are transient and take precedence over didEnd selectors. The CPAlertTest demo was updated to fix some bugs and added the ability to run the tests using blocks. --- AppKit/CPAlert.j | 87 +++++++++++++++++------- Tests/Manual/CPAlertTest/AppController.j | 54 +++++++++++++-- 2 files changed, 112 insertions(+), 29 deletions(-) diff --git a/AppKit/CPAlert.j b/AppKit/CPAlert.j index 37208a12e..5f8db4129 100644 --- a/AppKit/CPAlert.j +++ b/AppKit/CPAlert.j @@ -96,7 +96,8 @@ var bottomHeight = 71; id _delegate @accessors(property=delegate); id _modalDelegate; - SEL _didEndSelector; + SEL _didEndSelector @accessors(property=didEndSelector); + Function _didEndBlock; _CPAlertThemeView _themeView @accessors(property=themeView, readonly); CPWindow _window @accessors(property=window, readonly); @@ -224,16 +225,15 @@ var bottomHeight = 71; } -/*! @deprecated -*/ -- (void)setWindowStyle:(int)aStyle +/*! @deprecated */ +- (void)setWindowStyle:(int)style { CPLog.warn("DEPRECATED: setWindowStyle: is deprecated. use setTheme: instead"); - [self setTheme:(aStyle === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]]; + + [self setTheme:(style === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]]; } -/*! @deprecated -*/ +/*! @deprecated */ - (int)windowStyle { CPLog.warn("DEPRECATED: windowStyle: is deprecated. use theme instead"); @@ -242,18 +242,19 @@ var bottomHeight = 71; /*! - set the text of the alert's message + Set the text of the alert's message. @param aText CPString containing the text */ -- (void)setMessageText:(CPString)aText +- (void)setMessageText:(CPString)text { - [_messageLabel setStringValue:aText]; + [_messageLabel setStringValue:text]; _needsLayout = YES; } /*! - return the content of the message text + Return the content of the message text. + @return CPString containing the message text */ - (CPString)messageText @@ -262,13 +263,13 @@ var bottomHeight = 71; } /*! - set the text of the alert's informative text + Set the text of the alert's informative text. @param aText CPString containing the informative text */ -- (void)setInformativeText:(CPString)aText +- (void)setInformativeText:(CPString)text { - [_informativeLabel setStringValue:aText]; + [_informativeLabel setStringValue:text]; _needsLayout = YES; } @@ -285,6 +286,7 @@ var bottomHeight = 71; /*! Sets the title of the alert window. This API is not present in Cocoa. + @param aTitle CPString containing the window title */ - (void)setTitle:(CPString)aTitle @@ -294,7 +296,7 @@ var bottomHeight = 71; } /*! - set the accessory view + Set the accessory view. @param aView the accessory view */ @@ -305,7 +307,7 @@ var bottomHeight = 71; } /*! - set if alert shows the suppression button + Set if the alert shows the suppression button. @param shouldShowSuppressionButton YES or NO */ @@ -596,6 +598,17 @@ var bottomHeight = 71; [CPApp runModalForWindow:_window]; } +/*! + The same as \c runModal, but executes the code in \c block when the + alert is dismissed. +*/ +- (void)runModalWithDidEndBlock:(Function /*(CPAlert alert, int returnCode)*/)block +{ + _didEndBlock = block; + + [self runModal]; +} + /*! Runs the receiver modally as an alert sheet attached to a specified window. @@ -630,6 +643,20 @@ var bottomHeight = 71; [self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil]; } +/*! + Runs the receiver modally as an alert sheet attached to a specified window. + Executes the code in \c block when the alert is dismissed. + + @param window The parent window for the sheet. + @param block Code block to execute on dismissal +*/ +- (void)beginSheetModalForWindow:(CPWindow)aWindow didEndBlock:(Function /*(CPAlert alert, int returnCode)*/)block +{ + _didEndBlock = block; + + [self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil]; +} + #pragma mark Private /*! @@ -697,14 +724,28 @@ var bottomHeight = 71; */ - (void)_alertDidEnd:(CPWindow)aWindow returnCode:(int)returnCode contextInfo:(id)contextInfo { - if (_didEndSelector) - objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo); + if (_didEndBlock) + { + if (typeof(_didEndBlock) === "function") + _didEndBlock(self, returnCode); + else + CPLog.warn("%s: didEnd block is not a function", [self description]); - _modalDelegate = nil; - _didEndSelector = nil; - - if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)]) - [_delegate alertDidEnd:self returnCode:returnCode]; + // didEnd blocks are transient + _didEndBlock = nil; + } + else if (_modalDelegate) + { + if (_didEndSelector) + objj_msgSend(_modalDelegate, _didEndSelector, self, returnCode, contextInfo); + } + else if (_delegate) + { + if (_didEndSelector) + objj_msgSend(_delegate, _didEndSelector, self, returnCode); + else if ([_delegate respondsToSelector:@selector(alertDidEnd:returnCode:)]) + [_delegate alertDidEnd:self returnCode:returnCode]; + } } @end diff --git a/Tests/Manual/CPAlertTest/AppController.j b/Tests/Manual/CPAlertTest/AppController.j index a0d1d97a0..bb91b42b3 100644 --- a/Tests/Manual/CPAlertTest/AppController.j +++ b/Tests/Manual/CPAlertTest/AppController.j @@ -16,9 +16,10 @@ CPArray variations; CPArray messages; int messageIndex; + BOOL useBlocks; } -- (void)applicationDidFinishLaunching:(CPNotification)aNotification +- (void)_init { messages = [ [@"Are you sure you want to theorise before you have data?", @@ -30,6 +31,7 @@ [@"Sometimes a message can be really long and just appear to go on and on. It could be a speech. It could be the television.", nil] ]; + messageIndex = 0; variations = [ @@ -43,6 +45,11 @@ [CPDocModalWindowMask, CPInformationalAlertStyle], [CPDocModalWindowMask, CPCriticalAlertStyle] ]; +} + +- (void)applicationDidFinishLaunching:(CPNotification)aNotification +{ + [self _init]; theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(100, 100, 500, 500) styleMask:CPTitledWindowMask]; [theWindow setTitle:@"CPAlert Test"]; @@ -54,6 +61,13 @@ [label setStringValue:"Respond to the alert dialog with the mouse or the keyboard."]; [contentView addSubview:label]; + var button = [CPButton buttonWithTitle:@"Start again using didEnd blocks"]; + + [button setTarget:self]; + [button setAction:@selector(testWithBlocks:)]; + [button setCenter:[contentView center]]; + [contentView addSubview:button]; + [theWindow orderFront:self]; [self showNextAlertVariation]; @@ -62,9 +76,17 @@ //[CPMenu setMenuBarVisible:YES]; } +- (@action)testWithBlocks:(id)sender +{ + useBlocks = YES; + [self _init]; + [self showNextAlertVariation]; +} + - (void)alertDidEnd:(CPAlert)anAlert returnCode:(CPInteger)returnCode { - CPLogConsole(_cmd); + CPLog.info("%s alert = %s, code = %d", _cmd, [anAlert description], returnCode); + if (returnCode === 0) [label setStringValue:"You chose the default action."]; else @@ -75,7 +97,7 @@ - (void)customDidEnd:(CPAlert)anAlert code:(id)code context:(id)context { - CPLogConsole(_cmd + anAlert + code + context); + CPLog.info("%s alert = %s, code = %d, context = %s", _cmd, [anAlert description], code, context); } - (void)showNextAlertVariation @@ -92,17 +114,37 @@ var windowStyle = variation[0]; [alert setDelegate:self]; - [alert setMessageText:message[0]]; - [alert setInformativeText:message[1]]; + [alert setMessageText:message[0] || @""]; + [alert setInformativeText:message[1] || @""]; + if (message.length > 2) [alert addButtonWithTitle:message[2]]; + if (message.length > 3) [alert addButtonWithTitle:message[3]]; + [alert setTheme:(windowStyle === CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]]; [alert setAlertStyle:variation[1]]; if (windowStyle & CPDocModalWindowMask) - [alert beginSheetModalForWindow:theWindow modalDelegate:self didEndSelector:@selector(customDidEnd:code:context:) contextInfo:@"here is some context"]; + { + if (useBlocks) + [alert beginSheetModalForWindow:theWindow didEndBlock:function(alert, returnCode) + { + CPLog.info("didEndBlock: alert = %s, code = %d", [alert description], returnCode); + + [self showNextAlertVariation]; + }]; + else + [alert beginSheetModalForWindow:theWindow modalDelegate:self didEndSelector:@selector(customDidEnd:code:context:) contextInfo:@"here is some context"]; + } + else if (useBlocks) + [alert runModalWithDidEndBlock:function(alert, returnCode) + { + CPLog.info("didEndBlock: alert = %s, code = %d", [alert description], returnCode); + + [self showNextAlertVariation]; + }]; else [alert runModal]; }