Editing Custom material from modal dialog vs panel mode

In Rhino there are different ways to edit materials/environments, and handling these from the plugin is bit different (dependent on the actual mode), which causes problem here.

Normally in case the user edits the Custom Material from the Material panel. In this case every change happens instantly. But it is also possible to edit the material from a Modal Dialog (for example “Layer Material”) and in this case only after the user clicks on the OK button should the plugin accept the new values, but from the Eto Collapsible form it is not visible. I mean the plugin has no idea about the mode the material is edited and how to handle the change. (Also it knows nothing about what button the user clocks at the and - OK or Cancel).

What is the correct way to handle it? Maybe I could try to investigate what kind if dialog is on the top, and this could be a sign for the plugin how to handle changes, but it is a bit messy solution.

Márton

@nathanletwory - is this something you can help with?

I don’t know the modal dialog very well, I use it about never. I don’t know if any of my materials in plug-ins (raytraced blend material, raytraced materials) work with it.

@marton.parlagh, feel free to have a look at https://github.com/mcneel/RhinoCycles/blob/rhino-6.x/Materials/DiffuseMaterial.cs and see if they work with the modal dialog as expected. At least I haven’t got any bug reports related to that. AFAIK it all just works. There is nothing special in the materials as far as I can tell.

It’d be helpful if you could explain and illustrate the problems you are seeing, along with what you’re doing that makes it difficult.

Hello Nathan,

How can I find these Cycles materials normally in Rhino. These are not part of the base install right?

Márton

Those come with Rhino, but aren’t visible by default. You have to run the test command TestShowPrivateContent. After this the materials will be accessible through the UI.

I think you use only Automatic Userinterface section right?

In my case I use only hidden fields, and the plugin updates the fields with the new data, when the user changes parameter on the custom UI.

I tried with automatic Fields, and those are OK.
But I am not sure how to handle this with my custom UI.

When the user changes my custom ui control, the plugin updates the relevant hidden content Field like this:

renderContent.BeginChange(Rhino.Render.RenderContent.ChangeContexts.UI);

renderContent.Fields.Set(myKey, myValue);

renderContent.EndChange();

And when Rhino opens my custom UI, the plugin reads the field values, and updates the custom UI according to the actual Field values.

It works fine with modeless dialogs, but the model dialog changes something.
The result is, when user clicks on cancel, the rendercontent keeps the latest values. And when user clicks OK, rendercontent reverses the changes. So as a result the behavior is the opposite of the expected.

Márton

I use only automatic user interface in these materials, correct.

For a modal dialog you’ll be working with a material that is not in the document, but in a sandbox. I’m not sure how you should handle that case, @maxsoder may know better.

Hi, @marton.parlagh If I understood it correctly the Modal dialog buttons (Ok, Cancel) work as if the buttons where switched. “Opposite as of expected”. It sounds like there is a bug in Rhino.

I have one question. I assume this is Windows as the button reads Ok instead of Apply. I would be interested to know if this happens also for your plugin on the Mac.

Regards, Max

Unfortunately I was not able to test it under OSX. There is a bug with Eto forms (maybe you have already fixed it but my version still crashes), and when I try to change layer material to my Material type in Modal dialog mode, it crashes.

Márton

Have you ran this with the debugger attached?

When you are updating your UI you have to ensure you’re not using the material from the document that this modal dialog is shown for. No references to a Rhino document should be made, including ActiveDoc.

Yes, and also without debugger. And it seems it has problem with eto form parent??? or something like that.

Without seeing the callstack and accompanying code it is hard to say why the crash happens.

edit: nvm, I realized this is the crash you already had a code snippet posted for earlier. (Under OSX Eto form can crash Rhino).

Do you have a slightly more elaborate code sample based on that from the post that shows the problem with the modal material dialog that you’re experiencing?

I think it is already fixed

https://mcneel.myjetbrains.com/youtrack/issue/RH-56323

It seems the crash fix will be in v.6.23.

I will test this on both the Mac and Win and try to figure out what is wrong with closing of the modal dialog.

Hmmmm, I am confused now.

As I was trying to put together a bit better sample code, but the result I wanted to show you is actually working normally. It means I have to recheck my own code, the Rhino dialog OK/Cancel buttons probably work fine… I will be back…

Márton

I also made a simple sample and tested on the Mac and Win. The Modal dialog case seems to work. I did my tests on 7.x, but the mechanism should be the same as in v6.

I will put my test code here so that others can have a look at it.

My test code

