Open alert box (NASlert) with a delegate and block all other windows? - objective-c

Is there a way to open a NSAlert window, set a delegate for didEnd callback and while the alert is shown, all other windows should be "disabled" (can the window itself but not push any button or change any text)?

In your NSAlert code add
NSModalSession session = [NSApp beginModalSessionForWindow:theWindow];
[NSApp runModalSession:session];
// NSAlert stuff here ...
In your didEnd callback add
[NSApp endModalSession:session];
For more information about modal windows read NSApplication's "Managing the Event Loop" section.
Update:
Here is a sample code from Apple's doc showing how to run modal without callbacks.
NSModalSession session = [NSApp beginModalSessionForWindow:theWindow];
for (;;) {
if ([NSApp runModalSession:session] != NSRunContinuesResponse)
break;
[self doSomeWork];
}
[NSApp endModalSession:session];

Related

Transient NSPopover swallows first click on parent window control

I have a transient NSPopover and when it's open and I click a button in the parent window the popover is dismissed instead and the click "swallowed". Only the second click on the button triggers the action properly.
Is there a way to pass the first click through to the control directly and dismiss the popover in one step?
NSPopover does seem to swallow the event for the targeted position view. Other views are fine. My solution is to get the delegate to forward the last mouse down event to the target view if hit testing reveals it was the clicked view. Unfortunatety NSApp -currentEvent is nil when the delegate gets messaged - not sure why. So I added an event monitor to the app delegate like so:
- (void)addEventMonitor
{
if (self.eventMonitor) {
return;
}
self.eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:(NSLeftMouseDownMask) handler:^(NSEvent *incomingEvent) {
NSEvent *result = incomingEvent;
self.monitoredEvent = result;
return result;
}];
}
When the delegate closes it checks to see it the last monitored event was in the target:
- (void)popoverDidClose:(NSNotification *)notification
{
// [NSApp currentEvent] is nil here
NSEvent *event = [(BPApplicationDelegate *)[NSApp delegate] monitoredEvent];
if (event && (event.type & NSLeftMouseDown)) {
NSPoint pt = [self.targetView.superview convertPoint:event.locationInWindow fromView:nil];
if ([self.TargetView hitTest:pt]) {
[NSApp postEvent:event atStart:NO];
}
}
}

Cocoa performClick on button waiting for next external event

