JavaFX show ContextMenu below cursor - javafx

I have a TextField where the user can type. I would like to show a ContextMenu below cursor when the user hit Ctrl+Space key combination.
codeArea.setOnKeyPressed(event -> {
if( event.getCode().equals( KeyCode.SPACE ) && event.isControlDown() ) {
int cursorX = ?;
int cursorY = ?;
cm.show(codeArea, x, y);
} else {
cm.hide();
}
});
How I get the cursor current position? I must give it's (screen) XY coords to the show() function.
I'd like use it for auto completion.
Thanks.

Add a MouseListener and save the last position in a Variable :)
You might wanna use getScreenX() and getScreenY() not sure of the difference at the moment.
codeArea.setOnMouseMoved(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent event) {
this.cursorX = event.getX();
this.cursorY = event.getY();
}
});

Related

Selecting an entire line from TextArea on mouse click

I am using JavaFX for my application. In my application I have a button,on click on that button should display the results on textarea, that I am able to do. Now I would like to select the entire line on click from the text area. But The code which I have written is able to select only the value which I click which means like only word on which I have clicked. Please suggest me to modify this.
#FXML
public void find_btn_action(ActionEvent event) throws MWException
{
double[] peaks= {1.2,5.6,8.0,9.0};
StringBuilder sb = new StringBuilder(peaks.length);
for(int i= 0; i < peaks.length ; i++)
{
result[i] = peaks[i];
sb.append(result[i]+"\n");
}
auto.setText(sb.toString());
auto.setOnMouseClicked(new EventHandler<Event>()
{
#Override
public void handle(Event arg0)
{
selectedVal = auto.getSelectedText();
System.out.println("selected text:"+ selectedVal);
}
});
}
In your mouse click listener check, if the click was inside the content area and if this is the case, use the caret position to determine the next/previous line break in the TextArea's text and select this range:
textArea.setOnMouseClicked(evt -> {
if (evt.getButton() == MouseButton.PRIMARY) {
// check, if click was inside the content area
Node n = evt.getPickResult().getIntersectedNode();
while (n != textArea) {
if (n.getStyleClass().contains("content")) {
// find previous/next line break
int caretPosition = textArea.getCaretPosition();
String text = textArea.getText();
int lineBreak1 = text.lastIndexOf('\n', caretPosition - 1);
int lineBreak2 = text.indexOf('\n', caretPosition);
if (lineBreak2 < 0) {
// if no more line breaks are found, select to end of text
lineBreak2 = text.length();
}
textArea.selectRange(lineBreak1, lineBreak2);
evt.consume();
break;
}
n = n.getParent();
}
}
});

Wait until any button is pressed?

I am writing a TicTacToe game in JavaFX. I've decided to make a board as 9 (3x3) buttons with changing text: "" (if empty) or "X" or "O". Everything is going ok beside one thing... I got stuck here:
public void game() {
while(keepPlaying) {
if(computerTurn) {;
computerMove();
}else {
while(waitForUser) {
//wait until any of 9 buttons is pushed!
}
}
if (checkResult()) {
keepPlaying = false;
}
computerTurn = !computerTurn;
}
}
How to wait for user pushing any of those 9 buttons and then continue with computer turn??
I need something like waiting for scanner input in console application, but this input must be one of 9 buttons...
I know that there are few "possible duplicates", but in fact those problems were solved using methods I can't use here, for example timer. Correct me if I am wrong.
Blocking the application thread in JavaFX should not be done since it freezes the UI. For this reason a loop like this is not well suited for a JavaFX application. Instead you should react to user input:
public void game() {
if (keepPlaying && computerTurn) {
computerMove();
if (checkResult()) {
keepPlaying = false;
}
computerTurn = false;
}
}
// button event handler
private void button(ActionEvent event) {
if (keepPlaying) {
Button source = (Button) event.getSource();
// probably the following 2 coordinates are computed from GridPane indices
int x = getX(source);
int y = getY(source);
// TODO: update state according to button pressed
if (checkResult()) {
keepPlaying = false;
} else {
computerMove();
if (checkResult()) {
keepPlaying = false;
}
}
}
}
Starting with javafx 9 there is a public API for "pausing" on the application thread however:
private static class GridCoordinates {
int x,y;
GridCoordinates (int x, int y) {
this.x = x;
this.y = y;
}
}
private final Object loopKey = new Object();
public void game() {
while(keepPlaying) {
if(computerTurn) {
computerMove();
} else {
// wait for call of Platform.exitNestedEventLoop​(loopKey, *)
GridCoordinates coord = (GridCoordinates) Platform.enterNestedEventLoop​(loopKey);
// TODO: update state
}
if (checkResult()) {
keepPlaying = false;
}
computerTurn = !computerTurn;
}
}
private void button(ActionEvent event) {
if (keepPlaying) {
Button source = (Button) event.getSource();
// probably the following 2 coordinates are computed from GridPane indices
int x = getX(source);
int y = getY(source);
Platform.exitNestedEventLoop​(loopKey, new GridCoordinates(x, y));
}
}

