Plugin opens on Mac not correctly

I have a plugin on Mac and PC.
On PC it opens fine (fully), but on the Mac only the top is shown.
The code is the same on PC and Mac.
I realize this is a very open question, but could you give me some hints where to look?
Here some code, that might be related?

class MenuCommand(c4d.plugins.CommandData):
    dialog = None
    area = Area()
    areaRender = AreaRender()
    def Execute(self, doc):
        if self.dialog is None:
            self.dialog = TreeViewDialog(self.area, self.areaRender)
        return self.dialog.Open(c4d.DLG_TYPE_ASYNC, PLUGIN_ID_TGSTEXTUREMANAGER)      #, defaulth=600, defaultw=600) Did not work!

    def RestoreLayout(self, sec_ref):
        if self.dialog is None:
            self.dialog = TreeViewDialog(self.area, self.areaRender)
        return self.dialog.Restore(PLUGIN_ID_TGSTEXTUREMANAGER, secret=sec_ref)
    okyn = c4d.plugins.RegisterCommandPlugin(PLUGIN_ID_TGSTEXTUREMANAGER, PLUGINSTRING, 0, bmp, PLUGINSTRING, MenuCommand())

Screenshot 2023-02-01 at 12.05.39.png

Hello @pim,

Thank you for reaching out to us. I cannot test it right now, because my mac is at home, and I am at work - genius planning, I know 🙂 . I will give it a spin tomorrow, but three things stand out for me which you might be able to answer before I pick up your topic tomorrow.

  1. Judging from your code, your core claim seems to be here that defaulth and defaultw have no effect for GeDialog.Open on macOS, at least when the dialog mode is DLG_TYPE_ASYNC?
  2. Even when you pass no default values, a dialog should automatically resize itself to its minimum boundaries when the layout has been done correctly (using BFH_SCALEFIT and BFV_SCALEFIT on the relevant groups and at least outer group). You unfortunately do not share your dialog code. I would urge you to do so, as helping you will otherwise be hard. As always, when you do not want to share code publicly, you can do so confidentially via sdk_support(at)maxon(dot)net.
  3. The title of your dialog says 'Texture Manager 2.0', which makes it sound like this is a regression. Is that true, did the plugin of yours work in prior versions of Cinema 4D macOS and is only faulting with 2023.x.x ? If so, in which version did it work, and in which revision of 2023 is it failing for you?


MAXON SDK Specialist

Hi Ferdinand,

Thanks for the quick reply.

  1. yes the dialog is DLG_TYPE_ASYNC and defaulth and defaultw have no effect.
  2. I did not put in the complete dialog, because there is a lot of code.

After Adding a menu, here the code showing we use BFH_SCALEFIT|c4d.BFV_SCALEFIT for the top Group.

def CreateLayout(self):
... add menu

# 3 koloms dialog
 self.GroupBegin(ID_GRP_SUB, flags=c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT, cols=3, groupflags =c4d.BFV_GRIDGROUP_ALLOW_WEIGHTS)
 self.GroupBorderSpace(10, 0, 0, 10)  # left, top, right, bottom)       
  1. No, it was there already in previous versions, yet I am nor sure when it started (which version).

And yes, I fully understand it is hard to answer such a generic question.
I was hoping you had seen this issue before.
I will try to delete a lot of code, to find where this issue is coming from.

Note: after resizing the plugin dialog, the next time you open it in the same cinema 4d session, it is ok.


Hey @pim,

thank you for the quick reply, I will have a look tomorrow.


MAXON SDK Specialist

Using "BFH_SCALEFIT|c4d.BFV_SCALEFIT for the top Group" did scale the plugin dialog, but it used the given values (600x600).
So, still the whole dialog was not shown.

I will keep on testing.


Hello @pim,

So, I gave it a spin but I cannot reproduce it, tested with 2023.0 and 2023.1. Which was not too surprising, because a bug on such a high level seemed unlikely. That does not mean that there is not any bug, but without your dialog code we cannot help you. Please also make sure to strip your code from irrelevant things and that is is executable.

What you should check:

  1. Did you make the dialog part of a layout where you scaled the dialog down? When you then load that layout, the dialog will always open in the scaled down state.
  2. There are indicators that you implement a GeUserArea which you use in the dialog. Make sure that it returns its correct minimum height and width.
  3. If that does not help, you should debug your CreateLayout, this looks very much like a layout mistake one can easily make by marking something as not vertically fitting, causing the dialog to minimize its vertical space unless it is forced to be taller.

