[Python][Eto] Issues making a form modeless

I took one of the examples and wish to make it modeless.
As I understand modeless correctly it allows the form to run without locking Rhino.
A very important thing for me.

Here’s the code:

###################
### TESTING ETO ###
###################
import System
import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino
import time

tol = sc.doc.ModelAbsoluteTolerance

# Imports
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms

#################
#               #
#################
class CollapsableEtoDialog(Eto.Forms.Form): # return True or False from ShowModal()
    def __init__(self):
        self.Title = "My Collapsible Eto Form"
        self.Resizable = False
        self.Padding = drawing.Padding(5)
        
        # very important hook!
        self.Closed += self.OnFormClosed
        
        # Custom label helper to set alignment
        def L(text):
            m_label = forms.Label()
            m_label.Text = text
            m_label.VerticalAlignment = forms.VerticalAlignment.Center
            m_label.TextAlignment = forms.TextAlignment.Right
            return m_label
            
            
        # set content of the collapsed section
        self.collapsePanel = forms.DynamicLayout(Visible = False, Padding = drawing.Padding(40, 10), DefaultSpacing = drawing.Size(5, 5))
        self.collapsePanel.BeginVertical()
        self.collapsePanel.AddRow(None, L("Value1:"), forms.NumericUpDown())
        self.collapsePanel.AddRow(None, L("Value2:"), forms.NumericUpDown())
        self.collapsePanel.AddRow(None, L("Value3:"), forms.NumericUpDown())
        self.collapsePanel.AddRow(None, L("Value4:"), forms.NumericUpDown())
        self.collapsePanel.AddRow(None, L("Value5:"), forms.NumericUpDown())
        self.collapsePanel.AddRow(None, L("Value6:"), forms.NumericUpDown())
        self.collapsePanel.AddRow(None, L("Value7:"), forms.NumericUpDown())
        self.collapsePanel.AddRow(forms.Button(Text="Atta boy, a button"))
        self.collapsePanel.EndVertical()
        self.collapsePanel.BeginVertical()
        self.collapsePanel.AddRow(None, forms.CheckBox(Text = "CheckBox1"))
        self.collapsePanel.AddRow(None, forms.CheckBox(Text = "CheckBox2"), forms.CheckBox(Text = "CheckBox2.1"), None)
        self.collapsePanel.AddRow(None, forms.CheckBox(Text = "CheckBox3"))
        self.collapsePanel.EndVertical()
        
        
        
        # button to toggle collapsing
        self.collapseButton = forms.Button(Text = "v", MinimumSize = drawing.Size.Empty)
        self.collapseButton.Click += self.collapseButton_Click
        
        self.collapsePanel1 = forms.DynamicLayout(Visible = False, Padding = drawing.Padding(40, 110), DefaultSpacing = drawing.Size(5, 5))
        self.collapsePanel1.BeginVertical()
        self.collapsePanel1.AddRow(None, L("Value1:"), forms.NumericUpDown())
        self.collapsePanel1.AddRow(None, L("Value2:"), forms.NumericUpDown())
        self.collapsePanel1.AddRow(None, L("Value3:"), forms.NumericUpDown())
        self.collapsePanel1.AddRow(None, L("Value4:"), forms.NumericUpDown())
        self.collapsePanel1.AddRow(None, L("Value5:"), forms.NumericUpDown())
        self.collapsePanel1.AddRow(None, L("Value6:"), forms.NumericUpDown())
        self.collapsePanel1.AddRow(None, L("Value7:"), forms.NumericUpDown())
        self.collapsePanel1.AddRow(forms.Button(Text="Atta boy, a button"))
        self.collapsePanel1.EndVertical()
        self.collapsePanel1.BeginVertical()
        self.collapsePanel1.AddRow(None, forms.CheckBox(Text = "CheckBox1"))
        self.collapsePanel1.AddRow(None, forms.CheckBox(Text = "CheckBox2"), forms.CheckBox(Text = "CheckBox2.1"), None)
        self.collapsePanel1.AddRow(None, forms.CheckBox(Text = "CheckBox3"))
        self.collapsePanel1.EndVertical()
        
        # button to toggle collapsing
        self.collapseButton1 = forms.Button(Text = "v", MinimumSize = drawing.Size.Empty)
        self.collapseButton1.Click += self.collapseButton_Click1
        
        toleranceUpDown = forms.NumericUpDown()
        
        # our main layout
        layout = forms.DynamicLayout(DefaultSpacing = drawing.Size(2,2))
        layout.AddSeparateRow(None, L("Tolerance"), toleranceUpDown, L("millimeters"), self.collapseButton)
        layout.AddCentered(self.collapsePanel) # we need this auto-sized so we can get its width to adjust form height
        layout.Add(None) # expanding space, in case you want the form re-sizable
        ###################################
        #layout = forms.DynamicLayout(DefaultSpacing = drawing.Size(2,2))
        layout.AddSeparateRow(None, L("Tolerance"), toleranceUpDown, L("millimeters"), self.collapseButton1)
        layout.AddCentered(self.collapsePanel1) # we need this auto-sized so we can get its width to adjust form height
        layout.Add(None) # expanding space, in case you want the form re-sizable
        
        
        self.Content = layout
        
        # a few buttons always shown at the bottom
        self.previewButton = forms.Button(Text = "Preview")
        self.cancelButton = forms.Button(Text = "Cancel")
        self.okButton = forms.Button(Text = "OK")
        
        
        
    def collapseButton_Click(self, sender, e):
        if self.collapsePanel.Visible:
           self.ClientSize = drawing.Size(self.ClientSize.Width, self.ClientSize.Height - self.collapsePanel.Height)
           self.collapsePanel1.Visible = False
           self.collapseButton.Text = "^"
        else:
           self.collapsePanel.Visible = True
           self.collapseButton.Text = "v"
           self.ClientSize = drawing.Size(max(self.ClientSize.Width, self.collapsePanel.Width), self.ClientSize.Height + self.collapsePanel.Height)
     
    
    
    def collapseButton_Click1(self, sender, e):
        if self.collapsePanel1.Visible:
           self.ClientSize = drawing.Size(self.ClientSize.Width, self.ClientSize.Height + self.collapsePanel1.Height - self.collapsePanel1.Height)
           self.collapsePanel1.Visible = False
           self.collapseButton1.Text = "+"
        else:
           self.collapsePanel1.Visible = True
           self.collapseButton1.Text = "-"
           self.ClientSize = drawing.Size(max(self.ClientSize.Width, self.collapsePanel1.Width), self.ClientSize.Height + self.collapsePanel.Height + self.collapsePanel1.Height)
           
    
    
    def OnFormClosed(self, sender, e):
        # Dispose of the form and remove it from the sticky dictionary
        if sc.sticky.has_key('sample_modeless_form'):
            form = sc.sticky['sample_modeless_form']
            if form:
                form.Dispose()
                form = None
            sc.sticky.Remove('sample_modeless_form')
################################################################################
# Creating a dialog instance and displaying the dialog.
################################################################################

def TestSampleEtoModelessForm():
    # See if the form is already visible
    if sc.sticky.has_key('sample_modeless_form'):
        return
    # Create and show form
    form = CollapsableEtoDialog()
    #form = SampleEtoPushPickButtonDialog()
    form.Owner = Rhino.UI.RhinoEtoApp.MainWindow
    form.Show()
    # Add the form to the sticky dictionary so it
    # survives when the main function ends.
    sc.sticky['sample_modeless_form'] = form

if __name__ == "__main__":
    ts = time.time()
    #rs.EnableRedraw(False)
    
    TestSampleEtoModelessForm()
    
    print "Elapsed time is {:.2f}".format(time.time()-ts)

First thing one can see right away is the missing buttons that are supposed to be always visible.

Then I cannot make it work creating a second drop-down. :frowning:

NOTE:
Since one cannot create dockable form with Python. This is my workaround.

You probably will want to add your preview/cancel/ok buttons also to your layout. You create them, but you don’t add them to the layout.

Thanks @nathanletwory,

This worked:

layout.AddSeparateRow(None,self.previewButton,self.cancelButton,self.okButton,None)