Attention is a funny thing. Some users and developers never notice or quickly learn to ignore quirks in a FileMaker layout’s UI/UX. Others (I’m one) may find those quirks consuming a disproportionate amount of attention, even after years spent using that layout. This article will focus on one UX anomaly with number fields that bothered me enough to create a method of changing the default behavior to meet my expectation.
Numbers and Data Presentation in Development
Consider how much of our development work focuses on numbers: quantities, costs, prices, and (often) the summarization of those values into various totals. When any of those values are fractional, we need to determine how best to present that information to the user. We often decide that we’ll right-align our number field and—when rounding is preferred over truncation—enable the Data Formatting option “fixed number of decimals” for the field. There are many use cases—especially in columnar data arrays—for numbers being neatly aligned on their decimal point in a visually appealing and easily consumable format.
We might also use Data Formatting to apply currency formatting options, change the way negative values are displayed, and apply (or even change) the “decimal” and “thousands separator” values. All of these lend consistency and readability to the presentation of raw numeric data. But what happens when a user clicks into one of these fields intending to edit a single digit? When the raw value differs from the “display” version by having more or fewer decimal digits, the cursor lands at the spot where the user clicked within the underlying “raw” field value. In some circumstances, the insertion point will be in a disconcertingly different position within the raw value than it would have been if the raw and “display” values matched one another.
Cursor Placement Discrepancies: An Annoying Quirk
Here’s a simple example: A number field that applies Data Formatting with a “fixed number of decimals” value of 2 contains the raw value “1234” displayed as “1234.00”. When the user clicks into that field with the cursor arrow pointing between the “4” and the “.” the insertion point in the field lies between the “1” and the “2”. Clicking the same spot when the raw number is “1234.56789” puts the insertion point between the “7” and the “8”. Similar issues arise when Data Formatting is set to use—but the raw number does not have—a thousands separator.
Navigating Cursor Placement: The Challenge and Solution
So what can we do, given discrepant display and raw field values, to identify the intended insertion point and put the cursor in that location? Attaching an OnObjectEnter script to the field is an obvious requirement. We’ll need to give the field an object name. And we’ll need to identify the point at which the user clicked into the field relative to the value displayed. While there may be more than one way to meet the latter requirement, the method I chose (which is modeled in the accompanying example file) uses a series of text objects with a single space character, each assigned a tooltip to instantiate a variable. That variable ( $$cFR, for “Cursor From Right” ) is then picked up and used in the arithmetic that identifies the insertion point in the raw number.
Methodology: Using Tooltip-Driven Calculations
The tooltip calculation is simple: Let ( $$cFR = n ; “” ) where “n” in the right-most text object is zero and each sequential object to its left is one more than the “n” value to its right. Why this method? There’s no visual clutter for the user (because the text object is invisible) and the tooltip calculation’s result is null (so hovering does not open the tooltip.) Although the display of a tooltip’s value takes a second or two when hovering over it, the tooltip calculation actually evaluates immediately.
The screenshot below…
… was taken from the demo file [available if for download at the bottom of this page] and shows how the text objects are aligned: centered on the space between characters and covering half of the digit to its left and right. Note that there’s a text object on each side of the decimal’s position, so we can determine whether the cursor should land ahead of or behind it in the raw number when it’s not an integer. In our simple example, our number field’s options include Validation >Require>Strict data type>Numeric Only, and we’re presuming that the user is not manually entering thousands separator characters, which that validation ignores even when they’re inappropriate.
Unveiling the OnObjectEnter Script: Behind the Scenes
The demo file shows three versions of our number field: As Entered, Data Formatting Applied, and Formatting Applied+Cursor Correction. When the user clicks into the latter field, the tooltip calculation of the text object below the cursor has already instantiated $$cFR with the text object’s positional value. The OnObjectEnter script attached to the number field will eventually use the value of $$cFR in computations that identify the correct insertion point. So what happens next?
Script-Driven Data Capture and Decoding
The OnObjectEnter script captures the raw number value using Get ( ActiveFieldContents ) and instantiates it as $rawNumber. It captures the displayed number value using GetLayoutObjectAttribute ( Get ( ActiveLayoutObjectName ) ; “content” ) and instantiates it as $displayNumber. It captures the displayed number’s currency symbol—if any—using Filter ( $displayNumber ; “$£€¥₿” ) and instantiates it as $displayNumberCurrency. We need to identify the “fixed number of decimals” value assigned to the displayed number, and to do that, we need to determine what character is used to represent the decimal point in the displayed number.
Calculating the Correct Insertion Point
The $displayNumber’s value is a number type, so we need to get creative to account for the possibility that it’s displaying an integer and therefore has no decimal when treated as a number. We’ll cast as text using GetAsText ( $displayNumber ) and we’ll use Substitute to delete the $displayNumberCurrency. That step is important in the case that Data Formatting puts it to the right of the number value.*
We’ll use a While function to start with the right-most character of the result and work our way left to find the first character that’s not a digit. When we find it, we instantiate that character as the value of $displayNumberDecimal and instantiate the $displayNumberPrecision we identified.
Addressing Challenges and Looking Ahead
We now have all the values we need to do the math to identify our insertion point. We need to account for differences in the raw and display numbers’ precision values, the possibility that the raw number is an integer and thus has no decimal point, and whether the user clicked in a position that may be several characters left of the first digit. Note that an insertion point of 1 puts the cursor left of the left-most character in a field, so we need to consider that in our equation when the insertion point is anything other than in front our first digit.
This technique puts our cursor in the expected location within the raw number, which meets our original objective. But there’s still dissonance when clicking into a field that displays thousands separators and/or a currency symbol: those “waypoints” are missing in the raw number. And the technique breaks down if the user enters one of the non-numeric characters that pass the validation mentioned above ( Require>Strict data type>Numeric Only ) but detract from the purity of the number.
Exploring Future Solutions and Enhancements
So what can be done about that? In a follow-up article, we’ll discuss a technique that resolves that issue while preserving the “waypoints” and updates the formatted version of the number with every relevant keystroke.
Stay tuned….
* If your formatting puts the currency symbol on the right, you’ll need to move the text objects and widen the right most so it covers the middle of the last digit and all of the currency symbol.
Built with you in mind
Speak to one of our expert consultants about making sense of your data today. During
this free consultation, we'll address your questions, learn more about your business, and
make some immediate recommendations.