JavaFX Spinner change is slow with click and hold of mouse button

The speed of Spinner update is slow when I click and hold the up/down arrow buttons. Is there a way to increase the change speed?
When I click, click, click with the mouse, the spinner values change as fast as I click. It also changes fast if I use the up/down arrows on the keyboard for each key press or if I hold down the up/down arrow keys. I want the values to change that fast when I click and hold on the arrow buttons.
Anyone know a way to do that?
The SpinnerBehavior of the SpinnerSkin triggers updates every 750 ms. Unfortunately there is no way to simply set/modify this behavour without using reflection to access private members. Therefore the only way to do this without reflection is using event filters to trigger the updates at a faster rate:
private static final PseudoClass PRESSED = PseudoClass.getPseudoClass("pressed");
#Override
public void start(Stage primaryStage) {
Spinner<Integer> spinner = new Spinner(Integer.MIN_VALUE, Integer.MAX_VALUE, 0);
class IncrementHandler implements EventHandler<MouseEvent> {
private Spinner spinner;
private boolean increment;
private long startTimestamp;
private static final long DELAY = 1000l * 1000L * 750L; // 0.75 sec
private Node button;
private final AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
if (now - startTimestamp >= DELAY) {
// trigger updates every frame once the initial delay is over
if (increment) {
spinner.increment();
} else {
spinner.decrement();
}
}
}
};
#Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.PRIMARY) {
Spinner source = (Spinner) event.getSource();
Node node = event.getPickResult().getIntersectedNode();
Boolean increment = null;
// find which kind of button was pressed and if one was pressed
while (increment == null && node != source) {
if (node.getStyleClass().contains("increment-arrow-button")) {
increment = Boolean.TRUE;
} else if (node.getStyleClass().contains("decrement-arrow-button")) {
increment = Boolean.FALSE;
} else {
node = node.getParent();
}
}
if (increment != null) {
event.consume();
source.requestFocus();
spinner = source;
this.increment = increment;
// timestamp to calculate the delay
startTimestamp = System.nanoTime();
button = node;
// update for css styling
node.pseudoClassStateChanged(PRESSED, true);
// first value update
timer.handle(startTimestamp + DELAY);
// trigger timer for more updates later
timer.start();
}
}
}
public void stop() {
timer.stop();
button.pseudoClassStateChanged(PRESSED, false);
button = null;
spinner = null;
}
}
IncrementHandler handler = new IncrementHandler();
spinner.addEventFilter(MouseEvent.MOUSE_PRESSED, handler);
spinner.addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> {
if (evt.getButton() == MouseButton.PRIMARY) {
handler.stop();
}
});
Scene scene = new Scene(spinner);
primaryStage.setScene(scene);
primaryStage.show();
}
I modified the answer of fabian a little bit to decrease the speed of the spinner while holding mouse down:
private int currentFrame = 0;
private int previousFrame = 0;
#Override
public void handle(long now)
{
if (now - startTimestamp >= initialDelay)
{
// Single or holded mouse click
if (currentFrame == previousFrame || currentFrame % 10 == 0)
{
if (increment)
{
spinner.increment();
}
else
{
spinner.decrement();
}
}
}
++currentFrame;
}
And after stopping the timer we adjust previousFrame again:
public void stop()
{
previousFrame = currentFrame;
[...]
}
A small improvement to Fabian's answer. Making the following mod to the MOUSE_RELEASED addEventerFilter will stop a NullPointerException caused when clicking the textfield associated with the spinner. Cheers Fabian!
spinner.addEventFilter(MouseEvent.MOUSE_RELEASED, evt -> {
Node node = evt.getPickResult().getIntersectedNode();
if (node.getStyleClass().contains("increment-arrow-button") ||
node.getStyleClass().contains("decrement-arrow-button")) {
if (evt.getButton() == MouseButton.PRIMARY) {
handler.stop();
}
}
});
An alternative to changing the update speed might in some cases be adjusting the amount by which the value increments/decrements per update.
SpinnerValueFactory.IntegerSpinnerValueFactory intFactory =
(SpinnerValueFactory.IntegerSpinnerValueFactory) spinner.getValueFactory();
intFactory.setAmountToStepBy(100);
Reference: http://news.kynosarges.org/2016/10/28/javafx-spinner-for-numbers/

javafx Minesweeper: how to tell between right and left mouse button input

