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

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.

Related

Start Editing a TableCell on single click instead of double click

I would like to begin editing cells on my TableView on a single click rather than a double click.
I tried the following, but it does not work.
tableView.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
TablePosition selectedCellPosition = tableView.getFocusModel().getFocusedCell();
if ( selectedCellPosition != null ) {
tableView.edit(selectedCellPosition.getRow(), selectedCellPosition.getTableColumn());
}
}
});
How can I make it so the table cell begins editing on the first click rather than on a double click?
This worked for me. The Platform.runLater() was necessary for some reason.
tableView.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) -> {
if (newSelection != null) {
int row = tableView.getSelectionModel().getSelectedCells().get(0).getRow();
Platform.runLater(() -> tableView.edit(row, columnToEdit));
}
});

Remove hbox when unselected

I would like to create menu with 3 radio buttons(comm,med,all). Where for example Comm button should create hbox, but when the other option is selected, this hbox should disapear, but it wont.
Could anyone set me to the right direction?
Thank you a lot.
Heres what Ive got:
comm.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
if(comm.isSelected())
root.add(commBox, 0,1);
else if(med.isSelected()||all.isSelected())
root.getChildren().remove(commBox);
}
});
The onAction handler for the radio button is invoked when an action is performed on that button. The radio button will become deselected when one of the other buttons in the same toggle group is selected. So your handler does not get invoked when the button is deselected.
Register a listener with the selectedProperty instead:
comm.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
if (isNowSelected) {
root.add(commBox, 0,1);
} else {
root.getChildren().remove(commBox);
}
});
Alternatively, you could register a listener with the toggle group:
// assuming the following existing code, or its equivalent:
ToggleGroup toggleGroup = new ToggleGroup();
comm.setToggleGroup(toggleGroup);
med.setToggleGroup(toggleGroup);
all.setToggleGroup(toggleGroup);
// then this will work:
toggleGroup.selectedToggleProperty().addListener((obs, oldToggle, newToggle) -> {
if (newToggle == comm) {
root.add(commBox, 0, 1);
} else {
root.getChildren().remove(commBox);
}
// maybe more logic here to handle med or all selected...
});

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 have an eventfilter remove itself

