TweetFollow Us on Twitter

VanGogh Smalltalk

Volume Number: 13 (1997)
Issue Number: 3
Column Tag: Tools Of The Trade

Making Smalltalk with the Macintosh Toolbox

By Lee Ann Rucker, Sunnyvale, CA

Using VanGogh and the next version of VisualWorks to build multiplatform applications with an Object-Oriented Smalltalk Interface to the Macintosh Toolbox

Introduction

VisualWorks is a Smalltalk application development environment from ParcPlace-Digitalk providing a very high degree of portability. Applications developed in VisualWorks run unchanged on a variety of platforms including Macintosh, Windows, OS/2 and unix. Unfortunately, it achieves this portability at the expense of platform compliance, by emulating all user interface elements ("widgets") using a small set of platform-specific graphic primitives.

This article discusses the VanGogh platform architecture - the reasons for its creation, how it manages cross-platform compatibility while providing native look-and-feel, how it works with the VisualWorks framework, and how it can be used to write Macintosh-specific code.

History

In the early versions of Smalltalk, the user interface on all platforms used a variation of the original Smalltalk look. The release of VisualWorks 1.0 added the concept of "LookPolicy". Each user interface had its own LookPolicy - MacLookPolicy, WinLookPolicy, etc. The emulation allowed cross-platform developers the opportunity to preview how their applications would look on the deployment platform. However, the benefits of this were overshadowed by the increasing liabilities - the challenge of faithfully rendering a given platform's look and especially feel and the maintenance cost to keep up with changes to the interfaces, which became even more difficult when customizable looks were introduced for Macintosh and Windows. The Macintosh look used the pre-System 7 black-and-white controls exclusively, the menu bars were a part of the window, scroll bars alongside the edge of a window were not constrained to their proper position, and the grow box was obscured by the window contents (Figure 1).

Figure 1. VisualWorks Macintosh window containing the menu bar.

The objective of the VanGogh project is to provide a VisualWorks product that maintains the portability of its predecessors, but adds host compliance by connecting directly to host widgets. In addition, it allows platform-specific codethat can coexist with writing applications that use the portable VisualWorks framework.

VanGogh Architecture

One of the goals of VanGogh was to maintain cross-platform portability. To that end, we implemented a layered architecture, shown in Figure 2. It divides objects into three layers: in the top-most layer are portable objects that are able to draw on the services provided by two lower layers. The middle layer implements a layered arch consisting of Bridge objects that supply a platform-independent API to the portable objects using capabilities drawn from the bottom-most layer. This bottom-most layer is shown in Figure 2.

Figure 2. Layered Architecture.

Portable layer

The Portable layer is used by a HostLookPolicy and has exactly the same interface, so existing applications already accustomed to using a LookPolicy for user interface elements run unchanged.

VanGogh allows the new framework to retain the snapshot portability of traditional VisualWorks, which allowed a system to be restarted in exactly the same state as when it was saved, a "snapshot" in Smalltalk terminology. When the snapshot occurs, the portable objects query their bridges for the current state. On restart, new Bridges and Proxies, appropriate for the platform, are created using the state stored in the snapshot. Thus a system can be shut down on a Macintosh and restarted on Windows, and it picks up precisely where it left off.

Bridge layer

The Bridge layer provides the portable layer with a platform-independent interface to the host. The interface between the Bridge and Portable layers is identical for all platforms. Each class in the Portable layer is matched with a class in the Bridge layer. However, the implementation and actual class hierarchy in the Bridge layer differs on each platform to take advantage of variations in implementations of the platform objects.

Platform access layer

The purpose of a platform access layer is two-fold. To provide support for the portable objects ("Proxies") and to furnish convenient direct access to the platforms facilities for discretionary use by the application developer. Proxy objects provide an interface to Toolbox functions, methods for accessing structure fields, utilities to support changes in the Macintosh user interface, and finalization. We discuss these in greater detail in the section "Features of Proxies."

Many of the proxy classes (see Figure 3) correspond fairly directly to the various Macintosh Toolbox Managers. Other classes support user interface elements not provided by the Macintosh, such as dividers and notebooks.

Event handling

One of the features provided by the Bridge is event dispatching and processing. VanGogh provides standard event processors for every component. Low-level Macintosh events are dispatched to the Bridges that use the proxies to handle them, then creates high-level VisualWorks events such as value or focus change.

Figure 3. Proxy Class Hierarchy.

Features of Proxies

