Difference between revisions of "WxMac-specific topics"

From WxWiki
Jump to navigation Jump to search
m (Text replacement - "<source>" to "<syntaxhighlight lang="cpp">")
 
(43 intermediate revisions by 18 users not shown)
Line 1: Line 1:
See also the [http://www.wxwidgets.org/docs/faqmac.htm wxMac FAQ], and [http://www.wxwidgets.org/wiki/index.php/Getting_started_on_OS_X Getting Started on OS X]
+
See also the [https://www.wxwidgets.org/docs/faq/osx/ wxMac FAQ]
  
 +
== Built-In Versions ==
  
 +
MacOS ships with the wxWidgets libraries.
 +
 +
* 10.4 has wxWidgets 2.5
 +
* 10.5 has wxWidgets 2.8.4
 +
* 10.6 has wxWidgets 2.8.8
 +
* 10.7 does not ship with wxWidgets.  Apps linked to system libraries on 10.4-10.6 will not run.
  
 
== My app can't be brought to the front! ==
 
== My app can't be brought to the front! ==
Line 9: Line 16:
 
an [[WxMac_Issues#Building_a_MacOSX_application_bundle|application bundle]] (there is a section about this below). The most easy way to get a bundle is to use an "Application" target in xCode (a "shell tool" target will not produce a bundle).
 
an [[WxMac_Issues#Building_a_MacOSX_application_bundle|application bundle]] (there is a section about this below). The most easy way to get a bundle is to use an "Application" target in xCode (a "shell tool" target will not produce a bundle).
 
Another possible way to solve this quickly is to add the following
 
Another possible way to solve this quickly is to add the following
lines of code to your app :
+
lines of code to your app (carefully wrapped in #ifdef __WXMAC__ [..] #endif) :
  
<source>
+
<syntaxhighlight lang="cpp">
 
#include <ApplicationServices/ApplicationServices.h>
 
#include <ApplicationServices/ApplicationServices.h>
  
Line 17: Line 24:
 
GetCurrentProcess(&PSN);
 
GetCurrentProcess(&PSN);
 
TransformProcessType(&PSN,kProcessTransformToForegroundApplication);
 
TransformProcessType(&PSN,kProcessTransformToForegroundApplication);
</source>
+
</syntaxhighlight>
 +
 
 +
This is also covered in the [https://www.wxwidgets.org/docs/faq/osx/#nofocus official wxMac FAQ]
  
 
== The Mac OS menu bar ==
 
== The Mac OS menu bar ==
 +
 +
'''Warning''' : it was reported that calling ->SetMenuBar() before filling the menu bar object with items may result in these special items not working properly. Similarly, appending a wxMenu to the wxMenuBar before the wxMenu was filled with items may cause problems
 +
 +
=== Single menu bar ===
  
 
All versions of Mac OS have a single menu bar at the top of
 
All versions of Mac OS have a single menu bar at the top of
Line 26: Line 39:
 
Mac windows never have their own menu bar.
 
Mac windows never have their own menu bar.
  
wxMac handles this automatically by displaying the
+
'''wxMac handles this automatically''' by displaying the
 
menu bar associated with each wxFrame at the top of the
 
menu bar associated with each wxFrame at the top of the
 
screen whenever that frame is in front.  If your program
 
screen whenever that frame is in front.  If your program
Line 32: Line 45:
 
to the front, that frame's menu bar will be switched in.
 
to the front, that frame's menu bar will be switched in.
  
Warning: be careful of modeless windows which do not have
+
'''Warning''': be careful of modeless windows which do not have
 
menu bars.  (Modal dialogs are okay.)  If a modeless window
 
menu bars.  (Modal dialogs are okay.)  If a modeless window
 
gets brought to the front and it doesn't have a menu bar,
 
gets brought to the front and it doesn't have a menu bar,
Line 44: Line 57:
 
(maybe with only a Quit item).
 
(maybe with only a Quit item).
  
On the Mac, the name of the item which exits the program
+
=== Keyboard Shortcuts ===
is traditionally called "Quit" instead of "Exit".
 
wxMac handles this for you - just name the item "Exit"
 
and wxMac will change it for you.
 
 
 
 
Keyboard shortcuts use the command-key (the cloverleaf or
 
Keyboard shortcuts use the command-key (the cloverleaf or
 
open-apple key on the keyboard) instead of the control
 
open-apple key on the keyboard) instead of the control
Line 56: Line 65:
 
the accelerator will be removed and the Ctrl will be
 
the accelerator will be removed and the Ctrl will be
 
replaced with a cloverleaf in the menu.
 
replaced with a cloverleaf in the menu.
 +
 +
=== Special Menu Items ===
  
 
The layout of Mac menus is slightly different, and here
 
The layout of Mac menus is slightly different, and here
 
is where you will have to add a line of code to your
 
is where you will have to add a line of code to your
 
program to accomodate it.  On the Mac, the "About"
 
program to accomodate it.  On the Mac, the "About"
menu item should go in a particular place (in the
+
menu item should go in the program menu; same thing for
Apple menu on Mac OS 8/9, or in the program menu
+
the "Quit", "Help" and "Preferences" menu items.
on Mac OS X).  wxMac will automatically move it for
 
you if you tell it the ID of your About item:
 
  
<source>
+
There are two ways to handle this :
 +
 
 +
==== Let wx identify menu items by their ID ====
 +
Here is a list of the Mac IDs that might have to be tweaked if you are not yet using their standard names (from defs.h):
 +
 
 +
<syntaxhighlight lang="cpp">
 +
wxID_ABOUT,
 +
wxID_EXIT,
 +
wxID_PREFERENCES,
 +
wxID_HELP
 +
</syntaxhighlight>.
 +
 
 +
These constants are not Mac-specific, they can be used on all platforms.
 +
 
 +
See also the [https://www.wxwidgets.org/docs/faq/osx/ wxMac FAQ].
 +
 
 +
* Example of how to catch about and preferences events:
 +
<syntaxhighlight lang="cpp">
 +
#include <wx/wx.h>
 +
 
 +
class MyApp: public wxApp
 +
{
 +
    bool OnInit();
 +
   
 +
    wxFrame *frame;
 +
public:
 +
    DECLARE_EVENT_TABLE()
 +
   
 +
    void OnAbout(wxCommandEvent& evt);
 +
    void OnPrefs(wxCommandEvent& evt);
 +
};
 +
 
 +
IMPLEMENT_APP(MyApp)
 +
 
 +
BEGIN_EVENT_TABLE(MyApp, wxApp)
 +
EVT_MENU(wxID_ABOUT, MyApp::OnAbout)
 +
EVT_MENU(wxID_PREFERENCES, MyApp::OnPrefs)
 +
END_EVENT_TABLE()
 +
 
 +
bool MyApp::OnInit()
 +
{
 +
    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
 +
    frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Hello wxWidgets"), wxPoint(50,50), wxSize(800,600));
 +
 +
    wxMenuBar* menubar = new wxMenuBar();
 +
    wxMenu* testmenu = new  wxMenu();
 +
 +
    testmenu->Append(wxID_ANY, _("New\tCtrl-N"));
 +
    testmenu->Append(wxID_ANY, _("Open\tCtrl-O"));
 +
   
 +
    testmenu->Append(wxID_ABOUT, _("About"));
 +
    testmenu->Append(wxID_HELP, _("Help"));
 +
    testmenu->Append(wxID_PREFERENCES, _("Preferences"));
 +
    testmenu->Append(wxID_EXIT, _("Exit"));
 +
   
 +
    menubar->Append(testmenu, _("File"));
 +
   
 +
    frame->SetMenuBar(menubar);
 +
    frame->Show();
 +
   
 +
   
 +
    return true;
 +
}
 +
 
 +
void MyApp::OnAbout(wxCommandEvent& evt)
 +
{
 +
    wxMessageBox(_("About this application... not much to say for such a simple example, really"));
 +
}
 +
 
 +
void MyApp::OnPrefs(wxCommandEvent& evt)
 +
{
 +
    wxMessageBox(_("Here are preferences. Not much to configure in this simple example =)"));
 +
}
 +
</syntaxhighlight>
 +
 
 +
==== Let wx identify menu items by their name ====
 +
This is another way, perhaps less convenient than the IDs method described above.
 +
 
 +
A menu item named "Exit" will be moved to the appropriate location for you.
 +
 
 +
The same is true for the About menu, and you tell wx the name of your
 +
about menu this way :
 +
<syntaxhighlight lang="cpp">
 
#ifdef __WXMAC__
 
#ifdef __WXMAC__
 
wxApp::s_macAboutMenuItemId = AboutID;
 
wxApp::s_macAboutMenuItemId = AboutID;
 
#endif
 
#endif
</source>
+
</syntaxhighlight>
 
 
On Mac OS X, the Preferences item goes in a special
 
place, too.  To tell it the ID of your Preferences item:
 
  
<source>
+
To tell it the name of your Preferences item :
 +
<syntaxhighlight lang="cpp">
 
#ifdef __WXMAC__
 
#ifdef __WXMAC__
 
wxApp::s_macPreferencesMenuItemId = PreferencesID;
 
wxApp::s_macPreferencesMenuItemId = PreferencesID;
 
#endif
 
#endif
</source>
+
</syntaxhighlight>
  
 
Look at defs.h for other such items.
 
Look at defs.h for other such items.
  
'''Remark''': You might have to tell wxMac the name of your help menu as well, since it assumes that it's "&Help". For example add the following lines
+
To tell it the name of your help menu item (default is "&Help") :
  
<source>
+
<syntaxhighlight lang="cpp">
 
#ifdef __WXMAC__
 
#ifdef __WXMAC__
 
wxApp::s_macHelpMenuTitleName = "Help";
 
wxApp::s_macHelpMenuTitleName = "Help";
 
#endif
 
#endif
</source>
+
</syntaxhighlight>
 +
 
 +
== Visuals ==
 +
 
 +
If you have a dialog window on Windows and Linux, you can set the background color of the dialog directly.  However, on OSX you need to attach a wxPanel to the dialog before background color will take.  It's generally a better idea to use a wxPanel as the root control of a dialog anyway.
 +
 
 +
In the create handler of your wxDialog-derived class where you would normally do this:
  
if your help menu has the name "Help".
+
<syntaxhighlight lang="cpp">
 +
wxDialog* dlg = this;
 +
dlg->SetBackgroundColour(wxColour(192, 128, 64));
 +
</syntaxhighlight>
  
You can achieve similar effects by using standard widget IDs. Here is a list of the Mac IDs that might have to be tweaked if you don't use their standard names (from defs.h):
+
You can do this instead:
wxID_ABOUT, wxID_EXIT, wxID_PREFERENCES, wxID_HELP.
 
  
See also the [http://www.wxwidgets.org/docs/faqmac.htm wxMac FAQ].
+
<syntaxhighlight lang="cpp">
 +
wxPanel* dlg = new wxPanel(this, wxID_ANY);
 +
dlg->SetBackgroundColour(wxColour(192, 128, 64));
 +
wxBoxSizer* mainSizer = new wxBoxSizer(wxHORIZONTAL);
 +
this->setSizer(mainSizer);
 +
mainSizer->Add(dlg);
 +
</syntaxhighlight>
 +
 
 +
You can then create controls as normal, for instance:
 +
 
 +
<syntaxhighlight lang="cpp">
 +
wxStaticText* text1 = new wxStaticText(dlg, wxID_ANY, _("Some Text"));
 +
</syntaxhighlight>
  
 
== When to close the program ==
 
== When to close the program ==
Line 119: Line 228:
 
If you want to support this behavior, there is a standard wxApp call, SetExitOnFrameDelete(), which tells the program not to exit when its last top-level frame is closed.  To setup the menubar to be displayed when no frames are open, there is a wxMac-only static function in wxMenuBar, MacSetCommonMenuBar().
 
If you want to support this behavior, there is a standard wxApp call, SetExitOnFrameDelete(), which tells the program not to exit when its last top-level frame is closed.  To setup the menubar to be displayed when no frames are open, there is a wxMac-only static function in wxMenuBar, MacSetCommonMenuBar().
  
<source title="Using the common menubar">
+
<syntaxhighlight lang="cpp" title="Using the common menubar">
 
bool myApp::OnInit() {
 
bool myApp::OnInit() {
 
   wxApp::SetExitOnFrameDelete(false);
 
   wxApp::SetExitOnFrameDelete(false);
Line 126: Line 235:
 
   wxMenuBar::MacSetCommonMenuBar(menubar);
 
   wxMenuBar::MacSetCommonMenuBar(menubar);
 
}
 
}
</source>
+
</syntaxhighlight>
  
 
Alternatively, you may employ a little trick: create an offscreen frame
 
Alternatively, you may employ a little trick: create an offscreen frame
Line 144: Line 253:
 
== Icons ==
 
== Icons ==
  
OS X uses its own icon format : .icns. You can easily create icns files using an app like [http://www.stalkingwolf.net/software/cocothumbx/ CocoThumbX].
+
OS X uses its own icon format : .icns. You can easily create icns files using an app like [http://www.macupdate.com/app/mac/17460/cocothumbx]. Also ''Xcode Tools'' comes with an application called ''Icon Composer''.
 +
 
 +
If you want to create .icns files from the command line, the OS X ''iconutil'' utility can convert between and iconset and a .icns file.  An iconset is just a directory containing specially named PNG files of the icon at different resolutions.  You can see the required name format by taking an existing .icns file and converting it to an iconset with iconutil.
  
 
== File associations ==
 
== File associations ==
Line 150: Line 261:
 
In order to have your application handle files that are dropped on the application icon,
 
In order to have your application handle files that are dropped on the application icon,
 
and respond to double-clicking on some file types from the Finder, override the following method in your wxApp :
 
and respond to double-clicking on some file types from the Finder, override the following method in your wxApp :
<source>
+
<syntaxhighlight lang="cpp">
 
virtual void wxApp::MacOpenFile(const wxString &fileName)
 
virtual void wxApp::MacOpenFile(const wxString &fileName)
</source>
+
</syntaxhighlight>
  
This is handled automatically in wxMac-2.8.4 (osx)
+
This is handled automatically in wxMac-2.8.4 (osx) provided you are using wxDocManager,
  
 
As of OS X, there are two ways to bind a document to an application. The former is the ''file extension'', for instance myfile'''.jpg'''. This mechanism is common on Linux and Windows. The second mechanism is the 4-character TYPE code. This code comes from OS pre-X, but was kept in OS X because it is arguably more powerful that plain file extensions. Any of the two or both can be used to perform file associations.
 
As of OS X, there are two ways to bind a document to an application. The former is the ''file extension'', for instance myfile'''.jpg'''. This mechanism is common on Linux and Windows. The second mechanism is the 4-character TYPE code. This code comes from OS pre-X, but was kept in OS X because it is arguably more powerful that plain file extensions. Any of the two or both can be used to perform file associations.
Line 162: Line 273:
 
You will need to set the information correctly in the Info.plist file inside your app bundle. Example (full docs can be found at Apple and are out of the scope of this document) :
 
You will need to set the information correctly in the Info.plist file inside your app bundle. Example (full docs can be found at Apple and are out of the scope of this document) :
  
<source>
+
<syntaxhighlight lang="cpp">
 
<?xml version="1.0" encoding="UTF-8"?>  
 
<?xml version="1.0" encoding="UTF-8"?>  
 
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
Line 201: Line 312:
 
</plist>
 
</plist>
  
</source>
+
</syntaxhighlight>
  
  
 
=== The TYPE code way ===
 
=== The TYPE code way ===
Beyond the mechanism of file extensions, mac OS also uses four-character TYPE and CREATOR. The former
+
Beyond the mechanism of file extensions, mac OS also uses four-character TYPE and CREATOR.
 +
 
 +
''Beware if you target recent OS X versions, as according to Wikipedia "In Mac OS X 10.6 Snow Leopard, the creator code is ignored by the operating system"''
 +
 
 +
The former
 
specifiies the type of the file independent of its three-character extension (which is not required); the latter
 
specifiies the type of the file independent of its three-character extension (which is not required); the latter
 
is the "signature" of the application that created it.
 
is the "signature" of the application that created it.
Line 211: Line 326:
 
There are methods in wxFilename which allow you to get or set these codes:
 
There are methods in wxFilename which allow you to get or set these codes:
  
<source>
+
<syntaxhighlight lang="cpp">
 
bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
 
bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
 
bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator )
 
bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator )
Line 217: Line 332:
 
bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
 
bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
 
void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )
 
void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )
</source>
+
</syntaxhighlight>
  
 
Note that types and creators are defined as wxUint32 instead of a string or
 
Note that types and creators are defined as wxUint32 instead of a string or
Line 231: Line 346:
 
appropriate type to invoke the HelpView application
 
appropriate type to invoke the HelpView application
 
(see utils/helpview/src, CVS HEAD).
 
(see utils/helpview/src, CVS HEAD).
 
 
 
 
== Converting .rsrc files into .r files ==
 
 
If you are copying your project files onto non Apple Volumes, or storing them into a cvs, you may prefer to convert the binary .rsrc files into textual representations (like the .rc file on msw). You can do this via the DeRez terminal tool. This tool is available under OSX via /Developer/Tools/DeRez , or if you are on MacOS Classic only via [http://developer.apple.com/tools/mpw-tools MPW]. Type DeRez followed by the full pathname to your .rsrc file, followed by a redirect output >> to your target .r filename.
 
 
You can now commit this .r file to your cvs and add it to your CodeWarrior Project instead of the .rsrc file.
 
 
To DeRez your file using CodeWarrior, make sure the RSRC file mapping in your project settings
 
has Rez as the compiler and .rsrc as the extension. Also create a mapping with the same settings
 
but a lower-case rsrc type instead of RSRC, to be on the safe side. Using ResEdit and showing
 
the Info for your icon, make sure the type is set to RSRC (and not, for example, Icon). Now add your .rsrc
 
file and select the Project Disassemble menu item to get a text version of the file, which you can then
 
save as a .r file and add back to your project. Then clear the .rsrc file from your project. Phew!  Do a
 
Google Groups search on "DeRez CodeWarrior" for more detail on this.
 
 
Julian Smart writes: the above is a bit overcomplicated, so here's my own recipe for getting icons working on MacOS X using CodeWarrior.
 
 
1) Prepare PNG images, with transparency, on a Windows machine or other system of your choice using (for example) Paint Shop Pro or Gimp. Prepare 16x16, 32x32, 64x64 and 128x128 PNGs with 16 or 256 colours (the 128x128 could and should have an alpha channel but so far I've just been working with simple transparency). Copy these to the Mac.
 
 
2) On MacOS X, create a new icon in Icon Composer (see the Developer folder for this utility). In Finder, open each PNG in turn, copy the image to the clipboard, then select the relevant icon outline in Icon Composer, and paste the icon into it, choosing to create the mask automatically when prompted. Do not attempt to import the PNG from a file since this can crash Icon Composer. Now save the icns file.
 
 
3) Launch Iconographer, a shareware icon editor, and open the icns file you previous saved. For the missing icons listed in various bit depths, copy one of the others of the same dimensions and paste into the blanks. Note that pasting from the Finder doesn't seem to preserve masks in Iconographer (or at least in the version I have), hence the use of Icon Composer as a first step. Save as a Mac OS Universal (Resources) file when you're done.
 
 
4) Now run DeRez on the file, e.g. /Developer/Tools/DeRez myicons > myicons.r.
 
 
5) Open myicons.r in a text editor, and do a global substitution changing the negative number specified in each icon resource to e.g. 128 as specified in your bundle .r file (see samples/docview/docview.r for an example bundle). If you don't have seperate icons for application and document, you can make a copy of the icons in myicons.r and change e.g. 128 to 129.
 
 
6) Add myicons.r to your project.
 
 
  
 
== Building a MacOSX application bundle ==
 
== Building a MacOSX application bundle ==
 
 
  
 
MacOSX introduces a new way of putting together an application.  Instead of adding a resource fork to the executable file, you can simply create a special directory (folder).  This is the preferred method for OSX.
 
MacOSX introduces a new way of putting together an application.  Instead of adding a resource fork to the executable file, you can simply create a special directory (folder).  This is the preferred method for OSX.
  
In it's most basic form, on OSX application bundle is a set of nested folders, with your executable in the "MacOS" folder:
+
In its most basic form, an OSX application bundle is a tree of nested folders, with your executable in the "MacOS" folder:
  
 
* YourApp.app
 
* YourApp.app
Line 282: Line 363:
  
 
For more information on the file formats and layout of an application bundle folder, consult the Apple developer website.
 
For more information on the file formats and layout of an application bundle folder, consult the Apple developer website.
For example: http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFBundles/CFBundles.html  
+
For example: http://developer.apple.com/mac/library/documentation/CoreFoundation/Conceptual/CFBundles/index.html  
  
 
If you are using XCode or ProjectBuilder, those tools will create an application bundle for you with metadata plist files based on values you enter in the project settings. See the wxWidgets example programs for a method to create an application bundle folder from makefiles.  A basic bundle folder can be created from "make" with the following rule:
 
If you are using XCode or ProjectBuilder, those tools will create an application bundle for you with metadata plist files based on values you enter in the project settings. See the wxWidgets example programs for a method to create an application bundle folder from makefiles.  A basic bundle folder can be created from "make" with the following rule:
  
<source lang="bash">
+
<syntaxhighlight lang="bash">
 
YourApp.app: Info.plist YourApp version.plist InfoPlist.strings YourAppMacIcons.icns AnotherResource.txt   
 
YourApp.app: Info.plist YourApp version.plist InfoPlist.strings YourAppMacIcons.icns AnotherResource.txt   
 +
  SetFile -t APPL YourApp
 
   -mkdir YourApp.app     
 
   -mkdir YourApp.app     
 
   -mkdir YourApp.app/Contents
 
   -mkdir YourApp.app/Contents
Line 299: Line 381:
 
   cp YourApp YourApp.app/Contents/MacOS/YourApp
 
   cp YourApp YourApp.app/Contents/MacOS/YourApp
 
   cp YourAppMacIcons.icns AnotherResource.txt YourApp.app/Contents/Resources/
 
   cp YourAppMacIcons.icns AnotherResource.txt YourApp.app/Contents/Resources/
</source>
+
</syntaxhighlight>
  
 
Then you can run "make YourApp.app", or you can add "YourApp.app" as a dependency of "all".  In my applications, I use autoconf, and generate Info.plist, version.plist, etc. from templates using configure.
 
Then you can run "make YourApp.app", or you can add "YourApp.app" as a dependency of "all".  In my applications, I use autoconf, and generate Info.plist, version.plist, etc. from templates using configure.
  
== Universal Binaries ==
+
How to deal with bundles and wxWidgets
 +
* Mac OS X comes with dynamic libraries of wxWidgets (2.5 on Tiger, 2.8 on Leopard). If you link against these libraries then you don't need to package wxWidgets in any way.
 +
* If you built wxWidgets the static way, by making sure to use built-in libs for e.g. png/regex/xml and not ones you installed yourself, you should have nothing special to do to make the bundle distributable.
 +
* wxWidgets dynamic libraries as built from the Xcode project will be automatically ''bundle-able'' (their path should be set to '''@executable_path/Contents/Frameworks/libname.dylib'''), which essentially means '''myProgram.app/Contents/Frameworks/libname.dylib''' To achieve this in your program's Xcode project, add a build phase to "Copy Files" and copy to the Executable path, with a subpath of "../Frameworks". Then when you build your app, Xcode will copy the dylib into the app's package where the linker can find it. And your app is linking to a relative path, which is easily transportable! (but check it manually with '''otool -L''' to make sure everything is right)
 +
* Otherwise, if you really need dynamic libraries, and can't use those installed by default on the system (because e.g. you can want OS X 10.4 compatibility AND wxWidgets 2.8) check ''otool'' (especially ''otool -L'') and ''install_name_tool'' on the terminal
 +
* http://macdylibbundler.sf.net does the same but more automatized
  
As of wxWidgets 2.6.3, there is support for building "universal binaries" (i.e., binaries that will run on both PPC and Intel Macs).  There are two approaches:
+
== Packaging your Mac application up for distribution ==
* Pass --enable-universal_binary to configure when building wxWidgets.  This will easily create a library that will work with Tiger (Mac OS X 10.4) and the latest Panther release (Mac OS X 10.3.9).
 
* If you use something older : build the library and your application once for PPC and once for Intel, and lipo them together:
 
    % lipo -create hello-intel hello-ppc -output hello             
 
  
More details on the lipo technique can be found near the bottom of this [http://lists.wxwidgets.org/cgi-bin/ezmlm-cgi?8:mss:89244:200603:ohgmnanakndofcigmlbc mailing list message]                                                                                                     
+
You should read Apple's [http://developer.apple.com/documentation/DeveloperTools/Conceptual/SoftwareDistribution/Introduction/chapter_1_section_1.html#//apple_ref/doc/uid/10000145i-CH1-DontLinkElementID_69 Documentation on Software Delivery]. It boils down to: for the most part, your users should be able to install your application by dragging and dropping it whereever they choose on their hard drive.
=== Fink Caveats ===
 
* '''If you have Fink installed, make sure there are no CFLAGS or LDFLAGS set in your .profile.''' Why? [http://www.mail-archive.com/[email protected]/msg22310.html Fink doesn't build Universal binaries.] The wxWidgets configure script will attempt to use the Fink versions of certain libraries (in particular libiconv) instead of the ones shipped with the Apple Developer Tools, resulting in link errors when the libraries are built. Unsetting the CFLAGS and LDFLAGS lines in your .profile and reopening Terminal/xterm should work. Note that just running Fink's init.sh won't interfere with wxWidgets; these are lines that have to be added to your .profile manually during the Fink setup process.
 
=== OS X 10.5 Leopard issues ===
 
If you get errors like:
 
  
ld: in /Developer/SDKs/MacOSX10.4u.sdk/usr/local/lib/libPng.dylib, file is not of required architecture for architecture ppc
+
Apple suggests that you package your application (manual install or not), inside a "disk image" file (which (when opened), the system mounts and treats like physical disks.). It is custom to have a background image in your disk image, your application aligned perfectly, etc etc, which can be a pain. There are a few tools that can automate this process: [http://www.araelium.com/dmgcanvas/ Araelium Groups' DMG Canvas], Dr Nic (from the Ruby on Rails community)'s [http://drnicwilliams.com/2009/02/03/choctop-packaging-and-deployment-of-cocoa-applications/#more-421 ChocTop] ruby gem and Rakefile solution, or Ryan Wilcox's [https://anon:@scm.wilcoxd.com:8081/svn/OpenSource/Scripts/sane_build_disk_image/sane_build_disk_image.py sane build disk image.py] command line utility.
  
You might need to add this to your configure line: --with-macosx-sdk=/Developer/SDKs/MacOSX10.5.sdk --with-macosx-version-min=10.4
+
If your application does need files installed with an installer application, the Apple document above also talks about that.
  
Someone should verify if this can work if used with a lower min version than 10.4.
+
See also [[Distributing_WxWidgets_Applications-Distributing_WxMac_Programs]] for some more/different information.
  
 
== Making apps look better on mac ==
 
== Making apps look better on mac ==
Line 330: Line 410:
  
 
=== Application Level ===
 
=== Application Level ===
* Handling files opened by double-clicking a file (or dragging file onto application): Implement the wxApp virtual method [http://www.wxwidgets.org/wiki/index.php/WxMac_Issues#Opening_files_by_double-clicking_and.2For_drag-and-drop MacOpenFile]. This is handled automatically for you if you use the [http://www.wxwidgets.org/manuals/stable/wx_docviewoverview.html#docviewoverview Document/View Framework]
+
* Handling files opened by double-clicking a file (or dragging file onto application): Implement the wxApp virtual method [[WxMac-specific topics#File_associations]]. This is handled automatically for you if you use the [https://docs.wxwidgets.org/stable/overview_docview.html Document/View Framework]
*Not Quitting when last window closes: (Mac users don't expect this behavior): call wxApp's SetExitOnFrameDelete(false) somewhere in you app (in your OnInit() maybe?)
+
* Keep the application running after last window closes: (Mac users expect this behavior for document-based applications): call wxApp's SetExitOnFrameDelete(false) somewhere in you app (in your OnInit() maybe?)
  
 
=== Top Level Windows ===
 
=== Top Level Windows ===
 
* The System Option mac.window-plain-transition can get rid of the "zooming" window transition that happens automatically when a window is opened or closed.  
 
* The System Option mac.window-plain-transition can get rid of the "zooming" window transition that happens automatically when a window is opened or closed.  
<source>
+
<syntaxhighlight lang="cpp">
 
wxSystemOptions::SetOptionInt( wxMAC_WINDOW_PLAIN_TRANSITION, 0 )
 
wxSystemOptions::SetOptionInt( wxMAC_WINDOW_PLAIN_TRANSITION, 0 )
</source>
+
</syntaxhighlight>
  
* Modal wxDialogs should '''not''' have enabled close buttons on them. Make sure your styles for wxDialog based instances of this type do not contain wxCLOSE_BOX. See the discussion of dialog boxes in the [http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html Apple Human Interface Guidelines]
+
* Modal wxDialogs should '''not''' have enabled close buttons on them. Make sure your styles for wxDialog based instances of this type do not contain wxCLOSE_BOX. See the discussion of dialog boxes in the [http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/index.html Apple Human Interface Guidelines]
  
* Pay attention to the [http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html Full Size Control Spacing Guidelines] of the Apple Human Interface Guidelines. (These are the values you should use for your sizer's border size)
+
* Pay attention to the [http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/index.html Full Size Control Spacing Guidelines] of the Apple Human Interface Guidelines. (These are the values you should use for your sizer's border size)
  
 
=== Fonts ===
 
=== Fonts ===
Line 347: Line 427:
 
Usually wxSMALL_FONT, etc do the right thing on OS X. But sometimes the HIGs recommend a font that doesn't exist as a preexisting wxWidgets font. There's an answer to this:
 
Usually wxSMALL_FONT, etc do the right thing on OS X. But sometimes the HIGs recommend a font that doesn't exist as a preexisting wxWidgets font. There's an answer to this:
  
<source>
+
<syntaxhighlight lang="cpp">
 
wxFont macFont;
 
wxFont macFont;
  
 
macFont.MacCreateThemeFont(APPEARANCE MANAGER FONT ID );
 
macFont.MacCreateThemeFont(APPEARANCE MANAGER FONT ID );
</source>
+
</syntaxhighlight>
  
 
The list of possible parameters for this function is found at [http://developer.apple.com/documentation/Carbon/Reference/Appearance_Manager/Reference/reference.html#//apple_ref/c/tdef/ThemeFontID Appearance Manager: Font IDs]. The  
 
The list of possible parameters for this function is found at [http://developer.apple.com/documentation/Carbon/Reference/Appearance_Manager/Reference/reference.html#//apple_ref/c/tdef/ThemeFontID Appearance Manager: Font IDs]. The  
Line 362: Line 442:
 
By default this feature is off, but you can turn it on by default by turning on the system option  wxMAC_TEXTCONTROL_USE_SPELL_CHECKER
 
By default this feature is off, but you can turn it on by default by turning on the system option  wxMAC_TEXTCONTROL_USE_SPELL_CHECKER
  
<source>
+
<syntaxhighlight lang="cpp">
 
wxSystemOptions::SetOptionInt( wxMAC_TEXTCONTROL_USE_SPELL_CHECKER, 1 );
 
wxSystemOptions::SetOptionInt( wxMAC_TEXTCONTROL_USE_SPELL_CHECKER, 1 );
</source>
+
</syntaxhighlight>
  
 
If you want to turn spell checking on for an individual control, but not for every text control in your app, call the wxTextCtrl's MacCheckSpelling(spellCheckerOn).
 
If you want to turn spell checking on for an individual control, but not for every text control in your app, call the wxTextCtrl's MacCheckSpelling(spellCheckerOn).
  
<source>
+
<syntaxhighlight lang="cpp">
 
wxTextCtrl* myTextControl = ....
 
wxTextCtrl* myTextControl = ....
  
 
#if __WXMAC__
 
#if __WXMAC__
myTextControl->MacCheckSpelling(true);  spell checking on!
+
myTextControl->MacCheckSpelling(true);  //spell checking on!
 
#endif
 
#endif
</source>
+
</syntaxhighlight>
 +
== Retina display support ==
 +
To have text rendered sharply on macs with a Retina display the key "NSPrincipalClass" should exist in the info.plist of your application. Its value seems to be irrelevant - probably it's best to just set it to the default "NSApplication".

Latest revision as of 19:37, 19 October 2018

See also the wxMac FAQ

Built-In Versions

MacOS ships with the wxWidgets libraries.

  • 10.4 has wxWidgets 2.5
  • 10.5 has wxWidgets 2.8.4
  • 10.6 has wxWidgets 2.8.8
  • 10.7 does not ship with wxWidgets. Apps linked to system libraries on 10.4-10.6 will not run.

My app can't be brought to the front!

This is a common problem met by people new to wxMac. Simply building a wxWidgets app from terminal and producing a raw executable is not enough on wxMac. You need to build an application bundle (there is a section about this below). The most easy way to get a bundle is to use an "Application" target in xCode (a "shell tool" target will not produce a bundle). Another possible way to solve this quickly is to add the following lines of code to your app (carefully wrapped in #ifdef __WXMAC__ [..] #endif) :

#include <ApplicationServices/ApplicationServices.h>

ProcessSerialNumber PSN;
GetCurrentProcess(&PSN);
TransformProcessType(&PSN,kProcessTransformToForegroundApplication);

This is also covered in the official wxMac FAQ

The Mac OS menu bar

Warning : it was reported that calling ->SetMenuBar() before filling the menu bar object with items may result in these special items not working properly. Similarly, appending a wxMenu to the wxMenuBar before the wxMenu was filled with items may cause problems

Single menu bar

All versions of Mac OS have a single menu bar at the top of the screen. While pop-up menus and context-sensitive menus are supported and are appropriate under some circumstances, Mac windows never have their own menu bar.

wxMac handles this automatically by displaying the menu bar associated with each wxFrame at the top of the screen whenever that frame is in front. If your program has multiple frames and the user brings a different frame to the front, that frame's menu bar will be switched in.

Warning: be careful of modeless windows which do not have menu bars. (Modal dialogs are okay.) If a modeless window gets brought to the front and it doesn't have a menu bar, the last menu bar will stay at the top of the screen. When the user selects something from one of these menus, the selection might never get handled, since the active frame doesn't recognize the command. You have two choices: either handle your commands in your wxApp subclass (since the commands will eventually get sent there), or create a degenerate wxMenuBar for all of your frames (maybe with only a Quit item).

Keyboard Shortcuts

Keyboard shortcuts use the command-key (the cloverleaf or open-apple key on the keyboard) instead of the control key. wxMac lets you specify keyboard shortcuts MS Windows style, and they are automatically translated. So Open should be specified as "&Open\tCtrl+O", and on the Mac the accelerator will be removed and the Ctrl will be replaced with a cloverleaf in the menu.

Special Menu Items

The layout of Mac menus is slightly different, and here is where you will have to add a line of code to your program to accomodate it. On the Mac, the "About" menu item should go in the program menu; same thing for the "Quit", "Help" and "Preferences" menu items.

There are two ways to handle this :

Let wx identify menu items by their ID

Here is a list of the Mac IDs that might have to be tweaked if you are not yet using their standard names (from defs.h):

wxID_ABOUT,
wxID_EXIT,
wxID_PREFERENCES,
wxID_HELP

.

These constants are not Mac-specific, they can be used on all platforms.

See also the wxMac FAQ.

  • Example of how to catch about and preferences events:
#include <wx/wx.h>

class MyApp: public wxApp
{
    bool OnInit();
    
    wxFrame *frame;
public:
    DECLARE_EVENT_TABLE()
    
    void OnAbout(wxCommandEvent& evt);
    void OnPrefs(wxCommandEvent& evt);
};

IMPLEMENT_APP(MyApp)

BEGIN_EVENT_TABLE(MyApp, wxApp)
EVT_MENU(wxID_ABOUT, MyApp::OnAbout)
EVT_MENU(wxID_PREFERENCES, MyApp::OnPrefs)
END_EVENT_TABLE()

bool MyApp::OnInit()
{
    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
    frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Hello wxWidgets"), wxPoint(50,50), wxSize(800,600));
	
    wxMenuBar* menubar = new wxMenuBar();
    wxMenu* testmenu = new  wxMenu();
	
    testmenu->Append(wxID_ANY, _("New\tCtrl-N"));
    testmenu->Append(wxID_ANY, _("Open\tCtrl-O"));
    
    testmenu->Append(wxID_ABOUT, _("About"));
    testmenu->Append(wxID_HELP, _("Help"));
    testmenu->Append(wxID_PREFERENCES, _("Preferences"));
    testmenu->Append(wxID_EXIT, _("Exit"));
    
    menubar->Append(testmenu, _("File"));
    
    frame->SetMenuBar(menubar);
    frame->Show();
    
    
    return true;
} 

void MyApp::OnAbout(wxCommandEvent& evt)
{
    wxMessageBox(_("About this application... not much to say for such a simple example, really"));
}

void MyApp::OnPrefs(wxCommandEvent& evt)
{
    wxMessageBox(_("Here are preferences. Not much to configure in this simple example =)"));
}

Let wx identify menu items by their name

This is another way, perhaps less convenient than the IDs method described above.

A menu item named "Exit" will be moved to the appropriate location for you.

The same is true for the About menu, and you tell wx the name of your about menu this way :

#ifdef __WXMAC__
wxApp::s_macAboutMenuItemId = AboutID;
#endif

To tell it the name of your Preferences item :

#ifdef __WXMAC__
wxApp::s_macPreferencesMenuItemId = PreferencesID;
#endif

Look at defs.h for other such items.

To tell it the name of your help menu item (default is "&Help") :

#ifdef __WXMAC__
wxApp::s_macHelpMenuTitleName = "Help";
#endif

Visuals

If you have a dialog window on Windows and Linux, you can set the background color of the dialog directly. However, on OSX you need to attach a wxPanel to the dialog before background color will take. It's generally a better idea to use a wxPanel as the root control of a dialog anyway.

In the create handler of your wxDialog-derived class where you would normally do this:

wxDialog* dlg = this;
dlg->SetBackgroundColour(wxColour(192, 128, 64));

You can do this instead:

wxPanel* dlg = new wxPanel(this, wxID_ANY);
dlg->SetBackgroundColour(wxColour(192, 128, 64));
wxBoxSizer* mainSizer = new wxBoxSizer(wxHORIZONTAL);
this->setSizer(mainSizer);
mainSizer->Add(dlg);

You can then create controls as normal, for instance:

wxStaticText* text1 = new wxStaticText(dlg, wxID_ANY, _("Some Text"));

When to close the program

On all versions of the Mac OS, it is okay for a program to be open but to have no windows open at all. This is similar to how a MS Windows MDI program can have its MDI Parent Frame open with no documents open within it, but on the Mac it has no open frames at all. The only clue that the program is active is that the menu bar changes.

If your program is dialog-based or otherwise just has one main frame, don't worry about this. When the user closes the main program or dialog, your program can just exit.

On the other hand, if your program is document-based, a Mac user will be confused if he/she closes a frame and the whole program exits. The user may have been intending to close one document and then open another.

If you want to support this behavior, there is a standard wxApp call, SetExitOnFrameDelete(), which tells the program not to exit when its last top-level frame is closed. To setup the menubar to be displayed when no frames are open, there is a wxMac-only static function in wxMenuBar, MacSetCommonMenuBar().

bool myApp::OnInit() {
  wxApp::SetExitOnFrameDelete(false);
  wxMenuBar *menubar = new wxMenuBar;
  // add open, new, etc options to your menubar.
  wxMenuBar::MacSetCommonMenuBar(menubar);
}

Alternatively, you may employ a little trick: create an offscreen frame with a menu bar (give it top-left coordinates of [5000, 5000] or something like that, but don't hide it using wxFrame::Hide). Give the frame a style of wxFRAME_NO_TASKBAR, so that the frame does not appear under the "Window" menu. When all of your other frames are closed, this frame will be the frontmost, and its menu bar will appear. This menu bar should include items like Open, Quit, and maybe Preferences.

On OSX, creating a window of wxSize(0,0) and window style 0 also works to create an invisible window - however this does not work under Classic.

Icons

OS X uses its own icon format : .icns. You can easily create icns files using an app like [1]. Also Xcode Tools comes with an application called Icon Composer.

If you want to create .icns files from the command line, the OS X iconutil utility can convert between and iconset and a .icns file. An iconset is just a directory containing specially named PNG files of the icon at different resolutions. You can see the required name format by taking an existing .icns file and converting it to an iconset with iconutil.

File associations

In order to have your application handle files that are dropped on the application icon, and respond to double-clicking on some file types from the Finder, override the following method in your wxApp :

virtual void wxApp::MacOpenFile(const wxString &fileName)

This is handled automatically in wxMac-2.8.4 (osx) provided you are using wxDocManager,

As of OS X, there are two ways to bind a document to an application. The former is the file extension, for instance myfile.jpg. This mechanism is common on Linux and Windows. The second mechanism is the 4-character TYPE code. This code comes from OS pre-X, but was kept in OS X because it is arguably more powerful that plain file extensions. Any of the two or both can be used to perform file associations.

The file extension way

You will need to set the information correctly in the Info.plist file inside your app bundle. Example (full docs can be found at Apple and are out of the scope of this document) :

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>       <string>English</string>
	<key>CFBundleExecutable</key>              <string>'your_executable_name'</string>
	<key>CFBundleInfoDictionaryVersion</key>   <string>6.0</string>
	<key>CFBundlePackageType</key>             <string>APPL</string>
	<key>CSResourcesFileMapped</key>           <true/>
	
	<key>CFBundleVersion</key>                 <string>'your app version, e.g. 1.5'</string>
	<key>CFBundleShortVersionString</key>      <string>'your app version, e.g. 1.5'</string>
	
	<key>CFBundleName</key>                    <string>'your apps user-visible name'</string>
	<key>CFBundleIconFile</key>                <string>'your_app_icon.icns'</string>
		
	<key>CFBundleDocumentTypes</key> <array>
		<dict>
			<key>CFBundleTypeExtensions</key>
			<array>
                                <!-- Here goes the file formats your app can read -->
				<string>'gif'</string>
				<string>'jpeg'</string>
				<string>'png'</string>
				<string>'tiff'</string>
			</array>
			<key>CFBundleTypeIconFile</key>      <string>'icon_for_your_documents.icns'</string>
			<key>CFBundleTypeName</key>          <string>'User-visible description for these documents, e.g. image files'</string>
			<key>CFBundleTypeRole</key>          <string>Editor</string>
			<key>LSIsAppleDefaultForType</key>   <true/>
			<key>LSTypeIsPackage</key>           <false/>
		</dict>
		
	</array>
	
</dict>
</plist>


The TYPE code way

Beyond the mechanism of file extensions, mac OS also uses four-character TYPE and CREATOR.

Beware if you target recent OS X versions, as according to Wikipedia "In Mac OS X 10.6 Snow Leopard, the creator code is ignored by the operating system"

The former specifiies the type of the file independent of its three-character extension (which is not required); the latter is the "signature" of the application that created it.

There are methods in wxFilename which allow you to get or set these codes:

bool wxFileName::MacSetTypeAndCreator( wxUint32 type , wxUint32 creator )
bool wxFileName::MacGetTypeAndCreator( wxUint32 *type , wxUint32 *creator )
bool wxFileName::MacSetDefaultTypeAndCreator()
bool wxFileName::MacFindDefaultTypeAndCreator( const wxString& ext , wxUint32 *type , wxUint32 *creator )
void wxFileName::MacRegisterDefaultTypeAndCreator( const wxString& ext , wxUint32 type , wxUint32 creator )

Note that types and creators are defined as wxUint32 instead of a string or character type because all Mac compilers allow you to specify types and creators as: 'TEXT', 'APPL', 'ttro', 'R*ch', etc. - where the four characters are packed into one 32-bit int.

If you want to set the type of a file (for example before distributing it), you can do something like this:

/Developer/Tools/Rez -t HTBD -o myfile.htb < /dev/null

This sets the wxWidgets HTML help file 'myfile.htb' to the appropriate type to invoke the HelpView application (see utils/helpview/src, CVS HEAD).

Building a MacOSX application bundle

MacOSX introduces a new way of putting together an application. Instead of adding a resource fork to the executable file, you can simply create a special directory (folder). This is the preferred method for OSX.

In its most basic form, an OSX application bundle is a tree of nested folders, with your executable in the "MacOS" folder:

  • YourApp.app
    • Contents
      • MacOS
        • YourApp (this is your executable file)
      • Resources
        • ...

Instead of being embeded in a resource fork, your application's resources can be placed as seperate files in "Contents/Resources". Additional metadata about your application can be contained in "Contents/Info.plist" and "Contents/version.plist". Locale/Language-specific data is contained in "Contents/Resources/<Language>.lproj". Dependent libraries and frameworks (e.g. wxWidgets) can also be contained in the bundle folders.

For more information on the file formats and layout of an application bundle folder, consult the Apple developer website. For example: http://developer.apple.com/mac/library/documentation/CoreFoundation/Conceptual/CFBundles/index.html

If you are using XCode or ProjectBuilder, those tools will create an application bundle for you with metadata plist files based on values you enter in the project settings. See the wxWidgets example programs for a method to create an application bundle folder from makefiles. A basic bundle folder can be created from "make" with the following rule:

YourApp.app: Info.plist YourApp version.plist InfoPlist.strings YourAppMacIcons.icns AnotherResource.txt  
   SetFile -t APPL YourApp
   -mkdir YourApp.app    
   -mkdir YourApp.app/Contents
   -mkdir YourApp.app/Contents/MacOS
   -mkdir YourApp.app/Contents/Resources
   -mkdir YourApp.app/Contents/Resources/English.lproj
   cp Info.plist YourApp.app/Contents/
   cp version.plist YourApp.app/Contents/
   cp InfoPlist.strings YourApp.app/Contents/Resources/English.lproj/
   echo -n 'APPL????' > YourApp.app/Contents/PkgInfo
   cp YourApp YourApp.app/Contents/MacOS/YourApp
   cp YourAppMacIcons.icns AnotherResource.txt YourApp.app/Contents/Resources/

Then you can run "make YourApp.app", or you can add "YourApp.app" as a dependency of "all". In my applications, I use autoconf, and generate Info.plist, version.plist, etc. from templates using configure.

How to deal with bundles and wxWidgets

  • Mac OS X comes with dynamic libraries of wxWidgets (2.5 on Tiger, 2.8 on Leopard). If you link against these libraries then you don't need to package wxWidgets in any way.
  • If you built wxWidgets the static way, by making sure to use built-in libs for e.g. png/regex/xml and not ones you installed yourself, you should have nothing special to do to make the bundle distributable.
  • wxWidgets dynamic libraries as built from the Xcode project will be automatically bundle-able (their path should be set to @executable_path/Contents/Frameworks/libname.dylib), which essentially means myProgram.app/Contents/Frameworks/libname.dylib To achieve this in your program's Xcode project, add a build phase to "Copy Files" and copy to the Executable path, with a subpath of "../Frameworks". Then when you build your app, Xcode will copy the dylib into the app's package where the linker can find it. And your app is linking to a relative path, which is easily transportable! (but check it manually with otool -L to make sure everything is right)
  • Otherwise, if you really need dynamic libraries, and can't use those installed by default on the system (because e.g. you can want OS X 10.4 compatibility AND wxWidgets 2.8) check otool (especially otool -L) and install_name_tool on the terminal
  • http://macdylibbundler.sf.net does the same but more automatized

Packaging your Mac application up for distribution

You should read Apple's Documentation on Software Delivery. It boils down to: for the most part, your users should be able to install your application by dragging and dropping it whereever they choose on their hard drive.

Apple suggests that you package your application (manual install or not), inside a "disk image" file (which (when opened), the system mounts and treats like physical disks.). It is custom to have a background image in your disk image, your application aligned perfectly, etc etc, which can be a pain. There are a few tools that can automate this process: Araelium Groups' DMG Canvas, Dr Nic (from the Ruby on Rails community)'s ChocTop ruby gem and Rakefile solution, or Ryan Wilcox's sane build disk image.py command line utility.

If your application does need files installed with an installer application, the Apple document above also talks about that.

See also Distributing_WxWidgets_Applications-Distributing_WxMac_Programs for some more/different information.

Making apps look better on mac

Mac users expect apps on OS X to look pretty. If it doesn't look pretty you'll have cranky users. Cranky users can be avoided by implementing some of these tips. (This page is, of course, a work in progress)

This could also be seen as a page where wxMac specific functionality is documented...

Application Level

  • Handling files opened by double-clicking a file (or dragging file onto application): Implement the wxApp virtual method WxMac-specific topics#File_associations. This is handled automatically for you if you use the Document/View Framework
  • Keep the application running after last window closes: (Mac users expect this behavior for document-based applications): call wxApp's SetExitOnFrameDelete(false) somewhere in you app (in your OnInit() maybe?)

Top Level Windows

  • The System Option mac.window-plain-transition can get rid of the "zooming" window transition that happens automatically when a window is opened or closed.
wxSystemOptions::SetOptionInt( wxMAC_WINDOW_PLAIN_TRANSITION, 0 )
  • Modal wxDialogs should not have enabled close buttons on them. Make sure your styles for wxDialog based instances of this type do not contain wxCLOSE_BOX. See the discussion of dialog boxes in the Apple Human Interface Guidelines

Fonts

Usually wxSMALL_FONT, etc do the right thing on OS X. But sometimes the HIGs recommend a font that doesn't exist as a preexisting wxWidgets font. There's an answer to this:

wxFont macFont;

macFont.MacCreateThemeFont(APPEARANCE MANAGER FONT ID );

The list of possible parameters for this function is found at Appearance Manager: Font IDs. The Human Interface Guidelines Font Section is useful for seeing the font required for a given situation. (Use the Carbon Constant!)

Automatic spellchecking

  • Spell Checking: OS X comes with a built-in spell checker for editable text controls that want to turn it on. OS X users expect their text to be spell checked, except in places where this checking just doesn't make any sense (like logs or source code).

By default this feature is off, but you can turn it on by default by turning on the system option wxMAC_TEXTCONTROL_USE_SPELL_CHECKER

wxSystemOptions::SetOptionInt( wxMAC_TEXTCONTROL_USE_SPELL_CHECKER, 1 );

If you want to turn spell checking on for an individual control, but not for every text control in your app, call the wxTextCtrl's MacCheckSpelling(spellCheckerOn).

wxTextCtrl* myTextControl = ....

#if __WXMAC__
myTextControl->MacCheckSpelling(true);   //spell checking on!
#endif

Retina display support

To have text rendered sharply on macs with a Retina display the key "NSPrincipalClass" should exist in the info.plist of your application. Its value seems to be irrelevant - probably it's best to just set it to the default "NSApplication".