FilterInfo

A FilterInfo object contains information on a game filter, which is essentially a selection rule that selects a subset of games to show in the wheel UI. The system provides a number of predefined filters, such as the era filters ("1970s games"), manufacturer filters ("Gottlieb games"), and system filters ("Visual Pinball X games"). You can also create your own custom filters, which we'll explain below.

The main wheel UI lets the user select which filter to use at any given time via the menu system. Whenever the selected filter changes, a filterselect event is fired. You can use this to do additional work as needed when the filter selection changes.

Creation

You don't create FilterInfo objects directly. You get them by calling gameList.getFilterInfo(). That function returns a FilterInfo object populated with details about the filter you specified.

If you want to create a custom filter, see Custom filters below.

Properties and methods

cmd: An integer command ID value. This will always be in the range from command.FilterFirst to command.FilterLast (see Commands). (The command ID is assigned for the current session only, so it shouldn't be saved externally.)

getGames(): Returns an array of GameInfo objects listing all games that the filter currently selects.

group: A string giving the menu group for the filter. System filters all have groups with names enclosed in [square brackets]. These are never displayed to the user; the system uses localized menu titles for the groups instead. User-defined filters have optional group names defined when the filter was created; these can refer to system groups, or can be given as the actual menu title to show in the main menu. This is omitted if the filter isn't part of a menu group.

groupCmd: An integer giving the command ID associated with the group. This is the command used for the menu item shown in the main menu, if any, that opens the group submenu. This is undefined if the filter isn't part of a group, or it's part of one of the top-level groups that doesn't have an associated command ([Top], [Op]).

id: The ID of the filter (see Filter IDs). This is a permanent ID for the filter, so it can be stored in external files or settings.

testGame(game): Determines whether the filter includes or excludes the given game. game is a GameInfo object specifying the game to test. Returns true if the filter includes the game in its selection list, false if the filter excludes the game. (If game doesn't refer to a valid game, the return value is null, which for conditional purposes is equivalent to false, but can be distinguished with the Javascript === operator if the distinction is important to your code for some reason.)

title: A string giving the display title of the filter as it appears in menus. Note that this might be localized, and might change across versions, so you shouldn't use it to recognize a particular filter; use the id value for that.

Filter IDs

Each filter defined by the system has a unique ID string. This ID string is designed to be permanent across sessions, so it can be stored in external files and saved settings.

Each class of predefined system filters uses an ID that follows a particular format. You can test filter IDs against these formats if you want to recognize specific filters or specific classes of filters. Here are the ID formats for the system's filters:

Custom filters

You can define your own custom filters via gameList.createFilter(desc). This function creates a new filter with a selection rule you define via a Javascript function, which can express just about any kind of selection criteria you can think of.

The return value from gameList.createFilter() is a command ID that the system assigns for use by the filter. This is used if the filter is automatically added to system menus (see Automatic menu placement below). You can also use this command ID if you want to create an item for this filter in any custom menus. The command will always be in the range from command.FilterFirst to command.FilterLast, which is shared with the built-in system filters. The newly assigned command will always be chosen to be unique, so it won't conflict with any system filters.

The desc argument to createFilter() is an object describing the new filter. It contains the following properties:

Automatic menu placement

Custom filter group menus: If you give the filter a group property, the system automatically creates a new menu item for the group in the top-level menu. Selecting this menu opens a sub-menu showing all of the filters with the same group property. The group menu is added after the standard filter group items in the standard menu (Filter by Era, Filter by Manufacturer, and so on), so this lets you create your own custom filter groups of the same style. The group property string is displayed as the menu title, so you'll probably want to use similar wording ("Filter by something") so that the menu meshes well with the existing items.

Adding the filter to an existing group: Instead of creating a custom filter group, you can add a custom filter to one of the existing groups. This would be appropriate if the filter augments one of those groups. For example, you could add your own "recently played" filter that selects a time period that's not covered in the standard set, or a "manufacturer" filter that selects games from all of the WMS brands. For the predefined system filter groups, though, you don't use the name that appears in the menu. Instead, you have to refer to a special internal name. We use the internal name instead of the name in the menu because the menu name can be localized for different languages; the internal name doesn't have to be translated, since it's never displayed to the user, so you can always count on it being the same. The system filter groups are shown below.

Adding to a top-level menu: There are two special filter groups that go in top-level menus rather than in group submenus, "[Top]" (the main menu), and "[Op]" (the operator menu). If you use one of these strings for the group property, your filter will appear in the corresponding menu. Use this capability sparingly, since the menus will quickly become unusable if you add too many top-level items. If you're creating more than one or two filters that don't belong in obvious groups, you'll probably want to create a "Custom Filters" or "Filter by Misc" group as a catch-all, to avoid too much clutter in the top level menus.

If you want to skip the automatic menu inclusion: Just leave group undefined if you don't want the system to automatically show the filter in any of the standard menus. You can control the menu placement more precisely using the menu customization mechanisms described in Menus. You can also activate the filter entirely under script control via gameList.setCurFilter().

Examples

"Played within two weeks" filter: Suppose you wanted to supplement the standard "played within" filters with some more time periods than offered in the standard menus. Here's a filter that selects games played within the last two weeks. The filter is automatically included in the "Filter by Last Played" group because it uses the special system group "[Played]".

let filterReferenceDate; gameList.createFilter({ id: "PlayedWithin.14", title: "Played Within 2 Weeks", group: "[Played]", // add to the standard Recently Played menu sortKey: "00014", // number of days as a five-digit string before: function() { // Figure the reference date: 12:00AM at the start of the // day 14 days ago. Javascript's Date constructor conveniently // accepts day-of-the-month values outside of the 1..31 range, // which it interprets by projecting forwards or backwards into // ajacent months until finding a valid date. So if the current // day is Jan 1, 2019, then new Date(2019, 0, -13) will work // backwards to December 18, 2018. And since that works in // terms of dates in the local time zone, it even properly // takes into account special situtions, such as the usually // nettlesome transitions between daylight time and standard // time. // // Note that we *could* compute this value on every select() // call rather than caching it, but it makes things go a little // faster if we do this computation once ahead of time. The // system will call this before() function every time it's // about to do a scan of the table list, so we'll always have // a fresh reference point for each new scan. // start with today's date let d = new Date(); // now construct a date 14 days before today, at 12:00 AM filterReferenceDate = new Date(d.getFullYear(), d.getMonth(), d.getDate() - 14, 0, 0, 0, 0); }, select: function(game) { return game.lastPlayed >= filterReferenceDate; } });

Short, medium, and long titles: For this example, we'll create a group of filters that select according to the length of a game's title: short (6 character or fewer), medium (7-15 characters), or long (16+ characters). We'll create these as three filters that appear on a custom submenu, which will be reachable from a new top-level menu item called "Filter by Title Length". This is a pretty contrived example, but it shows how to create a custom filter group and present it in the main menu.

gameList.createFilter({ id: "Names.Short", title: "Short Titles", group: "Filter by Title Length", select: function(game) { return game.title.length <= 6; }, sortKey: "A" }); gameList.createFilter({ id: "Names.Medium", title: "Medium-length Titles", group: "Filter by Title Length", select: function(game) { return game.title.length > 6 && game.title.length <= 15; }, sortKey: "B" }); gameList.createFilter({ id: "Names.Long", title: "Long Titles", group: "Filter by Title Length", select: function(game) { return game.title.length > 15; }, sortKey: "C" });

One thing to note here is that we gave each filter a sort key. Sort keys are optional, but in this case, it's nice to include one. Without an explicit sort key, the system would just sort the menu items alphabetically by their labels. That's usually great, but this is one of those cases where the "natural" order that a user would expect isn't alphabetical. The intuitive order here is clearly short, medium, long. The sort keys let us override the default alphabetical sorting and provide our own custom sorting instead. When you provide sort keys, the system sorts the menus alphabetically by the sort keys, rather than alphabetically by the visible label. That lets you put the menus in any order you want, without having to change the labels.

The sort keys aren't visible to the user anywhere, so we can use any arbitrary strings that are convenient for putting things into the right order. In this case, we simply use "A", "B", and "C". If you wanted to leave gaps for inserting more items in the future, you could have made those "A", "M", and "Z", or something more flexible like "1000", "2000", and "3000". But for our fixed list of three items, the simplistic "A", "B", "C" will do just fine.

Note that you can use numeric strings like "1000" and "2000" for sort keys, but remember that they're still treated as strings, meaning they're sorted "lexically" rather than numerically. This has some odd consequences when you use strings that happen to contain digits. For example, the string "10" sorts before the string "2", for the same reason that "A0" sorts before "B". Lexical sorting mean that we compare the strings one character at a time, starting at the left end of the string. So if the very first character of string A sorts before the first character of string B, string A will go before string B, even if A has more characters following that make it look like a larger number when interpreted as a number. Hence "10" goes before "2", simply because "1" sorts before "2". The easy way to deal with this is to make sure that digit strings used as sort keys are all the same number of digits, using leading zeroes if necessary - so "0002" rather than just "2", for example.

Top-rated games: For a more extensive example, see A "Top 10" Filter in the Worked Examples section. That shows how to create several variations on a Top 10 filter that selects the current top-rated games in your collection.