Proxy classes are used to implement an object-oriented Toolbox. Its structure parallels as closely as possible to the Toolbox Managers, along with additional classes to support user interface elements not provided by the Toolbox (Figure 3). The methods are derived from the Toolbox API following a standard convention, and utility methods are added to standardize the API, allowing the abstraction of common functionality.

Toolbox methods

Toolbox methods are Smalltalk versions of functions provided by the Toolbox. To allow easy conversion from the Toolbox, we have adopted a consistent naming convention for Toolbox methods. According to this convention, Toolbox methods are created from Toolbox function names by removing the identifier for the corresponding Manager. The initial letter is lowercase for compliance with Smalltalk. Though this may seem more confusing than simply keeping the names of the Toolbox functions intact, it provides benefits in an object-oriented environment.

Consider the following: DisposeControl, TEDispose, DisposeWindow, DisposeMenu, DisposeDialog, LDispose, DisposeRgn, DisposeGWorld. Following the convention, all of these are implemented in the Proxy as dispose. We now have a uniform means of disposing of these Toolbox elements, while still keeping the names similar enough to the Mac Toolbox so that a Toolbox programmer will have little trouble converting existing Toolbox code.

The naming convention for methods with more than one parameter is to use the parameter name in the function specification as the Smalltalk keyword. (In Smalltalk, keyword messages are methods whose names consist of words followed by arguments, and the words themselves are called keywords.) For example, DragControl is defined in the Toolbox Assistant as follows:

void DragControl(ControlHandle theControl, Point *startPt,
 const Rect *limitRect, const Rect *slopRect,
 short axis);

We know that theControl corresponds to the proxy, so startPt is the first parameter of the corresponding Toolbox method. Therefore the method name is composed of the names of the remaining parameters, and when sent to a control object is used as follows:

aControlProxy drag: startPt limitRect: limitRect slopRect: slopRect axis: 
axis

Finalization

When the Proxy is finalized as part of Smalltalk's automatic garbage collection, the handle is automatically disposed of using the dispose method as described in the Toolbox Methods section. This helps prevent resource leaks and double-disposing, both common problems in Macintosh programming.

Toolbox extension methods

The Apple Human Interface Guidelines specify some behaviors and graphical elements which the Toolbox does not provide: typing selections in Lists, the keyboard focus border on Lists, the default border on default Push Buttons, the border and scroll bars for a TextEdit and the window grow box. Inside Macintosh and Apple DTS provide sample implementations of these facilities, which we include in the Proxies as Apple extensions to the Toolbox.

Shortcut methods

Proxies provide shortcut methods that simplify calling certain Toolbox methods.

• Methods with parameters that are ignored, either by the MacOS or by the programmer. Shortcut methods provide default values or placeholders where needed.

• Accessor methods which obtain values by passing a pointer to a parameter. Shortcuts for these methods create a temporary pointer and return the pointer contents.

The following example of a shortcut for GetDItem demonstrates both kinds of shortcuts - it provides placeholders for the unused itemType and box, and returns the contents of the pointer to the item. The section "Using the toolbox directly" discussed below, shows how to use this method.

MacDialogProxy
getItem: itemNo

    "Answer a handle to an item so that you can manipulate it"

 | itemType item box |

    "Allocate temporary pointers for the parameters"
 itemType := Host api short malloc.
 item := Host api Handle malloc.
 box := Host api Rect malloc.

    "Call the Toolbox"
 self getItem: itemNo
 itemType: itemType
 item: item
 box: box.
 ^item contents


Enhanced methods

As the Macintosh User Interface evolved, Apple sometimes had to overload existing functions to provide new capabilities. One example of this is submenus - these did not exist in early System versions. A submenu is added by overloading SetMenuItemCmd, and passing in a special constant for the cmdChar. We conceal the clumsiness of these overloaded functions by providing enhanced methods like this one that takes the item and submenuID and calls SetMenuItemCmd with the appropriate constant.

MacMenuProxy
setSubmenu: item submenuID: aMenuID

    "Utility to set itemCmd and itemMark correctly for a submenu with the given id"

 self setItemCmd: item
 cmdChar: #hMenuCmd;
 setItemMark: item
 markChar: aMenuID

Data conversion

Toolbox methods provide data conversion when calling the Toolbox.

• Smalltalk Booleans are converted to integers.

• Smalltalk Strings are converted to Pascal Strings, which begin with a length byte.

• Toolbox flag constants are passed in a Smalltalk Set and automatically "or"ed together.

