Open Search
    GeDialog Manual

    About

    GeDialog is the base class for interface elements of Cinema 4D. An asynchronous (non-modal) dialog acts as a panel that can be added to the application layout or as a free floating window. In contrast a synchronous (modal) dialog blocks the application's main thread until it is closed. A custom dialog is added by creating a subclass of GeDialog. GeDialog is also the base class of iCustomGui which is used to create custom GUI elements for NodeData parameters.

    Allocation

    A GeDialog based window is typically owned by a CommandData plugin. This CommandData stores and opens the dialog instance.

    // This CommandData example opens and restores a GeDialog based window.
    // g_geDialogID is the plugin ID of the CommandData plugin.
    class OpenExampleDialog : public CommandData
    {
    INSTANCEOF(OpenExampleDialog, CommandData)
    private:
    ExampleDialog _dialog;
    public:
    // "Execute" function opens the dialog window
    Bool Execute(BaseDocument* doc, GeDialog* parentManager)
    {
    // open the dialog window if it is not already open
    if (_dialog.IsOpen() == false)
    _dialog.Open(DLG_TYPE::ASYNC, g_geDialogID, -1, -1, 400, 400);
    else
    _dialog.Close();
    return true;
    };
    // "RestoreLayout" is called to restore the dialog after a layout change
    Bool RestoreLayout(void* secret)
    {
    return _dialog.RestoreLayout(g_geDialogID, 0, secret);
    };
    static OpenExampleDialog* Alloc() { return NewObjClear(OpenExampleDialog); }
    };
    #define INSTANCEOF(X, Y)
    Definition: c4d_baselist.h:38
    Definition: c4d_basedocument.h:497
    Definition: c4d_commanddata.h:65
    virtual Bool RestoreLayout(void *secret)
    virtual Bool Execute(BaseDocument *doc, GeDialog *parentManager)
    Definition: c4d_gui.h:1136
    maxon::Bool Bool
    Definition: ge_sys_math.h:55
    @ ASYNC
    Non-modal (asynchronous) dialog.
    const char * doc
    Definition: pyerrors.h:226

    The GeDialog is opened with GeDialog::Open(). The different types of dialogs are:

    The GeDialog window can be closed with GeDialog::Close(). This will call GeDialog::AskClose() (see below).

    GeDialog Based Classes

    A custom window is created by implementing a subclass of GeDialog. This subclass can implement different virtual functions to define the layout and behaviour of the dialog.

    Dialog setup:

    // create the layout
    Bool CreateLayout()
    {
    SetTitle("Example Dialog"_s);
    AddEditNumber(1000, BFH_SCALEFIT, 0, 10);
    // sets the timer to 1000 milliseconds
    this->SetTimer(1000);
    return true;
    }
    // Initialize internal data and set the values
    Bool InitValues()
    {
    SetFloat(1000, 123.456);
    return true;
    }
    @ BFH_SCALEFIT
    Scale fit. BFH_SCALE|BFH_FIT.
    Definition: gui.h:316

    Messages and Interaction:

    // This "Command" function checks if the gadget with the ID 1000 was changed.
    Bool Command(Int32 id, const BaseContainer& msg)
    {
    if (id == 1000)
    {
    ApplicationOutput("Gadget 1000 was changed.");
    }
    return GeDialog::Command(id, msg);
    }
    // This "Message" function checks if any interaction in the GeDialog starts or ends.
    {
    // messages are identified
    // by the BaseContainer ID
    switch (msg.GetId())
    {
    {
    ApplicationOutput("Interaction start");
    break;
    }
    {
    ApplicationOutput("Interaction end");
    break;
    }
    }
    return SUPER::Message(msg, result);
    }
    Definition: c4d_basecontainer.h:48
    virtual Bool Command(Int32 id, const BaseContainer &msg)
    PyObject PyObject * result
    Definition: abstract.h:43
    maxon::Int32 Int32
    Definition: ge_sys_math.h:60
    @ BFM_INTERACTSTART
    Definition: gui.h:922
    @ BFM_INTERACTEND
    Sent when user interaction ends.
    Definition: gui.h:941
    #define ApplicationOutput(formatString,...)
    Definition: debugdiagnostics.h:210
    const char const char * msg
    Definition: object.h:438

    Timed Actions:

    // This "Timer" function is called depending on the value set with SetTimer()
    void Timer(const BaseContainer& msg)
    {
    ApplicationOutput("Tick..");
    }

    Read-Only Properties

    It is possible to check these GeDialog properties:

    It is also possible to read the pixel ratio of the current screen:

    // This example adds a BitmapButton to the layout of the GeDialog.
    // Depending on the pixel ratio a low-res or high-res bitmap file is loaded
    // and applied to the BitmapButton.
    void* const customGUI = AddCustomGui(1000, CUSTOMGUI_BITMAPBUTTON, ""_s, BFH_SCALEFIT, 300, 300, settings);
    BitmapButtonCustomGui* const bitmapButtonGUI = static_cast<BitmapButtonCustomGui*>(customGUI);
    if (bitmapButtonGUI)
    {
    // allocate bitmap
    if (bitmap)
    {
    // get pixel ratio
    const Float pixelRatio = GetPixelRatio();
    // check if Retina or not
    if (pixelRatio == 1.0)
    filename = "lowRes.png";
    else
    filename = "highRes.png";
    // load bitmap (GetFullFilename() is just a custom utility function)
    const String fullFileName = GetFullFilename(filename);
    if (bitmap->Init(fullFileName) == IMAGERESULT::OK)
    {
    // store ratio
    bitmap->SetData(BASEBITMAP_DATA_GUIPIXELRATIO, pixelRatio);
    // apply to BitmapButton
    bitmapButtonGUI->SetImage(bitmap, true, false);
    }
    }
    }
    PyCompilerFlags const char * filename
    Definition: ast.h:15
    Definition: ge_autoptr.h:37
    Definition: customgui_bitmapbutton.h:119
    Bool SetImage(BaseBitmap *bmp, Bool copybmp, Bool secondstate=false)
    Definition: c4d_string.h:39
    #define CUSTOMGUI_BITMAPBUTTON
    Bitmap button custom GUI ID.
    Definition: customgui_bitmapbutton.h:25
    maxon::Float Float
    Definition: ge_sys_math.h:66
    #define BASEBITMAP_DATA_GUIPIXELRATIO
    Float.
    Definition: c4d_basebitmap.h:104
    @ OK
    Image loaded/created.

    Layout

    Create Layout

    The layout of a GeDialog - the arrangement of groups and gadgets - is defined in the implementation of GeDialog::CreateLayout(). This layout can be defined by creating the groups and gadgets individually or by loading a dialog resource file.

    General functions to handle GeDialog gadgets are:

    // This example loads a dialog layout from a resource file and edits it.
    Bool CreateLayout()
    {
    // call default CreateLayout()
    return false;
    // load dialog from resource file
    if (!LoadDialogResource(DLG_CUSTOM_DIALOG, nullptr, 0))
    return false;
    // set a different title
    this->SetTitle("New Title"_s);
    // disable a GUI element
    this->Enable(IDC_CUSTOM_CHECKBOX, false);
    return true;
    }
    virtual Bool CreateLayout()

    Menus

    GeDialog based windows contain a menu bar. This menu bar can contain various sub-menus and also any kind of gadget in a special sub-group.

    Menus are created with:

    Sub-menus are added with these functions:

    Menu items are added with these functions:

    // This example creates two sub-menus.
    MenuFlushAll();
    // submenu "Cinema Commands"
    MenuSubBegin("Cinema 4D Commands"_s);
    // add Cinema 4D commands
    const Int32 saveAsCommandID = 12218; // "Save as" Command
    const Int32 saveCommandID = 12098; // "Save" Command
    MenuAddCommand(saveAsCommandID);
    MenuAddCommand(saveCommandID);
    MenuSubEnd();
    // submenu "Dialog Commands"
    MenuSubBegin("Dialog Commands"_s);
    // add dialog commands
    MenuAddString(ID_COMMAND_A, "Action A"_s);
    MenuAddSeparator();
    MenuAddString(ID_COMMAND_B, "Action B"_s);
    MenuSubEnd();
    MenuFinished();
    // set state
    MenuInitString(ID_COMMAND_B, true, true); // set as checked

    It is possible to add arbitrary gadgets to a special sub-group of the menu bar:

    // This example adds a group to the menu bar.
    // This menu group contains a button.
    GroupBeginInMenuLine();
    GroupBegin(1000, BFH_RIGHT, 2, 1, ""_s, 0);
    AddButton(ID_MENU_BUTTON, BFH_SCALEFIT, 0, 10, "Button"_s);
    GroupEnd();
    GroupEnd();
    @ BFH_RIGHT
    Aligned to the right. 1<<4.
    Definition: gui.h:313

    Groups

    The arrangement of elements of the dialog layout is defined with groups. Such groups can define rows and columns and can contain further sub-groups.

    Groups are created with these functions:

    // This example creates a group with two columns.
    // The subgroup within the left column contains a tab group with two tabs.
    // parent group with the whole width of the GeDialog
    GroupBegin(100, BFH_SCALEFIT, 2, 0, ""_s, 0, 0, 300);
    // left subgroup
    GroupBegin(200, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, ""_s, 0, 0, 0);
    // tab parent group within the left subgroup
    TabGroupBegin(210, BFH_SCALEFIT | BFV_SCALEFIT);
    // first tab group
    GroupBegin(220, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, "Tab 1"_s, 0, 400, 300);
    AddStaticText(221, BFH_SCALEFIT | BFH_LEFT, 0, 10, "Tab 1"_s, BORDER_NONE);
    GroupEnd();
    // second tab group
    GroupBegin(230, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, "Tab 2"_s, 0, 400, 300);
    AddStaticText(231, BFH_SCALEFIT | BFH_LEFT, 0, 10, "Tab 2"_s, BORDER_NONE);
    GroupEnd();
    GroupEnd();
    GroupEnd();
    // right subgroup
    GroupBegin(300, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, ""_s, 0, 0, 0);
    AddStaticText(301, BFH_SCALEFIT | BFH_LEFT, 0, 10, "Right subgroup"_s, BORDER_NONE);
    GroupEnd();
    GroupEnd();
    @ BORDER_NONE
    No border.
    Definition: gui.h:256
    @ BFH_LEFT
    Aligned to the left. 1<<3.
    Definition: gui.h:312
    @ BFV_SCALEFIT
    Scale fit. BFV_SCALE|BFV_FIT.
    Definition: gui.h:308

    The border style and spacing of groups can be defined with these functions:

    // This example creates a group with space settings and a border.
    GroupBegin(600, BFH_SCALEFIT, 2, 1, "Group"_s, BFV_BORDERGROUP_CHECKBOX, 0, 100);
    GroupSpace(100, 0); // space between group elements
    GroupBorder(BORDER_GROUP_IN); // group border
    GroupBorderSpace(10, 10, 10, 10); // space around the group
    AddButton(601, BFH_SCALEFIT | BFV_SCALEFIT, 0, 0, "Button A"_s);
    AddButton(602, BFH_SCALEFIT | BFV_SCALEFIT, 0, 0, "Button B"_s);
    GroupEnd();
    @ BFV_BORDERGROUP_CHECKBOX
    Checkbox in title of a border group.
    Definition: gui.h:195
    @ BORDER_GROUP_IN
    Group border inside.
    Definition: gui.h:261

    If the group flag BFV_GRIDGROUP_ALLOW_WEIGHTS is set the user can change the width/height of group's columns/rows. The size of columns/rows is stored as "weights" which can be stored.

    See also BFM_WEIGHTS_CHANGED in GUI and Interaction Messages Manual.

    // This example creates a group with two columns.
    // The width of these columns can be changed by the user.
    GroupBegin(WEIGHT_GROUP, BFH_SCALEFIT | BFV_SCALEFIT, 3, 1, ""_s, BFV_GRIDGROUP_ALLOW_WEIGHTS);
    GroupBegin(GROUP_LEFT, BFH_SCALEFIT | BFV_SCALEFIT, 1, 1, ""_s, 0);
    AddStaticText(100, 0, 0, 10, "Left Column"_s, 0);
    GroupEnd();
    AddSeparatorV(1);
    GroupBegin(GROUP_RIGHT, BFH_SCALEFIT | BFV_SCALEFIT, 1, 1, ""_s, 0);
    AddStaticText(200, 0, 0, 10, "Right Column"_s, 0);
    GroupEnd();
    GroupEnd();
    GroupWeightsLoad(WEIGHT_GROUP, _weights);
    @ BFV_GRIDGROUP_ALLOW_WEIGHTS
    Allow the user to move the weights.
    Definition: gui.h:199

    The content of a group can be flushed and replaced with new gadgets:

    // This example flushes a layout group and fills it with new gadgets.
    // flush group
    LayoutFlushGroup(100);
    // add new gadget
    AddStaticText(101, BFH_SCALEFIT | BFH_LEFT, 0, 10, "New Element"_s, BORDER_NONE);
    // update group
    LayoutChanged(100);
    // This example flushes the group with the ID 1000 and adds new elements to it.
    // begin layout change and store data
    UpdateDialogHelper updateDialog = BeginLayoutChange(1000, true);
    // add new elements to flushed group
    AddStaticText(1100, BFH_SCALEFIT, 0, 10, "Add"_s, BORDER_NONE);
    AddStaticText(1101, BFH_SCALEFIT, 0, 10, "new"_s, BORDER_NONE);
    AddStaticText(1102, BFH_SCALEFIT, 0, 10, "elements"_s, BORDER_NONE);
    // update group
    updateDialog.CommitChanges();
    Helper class for GeDialog::BeginLayoutChange()
    Definition: c4d_gui.h:1098

    A scroll group allows to scroll the content of a group using the mouse-wheel:

    Note
    A scroll group must always contain another sub-group that stores the actual gadgets.

    See also BFM_SETSTATUSBAR in Specific GUI Elements.

    // This example creates a scroll group and fills it with multiple gadgets.
    ScrollGroupBegin(500, BFH_SCALEFIT, SCROLLGROUP_VERT | SCROLLGROUP_BORDERIN, 0, 100);
    // layout group within the scroll group
    GroupBegin(501, BFV_TOP | BFH_SCALEFIT, 1, 0, ""_s, 0);
    // add gadgets
    for (Int32 i = 0; i < 100; ++i)
    AddCheckbox(502 + i, BFH_SCALEFIT | BFH_LEFT, 0, 10, "element"_s);
    GroupEnd();
    GroupEnd();
    Py_ssize_t i
    Definition: abstract.h:645
    @ SCROLLGROUP_VERT
    Allow the group to scroll vertically.
    Definition: gui.h:439
    @ SCROLLGROUP_BORDERIN
    Display a small border around the scroll group.
    Definition: gui.h:443
    @ BFV_TOP
    Aligned to the top. 1<<0.
    Definition: gui.h:304
    // This example gets the visible area of a scroll group and changes it.
    Int32 x1 = NOTOK;
    Int32 y1 = NOTOK;
    Int32 x2 = NOTOK;
    Int32 y2 = NOTOK;
    // access the visible area of the scroll group
    if (GetVisibleArea(scrollGroupID, &x1, &y1, &x2, &y2))
    {
    const Int32 height = y2 - y1;
    SetVisibleArea(scrollGroupID, x1, 0, x2, height);
    }
    #define NOTOK
    Definition: ge_sys_math.h:267

    Note that GeDialog::GetItemDim will take scrollbars of a scroll group into account, while GeDialog::SetVisibleArea will not. When computing the scroll area of a scroll group, e.g., for centering a gadget inside the scroll group, this must be taken into account, as otherwise GeDialog::SetVisibleArea will on scroll frames that are larger than the visible area.

    maxon::Result<void> CenterScrollGroup(const Int32 idScrollGroup, const Int32 idGadget)
    {
    // Get the gadget dimensions of the scroll group and the gadget.
    Int32 sgx, sgy, scrollWidth, scrollHeight;
    Int32 gx, gy, gadgetWidth, gadegtHeight;
    if (!GetItemDim(idScrollGroup, &sgx, &sgy, &scrollWidth, &scrollHeight) ||
    !GetItemDim(idGadget, &gx, &gy, &gadgetWidth, &gadegtHeight))
    return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not get item dimensions."_s);
    // GetItemDim() takes the scrollbars of a gadget into account, but SetVisibleArea() does not,
    // since the scrollbars are not part of the visible area. So, when it's about to center something,
    // or mix these two methods in any other way, these scrollbars need to be taken into account.
    // Compute the scroll bars as the delta between the visible are of the the scroll group and its
    // total size.
    Int32 svax1, svay1, svax2, svay2;
    if (!GetVisibleArea(idScrollGroup, &svax1, &svay1, &svax2, &svay2))
    return maxon::UnexpectedError(MAXON_SOURCE_LOCATION,
    "Could not get visible area of scroll group."_s);
    Int32 dx = (scrollWidth - (svax2 - svax1));
    Int32 dy = (scrollHeight - (svay2 - svay1));
    // Subtract the deltas from the width and height of the scroll group, compute the x and y offsets
    // for a centered gadget.
    Int32 realScrollWidth = scrollWidth - dx;
    Int32 realScrollHeight = scrollHeight - dy;
    Int32 x = static_cast<Int32>(((gadgetWidth - realScrollWidth) * .5));
    Int32 y = static_cast<Int32>(((gadegtHeight - realScrollHeight) * .5));
    // With that information, center the scroll group on the gadget.
    if (!SetVisibleArea(idScrollGroup, x, y, x + realScrollWidth, y + realScrollHeight))
    return maxon::UnexpectedError(MAXON_SOURCE_LOCATION,
    "Could not set visible area of scroll group."_s);
    return maxon::OK;
    }
    PyObject * x
    Definition: bytesobject.h:38
    return OK
    Definition: apibase.h:2747
    #define MAXON_SOURCE_LOCATION
    Definition: memoryallocationbase.h:67
    #define iferr_scope
    Definition: resultbase.h:1386

    Gadget IDs

    A gadget that is part of a GeDialog layout can be identified in two ways:

    • With a GadgetPtr. This GadgetPtr is returned by functions creating the gadget in question.
    • With the gadget ID. This gadget ID is used with the function that creates the gadget in question.
    // This example creates two text edit fields.
    // One edit field is edited using the C4DGadget pointer,
    // the other using just the ID.
    // add text edit field and store the C4DGadget pointer
    C4DGadget* const textA = AddEditText(ID_TEXT_A, BFH_SCALEFIT, 0, 10, 0);
    if (textA)
    SetString(textA, "This is some text."_s);
    // add text edit field
    AddEditText(ID_TEXT_B, BFH_SCALEFIT, 0, 10, 0);
    SetString(ID_TEXT_B, "This is some other text."_s);
    Represents a gadget in a dialog.
    Definition: c4d_gui.h:107

    Default Gadgets

    Default GUI gadgets can be added to a GeDialog with these member functions:

    Number related gadgets:

    Text related gadgets:

    Color related gadgets:

    Buttons:

    Seperators:

    Further gadgets are:

    // This example just adds some generic gadgets to the layout.
    AddCheckbox(1000, BFH_SCALEFIT, 0, 10, "Checkbox"_s);
    AddButton(2000, BFH_SCALEFIT, 0, 10, "Button"_s);
    AddSeparatorH(0, BFH_SCALEFIT);
    AddEditText(3000, BFH_SCALEFIT, 0, 10, 0);
    // This example adds a list view to the GeDialog layout.
    private:
    SimpleListView _listview;
    public:
    Bool CreateLayout()
    {
    SetTitle("Listview Dialog"_s);
    // add list view element to layout
    AddListView(1000, BFH_SCALEFIT | BFV_SCALEFIT, 0, 0);
    _listview.AttachListView(this, 1000);
    return true;
    }
    Bool InitValues()
    {
    // configure listview columns
    BaseContainer layout;
    layout.SetInt32(CHECKBOX, LV_COLUMN_CHECKBOX);
    _listview.SetLayout(2, layout);
    // don't allow multi selection
    // fill with content
    Int32 line = 0;
    data.SetBool(CHECKBOX, true);
    data.SetString(NAME, "Element A"_s);
    _listview.SetItem(line, data);
    ++line; // increment ID
    data.SetBool(CHECKBOX, false);
    data.SetString(NAME, "Element B"_s);
    _listview.SetItem(line, data);
    // update
    _listview.DataChanged();
    return true;
    }
    void SetBool(Int32 id, Bool b)
    Definition: c4d_basecontainer.h:572
    void SetString(Int32 id, const maxon::String &s)
    Definition: c4d_basecontainer.h:643
    void SetInt32(Int32 id, Int32 l)
    Definition: c4d_basecontainer.h:579
    void DataChanged()
    Forces a recalculation. Called when the list view data has been changed.
    Bool AttachListView(GeDialog *cd, Int32 id)
    A class for simple list views. See ListView.cpp SDK example.
    Definition: c4d_listview.h:148
    Bool SetLayout(Int32 columns, const BaseContainer &data)
    Bool SetProperty(Int32 id, Int32 val)
    Bool SetItem(Int32 id, const BaseContainer &data)
    @ LV_COLUMN_CHECKBOX
    Checkbox.
    Definition: ge_prepass.h:5390
    @ LV_COLUMN_TEXT
    Text.
    Definition: ge_prepass.h:5387
    #define SLV_MULTIPLESELECTION
    Multiple selection allowed.
    Definition: c4d_listview.h:141
    const char const char const char int line
    Definition: object.h:440
    #define NAME
    Definition: token.h:14

    A multi-line text edit field can be created and configured with these functions:

    See also Specific GUI Elements.

    // This example creates a multi line edit field with Python syntax highlighting.
    AddMultiLineEditText(4000, BFH_SCALEFIT, 0, 200, style);
    // set some python code
    SetString(4000, "import c4d\n\nprint(\"hello world\")\n"_s);
    // set the cursor position at the beginning of the last line
    SetMultiLinePos(4000, 4, 0);
    @ DR_MULTILINE_MONOSPACED
    Monospaced font.
    Definition: gui.h:319
    @ DR_MULTILINE_PYTHON
    Python line return handling.
    Definition: gui.h:326
    @ DR_MULTILINE_SYNTAXCOLOR
    Python syntax highlighting.
    Definition: gui.h:320
    @ DR_MULTILINE_STATUSBAR
    Display a statusbar with the cursor position.
    Definition: gui.h:321
    @ DR_MULTILINE_WORDWRAP
    Word wrap multi-line field.
    Definition: gui.h:327
    @ DR_MULTILINE_HIGHLIGHTLINE
    Highlight lines.
    Definition: gui.h:322

    Several gadgets can contain multiple child-elements, typically for selection purposes:

    // This example creates a combo box and a combo button.
    AddComboBox(5000, BFH_LEFT, 100, 10, false);
    AddChild(5000, 0, "Child 0"_s);
    AddChild(5000, 1, "Child 1"_s);
    AddChild(5000, 2, "Child 2"_s);
    AddComboButton(6000, BFH_LEFT, 100, 10, 0);
    BaseContainer children;
    children.SetString(0, "Child 0"_s);
    children.SetString(1, "Child 1"_s);
    AddChildren(6000, children);
    // This example creates a combo box and defines the used icons.
    // loading the icon data
    IconData data1, data2;
    GetIcon(Ocube, &data1);
    GetIcon(Osphere, &data2);
    // creating the combo box
    // the address of the icon data is written into the String
    AddComboBox(5001, BFH_LEFT, 100, 10, false);
    const String iconData1Address = String::HexToString((UInt) & data1);
    AddChild(5001, 0, "Cube &" + iconData1Address + "&");
    const String iconData2Address = String::HexToString((UInt) & data2);
    AddChild(5001, 1, "Sphere &" + iconData2Address + "&");
    static String HexToString(UInt32 v, Bool prefix0x=true)
    Definition: c4d_string.h:478
    maxon::UInt UInt
    Definition: ge_sys_math.h:65
    #define Ocube
    Cube.
    Definition: ge_prepass.h:1110
    #define Osphere
    Sphere.
    Definition: ge_prepass.h:1111
    Bool GetIcon(Int32 lIconID, IconData *pData)
    Represents a single icon in a large bitmap array.
    Definition: operatingsystem.h:771

    Further interaction with gadgets is typically done by sending messages to the gadget:

    See also GUI and Interaction Messages Manual.

    // This example creates a "Progress Bar" custom GUI element.
    const BaseContainer settings;
    AddCustomGui(10000, CUSTOMGUI_PROGRESSBAR, String(), flags, SizePix(100), SizePix(12), settings);
    PyCompilerFlags * flags
    Definition: ast.h:14
    Int32 SizePix(Int32 pixels)
    Definition: c4d_gui.h:3214
    #define CUSTOMGUI_PROGRESSBAR
    Progress bar.
    Definition: lib_description.h:227
    @ BFV_FIT
    Fit. BFV_BOTTOM|BFV_TOP.
    Definition: gui.h:306
    // This example sends a message to the progress bar custom GUI element.
    m.SetBool(BFM_STATUSBAR_PROGRESSON, true);
    m.SetFloat(BFM_STATUSBAR_PROGRESS, _someValue);
    m.SetData(BFM_STATUSBAR_TINT_COLOR, Vector(0.0, 0.0, 1.0));
    SendMessage(10000, m);
    const char * m
    Definition: abstract.h:692
    maxon::Vec3< maxon::Float64, 1 > Vector
    Definition: ge_math.h:145
    @ BFM_STATUSBAR_PROGRESSON
    Bool Statusbar active.
    Definition: gui.h:838
    @ BFM_STATUSBAR_TINT_COLOR
    Int32 Color ID for the status bar, or as RGB value (Vector).
    Definition: gui.h:844
    @ BFM_SETSTATUSBAR
    To set a statusbar (e.g. inside a SCROLLGROUP (dialog element)):
    Definition: gui.h:836
    @ BFM_STATUSBAR_PROGRESS
    Float Between 0.0 and 1.0.
    Definition: gui.h:840

    Images

    There is no default gadget to display an image (BaseBitmap) in a GeDialog. Two possible solutions are:

    // This example creates a BitmapButtonCustomGui to present an image.
    const Int32 width = SizePix(300);
    const Int32 height = SizePix(50);
    BaseContainer settings;
    settings.SetBool(BITMAPBUTTON_BUTTON, false);
    void* const customGUI = AddCustomGui(5050, CUSTOMGUI_BITMAPBUTTON, ""_s, flags, width, height, settings);
    BitmapButtonCustomGui* const bitmapButtonGUI = static_cast<BitmapButtonCustomGui*>(customGUI);
    if (bitmapButtonGUI)
    {
    bitmapButtonGUI->SetImage(buttonBitmap, true, false);
    }
    #define BITMAPBUTTON_BUTTON
    Definition: customgui_bitmapbutton.h:35
    @ BFH_CENTER
    Centered horizontally.
    Definition: gui.h:311
    unsigned long Py_ssize_t width
    Definition: pycore_traceback.h:88

    Custom GUI Elements

    Further gadgets are implemented as custom GUI elements. Such custom GUI elements have to be created using their type ID.

    See Custom GUI Elements.

    Note
    A custom GUI is typically configured using a BaseContainer given to GeDialog::AddCustomGui(). Only the parameters accessible with the custom GUI class itself can be changed afterwards.
    Custom GUI element instances are based on BaseCustomGui. Using this base class it is possible to further edit the instance.
    Some "custom GUIs" are no real custom GUI elements but are hardcoded in the DescriptionCustomGui and cannot be used in a GeDialog (e.g. CUSTOMGUI_VECTOR).
    // This example creates and configures a "Hyperlink" custom GUI element.
    BaseContainer hlSettings;
    hlSettings.SetBool(HYPERLINK_IS_LINK, true);
    const Int32 customGuiID = CUSTOMGUI_HYPER_LINK_STATIC;
    void* const customGui = AddCustomGui(11000, customGuiID, String(), BFH_SCALEFIT, 100, 50, hlSettings);
    HyperLinkCustomGui* const linkGustomGui = static_cast<HyperLinkCustomGui*>(customGui);
    if (linkGustomGui != nullptr)
    {
    const String linkTitle { "MAXON" };
    const String url { "https://www.maxon.net" };
    linkGustomGui->SetLinkString(&url, &linkTitle);
    }
    #define HYPERLINK_IS_LINK
    Bool true for hyperlinks, static text otherwise.
    Definition: customgui_hyperlink.h:34
    // This example accesses a custom GUI element that is part of the GeDialog.
    // The value presented in the GUI element is first set using the specific GUI element class.
    // Alternatively one can also set the value using the BaseCustomGui base class.
    // search for custom gui gadget
    void* const customGUI = FindCustomGui(ID_GRADIENT, CUSTOMGUI_GRADIENT);
    // cast into specific class
    GradientCustomGui* const gradientGUI = static_cast<GradientCustomGui*>(customGUI);
    if (gradientGUI)
    {
    // set data
    AutoAlloc<Gradient> gradientData;
    if (gradientData)
    {
    // set gradient data using the GradientCustomGui class function
    // add a knot
    firstKnot.pos = 0.25;
    firstKnot.col = maxon::Color(1.0, 0, 0); // red
    gradientData->InsertKnot(firstKnot);
    gradientGUI->SetGradient(gradientData);
    // set gradient data using the BaseCustomGui class function
    // add a knot
    maxon::GradientKnot secondKnot;
    secondKnot.pos = 0.75;
    secondKnot.col = maxon::Color(0, 0, 1.0); // blue
    gradientData->InsertKnot(secondKnot);
    // store as GeData
    GeData geData;
    geData.SetCustomDataType<Gradient>(gradientData);
    // store as TriState
    TriState<GeData> triStateData;
    triStateData.Add(geData);
    // set data of the GUI element
    gradientGUI->SetData(triStateData);
    }
    }
    Bool SetData(const TriState< GeData > &tristate)
    Definition: customgui_base.h:108
    Definition: c4d_gedata.h:83
    void SetCustomDataType(const DATATYPE &v)
    Definition: c4d_gedata.h:747
    Definition: customgui_gradient.h:307
    Bool SetGradient(Gradient *grad)
    Definition: customgui_gradient.h:117
    Definition: c4d_gui.h:969
    void Add(const TYPE &val)
    Definition: c4d_gui.h:1015
    #define CUSTOMGUI_GRADIENT
    Gradient custom GUI ID.
    Definition: customgui_gradient.h:22
    Col3< Float, 1 > Color
    Definition: vector.h:84
    Represents a knot in a gradient.
    Definition: gradient.h:40
    Float pos
    Position.
    Definition: gradient.h:43
    Color col
    Color.
    Definition: gradient.h:41

    GeUserAreas

    GeUserArea is the base class for all dialog gadgets. A subclass of GeUserArea defines a new custom gadget. Such a custom gadget can be added to the GeDialog.

    See also GeUserArea Manual.

    // This example adds a user area (that is owned by the GeDialog) to the layout.
    C4DGadget* userarea = this->AddUserArea(12000, BFH_LEFT, 300, 100);
    if (userarea)
    this->AttachUserArea(_userArea, userarea);

    Sub-Dialogs

    It is possible to add a sub-dialog to a given GeDialog based window. Such a sub-dialog is based on SubDialog which in return is also based on GeDialog.

    // This example adds a SubDialog based sub-dialog to the layout.
    AddSubDialog(14000, BFH_SCALEFIT | BFV_SCALEFIT);
    AttachSubDialog(&_subDialog, 14000);

    For convenience it is possible to add a default group of gadgets to the dialog. This group can contain the standard buttons "OK" or"Cancel". See DLG.

    // This example adds the generic "OK" and "Cancel" buttons to the layout.
    AddDlgGroup(DLG_OK | DLG_CANCEL);
    @ DLG_OK
    OK button.
    Definition: ge_prepass.h:3877
    @ DLG_CANCEL
    Cancel button.
    Definition: ge_prepass.h:3878
    // The "Command" function is called when a button is pressed.
    Bool Command(Int32 id, const BaseContainer& msg)
    {
    switch (id)
    {
    // the "OK" button of a AddDlgGroup() group, IDC_CANCEL is the other
    case IDC_OK:
    {
    ApplicationOutput("OK was pressed");
    break;
    }
    }
    return true;
    }
    #define IDC_OK
    Definition: c4d_gui.h:15

    Pixel Size

    For most gadgets it is possible to define the size of the gadget. To define this size one must use these functions to bake the pixel size:

    • SizePix(): Bakes the given pixel size.
    • SizeChr(): Bakes the given character count into a size.
    • SizePixChr(): Bakes the given pixel size and character count.
    // This example creates a button with a height of 50 pixels.
    AddButton(15000, BFH_SCALEFIT, 0, SizePix(50), ""_s);

    Layout Flags

    The position and behaviour of groups and gadgets is defined with these flags:

    Horizontal alignment:

    Vertical alignment:

    // This example creates a group with three subgroups.
    // The left and right subgroup have a fixed width.
    // The center subgroup is scaled horizontally.
    // the parent group with three columns
    GroupBegin(20000, BFH_SCALEFIT, 3, 1, String(), BFV_GRIDGROUP_ALLOW_WEIGHTS);
    // left group
    GroupBegin(21000, BFH_LEFT | BFV_SCALEFIT, 1, 1, String(), 0, 100);
    AddButton(21100, BFH_SCALEFIT | BFH_SCALEFIT, 0, 0, ""_s);
    GroupEnd();
    // center group
    GroupBegin(22000, BFH_SCALEFIT | BFV_SCALEFIT, 1, 1, String(), 0);
    AddButton(22100, BFH_SCALEFIT | BFH_SCALEFIT, 0, 0, ""_s);
    GroupEnd();
    // right group
    GroupBegin(23000, BFH_RIGHT | BFV_SCALEFIT, 1, 1, String(), 0, 100);
    AddButton(23100, BFH_SCALEFIT | BFH_SCALEFIT, 0, 0, ""_s);
    GroupEnd();
    GroupEnd();

    Gadget Values

    GUI elements are used to display values and to allow user interaction to change these values. For default GUI elements there are three kinds of functions to edit these values:

    • Plain getter/setter: Simple functions that allow to edit the value displayed in the given gadget.
    • Container getter/setter: Functions that access the value from a given BaseContainer.
    • TriState setter: Functions that set the value using the given TriState object (see TriState Manual)

    Editing Boolean values:

    Editing integer values:

    Editing float values:

    Editing Vector values:

    Editing Time values:

    Editing String values:

    Editing Filename values: These functions just wrap around GeDialog::GetString() and GeDialog::SetString().

    Editing Color values:

    // This example adds different GUI elements to the layout and sets their values.
    // add a text field and define the text
    AddEditText(1000, BFH_SCALEFIT, 0, 10, 0);
    SetString(1000, "Hello World"_s);
    // add a number field and set the value using a TriState object
    AddEditNumber(2000, BFH_SCALEFIT, 0, 10);
    TriState<Int32> numberData;
    numberData.Add(100);
    numberData.Add(200);
    SetInt32(2000, numberData);

    Interaction

    Messages

    A GeDialog receives messages from both its gadget and from the Cinema 4D GUI. They are sent to GeDialog::Message().

    See GUI and Interaction Messages Manual.

    // This example catches the event when the "F1" key is pressed in GeDialog::Message().
    case BFM_INPUT:
    {
    // check if this event was triggered by some keyboard interaction
    {
    // check if the "F1" triggered the event
    if (msg.GetInt32(BFM_INPUT_CHANNEL) == KEY_F1)
    {
    ApplicationOutput("Pressed \"F1\" key."_s);
    return true;
    }
    }
    break;
    }
    @ BFM_INPUT_KEYBOARD
    Keyboard.
    Definition: gui.h:709
    @ BFM_INPUT
    A dialog/user area receives this message if any mouse or keyboard input is received....
    Definition: gui.h:702
    @ BFM_INPUT_CHANNEL
    Int32 Contains the key or mouse button. See also KEY.
    Definition: gui.h:712
    @ BFM_INPUT_DEVICE
    Int32 Device:
    Definition: gui.h:707
    @ KEY_F1
    Definition: gui.h:85

    Interaction with Gadgets

    When a gadget is changed it sends a message to the parent GeDialog. This message can be caught in GeDialog::Command().

    See GUI and Interaction Messages Manual.

    // This example adds a button to the layout.
    AddButton(ID_BUTTON_ACTION, BFH_SCALEFIT, 0, 20, "Button"_s);
    // The "Command" function is called when the button is pressed.
    Bool Command(Int32 id, const BaseContainer& msg)
    {
    if (id == ID_BUTTON_ACTION)
    {
    ApplicationOutput("Button pressed");
    }

    When a gadget is changed one might need to know the state of the mouse or keyboard when this happened.

    // This example is executed in a GeDialog::Command() function.
    // When the text gadget with the ID 1000 is changed its value is read.
    // If the "Enter" key was pressed, the value is printed to the console.
    if (id == 1000)
    {
    // get String value
    if (GetString(1000, value))
    {
    // get state of the "Enter" key.
    // check if "Enter" key is pressed
    if (state.GetInt32(BFM_INPUT_VALUE))
    {
    ApplicationOutput("Value: " + value);
    }
    }
    }
    PyObject * value
    Definition: abstract.h:715
    Bool GetInputState(Int32 askdevice, Int32 askchannel, BaseContainer &res)
    PyArena _PyASTOptimizeState * state
    Definition: compile.h:99
    @ BFM_INPUT_VALUE
    Int32 Value of the input channel (true/false or a Int32 value, e.g. for scroll wheel data).
    Definition: gui.h:722
    @ KEY_ENTER
    Definition: gui.h:70
    Definition: grammar.h:37
    // This example catches an interaction message in GeDialog::Message(). If the
    // left mouse button is pressed more mouse events are processed via GeDialog::GetInputState().
    // After the left mouse button is released all queued events are freed.
    {
    // messages are identified by the BaseContainer ID
    switch (msg.GetId())
    {
    // interaction event
    case BFM_INPUT:
    {
    // check if mouse event
    {
    // check if left mouse button
    {
    // save initial start position
    Int32 xPos = msg.GetInt32(BFM_INPUT_X);
    Int32 yPos = msg.GetInt32(BFM_INPUT_Y);
    // loop and process queued events
    {
    // check if left mouse button is still pressed
    if (!res.GetInt32(BFM_INPUT_VALUE))
    break;
    // get coordinates
    const Int32 currentX = res.GetInt32(BFM_INPUT_X);
    const Int32 currentY = res.GetInt32(BFM_INPUT_Y);
    // compare coordinates
    const Bool xPosChanged = currentX != xPos;
    const Bool yPosChanged = currentY != yPos;
    if (xPosChanged || yPosChanged)
    {
    ApplicationOutput("Mouse dragged and moved");
    xPos = currentX;
    yPos = currentY;
    }
    }
    // delete all other events that might have occurred in the meantime
    KillEvents();
    return true;
    }
    }
    break;
    }
    }
    return SUPER::Message(msg, result);
    }
    Py_UCS4 * res
    Definition: unicodeobject.h:1113
    @ BFM_INPUT_MOUSELEFT
    Left mouse button.
    Definition: gui.h:713
    @ BFM_INPUT_MOUSE
    Mouse.
    Definition: gui.h:708
    @ BFM_INPUT_Y
    Float Y value.
    Definition: gui.h:725
    @ BFM_INPUT_X
    Float X value.
    Definition: gui.h:724

    Drag and Drop

    The user can drag and drop various elements onto a GeDialog. The GeDialog is informed about this event through messages sent to GeDialog::Message(). These functions are used to react to these messages:

    See also Drag and Drop.

    // This example catches a Drag&Drop message.
    // If the dragged element is a C4DAtom the name of the element is displayed in a text field.
    {
    // messages are identified by the BaseContainer ID
    switch (msg.GetId())
    {
    {
    // Check if the drag is over a certain gadget
    if (CheckDropArea(ID_TEXT_NAME, msg, true, true))
    {
    void* object = nullptr;
    // get the dragged element
    if (!GetDragObject(msg, &type, &object))
    return false;
    // if the drag is finished set the text gadget
    const Bool typeIsAtom = type == DRAGTYPE_ATOMARRAY;
    const Int32 isFinished = msg.GetInt32(BFM_DRAG_FINISHED);
    if (typeIsAtom && isFinished)
    {
    const AtomArray* const atomArray = static_cast<AtomArray*>(object);
    // check if the AtomArray can be accessed and if it contains any objects
    if (atomArray && atomArray->GetCount() > 0)
    {
    C4DAtom* const atom = atomArray->GetIndex(0);
    // check if the given C4DAtom is a BaseList2D element
    if (atom && atom->IsInstanceOf(Tbaselist2d))
    {
    BaseList2D* const baselist = static_cast<BaseList2D*>(atom);
    SetString(ID_TEXT_NAME, baselist->GetName());
    }
    }
    return true;
    }
    else
    {
    return SetDragDestination(MOUSE_POINT_HAND);
    }
    }
    return SetDragDestination(MOUSE_FORBIDDEN);
    }
    }
    return SUPER::Message(msg, result);
    }
    PyObject * object
    Definition: asdl.h:7
    Definition: c4d_baselist.h:1664
    Int32 GetCount() const
    Definition: c4d_baselist.h:1695
    C4DAtom * GetIndex(Int32 idx) const
    Definition: c4d_baselist.h:1710
    Definition: c4d_baselist.h:2245
    String GetName() const
    Definition: c4d_baselist.h:2412
    Definition: c4d_baselist.h:1396
    #define atom
    Definition: graminit.h:72
    @ BFM_DRAG_FINISHED
    Bool Drag finished.
    Definition: gui.h:793
    @ DRAGTYPE_ATOMARRAY
    AtomArray.
    Definition: gui.h:782
    @ BFM_DRAGRECEIVE
    Drag receive. (See DragAndDrop.)
    Definition: gui.h:770
    static const Int32 MOUSE_POINT_HAND
    Point hand cursor.
    Definition: ge_prepass.h:2681
    static const Int32 MOUSE_FORBIDDEN
    Forbidden cursor.
    Definition: ge_prepass.h:2670
    #define Tbaselist2d
    2D list.
    Definition: ge_prepass.h:992
    PyObject ** type
    Definition: pycore_pyerrors.h:34

    Core Messages

    A GeDialog can also receive core messages. These messages are sent to inform the dialog about global events e.g. when something in the active document changed. The messages are sent to GeDialog::CoreMessage().

    See also GUI and Interaction Messages Manual and Core Messages Manual.

    // This example catches the core message EVMSG_CHANGE and re-initializes the dialog's values.
    // This is typically done to update GUI elements that correspond to certain scene elements.
    Bool CoreMessage(Int32 id, const BaseContainer& msg)
    {
    switch (id)
    {
    {
    // check if this core message is new
    if (!CheckCoreMessage(msg))
    break;
    this->InitValues();
    break;
    }
    }
    }
    Bool InitValues()
    {
    // set default value
    SetString(1000, "---"_s);
    // access active document and object
    if (doc == nullptr)
    return true;
    BaseObject* const object = doc->GetActiveObject();
    if (object == nullptr)
    return true;
    // set string value
    SetString(1000, object->GetName());
    return true;
    }
    BaseDocument * GetActiveDocument()
    Definition: c4d_baseobject.h:248
    virtual Bool CoreMessage(Int32 id, const BaseContainer &msg)
    #define EVMSG_CHANGE
    Sent by EventAdd().
    Definition: ge_prepass.h:2716
    Definition: object.h:105

    GeDialog Parent

    A GeDialog can be the base of a sub-dialog or a custom GUI element. In this case it must send messages to a parent dialog. The typical use case is a custom GUI element (based on CustomGuiData, iCustomGui) that has to inform the parent dialog that a stored value has changed.

    // This example sends a message from a GeDialog (iCustomGui)
    // to the parent GeDialog to inform about change of stored value.
    // construct the message
    message.SetInt32(BFM_ACTION_ID, GetId());
    message.SetData(BFM_ACTION_VALUE, _value);
    // send the message
    SendParentMessage(message);
    const char * message
    Definition: pyerrors.h:189
    @ BFM_ACTION_VALUE
    GeData Action value.
    Definition: gui.h:744
    @ BFM_ACTION_ID
    Int32 ID of the dialog element that triggered the action.
    Definition: gui.h:743
    @ BFM_ACTION
    One of the child elements made any action:
    Definition: gui.h:742

    Utility

    Coordinates

    These utility functions allow to access the dimensions of a gadget or to transfer given coordinates into various spaces:

    // This example catches the event when the button with the ID 4000 is pressed.
    // The dimensions of the button are accessed and transferred into screen space
    // so a pop up menu can be displayed beside it.
    if (id == 4000)
    {
    // get button dimensions
    Int32 x, y, w, h = 0;
    GetItemDim(id, &x, &y, &w, &h);
    // construct position of the pop up menu
    x += w;
    Local2Screen(&x, &y);
    // define pop up menu
    bc.InsData(5159, "CMD"); // cube
    bc.InsData(0, String());
    bc.InsData(5160, "CMD"); // sphere
    // show pop up menu
    ShowPopupMenu(Get(), x, y, bc);
    }
    Int32 ShowPopupMenu(CDialog *cd, Int32 screenx, Int32 screeny, const BaseContainer &bc, Int32 flags=POPUP_RIGHT|POPUP_EXECUTECOMMANDS, Int32 *res_mainid=nullptr)
    GeData * InsData(Int32 id, const GeData &n)
    Definition: c4d_basecontainer.h:258
    const Class< R > & Get(const Id &cls)
    Definition: objectbase.h:2060

    Colors

    Color related functions are:

    // This example edits the color of the text gadget with the ID 1000.
    // The text color is set to red.
    SetDefaultColor(1000, COLOR_TEXT_EDIT, Vector(1.0, 0.0, 0.0));
    @ COLOR_TEXT_EDIT
    Definition: c4d_colors.h:108

    Further Reading