Skip to content

Commit

Permalink
Merge pull request #175 from rladstaetter/132-logorrr-remember-scroll…
Browse files Browse the repository at this point in the history
…-position

132 logorrr remember scroll position
  • Loading branch information
rladstaetter committed Dec 7, 2023
2 parents 064be00 + 960a715 commit 06d9bbb
Show file tree
Hide file tree
Showing 33 changed files with 590 additions and 640 deletions.
6 changes: 0 additions & 6 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,5 @@ If applicable, add screenshots to help explain your problem.
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
34 changes: 23 additions & 11 deletions app/src/main/scala/app/logorrr/LogoRRRApp.scala
Original file line number Diff line number Diff line change
@@ -1,34 +1,46 @@
package app.logorrr

import app.logorrr.conf.{LogoRRRGlobals, Settings, SettingsIO}
import app.logorrr.io.FilePaths
import app.logorrr.meta.AppMeta
import app.logorrr.util.CanLog
import app.logorrr.views.main.LogoRRRStage
import app.logorrr.util.{CanLog, JfxUtils}
import app.logorrr.views.main.{LogoRRRMain, LogoRRRStage}
import javafx.application.{Application, HostServices}
import javafx.stage.Stage

import java.nio.file.Paths

object LogoRRRApp extends CanLog {

/** LogoRRRs own log formatting string */
val logFormat = """[%1$tF %1$tT.%1$tN] %3$-40s %4$-13s %5$s %6$s %n"""
/**
* Main starting point for LogoRRR Application
*/
// have fun and thanks for reading the code!
object LogoRRRApp {

def main(args: Array[String]): Unit = {
System.setProperty("user.language", "en")
System.setProperty("java.util.logging.SimpleFormatter.format", logFormat)
logInfo(s"Started ${AppMeta.fullAppNameWithVersion} in ${Paths.get("").toAbsolutePath.toString}")
System.setProperty("java.util.logging.SimpleFormatter.format", CanLog.LogFormat)
// make sure to set css before anything is initialized otherwise the rules won't apply
LogoRRRNative.loadNativeLibraries()

javafx.application.Application.launch(classOf[LogoRRRApp], args: _*)
}

def start(stage: Stage
, settings: Settings
, hostServices: HostServices): Unit = {
LogoRRRGlobals.set(settings, hostServices)
val logoRRRMain = new LogoRRRMain(JfxUtils.closeStage(stage))
LogoRRRStage.init(stage, logoRRRMain)
LogoRRRStage.show(stage, logoRRRMain)
}
}

class LogoRRRApp extends javafx.application.Application with CanLog {

def start(stage: Stage): Unit = {
val settings: Settings = SettingsIO.fromFile()
LogoRRRGlobals.set(settings, getHostServices)
LogoRRRStage(stage).show()
Application.setUserAgentStylesheet("/app/logorrr/LogoRRR.css")
logInfo(s"Started ${AppMeta.fullAppNameWithVersion} in '${Paths.get("").toAbsolutePath.toString}'")
LogoRRRApp.start(stage, SettingsIO.fromFile(FilePaths.settingsFilePath), getHostServices)
}

}
23 changes: 6 additions & 17 deletions app/src/main/scala/app/logorrr/conf/LogoRRRGlobals.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,19 @@ object LogoRRRGlobals extends CanLog {
/** a case class representing current setting state */
def getSettings: Settings = mutSettings.petrify()

def setSomeActive(sActive: Option[String]): Unit = mutSettings.setSomeActive(sActive)
def setSomeActiveLogFile(sActive: Option[String]): Unit = {
mutSettings.setSomeActive(sActive)
}

def getSomeActive: Option[String] = mutSettings.getSomeActive
def getSomeActiveLogFile: Option[String] = mutSettings.getSomeActiveLogFile

def getSomeLastUsedDirectory: Option[Path] = mutSettings.getSomeLastUsedDirectory

def setSomeLastUsedDirectory(someDirectory: Option[Path]): Unit = mutSettings.setSomeLastUsedDirectory(someDirectory)

def removeLogFile(pathAsString: String): Unit = timeR({

mutSettings.removeLogFileSetting(pathAsString)
mutSettings.setSomeActive(mutSettings.getSomeActive match {
mutSettings.setSomeActive(mutSettings.getSomeActiveLogFile match {
case Some(value) if value == pathAsString => None
case x => x
})
Expand All @@ -95,18 +96,6 @@ object LogoRRRGlobals extends CanLog {
mutSettings.getMutLogFileSetting(pathAsString)
}

def mupdate(t: MutLogFileSettings => Unit)(pathAsString: String): Unit =
Option(mutSettings.getMutLogFileSetting(pathAsString)) match {
case Some(logFileSettings) => t(logFileSettings)
case None => logWarn(s"$pathAsString not found.")
}


def setBlockSettings(pathAsString: String, bs: BlockSettings): Unit =
mupdate({ lfs: MutLogFileSettings => lfs.setBlockSettings(bs) })(pathAsString)

def setDividerPosition(pathAsString: String, dividerPosition: Double): Unit = mutSettings.getMutLogFileSetting(pathAsString).setDividerPosition(dividerPosition)

def updateLogFile(fs: LogFileSettings): Unit = mutSettings.putMutLogFileSetting(MutLogFileSettings(fs))
def registerSettings(fs: LogFileSettings): Unit = mutSettings.putMutLogFileSetting(MutLogFileSettings(fs))

}
8 changes: 3 additions & 5 deletions app/src/main/scala/app/logorrr/conf/SettingsIO.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package app.logorrr.conf

import app.logorrr.io.FilePaths
import app.logorrr.util.CanLog
import com.typesafe.config.ConfigRenderOptions
import pureconfig.ConfigSource

import java.nio.file.Path
import scala.util.{Failure, Success, Try}

/**
Expand All @@ -16,15 +16,13 @@ object SettingsIO extends CanLog {
val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false)

/** read settings from default place and filter all paths which don't exist anymore */
def fromFile(): Settings = {
val settingsFilePath = FilePaths.settingsFilePath

def fromFile(settingsFilePath : Path): Settings = {
Try(ConfigSource.file(settingsFilePath).loadOrThrow[Settings].filterWithValidPaths()) match {
case Failure(_) =>
logWarn(s"Could not load $settingsFilePath, using default settings ...")
Settings.Default
case Success(value) =>
logInfo(s"Loaded settings from $settingsFilePath.")
logTrace(s"Loaded settings from '$settingsFilePath'.")
value
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ class MutLogFileSettings {
filtersProperty.setAll(filters.asJava)
}

def setFilter(index: Int, filter: Filter): Unit = {
filtersProperty.set(index, filter)
}

val hasLogEntrySettingBinding: BooleanBinding = new BooleanBinding {
bind(someLogEntrySettingsProperty)

Expand Down
4 changes: 2 additions & 2 deletions app/src/main/scala/app/logorrr/conf/mut/MutSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class MutSettings {

def setSomeActive(path: Option[String]): Unit = someActiveLogProperty.set(path)

def getSomeActive: Option[String] = someActiveLogProperty.get()
def getSomeActiveLogFile: Option[String] = someActiveLogProperty.get()

def setLogFileSettings(logFileSettings: Map[String, LogFileSettings]): Unit = {
val m = for ((k, v) <- logFileSettings) yield {
Expand All @@ -81,7 +81,7 @@ class MutSettings {
val logFileSettings: Map[String, LogFileSettings] = (for ((k, v) <- mutLogFileSettingsMapProperty.get.asScala) yield {
k -> v.petrify()
}).toMap
Settings(mutStageSettings.petrify(), logFileSettings, getSomeActive, getSomeLastUsedDirectory)
Settings(mutStageSettings.petrify(), logFileSettings, getSomeActiveLogFile, getSomeLastUsedDirectory)
}

def setStageSettings(stageSettings: StageSettings): Unit = {
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/scala/app/logorrr/io/FileManager.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object FileManager extends CanLog {

def fromPathUsingSecurityBookmarks(logFile: Path): Seq[String] = {
if (OsUtil.enableSecurityBookmarks) {
logInfo(s"Registering security bookmark for `${logFile.toAbsolutePath.toString}`")
logTrace(s"Registering security bookmark for '${logFile.toAbsolutePath.toString}'")
OsxBridge.registerPath(logFile.toAbsolutePath.toString)
}
val lines = FileManager.fromPath(logFile)
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/scala/app/logorrr/util/JfxUtils.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package app.logorrr.util

import javafx.application.Platform
import javafx.beans.{InvalidationListener, Observable}
import javafx.beans.value.{ChangeListener, ObservableValue}
import javafx.collections.ListChangeListener
import javafx.scene.control.ListView
import javafx.stage.{Stage, WindowEvent}

object JfxUtils extends CanLog {

def scrollTo[T](lv: ListView[T], cellHeight: Int, relativeIndex: Int): Unit = {
val visibleItemCount = (lv.getHeight / cellHeight).asInstanceOf[Int] / 2
lv.scrollTo(relativeIndex - visibleItemCount)
}

def execOnUiThread(f: => Unit): Unit = {
if (Platform.isFxApplicationThread) {
f
Expand All @@ -25,6 +32,7 @@ object JfxUtils extends CanLog {
)
}

def mkInvalidationListener(invalidated: Observable => Unit): InvalidationListener = (observable: Observable) => invalidated(observable)

def onNew[T](f: T => Unit): ChangeListener[T] = (_: ObservableValue[_ <: T], _: T, t1: T) => f(t1)

Expand Down
33 changes: 5 additions & 28 deletions app/src/main/scala/app/logorrr/util/MathUtil.scala
Original file line number Diff line number Diff line change
@@ -1,38 +1,15 @@
package app.logorrr.util

import app.logorrr.model.LogEntry
import app.logorrr.views.block.BlockImage
import javafx.beans.property.{ReadOnlyDoubleProperty, SimpleIntegerProperty}

import scala.math.BigDecimal.RoundingMode

object MathUtil extends CanLog {
object MathUtil {

def roundUp(nrRows: Double): Int = {
BigDecimal.double2bigDecimal(nrRows).setScale(0, RoundingMode.UP).intValue
def roundUp(doubleNumber: Double): Int = {
BigDecimal.double2bigDecimal(doubleNumber).setScale(0, RoundingMode.UP).intValue
}

def roundDown(nrRows: Double): Int = {
BigDecimal.double2bigDecimal(nrRows).setScale(0, RoundingMode.DOWN).intValue
def roundDown(doubleNumber: Double): Int = {
BigDecimal.double2bigDecimal(doubleNumber).setScale(0, RoundingMode.DOWN).intValue
}

def calcBoundedHeight(widthProperty: ReadOnlyDoubleProperty
, blockSizeProperty: SimpleIntegerProperty
, entriesProperty: java.util.List[LogEntry]
, listViewHeightProperty: ReadOnlyDoubleProperty): (Int, Int, Int) = {
val w = if (widthProperty.get() - BlockImage.ScrollBarWidth >= 0) widthProperty.get() - BlockImage.ScrollBarWidth else widthProperty.get()

val cols: Int = MathUtil.roundUp(w / blockSizeProperty.get())
val rows: Int = if (entriesProperty.size() < cols) 1 else entriesProperty.size() / cols

// per default, use 4 cells per visible page, align height with blocksize such that
// we don't get artifacts. Further, make sure that the calculated height does not exceed
// MaxHeight of underlying texture painting mechanism.

// DO NOT REMOVE since the first division throws away the remainder and the multiplication
// yields the best approximation of MaxHeight.
val maxHeight = (BlockImage.MaxHeight / blockSizeProperty.get()) * blockSizeProperty.get()
val height = Math.min(MathUtil.roundDown((listViewHeightProperty.get() / BlockImage.DefaultBlocksPerPage) / blockSizeProperty.get()) * blockSizeProperty.get(), maxHeight)
(cols, rows, height)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,5 @@ class BlockImage(blockNumber: Int
, blockSizeProperty
, entries
, filtersProperty
, Array.fill(widthProperty.get().toInt * heightProperty.get())(ColorUtil.toARGB(Color.GREEN))
, Array.fill(widthProperty.get().toInt * heightProperty.get())(LPixelBuffer.defaultBackgroundColor)
, selectedLineNumberProperty)) with CanLog
26 changes: 25 additions & 1 deletion app/src/main/scala/app/logorrr/views/block/Chunk.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@ import scala.collection.mutable.ListBuffer

object Chunk {

def calcDimensions(widthProperty: ReadOnlyDoubleProperty
, blockSizeProperty: SimpleIntegerProperty
, listViewHeightProperty: ReadOnlyDoubleProperty): (Int, Int) = {
val w = if (widthProperty.get() - BlockImage.ScrollBarWidth >= 0) widthProperty.get() - BlockImage.ScrollBarWidth else widthProperty.get()

// to not get into division by zero territory
val cols: Int = if (w < blockSizeProperty.get()) 1 else MathUtil.roundUp(w / blockSizeProperty.get())

// per default, use 4 cells per visible page, align height with blocksize such that
// we don't get artifacts. Further, make sure that the calculated height does not exceed
// MaxHeight of underlying texture painting mechanism.

// DO NOT REMOVE since the first division throws away the remainder and the multiplication
// yields the best approximation of MaxHeight.
val maxHeight = (BlockImage.MaxHeight / blockSizeProperty.get()) * blockSizeProperty.get()
val heightCandidate = MathUtil.roundDown((listViewHeightProperty.get() / BlockImage.DefaultBlocksPerPage) / blockSizeProperty.get()) * blockSizeProperty.get()
// height is constrained by MaxHeight (which is 4096 currently in my experience) and BlockImage.DefaultBlocksPerPage x blocksize
// as a lower bound, otherwise we'll get later problems
val h2 = Math.max(heightCandidate, BlockImage.DefaultBlocksPerPage * blockSizeProperty.get())
val height = Math.min(h2, maxHeight)
(cols, height)
}


def mkChunks(entriesProperty: ObservableList[LogEntry]
, widthProperty: ReadOnlyDoubleProperty
, blockSizeProperty: SimpleIntegerProperty
Expand All @@ -17,7 +41,7 @@ object Chunk {
// nr of chunks is calculated as follows:
// how many entries fit into a chunk?
// given their size and the width and height of a chunk it is easy to calculate.
val (cols, _, height) = MathUtil.calcBoundedHeight(widthProperty, blockSizeProperty, entriesProperty, listViewHeightProperty)
val (cols, height) = calcDimensions(widthProperty, blockSizeProperty, listViewHeightProperty)
val nrElements = height / blockSizeProperty.get() * cols

val entriesSize = entriesProperty.size()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ChunkListCell(selectedLineNumberProperty: SimpleIntegerProperty
, widthProperty: ReadOnlyDoubleProperty
, blockSizeProperty: SimpleIntegerProperty
, filtersProperty: ObservableList[Filter]
, selectInTextView: LogEntry => Unit
, scrollTo: LogEntry => Unit
) extends ListCell[Chunk] with CanLog {

// if user selects an entry in the ChunkListView set selectedLineNumberProperty. This property is observed
Expand All @@ -32,10 +32,10 @@ class ChunkListCell(selectedLineNumberProperty: SimpleIntegerProperty
val index = BlockImage.indexOf(me.getX.toInt, me.getY.toInt, blockSizeProperty.get, widthProperty.get.toInt - BlockImage.ScrollBarWidth)
getEntryAt(getItem, index) match {
case Some(value) =>
// set selected property such that the next repaint will highlight this entry
selectedLineNumberProperty.set(value.lineNumber)
// we have to select also the entry in the LogTextView, couldn't get it to work with bindings / listeners
selectInTextView(value)
scrollTo(value)

case None => System.err.println("no element found")
}
}
Expand Down

0 comments on commit 06d9bbb

Please sign in to comment.