The following example from the MacAppleEventProxy calls the Toolbox method representing AESend.

    "Send an AppleEvent without waiting for a reply"
 self send: theEvent
 reply: reply
 sendMode: #(#kAENoReply #kAENeverInteract                     
 #kAECanSwitchLayer)
 sendPriority: #kAENormalPriority
 timeOutInTicks: #kAEDefaultTimeout
 idleProc: nil
 filterProc: nil.

Complex types like Points and Rects are not converted in place by Toolbox methods, though there are utility routines that will convert complex types to and from their Toolbox representations.

Accessor methods

Proxies provide accessors to structure elements. This allows us to rationalize the names that can simplify the code. It also gives the appearance of data hiding, an important feature of object-oriented programming.

Other utility methods

VisualWorks sometimes does operations or requests information not directly available from the Toolbox. For example, VisualWorks has a bounds method that moves and resizes a widget. It also explicitly sets the row height of a List, because it scrolls the List in pixels, not rows. Utilities like bounds, which may be generally useful, are implemented in the Proxy to avoid forcing the Toolbox programmer to duplicate code. Others, such as setting the row height, are implemented in bridge objects as Toolbox programmers seldom need them.

Other utilities rationalize the names of functions that exist in different forms for different proxies - activation, for instance, is done by passing 0 or 255 to HiliteControl, true or false to HiliteWindow and LActivate, and calling either TEActivate or TEDeactivate. We add an isActive method to all of the appropriate proxy classes that calls the corresponding Toolbox method and checks to determine whether the object is in an active window. The following examples show the isActive methods for Controls, Lists, and TextEdits. Note that the TextEdit method includes an extra check to see if the activation is changing, because activating an already active TextEdit causes annoying flickering.

MacControlProxy
isActive: aBoolean

    "Make this proxy active or inactive"
 isActive := aBoolean.
    "Control is active iff isActive is true and window isActive is true"
 self isActive   
 ifTrue: [self hilite: 0]
 ifFalse: [self hilite: 255]

MacListProxy
isActive: aBoolean

"Make this proxy active or inactive"
 isActive := aBoolean.
    "List is active iff isActive is true and window isActive is true"
 self activate: self isActive 

MacTextEditProxy
isActive: aBoolean

    "Make this proxy active or inactive"
 | wasActive |
    "Don't set activation if it's not changing - bad behaviour results"
 wasActive := isActive.
 isActive := aBoolean.
    "TextEdit is active iff isActive is true and window isActive is true"
 wasActive = self isActive ifFalse: [
 self isActive ifTrue: [
 self activate]
 ifFalse: [
 self deactivate]]

Using the toolbox directly

Sometimes there is no reason to use a proxy object, and it is better to call the Toolbox directly.

• When the Toolbox function operates on a global state, rather than a parameter. For example, QuickDraw drawing functions operate on the currently active GrafPort. Although it is possible to use a QuickDraw Proxy, it is more efficient to do drawing the traditional way, by setting the port and then performing a series of drawing operations.

• When the host resource is not created by the programmer, it has no Proxy object. For example, the controls in a Dialog or List are created internally by the Toolbox. The code snippet below uses the shortcut method shown above to get a ControlHandle from a dialog proxy, then calls the Toolbox directly to set its value.

 control := dialogProxy getItem: i.
 Host api SetControlValue: control theValue: aValue

Event Handling

Event Dispatcher

The VanGogh environment maintains a single MacEventDispatcher object. This object polls WaitNextEvent, handles suspend/resume events, and dispatches window events to the appropriate window.

When an event occurs on a specific window, the Event Dispatcher object sends a MacEventProxy object to the Smalltalk object which has registered an interest on that event on that window. The following example shows how a windowBridge registers an interest on mouseDown events which occur in a particular window.

windowProxy forwardEvent: #mouseDown to: windowBridge

The Event Dispatcher object allows registry of window events and nullEvents, which are sent to the currently active window. The inclusion of nullEvents allows windows a chance to do any recurring operation, such as sending idle to the currently active TextEdit proxy.

We don't dispatch user input events to component proxies in the MacWindowProxy. The Bridge level handles this for VisualWorks. The utilities that the bridge uses when handling events are part of the Proxies and are accessible to non-VisualWorks code. Amongst these, the most complicated is the findChild: utility discussed in the next section.

The findChild utility method

The Macintosh Toolbox does not provide a general-purpose hit testing routine. FindControl is a useful function, but it has its limitations. As anyone who has used it on a window with Lists has discovered, it will return the ControlHandle for controls created as part of a List. This causes problems when trying to write generic tracking routines, as those controls must be handled by LClick, not TrackControl.

