SDK:Parametric Custom Shape Pane

From Vectorworks Developer
Jump to navigation Jump to search

.SDK|SDK ..SDK:Types|SDK Types ..SDK:Using the SDK|Using the SDK ..VCOM:VCOM (Vectorworks Component Object Model)|VCOM Basics ..VCOM:Class Reference|VCOM Class Reference

By Vladislav Stanev

What's that

Parametric Objects can customize the "Object Info - Shape Pane" palette in Vectorworks.

By default the "Object Info - Shape Pane" palette shows the parameters of the parametric object as they are defined from the resources of the parametric plug-in. See SDK:Parametric General Info

The image above show "Object Info - Shape Pane" palette for object with the following parameters:

    static SParametricParamChc    gArrChoices[] = {
        { 128,    "c1",   {11002, 1} },
        { 128,    "c2",   {11002, 2} },
        { 128,    "c3",   {11002, 3} },
        // TERMINATE
        { NULL, NULL, {0,0} }
    };

    static SParametricParamDef gArrParameters[] = {
        { "p1",            {11001, 1},        "Default Text",    "Default Text",    kFieldText,      0 },
        { "p2",            {11001, 2},        "1",               "1",               kFieldBoolean,   0 },
        { "p3",            {11001, 3},        "@11002,2",        "@11002,2",        kFieldPopUp,     128 },
        // TERMINATE
        { NULL, {0,0}, NULL, NULL, EFieldStyle(0), 0 }
    };

Note The '@' is used for the default text of a parameter, if the string is to come from a STR# resource.

The Problem

A parametric plug-in is able to override that standard behavior and customize the appearance of the "Object Info - Shape Pane" Palette.

This approach allows a complex plug-in parametric to make good distinction between data and UI of the parameters.

Let's define some terminology:

  • Data is the internal record that holds the parameters of the object; Data is defined by the static 'SParametricParamDef' variable passed to the constructor of VWFC:PluginSupport:VWExtensionParametric. This makes one format with this name when object of this type is created. Each instance of that object contains record instance in its AUX list holding the parameters for that particular parametric object instance.
  • UI is the custom defined widgets that appear in the Shape pane. The plug-in can define the widgets that appear in the shape pane and can bind them to the parameter's data. This will make Vectorworks automatically transfer data between the widgets and the parameters record.

The sample demonstrated in this article will create a custom shape pane adding one additional button by manually arranging the OIP.

Setting up: Registering an event sink

In order to create custom user interface for parametric object your parametric plug-in should implement an additional event sink VCOM:VectorWorks:Extension:IProviderShapePane.

First of all you need to provide the additional event sink for you parametric extension class.

    class CExtObjParametricObj : public VWExtensionParametric
    {
    DEFINE_VWParametricExtension;
    public:
                    CExtObjParametricObj(CallBackPtr cbp);
        virtual    ~CExtObjParametricObj();

    // IExtensionApp
    public:
        virtual void	DefineSinks();
    };

The 'Define Sinks' function registers classes as event sinks provided by this parametric extension. VWFC implementation will handle creating and deleting instance of the event sink classes.

void CExtObjParametricObj::DefineSinks()
{
    this->DefineSink<CProviderShapePane>( IID_ProviderShapePane );
}

The class 'CProviderShapePane' is the implementation of the Shape Pane event sink VCOM:VectorWorks:Extension:IProviderShapePane, which is recognized by the VWIID IID_ProviderShapePane that is defined for this sink.

The IProviderShapePane event sink

We have to implement the VCOM:VectorWorks:Extension:IProviderShapePane interface which will receive events from Vectorworks about the OIP of the parametric extension.

Here is the header file ProviderShapePane.h that defines our sink:

#include "VectorWorks/Extension/IProviderShapePane.h"

namespace TesterModule
{
    using namespace VectorWorks::Extension;

    // ------------------------------------------------------------------------------------------------------------------------------------
    class DYNAMIC_ATTRIBUTE CProviderShapePane : public VCOMImpl<IProviderShapePane>
    {
    public:
                        CProviderShapePane(IVWUnknown* parent);
        virtual         ~CProviderShapePane();

    // IProviderShapePane
    public:
        virtual void VCOM_CALLTYPE    Init(CodeRefID objectID, IExtendedProps* extProps);
        virtual void VCOM_CALLTYPE    Activate(MCObjectHandle hObject, const SSelectionContext& selContext);
        virtual void VCOM_CALLTYPE    Deactivate();
        virtual void VCOM_CALLTYPE    Update(IWidgetsProvider* widgetProvider);
        virtual bool VCOM_CALLTYPE    OnWidgetChange(SShapePaneWidgetOnWidgetChange &data, bool &outNeedReset, bool &outChangeOk);
    };
}

