Wednesday, October 31, 2012

Detecting UI Access from Threads

As most of you are aware, accessing user interface elements like controls and windows from a thread can cause crashes, deadlocks, and other bugs (especially with Cocoa on OS X). A good way to deal with this is to have a Timer update the UI based on information it retrieves from a thread (see Doing Progress Right).

However, there hasn't been an easy way to detect these problems in your applications without manually auditing the code or waiting until an error occurs. Until now. The command-line tool threadedui, works on OS X to check your applications to see if they access the user interface from a thread.


Download: threadedui.


To use threadedui, run it from Terminal supplying it with the path to your application. Like this:


# sudo ./threadedui /Applications/MyApp.app

This launches your application. Use it as you normally would. As it runs, information will appear in the Terminal window. Quit your application when you are finished.

If your application does not have any threaded UI access, then the output in Terminal looks something like this:

Launched /MyApp.app/Contents/MacOS/MyApp (24126)
DTrace attached to 24127
Main thread found, trace starting (may have missed some events)
Program exited, detected no UI operations from a thread

If your application does have threaded UI access, then significantly more information is displayed. This code has a Thread on a Window that directly updates a ProgressBar using this code in its Run event handler:

ProgressBar1.Value = 100

When run using threadedui, this is what the output looks like:

Launched /MyApp.app/Contents/MacOS/MyApp (24210)
DTrace attached to 24211
Main thread found, trace starting (may have missed some events)

   MyApp`REALbasic._UITrap
   MyApp`ProgressBar.Value.Set%%o<ProgressBar>i4i4+0x2c
   MyApp`Window1.Window1.Thread1_Run%%o<Window1.Window1>o<Thread>+0x97
   MyApp`Delegate.IM_Invoke%%o<Thread>+0x6e
   MyApp`AddHandler.Stub.0%%+0x33
   rbframework.dylib`0x001f48a0+0xc0
   CarbonCore`CooperativeThread+0x134
   libsystem_c.dylib`_pthread_start+0x14f
   libsystem_c.dylib`thread_start+0x22


Program exited, detected 1 UI operations from a thread


As you can see, the output tells you that the Thread1 Run event is accessing the UI and it specifically mentions ProgressBar.Value. Once the problem areas of you application have been identified in this manner, you can go about updating them.

Monday, October 29, 2012

Inspecting WebSessions at Runtime

Every once in a while when working a particularly elusive web application bug, it's nice to be able to see the information stored in the current Session to see if any patterns arise. The easiest way to do this is to add your own variable, like this:

#If DebugBuild then
  Dim sess as WebSession = Session
  Break
#EndIf

You'll notice that I wrapped the variable assignment in an #If DebugBuild pragma statement. In this case, Session is actually a Method which looks up which session is currently active. If calling this method while debugging, you will probably have only one or two sessions to iterate over. On the other hand, if you leave this call in your production code it will have to iterate over all of the active sessions looking for which one is current. The more concurrent sessions you have, the more expensive the call will be in cpu time.

When the debugger comes upon this code, click on the "sess" object and you will see a plethora of information about the connected browser, including:


  • Browser Type
  • Browser Version
  • Client Date/Time
  • Cookie Information
  • The Current Page
  • Hash Tag
  • Platform
  • Protocol (HTTP vs HTTPS)
  • Remote Address
  • The Rendering Engine of the browser

This is a great way to gather information about the computers and browsers that are connecting to your app!

Friday, October 26, 2012

Is this item still in use?

As projects get bigger, it's easy to end up creating stuff you don't actually use. If you ever suspect that a particular window, webpage, class, module, control, method, property, or just about anything, is not really being used there's an easy way to find out.
  1. First, rename the item. You can do this by just adding an extra letter ("x" for example) to the end of the name.
  2. Run your app.
If the item whose name you changed is being used anywhere in your code, the compiler won't be able to find it by its old name and will generate a compile error. Specifically, the message is:




 If you get no compile errors, then the item is not being used and can be deleted.

There are some exceptions to this. If the item is a window or webpage and you don't refer to it in code but it is selected as the default Window or WebPage, renaming it won't cause an error. The same holds true for menubars. However, these are items you almost certainly know are in use anyway.

Friday, October 12, 2012

Supporting Retina Displays

Update (2013-07-17): Updated Info.plist change to use a boolean rather than a string.
Update (2013-01-22): Note that sample code and app requires OS X Lion 10.7.4 or newer

Retina displays are displays that have extremely high pixel resolution.  These displays use more than one pixel to draw information on the screen resulting in much sharper text and other UI elements.  They also allow for higher resolution pictures to fit in a smaller space on the screen. Currently, only Apple offers retina-type displays with their iPad, iPhone, iPod touch and Macbook Pro products.

With a bit of tweaking, your Real Studio Cocoa applications can be made retina-aware so that UI elements and graphics draw at high quality when run on a retina MacBook Pro.

This is a two-step process. First, you need to update the Info.plist in your application bundle to enable retina support.

Second, if your application has graphics, you need to update them in order for them to appear at retina quality. Generally this means that you need to provide much larger graphics and then scale them down to the size you need.  Usually you supply a 2x resolution image and then scale it to fit the available area.  OS X will automatically handle this.

For some graphics, you may find that having separate pre-scaled graphics for retina and non-retina displays work best.  For example, you might want to swap out a 1x image when on non-retina displays, but use a 2x image when on a retina display.  To do this, you need to ask OS X what the scale factor is so that you can determine which image to use.

Enabling Retina Support

To enable retina support, you need to add a special key (above the </dict> entry near the end) to the Info.plist file in the application bundle:

<key>NSHighResolutionCapable</key>
<true/>

This enables retina support for the UI elements, such as buttons and text.

Because you need to edit the plist before the application runs, it can be hard to test this in debug mode.  One solution is to use the “Run Paused” feature.  This will build your application and pause before running it.  You can make the plist changes while it is paused and then continue to run it.  Alternatively, you can use Build Automation to add this key to the plist file after your application is built.

Note: If you do not have a retina MacBook Pro, you can still see how a retina application looks.  One option is to use an iPad with retina display as a extra display for your Mac (using an app such as AirDisplay). Or you can download Quartz Debug from Apple (in their Graphics Tools) and use it to enable HiDPI display modes for your display. Once you do this (select Window->UI Resolution and "Enable HiDPI display modes), extra display modes (with greatly reduced resolution) appear in your display preferences. For example, on my 1920x1200 display, the best HiDPI mode I can select is 960x600.

Retina Graphics

If you just add large (2x) graphics to your projects and scale them down to fit when drawing them, then you automatically get the high resolution graphics used on a retina display.  A non-retina display uses the scaled version.

But using a scaled version can sometimes lead to non-optimal results, so Apple recommends having two separate images included in your application, one for retina displays and one for non-retina displays.

In order to detect if your application is running on a retina display, you can check the BackingScaleFactor function in the Cocoa API (available on OS X 10.7 and newer).  Add this code to a Window:

Function ScalingFactor() As Single
#If TargetCocoa Then
  Declare Function BackingScaleFactor Lib "AppKit" Selector "backingScaleFactor" (target As WindowPtr) As Double
  Return BackingScaleFactor(Self)
#Else
  Return 1
#Endif
End Function

The ScalingFactor is 2 for a Retina MacBook Pro (or other HiDPI modes) and 1 for anything else, but the values could change depending on type of retina display.  You can use this value to determine the images size to use or for other scaling you may need.

Note that this only applies for bitmap graphics that are drawn on the Graphics instance in the Paint event handler.  Vector graphics scale automatically.

If you are manually double-buffering your graphics, you may find that they are not automatically drawn in retina quality.  Rather than double-buffering yourself, you should instead draw directly to the Graphics instance in the Paint event handler.  The DoubleBuffer property of Canvas works with retina displays.


Retina Graphics Sample Project

This sample project includes a built app that runs in retina mode on the retina MacBook Pro. You can see the difference quite readily.
Non-Retina Example

Retina Example




Wednesday, October 10, 2012

Code Editor Fonts

There are a wide variety of fonts that work great with the Real Studio code editor.  All are much better choices than using System or Courier! For best results, you are going to want to use a monospaced font.  Below are some of my favorites.

Monaco
This font is installed by default on OS X. It is big and very easy to read.



Code Editor using Monaco


Inconsolata
I've been using this font for years. It is a little smaller than other fonts at the same point size.


Code Editor using Inconsolata

Source Code Pro
Adobe just released this font and it is very nice. I'm going to try using it for a while.


Code Editor with SourceCodePro


ProFont
This is the first alternative font I used. I think the other mentioned here are nicer.



What do you use?
There are dozens of other great programming fonts. What is your favorite?

Thursday, October 4, 2012

Drawing Objects in a Canvas with the Paint Event

The Canvas control is a great way to draw pretty much anything to a window. We have been recommending for some time that all Canvas-based drawing be done only from the Paint event (or events called from it). This reduces flicker and increases performance.

I've had many people ask for an example for how to create a Canvas that allows you to:
  • Draw pictures within it (as objects)
  • Move these objects
  • Remove them
  • Add labels to them
  • Programmatically select one
ObjectsInCanvas Example Project
This example demonstrates how to do all these things. It has a large Canvas on the window with several buttons that let you add and manage the objects on the Canvas.

Use the SegmentControl to select the type of object you want to add to the Canvas and click the Add Object button.  You can optionally specify text to label the object using the field below the button.

Add as many objects as you want. You can click to select an object and it will have a black border drawn around it. You can drag any object within the Canvas.

The other buttons center or remove a selected object.  You can use the Select Object # button to select the object number you specify (objects are numbered starting at 0).

And there is no flickering on Windows.

About the Project

The CanvasObject class represents an object. It contains its coordinates, label text, the icon to display and other properties.

The ObjectContainerCanvas class is a subclass of Canvas that is responsible for all the drawing, moving and removing of CanvasObjects. This class has an array of CanvasObjects that it knows about. The objects in this array are drawn by the Canvas Paint event handler when appropriate.

To allow you to drag an object within the Canvas, the MouseDrag event handler is used. It knows what the currently selected CanvasObject is and as you drag the mouse, it updates the coordinates for the CanvasObject.

When the Canvas is told to redraw itself, usually through a call to Invalidate, the Paint event is called. Here each CanvasObject in the array is drawn on the screen.  This is the Paint event:

  If Background <> Nil Then
    g.DrawPicture(Background, 0, 0)
  End If
  
  For Each co As CanvasObject In mCanvasObjects
    Draw(g, co)
  Next

The Draw method draws the image for the CanvasObject at the specified coordinates and if it is selected also draws a border around it. This is the Draw method:

  // Draws a object on the canvas.
  
  g.DrawPicture(co.Image, co.Left, co.Top)
  
  If co.Selected Then
    Const kBorder = 2
    // Draw selection
    g.PenHeight = kBorder
    g.PenWidth = kBorder
    g.DrawRect(co.Left-kBorder, co.Top-kBorder, co.Width+kBorder*2, co.Height+kBorder*2)
  End If


In addition, ObjectContainerCanvas has two events that are called when an object is selected (ObjectSelected) and when an object is moved (ObjectMoved).

You can download the ObjectsInCanvas example from from here. You can also check out the other methods on ObjectContainerCanvas that are used to add, move, remove and select objects. I hope you find it useful in your projects.

Monday, October 1, 2012

Controls and Styles in iOS

We've got another update on Real Studio's developing iOS framework. iOS relies heavily on the use of styles so we want to get those out of the way early and that's the focus right now. Additionally, we are filling out the majority of events for the basic controls such as label, textfield, textarea and button.


Your app will have a default style which all controls will use unless you override it. In the above example, the first label is showing the default style. The other controls have custom styles. If this does not yet look like a native iOS app, know that these are all native controls. Most iOS apps have navigation bars and tabpanels which we have not yet implemented. But those are certainly on the schedule for the first release that includes iOS support.

It's important that we get the style support right because eventually web and desktop apps will use the same style API we develop for iOS. Put another way, we are designing a style API for iOS with the desktop, the web and iOS in mind.