Cocoa StringGadget functions

Mac OSX specific forum
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Cocoa StringGadget functions

Post by kenmo »

Converting some Windows code to Cocoa... Does anyone know how to achieve these two StringGadget functions?

1. "Select All" a StringGadget, equivalent to Windows #EM_SETSEL

2. Replace selection (could be empty) with a string, equivalent to #EM_REPLACESEL

Thanks for any help!
WilliamL
Addict
Addict
Posts: 1224
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: Cocoa StringGadget functions

Post by WilliamL »

I had this in my notes and I think it is from wilbert. (Maybe it should be added to the 'Cocoa - Methods, Tips & Tricks' thread?)

Code: Select all

EnableExplicit

CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
  Global NSRangeZero.NSRange
  Procedure.i TextEditor(Gadget.i)
    Protected TextField.i = GadgetID(Gadget)
    Protected Window.i = CocoaMessage(0, TextField, "window")
    ProcedureReturn CocoaMessage(0, Window, "fieldEditor:", #YES, "forObject:", TextField)
  EndProcedure
CompilerEndIf

; Sets the selection in a StringGadget() independent of Operating System
; If Start.i < 1, every selectionis removed.
; If Length.i = 0, only the cursor is set.
Procedure SetStringGadgetSelection(GadgetID.i, Start.i, Length.i)
  If Start.i > 0 And Length.i > -1
    ; Set selection
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_Windows
        SendMessage_(GadgetID(GadgetID.i), #EM_SETSEL, Start.i - 1, Start.i + Length.i - 1)  
      CompilerCase #PB_OS_Linux
        ; TODO
      CompilerCase #PB_OS_MacOS
        Protected Range.NSRange\location = Start - 1 : Range\length = Length
        CocoaMessage(0, TextEditor(GadgetID), "setSelectedRange:@", @Range)
    CompilerEndSelect
  Else
    ; Deselect all
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_Windows
        SendMessage_(GadgetID(GadgetID.i), #EM_SETSEL, -1, 0)
      CompilerCase #PB_OS_Linux
        ; TODO
      CompilerCase #PB_OS_MacOS
        CocoaMessage(0, TextEditor(GadgetID), "setSelectedRange:@", @NSRangeZero)
    CompilerEndSelect
  EndIf
EndProcedure

Procedure.i GetStringGadgetSelectionStart(GadgetID.i)
  ; Get selection
  Protected Start.i = 0
  Protected Stop.i = 0
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      SendMessage_(GadgetID(GadgetID.i), #EM_GETSEL, @Start.i, @Stop.i)
      ProcedureReturn Start.i + 1
    CompilerCase #PB_OS_Linux
      ; TODO
    CompilerCase #PB_OS_MacOS
      Protected Range.NSRange
      CocoaMessage(@Range, TextEditor(GadgetID), "selectedRange")
      ProcedureReturn Range\location + 1
  CompilerEndSelect
EndProcedure

Procedure.i GetStringGadgetSelectionLength(GadgetID.i)
  ; Get selection
  Protected Start.i = 0
  Protected Stop.i = 0
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      SendMessage_(GadgetID(GadgetID.i), #EM_GETSEL, @Start.i, @Stop.i)
      ProcedureReturn Stop.i - Start.i
    CompilerCase #PB_OS_Linux
      ; TODO
    CompilerCase #PB_OS_MacOS
      Protected Range.NSRange
      CocoaMessage(@Range, TextEditor(GadgetID), "selectedRange")
      ProcedureReturn Range\length
  CompilerEndSelect
EndProcedure

If OpenWindow(0, 200, 300, 195, 160, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  Define strGadgID.i = StringGadget(#PB_Any, 10, 10, 180, 20, "Testcontent")
  
  SetActiveGadget(strGadgID.i)
  
  SetStringGadgetSelection(strGadgID.i, 3, 4) ; mark 4 characters beginning with the 3rd character
  
  Debug "Cursor before " + Str(GetStringGadgetSelectionStart(strGadgID.i))
  Debug "Marked characters: " + Str(GetStringGadgetSelectionLength(strGadgID.i))
  
  Repeat
    Define Event.i = WaitWindowEvent()
    If Event.i = #PB_Event_CloseWindow
      Define Quit.i = 1
    EndIf
  Until Quit.i = 1
EndIf
End
MacBook Pro-M1 (2021), Sonoma 14.4.1, PB 6.10LTS M1
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: Cocoa StringGadget functions

Post by kenmo »

Hi WilliamL, thank you, that example works well for setting the selection range.
Also, I discovered that SetActiveGadget() seems to automatically "select all" (at least on PB 5.42, OSX 10.11 ?) so that's easy enough.


Any idea about question # 2? Inserting a string at the cursor?
If the StringGadget has focus, I can get its "currentEditor" handle and then call its "insertText" method -- this works.
If the gadget does NOT have focus, its "currentEditor" is null. If I call SetActiveGadget() then "currentEditor" works, but this will select-all and replace the entire gadget contents...
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: Cocoa StringGadget functions

Post by kenmo »

Here's a code example of what I'm trying to do.

Press Enter when Gadget # 0 has focus, and when Gadget # 1 has focus...

Code: Select all

OpenWindow(0, 0, 0, 320, 240, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
StringGadget(0, 10, 10, 300, 25, "Select part of this text, then press Enter")
StringGadget(1, 10, 50, 300, 25, "Focus here, then press Enter")
AddKeyboardShortcut(0, #PB_Shortcut_Return, 0)

Procedure InsertText()
  *P = GadgetID(0)
  ;SetActiveGadget(0) ; this helps, but selects ALL of Gadget 0 text
  *P = CocoaMessage(#Null, *P, "currentEditor")
  If (*P)
    CocoaMessage(#Null, *P, "insertText:$", @"TEST")
  Else
    Debug "No currentEditor for Gadget #0"
  EndIf
EndProcedure

SetActiveGadget(0)
Repeat
  E = WaitWindowEvent()
  If (E = #PB_Event_CloseWindow)
    Break
  ElseIf (E = #PB_Event_Menu)
    If (EventMenu() = #PB_Menu_Quit)
      Break
    Else
      InsertText()
    EndIf
  EndIf
ForEver
User avatar
kenmo
Addict
Addict
Posts: 1967
Joined: Tue Dec 23, 2003 3:54 am

Re: Cocoa StringGadget functions

Post by kenmo »

I think I solved my own question.

Sounds like OS X does not keep the cursor/selection when a text field loses focus... so when a StringGadget does NOT have focus, the best I can do is insert text at the end.

This procedure is OK for me:

Code: Select all

Procedure InsertGadgetText(Gadget.i, Text.s)
  CompilerIf (#PB_Compiler_OS = #PB_OS_Windows)
    SendMessage_(GadgetID(Gadget), #EM_REPLACESEL, #Null, @Text)
    If (GetActiveGadget() <> Gadget)
      SetActiveGadget(Gadget)
    EndIf
  CompilerElseIf (#PB_Compiler_OS = #PB_OS_MacOS)
    Protected *GID = GadgetID(Gadget)
    If (GetActiveGadget() <> Gadget)
      SetActiveGadget(Gadget)
      Protected *Win = CocoaMessage(#Null, *GID, "window")
      Protected *FE = CocoaMessage(#Null, *Win, "fieldEditor:", #YES, "forObject:", *GID)
      If (*FE)
        Protected Range.NSRange
        Range\location = Len(GetGadgetText(Gadget))
        Range\length = 0
        CocoaMessage(#Null, *FE, "setSelectedRange:@", @Range)
      EndIf
    EndIf
    Protected *CE = CocoaMessage(#Null, *GID, "currentEditor")
    If (*CE)
      CocoaMessage(#Null, *CE, "insertText:$", @Text)
    EndIf
  CompilerEndIf
EndProcedure

OpenWindow(0, 10, 10, 320, 80, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
StringGadget(0, 10, 10, 300, 25, "Select some of this text and press Enter")
StringGadget(1, 10, 45, 300, 25, "Dummy")
AddKeyboardShortcut(0, #PB_Shortcut_Return, 0)
SetActiveGadget(0)

Repeat
  Event = WaitWindowEvent()
  If (Event = #PB_Event_CloseWindow)
    Done = #True
  ElseIf (Event = #PB_Event_Menu)
    InsertGadgetText(0, "ABC")
  EndIf
Until (Done)
Post Reply