The objects section describes objects which are to be created when the file is loaded into a running application. The section describes the intention of the programmer about the objects to be created - the logic of the GUI objects in other words - rather than the precise details of how the objects are to be created on each platform. For example, a button with title 'Quit' and which, when clicked, terminates the application, is described in the file as:
<button title="Quit" action="terminate:" />The actual appearance of the button (border, imaged used for borders, font used for the title, changing in the button appearance when it's clicked etc) is not specified - on each platform it will different - on platforms supporting themes or just preferences like allowing the user to change the default font size, it might even change each time depending on the configuration. The actual size of the button is computed automatically by the system at runtime when the button object is created; the title is also translated automatically at runtime (and the button sized depending on the translated title). All this information (appearance of buttons, fonts, images, translation of titles etc) really is not known till the program is run, so the button object will result different on each platform and each configuration.
For this reason, the list of objects really is more an abstract description of the logic of the GUI interface rather than an exact specification. Most tags correspond to objects; a few tags (such as the separators tag, used to add separators in menus and hboxes/vboxes) don't necessarily correspond to objects.
In some cases, different tags produce objects of the same class; for example the <label> tag creates a NSTextField object used to display a non-editable string (such as a title or a message), while the <textField> tag creates a NSTextField object used to display an area on screen where the user can insert some text. While they are implemented using the same class, they are conceptually completely different GUI objects, because they have a completely different function.
In other cases, the same tag could produce objects of different classes depending on the attributes it contains. As an extreme example, all tags representing view and window objects support an instanceOf attribute - if this attribute is set to a value, the value is used as the name of the class of the object to create (provided that this class is a subclass of the class which would be used by default). For example,
<button title="Example" />would create a button object (an instance of NSButton). Now if MyButton is a subclass of NSButton, then
<button title="Example" instanceOf="MyButton" />would do exactly the same, but allocating the object using the custom subclass MyButton instead of the default class NSButton. This is a very powerful and simple way of embedding customized objects in windows.
Normally, each tag creates an object, except in special cases, which are clearly documented.
<?xml version="1.0"?> <!DOCTYPE gsmarkup> <gsmarkup> <objects> <window> </window> </objects> </gsmarkup>or more simply (using the abbreviation <window />, which in XML means <window> </window>):
<?xml version="1.0"?> <!DOCTYPE gsmarkup> <gsmarkup> <objects> <window /> </objects> </gsmarkup>(in this example, we showed all the document; in the next examples of object tags, we will omit all but the tags we are discussing). If you load this example into an application, it will create a window, with no particular attributes and nothing inside it.
<window closable="no" />This causes the window to be created without the close button. Attributes are generally used to change the properties of that specific object. Each object is normally created with default properties set up for the most common case, so you only need to specify attributes when you need some special behaviour. In the example of the window, all windows are created by default with a close button, unless you specify closable="no".
A tag can have more than one attribute - it can have as many attributes as you need. The attributes are written one after the other one, separated by spaces. For example, if you want both to specify that a window should not be closable and not resizable, you can write
<window closable="no" resizable="no" />
We now examine and classify all the different types of attributes you can use for a tag. Some attributes have a string value, other a boolean value, other even describe a color or a font; other still give a name to the object so that it can be referenced from elsewhere; other are references to other objects.
<window title="Game Board" />The attribute title is set to a string, Game Board in the example. You can use any character in the string, except a few special XML characters - to insert them, you need to use the entities listed in table 2.1. Each tag has a different set of available string attributes - the reference documentation later on in this document lists all available string attributes for each tag.
<window title="Game Board" />created in your main.gsmarkup file. To translate the window into Italian, you need to add a file Italian.lproj/main.strings to your application, containing the following line:
"Game Board"="Tavolo di Gioco";when the application loads the .gsmarkup file, it automatically translates it into the appropriate language by using the appropriate main.strings file for that language. In this example, when the user sets Italian as his preferred language, the window will be created with title Tavolo di Gioco rather than Game Board. Please note that by default different .gsmarkup files use different .strings files - for each .gsmarkup file there is a corresponding .strings file. This makes it easier to keep the .strings files updated (you only need to update a .strings file when the corresponding .gsmarkup files has changed), makes clear where each string comes from, and allows different .gsmarkup files to contain the same English string, and yet to translate it in different ways.
<window resizable="no" />Resizable is a boolean attribute, which can be YES or NO. The example sets it to NO. For consistency with other stuff which is always written lowercase, we prefer to use yes and no (lowercase), but it is only a matter of taste: Yes, YES are valid as well as attribute values, and they mean the same thing as yes; No and NO are valid as well to mean no. For example, it is perfectly correct to write
<window resizable="NO" />and it means the same as before.
Each boolean attribute normally has a default value (documented in the documentation); if a boolean attribute is not present, or if its value can't be parsed as a yes or a no, then it's ignored, which implicitly means that the default for it is used.
<window width="200" height="50" />
<button image="mail" title="Read Mail" imagePosition="above" />would set the button image to the result of calling
[NSImage imageNamed: @"mail"]and position it above the title.
<button sound="mail" title="Read Mail" />would set the button sound to the result of calling
[NSSound soundNamed: @"mail"]
Color attributes are evaluated in the following way by the system:
<colorWell width="50" height="50" color="red" />will create a colorWell which displays the color returned by [NSColor redColor]. In this way, you can access all standard colors by name, such as black, white, red, blue, green, yellow, windowBackground, etc. (advanced tip: if you want to access a non-standard one by name, you can always implement a NSColor category, and add the method you need).
In some cases - for example when creating panels - you might want to display a title on top of your panel, or other information which you want to display in a more prominent font. This is typically needed by labels (objects which display non-editable strings such as those used by titles or messages). A font attribute allows you to specify the size and type of font you want.
The font for labels is normally found by calling the [NSFont labelFontOfSize:0] method (or the [NSFont systemFontOfSize:0] on older systems which don't have +labelFontOfSize:).
You normally only need to make the font bigger or smaller. To do it, you can use the following font values:
<label font="Big">Contents</label>displays Contents using the default label font, with the default label font size multiplied by 1.5.
You can also specify a float, which is read and interpreted as a scaling factor. For example,
<label font="1.5">Contents</label>is completely equivalent to setting the font to Big.
Normally, you only want to change the fonts in labels, and you don't need to bother with the font type. For very special cases, you might need to change font type. The current API used on OpenStep systems provides NSFont methods to get recommended fonts to be used on that particular platform to display various types of gui objects - for example, [NSFont userFontOfSize: 0] returns the font to be used for user editable text, in the default size. To choose that type of font, you just specify user as the font value, as in
<label font="user">Testing fonts</label>this draws the label using the font [NSFont userFontOfSize: 0]. In general, if the value of the font attribute is xxx, the NSFont method +xxxFontOfSize: (if it exists) is used to get the font. The types of fonts available on gsmarkup at the moment are label, boldSystem, user, userFixedPitch, menu, message, palette, system, titleBar, toolTips. These fonts should be available on other OpenStep systems as well, but not all them are available on older OpenStep systems, and you would extremely rarely need to use them anyway.
Anyway, if for example you want to display a button containing a title with a bigger font, you may need them.
You can use one of those standard fonts in any size, for example
<label font="user big">Testing fonts</label>is valid, and uses the same font returned by [NSFont userFontOfSize: 0] but enlarged by a factor of 1.20.
<window instanceOf="NPWindow" />This will create a window using the standard procedure used for windows, except it will lookup the class named NPWindow at run time, and allocate the object using that class instead of NSWindow class. Please note that all the standard attributes of a window object are still recognized:
<window instanceOf="NPWindow" width="100" height="100" resizable="no" />the attributes will be read, and applied to the window exactly as if the window was a standard NSWindow object. Because NPWindow is a subclass of NSWindow (and Renaissance will check it at run time), all the attributes and methods which are valid for a NSWindow are expect to be available for an NPWindow object too, so this is expected to work.
In detail, when you specify a instanceOf="XXX" attribute for a view or window tag, Renaissance at run time looks up XXX as a class name, searching for a class with that name. If a class with that name is found (in the executable), Renaissance checks that the class is a subclass of the default class (the one which would be used if no instanceOf attribute had been specified). If it is, then Renaissance allocates the new object using class XXX instead of the default class. It is important to check that the class XXX is a subclass of the default class, because that makes sure that the object created supports all the attributes and methods that a default object would. Then, everything else goes on as if the created object was of the default class.
The reference documentation should mark clearly tags which recognize the instanceOf attribute, and tags which ignore it.
As a final note, if you need to allocate an object which is not a subclass of a view or of a window, you can use the <instance> tag, which allows you to allocate an instance of any class (still specified with the instanceOf attribute). This is mostly used to create controller objects. Because of the wide generality of the objects allocated by the <instance> tag (which could be of any class), there are no defined attributes you can set for the object created, except for embedded outlets (described in the next sections), which are always available for all objects. The <instance> tag is normally used if you want to create an instance of a controller class from a gsmarkup file; it's not used to embed custom controls in windows, or to create custom windows: in those case, you are better off using a view or window tag with a instanceOf attribute, because then Renaissance will know what type of object it is and will treat it accordingly, including recognizing all the appropriate attributes.
<window id="board" />creates a window, and tags it with the id board. You can then refer to this window object by name whenever you need it (we'll explain later on when you might need to refer to the window object). Of course, you don't need to tag all objects with names - you would just be wasting time and making your gsmarkup files more difficult to read if you do - you only need to tag the ones which you need to refer to. The id name of the window is never shown to the user - it's only used internally to refer to objects by name, and establish connections between objects.
<button title="Test" action="dummyAction:" sendActionOn="leftMouseDown | leftMouseUp" />Note that spaces around the | character are ignored, so you can use them for readability if you want.
<window id="board" />then you can refer to that window object by using the syntax #board. #board simply means the object whose id name is board.
This is normally used to establish connections between objects. There are two ways of building connections between objects: one is adding a connector to a connectors section. A gsmarkup file can contain one or more connectors sections, where you can freely build up connections between objects (and you refer to objects by using their ids and the syntax just explained). The other way, the one we learn about here, is by using attributes which take objects as values.
Some attributes wants other objects as values. For example, a window can have a delegate, and this delegate is an object. You can specify the delegate object for a window by using the following syntax:
<window delegate="#myDelegate">When this file is loaded in a running application, the attribute delegate is treated specially. It is treated specially because its value begins with a hash (#), which means it is a reference to another object (in the file or even outside the file). When the file is loaded, the method setDelegate: of the window object will automatically be called, with argument the object which has id name myDelegate. This object can be
<window delegate="#NSOwner">Normally, all connections between the objects loaded in the file and the existing application objects are done through the file owner.
<popUpButton> <popUpButtonItem title="terminate the app" action="terminate:" target="#NSApp" /> </popUpButton>
Here is another example -
<window> <hbox> <button title="yes" id="yes" nextKeyView="#no" /> <button title="no" id="no" nextKeyView="#yes" /> </hbox> </window>in this example, a window with two buttons is created. The first button has title yes, the second one has title no. The first button is given id yes, and the second one is given id no. Then, the nextKeyView attribute of the first button is set to point to the second one, and the nextKeyView attribute of the second button is set to point to the first one. This allows users to navigate between the buttons using the keyboard - pressing TAB while the input focus is on an object moves the focus to the nextKeyView of that object. In the example, the nextKeyView of the first button is the second one, and viceversa, so by pressing TAB the user can move the input focus between the two buttons.
Technically, whenever an attribute has a value which begins with a single hash (#), the system automatically creates an outlet connector when the file is loaded (and adds it to the list of outlet connections to be established), and removes the attribute from the list of attributes. So if you wish to encode attribute text beginning with a hash, you must escape that leading hash by doubling it. No check is done on the attribute name, so you are free to setup any sort of connectors you want. When the outlet connections are established, the outlet will establish the connection from the source (the object bound to the tag where the attribute was found) to the target (the object which is referred by the attribute) using as key the attribute name. Establishing outlet connections uses key-value coding, so in practice by writing
<window delegate="#myDelegate">you are effectively establishing that the value of the key delegate of the created window object should be set to the object whose id is myDelegate.
This way of embedding outlets inside object description is both very natural and very powerful; it is certainly much more natural than having to write in the separated connectors section.
Renaissance allows the advanced syntax
<window delegate="#NSOwner.windowDelegate" />to mean extracting the value for the key windowDelegate of the #NSOwner object, and setting it as the delegate of the window. ``Extracting the value for the key'' will use key-value coding to get it. In practice, if the #NSOwner responds to one of the methods getWindowDelegate, windowDelegate, _getWindowDelegate, _windowDelegate, or has an instace variable called _windowDelegate or windowDelegate, to get the value for the key, the method will be called, or the instance variable will be read2.2.
This can be a very handy trick. Typically, you can have delegates (or other similar attributes) of objects created from the gsmarkup to be set to point to instance variables (or the result of calling certain methods) of the #NSOwner. It's an additional layer of flexibility.
Renaissance also allows multiple key-value coding in sequence, for example
<window delegate="#NSOwner.controller.preferenceWindowDelegate" />This will start with #NSOwner, use key-value coding to extract the value for key controller of that object, then use key-value coding to extract the value for key preferenceWindowDelegate of the resulting object, and finally set the window's delegate to the result.
To support both simple and multiple key-value coding, Renaissance uses -valueForKeyPath:. For example, when Renaissance processes the code
<window delegate="#NSOwner.windowDelegate" />it assigns to the window's delegate the result of calling
[#NSOwner valueForKeyPath: @"windowDelegate"]which, in this case, because there are no dots in the keypath, does the same as [#NSOwner valueForKey: @"windowDelegate"].
As a general rule, if the id of the source or target of an embedded outlet (or other connector) contains a dot (that is, the character '.'), then the string up to the dot is the id of an object to use, and the string following the dot is interpreted as a key path value to apply to the object in order to get the actual source or target of the connector. So, #xxx.yyy is to be interpreted as meaning ``the result of [#xxx valueForKeyPath: @"yyy"], where #xxx is the object with id xxx.''
For example, the expression #NSApp.mainMenu means the result of calling
[NSApp valueForKeyPath: @"mainMenu"]which will return the application main menu. #NSApp.delegate.description means the result of calling
[NSApp valueForKeyPath: @"delegate.description"]which will invoke the description method of the delegate of NSApp.
For more information, please refer to the documentation of key-value coding, for example the documentation for the -valueForKeyPath: and -valueForKey: methods.
<label>Please reboot your system</label>the open tag is <label>, the close tag is </label>, and the string inside is the content of the tag. Often, the content of a tag can include other tags. For example,
<window> <button title="Click me to quit" action="terminate:" /> </window>in this case, the content of the window tag is the button tag.
In general, while attributes are used to change the properties of the specific objects represented by that tag, the content of a tag are used to describe things which go inside the object. This concept is of course human and not completely well defined, anyway the idea is that, for example, the attributes of a <window> tag (such as closable="no", width="200", title="Main Window") change the general properties of the window, while the tags inside the window tag represent objects which should be displayed inside the window.
Now we examine typical examples of tags and the type of content they can hold.
<label>Missing image</label>displays a label with the string Missing image, while
<textField>user@host</textField>displays an editable textField which contains the string user@host when it is first displayed. In this case, the string to display is in the content of the tag because it can potentially be very big (and perhaps optionally contain formatting tags (html?) in the future for attributed strings ?).
In most cases, string content are localizable - they are automatically localized by the gsmarkup framework in the same way as it is done for localizable string attributes.
<menu type="main"> <menuItem title="Info" action="showStandardInfoPanel:" keyEquivalent="i" /> <menuItem title="Hide" action="hide:" keyEquivalent="h" /> <menuItem title="Quit" action="terminate:" keyEquivalent="q" /> </menu>When the menu is created, the menuitems are created and added to it. A menuitem has no content. If a menu contains another menu in its content, this other menu is created as a submenu.
<hbox> <button title="Save" /> <button title="Discard" /> <button title="Cancel" /> </hbox>displays the buttons inside the horizontal box (which is invisible, it is only used to align the buttons). Simple boxes contain a single object, which they display surrounded by a border (and optionally with a title). For example,
<box title="Mail Preferences"> <vbox> <!-- missing - vbox content --> </vbox> </box>displays a border box with title Mail Preferences, and inside it a vertical (invisible) box containing any sort of objects (not shown in the example).