Cleaning CPAlert, add accessoryView

This commit is contained in:
Antoine Mercadal
2010-11-09 15:21:25 +01:00
parent 187c32d231
commit bf63147ca3
2 changed files with 193 additions and 174 deletions
+183 -169
View File
@@ -51,6 +51,8 @@ CPInformationalAlertStyle = 1;
*/
CPCriticalAlertStyle = 2;
var CPAlertLabelOffset = 3.0;
/*!
@ingroup appkit
@@ -75,18 +77,19 @@ CPCriticalAlertStyle = 2;
*/
@implementation CPAlert : CPView
{
CPPanel _alertPanel;
CPTextField _messageLabel @accessors(getter=messageText);
CPTextField _informativeLabel @accessors(getter=informativeText);
CPAlertStyle _alertStyle @accessors(property=alertStyle);
id _delegate @accessors(property=delegate);
CPView _accessoryView @accessors(property=accessoryView);
CPTextField _messageLabel;
CPTextField _informativeLabel;
CPImageView _alertImageView;
CPAlertStyle _alertStyle;
CPString _windowTitle;
int _windowStyle;
CPArray _buttons;
id _delegate;
CPPanel _alertPanel;
SEL _didEndSelector;
id _modalDelegate;
}
@@ -104,6 +107,7 @@ CPCriticalAlertStyle = 2;
CGPointMake(15, 12),
[CPNull null],
[CPNull null],
[CPNull null],
[CPNull null]
]
forKeys:[@"size", @"content-inset", @"informative-offset", @"button-offset",
@@ -112,10 +116,40 @@ CPCriticalAlertStyle = 2;
@"image-offset",
@"information-image",
@"warning-image",
@"error-image"
@"error-image",
@"bezel-color"
]];
}
+ alertWithMessageText:(CPString)aMessage defaultButton:(CPString)defaultButtonText alternateButton:(CPString)alternateButtonText otherButton:(CPString)otherButtonText informativeTextWithFormat:(CPString)informativeText
{
var alert = [[CPAlert alloc] init];
[alert setMessageText:aMessage];
[alert addButtonWithTitle:defaultButtonText];
if (alternateButtonText)
[alert addButtonWithTitle:alternateButtonText];
if (otherButtonText)
[alert addButtonWithTitle:otherButtonText];
if (informativeText)
[alert setInformativeText:informativeText];
return alert;
}
+ alertWithError:(CPString)anErrorMessage
{
var alert = [[CPAlert alloc] init];
[alert setMessageText:anErrorMessage];
[alert setStyle:CPCriticalAlertStyle];
return alert;
}
/*!
Initializes a \c CPAlert panel with the default alert style \c CPWarningAlertStyle.
*/
@@ -126,7 +160,6 @@ CPCriticalAlertStyle = 2;
_buttons = [CPArray array];
_alertStyle = CPWarningAlertStyle;
_alertPanel = nil;
_windowStyle = nil;
_didEndSelector = nil;
_messageLabel = [CPTextField labelWithTitle:@"Alert"];
@@ -137,27 +170,16 @@ CPCriticalAlertStyle = 2;
return self;
}
/*!
Sets the window appearance. If CPHUDBackgroundWindowMask is set, the default HUD theme
will be activated.
@param styleMask - Either CPHUDBackgroundWindowMask or nil for standard.
*/
- (void)setWindowStyle:(int)styleMask
- (void)_createPanelWithStyle:(int)forceStyle
{
_windowStyle = styleMask;
var frame = CGRectMakeZero(),
styleMask = [self currentValueForThemeAttribute:@"bezel-color"] ? CPBorderlessWindowMask : CPTitledWindowMask;
[self setTheme:(_windowStyle & CPHUDBackgroundWindowMask) ? [CPTheme defaultHudTheme] : [CPTheme defaultTheme]];
// We'll need to recreate the panel to get the new window style.
_alertPanel = nil;
}
- (void)_createPanel
{
var frame = CGRectMakeZero();
frame.size = [self currentValueForThemeAttribute:@"size"];
_alertPanel = [[CPPanel alloc] initWithContentRect:frame styleMask:_windowStyle ? _windowStyle | CPTitledWindowMask : CPTitledWindowMask];
_alertPanel = [[CPPanel alloc] initWithContentRect:frame styleMask:forceStyle || styleMask];
[_alertPanel setMovableByWindowBackground:YES];
var contentView = [_alertPanel contentView],
count = [_buttons count];
@@ -175,98 +197,25 @@ CPCriticalAlertStyle = 2;
[contentView addSubview:_informativeLabel];
}
/*!
Sets the window's title. If this is not defined, a default title based on your warning level will be used.
@param aTitle the title to use in place of the default. Set to nil to use default.
*/
- (void)setTitle:(CPString)aTitle
{
_windowTitle = aTitle;
}
/*!
Gets the window's title.
Gets the window
*/
- (CPString)title
- (CPWindow)window
{
return _windowTitle;
return [_alertPanel window];
}
/*!
Gets the window's style.
*/
- (int)windowStyle
- (void)setMessageText:(CPString)aText
{
return _windowStyle;
[_messageLabel setStringValue:aText];
}
/*!
Sets the receivers delegate.
@param delegate - Delegate for the alert. nil removes the delegate.
*/
- (void)setDelegate:(id)delegate
- (void)setInformativeText:(CPString)aText
{
_delegate = delegate;
[_informativeLabel setStringValue:aText];
}
/*!
Gets the receiver's delegate.
*/
- (void)delegate
{
return _delegate;
}
/*!
Sets the alert style of the receiver.
@param style - Alert style for the alert.
*/
- (void)setAlertStyle:(CPAlertStyle)style
{
_alertStyle = style;
}
/*!
Gets the alert style.
*/
- (CPAlertStyle)alertStyle
{
return _alertStyle;
}
/*!
Sets the receivers message text, or title, to a given text.
@param messageText - Message text for the alert.
*/
- (void)setMessageText:(CPString)messageText
{
[_messageLabel setStringValue:messageText];
}
/*!
Returns the receiver's message text body.
*/
- (CPString)messageText
{
return [_messageLabel stringValue];
}
/*!
Sets the receiver's informative text, shown below the message text.
@param informativeText - The informative text.
*/
- (void)setInformativeText:(CPString)informativeText
{
[_informativeLabel setStringValue:informativeText];
}
/*!
Returns the receiver's informative text.
*/
- (CPString)informativeText
{
return [_informativeLabel stringValue];
}
/*!
Adds a button with a given title to the receiver.
@@ -301,37 +250,13 @@ CPCriticalAlertStyle = 2;
[_buttons insertObject:button atIndex:0];
}
- (void)layoutPanel
- (void)_layoutMessageView
{
if (!_alertPanel)
[self _createPanel];
var inset = [self currentValueForThemeAttribute:@"content-inset"],
iconOffset = [self currentValueForThemeAttribute:@"image-offset"],
theTitle,
theImage;
switch (_alertStyle)
{
case CPWarningAlertStyle: theImage = [self currentValueForThemeAttribute:@"warning-image"];
theTitle = @"Warning";
break;
case CPInformationalAlertStyle: theImage = [self currentValueForThemeAttribute:@"information-image"];
theTitle = @"Information";
break;
case CPCriticalAlertStyle: theImage = [self currentValueForThemeAttribute:@"error-image"];
theTitle = @"Error";
break;
}
[_alertImageView setImage:theImage];
var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
[_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
[_alertPanel setTitle:_windowTitle ? _windowTitle : theTitle];
[_alertPanel setFloatingPanel:YES];
[_alertPanel center];
sizeWithFontCorrection = 6.0,
messageLabelWidth,
messageLabelTextSize;
[_messageLabel setTextColor:[self currentValueForThemeAttribute:@"message-text-color"]];
[_messageLabel setFont:[self currentValueForThemeAttribute:@"message-text-font"]];
@@ -340,62 +265,151 @@ CPCriticalAlertStyle = 2;
[_messageLabel setAlignment:[self currentValueForThemeAttribute:@"message-text-alignment"]];
[_messageLabel setLineBreakMode:CPLineBreakByWordWrapping];
messageLabelWidth = [_alertPanel frame].size.width - inset.left - inset.right,
messageLabelTextSize = [[_messageLabel stringValue] sizeWithFont:[_messageLabel font] inWidth:messageLabelWidth];
[_messageLabel setFrame:CGRectMake(inset.left, inset.top, messageLabelTextSize.width, messageLabelTextSize.height + sizeWithFontCorrection)];
}
#pragma mark -
#pragma mark Layouting
/*! @ignore
*/
- (void)_layoutInformativeView
{
var inset = [self currentValueForThemeAttribute:@"content-inset"],
sizeWithFontCorrection = 6.0,
informativeLabelWidth,
informativeLabelOriginY,
informativeLabelTextSize;
[_informativeLabel setTextColor:[self currentValueForThemeAttribute:@"informative-text-color"]];
[_informativeLabel setFont:[self currentValueForThemeAttribute:@"informative-text-font"]];
[_informativeLabel setTextShadowColor:[self currentValueForThemeAttribute:@"informative-text-shadow-color"]];
[_informativeLabel setTextShadowOffset:[self currentValueForThemeAttribute:@"informative-text-shadow-offset"]];
[_informativeLabel setLineBreakMode:CPLineBreakByWordWrapping];
// FIXME sizeWithFontCorrection shouldn't be needed.
var bounds = [[_alertPanel contentView] bounds],
offsetX = CGRectGetWidth(bounds) - inset.right,
informativeOffset = [self currentValueForThemeAttribute:@"informative-offset"],
informativeLabelWidth = [_alertPanel frame].size.width - inset.left - inset.right,
informativeLabelOriginY = [_messageLabel frameOrigin].y + [_messageLabel frameSize].height + CPAlertLabelOffset,
informativeLabelTextSize = [[_informativeLabel stringValue] sizeWithFont:[_informativeLabel font] inWidth:informativeLabelWidth];
[_informativeLabel setFrame:CGRectMake(inset.left, informativeLabelOriginY, informativeLabelTextSize.width, informativeLabelTextSize.height + sizeWithFontCorrection)];
}
/*! @ignore
*/
- (void)_layoutAccessoryView
{
if (_accessoryView)
{
var inset = [self currentValueForThemeAttribute:@"content-inset"],
accessoryViewWidth = [_alertPanel frame].size.width - inset.left - inset.right,
accessoryViewOriginY = CPRectGetMaxY([_informativeLabel frame]) + CPAlertLabelOffset;
[_accessoryView setFrameOrigin:CGPointMake(inset.left, accessoryViewOriginY)];
[[_alertPanel contentView] addSubview:_accessoryView];
}
}
/*! @ignore
*/
- (CGSize)_layoutButtonsFromView:(CPView)lastView
{
var inset = [self currentValueForThemeAttribute:@"content-inset"],
minimumSize = [self currentValueForThemeAttribute:@"size"],
buttonOffset = [self currentValueForThemeAttribute:@"button-offset"],
textWidth = offsetX - inset.left,
messageSize = [([_messageLabel stringValue] || " ") sizeWithFont:[_messageLabel font] inWidth:textWidth],
informationString = [_informativeLabel stringValue],
informativeSize = [(informationString || " ") sizeWithFont:[_informativeLabel font] inWidth:textWidth],
sizeWithFontCorrection = 6.0;
[_messageLabel setFrame:CGRectMake(inset.left, inset.top, textWidth, messageSize.height + sizeWithFontCorrection)];
[_informativeLabel setFrame:CGRectMake(inset.left, CGRectGetMaxY([_messageLabel frame]) + informativeOffset, textWidth, informativeSize.height + sizeWithFontCorrection)];
// Don't let an empty informative label partially cover the buttons.
[_informativeLabel setHidden:!informationString];
var aRepresentativeButton = _buttons[0],
buttonY = MAX(CGRectGetMaxY([_alertImageView frame]), CGRectGetMaxY(informationString ? [_informativeLabel frame] : [_messageLabel frame])) + buttonOffset; // the lower of the bottom of the text and the bottom of the icon.
aRepresentativeButton = [_buttons objectAtIndex:0],
panelSize = [_alertPanel frame].size,
buttonsOriginY,
offsetX;
[aRepresentativeButton setTheme:[self theme]];
[aRepresentativeButton sizeToFit];
// Make the window just tall enough to fit everything. Bit of a hack really.
var minimumSize = [self currentValueForThemeAttribute:@"size"],
desiredHeight = MAX(minimumSize.height, buttonY + CGRectGetHeight([aRepresentativeButton bounds]) + inset.bottom),
deltaY = desiredHeight - CGRectGetHeight(bounds),
frameSize = CGSizeMakeCopy([_alertPanel frame].size);
panelSize.height = CPRectGetMaxY([lastView frame]) + CPAlertLabelOffset + [aRepresentativeButton frameSize].height;
frameSize.height += deltaY;
[_alertPanel setFrameSize:frameSize];
if (panelSize.height < minimumSize.height)
panelSize.height = minimumSize.height;
var count = [_buttons count];
buttonsOriginY = panelSize.height - [aRepresentativeButton frameSize].height + buttonOffset,
offsetX = panelSize.width - inset.right;
while (count--)
for (var i = [_buttons count] - 1; i >= 0 ; i--)
{
var button = _buttons[count];
var button = _buttons[i];
[button setTheme:[self theme]];
[button sizeToFit];
var buttonBounds = [button bounds],
width = MAX(80.0, CGRectGetWidth(buttonBounds)),
height = CGRectGetHeight(buttonBounds);
var buttonFrame = [button frame],
width = MAX(80.0, CGRectGetWidth(buttonFrame)),
height = CGRectGetHeight(buttonFrame);
offsetX -= width;
[button setFrame:CGRectMake(offsetX, buttonY, width, height)];
[button setFrame:CGRectMake(offsetX, buttonsOriginY, width, height)];
offsetX -= 10;
}
panelSize.height += [aRepresentativeButton frameSize].height + inset.bottom + buttonOffset;
return panelSize;
}
/*! @ignore
*/
- (void)layoutPanel
{
if (!_alertPanel)
[self _createPanelWithStyle:nil];
var iconOffset = [self currentValueForThemeAttribute:@"image-offset"],
theTitle = @"",
theImage,
finalSize;
switch (_alertStyle)
{
case CPWarningAlertStyle:
theImage = [self currentValueForThemeAttribute:@"warning-image"];
theTitle = "Warning";
break;
case CPInformationalAlertStyle:
theImage = [self currentValueForThemeAttribute:@"information-image"];
theTitle = "Information";
break;
case CPCriticalAlertStyle:
theImage = [self currentValueForThemeAttribute:@"error-image"];
theTitle = @"Critical";
break;
}
if ([_alertPanel styleMask] == CPTitledWindowMask)
[_alertPanel setTitle:theTitle];
[_alertImageView setImage:theImage];
var imageSize = theImage ? [theImage size] : CGSizeMakeZero();
[_alertImageView setFrame:CGRectMake(iconOffset.x, iconOffset.y, imageSize.width, imageSize.height)];
[_alertPanel setFloatingPanel:YES];
[_alertPanel center];
[[_alertPanel contentView] setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
[self _layoutMessageView];
[self _layoutInformativeView];
[self _layoutAccessoryView];
finalSize = [self _layoutButtonsFromView:(_accessoryView || _informativeLabel)];
if (([_alertPanel styleMask] & CPDocModalWindowMask) || ([_alertPanel styleMask] & CPBorderlessWindowMask))
finalSize.height -= 26; // adjust the absence of title bar
//alert panel size resetting
[_alertPanel setFrameSize:finalSize];
}
/*!
Displays the \c CPAlert panel as a modal dialog. The user will not be
able to interact with any other controls until s/he has dismissed the alert
@@ -417,8 +431,8 @@ CPCriticalAlertStyle = 2;
*/
- (void)beginSheetModalForWindow:(CPWindow)window modalDelegate:(id)modalDelegate didEndSelector:(SEL)alertDidEndSelector contextInfo:(void)contextInfo
{
if (!(_windowStyle & CPDocModalWindowMask))
[self setWindowStyle:CPDocModalWindowMask];
if (!([_alertPanel styleMask] & CPDocModalWindowMask))
[self _createPanelWithStyle:CPDocModalWindowMask]
[self layoutPanel];
_didEndSelector = alertDidEndSelector;
+10 -5
View File
@@ -1422,15 +1422,19 @@ var themedButtonValues = nil,
+ (CPAlert)themedAlert
{
var alert = [CPAlert new],
size = CGSizeMake(400.0, 110.0),
inset = CGInsetMake(15, 15, 15, 80),
imageOffset = CGPointMake(15, 18),
buttonOffset = 10.0,
alertBezelColor = nil,
messageFont = [CPFont boldSystemFontOfSize:13.0],
informativeFont = [CPFont systemFontOfSize:12.0],
informationIcon = PatternImage("alert-info.png", 53.0, 46.0),
warningIcon = PatternImage("alert-warning.png", 53.0, 46.0),
errorIcon = PatternImage("alert-error.png", 53.0, 46.0);
errorIcon = PatternImage("alert-error.png", 53.0, 46.0),
themedAlertValues =
[
[@"size", size],
@@ -1444,14 +1448,15 @@ var themedButtonValues = nil,
[@"image-offset", imageOffset],
[@"information-image", informationIcon],
[@"warning-image", warningIcon],
[@"error-image", errorIcon]
[@"error-image", errorIcon],
[@"bezel-color", alertBezelColor],
[@"button-offset", buttonOffset]
];
[self registerThemeValues:themedAlertValues forView:alert];
return alert;
}
+ (CPStepper)themedStepper
{
var stepper = [CPStepper stepper],