The findChild method implemented in MacWindowProxy, provides a way to identify which proxy was hit on a window. It works by first using FindControl. If a control was hit, we check the control's refCon to determine whether the control is part of a List or other composite proxy - the refCon of a subcontrol holds the handle of the proxy that it belongs to.

If FindControl doesn't find anything, then the proxy hit may be a List or TextEdit, or one of the enhanced proxies. In this case, findChild sends the hitTest method to all the proxy objects contained by the window proxy object. A proxy must return true if the mouse point is within its boundaries and it is capable of handling mouse events.

If the point is not in an active proxy, findChild returns nil. Otherwise, it returns a MacEventDetails object which contains the proxy that was hit and any extra information needed for tracking, such as the part code or scroll bar. This extra information is used during scroll-bar tracking, amongst other things.

Event Proxy

The MacEventProxy represents a Macintosh EventRecord. After EventDispatcher determines which window should receive the event, the WindowBridge dispatches the event to the appropriate subcomponent.

For example, if the mouse is clicked in the scroll bar of a list, the EventDispatcher would send a mouseDown event to the window bridge. The window bridge would find the part hit, in this case inContent, and call mouseDownInContent. This dispatches the event to the result of findChild.

The findChild for the list sets the keyboard focus, then sends the event to the Proxy, using the proxy method for LClick. It uses the scroll bar information in the details object to make sure it doesn't update the selection when the user scrolls.

The elements of the EventRecord are accessed using Smalltalk methods with the same name as the struct elements. We also add utilities such as local, that returns the point local to the front window.

MacScheduledWindowBridge
mouseDownInContent: theEvent

    "Handle a mouse down event in the content area of my window"

 | thePoint eventDetails |

    "Get the local point for my window"
 thePoint := theEvent local.

    "Find the child which was hit. The result contains the proxy hit and any extra information it may need, or 
nil if nothing hit"

 eventDetails := self macWindow findChild: thePoint.
 eventDetails isNil ifTrue: [^nil].

    "Send the event to the bridge representing the child"
 (self bridgeFor: eventDetails proxy) mouseDown: theEvent
 details: eventDetails

MacHostListBridge
mouseDown: theEvent details: anEventDetails

    "Handle a mouse down event at the specified point, with the given modifer keys pressed. anEventDetails 
stores information needed to determine which part of the List was hit"

 | isDoubleClick lastClick |

 self listBox hasFocus 
 ifFalse: [self setFocus].

    "Send the click information to the toolbox, and answer whether it was a double click"
 isDoubleClick := self listBox click: theEvent local modifiers: theEvent 
modifiers.

    "anEventDetails details is nil if hit in my content, otherwise it is the scroll bar handle. 
    We don't want to send selection changed when the scroll bar was hit"
 anEventDetails details isNil ifTrue: [
 lastClick := self listBox lastClick.

self updateWidgetSelections: lastClick.
 isDoubleClick ifTrue: [self widget eventDoubleClick]]

Menu and Menu Bar Proxy

The MacMenuProxy provides access to the Menu Manager. The Menu Manager provides support for individual menus, but very little support for multiple menubars, as the original assumption of the Macintosh User Interface was that each application had a single menubar. VisualWorks assumes multiple menubars, as is the case in many other platforms. Therefore, the MacMenuBarProxy class provides the support that allows each window its own menubar.

VanGogh creates a MBarHandle for each window and uses SetMenuBar to reinstall it when the window is switched to the front. MenuBars that change with the context have become accepted with the introduction of OpenDoc and their usage in applications such as Word and Netscape.

Figure 4. The VanGogh Programming Environment.

Figure 4 shows the VisualWorks development environment with the Launcher's window and menubar active. The menubar proxy works with the event dispatcher to provide setup and handling of the Apple, Application and Help menus, and the MacHelpMenuProxy class provides utilities for adding and managing items on the Help menu.

We also provide facilities within the VisualWorks framework for Smalltalk-style pop-up menus controlled by option-clicking or three-button mice. Although not Mac-compliant, these are available for use by Toolbox programmers.

Undo Proxy

Figure 5. The Undo Proxy.

The MacUndoProxy (Figure 5) stores the data and code objects needed to perform an Undo operation for a particular proxy. This is an example of the Memento pattern (Gamma et al 1995).

