<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>TextArea &#8211; Xojo Programming Blog</title>
	<atom:link href="https://blog.xojo.com/tag/textarea/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.xojo.com</link>
	<description>Blog about the Xojo programming language and IDE</description>
	<lastBuildDate>Wed, 25 Sep 2024 14:26:35 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Updated Tutorial: Active Words</title>
		<link>https://blog.xojo.com/2021/09/16/updated-tutorial-active-words/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Thu, 16 Sep 2021 13:00:00 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[TextArea]]></category>
		<category><![CDATA[Xojo API 2.0]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=9171</guid>

					<description><![CDATA[Follow this tutorial to create active, aka clickable, words in the text of a TextArea control in your Xojo projects. Learn to use the Object-oriented Delegate design pattern to dynamically change how your app reacts when the user clicks on those active words. Best of all, this project is cross-platform, so you can use it for macOS, Windows and Linux!]]></description>
										<content:encoded><![CDATA[
<p><strong>This post was originally <a href="https://blog.xojo.com/2018/07/31/tutorial-active-words/">published</a> in 2018 and has been updated to use Xojo API 2.0</strong>.</p>



<p>Follow this tutorial&nbsp;to create active, aka clickable, words in the text of a TextArea control in your Xojo projects. Learn to use the Object-oriented <a href="https://blog.xojo.com/2016/07/25/make-an-rss-reader-with-xojoin-33-lines-of-code/">Delegate design pattern</a> to dynamically change how your app reacts when the user clicks on those active words. Best of all, this project is cross-platform, so you can use it for macOS, Windows and Linux!</p>



<span id="more-9171"></span>



<p>The active words&nbsp;will be based on the use of <a href="https://documentation.xojo.com/api/data_types/pair.html">Pairs</a>, so one piece of information will be the active word and the other piece of information will be the associated data, which is always a String. The associated data could be a link or text you want to show in another control when the user clicks on the active word.</p>



<p>For the sake of this tutorial, the class we are creating has some limitations. For example, you won&#8217;t be able to repeat the same word and assign it to take different actions. If you want your code to react the same to the same active word or words throughout your project, well then, that&#8217;s what it will do. Anyway, this tutorial serves as a good starting point and you&#8217;ll be able to modify and improve it to meet your own needs.</p>



<p>Download the class with the example project from <a href="https://www.dropbox.com/s/11z4g56q99lvw3j/URLTextArea.xojo_binary_project.zip?dl=1">here</a>.</p>



<div class="wp-block-image is-style-default"><figure class="aligncenter"><img fetchpriority="high" decoding="async" width="802" height="666" src="https://blog.xojo.com/wp-content/uploads/2021/09/ActiveWords.gif" alt="" class="wp-image-9172"/></figure></div>



<h3 class="wp-block-heading">TextArea Subclass</h3>



<p>This feature will be based in the <a href="https://documentation.xojo.com/api/deprecated/textarea.html">TextArea</a> class, the first step is add a new TextArea control from the Library panel to the Navigator, change its name to <code>LinkDetectorTextArea</code>. Next, add the following Event Handlers to the TextArea subclass. These will be responsible for following the pointer movement and reacting to the registered active words for the instance.</p>



<ul class="wp-block-list"><li><b>Open</b>. To assign the pointer cursor, more appropriate for the kind of functionality this subclass will provide</li><li><b>MouseMove</b>. To detect if there is an active word under the cursor pointer to click on</li><li><b>MouseDown</b>. To call all the registered Observers when the user clicks on an active word, passing along the detected word and the associated information</li></ul>



<p>Since we also want to make these events available for any instance created from the subclass, we have to create them again as Event Definitions. With the subclass selected in the Navigator, select <code>Insert &gt; Event Definition</code> using the following signatures:</p>



<ul class="wp-block-list"><li><b>Event Name. Open</b>.</li><li><b>Event Name. MouseMove</b>. Parameters: <code>X as Integer, Y as Integer</code>.</li><li><b>Event Name. MouseDown</b>. Parameters: <code>X as Integer, Y as Integer</code>. Return Type: <code>Boolean</code>.</li></ul>



<p>Add some Private properties used by the added Event Handlers and other methods we have yet to define to the class. Set these properties as private because they just need to be accessed from the class itself and not from other pieces of external code or additional classes inherited from the subclass:</p>



<ul class="wp-block-list"><li><b>Name. boundedWord</b>. Type: <code>Pair</code>. This is the property that will point to the current matched active word from the text.</li><li><b>Name. previousBoundedWord</b>. Type: <code>Pair</code>. This property points to the previous matched word from the text.</li><li><b>Name. linkedWords</b>. Type: <code>Dictionary</code>. This is the Dictionary that will contain all the active words / additional data pairs.</li><li><b>Name. observers()</b>. Type: <code>ActionDelegate</code>. This is an Array containing all the registered observers of the type <code>ActionDelegate</code> yet to be defined.</li></ul>



<p>As we have seen, we need to define a Delegate type for the class. Select <code>Insert &gt; Delegate</code> with the following data:</p>



<ul class="wp-block-list"><li><b>Delegate Name</b>: ActionDelegate.</li><li><b>Parameters</b>: boundedWord as Pair.</li><li><b>Scope</b>: Public.</li></ul>



<h3 class="wp-block-heading">Assigning the Cursor Type</h3>



<p>Select the <code>Open</code> Event and add the following code in the corresponding Code Editor:</p>



<pre class="wp-block-preformatted">Me.MouseCursor = System.cursors.standardpointer
RaiseEvent open</pre>



<p>We simply use this Event for assigning the Arrow cursor type that is most appropriate.</p>



<h3 class="wp-block-heading">Matching the Active Words</h3>



<p>Next, select the <code>MouseMove</code> Event Handler and type the following code in the corresponding Code Editor:</p>



<pre class="wp-block-preformatted">Var tlen As Integer = Me.Text.Length
If previousBoundedWord &lt;&gt; Nil Then
  setStyleForWord(previousBoundedWord, False)
  previousBoundedWord = Nil
End If

Var boundLeft, boundRight As Integer = -1
Var startPosition As Integer = Me.CharacterPosition(x,y)

If CharacterPosition(x,y) &gt;= tLen Then
  If boundedWord &lt;&gt; Nil Then setStyleForWord( boundedWord, False )
  mboundedWord = Nil
ElseIf Me.Text.Middle(startPosition,1) &lt;&gt; Chr(32) And Me.Text.Middle(startPosition,1) &lt;&gt; EndOfLine Then
  For n As Integer = startPosition DownTo 0
    If Me.Text.Middle(n,1) = Chr(32) Or Me.Text.Middle(n,1) = EndOfLine or n = 0 Then
      boundLeft = n
      Exit
    End
    
  Next
  For n As Integer = startPosition To tlen
    If Me.Text.Middle(n+1,1) = Chr(32) Or Me.Text.Middle(n+1,1) = EndOfLine Or n = tlen Then
      boundRight = n+1
      Exit
    End
  Next
End

If boundLeft &lt;&gt; -1 And boundRight &lt;&gt; -1 Then
  Var isolatedWord As String = Me.Text.Middle(boundLeft, boundRight - boundLeft)
  Var check As pair = wordInDictionary( isolatedWord, boundleft, boundRight )
  If check  &lt;&gt; Nil Then 
    mboundedWord  = check
    If previousBoundedWord = Nil Then previousBoundedWord = mboundedWord
    setStyleForWord(previousBoundedWord, True)
  Else
    mboundedWord = Nil 
  End If
End If

RaiseEvent mouseMove(X, Y)</pre>



<p>This code is in charge of detecting the limits of the word under the cursor and finding if it is one of the active words registered for the class.</p>



<h3 class="wp-block-heading">Reacting to Active Words</h3>



<p>Finally, select the <code>MouseDown</code> Event Handler and write the following code into the associated Code Editor:</p>



<pre class="wp-block-preformatted">If linkedWords &lt;&gt; Nil And boundedWord &lt;&gt; Nil Then
  Var p As New pair(mboundedWord.Left,linkedWords.Value(mboundedWord.Left))
  For Each item As LinkDetectorTextArea.ActionDelegate In observers
    item.Invoke p
  Next
  Return True
End If

Return RaiseEvent mousedown(x,y)</pre>



<p>This code will invoke all the registered Observers for the active word detected under the cursor.</p>



<h3 class="wp-block-heading">Registering the Active Words</h3>



<p>We need a way to inform the instances about the active words to be aware of. We could do that from a Constructor or using a Computed Property. In this case, we will use a method for assigning the received Dictionary of Pairs to the Property in charge of storing that information. Select <code>Insert &gt; Method</code> and use this data in the associated Inspector Panel:</p>



<ul class="wp-block-list"><li><b>Name</b>: setDictionary.</li><li><b>Parameters</b>: d As Dictionary.</li><li><b>Scope</b>: Public.</li></ul>



<p>In the associated Code Editor, put this simple line of code:</p>



<pre class="wp-block-preformatted">linkedWords = d</pre>



<h3 class="wp-block-heading">Registering (and Deregistering) Observers</h3>



<p>As we have said, the class will be able to react to the active words, so we need to provide a couple of methods to register and deregister Observers at any time. Use the following information for the first method:</p>



<ul class="wp-block-list"><li><b>Method Name</b>: registerObserver.</li><li><b>Parameters</b>: observer as ActionDelegate.</li><li><b>Scope</b>: Public.</li></ul>



<p>And write this line of code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">if observer &lt;&gt; nil then observers.add observer</pre>



<p>Add a second method that will be in charge of deregistering an Observer. Use the following data for that:</p>



<ul class="wp-block-list"><li><b>Method Name</b>: deleteObserver.</li><li><b>Parameters</b>: observer As LinkDetectorTextArea.ActionDelegate.</li><li><b>Scope</b>: Public.</li></ul>



<p>This is the code that will search and remove the received <code>ActionDelegate</code> from the instance Array of Observers:</p>



<pre class="wp-block-preformatted">If observer &lt;&gt; Nil Then
  Var n As Integer = observers.IndexOf(observer)
  observers.RemoveAt(n)
end</pre>



<h3 class="wp-block-heading">Regular Expressions to the Rescue …&nbsp;and Styling!</h3>



<p>Finally, add a couple of methods to finish the TextArea subclass. These will be Private for the class, auxiliary methods called mainly from the Event Handlers. The first one is the method that does the matching with the received candidate and upon finding a match, will return the corresponding data pair to the caller. To do this, add a new Method using the following data:</p>



<ul class="wp-block-list"><li><b>Method Name</b>: wordInDictionary.</li><li><b>Parameters</b>: word As string, leftposition As integer, RightPosition As integer.</li><li><b>Return Type</b>: Pair.</li><li><b>Scope</b>: Private.</li></ul>



<p>And write the following code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">#Pragma Unused RightPosition

Var theRightPosition As Integer
Var re As New RegEx
re.SearchPattern = "[a-zA-Z]+"
Var rm As RegExMatch
rm = re.Search(word)

If rm &lt;&gt; Nil Then 
  Var foundword As String = rm.SubExpressionString(0)
  Var characterPosition As Integer = word.LeftBytes(rm.SubExpressionStartB(0)).Length
  word = foundword
  leftposition = leftposition + characterPosition
  therightposition = leftposition + word.Length
End If

If linkedWords.HasKey(word) Then Return New pair(word,leftposition.ToString+"-"+ theRightPosition.ToString)

Var blu As Integer
Var dictionaryKeys() as variant = linkedWords.Keys

for each thekey as string in dictionaryKeys  
  If thekey.IndexOf(word) &lt;&gt; -1 Then
    blu = Me.Text.IndexOf(thekey)
    If leftposition &gt;= blu And leftposition &lt;= (blu + thekey.Length) Then
      Var finalposition As Integer =blu+thekey.Length
      Return New pair(thekey,blu.ToText+"-"+finalposition.ToString)
    end if
  end
next

Return nil</pre>



<h3 class="wp-block-heading">Adding Style</h3>



<p>Finally, we need to choose how to inform the user that the word below the cursor is an active word so they can click on it. For this example, I&#8217;ve used the Underline text style, but you can change that. Add the last method for the class using the following data:</p>



<ul class="wp-block-list"><li><b>Method Name</b>: setStyleForWord</li><li><b>Parameters</b>: word As pair, mode As Boolean.</li><li><b>Scope</b>: Private.</li></ul>



<p>Use the <code>mode</code> parameter in order to use the method to apply or delete the style. For example, removing the style from the previous detected active word and styling the new one. Write the following code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">Var cStart, cEnd As Double
cStart = word.Right.StringValue.NthField("-",1).Val
cEnd = word.Right.StringValue.NthField("-",2).Val - cStart

Var t As StyledText

t = Me.StyledText

cstart = If( cstart - 1 &lt; 0, 0, cstart)

t.Underline(cstart, cEnd) = mode</pre>



<h3 class="wp-block-heading">Putting it all together!</h3>



<p>With the subclass finished it is time to do some Window Layout so we can test the subclass functionality. Select the <code>Window1</code> item in the Navigator to access the Window Layout Editor and drag the <code>LinkDetectorTextArea</code> subclass from the Navigator onto the Window Layout. Use the Inspector to change its name to <code>myURLField</code>. Then add three Label controls and an HTMLViewer, both from the Library. The HTMLViewer will show the URL associated with the active word, while one of the three Label controls will show the detected active words. Both the URL loading as the displaying of the detected word will be done by a previously registered Observer (a window method). The finished window layout should look something like this:</p>



<div class="wp-block-image is-style-default"><figure class="aligncenter"><img decoding="async" width="1732" height="1420" src="https://blog.xojo.com/wp-content/uploads/2018/07/TextAreaLinkLayout.png" alt="" class="wp-image-4608"/></figure></div>



<p>Where the text under the &#8220;Sample Text&#8221; label is the one assigned to the <code>Text</code> property of the <code>LinkDetectorTextArea</code> instance.</p>



<p>Now, let&#8217;s add to the <code>Window1</code> object the method that will act as an Observer, select the <code>Add &gt; Method</code> option in combination with the following data:</p>



<ul class="wp-block-list"><li><b>Name</b>: updateContents</li><li><b>Parameters</b>: p as pair</li><li><b>Scope</b>: Public</li></ul>



<p>And put the following code in the associated Code Editor:</p>



<pre class="wp-block-preformatted">linkedControl.Text = p.Left
HTMLViewer1.LoadURL p.Right</pre>



<p>Lastly, add the <code>Open</code> event to the <code>Window1</code> window, adding the following code to the associated Code Editor. This will initialize the <code>LinkDetectorTextArea</code> instance providing the clickable words and their associated URLs:</p>



<pre class="wp-block-preformatted">myURLField.setDictionary New Dictionary("Xojo":"https://www.xojo.com","iOS":"http://www.apple.com/ios/",_
"macOS":"http://www.apple.com/osx/", "Windows":"https://www.microsoft.com/en-US/windows","Linux":"https://es.wikipedia.org/wiki/GNU/Linux",_
"Raspberry Pi":"https://www.raspberrypi.org/","AprendeXojo":"https://www.aprendexojo.com","Web":"https://www.xojo.com/web")

myURLField.registerObserver WeakAddressOf updateContents</pre>



<p>Run the project, move the cursor over the text and you will see the how the active words are underlined on the fly; and if you click on any of them, the Window will display the detected word, while loading the associated URL.</p>



<p>Of course, this can be vastly improved and expanded upon … enjoy!</p>



<p><em>Paul learned to program in BASIC at age 13 and has programmed in more languages than he remembers, with Xojo being an obvious favorite. When not working on Xojo, you can find him talking about retrocomputing at <a href="https://goto10.substack.com" target="_blank" rel="noreferrer noopener">Goto 10</a> and </em>on Mastodon @lefebvre@hachyderm.io.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Tutorial: Active Words</title>
		<link>https://blog.xojo.com/2018/07/31/tutorial-active-words/</link>
		
		<dc:creator><![CDATA[Javier Menendez]]></dc:creator>
		<pubDate>Tue, 31 Jul 2018 10:00:50 +0000</pubDate>
				<category><![CDATA[Cross-Platform]]></category>
		<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Learning]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Design Patterns]]></category>
		<category><![CDATA[TextArea]]></category>
		<category><![CDATA[Xojo Programming Language]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=4605</guid>

					<description><![CDATA[Follow this tutorial to see how you can show active (clickable) words in a text of a TextArea control using the OOP Delegate pattern.]]></description>
										<content:encoded><![CDATA[<p>Follow this tutorial to learn how to create active (clickable) words in a text of a TextArea control using the <b>OOP</b> <a href="https://blog.xojo.com/2016/07/25/make-an-rss-reader-with-xojoin-26-lines-of-code/">Delegate design pattern</a>, which allows you to dynamically change how your app will react when the user clicks on any of these active words. Best of all, this is cross-platform, so you can use it for macOS, Windows and Linux deployments!<span id="more-4605"></span></p>
<p>Our <em>active words</em> will be based on the use of <a href="http://developer.xojo.com/pair"><b>Pairs</b></a>, so one of the pieces of information will be the active word (or words) we want to detect on the text, and the other piece of information for the <b>Pair</b> will be any associated data you want…this is always a String. For example, the associated data could be a link or text you want to show in another control when the user clicks on the word.</p>
<p>For the sake of this tutorial, the class created has some limitations. For example, you can&#8217;t repeat the same word (or same combination of words) assigning them to different actions. If you do that, the class will alway react to the first occurrence found; if you want your code to always react the same to the same active word or words…then that&#8217;s ok. Anyway, this tutorial serves as a good starting point so you can modify and improve it to meet your own needs.</p>
<p>You can download the class with the example project from <a href="https://www.dropbox.com/s/qe9qu5gcx883e1o/LinkDetectorTextArea.zip?dl=1">here</a>.</p>
<h2>TextArea subclass</h2>
<p>This feature will be based in the <a href="http://documentation.xojo.com/api/deprecated/textarea.html"><b>TextArea</b></a> class, so the first step is add a new TextArea control from the <b>Library</b> panel to the <b>Navigator</b>, changing its name to <code>LinkDetectorTextArea</code>. Next, add the following <b>Event Handlers</b> to our TextArea subclass. These will be responsible for following the pointer movement and reacting to the registered active words for the instance:</p>
<ul>
<li><b>Open</b>. Just to assign the <b>Pointer</b> cursor, more appropriate for the kind of functionality this subclass will provide.</li>
<li><b>MouseMove</b>. Here is where we will detect if there is an active word (or combination of words) found under the cursor pointer.</li>
<li><b>MouseDown</b>. This is the Event Handler that will call all the registered <b>Observers</b> when the user clicks on an active word, passing along the detected word and the associated information.</li>
</ul>
<p>As we want to make these events also available for any instance created from our subclass, then we have to create them again as <b>Event Definitions</b>. With the subclass selected in the Navigator, select <code>Insert &gt; Event Definition</code> using the following signatures:</p>
<ul>
<li><b>Event Name. Open</b>.</li>
<li><b>Event Name. MouseMove</b>. Parameters: <code>X as Integer, Y as Integer</code>.</li>
<li><b>Event Name. MouseDown</b>. Parameters: <code>X as Integer, Y as Integer</code>. Return Type: <code>Boolean</code>.</li>
</ul>
<p>Next we will need to add to the class some <b>Private</b> properties used by the added Event Handlers and other methods yet to define. These properties will be set as private because they just need to be accessed from the class itself, and not from other pieces of external code or additional classes inherited from our own subclass:</p>
<ul>
<li><b>Name. boundedWord</b>. Type: <code>Pair</code>. This is the property that will point to the current matched active word from the text.</li>
<li><b>Name. previousBoundedWord</b>. Type: <code>Pair</code>. This property points to the previous matched word from the text.</li>
<li><b>Name. linkedWords</b>. Type: <code>Dictionary</code>. This is the Dictionary that will contain all the active words / additional data pairs.</li>
<li><b>Name. observers()</b>. Type: <code>ActionDelegate</code>. Array containing all the registered observers of the type <code>ActionDelegate</code> yet to define.</li>
</ul>
<p>As we have seen, we need to define a <b>Delegate</b> type for our class. Select <code>Insert &gt; Delegate</code> with the following data:</p>
<ul>
<li><b>Delegate Name</b>: ActionDelegate.</li>
<li><b>Parameters</b>: boundedWord as Pair.</li>
<li><b>Scope</b>: Public.</li>
</ul>
<h3>Assigning the Cursor type</h3>
<p>Select the <code>Open</code> Event and add the following code in the corresponding Code Editor. We simply use this Event for assigning the Arrow cursor type that is more appropriate for the kind of functionality offered:</p>
<pre>Me.MouseCursor = System.cursors.standardpointer
RaiseEvent open</pre>
<h3>Matching the Active Words</h3>
<p>Next, select the <code>MouseMove</code> Event Handler and type the following code in the corresponding Code Editor. This code is in charge of detecting the limits of the word under the cursor and finding if it is one of the active words registered for the class:</p>
<pre>Dim length As Integer = Me.Text.Len
if previousBoundedWord &lt;&gt; nil then
  setStyleForWord(previousBoundedWord, false)
  previousBoundedWord = nil
end if
dim boundLeft, boundRight as integer

Dim startPosition As Integer = Me.CharPosAtXY(x,y)

If CharPosAtxy(x,y) &gt;= length Then
  if boundedWord &lt;&gt; nil then setStyleForWord( boundedWord, false )
  mboundedWord = nil
elseif me.text.mid(startPosition,1) &lt;&gt; chr(32) and me.Text.mid(startPosition,1) &lt;&gt; EndOfLine then
  for n as integer = startPosition DownTo 1
    if me.Text.mid(n-1,1) = chr(32) or me.Text.mid(n-1,1) = EndOfLine then
      boundLeft = n
      exit
    end
  next
  for n as integer = startPosition to length
    if me.Text.mid(n+1,1) = chr(32) or me.Text.mid(n+1,1) = EndOfLine or n = length then
      boundRight = n+1
      exit
    end
  next
end
dim isolatedWord as string = me.Text.mid(boundLeft, boundRight - boundLeft)
dim check as pair = wordInDictionary( isolatedWord, boundleft, boundRight )
if check  &lt;&gt; nil then
  mboundedWord  = check
  if previousBoundedWord = nil then previousBoundedWord = mboundedWord
  setStyleForWord(previousBoundedWord, true)
else
  mboundedWord = nil
end if
RaiseEvent mouseMove(X, Y)</pre>
<h3>Reacting to Active Words</h3>
<p>Finally, select the <code>MouseDown</code> Event Handler and write the following code into the associated Code Editor. This code will invoke all the registered observers for the active word detected under the cursor pointer (if any):</p>
<pre>If linkedWords &lt;&gt; Nil And boundedWord &lt;&gt; Nil Then
  Dim p As New pair(mboundedWord.Left,linkedWords.Value(mboundedWord.Left))
  For Each item As LinkDetectorTextArea.ActionDelegate In observers
    item.Invoke p
  Next
  Return True
End If
Return RaiseEvent mousedown(x,y)</pre>
<h2>Registering the Active Words</h2>
<p>We need a way to inform the instances about the active words it has to be aware of. We could do that from a <b>Constructor</b> or using a <b>Computed Property</b> instead of a regular one. In this case, we will use a method for assigning the received Dictionary of Pairs to the Property in charge of storing that information. So, select <code>Insert &gt; Method</code> and use this data in the associated Inspector Panel:</p>
<ul>
<li><b>Name</b>: setDictionary.</li>
<li><b>Parameters</b>: d As Dictionary.</li>
<li><b>Scope</b>: Public.</li>
</ul>
<p>In the associated Code Editor we just need to put this simple line of code:</p>
<pre>linkedWords = d</pre>
<h2>Registering (and deregistering) Observers</h2>
<p>As we have said, our class will be able to <b>react</b> to the active words, so we need to provide a couple of methods to register and deregister <b>Observers</b> at any time. Use the following information for the first method:</p>
<ul>
<li><b>Method Name</b>: registerObserver.</li>
<li><b>Parameters</b>: observer as ActionDelegate.</li>
<li><b>Scope</b>: Public.</li>
</ul>
<p>And write this line of code in the associated Code Editor:</p>
<pre>if observer &lt;&gt; nil then observers.Append observer</pre>
<p>Add a second method that will be in charge of deregistering an observer. Use the following data for that:</p>
<ul>
<li><b>Method Name</b>: deleteObserver.</li>
<li><b>Parameters</b>: observer As LinkDetectorTextArea.ActionDelegate.</li>
<li><b>Scope</b>: Public.</li>
</ul>
<p>This is the code that will search and remove the received <code>ActionDelegate</code> from the instance Array of observers:</p>
<pre>If observer &lt;&gt; Nil Then
  observers.Remove( observers.IndexOf( observer ) )
end</pre>
<h2>Regular Expressions to the rescue… and Styling!</h2>
<p>Finally we just need to add a couple of methods more for finishing our TextArea subclass. These will be <b>Private</b> for the class, auxiliary methods called mainly from the Event Handlers. The first one will be the method in charge of doing the matching from the received candidate, and if it finds it, then it will return the corresponding pair of data to the caller. For this one, add a new Method using the following data:</p>
<ul>
<li><b>Method Name</b>: wordInDictionary.</li>
<li><b>Parameters</b>: word As string, leftposition As integer, RightPosition As integer.</li>
<li><b>Return Type</b>: Pair.</li>
<li><b>Scope</b>: Private.</li>
</ul>
<p>And write the following code in the associated Code Editor:</p>
<pre>Dim theRightPosition As Integer
dim re as new RegEx
re.SearchPattern = "[a-zA-Z]+"
dim rm as RegExMatch
rm = re.Search(word)
if rm &lt;&gt; nil then
  dim foundword as string = rm.SubExpressionString(0)
  dim characterPosition as integer = word.LeftB(rm.SubExpressionStartB(0)).Len
  word = foundword
  leftposition = leftposition + characterPosition
  therightposition = leftposition + word.Len
end if
if linkedWords.HasKey(word) then Return new pair(word,leftposition.ToText+"-"+ theRightPosition.ToText)
dim blu as integer
dim dictionaryKeys() as variant = linkedWords.Keys
for each thekey as string in dictionaryKeys
  if thekey.InStr(word) &gt; 0 then
    blu = me.text.instr(thekey)
    if leftposition &gt;= blu and leftposition &lt;= (blu + thekey.Len) then
      dim finalposition as integer =blu+thekey.len
      Return new pair(thekey,blu.ToText+"-"+finalposition.totext)
    end if
  end
next
Return nil</pre>
<h3>Adding some style</h3>
<p>Finally, we need a way to inform the user that the word down the pointer cursor is active so she can click on it. For this example I&#8217;ve used the Underline text style, but you can adapt it once you see how this is done. So, let&#8217;s add the last method for the class using the following data:</p>
<ul>
<li><b>Method Name</b>: setStyleForWord</li>
<li><b>Parameters</b>: word As pair, mode As Boolean.</li>
<li><b>Scope</b>: Private.</li>
</ul>
<p>We use the <code>mode</code> parameter so we can use the method to apply or delete the style to the received word; removing for example the style from the previous detected active word and styiling the new one. Write the following code in the associated Code Editor:</p>
<pre>Dim cStart, cEnd, sStart As Double
cStart = NthField(word.Right,"-",1).Val
cEnd = NthField(word.Right,"-",2).Val - cStart
Dim t As StyledText
t = Me.StyledText
cstart = If( cstart - 1 &lt; 0, 0, cstart-1)
t.Underline(cstart, cEnd) = mode</pre>
<h2>Putting it all together!</h2>
<p>With our subclass finished it is time to do some Window Layout so we can test the subclass functionality. Select the <code>Window1</code> item in the Navigator to access the Window Layout Editor and drag the <code>LinkDetectorTextArea</code> subclass from the Navigator onto the Window Layout in order to create a new instance. Use the Inspector to change its name to <code>myURLField</code>. Then add three <b>Label</b> controls from the Library and an HTMLViewer. The HTMLViewer will show the URL associated with the active word, while one of the three Label controls will show the detected active words. Both the URL loading as the displaying of the detected word will be done by a previously registered observer (a window method). The Finished window layout should be something like this:</p>
<p><img decoding="async" class="size-full wp-image-4608 aligncenter" src="https://blog.xojo.com/wp-content/uploads/2018/07/TextAreaLinkLayout.png" alt="" width="1732" height="1420" /></p>
<p>Where the text under the &#8220;Sample Text&#8221; label is the one assigned to the <code>Text</code> property of the <code>LinkDetectorTextArea</code> instance.</p>
<p>Let&#8217;s add now to the <code>Window1</code> object the method that will act as an observer, so select the <code>Add &gt; Method</code> option in combination with the following data:</p>
<ul>
<li><b>Name</b>: updateContents</li>
<li><b>Parameters</b>: p as pair</li>
<li><b>Scope</b>: Public</li>
</ul>
<p>And put the following code in the associated Code Editor:</p>
<pre>linkedControl.Text = p.Left
HTMLViewer1.LoadURL p.Right</pre>
<p>Lastly, add now the <code>Open</code> event to the <code>Window1</code> window, adding the following code to the associated Code Editor. Here is where we will initialize our <code>LinkDetectorTextArea</code> instance providing the clickable words and their associated URL:</p>
<pre>myURLField.setDictionary New Dictionary("Xojo":"http://www.xojo.com","Web":"https://www.w3.org","iOS":"http://www.apple.com/ios/",_"OS X":"http://www.apple.com/osx/", "Windows":"https://www.microsoft.com/en-US/windows","Linux":"https://es.wikipedia.org/wiki/GNU/Linux",_
"Raspberry Pi":"https://www.raspberrypi.org/","AprendeXojo":"http://www.aprendexojo.com")
myURLField.registerObserver WeakAddressOf updateContents</pre>
<p>Run the project, move the pointer cursor over the text and you will see the how the detected active words are underlined on the fly; and if you click on any of them, then the Window will display the detected word, loading the associated URL.</p>
<p>Of course, this can be vastly improved…and that is something that maybe you can do in any number of ways!</p>
<p><em>Javier Rodri­guez has been the Xojo Spanish Evangelist since 2008, he’s also a Developer, Consultant and Trainer who has be using Xojo since 1998. He manages <a href="http://www.aprendexojo.com">AprendeXojo.com</a> and is the developer behind the GuancheMOS plug-in for Xojo Developers, Markdown Parser for Xojo, HTMLColorizer for Xojo and the Snippery app, among others</em></p>
<p>*<a href="https://www.aprendexojo.com/2016/04/linkdetectortextarea-subclase-y-patron-observer/">Read this post in Spanish</a></p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
