From cf088ce9f7b6c6036d82fe48cfee9fa57a56b26f Mon Sep 17 00:00:00 2001 From: UnicornFreedom Date: Fri, 22 Aug 2025 14:24:11 +0200 Subject: [PATCH] Add context menu to Ocelot component log entries --- .../ocelot/desktop/ui/widget/LogWidget.scala | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala b/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala index b9953b6..8b8f033 100644 --- a/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala +++ b/src/main/scala/ocelot/desktop/ui/widget/LogWidget.scala @@ -3,9 +3,13 @@ package ocelot.desktop.ui.widget import ocelot.desktop.ColorScheme import ocelot.desktop.color.RGBAColorNorm 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.widget.LogWidget.{BorderThickness, EntryMargin, EntryPadding, LogEntry} +import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry} import ocelot.desktop.util.{DrawUtils, Orientation} import scala.annotation.tailrec @@ -14,13 +18,27 @@ import scala.collection.mutable abstract class LogWidget extends Widget { 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) private val entries = mutable.ArrayDeque.empty[Entry] 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 applyRemovedOffset(): Unit = { @@ -72,15 +90,16 @@ abstract class LogWidget extends Widget { } } + private def visibleEntries: Iterator[Entry] = { + val firstVisibleIdx = firstVisibleIdxSearch(parent.get.asInstanceOf[ScrollView].offset.y) + entries.iterator.drop(firstVisibleIdx).takeWhile(_.absoluteBounds.y <= clippedBounds.max.y) + } + override def draw(g: Graphics): Unit = { applyRemovedOffset() - - val firstVisibleIdx = firstVisibleIdxSearch(parent.get.asInstanceOf[ScrollView].offset.y) - - for (entry <- entries.iterator.drop(firstVisibleIdx).takeWhile(_.absoluteBounds.y <= clippedBounds.max.y)) { + for (entry <- visibleEntries) { entry.draw(g) } - super.draw(g) } @@ -100,7 +119,7 @@ abstract class LogWidget extends Widget { 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)) def minimumSize: Size2D = Size2D(