To do an Undo you must store the state of the object prior to the undoable action, and some way of determining how to effect the undo. The simple way to do this is to store the value and restore it for an Undo, but that is only feasible for simple objects like Controls - for Text objects this could result in the duplication of quite a lot of text. Therefore, the TextProxy creates an UndoProxy and stores just the text to be changed (the current selection or empty selection), the location of the text, and a BlockClosure, which is a section of code that is bundled with its context, to store the code needed to undo the action. When it is time for the undo, the Undo Proxy simply executes the stored code. Since the context of the code is that of the object that needs the undo, it has access to that object and all the information stored in the undo proxy for that object. If the action is a paste, the undo is treated as a paste, which includes creating an UndoProxy to undo this undo.

Since Smalltalk is an untyped language, the data in the UndoProxy can be any object. For instance, a draw program might store a shape object and its current size to undo a cut or resize.

One advantage of an object-oriented language like Smalltalk is that UndoProxies are self-contained objects linked to the Proxies that created them, and the programmer does not need to keep track of them. The standard Undo menu handler for a Proxy simply executes the UndoProxy if it exists.

Creator Objects

The Creator object uses the Accumulator pattern (Yelland 1996) to gather information needed to create a platform object. While some Toolbox calls to create platform objects are fairly simple, others are quite complex. Consider the Toolbox call to create a new control.

NewControl(myWindow, &aRect, ìPush Meî, true, 0, 0, 1, pushButProc | 
useWFont, 0);

A direct transcription of this into Smalltalk would be rather clumsy. In addition, all pushbuttons share the same value, min and max. Rather than require all default values be explicitly declared, and attributes defined by flags to be specified all at once with a somewhat clumsy series of ORs, we use the Creator object. This object is created when a particular widget is being built and understands messages which set the various attributes. Any attributes not set when the widget is finally created use default values. The flag attributes are specified by individual messages to the Creator, which makes the code more readable as well as allowing sets of attributes to be set independently - scrollBars, for example, don't need useWFont, so the test whether that should be used is only done in the code specific to the various Buttons.

The code below creates the same button as above. The actual VanGogh code is even simpler, as the button classes use abstraction to set most of the values, since another advantage is that not all attributes need be declared in the same statement.

 buttonProxy := (MacControlProxy creatorClass new)
 pushButton;
 useWindowFont;
 title: ëPush Meí;
 bounds: aRect;
 parent: myWindow;
 create.

In addition:

• It sets the parent-child relationship between a window and other proxies, so that hit testing and updating are handled correctly.

• It sets the refCon in controls that are part of other host resources so that their owner can easily be identified.

• It establishes the relationship between a menu bar proxy and its window, so the correct menu bar is installed when the window is active.

• It blocks context switching during TextEdit creation. Unlike other creation functions, TENew does not take a GrafPtr as a parameter, so if the GrafPort gets changed by another thread, the TextEdit will be created on the wrong window.

Sample Code

The Sample Code shows how to create a proxy for a Type-In Pop-Up Menu Control, as described in Macintosh Human Interface Guidelines, pages 91-92. The MacTypeInPopUpProxy class provides methods which create subcomponents (a TextEdit and a PopUpMenuControl), set the bounds, and handles events specific to this kind of control. The mouseDown event for the menu subcomponent must insure that the menu displays the correct behavior, which is to insert the text from the TextEdit into the menu, followed by a separator, when the contents of the TextEdit do not match any of the menu values.

The example method produces the window shown in Figure 6.

Figure 6. A Type-In Pop-Up Menu Control.

Listing: MacTypeInPopUpProxy.st

‘From VisualWorks(R) VanGogh beta release 1 of January 1996 on March 
5, 1996 at 11:07:50 am'!

MacComponentProxy subclass: #MacTypeInPopUpProxy
 instanceVariableNames: ‘control inputField items ‘
 classVariableNames: ‘'
 poolDictionaries: ‘'
 category: ‘Mac PlatPok-Examples'!
MacTypeInPopUpProxy comment:
‘This class implements a Type-In Pop-Up Menu, which consists of a TextEdit and a PopUpMenu Control. 
It is described in detail in Macintosh Human Interface Guidelines, pages 91-92. 
Although it is documented, the Macintosh does not provide a single control which implements this behaviour. 
Therefore this class implements it by holding on to a MacTextEdit and a MacPopUpMenuProxy. 
This class is an example, and only implements the behaviour. A real proxy would provide event notification 
and data accessing so a client could do something when the value changes.

Instance Variables
    control: a MacPopUpMenuProxy
    inputField: a MacTextEdit
    items: for speed we store the menu items'!


!MacTypeInPopUpProxy methodsFor: ‘instance creation'!

createBounds:items:initialSelection:
Create the menu and input fieldsubcomponents and set the initial values. Adjust the bounds as appropriate 
for each subcomponent.

