Window Controller uses four tables: Window Size, Window Alignment, Window Settings, and the System table to rule them all, that is to actually calculate the final dimensions. All these tables are only for you, users never need any of them.

To use Window Controller you define window sizes and alignments, name them and then refer to them by name, e.g. as to “Preferences” window with “Default” alignment. This is probably the most common use, but if necessary, you can also override any parameter at runtime.
Both Size and Alignment are simple tables and all their fields are pure data (with just a few validation rules that are meant to be more like hints). Here's the interface of the Size table.

The name is a unique name for the window. This is an internal name and its main purpose is to fetch the settings for the window. But if you don't specify a custom title when opening a window, the system will use this internal name as the title, so I typically select meaningful names.
The width and height can be specified not only in pixels, but also in inches or centimeters. The status area and zoom settings specify the target settings for the new window. The controller users them to calculate the final size and the primary script that opens new windows automatically applies these settings.
The option to scale down to fit is designed to open properly sized preview windows. Previews don't have to display at 100%; actually, they may look better at smaller zoom level. What's important for them, I think, is to show the whole page without empty space around:

and, of course, make sure the window fits on screen. You cannot know beforehand what zoom level you need, so you set some starting zoom level (likely 100%) and turn on the “scale down” checkbox. This makes the controller to try different zoom levels until the window fits.
There's also a special advanced option that defines a scale margin. It is set once right in the controller's code.
The view settings specifies the view state of the new window. Unlike status are and zoom level it is not applied automatically (nor is it saved in settings).
Finally the default alignment is one of predefined alignments from the Alignment table, which we review next:

To specify an alignment you select the reference, that is the object to align against. It can be the desktop, the current window or some layout object. Then you select which points of the reference and the new window to align and optionally specify horizontal and vertical offset.
If you have a window on your desktop and a named object inside the window (as on the scheme on the right) then the following alignment settings will work as shown:
| Alignment settings | Meaning | Result |
![]() |
Align the center of the new window to the center of desktop. | ![]() |
![]() |
Align the top left corner of the new window to the top right corner of the current window. | ![]() |
![]() |
Align the top left corner of the new window to the bottom left corner of “My Object” and shift down by 2 px. | ![]() |
![]() |
Align the top left corner of the new window to the top left corner of the current window and shift down and right by 16 px. | ![]() |
Since sizes and alignments are records, you can import them between projects and let common ones stay in your standard template.
To test a new window use the Test command in the list of sizes. It tries to apply everything: size, status of FileMaker controls, alignment, etc. The only thing it cannot test, of course, is alignment against an object.
In FileMaker you either ‘simply’ open a window or open it when going to related record. The former is rather generic, so Window Controller wraps this into a single script:

The Open script wraps several lower-level steps. First, it reads window settings, then opens a new window and finally applies status and zoom settings.

To open a new window when going to related record you directly use these steps, but replace Open Window with Go to Related Record:

First, you use the Read settings script to read settings for the window:

The Read settings scripts places the calculated dimensions into global fields in the System table. On the Go to Related Record step you pick these values:

To open a window for a print preview, you need to change the steps a bit. The preview mode doesn't copy zoom level from browse mode, but keeps its own settings, so to open a properly zoomed window for preview you need to apply zoom settings in preview mode. In most cases there will be some extra steps in-between:

The special option to zoom down a window to fit is applied automatically.
Sometimes your application needs to have only one window for something, so your scripts need to open the window only if it is not open yet, but if it is open, they should just switch to this window. But how do you know if it's already open or not? Checking the result of Get( WindowNames ) is not enough, because it lists all windows, including those from other files. The correct way is to try to switch to this window with “current file only” on:

and then check if this raised error 112, “Window is missing”:

To correctly resize window when status toolbar is turned on or off, define a new View menu or whatever menu you use to keep the Status Toolbar command (it used to be Status Area) and change the command to call the Toggle Status Toolbar script. The script toggles the toolbar and adjusts window width or height, so the content size doesn't change.

To open a window with a custom title, specify it as the “title” parameter for the Open script. (When you open a new window during Go to Related Record step you specify the title directly.) Note that custom titles mean more work when saving window settings.

To save window settings define a new File menu or whatever menu you use to keep the Close command in and change the command to call the Save settings and close script.

