Making Your Windows Self-Aware (Mac Only!)

By Daniel Wood, 6 May 2011

In this article the term "self-aware window" refers to a FileMaker window that can know in real-time whether or not it is the currently selected window, and act accordingly. We introduce a technique for allowing individual windows to calculate this, and some examples of what windows could do with that knowledge.

NOTE: This technique only appears to work on a Mac - sorry Windows, back in the box!

pc

There are a number of different wee bits and pieces that are used to tie this all together, so lets get stuck in with the first...

WindowNames function

One of my favorite functions, it can do some pretty cool things. In this case, one of the unique properties of the WindowNames function is that the currently active window name always appears first in the list of window names returned by the function. To try this, you can open a few windows, and put the following function in the data viewer:

WindowNames ( Get ( FileName ) )

When you click from window to window, you'll notice that all though the function returns all window names open for that file, the one you click on is always moved to the top of the list.

Now, why is this useful? Well, for one it gives us a test we can perform. If we can get each window to independently evaluate the WindowNames function, and compare its own window name to that which appears first in the list, then we can tell whether or not that window is currently selected.

Web Viewers

It's no mystery that web viewers can do a lot more than just show web pages as may have been originally intended. Over the years they've been used for all kinds of things, from simply displaying a web page, to used in plugins, to used as a substitute for calculation fields, the list goes on.

In this situation we are going to make use of the fact that a web viewer is essentially an on-layout calculation engine. What sets the web viewer calculation engine apart from say perhaps a regular calculation field, or conditional formatting, is that the calculation in a web viewer is always evaluating. That is to say, a web viewer does not require a manual or automatic refresh of a window to update its display.

Consider conditional formatting, or an unstored calculation on a layout, these things in order to update their display require you to resize the window, run a refresh window script, enter into the field, and so on. Something must trigger the refresh. Web viewers are different. They do not require a refresh in order to update, which is great for us!

Better yet, a calculation in a web viewer will re-evaluate even when:

  • The web viewer is hidden on the layout
  • The web viewer returns no result
  • The web viewer is not in the active window
  • The web viewer doesn't reference fields at all - only functions

All of these points make the web viewer calculation engine a perfect tool for us. If we can put our WindowNames checking calculation in a web viewer on layouts, then each window can become "self-aware" because the calculation will re-evaluate when the current window changes

Putting it all together

The idea here is that we put a check in the web viewer to test whether the current window is the selected one. We do this by comparing the Get ( WindowName ) function with that of the first value returned from the WindowNames function. If they match, then that window is the currently selected window

As a point of note - you can have two windows with the same name, so pay attention if using this technique that the window names appear unique otherwise it may yield incorrect results.

An Example Use

Below we have a calculation defined in a web viewer:

selfawarewindows 1

What we are doing here is constructing a data URL. This format allows us to display text direct within the web viewer instead of referencing a web site URL.

Most of the calculation is concerning itself with setting up the data URL and formatting the resulting text, but the key line in all of this is:

If ( windowSelectionStatus ; "Selected"  ; "Not Selected" )

Here we have defined a custom function called windowSelectionStatus. If it returns true, we have the web viewer display the word "Selected", otherwise "Not Selected". No prizes for guessing what the windowSelectionStatus function does :)

selfawarewindows 2

Here is the calculation for this custom function, and it's basically what we have talked about to this point. We obtain the first window in the list of WindowNames, and compare it to the current windows name, returning true if they match, false otherwise.

Every web viewer that evaluates this calculation is acting in its own window, and so the result of the calculation and thus the subsequent value shown in the web viewer is completely independent of any other window - and it updates in real time!

If you were to place a web viewer on your layouts, and open up multiple windows, you would see the web viewer change its resulting text in real time as you click from window to window, without the assistance of a refresh.

So What's the Point?

While this is all nice and well, it's pointless if there is no real application for the technique. Perhaps you just want to show something different in the web viewer depending on whether the window is selected, in which case that's fine, the technique to this point is fine.

This article came about because we were working on an issue in a client database, where the user was getting into a record locking situation when having multiple windows open on screen at once. Often, they would end up with two windows open on the same record, either a data record, or their Home record in an interface/data solution. The user may be modifying a record in one window, only to change to another, and upon trying to edit the same record, be presented with the following dialog:

selfawarewindows 3

The message is pretty self explanatory as to what has just occurred. Perhaps we can make use of this self-aware window functionality to allow windows to tidy up after themselves when deselected.

Having a Window Commit Itself

Wouldn't it be nice if when a window was deselected, it was able to commit its record right away. That way, the record is not left uncommitted and subsequent locking errors can be avoided.

Now, in order to commit a record, we have to run a script, and in the web viewer we only have access to the calculation engine, so at this time we have to make use of a plugin that is capable of running a script from the calculation engine. There are various plugins out there which are capable of doing this so the choice is yours. In the example file supplied with this article, we include examples for Digital Fusion's Reactor plugin, and MonkeyBread Software's MBS Plugin.

Ideally we want the window to commit itself when:

  • It becomes the deselected window, and
  • The record is in an uncommitted state

Well, we already know how to test for the first case. The second case can be tested for using the Get ( RecordOpenState ) function - any result that is non-zero means we should run the commit.

The CommitWindow custom function

Below is the new custom function defined called commitWindow. As you can see, it is setup in a similar way to the earlier function to test a windows active status.

selfawarewindows 4

There are a couple of points of difference. The first is that we also check Get ( RecordOpenState).

The second is that the result of this function is a call to a plugin function capable of executing a script. The above is taken from the example file, so you can see a couple of different examples of function calls for the plugins used. In both cases the script run is called "CommitRecord".