createBounds: bounds items: aSet initialSelection: anInteger 

    "Create a Type-In PopUpMenu Control with the given values."

    "Create my components"
 self createMenuControl: (self menuBoundsFrom: bounds);
 createTextControl: (self inputFieldBoundsFrom: bounds).

    "Cache the items, then insert them into the menu"
 items := aSet.
 items do: [:item | 
 control menu append: item asPascalString]. 
    
    "Set the input field to the initial selection"
 inputField setText: (aSet at: anInteger)! !

!MacTypeInPopUpProxy methodsFor: ‘component creation'!

createMenuControl:
Use the appropriate creator object to make a pop-up menu control

createMenuControl: bounds

    "Create my menu and configure it appropriately"

 control := (MacPopUpMenuProxy creatorClass new)
 smalltalkWindowClass: MacPopUpMenuProxy;
 clientBounds: bounds;
 window: self window;
 createWindow!

createTextControl:
Use the appropriate creator object to make a text edit object

createTextControl: bounds

    "Create my input field and configure it appropriately"

 inputField := (MacTextEditProxy creatorClass new)
 clientBounds: bounds;
 styledText: false;
 window: self window;
 createWindow! !

!MacTypeInPopUpProxy methodsFor: ‘bounds utilities'!

inputFieldBounds:
Adjust aBounds to allow space for a pop-up menu button on the right side

inputFieldBoundsFrom: aBounds

    "Answer a bounds rectangle for the input field"

 ^(aBounds copy) 
 right: aBounds right - Host api kMenuWidth - 1;
 bottom: aBounds top + Host api kMenuHeight!

menuBoundsFrom:
Adjust aBounds to put the pop-up menu button on its right side

menuBoundsFrom: aBounds

    "Answer a bounds rectangle for the popup button"

 ^(aBounds copy) 
 left: aBounds right - Host api kMenuWidth;
 bottom: aBounds top + Host api kMenuHeight! !


!MacTypeInPopUpProxy methodsFor: ‘event processing'!

activateEvt:
Handle an activate event by forwarding the event to the subcomponents

activateEvt: theEvent

    "Dispatch the events to my components"

 control activateEvt: theEvent.
 inputField activateEvt: theEvent!

mouseDown:
Let the window handle non-content area events. standardMouseDown: answers true if it handled the event 
- if false, handle it in mouseDownInContent:, below.

mouseDown: theEvent

    "Handle a mouse down event in my window"

    "The standardMouseDown: method handles everything but inContent"
 (self window standardMouseDown: theEvent) ifFalse: [
 self mouseDownInContent: theEvent]!

mouseDownInContent:
Find the subcomponent that was hit. If it was the menu, handle it in mouseDownInMenu:, if not, let the input 
field proxy do its default event processing.
mouseDownInContent: theEvent

    "Handle a mouse down event in my window's content"

 | thePoint eventDetails |

    "Find the child which was hit. eventDetails contains the control object and the part code, or nil"
 eventDetails := self window findChild: theEvent local.
 eventDetails isNil ifTrue: [^nil].

    "Send the event to the appropriate component"
 eventDetails proxy = control ifTrue: [
 self mouseDownInMenu: theEvent]
 ifFalse: [
 inputField mouseDown: theEvent details: eventDetails]!

mouseDownInMenu:
If the input field contains something other than what is in the menu, add its contents to the menu as specified 
in the Human Interface Guidelines

mouseDownInMenu: theEvent

    "Handle a mouse down event in my menu"

 | match thePoint |

    "Add the input field text if it's not in the menu"
 (match := items includes: inputField getText) ifFalse: [
 control menu insert: inputField getText.
 control menu insert: Host api menuSeparator.

    "Track the control. If a menu item is selected, put its value into the input field"
 (control track: theEvent local) > 0 ifTrue: [
 inputField setText: (control menu getItemText: control getValue)].

    "Remove the extra items, so the menu only has them during mouse processing"
 match ifFalse: [
 control menu deleteItem: 1;
 deleteItem: 1]]! !

!
"- - - - - - - - - - - - - - - - - - "!

MacTypeInPopUpProxy class
 instanceVariableNames: ‘'!

!MacTypeInPopUpProxy class methodsFor: ‘instance creation'!

newOnWindow:bounds:items:initialSelection:
Create a new proxy and set its initial values

newOnWindow: aWindow bounds: aBounds items: aSet initialSelection: anInteger

    "Create and return a new proxy"
 ^(self new)
 window: aWindow;
 createBounds: aBounds items: aSet initialSelection: anInteger;
 yourself! !