I have NSAlert instance which i run as modal for user confirmation on cancelling some operation. When user doesn't respond and the operations gets completed , i need to close this modal window. So, for this i'm calling performClick on default button in alert. But i observe that the perform click doesnt get executed instantly but waits for some external event such as mouse move event. Why is this happening? Apart from posting fake event, what are the other solutions?
Here is what you need to do.
Assumption:
1. IBAction is connect to NSButton Which will display the Alert View after clicking upon it.
2. It will perform Click operation by itself on the Second button of the Alert View.
Hope the below code will help you....
- (IBAction)showAlert:(id)sender
{
//display the alert
self.myAlert = [NSAlert alertWithMessageText:#"Sample Test" defaultButton:#"OK" alternateButton:#"DO Nothing" otherButton:#"CANCEL" informativeTextWithFormat:#"TEST",nil];
[self.myAlert beginSheetModalForWindow:[self window]
modalDelegate:self
didEndSelector:#selector(errorAlertDidEnd:returnCode:contextInfo:)
contextInfo:nil];
NSArray *buttonArray = [self.myAlert buttons];
NSLog(#"Button Arrays %#",buttonArray);
//Close by itself without a mouse click by the user
//Assuming the Default Button as the Second one "Do Nothing
NSButton *myBtn = [buttonArray objectAtIndex:2];
[myBtn performClick:self.myAlert];
}
- (void)errorAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
NSLog(#"TEST");
}
To know which button is clicked you can modify the mTo know which button is clicked you can modify the method errorAltertDidEnd
- (void)errorAlertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
if(returnCode == NSAlertAlternateReturn)
{
NSLog(#"TEST Alternate %ld",returnCode);
}
if(returnCode == NSAlertDefaultReturn)
{
NSLog(#"TEST Default %ld",returnCode);
}
if(returnCode == NSAlertOtherReturn)
{
NSLog(#"Test Other %ld",returnCode);
}
}
Could you please elaborate on this "But the click event(generated from performClick) itself waits for some external event(example: mouse move) –"

Running NSAlert within NSPopover

we are developing a menu bar item app,
and I'd love to write a NSAlert category which shows the alert within a NSPopover, that appears below the NSStatusItem.
So far, the category implements the following new method:
- (void) runAsMenuItemPopUpWithCompletionBlock:(NSAlertCompletionBlock)block {
// Get content view of NSAlert
NSView *alertContentView = [self.window contentView];
// Ask the menu item to show the view as a NSPopOver
[[GFMenuItem sharedInstance] popOverView:alertContentView];
// (...) Handle response with callback
}
But opening an alert
NSAlert *alert = [NSAlert alertWithMessageText:#"Learn more?" defaultButton:#"Learn more" alternateButton:#"Cancel" otherButton:nil informativeTextWithFormat:#"Do you want to view detailed information?"];
[alert runAsMenuItemPopUpWithCompletionBlock:nil];
results in the following visualization:
The problem is the third empty button, the help button and the Checkbox, which have not been set up to be shown. Any idea on how to get rid of them if they have not been set up?
Turns out you can call [alert layout] to trigger the manual layout processing. It will hide any buttons which aren't set up to show!
Corrected method:
- (void) runAsMenuItemPopUpWithCompletionBlock:(NSAlertCompletionBlock)block {
// Trigger the layout processing and get content view of NSAlert
[self layout];
NSView *alertContentView = [self.window contentView];
// Ask the menu item to show the view as a NSPopOver
[[GFMenuItem sharedInstance] popOverView:alertContentView];
// (...) Handle response with callback
}

How do I quit a mac application programmatically?

I need to add a quit-button to my application that runs from the menubar in mac.
How do I programmatically quit an application in mac?
There is a simpler form to quit from code:
[NSApp terminate:self];
But as you're adding a button, all you have to do is to control drag from your button to the Application icon and connect the method terminate:.
[[NSApplication sharedApplication] terminate:self];
Try the following:
[NSApp terminate: nil];
In some case you cannot close app when call [NSApp terminate:self];. Like when showing NSAlert as sheet on the doc window (NSAlert -beginSheetModalForWindow:completionHandler:) ...
You can close all window and alert before call terminate, like following code:
for (NSWindow *window in [NSApplication sharedApplication].windows) {
[window close];
}
[NSApp terminate:self];

Can't open a sheet on a window twice

I'm opening a sheet on a window , the first time the sheet opens
correctly, but if I close it, and try to open again it doesn't work, I
just get the system alert sound.
- (IBAction) showSpeedSheet:(id)sender
{
[NSApp beginSheet:addEditPackagePanel
modalForWindow:[[NSApp delegate] window]
modalDelegate:nil
didEndSelector:nil
contextInfo:nil];
}
-(IBAction)endSpeedSheet:(id)sender
{
[NSApp endSheet:addEditPackagePanel];
[addEditPackagePanel orderOut:sender];
}
I can't find what's wrong, the app doesn't print any error on the log.
A delegate is not required.
The beep occurs because the system believes there is already a sheet open on the window (whether or not that sheet is technically visible). It's not the greatest error reporting, but that's what it is.
In my code sheets have window controllers and I do both of the following steps in every action that is attached to a sheet-closing button:
[NSApp endSheet:[windowController window]];
[windowController close];
With these steps, subsequent sheets are able to display without beeping.
I think you may need to implement the modal delegate and didEndSelector. The orderOut should be called from the did-end selector.
[NSApp beginSheet:addEditPackagePanel
modalForWindow:[[NSApp delegate] window]
modalDelegate: self
didEndSelector: #selector(didEndSheet:returnCode:contextInfo:)
contextInfo: nil];
and
- (void)didEndSheet:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo
{
[sheet orderOut:self];
}
I believe control is sent to the did-end selector has soon as endSheet is called.