cm.abstract.floorMarking

Floor marking abstract

Added a new abstract cm.abstract.floorMarking that the "Marking & Signage" extension implements for its floor marking snappers.

cm.abstract.materialHandling

Multi-bay and multi-frame support

The typical child snapper structure of racking snappers looks like this:

Row
|---Bay
|   |---Level
|   |---Level
|   |---Level
|---Bay
|   |---Level
|   |---Level
|   |---Level
|---Frame
|   |---Protector
|---Frame
|   |---Protector

We have made improvements to better support having bays within bays and frames within frames, e.g. child bays within a multibay.

Row
|---MultiBay
|   |---Level
|   |---ChildBay
|       |---Level
|       |---Level
|---MultiFrame
|   |---ChildFrame
|   |---ChildFrame

If you would like to implement this structure, you can set MhMasterLinkFinalizeFunction.runMultiChildrenLink = true to utilize the multibay/multiframe linking logic within this class. Do so by returning true for the shouldRunMultiChildrenLink() method in the following classes:

  • MhBayRowEngineBehavior
  • MhAisleRowEngineBehavior
  • MhFlueGapRowEngineBehavior
Example:
public class MhBayRowEngineBehavior extends MhEngineBehavior {

    /**
     * Should run multi children link?
     */
    extend public bool shouldRunMultiChildrenLink() {
        return false;
    }
}

With this type of structure, you may sometimes want to operate on a flattened structure of engine entries where each child bay/child frame entry is within the row entry's coordinate system. You can use the engine functions MhRowFlattenChildrenFunction and MhRowUnflattenChildrenFunction for this. The functions (or just code) you wish to execute on a flattened structure should run between the flatten and unflatten functions.

MhRowFlattenChildrenFunction("flattenRowChildren");
MhRowUnflattenChildrenFunction("unflattenRowChildren");

Example:
flattenRowChildren
function1
function2
unflattenRowChildren

There is also a new engine function MhRowValidateChildrenStructureFunction that will put/remove child bays/frames with a multi bay/frame parent.

MhRowValidateChildrenStructureFunction("validateRowChildrenStructure");

MhRowShape also has two new fields:

public class MhRowShape extends MhSnapperShape {
    /**
     * Stretcher for single line.
     */
    public Connector[] leftStretchers : stream=null, copy=null;
    public Connector[] rightStretchers : stream=null, copy=null;
}

These are populated when your row has child bays, and will generate a pair of stretchers for each y-pos of child bays. These are used to increase/decrease the number of bays at a particuler y-pos.

Multibay Stretchers

You may turn these off by overriding this method.

public class MhRowShape extends MhSnapperShape {

    /**
     * Return true if should show the stretch for each bay.
     */
    extend public bool showStretcherOnEachBay() {
        return true;
    }
}

RAL Material template extension

Added a template extension generator that utilizes RAL material in the "Selective Rack Demo" extension (custom.palletRackingDemo). This is to provide sample code on how to use the RAL materials from the "RAL Material Pack" extension in your own custom extensions.

To generate this template extension, go to "Selective Rack Demo" toolbox. Click on the "Generate" button.

Selective Rack Demo toolbox

In the popup, enter an extension prefix, leave the "Template" dropdown as "Selective Racking" and ensure "Use RAL" checkbox is ticked. Click the Ok button to generate the extension.

RAL Template generator

The extension is now generated. You can click Yes for CET to restart and you can now turn on the generated extension to test out the feature. The required code for RAL materials should be found in materials.cm and XXXFrameShape.cm

Bay editor new Dimension tab

A new bay editor tab has been added to the abstract MhBayDimensionEditorItem. This tab is used to modify the text style and dimension style of the selected dimension in the Bay Editor preview.

Bay Editor Dimensions tab

cm.abstract.part

class CustomSpecOption

CustomSpecOption is an extension of SpecOption that represents a user-defined option.

  • It is generated from a CustomOptionSpecial
  • It carries a stable specials key (_specialsKey) that uniquely identifies the option and ties it back to its originating special
  • Behaves like a standard SpecOption but provides linkage to its backing special.

Constructor

public constructor(
    str key,
    int sequence,
    str code,
    str description,
    str groupDescription = null,
    int level = 0,
    double price = 0.0,
    bool exportable = true
)
  • Parameters:
    • key → the special key (comes from CustomOptionSpecial).
    • sequence → sequence index of custom option in Parts specOptions
    • code, description, price → user-provided values.
    • groupDescription, level, exportable → standard SpecOption fields.
  • Stores the key in _specialsKey, sets the sequence, then calls the base SpecOption constructor for the rest.

Key Fields

  • _specialsKey
    • Stores the unique identifier for this custom option.
    • Assigned at construction and returned by specialsKey().
    • Used for retrieval from PartSpecialHolder and ensuring persistence across sessions.

Key Methods

  • specialsKey()
    • Returns _specialsKey
    • Provides an identifier for mapping between CustomSpecOption and its originating CustomOptionSpecial

Impact

  • Integration with CustomOptionSpecial
    • CustomSpecOption is generated by CustomOptionSpecial.generateCustomOption()
    • This ensures each option has a consistent key and can be invalidated/regenerated as needed
  • Consistency with standard SpecOptions
    • Adds one distinguishing feature: the specialsKey link to its backing special.
  • Developer Workflow
    • When working with custom options:
      • Retrieve them via the PartPartSpecialHolderCustomOptionSpecial.option() chain
      • Use specialsKey() when you need to identify a specific custom option
/**
 * CustomSpecOption.
 * A custom SpecOption created from user-input.
 * Usually generated from a CustomOptionSpecial.
 */
public class CustomSpecOption extends SpecOption {
    
    /**
     * Special Key.
     */
    public str _specialsKey;
    
    
    /**
     * Create a new spec option.
     */
    public constructor(str key, int sequence|, str code, str description, str groupDescription=null,
                       int level=0, double price=0.0, bool exportable=true) { ... }
    
    
    /**
     * Special Key.
     */
    public str specialsKey() { ... }
}

class CustomOptionSpecial

New Custom Option Special System

The CustomOptionSpecial type extends the existing PartSpecial workflow to support freeform options.

  • Users can now dynamically add custom options to parts. These are represented by CustomOptionSpecial instances.
  • Each CustomOptionSpecial is stored in the PartSpecialHolder, uniquely identified by the owning part key and the initial option’s key
Creating a Custom Option
  • When a user adds a new freeform option to a part through the Query Dialog:
    • A new CustomOptionSpecial is created
    • It is inserted into the PartSpecialHolder under its unique key
    • The CustomOptionSpecial holds both:
      • A stable key (does not change once created)
      • A cached CustomSpecOption, generated lazily and invalidated when relevant fields change
Part Rebuilds Options from Specials
  • On specOptions() access, a Part gathers all CustomOptionSpecials associated with its key from the PartSpecialHolder
  • For each CustomOptionSpecial, the part calls option() to retrieve a CustomSpecOption
  • These CustomSpecOptions are then appended into the part’s SpecOptions sequence
    • See documentation in cm.abstract.part.ProdPart for more info

CustomOptionSpecial Interface

CustomOptionSpecial is an extension of OptionSpecial designed to generate CustomSpecOptions.

  • Utilizes a caching mechanism for the generated option
  • Derives a unique key from its internal fields
  • Invalidates its cached option when critical fields are modified
