Eto Forms resize some content, not others

Hi all,

I’m struggling with Eto layouts. I made a little tool that lets you search for blocks based on the block name (seems like this will be a Rhino 8 feature…). Like the below:

I want to make the form resizable with the listbox changing size and all the other elements remaining a fixed size. Currently, when the form is resized, the ok button streches and the listbox remains the same size.

If I remove the OK button and have the listbox as the last piece of content in the layout, then the listbox resizes correctly.

How can I keep the OK button but have the listbox resize instead?

Thanks in advance

#&description& Zoom and select to a choosen block

# Imports
from Rhino.UI import *
import Eto.Drawing as drawing
import Eto.Forms as forms
# https://developer.rhino3d.com/guides/rhinopython/eto-controls-python/
import Rhino
import rhinoscriptsyntax as rs

class BlockDefDialog(forms.Dialog[bool]):

    # Dialog box Class initializer
    def __init__(self, blockNames):
        # Initialize dialog box
        self.Title = 'Block Searcher'
        self.Padding = drawing.Padding(10)
        self.Resizable = True
        
        self.u_blockNames = blockNames
        
        self.m_checkbox = forms.CheckBox(Text = "Zoom Select ALL Block Instances")
        self.m_checkbox.CheckedChanged += self.OnValueChange
        
        self.m_infolabel_1 = forms.Label(Text="Click on a block name to zoom select it. Does not include locked or hidden blocks.")
        
        self.m_infolabel_2 = forms.Label(Text="Filter the block names by typing below.")
        
        self.m_textbox = forms.TextBox()
        
        self.m_searchResultLabel = forms.Label(Text="")
        
        self.m_listbox = forms.ListBox()
        self.m_listbox.DataStore = self.u_blockNames
        self.m_listbox.Height = 300
        self.m_listbox.Width = 600
        self.m_listbox.SelectedValueChanged += self.OnValueChange
        
        # Create the search button
        self.SearchButton = forms.Button(Text = 'Filter')
        self.SearchButton.Click += self.OnSearchButtonClick
        
        # Create the default button
        self.DefaultButton = forms.Button(Text = 'OK')
        self.DefaultButton.Click += self.OnOKButtonClick
        self.DefaultButton.Height = 20
        self.DefaultButton.Width = 100

        # Create the abort button
        self.AbortButton = forms.Button(Text = 'Cancel')
        self.AbortButton.Click += self.OnCloseButtonClick
        
        # Create a table layout and add all the controls
        layout = forms.DynamicLayout()
        layout.Spacing = drawing.Size(5, 5)
        
        layout.BeginVertical()
        layout.AddRow(self.m_infolabel_1)
        layout.AddRow(None, "")
        layout.AddRow(self.m_checkbox)
        layout.AddRow(None, "")
        
        layout.AddRow(self.m_infolabel_2)
        layout.AddRow(self.m_textbox, self.SearchButton)
        layout.AddRow(self.m_searchResultLabel)
        layout.EndVertical()
        
        layout.BeginVertical()
        layout.AddRow(self.m_listbox)
        layout.EndVertical()
        
        layout.BeginVertical()
        layout.AddRow(None)
        layout.AddRow(None, self.DefaultButton)
        layout.EndVertical()

        # Set the dialog content
        self.Content = layout

    # Start of the class functions
    
    def OnValueChange(self, sender, e):
        bn = self.m_listbox.SelectedValue
        if bn:
            #print self.m_checkbox.Checked
            zoomSelBlockName(bn, self.m_checkbox.Checked)

    def OnSearchButtonClick(self, sender, e):
        searchText = self.m_textbox.Text.lower()
        
        passed_search = []
        for n in self.u_blockNames:
            if searchText in n.lower():
                passed_search.append(n)

        self.m_searchResultLabel.Text = str(len(passed_search)) + " block defintions match your filter."
        self.m_listbox.DataStore = passed_search
        return passed_search

    # Close button click handler
    def OnCloseButtonClick(self, sender, e):
        self.Close(False)

    # OK button click handler
    def OnOKButtonClick(self, sender, e):
        self.Close(True)
        
    def GetBlockName(self):
        return self.m_listbox.SelectedValue


def searchBlockDefs(blocknames):
    dialog = BlockDefDialog(blocknames);
    rc = dialog.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)
    if (rc):
        #print rc
        res = dialog.GetBlockName()
        return res

def zoomSelBlockName(bn, boolSelAll):
    rs.EnableRedraw(False)
    rs.UnselectAllObjects()
    if bn:
        rs.Command('-SelBlockInstanceNamed "' + bn+'"', echo=False)
        
        # for single selection
        if not boolSelAll:
            sel = rs.SelectedObjects()[0]
            rs.UnselectAllObjects()
            rs.SelectObject(sel)
        
        rs.Command("ZoomSelected", echo=False)
    rs.EnableRedraw(True)

if __name__ == "__main__":
    
    # dont get any nested blocks, only top level blocks
    rs.EnableRedraw(False)
    rs.Command("SelAll", echo=False)
    sel_objs = rs.SelectedObjects()
    rs.UnselectObjects(sel_objs)
    rs.EnableRedraw(True)
    
    block_names = []
    
    # Sort blocks and other objects
    for obj in sel_objs:
        #print obj
        if rs.IsBlockInstance(obj):
            bName = rs.BlockInstanceName(obj)
            if bName not in block_names:
                block_names.append(bName)
    
    block_names.sort()
    
    blockName = searchBlockDefs(block_names)

I’d try StackLayout, a much more straightforward way to control resizing
stack

DynamicLayout rows are tricky

4 Likes

This is great! Thanks @Will_Wang :slight_smile:

I didn’t even know that existed as an option, I missed the small link on the rhino developer page to the example showing it.