Skip to content

Commit

Permalink
#150: adds height/width listeners for ChunkListView
Browse files Browse the repository at this point in the history
  • Loading branch information
rladstaetter committed Nov 2, 2023
1 parent 83d7585 commit 5182ba2
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 43 deletions.
1 change: 0 additions & 1 deletion app/src/main/java/app/logorrr/io/FileManager.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ object FileManager extends CanLog {
private def openFileWithDetectedEncoding(path: Path): BufferedReader = {
val encoding = FEncoding(path)
if (encoding == Unknown) {
logTrace(s"Reading `${path.toAbsolutePath.toString}` with UTF-8 encoding.")
new BufferedReader(new InputStreamReader(new FileInputStream(path.toFile), UTF8.asString))
} else {
new BufferedReader(new InputStreamReader(new FileInputStream(path.toFile), encoding.asString))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import app.logorrr.conf.mut.MutLogFileSettings
import app.logorrr.model.LogEntry
import app.logorrr.util.{CanLog, JfxUtils}
import app.logorrr.views.search.Filter
import atlantafx.base.theme.Styles
import javafx.beans.property.{SimpleDoubleProperty, SimpleIntegerProperty}
import javafx.beans.{InvalidationListener, Observable}
import javafx.collections.{FXCollections, ObservableList}
import javafx.scene.control.ListView
import javafx.scene.layout.BorderPane

import scala.util.{Failure, Success, Try}

Expand All @@ -26,12 +26,16 @@ object ChunkListView {
}

/**
* Each ListCell contains one or more LogEntries; the cells group LogEntries together. This approach
* allows us to use a vanilla ListView which takes care of painting the cells if needed, we don't need
* to care about the virtual flow which is happening in the standard javafx library.
* Each ListCell contains one or more Logentries - those regions are called 'Chunks'.; Those chunks
* group LogEntries; this grouping serves no other purpose than to optimize painting all entries via
* a ListView. In this way we get a stable and prooven virtual flow implementation under the hood and we
* don't have to reinvent this again.
*
* @param entries log entries to display
* @param blockSizeProperty size of blocks to display
* @param entries log entries to display
* @param selectedLineNumberProperty which line is selected by the user
* @param blockSizeProperty size of blocks to display
* @param filtersProperty which filters are active
* @param dividersProperty position of divider of splitpane
*/
class ChunkListView(val entries: ObservableList[LogEntry]
, val selectedLineNumberProperty: SimpleIntegerProperty
Expand All @@ -43,10 +47,27 @@ class ChunkListView(val entries: ObservableList[LogEntry]
var repaints = 0

val repaintInvalidationListener = new InvalidationListener {
override def invalidated(observable: Observable): Unit = {
logTrace("invalidated")
repaint()
}
override def invalidated(observable: Observable): Unit = repaint()
}

val repaintChangeListener = JfxUtils.onNew[Number](_ => repaint())

def addListeners(): Unit = {
entries.addListener(repaintInvalidationListener)

dividersProperty.addListener(repaintChangeListener)
blockSizeProperty.addListener(repaintChangeListener)
widthProperty().addListener(repaintChangeListener)
heightProperty().addListener(repaintChangeListener)
}

def removeListeners(): Unit = {
entries.removeListener(repaintInvalidationListener)

dividersProperty.removeListener(repaintInvalidationListener)
blockSizeProperty.removeListener(repaintInvalidationListener)
widthProperty().removeListener(repaintInvalidationListener)
heightProperty().removeListener(repaintChangeListener)
}

getStylesheets.add(getClass.getResource("/app/logorrr/views/block/ChunkListView.css").toExternalForm)
Expand All @@ -58,18 +79,6 @@ class ChunkListView(val entries: ObservableList[LogEntry]
, filtersProperty))


def addListener(): Unit = {
entries.addListener(repaintInvalidationListener)
blockSizeProperty.addListener(repaintInvalidationListener)
dividersProperty.addListener(repaintInvalidationListener)
}

def removeListener(): Unit = {
entries.removeListener(repaintInvalidationListener)
blockSizeProperty.removeListener(repaintInvalidationListener)
dividersProperty.removeListener(repaintInvalidationListener)
}

// if width/height of display is changed, also elements of this listview will change
// and shuffle between cells. this method recreates all listview entries.
def updateItems(): Unit = Try {
Expand Down Expand Up @@ -97,8 +106,15 @@ class ChunkListView(val entries: ObservableList[LogEntry]
case Failure(exception) => logException(exception.getMessage, exception)
}

def doRepaint: Boolean = widthProperty().get() > 0 & blockSizeProperty.get() > 0 && heightProperty.get() > 0
def doRepaint: Boolean = widthProperty().get() > 0 && heightProperty.get() > 0 && blockSizeProperty.get() > 0

/**
* Repaint method takes care of painting the blocks on the raw byte array.
*
* This method may be called more often than necessary, the 'doRepaint' if statement makes sure we don't paint
* if not all prerequisites are met. Also, this has to run on the javafx thread such that those updates are displayed
* correctly.
*/
// do not remove JfxUtils.execOnUiThread
def repaint(): Unit = JfxUtils.execOnUiThread {
if (doRepaint) {
Expand All @@ -112,5 +128,6 @@ class ChunkListView(val entries: ObservableList[LogEntry]
logTrace(msg)
}
}

}


17 changes: 7 additions & 10 deletions app/src/main/scala/app/logorrr/views/logfiletab/LogFileTab.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,13 @@ class LogFileTab(val pathAsString: String
op
}

private val chunkListView = {
val clv = ChunkListView(filteredList, mutLogFileSettings)
clv
}
private val borderedChunkListView = ChunkListView(filteredList, mutLogFileSettings)

private val logTextView = new LogTextView(mutLogFileSettings, filteredList)

// start listener declarations
private lazy val scrollToEndEventListener: InvalidationListener = (_: Observable) => {
chunkListView.scrollTo(chunkListView.getItems.size())
borderedChunkListView.scrollTo(borderedChunkListView.getItems.size())
logTextView.scrollTo(logTextView.getItems.size)
}

Expand Down Expand Up @@ -139,7 +136,7 @@ class LogFileTab(val pathAsString: String
}
})

def repaint(): Unit = chunkListView.repaint()
def repaint(): Unit = borderedChunkListView.repaint()


/** execute repaints either in their own thread, if it takes too long cancel operation if there is a new value to process */
Expand Down Expand Up @@ -172,14 +169,14 @@ class LogFileTab(val pathAsString: String
initBindings()

// setup split pane before listener initialisation
splitPane.getItems.addAll(chunkListView, logTextView)
splitPane.getItems.addAll(borderedChunkListView, logTextView)

addListeners()

setOnSelectionChanged(e => {
if (isSelected) {
logTrace("selectionchanged")
chunkListView.repaint()
borderedChunkListView.repaint()
}
})

Expand All @@ -204,7 +201,7 @@ class LogFileTab(val pathAsString: String


private def addListeners(): Unit = {
chunkListView.addListener()
borderedChunkListView.addListeners()
selectedProperty().addListener(selectedListener)
divider.positionProperty().addListener(repaintChunkListViewListener)

Expand All @@ -219,7 +216,7 @@ class LogFileTab(val pathAsString: String
}

private def removeListeners(): Unit = {
chunkListView.removeListener()
borderedChunkListView.removeListeners()
selectedProperty().removeListener(selectedListener)

divider.positionProperty().removeListener(repaintChunkListViewListener)
Expand Down
11 changes: 7 additions & 4 deletions app/src/main/scala/app/logorrr/views/main/LogoRRRMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,21 @@ class LogoRRRMain(closeStage: => Unit)
val tab = new LogFileTab(lfs.pathAsString, es)
tab.init()
mainBorderPane.addLogFileTab(tab)
tab.repaint()
})
// after loading everything, set active log like specified in the config file
LogoRRRGlobals.getSomeActive.foreach(p => selectLog(p))

// only after loading all files we initialize the 'add' listener
// otherwise we would overwrite the active log everytime
mainBorderPane.logViewTabPane.initLogFileAddListener()
mainBorderPane.logViewTabPane.initSelectionListener()

// after loading everything, set active log like specified in the config file
LogoRRRGlobals.getSomeActive.foreach(p => selectLog(p))

})
case Failure(exception) =>
logException("Could not load logfiles", exception)
// init listener also in error case
mainBorderPane.logViewTabPane.initLogFileAddListener()
mainBorderPane.logViewTabPane.initSelectionListener()
}

)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class LogoRRRMainTabPane extends TabPane with CanLog {
LogoRRRGlobals.setSomeActive(Option(logFileTab.pathAsString))
// to set 'selected' property in Tab and to trigger repaint correctly (see issue #9)
getSelectionModel.select(logFileTab)
case _ =>
case _ => ???
}

init()
Expand All @@ -43,7 +43,7 @@ class LogoRRRMainTabPane extends TabPane with CanLog {
/**
* Defines what should happen when a tab is selected
* */
def initLogFileAddListener(): Unit = {
def initSelectionListener(): Unit = {
getSelectionModel.selectedItemProperty().addListener(selectedLogFileTab)
}

Expand Down Expand Up @@ -75,7 +75,7 @@ class LogoRRRMainTabPane extends TabPane with CanLog {
def selectLog(pathAsString: String): Unit = {
getLogFileTabs.find(_.pathAsString == pathAsString) match {
case Some(value) =>
logTrace(s"Activating view for `$pathAsString`.")
logTrace(s"Activated tab for `$pathAsString`.")
getSelectionModel.select(value)
case None =>
logWarn(s"Couldn't find tab with $pathAsString, selecting last tab ...")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class ChunkListTestApp extends Application with CanLog {
, new SimpleIntegerProperty(blockSize)
, filtersProperty
, new SimpleDoubleProperty(dividerPosition))
clv.addListener()
clv.addListeners()
val sp = new SplitPane(clv, new BorderPane(new Label("Test")))

val slider = new Slider(2, 100, 10)
Expand Down

0 comments on commit 5182ba2

Please sign in to comment.