mirror of
https://gitlab.com/cc-ru/ocelot/ocelot-desktop.git
synced 2025-12-19 18:49:19 +01:00
Allow selecting current word by double click
This commit is contained in:
parent
a8dc52f1b2
commit
4e9d7c96e2
@ -0,0 +1,5 @@
|
||||
package ocelot.desktop.ui.event
|
||||
|
||||
import ocelot.desktop.geometry.Vector2D
|
||||
|
||||
case class DoubleClickEvent(button: MouseEvent.Button.Value, mousePos: Vector2D) extends Event
|
||||
@ -2,8 +2,8 @@ package ocelot.desktop.ui.event.handlers
|
||||
|
||||
import ocelot.desktop.geometry.Vector2D
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.event.handlers.MouseHandler.Tolerance
|
||||
import ocelot.desktop.ui.event.{ClickEvent, DragEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.event.handlers.MouseHandler.{DoubleClickTime, Tolerance}
|
||||
import ocelot.desktop.ui.event.{ClickEvent, DoubleClickEvent, DragEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.Widget
|
||||
|
||||
import scala.collection.mutable
|
||||
@ -13,13 +13,18 @@ trait MouseHandler extends Widget {
|
||||
private val prevPositions = new mutable.HashMap[MouseEvent.Button.Value, Vector2D]()
|
||||
private val dragButtons = new mutable.HashSet[MouseEvent.Button.Value]()
|
||||
|
||||
private var lastClickPosition: Vector2D = Vector2D.Zero
|
||||
private var lastClickTime: Long = 0
|
||||
private var lastClickButton = MouseEvent.Button.Left
|
||||
|
||||
override def receiveMouseEvents: Boolean = receiveClickEvents || receiveDragEvents
|
||||
|
||||
protected def receiveClickEvents: Boolean = false
|
||||
|
||||
protected def receiveDragEvents: Boolean = false
|
||||
|
||||
/** If `true`, a [[ClickEvent]] will be registered even if the mouse button is released
|
||||
/**
|
||||
* If `true`, a [[ClickEvent]] will be registered even if the mouse button is released
|
||||
* outside the tolerance threshold, as long as it stays within the widget's bounds.
|
||||
*/
|
||||
protected def allowClickReleaseOutsideThreshold: Boolean = !receiveDragEvents
|
||||
@ -39,7 +44,7 @@ trait MouseHandler extends Widget {
|
||||
if (allowClickReleaseOutsideThreshold) {
|
||||
clippedBounds.contains(mousePos)
|
||||
} else {
|
||||
(p - mousePos).lengthSquared < Tolerance * Tolerance
|
||||
withinTolerance(p, mousePos)
|
||||
}
|
||||
})
|
||||
)
|
||||
@ -50,11 +55,23 @@ trait MouseHandler extends Widget {
|
||||
|
||||
if (clicked) {
|
||||
handleEvent(ClickEvent(button, mousePos))
|
||||
|
||||
val inTimeForDoubleClick = (System.currentTimeMillis() - lastClickTime) < DoubleClickTime * 1000
|
||||
val sameButton = lastClickButton == button
|
||||
val roughlySamePosition = withinTolerance(lastClickPosition, mousePos)
|
||||
if (inTimeForDoubleClick && sameButton && roughlySamePosition) {
|
||||
handleEvent(DoubleClickEvent(button, mousePos))
|
||||
}
|
||||
lastClickTime = System.currentTimeMillis()
|
||||
lastClickPosition = mousePos
|
||||
lastClickButton = button
|
||||
}
|
||||
|
||||
startPositions.remove(button)
|
||||
}
|
||||
|
||||
private def withinTolerance(a: Vector2D, b: Vector2D): Boolean = (b - a).lengthSquared < Tolerance * Tolerance
|
||||
|
||||
override def update(): Unit = {
|
||||
super.update()
|
||||
|
||||
@ -65,7 +82,7 @@ trait MouseHandler extends Widget {
|
||||
val mousePos = UiHandler.mousePosition
|
||||
|
||||
for ((button, startPos) <- startPositions) {
|
||||
if (!dragButtons.contains(button) && (startPos - mousePos).lengthSquared > Tolerance * Tolerance) {
|
||||
if (!dragButtons.contains(button) && !withinTolerance(startPos, mousePos)) {
|
||||
handleEvent(DragEvent(DragEvent.State.Start, button, mousePos, startPos, Vector2D(0, 0)))
|
||||
dragButtons += button
|
||||
prevPositions += (button -> mousePos)
|
||||
@ -90,4 +107,5 @@ trait MouseHandler extends Widget {
|
||||
|
||||
object MouseHandler {
|
||||
private val Tolerance = 8
|
||||
private val DoubleClickTime = 0.2
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import ocelot.desktop.graphics.{Font, Graphics, IconSource}
|
||||
import ocelot.desktop.ui.UiHandler
|
||||
import ocelot.desktop.ui.event.handlers.MouseHandler
|
||||
import ocelot.desktop.ui.event.sources.KeyEvents
|
||||
import ocelot.desktop.ui.event.{DragEvent, KeyEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.event.{DoubleClickEvent, DragEvent, KeyEvent, MouseEvent}
|
||||
import ocelot.desktop.ui.widget.TextInput.{Cursor, Selector, Text}
|
||||
import ocelot.desktop.ui.widget.contextmenu.{ContextMenu, ContextMenuEntry}
|
||||
import ocelot.desktop.ui.widget.traits.HoverAnimation
|
||||
@ -15,6 +15,8 @@ import ocelot.desktop.util.{DrawUtils, Register, Watcher}
|
||||
import ocelot.desktop.util.animation.ColorAnimation
|
||||
import org.lwjgl.input.Keyboard
|
||||
|
||||
import java.lang.Character.isWhitespace
|
||||
|
||||
|
||||
class TextInput(val initialText: String = "") extends Widget with MouseHandler with HoverAnimation {
|
||||
private val CursorBlinkTime = 2f
|
||||
@ -98,8 +100,12 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
override def minimumSize: Size2D = Size2D(200, 24)
|
||||
override def maximumSize: Size2D = Size2D(Float.PositiveInfinity, 24)
|
||||
|
||||
override def receiveAllMouseEvents = true
|
||||
override def receiveDragEvents: Boolean = true
|
||||
override def receiveClickEvents: Boolean = true
|
||||
|
||||
private def mouseInBounds: Boolean = clippedBounds.contains(UiHandler.mousePosition)
|
||||
|
||||
protected def font: Font = Font.NormalFont
|
||||
|
||||
@ -121,7 +127,7 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w
|
||||
|
||||
eventHandlers += {
|
||||
case MouseEvent(MouseEvent.State.Pressed, MouseEvent.Button.Left) if enabled =>
|
||||
val inBounds = clippedBounds.contains(UiHandler.mousePosition)
|
||||
val inBounds = mouseInBounds
|
||||
if (isFocused && !inBounds) unfocus()
|
||||
if (!isFocused && inBounds) focus()
|
||||
if (isFocused) {
|
||||
@ -132,8 +138,7 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w
|
||||
}
|
||||
|
||||
case MouseEvent(MouseEvent.State.Pressed, MouseEvent.Button.Right) if isFocused =>
|
||||
val inBounds = clippedBounds.contains(UiHandler.mousePosition)
|
||||
if (inBounds) {
|
||||
if (mouseInBounds) {
|
||||
val menu = new ContextMenu
|
||||
if (selector.active) {
|
||||
menu.addEntry(ContextMenuEntry("Cut", IconSource.Icons.Cut) { cutSelection() })
|
||||
@ -149,6 +154,12 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w
|
||||
root.get.contextMenus.open(menu)
|
||||
}
|
||||
|
||||
case event @ DoubleClickEvent(MouseEvent.Button.Left, _) if isFocused =>
|
||||
if (mouseInBounds) {
|
||||
selectWord()
|
||||
event.consume()
|
||||
}
|
||||
|
||||
case event @ DragEvent(DragEvent.State.Start, MouseEvent.Button.Left, mouse) if isFocused =>
|
||||
val pos = pixelToCursorPosition(mouse.x - bounds.x)
|
||||
selector.active = true
|
||||
@ -238,6 +249,11 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w
|
||||
selectAll()
|
||||
event.consume()
|
||||
|
||||
case event @ KeyEvent(KeyEvent.State.Press | KeyEvent.State.Repeat, Keyboard.KEY_W, _)
|
||||
if isFocused && KeyEvents.isControlDown =>
|
||||
selectWord()
|
||||
event.consume()
|
||||
|
||||
case event @ KeyEvent(KeyEvent.State.Press | KeyEvent.State.Repeat, Keyboard.KEY_C, _)
|
||||
if isFocused && KeyEvents.isControlDown && selector.active =>
|
||||
copySelection()
|
||||
@ -288,6 +304,13 @@ class TextInput(val initialText: String = "") extends Widget with MouseHandler w
|
||||
selector.to = _text.chars.length
|
||||
}
|
||||
|
||||
private def selectWord(): Unit = {
|
||||
selector.active = true
|
||||
selector.from = 0 max (_text.chars.lastIndexWhere(isWhitespace, cursor.position - 1) + 1)
|
||||
val to = _text.chars.indexWhere(isWhitespace, cursor.position)
|
||||
selector.to = if (to >= 0 && to < _text.chars.length) to else _text.chars.length
|
||||
}
|
||||
|
||||
private def copySelection(): Unit = {
|
||||
UiHandler.clipboard = selectedText
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user