johnpierson / Relay

Relay allows for you to add Dynamo graphs to your Revit ribbon.
GNU General Public License v3.0
54 stars 12 forks source link

UI Improvement Suggestion - SplitButtonGroups for Multiple Dyns #14

Open EwanOpie opened 1 year ago

EwanOpie commented 1 year ago

Hi @johnpierson Firstly, wicked tool! Going to be heaps of fun to use.

Secondly, I see that when there is more than 1 dyn in a folder ( Tab section ) that they are inserted in a stack of 2. image

Would a slight tweak be possible to allow for when there are more than 2 dyns in a folder a SplitButtongroup is created that allows the usual dropdown selection for Revit toolbar items? image

johnpierson commented 1 year ago

Hey @EwanOpie! Thanks so much for the kind words. I can definitely add it to the wishlist as that has came up once or twice. I mostly need to conceptualize how that gets defined by the user. Do you have any ideas? Folder suffix or something?

Eg. "My Dynamo Folder 1.split" would result in split buttons.

EwanOpie commented 1 year ago

I think a suffix would work well and keep thing self-explanetory for users. Keeping the naming aligned with the API definitions could be a good way to do this.

E.g. FolderName.SplitButtons FolderName.StackedButtons

Do you think a fail-safe should be included if a user specifies more than 3 dyn's in a FolderName.StackedButtons folder ( I think that was Revit's maximum ), that it defaults to use FolderName.SplitButton settings? Would at least keep functionality while giving the user an indication there is something not quite correct?