A snippet of my code is as follows:
//button clicked method
#FXML
public void newHeatExchanger2ButtonClicked(ActionEvent event) throws Exception {
//new pane created
Pane pane = new Pane();
//method call everytime the button is clicked
create2DExchanger(pane);
}
//method declaration
private void create2DExchanger(Pane pane) {
EventHandler<MouseEvent> panePressed = (e -> {
if (e.getButton() == MouseButton.SECONDARY){
do stuff
}
if (e.getButton() == MouseButton.PRIMARY){
do stuff
}
});
EventHandler<MouseEvent> paneDragged = (e -> {
if (e.getButton() == MouseButton.PRIMARY){
do stuff
}
});
EventHandler<MouseEvent> paneReleased = (e -> {
if (e.getButton() == MouseButton.PRIMARY){
do stuff;
}
});
EventHandler<MouseEvent> paneMoved = (t -> {
do stuff;
});
EventHandler<MouseEvent> paneClicked = (t -> {
//I need this filter to remove itself right here
t.consume();
pane.removeEventFilter(MouseEvent.MOUSE_MOVED, paneMoved);
pane.addEventHandler(MouseEvent.MOUSE_PRESSED, panePressed);
pane.addEventHandler(MouseEvent.MOUSE_DRAGGED, paneDragged);
pane.addEventHandler(MouseEvent.MOUSE_RELEASED, paneReleased);
});
pane.removeEventHandler(MouseEvent.MOUSE_PRESSED, panePressed);
pane.removeEventHandler(MouseEvent.MOUSE_DRAGGED, paneDragged);
pane.removeEventHandler(MouseEvent.MOUSE_RELEASED, paneReleased);
pane.addEventFilter(MouseEvent.MOUSE_MOVED, paneMoved);
pane.addEventFilter(MouseEvent.MOUSE_PRESSED, paneClicked);
}
Initially I set the pane to have only the event filters of mouse_moved, and mouse_pressed. As soon as the mouse is clicked I need the mouse filter for mouse_pressed and mouse_moved to go away and add the eventHandlers as I do in the paneClicked filter llamda. I need the first set of events to be filters because there are children nodes I do not want to receive the event (i.e. an event filter on an arc that is a child of the pane). The second set need to be handlers because the arc event filter needs to consume the event before the pane eventHandlers receive it.
Convenience events like:
pane.setOnMousePressed()
can remove themselves by calling
pane.setOnMousePressed(null);
but I need this initial event filter to remove itself. I will need the functionality of an event removing itself later as well in the code, but if I try to add
pane.removeEventFilter(MouseEvent.MOUSE_PRESSED, paneClicked);
to
EventHandler<MouseEvent> paneClicked = (t -> {
//I need this filter to remove itself right here
t.consume();
pane.removeEventFilter(MouseEvent.MOUSE_MOVED, paneMoved);
pane.addEventHandler(MouseEvent.MOUSE_PRESSED, panePressed);
pane.addEventHandler(MouseEvent.MOUSE_DRAGGED, paneDragged);
pane.addEventHandler(MouseEvent.MOUSE_RELEASED, paneReleased);
});
It will not compile. I have been researching for a couple of days on how to get the functionality of an eventFilter or eventHandler to remove itself but I am coming up short. I have not found anything online or on stackexchange in my Google searches either. I would really appreciate being able to figure this thing out. Thanks.
I believe the problem stems from the fact that you try to access paneClicked before it was fully declared.
You can overcome this using an anonymous class with the this keyword:
EventHandler<MouseEvent> paneClicked = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
event.consume();
pane.removeEventFilter(MouseEvent.MOUSE_PRESSED, this);
pane.removeEventFilter(MouseEvent.MOUSE_MOVED, paneMoved);
pane.addEventHandler(MouseEvent.MOUSE_PRESSED, panePressed);
pane.addEventHandler(MouseEvent.MOUSE_DRAGGED, paneDragged);
pane.addEventHandler(MouseEvent.MOUSE_RELEASED, paneReleased);
}
};
Or by referencing a fully qualified static function:
public class ContainingClass {
...
private static EventHandler<MouseEvent> paneClicked = (t -> {
t.consume();
pane.removeEventFilter(
MouseEvent.MOUSE_PRESSED, ContainingClass.paneClicked
);
});
}
See also:
Why do lambdas in Java 8 disallow forward reference to member variables where anonymous classes don't?
Java8 Lambdas vs Anonymous classes

graphicsview receives mouse event before the item

I have implemented the panning view on the QGraphicsView, using the mouse move event using
void View::mouseMoveEvent(QMouseEvent* event) {
pan();
QGraphicsView::mouseMoveEvent(event);
}
and in the scene of this view I have added few items where some of the items are resizable, so I implemented
void Item::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(m_resizeMode)
{
resize();
e->accept();
}
}
I tried to filter the mouse move not to propagate to any further using e->accept()
but my View mouseMove event has been called first , so when ever I tried to resize the item, the view started to pan all the way.
How can I avoid this event propagation from view to scene.
You can call the base class implementation of the QGraphicsView and check if the event is accepted. However I would do this in the mousePressEvent instead of the mouseMoveEvent. After all this is where you determine if you should resize an item or do some panning. This is how I do something similar in my project:
void View::mousePressEvent(QMouseEvent *event)
{
...
QGraphicsView::mousePressEvent(event);
if(event->isAccepted())
move = false;
else
move = true;
}
void View::mouseMoveEvent(QMouseEvent *event)
{
if(!move)
{
QGraphicsView::mouseMoveEvent(event);
return;
}
... // you would do the panning here
QGraphicsView::mouseMoveEvent(event);
}
void View::mouseReleaseEvent(QMouseEvent *event)
{
if(!move)
{
QGraphicsView::mouseReleaseEvent(event);
return;
}
else
{
...
move = false;
}
QGraphicsView::mouseReleaseEvent(event);
}
The view will always receive the mouse event first.
So, in the view, check to see if the mouse is over an item before allowing it to pan by getting the mouse pos in scene coordinates and retrieving the items at that position with QGraphicsScene::items( )

Resources