This script works best when you use internal names as titles, because in this case you don't need to specify any parameters. If you use custom titles, you need to explicitly pass the internal window name.
Once saved, these settings are used automatically. That is when you use the Open or Read window settings scripts, they check if the current user has saved settings and if yes, use these settings; otherwise they pick default ones.
The settings are kept in the Window Setting table. The table is again a pure data table with some auto-enter options. It remembers user account, window name, top, left, width and height of contents, state of the status area, zoom level and optionally ID of a record and some custom state.
The table has no user interface, it just works behind the scenes. There's a subscript to delete settings from this table, with optional “user” parameter; if you need it, place it where it will make sense for your users.
Note that you need to migrate window settings data between versions as data from any other user table. Also note that the Close command disappears in runtime applications, so if you need it a runtime, don't override the Close command, but use a blank command instead.
Saving settings for individual records. The controller can save settings not only for whole classes of windows, but also for individual items. E.g. you may open some text notes in individual windows. The notes may be of different length and users will resize the windows to read them comfortably, and, of course, it makes perfect sense to store these settings not for the Note window, but for each individual note. To do this you pass the optional ID parameter both when opening and when closing the window.

Make sure the ID is not zero, because the Controller uses zero as ‘no ID’.
Remembering layout state. Sometimes you might have more than one variant for the same layout. E.g. a calendar may display a single day or a whole week.

To properly remember the state of this window, you need to remember not only the size, position, etc., but also the particular state, “day”, “week” or “overview”. To do this pass an optional state parameter when closing the window:

When you tell the Controller to read window settings, it will restore the saved state in the global System::State field, so your script can read it and behave accordingly.
Going without user accounts. The controller identifies users by their accounts. (It uses the System::User field, which is an unstored calculation that evaluates to user account.) But sometimes there's no accounts. E.g. in my DDR Viewer, which is a locked app, all users automatically log in as Guests. But this app can be used on a server, so I made it to use user name instead of account to identify users.
The window name fetches default or saved width, height, top, left, status area, view and zoom. If necessary, you can override any of these settings. For example, you might need to open a window for a picture; you can get picture dimensions from the container field, calculate the required content width and height and then specify window, width and height:

In this case the system will use default or saved settings for the window, but will override width and height. Another example: you might need to open a list with few items and resize it vertically to show only the items, without empty space. In this case you need to override only the height.
Note that in all calls you specify the width and height of the content, not the final window dimensions.
If you specify the alignment, it will override saved or default settings, but if you specify explicit top and/or left, they will take precedence.
It's nice to remember window sizes, but it also brings a specific problem: what if saved settings make a window to open completely off screen?
I used to think this must be a rare case until I run into this myself, and, of course, exactly when I was demoing a system to a customer using Apple Remote Desktop. Somehow this software makes it very easy to drag a window off screen so it could not be fetched back. I could close it using a keyboard shortcut, but it remembered the saved settings and the next time I re-opened the window, it opened off screen again, without any way to bring it back.
To prevent this from happening, Window Controller adjusts the position of the window to make sure it stays on screen. “On screen” actually means “at some distance from screen edges”; the distance is called safety margin and is defined once right in the code.
The controller keeps some settings right in the code. The settings include scale and safety margins and also some things FileMaker cannot see on its own: toolbars and Windows status bar (they only exist in v7-9 anymore). These settings are explained right in the comments of the Window Controller field of the System table. Note that they're calculated and can, for example, vary by platform.
When user opens a new window, FileMaker adds a numeric suffix to it, so if it was “My App”, it becomes “My App - 2”. Sometimes it's necessary to get the original name. The Controller has two functions that extracts the original window name or the numeric suffix.
Finally, there's a script to select a window by original name or by position, like “first window below”.

If you pass it only a name, as on the figure above, it will select the first “My Window”, “My Window - 2”, “My Window - 3”, etc., starting with the current window. If you pass only a number, e.g. “2”:

it will select the next window below (the current window is counted as 1). The name of the window doesn't matter. Finally, if you pass both name and number:

it will select the first “My Window”, “My Window - 2”, “My Window - 3”, etc. starting with the next window below.
The script selects only current file's windows.
blog comments powered by Disqus