public class TestMaterialUiSection : Rhino.UI.Controls.EtoCollapsibleSection
  {
    private CheckBox m_cb;

    public Rhino.Render.RenderContentCollection RenderContentCollectionSelectionReader
    {
      get
      {
        return ViewModel.GetData(Rhino.UI.Controls.DataSource.ProviderIds.ContentSelection, false, false) as Rhino.Render.RenderContentCollection;
      }
    }

    public TestMaterialUiSection()
    {
      DataChanged += OnDataChanged;

      m_cb = new CheckBox();
      m_cb.Text = "My custom bool value";
      m_cb.ThreeState = false;
      m_cb.CheckedChanged += OnCheckedChanged;

      Label label = new Label();
      label.Text = "Hello there";

      TableLayout table = new TableLayout()
      {
        Spacing = new Eto.Drawing.Size(5, 5),
        Rows =
        {
          new TableRow(label),
          new TableRow(m_cb),
          null
        }
      };

      Content = table;
    }

    public override Guid ViewModelId
    {
      get
      {
        return Guid.Empty;
      }
    }

    public override Rhino.UI.LocalizeStringPair Caption
    {
      get
      {
        return new LocalizeStringPair("TestMaterial UI", "TestMaterial UI");
      }
    }

    public override int SectionHeight
    {
      get
      {
        return 250;
      }
    }

    private void OnDataChanged(object sender, Rhino.UI.Controls.DataSource.EventArgs args)
    {
      if (args.DataType == Rhino.UI.Controls.DataSource.ProviderIds.ContentParam ||
          args.DataType == Rhino.UI.Controls.DataSource.ProviderIds.ContentSelection)
        DisplayData();
    }

    private void DisplayData()
    {
      Rhino.Render.RenderContentCollection collection = RenderContentCollectionSelectionReader;

      if (collection != null)
      {
        ContentCollectionIterator iterator = collection.Iterator();

        Rhino.Render.RenderContent content = iterator.First();

        if (content != null)
        {
          bool test_key = false;
          content.Fields.TryGetValue("My custom bool value", out test_key);

          m_cb.CheckedChanged -= OnCheckedChanged;
          m_cb.Checked = test_key;
          m_cb.CheckedChanged += OnCheckedChanged;
        }

        iterator.DeleteThis();
      }
    }

    private void OnCheckedChanged(object sender, EventArgs e)
    {
      Rhino.Render.RenderContentCollection collection = RenderContentCollectionSelectionReader;

      if (collection != null)
      {
        ContentCollectionIterator iterator = collection.Iterator();

        Rhino.Render.RenderContent content = iterator.First();

        if (content != null)
        {
          content.BeginChange(Rhino.Render.RenderContent.ChangeContexts.UI);

          content.Fields.Set("My custom bool value", (bool)m_cb.Checked);

          content.EndChange();
        }

        iterator.DeleteThis();
      }
    }
  }

  [Guid ("37D8ABBD-14BD-488C-B717-1FAA1F14EF62")]
  public class TestMaterial : RenderMaterial
  {
    public override string TypeName { get { return "Test Material (DEV)"; } }
    public override string TypeDescription { get { return "Test Material (DEV)"; } }

    public float Gamma { get; set; }

    public TestMaterial ()
    {
      Fields.Add ("My custom bool value", false);
      Fields.Add ("diffuse_color", Rhino.Display.Color4f.White, "Diffuse Color");
    }

    protected override void OnAddUserInterfaceSections ()
    {
      AddAutomaticUserInterfaceSection ("Parameters", 0);

      AddUserInterfaceSection (new TestMaterialUiSection ());
    }

    public override void SimulateMaterial (ref Rhino.DocObjects.Material simulatedMaterial, bool forDataOnly)
    {
      base.SimulateMaterial (ref simulatedMaterial, forDataOnly);

      Rhino.Display.Color4f color;
      if (Fields.TryGetValue ("diffuse_color", out color)) {
        simulatedMaterial.DiffuseColor = color.AsSystemColor ();
      }
    }
  }
´´´

In my code sample I get the content from the Selection datasource “RenderContentCollectionSelectionReader”. This datasource handles the Modal/Non Modal case. The UI Section does not need to know if it is displayed in a Modal or a non Modal dialog. In the Modal case the RenderContentCollectionSelectionReader datasource will return a content that is a copy from the actual content in the document. If the user click Ok, then the data will be copied to the content in the document. If the user clicks cancel, then the copied content is just discarded.

In the non modal case, the RenderContentCollectionSelectionReader will return the actual content in the document.

Thanks a lot!

It works now!
My code was similar, but instead of

if (args.DataType == Rhino.UI.Controls.DataSource.ProviderIds.ContentParam ||
          args.DataType == Rhino.UI.Controls.DataSource.ProviderIds.ContentSelection)

I used only
args.DataType == Rhino.UI.Controls.DataSource.ProviderIds.ContentSelection

and it seems it has caused the miss-synchronization.

Márton

1 Like

Good to know you got it solved :slight_smile:

In my case on my custom environments

if (args.DataType == Rhino.UI.Controls.DataSource.ProviderIds.ContentParam ||
          args.DataType == Rhino.UI.Controls.DataSource.ProviderIds.ContentSelection)

is not always good (enough) indicator to update the ui display.

But this one works:

if (e.DataType == Rhino.UI.Controls.DataSource.ProviderIds.ContentDatabase ||
e.DataType == Rhino.UI.Controls.DataSource.ProviderIds.ContentSelection)

Is it wrong?

Márton