1 /* 2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.scene.control.behavior; 27 28 import com.sun.javafx.PlatformUtil; 29 import com.sun.javafx.geom.transform.Affine3D; 30 import com.sun.javafx.scene.control.skin.TextAreaSkin; 31 import com.sun.javafx.scene.text.HitInfo; 32 import javafx.beans.value.ChangeListener; 33 import javafx.beans.value.ObservableValue; 34 import javafx.geometry.Bounds; 35 import javafx.geometry.Point2D; 36 import javafx.geometry.Rectangle2D; 37 import javafx.scene.Scene; 38 import javafx.scene.control.ContextMenu; 39 import javafx.scene.control.IndexRange; 40 import javafx.scene.control.TextArea; 41 import javafx.scene.input.ContextMenuEvent; 42 import javafx.scene.input.MouseButton; 43 import javafx.scene.input.MouseEvent; 44 import javafx.stage.Screen; 45 import javafx.stage.Window; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 50 import static com.sun.javafx.PlatformUtil.isMac; 51 import static com.sun.javafx.PlatformUtil.isWindows; 52 import static javafx.scene.input.KeyCode.*; 53 import static javafx.scene.input.KeyEvent.KEY_PRESSED; 54 55 56 /** 57 * Text area behavior. 58 */ 59 public class TextAreaBehavior extends TextInputControlBehavior<TextArea> { 60 /************************************************************************** 61 * Setup KeyBindings * 62 *************************************************************************/ 63 protected static final List<KeyBinding> TEXT_AREA_BINDINGS = new ArrayList<KeyBinding>(); 64 static { 65 TEXT_AREA_BINDINGS.add(new KeyBinding(HOME, KEY_PRESSED, "LineStart")); // changed 66 TEXT_AREA_BINDINGS.add(new KeyBinding(END, KEY_PRESSED, "LineEnd")); // changed 67 TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "PreviousLine")); // changed 68 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "PreviousLine")); // changed 69 TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "NextLine")); // changed 70 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "NextLine")); // changed 71 TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_UP, KEY_PRESSED, "PreviousPage")); // new 72 TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_DOWN, KEY_PRESSED, "NextPage")); // new 73 TEXT_AREA_BINDINGS.add(new KeyBinding(ENTER, KEY_PRESSED, "InsertNewLine")); // changed 74 TEXT_AREA_BINDINGS.add(new KeyBinding(TAB, KEY_PRESSED, "InsertTab")); // changed 75 76 TEXT_AREA_BINDINGS.add(new KeyBinding(HOME, KEY_PRESSED, "SelectLineStart").shift()); // changed 77 TEXT_AREA_BINDINGS.add(new KeyBinding(END, KEY_PRESSED, "SelectLineEnd").shift()); // changed 78 TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectPreviousLine").shift()); // changed 79 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectPreviousLine").shift()); // changed 80 TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectNextLine").shift()); // changed 81 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectNextLine").shift()); // changed 82 TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_UP, KEY_PRESSED, "SelectPreviousPage").shift()); // new 83 TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_DOWN, KEY_PRESSED, "SelectNextPage").shift()); // new 84 // Platform specific settings 85 if (isMac()) { 86 TEXT_AREA_BINDINGS.add(new KeyBinding(LEFT, KEY_PRESSED, "LineStart").shortcut()); // changed 87 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_LEFT, KEY_PRESSED, "LineStart").shortcut()); // changed 88 TEXT_AREA_BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED, "LineEnd").shortcut()); // changed 89 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED, "LineEnd").shortcut()); // changed 90 TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "Home").shortcut()); 91 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "Home").shortcut()); 92 TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "End").shortcut()); 93 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "End").shortcut()); 94 95 TEXT_AREA_BINDINGS.add(new KeyBinding(LEFT, KEY_PRESSED, "SelectLineStartExtend").shift().shortcut()); // changed 96 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_LEFT, KEY_PRESSED, "SelectLineStartExtend").shift().shortcut()); // changed 97 TEXT_AREA_BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED, "SelectLineEndExtend").shift().shortcut()); // changed 98 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED, "SelectLineEndExtend").shift().shortcut()); // changed 99 TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectHomeExtend").shortcut().shift()); 100 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectHomeExtend").shortcut().shift()); 101 TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectEndExtend").shortcut().shift()); 102 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectEndExtend").shortcut().shift()); 103 104 TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "ParagraphStart").alt()); 105 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "ParagraphStart").alt()); 106 TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "ParagraphEnd").alt()); 107 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "ParagraphEnd").alt()); 108 109 TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectParagraphStart").alt().shift()); 110 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectParagraphStart").alt().shift()); 111 TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectParagraphEnd").alt().shift()); 112 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectParagraphEnd").alt().shift()); 113 } else { 114 TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "ParagraphStart").ctrl()); 115 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "ParagraphStart").ctrl()); 116 TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "ParagraphEnd").ctrl()); 117 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "ParagraphEnd").ctrl()); 118 TEXT_AREA_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "SelectParagraphStart").ctrl().shift()); 119 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_UP, KEY_PRESSED, "SelectParagraphStart").ctrl().shift()); 120 TEXT_AREA_BINDINGS.add(new KeyBinding(DOWN, KEY_PRESSED, "SelectParagraphEnd").ctrl().shift()); 121 TEXT_AREA_BINDINGS.add(new KeyBinding(KP_DOWN, KEY_PRESSED, "SelectParagraphEnd").ctrl().shift()); 122 } 123 // Add the other standard key bindings in 124 TEXT_AREA_BINDINGS.addAll(TextInputControlBindings.BINDINGS); 125 // However, we want to consume other key press / release events too, for 126 // things that would have been handled by the InputCharacter normally 127 TEXT_AREA_BINDINGS.add(new KeyBinding(null, KEY_PRESSED, "Consume")); 128 } 129 130 private TextAreaSkin skin; 131 private ContextMenu contextMenu; 132 private TwoLevelFocusBehavior tlFocus; 133 134 /************************************************************************** 135 * Constructors * 136 *************************************************************************/ 137 138 public TextAreaBehavior(final TextArea textArea) { 139 super(textArea, TEXT_AREA_BINDINGS); 140 141 contextMenu = new ContextMenu(); 142 if (IS_TOUCH_SUPPORTED) { 143 contextMenu.getStyleClass().add("text-input-context-menu"); 144 } 145 146 // Register for change events 147 textArea.focusedProperty().addListener(new ChangeListener<Boolean>() { 148 @Override 149 public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 150 // NOTE: The code in this method is *almost* and exact copy of what is in TextFieldBehavior. 151 // The only real difference is that TextFieldBehavior selects all the text when the control 152 // receives focus (when not gained by mouse click), whereas TextArea doesn't, and also the 153 // TextArea doesn't lose selection on focus lost, whereas the TextField does. 154 final TextArea textArea = getControl(); 155 if (textArea.isFocused()) { 156 if (PlatformUtil.isIOS()) { 157 // Special handling of focus on iOS is required to allow to 158 // control native keyboard, because native keyboard is popped-up only when native 159 // text component gets focus. When we have JFX keyboard we can remove this code 160 final Bounds bounds = textArea.getBoundsInParent(); 161 double w = bounds.getWidth(); 162 double h = bounds.getHeight(); 163 Affine3D trans = TextFieldBehavior.calculateNodeToSceneTransform(textArea); 164 String text = textArea.textProperty().getValueSafe(); 165 166 // we need to display native text input component on the place where JFX component is drawn 167 // all parameters needed to do that are passed to native impl. here 168 textArea.getScene().getWindow().impl_getPeer().requestInput(text, TextFieldBehavior.TextInputTypes.TEXT_AREA.ordinal(), w, h, 169 trans.getMxx(), trans.getMxy(), trans.getMxz(), trans.getMxt(), 170 trans.getMyx(), trans.getMyy(), trans.getMyz(), trans.getMyt(), 171 trans.getMzx(), trans.getMzy(), trans.getMzz(), trans.getMzt()); 172 } 173 if (!focusGainedByMouseClick) { 174 setCaretAnimating(true); 175 } 176 } else { 177 // skin.hideCaret(); 178 if (PlatformUtil.isIOS() && textArea.getScene() != null) { 179 // releasing the focus => we need to hide the native component and also native keyboard 180 textArea.getScene().getWindow().impl_getPeer().releaseInput(); 181 } 182 focusGainedByMouseClick = false; 183 setCaretAnimating(false); 184 } 185 } 186 }); 187 188 // Only add this if we're on an embedded platform that supports 5-button navigation 189 if (com.sun.javafx.scene.control.skin.Utils.isTwoLevelFocus()) { 190 tlFocus = new TwoLevelFocusBehavior(textArea); // needs to be last. 191 } 192 } 193 194 @Override public void dispose() { 195 if (tlFocus != null) tlFocus.dispose(); 196 super.dispose(); 197 } 198 199 // An unholy back-reference! 200 public void setTextAreaSkin(TextAreaSkin skin) { 201 this.skin = skin; 202 } 203 204 /************************************************************************** 205 * Key handling implementation * 206 *************************************************************************/ 207 208 @Override public void callAction(String name) { 209 final TextArea textInputControl = getControl(); 210 211 boolean done = false; 212 213 if (textInputControl.isEditable()) { 214 // fnCaretAnim(false); 215 // setCaretOpacity(1.0); 216 setEditing(true); 217 done = true; 218 if ("InsertNewLine".equals(name)) insertNewLine(); 219 else if ("InsertTab".equals(name)) insertTab(); 220 else { 221 done = false; 222 } 223 setEditing(false); 224 } 225 226 if (!done) { 227 done = true; 228 if ("LineStart".equals(name)) lineStart(false, false); 229 else if ("LineEnd".equals(name)) lineEnd(false, false); 230 else if ("SelectLineStart".equals(name)) lineStart(true, false); 231 else if ("SelectLineStartExtend".equals(name)) lineStart(true, true); 232 else if ("SelectLineEnd".equals(name)) lineEnd(true, false); 233 else if ("SelectLineEndExtend".equals(name)) lineEnd(true, true); 234 else if ("PreviousLine".equals(name)) skin.previousLine(false); 235 else if ("NextLine".equals(name)) skin.nextLine(false); 236 else if ("SelectPreviousLine".equals(name)) skin.previousLine(true); 237 else if ("SelectNextLine".equals(name)) skin.nextLine(true); 238 239 else if ("ParagraphStart".equals(name)) skin.paragraphStart(true, false); 240 else if ("ParagraphEnd".equals(name)) skin.paragraphEnd(true, isWindows(), false); 241 else if ("SelectParagraphStart".equals(name)) skin.paragraphStart(true, true); 242 else if ("SelectParagraphEnd".equals(name)) skin.paragraphEnd(true, isWindows(), true); 243 244 else if ("PreviousPage".equals(name)) skin.previousPage(false); 245 else if ("NextPage".equals(name)) skin.nextPage(false); 246 else if ("SelectPreviousPage".equals(name)) skin.previousPage(true); 247 else if ("SelectNextPage".equals(name)) skin.nextPage(true); 248 else { 249 done = false; 250 } 251 } 252 // fnCaretAnim(true); 253 254 if (!done) { 255 super.callAction(name); 256 } 257 } 258 259 private void insertNewLine() { 260 TextArea textArea = getControl(); 261 textArea.replaceSelection("\n"); 262 } 263 264 private void insertTab() { 265 TextArea textArea = getControl(); 266 textArea.replaceSelection("\t"); 267 } 268 269 @Override protected void deleteChar(boolean previous) { 270 skin.deleteChar(previous); 271 } 272 273 @Override protected void deleteFromLineStart() { 274 TextArea textArea = getControl(); 275 int end = textArea.getCaretPosition(); 276 277 if (end > 0) { 278 lineStart(false, false); 279 int start = textArea.getCaretPosition(); 280 if (end > start) { 281 replaceText(start, end, ""); 282 } 283 } 284 } 285 286 private void lineStart(boolean select, boolean extendSelection) { 287 skin.lineStart(select, extendSelection); 288 } 289 290 private void lineEnd(boolean select, boolean extendSelection) { 291 skin.lineEnd(select, extendSelection); 292 } 293 294 protected void scrollCharacterToVisible(int index) { 295 // TODO this method should be removed when TextAreaSkin 296 // TODO is refactored to no longer need it. 297 skin.scrollCharacterToVisible(index); 298 } 299 300 @Override protected void replaceText(int start, int end, String txt) { 301 getControl().replaceText(start, end, txt); 302 } 303 304 /** 305 * If the focus is gained via response to a mouse click, then we don't 306 * want to select all the text even if selectOnFocus is true. 307 */ 308 private boolean focusGainedByMouseClick = false; // TODO!! 309 private boolean shiftDown = false; 310 private boolean deferClick = false; 311 312 @Override public void mousePressed(MouseEvent e) { 313 TextArea textArea = getControl(); 314 super.mousePressed(e); 315 // We never respond to events if disabled 316 if (!textArea.isDisabled()) { 317 // If the text field doesn't have focus, then we'll attempt to set 318 // the focus and we'll indicate that we gained focus by a mouse 319 // click, TODO which will then NOT honor the selectOnFocus variable 320 // of the textInputControl 321 if (!textArea.isFocused()) { 322 focusGainedByMouseClick = true; 323 textArea.requestFocus(); 324 } 325 326 // stop the caret animation 327 setCaretAnimating(false); 328 // only if there is no selection should we see the caret 329 // setCaretOpacity(if (textInputControl.dot == textInputControl.mark) then 1.0 else 0.0); 330 331 // if the primary button was pressed 332 if (e.getButton() == MouseButton.PRIMARY && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown())) { 333 HitInfo hit = skin.getIndex(e.getX(), e.getY()); 334 int i = com.sun.javafx.scene.control.skin.Utils.getHitInsertionIndex(hit, textArea.textProperty().getValueSafe()); 335 // int i = skin.getInsertionPoint(e.getX(), e.getY()); 336 final int anchor = textArea.getAnchor(); 337 final int caretPosition = textArea.getCaretPosition(); 338 if (e.getClickCount() < 2 && 339 (e.isSynthesized() || 340 (anchor != caretPosition && 341 ((i > anchor && i < caretPosition) || (i < anchor && i > caretPosition))))) { 342 // if there is a selection, then we will NOT handle the 343 // press now, but will defer until the release. If you 344 // select some text and then press down, we change the 345 // caret and wait to allow you to drag the text (TODO). 346 // When the drag concludes, then we handle the click 347 348 deferClick = true; 349 // TODO start a timer such that after some millis we 350 // switch into text dragging mode, change the cursor 351 // to indicate the text can be dragged, etc. 352 } else if (!(e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown() || e.isShortcutDown())) { 353 switch (e.getClickCount()) { 354 case 1: skin.positionCaret(hit, false, false); break; 355 case 2: mouseDoubleClick(hit); break; 356 case 3: mouseTripleClick(hit); break; 357 default: // no-op 358 } 359 } else if (e.isShiftDown() && !(e.isControlDown() || e.isAltDown() || e.isMetaDown() || e.isShortcutDown()) && e.getClickCount() == 1) { 360 // didn't click inside the selection, so select 361 shiftDown = true; 362 // if we are on mac os, then we will accumulate the 363 // selection instead of just moving the dot. This happens 364 // by figuring out past which (dot/mark) are extending the 365 // selection, and set the mark to be the other side and 366 // the dot to be the new position. 367 // everywhere else we just move the dot. 368 if (isMac()) { 369 textArea.extendSelection(i); 370 } else { 371 skin.positionCaret(hit, true, false); 372 } 373 } 374 // skin.setForwardBias(hit.isLeading()); 375 // if (textInputControl.editable) 376 // displaySoftwareKeyboard(true); 377 } 378 if (contextMenu.isShowing()) { 379 contextMenu.hide(); 380 } 381 } 382 } 383 384 @Override public void mouseDragged(MouseEvent e) { 385 final TextArea textArea = getControl(); 386 // we never respond to events if disabled, but we do notify any onXXX 387 // event listeners on the control 388 if (!textArea.isDisabled() && !e.isSynthesized()) { 389 if (e.getButton() == MouseButton.PRIMARY && 390 !(e.isMiddleButtonDown() || e.isSecondaryButtonDown() || 391 e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown())) { 392 skin.positionCaret(skin.getIndex(e.getX(), e.getY()), true, false); 393 } 394 } 395 deferClick = false; 396 } 397 398 @Override public void mouseReleased(final MouseEvent e) { 399 final TextArea textArea = getControl(); 400 super.mouseReleased(e); 401 // we never respond to events if disabled, but we do notify any onXXX 402 // event listeners on the control 403 if (!textArea.isDisabled()) { 404 setCaretAnimating(false); 405 if (deferClick) { 406 deferClick = false; 407 skin.positionCaret(skin.getIndex(e.getX(), e.getY()), shiftDown, false); 408 shiftDown = false; 409 } 410 setCaretAnimating(true); 411 } 412 } 413 414 @Override public void contextMenuRequested(ContextMenuEvent e) { 415 final TextArea textArea = getControl(); 416 417 if (contextMenu.isShowing()) { 418 contextMenu.hide(); 419 } else if (textArea.getContextMenu() == null) { 420 double screenX = e.getScreenX(); 421 double screenY = e.getScreenY(); 422 double sceneX = e.getSceneX(); 423 424 if (IS_TOUCH_SUPPORTED) { 425 Point2D menuPos; 426 if (textArea.getSelection().getLength() == 0) { 427 skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false, false); 428 menuPos = skin.getMenuPosition(); 429 } else { 430 menuPos = skin.getMenuPosition(); 431 if (menuPos != null && (menuPos.getX() <= 0 || menuPos.getY() <= 0)) { 432 skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false, false); 433 menuPos = skin.getMenuPosition(); 434 } 435 } 436 437 if (menuPos != null) { 438 Point2D p = getControl().localToScene(menuPos); 439 Scene scene = getControl().getScene(); 440 Window window = scene.getWindow(); 441 Point2D location = new Point2D(window.getX() + scene.getX() + p.getX(), 442 window.getY() + scene.getY() + p.getY()); 443 screenX = location.getX(); 444 sceneX = p.getX(); 445 screenY = location.getY(); 446 } 447 } 448 449 skin.populateContextMenu(contextMenu); 450 double menuWidth = contextMenu.prefWidth(-1); 451 double menuX = screenX - (IS_TOUCH_SUPPORTED ? (menuWidth / 2) : 0); 452 Screen currentScreen = com.sun.javafx.util.Utils.getScreenForPoint(screenX, 0); 453 Rectangle2D bounds = currentScreen.getBounds(); 454 455 if (menuX < bounds.getMinX()) { 456 getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX); 457 getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX); 458 contextMenu.show(getControl(), bounds.getMinX(), screenY); 459 } else if (screenX + menuWidth > bounds.getMaxX()) { 460 double leftOver = menuWidth - ( bounds.getMaxX() - screenX); 461 getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX); 462 getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX); 463 contextMenu.show(getControl(), screenX - leftOver, screenY); 464 } else { 465 getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", 0); 466 getControl().getProperties().put("CONTEXT_MENU_SCENE_X", 0); 467 contextMenu.show(getControl(), menuX, screenY); 468 } 469 } 470 471 e.consume(); 472 } 473 474 @Override protected void setCaretAnimating(boolean play) { 475 skin.setCaretAnimating(play); 476 } 477 478 protected void mouseDoubleClick(HitInfo hit) { 479 final TextArea textArea = getControl(); 480 textArea.previousWord(); 481 if (isWindows()) { 482 textArea.selectNextWord(); 483 } else { 484 textArea.selectEndOfNextWord(); 485 } 486 } 487 488 protected void mouseTripleClick(HitInfo hit) { 489 // select the line 490 skin.paragraphStart(false, false); 491 skin.paragraphEnd(false, isWindows(), true); 492 } 493 494 // public function mouseWheelMove(e:MouseEvent):Void { 495 // def textBox = bind skin.control as TextBox; 496 // // we never respond to events if disabled, but we do notify any onXXX 497 // // event listeners on the control 498 // if (not textBox.disabled) { 499 // var rot = Math.abs(e.wheelRotation); 500 // while (rot > 0) { 501 // rot--; 502 // scrollText(e.wheelRotation > 0); 503 // } 504 // } 505 // } 506 507 }