Find below the code I used.


Opening the dialog without a width or height value or with it. These values do not have any impact because the outmost layout group is set to scale and fit both vertically and horizontally, i.e., the dialog will match the size of its content. The odd border spacings are cause by the values you set in your code.

Screenshot 2023-02-02 at 10.52.04.png

"""MacOS GeDialog.Open Test
import c4d

class SimpleDialog (c4d.gui.GeDialog):
    """Example dialog that does nothing.
    ID_GRP_MAIN: int = 1000

    def CreateLayout(self) -> bool:
        """Called by Cinema 4D to populate the dialog with gadgets.

            SimpleDialog.ID_GRP_MAIN, flags=c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT, cols=3, 
            groupflags =c4d.BFV_GRIDGROUP_ALLOW_WEIGHTS)
        self.GroupBorderSpace(10, 0, 0, 10)

        i: int = 2000
        for _ in range(10):
            i += 1
            self.AddStaticText(i, c4d.BFH_SCALEFIT, 0, 0, f"Item {i}:")
            i += 1
            self.AddEditText(i, c4d.BFH_SCALEFIT)
            i += 1
            self.AddCheckbox(i, c4d.BFH_FIT, 0, 0, "Foo")


        return True

class MenuCommand(c4d.plugins.CommandData):

    DIALOG: SimpleDialog = None
    PLUGIN_ID: int = 1060499
    def Execute(self, doc):
        if MenuCommand.DIALOG is None:
            MenuCommand.DIALOG = SimpleDialog()
        return MenuCommand.DIALOG.Open(c4d.DLG_TYPE_ASYNC, MenuCommand.PLUGIN_ID) #, defaulth=600, defaultw=600)

    def RestoreLayout(self, sec_ref):
        if MenuCommand.DIALOG is None:
            MenuCommand.DIALOG = SimpleDialog()
        return MenuCommand.DIALOG.Restore(MenuCommand.PLUGIN_ID, secret=sec_ref)

def RegisterPlugins() -> bool:
    """Registers the example.
    return c4d.plugins.RegisterCommandPlugin(

if __name__ == '__main__':
    if not RegisterPlugins():
        raise RuntimeError(
            f"Failed to register {MenuCommand} plugin.")

MAXON SDK Specialist

I am stripping the plugin more and more, to see where the error / issue occurs.

PS: I added a point, this might be related to the user area your code is hinting at to be used in the dialog.

MAXON SDK Specialist

Yes, I am using 2 user areas.
I will check.

Oops, my fault!
After stripping a lot of the plugin, I came to the conclusion that
GroupBegin() and GroupEnd() where not matching.

This is apparently more importan on the Mac than on the PC!

Thanks for the patience and the support.


No worries, I am glad that you found your fix!

MAXON SDK Specialist

Alas, I was wrong!
After trying to match GroupBegin() and GroupEnd(), it proved to be ok.
So, back to stripping the code to see where it fails.

Doing that I found out another (related) issue.
After stripping to only using a Treeview, I noticed that the dialog also did open completely. So yes related to the initial issue (I think).


It should open like this (I resized the dialog window myself).

After checking the example "C4D TreeView example", I am beginning to belive the issue is not in the dialog code, but in the filling of the tree.

If you could have a look, I will send the code and some data to sdk_support(at)maxon(dot)net.

Hello @pim,

In addition to my answer via mail, I will also answer here, as this might be interesting for the rest of the community.


So, the question was here "Why does my tree view not open with the right size?". The easy answer to this is that:

  1. You did neither set a minimum size for the dialog in GeDialog.Open().
  2. Nor one for the tree view itself via GeDialog.AddCustomGui().

Both in conjunction did result in your dialog collapsing down to zero height.

What can I do?

Not much, the TreeViewCustomGui is not designed to scale to the size of its content. The underlying question is what you expect to happen here.

a. Just have the tree view have some fixed minimum size, regardless of its content.
b. Have the tree view initialize automatically to size, i.e., when the view has 10 items upon opening, it should have exactly 10 items height.

When it is (a.) what you want, then this is easily doable with the minimum size passed to GeDialog.AddCustomGui(). When it is (b.), then you are more or less out of luck, as a tree view cannot scale automatically to the size of its content.

You can adjust the minimum size dynamically based on the content which is going to be placed in the tree view, but when the content changes, you will have to flush your layout in order to be able to set a new minimum size.

On a practical level it is also not so desirable to have a tree view scale like this, as this minimum height is not well defined. Should it be all items, or just all top level items, i.e., fully collapsed or fully expanded (which is implied by your example as all nodes start out as expanded). Let's say we choose fully collapsed. What happens when you have so many root nodes that the tree view will not fit on screen when making space for all root nodes.



The dialog is set to have a minimum height which matches the total number of nodes in it.



I had to cut here a bit, but the relevant parts are:

class TreeNode:
    # ...
    def __len__(self):
        """(f_hoppe): Counts all descendants of this node, including the node itself.

        Implemented fully recursively. Should be implemented iteratively for production due to stack
        overflows and Python's recursion limit preventing them. Or the data should be acquired
        when textures are collected.
        count: int = 1
        for child in self.children:
            count += len(child)

class ListView(c4d.gui.TreeViewFunctions):
    COLUMN_COUNT: int = 1
    MIN_LINE_HEIGHT: int = 24
    MIN_WIDTH: int = 500

    def __init__(self):
        # The root nodes of the tree view.
        self._rootNodes: list[TreeNode] = []

    def __len__(self):
        """(f_hoppe): Returns the number of tree nodes in the instance.
        return sum([len(node) for node in self._rootNodes])

    def GetMinSize(self) -> tuple[int, int]:
        """(f_hoppe): Returns the minimum GUI size for the data of this ListView instance.
        # But all these classic API pixel values are quite wonky anyways and the tree view does
        # many custom things. So we must do some ugly magic number pushing. Subtracting nine units 
        # from the actual height of each row gave me sort of the best results, but the tree view
        # GUI does not scale linearly in height with the number of rows. Meaning that what looks 
        # good for 5 items might not look good for 50 items.
        return (ListView.MIN_WIDTH, (ListView.MIN_LINE_HEIGHT - 9) * len(self))

    def GetColumnWidth(self, root: TreeNode, userdata: None, obj: TreeNode,
                       col: int, area: c4d.gui.GeUserArea):
        """(f_hoppe): This cannot be a constant value, as we will otherwise clip data.
        return area.DrawGetTextWidth(obj.textureName) + 24

    def GetLineHeight(self, root, userdata, obj, col, area):
        """(f_hoppe): Used constant value.
        return ListView.MIN_LINE_HEIGHT
    # ...

class SimpleDialog (c4d.gui.GeDialog):
    ID_TRV_TEXTURES: int = 1000

    def __init__(self) -> None:
        self._treeView: c4d.gui.TreeViewCustomGui = None
        # This builds the tree node data so that self._listView._rootNodes holds the top level
        # node(s) for the tree managed by this ListView instance.
        self._listView: ListView = self.GetTree()

    def CreateLayout(self) -> bool:
        """(f_hoppe): I substantially rewrote this.
        # Because we already initialized the ListView data, we can use it to compute the minimum
        # size of the gadget.
        w, h = self._listView.GetMinSize()
        print(f"{self._listView.GetMinSize() = }, {len(self._listView) = }")

        # Use these values to define a default size so that it shows all columns. What you want to
        # be done here is sort of not intended by the TreView GUI, although admittedly desirable.
        # The closest thing we can do is set the minimum size for the gadget, so that all lines
        # will fit into it. This will then ofc also have the side effect that the GUI cannot be
        # scaled down beyond this point. You might want to implement ListView.GetMinSize() in a
        # different manner, so that it does not take into account all nodes and instead just the
        # top level nodes.

        self._treeView = self.AddCustomGui(
            flags=c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT,
            minw=w, minh=h,
        if not isinstance(self._treeView, c4d.gui.TreeViewCustomGui):
            raise MemoryError(f"Could not allocate tree view.")

        # If you want to do this at runtime, i.e., load a new texture path, you would have to
        # call GeDialog.LayoutChanged() on the layout group which contains the tree view, add
        # the tree view again (with new min size values), and then call GeDialog.LayoutChanged()
        # on the group. It might be easier to just live with a fixed minimum size just as
        # (300, 300)

        return True
    # ...

MAXON SDK Specialist