For what Relay provides, I don't think any other styles of panel items would be required, as any kind of additional inputs required by the user should be coded into the dyn's (E.g. Winform's or Dialogs)

EwanOpie commented 1 year ago

I might try and find some time to load all the references into VS to try this 😅, probably not as simple as that though...

EDIT: This worked a treat, not using folder suffix for now as my use case is purely testing, and something wasn't quite working with the 'Sync' function, so I temporarily disabled it. Will grab some feedback from my testing group 👍

image

image

EwanOpie commented 1 year ago

@johnpierson Just a comment, when implementing the SplitButtons, there are some timing considerations for when / how the Globals.GraphToRun gets updated. Specifically as to wait for the ComponentManagerOnUIElementActivated to complete fully and return the new "Current" item. Revit seemed to want to do things to quickly, running the Dynamo graph last selected before the new one was registered as the new Global.

I had to Invoke some Delegates and use a ManualResetEvent to get this to work, with a stop-gap of writing an external "GraphToRun" file as the condition to then continue with the cmd execution. I also had to use a different variable to access which button was 'selected',

var adskType = e.UiElement.GetType();
PropertyInfo adskProp = adskType.GetProperty("CommandParameter");
object cmdItem = adskProp.GetValue(e.UiElement);

Process

I hope some of that helps when you get a chance to look at things.

brencass commented 1 year ago

Within the RibbonUtils.AddItems method you add a way to tell if a button is to be stackable, then this is added to a stackable button model in a similar way to the following: image

StackableButtonModel: image

Once you have a list of stackable buttons, you could utilise the following logic to happen after the main set of ribbon has been completed.

if (Globals.StackAbleButtons.Count > 0)
{
    foreach ( Model.StackableButtonModel stackableButton in Globals.StackAbleButtons)
    {
        int pushButtonDataCount = stackableButton.PushButtonList.Count;

        if (pushButtonDataCount == 1)
        {
            stackableButton.RevitPanel.AddItem(stackableButton.PushButtonList.FirstOrDefault());
        }
        else if (pushButtonDataCount > 2)
        {
            if (pushButtonDataCount == 2)
            {
                stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[0], stackableButton.PushButtonList[1]);

                Autodesk.Windows.RibbonItem tempbutton1 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[0].Name);
                Autodesk.Windows.RibbonItem tempbutton2 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[1].Name);

                tempbutton1.ShowText = false;
                tempbutton1.Size = RibbonItemSize.Large;
                tempbutton2.ShowText = false;
                tempbutton2.Size = RibbonItemSize.Large;
            }
            else if (pushButtonDataCount == 4)
            {
                stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[0], stackableButton.PushButtonList[1]);
                stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[2], stackableButton.PushButtonList[3]);

                Autodesk.Windows.RibbonItem tempbutton1 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[0].Name);
                Autodesk.Windows.RibbonItem tempbutton2 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[1].Name);
                Autodesk.Windows.RibbonItem tempbutton3 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[2].Name);
                Autodesk.Windows.RibbonItem tempbutton4 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[3].Name);

                tempbutton1.ShowText = false;
                tempbutton1.Size = RibbonItemSize.Large;
                tempbutton2.ShowText = false;
                tempbutton2.Size = RibbonItemSize.Large;
                tempbutton3.ShowText = false;
                tempbutton3.Size = RibbonItemSize.Large;
                tempbutton4.ShowText = false;
                tempbutton4.Size = RibbonItemSize.Large;
            }
            else if (pushButtonDataCount == 3)
            {
                stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[0], stackableButton.PushButtonList[1],stackableButton.PushButtonList[2]);
            }
            else
            {
                int numBoxes = pushButtonDataCount / 3;
                int remainder = pushButtonDataCount % 3;

                if (remainder == 0)
                {
                    int i = 0;

                    while (i < numBoxes)
                    {
                        List<int> stackCountList = Enumerable.Range(i * 3, 3).ToList();

                        stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[stackCountList[0]], stackableButton.PushButtonList[stackCountList[1]], stackableButton.PushButtonList[stackCountList[2]]);

                        Autodesk.Windows.RibbonItem tempbutton1 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[stackCountList[0]].Name);
                        Autodesk.Windows.RibbonItem tempbutton2 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[stackCountList[1]].Name);
                        Autodesk.Windows.RibbonItem tempbutton3 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[stackCountList[2]].Name);

                        tempbutton1.ShowText = false;
                        tempbutton2.ShowText = false;
                        tempbutton3.ShowText = false;

                        i++;
                    }
                }
                else if (remainder == 2)
                {
                    Autodesk.Windows.RibbonItem tempbutton1;
                    Autodesk.Windows.RibbonItem tempbutton2;
                    Autodesk.Windows.RibbonItem tempbutton3;

                    int i = 0;

                    stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[pushButtonDataCount - 2], stackableButton.PushButtonList[pushButtonDataCount - 1]);

                    tempbutton1 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[pushButtonDataCount - 2].Name);
                    tempbutton2 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[pushButtonDataCount - 1].Name);

                    tempbutton1.ShowText = false;
                    tempbutton1.Size = RibbonItemSize.Large;
                    tempbutton2.ShowText = false;
                    tempbutton2.Size = RibbonItemSize.Large;

                    while (i < numBoxes)
                    {
                        List<int> stackCountList = Enumerable.Range(i * 3, 3).ToList();

                        stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[stackCountList[0]], stackableButton.PushButtonList[stackCountList[1]], stackableButton.PushButtonList[stackCountList[2]]);

                        tempbutton1 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[stackCountList[0]].Name);
                        tempbutton2 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[stackCountList[1]].Name);
                        tempbutton3 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[stackCountList[2]].Name);

                        tempbutton1.ShowText = false;
                        tempbutton2.ShowText = false;
                        tempbutton3.ShowText = false;

                        i++;
                    }
                }
                else
                {
                    Autodesk.Windows.RibbonItem tempbutton1;
                    Autodesk.Windows.RibbonItem tempbutton2;
                    Autodesk.Windows.RibbonItem tempbutton3;
                    Autodesk.Windows.RibbonItem tempbutton4;

                    int i = 0;

                    stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[pushButtonDataCount - 4], stackableButton.PushButtonList[pushButtonDataCount - 3]);
                    stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[pushButtonDataCount - 2], stackableButton.PushButtonList[pushButtonDataCount - 1]);

                    tempbutton1 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[pushButtonDataCount - 4].Name);
                    tempbutton2 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[pushButtonDataCount - 3].Name);
                    tempbutton3 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[pushButtonDataCount - 2].Name);
                    tempbutton4 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[pushButtonDataCount - 1].Name);

                    tempbutton1.ShowText = false;
                    tempbutton1.Size = RibbonItemSize.Large;
                    tempbutton2.ShowText = false;
                    tempbutton2.Size = RibbonItemSize.Large;
                    tempbutton3.ShowText = false;
                    tempbutton3.Size = RibbonItemSize.Large;
                    tempbutton4.ShowText = false;
                    tempbutton4.Size = RibbonItemSize.Large;

                    numBoxes = numBoxes - 1;

                    while (i < numBoxes)
                    {
                        List<int> stackCountList = Enumerable.Range(i * 3, 3).ToList();

                        stackableButton.RevitPanel.AddStackedItems(stackableButton.PushButtonList[stackCountList[0]], stackableButton.PushButtonList[stackCountList[1]], stackableButton.PushButtonList[stackCountList[2]]);

                        tempbutton1 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[stackCountList[0]].Name);
                        tempbutton2 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[stackCountList[1]].Name);
                        tempbutton3 = RibbonUtils.GetButton(stackableButton.TabName, stackableButton.Name, stackableButton.PushButtonList[stackCountList[2]].Name);

                        tempbutton1.ShowText = false;
                        tempbutton2.ShowText = false;
                        tempbutton3.ShowText = false;

                        i++;
                    }
                }

            }
        }
    }
}
brencass commented 1 year ago

@EwanOpie How did you get around the common bug when using FileSystemWatcher because it does sometimes fire two change events off.

EwanOpie commented 1 year ago

I don't think I have experienced that particular bug @brencass . Can you post an example log?

So far my implementation has been performing well, the only minor item being users need to select the tool from the drop-down but run it using the top button once it has been selected.

brencass commented 1 year ago

@EwanOpie Maybe revit is stopping a secondary load of dynamo, so it is not becoming apparent. I have noticed in a test i did that the event that fires up was loading twice when data was saved to a specific file the FileSystemWatcher was watching.

It seems to be a known bug - https://stackoverflow.com/a/1765094