Search code examples
javafxfxml

How do I stop the hiding of a node when it is active?


The code to which I refer: Show / Hide a Node within a stage

Following the previous question and answer on autohiding: How to show / hide / auto hide a node

Thanks to c0der for solving the previous question.

There is a problem with it as if it is active (like moving the cursor or clicking), the Vbox node will still autohide. How do I make the Vbox node stay visible and not hide when I move the cursor or some event is happening?

The current behavior is if I move the cursor or click something on the VBox node, after 5 seconds it will still hide.

Testinggg.java:

package testinggg;

import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Testinggg extends Application {

private TestController controller;
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Test.fxml"));
        Parent root = loader.load();
        controller = loader.getController();
        stage.setScene(new Scene(root));
        stage.setFullScreen(true);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

TestController.java:

package testinggg;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.animation.PauseTransition;
import javafx.animation.TranslateTransition;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.VBox;
import javafx.util.Duration;

public class TestController implements Initializable {

    @FXML private VBox statusContainer;
    private TranslateTransition showStatus;
    private TranslateTransition hideStatus;
    private boolean showsStatus = false;
    private static final int AUTO_HIDE_DELAY = 5;  

    public void toggleStatus() {
        if( showsStatus ) {
            hide();
        }
        else {
            show();
        }
    }
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        showStatus = new TranslateTransition(Duration.millis(250), statusContainer);
        showStatus.setByY(-1080.0);
        showStatus.setOnFinished(event -> {
            showsStatus = true;
            autoHide();
        });

        hideStatus = new TranslateTransition(Duration.millis(250), statusContainer);
        hideStatus.setByY(1080.0);
        hideStatus.setOnFinished(new EventHandler<ActionEvent>() {
            @Override public void handle(ActionEvent event) {
                showsStatus = false;
            }
        });
    }  

    private void show(){
        hideStatus.stop();
        showStatus.play();
    }

    private void hide(){
        showStatus.stop();
        hideStatus.play();
    }

    private void autoHide(){
        Duration duration = Duration.seconds(AUTO_HIDE_DELAY);
        PauseTransition transition = new PauseTransition(duration);
        transition.setOnFinished(evt ->{
            if( showsStatus ) {
                hide();
            }
        });
        transition.play();
    }
}

Test.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="testinggg.TestController">
   <children>
      <AnchorPane id="AnchorPane" maxHeight="-Infinity" prefHeight="1080.0" prefWidth="1920.0" StackPane.alignment="TOP_LEFT">
         <children>
            <Button mnemonicParsing="false" onAction="#toggleStatus" prefHeight="1080.0" prefWidth="1920.0" text="Button" />
         </children>
      </AnchorPane>
      <VBox fx:id="statusContainer" maxHeight="1080.0" prefHeight="1080.0" translateY="1080.0" StackPane.alignment="BOTTOM_LEFT">
         <children>
            <AnchorPane prefHeight="668.0" prefWidth="1266.0">
               <VBox.margin>
                  <Insets bottom="50.0" left="50.0" right="50.0" top="50.0" />
               </VBox.margin>
               <children>
                  <ImageView fitHeight="540.0" fitWidth="1820.0" layoutY="43.0" pickOnBounds="true">
                     <image>
                        <Image url="@../Rainbow%20Poro.png" />
                     </image>
                  </ImageView>
                  <ImageView fitHeight="44.0" fitWidth="153.0" layoutX="857.0" pickOnBounds="true" preserveRatio="true">
                     <image>
                        <Image url="@../logo.png" />
                     </image>
                  </ImageView>
                  <ScrollPane layoutY="582.0" prefHeight="391.0" prefViewportHeight="208.0" prefViewportWidth="1266.0" prefWidth="1820.0">
                     <content>
                        <TextArea editable="false" layoutY="460.0" prefHeight="515.0" prefWidth="1804.0" text="Sometexthere SometexthereSometexthereSometexthereSometexthereSometexthereSometexthereSometexthereSometexthereSometexthere&#10;Some&#10;Text&#10;Here&#10;Some&#10;Text &#10;Here&#10;Some&#10;Text &#10;Here&#10;Some&#10;Text &#10;Here&#10;Some&#10;Text &#10;Here&#10;Some&#10;Text &#10;HereSome&#10;Text &#10;HereSome&#10;Text &#10;HereSome&#10;Text &#10;HereSome&#10;Text &#10;HereSome&#10;Text &#10;HereSome&#10;Text &#10;Here&#10;Some&#10;Text &#10;Here" />
                     </content>
                  </ScrollPane>
                  <Button layoutX="1775.0" mnemonicParsing="false" onAction="#toggleStatus" text="Close" />
               </children>
            </AnchorPane>
         </children>
      </VBox>
   </children>
   <stylesheets>
    <URL value="@test1.css" />
  </stylesheets>
</StackPane>

Solution

  • You could add a control boolean to the controller:

    private boolean isStatusContainerBusy = false;
    

    Let the event of interest toggle it. For example set it to true when mouse enters, and false when mouse exits:

    statusContainer.setOnMouseEntered(e-> isStatusContainerBusy = true);
    statusContainer.setOnMouseExited(e-> isStatusContainerBusy = false);
    

    And use it to control auto-hide:

    transition.setOnFinished(evt ->{
        if( showsStatus ) {
            if(isStatusContainerBusy) {
                transition.play(); //start auto hide transition again
            }else{
                hide();
            }
        }
    });
    

    Putting it all together:

    public class TestController {
    
        @FXML private VBox statusContainer;
    
        private TranslateTransition showStatus;
        private TranslateTransition hideStatus;
        private boolean showsStatus = false;
        private static final int AUTO_HIDE_DEALY = 5;
        private boolean isStatusContainerBusy = false;
    
        @FXML void initialize() {
    
            showStatus = new TranslateTransition(Duration.millis(250), statusContainer);
            showStatus.setByY(-100.0);
            showStatus.setOnFinished(event -> {
                showsStatus = true;
                autoHide();
            });
            hideStatus = new TranslateTransition(Duration.millis(250), statusContainer);
            hideStatus.setByY(100.0);
            hideStatus.setOnFinished(event -> showsStatus = false);
            statusContainer.setOnMouseEntered(e-> isStatusContainerBusy = true);
            statusContainer.setOnMouseExited(e-> isStatusContainerBusy = false);
        }
    
        public void toggleStatus() {
            if( showsStatus ) {
                hide();
            }
            else {
                show();
            }
        }
    
        private void show(){
            hideStatus.stop();
            showStatus.play();
        }
    
        private void hide(){
            showStatus.stop();
            hideStatus.play();
        }
    
        private void autoHide() {
            Duration duration = Duration.seconds(AUTO_HIDE_DEALY);
            PauseTransition transition = new PauseTransition(duration);
            transition.setOnFinished(evt ->{
                if( showsStatus ) {
                    if(isStatusContainerBusy) {
                        transition.play(); //start auto hide transition again
                    }else{
                        hide();
                    }
                }
            });
            transition.play();
        }
    }