Over 46,000+ Business Solution Developers Find answers, ask questions, and connect with our community of business solutions developers, business owners and partners.
Window Controller opens properly sized and aligned windows on both platforms and remembers window settings for every user. Works with FileMaker v8-10. The main functionality could be reworked to work in v7, but there’s no custom menus that make it convenient to save settings.
Starting with version 7 FileMaker can use multiple windows per file. This functionality came with a full set of of commands and functions to manipulate windows, but as it turned out opening a properly sized and aligned window is a rather tough task.
What I mean by “properly sized” is that a new window should open exactly to the required size and at the exact place. It should not open to some other size and then get adjusted, nor should it open off screen and then get in the view; it should just open and display content.
Although it sounds simple, there’s quite a few obstacles that come in the way. The FileMaker command to open a window requires you to specify final dimensions that include window title, borders, scroll bars, but the size of these elements varies between platforms, so windows that are set to display identical content need different dimensions.
- To display the same content (shown as a gray rectangle) on Windows and Mac you need different window sizes (red and blue respectively). And this is just the tip of the iceberg…
FileMaker has its own controls and they may be on or off and you need to account for this as well. Sometimes the size of a control may vary: on Mac the user can change the height of the status toolbar, while Windows lets users to change the size of system controls. And this is just about the size; if you want to position a window, you need to calculate even more. And even if you calculated all this, it’s just a part of bringing good user experience, because with multiple windows the good behavior is to let the user size and position them (and maybe zoom them) and then just remember these settings.
What’s especially inconvenient about this is that all this normally comes to you when you write something else and simply need to open a window. At this moment you’re certainly not in the mood to go into complex calculations. This functionality is a very good thing to wrap into something you can call with an option or two.
Window Controller is exactly such a wrapper. I started it in days of v7, it went through several major changes and this is like a fourth version of it, which is both the simplest and most flexible of them all. There’s still a room for improvement, of course:
The size of the status toolbar under Mac OS X may vary depending on mode, but there’s no good support for this yet.
I could imagine a better handing of custom titles.
Using Window Controller
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.
- Window Controller keeps window sizes, alignments and saved settings in tables.
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.
Define sizes and alignments
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 interface to specify window size.
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:
- A typical preview (left) shows the report at 100% in an window that doesn’t match the paper, so the report doesn’t fit but some empty space does. A better-looking preview (right) resizes the window and adjusts the zoom level to exactly match the paper size.
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:
- The interface to specify alignment.
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:
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:
- In most cases you only need a name of a window to open and align it.
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.
- Contents of the Open script.
To open a new window when going to related record you directly use these steps, but replace Open Window with Go to Related Record:
- To open a window when going to a related record you use the same steps as the Open script.
First, you use the Read settings script to read settings for the window:
- First you tell the controller to read the settings…
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:
- …and then pick the dimensions from global fields in the System table.
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:
- To open a window in preview mode you need to apply zoom settings in Preview mode.
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”:
- The typical pattern of ‘reusing’ a window.
Toggling status toolbar (area) or on off
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.
- A custom script to toggle status toolbar (area) preserves the content size and makes the behavior consistent on all platforms and versions.
Using custom titles
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.
- Using a custom title for a window.
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.
- A custom Close command runs a script that saves window settings.
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.
More advanced stuff
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.
- Use the ID parameter to remember window settings for individual records.
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.
- This calendar has three specific “states”: Day, Week and Overview.
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:
- To save a custom state pass it as a parameter and then get from System::State when reading settings.
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.
Overriding default or saved settings
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:
- Overriding size at runtime.
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.
Keeping windows on screen
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.
What else is inside
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”.
- The script to select window by original name or position.
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.