Multiuser Clipboard
In this multiuser clipboard, all connected users can set the contents of a shared text field, as might be required when passing snippets of text between computers on a local network or the Internet.
Note: this example focuses on the Reactor API, and therefore shows only the bare minimum code required to create a clipboard user interface.
The multiuser clipboard example demonstrates the following Reactor techniques:
- Creating a room
- Joining a room
- Setting room attributes
- Responding to room attribute updates
- Automatic reconnection
- Displaying a user count
- Displaying a "Connecting..." message
- Displaying connection status
The Code
Here's the code for the multiuser clipboard:
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 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | package { import flash.display.Sprite; import flash.events.FocusEvent; import flash.events.MouseEvent; import flash.system.System; import flash.text.TextField; import flash.text.TextFieldType; import flash.text.TextFormat; import flash.text.TextFormatAlign; import flash.utils.setTimeout; import net.user1.reactor.AttributeEvent; import net.user1.reactor.ConnectionEvent; import net.user1.reactor.ConnectionManagerEvent; import net.user1.reactor.Reactor; import net.user1.reactor.ReactorEvent; import net.user1.reactor.Room; import net.user1.reactor.RoomEvent; import net.user1.reactor.RoomSettings; public class UnionPasteBoard extends Sprite { //============================================================================== // VARIABLES //============================================================================== // Constants public static const ROOM_NAME:String = "examples.pasteboard"; public static const PASTEBOARD_ATTRIBUTE:String = "pasteboard"; // Reactor variables protected var reactor:Reactor; protected var room:Room; // UI variables protected var pasteBoardLabel:TextField; protected var numViewers:TextField; protected var pasteBoardButton:Sprite; protected var pasteBoard:TextField; protected var inputLabel:TextField; protected var input:TextField; protected var status:TextField; protected var updateButton:Sprite; //============================================================================== // CONSTRUCTOR //============================================================================== public function UnionPasteBoard () { // Create user interface createUI(); // Create Reactor reactor = new Reactor(); // Automatically reconnect every 10 seconds if the connection is lost reactor.getConnectionMonitor().setAutoReconnectFrequency(10000); // Register for Reactor events reactor.addEventListener(ReactorEvent.READY, readyListener); reactor.addEventListener(ReactorEvent.CLOSE, closeListener); // Register for ConnectionManager events reactor.getConnectionManager().addEventListener( ConnectionEvent.BEGIN_CONNECT, beginConnectListener); // Connect to Union reactor.connect("tryunion.com", 80); } //============================================================================== // REACTOR LISTENERS //============================================================================== // Triggered when the connection is ready protected function readyListener (e:ReactorEvent):void { // Define settings for the application room var settings:RoomSettings = new RoomSettings(); settings.removeOnEmpty = false; // Make the room permanent // Create the application room room = reactor.getRoomManager().createRoom(UnionPasteBoard.ROOM_NAME, settings); // Register room event listeners room.addEventListener(RoomEvent.SYNCHRONIZE, roomSynchronizeListener); room.addEventListener(AttributeEvent.UPDATE, updateRoomAttributeListener); room.addEventListener(RoomEvent.OCCUPANT_COUNT, occupantCountListener); room.addEventListener(RoomEvent.REMOVED, roomRemovedListener); // Join the room room.join(); // Update the on-screen connection status message displayStatus("Connected to Union"); } // Triggered when the connection is lost or fails protected function closeListener (e:ReactorEvent):void { displayStatus("Disconnected"); } //============================================================================== // CONNECTION MANAGER EVENT LISTENERS //============================================================================== // Triggered when a connection attempt begins protected function beginConnectListener (e:ConnectionManagerEvent):void { displayStatus("Connecting..."); } //============================================================================== // ROOM EVENT LISTENERS //============================================================================== // Triggered when one of the room's attributes changes protected function updateRoomAttributeListener (e:AttributeEvent):void { if (e.getChangedAttr().name == UnionPasteBoard.PASTEBOARD_ATTRIBUTE) { displayPasteBoard(e.getChangedAttr().value); } } // Triggered when the client-side state of the room has been updated // to match the server-side state of the room. protected function roomSynchronizeListener (e:RoomEvent):void { var pasteBoardContent:String = room.getAttribute(UnionPasteBoard.PASTEBOARD_ATTRIBUTE); if (pasteBoardContent != null) { displayPasteBoard(pasteBoardContent); } displayStatus("Ready"); } // Triggered when the number of clients in the room changes protected function occupantCountListener (e:RoomEvent):void { if (e.getNumClients() < 2) { displayNumViewers("(No other viewers)"); } else { displayNumViewers("(" + (e.getNumClients()-1) + " other" + (e.getNumClients() > 2 ? "s" : "") + " now viewing)"); } } // Triggered when the room is removed protected function roomRemovedListener (e:RoomEvent):void { // If the room is removed while this client is still connected // (say, by an admin), then just disconnect and wait for // the autoreconnect. The room will be recreated at the next connection. if (reactor.isReady()) { reactor.disconnect(); } } //============================================================================== // UI CONTROL //============================================================================== public function displayPasteBoard (message:String):void { pasteBoard.text = message; stage.focus = pasteBoard; pasteBoard.setSelection(0, pasteBoard.length); } public function displayStatus (message:String):void { status.text = message; } public function displayNumViewers (message:String):void { numViewers.text = message; } //============================================================================== // UI LISTENERS //============================================================================== protected function updateClickListener (e:MouseEvent):void { if (reactor.isReady() && reactor.self().isInRoom(UnionPasteBoard.ROOM_NAME) && input.length > 0) { room.setAttribute(UnionPasteBoard.PASTEBOARD_ATTRIBUTE, input.text); } } protected function copyClickListener (e:MouseEvent):void { // Only copy if there's text in the paste board. if (pasteBoard.length > 0) { System.setClipboard(pasteBoard.text); } } protected function inputFocusListener (e:FocusEvent):void { // Run this 50ms later, otherwise Flash won't select the text setTimeout(input.setSelection, 50, 0, input.length); } protected function pasteBoardClickListener (e:MouseEvent):void { pasteBoard.setSelection(0, pasteBoard.length); } //============================================================================== // UI CREATION //============================================================================== protected function createUI ():void { pasteBoardLabel = createOutputField("Paste Board", 10, 10, 100, 20, 0xFFFFFF, false, true); numViewers = createOutputField("", 90, 10, 200, 20, 0xFFFFFF, false, true); pasteBoardButton = createButton("Copy to Clipboard", copyClickListener, 240, 310, 150, 20); pasteBoard = createOutputField("", 10, 35, 380, 270, 0, true, false); pasteBoard.addEventListener(MouseEvent.CLICK, pasteBoardClickListener); inputLabel = createOutputField("Enter text here", 10, 345, 150, 20, 0xFFFFFF, false, true); input = createInputField("", 10, 365, 380, 200); input.addEventListener(FocusEvent.FOCUS_IN, inputFocusListener); status = createOutputField("", 10, 570, 200, 20, 0xFFFFFF, false, true); updateButton = createButton("Update Paste Board", updateClickListener, 240, 570, 150, 20); addChild(pasteBoardLabel); addChild(numViewers); addChild(pasteBoardButton); addChild(pasteBoard); addChild(inputLabel); addChild(input); addChild(status); addChild(updateButton); } protected function createInputField (text:String, x:int, y:int, width:int, height:int):TextField { var t:TextField = new TextField(); t.text = text; t.type = TextFieldType.INPUT; t.width = width; t.height = height; t.x = x; t.y = y; t.background = true; t.border = true; t.multiline = true; return t; } protected function createOutputField (text:String, x:int, y:int, width:int, height:int, color:uint, border:Boolean, formatAsLabel:Boolean):TextField { var t:TextField = new TextField(); var format:TextFormat; t.width = width; t.height = height; t.x = x; t.y = y; t.background = border; t.border = border; if (formatAsLabel) { format = new TextFormat(); format.bold = true; format.font = "_sans"; format.color = color; t.defaultTextFormat = format; t.selectable = false; } t.text = text; return t; } protected function createButton (label:String, clickListener:Function, x:int, y:int, width:int, height:int):Sprite { var t:TextField = new TextField(); t.text = label; t.width = width; t.height = height; t.background = true; t.border = true; t.selectable = false; var format:TextFormat = new TextFormat(); format.bold = true; format.align = TextFormatAlign.CENTER; format.font = "_sans"; t.setTextFormat(format); var s:Sprite = new Sprite; s.x = x; s.y = y; s.buttonMode = true; s.mouseChildren = false; s.addEventListener(MouseEvent.CLICK, clickListener); s.addChild(t); return s; } } } |