Compile Time Changes

MhPopupToolbarModelItem moved to core

The following classes have been moved from cm.abstract.materialHandling.ui to cm.core and have been renamed.

Old: MhPopupToolbarModelItem
New: PopupToolbarModelItem

Old: MhMultiTogglePopupInfo
New: MultiTogglePopupInfo

Old: MhMultiTogglePopup
New: MultiTogglePopupWindow

Old: MhMultiTogglePopupToolbarModelItem
New: MultiTogglePopupToolbarModelItem

Additionally, there have been several interface changes made to the classes within the class hierarchy. PopupToolbarModelItem has had any fields and logic related to on/off images moved down to the child class MhMultiTogglePopupToolbarModelItem. It is also no longer an abstract class.

Old:
public class MhPopupToolbarModelItem extends ToolbarModelItem : abstract {


New:
public class PopupToolbarModelItem extends ToolbarModelItem {
    Removed: private Image _onImage : copy=reference, public readable;
    Removed: private Image _offImage : copy=reference, public readable;


Old:
    extend public void showPopup(ContextualViewToolbarModel model) : abstract { }
New:
    extend public void showPopup(ContextualViewToolbarModel model) { }


Removed:
    extend public void syncImageToState(ContextualViewToolbarModel model) { }


Removed:
    extend public Image onImage() {


Removed:
    extend public Image offImage() {
}


public class MultiTogglePopupToolbarModelItem extends PopupToolbarModelItem {
    Added: private Image _onImage : copy=reference, public readable;
    Added: private Image _offImage : copy=reference, public readable;


    Old: public function(ToolbarModelItem, ContextualViewToolbarModel):MhMultiTogglePopupInfo[] infosCallback;
    New: public function(ToolbarModelItem, ContextualViewToolbarModel):MultiTogglePopupInfo[] infosCallback;


Added:
    extend public void syncImageToState(ContextualViewToolbarModel model) {


Added:
    final public Image onImage() {
    extend public Image defaultOnImage() {


Added:
    extend public Image offImage() {
    extend public Image defaultOffImage() {
}

MultiTogglePopup has had an unused constructor removed.

public class MultiTogglePopup extends Control {

Removed:
    public constructor(Window parent,
                       MhMultiTogglePopupInfo[] infos |, 
                       // Inherited arguments.
                       Font font=controlFont,
                       Brush brush=whiteBrush,
                       FrameStyle frameStyle=noFrame,
                       frame3DState frameState=frameStateDown,
                       pointI origin=(0, 0),
                       bool selectable=true,
                       pointI pos=(0, 0),
                       sizeI size=(0, 0),
                       pointI margins=(0, 0),
                       alignment align=middle,
                       ControlGroup group=null,
                       bool popup=true,
                       bool shadow=true,
                       str key=null,
                       SrcRef src=#:src) {
}


Replace with:
    public constructor(Window parent,
                       MultiTogglePopupInfo[] infos,
                       MultiTogglePopupToolbarModelItem item |, 
                       // Inherited arguments.
                       Font font=controlFont,
                       Brush brush=whiteBrush,
                       FrameStyle frameStyle=noFrame,
                       frame3DState frameState=frameStateDown,
                       pointI origin=(0, 0),
                       bool selectable=true,
                       pointI pos=(0, 0),
                       sizeI size=(0, 0),
                       pointI margins=(0, 0),
                       alignment align=middle,
                       ControlGroup group=null,
                       bool popup=true,
                       bool shadow=true,
                       str key=null,
                       SrcRef src=#:src) {

MhStorageConfiguration changes

Removed a duplicate UnitLoad unitLoad() method. Replace overrides to it with UnitLoad unitLoad(Object env=null) instead.

public class MhStorageConfiguration extends MhSystemConfiguration {

Removed:
    extend public UnitLoad unitLoad() {
        return getValue(cMhUnitLoadPK).?UnitLoad;
    }


Replace with:
    extend public UnitLoad unitLoad(Object env=null) {
        UnitLoad res = getValue(cMhUnitLoadPK, env=env).?UnitLoad;
        if (res) return res;

        for (compType in orderedCompartmentTypes()) {
            if (UnitLoad ul = compType.compProps.unitLoad) {
                return ul;
            }
        }

        return null;
    }
}

MhShelvingBayAdoptionBehavior changes

Updated interface for bool validLevelPosition(point2D levelPos) with a new Snapper owner argument.

public class MhShelvingBayAdoptionBehavior extends MhAdoptionBehavior {

Old:
    extend public bool validLevelPosition(point2D levelPos) {
New:
    extend public bool validLevelPosition(Snapper owner, point2D levelPos) {
}

MhSystemExportFunction changes

Updated interface for bool exportFailed(MhEngineEntry entry, box exportedBound) with a new StrBuf error=null argument.

public class MhSystemExportFunction extends MhSystemEngineFunction {

Old:
    public bool exportFailed(MhEngineEntry entry, box exportedBound) {
New:
    public bool exportFailed(MhEngineEntry entry, box exportedBound, StrBuf error=null) {
}

Unit load population changes

Changes have been made to support modifying the population direction of unit loads. MhUnitLoadConstructionalPopulateFunction has a new field Point populateOffset that can be passed in as an argument, using this field value in vector populateVector() if there is any. This field previously existed in the subclass MhDeepstorageUnitLoadConstructionalPopulateFunction but has now been moved here.

Point unitLoadPopulateVector is also a new field in MhEngineLevelConstructionEntryInfo so each level can have a different direction. MhLevelConstructionalPopulateFunction has been updated to check for this value of each level entry info and pass it into "unitLoadConstruction".

Multi-racking framework changes

In 17.0 Minor we added a new package cm.abstract.materialHandling.storage.multi that is meant to contain most of the code related to the multi-racking framework that utilizes multi-bays and multi-frames. In 17.5 Major we are further expanding on the framework as well as moving multi-racking related code from other packages into the multi package. This section mainly cover interface changes made to improve the multi-racking framework. For new features related to this, see the "Multi-racking framework changes" section in the New Features pages.

1. Moved code

The following classes have been moved from cm.abstract.materialHandling.storage to cm.abstract.materialHandling.storage.multi:

Behaviors:
MhMultiBayEngineBehavior
MhMultiBayStretchEngineBehavior
MhMultiSplitRowAnimationEngineBehavior

Engine:
MhMultiEntryArrangeFunction
MhMultiEntryUpdateShapeFunction
MhMultiEntryUpdateClassificationFunction

2. Engine functions changes

The following engine function keys have been modified. If you have implemented multi-racking, ensure you search your codebase for the following old keys and replace them.

Old: multiBayPopulate
New: multiBayConstruction
Now associated with class MhMultiBayConstructionalPopulateFunction

Old: frameConstruction
New: multiFrameConstruction
Now associated with class MhMultiFrameConstructionalPopulateFunction

MhMultiBayConstructionFunction has been renamed to MhMultiBayConstructionalPopulateFunction.

MhMultiBayConstructionalFunction has been removed. It is to be replaced with MhMultiBayConstructionalPopulateFunction.

MhMultiFrameConstructionFunction has been renamed to MhMultiFrameConstructionalPopulateFunction.

MhMultiFrameConstructionalFunction has been removed. It is to be replaced with MhMultiFrameConstructionalPopulateFunction.

Removed method in MhMultiFrameEntryConstructionArrangement double calculateSpacerZSpacing(MhSpacingItem spacingItem). This logic is now handled in MhSpacingItem method double calcZSpacing(double frameHeight).

3. Other changes

For MhMultiRowConfiguration, the implementation of calcDepthFromAisles(MhRowAnimationInfo info, int numAisles) has been moved into the method that was calling it, calcWidthFromAisles(MhRowAnimationInfo info, int numAisles).

public class MhMultiRowConfiguration extends MhStorageRowConfiguration {

Removed:
    extend public double calcDepthFromAisles(MhRowAnimationInfo info, int numAisles) {


Updated:
    public double calcWidthFromAisles(MhRowAnimationInfo info, int numAisles) {
        // need to reset from previous time rowLayout.current was used
        rowLayout.current = 0;

        double aisleW = info.aisleWidth;
        if (aisleW == 0) aisleW = aisleWidth().safeDouble;

        double firstRowDepth = rowDepth(rowLayout, first=true);
        double lastRowDepth = rowDepth(rowLayout, last=true);
        double overhang = rearOverhang().safeDouble;
        double systemD = (aisleW + firstRowDepth + lastRowDepth +
                          info.offsetLeft + info.offsetRight + overhang*2);

        if (numAisles > 1) {
            double middleRowDepth = rowDepth(rowLayout, middle=true);
            for (x in 1..numAisles - 1) {
                systemD += aisleW;
                systemD += middleRowDepth;
            }
        }

        return systemD + 0.01;
    }
}

MhSpacingItem has a new argument for its constructor used to set the frame spacer count.

public class MhSpacingItem extends MhFramelinePatternItem {

Old:
    public constructor(MhSystemConfiguration config, Double d=null) {
        ...
        frameSpacerCount = config.getValue(translateKey(cMhFrameSpacerCountPK)).?int;
    }


New:
    public constructor(MhSystemConfiguration config, Double d=null, Int spacerCount=null) {
        ...
        if (spacerCount) frameSpacerCount = spacerCount.v;
        else frameSpacerCount = config.getValue(translateKey(cMhFrameSpacerCountPK)).?int;
    }
}

Multi-bays are now also intended to hold the "bottom level" (level at z=0) to allow for unit load insertion on the floor. We updated bay config logic to work better with this. In bay config behavior, reduced the cases where false is returned for shouldPutConfigToManager. Previously for a child-bay, as long as the parent multi-bay had a level child it would not put the child-bay config into the manager. We have omitted the case where the parent multi-bay has a bottom level with no unit loads so child-bays will still be config owners for this case.

public class MhBayConfigBehavior extends MhStorageConfigBehavior {

Old:
    public bool shouldPutConfigToManager(MhSnapper z) {
        ?MhSnapper parent = z.parent;
        while (parent) {
            if (parent.getChildren(levelFilter).any) return false;
            ?parent = parent.parent;
        }
        if (!z.getChildren(levelFilter).any) return false;
        return super(..);
    }

New:
    public bool shouldPutConfigToManager(MhSnapper z) {
        // Check if parent has level (false if yes).
        ?MhSnapper parent = z.parent;
        while (parent) {
            Snapper{} children = parent.getChildren(levelFilter);
            for (child in children) {
                if (child.isBottom) { // If parent has bottom level with loads.
                    if (child as MhSnapper) {
                        if (child.snapperInfos.any or child.getChildren().any) {
                            return false;
                        }
                    }
                } else { // If parent has any other (non-bottom) level.
                    return false;
                }
            }
            ?parent = parent.parent;
        }

        // Check if bay has levels (false if no).
        if (!z.getChildren(levelFilter).any) {
            return false;
        }
        return super(..);
    }
}

Engine function key update

A typo has been fixed in a function key. If using the key systemColumGrouping in your codebase, ensure this is updated to systemColumnGrouping.

Old: systemColumGrouping
New: systemColumnGrouping

MhBayShape changes

Removed method void appendLevelBracketPrimitives(CollisionPrimitive{} prims, Transform t=null).

public class MhBayShape extends MhBoxSnapperShape {

Old:
    public CollisionPrimitive collisionPrimitive(MhCollisionFetchEnv env) {
        ...
        appendLevelBracketPrimitives(geos, env.?transform);
        appendTopApproachPrimitives(geos, env);
        return set;
    }

New:
    public CollisionPrimitive collisionPrimitive(MhCollisionFetchEnv env) {
        ...
        appendTopApproachPrimitives(geos, env);
        return set;
    }


Removed:
    extend public void appendLevelBracketPrimitives(CollisionPrimitive{} prims, Transform t=null) { // FIXME 17.5 remove
        /* CUT THIS OUT  Thu Apr 30 13:29:18 2026 /kenng
        // Dont let the level brackets go past first hole
        Layer bracketLayer = geometryLayer(owner, sLevelBracket);
        box bracketBound(rect(point2D0, w, d).grown(0.01), 0, holeStartZ - holePitch/2);
        CollisionPrimitive bracketPrim = mhGetBoxCollisionPrimitive(bracketBound, bracketLayer, t);
        prims <<? bracketPrim;//mhInstanceCollisionPrim(bracketPrim, null);
        * CUT THIS OUT /kenng */
    }
}

MhRowShape changes

Removed method bool buildRowEntryLayoutCheckExactMatch.

public class MhRowShape extends MhSnapperShape {

Removed:
    extend public bool buildRowEntryLayoutCheckExactMatch() {
        return false;
    }
}

MhBasePropsSchemeData changes

The class MhBasePropsSchemeData has been moved from cm.abstract.materialHandling.behavior to cm.abstract.materialHandling.storage.

Feature snapping changes

The class MhFSGridFeatureSearchSnapAlternative has been renamed to MhFeatureSearchSnapAlternative as it is no longer just used by MhFSGridRectInsertAnimationG2. It is now also used by MhRowInsertAnimation and MhRowInsertToolAnimationG2.

The class MhFSGridSpaceVolumeSnapperFilter has been renamed to MhActiveSpaceVolumeSnapperFilter.

These changes are to allow racking insert animation to feature snap to unmovable snappers as well as to set the animation's elevation to the feature points elevation.

Old: public class MhFSGridFeatureSearchSnapAlternative extends FeatureSearchSnapAlternative {
New: MhFeatureSearchSnapAlternative

Old: public class MhFSGridSpaceVolumeSnapperFilter extends ActiveSpaceVolumeSnapperFilter {
New: public class MhActiveSpaceVolumeSnapperFilter extends ActiveSpaceVolumeSnapperFilter {

MhSnapperAbsMezzEngineEntry changes

Removed method void removeSnapper(AbsMezzEngineEnvironment env, Snapper{} removed, Space space) as it was a duplicate method of a base class. Replace with bool removeSnapper(AbsMezzEngineEnvironment env, Space space).

public class MhSnapperAbsMezzEngineEntry extends AbsMezzForeignEngineEntry {

Removed:
    extend public void removeSnapper(AbsMezzEngineEnvironment env,
                                     Snapper{} removed,
                                     Space space) {}
}

Configuration tooltip changes

There were two methods for generating property tooltips for configuration tabs so we have combined them. MhStorageConfigurationItem method str getToolTip(PropObj propertyOwner, str key) has been removed and MhSystemConfigurationItem method str toolTip(str key, PropObj owner) has been renamed to str getToolTip(str key, PropObj owner). You should be overriding str getToolTip(str key, PropObj owner) for tooltip implementations.

public class MhSystemConfigurationItem extends CoreObject {

Old:
    extend public str toolTip(str key, PropObj owner) {
        return null;
    }

New:
    extend public str getToolTip(str key, PropObj owner) {
        return null;
    }
}

public class MhStorageConfigurationItem extends MhSystemConfigurationItem {

Removed:
    extend public str getToolTip(PropObj propertyOwner, str key) {
        return toolTip(key, propertyOwner);
    }
}

Catalog-excel compilation changes

Updated interfaces in MhExcelCatalogFactory. The creation of ToC and population of features have been decoupled from the writing of db file. MhExcelCatalogFactory is also now an abstract class.

public class MhExcelCatalogFactory : abstract {

Old:
    extend public void process(bool showProgress=true,
                               str startingSheetName=null,
                               bool createToC=false,
                               StrBuf error=null) {
                    ...
                    populateCatalog();
                    createDb(createToC);
    }


New:
    extend public void process(bool showProgress=true,
                               str startingSheetName=null,
                               bool writeDb=true,
                               bool createToC=false,
                               StrBuf error=null) {
                    ...
                    populateCatalog();
                    populateFeatures();
                    cat.updateDependencies();
                    if (createToC) finalizeToC();
                    if (writeDb) createDb();
    }


Old:
    extend public void createDb(bool createToC=false) {


New:
    extend public void createDb() {
}

Debugging

We have consolidated most of the existing MH debug dialogs into a single dialog. Each of the old dialogs now exist as a window (tab) in the new Debug Dialog.

See "MH Debug tools revamp" in the New Features page for more info.

The following public interfaces have been modified.

Old: void showMhDebugDialog(Window parent=null)
New: void showMhDebugDialog(Window parent=null, str selectToolsCardKey=null)

Added: MhDebugDialog mhDebugDialog()

If showMhDebugDialog is passed in a selectToolsCardKey, it will open the dialog to that specific tab. Any existing code to open a specific debug dialog should be updated to use this function instead. For example, replace dbg_showDebugBehaviorsDialog() with showMhDebugDialog(selectToolsCardKey=cDbgBehaviorStoreKey).

These are the list of keys we use for the debug windows described below.

public const str cDbgEngineTrackerKey = "dbgEngineTracker";
public const str cDbgEngineVisualiserKey = "dbgEngineVisualiser";
public const str cDbgClassViewerKey = "dbgClassViewer";
public const str cDbgBehaviorStoreKey = "dbgBehaviorStore";
public const str cDbgConfigManagerTrackerKey = "dbgConfigManagerTracker";
public const str cDbgLinkedNeighborsKey = "dbgLinkedNeighbors";
public const str cDbgConfigurationsKey = "dbgConfigurations";
  1. Behavior Debug

The following public interfaces have been modified.

Old: class DebugBehaviorsDialog
New: class MhDebugBehaviorStoreWindow

Removed: void dbg_showDebugBehaviorsDialog()
Removed: void showDebugBehaviorsDialog(Window parent=null)

Added: MhDebugBehaviorStoreWindow mhDebugBehaviorStoreWindow()

The TreeView used to represent the behavior debug window has been revamped. The TreeViewItems now have a shared base class MhDebugObjectItem that has a field Object obj to contain a reference object for the item to have a reference to the relevant object, similar to the previous MyObjectItem. For behaviors, the referenced object will now be MhBehaviorController instead of the Behavior object and assortments will now be MhAssortmentController.

Old: class MyBehaviorGroupItem
New: class MhDebugBehaviorGroupItem

Old: class MyObjectItem
New: class MhDebugObjectItem

Old: class MySpawnerItem
New: class MhDebugObjectItem

Old: class MyBehaviorItem
New: class MhDebugBehaviorItem

Removed: class MyEngineFunctionItem
  1. Class Viewer

The previously named "Debug Functions/Behaviors" is now the Class Viewer tab of the Debug Dialog.

The following public interfaces have been modified.

Old: class MhDebugMaterialHandlingDialog
New: class MhDebugClassViewerWindow

Removed: void dbg_showDebugMaterialHandlingDialog()

Added: MhDebugClassViewerWindow mhDebugClassViewerWindow()
  1. Config Tracker

The config tracker was a private class that has now been made public.

The following public interfaces have been modified.

Old: private class MhConfigManagerTrackerDialog
New: public class MhDebugConfigManagerTrackerWindow

Old: MhConfigManagerTrackerDialog mhConfigManagerTrackerWindow 
New: MhDebugConfigManagerTrackerWindow mhDebugConfigManagerTrackerWindow()

Removed: void showConfigManagerTracker()
  1. Configurations

Previously the Debug Configurations dialog had public interfaces to open it but was never added to the debug library. It is now a tab in the Debug Dialog.

The following public interfaces have been modified.

Old: class DebugConfigurationsDialog
New: class MhDebugConfigurationWindow

Old: class MyConfigurationGroupItem
New: class MhDebugConfigurationGroupItem

Old: class MyConfigurationItem
New: class MhDebugConfigurationItem

Removed: void dbg_showDebugConfigurationsDialog()
Removed: void showDebugConfigurationsDialog(Window parent=null)

Added: MhDebugConfigurationWindow mhDebugConfigurationWindow()
  1. Engine Tracker

This has been moved from cm.abstract.materialHandling.engine to cm.abstract.materialHandling.debug.

The following public interfaces have been modified.

Old: class MhEngineTrackerWindow
New: class MhDebugEngineTrackerWindow

Old: void updateEngineTrackers()
New: void mhDebugUpdateEngineTrackers(str event)

Removed: void showEngineTracker()

Added: str->MhDebugEngineFunctionTracker dbgEngineFunctionTrackers()
Added: void dbgRegisterEngineFunctionTracker(MhDebugEngineFunctionTracker eft)
Added: void dbgUnregisterEngineFunctionTracker(str key)
Added: MhDebugEngineTrackerWindow mhDebugEngineTrackerWindow

Old: class MhEngineFunctionTracker
New: class MhDebugEngineFunctionTracker
  1. Engine Visualiser

This has been moved from cm.abstract.materialHandling.engine to cm.abstract.materialHandling.debug.

The engine visualiser was a private class that has now been made public.

The following public interfaces have been modified.

Old: private class EngineVisualiserDialog
New: public class MhDebugEngineVisualiserWindow

Removed: void showEngineVisualiser()
Removed: void showEngineVisualiserFunctionSelectorWindow()

Old: void updateEngineVisualiser()
New: void updateEngineVisualiser(str event)

Old: DebugCollisionPrimitiveVisualizer
New: MhDebugCollisionPrimitiveVisualizer

Removed: MhEngineVisPopulatorData dbg_getPopulatorData()
Removed: void dbg_resetPopulatorData()

For updating populator data to be displayed in the visualiser, we no longer use dbg_getPopulatorData() and dbg_resetPopulatorData(). Instead a new cache MhDebugEngineVisEngineFuncDataCache dbg_getEngineVisFuncDataCache() has been introduced and this cache now holds the populator data. Use the below interfaces instead. This cache is also intended to hold other data related to engine functions for visualisation purposes, currently the other type of data being entries that failed to export.

Added: MhDebugEngineVisEngineFuncDataCache dbg_getEngineVisFuncDataCache()

Added: void dbg_resetEnginVisFuncDataCache()

Added:
public class MhDebugEngineVisEngineFuncDataCache {

    extend public MhDebugEngineVisPopulatorData getPopulatorData(Vector direction=null) {
        if (direction) {
            if (!populatorData) {
                populatorData = MhDebugEngineVisPopulatorData(direction.v);
            } else if (populatorData.populatorDirection != direction.v) {
                populatorData.populatorDirection = direction.v;
                populatorData.steps.?clear();
            }
        }
        return populatorData;
    }


    extend public void appendPopulatorData(Vector direction, MhDebugEngineVisStepData stepData) {
        getPopulatorData(direction).add(stepData);
    }
}
  1. Linked Neighbors

The global dbg_snapperLinks to enable snapper link 3D graphics drawn by the MhDebugLinksBehavior is now toggled whenever you open or close the Debug Dialog.

The following public interfaces have been modified.

Old: class DebugLinkDialog
New: class MhDebugLinkWindow

Removed: void dbg_showDebugLinkDialog()

Removed: void showDebugLinkDialog(Window parent=null)

Added: MhDebugLinkWindow mhDebugLinkWindow()

Old: class MyObjectItem
New: class MhDebugLinkItem

Aside from the new debug dialog, there are some other interface changes reltaed to debugging.

Removed global bool dbg_statelessCollCache and replaced with bool _spawnerBehaviorCollCacheEnabled. This is now exposed to the debug library toolbox as the toggle "Enable Spawner Behavior Collection Cache".

Removed: bool dbg_statelessCollCache
Added: bool mhSpawnerBehaviorCollCacheEnabled()
Added: bool mhEnableSpawnerBehaviorCollCache(bool v)

Runtime/Behavior Changes

MhPrimPopulatorStepper now includes previous collision primitive when calculating next step

Previously in MhPrimPopulatorStepper.nextStep(MhPopulator populator, Object obj) the steps returned only contained the CollisionPrimitive of the passed in obj. It did not contain the CollisionPrimitive of the steps that came before it, meaning that when checking for collision with the previousStep, it does not check for collision with the primitives of the steps that came before it. We have updated this method to include primitives from the previous steps as well in each returned step.

public class MhPrimPopulatorStepper extends MhLimitPopulatorStepper {

Old:
    public MhPopulatorStep nextStep(MhPopulator populator, Object obj) {
                ...
                prim = mhInstanceCollisionPrim(prim, moveTransform(dp.v));
                if (!(endPrim and primConflicts(prim, endPrim))) {
                    return MhPopulatorStep(l, stepSize, prim);
                }
    }


New:
    public MhPopulatorStep nextStep(MhPopulator populator, Object obj) {
                ...
                MhCollisionPrimitiveSet set(null, null);
                set << mhInstanceCollisionPrim(prim, moveTransform(dp.v));
                if (lastStep) set << lastPrim;
                prim = set;

                if (!(endPrim and primConflicts(prim, endPrim))) {
                    return MhPopulatorStep(l, stepSize, prim);
                }
    }
}

unitLoad lookup fallbacks

As part of our efforts to support having no unit loads generated for levels, we've added some fallback lookups to minimize cases where unit loads are not generated when they actually should be, such as trying to insert a unit load from the toolbox when the configuration is currently set to no unit load.

MhStorageConfiguration.unitLoadKey() has been updated to also check unitLoad() if there is no value for the unitLoadKey property. unitLoad() has also been updated to work with compartment types.

public class MhStorageConfiguration extends MhSystemConfiguration {

Old:
    extend public Str unitLoadKey() {
        return getValue(cMhUnitLoadKeyPK).?Str;
    }


New:
    extend public Str unitLoadKey() {
        Str res = getValue(cMhUnitLoadKeyPK).?Str;
        if (res) return res;

        if (UnitLoad ul = unitLoad()) {
            return ul.gid.hex;
        }

        return null;
    }


Old:
    extend public UnitLoad unitLoad(Object env=null) {
        return getValue(cMhUnitLoadPK, env=env).?UnitLoad;
    }


New:
    extend public UnitLoad unitLoad(Object env=null) {
        UnitLoad res = getValue(cMhUnitLoadPK, env=env).?UnitLoad;
        if (res) return res;

        for (compType in orderedCompartmentTypes()) {
            if (UnitLoad ul = compType.compProps.unitLoad) {
                return ul;
            }
        }

        return null;
    }
}

MhUnitLoadSpawner.shapeCreationProps() has a small change to find first unitLoadKey from domain if configuration does not return a unitLoadKey.

public class MhUnitLoadSpawner extends MhStorageSpawner {

Old:
    public str->Object shapeCreationProps() {
            ...
            if (!ul) {
                SubSet ss = config.getDomain(cMhUnitLoadKeyPK);
                if (ss.any) ?key = ss.next(key);
            }

            return props { unitLoadKey=key };
        }
    }


New:
    public str->Object shapeCreationProps() {
            ...
            if (!ul) {
                SubSet ss = config.getDomain(cMhUnitLoadKeyPK);
                if (ss.any) {
                    if (!key.any) ?key = ss.first();
                    if (?str next = ss.next(key)) key = next;
                }
            }

            return props { unitLoadKey=key };
        }
    }
}

Behavior changes for spawners

MhRackFrameSpawner was mistakenly missing a behavior mhRowChildCopyPasteBehavior in its method override of MhBehavior[] customOtherBehaviors(). This has now been added so if you are also overriding customOtherBehaviors() to add this behavior in after super(), that can now be removed.

public class MhRackFrameSpawner extends MhFrameSpawner {

Updated:
    public MhBehavior[] customOtherBehaviors() {
        ...
        res << mhRowChildCopyPasteBehavior;
        return res;
    }
}

MhFrameSpawner now has an override for customDebugBehaviors() to use mhDebugLinksBehavior.

public class MhFrameSpawner extends MhStorageSpawner {

    public MhBehavior[] customDebugBehaviors() {
        return [MhBehavior: mhDebugLinksBehavior];
    }
}

MhSpacerSpawner has removed mhDebugLinksBehavior from customOtherBehaviors() and moved it to customDebugBehaviors().

public class MhSpacerSpawner extends MhStorageSpawner {

Old: public MhBehavior[] customOtherBehaviors() { ... res << mhDebugLinksBehavior; return res; }

New: public MhBehavior[] customOtherBehaviors() { ... return res; }

New: public MhBehavior[] customDebugBehaviors() { return [MhBehavior: mhDebugLinksBehavior]; } }

MhUnitLoadSpawner has removed mhDebugResolverPrimitivesBehavior from customOtherBehaviors() and moved it to customDebugBehaviors().

public class MhUnitLoadSpawner extends MhStorageSpawner {

Old:
    public MhBehavior[] customOtherBehaviors() {
        ...
        res << mhDebugResolverPrimitivesBehavior;
        return res;
    }

New:
    public MhBehavior[] customOtherBehaviors() {
        ...
        return res;
    }


New:
    public MhBehavior[] customDebugBehaviors() {
        return [MhBehavior: mhDebugResolverPrimitivesBehavior];
    }
}

MhSnapperInfo changes

When a MhSnapperInfo is appended to MhSnapperBehavior, we no longer suppress the removeChild and setParent snapper functions if it was a silent append. They will now be called with the silent flag passed into them, allowing the ChildSnapperNode family to be appropriately updated.

public class MhSnapperBehavior extends MhBehavior {

Old:
    extend public void append(MhSnapper snapper, bool silent) {
        init? infos();
        MhSnapperInfo info = createSnapperInfo(snapper);
        infos << info;
        
        if (!silent) {
            snapper.parent.?removeChild(snapper);
            snapper.setParent(null);
        }
    }


New:
    extend public void append(MhSnapper snapper, bool silent) {
        init? infos();
        MhSnapperInfo info = createSnapperInfo(snapper);
        infos << info;
        
        snapper.parent.?removeChild(snapper, silent=silent);
        snapper.setParent(null, silent=silent);
    }
}

Master link changes

The function MhMasterLinkFinalizeFunction has had it's multiChildrenLink functionality modified. In linkSiblingAndCousin(MhEngine engine, MhEngineEntry entry, Transform rootParentTransform), it now only links bays and frames. This change was made to avoid linking levels together with anything, as it was linking spanning levels with frames and bays.

public class MhMasterLinkFinalizeFunction extends MhSystemEngineFunction {

    extend public MhEngineEntry[] getAllowedMultiLinkChildren(MhEngineEntry[] children) {
        MhEngineEntry[] res(children.count);
        MhEngineEntryFilter filter = multiLinkChildrenFilter();
        for (child in children) {
            if (filter.accepts(child)) {
                res << child;
            }
        }

        return res;
    }


    extend public MhEngineEntryFilter multiLinkChildrenFilter() {
        return mhFrameOrBayEntryFilter;
    }
}