Page 13 of 16

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Mon May 02, 2016 6:54 pm
by Shardik
I have taken my example from above for the ListIconGadget and removed/changed some parts in order to implement SetGadgetItemColor() also for the ListViewGadget. Now it's possible to colorize the text color and background color of single rows in a ListViewGadget. This example is not cross-platform anymore because this feature is not implemented in the PB versions for Linux and Windows!

Code: Select all

EnableExplicit

ImportC ""
  class_addMethod(Class.I, Selector.I, *Callback, Types.P-ASCII)
  sel_registerName(MethodName.P-ASCII)
EndImport

Define AppDelegate.I = CocoaMessage(0, CocoaMessage(0, 0,
  "NSApplication sharedApplication"), "delegate")
Define DelegateClass.I = CocoaMessage(0, AppDelegate, "class")
Define i.I
Define Selector.I = sel_registerName("tableView:willDisplayCell:forTableColumn:row:")

Dim ListIconCellColor.Q(0)

ProcedureC CellDisplayCallback(Object.I, Selector.I, TableView.I, Cell.I,
  *Column, Row.I)
  Shared ListIconCellColor.Q()

  Protected Alpha.CGFloat
  Protected BackColor.L
  Protected Blue.CGFloat
  Protected CellColor.Q
  Protected FrontColor.L
  Protected Green.CGFloat
  Protected Red.CGFloat

  CellColor = ListIconCellColor(Row)

  If CellColor = 0
    CocoaMessage(0, Cell, "setDrawsBackground:", #NO)
    CocoaMessage(0, Cell, 
      "setTextColor:", CocoaMessage(0, 0, "NSColor blackColor"))
  Else
    BackColor = CellColor & $FFFFFF

    If BackColor = 0
      CocoaMessage(0, Cell, "setDrawsBackground:", #NO)
    Else
      CocoaMessage(0, Cell, "setDrawsBackground:", #YES)
      Alpha = 1
      Red = Red(BackColor) / 255
      Green = Green(BackColor) / 255
      Blue = Blue(BackColor) / 255
      CocoaMessage(0, Cell, "setBackgroundColor:", CocoaMessage(0, 0,
        "NSColor colorWithDeviceRed:@", @Red,
        "green:@", @Green,
        "blue:@", @Blue,
        "alpha:@", @Alpha))
    EndIf

    FrontColor = (CellColor >> 32) & $FFFFFF

    If FrontColor = 0
      CocoaMessage(0, Cell, 
        "setTextColor:", CocoaMessage(0, 0, "NSColor blackColor"))
    Else
      Alpha = 1
      Red = Red(FrontColor) / 255
      Green = Green(FrontColor) / 255
      Blue = Blue(FrontColor) / 255
      CocoaMessage(0, Cell, "setTextColor:", CocoaMessage(0, 0,
        "NSColor colorWithDeviceRed:@", @Red,
        "green:@", @Green,
        "blue:@", @Blue,
        "alpha:@", @Alpha))
    EndIf
  EndIf
EndProcedure

Procedure SetGadgetItemColorEx(GadgetID.I, Row.I, ColorType.I, Color.I)
  Shared ListIconCellColor.Q()

  Protected CellColor.Q
  Protected RowCount.I

  If ArraySize(ListIconCellColor()) = 0
    RowCount = CocoaMessage(0, GadgetID(GadgetID), "numberOfRows")
    Dim ListIconCellColor(RowCount - 1)
  EndIf

  CellColor = ListIconCellColor(Row)

  Select ColorType
    Case #PB_Gadget_BackColor
      CellColor ! (Color & $FFFFFF)
    Case #PB_Gadget_FrontColor
      CellColor ! ((Color & $FFFFFF) << 32)
  EndSelect

  ListIconCellColor(Row) = CellColor
EndProcedure

Macro SetGadgetItemColor(GadgetID, Row, ColorType, Color)
  SetGadgetItemColorEx(GadgetID, Row, ColorType, Color)
EndMacro

OpenWindow(0, 200, 100, 430, 150,
  "Change color of text and background in single rows")
ListViewGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20)

For i = 1 To 10
  AddGadgetItem(0, -1, "Line " + Str(i))
Next i

class_addMethod(DelegateClass, Selector, @CellDisplayCallback(), "v@:@@@@")
CocoaMessage(0, GadgetID(0), "setDelegate:", AppDelegate)

; ----- Set background color of line 1 to yellow
SetGadgetItemColor(0, 0, #PB_Gadget_BackColor, $FFFF)

; ----- Set background color of line 5 to X11 color "Aquamarine"
SetGadgetItemColor(0, 4, #PB_Gadget_BackColor, $D4FF7F)

; ----- Set text color of line 3 to blue
SetGadgetItemColor(0, 2, #PB_Gadget_FrontColor, $FF0000)

; ----- Set text color of line 5 to red
SetGadgetItemColor(0, 4, #PB_Gadget_FrontColor, $FF)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
The code above only supports one single ListViewGadget. If you need to support multiple ListViewGadgets please take a look into this extended example.

I had to modify this example to also work with the new PB 6.00 x64 Beta 7 because Fred modified the ListViewGadget and ListIconGadget internally:
Fred wrote:We now use a specialized PBIconTextCell for ListIconGadget and ListViewGadget (in the :dataCellForTableColumn callback). It allows us to properly display an icon in the same column as text for ListIconGadget() instead of having a separate column as before (which was not good looking).
You find the modified example (working in both PB 6.00 x64 Beta 7 and pre Beta 7) here.

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Thu Jul 21, 2016 1:11 pm
by deseven
Setting column title via Cocoa (that way you can set title for special columns such as image or checkbox) for ListIconGadget() and anything else that uses NSTableView.

Code: Select all

Procedure ListIconGadgetColumnTitle(gadget.i,index.i,title.s)
  Protected column = CocoaMessage(0,CocoaMessage(0,GadgetID(gadget),"tableColumns"),"objectAtIndex:",index)
  If column
    CocoaMessage(0,column,"setTitle:$",@title)
  EndIf
EndProcedure
EDIT: OS X 10.10+

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Thu Jul 21, 2016 9:25 pm
by Shardik
deseven,

unfortunately your procedure ListIconGadgetColumnTitle() doesn't work on MacOS 10.6.8 (Snow Leopard) and MacOS 10.9.5 (Mavericks). It stops with the error message
PB Debugger wrote:[ERROR] Object does not respond to method "setTitle:".
Perhaps this is caused by Apple's API changes in the Cocoa framework of more recent MacOS versions from cell based to view based table views.

The following example works fine on Snow Leopard and Mavericks. Unfortunately my external hard disk with MacOS versions from Lion to El Capitan is damaged, so that I am currently unable to test the following example also on Yosemite or El Capitan:

Code: Select all

Procedure SetColumnTitle(ListIconID.I, ColumnIndex.I, Title.S)
  Protected Column = CocoaMessage(0, CocoaMessage(0, GadgetID(ListIconID),
    "tableColumns"), "objectAtIndex:", ColumnIndex)

  If Column
    CocoaMessage(0, CocoaMessage(0, Column, "headerCell"),
      "setStringValue:$", @Title.S)
  EndIf
EndProcedure

OpenWindow(0, 200, 100, 450, 110, "ListIcon Example")
ListIconGadget(0, 10, 10, WindowWidth(0) - 20, WindowHeight(0) - 20,
  "Name", 110)
AddGadgetColumn(0, 1, "Address", GadgetWidth(0) - GetGadgetItemAttribute(0, 0,
  #PB_ListIcon_ColumnWidth) - 8)
AddGadgetItem(0, -1, "Harry Rannit" + #LF$ +
  "12 Parliament Way, Battle Street, By the Bay")
AddGadgetItem(0, -1, "Ginger Brokeit" + #LF$ +
  "130 PureBasic Road, BigTown, CodeCity")
AddGadgetItem(0, -1, "Didi Foundit" + #LF$ +
  "321 Logo Drive, Mouse House, Downtown")

SetColumnTitle(0, 0, "Title changed!")

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
For only changing the title of a column header you even don't need Cocoa functions at all. You only need the following native PB function: :wink:

Code: Select all

SetGadgetItemText(0, -1, "Title Changed!", 0)

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Thu Jul 21, 2016 9:50 pm
by deseven
Shardik wrote:For only changing the title of a column header you even don't need Cocoa functions at all.
Hello Shardik!

Unfortunately you misread my post (or maybe it's my fault that i can't explain why someone should need it), here is the example:

Code: Select all

OpenWindow(0,#PB_Ignore,#PB_Ignore,400,300,"ListIcon Example",#PB_Window_ScreenCentered)
ListIconGadget(0,10,10,380,280,"Column1",110,#PB_ListIcon_CheckBoxes)
AddGadgetColumn(0,1,"Column2",100)
AddGadgetItem(0,-1,"Text1" + Chr(10) + "Text2")

; you can't change the title of the special column with checkboxes (or icons)
; column #0 is actually column #1, etc
SetGadgetItemText(0,-1,"Title Changed!",0)

; however you can still do it with Cocoa
column = CocoaMessage(0,CocoaMessage(0,GadgetID(0),"tableColumns"),"objectAtIndex:",0)
CocoaMessage(0,column,"setTitle:$",@"Title Changed!")

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
It can lead to something like that (using special symbols as a column title):
Image

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Fri Jul 22, 2016 9:30 am
by Shardik
Hello deseven,

yes, I misunderstood your description. You were talking about "Setting column title" and with "column title" I always associate the title of a column header. I think "cell content" or "column content" would have been more appropriate but I am not a native speaker of English, so may be I am wrong... :wink:

But thank you for your image and your new example code which makes all pretty clear.

Nevertheless, in MacOS 10.6.8 (Snow Leopard) and MacOS 10.9.5 (Mavericks) your new code example still throws the above mentioned error message (perhaps because of the already mentioned API changes in MacOS beginning with Yosemite from cell based to view based table views):
PB debugger wrote:[ERROR] Zeile: 12
[ERROR] Object does not respond to method "setTitle:".

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Fri Jul 22, 2016 10:18 am
by wilbert
I'm a bit confused about this setting of column title.
As far as I can tell, the code posted by Shardik (headerCell / setStringValue) does exactly the same as setTitle :?

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Fri Jul 22, 2016 10:42 am
by deseven
Yes, "setStringValue" method should work on 10.6+, but "setTitle" works only on 10.10 or higher.
Shardik wrote:You were talking about "Setting column title" and with "column title" I always associate the title of a column header. I think "cell content" or "column content" would have been more appropriate
I don't know, but this api method called "setTitle" and it works with object called "NSTableColumn". Based on that i think that "column title" sounds ok.
Of course i am not a native english speaker too.
wilbert wrote:As far as I can tell, the code posted by Shardik (headerCell / setStringValue) does exactly the same as setTitle
The only difference is api changes, probably the correct way is to check the OS version and use the appropriate method based on that info.

Code: Select all

Procedure ListIconGadgetColumnTitle(gadget.i,index.i,title.s)
  Protected column = CocoaMessage(0,CocoaMessage(0,GadgetID(gadget),"tableColumns"),"objectAtIndex:",index)
  If column
    If OSVersion() >= #PB_OS_MacOSX_10_10
      CocoaMessage(0,column,"setTitle:$",@title)
    Else
      CocoaMessage(0,CocoaMessage(0,column,"headerCell"),"setStringValue:$",@title)
    EndIf
  EndIf
EndProcedure

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Tue Jul 26, 2016 3:55 pm
by deseven
Not the exact method, but a technique which shows how to create statusbar-based applications with the ability to restore a window by running the same .app again (most part of similar apps act like that).

Code: Select all

; in this example we're creating a complete app with status bar icon and the ability to turn it off or on

; if you'll turn off the status bar icon and close the main window
; you'll still be able to access it by running the same .app again

; compile it into .app bundle,
;
; edit the Info.plist like that:
; <key>LSUIElement</key>
;	<true/>
;
; run as a regular app

#NSWindowButtonMinimize = 1
#NSWindowButtonMaximize = 2

EnableExplicit

DataSection
  icon:
  IncludeBinary "icon.png"
EndDataSection

UsePNGImageDecoder()
CatchImage(0,?icon)
CocoaMessage(0,ImageID(0),"setTemplate:",#True)

; due to somehow strange PB behavior in managing windows we will use cocoa methods to hide or show our app
Define app.i = CocoaMessage(0,0,"NSApplication sharedApplication")

OpenWindow(0,#PB_Ignore,#PB_Ignore,170,40,"statusBar",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
StickyWindow(0,#True)
CheckBoxGadget(0,10,10,150,20,"enable status bar icon")
CocoaMessage(0,CocoaMessage(0,WindowID(0),"standardWindowButton:",#NSWindowButtonMinimize),"setHidden:",#YES)
CocoaMessage(0,CocoaMessage(0,WindowID(0),"standardWindowButton:",#NSWindowButtonMaximize),"setHidden:",#YES)

Procedure advancedStatusBar()
  Static statusBar.i,statusItem.i
  Shared app.i
  Protected itemLength.CGFloat = 24
  If GetGadgetState(0) = #PB_Checkbox_Checked
    If Not statusBar
      statusBar = CocoaMessage(0,0,"NSStatusBar systemStatusBar")
    EndIf
    If Not statusItem
      statusItem = CocoaMessage(0,CocoaMessage(0,statusBar,"statusItemWithLength:",itemLength),"retain")
    EndIf
    CreatePopupMenu(0)
    MenuItem(0,"Window")
    MenuBar()
    MenuItem(1,"Quit")
    CocoaMessage(0,statusItem,"setHighlightMode:",#YES)
    CocoaMessage(0,statusItem,"setLength:@",@itemLength)
    CocoaMessage(0,statusItem,"setImage:",ImageID(0))
    CocoaMessage(0,statusItem,"setMenu:",CocoaMessage(0,MenuID(0),"firstObject"))
  Else
    If statusBar And statusItem
      CocoaMessage(0,statusBar,"removeStatusItem:",statusItem)
    EndIf
    statusItem = 0
    If IsMenu(0) : FreeMenu(0) : EndIf
  EndIf
EndProcedure

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_Gadget
      advancedStatusBar()
    Case #PB_Event_Menu
      Select EventMenu()
        Case 0
          CocoaMessage(0,app,"activateIgnoringOtherApps:",#YES)
        Case 1
          Break
      EndSelect
    Case #PB_Event_CloseWindow
      If GetGadgetState(0) = #PB_Checkbox_Unchecked
        MessageRequester("",~"You're closing the main window without active status bar icon.\n\nThe app will still run in background.\n\nYou'll be able to open the main window if you have a valid .app by launching the same .app again.")
      EndIf
      CocoaMessage(0,app,"hide:")
  EndSelect
ForEver
icon.png

Sharing because i spent about 2 days trying to figure out how to do it safe and reliable :)
Tested on OS X 10.8 and 10.11.

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Thu Sep 15, 2016 9:26 pm
by deseven
Detect whether "dark mode" on OS X 10.10 or higher is active:

Code: Select all

Procedure isDarkMode()
  Protected appearance = CocoaMessage(0,CocoaMessage(0,0,"NSUserDefaults standardUserDefaults"),"stringForKey:$",@"AppleInterfaceStyle")
  If appearance
    appearance = CocoaMessage(0,appearance,"UTF8String")
    If PeekS(appearance,-1,#PB_UTF8) = "Dark"
      ProcedureReturn #True
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure

If isDarkMode()
  Debug "dark mode enabled"
Else
  Debug "dark mode disabled"
EndIf
Can be useful if you use complex status bar icons which are not "setTemplate" compatible.

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Fri Sep 16, 2016 11:43 am
by deseven
More on that (do something when user changes interface mode):

Code: Select all

Define app = CocoaMessage(0,0,"NSApplication sharedApplication")
Define appDelegate = CocoaMessage(0,app,"delegate")
Define delegateClass = object_getClass_(appDelegate)
Define selector = sel_registerName_("darkModeChanged:")
Define distributedNotificationCenter = CocoaMessage(0,0,"NSDistributedNotificationCenter defaultCenter")

Procedure darkModeChanged(notification)
  Debug "mode changed"
EndProcedure

class_addMethod_(delegateClass,selector,@darkModeChanged(),"v@:@")
CocoaMessage(0,distributedNotificationCenter,
             "addObserver:",appDelegate,
             "selector:",selector,
             "name:$",@"AppleInterfaceThemeChangedNotification",
             "object:",#nil)

OpenWindow(0,#PB_Ignore,#PB_Ignore,100,100,"",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)

Repeat : Until WaitWindowEvent(100) = #PB_Event_CloseWindow
Again thanks wilbert for showing me how to add observers :)

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Mon Dec 12, 2016 8:22 pm
by wilbert
Small code to show how to format text of an EditorGadget with html code.

Code: Select all

If OpenWindow(0, 0, 0, 320, 150, "EditorGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  EditorGadget(0, 10, 10, 300, 130)
  
  
  HTMLCode.s = "<font face='Helvetica' size='14'><b>Test</b> <font color='red'>code</font></font>"

  AttributedString = CocoaMessage(0, CocoaMessage(0, 0, "NSAttributedString alloc"), "initWithHTML:", 
                                  CocoaMessage(0, CocoaMessage(0, 0, "NSString stringWithString:$", @HTMLCode), 
                                               "dataUsingEncoding:", 10), "documentAttributes:", #Null)
  If AttributedString
    TextStorage = CocoaMessage(0, GadgetID(0), "textStorage")
    CocoaMessage(0, TextStorage, "setAttributedString:", AttributedString)
    CocoaMessage(0, AttributedString, "release")
  EndIf
  
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
  
EndIf

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Thu May 04, 2017 11:23 pm
by deseven
Restarting your app (code ported from this gist)

Code: Select all

Procedure restartApp(delay.i)
  Protected task = CocoaMessage(0,CocoaMessage(0,CocoaMessage(0,0,"NSTask alloc"),"init"),"autorelease")
  Protected args = CocoaMessage(0,0,"NSMutableArray arrayWithCapacity:",0)
  Protected appPath.s = PeekS(CocoaMessage(0,CocoaMessage(0,CocoaMessage(0,0,"NSBundle mainBundle"),"bundlePath"),"UTF8String"),-1,#PB_UTF8)
  Protected command.s = "sleep " + Str(delay) + ~"; open -a \"" + appPath + ~"\""
  CocoaMessage(0,args,"addObject:$",@"-c")
  CocoaMessage(0,args,"addObject:$",@command)
  CocoaMessage(0,task,"setLaunchPath:$",@"/bin/sh")
  CocoaMessage(0,task,"setArguments:",args)
  CocoaMessage(0,task,"launch")
  End
EndProcedure

OpenWindow(0,#PB_Ignore,#PB_Ignore,400,300,"restart app test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ButtonGadget(0,150,135,100,30,"restart")

Repeat
  ev = WaitWindowEvent()
  If ev = #PB_Event_Gadget And EventGadget() = 0
    restartApp(1)
  EndIf
Until ev = #PB_Event_CloseWindow

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Fri Nov 03, 2017 4:39 pm
by wilbert
Create UUID.

Method 1

Code: Select all

ImportC ""
  CFUUIDCreate(alloc = #Null)
  CFUUIDCreateString(alloc, uuid)
EndImport

Procedure.s CreateUUID()
  Protected.i uuidRef, uuidStringRef, UUID.s
  uuidRef = CFUUIDCreate()
  uuidStringRef = CFUUIDCreateString(#Null, uuidRef)
  CFRelease_(uuidRef)
  UUID = PeekS(CocoaMessage(0, uuidStringRef, "UTF8String"), -1, #PB_UTF8)
  CFRelease_(uuidStringRef)
  ProcedureReturn UUID  
EndProcedure

Debug CreateUUID()
Method 2 (MacOS 10.8+)

Code: Select all

UUID.s = PeekS(CocoaMessage(0, CocoaMessage(0, CocoaMessage(0, 0, "NSUUID UUID"), "UUIDString"), "UTF8String"), -1, #PB_UTF8)
Debug UUID

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Sat Jun 13, 2020 10:24 am
by collectordave
I have created a script to renew an applications icon and saved it as a script.

Can wilberts AppleScript procedure be used to run the script or is there another way?

Regards

CD

Re: [PB Cocoa] Methods, Tips & Tricks

Posted: Sat Jun 13, 2020 5:12 pm
by wilbert
collectordave wrote:Can wilberts AppleScript procedure be used to run the script or is there another way?
If you load the script you can use that procedure.
Another option might be the terminal command osascript