!MacTypeInPopUpProxy class methodsFor: ‘examples'!

example
Create a window proxy which contains a type-in pop-up proxy. Register interest in the events we need to 
handle. Since this window has only one input field, we can have the keyboard events sent directly to the 
input field. The only event which requires special handling is mouseDown, because we have to set up the 
menu if the text has changed. Otherwise we could let the window handle all events.

example

    "Demonstrate a Type-In PopUpMenu Control"
    "MacTypeInPopUpProxy example"

 | windowProxy proxy |
    "Use a window creator to make a window proxy"
 windowProxy := (MacWindowProxy creatorClass new)
 clientBounds: (50@50 extent: 180@100);
 title: ‘PopUp Example';
 createWindow.

    "Create a Type-In Pop-Up proxy on the windowProxy"
 controlProxy := self newOnWindow: windowProxy
 bounds: (10@10 extent: 100@19)
 items: #(‘Seagoon' ‘Eccles' ‘Moriarty' ‘Henry' ‘Min' ‘Bluebottle')
 initialSelection: 2.

    "Register events which the proxy will handle"
 windowProxy
 forwardEvents: #(
 mouseDown)
 to: controlProxy.

    "Register events which only the text field needs to handle"
 windowProxy
 forwardEvents: #(
 keyDown
 autoKey
 nullEvent)
 to: controlProxy inputField.

    "Register events which the window can handle"
 windowProxy
 forwardEvents: #(
 activateEvt
 updateEvt)
 to: windowProxy.

windowProxy show.

 ^windowProxy! !

Bibliography and References

Yelland, Phillip M. "Creating Host Compliance in a Portable Framework: A Study in the Reuse of Design Patterns". OOPSLA 1996.

Goldberg, Adele and Robson, David. Smalltalk-80: The Language and its Implementation. Addison-Wesley, 1983.

Apple Computer Inc. Macintosh Human Interface Guidelines. Addison-Wesley, 1992.

Gamma, E., Helm, R., Johnson, R., Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.

 
AAPL
$100.75
Apple Inc.
+0.64
MSFT
$46.36
Microsoft Corpora
-0.08
GOOG
$577.36
Google Inc.
+1.00

MacTech Search:
Community Search:

Software Updates via MacUpdate

QuickBooks 2015 16.0.0.1352 R1 - Financi...
QuickBooks 2015 helps you manage your business easily and efficiently. Organize your finances all in one place, track money going in and out of your business, and spot areas where you can save.... Read more
Mac DVDRipper Pro 5.0.1 - Copy, backup,...
Mac DVDRipper Pro is the DVD backup solution that lets you protect your DVDs from scratches, save your batteries by reading your movies from your hard disk, manage your collection with just a few... Read more
Apple OS X bash Update 1.0 - Fix for sec...
The OS X bash Update fixes a security flaw in the bash UNIX shell on OS X 10.9.5 (also on OS X 10.8 and 10.7 [see Related Links below]). OS X 10.9.5 or later Downloads for OS X 10.8 and OS X 10.7 in... Read more
SyncTwoFolders 2.0.5 - Syncs two user-sp...
SyncTwoFolders simply synchronizes two folders. It supports synchronization across mounted network drives and it is a possibility to run a simulation showing in a log what will be done. Please visit... Read more
FinderPop 2.5.7 - Classic Mac utility, n...
FinderPop is a Universal preference pane that extends OS X's contextual menus using a FinderPop Items folder much as the Apple Menu Items folder used to do for the Apple menu. It has other features... Read more
VueScan 9.4.45 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
LibreOffice 4.3.2.2 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
calibre 2.4 - Complete e-library managem...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more
Default Folder X 4.6.9b1 - Enhances Open...
Default Folder X attaches a toolbar to the right side of the Open and Save dialogs in any OS X-native application. The toolbar gives you fast access to various folders and commands. You just click... Read more
Beamer 2.0.4 - Stream any movie file fro...
Beamer streams to your Apple TV.... Plays any movie file - Just like the popular desktop movie players, Beamer accepts all common formats, codecs and resolutions. AVI, MKV, MOV, MP4, WMV, FLV. To... Read more

Latest Forum Discussions

See All