The function is then placed inside the web viewer as a calculation. Because we are only concerned with running a script, and not with what the web viewer shows, there is no need for the function to return anything worth displaying in the web viewer, its job is to run a script and that's all it does.

Why not just use an onTimer?

Now where is the fun in that! While it is true you can set an onTimer running in all of your windows to monitor the windows active status and commit accordingly, there are issues!

Firstly, onTimers require the script to continually be running after defined intervals. In order to safely commit the record fast enough before the user tries to modify in another window, the internal has to be sufficiently low. The smaller the interval, the more frequently the script runs. Perhaps this doesn't add overhead, but the idea of a script running in every window every 5 seconds doesn't sit well with me :-) The web viewer method - while requiring a plugin - is more passive. It only executes the script when it absolutely needs to, and in doing so, the effect is instant.

Secondly, there is a peculiar bug/feature/issue with onTimer scripts that we noticed in writing this article.

Each window has its own onTimer running, that is known. When the script is eventually run, "parts" of the script evaluate from the context of the window from which it was triggered, however other parts of the script execute in whatever the current window is. We haven't fully explored this, but in trying to produce an onTimer method, we found that each window can successfully determine whether it is the selected window via an onTimer. So, each window is able to evaluate windowNames, and Get(WindowName) from their own context.

The problem however comes when the Commit Records/Requests step is run. This script step is always run from the context of whatever the current window is.

So, if you have two windows open A & B, and you modify a record in window A, then navigate to window B, the onTimer that runs in window A will determine successfully that it is uncommitted, and no longer the active window, but the commit record step is executed in the newly selected window B - confused?

To put it another way - in order for an onTimer implementation to work, the script must first navigate back to the window from which the script was executed, run the commit and then navigate back to the window that was (and now is) the currently selected window! (couple that with the fact script is still continually running...)

In short, yes you could work a solution using onTimer, in conjunction with the windowNames first value method. Both are possible, but both come with their own pros and cons, choose wisely :)

We have included an onTimer scenario in the example file so you can see this issue in action, and perhaps modify the file to work correctly, have a play and see what you come up with.

Example File

Please find attached an example file. This file was used for all the screenshots in this article, and is provided to help you fully understand what is going on in this article, and to let you experiment in FileMaker with this solution.

Download Example File

Something to say? Post a comment...

Comments

  • Cronk 28/01/2012 10:03am (10 years ago)

    Daniel: Thanks for writing this up! This is almost exactly a solution that I was looking for to a problem we are having. I did have to make some changes: I used the logic that Bruce suggested ("not windowSelectionStatus and Get ( RecordOpenState )").

    I was also able to get it working (at least on superficial testing and inspection) by using an OnTimer script. The Commit Records step DOES appear to evaluate based on the the Timer's context (and thus the context of the non-selected window).

    This is all on a Windows system, too.

    Thanks again!

  • Brent Hedden 26/05/2011 3:22am (10 years ago)

    This technique is slick, and it's too bad about Windows behaving this way. There are many people that have to deal with record locking issues.

    One observation that I noticed that may be helpful to others wanting to use this in a Windows system - The web viewer will evaluate as expected whenever the cursor in inside of a field of the focus window, and then you switch to another window. But if the focus is just on the window and not a particular field then switch is when it appears to not reevaluate correctly. So I can have several windows all with the status of 'selected'.

  • Daniel Wood 10/05/2011 11:43am (10 years ago)

    Hi Bob, thanks for the comment. I can only assume it breaks in windows due to how window refreshes & web viewers work in either platform, really don't know for sure. Basically, the web viewer loads and evaluates correctly upon loading, and when you click into the window, but once that value has been evaluated in the web viewer, it remains unchanged if you go to another window, it does not detect the change in current window and does not refresh the web viewer. Bit of a nuisance, but it seems to be the way that a lot of things you can do in FM on the mac just don't work as nicely, or at all on windows.

  • Bob Stuart 10/05/2011 11:03am (10 years ago)

    That's SO clever, Daniel! I haven't had time to play with the example file yet, but I'd love to use this technique to magnify/reduce the layout, based on window size.

    Why does it break in Windows?

  • Tim Anderson 07/05/2011 10:44am (10 years ago)

    Really useful to learn that the web viewer re-evaluates without a trigger- thank you

  • Peter Gort 07/05/2011 10:10am (10 years ago)

    To solve the commit issue, I set up two scripts, (a) one to set the timer, (b)one to execute a commit and stop the timer. Then I set up a layout trigger for keystrokes to call script (a). Setting a timer automatically cancels an existing timer, and I set it for 5 minutes. It results in a script running with every keystroke, but that script only sets the timer so it executes really really fast. Script (b) runs when the 5 minutes is up with no keystrokes on that layout, and successfully executes in hidden windows if you've navigated away from it. Nothing fancy, and works cross platform. Oh, and I created a custom function "Record is Open" so the script logic runs if [ Record is Open ] commit record. I've been known to get a little pedantic on occasion...;-)

    However, the point that web viewers can be a calc engine that operates real time is something I hadn't thought of, and I have no doubt it will come in handy! Thanks for the article.

  • Bruce Robertson 07/05/2011 3:23am (10 years ago)

    Additionally it seems the on timer script statement should really be as follows, which does allow the non-active window to commit itself without becoming selected:
    not windowSelectionStatus and Get ( RecordOpenState )

  • Bruce Robertson 07/05/2011 2:50am (10 years ago)

    Regarding operating on windows that are not frontmost - since this is a Mac based technique - you can perform a save (commit) operation in any window.
    Perform applescript "tell window 'A' to save" can be run in window B. You can do lots of things on unselected windows, including fast operations on minimized windows.

RSS feed for comments on this page | RSS feed for all comments