I am working on a javafx Minesweeper game, and currently am only using left mouse button input. I would like to use the right mouse button also so users can flag possible bombs. I looked at the Oracle webpage for Button class, and it says:
"When a button is pressed and released a ActionEvent is sent. Your application can perform some action based on this event by implementing an EventHandler to process the ActionEvent. Buttons can also respond to mouse events by implementing an EventHandler to process the MouseEvent."
https://docs.oracle.com/javafx/2/api/javafx/scene/control/Button.html
Ive tried this several different ways, with no success.
Included is my current EventHandler code. If anyone can explain the best way to handle right/left mouse clicks, or point me in the right direction of where to find that information, it is greatly appreciated.
MineButton is a custom class that extends Button. I would like on right click to mark as "m" and change cell color, and left click would remain the same.
for (int row = 0; row < 8; row++){
for (int col = 0; col <8; col++){
MineButton button = new MineButton(row, col);
button.setPrefSize(100, 100);
button.setText("?");
button.setStyle("-fx-font: 22 arial; -fx-base:#dcdcdc;");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if ( button.isAMine() == true){
button.setText("B");
for( int row1 = 0; row1 < 8; row1++){
for ( int col1 = 0; col1 < 8; col1++){
if (mineButtons[row1][col1].isAMine() == true){
mineButtons[row1][col1].setText("B");
mineButtons[row1][col1].setStyle("-fx- font: 22 arial; -fx-base: #dc143c;");
}
}
}
}
else{
recursion(mineButtons, button.getX(), button.getY());
}
}
});
You cannot handle right clicks on a button by attaching an event handler on action event. Instead you need to add a handler to the mouse event:
button.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if(mouseEvent.getButton().equals(MouseButton.SECONDARY)){
System.out.println("Set flag on the button");
}
}
});
If u wanna handle the MouseEvent use this code. It will work.
button.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if(event.getButton() == MouseButton.SECONDARY){
// Type code to set flag here
}
}
});
scene.setOnMousePressed(event ->{
if(event.getButton() == MouseButton.PRIMARY) {
anchorX = event.getSceneX();
anchorY = event.getSceneY();
anchorAngleX = angleX.get();
anchorAngleY = angleY.get();
}
});
scene.setOnMouseDragged((MouseEvent event) -> {
if(event.getButton() == MouseButton.PRIMARY) {
angleX.set(anchorAngleX - (anchorY - event.getSceneY()));
angleY.set(anchorAngleY - (anchorX - event.getSceneX()));
}
});
This code rotates a JavaFX group in a scene with a left mouse button and a drag. You can print the event.getButton to make certain your primary and secondary are working. I have tried many other ways including JavaFXtras. This is by far the simplest way to handle the primary and secondary mouse click events.

processing: button press switch state

So I have a ON-OFF button that draws a circle. The trouble I am encountering is that the ON OFF states are random depending on how long I press the button. I guess this is due to the draw() function which also loops my button function in time with framerate. What I want is for the button to turn on when pressed once and turn off when pressed again irrespective of how long the button is pressed. Here is the code.
else if (circle4.pressed()) {
println("button 4 is pressed");
if(drawCirclesPrimary){
drawCirclesPrimary = false;
}
else{
drawCirclesPrimary = true;
}
println("drawCirclesPrimary"+drawCirclesPrimary);
}
I would suggest looking at the Buttons tutorial on processing.org. The following code is a subset of what is contained in the tutorial (you will need to review all the code in the tutorial, however). Comments are mine.
void setup() {
// Create instances of your button(s)
}
void draw() {
// Draw buttons, update cursor position, check if buttons have been clicked.
}
// Provides the overRect() method (among others).
class Button
{
// If the cursor is placed within the footprint of the button, return true.
boolean overRect(int x, int y, int width, int height)
{
if (mouseX >= x && mouseX <= x+width && mouseY >= y && mouseY <= y+height) {
return true;
}
else {
return false;
}
}
}
class RectButton extends Button
{
// Create a rectangle button with these size/color attributes.
RectButton(int ix, int iy, int isize, color icolor, color ihighlight)
{
x = ix;
y = iy;
size = isize;
basecolor = icolor;
highlightcolor = ihighlight;
currentcolor = basecolor;
}
// Determines whether the cursor is over the button.
boolean over()
{
if( overRect(x, y, size, size) ) {
over = true;
return true;
}
else {
over = false;
return false;
}
}
// Draws the rectangle button into your sketch.
void display()
{
stroke(255);
fill(currentcolor);
rect(x, y, size, size);
}
}
This thread has some example code for drawing an object only while a key is pressed. That's pretty similar to what you want.
Instead of keyPressed and keyReleased, you can use mouseClicked. which is called once after a mouse button has been pressed and then released. Use a boolean variable to store the on/off state. Inside of mouseClicked, toggle the value of that boolean variable.

Resources