0

I have a login window view, and I want to display a progress bar when I click/press enter button while slick is querying the password. If I change the visible attribute for the progress bar at the button actionEvent it doesn´t appear until after the query is done. Also I don't want the progress bar to be taking space while its invisible. Does anybody know how to do these things?

object SPM extends JFXApp {

/*
* Primary stage: Log in
* */
stage = new PrimaryStage {
// error message hidden label
val errorLabel = new Label()
errorLabel.textFill = Color.Red
errorLabel.font = Font.font("Helvetica", FontWeight.ExtraLight, 12)

val usernameField = new TextField {
  promptText = "User"
  maxWidth = 250
  prefHeight = 35
}

val passwordField = new PasswordField() {
  promptText = "Password"
  maxWidth = 250
  prefHeight = 35
}

val progressBar = new ProgressBar {
  maxWidth = 300
  visible = false
}
title = "Software Project Management"
scene = new Scene(800, 600) {
  root = new VBox {
    spacing = 10
    padding = Insets(20)
    alignment = Pos.Center
    children = List(
      new ImageView {
        image = new Image(
          this.getClass.getResourceAsStream("/images/logo.png"))
        margin = Insets(0, 0, 20, 0)
      },
      new Label {
        text = "Software Project Management"
        font = Font.font("Helvetica", FontWeight.ExtraLight, 32)
      },
      new Label {
        text = "Sign in to get started"
        font = Font.font("Helvetica", FontWeight.Thin, 18)
      },
      errorLabel,
      progressBar,
      usernameField,
      passwordField,
      new Button {
        text = "Enter"
        defaultButton = true
        prefHeight = 35
        font = Font.font("Helvetica", FontWeight.Thin, 18)
        maxWidth = 250
        onAction = (ae: ActionEvent) => {
          progressBar.visible = true
          val password = Users.checkPassword(usernameField.text.value)
          if (password != passwordField.text.value)
            errorLabel.text = "Please re-enter your password"
          else root = chooseProject
        }
      }
    ) // children
  } // root
} // scene
mimo
  • 33
  • 3

1 Answers1

1

Your Button.onAction handler is running on JavaFX application thread. The same that is used to update UI. When you run long running task you should run it on a separate thread it will help UI to react properly. The common way to do that is to use JavaFX Task. General pattern is like this:

// Define your task
val task = new javafx.concurrent.Task[T] {
  override def call(): T = {
    // Do your task and return result    
    // Executed off JavaFX Application thread     
  }
  override def succeeded(): Unit = {
    // Update UI to finish processing
    // Executed on JavaFX Application thread     
  }
  override def failed(): Unit = {
    // Handle errors, if any
    // Executed on JavaFX Application thread 
  }
}

// Run your task
val t = new Thread(task, "My Task")
t.setDaemon(true)
t.start()

```

Here is how it could look in your code:

  root = new VBox { _root =>
  ...

        onAction = (ae: ActionEvent) => {
          progressBar.visible = true
          _root.disable = true
          //              progressBar.visible = true
          val task = new javafx.concurrent.Task[Boolean] {
            override def call(): Boolean = {
              println("Checking password... ")
              Thread.sleep(3000)
              println("Password checked. ")
              // Assume password is correct
              true
            }
            override def succeeded(): Unit = {
              progressBar.visible = false
              _root.disable = false
              val passwordOK = get()
              if (passwordOK) {
                new Alert(AlertType.Information) {
                  headerText = "Password OK"
                }.showAndWait()
              } else {
                new Alert(AlertType.Warning) {
                  headerText = "Invalid Password"
                }.showAndWait()

              }
            }
            override def failed(): Unit = {
              println("failed")
              progressBar.visible = false
              _root.disable = false
            }
          }

          val t = new Thread(task, "Password Task")
          t.setDaemon(true)
          t.start()
        }
Jarek
  • 1,513
  • 9
  • 16
  • Also good tip to put the root disabled while doing the task, thanks a lot. By the way now that I got a better understanding of threads, do you think that putting the query on a scala future for concurrency would be better or the javafx task is more suitable? – mimo Nov 05 '15 at 19:55
  • You can use Scala Feature as well, just keep in mind that updates to UI need to be done on JavaFX Application thread. JavaFX Task also gives you easy handling of progress value updates and progress messages, if needed. – Jarek Nov 06 '15 at 21:35