Posts Tagged ‘smileys’

On my way to building my own Flex-based chat application, I came to learn that displaying smileys, or emoticons as they’re often known, is one of the most difficult things to achieve in Flash/Flex. I scoured Google once again searching for a ready-made component and found no such thing. I began a tedious endeavor to do it myself and I offer my rough stab at it here, open source and free for all.

So, the method I chose to display the smileys was to use a standard Flex Canvas which receives the smiley images as children and itself contains a child TextArea which receives the text and grows in height depending upon the size of the text. The TextArea thereby lies in back of the smileys. The Canvas’ scrollbar is set to auto and when the TextArea grows past the display height of the Canvas, the scrollbar is then displayed, allowing the text and smileys both to be scrolled simultaneously, an otherwise difficult task that actually came very easily given the method used.

There are many things I’ve overlooked and possibly bugs to be found, but in general, it works rather well. I’ve used an extended CoolRichTextEditor component which allows the controlbar to be arranged above the text area rather than below it. The component was developed by someone else and for the life of me, I can’t seem to find the developer’s site again. If someone finds it, I’d be awfully gracious if they might post a link to the site so that I might give the author proper credit. I also included a set of smileys which I assume to be under a creative commons license since the only reference I could find was on Wikimedia Commons.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
	backgroundColor="#FFFFFF" width="100%" height="100%" creationComplete="init()"
	xmlns:ns1="*" viewSourceURL="srcview/index.html">
 
	<mx:Script>
		<![CDATA[
			import mx.controls.Image;
			import flash.events.KeyboardEvent;
			import mx.core.mx_internal;
			use namespace mx_internal;
			public var smiles:XML;
 
			public function init():void
			{
				//we want the textArea control to grow in height with its content
				textArea.height = textArea.textHeight + 10;
 
				textInput.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
				textInput.textArea.setFocus();
 
				//we'll define smileys here, though these can be read in from xml file
				smiles = <smiles>
					<smile file="angry.gif" code=":{" width="20" height="20" />
					<smile file="smile.gif" code=":)" width="20" height="20" />
					<smile file="smile.gif" code=":-)" width="20" height="20" />
					<smile file="biggrin.gif" code=":D" width="20" height="20" />
					<smile file="ohmy.gif" code=":O" width="20" height="20" />
					<smile file="sad.gif" code=":(" width="20" height="20" />
					<smile file="sad.gif" code=":-(" width="20" height="20" />
					<smile file="tongue.gif" code=":p" width="20" height="20" />
					<smile file="wink.gif" code=";)" width="20" height="20" />
				</smiles>;
 
				//set some initial text
				textInput.textArea.htmlText="Smileys example.&#xa;Available smileys: :{,:),:D,:O,:(,;),:p.";
 
				//run the smileyHandler routine right at the start
				smileyHandler();
			}
 
			private function keyDown(event:KeyboardEvent):void
			{
				// Check to see if ENTER pressed.
				if (event.keyCode == Keyboard.ENTER)
				{
					smileyHandler();
				}
			}
 
			public function smileyHandler():void
			{
				var smileyIndex:int = -1;
				var searchIndex:int = -1;
				var testSearchIndex:int = -1;
				var testString:String;
				var searchString:String = textInput.htmlText;
 
				//recurse through all smileys to see if they occur in searchString
				for (var x:int = 0; x < smiles.child("*").length(); x++)
				{
					//setup testString by escaping various punctuation marks before search
					testString = smiles.smile[x].attribute("code").toString().replace(/(\)|\()/, "\\$&");
					searchIndex = searchString.search(testString);
					if (searchIndex >= 0)
					{
						//occurrence of this smiley found. test for previous occurence
						//and see if it's earlier in the text than this occurence
						if (testSearchIndex < 0)
						{
							//no previous smiley found, this is the first
							testSearchIndex = searchIndex;
							smileyIndex = x;
						}
						else
						{
							//previous smiley found. test if this occurrence is earlier
							//in text than previous occurence
							if (testSearchIndex > searchIndex)
							{
								testSearchIndex = searchIndex;
								smileyIndex = x;
							}
						}
					}
				}
				searchIndex = testSearchIndex;
				var newText:String = "";
				var oldText:String = textArea.htmlText;
				//if no smiley found, add textInput to textArea
				if (searchIndex < 0)
				{
					newText = textInput.htmlText;
				}
 
				//recurse until no smileys found in searchString
				while (searchIndex >= 0)
				{
					//set replacement string as text smiley with background color so that it's semi-invisible
					var smileyReplaceString:String = '<FONT COLOR="#'
						smileyReplaceString += chatCanvas.getStyle("backgroundColor").toString(16);
						smileyReplaceString += '"> ';
						smileyReplaceString += smiles.smile[smileyIndex].attribute("code");
						smileyReplaceString += '  </FONT>';
 
					//slice searchString at index where smiley occurs and put that in textArea for operation
					//notice: +1 is added to searchIndex here to compensate for smiley at index 0
					textArea.htmlText = oldText + newText + searchString.slice(0, searchIndex + 1);
 
					textArea.validateNow();
 
					//get necessary coordinates and display smiley
					var numLines:int = textArea.mx_internal::getTextField().numLines;
					var smiley:Image = new Image();
					smiley.source = "assets/" + smiles.smile[smileyIndex].attribute("file");
					smiley.x = textArea.getLineMetrics(numLines - 1).width;
					smiley.y = textArea.textHeight - textArea.getLineMetrics(numLines - 1).height;
					chatCanvas.addChild(smiley);
 
					newText += searchString.slice(0, searchIndex) + smileyReplaceString;
					var codeLength:int = smiles.smile[smileyIndex].attribute("code").toString().length;
					searchString = searchString.slice(searchIndex + codeLength);
 
					//begin with smiley search again
					searchIndex = -1;
					testSearchIndex = -1;
					smileyIndex = -1;
					for (x = 0; x < smiles.child("*").length(); x++)
					{
						//setup testString by escaping various punctuation marks before search
						testString = smiles.smile[x].attribute("code").toString().replace(/(\)|\()/, "\\$&");
						searchIndex = searchString.search(testString);
						if (searchIndex >= 0)
						{
							//occurrence of this smiley found. test for previous occurence
							//and see if it's earlier in the text than this occurence
							if (testSearchIndex < 0)
							{
								//no previous smiley found, this is the first
								testSearchIndex = searchIndex;
								smileyIndex = x;
							}
							else
							{
								//previous smiley found. test if this occurrence is earlier
								//in text than previous occurence
								if (testSearchIndex > searchIndex)
								{
									testSearchIndex = searchIndex;
									smileyIndex = x;
								}
							}
						}
					}
					searchIndex = testSearchIndex;
					if (searchIndex < 0)
					{
						newText += searchString;
					}
				}
				textArea.htmlText = oldText + newText;
				textArea.validateNow();
 
				//set height of textArea to height of new text content
				textArea.height = textArea.textHeight + 10;
 
				//clear textInput
				textInput.htmlText = "";
 
				//scroll to bottom of canvas
				chatCanvas.validateNow();
				chatCanvas.verticalScrollPosition = chatCanvas.maxVerticalScrollPosition;
			}
 
		]]>
	</mx:Script>
 
	<mx:VBox x="0" y="0" width="100%" height="100%">
		<mx:Canvas id="chatCanvas" width="100%" height="90%" horizontalScrollPolicy="off"
			backgroundColor="#FFFFFF" borderStyle="solid">
			<mx:TextArea width="100%" id="textArea" editable="false"
				fontSize="12" verticalScrollPolicy="off" borderStyle="none">
			</mx:TextArea>
		</mx:Canvas>
		<ns1:CoolRichTextEditor width="100%" height="147" id="textInput" cornerRadius="5" headerHeight="0"
			roundedBottomCorners="true" verticalScrollPolicy="off" fontSize="12"
			 controlBarPosition="top"/>
	</mx:VBox>
 
</mx:Application>

Example: dm_smileys.swf
Flex Project: dm_smileys.zip