Skip to content

Commit

Permalink
#112: implements a basic autoscroll behavior for TextView
Browse files Browse the repository at this point in the history
  • Loading branch information
rladstaetter committed Oct 29, 2022
1 parent dd259c2 commit 83a0b45
Show file tree
Hide file tree
Showing 21 changed files with 276 additions and 97 deletions.
15 changes: 12 additions & 3 deletions app/src/main/scala/app/logorrr/conf/mut/MutLogFileSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,32 @@ object MutLogFileSettings {
}

class MutLogFileSettings extends Petrify[LogFileSettings] {
def getFontSize(): Int = fontSizeProperty.get()

def getFilters() = filtersProperty.asScala.toSeq

private val pathAsStringProperty = new SimpleStringProperty()
private val firstOpenedProperty = new SimpleLongProperty()
val selectedIndexProperty = new SimpleIntegerProperty()
val dividerPositionProperty = new SimpleDoubleProperty()
val fontSizeProperty = new SimpleIntegerProperty()
val autoScrollProperty = new SimpleBooleanProperty()
val filtersProperty = new SimpleListProperty[Filter](FXCollections.observableArrayList())
val someLogEntrySettings = new SimpleObjectProperty[Option[LogEntryInstantFormat]]()
val blockWidthSettingsProperty = new SimpleIntegerProperty()

val fontStyle: ObservableValue[_ <: String] = new StringBinding {
bind(fontSizeProperty)

override def computeValue(): String = LogoRRRFonts.jetBrainsMono(fontSizeProperty.get())
}

def setAutoScroll(autoScroll: Boolean): Unit = autoScrollProperty.set(autoScroll)

def isAutoScroll(): Boolean = autoScrollProperty.get()

def getFontSize(): Int = fontSizeProperty.get()

def getFilters() = filtersProperty.asScala.toSeq

def setBlockSettings(bs: BlockSettings): Unit = blockWidthSettingsProperty.set(bs.width)

def setPathAsString(path: String): Unit = pathAsStringProperty.set(path)
Expand All @@ -65,6 +73,7 @@ class MutLogFileSettings extends Petrify[LogFileSettings] {
, fontSizeProperty.get()
, filtersProperty.get().asScala.toSeq
, BlockSettings(blockWidthSettingsProperty.get())
, someLogEntrySettings.get())
, someLogEntrySettings.get()
, autoScrollProperty.get())
}

7 changes: 5 additions & 2 deletions app/src/main/scala/app/logorrr/model/LogFileSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ object LogFileSettings {
val DefaultDividerPosition = 0.5
val DefaultBlockSettings = BlockSettings(10)
val DefaultLogFormat: Option[LogEntryInstantFormat] = None
val DefaultAutoScroll = false

val finest: Filter = new Filter("FINEST", Color.GREY.toString)
val info: Filter = new Filter("INFO", Color.GREEN.toString)
Expand All @@ -37,7 +38,8 @@ object LogFileSettings {
, DefaultFontSize
, DefaultFilter
, DefaultBlockSettings
, DefaultLogFormat)
, DefaultLogFormat
, DefaultAutoScroll)

}

