EditorGadget

Mac OSX specific forum
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

EditorGadget

Post by coder14 »

Hi guys! Is there any way to turn off the scroll bars in the EditorGadget? The inactive bars are displayed even when the text does not exceed the viewable window?

Thanks!
User avatar
Shardik
Addict
Addict
Posts: 1989
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: EditorGadget

Post by Shardik »

coder14 wrote:Hi guys! Is there any way to turn off the scroll bars in the EditorGadget? The inactive bars are displayed even when the text does not exceed the viewable window?
Unfortunately it is not possible to turn off the scrollbars in the EditorGadget.
The EditorGadget on the Mac is realized by embedding a TXN object in a
UserPane. Using the MLTE (Multilingual Text Engine Reference) function
TXNCreateObject() you have to specify at creation of the TXN object whether
you want to have vertical and/or horizontal scrollbars. PureBasic internally
seems to demand a vertical and horizontal scrollbar always as a default and
it is not possible to change this setting afterwards. But if you create the TXN
object yourself it is well possible to create an editor-like gadget with or
without scrollbars. The only drawback is that you can't use PB's Gadget
functions anymore to access your customized EditorGadget. You will have
to use API functions instead of the PB functions.

A simple example:

Code: Select all

ImportC ""
  TXNAttachObjectToWindowRef(TXNObject.L, WindowRef.L)
  TXNCreateObject(*FrameRect, FrameOptions.L, *TXNObject)
  TXNFocus(TXNObject.L, BecomingFocused.L)
  TXNSetData(TXNObject.L, DataType.L, *DataPtr, DataSize.L, StartOffset.L, EndOffset.L)
EndImport

#kTXNTextData = 'TEXT'
#kTXNWantHScrollBarBit = 2
#kTXNWantVScrollBarBit = 3
#kTXNWantHScrollBarMask = 1 << #kTXNWantHScrollBarBit
#kTXNWantVScrollBarMask = 1 << #kTXNWantVScrollBarBit

Define Text.S

For i = 1 To 5
  Text + "Line " + Str(i) + #CR$
Next i

