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.
{
private:
ExampleDialog _dialog;
public:
{
if (_dialog.IsOpen() == false)
else
_dialog.Close();
return true;
};
{
return _dialog.RestoreLayout(g_geDialogID, 0, secret);
};
static OpenExampleDialog* Alloc() { return NewObjClear(OpenExampleDialog); }
};
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:
{
SetTitle("Example Dialog"_s);
this->SetTimer(1000);
return true;
}
{
SetFloat(1000, 123.456);
return true;
}
Messages and Interaction:
{
if (id == 1000)
{
}
}
{
{
{
break;
}
{
break;
}
}
return SUPER::Message(msg, result);
}
Timed Actions:
Read-Only Properties
It is possible to check these GeDialog properties:
It is also possible to read the pixel ratio of the current screen:
if (bitmapButtonGUI)
{
if (bitmap)
{
const Float pixelRatio = GetPixelRatio();
if (pixelRatio == 1.0)
filename = "lowRes.png";
else
filename = "highRes.png";
const String fullFileName = GetFullFilename(filename);
{
bitmapButtonGUI->
SetImage(bitmap,
true,
false);
}
}
}
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:
{
return false;
if (!LoadDialogResource(DLG_CUSTOM_DIALOG, nullptr, 0))
return false;
this->SetTitle("New Title"_s);
this->Enable(IDC_CUSTOM_CHECKBOX, false);
return true;
}
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:
MenuFlushAll();
MenuSubBegin("Cinema 4D Commands"_s);
const Int32 saveAsCommandID = 12218;
const Int32 saveCommandID = 12098;
MenuAddCommand(saveAsCommandID);
MenuAddCommand(saveCommandID);
MenuSubEnd();
MenuSubBegin("Dialog Commands"_s);
MenuAddString(ID_COMMAND_A, "Action A"_s);
MenuAddSeparator();
MenuAddString(ID_COMMAND_B, "Action B"_s);
MenuSubEnd();
MenuFinished();
MenuInitString(ID_COMMAND_B, true, true);
It is possible to add arbitrary gadgets to a special sub-group of the menu bar:
GroupBeginInMenuLine();
GroupEnd();
GroupEnd();
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:
GroupEnd();
GroupEnd();
GroupEnd();
GroupEnd();
GroupEnd();
GroupEnd();
The border style and spacing of groups can be defined with these functions:
GroupSpace(100, 0);
GroupBorderSpace(10, 10, 10, 10);
GroupEnd();
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.
AddStaticText(100, 0, 0, 10, "Left Column"_s, 0);
GroupEnd();
AddSeparatorV(1);
AddStaticText(200, 0, 0, 10, "Right Column"_s, 0);
GroupEnd();
GroupEnd();
GroupWeightsLoad(WEIGHT_GROUP, _weights);
The content of a group can be flushed and replaced with new gadgets:
LayoutFlushGroup(100);
LayoutChanged(100);
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.
for (
Int32 i = 0; i < 100; ++i)
GroupEnd();
GroupEnd();
if (GetVisibleArea(scrollGroupID, &x1, &y1, &x2, &y2))
{
const Int32 height = y2 - y1;
SetVisibleArea(scrollGroupID, x1, 0, x2, height);
}
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.
{
Int32 sgx, sgy, scrollWidth, scrollHeight;
Int32 gx, gy, gadgetWidth, gadegtHeight;
if (!GetItemDim(idScrollGroup, &sgx, &sgy, &scrollWidth, &scrollHeight) ||
!GetItemDim(idGadget, &gx, &gy, &gadgetWidth, &gadegtHeight))
Int32 svax1, svay1, svax2, svay2;
if (!GetVisibleArea(idScrollGroup, &svax1, &svay1, &svax2, &svay2))
"Could not get visible area of scroll group."_s);
Int32 dx = (scrollWidth - (svax2 - svax1));
Int32 dy = (scrollHeight - (svay2 - svay1));
Int32 realScrollWidth = scrollWidth - dx;
Int32 realScrollHeight = scrollHeight - dy;
Int32 x = (
Int32)((gadgetWidth - realScrollWidth) * .5);
Int32 y = (
Int32)((gadegtHeight - realScrollHeight) * .5);
if (!SetVisibleArea(idScrollGroup, x, y, x + realScrollWidth, y + realScrollHeight))
"Could not set visible area of scroll group."_s);
}
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.
if (textA)
SetString(textA, "This is some text."_s);
SetString(ID_TEXT_B, "This is some other text."_s);
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:
private:
public:
{
SetTitle("Listview Dialog"_s);
return true;
}
{
++line;
return true;
}
A multi-line text edit field can be created and configured with these functions:
See also Specific GUI Elements.
SetString(4000, "import c4d\n\nprint(\"hello world\")\n"_s);
SetMultiLinePos(4000, 4, 0);
Several gadgets can contain multiple child-elements, typically for selection purposes:
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);
AddChildren(6000, children);
AddComboBox(5001,
BFH_LEFT, 100, 10,
false);
AddChild(5001, 0, "Cube &" + iconData1Address + "&");
AddChild(5001, 1, "Sphere &" + iconData2Address + "&");
Further interaction with gadgets is typically done by sending messages to the gadget:
See also GUI and Interaction Messages Manual.
Images
There is no default gadget to display an image (BaseBitmap) in a GeDialog. Two possible solutions are:
if (bitmapButtonGUI)
{
bitmapButtonGUI->
SetImage(buttonBitmap,
true,
false);
}
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).
void*
const customGui = AddCustomGui(11000, customGuiID,
String(),
BFH_SCALEFIT, 100, 50, hlSettings);
if (linkGustomGui != nullptr)
{
const String linkTitle {
"MAXON" };
const String url {
"https://www.maxon.net" };
}
if (gradientGUI)
{
if (gradientData)
{
triStateData.
Add(geData);
gradientGUI->
SetData(triStateData);
}
}
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.
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.
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.
{
switch (id)
{
{
break;
}
}
return true;
}
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.
Layout Flags
The position and behaviour of groups and gadgets is defined with these flags:
Horizontal alignment:
Vertical alignment:
GroupEnd();
GroupEnd();
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:
SetString(1000, "Hello World"_s);
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.
{
{
{
return true;
}
}
break;
}
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.
AddButton(ID_BUTTON_ACTION,
BFH_SCALEFIT, 0, 20,
"Button"_s);
{
if (id == ID_BUTTON_ACTION)
{
}
When a gadget is changed one might need to know the state of the mouse or keyboard when this happened.
if (id == 1000)
{
if (GetString(1000, value))
{
{
}
}
}
{
{
{
{
{
{
break;
const Bool xPosChanged = currentX != xPos;
const Bool yPosChanged = currentY != yPos;
if (xPosChanged || yPosChanged)
{
xPos = currentX;
yPos = currentY;
}
}
KillEvents();
return true;
}
}
break;
}
}
return SUPER::Message(msg, result);
}
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.
{
{
{
if (CheckDropArea(ID_TEXT_NAME, msg, true, true))
{
void* object = nullptr;
if (!GetDragObject(msg, &type, &object))
return false;
if (typeIsAtom && isFinished)
{
if (atomArray && atomArray->
GetCount() > 0)
{
{
SetString(ID_TEXT_NAME, baselist->
GetName());
}
}
return true;
}
else
{
}
}
}
}
return SUPER::Message(msg, result);
}
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.
{
switch (id)
{
{
if (!CheckCoreMessage(msg))
break;
this->InitValues();
break;
}
}
}
{
SetString(1000, "---"_s);
if (doc == nullptr)
return true;
if (object == nullptr)
return true;
SetString(1000, object->
GetName());
return true;
}
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.
SendParentMessage(message);
Utility
Coordinates
These utility functions allow to access the dimensions of a gadget or to transfer given coordinates into various spaces:
if (id == 4000)
{
GetItemDim(id, &x, &y, &w, &h);
x += w;
Local2Screen(&x, &y);
}
Colors
Color related functions are:
Further Reading