Add context menu to Ocelot component log entries

This commit is contained in:
UnicornFreedom 2025-08-22 14:24:11 +02:00
parent 6db5ff3f37
commit cf088ce9f7
No known key found for this signature in database
GPG Key ID: B4ED0DB6B940024F

View File

@ -3,9 +3,13 @@ package ocelot.desktop.ui.widget
import ocelot.desktop.ColorScheme import ocelot.desktop.ColorScheme
import ocelot.desktop.color.RGBAColorNorm import ocelot.desktop.color.RGBAColorNorm
import ocelot.desktop.geometry.{Padding2D, Rect2D, Size2D, Vector2D} import ocelot.desktop.geometry.{Padding2D, Rect2D, Size2D, Vector2D}
import ocelot.desktop.graphics.Graphics import ocelot.desktop.graphics.{Graphics, IconSource}
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.event.{ClickEvent, MouseEvent}
import ocelot.desktop.ui.event.handlers.MouseHandler
import ocelot.desktop.ui.layout.{Layout, LinearLayout} import ocelot.desktop.ui.layout.{Layout, LinearLayout}
import ocelot.desktop.ui.widget.LogWidget.{BorderThickness, EntryMargin, EntryPadding, LogEntry} import ocelot.desktop.ui.widget.LogWidget.{BorderThickness, EntryMargin, EntryPadding, LogEntry}
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
import ocelot.desktop.util.{DrawUtils, Orientation} import ocelot.desktop.util.{DrawUtils, Orientation}
import scala.annotation.tailrec import scala.annotation.tailrec
@ -14,13 +18,27 @@ import scala.collection.mutable
abstract class LogWidget extends Widget { abstract class LogWidget extends Widget {
override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical) override protected val layout: Layout = new LinearLayout(this, orientation = Orientation.Vertical)
private object MessageListWidget extends Widget { messageList => private object MessageListWidget extends Widget with MouseHandler { messageList =>
override protected val layout: Layout = new Layout(this) override protected val layout: Layout = new Layout(this)
private val entries = mutable.ArrayDeque.empty[Entry] private val entries = mutable.ArrayDeque.empty[Entry]
val dummyEntry: Entry = new RxEntry("", nextMessageY) val dummyEntry: Entry = new RxEntry("", nextMessageY)
override def receiveClickEvents: Boolean = true
eventHandlers += {
case ClickEvent(MouseEvent.Button.Right, mouse) =>
visibleEntries.find(_.absoluteBounds.contains(mouse)).foreach {
case entry: MessageEntry =>
val menu = new ContextMenu
menu.addEntry(ContextMenuEntry("Copy", IconSource.Icons.Copy) {
UiHandler.clipboard = entry.message
})
root.get.contextMenus.open(menu)
}
}
private def removedOffset: Float = entries.headOption.map(_.y - EntryMargin).getOrElse(0) private def removedOffset: Float = entries.headOption.map(_.y - EntryMargin).getOrElse(0)
private def applyRemovedOffset(): Unit = { private def applyRemovedOffset(): Unit = {
@ -72,15 +90,16 @@ abstract class LogWidget extends Widget {
} }
} }
override def draw(g: Graphics): Unit = { private def visibleEntries: Iterator[Entry] = {
applyRemovedOffset()
val firstVisibleIdx = firstVisibleIdxSearch(parent.get.asInstanceOf[ScrollView].offset.y) val firstVisibleIdx = firstVisibleIdxSearch(parent.get.asInstanceOf[ScrollView].offset.y)
entries.iterator.drop(firstVisibleIdx).takeWhile(_.absoluteBounds.y <= clippedBounds.max.y)
for (entry <- entries.iterator.drop(firstVisibleIdx).takeWhile(_.absoluteBounds.y <= clippedBounds.max.y)) {
entry.draw(g)
} }
override def draw(g: Graphics): Unit = {
applyRemovedOffset()
for (entry <- visibleEntries) {
entry.draw(g)
}
super.draw(g) super.draw(g)
} }
@ -100,7 +119,7 @@ abstract class LogWidget extends Widget {
final def absoluteBounds: Rect2D = Rect2D(position + messageList.position, size) final def absoluteBounds: Rect2D = Rect2D(position + messageList.position, size)
} }
private abstract class MessageEntry(message: String, override var y: Float) extends Entry { private abstract class MessageEntry(val message: String, override var y: Float) extends Entry {
private val lines = Text.wrap(message, Some(textWidth)) private val lines = Text.wrap(message, Some(textWidth))
def minimumSize: Size2D = Size2D( def minimumSize: Size2D = Size2D(