Search code examples
scalamenubarscalafxborderpane

I can't get my scalaFX menubar to show up in my gui


I'm playing with ScalaFX because I want to make an encrypted file app but, like the title said, I'm running in to problems. I want to avoid using brackets as I think they are ugly.

import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.Scene
import scalafx.scene.control._
import scalafx.scene.layout.BorderPane
import scalafx.event.ActionEvent
import scalafx.scene.layout.HBox

object Start extends JFXApp {
  
  
  stage = new PrimaryStage {
    title = "Eencrypt THAT"
    
   
    scene = new Scene(600, 300) {
      val border = new BorderPane
      root = border
      
      val topMenuBar = new MenuBar
      border.top = topMenuBar
      val topMenuBarList = List()
      topMenuBar.menus = topMenuBarList
      
      val menuFileItems = List()
      val menuFile = new Menu("File")
          menuFile.items = menuFileItems
          val menuFileItemNew = new MenuItem("New...")
          menuFileItems :+ menuFileItemNew
          val menuFileItemSave = new MenuItem("Save")
          menuFileItems :+ menuFileItemSave
      topMenuBarList :+ menuFile
      
      val menuEditItems = List()
      val menuEdit = new Menu("Edit")
          menuEdit.items = menuEditItems
          val menuEditItemCut = new MenuItem("Cut")
          menuEditItems :+ menuEditItemCut
          val menuEditItemCopy  =  new MenuItem("Copy")
          menuEditItems :+ menuEditItemCopy
          val menuEditItemPaste  =  new MenuItem("Paste")
          menuEditItems :+ menuEditItemPaste 
      topMenuBarList :+ menuEdit
    
      border.bottom = Label("Lable")
     
    }
  }
  
}

Also is there any way to do some thing like this:

topMenuBar.menus = val topMenuBarList = List()

That way it will be on one line but I'll still have a named list to push too.


Solution

  • One of the issues here is that List() doesn't work the way you appear to think it does.

    Firstly, the :+ operator does indeed append an element to the list (which is a slow operation, since the entire list must be copied each time; you should prefer prepending instead). However, this operator returns a new list containing the appended element, but since you don't store the result, it is thrown away. (Scala Lists are immutable elements, and cannot be modified, so List operators return new List instances instead. This is actually highly desirable for Scala's Functional Programming paradigm.)

    Secondly, Lists are not observable. Even if you could modify lists by adding elements to it, there would be no way for topMenuBar.menus, menuFile.items or menuEdit.items to know about the additions to their lists. So, all you're doing is copying empty lists to each element.

    There are a few alternatives, one which involves initializing List instances before assigning their contents to the GUI, and the other which uses an ObservableBuffer instead of a List. (If using an ObservableBuffer, you would have to bind it to the associated property, using the ScalaFX <== operator; there are examples in the ScalaFX demo models. For further background, ObservableBuffer is the ScalaFX equivalent of JavaFX's ObservableList.) I'd prefer the former, because observable elements should typically only be used if you need to dynamically modify them during your program's execution.

    Here's the first approach, which populates the lists first, then assigns them to the associated GUI element:

    import scalafx.application.JFXApp
    import scalafx.application.JFXApp.PrimaryStage
    import scalafx.scene.Scene
    import scalafx.scene.control._
    import scalafx.scene.layout.BorderPane
    import scalafx.event.ActionEvent
    import scalafx.scene.layout.HBox
    
    object Start extends JFXApp {
    
      stage = new PrimaryStage {
        title = "Encrypt THAT"
    
        scene = new Scene(600, 300) {
    
          // Create the border pane as the root of the scene.
          root = new BorderPane {
    
            // Create the menu bar as the top of the border pane.
            top = new MenuBar {
    
              // File menu.
              val menuFile = new Menu("File") {
                val itemNew = new MenuItem("New...")
                val itemSave = new MenuItem("Save")
                items = List(itemNew, itemSave)
              }
    
              // Edit menu
              val menuEdit = new Menu("Edit") {
                val itemCut = new MenuItem("Cut")
                val itemCopy = new MenuItem("Copy")
                val itemPaste = new MenuItem("Paste")
                items = List(itemCut, itemCopy, itemPaste)
              }
    
              // Add the File and Edit menu to the menu bar.
              menus = List(menuFile, menuEdit)
            }
    
            // Bottom label.
            bottom = Label("Label")
          }
        }
      }
    }
    

    BTW, to avoid using braces, you might want to wait for Scala 3 (a.k.a. Dotty), which doesn't need them. In the meantime, your intent might be clearer (and your code less ugly to other Scala programmers) if you put them in. ;-)

    Alternatively, provided you populate the lists before assigning them to the GUI elements, you could adapt this code to your braceless style fairly easily.