GeUserArea Manual
GeUserArea is the base class for all gadgets that can be displayed in a GeDialog . A new gadget is created by implementing a subclass of GeUserArea . Such a gadget manages user interaction and draws the gadget interface in the GUI.
An instance of a GeUserArea based class is typically stored as a member of the parent GeDialog . It is added to the dialog layout using:
见 GeDialog 手册 .
// This example adds the given user area "userarea" to the GeDialog layout.The parent dialog is accessible with:
A custom user area is created by implementing a subclass of GeUserArea . This subclass can implement different virtual functions that define the behaviour of the gadget.
The user area is initiated with:
The size of the user area is handled with:
// This example stores the width and height after the GeUserArea was resized.
void Sized( Int32 w, Int32 h) { _width = w; _height = h; }Different messages are sent to a user area:
The primary purpose of a user area is to draw the actual user interface:
A user area can catch user interaction events:
另请参阅 GeDialog Gadget Interaction .
These properties can be read from a user area:
// This example changes the color of the user area depending on the focus.
void DrawMsg( Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer & msg) { OffScreenOn(); SetClippingRegion(x1, y1, x2, y2);// check if the user area has the focus if (this->HasFocus()) DrawSetPen( 向量 (0.0, 0.0, 1.0)); else DrawSetPen( 向量 (0.0, 0.2, 0.8)); DrawRectangle(x1, y1, x2, y2); }
A user area can use an internal timer that calls GeUserArea::Timer() periodically.
// This example implements GeUserArea::Timer(). // The function is called in the interval defined with GeUserArea::SetTimer().
void Timer( const BaseContainer & msg) { ApplicationOutput ( "Timer tick" ); }A user area can handle user interaction events by implementing GeUserArea::InputEvent() . The following functions are used to get more detailed information on the current event:
A special operation is a mouse drag event inside the user area. Such a mouse drag is initiated in reaction to certain user interaction using these functions:
// check if this is a mouse event triggered by the left mouse button if (device == BFM_INPUT_MOUSE && channel == BFM_INPUT_MOUSELEFT ) { BaseContainer channels; BaseContainer state; Int32 startX = msg. GetInt32 ( BFM_INPUT_X ); Int32 startY = msg. GetInt32 ( BFM_INPUT_Y ); Float deltaX = 0.0f; Float deltaY = 0.0f; Global2Local(&startX, &startY);
// start mouse drag MouseDragStart( BFM_INPUT_MOUSELEFT , ( Float )startX, ( Float )startY, MOUSEDRAGFLAGS::DONTHIDEMOUSE );
// handle mouse drag while (MouseDrag(&deltaX, &deltaY, &channels) == MOUSEDRAGRESULT::CONTINUE ) { // get state of the left mouse button if (! GetInputState ( BFM_INPUT_MOUSE , BFM_INPUT_MOUSELEFT , state)) break ;
// check if the mouse button is not pressed if (state. GetInt32 ( BFM_INPUT_VALUE ) == 0) break ;
// print something if the mouse has moved if (deltaX != 0.0 || deltaY != 0.0) ApplicationOutput ( "Mouse Drag" ); } MouseDragEnd(); return true ; }
The user can drag and drop various elements onto a user area. The user area is informed about this event through messages sent to GeUserArea::Message() . These functions are used to react to those messages:
另请参阅 Drag and Drop .
// This example handles a drag and drop event in GeUserArea::Message(). case BFM_DRAGRECEIVE : { // check drag area if (!CheckDropArea(msg, true , true )) break ; Int32 type; void * data = nullptr ;// get the drag data if (!GetDragObject(msg, &type, &data)) return false ;
// check if an C4DAtom was dragged if (type != DRAGTYPE_ATOMARRAY ) return false ;
// get content const AtomArray * const elements = static_cast< AtomArray * > (data); // check content if (!elements-> GetIndex (0)) return false ;
// check if drag has finished if (msg. GetInt32 ( BFM_DRAG_FINISHED )) { // loop through all elements of the AtomArray for ( Int32 i = 0; i < elements-> GetCount (); ++i) { const C4DAtom * const atom = elements-> GetIndex (i);
// check if the given C4DAtom is a BaseList2D element if (atom && atom-> IsInstanceOf ( Tbaselist2d )) { const BaseList2D * const baselistObject = static_cast< const BaseObject * > (atom); ApplicationOutput ( "Dragged object: " + baselistObject-> GetName ()); } } } else { // if in drag, show different mouse cursor return SetDragDestination( MOUSE_POINT_HAND ); } return true ; }
It is also possible to start a drag and drop operation from a user area. This is typically done in reaction to some user interaction.
// start a drag & drop event of dragging a color value if (HandleMouseDrag(msg, DRAGTYPE_RGB , &red, 0)) return true ; } } return false ; }
A user area can send messages to its parent GeDialog . This is typically done to inform the dialog that some user interaction occurred and that the managed values changed.
见 GUI and Interaction Messages Manual .
// This example sends the BFM_ACTION message from a GeUserArea to the parent GUI element. // This message can be caught in GeDialog::Message() or GeDialog::Command(). BaseContainer action( BFM_ACTION ); action.SetInt32( BFM_ACTION_ID , GetId()); SendParentMessage(action);The central task of a user area is to draw something to Cinema 4D 's user interface. The drawing operations have to happen inside the GeUserArea::DrawMsg() 函数。
The drawing operation can be triggered with:
The following functions can be used to draw inside the canvas provided by the user area. For more advanced drawing operations one can use a GeClipMap and GeUserArea::DrawBitmap() .
The color and opacity settings are handled with:
c4d_colors.h
.
// draw a white background DrawSetPen( 向量 (1.0f)); DrawRectangle(x1, y1, x2, y2);
// draw some text DrawSetTextCol( 向量 (0.0f), 向量 (1.0f)); DrawText( "Hello World!" _s, 0, 0, DRAWTEXT_HALIGN_LEFT );
Different styles used with various drawing functions are ( LINESTYLE ):
These functions draw primitive shapes:
// draw base MAXON_SCOPE { const maxon::Vector2d center(300, 60); const maxon::Vector2d radius(40, 40); DrawSetPen( 向量 (1, 1, 0)); DrawEllipseFill(center, radius); DrawSetPen( 向量 (0, 0, 0)); DrawEllipseLine(center, radius); }
// draw eyes DrawSetPen( 向量 (0, 0, 0)); MAXON_SCOPE { const maxon::Vector2d center(285, 50); const maxon::Vector2d radius(3, 3); DrawEllipseFill(center, radius); } MAXON_SCOPE { const maxon::Vector2d center(315, 50); const maxon::Vector2d radius(3, 3); DrawEllipseFill(center, radius); }
// draw mouth MAXON_SCOPE { BezierPoint p; p. c1 = Vector2d (280, 90); p. c2 = Vector2d (320, 90); p. p = Vector2d (320, 70); DrawBezierLine( Vector2d (280, 70), maxon::ToSingletonBlock (p), false , 1.0, LINESTYLE::NORMAL ); }
A BaseBitmap can both be drawn to the canvas and filled with the current pen:
另请参阅 BaseBitmap Manual .
// This example fills a GeClipMap and draws the resulting BaseBitmap in the user area. AutoAlloc<GeClipMap> clipMap; if (clipMap) { // get monospaced font BaseContainer font; GeClipMap::GetDefaultFont ( GE_FONT_DEFAULT_MONOSPACED , &font); clipMap-> Init (200, 50, 32); clipMap-> BeginDraw ();// fill background clipMap-> SetColor (255, 255, 255, 255); clipMap-> FillRect (0, 0, 200, 50);
// draw text clipMap-> SetFont (&font, 20.0); clipMap-> SetColor (0, 0, 0, 255); clipMap-> TextAt (0, clipMap-> GetTextHeight (), "This is some text." ); clipMap-> EndDraw ();
// get bitmap BaseBitmap * const bitmap = clipMap-> GetBitmap (); if (bitmap) { // draw bitmap const Int32 width = bitmap-> GetBw (); const Int32 height = bitmap-> GetBh (); DrawBitmap(bitmap, 0, 0, width, height, 0, 0, width, height, BMP_NORMAL ); } }
The border style of the user area is defined with:
Text is drawn with these functions:
// draw text DrawSetTextCol( 向量 (0.0f), 向量 (1.0f)); DrawSetFont( FONT_MONOSPACED ); DrawText(text, left, 0, DRAWTEXT_HALIGN_LEFT );
// draw a line const Int32 baseline = DrawGetFontBaseLine() + 1; const Int32 length = DrawGetTextWidth(text); DrawSetPen( 向量 (0.5f)); DrawLine(left, baseline, left + length, baseline);
A user area can support a dynamic fading effect that is typically used to change the background color of the gadget after the cursor moved over it:
另请参阅 衰退 .
// This example shows a GeUserArea that is "fading". // When the cursor is over the user area the fading is activated. // Cinema 4D will then send the message BFM_FADE to the user area so // it can interpolate a color and use this color to redraw itself. class FadingUserArea : public GeUserArea { public : FadingUserArea() { }; ~FadingUserArea() { }; Int32 消息 ( const BaseContainer & msg, BaseContainer & result) { // messages are identified by the BaseContainer ID switch (msg. GetId ()) { case BFM_GETCURSORINFO : { // cursor is over the user area // start fading ActivateFading (100); return true ; } case BFM_FADE : { // receive fade message and update the COLOR_BG used in DrawMsg() const Float percentage = msg. GetFloat ( BFM_FADE ); AdjustColor ( COLOR_BG , COLOR_BG_HIGHLIGHT , percentage);// redraw using the updated COLOR_BG Redraw (); return true ; } } return GeUserArea::Message (msg, result); } void DrawMsg ( Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer & msg) { OffScreenOn (); SetClippingRegion (x1, y1, x2, y2); // draw background with COLOR_BG DrawSetPen ( COLOR_BG ); DrawRectangle (x1, y1, x2, y2); // draw text DrawSetTextCol ( COLOR_CONSOLE_TEXT , COLOR_TRANS ); DrawSetFont ( FONT_MONOSPACED ); DrawText ( "User Area" _s, 0, 0, DRAWTEXT_HALIGN_LEFT ); } };
It is also possible to be informed when the cursor leaves the user area:
// declarations static void RemoveCursorInfo(); class RemoveCursorUserArea; static RemoveCursorUserArea* g_userArea = nullptr ; // static variable storing pointer to a user area
// user area will display a different color depending if the cursor is over the area or not class RemoveCursorUserArea : public GeUserArea { public : RemoveCursorUserArea() { if (g_userArea == this ) g_userArea = nullptr ; }; ~RemoveCursorUserArea() { }; Int32 消息 ( const BaseContainer & msg, BaseContainer & result) { // messages are identified by the BaseContainer ID switch (msg. GetId ()) { case BFM_GETCURSORINFO : { // register RemoveCursorInfo() callback g_userArea = this ; RemoveLastCursorInfo (RemoveCursorInfo); _cursorInside = true ; Redraw (); return true ; } case BFM_CURSORINFO_REMOVE : { // message "BFM_CURSORINFO_REMOVE" sent by RemoveCursorInfo() _cursorInside = false ; Redraw (); break ; } } return GeUserArea::Message (msg, result); } void DrawMsg ( Int32 x1, Int32 y1, Int32 x2, Int32 y2, const BaseContainer & msg) { OffScreenOn (); SetClippingRegion (x1, y1, x2, y2);
// gadget color is controlled by the cursor position
if (_cursorInside) DrawSetPen ( 向量 (1.0, 0.0, 0.0)); else DrawSetPen ( 向量 (0.0, 1.0, 0.0)); DrawRectangle (x1, y1, x2, y2); } private : Bool _cursorInside = false ; // set to true if the cursor is over the user area };// function will be called by the user area when the cursor left its area static void RemoveCursorInfo() { if (g_userArea == nullptr ) return ;
// send message to the user area BaseContainer bc; g_userArea->Message( BaseContainer ( BFM_CURSORINFO_REMOVE ), bc); }
Clipping is used to limit drawing operations to certain areas:
Miscellaneous functions are:
These functions are used to transform coordinates into different spaces:
// check if this is a mouse event triggered by the left mouse button if (device == BFM_INPUT_MOUSE && channel == BFM_INPUT_MOUSERIGHT ) { // get the cursor position Int32 x = msg. GetInt32 ( BFM_INPUT_X ); Int32 y = msg. GetInt32 ( BFM_INPUT_Y ); Global2Local(&x, &y); Local2Screen(&x, &y);
// define pop up menu BaseContainer bc; bc. InsData (5159, "CMD" ); // cube bc. InsData (0, String ()); bc. InsData (5160, "CMD" ); // sphere
// show pop up menu ShowPopupMenu (GetDialog()-> Get (), x, y, bc); return true ; } return false ; }