The fields I1
(part base price) and O1
(option price) have been added to SIF exports for parts. OL
for option price has been removed.
final package str[] generateConfiguraSifRows(DsPart part, Space space) { ... // I1= base price (without option upcharges) addSifRow(env, "I1", part.basePrice().toS()); ... //Options for (DsPDataOption pO in sortedSelOptions, index=index) { ... addSifRow(env, "O1", o.price(this).toS); ... } ... }
The fields I1
(part base price) and O1
(option price) have been added to SIF exports for parts, leading to these additions in DsFreeformPData
:
public str[] generateSifRows(DsPart part, Space space) { ... // I1= base price (without option upcharges) addSifRow(env, "I1", part.basePrice().toS()); ... //Options for (pO in sortedSelOptions, index=index) if (pO as DsPDataOption) { ... addSifRow(env, "O1", o.price(this).toS); ... } ... }
cDsFreeformMeasTypeSubSet
now excludes enum types unknown
, mirrorPointX
, mirrorPointY
, and insertElevation
. This means these columns are no longer available as columns in the freeform picklist.
The following functions have been added to support filtering of materials in DsMaterialLegendSnapper
s.
DsMaterialLegendSnapper Added: public ObjectLabelSubSet _filterSubSet; Added: public void initFilters() {} Added: extend public ObjectSubSet filterSubSet() {}
The following functions have been added to support length units for COMs in the in DsMaterialLegendItem
s.
DsMaterialLegendItem Added: extend public Graph graphLengthUnit(UserTextStyle style) {} DsMaterialLegendItemBuildEnv Added: public constructor(bool buildName=true, bool buildDescription=true, bool buildID=true, bool buildEnterprise=true|, bool buildLengthUnit=false) {}
To support filtering of DsMaterialLegend
s, ObjFilter
s are utilized to filter materials in the legend. As a result, the optional
bool filtered
parameter was added when retrieving the sorted item list for legends..
DsMaterialLegend Old: public sorted str->MaterialLegendItem sortedList() {} New: public sorted str->MaterialLegendItem sortedList(bool filtered=true) {}
On drawing load, instances of DrawEllipse
will be replaced by instances of Draw3DEllipse
. The only major difference between them is that Draw3DEllipse
optionally supports 3D graphics outside of paper space.
The following overidden getter/setter functions have been added. Now, changes to description on
COMPartProxy
change the description on the referenced CustomerOwnMaterial
.
// get/set now reference CustomerOwnMaterial description Added: public str description() {} Added: public str description=(str v) {}
The following functions have been added to support updating a specific MaterialTreeViewItem
in the tree view
of materials and CustomerOwnMaterial undo operations.
// Updates a specific MaterialTreeViewItem in the tree view Added: extend public void updateMaterial(Material mat) {} // returns the COMUndoOp for the CustomerOwnMaterial and the operation being performed Added: extend public COMUndoOp comUndoOp(CustomerOwnMaterial com, comUndoOperation operation) {}
Change sorting of snapper from locally defined snapper child depth sorting to use newly added mhSnapperFuncRunOrderSort(Snapper a, Snapper b, Object env)
.
Change sorting of snapper from locally defined snapper child depth sorting to use newly added mhSnapperFuncRunOrderSort(Snapper a, Snapper b, Object env)
during export.
Change sorting of snapper from locally defined snapper child depth sorting to use newly added mhSnapperFuncRunOrderSort(Snapper a, Snapper b, Object env)
.
Implement sorting of snapper children to use newly added mhSnapperRootXYZPosSort(Snapper a, Snapper b, Object env)
during property change.
Split run functions into multiple functions for clarity. This means that runFunctions
will be a stand alone function and no longer call post engine run and finalize engine run.
int i; while (i <= list.lastIndex) { if (MhEngineRun r = list[i]) { currentRun = r; r.beforeEngineRun(e, runEnv); r.mainRunFunctions(e, runEnv); r.postRunFunctions(e, runEnv); r.finalizeRunFunctions(e, runEnv); r.afterEngineRun(e, runEnv); currentRun = null; } i++; }
For function that called runFunctions
outside of engine manager, this will have to change as below.
Example: Old: { run.runFunctions(runEngine, env); } New: { run.mainRunFunctions(runEngine, env); run.postRunFunctions(runEngine, env); }
The function mhConfigTempCopy(MhConfigRef ref)
will now also set the field id = 0
of MhConfigRef
objects where their field isCustom = true
.
Old: public MhConfigRef mhConfigTempCopy(MhConfigRef ref) { MhConfigRef cRef = copy(ref); cRef.gid = guid(); if (ref.isCustom) { if (developMode) if (cRef.id != -1) ptrace("Expected ID to be -1 for custom!".eAngry); } else { cRef.id = 0; cRef.name = ""; } return cRef; } New: public MhConfigRef mhConfigTempCopy(MhConfigRef ref) { if (!ref) return null; MhConfigRef cRef = copy(ref); cRef.gid = guid(); cRef.id = 0; if (!ref.isCustom) cRef.name = ""; return cRef; }
This is because MhConfigRef
objects are only considered temporary if id == 0
.
public class MhConfigRef { /** * Temp. */ final public bool temp() { return id == 0; } }
Without resetting the id
field back to 0
, they will not be treated as temporary config refs by MhConfigManager
, stopping those instances from being merged with identical config refs in the MhConfigManager.configs
container field.
A new field public Int noOfLevels
has been added to MhLevelConstructionalPopulateFunction
. When a noOfLevels
value is passed in from your engine behavior, this function will only populate loads up to the noOfLevels
value instead of using the parent bound and populating as much as possible up to the bound height.
Drawing configurations are now directly editable in the bay and frame editors. We have made several updates to the editors and configurations to support this.
You can control which configurations are editable by overriding isConfigEditable(MhStorageEditorConfiguration config)
. This will disable the various editor tabs, quick properties as well as preview snappers for the given configuration.
public class MhStorageEditorDialog extends DialogWindow : abstract { /** * Is config editable? */ extend public bool isConfigEditable(MhStorageEditorConfiguration config) { if (config.?drawingConfig) { for (snapper in mainSpace.snappers) { forChildrenRecursive(MhSnapper child in snapper) { if (child.configEq(config)) { if (child.readOnly()) { return false; } } } } return drawingConfigsEditable(); } return true; } }
Additionally if you do not want drawing configurations to be editable like before, then you can override drawingConfigsEditable()
.
public class MhStorageEditorDialog extends DialogWindow : abstract { /** * Drawing configs editable? */ extend public bool drawingConfigsEditable() { return true; } }
MhStorageEditorDialog
now has a default implementation for buildApplyButtons(Window w)
. It is broken up into 2 sub-windows, spreadSub
and saveAndApplyButtonsSub
. spreadSub
is built by the configuration and should already be present in existing implementations. We have added saveAndApplyButtonsSub
as a new sub-window which by default is positioned to the left of spreadSub
. saveAndApplyButtonsSub
is used by drawing configurations to apply the modifications directly to snappers in the drawing.
If you have overridden how your dialog builds its windows, you may have to look into adding the saveAndApplyButtonsSub
to your overridden code to ensure users can apply drawing configuration changes. The methods below were added to support controlling these 2 sub-windows.
public class MhStorageEditorDialog extends DialogWindow : abstract { /** * Build apply buttons. */ extend public Window buildApplyButtons(Window w) { MhStorageEditorConfiguration config = currentConfig(); if (!config) return null; SubWindow sub(w, frameStyle=noFrame); spreadSub = config.?buildSpreadButtons(sub, spreadButtonCallback); saveAndApplyButtonsSub = buildSaveAndApplyButtons(sub); if (saveAndApplyButtonsSub and spreadSub) { spreadSub.rightOf(saveAndApplyButtonsSub); saveAndApplyButtonsSub.alignCenterY(spreadSub); } sub.autoSize(); return sub; } /** * Spread buttons subwindow enabled? */ extend public bool spreadSubEnabled() { MhStorageEditorConfiguration config = currentConfig(); return config and (!config.drawingConfig or !isConfigModified(config)); } /** * Show save and apply buttons. */ extend public bool showSaveAndApplyButtons() { return currentConfig().?drawingConfig; } /** * Save and apply button enabled? */ extend public bool saveAndApplyButtonEnabled() { MhStorageEditorConfiguration config = currentConfig(); return config and config.drawingConfig and isConfigModified(config); } /** * Save and apply all button enabled? */ extend public bool saveAndApplyAllButtonEnabled() { return anyModifiedConfigs(); } /** * Build save and apply buttons. */ extend public Window buildSaveAndApplyButtons(Window w) { SubWindow sub(w, frameStyle=noFrame); saveAndApplyBtn = Button(sub, key="saveAndApply", label=$saveAndApplyExisting, textSide=alignment.down, callback=buttonCallback()); saveAndApplyAllBtn = Button(sub, key="saveAndApplyAll", label=$saveAndApplyAll, textSide=alignment.down, callback=buttonCallback()); saveAndApplyAllBtn.rightOf(saveAndApplyBtn); sub.autoSize(); return sub; } /** * Toggle spread buttons. */ extend public void toggleSpreadButtons() { spreadSub.?enable(spreadSubEnabled(), update=true); } /** * Toggle save and apply buttons. */ extend public void toggleSaveAndApplyButtons() { if (showSaveAndApplyButtons()) { saveAndApplyButtonsSub.show(); saveAndApplyBtn.?enable(saveAndApplyButtonEnabled(), update=true); saveAndApplyAllBtn.?enable(saveAndApplyAllButtonEnabled(), update=true); } else { saveAndApplyButtonsSub.hide(); } } }
As for applying configurations to drawing snappers, a new method in MhStorageEditorConfiguration
was added to support this which uses the existing spread animation defined by this class.
public class MhStorageEditorConfiguration extends MhSystemConfiguration { /** * Apply to space. */ extend public bool applyToSpace(Space space, Snapper{} affected=null, bool validate=false) { if (MhSnapperApplyAnimation anim = getApplyToSpaceAnim(space)) { animate(anim); anim.applyEvent(); if (affected) affected += anim.affectedSnappers; if (validate) space.validateSnapperInvalidations(); abortAnimation(); return true; } return false; } }
When a user attempts to close the bay/frame editor, if there are any unapplied drawing configuration changes, they will by default receive a prompt notifying them. You can override this feature with promptUnappliedChanges()
.
public class MhStorageEditorDialog extends DialogWindow : abstract { /** * Close event. */ public void close() { if (!promptUnappliedChanges()) return; ... } /** * Prompt unapplied changes dialog. * Return false when user clicks cancel. */ extend public bool promptUnappliedChanges() { if (anyModifiedConfigs()) { str label = unappliedChangesLabel(); if (!label) { if (developMode) ptrace("Missing label"); return true; } FlexDialog mgBox(label); mgBox.setCustomCaption(getLabel()); mgBox.addButton($saveAndApplyAll, dialogResult.ok, image=icon("apply"), closeOnClick=true); mgBox.addButton($close, dialogResult.no, image=icon("no"), closeOnClick=true); mgBox.addButton($cancelButtonLabel, dialogResult.cancel, image=icon("cancel"), closeOnClick=true); int res = mgBox.showModal(); if (res.dialogResult.cancel) { return false; } else if (res.dialogResult.ok) { event("saveAndApplyAll"); } } return true; } }
Lastly, we now emit different events for selecting a snapper selectionChanged
as well as renaming configs configRenamed
. Previously both these actions would emit the afterPropertyChanged
event.
package class MhStorageEditorWorldEvents extends EventViewer { Old: /** * Event. */ public bool event(symbol event, Object z, str->Object args) { if (event == #endUndoStep) { if (z as UndoStep) { mhStorageEditorNotifyEvent("afterPropertyChanged"); } } return false; } New: /** * Event. */ public bool event(symbol event, Object z, str->Object args) { if (event == #endUndoStep) { if (z as UndoStep) { if (z.operations.count == 1) { UndoOp op = z.operations.first; if (op in UndoSelectOp) { mhStorageEditorNotifyEvent("selectionChanged"); } else if (op in MhStorageEditorConfigRenameUndoOp) { mhStorageEditorNotifyEvent("configRenamed"); } } else { mhStorageEditorNotifyEvent("afterPropertyChanged"); } } } return false; } }
MhRowShape
has extended the breakAllConnections
feature to also break all soft connections.
public class MhRowShape extends MhSnapperShape { /** * Disconnect all connections in this snapper, except those * who are attached to a snapper in 'exceptTo'. */ public void breakAllConnections(Snapper{} exceptTo=null) { if (owner.isActive and owner.useSoftConnect()) { mhBreakAllSoftConnections(owner, exceptTo=exceptTo); } } }
MhRowStretchAnimation
has also been updated to maintain soft connections so that all soft connections in a system will not be broken whenever the stretch animation is run.
public class MhRowStretchAnimation extends MhPropertyStretchAnimation { /** * Keep connected. */ public Snapper{} keepConnected() { Snapper{} res = super(..); Snapper{} snappers(); snappers += ogSelection.snappers; Snapper{} visited(); for (z in snappers) { for (MhRowConnectLine c in z.connectors()) { if (c.isSoftConnected()) { bool cr = c.keepConnectedDuringAnimation(ogSelection); Snapper{} connSnappers = mhGetSoftConnectedSnappers(c); for (z2 in connSnappers) { if (z2 in visited) continue; visited << z2; if (z2.keepConnectedDuringAnimation(ogSelection) or cr) { snappers += connSnappers; } } } } } return res + snappers; }
When loading configuration, instead of checking the configuration package against class.pkg
, check with the package
. As well as when getting addtional suffixes in additionalSuffixes
, embeding security info in embedPackageInfo
, and validating package info in validPackageInfo
. This is due to when we save the configuration, we put the package
into the formatter.
/** * Pre loading check for configuration file. */ public bool isAllowedLoadConfigurationFile(Url url) { bool canLoad = super(..); using (File f = url.openForRead()) { RobustFormatter formatter(f, Version(fastRobustVersion)); if (formatter.stream.remaining > 0) { // label object. formatter.getObject(); } if (formatter.stream.remaining > 0) { // package object. ?symbol savedPkg = formatter.getObject(); if (savedPkg != package) canLoad = false; } } return canLoad; }
MhLevelArrangeFunction2
is replacing the old MhLevelArrangeFunction
, providing simpler logic and clearer structure. It also separate out levelChildArrange
into another engine function.
- New: public class MhUnitLoadStandOnUpdateFunction extends MhSystemEngineFunction { - New: public class MhLevelArrangeFunction2 extends MhSystemEngineFunction {
New unit load arrangement class to help populating unit loads on level. MhUnitLoadConstructionalPopulateFunction
will now be using the arrangement class to handle unit load creation.
- New: public class MhUnitLoadArrangement - New: public class MhConstructionUnitLoadArrangement extends MhUnitLoadArrangement - New: public class MhPopulateUnitLoadArrangement extends MhUnitLoadArrangement - New: public class MhClearanceUnitLoadArrangement extends MhUnitLoadArrangement - New: public class MhFillSpacesUnitLoadArrangement extends MhUnitLoadArrangement
Introduce MhUnitLoadEnsureClearanceFunction2
to replace MhUnitLoadEnsureClearanceFunction
to use the new unit load arrangement. The MhUnitLoadEnsureClearanceFunction
will still be available for extensions to opt-out from this changes by overriding the engine function registered in assortment.
Added new method reset
in MhPopulator
class to reset the state of populator. Engine
now is a copy by reference field to allow for populator copy.
- New: public void reset() {
Added support noOfUnitLoadsY
to handle number of unitload in perpendecular direction.
Change sorting of entry from locally defined entry snapper child depth sorting to use newly added mhEntryFuncRunOrderSort(MhEngineEntry a, MhEngineEntry b, Object env)
.
Change sorting of entry from locally defined entry local position sorting to use newly added mhEntryFuncRunOrderSort(MhEngineEntry a, MhEngineEntry b, Object env)
.
Move logic that break linking after export to actual import function MhSystemImportFunction
- New: public void afterImport(MhEngineEntry{} imported, MhEngine engine) {
Add logic to explicitly allowNewSpacer(..)
calculated using aisle depth and implemented as always true by default.
MhDeepstorageUnitLoadConstructionalPopulateFunction
is now able to populate based on noOfUnitLoadsInDepth
that is passed in as an argument. A new flag is added populateBasedOnNoUlDepth
, so that you can decide whether to populate unit load based on the noOfUnitLoadsInDepth
given or populate as many as possible.
public class MhDeepstorageUnitLoadConstructionalPopulateFunction extends MhUnitLoadConstructionalPopulateFunction { - New: public Bool populateBasedOnNoUlDepth; - New: public Int noOfUnitLoadsInDepth;
The field I1
(part base price) has been added to SIF exports for parts, leading to this addition:
extend public str[] internal_generateConfiguraSifRows(Space space) { ... addSifRow("I1", basePrice().toS(), lines); ... }
'cm/core/coreProperties.cm::build(PreCorePropObj z, symbol{} attributes=null, BuildPropertiesEnv env=null, str->Object args=null)`
Resolved a bug during properties rebuild, with existing properties not getting removed correctly if the rebuilt properties has zero item or only one item left. This may impact developers using workarounds to manually remove properties that are incorrectly left behind.
In runtime REDRenderJOB work the same as it before.
In runtime REDRenderJOB work the same as it before.
With the addition of Extension Tabs (not to be confused with Toolbox Component Tabs), the toolbox card can now contain multiple instances of ToolboxSectionedScrollableSubWindow
, one for each tab. To differentiate between these instances, the key
of the window can be used instead. The format of a key
is $cardKey!$tabKey
.
Changes to the toolbox configuration have been made as well. It stores information such as what sections should be hidden, what the order of each section should be, and what was collapsed by the user. If the Component Tab uses Extension Tabs, the key
of each tab is prepended to the section keys that are stored into the config.
If your tab does not have any tabs, behaviour is unchanged from previous versions.
Many functions inside cm.core.toolbox/toolbox.cm uses the name of the function that created a library to find it.
In facelift, they key for the paper tools library is instead faceliftPaperToolsLibrary
. So when trying to find the paper tools library, make sure to search for that when useFacelift
is true
.
Affected functions are:
public bool updateToolboxCheckBox(symbol pkg, str libraryFun, str checkBoxKey, bool check, CoreAppWindow appWindow=null, bool invokeCallback=true) { public bool updateAllTbCheckBoxes(symbol pkg, str libraryFun, str checkBoxKey, bool check, bool invokeCallback=true) { public bool updateToolboxSubSetDropDown(symbol pkg, str libraryFun, str dropDownLimbKey, str selectItemKey, CoreAppWindow appWindow=null, bool invokeCallback=true) { public bool rebuildToolboxSubSetDropDown(symbol pkg, str libraryFun, str dropDownLimbKey, Object initial, Object[] seq, CoreAppWindow appWindow=null, bool invokeCallback=true) { public bool updateAllTbSubSetDropDowns(symbol pkg, str libraryFun, str dropDownLimbKey, str selectItemKey, bool invokeCallback=true) { public bool updateToolboxMaterialTreeDropDown(symbol pkg, str libraryFun, str dropDownLimbKey, str selectItemKey, CoreAppWindow appWindow=null, bool invokeCallback=false) { public bool updateAllTbMaterialTreeDropDowns(symbol pkg, str libraryFun, str dropDownLimbKey, str selectItemKey, bool invokeCallback=false) { public bool updateToolboxEnumDropDown(symbol pkg, str libraryFun, str dropDownLimbKey, str selectItemKey, CoreAppWindow appWindow=null, bool invokeCallback=true) { public bool updateAllTbEnumDropDowns(symbol pkg, str libraryFun, str dropDownLimbKey, str selectItemKey, bool invokeCallback=true) { public bool updateToolboxDistanceLimb(symbol pkg, str libraryFun, str distanceLimbKey, Object object, CoreAppWindow appWindow=null, bool invokeCallback=true) { public bool updateAllTbDistanceDropDowns(symbol pkg, str libraryFun, str distanceLimbKey, Object object, bool invokeCallback=true) { public bool updateToolboxButton(symbol pkg, str libraryFun, str buttonKey, CoreAppWindow appWindow=null) { public bool updateAllTbButtons(symbol pkg, str libraryFun, str buttonKey) { public bool updateToolboxButtonColor(symbol pkg, str libraryFun, str buttonKey, color c, CoreAppWindow appWindow=null) { public bool updateAllTbButtonColors(symbol pkg, str libraryFun, str buttonKey, color c) { public bool updateToolboxDoubleTextField(symbol pkg, str libraryFun, str textFieldKey, Object object, CoreAppWindow appWindow=null, bool invokeCallback=true) { public bool updateToolboxDistanceTextField(symbol pkg, str libraryFun, str textFieldKey, Object object, CoreAppWindow appWindow=null, bool invokeCallback=true) { public bool updateAllTbDoubleTextFields(symbol pkg, str libraryFun, str textFieldKey, Object object, bool invokeCallback=true) { public LazyLibraryCard getLibraryCard(symbol pkg, str libraryFun, CoreAppWindow appWindow=null) { public SubSetLimbDropDown getSubSetDropDown(symbol pkg, str libraryFun, str dropDownLimbKey) {
Possibly other in other packages are also affected. Anything that used stdPaperToolsLibrary
as a key and is also supposed to work in facelift, essentially.
We have fixed an issue with DotNetInvokerServer failing to resolve necessary DLL dependencies. Previously it calls .NET Assembly library's LoadFile
which loads the specified assembly, but this method does not resolve dependencies. This means custom COM code that references an external library will not work properly or silently fail.
This has been resolved by calling LoadFrom()
instead, which attempts to automatically resolve dependencies, for example it will attempt to search for dependencies in the same folder and load them. However, in rare cases the dependency may resolve to an already loaded version with the same identity but on a different path, which might potentially be an issue in development environments. Please perform a smoke test with your COM interop functionalities to ensure they are working fine.
Through PackageStreamRenamer
, the classes below will be replaced as followed when a drawing or favorite is loaded:
DrawTemplate3DCircle -> Draw3DCircle DrawTemplate3DRect -> Draw3DRect DrawTemplate3DArc -> Draw3DArc DrawTemplate3DLine -> Draw3DLine DrawTemplate3DHelpLine -> Draw3DHelpLine DrawTemplate3D2SidedRect -> Draw3D2SidedRect DrawTemplate3D3SidedRect -> Draw3D3SidedRect
Added new argument to isValid
method of G3StatsEntity
in cm.win.eventLogG3.cm
to allow for different validities for different event actions.
Old: extend public bool isValid() New: extend public bool isValid(str action)
The following functions have been overritten in Display
affecting displays with passive=true
.
public void rightClick(pointI p) { public void rightClick2(pointI p) { public void rightRelease(pointI p) { public void rightRelease2(pointI p) {
The setGlobalSkin(Skin)
function is now a no-op when CET is in the new UI. This is part of larger plan to remove the Skin
concept entirely in a future version of CET.