iCustomGUI notify about changed value



  • On 15/07/2013 at 10:48, xxxxxxxx wrote:

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

    ---------
    I'm writing a Custom GUI for the DescID datatype. When my iCustomGUI dialog is allocated, it
    is notified about the data it should display with iCustomGUI::SetData().

    I want to accept dragging&dropping DescID's from the Attribute Manager, which basically works.
    The value change is reflected in the custom GUI, but not in the object's data! iCustomGUI::GetData()
    is never called. How can I notify the description host when the value was changed from within the
    custom-gui dialog?

    Thanks,
    -Nik



  • On 16/07/2013 at 02:18, xxxxxxxx wrote:

    I had the same problem a while ago. This part is a little tricky and quite impossible without knowing it ;)
    The following code fragment is from the String customgui example from here
    and is used to notify description changes.

      
    BaseContainer m( msg );   
    m.SetLong(BFM_ACTION_ID, GetId());   
    m.RemoveData(BFM_ACTION_VALUE);   
    m.SetData(BFM_ACTION_VALUE, GetData().GetValue());   
    SendParentMessage(m);   
    


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

    Thanks Burak, this worked!! :)



  • On 02/08/2013 at 05:30, xxxxxxxx wrote:

    #define COMMANDMSG_VALUECHANGED 1000
      
    /// ... From somewhere in the Dialog  
      
          if (catched) {  
              GeDialog* dlg = GetDialog();  
              BaseContainer actionmsg(BFM_ACTION), actionresult;  
              actionmsg.SetLong(BFM_ACTION_ID, COMMANDMSG_VALUECHANGED);  
              if (dlg) dlg->Message(actionmsg, actionresult);  
          }  
      
    /// ... Further stuff in the dialog (actually iCustomGui)  
      
      void SendValueChanged(BaseContainer msg) {  
          msg.SetLong(BFM_ACTION_ID, GetId());  
          msg.RemoveData(BFM_ACTION_VALUE);  
          msg.SetData(BFM_ACTION_VALUE, GetData().GetValue());  
          SendParentMessage(msg);  
      }  
      
      virtual Bool Command(LONG id, const BaseContainer& msg) {  
          if (id == COMMANDMSG_VALUECHANGED) {  
              SendValueChanged(msg);  
          }  
          return TRUE;  
      }
    

    This did the trick.



  • On 02/08/2013 at 08:14, xxxxxxxx wrote:

    What types of things can the iCustomGui class do?

    I've been meaning to try one some day. But the code I've seen so far is very confusing.
    Especially the registration section:

    Bool RegisterRealGUI()  
    {  
       static BaseCustomGuiLib mylib;  
       ClearMem(&mylib,sizeof(mylib));  
       FillBaseCustomGui(mylib);  
         
       return InstallLibrary(CINDIGO_REAL_CUSTOMGUI, &mylib, 1000, sizeof(mylib)) && RegisterCustomGuiPlugin("My String GUI", PLUGINFLAG_HIDE, gNew CindigoRealGui);  
    }
    

    That InstallLibrary stuff is all Greek to my eyes. 🤢
    I don't know what it does. Or if it's actually even needed.

    It would wonderful to have a fully working example to learn from.
    I hope one of you guys eventually posts one someday.

    -ScottA



  • On 02/08/2013 at 08:23, xxxxxxxx wrote:

    The customgui class enables you to make your own parameter types. Eg your own sliders or something like
    that in descriptions.

    For my current plugin, I made a LONG customgui that implements a tri-value like the Editor/Render modes of objects (undefined, on, off).

    The context menu appeared on the 4th parameter because I right-clicked on it.

    The library needs to be installed, otherwise no methods of your iCustomGui implementation will be called (and I don't know why, but that is fact).

    /**
     * Copyright (C) 2013, Niklas Rosenstein
     * All rights reserved.
     *
     * ProxyTriStateCustomGui.cpp
     */
      
    #include <c4d.h>
    #include "nrUtils/Gui.h"
    #include "../res/c4d_symbols.h"
      
    #define CUSTOMGUI_PROXYTRISTATE 1030913
      
    // Does not have to be unique, only used in dialog Command()
    #define COMMANDMSG_VALUECHANGED 1000
      
    // Only for the user-area, state value to draw the tri-state bitmap.
    #define MODE_TRISTATE 4
      
    static BaseBitmap* g_undef = NULL;
    static BaseBitmap* g_on = NULL;
    static BaseBitmap* g_off = NULL;
    static BaseBitmap* g_tristate = NULL;
      
    // Size and padding settings for the icon.
    struct {
        LONG w, h;
        LONG px, py;
    } sizeinfo = { 10, 10, 2, 2 };
      
    class ProxyTriStateUA : public GeUserArea {
      
    public:
      
        void SetState(LONG state) { m_state = state; }
      
        LONG GetState() const { return m_state; }
      
        // GeUserArea Overrides
      
        virtual Bool GetMinSize(LONG& w, LONG& h) {
            w = sizeinfo.w + sizeinfo.px * 2;
            h = sizeinfo.h + sizeinfo.py * 2;
            return TRUE;
        }
      
        virtual void DrawMsg(LONG x1, LONG y1, LONG x2, LONG y2, const BaseContainer& msg) {
            SetClippingRegion(x1, y1, x2, y2);
      
            DrawSetPen(COLOR_BG);
            DrawRectangle(x1, y1, x2, y2);
      
            BaseBitmap* icon = NULL;
            switch (m_state) {
                case MODE_UNDEF:
                    icon = g_undef;
                    break;
                case MODE_ON:
                    icon = g_on;
                    break;
                case MODE_OFF:
                    icon = g_off;
                    break;
                case MODE_TRISTATE:
                    icon = g_tristate;
                    break;
                default:
                    return;
            }
      
            if (icon) {
                DrawBitmap(icon, sizeinfo.px, sizeinfo.py, sizeinfo.w, sizeinfo.h,
                           0, 0, icon->GetBw(), icon->GetBh(), BMP_ALLOWALPHA);
            }
        }
      
        virtual Bool InputEvent(const BaseContainer& msg) {
            LONG device = msg.GetLong(BFM_INPUT_DEVICE);
            LONG channel = msg.GetLong(BFM_INPUT_CHANNEL);
            if (device != BFM_INPUT_MOUSE) return FALSE;
      
            Bool catched = FALSE;
            LONG xpos = msg.GetLong(BFM_INPUT_X);
            LONG ypos = msg.GetLong(BFM_INPUT_Y);
            Global2Local(&xpos, &ypos);
            switch (channel) {
                case BFM_INPUT_MOUSELEFT: {
                    if (
                        xpos >= sizeinfo.px && xpos <= (sizeinfo.w + sizeinfo.px + 1)
                     && ypos >= sizeinfo.py && ypos <= (sizeinfo.h + sizeinfo.py + 1)
                    ) {
                        m_state = (m_state + 1) % 3;
                        Redraw();
                        catched = TRUE;
                    }
                    break;
                }
                case BFM_INPUT_MOUSERIGHT: {
                    BaseContainer menubc;
                    menubc.SetString(1000 + MODE_UNDEF, GeLoadString(IDC_UNDEF));
                    menubc.SetString(1000 + MODE_ON, GeLoadString(IDC_ON));
                    menubc.SetString(1000 + MODE_OFF, GeLoadString(IDC_OFF));
                    LONG result = ShowPopupMenu(GetDialog()->Get(), MOUSEPOS, MOUSEPOS, menubc);
                    if (result >= 1000) {
                        m_state = (result - 1000) % 3;
                        Redraw();
                    }
                    catched = TRUE;
                    break;
                }
                default:
                    break;
            }
            if (catched) {
                GeDialog* dlg = GetDialog();
                nr::InvokeCommandCall(dlg, COMMANDMSG_VALUECHANGED);
            }
            return catched;
        }
      
    private:
      
        LONG m_state;
      
    };
      
    class ProxyTriStateGui : public nr::CustomGuiBase {
      
        typedef nr::CustomGuiBase super;
      
    public:
      
        ProxyTriStateGui(const BaseContainer& settings, CUSTOMGUIPLUGIN* plug)
        : super(settings, plug) {
        }
      
        // iCustomGui Overrides
      
        virtual LONG CustomGuiWidth() {
            LONG w = 0, h;
            m_ua.GetMinSize(w, h);
            return w;
        }
      
        virtual LONG CustomGuiHeight() {
            LONG w, h = 0;
            m_ua.GetMinSize(w, h);
            return h;
        }
      
        virtual void CustomGuiRedraw() {
            m_ua.Redraw();
        }
      
        virtual Bool SetData(const TriState<GeData>& tristate) {
            LONG mode;
            if (tristate.GetTri()) mode = MODE_TRISTATE;
            else {
                const GeData& value = tristate.GetValue();
                switch (value.GetLong()) {
                    case MODE_UNDEF:
                    case MODE_ON:
                    case MODE_OFF:
                        mode = value.GetLong();
                        break;
                    default:
                        mode = MODE_ON;
                        break;
                }
            }
            m_ua.SetState(mode);
            m_ua.Redraw();
            return TRUE;
        }
      
        virtual TriState<GeData> GetData() {
            LONG mode = m_ua.GetState();
            if (mode == MODE_TRISTATE) return TriState<GeData>();
            else return TriState<GeData>(GeData(mode));
        }
      
        // GeDialog Overrides
      
        virtual Bool CreateLayout();
      
        virtual Bool InitValues();
      
        virtual Bool Command(LONG id, const BaseContainer& msg) {
            if (id == COMMANDMSG_VALUECHANGED) {
                SendValueChanged(msg);
            }
            return TRUE;
        }
      
    private:
      
        ProxyTriStateUA m_ua;
      
    };
      
    Bool ProxyTriStateGui::CreateLayout() {
        AddUserArea(1000, BFH_SCALEFIT | BFV_SCALEFIT,
                    sizeinfo.w + sizeinfo.px * 2,
                    sizeinfo.h + sizeinfo.py * 2);
        AttachUserArea(m_ua, 1000);
        return TRUE;
    }
      
    Bool ProxyTriStateGui::InitValues() {
        return TRUE;
    }
      
    class ProxyTriStateData : public CustomGuiData {
      
        typedef CustomGuiData super;
      
    public:
      
        // CustomGuiData Overrides
      
        virtual LONG GetId() { return CUSTOMGUI_PROXYTRISTATE; }
      
        virtual CDialog* Alloc(const BaseContainer& settings) {
            GeDialog* dlg = gNew ProxyTriStateGui(settings, GetPlugin());
            if (!dlg) return NULL;
      
            return dlg->Get();
        }
      
        virtual void Free(CDialog* dlg, void* ud) {
            if (dlg) {
                gDelete((GeDialog*&) dlg);
            }
        }
      
        virtual const CHAR* GetResourceSym() {
            return "PROXYTRISTATE";
        }
      
        virtual LONG GetResourceDataType(LONG*& table) {
            static LONG l_table[] = {
                DA_LONG,
            };
            table = l_table;
            return sizeof(l_table) / sizeof(LONG);
        }
      
    };
      
    BaseBitmap* LoadResourceImage(const String& name) {
        AutoBitmap abmp(name);
        BaseBitmap* bmp = abmp;
        if (bmp) return bmp->GetClone();
        return NULL;
    }
      
    Bool RegisterProxyTriStateCustomGui() {
        // Register a fake library. Required for recieving callbaks in iCustomGui.
        static BaseCustomGuiLib lib;
        ClearMem(&lib, sizeof(lib));
        FillBaseCustomGui(lib);
        if (!InstallLibrary(CUSTOMGUI_PROXYTRISTATE, &lib, 1000, sizeof(lib))) return FALSE;
      
        g_undef = LoadResourceImage("proxytristate-undef.tif");
        g_on = LoadResourceImage("proxytristate-on.tif");
        g_off = LoadResourceImage("proxytristate-off.tif");
        g_tristate = LoadResourceImage("proxytristate-tristate.png");
      
        return RegisterCustomGuiPlugin("Proxy Tri State", CUSTOMGUI_SUPPORT_LAYOUTDATA, gNew ProxyTriStateData);
    }
      
    void FreeProxyTriStateCustomGui() {
        BaseBitmap::Free(g_undef);
        BaseBitmap::Free(g_on);
        BaseBitmap::Free(g_off);
        BaseBitmap::Free(g_tristate);
    }
    

    This is the complete source code of ProxyTriStateCustomGui.cpp. The nr::CustomGuiBase::SendValueChanged() and nr::InvokeCommandCall() only do what I have shown above already. nr::CustomGuiBase inherits directly from iCustomGui and does not override any methods.

    Cheers,
    -Nik



  • On 02/08/2013 at 08:44, xxxxxxxx wrote:

    Thanks for posting the code Nik.

    Would it be too much of an imposition to post a very simple plugin example with all the files?
    Because it will take me a while to figure out how to create all the various files(res, main, etc.) and I will probably run into some troubles when doing that. Since this is something I've never done before.
    For example: Supposing I'm able to create the GUI. How do I add this custom GUI to my plugin?

    If you don't have the time(or simply don't want to). Please don't go through the effort.
    You're allowed to say "no". 🙂
    But if it's something you can easily do. It would be a big help to me. And probably others too.

    Thanks,
    -ScottA



  • On 11/08/2013 at 12:47, xxxxxxxx wrote:

    https://github.com/nr-plugins/example-tristate-gui


Log in to reply