The tabs! They work!


eye kan kode gud
Would have been nice if the sample code was a little more clear. Still, I managed a successful application of the Sierra On-Line Adventure Game technique of problem solving: try random things until it works correctly.



eye kan kode gud
For reference: Add controls to groups, add groups to tabs. Remember to label the groups after the XpGROUP_ macro, not before.

        LWTypes::LWXPanelHint notify_hint[] =
            XpCHGNOTIFY( UI_Change_Notify ),
            XpLABEL    ( 0, node_name     ),
            XpDIVADD   ( Interface_Bump_Dropoff_ID ),
            XpGROUP_( Interface_Group_Settings_ID ),
                XpH( Interface_Use_Simple_Color_ID         ),
                XpH( Interface_Full_Color_Cutoff_ID        ),
                XpH( Interface_Use_Simple_Specular_ID      ),
                XpH( Interface_Full_Specular_Cutoff_ID     ),
                XpH( Interface_Specular_Cutoff_ID          ),
                XpH( Interface_Use_Simple_Roughness_ID     ),
                XpH( Interface_Full_Roughness_Cutoff_ID    ),
                XpH( Interface_Use_Simple_Transparency_ID  ),
                XpH( Interface_Full_Transparency_Cutoff_ID ),
                XpH( Interface_Use_Simple_Refraction_ID    ),
                XpH( Interface_Full_Refraction_Cutoff_ID   ),
                XpH( Interface_Refraction_Cutoff_ID        ),
                XpH( Interface_Normal_Cutoff_ID            ),
                XpH( Interface_Bump_Cutoff_ID              ),
            XpLABEL( Interface_Group_Settings_ID, "Settings" ),
            XpGROUP_( Interface_Group_GI_Settings_ID ),
                XpH( Interface_Independent_GI_Settings_ID     ),
                XpH( Interface_GI_Use_Simple_Color_ID         ),
                XpH( Interface_GI_Full_Color_Cutoff_ID        ),
                XpH( Interface_GI_Use_Simple_Specular_ID      ),
                XpH( Interface_GI_Full_Specular_Cutoff_ID     ),
                XpH( Interface_GI_Specular_Cutoff_ID          ),
                XpH( Interface_GI_Use_Simple_Roughness_ID     ),
                XpH( Interface_GI_Full_Roughness_Cutoff_ID    ),
                XpH( Interface_GI_Use_Simple_Transparency_ID  ),
                XpH( Interface_GI_Full_Transparency_Cutoff_ID ),
                XpH( Interface_GI_Use_Simple_Refraction_ID    ),
                XpH( Interface_GI_Full_Refraction_Cutoff_ID   ),
                XpH( Interface_GI_Refraction_Cutoff_ID        ),
                XpH( Interface_GI_Normal_Cutoff_ID            ),
                XpH( Interface_GI_Bump_Cutoff_ID              ),
            XpLABEL( Interface_Group_GI_Settings_ID, "GI Settings" ),
            XpTABS_( Interface_Tab_Optimize_Settings_ID ),
                XpH( Interface_Group_Settings_ID    ),
                XpH( Interface_Group_GI_Settings_ID ),


eye kan kode gud
Are the controls automatically erased/displayed when you switch tabs?

Yes, the XPanels system takes care of all of that. The tricky bit was just figuring out how to describe what I wanted to XPanels using the available hints. The sdk documents are kinda vague on that front.


More like "frustrating". :)

I posted this here so that others won't have to trudge through the same trial-and-error process I had to go through.


Got it working too now.
I am still a bit confused of how an LWXP_FORM panel is activated though.

If I use your code (modified for what I need), the tabs and the controls are there but inactive.
If I use the XpENABLE_ hint on the tabs and the groups everything updates when I click the tabs, but the controls are still disabled.

If i use XpENABLE_ for the controls too, they remain inactive.
If I use xpanelfuncs->viewInst and set it to anything, then all of a sudden everything (tabs and controls) works. But the SDK docs say that this should only be used for LWXP_VIEW panels.

So what is the bare minimum I need for an LWXP_FORM ?



eye kan kode gud
You also need to implement the xpanel interface get/set callbacks. Returning null from the Get() callback disables the control. Returning a pointer to where a control should retrieve its data from enables it.

