Overriding the isFlipped property in custom declared controls

What sucks when you are creating custom declared controls on macOS? The drawing coordinate system is reversed; Apple defines (0,0) as the lower left bottom. So you end up creating all kinds of conversions for your controls to align them with Xojo’s coordinate system. Or you give up and use Apple’s system, but that confuses a lot when you try to port something to iOS where the coordinates are like in Xojo.

NSView has an [quote]isFlipped:[/quote] property to determine when a view’s coordinate system should be treated Xojo-wise, but that property is read-only. Apple recommends to override this property. I could not find any Xojo information on how to and have finally figured it out. This may not be the most elegant way. If you have a better solution: Please! In case I have not been blind and there was no solution yet, here’s how I made it (and how you can change properties and add your custom properties too):

A property on an Apple class can be retrieved by Objective C’s class_getProperty (Class as Ptr, Propertyname as Cstring) method. That gives you the ptr to the property, You can retrieve the property list by property_copyAttributeList (Property as Ptr, Byref Count As Integer). This returns the unreleased ptr to a C array of objc_property_attribute_t structures. I tried to use that, but a structure of 2 Cstring values (objc_property_attribute_t has two of them: name and value) does not give a reliable size. The Memoryblock created by that has an undetermined size, and while you could work your way through it, casting the objc_property_attribute_t structure on its ptrvalue didn’t work.

I found it much easier then to use property_getAttributes (property as Ptr) which returns the attributes’ names and values as a comma-separated Cstring. I simply converted this to Text, split it by its commas and then started to build a new MutableMemoryblock – Let’s call it ResultBlock.
An objc_property_attribute_t, if viewed as a Memoryblock, starts with two ptr values: The first pointing to its Name as Cstring, the second to its value as Cstring. So I create the MutableMemoryblock in the size of the count of the split text * Integersize * 2 - 8 Bytes for each attribute on 32 and 16 bytes on 64 Bit. If I want to add attributes, count must be increased of course.

Then I simply append a new memoryblock containing the attribute name and set ResultBlock.ptrvalue(0) to the position of the name (I used a helper variable giving me the offset from the ResultBlock’s start) and another memoryblock with the Cstring of the attribute value. In case there is non, it must be a new memoryblock with size 1 – an empty byte depicting the end of a CString. ResultBlock.ptrvalue(Integersize) gets set to this position. And so on.

So when adding an attribute for a property like isFlipped which comes with 3 attributes, I end up with a memoryblock around 60 bytes on 32 Bit, where the first 8 * 4 Bytes are the ptrs to the attached Cstrings that follow after the ptrs.

The attribute names are one-character Code:

[quote]R The property is read-only (readonly).
CThe property is a copy of the value last assigned (copy).
& The property is a reference to the value last assigned (retain).
N The property is non-atomic (nonatomic).
G The property defines a custom getter selector name. The name follows the G (for example, GcustomGetter,).
S The property defines a custom setter selector name. The name follows the S (for example, ScustomSetter:,).
D The property is dynamic (@dynamic).
W The property is a weak reference (__weak).
P The property is eligible for garbage collection.
t Specifies the type using old-style encoding.[/quote]

Thus, I attached “S” as a new name and “setFlipped:” as its value, creating the setter for this property, and then installed it all via

class_replaceProperty (result, prop.OriginalName.toCString(xojo.core.TextEncoding.ASCII), resultmb.data, attcount) and registering the class pair as usual.

A new declare:

Protected Sub setFlipped lib AppkitFramework selector "setFlipped:" (id as ptr, value as Boolean) makes the property usable.

Why not do the same as in Objective-C and just subclass the NSView in the Objective-C Runtime and use that subclass for the Xojo control (and in that subclass override isFlipped)?

Maybe that would even work – I haven’t tried –, but you would only override the getter method, not the property.

With this, you can install properties on the API object itself which could come in handy where something comes in on a background thread. (Yes, I know, it’s unsupported). You cannot address Xojo instances from there, but you can use declares on datatypes, like the ptr of the object that usually comes in background “event” methods. So you could use the declares to write custom instance properties and use performSelectorOnMainThread to call a custom method that reads these values and forwards them to the Xojo instance.
And I still dream of getting rid of retain class dictionaries that keep the connection between the declared object and its Xojo wrapper control. A weakRef on the instance itself should be much faster to retrieve than a shared dictionary’s WeakRef value.

You’re honestly best off doing this.

Subclassing an NSView in the Objective-C Runtime is the correct way. Also note that a property in Objective-C is a method pair (setter and getter). The property flipped is private API and should not be used at all – you don’t know what happens after you return YES or NO in isFlipped – maybe the control is invalidated and redrawn and when you set the property directly this doesn’t happen. Etc. etc. The Apple docs state clearly:

Subclassing NSView or one of its children is also the correct way in Xojo for controls. Plugins are done that way, but also through declares it is done that way.

In fact, this is Postscript used as the display motor by OS X that uses 0,0 at the lower left/bottom start coordinates (instead of 0,0 as Left,Top).

But this changes nothing to the problem you are discussing.

Sure this all works in a subclass of NSView. I thought you meant only overriding the setter method. I only extended the BuildTargetClass method that was probably taken from MacOSLib by another helper class: TargetClassPropertyHelper. If flipped were a private property, why would Apple list it in its documentation and recommend to overwrite it?

The result of overriding the property, like Apple recommends it, is that isFlipped returns the value I assign to it via setFliiped: – and the view control behaves that way, which means a custom Canvas classed based on NSView draws with 0,0 being the upper left corner.

EDIT: And the same way, I can set the contentView of the window to flipped and pass the instance behavior frame properties directly to the declared view in both directions. No conversion between Xojo and macOS coordinates necessary!

There is no setter. setFlipped: has been deprecated for ten years or so.

Sorry Ulrich, no offense, but manipulating the private API of a framework like AppKit is just wrong.

No offense too, Eli, but here’s what I found – and what Apple’s documentation says too:
For anyone wishing to do this in Swift here’s how you override in your custom class:

class FlippedView: NSView { override var flipped:Bool { get { return true } } }

You can, as well, override the getter, but Apple recommends overriding the property in a subclass. What’s so different to overriding a method? This all leads to a custom subclass, I do never override one of the standard classes.

That is Swift specific sugar for the method isFlipped in the Objective-C runtime.

a) For Xojo you have to use the Objective-C documentation, not the Swift one.
b) A property in Objective-C is sugar for two method calls, a setter (if there is one) and a getter.

???
Go with the Objective-C documentation. If it says: subclass, then subclass. Even if it is only for one method call like isFlipped.

I really do not see what the problem is with subclassing. If you’ve never done it, I can post code for it. Or look at MacOSLib.

I appreciate your assistance, Eli, and I am sorry if I didn’t make it clear enough initially: I am building a subclass here (the main part of the class constructor are the usual Objective C method overrides), but for flipped I did not override the getter (which would work too) but the property, again with Objective C methods and in alignment with Apple’s documentation.

You are right in that this could have been solved with a getter override and maybe an event handling on Xojo side to enable instance flexibility. Setting the property directly saves a few CPU cycles where usually the internal event would have to fire, but that’s probably not too much (although the property is presumably called on each redraw, so that could sum up a bit though).

The point I tried to make is I did not find any documentation on how to override a property in a subclass or even create an own, whereby the latter one, if it should work, could be quite interesting because it could save some complicated workarounds in (admittedly rare) cases where a custom property on OS level would be handy. I just wanted to share what worked so far.

If you like to experiment with it, feel free to do so. If you don’t (or it’s everything else than new to you), feel free to ignore it. Whatever: You don’t have to convince me to use a custom subclass with official Objective C methods, because that’s what I do all the time. I am sorry if I didn’t make that clear enough, and again, thanks for pushing the necessity to align with official methods. That’s what I try.

In the current xDevMag I share my projectCyborg code which can reduce the need to subclass.

Ulrich, as an Xojo evangelist you shouldn’t post code which is a hack – and that is one. That is the reason I keep posting so stubborn that this is the wrong way. Because others reading this in the future should not do that too and they should not use the code above. And it also shouldn’t be in your libraries you publish.

You do not know what happens within the AppKit framework, before and after the method isFlipped is called. Apple has deprecated setFlipped: ten years ago for some reason unknown to us.

“Setting the property directly saves a few CPU cycles … and that could sum up a bit…”. You have tested that, haven’t you? No, you haven’t, because if you had, you would have posted the benchmarks. Just guessing to have a reason to manipulate a private part of an API is wrong. A hack needs a very very good reason, which usually is: there is absolutely no other way to do it.

And since you subclass, just use class_addMethod and add isFlipped and implement a shared method or module method in Xojo returning 0 or 1.

Now I see where our misunderstanding comes from, Eli: I am sorry I used the selector name – that was just for demonstration purposes and I didn’t think about that a former setter would have had this name. Yes, I agree, you shouldn’t do that! Sorry for a misleading name! In stressing the subclassing (which I did) I did not see that was the point you were trying to make!

I only meant to inform about the structure behind the class_property Objective C methods. As further investigation showed, they do work, but a different selector is not registered this way. So there would still be a different approach necessary to manipulate the property, and the ObjC framework doesn’t seem to offer these. (Of course in this case the getter method override will work).

Still, there are occasions where a property on a custom subclass would be great to have. Properties with missing selectors don’t seem to fit. But I guess investigating Ivars could be with a try. Anyway: Sorry about the confusion again! Was a good point-missing day yesterday!