Procedure OpenEditorWindow(WindowID.L, TitleText.S, Text.S, x.L, y.L, Flags.L)
  OpenWindow(WindowID, x, y, 270, 76, TitleText, #PB_Window_SystemMenu)
  TXNCreateObject(0, Flags, @TXNObject)
  TXNAttachObjectToWindowRef(TXNObject, WindowID(WindowID))
  TXNSetData(TXNObject, #kTXNTextData, @Text, Len(Text) - 1, 0, 0)
  ProcedureReturn TXNObject
EndProcedure

Editor0 = OpenEditorWindow(0, "Editor without scrollbars", Text, 100, 100, 0)
Editor1 = OpenEditorWindow(1, "Editor with horizontal scrollbar", Text, 200, 200, #kTXNWantHScrollBarMask)
Editor2 = OpenEditorWindow(2, "Editor with vertical scrollbar", Text, 300, 300, #kTXNWantVScrollBarMask)
TXNFocus(Editor2, #True)

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re: EditorGadget

Post by coder14 »

Shardik wrote:But if you create the TXN object yourself it is well possible to create an editor-like gadget with or without scrollbars.
Clearly, you're the man! Thank you - just what I needed.
User avatar
Shardik
Addict
Addict
Posts: 1989
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: EditorGadget

Post by Shardik »

This is a workaround which demonstrates how to get text from an
EditorGadget created with API functions (without scrollbars) which
was selected by the user or how to obtain the whole text:

Code: Select all

EnableExplicit

ImportC ""
  TXNAttachObjectToWindowRef(TXNObject.L, WindowRef.L)
  TXNClear(TXNObject.L)
  TXNCopy(TXNObject.L)
  TXNCreateObject(*FrameRect, FrameOptions.L, *TXNObject)
  TXNFocus(TXNObject.L, BecomingFocused.L)
  TXNGetSelection(TXNObject.L, *StartOffset, *EndOffset)
  TXNSelectAll(TXNObject.L)
  TXNSetData(TXNObject.L, DataType.L, *DataPtr, DataSize.L, StartOffset.L, EndOffset.L)
  TXNSetFrameBounds(TXNObject.L, Top.L, Left.L, Bottom.L, Right.L, FrameID.L)
  TXNUpdate(TXNObject.L)
EndImport

#kTXNTextData = 'TEXT'

Structure HIRect
  x.F
  y.F
  Width.F
  Height.F
EndStructure

Procedure.S GetEditorText(TXNObject.L, SelectAll.L = #True)
  Protected DataHandle.L
  Protected EndOffset.L
  Protected StartOffset.L
  Protected Text.S

  If SelectAll
    TXNSelectAll(TXNObject)
  EndIf

  TXNGetSelection(TXNObject, @StartOffset, @EndOffset)

  If StartOffset < EndOffset
    TXNCopy(TXNObject)
    Text = GetClipboardText()
  EndIf

  ProcedureReturn Text
EndProcedure

Define Bounds.HIRect
Define i.L
Define Text.S
Define TXNObject.L

For i = 1 To 5
  Text + "Line " + Str(i) + #CR$
Next i

OpenWindow(0, 200, 100, 280, 140, "EditorGadget without scrollbars", #PB_Window_SystemMenu)
ButtonGadget(0, (WindowWidth(0) - 140) / 2, WindowHeight(0) - 50, 140, 20, "Display whole text")
ButtonGadget(1, (WindowWidth(0) - 150) / 2, WindowHeight(0) - 25, 150, 20, "Display selected text")

While WindowEvent()
Wend

Bounds\x = 5
Bounds\y = 5
Bounds\Width = WindowWidth(0) - 10
Bounds\Height = 80

TXNCreateObject(@Bounds, 0, @TXNObject)
TXNAttachObjectToWindowRef(TXNObject, WindowID(0))
TXNSetData(TXNObject, #kTXNTextData, @Text, Len(Text) - 1, 0, 0)
TXNSetFrameBounds(TXNObject, 5, 5, 80, WindowWidth(0) - 10, 0)
TXNUpdate(TXNObject)
TXNFocus(TXNObject, #True)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventType() = #PB_EventType_LeftClick
        Select EventGadget() 
          Case 0
            Debug "Whole text: " + #DQUOTE$ + GetEditorText(TXNObject, #True) + #DQUOTE$
          Case 1
            Debug "Selected text: " + #DQUOTE$ + GetEditorText(TXNObject, #False) + #DQUOTE$
        EndSelect
      EndIf
  EndSelect
ForEver
An alternative solution for an editor-like control without scrollbars
would be to use the simpler EditUnicodeTextControl:

Code: Select all

ImportC ""
  CFRelease(CFTypeRef.L)
  CFStringCreateWithCString(CFAllocatorRef.L, CString, CFStringEncoding.L)
  CFStringGetCString(CFStringRef.L, *StringBuffer, BufferSize.L, CFStringEncoding.L)
  CFStringGetLength(CFStringRef.L)
  CreateEditUnicodeTextControl(WindowRef.L, *BoundsRect, CFStringText.L, IsPassword.L, *FontStyleRec, *ControlRef)
  GetControlData(ControlRef.L, ControlPartCode.L, ControlKindTagName.L, BufferSize.L, *Buffer, *ActualSize)
  SetControlData(ControlRef.L, ControlPartCode.L, ControlKindTagName.L, BufferSize.L, *Buffer)
  SetKeyboardFocus(WindowRef.L, ControlRef.L, FocusPart.L)
EndImport

#kControlEditTextPart = 5
#kControlEditTextInsertCFStringRefTag = 'incf'
#kControlFocusNextPart = -1

Structure Rect
  Top.W
  Left.W
  Bottom.W
  Right.W
EndStructure

Procedure.S GetEditorText(EditControlRef)
  Protected StringRef.L
  Protected SelectedText.S

  If GetControlData(EditControlRef, #kControlEditTextPart, #kControlEditTextInsertCFStringRefTag, SizeOf(StringRef), @StringRef, 0) = 0
    If StringRef
      SelectedText = Space(CFStringGetLength(StringRef) + 1)
      CFStringGetCString(StringRef, @SelectedText, Len(SelectedText), 0)
      CFRelease(StringRef)
    EndIf
  EndIf

  ProcedureReturn SelectedText
EndProcedure

Procedure.S SetEditorText(EditControlRef, Text.S)
  Protected StringRef.L

  StringRef = CFStringCreateWithCString(0, @Text, 0)

  If StringRef
    SetControlData(EditControlRef, #kControlEditTextPart, #kControlEditTextInsertCFStringRefTag, SizeOf(StringRef), @StringRef)
    CFRelease(StringRef)
  EndIf
EndProcedure

Define Bounds.Rect

OpenWindow(0, 200, 100, 300, 93, "EditUnicodeTextControl", #PB_Window_SystemMenu)
ButtonGadget(0, (WindowWidth(0) - 150) / 2, WindowHeight(0) - 25, 150, 20, "Display selected text")

Bounds\Left = 10
Bounds\Top = 10
Bounds\Right = WindowWidth(0) - 10
Bounds\Bottom = WindowHeight(0) - 35

CreateEditUnicodeTextControl(WindowID(0), @Bounds, 0, #False, 0, @EditControlRef)
SetEditorText(EditControlRef, "Line 1" + #CR$ + "Line 2" + #CR$ + "Line 3")
SetKeyboardFocus(WindowID(0), EditControlRef, #kControlFocusNextPart)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 0
        Debug "Selected text: " + #DQUOTE$ + GetEditorText(EditControlRef) + #DQUOTE$
      EndIf
  EndSelect
ForEver
WilliamL
Addict
Addict
Posts: 1224
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: EditorGadget

Post by WilliamL »

Thanks Shardik,

This is a complete solution with the ability to retrieve just the selected text (or all of the text)! :)

Very nice!
MacBook Pro-M1 (2021), Sonoma 14.4.1, PB 6.10LTS M1
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re: EditorGadget

Post by coder14 »

Couple them with Shardik's earlier event handlers, and you have yourself a real alternative to the EditorGadget. Great work!
User avatar
Shardik
Addict
Addict
Posts: 1989
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: EditorGadget

Post by Shardik »

This is another more complicated example of creating an EditorGadget
without scrollbars using solely API functions. It demonstrates how to
change the font of selected text, how to read out the current contents
of the EditorGadget and how to discriminate between the text written in
different fonts. It would also be possible to change font size and style of
selected text in a similar way thus programming an own WYSIWYG editor: :wink:

Code: Select all

EnableExplicit

ImportC ""
  ATSUFindFontFromName(*FontName, FontNameLength.L, FontNameCode.L, FontNamePlatform.L, FontNameScript.L, FontNameLanguage.L, *ATSUIFontID)
  TXNAttachObjectToWindowRef(TXNObject.L, WindowRef.L)
  TXNCountRunsInRange(TXNObject.L, StartOffset.L, EndOffset.L, *RunCount)
  TXNCreateObject(*FrameRect, FrameOptions.L, *TXNObject)
  TXNFocus(TXNObject.L, BecomingFocused.L)
  TXNGetData(TXNObject.L, StartOffset.L, EndOffset.L, *DataHandle)
  TXNGetIndexedRunInfoFromRange(TXNObject.L, RunIndex.L, StartOffset.L, EndOffset.L, *RunStartOffset, *RunEndOffset, *RunDataType, FontAttributeCount.L, *TypeAttributes)
  TXNGetSelection(TXNObject.L, *StartOffset, *EndOffset)
  TXNSelectAll(TXNObject.L)
  TXNSetData(TXNObject.L, DataType.L, *DataPtr, DataSize.L, StartOffset.L, EndOffset.L)
  TXNSetFrameBounds(TXNObject.L, Top.L, Left.L, Bottom.L, Right.L, FrameID.L)
  TXNSetSelection(TXNObject.L, StartOffset.L, EndOffset.L)
  TXNSetTXNObjectControls(TXNObject.L, ClearAll.L, ControlCount.L, ControlTags.L, ControlData.L)
  TXNSetTypeAttributes(TXNObject.L, AttributeCount.L, TXNTypeAttributes.L, StartOffset.L, EndOffset.L)
  TXNUpdate(TXNObject.L)
EndImport

#kATSUFontTag = 261
#kFontFullName = 4
#kFontNoLanguage = -1
#kFontNoPlatform = -1
#kFontNoScript = -1
#kTXNEndOffset = $7FFFFFFF
#kTXNTextData = 'TEXT'

Structure AttributeData
  StructureUnion
    DataPtr.L
    DataValue.L
    ATSUFeatures.L
    ATSUVariations.L
    URLRef.L
  EndStructureUnion
EndStructure

Structure TXNTypeAttributes
  TXTNTag.L
  TXNAttributeSize.L
  TXNAttributeData.AttributeData
EndStructure

Structure HIRect
  x.F
  y.F
  Width.F
  Height.F
EndStructure

Procedure.L ChangeFont(TXNObject.L, FontName.S)
  Protected EndOffset.L
  Protected FontID.L
  Protected StartOffset.L
  Protected Dim TypeAttributes.TXNTypeAttributes(0)

  If ATSUFindFontFromName(@FontName, Len(FontName), #kFontFullName, #kFontNoPlatform, #kFontNoScript, #kFontNoLanguage, @FontID) = 0
    ; ----- Select "Line 3"
    StartOffset = 14
    EndOffset = 20
    TXNSetSelection(TXNObject, StartOffset, EndOffset)

    ; ----- Change font of "Line 3"
    TypeAttributes(0)\TXTNTag = #kATSUFontTag
    TypeAttributes(0)\TXNAttributeSize = SizeOf(FontID)
    TypeAttributes(0)\TXNAttributeData\DataValue = FontID
    TXNSetTypeAttributes(TXNObject, 1, @TypeAttributes(0), StartOffset, EndOffset)
    TXNSetSelection(TXNObject, #kTXNEndOffset, #kTXNEndOffset)

    ProcedureReturn FontID
  EndIf
EndProcedure

Procedure.S GetEditorText(TXNObject.L, FontID.L)
  Protected CustomFontText.S
  Protected DataHandle.L
  Protected DefaultFontText.S
  Protected EndOffset.L
  Protected i.L
  Protected RunCount.L
  Protected RunDataType.L
  Protected RunEndOffset.L
  Protected RunStartOffset.L
  Protected StartOffset.L
  Protected Text.S
  Protected Dim TypeAttributes.TXNTypeAttributes(0)

  TypeAttributes(0)\TXTNTag = #kATSUFontTag
  TypeAttributes(0)\TXNAttributeSize = SizeOf(FontID)
  TypeAttributes(0)\TXNAttributeData\DataValue = #Null

  TXNSelectAll(TXNObject)
  TXNGetSelection(TXNObject, @StartOffset, @EndOffset)
  
  If StartOffset < EndOffset
    If TXNCountRunsInRange(TXNObject, StartOffset, EndOffset, @RunCount) = 0
      For i = 0 To RunCount - 1
        If TXNGetIndexedRunInfoFromRange(TXNObject, i, StartOffset, EndOffset, @RunStartOffset, @RunEndOffset, @RunDataType, 1, @TypeAttributes(0)) = 0
          If TXNGetData(TXNObject, RunStartOffset, RunEndOffset, @DataHandle) = 0
            Text = PeekS(PeekL(DataHandle), RunEndOffset - RunStartOffset, #PB_Unicode)

            If TypeAttributes(0)\TXNAttributeData\DataValue = FontID
              CustomFontText + Text
            Else
              DefaultFontText + Text
            EndIf                
          EndIf
        EndIf
      Next i
    EndIf
  EndIf

  TXNSetSelection(TXNObject, #kTXNEndOffset, #kTXNEndOffset)

  Text = "Text in default font:" + #CR$ + DefaultFontText + #CR$ + #CR$
  Text + "Text in font Apple Chancery:" + #CR$ + CustomFontText

  ProcedureReturn Text
EndProcedure

Define Bounds.HIRect
Define FontID.L
Define i.L
Define Text.S
Define TXNObject.L

For i = 1 To 5
  Text + "Line " + Str(i) + #CR$
Next i

OpenWindow(0, 200, 100, 420, 115, "Get text of MLTE TextObject containing different fonts", #PB_Window_SystemMenu)
ButtonGadget(0, (WindowWidth(0) - 370) / 2, WindowHeight(0) - 25, 370, 20, "Change font of 3rd line to Apple Chancery and get text", #PB_Button_MultiLine)

While WindowEvent()
Wend

Bounds\x = 5
Bounds\y = 5
Bounds\Width = WindowWidth(0) - 10
Bounds\Height = 80

TXNCreateObject(@Bounds, 0, @TXNObject)
TXNAttachObjectToWindowRef(TXNObject, WindowID(0))
TXNSetData(TXNObject, #kTXNTextData, @Text, Len(Text) - 1, 0, 0)
TXNSetFrameBounds(TXNObject, 5, 5, 80, WindowWidth(0) - 10, 0)
TXNUpdate(TXNObject)
TXNFocus(TXNObject, #True)

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventType() = #PB_EventType_LeftClick
        If EventGadget() = 0
          DisableGadget(0, #True)
          FontID = ChangeFont(TXNObject, "Apple Chancery")
          MessageRequester("Editor contents info", GetEditorText(TXNObject, FontID))
        EndIf
      EndIf
  EndSelect
ForEver
WilliamL
Addict
Addict
Posts: 1224
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: EditorGadget

Post by WilliamL »

Thanks shardik!

Now that we have some new features in the Editor gadget (by API) I'd like to see them incorporated into PureBasic...

Maybe after the summer break. :)
MacBook Pro-M1 (2021), Sonoma 14.4.1, PB 6.10LTS M1
Post Reply