Maxon..Can we please see the SliderGUI code?



  • On 19/08/2013 at 15:22, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   13 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    I've created my own custom slider GUI using a User Area.
    In my code I'm using the mouse's X position value to animate the size of a rectangle shape. Which is working well.
    I'm also able to output the slider's value (the mouse's x position) and use this to record keys and control things. Just like how the built-in slider works.
    The Xpresso output port also works. Reflecting the values of the slider.

    But...I cannot go the other way.
    I can't make the slider change according to the key frame values. When the user is not physically using the slider.
    It's a one way communication only. Once I have used the slider to set the keys. The slider just sits there and does not update itself. And I've been unable to figure out how to send any messages to my custom GUI's DrawMsg() function to change the slider (the rectangle shape).

    Is it possible to see the code for the custom slider GUI that ships with C4D?
    I need to see how they handled the receiving part of it.

    Thanks,
    -ScottA



  • On 20/08/2013 at 01:45, xxxxxxxx wrote:

    Sorry, no internal code will be disclosed.

    Anyway, this seems to be a problem with your implementation, and is probably not actually related to the actual GUI part of the code. My guess is that you simply didn't implement virtual Bool iCustomGui::SetData(const TriState <GeData> &tristate;);

    Or did you implement your own CustomDataType and did not implement virtual Bool CustomDataTypeClass::InterpolateKeys(GeData & res, const GeData& t_data1, const GeData& t_data2, Real mix, LONG flags)?

    Without any information about what you are doing, we can only speculate.

    Cheers,
    Frank



  • On 20/08/2013 at 08:57, xxxxxxxx wrote:

    Hi Frank,

    The problem is I don't know how to write the SetData() and the TriState code for this.
    That's probably what I'm missing.

    Maxon provided a CustomDataType example in the SDK.  So why can't they provide a custom GUI example also?
    I don't understand why one would be shareable knowledge. And the other one is a company secret?
    I'm not looking for company secrets. Just a working example of making a custom slider.
    I'd be happy to post my code though, if that's what Maxon requires.
    It's about 220 lines of code though. Kinda big.
    But if that's the only way to get a working example. I'm happy to do it.

    -ScottA



  • On 20/08/2013 at 14:06, xxxxxxxx wrote:

    I think I've finally got it figured out.
    My slider is now updating.

    -ScottA



  • On 25/08/2013 at 09:01, xxxxxxxx wrote:

    Hi Scott,

    would you share the solution with us?

    The description of your problem reminds me of some trouble I came across, too. iCustomGui::SetData() was called, but the visual didn't update. I figured it was due to
    the reason that no automatic refresh is invoked and you have to invoke this refresh yourself.

    Bool MyCustomGui::SetData(const TriState<GeData>& data) {
        // ...
      
        // Invoke a redraw on the user-area which is inside of our iCustomGui
        // (which is a GeDialog subclass).
        m_userarea.Refresh();
        return TRUE;
    }
    


  • On 25/08/2013 at 11:02, xxxxxxxx wrote:

    Old code example removed.

    -ScottA



  • On 25/08/2013 at 11:32, xxxxxxxx wrote:

    Hi Scott,

    some notes about your code:

    1. Do not store the mouse position and the user area width globally, you can store it as an
      attribute in your user-area. Furthermore, you can retrieve the width of a user-area at any
      point by calling GetWidth().
    2. You do not need to use the user-areas Timer() method. As I stated above, I have hit the
      same problem before and implementing the example I have shown does fix the updating
      problem (I tested it in your plugin source code you've linked). [See code-reference #1]
    3. The mouse position does not reflect the value of the slider 1-by-1. You can  calculate a value
      between 0 and 1 by dividing the mouse X position by the width of the user-area. To be more
      precise:

    Real v01 = static_cast<Real>(mx) / static_cast<Real>(this->GetWidth());

    You can then scale the value to match your minimum and maximum slider value. This is simple
    range mapping.

    Real vmm = v01 * (slider_max - slider_min) + slider_min;

    And then notify the parent about the value change as you have done properly in your code.
    See code-reference #2 for a more contextual snippet.
    4. The EventAdd() call from DrawMsg() is absolutely redundant and should be avoided.

    code-references

    #1

    virtual Bool SetData(const TriState<GeData> &tristate)
        {
            const GeData &value = tristate.GetValue();  //The value of the gui(the data port in xpresso & the data animation track)
            LONG data = value.GetLong();                //Assign the value to a long type variable so we can use it
            MousePos = data;                            //Use this value to set the slider's position using the data
            ua.Redraw();
            return TRUE;
        }

    #2

    class MyUserArea : public GeUserArea
        {

    public:

    **        Real value;**
    **        Real slider_min, slider_max;**

    virtual void DrawMsg(LONG x1, LONG y1, LONG x2, LONG y2, const BaseContainer& msg)
            {
                /* ... original code left out ... */

    LONG Left = static_cast <Real>(x2 - x1) * value;
                LONG Top = 0;
                LONG Bottom = 0;
                LONG Right = 0;
                DrawRectangle(x1+Left, y1+Top, x2-Right, y2-Bottom);

    // EventAdd();
            }

    virtual Bool InputEvent(const BaseContainer& msg)
            {
                BaseContainer action;                 //Create an empty container
                action.SetData(BFM_ACTION_ID, 1000);  //Store the ID for the active gizmo in the container

    BaseContainer state;
                while (GetInputState(BFM_INPUT_MOUSE, BFM_INPUT_MOUSELEFT, state)) //While the LMB is down 
                {
                    if (state.GetLong(BFM_INPUT_VALUE) == 0) break; //If the state value is 0 the LMB is no longer down..so don't continue

    LONG mx = state.GetReal(BFM_INPUT_X);
                    LONG my = state.GetReal(BFM_INPUT_Y);

    Global2Local(&mx,&my);
                    //GePrint("X Pos: " +LongToString(mx) + "   " +  "Y Pos: " +LongToString(my));

    **                // Calculate the value based on the user-areas size.**
    **                LONG width = GetWidth();**
    **                if (width == 0) continue; // avoid zero-division error, if the width is zero, nothing of the following needs to be done.**

    **                Real v01 = static_cast<Real>(mx) / static_cast<Real>(width);**
    **                value = v01 * (slider_max - slider_min) + slider_min;**

    **                // MousePos = mx;  //Send the position data to the global variable so that all the other classes and functions can use it**
                    Redraw();       //Refreshes the UA slider changes while dragging. Instead of when finished dragging

    //Send a message to the parent dialog telling it that the UA slider being used by the mouse in some manner and not finished
                    action.SetData(BFM_ACTION_INDRAG, TRUE);
                    SendParentMessage(action);

    //GeDialog *dlg = GetDialog(); 
                    BaseContainer actionmsg(BFM_ACTION);            //Create a container with the BFM_ACTION in it
                    actionmsg.SetLong(BFM_ACTION_ID, MY_MESSAGE);   //Add the ID of the dialog gizmo that has changed to the container 
                    BaseContainer actionresult;                     //Create another container to hold the result
                    dlg->Message(actionmsg, actionresult);          //Send a custom message containing this value to the dialog
                }

    //Notify the parent dialog that the dragging is now finished
                action.SetBool(BFM_ACTION_INDRAG, FALSE);
                SendParentMessage(action);
                Redraw();

    return TRUE;
            }
        };

    class MyCustomGui : public iCustomGui
        {
          typedef iCustomGui super;

    private:
            MyUserArea ua;   //A local copy of the User area so we can use it in this class

    public:

    MyCustomGui(const BaseContainer &settings, CUSTOMGUIPLUGIN *plug) : super(settings, plug) {
    **            // Retrieve the minimum and maximum slider value from the **
    **            ua.value = 0;**

    **            Real min_default = settings.GetReal(DESC_MIN, 0.0);**
    **            Real max_default = settings.GetReal(DESC_MAX, 1.0);**
    **            ua.slider_min = settings.GetReal(DESC_MINSLIDER, min_default);**
    **            ua.slider_max = settings.GetReal(DESC_MAXSLIDER, max_default);**
            }

    virtual Bool CreateLayout()
            {
                //Attach the User Area to the custom GUI gizmo
                AddUserArea(1000, BFH_SCALEFIT, 0, 0);
                AttachUserArea(ua, 1000);
                return TRUE;
            }
            virtual Bool InitValues(){ return TRUE; };
            virtual Bool Command(LONG id, const BaseContainer &msg)
            {
                if (id == MY_MESSAGE)
                {
                    valueChanged(this, msg);       //Execute this custom method when the LMB is pressed
                    this->SendParentMessage(msg);
                }

    return TRUE;
            }
            virtual Bool SetData(const TriState<GeData> &tristate)
            {
                const GeData &value = tristate.GetValue();  //The value of the gui(the data port in xpresso & the data animation track)
    **            // Real data = value.GetReal();                //Assign the value to a long type variable so we can use it**
    **            // MousePos = data;                            //Use this value to set the slider's position using the data**
    **            ua.value = value.GetReal();**
    **            ua.Redraw();**
                return TRUE;
            }

    };

    > Note : You still have to handle the case where multiple values need to be displayed in the GUI. You can check if you have to switch to the "Multiple Values" display by calling TriState<T>::GetTri(). You have to handle this case respectively in your UI.

    Best,
    -Niklas



  • On 25/08/2013 at 13:32, xxxxxxxx wrote:

    Thanks for the notes Nik.
    I had tried using the gizmo's width value to control the slider's position. But I could not get it to work properly.
    I used the SDK Sized() method for this rather than the GetWidth() function.
    But when I do that. The slider no longer matches (lines up with) the mouse cursor. So I just gave up on it and left it as is.
    It's really a minor thing. Because I can just set the max values to 300 in the UserData settings.
    But it bugs that it's not correct. And not working exactly like the Maxon slider.

    I tried using your code. But that just makes the gui flicker.

    From your reply. I can't tell whether your question was answered or not?

    -ScottA



  • On 25/08/2013 at 13:49, xxxxxxxx wrote:

    The flickering probably happened because I didn't point out all the places that needed to be adjusted. Replace your customgui.cpp with the file from this link and check it out. Work's like a charm here. The slider follows your mouse position correctly as long as you do the correct inverse calculation from InputEvent() inside DrawMsg(). You can of course also store the mouse position and draw the slider at this position, but this will make you unable to draw the slider when the value did not originate from mouse input but from a value in the objects container (for instance).

    I changed quite a few lines, you should look at the file in a diff viewer (eg. http://prettydiff.com/) too see all the changes clearly.

    https://dl.dropboxusercontent.com/u/99829745/stuff/2013-08-25-ScottA-slidercustomgui-changes-01.cpp

    Originally posted by xxxxxxxx

    From your reply. I can't tell whether your question was answered or not?

    I am not sure what you are referring to.

    -Niklas



  • On 25/08/2013 at 14:19, xxxxxxxx wrote:

    Oh sorry. I thought you were asking a question about timers.

    The full code example works much better. Thanks for posting that. 👍

    I'm going to delete the link to my example. But there really should be a compiled example for other people to use. This is something that should have been included in the SDK examples.
    Do you want to post one on your blog?
    Or would you like me to post one on my site?

    -ScottA


Log in to reply