diff --git a/app/src/main/scala/app/logorrr/views/ops/CopyLogButton.scala b/app/src/main/scala/app/logorrr/views/ops/CopyLogButton.scala index 4a178406..2be5d911 100644 --- a/app/src/main/scala/app/logorrr/views/ops/CopyLogButton.scala +++ b/app/src/main/scala/app/logorrr/views/ops/CopyLogButton.scala @@ -1,23 +1,78 @@ package app.logorrr.views.ops import app.logorrr.model.LogEntry +import javafx.animation.AnimationTimer import javafx.collections.ObservableList import javafx.scene.control.{Button, Tooltip} import javafx.scene.input.{Clipboard, ClipboardContent} -import org.kordamp.ikonli.fontawesome5.FontAwesomeSolid +import org.kordamp.ikonli.fontawesome5.{FontAwesomeRegular, FontAwesomeSolid} import org.kordamp.ikonli.javafx.FontIcon import scala.collection.mutable.ListBuffer +/** + * Copy current contents to clipboard. + * + * @param logEntries current active log entries + */ class CopyLogButton(logEntries: ObservableList[LogEntry]) extends Button { + private val icon = new FontIcon(FontAwesomeSolid.COPY) - setGraphic(icon) - setTooltip(new Tooltip("copy current selection to clipboard")) - setOnAction(_ => { - val lb = new ListBuffer[String] - logEntries.forEach(e => lb.addOne(e.value)) - copyToClipboard(lb.mkString("\n")) - }) + private val iconLight = new FontIcon(FontAwesomeRegular.COPY) + private val TooltipText = "copy current selection to clipboard" + private val defaultToolTip = new Tooltip(TooltipText) + + init() + + def init(): Unit = { + setGraphic(icon) + setTooltip(defaultToolTip) + + setOnAction(_ => { + val lb = new ListBuffer[String] + logEntries.forEach(e => lb.addOne(e.value)) + copyToClipboard(lb.mkString("\n")) + defaultToolTip.setText(s"Copied ${lb.size} entries to clipboard") + val bounds = localToScreen(getBoundsInLocal) + val x = bounds.getMinX + val y = bounds.getMaxY + defaultToolTip.show(this,x,y) + + mkTimer().start() // visual response to click + }) + } + + def mkTimer(): AnimationTimer = new AnimationTimer() { + private var startTime: Long = -1 + + def handle(now: Long): Unit = { + if (startTime < 0) { + setGraphic(iconLight) + startTime = now + } + + val elapsedSeconds = (now - startTime) / 1_000_000_000.0 + + // Stop the animation after 1 second + if (elapsedSeconds > 1) { + getGraphic.setOpacity(1) // Ensure it ends at full opacity + setGraphic(icon) + defaultToolTip.hide() + defaultToolTip.setText(TooltipText) + this.stop() + return + } + val alpha = elapsedSeconds / 1 // From 0 to 1 in one second + + if (alpha <= 0.5) { + // Fade out for the first half + getGraphic.setOpacity(1 - 2 * alpha) + } else { + // Fade in for the second half + getGraphic.setOpacity(2 * alpha - 1) + } + } + } def copyToClipboard(text: String): Unit = { val clipboard = Clipboard.getSystemClipboard diff --git a/app/src/main/scala/app/logorrr/views/search/OpsToolBar.scala b/app/src/main/scala/app/logorrr/views/search/OpsToolBar.scala index 809ff30e..8553bb63 100644 --- a/app/src/main/scala/app/logorrr/views/search/OpsToolBar.scala +++ b/app/src/main/scala/app/logorrr/views/search/OpsToolBar.scala @@ -64,8 +64,9 @@ class OpsToolBar(pathAsString: String val copySelectionButton = new CopyLogButton(filteredList) - val firstNEntries: ObservableList[LogEntry] = TimerSettingsLogView.mkEntriesToShow(logEntries) - val timerButton = new TimerButton(pathAsString, firstNEntries) +// val firstNEntries: ObservableList[LogEntry] = TimerSettingsLogView.mkEntriesToShow(logEntries) + +// val timerButton = new TimerButton(pathAsString, firstNEntries) def execSearchOnHitEnter(event: KeyEvent): Unit = { if (event.getCode == KeyCode.ENTER) { diff --git a/docs/ffmpeg.md b/docs/ffmpeg.md index 44c74ff2..62bd1e11 100644 --- a/docs/ffmpeg.md +++ b/docs/ffmpeg.md @@ -1,5 +1,4 @@ -# Convert mov to gif +# Convert mp4 to gif + +ffmpeg -i \#177-copy-to-clipboard.mp4 -vf "fps=10,scale=320:-1:flags=lanczos" -c:v gif -f gif \#177-copy-to-clipboard.gif -ffmpeg -i logorrr-poc.mov -pix_fmt rgb8 -s 389x308 out.gif -convert out.gif -verbose -coalesce -layers OptimizeFrame out-preopt.gif -gifsicle -O2 out-preopt.gif -o out-final.gif