Page 1 of 1

Cross plattform StringGadget text selection

Posted: Thu Nov 01, 2012 11:50 am
by Kukulkan
Hi,

I need some cross plattform function that allows me to select text and place cursor in a StringGadget. It also should be able to return the selection.

Here is my version for Windows. If you already did something before or are familiar with CocoaMessage() on MacOS, can you please insert your code at the TODO comments or at least give me a link to further information about how to get?

Code: Select all

EnableExplicit

; 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
        ; TODO
    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
        ; TODO
    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
      ; TODO
  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
      ; TODO
  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
Thank you!

Kukulkan

Cross plattform StringGadget text selection

Posted: Thu Nov 01, 2012 12:16 pm
by Wood51
Hi !
perhaps with gtk_editable_select_region .
@+ wood51

Re: Cross plattform StringGadget text selection

Posted: Thu Nov 01, 2012 1:54 pm
by wilbert
Try if this works on OS X

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

Re: Cross plattform StringGadget text selection

Posted: Thu Nov 01, 2012 3:34 pm
by Shardik
I have added support for Linux and MacOS X with Carbon framework (PB 4.61 and older or PB 5.00 with subsystem Carbon):

Code: Select all

EnableExplicit

CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
  CompilerIf #PB_Compiler_Version < 470 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
    ImportC ""
      GetControlData(ControlRef.L, ControlPartCode.L, TagName.L, BufferSize.L, *Buffer, *ActualSize)
      SetControlData(ControlRef.L, ControlPartCode.L, TagName.L, BufferSize.L, *Buffer)
    EndImport
    
    #kControlEditTextPart = 5
    #kControlEditTextSelectionTag = $73656C65 ; 'sele'
    
    Structure ControlEditTextSelectionRec
      SelStart.W
      SelEnd.W
    EndStructure
  CompilerElse
    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
CompilerEndIf

