Solved API for new behavior of opnening Windows in Layout

Hello,
Is there an Python API already for the new behaviour of some Commands like the Assetbrowser where the Window does open in the Layout not as a floating Window? I would like this for some of my Scripts.

Hello @holgerbiebrach,

please excuse the wait. So, this is possible in Python and quite easy to do. This new behavior is just the old dialog folding which has been reworked a little bit. I have provided a simple example at the end of the posting. There is one problem regarding title bars which is sort of an obstacle for plugin developers which want to distribute their plugins, it is explained in the example below.

I hope this helps and cheers,
Ferdinand

The result:
3453535.gif
The code:

"""Example for a command plugin with a foldable dialog as provided with the
Asset Browser or Coordinate Manger in Cinema 4D R25.

The core of this is just the old GeDialog folding mechanic which has been
changed slightly with R25 as it will now also hide the title bar of a folded
dialog, i.e., the dialog will be hidden completely.

The structure shown here mimics relatively closely what the Coordinate Manger
does. There is however one caveat: Even our internal implementations do not
hide the title bar of a dialog when unfolded. Instead, this is done via 
layouts, i.e., by clicking onto the ≡ icon of the dialog and unchecking the
"Show Window Title" option and then saving such layout. If you would want
to provide a plugin which exactly mimics one of the folding managers, you
would have to either ask your users to take these steps or provide a layout.

Which is not ideal, but I currently do not see a sane way to hide the title
bar of a dialog. What you could do, is open the dialog as an async popup which 
would hide the title bar. But that would also remove the ability to dock the 
dialog. You could then invoke `GeDialog.AddGadegt(c4d.DIALOG_PIN, SOME_ID)`to 
manually add a pin back to your dialog, so that you can dock it. But that is 
not how it is done internally by us, as we simply rely on layouts for that.
"""

import c4d


class ExampleDialog (c4d.gui.GeDialog):
    """Example dialog that does nothing.

    The dialog itself has nothing to do with the implementation of the
    folding.
    """
    ID_GADGETS_START = 1000
    ID_GADGET_GROUP = 0
    ID_GADGET_LABEL = 1
    ID_GADGET_TEXT = 2

    GADGET_STRIDE = 10
    GADEGT_COUNT = 5

    def CreateLayout(self) -> bool:
        """Creates dummy gadgets.
        """
        self.SetTitle("ExampleDialog")
        flags = c4d.BFH_SCALEFIT

        for i in range(self.GADEGT_COUNT):
            gid = self.ID_GADGETS_START + i * self.GADGET_STRIDE
            name = f"Item {i}"

            self.GroupBegin(gid + self.ID_GADGET_GROUP, flags, cols=2)
            self.GroupBorderSpace(5, 5, 5, 5)
            self.GroupSpace(2, 2)
            self.AddStaticText(gid + self.ID_GADGET_LABEL, flags, name=name)
            self.AddEditText(gid + self.ID_GADGET_TEXT, flags)
            self.GroupEnd()
        return True


class FoldingManagerCommand (c4d.plugins.CommandData):
    """Provides the implementation for a command with a foldable dialog.
    """
    ID_PLUGIN = 1058525
    REF_DIALOG = None

    @property
    def Dialog(self) -> ExampleDialog:
        """Returns a class bound ExampleDialog instance.
        """
        if FoldingManagerCommand.REF_DIALOG is None:
            FoldingManagerCommand.REF_DIALOG = ExampleDialog()

        return FoldingManagerCommand.REF_DIALOG

    def Execute(self, doc: c4d.documents.BaseDocument) -> bool:
        """Folds or unfolds the dialog.

        The core of the folding logic as employed by the Asset Browser
        or the Coordinate manager in R25.
        """
        # Get the class bound dialog reference.
        dlg = self.Dialog
        # Fold the dialog, i.e., hide it if it is open and unfolded. In C++
        # you would also want to test for the dialog being visible with
        # GeDialog::IsVisible, but we cannot do that in Python.
        if dlg.IsOpen() and not dlg.GetFolding():
            dlg.SetFolding(True)
        # Open or unfold the dialog. The trick here is that calling
        # GeDialog::Open will also unfold the dialog.
        else:
            dlg.Open(c4d.DLG_TYPE_ASYNC, FoldingManagerCommand.ID_PLUGIN)

        return True

    def RestoreLayout(self, secret: any) -> bool:
        """Restores the dialog on layout changes.
        """
        return self.Dialog.Restore(FoldingManagerCommand.ID_PLUGIN, secret)

    def GetState(self, doc: c4d.documents.BaseDocument) -> int:
        """Sets the command icon state of the plugin.

        This is not required, but makes it a bit nicer, as it will indicate
        in the command icon when the dialog is folded and when not.
        """
        dlg = self.Dialog
        result = c4d.CMD_ENABLED
        if dlg.IsOpen() and not dlg.GetFolding():
            result |= c4d.CMD_VALUE

        return result


def RegisterFoldingManagerCommand() -> bool:
    """Registers the example.
    """
    return c4d.plugins.RegisterCommandPlugin(
        id=FoldingManagerCommand.ID_PLUGIN,
        str="FoldingManagerCommand",
        info=c4d.PLUGINFLAG_SMALLNODE,
        icon=None,
        help="FoldingManagerCommand",
        dat=FoldingManagerCommand())


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

MAXON SDK Specialist
developers.maxon.net

Hello @holgerbiebrach,

thank you for reaching out to us. Please excuse the delay, I have not had the time to pick up your topic yet. There is at least no direct route solution to this like just a registration symbol for GeDialog (at least I see none). I will have to unpack on Monday if all the required interfaces for doing this are public (the likely answer from a first glance is no, but I will have to dig a bit deeper to know for sure).

