- Catalogue Creator Scheme Designer
- Structure Introduction
Catalogue Creator Scheme Designer
There is now a way for catalogue developers to create a scheme layout based on features in their catalog. The highly visual tool allows catalogue creators to drag & drop features, tabs, and sections to create a scheme, apply product rules, then publish it as part of the catalogue data, which will automatically be available for use by end-users through the standard Scheme dialog.
More information about the Scheme Designer tool in Catalogue Creator will be available in Release Notes and Training Materials.
Structure Introduction
When you set up a scheme layout in the catalog, the resulting file with a .pslayout(11.5)/.cmpsl(12.0) suffix will contain these Tabs, Sections, and Entries.
A layout contains 0 or more tabs.
A tab contains 0 or more sections.
A section contains 0 or more entries.
Tabs and Sections behave like containers and contains little information on themselves, like label. Entries represent the properties, and it contains the domain, selected value, default value, and filter rules.
Example of this hierarchy:
-Scheme Builder Demo (layout) -Desking (tab) -Work Top (section) -Top (entry) -Screen (section) -Upholstery (entry) -Storage (tab) -(Sections and entries hidden) -Seating (tab) -(Sections and entries hidden) -Sofa (tab) -(Sections and entries hidden) -Global Storage (tab) -(Sections and entries hidden)
Embedded Catalog Extensions
If you have schemes associated with your catalog, and you are building the embedded catalog with code, all you have to do to get the schemes button out on your extension is just use a DsDataCatalogLimb
to build your library and set allowScheme
to true in your Limb constructor. You can also specify a defaultSchemeKey
so that it shows the intended scheme when you click.
/** * Ds DataCatalog limb. */ public class DsDataCatalogLimb extends LibraryLimb : unstreamable { ... /** * Default scheme key. */ public str defaultSchemeKey; /** * Allow scheme? */ public bool allowScheme;
Data-driven PGC Extensions
If more advanced functionalities are desired, the aforementioned scheme layouts created using the Schemes Designer tool could also be extended upon and customized for use in Data-Driven PGC Extensions. The API to customize data-driven schemes revolves around PsLayout
objects provided in cm\core\propsScheme\layout\PsLayout.cm.
Examples of customizable behavior:
- Adding non-data properties to scheme layout
- Sorting the properties in a specific order
- Altering the way the props is pushed from scheme to snapper (e.g: push to all panel tiles in a panel) - described in the push behavior section below
Step 1: Extending the Scheme
Extend from PsLayoutPropsScheme
and overriding loadSchemeLayout()
like below:
use cm.core.propsScheme.layout; use cm.abstract.dataSymbol.scheme.layout; /** * Layout-driven props scheme. */ public class MyCustomLayoutPropsScheme extends PsLayoutPropsScheme { /** * Load scheme layout. */ public PsLayout loadSchemeLayout() { DsPData schemeLayoutData("testScheme", "1", "www.configura.com", ""); return dsLoadLayout(schemeLayoutData); //use this instead if you want to test out your layout before publishing Url schemeLayoutUrl = cmWritable("tryMigrateLayout.cmpsl"); return dsLoadLayout(schemeLayoutUrl, edData); } }
Step 2: Scheme Button in Library
Implement a VoidCallbackLimb
in your library that calls the following code. Please note that you should call addDefaultPropsScheme()
in the start method of your extension. It's added here for safety.
/** * Open Cat Scheme dialog. */ private void openCatScheme() { str key = "mySchemeKey"; if (!getPropsScheme(key)) { addDefaultPropsScheme(MyCustomLayoutPropsScheme()); } createPropsSchemesDialog(key); }
Step 3: Appending Custom Entries
Override getCustomEntries()
in your custom layoutScheme class. The method takes in a section key and a tab key, which is something you can set in the catalogue creator can set by doing SHIFT-right-click on the label of the entry/section/tab.
The base classes in PsLayoutPropsScheme
are empty but allow you to add custom tabs or sections:
/** * Get Custom entries. */ extend public PsLayoutAbstractPropertyEntry[] getCustomEntries(str sectionKey, str tabKey) { return null; } /** * Get custom sections. */ extend public PsLayoutSection[] getCustomSections(str tabKey) { return null; }
With the custom keys set in the creator, you can access any element in the layout by using these keys as path. Here are some examples of custom entries. You can easily create one by passing in key, label, domain, default, and a set property callback (optional).
/** * Get Custom entries. */ public PsLayoutAbstractPropertyEntry[] getCustomEntries(str sectionKey, str tabKey) { PsLayoutAbstractPropertyEntry[] res(); if (sectionKey == cUpperTileTypeSectionKey) { res << PsLayoutPropertyEntry(cUpperTileTypePropKey, $tileType, allUpTileTypeDomain, SgAcousticFabricTileType, function updateUpperTileTypeVisibility); } else if (sectionKey == cMiddleTileTypeSectionKey) { res << PsLayoutPropertyEntry(cMiddleTileTypePropKey, $tileType, allMiddleTileTypeDomain, SgAcousticFabricTileType, function updateMiddleTileTypeVisibility); } else if (sectionKey == cLowerTileTypeSectionKey) { res << PsLayoutPropertyEntry(cLowerTileTypePropKey, $tileType, allDownTileTypeDomain, SgAcousticFabricTileType, function updateLowerTileTypeVisibility); } else if (sectionKey == cPanel30TypeSectionKey) { res << PsLayoutPropertyEntry("height", $height, DoubleEnum(1.1, 1.15, 1.2), 1.2m); } else if (sectionKey == cPanel60TypeSectionKey) { res << PsLayoutPropertyEntry("height", $height, DoubleEnum(1.1, 1.15, 1.2, 1.65), 1.65m); } return res; }
Bonus step: Property Set Callback
The set property callbacks can be used to control what happens when the user sets a value on the scheme. In this case, I used it to show and hide different properties based on what was selected.
/** * UpdateUpperTileTypeVisibility. */ private void updateUpperTileTypeVisibility(PropsSchemeUIFactory root, PsLayout layout, Object obj) { if (obj as Class) { if (?PanelTileType panelType = obj.instantiate()) { for (DsPsLayoutPropertyEntry entry in layout.getEntriesFromSection("tiles")) { if (PropsSchemeUIFactory entryFact = root.getFactory(entry.path)) { entryFact."visible" = panelType.isUpperTileType(); } } } } }
Step 4: Reordering Properties
entryOrder()
controls how the property entries are ordered within a section,sectionOrder()
controls how the sections are ordered within a tab.propOrder()
controls what order you want to push your properties. Note that prop order is not controlled within a section, so you can specify order for properties from different sections.
/** * Entry order. */ public str[] entryOrder(str sectionKey, str tabKey) { return [getPath(cUpperTileTypePropKey, ..), getPath(cMiddleTileTypePropKey, ..), getPath(cLowerTileTypePropKey, ..)]; //Updated with getPath helper method in 11.5 }
The sorting is done based on partial order. For example, if I only specified order for specific entries, those will get higher priority than other entries that are not in the order sequence. Other entries will follow their original order in the layout.
Miscellaneous Customizations
If you wish to change the way the properties look in your scheme, you can also do that by having your own custom entries, and override schemeFactory()
to return your own UIFactory
from there.
public class MyPsLayoutPropertyEntry extends PsLayoutPropertyEntry { /** * UI Factory. * * This is the actual UI factory that is used in the PropsSchemeDialog. * Controls in this factory will write information to the scheme data directly. */ public PropsSchemeUIFactory schemeFactory() { return MyPsLayoutPropertyEntryUIFactory(key=path, editMode=false, args=props{entry=this}); } } /** * Layout property entry UI factory. */ public class MyPsLayoutPropertyEntryUIFactory extends PsLayoutPropertyEntryUIFactory { /** * Append default domain control. */ public void appendDefaultDomainControl(Window parent, Window[] controls, Object value, SubSet domain) { if (domain.empty) return; StateControlGroup controlGroup(callback=function selectControlGroupCB); for (member in domain.members) { BrushHoverSelectButton button(parent, key=key, label=member.toS); button.value = member; button.autoSize(); controlGroup << button; if (value as Double) if (member as Double) if (value.v == member.v) controlGroup.select(button); controls << button; } } }
Push Behaviours
By default, catalogue schemes data are pushed via group code, to all snappers that:
1. Match the rules specified in the Schemes Designer
2. Accept the Feature Code and Group Code specified.
Group codes are required to work with Catalogue Schemes. A group code is a code on a feature that helps linking features to products. While different features may be required to have different names, if they share a group code it can help developers and designers update similar materials at once.
The schemes data is pushed to the owner of the DsPData
by default (Data Owner, via DsPDataPushToDataOwnerBehavior
). If you wish to push directly to your data instead, there is a DsPDataPushToDataBehavior
class for use as well.
The push behavior is highly customizable, through overriding the PsLayoutPropertyProxyPushBehavior
classes as well as PsLayoutPropertyEntry
's proxyPushBehavior()
.
You will be able to override every parameter in pushProp()
, which gives you control over push value, type check, push prop bail check, etc.
On top of that you can also override the receiver, for example, if a certain property should push to the panel frame instead of your panel, just override proxyPushBehavior()
to return the frame instead.
package cm.core.propsScheme.layout; /** * Proxy push behavior. * * Controls how the proxyData that represents a layout property entry is pushed * onto the proxyPushReceiver. */ public class PsLayoutPropertyProxyPushBehavior : abstract { extend public str proxyPushKey() { return entry.key; } extend public PropObj proxyPushReceiver() { return receiver; } extend public Object proxyPushEnv() { return null; } extend public PushPropsEnv proxyPusherEnv() { return null; } extend public PsLayoutPropertyEntryProxyData proxyPushData() { .. } extend public bool proxyAllowPushProp(PropObj z) { .. } extend public void beforePushProp(PropObj translatedReceiver) { } extend public void beforePushProps() { } extend public void afterPushProp(PropObj translatedReceiver) { } extend public void afterPushProps(PropObj receiver) { .. } }
In order to customize this, just return the overridden push behavior from your receiving snapper as a prop as below. For example, if you want to customize how the data gets pushed to your tile, override like below in your PanelFrame
class.
/** * Props. */ public props { PsLayoutPropertyProxyPushBehavior cDsPDataPushBehaviorKey { Object get(..) { return MyDataPushBehavior(); } } }
Or, for your custom entries, you can do the following:
/** * My own Property entry. */ public class MyOwnLayoutPropertyEntry extends PsLayoutAbstractPropertyEntry { /** * Push behavior. */ public PsLayoutPropertyProxyPushBehavior proxyPushBehavior(PropObj receiver) { return MyPropertyPushBehavior(); } }
From 11.5, We now support multi behaviors, which means that a single apply can push to multiple receivers.
use cm.core.propsScheme; /** * Proxy push multi behavior. */ public class PropsSchemeMultiProxyPushBehavior extends PropsSchemeProxyPushBehavior { ... }
We have also added a helper method that appends your mData
s as a Multi behavior.
/** * Generate Multi Behavior from PropObj seq. */ public PropsSchemeMultiProxyPushBehavior schemeMultiDataBehaviors(PropObj[] objs) { PropsSchemeMultiProxyPushBehavior mBehaviors(); for (obj in objs) { mBehaviors.append(schemeMDataBehaviors(obj)); } return mBehaviors; } /** * Generate push behaviors for a MData. */ public PropsSchemeProxyPushBehavior[] schemeMDataBehaviors(PropObj obj) { PropsSchemeProxyPushBehavior[] allBehaviors(); ?DsMultiPData mData = obj; if (!mData) mData = dsMPData(obj); for (key in mData.get(cDsMPDataOrderProp).:str[]) { allBehaviors << DsPDataPushToMDataBehavior(mData, key); } return allBehaviors; }
To make the scheme apply to push to all your tiles with mData
:
Step 1: Override the Behavior on Panel Class
use cm.core.propsScheme; /** * Props. */ public props { PropsSchemeProxyPushBehavior cDsPDataPushBehaviorKey { Object get(..) { return that.schemePushBehaviors(); } } }
Step 2: Implement Behavior Method to Collect All Tiles
/** * Scheme push behaviors. */ extend public PropsSchemeProxyPushBehavior schemePushBehaviors() { PropObj[] mDataOwners = [PropObj: this]; for (FODataTile tile in upSkin.items) mDataOwners << tile; for (FODataTile tile in downSkin.items) mDataOwners << tile; return schemeMultiDataBehaviors(mDataOwners); }
And you are done!
Comments
0 comments
Please sign in to leave a comment.