Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    GeDialog TabGroup

    Cinema 4D SDK
    r19 c++
    3
    6
    1.1k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • C4DSC
      C4DS
      last edited by

      Hi,
      I have a GeDialog and would like to provide a group of tabs the user can select from.
      The number of tabs should be dynamical, for this I was playing with the idea to provide an empty "Add" tab. When the user selects this tab a new tab with gadgets is inserted before the "Add" tab.

      This seems to be an easy solution, but there is a drawback.
      When more tabs are created than available space, an extra button is displayed in the group, used to navigate through the tabs (left and right arrows).
      Unfortunately, when the last-but-one tab is selected and user presses this "right-navigation" button, the "Add" tab gets selected. Since the tabgroup is in the process of being updated as a result of navigation changes, and a press on the "Add" tab results in an extra tab being inserted, the whole thing triggers a breakpoint. I admit, not a crash, but still something I'd like to avoid.

      Is there a way to detect the user selected this "navigation" button? A message being sent?

      1 Reply Last reply Reply Quote 0
      • CairynC
        Cairyn
        last edited by

        Just from a GUI designer perspective: Don't.

        It may look like a good idea because of reasons... but it's an unusual way to use a tab; it's not what a user expects (even with it saying "Add" - it's a tab and not a button); it's breaking the familiar way of how a tab row works.

        1 Reply Last reply Reply Quote 0
        • C4DSC
          C4DS
          last edited by

          Thanks for the feedback.
          I just wanted to mimic the way Firefox or Chrome handles the tabs and creation of new ones.
          If that's a bad design, I'll look for something different.

          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by m_adam

            Hi @C4DS,
            First of all, as @Cairyn said, keep in mind this behavior/workflow is completely new for C4D user and it can be a bit weird for them. But is up to you to decide the overall design of your plugin.

            With that's said and as you already figured it out, the main issue is you are changing data within a redraw.
            To fix that, use a CoreMessage. See GeDialog Core Messages Manual.

            #define GROUP_ID 9999
            #define TAB_GROUP_ID 10000
            #define REDRAW_MSG_ID 1000001 // Get a unique ID at https://developers.maxon.net/forum/pid
            
            class TabDialog : public GeDialog
            {
            
            public:
            	maxon::BaseArray<Int32> tabIds;
            
            	Bool CreateLayout()
            	{
            		if (tabIds.IsEmpty())
            		{
            			tabIds.Append(10001);
            			tabIds.Append(10011);
            			tabIds.Append(10021);
            		}
            		DrawTab();
            		return true;
            	};
            
            	void DrawTab(Bool redraw=false)
            	{
            		// If we redraw, the group already exist so we flush it.
            		if (redraw)
            			LayoutFlushGroup(GROUP_ID);
            
            		// Otherwise we create it.
            		else
            			GroupBegin(GROUP_ID, BFH_SCALEFIT | BFV_TOP, 1, 1, ""_s, 0);
            
            		// Then we create our TabGroup
            		TabGroupBegin(TAB_GROUP_ID, BFH_SCALEFIT | BFV_SCALEFIT, TAB_TABS);
            		Int32* lastId = tabIds.GetLast();
            		for (Int32 x : tabIds)
            		{
            			String groupTitle;
            			if (x == *lastId)
            				groupTitle += "+"_s;
            			else
            				groupTitle += FormatString("Group @"_s, x);
            
            			GroupBegin(x, BFH_SCALEFIT | BFV_SCALEFIT, 1, 0, groupTitle, 0);
            			AddCheckbox(x + 1, BFH_LEFT, 0, 0, "Some Elements"_s);
            			GroupEnd();
            		}
            		GroupEnd();
            		GroupEnd();
            
            		if (redraw)
            			LayoutChanged(GROUP_ID);
            	};
            
            	Bool Command(Int32 id, const BaseContainer& msg)
            	{
            		// Check the command come from the Tab
            		if (id == TAB_GROUP_ID)
            		{
            			// Get the clicked tab ID
            			Int32 newTabId;
            			GetInt32(TAB_GROUP_ID, newTabId);
            
            			// Get the last value of our tabs
            			Int32* lastValue = tabIds.GetLast();
            			if (lastValue == nullptr)
            				return GeDialog::Command(id, msg);
            
            			// If it's the last value, we add a new tab, and call a Redraw
            			if (newTabId == *lastValue)
            			{
            				tabIds.Append(*lastValue + 10);
            				SpecialEventAdd(REDRAW_MSG_ID);
            			}
            			
            		}
            		return GeDialog::Command(id, msg);
            	};
            
            	Bool CoreMessage(Int32 id, const BaseContainer& msg)
            	{
            		switch(id)
            		{
            			case REDRAW_MSG_ID:
            			{
            				// check if this core message is new
            				if (!CheckCoreMessage(msg))
            					break;
            
            				DrawTab(true);
            				
            				// Set the active tab to the one which is just added
            				if (tabIds.GetCount() >= 2)
            					SetInt32(TAB_GROUP_ID, tabIds[tabIds.GetCount() - 2]);
            				break;
            			}
            		}
            		return GeDialog::CoreMessage(id, msg);
            	}
            
            };
            

            If you have any question, please let me know.
            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            1 Reply Last reply Reply Quote 2
            • C4DSC
              C4DS
              last edited by

              Thanks for the explanation and the code, Maxime.
              However, looking at your code I can see the logic how to avoid updating data during a redraw. But I still don't see how this solves the actual issue of the navigation button.

              With your example, when using the right navigation button, it will still create extra tabs due to the "Add" tab being selected.
              I was hoping to capture a message related to this navigation button and actually be able to skip the "set add tab active".
              Currently, there seems no possibility to distinguish between the actual user pressing a tab, or a tab being selected through the navigation tab. Let alone a way to detect if the navigation button's left or right part is being selected.

              1 Reply Last reply Reply Quote 0
              • M
                m_adam
                last edited by m_adam

                Hi @C4DS,

                Unfortunately, there are no messages for clicking on this button since Tab Group was really not designed to handle to this kind of cases.

                With that's said, I've thought of BFM_GETCURSORINFO, maybe you can find a way to get the coordinate of theses button, and if they are drawn. Then you can define a member variable for enabling/disabling the addition of a new Tab according to the mouse position.

                Cheers,
                Maxime.

                MAXON SDK Specialist

                Development Blog, MAXON Registered Developer

                1 Reply Last reply Reply Quote 1
                • First post
                  Last post