|
15 | 15 | import java.util.function.BiFunction; |
16 | 16 | import java.util.function.Consumer; |
17 | 17 | import java.util.function.Function; |
18 | | -import java.util.function.IntConsumer; |
19 | 18 | import java.util.function.IntFunction; |
20 | 19 | import java.util.function.IntSupplier; |
21 | 20 | import java.util.function.IntUnaryOperator; |
|
47 | 46 | import javafx.scene.Node; |
48 | 47 | import javafx.scene.control.ContextMenu; |
49 | 48 | import javafx.scene.control.IndexRange; |
| 49 | +import javafx.scene.input.MouseEvent; |
50 | 50 | import javafx.scene.layout.Background; |
51 | 51 | import javafx.scene.layout.BackgroundFill; |
52 | 52 | import javafx.scene.layout.CornerRadii; |
|
159 | 159 | * |
160 | 160 | * <h3>Overriding default mouse behavior</h3> |
161 | 161 | * |
162 | | - * The area's default mouse behavior cannot be partially overridden without it affecting other behavior (to do so, |
163 | | - * one would need to re-implement the entire default behavior with one minor adjustment). Rather, one should |
164 | | - * override the default mouse behavior by changing what happens at various events. |
165 | | - * For example, {@link #getOnSelectionDrop()} overrides what happens when some portion of the area's content is |
166 | | - * selected, then the mouse is pressed on that selection, the mouse moves to a new location, and the mouse is released. |
167 | | - * At that point, {@link #onSelectionDrop} is used to determine what should happen. |
| 162 | + * The area's default mouse behavior properly handles auto-scrolling and dragging the selected text to a new location. |
| 163 | + * As such, some parts cannot be partially overridden without it affecting other behavior. |
| 164 | + * |
| 165 | + * <p>The following lists either {@link org.fxmisc.wellbehaved.event.EventPattern}s that cannot be overridden without |
| 166 | + * negatively affecting the default mouse behavior or describe how to safely override things in a special way without |
| 167 | + * disrupting the auto scroll behavior.</p> |
| 168 | + * <ul> |
| 169 | + * <li> |
| 170 | + * <em>First (1 click count) Primary Button Mouse Pressed Events:</em> |
| 171 | + * (<code>EventPattern.mousePressed(MouseButton.PRIMARY).onlyIf(e -> e.getClickCount() == 1)</code>). |
| 172 | + * Do not override. Instead, use {@link #onOutsideSelectionMousePress}, |
| 173 | + * {@link #onInsideSelectionMousePressRelease}, or see next item. |
| 174 | + * </li> |
| 175 | + * <li>( |
| 176 | + * <em>All Other Mouse Pressed Events (e.g., Primary with 2+ click count):</em> |
| 177 | + * Aside from hiding the context menu if it is showing (use {@link #hideContextMenu()} some((where in your |
| 178 | + * overriding InputMap to maintain this behavior), these can be safely overridden via any of the |
| 179 | + * {@link org.fxmisc.wellbehaved.event.template.InputMapTemplate InputMapTemplate's factory methods} or |
| 180 | + * {@link org.fxmisc.wellbehaved.event.InputMap InputMap's factory methods}. |
| 181 | + * </li> |
| 182 | + * <li> |
| 183 | + * <em>Primary-Button-only Mouse Drag Detection Events:</em> |
| 184 | + * (<code>EventPattern.eventType(MouseEvent.DRAG_DETECTED).onlyIf(e -> e.getButton() == MouseButton.PRIMARY && !e.isMiddleButtonDown() && !e.isSecondaryButtonDown())</code>). |
| 185 | + * Do not override. Instead, use {@link #onNewSelectionDrag} or {@link #onSelectionDrag}. |
| 186 | + * </li> |
| 187 | + * <li> |
| 188 | + * <em>Primary-Button-only Mouse Drag Events:</em> |
| 189 | + * (<code>EventPattern.mouseDragged().onlyIf(e -> e.getButton() == MouseButton.PRIMARY && !e.isMiddleButtonDown() && !e.isSecondaryButtonDown())</code>) |
| 190 | + * Do not override, but see next item. |
| 191 | + * </li> |
| 192 | + * <li> |
| 193 | + * <em>All Other Mouse Drag Events:</em> |
| 194 | + * You may safely override other Mouse Drag Events using different |
| 195 | + * {@link org.fxmisc.wellbehaved.event.EventPattern}s without affecting default behavior only if |
| 196 | + * process InputMaps ( |
| 197 | + * {@link org.fxmisc.wellbehaved.event.template.InputMapTemplate#process(javafx.event.EventType, BiFunction)}, |
| 198 | + * {@link org.fxmisc.wellbehaved.event.template.InputMapTemplate#process(org.fxmisc.wellbehaved.event.EventPattern, BiFunction)}, |
| 199 | + * {@link org.fxmisc.wellbehaved.event.InputMap#process(javafx.event.EventType, Function)}, or |
| 200 | + * {@link org.fxmisc.wellbehaved.event.InputMap#process(org.fxmisc.wellbehaved.event.EventPattern, Function)} |
| 201 | + * ) are used and {@link org.fxmisc.wellbehaved.event.InputHandler.Result#PROCEED} is returned. |
| 202 | + * The area has a "catch all" Mouse Drag InputMap that will auto scroll towards the mouse drag event when it |
| 203 | + * occurs outside the bounds of the area and will stop auto scrolling when the mouse event occurs within the |
| 204 | + * area. However, this only works if the event is not consumed before the event reaches that InputMap. |
| 205 | + * To insure the auto scroll feature is enabled, set {@link #isAutoScrollOnDragDesired()} to true in your |
| 206 | + * process InputMap. If the feature is not desired for that specific drag event, set it to false in the |
| 207 | + * process InputMap. |
| 208 | + * <em>Note: Due to this "catch-all" nature, all Mouse Drag Events are consumed.</em> |
| 209 | + * </li> |
| 210 | + * <li> |
| 211 | + * <em>Primary-Button-only Mouse Released Events:</em> |
| 212 | + * (<code>EventPattern.mouseReleased().onlyIf(e -> e.getButton() == MouseButton.PRIMARY && !e.isMiddleButtonDown() && !e.isSecondaryButtonDown())</code>). |
| 213 | + * Do not override. Instead, use {@link #onNewSelectionDragEnd}, {@link #onSelectionDrop}, or see next item. |
| 214 | + * </li> |
| 215 | + * <li> |
| 216 | + * <em>All other Mouse Released Events:</em> |
| 217 | + * You may override other Mouse Released Events using different |
| 218 | + * {@link org.fxmisc.wellbehaved.event.EventPattern}s without affecting default behavior only if |
| 219 | + * process InputMaps ( |
| 220 | + * {@link org.fxmisc.wellbehaved.event.template.InputMapTemplate#process(javafx.event.EventType, BiFunction)}, |
| 221 | + * {@link org.fxmisc.wellbehaved.event.template.InputMapTemplate#process(org.fxmisc.wellbehaved.event.EventPattern, BiFunction)}, |
| 222 | + * {@link org.fxmisc.wellbehaved.event.InputMap#process(javafx.event.EventType, Function)}, or |
| 223 | + * {@link org.fxmisc.wellbehaved.event.InputMap#process(org.fxmisc.wellbehaved.event.EventPattern, Function)} |
| 224 | + * ) are used and {@link org.fxmisc.wellbehaved.event.InputHandler.Result#PROCEED} is returned. |
| 225 | + * The area has a "catch-all" InputMap that will consume all mouse released events and stop auto scroll if it |
| 226 | + * was scrolling. However, this only works if the event is not consumed before the event reaches that InputMap. |
| 227 | + * <em>Note: Due to this "catch-all" nature, all Mouse Released Events are consumed.</em> |
| 228 | + * </li> |
| 229 | + * </ul> |
| 230 | + * |
168 | 231 | * |
169 | 232 | * @param <PS> type of style that can be applied to paragraphs (e.g. {@link TextFlow}. |
170 | 233 | * @param <SEG> type of segment used in {@link Paragraph}. Can be only text (plain or styled) or |
@@ -266,9 +329,51 @@ private static int clamp(int min, int val, int max) { |
266 | 329 | @Override public Duration getMouseOverTextDelay() { return mouseOverTextDelay.get(); } |
267 | 330 | @Override public ObjectProperty<Duration> mouseOverTextDelayProperty() { return mouseOverTextDelay; } |
268 | 331 |
|
269 | | - private final Property<IntConsumer> onSelectionDrop = new SimpleObjectProperty<>(this::moveSelectedText); |
270 | | - @Override public final void setOnSelectionDrop(IntConsumer consumer) { onSelectionDrop.setValue(consumer); } |
271 | | - @Override public final IntConsumer getOnSelectionDrop() { return onSelectionDrop.getValue(); } |
| 332 | + private final BooleanProperty autoScrollOnDragDesired = new SimpleBooleanProperty(true); |
| 333 | + public final void setAutoScrollOnDragDesired(boolean val) { autoScrollOnDragDesired.set(val); } |
| 334 | + public final boolean isAutoScrollOnDragDesired() { return autoScrollOnDragDesired.get(); } |
| 335 | + |
| 336 | + private final Property<Consumer<MouseEvent>> onOutsideSelectionMousePress = new SimpleObjectProperty<>(e -> { |
| 337 | + CharacterHit hit = hit(e.getX(), e.getY()); |
| 338 | + moveTo(hit.getInsertionIndex(), SelectionPolicy.CLEAR); |
| 339 | + }); |
| 340 | + public final void setOnOutsideSelectionMousePress(Consumer<MouseEvent> consumer) { onOutsideSelectionMousePress.setValue(consumer); } |
| 341 | + public final Consumer<MouseEvent> getOnOutsideSelectionMousePress() { return onOutsideSelectionMousePress.getValue(); } |
| 342 | + |
| 343 | + private final Property<Consumer<MouseEvent>> onInsideSelectionMousePressRelease = new SimpleObjectProperty<>(e -> { |
| 344 | + CharacterHit hit = hit(e.getX(), e.getY()); |
| 345 | + moveTo(hit.getInsertionIndex(), SelectionPolicy.CLEAR); |
| 346 | + }); |
| 347 | + public final void setOnInsideSelectionMousePressRelease(Consumer<MouseEvent> consumer) { onInsideSelectionMousePressRelease.setValue(consumer); } |
| 348 | + public final Consumer<MouseEvent> getOnInsideSelectionMousePressRelease() { return onInsideSelectionMousePressRelease.getValue(); } |
| 349 | + |
| 350 | + private final Property<Consumer<Point2D>> onNewSelectionDrag = new SimpleObjectProperty<>(p -> { |
| 351 | + CharacterHit hit = hit(p.getX(), p.getY()); |
| 352 | + moveTo(hit.getInsertionIndex(), SelectionPolicy.ADJUST); |
| 353 | + }); |
| 354 | + public final void setOnNewSelectionDrag(Consumer<Point2D> consumer) { onNewSelectionDrag.setValue(consumer); } |
| 355 | + public final Consumer<Point2D> getOnNewSelectionDrag() { return onNewSelectionDrag.getValue(); } |
| 356 | + |
| 357 | + private final Property<Consumer<MouseEvent>> onNewSelectionDragEnd = new SimpleObjectProperty<>(e -> { |
| 358 | + CharacterHit hit = hit(e.getX(), e.getY()); |
| 359 | + moveTo(hit.getInsertionIndex(), SelectionPolicy.ADJUST); |
| 360 | + }); |
| 361 | + public final void setOnNewSelectionDragEnd(Consumer<MouseEvent> consumer) { onNewSelectionDragEnd.setValue(consumer); } |
| 362 | + public final Consumer<MouseEvent> getOnNewSelectionDragEnd() { return onNewSelectionDragEnd.getValue(); } |
| 363 | + |
| 364 | + private final Property<Consumer<Point2D>> onSelectionDrag = new SimpleObjectProperty<>(p -> { |
| 365 | + CharacterHit hit = hit(p.getX(), p.getY()); |
| 366 | + displaceCaret(hit.getInsertionIndex()); |
| 367 | + }); |
| 368 | + public final void setOnSelectionDrag(Consumer<Point2D> consumer) { onSelectionDrag.setValue(consumer); } |
| 369 | + public final Consumer<Point2D> getOnSelectionDrag() { return onSelectionDrag.getValue(); } |
| 370 | + |
| 371 | + private final Property<Consumer<MouseEvent>> onSelectionDrop = new SimpleObjectProperty<>(e -> { |
| 372 | + CharacterHit hit = hit(e.getX(), e.getY()); |
| 373 | + moveSelectedText(hit.getInsertionIndex()); |
| 374 | + }); |
| 375 | + @Override public final void setOnSelectionDrop(Consumer<MouseEvent> consumer) { onSelectionDrop.setValue(consumer); } |
| 376 | + @Override public final Consumer<MouseEvent> getOnSelectionDrop() { return onSelectionDrop.getValue(); } |
272 | 377 |
|
273 | 378 | private final ObjectProperty<IntFunction<? extends Node>> paragraphGraphicFactory = new SimpleObjectProperty<>(null); |
274 | 379 | @Override |
|
0 commit comments