SOLVED Can i get global input event ?

Hello everyone~
I have a GeDialogwindow now. and i want to close it when my mouse click in anywhare.
I can get the mouse click event in GeDialog.Message(), but it's just work inside of dialog.
So i think maybe i need to get the global input event . But i can't find any method in SDK.
How do i do that?

Hello @gheyret,

Thank you for reaching out to us. Yes, you can get the 'global' input event with c4d.gui.GetInputEvent, but the problems are that:

  1. When you click outside of the bounds of a dialog it will lose focus, and Cinema 4D will stop calling your dialog, and consider it dormant until it gains focus again. So, you must get hold of this focus event (c4d.BFM_LOSTFOCUS).
  2. A dialog losing focus seems to flush the input event queue, which makes sense, but limits your options a bit.

Find below a solution which likely does what you want to do. I would also recommend opening the dialog as a popup dialog, so that users do not get confused as to why your dialog behaves differently than 'normal' dialogs.

Cheers,
Ferdinand

The result:
dialog_close_oob.gif

The code:

"""Demonstrates how to close a dialog when it lost focus.

This example can be run from the script manager. It will open a dialog that is being closed when the
either clicks the 'Apply' button of the dialog, or clicks out of bounds of the dialog.
"""
import c4d

class MouseDialog (c4d.gui.GeDialog):
    """
    """
    ID_BTN_APPLY: int = 1004

    def CreateLayout(self) -> bool:
        """Called by Cinema 4D to populate the dialog with gadgets.
        """
        self.GroupBegin(1000, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, 1)
        self.GroupBorderSpace(5, 5, 5, 5)

        # Add three sliders.
        self.AddEditSlider(1001, c4d.BFH_SCALEFIT)
        self.AddEditSlider(1002, c4d.BFH_SCALEFIT)
        self.AddEditSlider(1003, c4d.BFH_SCALEFIT)
        
        # And the 'Apply' button.
        self.AddButton(self.ID_BTN_APPLY, c4d.BFH_SCALEFIT, name="Apply")
        self.GroupEnd()
        return super().CreateLayout()

    def Command(self, cid: int, msg: c4d.BaseContainer) -> bool:
        """Called by Cinema 4D on gadget interactions.
        """
        # The apply button has been clicked - do some stuff and then close the dialog.
        if cid == self.ID_BTN_APPLY:
            self.Close()

        return super().Command(cid, msg)

    def Message(self, msg: c4d.BaseContainer, result: c4d.BaseContainer) -> int:
        """Called by Cinema 4D to convey events to dialog.
        """
        # Once the dialog lost focus, close it. This will happen for example when the user clicks
        # out of bounds of the dialog.
        if msg.GetId() == c4d.BFM_LOSTFOCUS:
            # It might be tempting to attempt to poll the mouse device here, so that for example
            # the dialog is only being closed when it lost focus AND the left mouse button is being
            # pressed. This is unfortunately not possible, as the event queue seems to have already
            # been flushed at this point. This has nothing to do with opening the dialog in the 
            # Script Manger or not, the same happens for a CommandData plugin. I also tried 
            # BFM_ACTIVE_CHG and offloading the work to GeDialog.AskClose(), all to the same effect:
            # As soon as the dialog has lost focus, one looses the ability to poll for mouse events,
            # likely because the event queue has already been flushed at this point.
            self.Close()

        return super().Message(msg, result)

# Hack to keep an async dialog alive in a Script Manger environment, please do not do this in a
# production environment.
dlg: MouseDialog = MouseDialog()

def main() -> None:
    """Runs the example.
    """
    # To not to confuse users, a dialog which closes itself once it lost focus, should be opened
    # as a popup dialog. This will result in a dialog without a title bar. I chose here the resizable 
    # popup variant which has no title bar but a border. There is also the non-resizable popup
    # variant c4d.DLG_TYPE_ASYNC_POPUPEDIT which has no title bar and no border.
    dlg.Open(c4d.DLG_TYPE_ASYNC_POPUP_RESIZEABLE, defaultw=300)

if __name__ == '__main__':
    main()

@ferdinand
Wow~ Thank you so much bro