These Developer Tools and QA Tools extensions have been updated to match the new look in CET:
cm.core.debug has been updated to create facelift-compatible LibraryLimbs.Added the ability for users to assign a material quantity to a product in their catalogues, within a specified material quantity code that are specified in features or options.
A helper function has been added to DsiPData to check for missing external ref files:
/** * Any missing external ref files? */ Added: extend public bool anyMissingExternalRefFiles(Object owner=null) {}
Added the ability for users to assign a material quantity to a product in their catalogues, within a specified material quantity code that are specified in features or options.
We now have the ability to generate graphics whenever a property is selected in the preconfigurator.
Dimensions and blue highlighting in preview
We have added methods and 2 new classes to generate property visuals. These can be overridden in MhStorageConfiguration to introduce different types of visuals.
public class MhStorageConfiguration extends MhSystemConfiguration { extend public void updatePreviewPropertyVisuals(str key, MhStorageConfigurationItem item) { extend public void clearPreviewPropertyVisuals(str key, MhStorageConfigurationItem item) { extend public void clearPropertyHighlights() { extend public void clearPropertyDimensions() { extend public void spawnPreviewPropertyVisuals(str key, MhStorageConfigurationItem item) { extend public void spawnPropertyHighlights(str key, MhStorageConfigurationItem item) { extend public void spawnPropertyHighlight(MhConfigurationHighlightInfo info) { extend public void spawnPropertyDimensions(str key, MhStorageConfigurationItem item) { extend public void spawnPropertyDimension(MhConfigurationDimensionInfo info) { } public class MhConfigurationDimensionInfo { } public class MhConfigurationHighlightInfo { }
If you wish to generate dimensions or highlighting using the implementations above, you can extend the following methods in your individual configuration item classes to return MhConfigurationHighlightInfo or MhConfigurationDimensionInfo objects with the relevant values.
public class MhStorageConfigurationItem extends MhSystemConfigurationItem { extend public MhConfigurationHighlightInfo[] getPropertyHighlightInfos(str key) { extend public MhConfigurationDimensionInfo[] getPropertyDimensionInfos(str key) { }
Aside from visuals, you can also use the new method MhStorageConfiguration.currentPropertySelectedChanged(str key, MhStorageConfigurationItem item) to react to the user selecting a property.
public class MhStorageConfiguration extends MhSystemConfiguration { extend public void currentPropertySelectedChanged(str key, MhStorageConfigurationItem item) {
To accomodate for the new multi structure concept, we added few new behaviors specific for multi parents. If you wish your snapper to act as a multi parent, you should append these behaviors to their spawners.
Two new engine behaviors are added to handle sub children arrangement and update shape.
public class MhMultiBayEngineBehavior extends MhBayEngineBehavior { } public class MhMultiFrameEngineBehavior2 extends MhFrameEngineBehavior { }
We also separate out the stretch and animation engine behavior for multi from the normal one.
public class MhMultiBayStretchEngineBehavior extends MhBayStretchEngineBehavior { } public class MhMultiSplitRowAnimationEngineBehavior extends MhRowAnimationEngineBehavior { }
They are mostly responsible to populate sub children, arrange sub children, and update classification. In short, handling children events as a multi parent.
This is a new engine function to handle sub bay construction in a multi bay. It takes bayDepth as an argument if dev would like to override the defaultEntry depth.
public class MhMultiBayConstructionalFunction extends MhConstructionalEntryArrangementFunction { /** * Single sub bay depth. */ public Double bayDepth; }
This is a new engine function to handle sub frame construction in a multi frame. It takes frameDepth as an argument if dev would like to override the defaultEntry depth.
public class MhMultiFrameConstructionalFunction extends MhConstructionalEntryArrangementFunction { /** * Single sub frame depth. */ public Double frameDepth; }
This is a new engine function that handles sub children arrangement inside of a multi parent. It takes collideLayer as an argument to determine which layer the collision is occurring on.
public class MhMultiEntryArrangeFunction extends MhPopulateEntryArrangementFunction { /** * Collide layer. */ public Layer collideLayer; }
To accomodate for the engine function above, we added a new entry arrangement MhMultiPopulateEngineEntryArrangement. It has additional step of processing the imported entries, resetting the entries' pos and prim.
public class MhMultiPopulateEngineEntryArrangement extends MhPopulateEngineEntryArrangement { }
This is a new engine function to add #mhMulti classification to a parent that have the same classification as its children.
public class MhMultiEntryUpdateClassificationFunction extends MhSystemEngineFunction { }
This is a new engine function that update multi parent shape dimension from its children dimension.
public class MhMultiEntryUpdateShapeFunction extends MhSystemEngineFunction { }
We added a new method to check whether it's needed to generate diagonal bracing.
New: extend public bool generateDiagonalBracing() {
We added new class for mono post frame.
New: public class MhMonoPostFrameBracingBehavior extends MhBracingBehavior { New: public class MhMonoPostFrameGfxBehavior extends MhFrameGfxBehavior {
This engine function will update frames' classification when flue gap is inserted/deleted.
New: public class MhRowUpdateFrameLayoutFunction extends MhSystemEngineFunction {
This is a new layout that is a back-to-back row with no flue gap in the between.
New: public class MhRowSingleBackToBackLayout extends MhRowSingleEndLayout {
We added new shape for mono post frame.
New: public class MhMonoPostFrameShape extends MhFrameShape {
This animation is responsible for adding and removing flue gap in between rows. It helps you reconnect the rows in the system when event is applied. It also invalidate behaviors with the event #flueGapAnim.
New: public class MhFlueGapAnimation extends MhSnapperToolAnimationG2 {
We added a new method to check wehther this layout is a back-to-back layout.
New: extend public bool isBackToBackLayout() {
We added a new method to replace a previously hard-coded value of number of rows in double layout.
New: extend public int doubleRowCount() {
We added a new method to replace a previously hard-coded value of number of rows in a block.
New: extend public int blockRowCount(int rowCount) {
This behavior is to rebuild shuttle's rail geometry after afterInitialExport event.
New: public class MhShuttleRailUpdateShapeBehavior extends MhBehavior {
Added new interfaces for behaviors to override.
public class MhBehavior extends CoreObject { /** * Append prop defs to snapper. */ extend public void appendSnapperPropDefs(MhSnapper snapper, PropDefs defs) { } /** * Before snapper was user cut. */ extend public void beforeUserCut(MhSnapper snapper, SpaceSelection selection) { } /** * User cut. */ extend public void userCut(MhSnapper snapper, SpaceSelection selection) { } }
MhDeepstorageNoOfSingleFramesFunction now has a new argument blockSpread which can be passed in from engine behaviors. When blockSpread=true, it will block spreadFrames() from running.
We added a new row classification behavior for shuttle to for back-to-back row with no fluegap in the between.
New: public class MhShuttleDoubleRowClassificationBehavior extends MhDoubleRowClassificationBehavior {
A helper object for building GridWindows with ProdPart data has been added. Used together with a ProdPartGridBuilderEnv, it will
populate a GridWindow with data from a ProdPart sequence and their SpecOptions. The ProdPartGridBuilder extends PartGridBuilder.
Added: public class ProdPartGridBuilder extends PartGridBuilder {}
Relavent Merge Request:
A new feature for Ind. Tags has been introduced to help users requesting that their manual changes to Ind. Tags be preserved. This was confirmed by the Extension Alignment Board and will continue to be refined as it is currently in Beta.
This is opt-in feature for the user. Manufacture Snappers and Part objects could require work for compatibility.
Fundmentally when this setting is on, the behavior for itemTagKey() changes as well as we take advantage of the new userModified field on ItemTag to indicate to the system not to purge this tag from the Snapper.
ProdPart has been set up to help take care much of the work. Many manufactures have itemTagKey() or itemTagInfoKey() overridden. This is not recomended to maintain compatibility with this new setting.partSourceId on construction. This should be something generic to the source of the part. Something like leftTableLeg.The following new interface for creating a PartInfoTree for a SpecOption has been added to AbsPart.
An example implementation is in cm/abstract/dataSymbol/parts.cm.
/** * Create Info tree for option. * @option SpecOption instance for PartInfoTree * @parent optional parent PartInfoTree for option */ extend public PartInfoTree createInfoTree(SpecOption option, PartInfoTree parent=null) { return null; }
ProdPartOptionProdPartOption is a new class to help handle any previous implementations using SpecOption. A ProdPartOption has a collection of SpecOption items that would be part of the same options sequence. (Top level option and its sub options)
upcharge is now overridden on ProdPart to get pricing from SpecOptions.
/** * Upcharge. */ public Double upcharge() { double totalUpcharge = super().?v; for (o in specOptions) { totalUpcharge += o.upcharge(this); } return totalUpcharge; }
With the introduction of OptionSpecials, ProdPart now has interface to handle it's OptionSpecials.
The following interface has been added to ProdPart.
Added: extend public str optSpecialKey(SpecOption opt) {} Added: extend public bool containsOptSpecial(PropObj s=null) {} Added: extend public OptionSpecial getOptSpecial(SpecOption opt, PropObj s=null) {} Added: extend public void removeOptSpecial(SpecOption opt, PropObj s=null) {} Added: extend public void removeOptSpecials(PropObj s=null) {} Added: extend public void removeAllSpecials(PropObj s=null) {}
SpecOptionInfoTree has gained new interface to handle special information for it's SpecOption owner. Constructors have also been removed an consolidated into one.
Added: extend public ProdPart part() {} Added: public PartSpecial special() { Added: public str code() {} Added: public str description() {} Added: public double price() {} Added: public str groupDescription() {}
With the new OptionSpecial structure, a specialsKey() function has been added to SpecOption. It returns a key for a SpecOption to be utilized
by a ProdPart.
Also, a new interface for upcharge had been added to SpecOption. It acquires the price of the option, utilizing a special price if one is found (IMPORTANT: see Update in Miscellaneous section).
/** * Get key for specials. */ Added: extend public str specialsKey() {} /** * Option upcharge for Part (accounts for Specials) */ Added: extend public double upcharge(ProdPart owner=null) {}
An interface for storing specials information for SpecOptions has been added. It extends PartSpecial.
It stores information such as:
Added: public class OptionSpecial extends PartSpecial {}
The following interface has been added to ProdPart.
Added: extend public str optSpecialKey(SpecOption opt) {} Added: extend public bool containsOptSpecial(PropObj s=null) {} Added: extend public OptionSpecial getOptSpecial(SpecOption opt, PropObj s=null) {} Added: extend public void removeOptSpecial(SpecOption opt, PropObj s=null) {} Added: extend public void removeOptSpecials(PropObj s=null) {} Added: extend public void removeAllSpecials(PropObj s=null) {}
A helper object for building GridWindows with Part data has been added. A PartGridBuilderEnv is utilized as a helper to the PartGridBuilder.
It's purpose is to provide the grid builder with:
GridCells for a Part or totals rowPart rowNotable interface is:
/** * Flag indicating whether * to build totals row. */ public bool buildTotalsRow; /** * Default constructor. */ public constructor auto(); /** * Columns labels to display in the grid. * @return A seq of column names. */ extend public str[] columns() {} /** * Generates a row ID based on the given row object. * @data The object representing the row data. * @return A str of the row ID. */ extend public str rowIdentifier(Object data) {} /** * Generates the cells for a single row. * @data The object representing the row data. * @return A seq of GridCells for the row. */ extend public GridCell[] getRowCells(Object data) {} /** * Generates cells for a specific part row. * @part The part object for which to generate cells. * @return seq of GridCells representing the part row. */ extend public GridCell[] getPartRowCells(Part part) {} /** * Get GridCells for the totals row. * @return seq of GridCells representing the totals row. */ extend public GridCell[] getTotalCells() {}
Due to rising issues with list pricing in 16.0 Major, the following interfaces have been reverted to the 15.5 version:
customListPrice function in AbsPartupcharge function in ProdPartbasePrice function in AbsPartbasePrice function in DsPartupcharge function accounting for option specials in SpecOptionpublic double price(SpecOption[] specOs=null) in DsPDatapublic double price(SpecOption[] specOs=null) in OfmlPDataThese changes were originally added to account for the new core PartSpecials system.
Impact of changes:
QueryDialog will not account for special pricing for now (expected 16.0 Patch 1)Those who may be affected include:
PartSpecial and/or OptionSpecial systemQueryDialogAbsPart/ProdPart/DsPartAnyone affected may need to:
The new core QueryDialog does still account for special part numbers and descriptions as well as special option codes and descriptions. The fields for special pricing have been removed for the time being.
Link to Merge Request with changes
With the new core implementation of QueryDialog, a simple OptionMakeSpecialDialog has been made to allow users to
enter special information for SpecOptions. It extends PartMakeSpecialDialog.
It contains fields for entering:
When the user confirms creation of the special, the dialog creates a new OptionSpecial and invokes it's specialChangedEvent.
It passes QuerySpecialChangedEventArgs containing the original OptionSpecial and the new OptionSpecial as args.
/** * Handles OK button clicked event. * @sender Source of the event * @args Event arguments */ public void onOKButtonClick(Object sender, Object args) { if (original as OptionSpecial) { OptionSpecial newSpecial(partNumTF.text, descrTF.text, priceReplaceRB.currentState > 0, amountDF.value, optTF.text, optDescTF.text, original.originalPart, original.originalOptStr); if (checkSpecial(newSpecial)) { specialChangedEvent.invoke(this, QuerySpecialChangedEventArgs(original, newSpecial)); close(); } } }
A ProdPartQueryDialogBehavior is a stateless behaviorial object utilized for QueryDialogs.
It is an extension of the QueryDialogBehavior to handle ProdParts and SpecOptions.
See the documentation QueryDialogBehavior for more details.
A ProdPartQueryDialogDataEnv is an extension of the QueryDialogDataEnv. It is a data env object utilized for QueryDialogs.
It's main purposes are to:
The env has 1 extra data field:
str->str{} optionsPartSpecialHolder specials mapPartSpecialHolder specials mapIn ProdPartQueryDialogDataEnv... /** * Map of part special IDs to * a set of SpecOption special IDs. */ public str->str{} options;
To provide a QueryDialogDataEnv for a PropObj, return the customized data environment under the cQueryDataEnvPropKey key on the PropObjs props.
If a Snapper or PropObj provides AbsParts, the ProdPartQueryDialogDataEnv is a better option. This type is handled for Snappers in cm.abstract.office
but custom PropObjs may need to provide the appropriate data environment if a super class does not already.
Implementation in cm/abstract/office/panelFrame.cm /** * Prop defs. */ public props { ... QueryDialogDataEnv cQueryDataEnvPropKey : cached=false, stream=null { Object get(..) { return ProdPartQueryDialogDataEnv(that); } } ... }
As of 16.0, preview image support has been implemented for core Parts and is no longer exclusive to DsParts.
To support exporting these images, the following interface has been added to the Database class for PMX exports:
/** * Database file path. */ Added: private str filePath : public readable /** * Insert PictureData into DB. */ Added: final public int insertPictureData(PictureData in_PictureData) {} /** * Insert ItemData image directly. */ Added: extend public void insertPicture(ItemData item) {
As the PMX export and the Database type utilize NetObj, using the insertPictureData function (aka inserting PictureData objects through NetObj) was faulty for inserting the
image itself. Instead, this function is utilized to insert the picture meta-data (filename, tags, id, etc.) rather than the image itself.
The insertPicture function is utilized for inserting the image into the PMX database. It utilizes the SQLite library to insert the image directly rather
than relying on NetObj.
As of 16.0, preview image support has been implemented for core Parts and is no longer exclusive to DsParts.
To support exporting these images, a new NetObj type has been added for PMX export support. This new PictureData type
reflects the one given in the SpecificationDatabase.dll provided by Spec.
Added: public class PictureData extends DataDefinition {}
The following constants have been added to coreGlobals.cm:
// QueryDialog helper constants Added: public const str cQueryKey = "query"; Added: public const str cQueryBehaviorPropKey = "queryBehaviorPropKey"; Added: public const str cQueryDataEnvPropKey = "queryDataEnvPropKey";
With the new QueryDialog, interface has been added to Snapper to opt-in to utilize the new dialog.
The acceptQuery function is checked before adding the query context-menu item to a Snapper and the openQueryDialog function
is utilized to open a QueryDialog for a `Snapper.
By default, the acceptQuery function returns false and must be overidden to utilize the new QueryDialog. The openQueryDialog function
displays a QueryDialog with a QueryDialogDataEnv containing the Snapper.
The following interface has been added to Snapper:
// QueryDialog helper functions Added: extend public bool acceptQuery() {} Added: extend public void openQueryDialog() {}
As of 15.5, a new summation type setting was added to the Summary Control Panel in the Calculations dialog. It allows the user to choose the price type that their summary Sum items display (Sell, Buy, List, Profit).
As a result, new GlobalPartAdjustmentSum types have been added to represent the different summation types.
Some helper functions have also been added to GlobalPartAdjustmentSum.
In cm/core/calc/globalPartAdjustmentSums.cm Added: public class SellGlobalPartAdjustmentSum extends GlobalPartAdjustmentSum Added: public class ListGlobalPartAdjustmentSum extends GlobalPartAdjustmentSum Added: public class BuyGlobalPartAdjustmentSum extends GlobalPartAdjustmentSum Added: public class ProfitGlobalPartAdjustmentSum extends GlobalPartAdjustmentSum To GlobalPartAdjustmentSum class: // Summation type constant str key. Added: extend public str sumType() {} // Summation type string (ex. "Sell"). Added: extend public str sumLabel() {} // Summation type label (ex. "(Sell)"). Added: extend public str sumTypeLabel() {} // ex. // Summary line name # summation type label (ex. "Components (Sell)"). Added: extend public str displayName() {}
As of 15.5, a new summation type setting was added to the Summary Control Panel in the Calculations dialog. It allows the user
to choose the price type that their summary Sum items display (Sell, Buy, List, Profit). The default summation type is sell price (SellGlobalPartAdjustmentSum).
As a result, the following has been added to SummaryControlPanel:
In cm/core/calc/summaryControlPanel.cm // New UI element to display summation type options Added: private DropDownTreeView sumTypeSelector; // sumTypeSelector callback to update CalculationView's summary items Added: final public void updateSelectedTotal() {}
As of 16.0, preview image support has been implemented for core Parts and is no longer exclusive to DsParts. Supporting preview images
in core means rendering symbols in Space and generating images to display/export.
As a result, a new setting has been added to the Calculations Control Panel for the user to enable/disable rendered preview images. This
new setting is stored in core settings under the cPreviewImageSettingKey key.
Another control has been added to the Calculations Control Panel to allow the user to manually refresh their preview images. This option is only displayed when the previous setting is enabled.
In cm/core/calc/calculationDialog.cm Added: public bool renderedPreviewImagesEnabled() {}
As of 15.5, a new summation type setting was added to the Summary Control Panel in the Calculations dialog. It allows the user to choose the price type that their summary Sum items display (Sell, Buy, List, Profit).
As a result, a new helper function has been added to ArticleViewSettings to get the summation type from it's summaryAdjustments:
Added: extend public GlobalPartAdjustmentSum summationType() {
As of 15.5, a new summation type setting was added to the Summary Control Panel in the Calculations dialog. It allows the user to choose the price type that their summary Sum items display (Sell, Buy, List, Profit).
As a result, the following helper function have been added to CalculationView:
ArticleViewSummarySettingsAdded: extend public ArticleViewSummarySettings summarySettings() {} Added: extend public GlobalPartAdjustmentSum summationType() {}
As of 15.5, a new summation type setting was added to the Summary Control Panel in the Calculations dialog. It allows the user to choose the price type that their summary Sum items display (Sell, Buy, List, Profit).
As a result, a new helper function has been added to ArticleSummary to get the summation type from it's ArticleView:
Added: extend public GlobalPartAdjustmentSum summationType() {}
A new RefreshColumnTool has been added in cm.core.calc. This tool gives the user the option to refresh a column. PartColumnTools are presented to the user as context
menu items when they right-click a column header.
As of 16.0, the RefreshColumnTool is only utilized for the PartPreviewColumn.
In cm/core/calc/partColumnTool.cm Added: public class RefreshColumnTool extends PartColumnTool {}
The layer tab used to be horribly slow due to creating a ton of window handles.
It has now been reworked from the ground up, using a TreeView instead. We've also taken the opportunity to modernize the user experience.
An Xref tab has been added to the DwgDialog which allows users to control what external references of a DWG that they'd like to insert.
As of 16.0, preview image support has been implemented for core Parts and is no longer exclusive to DsParts.
As a result, a new preview image column has been created. With the column, a new PartColumnTool has been made, a PartPreviewRefreshColumnTool utilized to allow the user
to manually refresh their preview image column.
In cm/core/init/partPreviewColumn.cm Added: public class PartPreviewColumn extends BasicPartColumn : inherit constructors {} Added: public class PartPreviewRefreshColumnTool extends RefreshColumnTool {
Added convenient common UIHints that adhere to the facelift guidelines for button types that automatically render icons from Snapper 3D (i.e. prefer3D=true). This complements existing product button UIHints.
productButtonSmall3D productButtonMedium3D productButtonMediumWithLabel3D productButtonMediumTallWithLabel3D productButtonLarge3D productButtonLargeWithLabel3D productButtonLargeTallWithLabel3D productButtonLargeShort3D productButtonExtraLarge3D productButtonExtraLargeWithLabel3D
In accordance to an update in the Design guidelines, Medium Tall button types now allow up to two lines of text before being truncated. The line height of the text is reduced accordingly to account for limited spacing.
We have improved the tab switching experience related to the new HorizontalTab to allow more accurate click rates. We also added Left/Right shortcut keys to allow users to switch tabs while the focus is in the Toolbox, this complements the existing Up/Down shortcut keys that allows switching between Toolbox Cards.
We have improved the new library header tabs by allowing them to build lazily when the user clicks on the tab, this should improve performance of the initial building of large extension toolboxes. This behavior can be toggled temporarily using CET Facelift Test Tools settings > "Use lazy tabs building" or the Release Debug menu.
To ensure your toolbox work correctly with this change, check if your existing code for potentially unsafe operations such as if it expects certain Controls to be built ahead of time. This behavior can also be controlled through TabbedLibraryHeaderBuilder property bool lazyBuildTab.
Along with this change, we have also added a progress bar to show estimates of progress when the Extension is unsnoozing or when the toolbox is building its contents/subcomponents.
If your extension does custom instantiation of toolbox limbs and controls, and the new progress bar is interfering with your code, you can consider to optionally create your own UIBuilder instance (if you're using the Core's default UIBuilder through getUIBuilder()) or temporarily override the UIBuilder.allowProgress flag to disable the progress bar.
The text label in new library headers that were automatically truncated now show tooltips to allow the user to view the full text.
A new helper method has been added in LibraryLimb to allow quickly adding a standardized help button in the section label.
LibraryLimb + extend public void addHelpSectionButton(symbol pkg, str key) SectionButton + SectionButton now passes along a `pkg` argument.
The text label in each library section header that are too long should now automatically get truncated, along with tooltips to allow the user to view the full text.
The behaviors with certain LibraryLimb such as VoidCallbackLimb and BoolCallbackLimb now has improved auto truncation behaviors.
Toggles built with BoolCallbackLimb using toggleStyle=true can now be further configured to have toggles that align to the rightmost edge of the toolbox using limb.setStyles(boxRightAligned=true).
An interface for storing specials information for Parts has been added.
It stores information such as:
Added: public class PartSpecial {}
A helper object for building GridWindows with Part data has been added. Used together with a PartGridBuilderEnv, it will
populate a GridWindow with data from a Part sequence.
Notable interface is:
/** * Constructor. * @env (optional) The env for the grid builder. */ public constructor(PartGridBuilderEnv env=null) {} /** * Populates a GridWindow with part. * @part The partset to populate the grid with. * @grid The grid window to populate. * @env (optional) The env (overrides cached env). * @return A mapping of row indices to row IDs. */ extend public int->str populateGridWindow(Part[] part, GridWindow grid, PartGridBuilderEnv env=null) {}
With the addition of PartSpecials, the PartSpecialHolder, and the new QueryDialog, helper functions have been added to Part.
The following functions have been added to Part:
// PartSpecials and PartSpecialHolder Added: extend public str specialsKey() {} Added: extend public bool containsSpecial(PropObj s=null) {} Added: extend public bool containsAnySpecials(PropObj s=null) {} Added: extend public PartSpecial getSpecial(PropObj s=null) {} Added: extend public void putSpecial(PartSpecial special, PropObj s=null) {} Added: extend public void removeSpecial(PropObj s=null) {} Added: extend public str specialFlattenableKey() {} // QueryDialog helpers Added: extend public void appendQueryPopupMenuItems(TreeViewItem[] treeViewItems) {} Added: extend public void openQueryDialog() {} Added: extend public bool visibleInQuery() {}
A helper function has been added to Part to retrieve the profit price of the part. This function is now used in the TotalProfitPartColumn.
Added: extend public double profit(Space space=null) {}
With the addition of the new Part preview image system, the following interface has been added to Part:
allowRenderPreviewImage() function to allow opting-out of rendered preview imagesImage instance for a PartUrl instance for a PartAdded: extend public bool allowRenderPreviewImage() {} Added: extend public Image previewImage(sizeI size=cPartPreviewImageSize, bool generateIfNotFound=false, bool regenerate=false) {} Added: extend public Url previewImageUrl(bool generateIfNotFound=false) {}
With the addition of the new Part preview images system, the following helper functions have been added for retrieving
a Part's rendered preview image from Space cachedData:
In cm/core/part/partPreviewImages.cm // GET Added: public str->Url partPreviewImages(Space space = mainSpace()) {} Added: public Url getPartPreviewImageUrl(str key, Space space = mainSpace()) {} Added: public Image getPartPreviewImage(str key, Space space = mainSpace()) {} Added: public Url getPartPreviewImageUrl(Part part, Space space = mainSpace()) {} Added: public Image getPartPreviewImage(Part part, Space space = mainSpace()) {} // PUT Added: public void putPartPreviewImage(str key, Image img, Space space = mainSpace()) {} Added: public void putPartPreviewImage(str key, Url imageUrl, Space space = mainSpace()) {} Added: public void putPartPreviewImage(Part part, Image img, Space space = mainSpace()) {} Added: public void putPartPreviewImage(Part part, Url imageUrl, Space space = mainSpace()) {} // REMOVE Added: public void removePartPreviewImage(str key, Space space = mainSpace()) {} Added: public void removePartPreviewImage(Part part, Space space = mainSpace()) {} Added: public void clearPartPreviewImages(Space space=mainSpace()) {}
An interface for storing specials for Parts has been added.
The new PartSpecialHolder object is stored on a PropObj's prop data under the cSpecialHolderKey key. It's purpose is to store PartSpecial objects for a single PropObj's parts.
It stores a map of keys to PartSpecial objects (str->PartSpecial specials).
Added: public class PartSpecialHolder{}
The following functions have been added to PartInfoTrees:
Added: extend public bool containsSpecial() {} Added: extend public PartSpecial special() {} Added: extend public str code() {} Added: extend public str description() {} Added: extend public double price() {} Added: extend public str groupDescription() {}
A helper object for building GridWindows with ProdPart data has been added. A ProdPartGridBuilderEnv is utilized as a helper to the ProdPartGridBuilder. It
extends PartGridBuilderEnv.
Notable interface is:
Added: public class ProdPartGridBuilderEnv extends PartGridBuilderEnv {} /** * Generates cells for a specific SpecOption row. * @opt SpecOption to get values for * @part (optional) Part owner of @opt */ extend public GridCell[] getOptionRowCells(SpecOption opt, ProdPart part=null) {}
As of 15.5, a new summation type setting was added to the Summary Control Panel in the Calculations dialog. It allows the user
to choose the price type that their summary Sum items display (Sell, Buy, List, Profit). The default summation type is sell price (SellGlobalPartAdjustmentSum).
As a result, the following changes have been made to PartGroup:
cachedProfitPrice field has been added. This allows the SummaryPriceInfo to display sum according
to the profit price of parts.summaryPriceInfo function has been added and the old one deprecated. Because SummaryPriceInfo has a new constructor,
the new function in PartGroup supports the new constructorAdded: public double cachedProfitPrice; Old: final public SummaryPriceInfo summaryPriceInfo() : deprecated {} New: final public SummaryPriceInfo summaryPriceInfo(GlobalPartAdjustmentSum summationType) {}
As of 15.5, a new summation type setting was added to the Summary Control Panel in the Calculations dialog. It allows the user
to choose the price type that their summary Sum items display (Sell, Buy, List, Profit). The default summation type is sell price (SellGlobalPartAdjustmentSum).
As a result, a helper to retrieve the summation type from the PartListSummary's summaryAdjustments has been added:
Added: extend public GlobalPartAdjustmentSum summationType() {}
Some helper functions have been added in cm/core/part/functions.cm pertaining to the new Part preview image system.
Misuse of these functions could affect the user's experience with their preview images.
Added: public void beginRenderPreviewImagesTask(Space space=mainSpace()) {} Added: public void regeneratePreviewImages(Window progressParent=null, Space space=mainSpace()) {} Added: public void removePartPreviewImages(Space space=mainSpace()) {} Added: public void renderImages(Part[] parts, Window progressParent=null, bool progress=false) {} Added: public void clearCachedPreviewImageFiles() {} Added: public Image renderPartPreviewImage(Part part, sizeI dimension=cPartPreviewImageSize, bool force=false) {}
Due to rising issues with list pricing in 16.0 Major, the following interfaces have been reverted to the 15.5 version:
customListPrice function in AbsPartupcharge function in ProdPartbasePrice function in AbsPartbasePrice function in DsPartupcharge function accounting for option specials in SpecOptionpublic double price(SpecOption[] specOs=null) in DsPDatapublic double price(SpecOption[] specOs=null) in OfmlPDataThese changes were originally added to account for the new core PartSpecials system.
Impact of changes:
QueryDialog will not account for special pricing for now (expected 16.0 Patch 1)Those who may be affected include:
PartSpecial and/or OptionSpecial systemQueryDialogAbsPart/ProdPart/DsPartAnyone affected may need to:
The new core QueryDialog does still account for special part numbers and descriptions as well as special option codes and descriptions. The fields for special pricing have been removed for the time being.
Link to Merge Request with changes
With the new core implementation of QueryDialog, a simple PartMakeSpecialDialog has been made to allow users to
enter special information for Parts.
It contains fields for entering:
When the user confirms creation of the special, the dialog creates a new PartSpecial and invokes it's specialChangedEvent.
It passes QuerySpecialChangedEventArgs containing the original PartSpecial and the new PartSpecial as args.
/** * Handles OK button clicked event. * @sender Source of the event * @args Event arguments */ extend public void onOKButtonClick(Object sender, Object args) { PartSpecial newSpecial(partNumTF.text, descrTF.text, priceReplaceRB.currentState > 0, amountDF.value); if (checkSpecial(newSpecial)) { specialChangedEvent.invoke(this, QuerySpecialChangedEventArgs(original, newSpecial)); close(); } }
A QueryDialogDataEnv is a data env object utilized for QueryDialogs.
It's main purposes are to:
The env has 3 data fields:
PropObj ownerParts for the dialogPartSpecialHolder with special informationstr->Part partsPartSpecialHolder specials mapPart instance owning special keyint->str rowIDsQueryDialog's gridparts map (aka a key for PartSpecialHolder specials map)In QueryDialogDataEnv... /** * The PropObj owner of this environment */ public PropObj owner; /** * Map of part special ids to * their corresponding Part objects. */ public str->Part parts; /** * Map of row index * to it's corresponding row ID. */ public int->str rowIDs;
To provide a custom QueryDialogDataEnv for a PropObj, return the customized data environment under the cQueryDataEnvPropKey key on the PropObjs props.
The core implementation of QueryDialogDataEnv does not account for cm.abstract's ProdParts and SpecOptions. If a Snapper or PropObj provides AbsParts, the ProdPartQueryDialogDataEnv
is a better option.
Implementation in cm/core/snapper.cm /** * Props. */ public props : cached=true { QueryDialogDataEnv cQueryDataEnvPropKey : cached=false, stream=null { Object get(..) { return QueryDialogDataEnv(that); } } }
A QueryDialogBehavior is a statless behavioral object utilized for QueryDialogs.
It's main purposes are to:
SubWindows of a QueryDialogA QueryDialog has 3 SubWindow fields: topWindow, dataWindow, and bottomWindow.
The dataWindow is specifically designed/expected to hold only a GridWindow displaying the part data.
topWindow and bottomWindow are fully customizable.
On initialization, the dialog generates it's UI through it's behavior by calling the init and alignment functions below.
If bypassing the base QueryDialogBehavior's UI, these should be overridden.
/** * Initialize top SubWindow on QueryDialog. * @parent QueryDialog parent to build top window for * @return SubWindow for top of QueryDialog */ extend public SubWindow initTopWindow(QueryDialog parent) {} /** * Initialize data sub window on QueryDialog. * @parent QueryDialog parent to build data window for * @return SubWindow for data window of QueryDialog */ extend public SubWindow initDataWindow(QueryDialog parent) {} /** * Initialize bottom SubWindow on QueryDialog. * @parent QueryDialog parent to build bottom window for * @return SubWindow for bottom of QueryDialog */ extend public SubWindow initBottomWindow(QueryDialog parent) {} /** * Align controls of SubWindows * within QueryDialog. * @dialog QueryDialog to align SubWindows for */ extend public void alignControls(QueryDialog dialog) {}
See QueryDialog documentation for order of initialization and alignment calls.
During initialization, the QueryDialog calls the functions below on it's behavior instance.
/** * Initialize events for top window * in QueryDialog. * @topWindow Top SubWindow of QueryDialog to init events for */ extend public void initTopWindowEvents(SubWindow topWindow) {} /** * Initialize events for grid window * in QueryDialog. * @grid Grid SubWindow of QueryDialog to init events for */ extend public void initGridWindowEvents(GridWindow grid) {} /** * Initialize events for bottom window * in QueryDialog. * @grid Bottom SubWindow of QueryDialog to init events for */ extend public void initBottomWindowEvents(SubWindow bottomWindow) {}
The base implementation of QueryDialogBehavior utilizes the new event management system defined in cm.win.events for it's UI interaction processing.
This is not a requirement of subclasses though. If using a custom behavior, the standard callback system can still be utilized.
To provide a custom QueryDialogBehavior for a PropObj, return the customized behavior under the cQueryBehaviorPropKey key on the PropObjs props.
Implementation in cm/core/snapper.cm /** * Props. */ public props : cached=true { QueryDialogBehavior cQueryBehaviorPropKey : cached=false, stream=null { Object get(..) { return coreQueryDialogBehavior(); } } } Implementation in custom/fika/office/foDataSnapper.cm /** * Props. */ public props : cached=releaseMode { QueryDialogBehavior cQueryBehaviorPropKey : cached=false, stream=null { Object get(..) { return prodPartQueryDialogBehavior(); } } }
Global QueryDialogBehavior instances are provided in cm/core/part/query/queryDialogBehavior.cm and cm/abstract/part/query/prodPartQueryDialogBehavior.cm.
The core implementation provides UI and specials handling for core Parts. The abstract implementation provides handling for abstract ProdParts and their SpecOptions.
A QueryDialog is a dialog designed to represent Part data on a single PropObj. It is intended to be utilized for editing,
removing, and creating specials on Parts.
A QueryDialog is composed of two primary components:
QueryDialogBehavior - defines the UI structure and handles user interactions.QueryDialogDataEnv - stores and manages the data displayed in the dialog.The purpose of this design is to allow flexibility for the user-interface and data management of a QueryDialog.
Both the QueryDialogBehavior and the QueryDialogDataEnv types are documented within this migration guide.
QueryDialog owns a QueryDialogDataEnv which owns a PropObjPropObj provides the QueryDialogBehavior for the dialog through it's propsQueryDialog retrieves the QueryDialogBehavior from the data env's PropObj to construct the UIQueryDialogDataEnv)QueryDialog is instantiated, it receives a QueryDialogDataEnvQueryDialogBehavior from the data env's PropObj QueryDialogDataEnv + QueryDialogBehavior)QueryDialog is instantiated, it receives a QueryDialogDataEnv and a QueryDialogBehaviorQueryDialogBehavior QueryDialog through the UI element to modify the dataPart data are reflected in the QueryDialog through hooks defined in cm/core/part/query/hooks.cmHere is a simple diagram of the QueryDialog system structure:

Two functions are provided on Snapper to opt-in to utilizing the new QueryDialog implementation.
extend public bool acceptQuery()Snappers in Space and Part lines in the Calculations dialogSnapper. Override and return true to opt-inextend public void openQueryDialog()QueryDialog with a QueryDialogDataEnv for the current SnapperQueryDialogBehavior on your Snapper to customize the dialog UI and/or UI interaction (see "Providing a QueryDialogBehavior") Through PropObj properties, a QueryDialogBehavior can be provided under the cQueryBehaviorPropKey key.
QueryDialogBehaviors are designed to be stateless meaning many PropObj instances can utilize a single behavior instance.
Implementation in cm/core/snapper.cm /** * Props. */ public props : cached=true { QueryDialogBehavior cQueryBehaviorPropKey : cached=false, stream=null { Object get(..) { return coreQueryDialogBehavior(); } } } Implementation in custom/fika/office/foDataSnapper.cm /** * Props. */ public props : cached=releaseMode { QueryDialogBehavior cQueryBehaviorPropKey : cached=false, stream=null { Object get(..) { return prodPartQueryDialogBehavior(); } } }
Global QueryDialogBehavior instances are provided in cm/core/part/query/queryDialogBehavior.cm and cm/abstract/part/query/prodPartQueryDialogBehavior.cm.
See documentation for QueryDialogBehavior for more info.
To provide a custom QueryDialogDataEnv for a PropObj, return the customized data environment under the cQueryDataEnvPropKey key on the PropObjs props.
The core implementation of QueryDialogDataEnv does not account for cm.abstract's ProdParts and SpecOptions. If a Snapper or PropObj provides AbsParts, the ProdPartQueryDialogDataEnv
is a better option.
Implementation in cm/core/snapper.cm /** * Props. */ public props : cached=true { QueryDialogDataEnv cQueryDataEnvPropKey : cached=false, stream=null { Object get(..) { return QueryDialogDataEnv(that); } } }
A single function in cm/core/part/query/functions.cm returns a QueryDialog instance for a given QueryDialogDataEnv.
This function allows for customization of the QueryDialog parent Window and the title of the QueryDialog.
In the Fika implementation, a custom dialog title is provided.
/** * Get QueryDialog instance for a PropObj * (if none are already active). * @env QueryDialogDataEnv containing PropObj owner * @parent parent window of QueryDialog * @dialogLabel label for QueryDialog */ public QueryDialog queryDialog(QueryDialogDataEnv env, Window parent=session.mainWindow(), str dialogLabel=null) { if (!env.?owner) return null; if (QueryDialog activeDialog = getActiveQueryDialog(env.owner)) { activeDialog.setFocus(); return activeDialog; } if (activeQueryDialogs().empty()) initGlobalQueryHooks(); if (!dialogLabel) dialogLabel = $queryDialogCaption; return QueryDialog(..); } Implementation in custom/fika/functions.cm /** * Open query dialog for snapper. */ public void foOpenQueryDialog(PropObj data) { QueryDialog dialog = queryDialog(ProdPartQueryDialogDataEnv(data), dialogLabel=$fikaQueryDialogCaption); dialog.show(); }
A QueryDialog has 3 SubWindow fields: topWindow, dataWindow, and bottomWindow.
On initialization, the dialog calls initControls() which instantiates each SubWindow through it's behavior.
The alignment of controls within a SubWindow is handled by the behavior and the alignment of a SubWindow within the dialog is handled by the QueryDialog.
UI initialization and alignment /** * Initializes the QueryDialog. */ extend public void initialize() { behavior.preInit(this); initControls(); alignControls(); autoSizeWindows(); initSubWindowEvents(); behavior.postInit(this); } /** * Initializes dialog controls. */ extend public void initControls() { topWindow = behavior.initTopWindow(this); dataWindow = behavior.initDataWindow(this); bottomWindow = behavior.initBottomWindow(this); } /** * Aligns the controls within the dialog. */ extend public void alignControls() { topWindow.?pos = (cWindowMargin, 0); dataWindow.?below(topWindow, cWindowMargin); bottomWindow.?below(dataWindow, cWindowMargin); topWindow.?extendRight(cWindowMargin); dataWindow.?extendRight(cWindowMargin); bottomWindow.?extendRight(cWindowMargin); behavior.?alignControls(this); }
Here is a simple diagram of the layout of a QueryDialogs UI:

A few functions have been made public and some helper functions have been added in cm/core/red3D/redRenderSnapperEnv.cm.
Old: final private void renderRigidSnapper(Url imageFile, Snapper z, detailLevel detail) {} New: extend public void renderRigidSnapper(Url imageFile, Snapper z, detailLevel detail) {} Old: final private void doRender(Url imageFile, detailLevel detail=detailLevel.high) {} New: extend public void doRender(Url imageFile, detailLevel detail=detailLevel.high) {} Old: final private REDThumbnailEnv createThumbnailEnv(Snapper z) {} New: extend public REDThumbnailEnv createThumbnailEnv(Snapper z) {} Added: extend public Primitive3D getPrimitive3D(Snapper z, detailLevel detail=detailLevel.high) {} Added: extend public FetchEnv3D getFetchEnv3D(detailLevel detail) {}
To support the new preview image system for Parts, a new RenderSnapperPreviewImageEnv has been made. It is intended to be
used for rendering Snappers for the use of a preview image. Notably, it positions the camera for a rendering in a "preview image"
style. It also caches it's REDThumbnailEnv to save time for the preview images rendering task.
We have added some constants and helper functions that will help to determine the correct margin, spacing, and label heights for toolbox buttons. These can be found in cm/core/toolbox/toolboxGuidelines.cm.
Performs the default copy action (same as pressing ctrl+c).
Performs the default cut action (same as pressing ctrl+x).
Performs the default paste action (same as pressing ctrl+v).
Performs the default paste selection action, aka "animate" (same as pressing 'a').
Performs the default toggle freeze/unfreeze action (same as pressing ctrl+e). This replaces the previous FreezeSnappersInstruction and UnfreezeSnappersInstruction.
Looks up the value of a field on an object and writes the result to the blackboard.
Writes the snappers in the active selection to the blackboard. You can choose whether to get all snappers in selection or only the 'main' snapper.
Sends a quickPropertyChanged(..) event to a snapper. Note that this instruction uses the "old" quick property system. When ever possible, it's recommended to use PutPropInstruction instead.
A new test instruction that allows switching view to a single 2D view, a single 3D view, a split 2D and 3D view, or the paper view. Additionally it allows specifying which view should be set as the active view in the case that a split view is chosen.
This class replaces the old SwitchToView2DInstruction and SwitchToView3DInstruction, which were more limited.
Compares two blackboard values and checks that they are equal. Returns an error if they are not.
Compares two blackboard values and checks that they are not equal. Returns an error if they are.
We have added a new helper method to return if the painter has automatically truncated any text. In general, AutoPainter, AutoPainterEx, ComboTextPainter also now pass along truncated method calls to their auxillary painters.
/** * Return if the text has been truncated by the painter. */ public bool truncated() { return autoTruncate() and painter.truncated(); }
A new method has been introduced to check if a frame window is arranged / snapped by Windows. This is used by the automatic saving of position and sizes of FrameWindow.
New: final public bool isArranged() {
The following helper functions have been added to GridWindow:
// gets the index of a column in the grid based on the column's label Added: extend public int columnIndex(str columnLabel) { // builds a row in the grid given the row index and a sequence of GridCells Added: extend public int buildRow(int row, GridCell[] rowCells) {} // gets a sequence of the column labels in the grid Added: extend public GridLabel[] columnLabels() {}
We have added a new event management object the EventNotifier, aliased event.
It manages a single event and a map of observers (Object) to observer callbacks (ObserverCb).
The purpose of an event is to notify all observing Objects that an event has been invoked. It does this by calling the respective ObserverCb of that Object.
For example, a Button subclass may have a clicked event that it's parent Window may observe.
The button will invoke it's clicked event and this will call the registered ObserverCb on the parent Window.
public class ExampleButton extends Button { public event clicked; public void press() { ... // invoke the `clicked` event as appropriate clicked.invoke(this, args=null); ... } } public class ExampleWindow extends Window { private ExampleButton btn; public constructor() { btn = new ExampleButton(); ObserverCb callback = method ExampleWindow.onButtonClicked(Object, Object); // initialize the observer callback btn.clicked.add(this, callback); // add the observer callback to `btn`'s `clicked` event } public void onButtonClicked(Object sender, Object args) { if (sender as ExampleButton) { pln("Button clicked!!!"); } } }
ObserverCb is an alias for the method signature of callbacks that an EventNotifier handles.
The first Object parameter is reserved for the observer class that contains the callback function (ExampleWindow in the above example).
The second Object parameter is typically utilized as the Object instance that is invoking the event (ExampleButton btn in the above example).
The third Object parameter is typically utillized as Object args to provide extra information to the observers of the event (null in the above example but ButtonClickedEventArgs would be an example utilization).
The ObserverCb alias is defined in cm.win.events.eventNotifer.cm.
package cm.win.events; /** * A type alias for a callback method used in the EventNotifier class. */ public alias ObserverCb = method(Object, Object, Object):void;