And the implementation file ProviderShapePane.cpp

using namespace TesterModule;

namespace TesterModule
{
Note #1, see after the code block.
    // --------------------------------------------------------------------------------------------------------
    // widget constants
    const Sint32    kWidgetID_Widget1     = 1;
    const Sint32    kWidgetID_Widget2     = 2;
    const Sint32    kWidgetID_Widget3     = 3;
    const Sint32    kWidgetID_Widget4     = 4;
}

Note #2, see after the code block.
CProviderShapePane::CProviderShapePane(IVWUnknown* parent)
    : VCOMImpl<IProviderShapePane>( parent )
{
}

CProviderShapePane::~CProviderShapePane()
{
}

void CProviderShapePane::Init(CodeRefID objectID, IExtendedProps* extProps)
{
Note #3, see after the code block.
    IWidgetsEditProvider*    widgetsProvider    = NULL;
    if ( extProps && VCOM_SUCCEEDED( extProps->GetObjComponentTypeWidgets( objectID, kObjectRootComponentTypeID, widgetsProvider ) ) )
    {
        widgetsProvider->AddWidget( kTextWidgetID,            "p1" );
        widgetsProvider->AddWidget( kStateCheckWidgetID,      "p2" );
        widgetsProvider->AddWidget( kValuesPopupWidgetID,     "p3" );
        widgetsProvider->AddWidget( kButtonWidgetID,          kWidgetButton,       "Edit List..." );
    }
}

void CProviderShapePane::Activate(MCObjectHandle hObject, const SSelectionContext& selContext)
{
Note #4, see after the code block.
}

void CProviderShapePane::Deactivate()
{
Note #5, see after the code block.
}

void CProviderShapePane::Update(IWidgetsProvider* widgetProvider)
{
Note #6, see after the code block.
}

bool CProviderShapePane::OnWidgetChange(SShapePaneWidgetOnWidgetChange &data, bool &outNeedReset, bool &outChangeOk)
{
Note #7, see after the code block.
    bool   eventHandled    = false;
    if ( data.fWidgetID == kButtonWidgetID )
    {
        gSDK->AlertInform( "kButtonWidgetID", "CProviderShapePane::OnWidgetChange" );
        eventHandled   = true;
    }

    return eventHandled;
}

Note #1. Define UI Widget Constants

We need to define constants for the UI Widgets we'll put in the OIP in order to recognize them later in more C++ way.

Note #2: The Sink Implementation

This is the event sink implementation. Note that the constructor should accept IVWUnknown for the parent of the event sink. Creating this event sink will add one to the ref counter of the parent making sure that the parent will not be deleted before the sink.

Note #3: Init

The Init function is called only once for the parametric extension that provides this event sink class. The Init function works on the parametric type denoted by the 'objectID' parameter, that is because at this point there are no instances of this parametric object.

Uses IExtendedProps::GetObjComponentTypeWidgets to obtain widgets edit provider interface IWidgetsEditProvider that provides functions for defining the custom widgets for this parametric object type. Note that kObjectRootComponentTypeID is passed to retrieve the widgets editor provider for the parametric object itself. This is reserved for the future to support hierarchical OIP.

The widgets are added one by one and each widget has an Identifier (see Note #1). This identifier is used to recognize that particular widget later one. The widget identifiers SHOULD NOT repeat, each widget should have unique identifier number. This uniqueness should be ensured by the developer of the plug-in. Vectorworks doesn't check that.

The widgets in this example are added with automatic data hook to the parametric's record (by specifying the parameter's universal name). This means that data from and to the widget will come from that specified record field.

The widget types is determined by the type of the parameters in this example. You can define your widgets by type, see SDK:SWidgetDefinition#Remarks

Note #4: Activate

This notification is sent when the OIP is preparing to show a particular instance of the parametric object. The handle of the object is passed as parameter. The context is passed to allow the implementation to take care if there are multiple objects selected. Use the passed parametric as first to search for all selected objects.

Note #5: Deactivate

This notification is sent when the OIP is closing on the object(s) that it has been activated (see Note $4).

Note #6: Update

This notification is sent when the OIP widgets needs to be updated. The 'widgetProvider' parameter provides functions for enabling/disabling widgets as well as hiding/showing. Note that this notification have to be very fast for performance reasons -- it is called very often.

Note #7: OnWidgetChange

This notification is sent when there is a change to any widget. The 'data' parameter will provide more detailed information about the widget.

In the example this is the event that is used to capture the UI button.

See also

SDK:Parametric General Info | SDK:Parametric Extended Properties