Is it possible to subclass without Xcode?

I would like a NSTextField whose text is vertically centered. All examples of doing so, which I have been able to find, are to subclass NSTextField. All examples of subclassing which I have found use Xcode. (I refer to subclassing anything, not only NSTextField.) However, I do not use Xcode. So…is it possible to subclass without using Xcode and, if so, how? I located a subclass solution by Daniel Jalkut of Red Sweater Software, which I have included in hope anyone with a solution can use it in their explanation.

Thank you in advance for any assistance.

The .m file:

//
//  RSVerticallyCenteredTextField.m
//  RSCommon
//
//  Created by Daniel Jalkut on 6/17/06.
//  Copyright 2006 Red Sweater Software. All rights reserved.
//

#import "RSVerticallyCenteredTextFieldCell.h"

@implementation RSVerticallyCenteredTextFieldCell

- (NSRect)drawingRectForBounds:(NSRect)theRect
{
    // Get the parent's idea of where we should draw
    NSRect newRect = [super drawingRectForBounds:theRect];

    // When the text field is being 
    // edited or selected, we have to turn off the magic because it screws up 
    // the configuration of the field editor.  We sneak around this by 
    // intercepting selectWithFrame and editWithFrame and sneaking a 
    // reduced, centered rect in at the last minute.
    if (mIsEditingOrSelecting == NO)
    {
        // Get our ideal size for current text
        NSSize textSize = [self cellSizeForBounds:theRect];

        // Center that in the proposed rect
        float heightDelta = newRect.size.height - textSize.height;    
        if (heightDelta > 0)
        {
            newRect.size.height -= heightDelta;
            newRect.origin.y += (heightDelta / 2);
        }
    }
    
    return newRect;
}

- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(int)selStart length:(int)selLength
{
    aRect = [self drawingRectForBounds:aRect];
    mIsEditingOrSelecting = YES;    
    [super selectWithFrame:aRect inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
    mIsEditingOrSelecting = NO;
}

- (void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject event:(NSEvent *)theEvent
{    
    aRect = [self drawingRectForBounds:aRect];
    mIsEditingOrSelecting = YES;
    [super editWithFrame:aRect inView:controlView editor:textObj delegate:anObject event];
    mIsEditingOrSelecting = NO;
}

@end
[/AppleScript]

The .h file:


//
// RSVerticallyCenteredTextFieldCell.h
// RSCommon
//
// Created by Daniel Jalkut on 6/17/06.
// Copyright 2006 Red Sweater Software. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@interface RSVerticallyCenteredTextFieldCell : NSTextFieldCell
{
BOOL mIsEditingOrSelecting;
}

@end



Model: Mac Studio Ultra
Browser: Safari 605.1.15
Operating System: macOS 12

Here’s how to set up the SubClass, you’ll have to translate the functions


use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"

script RSVerticallyCenteredTextFieldCell
	property parent : class "NSTextFieldCell"
	property mIsEditingOrSelecting : false
	
	-- translate the functions here
	
end script

Also do you know how you plan to “implement” the SubClass and use it?

technomorph, thank you for your reply. After posting, I located a question about subclassing without Xcode on the forum of Late Night Software (https://forum.latenightsw.com/t/subclassing-with-applescriptobjc/2827). Shane gave a good explanation. In addition, I found a complete example on Takaaki Naganoya’s AppleScript site (http://piyocast.com/as/archives/category/custom-class). I have now written a couple of simple subclasses, which work fine. However, I’ve been update to implement vertically centering text in a NSTextField. I have followed three basic approaches that I located online, without success. All used Xcode; solutions were expressed in .m and .h files. I did my best to write the appropriate AppleScriptObjC version, but without success. Also, it is necessary to subclass NSTextFieldCell, not NSTextField, but I believe I have correctly implemented that.

My subclass script is below, using the method of Daniel Jalkut (shown in my earlier post.) His method is to override drawingRectForBounds such that it returns the rectangle in which you would like the text placed, i.e. a rectangle just large enough to hold the text and centered in the text field’s normal bounds. I started by seeing if I could implement that override; so my override simply returns the rectangle that applies to the textfield in my calling script. The result I get is what seems to be a normal NSTextField - no vertical centering.

You will notice two other overrides which I have commented out. Daniel’s code uses these when the textfield is being edited, otherwise the text will be top-aligned during editing. When I enabled them, the text in my textfield can not be selected. At lease that confirms that my script is doing something.

Any insight and advice will be appreciated.

The subclass code:

use framework "Foundation"
use framework "AppKit"
use framework "AppleScriptObjC"
use scripting additions

script MYTextFieldCell
    property parent : class "NSTextFieldCell"
    property newRect : missing value
    
    on drawingRectForBounds:aRect
        set newRect to current application's NSMakeRect(250, 47, 200, 21)
        return newRect
    end drawingRectForBounds:
    
    --on selectWithFrame:cFrame inView:cView editor:cEditor delegate:cDelegate start:cStart |length|:cLength
    --    super's selectWithFrame:(current application's NSMakeRect(250, 20, 200, 75)) inView:cView editor:cEditor delegate:cDelegate start:cStart |length|:cLength
    --end selectWithFrame:inView:editor:delegate:start:|length|:
    
    --on editWithFrame:aFrame inView:aView editor:aEditor delegate:aDelegate |event|:aEvent
    --    super's editWithFrame:(current application's NSMakeRect(250, 20, 200, 75)) inView:aView editor:aEditor delegate:aDelegate |event|:aEvent
    --end editWithFrame:inView:editor:delegate:|event|:
    
end script
[/AppleScript]

The calling script:


use framework “Foundation”
use framework “AppKit”
use framework “AppleScriptObjC”
use scripting additions

on run
set resourcePath to POSIX path of (path to me) & “Contents/Resources/Classes”
set theBundle to current application’s NSBundle’s bundleWithPath:resourcePath
theBundle’s loadAppleScriptObjectiveCScripts()
my performSelectorOnMainThread:“prepareWindow:” withObject:(missing value) waitUntilDone:true
end run

on prepareWindow:sender
set theWindow to current application’s NSWindow’s alloc’s initWithContentRect:(current application’s NSMakeRect(1300, 900, 500, 300)) styleMask:7 backing:2 defer:false

current application's NSTextField's setCellClass:(current application's MYTextFieldCell)
set textField2 to current application's NSTextField's alloc()'s initWithFrame:{{250, 20}, {200, 75}}
textField2's setBordered:false
textField2's setAlignment:1
textField2's setFont:(current application's NSFont's fontWithName:"SFProRounded-Light" |size|:18)
textField2's setBackgroundColor:(current application's NSColor's systemRedColor)
textField2's setDrawsBackground:true
textField2's setStringValue:"Hello"
current application's NSTextField's setCellClass:(current application's NSTextFieldCell)
theWindow's contentView's addSubview:textField2
theWindow's makeKeyAndOrderFront:me

end prepareWindow:

Your “init”ing a regular NSTextfield object.
You should be initiating your MYTextField class object.

Have a glance a Apples docs about subclasses.
Will give you a better understanding of how it all works together.

https://youtu.be/mLQBcB8wCa4

technomorph - I am subclassing NSTextFieldCell, not NSTextField itself. Take a look at the code - prior to init’ing NSTextField, I use the setCellClass method, which “tells” NSTextField to use my TextFieldCell. This approach is described in an Apple’s Programming Guides.

I was not successful creating a text field with vertically centered text. I developed an alternative that works very well. The original desire came about because I have several text fields in a row, all of which accept user input at any time. For ascetics I want each to have the same size and background, with text centered vertically. I create such a thing with an NSBox and NSTextField placed inside the NSBox. The box provides a fixed size and background. The NSTextField size is set to the minimum required to hold its text, in effect centering it vertically. The text field is subsequently easy to center in the box. Whenever the text is changed, the NSTextField is re-sized and positioned. It is also straightforward to accommodate text of more than one line.

This might even be a better approach, considering a relatively recent comment of Shane’s over at Late Night Software, concerning subclassing AppleScriptObjC and memory management problems (https://forum.latenightsw.com/t/how-to-get-properties-of-custom-nsview-inside-its-implementation/3305).

Try setting the Textfield cell class to an initialized object.
IE. textField2

textField2’s setCellClass:(current application’s MYTextFieldCell)

Also why are you subclassing a cell? And not the view itself.
Pretty much most views allow you to customize the cell via the view
Rather than digging into its cell