How make palette window frame height dynamic?

Trying to write a palette plugin that allows for activating instances from the sidebar.
I got it mostly working but somehow I can’t figure out how to adjust the actual palette window frame height to dynamically change according to the number of instances.

Any hints on how to make this work?

For example: The Light instance should be part of the list. (Also, I see ‘Multiline Label’ text in the .xib file…should I just set the title blank like this: title="" ?)
Screenshot 2023-05-21 at 1.30.39 AM

Here is the code:

from __future__ import division, print_function, unicode_literals
from GlyphsApp import *
from GlyphsApp.plugins import *
from AppKit import NSButton, NSMakeRect

class ActivateInstancesPalettePlugin (PalettePlugin):
	dialog = objc.IBOutlet()
	textField = objc.IBOutlet()

	def settings(self):
		self.name = Glyphs.localize({
			'en': 'Activate Instances',
			'de': 'Aktiviere Instanzen',
			'fr': 'Activer les instances',
			'es': 'Activar instancias',
			'pt': 'Ativar instâncias',
		})

		# Load .nib dialog (without .extension)
		self.loadNib('IBdialog', __file__)

	def start(self):
		# Adding a callback for the 'GSUpdateInterface' event
		Glyphs.addCallback(self.update, UPDATEINTERFACE)

	def __del__(self):
		Glyphs.removeCallback(self.update)

	@objc.IBAction
	def checkBoxAction_(self, sender):
		# Retrieve instance by sender's title
		instance = [i for i in Glyphs.font.instances if i.name == sender.title()][0]
		instance.active = sender.state()

	@objc.python_method
	def update(self, sender=None):
	    font = Glyphs.font

	    if font is not None:
	        instances = sorted(font.instances, key=lambda instance: font.instances.index(instance))
	        num_instances = len(instances)
	        checkbox_height = 22
	        padding = 5

	        # Calculate the required height for the checkboxes
	        checkboxes_height = checkbox_height * num_instances

	        # Calculate the overall height, including padding and space for the dialog title and buttons
	        overall_height = checkboxes_height + padding * 2 + 10  # Adjust the value based on your needs

	        width = 160

	        # Adjust the size of the dialog box
	        self.dialog.setFrame_(NSMakeRect(self.dialog.frame().origin.x, self.dialog.frame().origin.y, width, overall_height))

	        # remove old checkboxes
	        for checkbox in self.dialog.subviews():
	            if isinstance(checkbox, NSButton) and checkbox.title() in [instance.name for instance in instances]:
	                checkbox.removeFromSuperview()

	        # Add new checkboxes
	        for i, instance in enumerate(instances):
	            yPos = overall_height - padding - checkbox_height - i * (checkbox_height + padding)  # Calculate y-coordinate from top to bottom
	            checkbox = NSButton.alloc().initWithFrame_(NSMakeRect(12, yPos, width - 12, checkbox_height))
	            checkbox.setButtonType_(3)  # Set to checkbox
	            checkbox.setTitle_(instance.name)
	            checkbox.setState_(instance.active)
	            checkbox.setTarget_(self)
	            checkbox.setAction_(objc.selector(self.checkBoxAction_, signature=b"v@:@"))
	            self.dialog.addSubview_(checkbox)




	def __file__(self):
		"""Please leave this method unchanged"""
		return __file__

The height is changed by using auto-layout. You should probably use a NSStackview to layout the rows. But are you sure having instances in the sidebar is a good idea? Fonts tend to have dozens of them nowadays. So you will run out of space very quickly.
You might be better off with a dialog that you can invoke with a shortcut?

Thanks for the NSStackview hint. Managed to get it minimally working.
(It should still include an update to repopulate the list when another font is opened or maybe even if a new instance is added…and also to prevent anything running unnecessarily when the palette window is closed)

I know it won’t be so practical when there are dozens of instances but when there are one a handful, it makes it easier to toggle on/off any instance whether it is to quickly change which instance to export or to use with a plugin such as Show Styles.

from __future__ import division, print_function, unicode_literals
from GlyphsApp import *
from GlyphsApp.plugins import *
import vanilla


class ActivateInstancesPalettePlugin(PalettePlugin):
    def settings(self):
        self.name = Glyphs.localize({'en': 'Activate Instances'})

        self.checkBoxes = {}
        for instance in self.instances(Glyphs.font):
            checkbox = vanilla.CheckBox((0, 0, -10, 20), instance.name, callback=self.checkbox_callback)
            checkbox_state = instance.active  # Get the initial state from the instance
            checkbox.set(checkbox_state)  # Set the checkbox state
            self.checkBoxes[instance.name] = (checkbox, instance)

        self.paletteView = vanilla.Window(posSize=(200, 100))
        self.paletteView.verticalStackView = vanilla.VerticalStackView(
            posSize='auto',
            views=[checkbox for checkbox, _ in self.checkBoxes.values()],
            alignment='leading',
            spacing=3,
            edgeInsets=(2, 8, 8, 1),
        )
        # Set dialog to NSView
        self.dialog = self.paletteView.verticalStackView.getNSStackView()


    def start(self):
        Glyphs.addCallback(self.update, UPDATEINTERFACE)

    def __del__(self):
        Glyphs.removeCallback(self.update)

    def update(self, sender=None):
        print("Update")
        

    def checkbox_callback(self, sender):
        checkbox_state = sender.get()
        checkbox_name = sender.getTitle()
        checkbox, instance = self.checkBoxes[checkbox_name]
        if checkbox_state == 1:
            instance.active = True
        else:
            instance.active = False
        print(f"Checkbox '{checkbox_name}' state: {checkbox_state}")

    @objc.python_method
    def instances(self, font):
        return font.instances

    def __file__(self):
        return __file__

That instance viewer plugin should be switched to use the “visible” property and not the “active” any more. Then you can use the instance popup in the lower left to change visibility.

1 Like

You probably need to populate the stack view from in a different method so that you can call it when font.instances changes. You need to add an observer to the instances. I’ll need to put together some sample code.

1 Like