Overview

DIB Images

The handling of DIB (Device Independent Bitmap) images by CET Core has been improved, and it can now draw DibImage that have transparency.

As DIB images are regular memory allocated objects, it does not consume Windows GDI handles, resulting in reduced system resource usage, improving performance and stability.

MessageWindow

MessageWindow was originally made only to show in toolbox libraries. But now that there are more cases of it being used generally for UI, changes are made to easily use and size this outside of toolbox libraries. It sizes itself according to the text along with 3 sizing modes:

  • No width is provided : Size itself without text wrapping. (Use setWidth passing a 0 value)
  • Width is provided : Attempts to wrap the text if it crosses the width provided. (Use setWidth)
  • Calling extendRight : Attempts to wrap text according to how far it can extend right. (Use extendRight)

Compile Time Changes

SolidColorBrush

The field c in SolidColorBrush has now been changed to public readable to prevent alteration of existing brushes.

Old: public color c;
New: package color c : public readable;

The return type of brush cache function to get a cached solid color brush is now more specific.

Old: public Brush solidColorBrush(color c, bool use=false) {
New: public SolidColorBrush solidColorBrush(color c, bool use=false) {

Old: public Brush solidColorBrush(int r, int g, int b) {
New: public SolidColorBrush solidColorBrush(int r, int g, int b) {

Predefined solid brushes in cm/win/brush.cm now have their types changed to SolidColorBrush.

Old: public const Brush whiteBrush = SolidColorBrush(color(255, 255, 255));
public const SolidColorBrush whiteBrush = solidColorBrush(color(255, 255, 255));

GradientBrush

For both GradientHBrush and GradientVBrush, the field c2 has been changed to public readable.

Old: public color c2;
New: package color c2 : public readable;

ScrollableGridWindow

Added a new argument faceliftScrollBars=false in ScrollableGridWindow constructor.

MessageWindow

The MessageWindow class has been moved from cm.core.toolbox to cm.win.

Instead of passing a sizeI size in the constructor argument, only int width is passed instead.

Old: public constructor(Window parent,
		       str message,
		       Image image,
		       str key=null,
		       sizeI size=(0, 0),
		       int internalMargin=7,
		       Brush brush=ultraLightGrayBrush,
		       FrameStyle frameStyle=lightGrayPenFrame,
		       color textColor=black,
		       color linkColor=primary600,
		       color linkHoverColor=primary600,
		       int textSize=12,
		       str fontFace=null,
		       function(Control button, str key):bool linkCallback=null,
		       SrcRef src=#:src) {
New: public constructor(Window parent,
		       str message,
		       Image image,
		       str key=null,
		       int width=0,
		       int internalMargin=7,
		       Brush brush=ultraLightGrayBrush,
		       FrameStyle frameStyle=lightGrayPenFrame,
		       color textColor=black,
		       color linkColor=primary600,
		       color linkHoverColor=primary600,
		       int textSize=12,
		       str fontFace=null,
		       function(Control button, str key):bool linkCallback=null,
		       SrcRef src=#:src) {

BrushHoverButton

The constructor for BrushHoverButton has been updated to allow an optional configuration of underlining the button's label text when a button is hovered. This should facilitate a smoother integration for button styles, or more specifically, button states that adhere to CET's design system.

Old: public constructor(Window parent,
                        Brush stdBrush,
                        Brush hoverBrush,
                        Brush mouseDownBrush,
                        // inherited key arguments
                        Font font=controlFont,
                        FrameStyle frameStyle=noFrame,
                        frame3DState frameState=frameStateUp,
                        pointI pos=(0, 0),
                        sizeI size=sizeI(-1, -1),
                        pointI margins=(6, 4),
                        alignment align=middle,
                        str key=null,
                        str label="",
                        color labelColor=color(0, 0, 0),
                        color pressedLabelColor=color(colorType.none),
                        alignment textSide=undefinedAlignment,
                        Image image=null,
                        Image disabledImage=null,
                        color color=nocolor,
                        bool roundedCorners=false,
                        function(Control button) callback=null,
                        SrcRef src=#:src) {

New: public constructor(Window parent,
                        Brush stdBrush,
                        Brush hoverBrush,
                        Brush mouseDownBrush,
                        // inherited key arguments
                        Font font=controlFont,
                        FrameStyle frameStyle=noFrame,
                        frame3DState frameState=frameStateUp,
                        pointI pos=(0, 0),
                        sizeI size=sizeI(-1, -1),
                        pointI margins=(6, 4),
                        alignment align=middle,
                        str key=null,
                        str label="",
                        color labelColor=color(0, 0, 0),
                        color pressedLabelColor=color(colorType.none),
                        alignment textSide=undefinedAlignment,
                        Image image=null,
                        Image disabledImage=null,
                        color color=nocolor,
                        bool roundedCorners=false,
                        bool showUnderline=false, // Added
                        function(Control button) callback=null,
                        SrcRef src=#:src) {

As part of the interface change introduced for BrushHoverButton, some classes that extend from it will also be affected.

See other affected changes in:

  • cm.core.propsScheme.propsSchemeFilterButton
  • cm.win.frameStyleHoverButton
  • cm.win.progressButton

FaceliftSearchField

The constructor for FaceliftSearchField has been updated to accept an optional search icon that can be used to override the search field's icon where necessary.

Old: public constructor(Window parent,
                        // inherited key arguments
                        Font font=inputFieldFont,
                        Brush brush=whiteBrush,
                        FrameStyle frameStyle=faceliftStdFrame,
                        frame3DState frameState=frameStateDown,
                        pointI pos=(0, 0),
                        sizeI size=(0, 0),
                        pointI margins=(8, 8),
                        function(Control button) posChangedCallback=null,
                        function(Control button) callback=null,
                        function(Control button) escapeKeyCallback=null,
                        function(Control button) lostFocusCallback=null,
                        // extended
                        function(Control button) enterKeyCallback=null,
                        str key=null,
                        bool absFontH=false,
                        str promptText=null,
                        SrcRef src=#:src) {

New: public constructor(Window parent,
                        // inherited key arguments
                        Font font=inputFieldFont,
                        Brush brush=whiteBrush,
                        FrameStyle frameStyle=faceliftStdFrame,
                        frame3DState frameState=frameStateDown,
                        pointI pos=(0, 0),
                        sizeI size=(0, 0),
                        pointI margins=(8, 8),
                        Image searchIcon=icon("facelift2023/search"), // Added
                        function(Control button) posChangedCallback=null,
                        function(Control button) callback=null,
                        function(Control button) escapeKeyCallback=null,
                        function(Control button) lostFocusCallback=null,
                        // extended
                        function(Control button) enterKeyCallback=null,
                        str key=null,
                        bool absFontH=false,
                        str promptText=null,
                        SrcRef src=#:src) {

FrameStyleHoverButton

Following the interface change introduced for BrushHoverButton, the constructor for FrameStyleHoverButton has been updated.

Old: public constructor(Window parent,
                        FrameStyle mouseOverFrameStyle|,
                        // inherited key arguments
                        Brush stdBrush,
                        Brush hoverBrush,
                        Brush mouseDownBrush,
                        Font font=controlFont,
                        FrameStyle frameStyle=noFrame,
                        frame3DState frameState=frameStateUp,
                        pointI pos=(0, 0),
                        sizeI size=sizeI(-1, -1),
                        pointI margins=(6, 4),
                        alignment align=middle,
                        str key=null,
                        str label="",
                        color labelColor=color(0, 0, 0),
                        color pressedLabelColor=color(colorType.none),
                        alignment textSide=undefinedAlignment,
                        Image image=null,
                        Image disabledImage=null,
                        color color=nocolor,
                        bool roundedCorners=false,
                        function(Control button) callback=null,
                        SrcRef src=#:src) {

New: public constructor(Window parent,
                        FrameStyle mouseOverFrameStyle|,
                        // inherited key arguments
                        Brush stdBrush,
                        Brush hoverBrush,
                        Brush mouseDownBrush,
                        Font font=controlFont,
                        FrameStyle frameStyle=noFrame,
                        frame3DState frameState=frameStateUp,
                        pointI pos=(0, 0),
                        sizeI size=sizeI(-1, -1),
                        pointI margins=(6, 4),
                        alignment align=middle,
                        str key=null,
                        str label="",
                        color labelColor=color(0, 0, 0),
                        color pressedLabelColor=color(colorType.none),
                        alignment textSide=undefinedAlignment,
                        Image image=null,
                        Image disabledImage=null,
                        color color=nocolor,
                        bool roundedCorners=false,
                        bool showUnderline=false, // Added
                        function(Control button) callback=null,
                        SrcRef src=#:src) {

ProgressButton

Following the interface change introduced for BrushHoverButton, the constructor for ProgressButton has been updated.

Old: public constructor(Window parent,
                        Brush stdBrush,
                        Brush hoverBrush,
                        Brush mouseDownBrush,
                        // inherited key arguments
                        Font font=controlFont,
                        FrameStyle frameStyle=noFrame,
                        frame3DState frameState=frameStateUp,
                        pointI pos=(0, 0),
                        sizeI size=sizeI(-1, -1),
                        pointI margins=(6, 4),
                        alignment align=middle,
                        str key=null,
                        str label="",
                        color labelColor=color(0, 0, 0),
                        color pressedLabelColor=color(colorType.none),
                        alignment textSide=undefinedAlignment,
                        Image image=null,
                        Image disabledImage=null,
                        color color=nocolor,
                        bool roundedCorners=false,
                        function(Control button) callback=null,
                        SrcRef src=#:src) {

New: public constructor(Window parent,
                        Brush stdBrush,
                        Brush hoverBrush,
                        Brush mouseDownBrush,
                        // inherited key arguments
                        Font font=controlFont,
                        FrameStyle frameStyle=noFrame,
                        frame3DState frameState=frameStateUp,
                        pointI pos=(0, 0),
                        sizeI size=sizeI(-1, -1),
                        pointI margins=(6, 4),
                        alignment align=middle,
                        str key=null,
                        str label="",
                        color labelColor=color(0, 0, 0),
                        color pressedLabelColor=color(colorType.none),
                        alignment textSide=undefinedAlignment,
                        Image image=null,
                        Image disabledImage=null,
                        color color=nocolor,
                        bool roundedCorners=false,
                        bool showUnderline=false, // Added
                        function(Control button) callback=null,
                        SrcRef src=#:src) {

TreeView

A new bool facelift=false argument is added to TreeView's constructor.

Old: public constructor(Window parent,
		                // inherited key arguments
		                Font font=systemFont(),
		                Brush brush=null,
		                FrameStyle frameStyle=stdLightFrame,
		                frame3DState frameState=frameStateDown,
		                pointI pos=(0, 0),
		                sizeI size=(100, 100),
		                pointI margins=(-1, -1),
		                alignment align=middle,
		                bool popup=false,
		                bool shadow=false,
		                function (Control control) callback=null,
		                // extended key arguments
		                bool alwaysMultiSelect=false,
		                bool allowMultiSelect=false,
		                bool directSelect=false,
		                bool listBoxStyle=false,
		                treeViewSelectionStyle selectionStyle=treeViewSelectionStyle.undefined,
		                function (Control control) click2Callback=null,
		                bool transparentScrollBars=false,
		                bool noScrollBars=false,
		                str key=null,
		                SrcRef src=#:src) {
New: public constructor(Window parent,
		                // inherited key arguments
		                Font font=systemFont(),
		                Brush brush=null,
		                FrameStyle frameStyle=stdLightFrame,
		                frame3DState frameState=frameStateDown,
		                pointI pos=(0, 0),
		                sizeI size=(-1, -1),
		                pointI margins=(-1, -1),
		                alignment align=middle,
		                bool popup=false,
		                bool shadow=false,
		                function (Control control) callback=null,
		                // extended key arguments
		                bool alwaysMultiSelect=false,
		                bool allowMultiSelect=false,
		                bool directSelect=false,
		                bool listBoxStyle=false,
		                treeViewSelectionStyle selectionStyle=treeViewSelectionStyle.undefined,
		                function (Control control) click2Callback=null,
		                bool transparentScrollBars=false,
		                bool noScrollBars=false,
		                str key=null,
		                bool facelift=false,
		                SrcRef src=#:src) {

SpellChecker

The following methods have a new str languageTag argument, you're required to specify the language to obtain suggestions or replacements from.

Old: extend public str getSuggestions() {
New: extend public str getSuggestions(str languageTag) {

Old: extend public str getReplacement() {
New: extend public str getReplacement(str languageTag) {

PixelDevice

We have consolidated methods drawText and drawTextWithLineHeight by adding int limitLineHeight=-1 to drawText.

Old: final public void drawTextWithLineHeight(str text, rectI bound, rectI clipRect,
                                              int limitLineHeight,
                                              alignment align=middle,
                                              Font font=null,
                                              color textColor=color(0, 0, 0),
                                              color bkColor=color(192, 192, 192),
                                              bool transparency=true,
                                              bool clipping=false) {
Old: final public void drawText(str text, rectI bound, rectI clipRect,
                                alignment align=middle,
                                Font font=null,
                                color textColor=color(0, 0, 0),
                                color bkColor=color(192, 192, 192),
                                bool transparency=true,
                                bool clipping=false) {
New: final public void drawText(str text, rectI bound, rectI clipRect,
                                alignment align=middle,
                                Font font=null,
                                color textColor=color(0, 0, 0),
                                color bkColor=color(192, 192, 192),
                                bool transparency=true,
                                bool clipping=false,
                                int limitLineHeight=-1) { // Added

DibImage resizing function

The function dibResize() (introduced in 16.0 Minor) used to alter the passed in image and return a newly created copy of DibImage. This has now been improved to resize the DibImage in place, and no longer return a new copy of DibImage:

Old: public DibImage dibResize(DibImage im, sizeI newSize, str filter=null) {
New: public void dibResize(DibImage im, sizeI newSize, str filter=null) {

MemoryImage

To retrieve RGB pixel values from a MemoryImage, the following method has been introduced:

Old: final public RawImageData rawImageDataFixed(int targetBPP=-1) {
New: final public byte[] getPixelDataRGB() {

IconFinder

There have been some cleanups, argument ourOnlyHope has been replaced with debug.

Old: final public Image get(str name, bool debug=false, bool ourOnlyHope=true) {
New: final public Image get(str name, bool debug=false) {

Old: final public Image getDisabled(str name, bool ourOnlyHope=true) {
New: final public Image getDisabled(str name, bool debug=false) {

SvgImage

We have removed the MemoryPixelDevice bitmap field in SvgImage to reduce GDI bitmap object counts. It uses a Dib instead, similar to DibImage.

Old: public MemoryPixelDevice bitmap : copy=null, stream=null;
New: public Dib dib : copy=null, stream=null;

ImageSplashProgressControl

The constructors have been consolidated for this class.

Old: public constructor(str key, Image[] images, rectI bound, int durationMs=10000) {
New: public constructor(str key, Image[] images, rectI bound, int durationMs=10000, bool cycle=false, bool endWithUber=false) {

CircularProgressPainter

The access modifiers for field centerImage has been changed, a new setter method is introduced.

Old: public Image centerImage;
New: private Image centerImage : copy=reference, public readable;
New: final public bool setCenterImage(Image i) {

class GridWindow

The following interfaces have been changed or added in GridWindow for v16.5.

Old: extend public void insertRow(int y, str label=null, bool update=true) {}
New: extend public void insertRow(int y, str label=null, bool update=true, bool updateRowIndexLabels=false) {}

Old: extend public void insertRows(int y, int n) {}
New: extend public void insertRows(int y, int n, bool updateRowIndexLabels=false) {}

Old: extend public void removeRow(int y, bool update=true) {}
New: extend public void removeRow(int y, bool update=true, bool updateRowIndexLabels=false) {}

New: extend public void updateRowIndexLabels(int startRow=0) {}
New: extend public int appendRow(GridCell[] rowCells, str label=null, bool update=true) {}
New: extend public void insertRow(int y, GridCell[] rowCells, str label=null, bool update=true, bool updateRowIndexLabels=false) {}

Added updateRowIndexLabels Parameter

The bool updateRowIndexLabels parameter (default false) on the interfaces above was added in v16.5. It's purpose is to update row labels when row indexes change.

Important to note that if the rows are labeled as anything other than the row index, this parameters value should be set to false. It will update the row labels from the inserted/removed index to the end of the row sequence, setting the labels to the appropriate row index.

See documentation for the updateRowIndexLabels(int startRow=0) function for more info.

extend public void insertRow(int y, str label=null, bool update=true, bool updateRowIndexLabels=false) {
extend public void insertRows(int y, int n, bool updateRowIndexLabels=false) {
extend public void removeRow(int y, bool update=true, bool updateRowIndexLabels=false) {
	...
	
New:	if (updateRowIndexLabels) updateRowIndexLabels(startRow=y);

	...	
}

Added: extend public void updateRowIndexLabels(int startRow=0) {}

Updates the labels of grid rows so they display their current row index.

  • Iterates from the given startRow (default = 0, the first row).
  • Replaces each row’s label with its 1-based index (row + 1).
  • Negative startRow values are corrected to 0.
/**
 * Update labels of rows.
 * This will replace row labels with their index value.
 * @startRow optional row to begin label updates at; default 0 (top/first row)
 */
extend public void updateRowIndexLabels(int startRow=0) {
	if (startRow < 0) startRow = 0;
	while (int row = startRow; row < rowCount; row++) {
		setRowLabel(row, NameGridLabel((row+1).toS()));
	}
}

Added: extend public int appendRow(GridCell[] rowCells, str label=null, bool update=true) {}

Appends a new row of GridCells to the grid and returns its index.

Parameters
  • rowCells → sequence of GridCells to populate the new row.
  • label → optional label for the row.
  • update → whether to auto-update sizing and UI after insertion.
Return

The row index of the newly added row.

Added: extend public void insertRow(int y, GridCell[] rowCells, str label=null, bool update=true, bool updateRowIndexLabels=false) {}

Appends a new row of GridCells to the grid at a given index.

Parameters
  • y → index of row in grid to be inserted
  • rowCells → sequence of GridCells to populate the new row.
  • label → optional label for the row.
  • update → whether to auto-update sizing and UI after insertion.
  • updateRowIndexLabels → optional flag to update row index labels after insertion

Runtime/Behavior Changes

IconFinder

For icons resisiding in base/res/images/, CET will now return a DibImage instead of MemoryImage. This is to reduce reliance on GDI bitmap objects to display the CET UI. This only affects calls to icon that have their key "#default" or by calling dibIcon, such as:

icon("partTag")
icon("partTag", key=#default)

dibIcon("panel_frame.png", key=#fikaOffice);

For existing code that performed a cast check to MemoryImage, you have to migrate the code to also handle DibImage. Common issues are:

  • Images not loading
  • Disabled images are not grayed out (blend is not applied)

One such example is listed below:

// Previous logic only handles MemoryImage
byte beforeBlend = 255;
if (image as MemoryImage) {
    beforeBlend = image.blend;
    image.blend = 100;
}
image.transparentDraw(c.hdc, imgPos);
if (image as MemoryImage) image.blend = beforeBlend;

// New logic now handles MemoryImage, DibImage and SvgImage
byte beforeBlend = image.blend;
image.blend = 100;
image.transparentDraw(c.hdc, imgPos);
image.blend = beforeBlend;

Improved lifecyle of Images

In previous versions of CET, IconFinder would pass use=true when constructing an image. This would cause the Image to always be loaded as the image will never be destroyed (refCount never goes down to 0).

This has now been updated to not pass use=true, instead your dialog / control should be handling the use and release of the image. This is not required for ImagePainter as they already handle the use and release for you.

You may retrieve a terminated (blank) icon in some scenarios:

  • Retrieving an icon and directly drawing in your draw / repaint method.
  • Assigning an icon to a field and using it later.

A common pattern to fix this is to assign and use the icon on construction. Followed by releasing it on destruction (beforeRemove / removeWindow). This is used by BrushHoverDropDownMenuButton.

    /**
     * Second image.
     */
    private Image secondImage : package readable;


    /**
     * Constructor.
     */
    public constructor(Window parent, ...
                       Image secondImage=null, ...) {
        ...
        setSecondImage(secondImage, refresh=false);
        ...
    }


    /**
     * Set second image.
     */
    final public void setSecondImage(Image image, bool refresh=true) {
        if (this.secondImage) this.secondImage.release();
        this.secondImage = image;
        if (this.secondImage) this.secondImage.use();
        if (refresh) refresh();
    }


    /**
     * Before remove event, sent to all children (leaf first) before remove.
     */
    public void beforeRemove() {
        if (secondImage) secondImage.release();
        secondImage = null;
        super();
    }

Another example where we are not using a Window, but relying on finalizer for CustomTitleBar.

    /**
     * Right side icons.
     */
    private Image minimizeIcon;


    /**
     * Build a custom title bar using the config.
     */
    public constructor(FrameWindow parent, TitleBarConfig config) {
        ...
        minimizeIcon = dark ? icon("win/minimizeLight") : icon("win/minimizeDark");
        if (windowIcon) windowIcon.use();
        ...
    }


    /**
     * Retire if GCed.
     */
    private finalizer() {
        if (captionBrush) retire();
    }


    /**
     * Retire.
     */
    final public void retire() {
	    if (windowIcon) windowIcon.release();
	    windowIcon = null;
        ...
    }

Auto remember position and size

AppWindow and DialogWindow have the following overrides, subclasses now will automatically remember their last dialog position and size. If this causes issues with your dialog, you can override the methods in your own dialog to return false.

Old: public bool autoSavePos() { return false; }
New: public bool autoSavePos() { return true; }

Old: public bool autoSaveSize() { return false; }
New: public bool autoSaveSize() { return true; }

ComboTextPainter

setBound(rectI r) now attempts to reset its contained TextPainter width before autoSizing to make consistent initial width calculation when painting text. This potentially affects how text can be painted and truncated automatically.

class TextInputGridCell

Added: public bool keyTab() {}

Advances input focus from a TextInputGridCell to the next cell in the parent GridWindow on user tab-click.

/**
 * Key tab.
 */
public bool keyTab() {
	GridWindow gw = gw();
	if (gw) {
		gw.setFocus();
		gw.keyTab();
	}
	return true;
}

class GridWindow

Changed: extend public str clipboardValueIfAny(int x, int y) {}

  • The clipboardValueIfAny method in the grid was updated to handle MoneySumGridCell objects.

Changed: extend public int appendRow(str label=null, bool update=true) {}

appendRow(..) has been updated to call updateScrollBars() when the update parameter is true.

/**
* Append a new row and return the new row index.
*/
extend public int appendRow(str label=null, bool update=true) {
	...
	
	if (update) {
		updateRowSize(index);
		updateColumnSize(-1, updateRows=false);
New:	updateScrollBars();
		refreshG2();
	}

	...

	return index;
}