Custom dialog response

I want to build my own dialog box, with my own button style, etc.
So I made a template for the box in the IB.

That makes, I can not use AS’s "set theAnswer to display dialog … " and “if button returned of theAnswer is …”

In my case I need a response of a button click in the dialog. In the documents I found only the ‘setAction’ instance for buttons. But it is not realy helpfull, because it let me not come back to the script for a further script flow.

Here is my attempt:


 on myHandler()
            -- Dialog for 2 or 3 buttons: 
                set theText to "Soll diese Namenliste auch in die Datenbank eingesetzt werden?"
                set theIcon to "AlertCautionIcon"
                set theWindowTitle to ""
                set theFont to "Lucida Grande"
                set theSize to 14
                set theButton1Title to ""
                set theButton2Title to "Nein"
                set theButton3Title to "Einsetzen"
                set theButton1setHidden to true
                displayMyDialog(theText,theIcon,theWindowTitle,theFont,theSize,theButton1Title,theButton2Title,theButton3Title,theButton1setHidden)
               
                (* There is no return for script flow !! It's a mouse trap.*)
            end myHandler


on displayMyDialog(aText,anIcon,aWindowTitle,aFont,aSize,aButton1Title,aButton2Title,aButton3Title,button1setHidden)
        dialogTextView's setStringValue_(aText)
        dialogTextView's setFont_(current application's NSFont's fontWithName_size_(aFont, aSize))
        dialogIcon's setImage_(current application's (NSImage's imageNamed_(anIcon)))
        dialogWindow's setTitle_(aWindowTitle)
                
        dialogButton1's setHidden_(button1setHidden)
        dialogButton1's setTitle_(aButton1Title)
        dialogButton2's setTitle_(aButton2Title)
        dialogButton3's setTitle_(aButton3Title)
        
        set dummy1 to dialogButton1's setAction_("button1")
        set dummy2 to dialogButton2's setAction_("button2")
        set dummy3 to dialogButton3's setAction_("button3")
        
        dialogWindow's makeKeyAndOrderFront_(me)
        
    end displayMyDialog
    
    
    on button1()
        --set buttonReturned to  dialogButton1's title()
        --log buttonReturned -- for testing
        -- do nothing else
        dialogWindow's orderOut_(me) -- close dialog
    end button1
    
    on button2()
        --set buttonReturned to  dialogButton2's title()
        --log buttonReturned -- for testing
        -- do nothing else
        dialogWindow's orderOut_(me) -- close dialog
    end button2

    on button3()
        set buttonReturned to  dialogButton3's title()
        --log "aButton3Title: " & buttonReturned -- for testing
        dialogWindow's orderOut_(me) -- close dialog
        -- do stuff:
        listenWindow's makeKeyAndOrderFront_(me) -- call a window
        oeffnenKursTitel() -- do stuff with it
        namenWindow's makeKeyAndOrderFront_(me) -- call a 2. window
        handleNamenListeEinfuegen2(theContents, theCount) -- do stuff with it
    end button3


Does anyone know a solution?

Heiner

You probably need to give all your button handlers an underscore and a sender parameter, like a normal IBAction.

I did it, it’s the same as in setAction handler. There is no return to the script flow.

I see several problems in your code, but I don’t know what changes you made already – you should repost your updated code, and report what is wrong. Does clicking the buttons do what you expect?

One thing wrong are these lines:

setAction: has a return type of void so you shouldn’t be setting anything to them – should just be dialogButton1’s setAction_(“button1:”). But usually, you just connect the action method to the button in IB, rather than doing it in code.

When you say the it is not coming back to allow further script flow, where do you mean? Does it come back to myHandler or not (did you try a log statement at the end of myHandler to see if it is executed)?

Ric

It might not just be the buttons 1, 2 and 3 missing an (sender) perhaps that myHandler() needs to be myHandler(sender) ???

I’d like to see the entire code too.

It doesn’t need to be, and would have to be redefined to have one underscore for each parameter (or pass an array or dictionary)

Ric

To make it a bit more clearly, myHandler is not a handler of it’s own. It is just a part of a longer handler.
I tried up to now this:


 -- Dialog for 2 or 3 buttons: 
                set theText to "Soll diese Namenliste auch in die Datenbank eingesetzt werden?"
                set theIcon to "myAlertCaution"
                set theWindowTitle to ""
                set theFont to "Lucida Grande"
                set theSize to 14
                set theButton1Title to ""
                set theButton2Title to "Nein"
                set theButton3Title to "Einsetzen"
                set theButton1setHidden to true
                       displayMyDialog(theText,theIcon,theWindowTitle,theFont,theSize,theButton1Title,theButton2Title,theButton3Title,theButton1setHidden)
               
                (* There is no return for script flow !! It's a mouse trap.*)

                 log buttonReturned -- (null) but buttonReturned is set as a global property: missing value !

                 if buttonReturned is "Einfügen" then -- title of button 3
                     tell current application to beep
                     dialogWindow's orderOut_(me) -- close dialog
                 end if




on displayMyDialog(aText,anIcon,aWindowTitle,aFont,aSize,aButton1Title,aButton2Title,aButton3Title,button1setHidden)
        --settings
        dialogTextView's setStringValue_(aText)
        dialogTextView's setFont_(current application's NSFont's fontWithName_size_(aFont, aSize))
        dialogIcon's setImage_(current application's (NSImage's imageNamed_(anIcon)))
        dialogWindow's setTitle_(aWindowTitle)
                
        dialogButton1's setHidden_(button1setHidden)
        dialogButton1's setTitle_(aButton1Title)
        dialogButton2's setTitle_(aButton2Title)
        dialogButton3's setTitle_(aButton3Title)
        
        -- to get which button is clicked and send back
        
        --set dummy1 to dialogButton1's setAction_("button1")
        --set dummy2 to dialogButton2's setAction_("button2")
              
        
        dialogWindow's makeKeyAndOrderFront_(me)
        
        dialogButton1's setAction_("button1")
        dialogButton2's setAction_("button2")
        dialogButton3's setAction_("button3")
        
    end displayMyDialog

--  for button 3 only:

on button3()
       set buttonReturned to dialogButton3's title()  -- buttonReturned is set as a property!
       log "aButton3Title: " & buttonReturned -- mess.: "aButton3Title: Einsetzen" -- it's OK
       -- return buttonReturned -- this doesn't work too
        dialogWindow's orderOut_(me) --  dialog will not close
    end button3


Meanwhile I’m not sure if the setAction instance is the correct one to solve that problem.

The setAction is fine, I think that you have a misunderstanding of how dialogs work. The standard display alert or display dialog commands, cause the code to stop executing until you press a button to dismiss the alert, so that the execution picks up with the next line. Your dialog box doesn’t do this – I’m not sure if there is an easy way to make the code pause until you dismiss your dialog. So, the way your code is right now, the “log buttonReturned” will get executed right after the call to displayMyDialog – it doesn’t wait until you push a button which is why it logs NULL.

One way to get around this is to put the code you want to start running after you press a button in a dialog in another handler that you call from a buttonPressed_(sender) handler. Notice that I have the same action set for all three buttons, and that the name buttonPressed: has a colon on the end So something like:

on applicationWillFinishLaunching_(aNotification)
		set theText to "Soll diese Namenliste auch in die Datenbank eingesetzt werden?"
		set theIcon to "myAlertCaution"
		set theWindowTitle to ""
		set theFont to "Lucida Grande"
		set theSize to 14
		set theButton1Title to ""
		set theButton2Title to "Nein"
		set theButton3Title to "Einsetzen"
		set theButton1setHidden to true
		displayMyDialog(theText, theIcon, theWindowTitle, theFont, theSize, theButton1Title, theButton2Title, theButton3Title, theButton1setHidden)
	end applicationWillFinishLaunching_
	
	on displayMyDialog(aText, anIcon, aWindowTitle, aFont, aSize, aButton1Title, aButton2Title, aButton3Title, button1setHidden)
		dialogTextView's setStringValue_(aText)
		dialogTextView's setFont_(current application's NSFont's fontWithName_size_(aFont, aSize))
		dialogIcon's setImage_(current application's (NSImage's imageNamed_(anIcon)))
		dialogWindow's setTitle_(aWindowTitle)
		dialogButton1's setHidden_(button1setHidden)
		dialogButton1's setTitle_(aButton1Title)
		dialogButton2's setTitle_(aButton2Title)
		dialogButton3's setTitle_(aButton3Title)
		dialogWindow's makeKeyAndOrderFront_(me)
		dialogButton1's setAction_("buttonPressed:")
		dialogButton2's setAction_("buttonPressed:")
		dialogButton3's setAction_("buttonPressed:")
		
	end displayMyDialog
	
	
	on buttonPressed_(sender) -- sender will be the button pressed
		log sender's |title|()
		dialogWindow's orderOut_(me)
		resumeRestOfProgram_(sender's |title|())
	end buttonPressed_
	
	on resumeRestOfProgram_(buttonTitle)
		if buttonTitle as string is "Einfügen" then
			log "pressed button3"
			--do what ever you want to happen next
		else if buttonTitle as string is "Nein" then
			log "pressed button2"
			--do something else
		end if
	end resumeRestOfProgram_

Ric

That’s it!
Ric, your setAction-handler buttonPressed_(sender) is very fine. But the resumeRestOfProgram_(buttonTitle) handler doesn’t solve the problem realy, it is the next mouse trap.

The AS’s set display dialog to … is something like a function which gets one or more value(s) returned.
But how?

I think I should better go back to the pre-defined AS’s dialogs (at least now).

Thank you very much for your help!

Heiner

Look for running windows in modal mode. I think that’s what you where looking for. It’s a method in your NSApp

EDIT: Choose file, display dialog, display alert and choose file names are all modal windows.

That’s right – it’s runModalForWindow:, after which only code triggered by the window or its buttons will run, until one of them sends stopModal or abortModal.

A new sunrise!

I’m not sure that running windows in modal mode will do what Heiner wants, although it could be used to make his dialog behave just like an applescript display dialog – because I’m not sure what he wants. What do you mean by " resumeRestOfProgram_(buttonTitle) is the next mouse trap". The code that I posted does return a value, the title of the button you pressed, and allows you to go on with the program based on that value. This is the same thing that an applescript display dialog does.

Running windows in modal mode prevent users from interacting with your main window until they do something in your dialog, but it sounds like your problem is not preventing the user from doing something, but with something not happening – your “mouse trap”. Perhaps you could describe how you want the program to flow after the user interacts with your dialog.

Ric

Well modal window starts a new event loop while the main event loop pauses. This also means that the command who started the new event loop (runModalForWindow) isn’t finished before the new event loop stops so it looks like the command is paused until stopModal command is given. This way dialogs can presented to the screen. If you have a popup button in your modal window you can simply connect that value with a property so even if the modal run is stopped you still can get the values.

so first you tell

on showDialog()
set theCode to runModalForWindow_(theWindow)
if theCode is 0 then
--button1 is pressed
return button1's |title|()
else if theCode is 1 then
--button2 is pressed
return button2's |title|()
else if theCode is 2 then
--button3 is pressed
return button3's |title|()
end if 
end

on button1()
stopModalWithCode_(0) --for OK for instance
end

on button2()
stopModalWithCode_(1) --for cancel for instance
end 

on button3()
stopModalWithCode_(2) --for other button for instance
end

p.s. there can be typos – behind a computer without xcode at the moment
Now the theCode is set above and then with some if statements you’ll now how the modal window has stopped.

It’s high noon! “With a little help…”

To Ric:

It means I have lost some values set in the script above. Of course they can be set as global properties and then your idea will work properly. But DJ Bazzie Wazzie’s idea needs a global too (I think).

I did that:



...
-- Dialog for 2 or 3 buttons: 
                set theText to "Soll diese Namenliste auch in die Datenbank eingesetzt werden?"
                set theIcon to "myAlertCaution"
                set theWindowTitle to ""
                set theFont to "Lucida Grande"
                set theSize to 14
                set theButton1Title to ""
                set theButton2Title to "Nein"
                set theButton3Title to "Einsetzen"
                set theButton1setHidden to true
                displayMyDialog(theText,theIcon,theWindowTitle,theFont,theSize,theButton1Title,theButton2Title,theButton3Title,theButton1setHidden)
                
                 if buttonReturned is "Einsetzen" then -- buttonReturned is a global property
					listenWindow's makeKeyAndOrderFront_(sender)
					oeffnenKursTitel()
					namenWindow's makeKeyAndOrderFront_(sender)
					handleNamenListeEinfuegen2(theContents, theCount)
                 else
                     -- do nothing; button "Nein"
                end if

...

and later:

on displayMyDialog(aText,anIcon,aWindowTitle,aFont,aSize,aButton1Title,aButton2Title,aButton3Title,button1setHidden)
        --settings
        dialogTextView's setStringValue_(aText)
        dialogTextView's setFont_(current application's NSFont's fontWithName_size_(aFont, aSize))
        dialogIcon's setImage_(current application's (NSImage's imageNamed_(anIcon)))
        dialogWindow's setTitle_(aWindowTitle)
                
        dialogButton1's setHidden_(button1setHidden)
        dialogButton1's setTitle_(aButton1Title)
        dialogButton2's setTitle_(aButton2Title)
        dialogButton3's setTitle_(aButton3Title)
        
        set theCode to current application's NSApp's runModalForWindow_(dialogWindow)
        
        if theCode is 1 then
            -- button 1 is pressed
            set buttonReturned to dialogButton1's |title|() as text 
           
            -- buttonReturned is a global for work flow above
            
        else if theCode is 2 then
            set buttonReturned to dialogButton2's |title|() as text 
        else if theCode is 3 then
            set buttonReturned to dialogButton3's |title|() as text
        end if 
    end displayMyDialog

    
    on dialogButton1_(sender)
        current application's NSApp's stopModalWithCode_(1) 
        dialogWindow's orderOut_(me)
    end dialogButton1_

    on dialogButton2_(sender)
        current application's NSApp's stopModalWithCode_(2) 
        dialogWindow's orderOut_(me)
    end dialogButton2_

    on dialogButton3_(sender)
        current application's NSApp's stopModalWithCode_(3) 
        dialogWindow's orderOut_(me)
    end dialogButton3_



It works properly.

Thanks to all of you.

Heiner