Help: get SpreadsheetViewCell into editing mode works only sometimes

23 views
Skip to first unread message

Michael Weingran

unread,
Apr 23, 2021, 5:42:35 AM4/23/21
to ScalaFX Users
My Problem: My GUI-function works - but only sometimes

I guess it is pointless to post all my code. Better I describe the problem:
Here is what I want:

I have the following structure for my GUI:

scene-root -> borderpane
middle-pane -> scrollPane
scrollpane -> accordion
acordion -> TitledPane1 -> spreadsheetView1
        TitledPane2 -> spreadsheetView2
        TitledPane3 -> spreadsheetView3
    .
    .
    .
        TitledPaneN -> spreadsheetViewN
   
The Spreadsheetviews are inherited from the controlsFX library, everything else is from scalafx

The TitledPanes contain orders and order-information, the spreadsheetViews contain parts and their dimensions.
A key funktion of my applikation is to enter a barcode (via a barcode-scanner that behaves like a keyboard) and then to automatically open the right titledPane and to start editing the right cell in the spreadsheetView.

So somewhere in the borderpane is a TextField where I can enter the barcode.
When the onChange of this Textfield is called, I first open the Titledpane for the corresponding order (fauf). Then i push a certain Cell in the Spreadsheetview into editing mode. 

This kind of works - sometimes :-(

I've found out, that the desired cell turns into its editing state, only when the right titeldpane was already open.

When it is open i can repeat the process many times and the cell turns to editing mode reliably.
But when I open another TitledPane It will not do it any more: the right title pane opens but I can not enter Text into the cell, no matter how often i press enter in the textfield.
If I double klick a clell in the spreadSheetView to edit it, then I can go back to the textField and everything works again as expected.

So putting a cell into editing mode with sheet.edit(row, column) works only sometimes here.
I get no errors, no crash, nothing. When ist does not work it shows no signs why.

Here is the onChange method of the textfield.
the value 'fauf' is the order number from the barcode, wich I hardcoded here together with the part for simpler testing:

    onAction = (_: ActionEvent) => {
      Platform.runLater{
        val fauf = "000004372779"
        val part = "0018"
        val selectedBarCode = fauf + part

        val paneWithFauf: List[TitledPane] = guiTable.titledPaneList filter (pane => pane.text.toString.contains("FAUF:" + fauf))
        if (paneWithFauf.nonEmpty) {
          //guiTable.titledPaneList foreach { t: TitledPane => t.expanded = false } //first close all TitledTanes: makes no difference to do that
          val selectedPane = paneWithFauf.head
          guiTable.accordion.expandedPane = selectedPane  //expand the right TitledPane
          val sheetIndex: Int = guiTable.titledPaneList.indexOf(selectedPane)
          val selectedSheet: SpreadsheetView = guiTable.spreadSheetViewList(sheetIndex)
          val selectedColumn: SpreadsheetColumn = selectedSheet.getColumns.toList(2) //3. Spalte soll ausgewählt werden

  //selectedPane.requestFocus() //makes no difference
          //Thread.sleep(2000) //even that did not help
          selectedSheet.getSelectionModel.clearAndSelect(2, selectedColumn) //select the cell
          //selectedSheet.getSelectionModel.focus(2, selectedColumn) //makes no difference
          selectedSheet.edit(2, selectedColumn) //turn cell into editing mode
          //selectedSheet.getSelectionModel.focus(2, selectedColumn) //makes no difference
          println("isFocused: " + selectedSheet.isFocused() )

        }
      }
      println( barCodeEingabe.text.apply())
    }


I commented some things out that I tried but that did not change the behavior.
Interestingly the println statement prints always 'false'. My guess is that the spreadsheetView is not in focus but the cell is.
What confuses me: when I leave the Thread.seep(2000), the app hangs for 2 sec after I pressed enter in the text field (as expected), and then opens the TitledPane AFTER the 2 Sekonds waiting time !!??

It seems to me that I have to wait until the titledPane is completely open before I put the spreadsheetCell to editing mode.
I tried to do this with an onChange function for the expanded property in the TitledPanes, but this behaved exactly as before.

I am out of ideas now.

Any help would be appreceated


P.S.: I am running Java 8, all libraries pretty much up to date (scala 2.13.5, scalafx 15.0.1-R21) 

Jarek

unread,
Apr 23, 2021, 3:41:29 PM4/23/21
to ScalaFX Users
At quick glance you are doing your operations on the JavaFX Thread (using runLater()). Any requests to other components, like "focus", may not happen till their get their own time on the JavaFX Thread, after your "runLater" finished. It would nice to have some simple running code to reproduce the issue you are having.

Michael Weingran

unread,
Apr 26, 2021, 12:30:11 PM4/26/21
to ScalaFX Users
OK, I tried to boil this down as much as possible.
I hardcoded the barcode entry so that whatever one enters, the program will always jump to order 0005, part 0003.
So for this test just hit enter in the textfield.
The program should then open order 0005 and accept key-input for part 0003, but dosn't.
This code shows the described behavior:

package MesswertUebertragung

/* Content of build.sbt:
name := "MesswertUebertragung"
scalaVersion := "2.13.5"
libraryDependencies += "org.scalafx" %% "scalafx" % "15.0.1-R21"
libraryDependencies += "org.controlsfx" % "controlsfx" % "8.40.18"
// Prevent startup bug in JavaFX
fork := true
// Tell Javac and scalac to build for jvm 1.8
javacOptions ++= Seq("-source", "1.8", "-target", "1.8")
scalacOptions += "-target:jvm-1.8"
scalacOptions += "-feature"
*/

import org.controlsfx.control.spreadsheet._
import scalafx.Includes._
import scalafx.application.{JFXApp, Platform}
import scalafx.collections.ObservableBuffer
import scalafx.event.ActionEvent
import scalafx.scene.Scene
import scalafx.scene.control.{Accordion, ScrollPane, TextField, TitledPane}
import scalafx.scene.layout.BorderPane

import javafx.{collections => jfxc}

object Main extends JFXApp {

def partToSheetRow(part: String, rowIndex: Int): ObservableBuffer[SpreadsheetCell] = {
val row = new ObservableBuffer[SpreadsheetCell]()
row += SpreadsheetCellType.STRING.createCell(rowIndex, 0, 1, 1, part)
(1 to 14) foreach {
(i: Int) => row += SpreadsheetCellType.DOUBLE.createCell(rowIndex, i, 1, 1, java.lang.Double.NaN)
}
row(0).setEditable(false)
(0 to 14) foreach ((i: Int) => row(i).setStyle(" -fx-alignment: CENTER;"))
(1 to 14) foreach ((i: Int) => row(i).setEditable(true))
row
}

case class Order(orderNo: Int) {
override def toString = f"$orderNo%04d"

val partList: List[String] = (1 to 25).toList map { n: Int => f"$n%04d" }

val allSpreadSheetRows: ObservableBuffer[jfxc.ObservableList[SpreadsheetCell]] =
new ObservableBuffer[jfxc.ObservableList[SpreadsheetCell]]() ++= (partList map (part => partToSheetRow(part, partList.indexOf(part))))
}

val orderList: List[Order] = (1 to 50).toList map { i => Order(i) }

lazy val spreadSheetViewList: List[SpreadsheetView] = for {order <- orderList} yield {
val rowCount: Int = order.partList.length
val columnCount: Int = 20
val grid: GridBase = new GridBase(rowCount, columnCount)
val rows: ObservableBuffer[jfxc.ObservableList[SpreadsheetCell]] = order.allSpreadSheetRows
grid.setRows(rows)
val sheet: SpreadsheetView = new SpreadsheetView(grid) //***** hier wird das jeweilige SpreadsheetView definiert!
sheet.getGrid.getColumnHeaders.setAll("Nr.", "Abmessung",
"Bohr" + 0x2300.toChar + "Kopf", "WDKopf 0°", "WDKo. 120°", "WDKo. 240°",
"Bohr" + 0x2300.toChar + "Fuss", "WDFuss 0°", "WDFu. 120°", "WDFu. 240°",
"Ra-Wert OK", "Versiegelt", "Sichtkontr.", "Palette", "Status")
sheet.getColumns.get(0).setMinWidth(30) //setMinWidth(36)
sheet.getColumns.get(0).setResizable(false)
sheet.getColumns.get(1).setMinWidth(145)
sheet.getColumns.get(1).setResizable(true)
(2 to 14) foreach ((i: Int) => sheet.getColumns.get(i).setPrefWidth(124))
sheet
}


val titledPaneList: List[TitledPane] = {
(spreadSheetViewList zip orderList) map {
case (sheet, order) =>
new TitledPane {
prefWidth = 1870
style = "-fx-font-size: 14pt;"
text = order.toString
content = sheet
}
}
}

val accordion: Accordion = new Accordion() {
titledPaneList foreach { p => panes += p }
panes.sortBy(_.toString)
}

val barCodeEntry: TextField = new TextField() {
style = "-fx-font-size: 20pt;-fx-border-color: white;-fx-border-width: 5"
promptText = "Enter Barcode"
disable = false
editable = true
visible = true


onAction = (_: ActionEvent) => {
val ord = "005" //Hardcoded order number only for testing
val prt = "010" //Hardcoded part number only for testing

val paneWithFauf: List[TitledPane] = titledPaneList filter (pane => pane.text.toString.contains(ord))
if (paneWithFauf.nonEmpty) {
//titledPaneList foreach { t: TitledPane => t.expanded = false } //First close all Titled-Panes: does not change the bahavior
val selectedPane: TitledPane = paneWithFauf.head
accordion.expandedPane = selectedPane //expand the right TitledPane with the given order

val sheetIndex: Int = titledPaneList.indexOf(selectedPane)
val selectedSheet: SpreadsheetView = spreadSheetViewList(sheetIndex)
val selectedColumn: SpreadsheetColumn = selectedSheet.getColumns.toList(1)

//Platform.runLater {
//selectedPane.requestFocus()
//Thread.sleep(2000)
//selectedSheet.requestFocus()
//selectedSheet.focusedProperty.onChange { (_, oldValue, newValue) => println("sheet focused: " + newValue) }
selectedSheet.getSelectionModel.clearAndSelect(2, selectedColumn)
//selectedSheet.getSelectionModel.focus(2, selectedColumn)
selectedSheet.edit(2, selectedColumn)
//selectedSheet.getSelectionModel.edit(2, selectedColumn)
selectedSheet.getSelectionModel.focus(2, selectedColumn)
val grid: Grid = selectedSheet.getGrid
// (Controls.barCodeEingabe.requestFocus())
//}
}
}
}

stage = new JFXApp.PrimaryStage {
title = "Test spreadsheetView problem"
resizable = true
width = 1200
height = 800
fullScreen = true

scene = new Scene {
root = new BorderPane() {
center = new ScrollPane() {
content = accordion
}
bottom = barCodeEntry
style = "-fx-padding: 10;" + "-fx-border-style: solid inside;" + "-fx-border-width: 2;" +
"-fx-border-insets: 3;" + "-fx-border-radius: 1;" + "-fx-border-color: black;"
}
}
Platform.runLater(barCodeEntry.requestFocus())
}
}



I would be grateful for any advise on how to solve this

P.S. Sorry for the bad formatting. It was propperly formated before I pasted it. 
Hope you have auto-format

Jarek

unread,
Apr 27, 2021, 10:15:56 PM4/27/21
to ScalaFX Users

I think I found the reason why cell edit is not working. When Accordion is expanded a TitledPane does an animation. Looks that a SpreadSheetView is not rendered completely till that animation is finished. You need to wait for that off the JavaFX Application Thread, otherwise you will block the animation from finishing. Here is a modified code for onAction that does the wait:

    onAction = (_: ActionEvent) => {
      val ord = "005" //Hardcoded order number only for testing
      val prt = "010" //Hardcoded part number only for testing

      val paneWithFauf: List[TitledPane] = titledPaneList filter (pane => pane.text.toString.contains(ord))
      if
 (paneWithFauf.nonEmpty) {
        
val selectedPane: TitledPane = paneWithFauf.head
        accordion.expandedPane = selectedPane //expand the right TitledPane with the given order

        val sheetIndex: Int
 = titledPaneList.indexOf(selectedPane)

        // TitledPane does animation when it expends, default duration is 350ms.
        // Need to wait till that animation finishes, we will add extra 50ms to have "transition"
        // The wait need to happen off JavaFX Application Thread, so a new thread is created
        val th = new Thread(() => {
          // Wait for TitledPane animation to finish + 50ms
          Thread.sleep(350+50)
          Platform.runLater {
            // Prepare selected cell for editing

            val selectedSheet: SpreadsheetView = spreadSheetViewList(sheetIndex)
            val selectedColumn: SpreadsheetColumn = selectedSheet.getColumns.toList(1
)
            selectedSheet.getSelectionModel.clearAndSelect(2, selectedColumn)
            selectedSheet.edit(2, selectedColumn)
            selectedSheet.getSelectionModel.focus(2, selectedColumn)
          }
        })
        th.setDaemon(true)
        th.start()
      }
    }
  }