For instance, the Get() callback for my simplex noise node looks like this:

    // ------------------------------------------------------------------------ XPanel_Interface_Get()
    void * Simplex_Noise::XPanel_Interface_Get( unsigned int control_id )
        // Convenience Functions
        auto is_Connected           = Input_Functions->check;
        auto get_if_Unconnected     = [&]( LWTypes::NodeInputID input_id, void * data_address ) { return is_Connected( input_id ) ? nullptr : data_address; };
        // Return pointer to setting to be displayed in control, or nullptr if disabled.
        switch ( control_id )
            case Interface_Color_Foreground_ID : return get_if_Unconnected( Input_Color_Foreground.ID, &Settings_Color_Foreground.Value );
            case Interface_Color_Background_ID : return get_if_Unconnected( Input_Color_Background.ID, &Settings_Color_Background.Value );
            case Interface_Blending_ID         : return get_if_Unconnected( Input_Blending.ID,         &Settings_Blending.Value         );
            case Interface_Alpha_Foreground_ID : return get_if_Unconnected( Input_Alpha_Foreground.ID, &Settings_Alpha_Foreground.Value );
            case Interface_Alpha_Background_ID : return get_if_Unconnected( Input_Alpha_Background.ID, &Settings_Alpha_Background.Value );
            case Interface_Opacity_ID          : return get_if_Unconnected( Input_Opacity.ID,          &Settings_Opacity.Value          );
            case Interface_Invert_ID           : return &Settings_Invert.Value;

            case Interface_Small_Scale_ID      : return get_if_Unconnected( Input_Small_Scale.ID,      &Settings_Small_Scale.Value      );
            case Interface_Contrast_ID         : return get_if_Unconnected( Input_Contrast.ID,         &Settings_Contrast.Value         );
            case Interface_Octaves_ID          : return get_if_Unconnected( Input_Octaves.ID,          &Settings_Octaves.Value          );

            case Interface_Bump_Height_ID      : return get_if_Unconnected( Input_Bump_Height.ID,      &Settings_Bump_Height.Value      );

            case Interface_World_Coordinates_ID: return &Settings_World_Coodinates.Value;

            case Interface_Scale_X_ID          : return get_if_Unconnected( Input_Scale.ID,    &Settings_Scale.Value.X()   );
            case Interface_Scale_Y_ID          : return get_if_Unconnected( Input_Scale.ID,    &Settings_Scale.Value.Y()   );
            case Interface_Scale_Z_ID          : return get_if_Unconnected( Input_Scale.ID,    &Settings_Scale.Value.Z()   );

            case Interface_Position_X_ID       : return get_if_Unconnected( Input_Position.ID, &Settings_Position.Value.X());
            case Interface_Position_Y_ID       : return get_if_Unconnected( Input_Position.ID, &Settings_Position.Value.Y());
            case Interface_Position_Z_ID       : return get_if_Unconnected( Input_Position.ID, &Settings_Position.Value.Z());

            case Interface_Rotation_H_ID       : return get_if_Unconnected( Input_Rotation.ID, &Settings_Rotation.Value.H());
            case Interface_Rotation_P_ID       : return get_if_Unconnected( Input_Rotation.ID, &Settings_Rotation.Value.P());
            case Interface_Rotation_B_ID       : return get_if_Unconnected( Input_Rotation.ID, &Settings_Rotation.Value.B());
                return nullptr;

The get_if_Unconnected() method allows me to automatically disable a control when its corresponding node input is hooked up to some other incoming node by returning nullptr (to disable the control) if Input_Functions->check() returns true.

For controls that have no corresponding node input, I just directly return the address of where I'm storing the data for that control.

If you want to see a full xpanel implementation, the source code for the latest release of my plugins can be found here. If you look at the Get() callback for the optimizer nodes, you'll see logic in there that enables/disables controls depending on the state of other controls -- you can have things like a checkbox control alter the enable/disable state of other controls. Quite handy, that.

Edit: re-reading your post, I see you're instantiating an xpanel from some other context than a node. I only have experience with nodes, so I'm not actually sure what you'll need to do to get your controls working.
Last edited:


Got it working now, the Get/Set is only required for LWXP_VIEW xpanels, with LWXP_FORM you use the FSETINT etc macros.

The only thing I had to do is set some initial values and the controls "woke up" , so I can throw out the xpanelfuncs->viewInst.

But thanks a million for the example code, cleaned up the interface part of my plugin quite a bit.



More XPanels questions:

The only way to disable/enable controls on an XPanel that I could find is to add an XPEnable_ Hint (and link that to a ValID that has no control associated), add a trailing list of controls, and then change the ValID so the trailing list of controls gets enabled/disabled.

Seems a crude way to do it, but at least it works.
Is there a more elegant way to do this?

Top Bottom