Added a new abstract cm.abstract.floorMarking
that the "Marking & Signage" extension implements for its floor marking snappers.
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:
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.
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; } }
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.
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.
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
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.
CustomSpecOption
is an extension of SpecOption
that represents a user-defined option.
CustomOptionSpecial
_specialsKey
) that uniquely identifies the option and ties it back to its originating specialSpecOption
but provides linkage to its backing special.public constructor( str key, int sequence, str code, str description, str groupDescription = null, int level = 0, double price = 0.0, bool exportable = true )
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._specialsKey
specialsKey()
_specialsKey
CustomSpecOption
and its originating CustomOptionSpecial
CustomOptionSpecial
CustomSpecOption
is generated by CustomOptionSpecial.generateCustomOption()
SpecOption
s
specialsKey
link to its backing special.Part
→ PartSpecialHolder
→ CustomOptionSpecial.option()
chainspecialsKey()
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() { ... } }
The CustomOptionSpecial
type extends the existing PartSpecial
workflow to support freeform options.
CustomOptionSpecial
instances.CustomOptionSpecial
is stored in the PartSpecialHolder
, uniquely identified by the owning part key and the initial option’s keyCustomOptionSpecial
is createdPartSpecialHolder
under its unique keyCustomOptionSpecial
holds both:
CustomSpecOption
, generated lazily and invalidated when relevant fields changespecOptions()
access, a Part
gathers all CustomOptionSpecial
s associated with its key from the PartSpecialHolder
CustomOptionSpecial
, the part calls option()
to retrieve a CustomSpecOption
CustomSpecOption
s are then appended into the part’s SpecOption
s sequence
cm.abstract.part.ProdPart
for more infoCustomOptionSpecial
is an extension of OptionSpecial
designed to generate CustomSpecOption
s.
_specialsKey
generateKey()
_option
back to its special instance_option
CustomSpecOption
generateCustomOption()
optStr
, optDescStr
, level
, sequence
) are changedoption()
CustomSpecOption
_specialsKey
has not been generated, calls generateKey()
_option
is null, calls generateCustomOption()
generateKey()
optStr
, optDescStr
, level
, sequence
, amount
specialsKey
for CustomSpecOption
generateCustomOption()
CustomSpecOption
using the _specialsKey
, sequence
, optStr
, optDescStr
, and level
.invalidate()
_option
, forcing regeneration on next option()
calloptStr
, optDescStr
, level
, sequence
)
_option
to prevent stale cacheCustomOptionSpecial
provides a structure for specialized option generationgenerateKey()
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) { ... } }
As of 16.5, a new interface for lead time and PartAnnotation
s 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); ... } ... }
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() { ... } }
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) { }
DWGs in the DWG Dialog can now be multiselected and edited at once.
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:
leadTime()
in Part to get accurate value for core Lead Time columnoverridePartColumns()
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(); } }
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 Part
s 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; }
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 Part
s attributes is as follows:
PropObj
holds a PartAnnotationHolder
in its propData
under a constant str key (cAnnotationHolderKey
)
PropObj
is typically a Part
owner/creator (Snapper
, Component
, etc.)PartAnnotationHolder
holds a map of str->(PartAnnotation[])
objects
str
keys are unique identifiers for a Part
associated with the PropObj
PartAnnotation[]
values are the PartAnnotation
s associated with the Part
PartAnnotationHolder
PartAnnotation
holds a str note
and a str description
value
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) { ... }
To manage the new PartAnnotation
s for a PropObj
s 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 Part
s attributes is as follows:
PropObj
holds a PartAnnotationHolder
in its propData
under a constant str key (cAnnotationHolderKey
)
PropObj
is typically a Part
owner/creator (Snapper
, Component
, etc.)PartAnnotationHolder
holds a map of str->(PartAnnotation[])
objects
str
keys are unique identifiers for a Part
associated with the PropObj
PartAnnotation[]
values are the PartAnnotation
s associated with the Part
PartAnnotationHolder
PartAnnotation
holds a str note
and a str description
value
PartAnnotation
The str->(PartAnnotation[])
map contains:
str
keys
Part
on the PropObj
storing the holderPart
s annotationKey()
PartAnnotation[]
values
PartAnnotation
objects for the Part
corresponding to the key/** * 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) { ... } }
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.
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.
/** * 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(); } }
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 Part
s attributes is as follows:
PropObj
holds a PartAnnotationHolder
in its propData
under a constant str key (cAnnotationHolderKey
)
PropObj
is typically a Part
owner/creator (Snapper
, Component
, etc.)PartAnnotationHolder
holds a map of str->(PartAnnotation[])
objects
str
keys are unique identifiers for a Part
associated with the PropObj
PartAnnotation[]
values are the PartAnnotation
s associated with the Part
PartAnnotationHolder
PartAnnotation
holds a str note
and a str description
value
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.)
/*************************************************************************** * 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) { ... }
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.
/** * 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; } }
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:
ScrollableGridWindow attributesGrid
)SubWindow gridControlsSW
)/** * 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) { ... } }
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:
Display partInfoLabel
)/** * 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()); } }
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.
/** * 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() { ... } }
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 PartAttributesDialog
s.
/** * 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) { ... }
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:
Button addButton
),Button insertButton
), andButton removeButton
). /** * 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) { ... } }
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:
Button okButton
),Button closeButton
),Display dialogStaleLbl
)/** * 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) { ... } }
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.
/** * 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) { ... } }
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:
private Part _part
field owning the attribute Dataprivate PartAttributesDialogBehavior _behavior
defining the dialogs user interface and experience
PartAttributesDialogBehavior
for moreprivate : public readable
subwindows for the dialogs sections:
SubWindow topWindow
SubWindow dataWindow
SubWindow bottomWindow
event shownEvent
event closedEvent
/** * 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() {... } }
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:
PartAttributesDialog
s SubWindows
initDialogEvents(PartAttributesDialog dialog)
initTopWindow(PartAttributesDialog dialog)
initDataWindow(PartAttributesDialog dialog)
initBottomWindow(PartAttributesDialog dialog)
alignControls(PartAttributesDialog dialog)
checkStaleStatus(PartAttributesDialog dialog)
checkForStalePartAttributesDialogs
in cm/core/part/attributes/hooks.cmonDialogShown(Object sender, Object args)
onDialogClosed(Object sender, Object args)
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.
/** * 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) { ... } }
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.
/** * 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; } }
With the creation of the new PartAttributesDialog
and PartAttributeColumn
s 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 PartAttributeColumn
s.
/** * 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) { ... } }
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 GridCell
s appropriate for the part attribute data.
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:
PartAttributesGridCell
s for the grid rows/** * 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; } }
With the addition of the new PartAttributesDialog
, simple interface for opening a Part
s attributes dialog as well as some helper functions have been added in cm/core/part/attributes/functions.cm
.
/** * 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) { ... }
# 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)
Explodes a specified block snapper. It is intended to be used in combination with CreateBlockInstruction
.
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
.
Enables or disables the global user snap setting. By default, all instruction based test cases have user snapping enabled.
Allows setting the global user select setting to either single select or group select. By default, all instruction based test cases use group select.
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.
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 }
The CustomTitleBar now supports the following new features:
A new parameter tbConfig
is added to DialogWindow
, ResizableDialogWindow
to allow creation of a window with a custom caption title bar.
A new parameter enforceMinimumWidth
is added to DropDownMenu
to control the auto width sizing behavior of context menus.
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.
columns()
→ returns the column labels (abstract)getRowCells(data)
→ builds the GridCells for a row (abstract)beforeBuildGrid(data, grid)
→ run before grid populationafterBuildGrid(data, grid)
→ run after grid population (defaults to resizing/scrolling updates)beforeBuildRow(data, index, grid)
→ run before a row is builtafterBuildRow(data, row, grid)
→ run after a row is builtGridBuilderEnv
columns()
and getRowCells(data)
GridBuilder
GridWindow
/** * 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) {} }
The GridBuilder
class provides a base for constructing and populating GridWindow
s.
It works together with a GridBuilderEnv
, which defines how data maps to columns and rows. This separates grid setup (builder) from grid logic (env).
GridBuilderEnv
defines column labels and row-cell mappingcolumns(env)
getRowCells(data, env)
populateGridWindow
, populateRows
, and populateRow
handle filling a grid from dataGridBuilderEnv
to define columns and row-cell mappingGridBuilder
with that env.populateGridWindow(data, grid)
to build the full grid/** * 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) { ... } }