Hope this helps,

Jarek

Jarek

unread,
Apr 27, 2021, 10:21:53 PM4/27/21
to ScalaFX Users
Google groups reader does strange rendering of the code in the last message, so here it again:

Michael Weingran

unread,
Apr 28, 2021, 10:46:21 AM4/28/21
to ScalaFX Users

Thank you so much for that!

That does the trick.

 

I would feel much better if I could check somehow if the rendering of the spreadsheetView is completed before I call the edit-Method.

I checked all of the properties of the TitledPane that are remotely connected to this (animated, expended, collapsible, focused, pressed), but they are all set before the animation starts (I checked).

Therefore, I have to get it out of the spreadsheetView, but could not find something in the doku.

 

You don't happen to know a way to do this?

 

But, anyway. You found the problem and your solution works. Thank you very much for your effort.

 

Jarek

unread,
Apr 28, 2021, 10:59:35 AM4/28/21
to ScalaFX Users
I do not see a way to determine when an TitledPane animation finished. That may be a good question to JavaFX StackOverflow.

Though there maybe a way to avoid the "wait" from previous post buy turning off the animation, something like "titledPane.animated = false".

Michael Weingran

unread,
Apr 28, 2021, 3:59:30 PM4/28/21
to ScalaFX Users
I had already tried that: Doesn't work. Of course the delay can be probably shorter this way, but without it the error remains.
I will take the question to stackoverflow.

Thanks again

Reply all
Reply to author
Forward
0 new messages