Cheers,
Ferdinand

MAXON SDK Specialist
developers.maxon.net

Hello @holgerbiebrach,

please excuse the wait. So, this is possible in Python and quite easy to do. This new behavior is just the old dialog folding which has been reworked a little bit. I have provided a simple example at the end of the posting. There is one problem regarding title bars which is sort of an obstacle for plugin developers which want to distribute their plugins, it is explained in the example below.

I hope this helps and cheers,
Ferdinand

The result:
3453535.gif
The code:

"""Example for a command plugin with a foldable dialog as provided with the
Asset Browser or Coordinate Manger in Cinema 4D R25.

The core of this is just the old GeDialog folding mechanic which has been
changed slightly with R25 as it will now also hide the title bar of a folded
dialog, i.e., the dialog will be hidden completely.

The structure shown here mimics relatively closely what the Coordinate Manger
does. There is however one caveat: Even our internal implementations do not
hide the title bar of a dialog when unfolded. Instead, this is done via 
layouts, i.e., by clicking onto the ≡ icon of the dialog and unchecking the
"Show Window Title" option and then saving such layout. If you would want
to provide a plugin which exactly mimics one of the folding managers, you
would have to either ask your users to take these steps or provide a layout.

Which is not ideal, but I currently do not see a sane way to hide the title
bar of a dialog. What you could do, is open the dialog as an async popup which 
would hide the title bar. But that would also remove the ability to dock the 
dialog. You could then invoke `GeDialog.AddGadegt(c4d.DIALOG_PIN, SOME_ID)`to 
manually add a pin back to your dialog, so that you can dock it. But that is 
not how it is done internally by us, as we simply rely on layouts for that.
"""

import c4d


class ExampleDialog (c4d.gui.GeDialog):
    """Example dialog that does nothing.

    The dialog itself has nothing to do with the implementation of the
    folding.
    """
    ID_GADGETS_START = 1000
    ID_GADGET_GROUP = 0
    ID_GADGET_LABEL = 1
    ID_GADGET_TEXT = 2

    GADGET_STRIDE = 10
    GADEGT_COUNT = 5

    def CreateLayout(self) -> bool:
        """Creates dummy gadgets.
        """
        self.SetTitle("ExampleDialog")
        flags = c4d.BFH_SCALEFIT

        for i in range(self.GADEGT_COUNT):
            gid = self.ID_GADGETS_START + i * self.GADGET_STRIDE
            name = f"Item {i}"

            self.GroupBegin(gid + self.ID_GADGET_GROUP, flags, cols=2)
            self.GroupBorderSpace(5, 5, 5, 5)
            self.GroupSpace(2, 2)
            self.AddStaticText(gid + self.ID_GADGET_LABEL, flags, name=name)
            self.AddEditText(gid + self.ID_GADGET_TEXT, flags)
            self.GroupEnd()
        return True


class FoldingManagerCommand (c4d.plugins.CommandData):
    """Provides the implementation for a command with a foldable dialog.
    """
    ID_PLUGIN = 1058525
    REF_DIALOG = None

    @property
    def Dialog(self) -> ExampleDialog:
        """Returns a class bound ExampleDialog instance.
        """
        if FoldingManagerCommand.REF_DIALOG is None:
            FoldingManagerCommand.REF_DIALOG = ExampleDialog()

        return FoldingManagerCommand.REF_DIALOG

    def Execute(self, doc: c4d.documents.BaseDocument) -> bool:
        """Folds or unfolds the dialog.

        The core of the folding logic as employed by the Asset Browser
        or the Coordinate manager in R25.
        """
        # Get the class bound dialog reference.
        dlg = self.Dialog
        # Fold the dialog, i.e., hide it if it is open and unfolded. In C++
        # you would also want to test for the dialog being visible with
        # GeDialog::IsVisible, but we cannot do that in Python.
        if dlg.IsOpen() and not dlg.GetFolding():
            dlg.SetFolding(True)
        # Open or unfold the dialog. The trick here is that calling
        # GeDialog::Open will also unfold the dialog.
        else:
            dlg.Open(c4d.DLG_TYPE_ASYNC, FoldingManagerCommand.ID_PLUGIN)

        return True

    def RestoreLayout(self, secret: any) -> bool:
        """Restores the dialog on layout changes.
        """
        return self.Dialog.Restore(FoldingManagerCommand.ID_PLUGIN, secret)

    def GetState(self, doc: c4d.documents.BaseDocument) -> int:
        """Sets the command icon state of the plugin.

        This is not required, but makes it a bit nicer, as it will indicate
        in the command icon when the dialog is folded and when not.
        """
        dlg = self.Dialog
        result = c4d.CMD_ENABLED
        if dlg.IsOpen() and not dlg.GetFolding():
            result |= c4d.CMD_VALUE

        return result


def RegisterFoldingManagerCommand() -> bool:
    """Registers the example.
    """
    return c4d.plugins.RegisterCommandPlugin(
        id=FoldingManagerCommand.ID_PLUGIN,
        str="FoldingManagerCommand",
        info=c4d.PLUGINFLAG_SMALLNODE,
        icon=None,
        help="FoldingManagerCommand",
        dat=FoldingManagerCommand())


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

MAXON SDK Specialist
developers.maxon.net

Thank you @ferdinand . This is awesome. Thanks for taking the time.

Hello @HolgerBiebrach,

we will set this topic to 'Solved' when there are no further questions or replies until Monday, November the 22th.

Thank you for your understanding,
Ferdinand

MAXON SDK Specialist
developers.maxon.net