Vanilla: make EditText arrow-savvy

I have a vanilla EditText which I use for numbers and I want to be able to hit up/down (also with shift) to change the value. This really isn’t about Glyphs, but I haven’t been able to find a way to do it. How difficult is it?

Instead of vanilla.TextBox, simply use GSSteppingTextField. You initialize an object with something like:

self.w.numberField = GSSteppingTextField.alloc().init()

To get an overview, put GSSteppingTextField into the Method Reporter, then you see the PyObjC methods available. You will recognize a lot from Vanilla.

1 Like

I made a small subclass of EditBox:

GSSteppingTextField = objc.lookUpClass("GSSteppingTextField")
class ArrowEditText (EditText):
	nsTextFieldClass = GSSteppingTextField
	
	def _setCallback(self, callback):
		super(ArrowEditText, self)._setCallback(callback)
		if callback is not None and self._continuous:
			self._nsObject.setContinuous_(True)
			self._nsObject.setAction_(self._target.action_)
			self._nsObject.setTarget_(self._target)

then you can use it like this:

class EditTextDemo():
	def __init__(self):
		self.w = Window((100, 42))
		self.w.editText = ArrowEditText((10, 10, -10, 22), callback=self.editTextCallback, continuous=True)
		self.w.open()

	def editTextCallback(self, sender):
		print "text entry!", sender.get()

EditTextDemo()
7 Likes

Love it! I’ll buy you guys a beer at TYPO Labs!

Is there a way to transform the GSSteppingTextField into a cell? I currently use dynamically generated NSSliderCell in the ColumnDescriptions of a Vanilla List and would like to replace them by the Stepping Feld.

The cell can’t do anything. Only the NSControl that contains the cell can handle keyboard events.

What are you trying to do?

So among other items vanilla list can have a SliderListCell which I can customize by subclassing its NSSliderCell. As declared in the vanilla docu, these List Item Cells should only be used in the columnDescriptions argument during the construction. That’s what I did back then when writing a plugin. The code is very extensive and everything is built around the columnDescriptions architecture. The list items are loaded dynamically, context based.

But now I find the Sliders inconvenient UI-wise and would like to replace them with something that behaves like a GSSteppingTextField. Unfortunately I could only manage to use a NSTextFieldCell, but this one is annoying, because you need to double click and hit enter to change the value. Also you cannot change the value with the arrow keys.

This is a great snippet! Is there a way to change the “empty” input value from 0 to something else from outside? So when you click an arrow on an empty input it gives say 101 instead of 1?

You could just show the 100 as default? I would expect to always show a value, so there is not “Empty” state.

Sure, I just show default values as placeholders to make it more clear to see which are changed and which are not.

The steppingextField has a plainValue property. You can try to put your default value in there. Then stepping should pick it up as a starting point.

1 Like

Is it possible to access _isEditing from Python? In ObjC I managed to do so with the help of a subclass but I’m a kind of PyObjC noob.

I am asking because I’d like to implement a percentage text field that includes the % but if I add the % after each edit then typing in numbers becomes impossible (because the text cursor is placed at the end, after the %).

This is best achieved using an NSNumberFormatter with the NSNumberFormatterPercentStyle style.

Thanks. How do I implement that, being a PyObjC noob?

I have not tested it, but this should do it:

frame = NSMakeRect(20, 20, 65, 24) # some frame
textField = GSSteppingTextField.alloc().initWithFrame_(frame)
textField.setStringValue_("50%")
formatter = NSNumberFormatter.new()
formatter.setNumberStyle_(NSNumberFormatterPercentStyle)
formatter.setLocale_(NSLocale.autoupdatingCurrentLocale())
formatter.setLenient_(True)
textField.setFormatter_(formatter)

Thank you for the code.

Unfortunately, with this formatter (using Vanilla), as soon as the user types in text (without the percent sign, i.e. just select all and start typing), it is erased. So, I haven’t managed to get it to work.

FYI this is the project I’d need this for:

I had a look a the code.

Some hints.

  • The EditText class has a “formatter” argument that takes an NSNumberFormatter as Florian described as argument.
  • When you use a percent formatter, you need to set a float value in the range 0.0–1.0, not a string in the range 0–100.
  • You did set the placeholder on initializing the txt field. But then when you need it, you set a string value: “multiple”. If you would set an empty string, you would get the placeholder.
  • The text fields need at least 22 units height. If you like a smaller text field, you need to set the control size to “small”.

Just in case someone needs this, you also need to set lenient to True so that inputs that don’t match the expected format perfectly are still accepted. Like so:

formatter.setLenient_(True)
1 Like

Is there a way now to add the stepping feature using arrow keys to an editable cell using List that has columnDescriptions?

I was trying to test the demo code for list:

GSSteppingTextField = objc.lookUpClass("GSSteppingTextField")
class ArrowEditText (EditText):
	nsTextFieldClass = GSSteppingTextField
	
	def _setCallback(self, callback):
		super(ArrowEditText, self)._setCallback(callback)
		if callback is not None and self._continuous:
			self._nsObject.setContinuous_(True)
			self._nsObject.setAction_(self._target.action_)
			self._nsObject.setTarget_(self._target)
			
class EditTextDemo():
	def __init__(self):
		self.w = Window((100, 42))
		self.w.editText = ArrowEditText((10, 10, -10, 22), callback=self.editTextCallback, continuous=True)
		self.w.list = ArrowEditText((10, 40, -10, -70), [], columnDescriptions=[
            {"title": "Glyph Name", "identifier": "name", "width": 150},
            {"title": "Anchor Name", "identifier": "anchor", "width": 100},
            {"title": "X", "identifier": "x", "width": 50, "editable": True},
            {"title": "Y", "identifier": "y", "width": 50, "editable": True},
        ], editCallback=self.editTextCallback, continuous=True)		
		self.w.open()

	def editTextCallback(self, sender):
		print ("text entry!", sender.get())

EditTextDemo()

but get an error:
TypeError: __init__() got an unexpected keyword argument 'columnDescriptions'

This is the type of editable cell: