A Tab Control
|
Developer Tools | www.onegasoft.com |
This document explains how to build an effective tab system in FileMaker Pro: a control that shows and hides tabs and activates and disactivates one of them depending on things like current user's access rights or data state or anything that can be calculated in FileMaker. This system has something in common with "Smart Tabs" tip that was published long time ago, but this version is far more simpler and universal.
It's a long article, so if you'd like to have it printed get
the PDF version with FileMaker samples ( 139k).
There are only a few things we need to build a good tab system and the main are the tabs itself. We need two pictures: an active tab and an inactive one. The easiest way is to draw them with FileMaker tools, but you also can use any other image handling program, such as Photoshop. The sample files already have samples of both.
If you want to make your own tabs that will fit into the style of your solution note that the tabs should be of same size and assembled to fit seamlessly when placed onto a layout. It may require some skill and experience to draw them, but it is not complex as soon as you get it. For example, typical tabs look like they are part of stacked panels and one lies above the others. In fact there will be only one panel with all the tabs above it, so the active tab should have some extra elements to cover the panel and "melt" into it and the inactive one should either be shorter or repeat the panel's lines on itself to appear as if it were below.
Active and inactive tabs from the sample file; note the extra "panel" lines on the inactive one.
Step 1. Create pictures of an active and inactive tabs and paste them into global container fields gTabPictActive and gTabPictInactive.
Our goal is to display the tabs we need and activate a particular one. It means we need to assign a unique identifier to each tab, keep a list of those that are displayed at this moment and store the identifier of the active one. We are going to use tab names as their identifiers; it will be enough for a simple solution and can easily be changed later.
Step 2. Make a global text field gTabIDs and type names of our tabs, for example: "Info", "Orders" and "Notes". Make sure you have entered a carriage return after the last line.
To keep it simple we will use the names both as identifiers and as tab titles. In a complex solution, such as a multilingual one, these purposes might better be separated; this is a very simple modification.
Step 3. Make a global text field gTabActiveID.
Now let's build a field to display so many tabs as there are lines in gTabIDs and then modify it to show the active tab. FileMaker has a feature that is very handy for this purpose: a repeating field.
In fact, we need two repeating fields: one with the calculation, same for all repetitions, and the other with different values (in our case — sequential numbers), that will be used as parameters for the calculation. Same calculation with different parameters will evaluate to different results and that's exactly what we need.
Using a repeating field with different values as a parameter for another repeating field is essential for many repeating field tricks.
Step 4. Make a global field gTabNumbers with 3 repetitions and fill it with numbers from 1 to 3.
The number of lines in gTabIDs is easy to calculate:
PatternCount(gTabIDs}, "¶").
We should display a tab (let's take the inactive tab for this moment) in every repetition whose number is less or equal to number of tabs:
Case( PatternCount(gTabIDs}, "¶") >= gTabNumbers, gTabPictInactive )
Now we need to display the active tab. To simplify the formulas we're going to add a new field to calculate its position in the gTabIDs.
Step 5. Make a calculated field gTabActiveNo and enter the following formula into it:
PatternCount(
Left(
"¶" & gTabIDs,
Position("¶" & gTabIDs, "¶" & gTabActiveID & "¶", 1, 1)
),
"¶"
)
Now let's add a simple condition to our main formula and we're done:
Case(
PatternCount(gTabIDs, "¶") >= gTabNumbers,
If(
gTabNumbers = gTabActiveNo,
gTabPictActive,
gTabPictInactive
)
)
Step 6. Make a calculated field gTabPics and set it to container with 3 repetitions. Enter the above formula into it, carefully wrapping each non-repeating field into Extend() function, so it will look so:
Case(
PatternCount(Extend(gTabIDs), "¶") >= gTabNumbers,
If(
gTabNumbers = Extend(gTabActiveNo),
Extend(gTabPictActive),
Extend(gTabPictInactive)
)
)
Now look at what we have now: it works! It displays the correct number of tabs and rises the active one!
We also need to display the tab titles. It would be nice if the active tab title's style differed from others, so we're going to make two fields:
Step 7. Make a calculated field gTabTitlesActive, text, 3 repetitions, with the following formula (that is a simple way to extract a string from a text):
Case(
gTabNumbers = Extend(gTabActiveNo),
Middle(
Extend(gTabIDs),
Position(Extend(gTabIDs), "¶", 1, gTabNumbers - 1) + 1,
Position(Extend(gTabIDs), "¶", 1, gTabNumbers) -
Position(Extend(gTabIDs), "¶", 1, gTabNumbers - 1) - 1
)
)
Step 8. Duplicate the gTabTitlesActive, rename it to gTabTitlesInactive and change the condition = to <>:
Case(
gTabNumbers <>Extend(gTabActiveNo),
Middle(
Extend(gTabIDs),
Position(Extend(gTabIDs), "¶", 1, gTabNumbers - 1) + 1,
Position(Extend(gTabIDs), "¶", 1, gTabNumbers) -
Position(Extend(gTabIDs), "¶", 1, gTabNumbers - 1) - 1
)
)
These two calculations work with gTabIDs, because in our sample the tab IDs are also their titles. If your solution requires them to be separated, you should use your "titles" field instead.
Now place both these fields onto a layout and format them as you see fit. The tab system is nearly complete.
Of course, a good tab control should also change the tabs, that is, in FileMaker terms, the layouts. The exact way of doing this depends too much on the structure of your solution, so I am going just to briefly outline the beginning of this.
First you should use FileMaker tools to create invisible buttons and place them over the tabs. If you have assembled the tabs using the FileMaker tools, you may use one of the tabs; you'd need just to change their fill and outline to "none" to make it invisible. In fact, at this moment it's better to keep it visible, because you need to place them right above the tabs and tie scripts to them.
Since our control is dynamic and tabs can appear and disappear, we cannot assign direct "Go To Layout" commands to these buttons. Instead we'll make a set of generic scripts "Tab 1… Tab N" and one main navigation script (which is always a good thing to have). "Tab 1… Tab N" scripts will provide only a number of clicked tab; the main script will use the number to get the tab ID.
Step 1. Make such a global number field sGoToTabNo.
Step 2. Create a new script "Go To Tab" and enter the following script steps:
If ["sGoToTabNo > PatternCount(gTabIDs, "¶")"] Beep End If
Since we cannot hide the buttons we're making, user can click them at any situation, even if there's no tab at this place. So the first thing the script does is checking if the tab number is valid: if it's greater than the number of currently displayed tabs, the script beeps and finishes. We'll add an Else section to it later.
Step 3. Create a new script "Tab 1" and enter the following two steps:
Set Field ["sGoToTabNo",
"GetRepetition(
gTabNumbers,
TextToNum(Status(CurrentScriptName))
)"]
Perform Script [Sub-scripts, "Go To Tab"]
Note that the script uses its own name to get the number of the tab. It's a handy technique to use in scripts that are assigned to something from 1 to N — you have to just duplicate the first script and change its name to make it producing another value when run.
One might notice that it is not necessary to use 1 to get the 1st repetition of gTabNumbers, which also contains 1 — why not to just
Set Field ["sGoToTabNo", TextToNum(Status(CurrentScriptName)) ?
That's generally correct, but in some cases the numbers in gTabNumbers may be placed differently. Though this field serves mainly as a parameter for other repeating fields, the order of its numbers also controls the order the tabs appear. For instance, if you reverse it, the tabs will appear from right to left; it might be useful in some situations. The variant with GetRepetition() is chosen with this in mind: it will work if the order is natural or not just the same.
Step 4. Make two more copies of the script and change their names to "Tab 2" and "Tab 3".
Step 5. Assign these scripts to our buttons and make the buttons invisible.
Now you can click the buttons (the tabs) and they will call the main navigation script "Go To Tab" providing the number of the clicked tab. The main script could use this number to get the ID of this tab; this is a straightforward calculation that extracts a string from a field:
Middle(
gTabIDs,
Position(gTabIDs, "¶", 1, sGoToTabNo - 1) + 1,
Position(gTabIDs, "¶", 1, sGoToTabNo) -
Position(gTabIDs, "¶", 1, sGoToTabNo - 1) - 1
)
Step 6. Make a global text field sGoToTabID and modify the "Go To Tab" script:
If ["sGoToTabNo > PatternCount(gTabIDs}, "¶")"] Beep Else Set Field ["sGoToTabID, the above formula] End If
Now you have the ID of the tab to go and, I think, its enough; what to do then depends on your solution structure. The easiest way would be to use layout names as their IDs and go to a layout by name; of course, it might not be appropriate for a complex solution. Anyway, going to layout by name is useful and requires only two fields (global text sGoToLayoutName and global number sGoToLayutNo) and a two-step script:
Set Field ["sGoToLayoutNo",
"PatternCount(
Left(
"¶" & LayoutNames(Status(CurrentFileName)),
Position(
"¶" & LayoutNames(Status(CurrentFileName)) & "¶",
"¶" & sGoToLayoutName & "¶",
1,
1
)
),
"¶"
)"
Go to Layout ["sGoToLayoutNo"]
If the layouts are changed the active tab should also be reset; if the field isn't a calculation, add the following step to the "Go To Tab" script:
Set Field ["gActiveTabID", "sGoToTabID"]
At this moment you have a working tab control, dynamic and driven by just two fields: gTabIDs and gTabActive, that looks nice, but like a lab exercise. Now it's time to unleash its power.
In most cases you would like to turn the gTabIDs into a calculated field that "hides" and "shows" tabs automatically depending on some criteria, like that:
"Info¶" & Case(PatternCount(Status(CurrentGroups), "Sales"), "Orders¶") & Case(not IsEmpty(PrefShowNotes), "Notes¶")...
In this sample the "Info" tab is always visible, the "Orders" is shown only to users that belong to the "Sales" group and the visibility of the "Notes" tab is determined by preferences. The criteria can be as complex as FileMaker allows; in one of my own project I've created a solution-wide set of tabs that were driven from a single place.
The gTabActive can also be a calculated field that checks the Status(CurrentLayoutName), so it will automatically activate the proper tab as soon as the layout is changed.
If both these fields are calculations, the control becomes fully automatic; you don't need to bother switching tabs: just use their IDs to go to layouts and let the control change itself.
A particular case of such a fully automatic tab control are Smart Tabs. They work with what I has called a "layout group": a set of layouts whose names follow the pattern:
group name break symbol layout name
Group name works as an identifier and is never shown; break symbol just signals that this layout belongs to a group; layout name is what appear on the tab. The control is completely automatic: just change the layout names and order and it will update itself. There's a version 1 of this control, but its method of displaying tabs is far worse: it uses a separate picture for each combination of tabs, the tabs are difficult to draw and so on.
The new version is free from these inconveniences. Technically it's very simple: both gTabIDs and gTabActiveID are calculations, the former extracts the names of a "layout group" and removes the group names, the latter — cuts out the layout name. The first calculation is too complex to be printed here and then manually typed into FileMaker, so if you are interested, see "SmartTabs.fp5" file.
The appearance of the control depends on two pictures and three fields, so there isn't much to modify if you want a new look. The pictures can be changed in seconds; the size of the control and style of active and inactive captions — in minutes: modify the control on a single layout, check the buttons and then copy it and paste onto another layouts.
The whole control can be rotated to make organizer-like tabs. Sometimes rotated container fields may display badly; if you face this problem, do not rotate gTabPics. Instead, make rotated pictures and change gTabPics repetitions' orientation from horizontal to vertical. Do you remember that the order the tabs appear can be changed?
The control event doesn't have to be "tabs": you can turn it into anything that fits a rectangular grid: a row of buttons, a list, a stack, a menu.
A few styles for the tab control.
To add more tabs just increase the number of repetitions in gTabPics, gTabTitlesActive, gTabTitlesInactive and gTabNumbers, enter more values in the latter and add more scripts and buttons.
It's a long article, so if you'd like to have it printed get
the PDF version with FileMaker samples ( 139k).
Copyright © 2000-2004
Mikhail Edoshin
If you have problems viewing this site, please drop a note to the
webmaster