Key Fields
  • _specialsKey
    • Stores the generated key that uniquely identifies this special
    • Generated lazily on first access via generateKey()
    • Ties the _option back to its special instance
  • _option
    • Stores the cached CustomSpecOption
    • Created on first access via generateCustomOption()
    • Invalidated when core attributes (optStr, optDescStr, level, sequence) are changed
Key Methods
  • option()
    • Returns the current CustomSpecOption
    • If _specialsKey has not been generated, calls generateKey()
    • If _option is null, calls generateCustomOption()
  • generateKey()
    • Builds a unique string key by concatenating:
      • optStr, optDescStr, level, sequence, amount
    • Used as the specialsKey for CustomSpecOption
  • generateCustomOption()
    • Constructs a new CustomSpecOption using the _specialsKey, sequence, optStr, optDescStr, and level.
  • invalidate()
    • Clears _option, forcing regeneration on next option() call
  • Property Setters (optStr, optDescStr, level, sequence)
    • Each setter invalidates _option to prevent stale cache
Impact
  • Migration Considerations
    • CustomOptionSpecial provides a structure for specialized option generation
    • Extend the class to add new fields or customize key generation logic
    • If adding new fields that affect uniqueness, update generateKey() accordingly
/**
 * Special for generating CustomSpecOptions.
 */
public class CustomOptionSpecial extends OptionSpecial {

    /**
     * Special Key.
     * Specials Key for generated CustomSpecOption.
     */
    public str _specialsKey;

    /**
     * Cached CustomSpecOption.
     * Invalidated when certain fields are changed.
     */
    public CustomSpecOption _option;

    /**
     * Generate CustomSpecOption from this Special.
     * @return CustomSpecOption created from this Special.
     */
    extend public CustomSpecOption option() { ... }

    /**
     * Generate key from opt information.
     * Used for CustomSpecOption specialsKey.
     */
    extend public str generateKey() { ... }

    /**
     * Generate a CustomSpecOption from this special.
     * @return CustomSpecOption based on this specials data
     */
    extend public CustomSpecOption generateCustomOption() { ... }

    /**
     * Invalidate special.
     * Removes cached option.
     */
    extend public void invalidate() { ... }

    /**
     * Set option code value.
     */
    public str optStr=(str value) { ... }

    /**
     * Set option description value.
     */
    public str optDescStr=(str value) { ... }

    /**
     * Set option level value.
     */
    public int level=(int value) { ... }

    /**
     * Set option sequence value.
     */
    public int sequence=(int value) { ... }
}

cm.abstract.pmx

class ItemData

As of 16.5, a new interface for lead time and PartAnnotations have been added to core Part. These values are now exported with PMX exports leading to the following additions in ItemData.

In cm/abstract/pmx/itemData.cm

New: public AttributeData[] attributes;

/**
 * Instantiate ItemData (this) fields from Part.
 */
extend public void generateItemDataFromPart(Part part) {
	...
New:	this.leadtime = part.leadTime;

	if (part as ProdPart) {
		...
New: 	part.generateAttributeData(this);
		...
	}

	...
}

cm.abstract.unitLoad

Dimensions in unit load editor

The Unit Load Editor now draws dimensions in its preview window. The UnitLoad class has a default implementation for some generic dimensions. This can be overridden to draw dimensions at different locations/offsets/orientations instead.

public class UnitLoadDialog extends DialogWindow {

    /**
     * Append measurements.
     */
    extend public void appendMeasurements(UnitLoad mtbh, Space space) { ... }
}


public class UnitLoad extends CoreObject : abstract, replacing load, inherit constructors {

    /**
     * Return measurement lines for preview space.
     * <line, offset, orientation>.
     */
    extend public <line, double, point>[] measurements() { ... }
}

cm.core

cm.core

class InfoTipSnapperRef

The method str infoStr(str->bool filter=null, bool markup=false) has had its logic moved out into several different new methods for easier overrides.

