Skip to content

Commit

Permalink
Add property to area to detect overwrite mode (#1224) (#1226)
Browse files Browse the repository at this point in the history
* Add property to area to detect overwrite mode (#1224)
  • Loading branch information
PavelTurk committed May 16, 2024
1 parent 733a7f7 commit b3e4b04
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 31 deletions.
60 changes: 34 additions & 26 deletions richtextfx/src/main/java/org/fxmisc/richtext/GenericStyledArea.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
Expand Down Expand Up @@ -342,6 +343,13 @@ protected void invalidated() {
@Override public void setEditable(boolean value) { editable.set(value); }
@Override public boolean isEditable() { return editable.get(); }

private final ReadOnlyBooleanProperty overwriteMode;
/**
* Indicates weather the area is in overwrite or insert mode.
*/
public final ReadOnlyBooleanProperty overwriteModeProperty() { return overwriteMode; }
public boolean isOverwriteMode() { return overwriteMode.get(); }

// wrapText property
private final BooleanProperty wrapText = new SimpleBooleanProperty(this, "wrapText");
@Override public final BooleanProperty wrapTextProperty() { return wrapText; }
Expand Down Expand Up @@ -377,7 +385,7 @@ public void setLocale( Locale editorLocale ) {

private final ObjectProperty<IntFunction<? extends Node>> paragraphGraphicFactory = new SimpleObjectProperty<>(null);
@Override public ObjectProperty<IntFunction<? extends Node>> paragraphGraphicFactoryProperty() { return paragraphGraphicFactory; }

public void recreateParagraphGraphic( int parNdx ) {
ObjectProperty<IntFunction<? extends Node>> gProp;
gProp = getCell(parNdx).graphicFactoryProperty();
Expand All @@ -399,7 +407,7 @@ public Node getParagraphGraphic( int parNdx ) {
public final ObjectProperty<Node> placeholderProperty() { return placeHolderProp; }
public final Node getPlaceholder() { return placeHolderProp.get(); }
private Pos placeHolderPos = Pos.CENTER;

private ObjectProperty<ContextMenu> contextMenu = new SimpleObjectProperty<>(null);
@Override public final ObjectProperty<ContextMenu> contextMenuObjectProperty() { return contextMenu; }
// Don't remove as FXMLLoader doesn't recognise default methods !
Expand Down Expand Up @@ -816,9 +824,9 @@ public GenericStyledArea(
: EventStreams.never())
.subscribe(evt -> Event.fireEvent(this, evt));

new GenericStyledAreaBehavior(this);
this.overwriteMode = new GenericStyledAreaBehavior(this).overwriteModeProperty();

// Setup place holder visibility & placement
// Setup place holder visibility & placement
final Val<Boolean> showPlaceholder = Val.create
(
() -> getLength() == 0 && ! isFocused(),
Expand All @@ -827,11 +835,11 @@ public GenericStyledArea(

placeHolderProp.addListener( (ob,ov,newNode) -> displayPlaceHolder( showPlaceholder.getValue(), newNode ) );
showPlaceholder.addListener( (ob,ov,show) -> displayPlaceHolder( show, getPlaceholder() ) );

if ( Platform.isFxApplicationThread() ) initInputMethodHandling();
else Platform.runLater( () -> initInputMethodHandling() );
}

private void initInputMethodHandling()
{
if( Platform.isSupported( ConditionalFeature.INPUT_METHOD ) )
Expand Down Expand Up @@ -963,7 +971,7 @@ public final Optional<Integer> allParToVisibleParIndex(int allParIndex) {
List<Cell<Paragraph<PS, SEG, S>, ParagraphBox<PS, SEG, S>>> visibleList = virtualFlow.visibleCells();
int firstVisibleParIndex = visibleList.get( 0 ).getNode().getIndex();
int targetIndex = allParIndex - firstVisibleParIndex;

if ( allParIndex >= firstVisibleParIndex && targetIndex < visibleList.size() )
{
if ( visibleList.get( targetIndex ).getNode().getIndex() == allParIndex )
Expand Down Expand Up @@ -1054,7 +1062,7 @@ public Optional<Bounds> getCharacterBoundsOnScreen(int from, int to) {

if ( cursorBounds != null && ! cursorBounds.isEmpty() )
{
Bounds emptyCharBounds = new BoundingBox(
Bounds emptyCharBounds = new BoundingBox(
cursorBounds.getMinX()+1, cursorBounds.getMinY()+1,
cursorBounds.getWidth()-1, cursorBounds.getHeight()-2
);
Expand Down Expand Up @@ -1316,11 +1324,11 @@ public int getCurrentLineStartInParargraph() {
public int getCurrentLineEndInParargraph() {
return virtualFlow.getCell(getCurrentParagraph()).getNode().getCurrentLineEndPosition(caretSelectionBind.getUnderlyingCaret());
}

private double caretPrevY = -1;
private LineSelection<PS, SEG, S> lineHighlighter;
private ObjectProperty<Paint> lineHighlighterFill;
private ObjectProperty<Paint> lineHighlighterFill;

/**
* The default fill is "highlighter" yellow. It can also be styled using CSS with:<br>
* <code>.styled-text-area .line-highlighter { -fx-fill: lime; }</code><br>
Expand All @@ -1334,18 +1342,18 @@ public void setLineHighlighterFill( Paint highlight )
else {
boolean lineHighlightOn = isLineHighlighterOn();
if ( lineHighlightOn ) setLineHighlighterOn( false );

if ( highlight == null ) lineHighlighterFill = null;
else lineHighlighterFill = new SimpleObjectProperty( highlight );

if ( lineHighlightOn ) setLineHighlighterOn( true );
}
}

public boolean isLineHighlighterOn() {
return lineHighlighter != null && selectionSet.contains( lineHighlighter ) ;
}

/**
* Highlights the line that the main caret is on.<br>
* Line highlighting automatically follows the caret.
Expand All @@ -1355,18 +1363,18 @@ public void setLineHighlighterOn( boolean show )
if ( show )
{
if ( lineHighlighter != null ) return;

lineHighlighter = new LineSelection<>( this, lineHighlighterFill );
Consumer<Bounds> caretListener = b ->

Consumer<Bounds> caretListener = b ->
{
if ( lineHighlighter != null && (b.getMinY() != caretPrevY || getCaretColumn() == 1) ) {
if ( getSelection().getLength() != 0 ) lineHighlighter.deselect();
if ( getSelection().getLength() != 0 ) lineHighlighter.deselect();
else lineHighlighter.selectCurrentLine();
caretPrevY = b.getMinY();
}
};

caretBoundsProperty().addListener( (ob,ov,nv) -> nv.ifPresent( caretListener ) );
getCaretBounds().ifPresent( caretListener );
selectionProperty().addListener( (ob,ov,nv) ->
Expand Down Expand Up @@ -1394,7 +1402,7 @@ else if ( lineHighlighter != null ) {
public void nextLine(SelectionPolicy selectionPolicy) {
scrollLine( +1, selectionPolicy );
}

/**
* Scrolls the text one line DOWN while maintaining the caret's
* position on screen, so that it is now on the PREVIOUS line.
Expand Down Expand Up @@ -1431,7 +1439,7 @@ public void nextPage(SelectionPolicy selectionPolicy) {
}
else page( +1, selectionPolicy );
}

/**
* @param pgCount the number of pages to page up/down.
* <br>Negative numbers for paging up and positive for down.
Expand All @@ -1448,17 +1456,17 @@ private void scrollText(double deltaY, SelectionPolicy selectionPolicy)
{
// Use underlying caret to get the same behaviour as navigating up/down a line where the x position is sticky
Optional<Bounds> cb = caretSelectionBind.getUnderlyingCaret().getCaretBounds();

paging = true; // Prevent scroll from reverting back to the current caret position
suspendVisibleParsWhile( () -> virtualFlow.scrollYBy( deltaY ) );

cb.map( this::screenToLocal ) // Place caret near the same on screen position as before
.map( b -> hit( b.getMinX(), b.getMinY()+b.getHeight()/2.0 ).getInsertionIndex() )
.ifPresent( i -> caretSelectionBind.moveTo( i, selectionPolicy ) );

// Adjust scroll by a few pixels to get the caret at the exact on screen location as before
// Adjust scroll by a few pixels to get the caret at the exact on screen location as before
cb.ifPresent( prev -> getCaretBounds().map( newB -> newB.getMinY() - prev.getMinY() )
.filter( delta -> delta != 0.0 ).ifPresent( delta -> scrollYBy( delta ) ) );
.filter( delta -> delta != 0.0 ).ifPresent( delta -> scrollYBy( delta ) ) );
}

@Override
Expand Down Expand Up @@ -2030,7 +2038,7 @@ private void followCaret() {
Bounds region = extendLeft(caretBounds, graphicWidth);
double scrollX = virtualFlow.getEstimatedScrollX();

// Ordinarily when a caret ends a selection in the target paragraph and scrolling left is required to follow
// Ordinarily when a caret ends a selection in the target paragraph and scrolling left is required to follow
// the caret then the selection won't be visible. So here we check for this scenario and adjust if needed.
if ( ! isWrapText() && scrollX > 0.0 && getParagraphSelection( parIdx ).getLength() > 0 )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import java.util.function.Predicate;

import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.event.Event;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
Expand Down Expand Up @@ -53,7 +55,7 @@ class GenericStyledAreaBehavior {

/*
* KeyCodes are misinterpreted when using a different keyboard layout, for example:
* on Dvorak: C results in KeyCode I, X -> B, and V -> .
* on Dvorak: C results in KeyCode I, X -> B, and V -> .
* and on German layouts: Z and Y are reportedly switched
* so then editing commands such as Ctrl+C, or CMD+Z are incorrectly processed.
* KeyCharacterCombination however does keyboard translation before matching.
Expand Down Expand Up @@ -180,7 +182,7 @@ class GenericStyledAreaBehavior {
//Note that this is how several IDEs such JetBrains IDEs or Eclipse behave.
if (e.isControlDown() && e.isAltDown() && !e.isMetaDown() && e.getCharacter().length() == 1
&& e.getCharacter().getBytes()[0] != 0) return true;

return !e.isControlDown() && !e.isAltDown() && !e.isMetaDown();
}
return !e.isControlDown() && !e.isMetaDown();
Expand Down Expand Up @@ -303,7 +305,8 @@ private enum DragState {
/**
* Indicates weather the area is in overwrite or insert mode.
*/
private boolean overwriteMode = false;
private final ReadOnlyBooleanWrapper overwriteMode = new ReadOnlyBooleanWrapper(this, "overwriteMode", false);
final ReadOnlyBooleanProperty overwriteModeProperty() { return overwriteMode.getReadOnlyProperty(); }

/**
* Indicates whether an existing selection is being dragged by the user.
Expand Down Expand Up @@ -363,15 +366,15 @@ private void keyTyped(KeyEvent event) {
int start = range.getStart();
int end = range.getEnd();

if (overwriteMode && start == end) {
if (overwriteMode.get() && start == end) {
end = Math.min(end+1, view.getLength());
}

view.replaceText(start, end, text);
}

private void toggelOverwriteMode(KeyEvent ignore) {
overwriteMode = !overwriteMode;
overwriteMode.set(!overwriteMode.get());
}

private void deleteBackward(KeyEvent ignore) {
Expand Down

0 comments on commit b3e4b04

Please sign in to comment.