apeFilter (Music)
apeFilter 1.0 Device: iOS Universal Category: Music Price: $6.99, Version: 1.0 (iTunes) Description: | Read more »
Shred It! Review
Shred It! Review By Jennifer Allen on September 30th, 2014 Our Rating: :: GORGEOUS BUT BASICUniversal App - Designed for iPhone and iPad It might look lovely, but Shred It! is a pretty shallow endless runner/snowboarding game.   | Read more »
It Came From Canada: Angry Birds: Transf...
Anyone afraid that throwing Transformers into the Angry Birds mix would result in a Michael Bay-level of childhood pillaging can rest easy. While Rovio’s famous fowls may be a 21st century staple, Angry Birds: Transformers wears its affection for... | Read more »
Galaxy Trucker Review
Galaxy Trucker Review By Rob Thomas on September 30th, 2014 Our Rating: :: IN FOR THE LONG HAULiPad Only App - Designed for the iPad We got a great big convoy, rockin’ through the night… the endless night of outer space, that is.... | Read more »
Moon and Sun – children’s book Review
Moon and Sun – children’s book Review By Amy Solomon on September 30th, 2014 Our Rating: Moon and Sun is a fable-like children’s book with lush illustrations.   Developer: Marcio Monteiro Price: $3.99 Version: 1.0 App Reviewed on... | Read more »
Kairosoft Finally Brings The Pyraplex to...
Kairosoft Finally Brings The Pyraplex to iOS Posted by Jessica Fisher on September 30th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Yeti’s Parole Officer Review
Yeti’s Parole Officer Review By Jennifer Allen on September 30th, 2014 Our Rating: Universal App - Designed for iPhone and iPad Yeti’s Parole Officer proves that looks aren’t everything in this entertaining, if plain, piece of... | Read more »
Record Keeper and World Wide Words – Two...
Record Keeper and World Wide Words – Two New Final Fantasy Games Arrive on iOS in Japan Posted by Ellis Spice on September 30th, 2014 [ permalink ] | Read more »
iPollute Review
iPollute Review By Nadia Oxford on September 30th, 2014 Our Rating: :: iPOLLUTE, uPOLLUTEUniversal App - Designed for iPhone and iPad iPollute is a delightful-looking pollution simulator, though it’s a bit light on “game” content... | Read more »
This Week at 148Apps: September 22-26, 2...
Your Source For The Latest App Reviews   | Read more »

Price Scanner via MacPrices.net

MacBook Airs on sale for $100 off MSRP, start...
Best Buy has the new 2014 MacBook Airs on sale for $100 off MSRP on their online store. Choose free home shipping or free local store pickup (if available). Prices valid for online orders only, in-... Read more
Apple Releases OS X Mavericks bash Update 1.0...
Apple has released a patch update for OS X Mavericks users to address the recently-detected “Shellshock” security bug in BSD UNIX’s bash shell. Apple says only a few Mac users who had manually... Read more
Pivotal Payments Ready for Apple Pay – FlexPo...
Pivotal Payments, a provider of merchant services and global payment processing solutions, has announced its proprietary FlexPoint platform will support credit and debit transactions through Apple’s... Read more
iStabilizer Announces Tabarm — First Friction...
iStabilizer, a specialist in universal lightweight compact tripods, steady cams, dollies, mounts, and remotes for smartphones, tablets, and cameras, announced today the iStabilizer tabArm, the first... Read more
IStabilizer Flex Smartphone Tripod Wins Usa T...
iStabilizer, a specialist in universal lightweight compact tripods, steady cams, and other products for smartphones, tablets, and cameras, has announced today that its iStabilizer Flex smartphone... Read more
13-inch 2.8GHz Retina MacBook Pro on sale for...
B&H Photo has the new 2014 13″ 2.8GHz Retina MacBook Pro on sale for $1699.99 including free shipping plus NY sales tax only. They’ll also include free copies of Parallels Desktop and LoJack for... Read more
15-inch Retina MacBook Pros on sale for up to...
B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY sales tax only. They’ll also include free copies of Parallels Desktop... Read more
Get Down to Business On Your iPad With Free N...
Utah based Poem LLC has introduced Nexticy 1.0, a full-featured, affordable (Free) forms application for iPad. Nexticy makes it ‘way easy’ to create, store, retrieve and analyze paper-like forms of... Read more
13-inch Retina MacBook Pros on sale for $100...
Best Buy has 13-inch 2.6GHz Retina MacBook Pros on sale for $100 off MSRP on their online store. Choose free shipping or free local store pickup (if available). Prices are for online orders only, in-... Read more
Reports Outline Claimed 12-Inch Macbook Air D...
Blogger Jack March says that according to information he’s received from an anonymous “source familiar with Apple’s plans,” the next-generation MacBook Air will launch in mid-2015 and be available... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.