Expand All @@ -58,7 +60,8 @@ case class LogFileSettings(pathAsString: String
, fontSize: Int
, filters: Seq[Filter]
, blockSettings: BlockSettings
, someLogEntrySetting: Option[LogEntryInstantFormat]) extends CanLog {
, someLogEntrySetting: Option[LogEntryInstantFormat]
, autoScroll: Boolean) extends CanLog {

val path: Path = Paths.get(pathAsString)

Expand Down
6 changes: 6 additions & 0 deletions app/src/main/scala/app/logorrr/model/LogIdAware.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package app.logorrr.model

trait LogIdAware {

def pathAsString: String
}
47 changes: 39 additions & 8 deletions app/src/main/scala/app/logorrr/views/LogFileTab.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package app.logorrr.views
import app.logorrr.conf.{BlockSettings, LogoRRRGlobals}
import app.logorrr.model.{LogEntry, LogFileSettings}
import app.logorrr.util._
import app.logorrr.views.autoscroll.LogTailer
import app.logorrr.views.ops.{OpsRegion, SettingsOps}
import app.logorrr.views.search.{Filter, FiltersToolBar, Fltr, OpsToolBar}
import app.logorrr.views.text.LogTextView
import app.logorrr.views.visual.LogVisualView
import javafx.beans.binding.{Bindings, StringExpression}
import javafx.beans.property.{SimpleListProperty, SimpleObjectProperty}
import javafx.beans.property.SimpleListProperty
import javafx.beans.{InvalidationListener, Observable}
import javafx.collections.transformation.FilteredList
import javafx.collections.{ListChangeListener, ObservableList}
import javafx.scene.control._
Expand Down Expand Up @@ -67,7 +69,6 @@ class LogFileTab(val pathAsString: String
extends Tab
with CanLog {


selectedProperty().addListener(JfxUtils.onNew[java.lang.Boolean](b => {
if (b) {
setStyle(LogFileTab.BackgroundSelectedStyle)
Expand All @@ -76,7 +77,8 @@ class LogFileTab(val pathAsString: String
}
}))

val logoRRRTailer = LogoRRRTailer(pathAsString, logEntries)
// lazy since only if autoscroll is set start tailer
lazy val logTailer = LogTailer(pathAsString, logEntries)

def repaint(): Unit = logVisualView.repaint()

Expand Down Expand Up @@ -136,8 +138,35 @@ class LogFileTab(val pathAsString: String

private val logTextView = new LogTextView(pathAsString, filteredList, timings)

// val selectedEntryProperty = new SimpleObjectProperty[LogEntry]()

lazy val scrollToEndEventListener: InvalidationListener = (observable: Observable) => logTextView.scrollToEnd()

private def startTailer(): Unit = {
filteredList.addListener(scrollToEndEventListener)
logTailer.start()
}

private def stopTailer(): Unit = {
filteredList.removeListener(scrollToEndEventListener)
logTailer.stop()
}

def initAutoScroll(): Unit = {
LogoRRRGlobals.getLogFileSettings(pathAsString).autoScrollProperty.addListener(JfxUtils.onNew[java.lang.Boolean] {
b =>
if (b) {
startTailer()
} else {
stopTailer()
}
})

if (LogoRRRGlobals.getLogFileSettings(pathAsString).isAutoScroll()) {
startTailer()
}

val selectedEntryProperty = new SimpleObjectProperty[LogEntry]()
}


/** if a change event for filtersList Property occurs, save it to disc */
Expand Down Expand Up @@ -188,11 +217,11 @@ class LogFileTab(val pathAsString: String
/** don't monitor file anymore if tab is closed, free invalidation listeners */
setOnClosed(_ => closeTab())
textProperty.bind(computeTabTitle)
val tooltip = new Tooltip("jodel")
val tooltip = new Tooltip()
tooltip.textProperty().bind(Bindings.concat(Bindings.size(logEntries).asString, " lines"))
setTooltip(tooltip)

selectedEntryProperty.bind(logVisualView.selectedEntryProperty)
// selectedEntryProperty.bind(logVisualView.selectedEntryProperty)

// if user changes selected item in listview, change footer as well
//logTextView.listView.getSelectionModel.selectedItemProperty.addListener(logEntryChangeListener)
Expand All @@ -208,7 +237,7 @@ class LogFileTab(val pathAsString: String
t1: Number => LogoRRRGlobals.setDividerPosition(pathAsString, t1.doubleValue())
})

logoRRRTailer.start()
initAutoScroll()

setDivider(LogoRRRGlobals.getLogFileSettings(pathAsString).dividerPositionProperty.get())
initFiltersPropertyListChangeListener()
Expand All @@ -230,8 +259,10 @@ class LogFileTab(val pathAsString: String
}

def shutdown(): Unit = {
if (LogoRRRGlobals.getLogFileSettings(pathAsString).isAutoScroll()) {
stopTailer()
}
LogoRRRGlobals.removeLogFile(pathAsString)
logoRRRTailer.stop()
}


Expand Down
18 changes: 0 additions & 18 deletions app/src/main/scala/app/logorrr/views/LogoRRRTailer.scala

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package app.logorrr.views.autoscroll

import app.logorrr.conf.LogoRRRGlobals
import app.logorrr.model.LogIdAware
import javafx.scene.control.{CheckBox, Tooltip}


class AutoScrollCheckBox(val pathAsString: String) extends CheckBox with LogIdAware {
setTooltip(new Tooltip("autoscroll"))

selectedProperty().bindBidirectional(LogoRRRGlobals.getLogFileSettings(pathAsString).autoScrollProperty)

}
13 changes: 13 additions & 0 deletions app/src/main/scala/app/logorrr/views/autoscroll/AutoScroller.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package app.logorrr.views.autoscroll

import app.logorrr.conf.LogoRRRGlobals
import app.logorrr.model.LogIdAware

trait AutoScroller {
autoScroller: LogIdAware =>

def setAutoScroll(autoScroll: Boolean): Unit = LogoRRRGlobals.getLogFileSettings(pathAsString).setAutoScroll(autoScroll)

def isAutoScroll(): Boolean = LogoRRRGlobals.getLogFileSettings(pathAsString).isAutoScroll()

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package app.logorrr.util
package app.logorrr.views.autoscroll

import app.logorrr.conf.LogoRRRGlobals
import app.logorrr.model.LogEntry
import app.logorrr.util.{CanLog, JfxUtils}
import app.logorrr.views.search.Filter
import javafx.collections.ObservableList
import org.apache.commons.io.input.{Tailer, TailerListener}
Expand Down
43 changes: 43 additions & 0 deletions app/src/main/scala/app/logorrr/views/autoscroll/LogTailer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package app.logorrr.views.autoscroll

import app.logorrr.model.LogEntry
import app.logorrr.util.CanLog
import javafx.collections.ObservableList
import org.apache.commons.io.input.Tailer

import java.nio.file.Paths

/**
* If active, this class adds entries to the given logEntries observable list.
* @param pathAsString path to log file
* @param logEntries list which will be modified if log file changes
*/
case class LogTailer(pathAsString: String
, logEntries: ObservableList[LogEntry])
extends CanLog {

var currentTailer: Option[Tailer] = None

private def mkTailer(): Tailer = new Tailer(Paths.get(pathAsString).toFile, new LogEntryListener(pathAsString, logEntries), 40, true)

/** start observing log file for changes */
def start(): Unit = synchronized {
currentTailer match {
case Some(value) => logWarn("Not starting new LogTailer, already one in progress ...")
case None =>
currentTailer = Option(mkTailer())
timeR(currentTailer.foreach(t => new Thread(t).start()), s"Started LogTailer for file $pathAsString")
}
}

def stop(): Unit = timeR({
currentTailer match {
case Some(tailer) =>
tailer.stop()
currentTailer = None
case None =>
logWarn("No LogTailer was active, ignoring ...")
}
}, s"Stopped LogTailer for file $pathAsString")

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import app.logorrr.util.JfxUtils
import javafx.beans.property.SimpleListProperty
import javafx.collections.ListChangeListener
import javafx.collections.transformation.FilteredList
import javafx.scene.control.{Button, ToolBar}
import org.kordamp.ikonli.fontawesome5.FontAwesomeSolid
import org.kordamp.ikonli.javafx.FontIcon
import javafx.scene.control.ToolBar

import scala.jdk.CollectionConverters._

Expand All @@ -21,11 +19,6 @@ object FiltersToolBar {
|-fx-border-color: RED;
|""".stripMargin

class RemoveButton(filter: Filter, removeFilter: Filter => Unit) extends Button {
setGraphic(new FontIcon(FontAwesomeSolid.TIMES_CIRCLE))
setOnAction(_ => removeFilter(filter))
}

}


Expand Down
11 changes: 9 additions & 2 deletions app/src/main/scala/app/logorrr/views/search/OpsToolBar.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package app.logorrr.views.search

import app.logorrr.views.autoscroll.AutoScrollCheckBox
import app.logorrr.views.block.HasBlockSizeProperty
import app.logorrr.views.ops.{DecreaseBlockSizeButton, IncreaseBlockSizeButton}
import app.logorrr.views.text.{DecreaseTextSizeButton, IncreaseTextSizeButton}
Expand Down Expand Up @@ -38,7 +39,7 @@ class OpsToolBar(pathAsString: String, addFilterFn: Filter => Unit)
//setStyle(SearchToolBar.BackgroundSelectedStyle)
setStyle("""-fx-padding: 0px 0px 0px 4px;""")

val width = 490
val width = 510
setMaxWidth(width)
setMinWidth(width)

Expand All @@ -55,6 +56,8 @@ class OpsToolBar(pathAsString: String, addFilterFn: Filter => Unit)

private val searchButton = new SearchButton(searchTextField, regexToggleButton, colorPicker, addFilterFn)

val autoScrollCheckBox = new AutoScrollCheckBox(pathAsString)

def execSearchOnHitEnter(event: KeyEvent): Unit = {
if (event.getCode == KeyCode.ENTER) {
searchButton.fire()
Expand All @@ -75,6 +78,10 @@ class OpsToolBar(pathAsString: String, addFilterFn: Filter => Unit)
Seq(decreaseBlockSizeButton, increaseBlockSizeButton, decreaseTextSizeButton, increaseTextSizeButton)
}

getItems.addAll(searchItems ++ sizeItems: _*)
val otherItems: Seq[Control] = {
Seq(autoScrollCheckBox)
}

getItems.addAll(searchItems ++ sizeItems ++ otherItems: _*)

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package app.logorrr.views.text

import app.logorrr.conf.LogoRRRGlobals
import app.logorrr.model.LogIdAware

trait HasFontSizeProperty {

def pathAsString: String
trait HasFontSizeProperty extends LogIdAware {

def setFontSize(fontSize: Int): Unit = LogoRRRGlobals.getLogFileSettings(pathAsString).setFontSize(fontSize)

Expand Down
7 changes: 7 additions & 0 deletions app/src/main/scala/app/logorrr/views/text/LogTextView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class LogTextView(pathAsString: String

private val fixedCellSize = 26


/** 'pragmatic way' to determine width of max elems in this view */
val maxLength = filteredList.size().toString.length

Expand Down Expand Up @@ -114,6 +115,12 @@ class LogTextView(pathAsString: String

setCenter(listView)


/** scroll to end of listview */
def scrollToEnd(): Unit = {
listView.scrollTo(listView.getItems.size)
}

class LogEntryListCell extends ListCell[LogEntry] {
styleProperty().bind(LogoRRRGlobals.getLogFileSettings(pathAsString).fontStyle)
//setStyle(LogoRRRFonts.jetBrainsMono(LogTextView.fontSize))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ import javafx.scene.layout.BorderPane
class LogVisualView(pathAsString: String, entries: ObservableList[LogEntry], canvasWidth: Int)
extends BorderPane with CanLog {

def repaint() = blockViewPane.repaint()


// require(canvasWidth > 0, "canvasWidth must be greater than 0")
def repaint(): Unit = blockViewPane.repaint()

val selectedEntryProperty = new SimpleObjectProperty[LogEntry]()

Expand Down
Loading

0 comments on commit 83a0b45

Please sign in to comment.