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, "TraverseOrInsertTab")); // 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 ("TraverseOrInsertTab".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 if ("TraverseOrInsertTab".equals(name)) { 249 // RT-40312: Non-editabe mode means traverse instead of insert. 250 name = "TraverseNext"; 251 done = false; 252 } else { 253 done = false; 254 } 255 } 256 // fnCaretAnim(true); 257 258 if (!done) { 259 super.callAction(name); 260 } 261 } 262 263 private void insertNewLine() { 264 TextArea textArea = getControl(); 265 textArea.replaceSelection("\n"); 266 } 267 268 private void insertTab() { 269 TextArea textArea = getControl(); 270 textArea.replaceSelection("\t"); 271 } 272 273 @Override protected void deleteChar(boolean previous) { 274 skin.deleteChar(previous); 275 } 276 277 @Override protected void deleteFromLineStart() { 278 TextArea textArea = getControl(); 279 int end = textArea.getCaretPosition(); 280 281 if (end > 0) { 282 lineStart(false, false); 283 int start = textArea.getCaretPosition(); 284 if (end > start) { 285 replaceText(start, end, ""); 286 } 287 } 288 } 289 290 private void lineStart(boolean select, boolean extendSelection) { 291 skin.lineStart(select, extendSelection); 292 } 293 294 private void lineEnd(boolean select, boolean extendSelection) { 295 skin.lineEnd(select, extendSelection); 296 } 297 298 protected void scrollCharacterToVisible(int index) { 299 // TODO this method should be removed when TextAreaSkin 300 // TODO is refactored to no longer need it. 301 skin.scrollCharacterToVisible(index); 302 } 303 304 @Override protected void replaceText(int start, int end, String txt) { 305 getControl().replaceText(start, end, txt); 306 } 307 308 /** 309 * If the focus is gained via response to a mouse click, then we don't 310 * want to select all the text even if selectOnFocus is true. 311 */ 312 private boolean focusGainedByMouseClick = false; // TODO!! 313 private boolean shiftDown = false; 314 private boolean deferClick = false; 315 316 @Override public void mousePressed(MouseEvent e) { 317 TextArea textArea = getControl(); 318 super.mousePressed(e); 319 // We never respond to events if disabled 320 if (!textArea.isDisabled()) { 321 // If the text field doesn't have focus, then we'll attempt to set 322 // the focus and we'll indicate that we gained focus by a mouse 323 // click, TODO which will then NOT honor the selectOnFocus variable 324 // of the textInputControl 325 if (!textArea.isFocused()) { 326 focusGainedByMouseClick = true; 327 textArea.requestFocus(); 328 } 329 330 // stop the caret animation 331 setCaretAnimating(false); 332 // only if there is no selection should we see the caret 333 // setCaretOpacity(if (textInputControl.dot == textInputControl.mark) then 1.0 else 0.0); 334 335 // if the primary button was pressed 336 if (e.getButton() == MouseButton.PRIMARY && !(e.isMiddleButtonDown() || e.isSecondaryButtonDown())) { 337 HitInfo hit = skin.getIndex(e.getX(), e.getY()); 338 int i = com.sun.javafx.scene.control.skin.Utils.getHitInsertionIndex(hit, textArea.textProperty().getValueSafe()); 339 // int i = skin.getInsertionPoint(e.getX(), e.getY()); 340 final int anchor = textArea.getAnchor(); 341 final int caretPosition = textArea.getCaretPosition(); 342 if (e.getClickCount() < 2 && 343 (e.isSynthesized() || 344 (anchor != caretPosition && 345 ((i > anchor && i < caretPosition) || (i < anchor && i > caretPosition))))) { 346 // if there is a selection, then we will NOT handle the 347 // press now, but will defer until the release. If you 348 // select some text and then press down, we change the 349 // caret and wait to allow you to drag the text (TODO). 350 // When the drag concludes, then we handle the click 351 352 deferClick = true; 353 // TODO start a timer such that after some millis we 354 // switch into text dragging mode, change the cursor 355 // to indicate the text can be dragged, etc. 356 } else if (!(e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown() || e.isShortcutDown())) { 357 switch (e.getClickCount()) { 358 case 1: skin.positionCaret(hit, false, false); break; 359 case 2: mouseDoubleClick(hit); break; 360 case 3: mouseTripleClick(hit); break; 361 default: // no-op 362 } 363 } else if (e.isShiftDown() && !(e.isControlDown() || e.isAltDown() || e.isMetaDown() || e.isShortcutDown()) && e.getClickCount() == 1) { 364 // didn't click inside the selection, so select 365 shiftDown = true; 366 // if we are on mac os, then we will accumulate the 367 // selection instead of just moving the dot. This happens 368 // by figuring out past which (dot/mark) are extending the 369 // selection, and set the mark to be the other side and 370 // the dot to be the new position. 371 // everywhere else we just move the dot. 372 if (isMac()) { 373 textArea.extendSelection(i); 374 } else { 375 skin.positionCaret(hit, true, false); 376 } 377 } 378 // skin.setForwardBias(hit.isLeading()); 379 // if (textInputControl.editable) 380 // displaySoftwareKeyboard(true); 381 } 382 if (contextMenu.isShowing()) { 383 contextMenu.hide(); 384 } 385 } 386 } 387 388 @Override public void mouseDragged(MouseEvent e) { 389 final TextArea textArea = getControl(); 390 // we never respond to events if disabled, but we do notify any onXXX 391 // event listeners on the control 392 if (!textArea.isDisabled() && !e.isSynthesized()) { 393 if (e.getButton() == MouseButton.PRIMARY && 394 !(e.isMiddleButtonDown() || e.isSecondaryButtonDown() || 395 e.isControlDown() || e.isAltDown() || e.isShiftDown() || e.isMetaDown())) { 396 skin.positionCaret(skin.getIndex(e.getX(), e.getY()), true, false); 397 } 398 } 399 deferClick = false; 400 } 401 402 @Override public void mouseReleased(final MouseEvent e) { 403 final TextArea textArea = getControl(); 404 super.mouseReleased(e); 405 // we never respond to events if disabled, but we do notify any onXXX 406 // event listeners on the control 407 if (!textArea.isDisabled()) { 408 setCaretAnimating(false); 409 if (deferClick) { 410 deferClick = false; 411 skin.positionCaret(skin.getIndex(e.getX(), e.getY()), shiftDown, false); 412 shiftDown = false; 413 } 414 setCaretAnimating(true); 415 } 416 } 417 418 @Override public void contextMenuRequested(ContextMenuEvent e) { 419 final TextArea textArea = getControl(); 420 421 if (contextMenu.isShowing()) { 422 contextMenu.hide(); 423 } else if (textArea.getContextMenu() == null) { 424 double screenX = e.getScreenX(); 425 double screenY = e.getScreenY(); 426 double sceneX = e.getSceneX(); 427 428 if (IS_TOUCH_SUPPORTED) { 429 Point2D menuPos; 430 if (textArea.getSelection().getLength() == 0) { 431 skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false, false); 432 menuPos = skin.getMenuPosition(); 433 } else { 434 menuPos = skin.getMenuPosition(); 435 if (menuPos != null && (menuPos.getX() <= 0 || menuPos.getY() <= 0)) { 436 skin.positionCaret(skin.getIndex(e.getX(), e.getY()), false, false); 437 menuPos = skin.getMenuPosition(); 438 } 439 } 440 441 if (menuPos != null) { 442 Point2D p = getControl().localToScene(menuPos); 443 Scene scene = getControl().getScene(); 444 Window window = scene.getWindow(); 445 Point2D location = new Point2D(window.getX() + scene.getX() + p.getX(), 446 window.getY() + scene.getY() + p.getY()); 447 screenX = location.getX(); 448 sceneX = p.getX(); 449 screenY = location.getY(); 450 } 451 } 452 453 skin.populateContextMenu(contextMenu); 454 double menuWidth = contextMenu.prefWidth(-1); 455 double menuX = screenX - (IS_TOUCH_SUPPORTED ? (menuWidth / 2) : 0); 456 Screen currentScreen = com.sun.javafx.util.Utils.getScreenForPoint(screenX, 0); 457 Rectangle2D bounds = currentScreen.getBounds(); 458 459 if (menuX < bounds.getMinX()) { 460 getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX); 461 getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX); 462 contextMenu.show(getControl(), bounds.getMinX(), screenY); 463 } else if (screenX + menuWidth > bounds.getMaxX()) { 464 double leftOver = menuWidth - ( bounds.getMaxX() - screenX); 465 getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", screenX); 466 getControl().getProperties().put("CONTEXT_MENU_SCENE_X", sceneX); 467 contextMenu.show(getControl(), screenX - leftOver, screenY); 468 } else { 469 getControl().getProperties().put("CONTEXT_MENU_SCREEN_X", 0); 470 getControl().getProperties().put("CONTEXT_MENU_SCENE_X", 0); 471 contextMenu.show(getControl(), menuX, screenY); 472 } 473 } 474 475 e.consume(); 476 } 477 478 @Override protected void setCaretAnimating(boolean play) { 479 skin.setCaretAnimating(play); 480 } 481 482 protected void mouseDoubleClick(HitInfo hit) { 483 final TextArea textArea = getControl(); 484 textArea.previousWord(); 485 if (isWindows()) { 486 textArea.selectNextWord(); 487 } else { 488 textArea.selectEndOfNextWord(); 489 } 490 } 491 492 protected void mouseTripleClick(HitInfo hit) { 493 // select the line 494 skin.paragraphStart(false, false); 495 skin.paragraphEnd(false, isWindows(), true); 496 } 497 498 // public function mouseWheelMove(e:MouseEvent):Void { 499 // def textBox = bind skin.control as TextBox; 500 // // we never respond to events if disabled, but we do notify any onXXX 501 // // event listeners on the control 502 // if (not textBox.disabled) { 503 // var rot = Math.abs(e.wheelRotation); 504 // while (rot > 0) { 505 // rot--; 506 // scrollText(e.wheelRotation > 0); 507 // } 508 // } 509 // } 510 511 }