public class InfoTipSnapperRef extends InfoTipObjectRef {
    New: extend public void partTagsInfoStr(StrBuf buf, str->bool filter=null, bool markup=false) {
    New: extend public void categoriesInfoStr(StrBuf buf, str->bool filter=null, bool markup=false) {
    New: extend public void volumeLayerInfoStr(StrBuf buf, str->bool filter=null, bool markup=false) {
    New: extend public void partsInfoStr(StrBuf buf, str->bool filter=null, bool markup=false) {
    New: extend public void collabInfoStr(StrBuf buf, str->bool filter=null, bool markup=false) {
}

cm.core.dwg

DWG Dialog Multiselect

DWGs in the DWG Dialog can now be multiselected and edited at once.

cm.core.init

class LeadTimeColumn

As of 16.5, a new interface for lead time has been added to core Part. As a result, a new lead time column has been created in cm/core/init/leadTimeColumn.cm. Extensions who have their own Lead Time column should migrate to the core version to avoid duplication.

Developers can migrate by:

  • Preferred option:
    • If applicable, remove registry of a custom Lead Time Column
    • Implement leadTime() in Part to get accurate value for core Lead Time column
  • Secondary option:
    • If custom lead time column exists...
      • Remove registry of a custom Lead Time Column
      • Utilize overridePartColumns() on Part to return custom lead time column in place of core column
/**
 * Lead Time Column for Parts
 */
public class LeadTimeColumn extends BasicPartColumn {

    /**
     * Constructor.
     */
    public constructor() {
		super("coreLeadTime");
    }
    

    /**
     * Initial Visibility.
     */
    public symbol[] initialVisibility() {
		return [#none];
    }


    /**
     * Return property value for part.
     */
    public Object value(Part part, Space space) {
        return part.leadTime();
    }
}

cm.core.part

class Part

New Part-to-Owner Keys

New interfaces, partToOwnerKey and partToOwnerKeys, have been added to Part. These keys are str values that are unique for a Part within it's owner. Their purpose is to distinguish a single Part from multiple Parts on the same Snapper owner that may share the same flattenableKey but originate from different creators.

See documentation for FlattenedPartData for more information on how this value is used.

In cm/core/part/part.cm

/**
 * A unique key for a Part on it's owner.
 *
 * Ex. table legs flattened from 4 parts to 1
 * can have keys like {tableLeg0, tableLeg1, tableLeg2, tableLeg3};
 *
 * Utilize @partSourceId in @ProdPart to ensure uniqueness.
 */
extend public str partToOwnerKey() {
	return data.?articleCode();
}


/**
 * Unique keys for flattened Parts on owner.
 * 
 * Important if this part is flattened. Each key
 * should correspond to a distinct part included
 * in this flattened part.
 * 
 * Ex. table legs flattened from 4 parts to 1 
 * can have keys like {tableLeg0, tableLeg1, tableLeg2, tableLeg3};
 */
extend public str{} partToOwnerKeys() {
	str{} keys = {partToOwnerKey()};
	if (data as FlattenedPartData) keys += data.partToOwnerKeys();
	return keys;
}

New Part Attributes Interface

With the addition of the Part Attributes system and columns in Calculations, functions for easy access and modification to these values have been added to Part. The system for storing a Parts attributes is as follows:

  • a PropObj holds a PartAnnotationHolder in its propData under a constant str key (cAnnotationHolderKey)
    • the PropObj is typically a Part owner/creator (Snapper, Component, etc.)
  • a PartAnnotationHolder holds a map of str->(PartAnnotation[]) objects
    • the str keys are unique identifiers for a Part associated with the PropObj
    • the PartAnnotation[] values are the PartAnnotations associated with the Part
    • See documentation for PartAnnotationHolder
  • a PartAnnotation holds a str note and a str description value
    • These are displayed under the "Attribute Note" and "Attribute Description" columns for a line item in Calculations
    • See documentation for PartAnnotation

To support the new system, the following interface has been added in Part:

/***********************************************************************
 * PartAnnotation Interface
 ***********************************************************************/
 
/**
 * Keys for this parts annotations.
 */
extend public str annotationKey() { ... }


/**
 * Keys for this parts Annotation notes/descs.
 * Important if this part has been flattened.
 * Should contain unique keys for each individual flattened part.
 */
extend public str{} annotationKeys() { ... }


/**
 * Check if this Part contains any annotations.
 * @s PropObj owner of PartAnnotationHolder (defaults to @owner())
 * @returns true if Part has PartAnnotations; false if not
 */
extend public bool containsAnnotations(PropObj s=null) { ... }


/**
 * Get PartAnnotations for Part.
 * @s PropObj owner of PartAnnotationHolder (defaults to @owner())
 * @returns seq of PartAnnotation(s) associated with this Part
 */
extend public PartAnnotation[] getAnnotations(PropObj s=null) { ... }


/**
 * Append PartAnnotation to this Part.
 * @annotation PartAnnotation instance to append for Part
 * @s PropObj owner of PartAnnotationHolder (defaults to @owner())
 */
extend public void appendAnnotation(PartAnnotation annotation, PropObj s=null) { ... }


/**
 * Append PartAnnotation(s) to this Part.
 * @newAnnotations seq of PartAnnotation(s) to append
 * @s PropObj owner of PartAnnotationHolder (defaults to @owner())
 */
extend public void appendAnnotations(PartAnnotation[] newAnnotations, PropObj s=null) { ... }


/**
 * Set PartAnnotation(s) for this Part.
 * @newAnnotations seq of PartAnnotation(s) to set
 * @s PropObj owner of PartAnnotationHolder (defaults to @owner())
 */
extend public void setAnnotations(PartAnnotation[] newAnnotations, PropObj s=null) { ... }


/**
 * Remove PartAnnotation from this Part.
 * @annotation PartAnnotation instance to remove from Part
 * @s PropObj owner of PartAnnotationHolder (defaults to @owner())
 */
extend public void removeAnnotation(PartAnnotation annotation, PropObj s=null) { ... }


/**
 * Remove PartAnnotation(s) from this Part.
 * @annotations PartAnnotation seq to remove from Part
 * @s PropObj owner of PartAnnotationHolder (defaults to @owner())
 */
extend public void removeAnnotations(PartAnnotation[] annotations, PropObj s=null) { ... }


/**
 * Remove all PartAnnotations from this Part.
 * @s PropObj owner of PartAnnotationHolder (defaults to @owner())
 */
extend public void clearAnnotations(PropObj s=null) { ... }
    

class PartAnnotationHolder

To manage the new PartAnnotations for a PropObjs parts, the PartAnnotationHolder has been created. It holds a str->(PartAnnotation[]) map and has functions for accessing and manipulating this map.

The system for storing a Parts attributes is as follows:

  • a PropObj holds a PartAnnotationHolder in its propData under a constant str key (cAnnotationHolderKey)
    • the PropObj is typically a Part owner/creator (Snapper, Component, etc.)
  • a PartAnnotationHolder holds a map of str->(PartAnnotation[]) objects
    • the str keys are unique identifiers for a Part associated with the PropObj
    • the PartAnnotation[] values are the PartAnnotations associated with the Part
    • See documentation for PartAnnotationHolder
  • a PartAnnotation holds a str note and a str description value
    • These are displayed under the "Attribute Note" and "Attribute Description" columns for a line item in Calculations
    • See documentation for PartAnnotation

The str->(PartAnnotation[]) map contains:

  • str keys
    • uniquely identifiable keys for a Part on the PropObj storing the holder
    • these keys are typically generated from the Parts annotationKey()
  • PartAnnotation[] values
    • a sequence of PartAnnotation objects for the Part corresponding to the key

Simplified Interface

/**
 * Holder for multiple Parts PartAnnotation(s).
 * Typically stored on Part owner props.
 */
public class PartAnnotationHolder {

    /**
     * Map of unique Part key to PartAnnotation sequence,
     * Keys should be unique for Part on it's PropObj.
     */
    public str->(PartAnnotation[]) annotations;


    /**
     * Constructor.
     */
    public constructor() {}


    /**
     * Get PartAnnotation sequence for key.
     * @key key associated with PartAnnotation sequence
     * @returns seq of PartAnnotation(s) if found; null if not
     */
    extend public PartAnnotation[] getAnnotations(str key) { ... }


    /**
     * Append PartAnnotation for key.
     * @key key associated with PartAnnotation sequence
     * @annotation PartAnnotation instance to append
     */
    extend public void appendAnnotation(str key, PartAnnotation annotation) { ... }


    /**
     * Append PartAnnotation sequence for key.
     * @key key associated with PartAnnotation sequence
     * @newAnnotations seq of PartAnnotation(s) to to append
     */
    extend public void appendAnnotations(str key, PartAnnotation[] newAnnotations) { ... }


    /**
     * Set PartAnnotation sequence for key.
     * @key key associated with PartAnnotation sequence
     * @newAnnotations seq of PartAnnotation(s) to set
     */
    extend public void setAnnotations(str key, PartAnnotation[] newAnnotations) { ... }

    
    /**
     * Remove PartAnnotation for key.
     * @key key associated with PartAnnotation sequence
     * @annotation PartAnnotation instance to remove from Part
     */
    extend public void removeAnnotation(str key, PartAnnotation annotation) { ... }
    
    
    /**
     * Remove PartAnnotation sequence for key.
     * @key key associated with PartAnnotation sequence
     * @annotations PartAnnotation instances to remove from Part
     */
    extend public void removeAnnotations(str key, PartAnnotation[] annotations) { ... }


    /**
     * Remove all PartAnnotation sequence for key.
     * @key key associated with PartAnnotation sequence
     */
    extend public void clearAnnotations(str key) { ... }


    /**
     * Does holder contain any annotations?
     * @key optional key to specify seq to look for (default null)
     * @returns true if there are any annotations; false if not
     */
    extend public bool any(str key=null) { ... }


    /**
     * Is holder empty of annotations?
     * @key optional key to specify seq to look for (default null)
     * @returns true if there are no annotations; false if there are
     */
    extend public bool empty(str key=null) { ... }
}

class PartAnnotation

To align with the Spec application columns, new Attribute Note and Attribute Description columns have been introduced in Calculations. A single Part line may include multiple "attributes", each with a note and description. To encapsulate these values in a consistent and reusable way, a new type called PartAnnotation has been created.

Naming Note

The class is named PartAnnotation rather than PartAttribute. While the Spec and Calculations columns use the term attribute, that term was found to be misleading for these objects. After discussion, annotation was chosen as the clearer term to reflect their purpose.

Interface

/**
 * Representation of an annotation for a Part.
 * Utilized in Attribute Note/Description columns
 * in Calculations dialog and exported with SIF/PMX/OFDA exports.
 */
public class PartAnnotation {

    /**
     * Note for the annotation.
     */
    public str note;

    /**
     * Description for the annotation.
     */
    public str description;

    /**
     * Constructor.
     */
    public constructor auto();

    /**
     * Empty constructor.
     */
    public constructor() {}


    /**
     * Key for the annotation.
     */
    extend public str key() {
        return note # ':' # description;
    }


    /**
     * Return string representation of object.
     */
    public str toS() {
        return key();
    }
}

Part Attributes functions (cm/core/part/functions.cm)

With the addition of the Part Attributes system and columns in Calculations, functions for easy access and modification to these values have been added. The system for storing a Parts attributes is as follows:

  • a PropObj holds a PartAnnotationHolder in its propData under a constant str key (cAnnotationHolderKey)
    • the PropObj is typically a Part owner/creator (Snapper, Component, etc.)
  • a PartAnnotationHolder holds a map of str->(PartAnnotation[]) objects
    • the str keys are unique identifiers for a Part associated with the PropObj
    • the PartAnnotation[] values are the PartAnnotations associated with the Part
    • See documentation for PartAnnotationHolder
  • a PartAnnotation holds a str note and a str description value
    • These are displayed under the "Attribute Note" and "Attribute Description" columns for a line item in Calculations
    • See documentation for PartAnnotation

Each function below will get the PartAnnotationHolder from the passed in PropObj and perform the function operation on the holder. Each function requires a PropObj parameter and some additionally require a str key and/or PartAnnotation(s) depending on the operation being performed (get, append, remove, etc.)

Simplified Interface

/***************************************************************************
 * PartAnnotation interface
 ***************************************************************************/

/**
 * Initialize PartAnnotationHolder for PropObj.
 * @s PropObj holding PartAnnotationHolder
 * @return PartAnnotationHolder instance containing PartAnnotations
 */
public PartAnnotationHolder initAnnotationHolder(PropObj s) { ... }


/**
 * Get PartAnnotationHolder from PropObj.
 * @s PropObj holding PartAnnotationHolder
 * @return PartAnnotationHolder instance containing PartAnnotations
 */
public PartAnnotationHolder getAnnotationHolder(PropObj s) { ... }

/**
 * Remove PartAnnotationHolder from PropObj.
 * Removes all PartAnnotations associated with PropObj @s
 * @s PropObj holding PartAnnotationHolder
 */
public void removeAnnotationHolder(PropObj s) { ... }


/**
 * Get Annotations for key from PropObj.
 * @s PropObj holding PartAnnotationHolder
 * @key str key of owner of annotations (usually a part key)
 * @return seq of PartAnnotation(s) associated with @s And @key
 */
public PartAnnotation[] getAnnotations(PropObj s, str key) { ... }


/**
 * Append annotation to PropObj @s for @key.
 * @s PropObj holding PartAnnotationHolder
 * @key str key of owner of annotations (usually a part key)
 * @annotation PartAnnotation instance to append to sequence
 */
public void appendAnnotation(PropObj s, str key, PartAnnotation annotation) { ... }


/**
 * Append annotations to PropObj @s for @key.
 * @s PropObj holding PartAnnotationHolder
 * @key str key of owner of annotations (usually a part key)
 * @annotations seq of PartAnnotation(s) to append to sequence
 */
public void appendAnnotations(PropObj s, str key, PartAnnotation[] annotations) { ... }


/**
 * Set annotations for @key on PropObj @s.
 * @s PropObj holding PartAnnotationHolder
 * @key str key of owner of annotations (usually a part key)
 * @annotations PartAnnotation instance to set for @key
 */
public void setAnnotations(PropObj s, str key, PartAnnotation[] annotations) { ... }

/**
 * Remove annotation for @key on PropObj @s.
 * @s PropObj holding PartAnnotationHolder
 * @key str key of owner of annotations (usually a part key)
 * @annotations PartAnnotation instance to remove for @key
 */
public void removeAnnotation(PropObj s, str key, PartAnnotation annotation) { ... }


/**
 * Remove annotations for @key on PropObj @s.
 * @s PropObj holding PartAnnotationHolder
 * @key str key of owner of annotations (usually a part key)
 * @annotations PartAnnotation instances to remove for @key
 */
public void removeAnnotations(PropObj s, str key, PartAnnotation[] annotations) { ... }


/**
 * Remove all annotations for @key on PropObj @s.
 * @s PropObj holding PartAnnotationHolder
 * @key str key of owner of annotations (usually a part key)
 */
public void clearAnnotations(PropObj s, str key) { ... }

cm.core.part.attributes

class PartAttributeDescriptionColumn

With the new Attribute Description column in Calculations, the class PartAttributeDescriptionColumn has been made. It extends the new PartAttributeColumn base class and only overrides the public Object value(..) function to provide a Parts attribute description values.

Interface

/**
 * Part Attribute Description column.
 */
public class PartAttributeDescriptionColumn extends PartAttributeColumn {

    /**
     * Constructor.
     */
    public constructor() {
		super("attributeDescription");
    }
    

    /**
     * Return property value for part.
     */
    public Object value(Part part, Space space) {
        PartAnnotation[] attributes = annotations(part);
        if (attributes.empty()) return null;

        str[] descs();
        for (att in attributes) {
            descs << att.description;
        }
        return descs;
    }
}

class PartAttributesDataWindow

With the new Part Attributes Dialog (PartAttributesDialog), the subwindow for the data (middle) portion of the dialog has been made (PartAttributesDataWindow). It extends the new PartAttributesSubWindow base class overiding the initControls and alignControls functions.

This data subwindow contains the dialogs controls for:

  • The grid of part attributes (ScrollableGridWindow attributesGrid)
  • A subwindow with controls for editing the grids rows (SubWindow gridControlsSW)

Interface

/**
 * Data window for Part Attributes Dialog.
 * Holds grid and grid controls window.
 */
public class PartAttributesDataWindow extends PartAttributesSubWindow {

    /**
     * Part attributes grid.
     */
    public ScrollableGridWindow attributesGrid;

    /**
     * Grid controls subwindow.
     */
    public SubWindow gridControlsSW;


    /***********************************************************************
     * Initialization
     ***********************************************************************/
    
    /**
     * Initialize controls.
     */
    public void initControls() { ... }


    /**
     * Initialize grid.
     */
    extend public ScrollableGridWindow initGrid() { ... }


    /**
     * Intialize grid controls.
     */
    extend public SubWindow initGridControls() { ... }


    /**
     * Align controls.
     */
    public void alignControls() { ... }


    /**
     * Update grid column sizes and scrollbars.
     */
    extend public void updateGrid() { ... }


    /**
     * Update grid column widths.
     * Stretches columns to fit grid inner width.
     */
    extend public void updateColumnSizes() { ... }


    /**
     * Get grid size.
     */
    extend public sizeI gridSize() { ... }


    /**
     * Get grid width within first select column
     * and vertical scroll bar.
     */
    extend public int innerGridWidth() { ... }

    
    /***********************************************************************
     * Grid interface
     ***********************************************************************/

    /**
     * Get currently selected row.
     */
    extend public int selectedRow() { ... }


    /**
     * Get current row count.
     */
    extend public int rowCount() { ... }


    /**
     * Append row to end of grid.
     */
    extend public void appendRow() { ... }
    

    /**
     * Insert row into selected row of grid.
     */
    extend public void insertRow() { ... }


    /**
     * Remove selected row from grid.
     */
    extend public void removeRow() { ... }
    

    /**
     * Remove selected row from grid.
     */
    extend public void removeAllRows() { ... }


    /**
     * Check if row can be removed.
     */
    extend public bool canRemove(int row) { ... }


    /***********************************************************************
     * Collect data
     ***********************************************************************/

    /**
     * Collect PartAnnotation sequence from grid.
     * @returns PartAnnotation[] sequence from grid, skipping empty rows
     */
    extend public PartAnnotation[] collectAttributes() { ... }


    /**
     * Create a PartAnnotation for the row index.
     * @returns PartAnnotation for row; null if failed or row is empty
     */
    extend public PartAnnotation createRowAttribute(int row) { ... }
    

    /***********************************************************************
     * Events
     ***********************************************************************/

    /**
     * Dialog is stale. Update this window.
     */
    extend public void markStale() { ... }


    /**
     * Grid selected event.
     * Updates enablement of grid controls.
     */
    extend public void gridSelected() { ... }


    /**
     * Toggle enablement for grid controls.
     */
    extend public void toggleControlsEnablement() { ... }


    /**
     * Control callback.
     * @c Control instance prompting callback
     */
    extend public void controlCb(Control c) { ... }
}

class PartAttributesTopWindow

With the new Part Attributes Dialog (PartAttributesDialog), the subwindow for the top portion of the dialog has been made (PartAttributesTopWindow ). It extends the new PartAttributesSubWindow base class overiding the initControls and alignControls functions.

This subwindow contains the dialogs controls for:

  • A label indicating which Part the dialog is opened for (Display partInfoLabel)

Interface

/**
 * Part Attributes Dialog top GroupBox.
 * Holds part information display.
 */
public class PartAttributesTopWindow extends GroupBox {

    /**
     * Part Information display.
     */
    public Display partInfoLabel;


    /***********************************************************************
     * Initialization
     ***********************************************************************/

    /**
     * Initialize controls.
     */
    extend public void initControls() {
        partInfoLabel = Display(parent=this, key="partInfoLabel", label=getPartInfoText());
        partInfoLabel.autoSize();
        autoSize();
    }


    /**
     * Align controls.
     */
    extend public void alignControls() {
        partInfoLabel.alignCenter();
    }


    /**
     * Get text for part information display.
     * Default: part number : part description
     */
    extend public str getPartInfoText() {
        Part part = partAttributesDialog.?part;
        if (!part) return null;
        return concat(part.articleCode(), " : ", part.description());
    }
}

class PartAttributesGridCell

With the creation of the new PartAttributesDialog and its grid, the grid cell type PartAttributesGridCell has been created. The only purpose of this cell is for cosmetic reasons: to use a default whiteBrush for the cell background during draw(..), to shorten cell text that extends the cell width, and for adding tooltip text.

Simplified Interface

/**
 * Grid cell for grid in PartAttributesDialog.
 * 
 * Overidden to shorten cell text, use whiteBrush in @draw,
 * and for tooltip text.
 */
public class PartAttributesGridCell extends TextInputGridCell {

   /**
     * Draw.
     */
    public bool draw(PixelDevice dc, GridWindow gw, rectI r, int x, int y) { ... }


    /**
     * Clipped text for cell.
     * Only clipped if text exceeds cell width.
     */
    extend public str clipText(rectI r) { ... }


    /**
     * Return desired tool tip text.
     */
    public str toolTipText() { ... }
}

hooks.cm File

Part Attributes Dialog Interface

With the addition of the new PartAttributesDialog, the following hooks have been added in cm/core/part/attributes/hooks.cm to handle the life cycle of PartAttributesDialogs.

/**
 * Initialize global hooks for PartAttributesDialog
 */
public void initGlobalPartAttributesHooks() {
	setMainSpaceHooks << function attributeDialogSetMainSpaceHook;
    putSelectWorldHook(function attributeDialogSelectWorldHook);
    appendPostFinalizeAfterMergeHook(function checkForStalePartAttributesDialogs);
}


/**
 * Remove global hooks for PartAttributesDialog
 */
public void removeGlobalPartAttributesHooks() { ... }

            
/**            
 * Set main space hook.
 * 
 * This hook runs when a new main space is selected (alternatives).
 * It will close all active PartAttributesDialogs.
 */
package void attributeDialogSetMainSpaceHook(World world) { ... }

                               
/**
 * Select world hook.
 * 
 * This hook runs when a new World is selected.
 * It will close all active PartAttributesDialogs.
 */
package void attributeDialogSelectWorldHook(World world) { ... }


/**
 * Post Finalize Post Merge hook.
 * Updates dialogs with stale data.
 */
package Part[] checkForStalePartAttributesDialogs(Part[] list) { ... }

class PartAttributesGridControlsWindow

With the new Part Attributes Dialog (PartAttributesDialog), a subwindow to hold controls for the grid in the dialog has been made (PartAttributesGridControlsWindow). It extends the new PartAttributesSubWindow base class overiding the initControls and alignControls functions.

This subwindow contains controls for:

  • Appending rows to the dialogs grid (Button addButton),
  • inserting rows into the dialogs grid (Button insertButton), and
  • removing rows from the dialogs grid (Button removeButton).

Simplified Interface

/**
 * Controls window for Part Attributes Dialog grid.
 * Holds controls for manipulating grid.
 */
public class PartAttributesGridControlsWindow extends PartAttributesSubWindow {
    
    /**
     * Add button.
     */
    public Button addButton;

    /**
     * Insert button.
     */
    public Button insertButton;

    /**
     * Remove button.
     */
    public Button removeButton;
    

    /***********************************************************************
     * Initialization
     ***********************************************************************/

    /**
     * Initialize controls
     */
    public void initControls() { ... }


    /**
     * Align controls
     */
    public void alignControls() { ... }


    /***********************************************************************
     * Data Window Interface
     ***********************************************************************/
    
    /**
     * Parent PartAttributesDataWindow.
     */
    final public PartAttributesDataWindow dataWindow() { ... }


    /**
     * Append row to grid.
     */
    extend public void appendRow() { ... }


    /**
     * Insert row in grid.
     */
    extend public void insertRow() { ... }


    /**
     * Remove row from grid.
     */
    extend public void removeRow() { ... }


    /***********************************************************************
     * Events
     ***********************************************************************/

    /**
     * Control callback.
     * @c Control instance prompting callback
     */
    extend public void controlClicked(str controlKey) { ... }


    /**
     * Toggle controls enablement.
     */
    extend public void toggleEnablement(bool toggleOn) { ... }
}

class PartAttributesBottomWindow

With the new Part Attributes Dialog (PartAttributesDialog), the subwindow for the bottom portion of the dialog has been made (PartAttributesBottomWindow). It extends the new PartAttributesSubWindow base class overiding the initControls and alignControls functions.

This subwindow contains the dialogs controls for:

  • Applying changes in the dialog (Button okButton),
  • Canceling the interaction with the dialog (Button closeButton),
  • Indicating to the user that the dialogs data has gone stale (Display dialogStaleLbl)

Interface

/**
 * Part Attributes Dialog bottom SubWindow.
 * Holds OK and Close buttons
 */
public class PartAttributesBottomWindow extends PartAttributesSubWindow {

    /**
     * OK button
     */
    public Button okButton;

    /**
     * Close button
     */
    public Button closeButton;

    /**
     * Stale data label.
     */
    public Display dialogStaleLbl;


    /***********************************************************************
     * Initialization
     ***********************************************************************/

    /**
     * Initialize controls.
     */
    public void initControls() { ... }


    /**
     * Align controls.
     */
    public void alignControls() { ... }


    /**
     * Dialog is stale. Update this window.
     */
    extend public void markStale() { ... }


    /***********************************************************************
     * Events
     ***********************************************************************/

    /**
     * Control callback.
     * @c Control instance prompting callback
     */
    extend public void controlCb(Control c) { ... }
}

class PartAttributeColumn

With the new Attribute Note and Attribute Description columns in Calculations, the base class PartAttributeColumn has been made. It provides the new PartAttributeGridCell and common functionality for the new PartAttributeNoteColumn and PartAttributeDescriptionColumn subclasses.

Interface

/**
 * Part Attribute Column
 */
public class PartAttributeColumn extends BasicPartColumn {

    /**
     * Initial Visibility.
     */
    public symbol[] initialVisibility() { ... }


    /**
     * Get part annotations.
     */
    final public PartAnnotation[] annotations(Part part) { ... }


    /**
     * Return property value for part..
     */
    public Object value(Part part, Space space) { ... }


    /**
     * Return output.
     */
    public str output(Part part, PartCache cache, Space space) { ... }

    
    /**
     * Get GridCell for column
     */
    public GridCell gridCell(Part part, Space space, PartCache cache, symbol view=null) { ... }
    
    
    /**
     * Get tooltip text for grid cell.
     */
    extend public str tooltipText(Part part, Space space) { ... }
}

class PartAttributesDialog

With the new Attribute Note and Attribute Description columns in Calculations, a new dialog (PartAttributesDialog) has been made to allow for viewing and modifying the attribute note and description values on a Part.

This dialog window contains:

  • a private Part _part field owning the attribute Data
  • a swappable private PartAttributesDialogBehavior _behavior defining the dialogs user interface and experience
    • All subwindows are initialized with the behavior
    • All events from subwindows and their controls are handled by the behavior
    • See documentation for PartAttributesDialogBehavior for more
  • 3 private : public readable subwindows for the dialogs sections:
    • SubWindow topWindow
    • SubWindow dataWindow
    • SubWindow bottomWindow
  • Two events for open/close:
    • event shownEvent
    • event closedEvent

Simplified Interface

/**
 * Part Attributes Dialog.
 * Displays and allows manipulation of PartAnnotation(s) for a Part.
 */
public class PartAttributesDialog extends DialogWindow {

    /***********************************************************************
     * Fields
     ***********************************************************************/

    private Part _part;
    private PartAttributesDialogBehavior _behavior;
    private SubWindow topWindow : public readable;
    private SubWindow dataWindow : public readable;
    private SubWindow bottomWindow : public readable;
    public event shownEvent;
    public event closedEvent;

    /***********************************************************************
     * Construction
     ***********************************************************************/

    public constructor(Part part, Window parent) { ... }
    public constructor(Part part, PartAttributesDialogBehavior behavior, Window parent) { ... }

    /***********************************************************************
     * Field Access
     ***********************************************************************/

    final public Part part() { ... }
    final public PartAttributesDialogBehavior setBehavior(PartAttributesDialogBehavior value, bool rebuild=false) { ... }
    final public PartAttributesDialogBehavior behavior() { ... }
    final public PartAnnotation[] attributes() { ... }
    
    /***********************************************************************
     * Build/Rebuild/Init
     ***********************************************************************/
    
    final public void initialize() { ... }
    extend public void build() { ... }
    extend public void initControls() { ... }
    extend public void alignControls() { ... }
    public void rebuild() { ... }
    extend public void checkStaleStatus() { ... }

    /***********************************************************************
     * Data interface
     ***********************************************************************/

    extend public void applyChanges() { ... }
    extend public PartAnnotation[] collectAttributes() { ... }

    /***********************************************************************
     * Events
     ***********************************************************************/
    
	public void shown(bool v) { ... }
    public void removeWindow()  {... }
}

class PartAttributesDialogBehavior

With the new PartAttributesDialog, a new behavior (PartAttributesDialogBehavior) has been to handle the creation and event callbacks of it's UI elements. All of its interface is extendable and take in a PartAttributesDialog as a parameter. This makes the behavior fully customizable and static (a single behavior can host multiple dialogs).

The behavior contains:

  • Interface for initalizing PartAttributesDialogs SubWindows
    • initDialogEvents(PartAttributesDialog dialog)
    • initTopWindow(PartAttributesDialog dialog)
    • initDataWindow(PartAttributesDialog dialog)
    • initBottomWindow(PartAttributesDialog dialog)
    • alignControls(PartAttributesDialog dialog)
  • Interface for handling different hooks:
    • checkStaleStatus(PartAttributesDialog dialog)
      • checked during postFinalizeAfterMerge hooks
      • See checkForStalePartAttributesDialogs in cm/core/part/attributes/hooks.cm
  • Interface for handling dialog events:
    • onDialogShown(Object sender, Object args)
    • onDialogClosed(Object sender, Object args)
    • In these cases, Object sender should always be a PartAttributesDialog

NOTE: The standard and default behavior for a PartAttributesDialog is defined in cm/core/part/attributes/partAttributesDialogBehavior.cm as corePartAttributesBehavior. Unless customization is needed, this one should be used.

Simplified Interface

/**
 * Part Attributes Dialog Behavior.
 * Defines UI elements and their behavior in a PartAttributesDialog.
 * Designed to be static (aka one behavior can power multiple dialogs)
 */
public class PartAttributesDialogBehavior {

    /**
     * Empty Constructor.
     */
    public constructor() {}


    /**
     * Pre-Initialize of the PartAttributesDialog.
     * Called at start of @initialize in PartAttributesDialog
     * @dialog PartAttributesDialog about to initialize.
     */
    extend public void preInit(PartAttributesDialog dialog) { ... }


    /**
     * Post-Initialize of the PartAttributesDialog.
     * Called at end of @initialize in PartAttributesDialog
     * @dialog PartAttributesDialog that just initialized.
     */
    extend public void postInit(PartAttributesDialog dialog) {}


    /**
     * Before behavior change in dialog.
     * @dialog PartAttributesDialog changing behavior
     */
    extend public void beforeBehaviorChange(PartAttributesDialog dialog) { ... }


    /**
     * Unregister events events from PartAttributesDialog
     * @dialog PartAttributesDialog to remove events from
     */
    extend public void removeDialogEvents(PartAttributesDialog dialog) { ... }


    /***********************************************************************
     * Initialization/Alignment
     ***********************************************************************/

    /**
     * Initialize events for PartAttributesDialog.
     * @dialog PartAttributesDialog to init events for
     */
    extend public void initDialogEvents(PartAttributesDialog dialog) { ... }


    /**
     * Initialize top window.
     * @dialog PartAttributesDialog to initialize top window for
     * @returns SubWindow representing the top window in the dialog
     */
    extend public SubWindow initTopWindow(PartAttributesDialog dialog) { ... }


    /**
     * Initialize data window.
     * @dialog PartAttributesDialog to initialize data window for
     * @returns SubWindow representing the data (middle) window in the dialog
     */
    extend public SubWindow initDataWindow(PartAttributesDialog dialog) { ... }


    /**
     * Initialize bottom window.
     * @dialog PartAttributesDialog to initialize bottom window for
     * @returns SubWindow representing the bottom window in the dialog
     */
    extend public SubWindow initBottomWindow(PartAttributesDialog dialog) { ... }


    /**
     * Align controls within the dialogs SubWindows.
     * Called from @alignControls in @PartAttributesDialog.
     *
     * NOTE: This function is to handle alignment/sizing WITHIN the SubWindows.
     * Dialog will align the SubWindows itself.
     */
    extend public void alignControls(PartAttributesDialog dialog) { ... }


    /**
     * Check if data is stale and update UI accordingly.
     * @dialog Dialog with potentially stale data.
     */
    extend public void checkStaleStatus(PartAttributesDialog dialog) { ... }


    /**
     * Check if dialog data is stale.
     * @dialog Dialog with potentially stale data.
     * @returns true if data in dialog is stale; false if not.
     */
    extend public bool isStale(PartAttributesDialog dialog) { ... }


    /***********************************************************************
     * Hooks
     ***********************************************************************/

    /**
     * Initialize hooks for a PartAttributesDialog instance.
     * @dialog PartAttributesDialog to initialize hooks for
     */
    extend public void initHooks(PartAttributesDialog dialog) {}


    /**
     * Remove hooks for a PartAttributesDialog instance.
     * @dialog PartAttributesDialog to remove hooks from.
     */
    extend public void removeHooks(PartAttributesDialog dialog) {}


    /************************************************************
     * Dialog Event observers (responses to invoked events)
     ************************************************************/

    /**
     * Handles dialog shown event from a PartAttributesDialog.
     * @sender Source of the event
     * @args Event arguments
     */
    extend public void onDialogShown(Object sender, Object args) { ... }


    /**
     * Handles dialog closed event from a PartAttributesDialog.
     * @sender Source of the event
     * @args Event arguments
     */
    extend public void onDialogClosed(Object sender, Object args) { ... }
}

class PartAttributeNoteColumn

With the new Attribute Note column in Calculations, the class PartAttributeNoteColumn has been made. It extends the new PartAttributeColumn base class and only overrides the public Object value(..) function to provide a Parts attribute note values.

Interface

/**
 * Part Attribute Note column.
 */
public class PartAttributeNoteColumn extends PartAttributeColumn {

    /**
     * Constructor.
     */
    public constructor() {
	super("attributeNote");
    }


    /**
     * Return property value for part.
     */
    public Object value(Part part, Space space) {
        PartAnnotation[] attributes = annotations(part);
        if (attributes.empty()) return null;

        str[] notes();
        for (att in attributes) {
            notes << att.note;
        }
        return notes;
    }
}

class PartAttributeCalcGridCell

With the creation of the new PartAttributesDialog and PartAttributeColumns in Calculations, this grid cell type has been created. The only purpose of this cell is to open a PartAttributesDialog on double-click of the cell. It is used for the new PartAttributeColumns.

Interface

/**
 * Grid cell for a Part Attribute Note/Description Columns.
 */
public class PartAttributeCalcGridCell extends CalcGridCell {

    /**
     * Open cell.
     * Opens PartAttributesDialog for Part line.
     */
    public bool open(GridWindow gw) { ... }
}

class PartAttributesGridBuilderEnv

With the new PartAttributesDialog, a PartAttributesGridBuilderEnv has been made to aid in the building of the dialog's grid. The dialog uses a GridBuilder that is supplied the new PartAttributesGridBuilderEnv to provide the specific columns and GridCells appropriate for the part attribute data.

Use Case

In cm/core/part/attributes/partAttributesDataWindow.cm

/**
 * Initialize grid.
 */
extend public ScrollableGridWindow initGrid() {
	ScrollableGridWindow grid(parent=this, key=cPartAttributeGridKey, callback=function dataWindowControlCb);

	GridBuilder builder(env=PartAttributesGridBuilderEnv());
	?Object[] attributes = partAttributesDialog.?attributes();
	builder.populateGridWindow(attributes, grid);

	return grid;
}

The builder environment provides:

  • The Attribute Note and Attribute Description for the grid columns
  • A sequence of PartAttributesGridCells for the grid rows

Interface

/**
 * Env for building PartAnnotation(s) grid in
 * PartAttributesDialog.
 */
public class PartAttributesGridBuilderEnv extends GridBuilderEnv {

    /**
     * Columns labels to display in the grid.
     * @return A seq of column names.
     */
    public str[] columns() {
        return [$attributeNotePartColumnLabel, $attributeDescriptionPartColumnLabel];
    }
    

    /**
     * Generates the cells for a single row.
     * @data The object representing the row data.
     * @return A seq of GridCells for the row.
     */
    public GridCell[] getRowCells(Object data) {
        if (data as PartAnnotation) {
            PartAttributesGridCell noteCell(data.note, align=alignment.left);
            PartAttributesGridCell descCell(data.description, align=alignment.left);
            return [GridCell: noteCell, descCell];
        }
        return null;
    }
}

functions.cm

Part Attributes Dialog Interface

With the addition of the new PartAttributesDialog, simple interface for opening a Parts attributes dialog as well as some helper functions have been added in cm/core/part/attributes/functions.cm.

Notable interfaces
/**
 * Get PartAttributesDialog instance for a Part
 * (if none are already active).
 * @part Part for dialog
 * @parent parent window of PartAttributesDialog
 */
public PartAttributesDialog partAttributesDialog(Part part, Window parent=session.mainWindow()) { ... }


/**
 * Get PartAttributesDialog instance for a PropObj
 * (if none are already active).
 * @env PartAttributesDialogDataEnv containing PropObj owner
 * @behavior PartAttributesDialogBehavior for PartAttributesDialog
 * @parent parent window of PartAttributesDialog
 * @dialogLabel label for PartAttributesDialog
 */
public PartAttributesDialog partAttributesDialog(Part part, PartAttributesDialogBehavior behavior, Window parent=session.mainWindow()) { ... }


/**
 * Currently active PartAttributesDialogs.
 */
public PartAttributesDialog{} activePartAttributesDialogs() { ... }


/**
 * Get active PartAttributesDialog for Part.
 * Compares flattenableKeys for matching.
 * @part Part instance to get active dialog for
 */
public PartAttributesDialog getActivePartAttributesDialog(Part part) { ... }

cm.core.partTag

# PartTagDialog
Added: extend public void rebuildLabel(PartTagTree tree, bool isInCollWorldG2=false) 

# PTCategoryItem 
Added: extend public void rebuildLabel(PartTagTree tree, bool isInCollWorldG2=false) 

# PTTagItem 
Added: extend public void rebuildLabel(PartTagTree tree, bool isInCollWorldG2=false) 

cm.test.cmunit.testInstructions

class ExplodeBlockInstruction

Explodes a specified block snapper. It is intended to be used in combination with CreateBlockInstruction.

class GetTreeViewItemsInstruction

Gets one or more tree view items from a tree view and writes them to the blackboard. It can get all tree view items, visible tree view items, tree view items of a certain type, tree view item with exact key or by index.

This replaces the old GetTreeViewItemInstruction and GetTreeViewItemAtIndexInstruction.

class SetUserSnapEnabledInstruction

Enables or disables the global user snap setting. By default, all instruction based test cases have user snapping enabled.

class SetUserSelectInstruction

Allows setting the global user select setting to either single select or group select. By default, all instruction based test cases use group select.

cm.win

CM EdgeWebView2

In 16.5, we are introducing CM EdgeWebView2, which allows the usage of embedded web technologies natively in client applications. CET Core will ship with the necessary EdgeWebView2 binaries and dependencies, allowing developers to simplify deployment. Refer to cm/win/edgeWebViewApis.cm for usage information and configuration options.

The current implementation only supports displaying web pages, and does not allow for interop or handling events.

IconDB and IconFinder

We have added functionality to load icons as DibImage. This prevents CET from consuming GDI bitmap objects to load an icon. It is currently opt-in, you just need to replace the following interfaces:

Old: public Image icon(str name, symbol key=#default, bool debug=false) {
New: public Image dibIcon(str name, symbol key=#default, bool debug=false) {

Old: public Image disabledIcon(str name, symbol key=#default) {
New: public Image disabledDibIcon(str name, symbol key=#default) {

Here is are the required changes to migrate Fika to use dibIcon. You may refer to the Runtime/Behavior change section for IconFinder if you find some icons are not showing up correctly in your extension.

package Image foIcon(str image) {
    symbol k = (#fikaOffice);
    putIconDbExtensionDir(k, "custom/fika/office/res/images/");
    // return icon(image, k);  // Old 16.0 logic
    return dibIcon(image, k);  // New 16.5 logic
}

CustomTitleBar

The CustomTitleBar now supports the following new features:

  • Replace the caption title with a custom Window on the left
  • Hiding the default caption title label
  • Hiding the Maximize and Minimize buttons

DialogWindow

A new parameter tbConfig is added to DialogWindow, ResizableDialogWindow to allow creation of a window with a custom caption title bar.

DropDownMenu

A new parameter enforceMinimumWidth is added to DropDownMenu to control the auto width sizing behavior of context menus.

class GridBuilderEnv

The GridBuilderEnv is the companion to GridBuilder. It defines what the grid looks like (columns, cells, and lifecycle hooks), while GridBuilder handles how the grid is populated.

Subclass GridBuilderEnv to adapt a grid for a specific type of data.

Purpose

  • Encapsulates grid logic for a data type.
  • Defines column labels and how rows are generated.
  • Provides lifecycle hooks before/after grid or row building.

Key Methods

  • columns() → returns the column labels (abstract)
  • getRowCells(data) → builds the GridCells for a row (abstract)
  • beforeBuildGrid(data, grid) → run before grid population
  • afterBuildGrid(data, grid) → run after grid population (defaults to resizing/scrolling updates)
  • beforeBuildRow(data, index, grid) → run before a row is built
  • afterBuildRow(data, row, grid) → run after a row is built

Example Workflow

  1. Subclass GridBuilderEnv
  2. Implement columns() and getRowCells(data)
  3. Pass your env into a GridBuilder
  4. Use the builder to populate a GridWindow

Benefits

  • Keeps grid configuration separate from grid population.
  • Provides clear extension points for custom behavior.
  • Ensures consistency across different grids.

Simplified Interface

/**
 * GridBuilderEnv
 * Env for a GridBuilder. 
 * 
 * Subclass to handle certain types.
 */
public class GridBuilderEnv : abstract {

    /**
     * Default constructor.
     */
    public constructor() {}


    /**
     * Columns labels to display in the grid.
     * @return A seq of column names.
     */
    extend public str[] columns() : abstract {}
    

    /**
     * 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) : abstract {}


    /**
     * Before build grid call.
     * Called in @GridBuilder BEFORE @populateColumns and @populateRows
     * Optional processing before beginning grid building.
     * @data The data sequence building the grid.
     * @grid The grid window being built.
     */
    extend public void beforeBuildGrid(Object[] data, GridWindow grid) {}


    /**
     * After build grid call.
     * Called in @GridBuilder AFTER @populateColumns and @populateRows
     * Optional processing after building grid.
     * @data The data sequence building the grid.
     * @grid The grid window being built.
     */
    extend public void afterBuildGrid(Object[] data, GridWindow grid) { ... }


    /**
     * Before build row call.
     * Called in @GridBuilder BEFORE @populateRow
     * Optional processing before building a grid row.
     * @data The data object building the row.
     * @index The index of the @data in the original data sequence
     * @grid The grid window being built.
     */
    extend public void beforeBuildRow(Object data, int index, GridWindow grid) {}


    /**
     * After build row call.
     * Called in @GridBuilder AFTER @populateRow
     * Optional processing after building a grid row.
     * @data The data object building the row.
     * @row The row index of the @data in the grid
     * @grid The grid window being built.
     */
    extend public void afterBuildRow(Object data, int row, GridWindow grid) {}
}

class GridBuilder

The GridBuilder class provides a base for constructing and populating GridWindows. It works together with a GridBuilderEnv, which defines how data maps to columns and rows. This separates grid setup (builder) from grid logic (env).

Purpose

  • Standardizes grid population
  • Avoids repeating base functionality when adding columns and rows
  • Makes it easy to extend behavior for different object types

Key Ideas

  • Env: GridBuilderEnv defines column labels and row-cell mapping
  • Columns: Provided by columns(env)
  • Rows: Built by getRowCells(data, env)
  • Population: populateGridWindow, populateRows, and populateRow handle filling a grid from data

Example Workflow

  • Subclass GridBuilderEnv to define columns and row-cell mapping
  • Create a GridBuilder with that env.
  • Call populateGridWindow(data, grid) to build the full grid

Simplified Interface

/**
 * GridBuilder populates a GridWindow
 * using a given Object sequence. 
 * 
 * Subclass GridBuilderEnv to enhance
 * for specific types.
 */
public class GridBuilder {

    /**
     * Optional cached env for the GridBuilder.
     */
    public GridBuilderEnv env;


    /**
     * Constructor.
     * @env (optional) The env for the grid builder.
     */
    public constructor(GridBuilderEnv env=null) { ... }


    /***********************************************************************
     * Env interface.
     ***********************************************************************/

    /**
     * Defines the columns for the grid. 
     * @env (optional) The env (overrides cached env).
     * @return A seq of column labels.
     */
    extend public str[] columns(GridBuilderEnv env=null) { ... }
    
    
    /**
     * Generates the cells for a single row.
     * @data The object representing the row data.
     * @env (optional) The env (overrides cached env).
     * @return A seq of GridCells for the row.
     */
    extend public GridCell[] getRowCells(Object data, GridBuilderEnv env=null) { ... }


    /***********************************************************************
     * Populating GridWindow
     ***********************************************************************/

    /**
     * Populates a GridWindow with data.
     * @data The data sequence to populate the grid with.
     * @grid The grid window to populate.
     * @env (optional) The env (overrides cached env).
     */
    extend public void populateGridWindow(Object[] data, GridWindow grid, GridBuilderEnv env=null) { ... }


    /**
     * Populates a GridWindow with data.
     * @data The data set to populate the grid with.
     * @grid The grid window to populate.
     * @env (optional) The env (overrides cached env).
     */
    extend public void populateGridWindow(Object{} data, GridWindow grid, GridBuilderEnv env=null) { ... }


    /**
     * Populates the grid columns.
     * @grid The grid window to add columns to.
     * @env (optional) The env (overrides cached env).
     */
    extend public void populateColumns(GridWindow grid, GridBuilderEnv env=null) { ... }
    
    
    /**
     * Populates the grid with rows based on the given data seq.
     * @data The data seq to populate rows with.
     * @grid The grid window to populate.
     * @env (optional) The env (overrides cached env).
     */
    extend public void populateRows(Object[] data, GridWindow grid, GridBuilderEnv env=null) { ... }
    
    
    /**
     * Populates the grid with rows based on the given data set.
     * @data The data set to populate rows with.
     * @grid The grid window to populate.
     * @env (optional) The env (overrides cached env).
     */
    extend public void populateRows(Object{} data, GridWindow grid, GridBuilderEnv env=null) { ... }


    /**
     * Populates a single row in the grid using the provided data object.
     * @data The object representing the row data.
     * @grid The grid window to populate.
     * @env (optional) The env (overrides cached env).
     * @return The row index where data was inserted.
     */
    extend public int populateRow(Object data, GridWindow grid, GridBuilderEnv env=null) { ... }
	

    /**
     * Appends a row to the grid with the given GridCell array.
     * @rowCells The array of cells to populate the row with.
     * @grid The grid window to populate.
     * @env (optional) The env (overrides cached env).
     * @return The row index where the cells were inserted.
     */
    extend public int buildRow(GridCell[] rowCells, GridWindow grid, GridBuilderEnv env=null) { ... }
}