UserGuide

UI Design Tips

From Xojo Documentation

When designing your applications to be cross-platform, you should keep in mind the differences in user interface across the different platforms. An application that is well designed for Windows may look drastically out of place on Mac or Linux. And vice versa.

Menus

ApplicationMenuItem

Desktop applications usually have an About menu that displays an About window with the name of the application and its copyright information.

On Windows and Linux, this About menu appears in a Help menu. You can add a help menu by clicking the “Add Menu” button in the Menu Editor to add a new top-level menu. Name this menu "HelpMenu" and drag it so that it is rightmost. Now you can add the About menu item to the help menu.

On Mac, the About menu instead should appear in the Application menu rather than the help menu. To make it automatically move to the Application menu, you add your About menu to the Help menu so that it appears as expected for Windows and Linux. And then change its Super property in the Inspector from “MenuItem” to “ApplicationMenuItem”. Any menu that uses this class will be automatically moved to the Application menu when it is run on Mac.

PrefsMenuItem

Similarly, the Preferences menu is also located in the Application menu on Mac. On Windows and Linux, the Preferences menu is often located in the Edit menu and is instead called “Options”.

There is always a fixed Preferences menu in the Application menu on Mac, but is is disabled by default. To attach your Preferences menu to it, you set its Super property to “PrefsMenuItem”. Only one menu in your project should be set to PreferencesMenuItem.

To change the name of the Preferences menu for Mac and Windows/Linux, you use a constant. By default there are several constants on the App class that control the text for Edit ↠ Clear/Delete and File ↠ Quit/Exit. You can add another to handle preferences.

To do so, add a new constant and call it kPreferences, setting its default value to "&Options...". In the Constant Editor, click the “+” to add a new entry and select “macOS” as the Platform. For the Value, enter "Preferences..."

This creates a constant that uses the value "&Options..." on Windows and Linux, but the value "Preferences..." on Mac.

Now that you have created the constant, you can use it as the text of the menu. Add a new MenuItem to the Edit menu and set its Text property to “#App.kPreferences”. This tells it to use the value of the constant. Also set its Super to “PrefsMenuItem”.

You can use the preview buttons in the Menu Editor toolbar to see the text change between Mac, Windows and Linux. In addition, when you run the application on Mac, the Preferences menu appears in the Application menu instead of the Edit menu.

Dialog Buttons

Perhaps you have never noticed it, but when you use a dialog box on Windows and Linux, the buttons are in a different order than they are on Mac. On Mac, the default OK/Cancel buttons display as Cancel followed by OK. On Windows and Linux they appear as OK followed by Cancel.

For your applications to look proper on each platform, the buttons should appear in the appropriate positions. The easiest way to do this is to us a ContainerControl to swap the buttons for you at run-time. The example project OKCancelContainer in the Desktop/Custom Controls folder demonstrates how to do this.

  • Examples/Desktop/Custom Controls/OKCancelContainer

Fonts

Normally you will use the System font as the Font for your controls. This tells your application to use the default system font for each OS as the font for the control. As you might expect, the default system font varies by platform and sometimes even by updates within a platform.

This means that some controls may end up being too small on some platforms to fit the text you provided. It is important that you run your project on each platform and review the layout to make sure that everything is readable and fits as you expect.

You may find that you need to increase the size of some controls so that they display properly on all platforms. You can do this in the Layout Editor by increasing the size of a control. Or you can do it at runtime by increasing the size of the control in its Open event depending on the platform being used (using Conditional Compilation). For example, this code in the Open event handler of a PushButton increases its size when running on Linux:

#If TargetLinux Then
Me.Height = Me.Height + 20
#Endif

Windows-Specific Tips

Linux and Mac use a technique called double-buffering for all window drawing. This means that updates to a window are done offscreen and then shown on the actual screen in one update. This results in stable, flicker-free windows and graphics updates.

Windows, however, does not use this technique. The Xojo desktop framework attempts to minimize this for you. Below are some general tips that will help your Windows apps look their best.

Do not Overlap Controls

The easiest thing you ensure a rock-solid UI is to not overlap any controls. Overlapped controls result in more requests to redraw the controls which will be slower and can sometimes result in flickering.

Use a Canvas

For best results, display any graphics using the Paint event of a Canvas control. Stay away from using the Window Backdrop property, the Window Paint event, the Canvas.Backdrop property or the ImageWell control. Although those techniques work fine in certain situations, in more complex window layouts a Canvas gives you more control.

On the Canvas, the first thing you want to do is enable the DoubleBuffer property and disable the EraseBackground property. The DoubleBuffer property allows the Canvas to do its updates offscreen to minimize flicker. The EraseBackground property prevents the Canvas from being erased (and showing as a white rectangle) before it is redrawn, which is another common source of flicker.

With these tweak, you can do all your drawing in the Paint event using the supplied graphics object parameter: g.

You can have separate methods that update the graphics, but they need to be called from the Paint event with the graphics object supplied to the methods as a parameter. Another technique is to have a Picture property that you use to draw you graphics to and then in the Paint event handler you draw the Picture to the Canvas to display it.

When you want the Canvas to update itself, redrawing any changes to your graphics, you call the Invalidate method:

Canvas1.Invalidate(False)

You can also call the Refresh method:

Canvas1.Refresh(False)

The difference is that Invalidate tells the Canvas to update itself when it gets a redraw request from the operating system. The Refresh method tells the Canvas to update itself immediately. Normally you want to use Invalidate as it results in fewer draw requests, improving performance and reducing any flicker.

Both of the above commands pass False for the EraseBackground parameter (which defaults to True) so that the Canvas is not erased before its updated contents are drawn which improves performance and reduces any flicker.

By using these techniques, you can create a stable UI and graphics in your Windows applications.