Manually creating a Cocoa control - What's missing?

Mac OSX specific forum
Violet
Enthusiast
Enthusiast
Posts: 106
Joined: Sun Dec 23, 2007 6:30 pm

Manually creating a Cocoa control - What's missing?

Post by Violet »

As I would like to use Cocoa controls which are not provided by PureBasic directly, I evaluated the possibilities of creating them from code within a PureBasic (5.11 64 Bit) application. I tested it already from within an Xcode (5.0.1) project. The following code (inserted into AppDelegate's implementation method applicationDidFinishLaunching) works fine:

Code: Select all

NSButton *myButton = [[NSButton alloc] initWithFrame:NSMakeRect(10, 10, 130, 40)];
[[[self window] contentView] addSubview: myButton];
[myButton setTitle: @"Button title!"];
However, if I attempt to mimic this from within a PureBasic application, the button seems to exist but not being visible. Try this short example:

Code: Select all

EnableExplicit

OpenWindow( 0, 0, 0, 600, 400, "Manual Cocoa Controls", #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget )

Global *Window = WindowID( 0 )

Global *Frame.NSRect = AllocateMemory( SizeOf( NSRect ) )

*Frame\origin\x = 100;
*Frame\origin\y = 100;
*Frame\size\width = 100;
*Frame\size\height = 25;

Global *Button
CocoaMessage( @*Button, #Null, "NSButton alloc" )
Debug "Button address: " + *Button

Global InitResult.i
CocoaMessage( @InitResult, *Button, "initWithFrame:", *Frame )
Debug "Initialization result: "+ Str( InitResult )

Global *ButtonFrameTest.NSRect
CocoaMessage( @*ButtonFrameTest, *Button, "frame" )
Debug "Button frame: " + Str( *ButtonFrameTest\origin\x ) + ", " + Str( *ButtonFrameTest\origin\y ) + ", " + Str( *ButtonFrameTest\size\width ) + ", " +  Str( *ButtonFrameTest\size\height )

Global *ContentView
CocoaMessage( @*ContentView, *Window, "contentView" )
Debug "Content view: " + Str( *ContentView )

Global AddSubviewResult
CocoaMessage( @AddSubviewResult, *ContentView, "addSubview:", *Button )
Debug "AddSubviewResult: " + AddSubviewResult

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      End
  EndSelect
ForEver
Is it possible that PureBasic's windows are working slightly different concerning the contentView? Or am I just messing something up again with pointers'n'stuff? :?
best regards,

Violet
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Manually creating a Cocoa control - What's missing?

Post by wilbert »

Code: Select all

EnableExplicit

OpenWindow(0, 0, 0, 600, 400, "Manual Cocoa Controls", #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget)

Global ContentView = CocoaMessage(0, WindowID(0), "contentView")
Global Frame.NSRect

Frame\origin\x = 100
Frame\origin\y = 100
Frame\size\width = 100
Frame\size\height = 25

Global Button = CocoaMessage(0, CocoaMessage(0, 0, "NSButton alloc"), "initWithFrame:@", @Frame)
CocoaMessage(0, ContentView, "addSubview:", Button)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      End
  EndSelect
ForEver
The most difficult thing with Cocoa controls that aren't provided by PureBasic is not creating them but handling user interaction.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Shardik
Addict
Addict
Posts: 2060
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Manually creating a Cocoa control - What's missing?

Post by Shardik »

Violet,

i have modified your example and changed only the essential parts to make it work:

Code: Select all

EnableExplicit

OpenWindow( 0, 0, 0, 600, 400, "Manual Cocoa Controls", #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget )

Global *Window = WindowID( 0 )
Global Frame.NSRect

Frame\origin\x = 100;
Frame\origin\y = 100;
Frame\size\width = 100;
Frame\size\height = 25;

Global Button
CocoaMessage( @Button, #Null, "NSButton alloc" )
Debug "Button address: " + Button

Global InitResult.i
CocoaMessage( @InitResult, Button, "initWithFrame:@", @Frame )
Debug "Initialization result: "+ Str( InitResult )

Global ButtonFrameTest.NSRect
CocoaMessage( @ButtonFrameTest, Button, "frame" )
Debug "Button frame: " + Str( ButtonFrameTest\origin\x ) + ", " + Str( ButtonFrameTest\origin\y ) + ", " + Str( ButtonFrameTest\size\width ) + ", " +  Str( ButtonFrameTest\size\height )

Global ContentView
CocoaMessage( @ContentView, *Window, "contentView" )
Debug "Content view: " + Str( ContentView )

Global AddSubviewResult
CocoaMessage( @AddSubviewResult, ContentView, "addSubview:", Button )
Debug "AddSubviewResult: " + AddSubviewResult

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      End
  EndSelect
ForEver
Violet
Enthusiast
Enthusiast
Posts: 106
Joined: Sun Dec 23, 2007 6:30 pm

Re: Manually creating a Cocoa control - What's missing?

Post by Violet »

Thank you both. :D So it was because of me mixing up the thing with pointers again… I read some samples about how manual control creation works in Objective-C itself. As far as I know now, I need an Objective-C object as a message receiver for event handling, but how am I supposed to create that from PureBasic? :( Spontaneous idea:
  • Create a new class at runtime (just like in Interface Builder a custom class inheriting NSObject)
  • Add the event handling method (Could it be a PureBasic Procedure like a callback?)
  • Connect the (i.e.) button event to the event handling method
Background information: I really would like to get the "Source List" (a special form of NSOutlineView) as a ("self-made") PureBasic Gadget into PureBasic.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Manually creating a Cocoa control - What's missing?

Post by wilbert »

Violet wrote:Background information: I really would like to get the "Source List" (a special form of NSOutlineView as a ("self-made") PureBasic Gadget into PureBasic.
I'm not sure what you mean by "Source List" but the PureBasic TreeGadget is a NSOutlineView.
Maybe you could use that as a starting point.
Windows (x64)
Raspberry Pi OS (Arm64)
Violet
Enthusiast
Enthusiast
Posts: 106
Joined: Sun Dec 23, 2007 6:30 pm

Re: Manually creating a Cocoa control - What's missing?

Post by Violet »

Good to know, possibly that brings me further into the direction I would like to go. About the Source List: It is the sidebar like in Finder, iTunes or even Xcode itself, the tree control with the (in most cases) blueish background color. In the object library of Xcode it is also called "Source List". I found a suitable example via image search on Google:

Image

Before I came up manually creating a Source List or NSOutlineView, I tried to use a TreeGadget and simply color it appropriately. However I encountered problems:
  • I did spend only like two or three hours research until now, but I cannot figure out how to tell the PureBasic TreeGadget to use the Source List appearance (I did not even find any information on how to that in Objective-C or Xcode). It seems like there is also some custom implementation necessary by hand.
  • Trying to remove the Border of the PureBasic TreeGadget fails (setBorderType:0, object does not respond to this message), as however the PureBasic TreeGadget is implemented, it does not seem to be an NSOutlineView embedded into a bordered scroll view.
I could not figure out yet how to determine the PureBasic TreeGadget's nature as I somehow don't know how to invoke the Foundation Function NSStringFromClass from PureBasic.

Another option would be to simply make everything by hand based on a CanvasGadget. But I have a bad feeling of that idea. Because I think it is not possible to achieve behavior so close to its native Cocoa counterpart (in example: drag and drop).

Right now I think, it would already be easiest just to get rid of the TreeGadgets border and set the background (which actually is a gradient) like in other applications using the Source List. Any idea how to achieve that? :( Using the TreeGadget, however, would make it hard or even impossible to implement something like "Badges" for specific rows, even though such would only be a nice gimmick).

Best example (open source control on GitHub) of my goal is PXSourceList. They of how to implement it best way is my question.

Addition: I just remembered it should somehow be possible to import the Objective-C Runtime function NSStringFromClass as it is a C-Function… Unfortunately I cannot take a look into at before tomorrow evening. I will report any progress on this when it happens. :)
best regards,

Violet
User avatar
Shardik
Addict
Addict
Posts: 2060
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Manually creating a Cocoa control - What's missing?

Post by Shardik »

To set the background color of your TreeGadget to that of a Source List you may use

Code: Select all

CocoaMessage(0, GadgetID(0), "setSelectionHighlightStyle:", #NSTableViewSelectionHighlightStyleSourceList)
This is an example which displays the same output as your screenshot from above:

Code: Select all

#NSTableViewSelectionHighlightStyleSourceList = 1

OpenWindow(0, 270, 100, 300, 180, "Window")
TreeGadget(0, 0, 0, 130, WindowHeight(0))
AddGadgetItem (0, -1, "Item 1")
AddGadgetItem (0, -1, "Item 2", 0, 0)
AddGadgetItem (0, -1, "Item 2.1", 0, 1)
AddGadgetItem (0, -1, "Item 2.2", 0, 1)
AddGadgetItem (0, -1, "Item 2.2.1", 0, 2)
AddGadgetItem (0, -1, "Item 2.2.2", 0, 2)
AddGadgetItem (0, -1, "Item 3")
SetGadgetItemState(0, 1, #PB_Tree_Expanded)
SetGadgetItemState(0, 3, #PB_Tree_Expanded)

CocoaMessage(0, GadgetID(0), "setSelectionHighlightStyle:", #NSTableViewSelectionHighlightStyleSourceList)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Violet wrote:I could not figure out yet how to determine the PureBasic TreeGadget's nature...
The following code (modified from an example of wilbert) displays all inherited objects of PB's TreeGadget:

Code: Select all

Procedure.S GetInheritedObjects(Object)
  Protected Result.I
  Protected MutableArray.I = CocoaMessage(0, 0, "NSMutableArray arrayWithCapacity:", 10)
 
  Repeat
    CocoaMessage(0, MutableArray, "addObject:", CocoaMessage(0, Object, "className"))
    CocoaMessage(@Object, Object, "superclass")
  Until Object = 0
 
  CocoaMessage(@Result, MutableArray, "componentsJoinedByString:$", @"  -->  ")
  CocoaMessage(@Result, Result, "UTF8String")
 
  ProcedureReturn PeekS(Result, -1, #PB_UTF8)
 EndProcedure

OpenWindow(0, 270, 100, 200, 180, "TreeGadget")
TreeGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)
InheritedObjects$ = GetInheritedObjects(GadgetID(0))
CloseWindow(0)
MessageRequester("Inherited objects of PB's TreeGadget()", InheritedObjects$)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Manually creating a Cocoa control - What's missing?

Post by wilbert »

Violet wrote:Trying to remove the Border of the PureBasic TreeGadget fails (setBorderType:0, object does not respond to this message)
You need the enclosing scroll view for that.
Here's the code Shardik provided with the border removed

Code: Select all

#NSNoBorder = 0
#NSTableViewSelectionHighlightStyleSourceList = 1

OpenWindow(0, 270, 100, 300, 180, "Window")
TreeGadget(0, 0, 0, 130, WindowHeight(0))
AddGadgetItem (0, -1, "Item 1")
AddGadgetItem (0, -1, "Item 2", 0, 0)
AddGadgetItem (0, -1, "Item 2.1", 0, 1)
AddGadgetItem (0, -1, "Item 2.2", 0, 1)
AddGadgetItem (0, -1, "Item 2.2.1", 0, 2)
AddGadgetItem (0, -1, "Item 2.2.2", 0, 2)
AddGadgetItem (0, -1, "Item 3")
SetGadgetItemState(0, 1, #PB_Tree_Expanded)
SetGadgetItemState(0, 3, #PB_Tree_Expanded)

CocoaMessage(0, GadgetID(0), "setSelectionHighlightStyle:", #NSTableViewSelectionHighlightStyleSourceList)

EnclosingScrollView = CocoaMessage(0, GadgetID(0), "enclosingScrollView")
CocoaMessage(0, EnclosingScrollView, "setBorderType:", #NSNoBorder) 

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Windows (x64)
Raspberry Pi OS (Arm64)
Violet
Enthusiast
Enthusiast
Posts: 106
Joined: Sun Dec 23, 2007 6:30 pm

Re: Manually creating a Cocoa control - What's missing?

Post by Violet »

That's great and also simple! :D Thank you!
best regards,

Violet
Post Reply