Sliding Dialogs (Sheets) for MacOS X Leopard Topic is solved

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
Post Reply
joric
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sat Jan 26, 2008 12:52 am
Location: Palo Alto, CA
Contact:

Sliding Dialogs (Sheets) for MacOS X Leopard

Post by joric »

Is there a way to get a sliding dialog like this (on MacOS):

Image

(This one from msw Safari, but you know what I'm saying.) Leopard is crammed full of such dialogs.
Last edited by joric on Wed May 07, 2008 7:31 pm, edited 4 times in total.
Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria »

You'd need to use native code (a very similar question poped-up recently, if you search a bit you can probably find it. not sure if it contained any additional info though)
joric
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sat Jan 26, 2008 12:52 am
Location: Palo Alto, CA
Contact:

Post by joric »

Auria wrote:You'd need to use native code (a very similar question poped-up recently, if you search a bit you can probably find it
Can't find anything related =( Could you please find it? Sorry for inconvenience.
Ryan Wilcox
I live to help wx-kind
I live to help wx-kind
Posts: 194
Joined: Mon Aug 30, 2004 1:26 pm
Location: PA, USA
Contact:

Post by Ryan Wilcox »

They're called Sheets in OS X Terminology.

I thought there was this capacity in wxMac already, I'll do some more research tonight....
Ryan Wilcox
Wilcox Development Solutions
http://www.wilcoxd.com
Ryan Wilcox
I live to help wx-kind
I live to help wx-kind
Posts: 194
Joined: Mon Aug 30, 2004 1:26 pm
Location: PA, USA
Contact:

Post by Ryan Wilcox »

Right, so I guess it's not in wx itself.

Apple's Dev Docs should help a bit here, but I think something like this should work (but untested...)

Code: Select all


bool MySheetWindow::Show(bool visible)
{
   if (visible)
   {
     ::ShowSheetWindow( MacGetWindowRef(), GetParent()->MacGetWindowRef() );
     Refresh();
   }
   else
    ::HideSheetWindow( MacGetWindowRef() );

  return true;
}

//Then just construct a MySheetWindow in the normal way, making sure to specify, as the parent parameter, the window you want to be "under" the sheet.

//(OR, if the parent doesn't work, make a freshly named instance variable...)
 
It's important to override the Show() method (and NOT call inherited) because we're showing the sheet in a different way.[/code]
Ryan Wilcox
Wilcox Development Solutions
http://www.wilcoxd.com
joric
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sat Jan 26, 2008 12:52 am
Location: Palo Alto, CA
Contact:

Post by joric »

Thank you very much, that's it!
One more question: should it be done by patching wx source or it's possible to do locally? I didn't deal with subclassing in wx.
Ryan Wilcox
I live to help wx-kind
I live to help wx-kind
Posts: 194
Joined: Mon Aug 30, 2004 1:26 pm
Location: PA, USA
Contact:

Post by Ryan Wilcox »

I meant for it to be used in a subclass of wxFrame. It's not that hard :-)
Ryan Wilcox
Wilcox Development Solutions
http://www.wilcoxd.com
joric
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sat Jan 26, 2008 12:52 am
Location: Palo Alto, CA
Contact:

Post by joric »

I've searched in the forum about ShowSheetWindow and got this topic: http://forums.wxwidgets.org/viewtopic.php?t=19076
As there were no implementation details i've hacked a source.

Code: Select all

//wxWidgets-2.8.7/src/mac/carbon/dialog.cpp
...
bool wxDialog::Show(bool show)
{

/*
    if ( !wxDialogBase::Show(show) )
        // nothing to do
        return false;
*/

	wxFrame * dialog = (wxFrame*)this;
	wxFrame * frame = (wxFrame*)GetParent();

	if (show)
	{
		SetWindowClass (
			(WindowRef) dialog->MacGetWindowRef(),
			kSheetWindowClass );

		ShowSheetWindow (
			(WindowRef) dialog->MacGetWindowRef(),
			(WindowRef) frame->MacGetWindowRef()); 
	} 
	else
	{
		HideSheetWindow(
			(WindowRef) dialog->MacGetWindowRef());
	}

...
Then I've recompiled entire wxWidgets with make && sudo make install (one single file, so it's pretty fast) and it works like a charm!
To remove dialog captions you have to replace wxDEFAULT_DIALOG_STYLE to wxBORDER_NONE (an empty style gives ugly thin black border).
I didn't make a sleeker implementation, but it worth to be implemented as, say, additional dialog style in any next release of wxMac.
Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria »

That sounds like a great possibility indeed. Maybe it could be submitted on the feature request tracker
joric
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sat Jan 26, 2008 12:52 am
Location: Palo Alto, CA
Contact:

Post by joric »

I've made a patch.

Code: Select all

Name: wxWidgets 2.8.7 patch to enable MacOS sliding dialogs (sheets)
Version: 0.0.1
Usage: add wxBORDER_NONE style to dialog (does not affect anything else)
Maintainers: Joric (http://joreg.livejournal.com) 
===================================================================
--- wxWidgets-2.8.7/src/mac/dialog.cpp	2007-11-21 17:43:24.000000000 +0500
+++ wxWidgets-2.8.7/src/mac/dialog.cpp	2008-05-06 22:16:04.000000000 +0600
@@ -49,6 +49,9 @@
 
     // ...but not these styles
     style &= ~(wxYES | wxOK | wxNO); // | wxCANCEL
+    
+    if (style & wxBORDER_NONE)
+    	style = wxBORDER_NONE;
 
     if ( !wxTopLevelWindow::Create( parent, id, title, pos, size, style, name ) )
         return false;
@@ -106,9 +109,20 @@
 
 bool wxDialog::Show(bool show)
 {
-    if ( !wxDialogBase::Show(show) )
-        // nothing to do
-        return false;
+     if ( ( GetWindowStyleFlag() & wxBORDER_NONE ) && GetParent() )
+     {
+        if (show)
+        {
+            SetWindowClass ( (WindowRef) MacGetWindowRef(), kSheetWindowClass );
+
+            ShowSheetWindow ( (WindowRef) MacGetWindowRef(),
+                (WindowRef) ( ( wxFrame* ) GetParent() )->MacGetWindowRef() );
+        }
+        else
+            HideSheetWindow( (WindowRef) MacGetWindowRef() );
+    }
+    else
+    {
+        if ( !wxDialogBase::Show(show) )
+            return false;          
+    }
 
     if ( show )
         // usually will result in TransferDataToWindow() being called
Last edited by joric on Wed May 07, 2008 1:02 am, edited 1 time in total.
joric
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sat Jan 26, 2008 12:52 am
Location: Palo Alto, CA
Contact:

Post by joric »

Unfortunately there is a significant difference between these sheets and native leopard sheets. They are not semi-transparent and have bigger shadow (i've tried several styles). I don't know why, does anybody know?

Image

P.S. It maybe because i've linked in 10.4u compatibility mode, haven't checked with 10.5 SDK yet (it tooks awful amount of time to recompile the whole lib). But I think it's not the real case... Btw it looks much more native on Tiger.

P.P.S. I've tried to use style = 0, it gives the proper (small) shadow, but no transparency and the black border returned. Do you know a proper style for it?
joric
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sat Jan 26, 2008 12:52 am
Location: Palo Alto, CA
Contact:

Post by joric »

One more patch (for alerts / message dialogs). Looks like transparent, compiled on 10.4u.

Image

Code: Select all

Name: wxWidgets 2.8.7 patch to enable "Sheet" style for MacOS message dialogs
Version: 0.0.1
Usage: affects all message dialogs, no flags needed
Maintainers: Joric (http://joreg.livejournal.com) 
=================================================================== 
--- wxWidgets-2.8.7/src/mac/msgdlg.cpp	2007-11-21 18:43:25.000000000 +0500
+++ wxWidgets-2.8.7/src/mac/msgdlg.cpp	2008-05-07 06:39:28.000000000 +0600
@@ -34,6 +34,42 @@
     SetMessageDialogStyle(style);
 }
 
+pascal OSStatus mySheetHandler(  EventHandlerCallRef inRef,
+                                                EventRef inEvent,
+                                                void* userData)
+{
+
+    ControlRef control;
+    WindowRef sheet;
+    WindowRef parent;
+    UInt32 cmd;
+    GetEventParameter(inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &control);
+    GetControlCommandID(control, &cmd);    
+    sheet = GetControlOwner(control);
+    GetSheetWindowParent(sheet, &parent);
+    HideSheetWindow(sheet);
+    DisposeWindow(sheet);
+    QuitAppModalLoopForWindow(parent);
+    short result = -1;
+    switch(cmd)	
+    {
+        case kHICommandOK:
+            result = 1;
+        break;
+				
+        case kHICommandCancel:
+            result = 2;
+        break;
+								
+        case kHICommandOther:
+            result = 3;
+        break;						
+    }							
+    *(short*)userData = result;				
+    return noErr;
+}
+
+
 int wxMessageDialog::ShowModal()
 {
     int resultbutton = wxID_CANCEL;
@@ -177,9 +213,13 @@
         param.position = kWindowDefaultPosition;
         if ( !skipDialog )
         {
-            DialogRef alertRef;
-            CreateStandardAlert( alertType, cfTitle, cfText, &param, &alertRef );
-            RunStandardAlert( alertRef, NULL, &result );
+            DialogRef sheet = NULL;
+	     static EventTypeSpec controlEvent = { kEventClassControl, kEventControlHit };          
+            WindowRef parent = (WindowRef)((wxTopLevelWindow*) m_parent)->MacGetWindowRef();						
+            CreateStandardSheet(alertType, cfTitle, cfText, &param, GetWindowEventTarget(parent), &sheet);
+            InstallWindowEventHandler(GetDialogWindow(sheet), NewEventHandlerUPP(mySheetHandler), 1, &controlEvent, &result, NULL);
+            ShowSheetWindow(GetDialogWindow(sheet), parent);
+            RunAppModalLoopForWindow(parent);
         }
         else
         {
A bit agressive patch =) Turns all modal message boxes to "sheets".
Ryan Wilcox
I live to help wx-kind
I live to help wx-kind
Posts: 194
Joined: Mon Aug 30, 2004 1:26 pm
Location: PA, USA
Contact:

Post by Ryan Wilcox »

My thoughts are two fold: a) you don't really want all your dialogs turning into sheets (and what if there's no window up?) b) BUT usually Stefan Csomor (the lead wxMac guy) is really patient about working to get a patch right.
Ryan Wilcox
Wilcox Development Solutions
http://www.wilcoxd.com
joric
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sat Jan 26, 2008 12:52 am
Location: Palo Alto, CA
Contact:

Post by joric »

Subclassed version, no need to patch now.
If you meant this kind of subclassing (i.e. copypasting), it may for sure follow compatibility issues with future versions - dunno about wxFrame code tho, maybe it's better structured. BTW it does not allow to use DECLARE_DYNAMIC_CLASS or IMPLEMENT_DYNAMIC_CLASS, don't know why ("no appropriate default constructor available" even with wxMyMessageDialog(){}). But since XRC does not support message boxes it probably doesn't matter.

Code: Select all

// wxMyMessageDialog.h (for 2.8.7) enables "sheets" on MacOS
// compiled and checked on MacOS 10.4.4 and MacOS 10.5.1, both with 10.4u SDK

#if defined(__WXMAC__) && !defined(__WXMAC_CLASSIC__) && !defined(TARGET_API_MAC_OSX)

#include "wx/wxprec.h"

#include "wx/msgdlg.h"

#ifndef WX_PRECOMP
    #include "wx/intl.h"
    #include "wx/app.h"
#endif

#include "wx/mac/uma.h"

pascal OSStatus mySheetHandler(EventHandlerCallRef inRef, EventRef inEvent, void* userData)
{
    ControlRef control;
    WindowRef sheet;
    WindowRef parent;
    UInt32 cmd;
    GetEventParameter(inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &control);
    GetControlCommandID(control, &cmd);   
    sheet = GetControlOwner(control);
    GetSheetWindowParent(sheet, &parent);
    HideSheetWindow(sheet);
    DisposeWindow(sheet);
    QuitAppModalLoopForWindow(parent);
    short result = -1;
    switch(cmd)       
    {
        case kHICommandOK:
            result = 1;
        break;
                            
        case kHICommandCancel:
            result = 2;
        break;
                                                        
        case kHICommandOther:
            result = 3;
        break;      
    }             
    *(short*)userData = result;                               
    return noErr;
} 

class wxMyMessageDialog : public wxMessageDialog
{           
    public:
    wxMyMessageDialog(wxWindow *parent, const wxString& message, const wxString& caption = wxMessageBoxCaptionStr, 
    long style = wxOK|wxCENTRE, const wxPoint& pos = wxDefaultPosition)
    : wxMessageDialog(parent, message, caption, style, pos){};

    int ShowModal() 
    {
        int resultbutton = wxID_CANCEL;

        const long style = GetMessageDialogStyle();

        wxASSERT_MSG( (style & 0x3F) != wxYES, wxT("this style is not supported on Mac") );

        AlertType alertType = kAlertPlainAlert;
        if (style & wxICON_EXCLAMATION)
            alertType = kAlertCautionAlert;
        else if (style & wxICON_HAND)
            alertType = kAlertStopAlert;
        else if (style & wxICON_INFORMATION)
            alertType = kAlertNoteAlert;
        else if (style & wxICON_QUESTION)
            alertType = kAlertNoteAlert;

        
        short result;

        AlertStdCFStringAlertParamRec param;
        wxMacCFStringHolder cfNoString( _("No"), m_font.GetEncoding() );
        wxMacCFStringHolder cfYesString( _("Yes"), m_font.GetEncoding() );

        wxMacCFStringHolder cfTitle( m_caption, m_font.GetEncoding() );
        wxMacCFStringHolder cfText( m_message, m_font.GetEncoding() );

        param.movable = true;
        param.flags = 0;
        param.version = kStdCFStringAlertVersionOne;

        bool skipDialog = false;

        if (style & wxYES_NO)
        {
            if (style & wxCANCEL)
            {
                param.defaultText = cfYesString;
                param.cancelText = (CFStringRef) kAlertDefaultCancelText;
                param.otherText = cfNoString;
                param.helpButton = false;
                param.defaultButton = style & wxNO_DEFAULT ? kAlertStdAlertOtherButton : kAlertStdAlertOKButton;
                param.cancelButton = kAlertStdAlertCancelButton;
            }
            else
            {
                param.defaultText = cfYesString;
                param.cancelText = NULL;
                param.otherText = cfNoString;
                param.helpButton = false;
                param.defaultButton = style & wxNO_DEFAULT ? kAlertStdAlertOtherButton : kAlertStdAlertOKButton;
                param.cancelButton = 0;
            }
        }
        // the MSW implementation even shows an OK button if it is not specified, we'll do the same
        else
        {
            if (style & wxCANCEL)
            {
                // that's a cancel missing
                param.defaultText = (CFStringRef) kAlertDefaultOKText;
                param.cancelText = (CFStringRef) kAlertDefaultCancelText;
                param.otherText = NULL;
                param.helpButton = false;
                param.defaultButton = kAlertStdAlertOKButton;
                param.cancelButton = 0;
            }
            else
            {
                param.defaultText = (CFStringRef) kAlertDefaultOKText;
                param.cancelText = NULL;
                param.otherText = NULL;
                param.helpButton = false;
                param.defaultButton = kAlertStdAlertOKButton;
                param.cancelButton = 0;
            }
        }

        param.position = kWindowDefaultPosition;
        if ( !skipDialog )
        {          
            if (m_parent == NULL) 
            {
                // if no parent, use the old version
       	    DialogRef alertRef;
                CreateStandardAlert( alertType, cfTitle, cfText, &param, &alertRef );
                RunStandardAlert( alertRef, NULL, &result );
            }
            else 
            {
                // else use the new one
    	    DialogRef sheet = NULL;
    	    static EventTypeSpec controlEvent = { kEventClassControl, kEventControlHit };         
                WindowRef parent = (WindowRef)((wxTopLevelWindow*) m_parent)->MacGetWindowRef();                  
                CreateStandardSheet(alertType, cfTitle, cfText, &param, GetWindowEventTarget(parent), &sheet);
                InstallWindowEventHandler(GetDialogWindow(sheet), NewEventHandlerUPP(mySheetHandler), 1, 
                    &controlEvent, &result, NULL);
                ShowSheetWindow(GetDialogWindow(sheet), parent);
                RunAppModalLoopForWindow(parent); 
            }
        }
        else
        {
            return wxID_CANCEL;
        }

        if (style & wxOK)
        {
            switch ( result )
            {
            case 1:
                resultbutton = wxID_OK;
                break;

            case 2:
                // TODO: add Cancel button
                // if (style & wxCANCEL)
                //     resultbutton = wxID_CANCEL;
                break;

            case 3:
            default:
                break;
            }
        }
        else if (style & wxYES_NO)
        {
            switch ( result )
            {
            case 1:
                resultbutton = wxID_YES;
                break;

            case 2:
                if (!(style & wxCANCEL))
                    resultbutton = wxID_CANCEL;
                break;

            case 3:
                resultbutton = wxID_NO;
                break;

            default:
                break;
            }
        }        
    return resultbutton;
    }
};

#else 

class wxMyMessageDialog : public wxMessageDialog
{           
    public:
    wxMyMessageDialog(wxWindow *parent, const wxString& message, 
    const wxString& caption = wxMessageBoxCaptionStr, long style = wxOK|wxCENTRE, 
        const wxPoint& pos = wxDefaultPosition) 
    : wxMessageDialog(parent, message, caption, style, pos) {};
};

#endif
joric
Earned a small fee
Earned a small fee
Posts: 11
Joined: Sat Jan 26, 2008 12:52 am
Location: Palo Alto, CA
Contact:

Post by joric »

Post Reply