; Sets the selection in a StringGadget() independent of Operating System
; If Start.i < 1, every selection is 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), #EM_SETSEL, Start.i - 1, Start.i + Length.i - 1)
      CompilerCase #PB_OS_Linux
        gtk_editable_select_region_(GadgetID(GadgetID), Start - 1, Start -1 + Length)
      CompilerCase #PB_OS_MacOS
        CompilerIf #PB_Compiler_Version < 470 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
          Protected TextSelection.ControlEditTextSelectionRec
          TextSelection\selStart = Start - 1
          TextSelection\selEnd = Start -1 + Length
          SetControlData(GadgetID(GadgetID), #kControlEditTextPart, #kControlEditTextSelectionTag, SizeOf(ControlEditTextSelectionRec), @TextSelection)
        CompilerElse
          Protected Range.NSRange\location = Start - 1 : Range\length = Length
          CocoaMessage(0, TextEditor(GadgetID), "setSelectedRange:@", @Range)
        CompilerEndIf
    CompilerEndSelect
  Else
    ; Deselect all
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_Windows
        SendMessage_(GadgetID(GadgetID.i), #EM_SETSEL, -1, 0)
      CompilerCase #PB_OS_Linux
        gtk_editable_delete_selection_(GadgetID(GadgetID))
      CompilerCase #PB_OS_MacOS
        CompilerIf #PB_Compiler_Version < 470 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
          TextSelection\selStart = -1
          TextSelection\selEnd = -1
          SetControlData(GadgetID(GadgetID), #kControlEditTextPart, #kControlEditTextSelectionTag, SizeOf(ControlEditTextSelectionRec), @TextSelection)
        CompilerElse
          CocoaMessage(0, TextEditor(GadgetID), "setSelectedRange:@", @NSRangeZero)
        CompilerEndIf
    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
      gtk_editable_get_selection_bounds_(GadgetID(GadgetID), @Start, @Stop)
      ProcedureReturn Start + 1
    CompilerCase #PB_OS_MacOS
      CompilerIf #PB_Compiler_Version < 470 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
        Protected TextSelection.ControlEditTextSelectionRec
        GetControlData(GadgetID(GadgetID), #kControlEditTextPart, #kControlEditTextSelectionTag, SizeOf(ControlEditTextSelectionRec), @TextSelection.ControlEditTextSelectionRec, 0)
        ProcedureReturn TextSelection\SelStart + 1
      CompilerElse
        Protected Range.NSRange
        CocoaMessage(@Range, TextEditor(GadgetID), "selectedRange")
        ProcedureReturn Range\location + 1
      CompilerEndIf
  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), #EM_GETSEL, @Start.i, @Stop.i)
      ProcedureReturn Stop.i - Start.i
    CompilerCase #PB_OS_Linux
      gtk_editable_get_selection_bounds_(GadgetID(GadgetID), @Start, @Stop)
      ProcedureReturn Stop - Start
    CompilerCase #PB_OS_MacOS
      CompilerIf #PB_Compiler_Version < 470 Or (#PB_Compiler_Version >= 500 And Subsystem("Carbon"))
        Protected TextSelection.ControlEditTextSelectionRec
        GetControlData(GadgetID(GadgetID), #kControlEditTextPart, #kControlEditTextSelectionTag, SizeOf(ControlEditTextSelectionRec), @TextSelection.ControlEditTextSelectionRec, 0)
        ProcedureReturn TextSelection\SelEnd - TextSelection\SelStart
      CompilerElse
        Protected Range.NSRange
        CocoaMessage(@Range, TextEditor(GadgetID), "selectedRange")
        ProcedureReturn Range\length
      CompilerEndIf
  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
Update: I had to change

Code: Select all

#kControlEditTextSelectionTag = 'sele'
to

Code: Select all

#kControlEditTextSelectionTag = $73656C65 ; 'sele'
for the Carbon code in order to work correctly with both ASCII and Unicode compilation.

Re: Cross plattform StringGadget text selection

Posted: Fri Nov 02, 2012 7:58 am
by Kukulkan
Wow, just tested the last version of Shardik on Windows and Mac OS and it works great! Linux will get tested today, too.

Thank you! :D

I plan to do some autocomplete stringgadget for entering (multiple) email addresses. If ready, I will post the source here in the forum (based on this code).

Kukulkan

Re: Cross plattform StringGadget text selection

Posted: Fri Nov 02, 2012 8:42 am
by Shardik
Kukulkan wrote:Wow, just tested the last version of Shardik on Windows and Mac OS and it works great!
I forgot to say that I took wilbert's version with added MacOS X Cocoa support (default setting in PB 5.00) and added MacOS X Carbon support plus Linux. Did you test both Cocoa and Carbon versions on MacOS X? :wink:

Re: Cross plattform StringGadget text selection

Posted: Fri Nov 02, 2012 9:41 am
by Kukulkan
No, I just tested Cocoa method as I only compile with this.

I just tested now and it does not work with Carbon subsystem. It marks the whole text and GetStringGadgetSelectionLength() result is also wrong :(

Kukulkan

Re: Cross plattform StringGadget text selection

Posted: Fri Nov 02, 2012 10:21 am
by Shardik
Kukulkan wrote:I just tested now and it does not work with Carbon subsystem. It marks the whole text and GetStringGadgetSelectionLength() result is also wrong :(
Sorry to hear that! With which MacOS X version did you test? I tested with Snow Leopard (10.6.8 ) and the results of Cocoa and Carbon (PB 4.61 and PB 5.00 x86 with subsystem Carbon) were both identical (and the same as in Windows and Linux)...

Re: Cross plattform StringGadget text selection

Posted: Fri Nov 02, 2012 10:24 am
by wilbert
@Kukulkan, a little remark about my SetStringGadgetSelection cocoa code with Start < 1.
My current solution clears the selection and sets the cursor to the beginning of the text.
I don't know exactly what your goal is but if you want the focus on the Gadget also gone, there's a better way to accomplish that with.

Re: Cross plattform StringGadget text selection

Posted: Fri Nov 02, 2012 11:14 am
by Kukulkan
Hi,
With which MacOS X version did you test?
I'm using MacOS 10.7.4 (Lion).
My current solution clears the selection and sets the cursor to the beginning of the text. I don't know exactly what your goal is but if you want the focus on the Gadget also gone, there's a better way to accomplish that with.
No, keeping focus is good! If someone does not need, he can use SetActiveGadget() function.

Kukulkan

Re: Cross plattform StringGadget text selection

Posted: Fri Nov 02, 2012 2:20 pm
by Shardik
Kukulkan wrote:I just tested now and it does not work with Carbon subsystem. It marks the whole text and GetStringGadgetSelectionLength() result is also wrong :(
Kukulkan wrote:I'm using MacOS 10.7.4 (Lion).
Sorry, but I can't reproduce your described Carbon error neither in MacOS X 10.6.8 (Snow Leopard) nor in MacOS X 10.7.5 (Lion) using PB 4.61 and PB 5.00 x86 Beta 6 and Beta 8 with subsystem Carbon! I always obtain this result (the same as with Cocoa, Linux and Windows):

Image

Re: Cross plattform StringGadget text selection

Posted: Fri Nov 02, 2012 2:50 pm
by Kukulkan
This is how it looks on my Lion with "Subsystem = Carbon" run from IDE:

Image

I'm using PB 5.00 Beta 7

Kukulkan

Re: Cross plattform StringGadget text selection

Posted: Fri Nov 02, 2012 6:36 pm
by Shardik
I think I have solved our Carbon problem: I assume you have activated "Create unicode executable" in the Compiler Options. With unicode compilation I obtain the same result as you have documented... :wink:

I only had to change

Code: Select all

#kControlEditTextSelectionTag = 'sele'
in my above code to

Code: Select all

#kControlEditTextSelectionTag = $73656C65 ; 'sele'
and now the Carbon code works correctly in ASCII and Unicode mode. Sorry for my mistake... :wink:

Re: Cross plattform StringGadget text selection

Posted: Tue Dec 08, 2015 8:14 am
by davido
@Shardik,
Very useful.
Thank you for taking the trouble to make it cross-platform. :D