mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-23 23:34:43 +08:00
Input refactoring (#12)
* Fix folder name typo * Smaller fixes * Shortcut rename to Shortcuts * Fix quoted attributes in UE 5.3 * remove KeyboardShortcutAction * Remove more trivial classes * Rename IKeyboardShortcut * Node delete shortcut
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
export default class Shortcut {
|
||||
export default class Shortcuts {
|
||||
static deleteNodes = "Delete"
|
||||
static duplicateNodes = "(bCtrl=True,Key=D)"
|
||||
static selectAllNodes = "(bCtrl=True,Key=A)"
|
||||
static enableLinkDelete = "LeftAlt"
|
||||
static enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 1:1 zoom
|
||||
static selectAllNodes = "(bCtrl=True,Key=A)"
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
export default class Action {
|
||||
|
||||
apply() {
|
||||
#blueprint
|
||||
get blueprint() {
|
||||
return this.#blueprint
|
||||
}
|
||||
|
||||
revert() {
|
||||
constructor(blueprint) {
|
||||
this.#blueprint = blueprint
|
||||
}
|
||||
|
||||
fire() {
|
||||
}
|
||||
}
|
||||
|
||||
8
js/action/RemoveAllNodes.js
Normal file
8
js/action/RemoveAllNodes.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import Action from "./Actions"
|
||||
|
||||
export default class RemoveAllNodes extends Action {
|
||||
|
||||
fire() {
|
||||
this.blueprint.removeGraphElement(...this.blueprint.getNodes(true))
|
||||
}
|
||||
}
|
||||
@@ -129,12 +129,4 @@ export default class IElement extends LitElement {
|
||||
isSameGraph(element) {
|
||||
return this.blueprint && this.blueprint == element?.blueprint
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {IInput} V
|
||||
* @param {new (...args: any[]) => V} type
|
||||
*/
|
||||
getInputObject(type) {
|
||||
return /** @type {V} */(this.template.inputObjects.find(object => object.constructor == type))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import Configuration from "../Configuration.js"
|
||||
|
||||
/** @typedef {import("../Blueprint.js").default} Blueprint */
|
||||
|
||||
/** @template {HTMLElement} T */
|
||||
/** @template {Element} T */
|
||||
export default class IInput {
|
||||
|
||||
/** @type {T} */
|
||||
@@ -17,6 +17,8 @@ export default class IInput {
|
||||
return this.#blueprint
|
||||
}
|
||||
|
||||
consumeEvent = true
|
||||
|
||||
/** @type {Object} */
|
||||
options
|
||||
|
||||
@@ -35,6 +37,7 @@ export default class IInput {
|
||||
options.unlistenOnTextEdit ??= false
|
||||
this.#target = target
|
||||
this.#blueprint = blueprint
|
||||
this.consumeEvent = options.consumeEvent
|
||||
this.options = options
|
||||
}
|
||||
|
||||
|
||||
8
js/input/InputCombination.js
Normal file
8
js/input/InputCombination.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import IInput from "./IInput"
|
||||
|
||||
/** @typedef {import("../Blueprint.js").default} Blueprint */
|
||||
|
||||
export default class InputCombination {
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export default class Copy extends IInput {
|
||||
options.unlistenOnTextEdit ??= true // No nodes copy if inside a text field, just text (default behavior)
|
||||
super(target, blueprint, options)
|
||||
let self = this
|
||||
this.#copyHandler = _ => self.copied()
|
||||
this.#copyHandler = () => self.copied()
|
||||
}
|
||||
|
||||
listenEvents() {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import IInput from "../IInput.js"
|
||||
import ObjectSerializer from "../../serialization/ObjectSerializer.js"
|
||||
import KeyboardCanc from "../keybaord/KeyboardCanc.js"
|
||||
|
||||
export default class Cut extends IInput {
|
||||
|
||||
@@ -14,7 +13,7 @@ export default class Cut extends IInput {
|
||||
options.unlistenOnTextEdit ??= true // No nodes copy if inside a text field, just text (default behavior)
|
||||
super(target, blueprint, options)
|
||||
let self = this
|
||||
this.#cutHandler = _ => self.cut()
|
||||
this.#cutHandler = () => self.cut()
|
||||
}
|
||||
|
||||
listenEvents() {
|
||||
@@ -34,6 +33,6 @@ export default class Cut extends IInput {
|
||||
|
||||
cut() {
|
||||
this.blueprint.template.getCopyInputObject().copied()
|
||||
this.blueprint.template.getInputObject(KeyboardCanc).fire()
|
||||
this.blueprint.removeGraphElement(...this.blueprint.getNodes(true))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import IKeyboardShortcut from "./IKeyboardShortcut.js"
|
||||
import Shortcut from "../../Shortcut.js"
|
||||
|
||||
/** @typedef {import("../../Blueprint.js").default} Blueprint */
|
||||
|
||||
export default class KeyboardCanc extends IKeyboardShortcut {
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} target
|
||||
* @param {Blueprint} blueprint
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(target, blueprint, options = {}) {
|
||||
options.activationKeys = Shortcut.deleteNodes
|
||||
super(target, blueprint, options)
|
||||
}
|
||||
|
||||
fire() {
|
||||
this.blueprint.removeGraphElement(...this.blueprint.getNodes(true))
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import IKeyboardShortcut from "./IKeyboardShortcut.js"
|
||||
import Shortcut from "../../Shortcut.js"
|
||||
|
||||
/** @typedef {import("../../Blueprint.js").default} Blueprint */
|
||||
|
||||
export default class KeyboardSelectAll extends IKeyboardShortcut {
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} target
|
||||
* @param {Blueprint} blueprint
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(target, blueprint, options = {}) {
|
||||
options.activationKeys = Shortcut.selectAllNodes
|
||||
super(target, blueprint, options)
|
||||
}
|
||||
|
||||
fire() {
|
||||
this.blueprint.selectAll()
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import IKeyboardShortcut from "./IKeyboardShortcut.js"
|
||||
|
||||
/** @typedef {import("../../Blueprint.js").default} Blueprint */
|
||||
|
||||
/**
|
||||
* @template {HTMLElement} T
|
||||
* @extends IKeyboardShortcut<T>
|
||||
*/
|
||||
export default class KeyboardShortcutAction extends IKeyboardShortcut {
|
||||
|
||||
static #ignoreEvent =
|
||||
/** @param {KeyboardShortcutAction} self */
|
||||
self => { }
|
||||
|
||||
/**
|
||||
* @param {T} target
|
||||
* @param {Blueprint} blueprint
|
||||
* @param {Object} options
|
||||
* @param {(self: KeyboardShortcutAction<T>) => void} onKeyDown
|
||||
* @param {(self: KeyboardShortcutAction<T>) => void} onKeyUp
|
||||
*/
|
||||
constructor(
|
||||
target,
|
||||
blueprint,
|
||||
options,
|
||||
onKeyDown = KeyboardShortcutAction.#ignoreEvent,
|
||||
onKeyUp = KeyboardShortcutAction.#ignoreEvent
|
||||
) {
|
||||
super(target, blueprint, options)
|
||||
this.onKeyDown = onKeyDown
|
||||
this.onKeyUp = onKeyUp
|
||||
}
|
||||
|
||||
fire() {
|
||||
this.onKeyDown(this)
|
||||
}
|
||||
|
||||
unfire() {
|
||||
this.onKeyUp(this)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import IKeyboardShortcut from "./IKeyboardShortcut.js"
|
||||
import Shortcut from "../../Shortcut.js"
|
||||
import KeyboardShortcut from "./KeyboardShortcut.js"
|
||||
import Shortcuts from "../../Shortcuts.js"
|
||||
import Zoom from "../mouse/Zoom.js"
|
||||
|
||||
/** @typedef {import("../../Blueprint.js").default} Blueprint */
|
||||
|
||||
export default class KeyboardEnableZoom extends IKeyboardShortcut {
|
||||
export default class KeyboardEnableZoom extends KeyboardShortcut {
|
||||
|
||||
/** @type {Zoom} */
|
||||
#zoomInputObject
|
||||
@@ -15,12 +15,12 @@ export default class KeyboardEnableZoom extends IKeyboardShortcut {
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(target, blueprint, options = {}) {
|
||||
options.activationKeys = Shortcut.enableZoomIn
|
||||
options.activationKeys = Shortcuts.enableZoomIn
|
||||
super(target, blueprint, options)
|
||||
}
|
||||
|
||||
fire() {
|
||||
this.#zoomInputObject = this.blueprint.getInputObject(Zoom)
|
||||
this.#zoomInputObject = this.blueprint.template.getZoomInputObject()
|
||||
this.#zoomInputObject.enableZoonIn = true
|
||||
}
|
||||
|
||||
@@ -6,10 +6,14 @@ import KeyBindingEntity from "../../entity/KeyBindingEntity.js"
|
||||
/** @typedef {import("../../Blueprint.js").default} Blueprint */
|
||||
|
||||
/**
|
||||
* @template {HTMLElement} T
|
||||
* @template {Element} T
|
||||
* @extends IInput<T>
|
||||
*/
|
||||
export default class IKeyboardShortcut extends IInput {
|
||||
export default class KeyboardShortcut extends IInput {
|
||||
|
||||
static #ignoreEvent =
|
||||
/** @param {KeyboardShortcut} self */
|
||||
self => { }
|
||||
|
||||
/** @type {KeyBindingEntity[]} */
|
||||
#activationKeys
|
||||
@@ -21,8 +25,13 @@ export default class IKeyboardShortcut extends IInput {
|
||||
* @param {Blueprint} blueprint
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(target, blueprint, options = {}) {
|
||||
options.activateAnyKey ??= false
|
||||
constructor(
|
||||
target,
|
||||
blueprint,
|
||||
options = {},
|
||||
onKeyDown = KeyboardShortcut.#ignoreEvent,
|
||||
onKeyUp = KeyboardShortcut.#ignoreEvent
|
||||
) {
|
||||
options.activationKeys ??= []
|
||||
options.consumeEvent ??= true
|
||||
options.listenOnFocus ??= true
|
||||
@@ -44,6 +53,8 @@ export default class IKeyboardShortcut extends IInput {
|
||||
})
|
||||
|
||||
super(target, blueprint, options)
|
||||
this.onKeyDown = onKeyDown
|
||||
this.onKeyUp = onKeyUp
|
||||
|
||||
this.#activationKeys = this.options.activationKeys ?? []
|
||||
|
||||
@@ -55,15 +66,14 @@ export default class IKeyboardShortcut extends IInput {
|
||||
/** @param {KeyboardEvent} e */
|
||||
this.keyDownHandler = e => {
|
||||
if (
|
||||
this.options.activateAnyKey
|
||||
|| self.#activationKeys.some(keyEntry =>
|
||||
self.#activationKeys.some(keyEntry =>
|
||||
wantsShift(keyEntry) == e.shiftKey
|
||||
&& wantsCtrl(keyEntry) == e.ctrlKey
|
||||
&& wantsAlt(keyEntry) == e.altKey
|
||||
&& Configuration.Keys[keyEntry.Key] == e.code
|
||||
&& Configuration.Keys[keyEntry.Key.value] == e.code
|
||||
)
|
||||
) {
|
||||
if (options.consumeEvent) {
|
||||
if (this.consumeEvent) {
|
||||
e.preventDefault()
|
||||
e.stopImmediatePropagation()
|
||||
}
|
||||
@@ -77,16 +87,15 @@ export default class IKeyboardShortcut extends IInput {
|
||||
/** @param {KeyboardEvent} e */
|
||||
this.keyUpHandler = e => {
|
||||
if (
|
||||
this.options.activateAnyKey
|
||||
|| self.#activationKeys.some(keyEntry =>
|
||||
self.#activationKeys.some(keyEntry =>
|
||||
keyEntry.bShift && e.key == "Shift"
|
||||
|| keyEntry.bCtrl && e.key == "Control"
|
||||
|| keyEntry.bAlt && e.key == "Alt"
|
||||
|| keyEntry.bCmd && e.key == "Meta"
|
||||
|| Configuration.Keys[keyEntry.Key] == e.code
|
||||
|| Configuration.Keys[keyEntry.Key.value] == e.code
|
||||
)
|
||||
) {
|
||||
if (options.consumeEvent) {
|
||||
if (this.consumeEvent) {
|
||||
e.stopImmediatePropagation()
|
||||
}
|
||||
self.unfire()
|
||||
@@ -105,11 +114,13 @@ export default class IKeyboardShortcut extends IInput {
|
||||
document.removeEventListener("keydown", this.keyDownHandler)
|
||||
}
|
||||
|
||||
// Subclasses will want to override
|
||||
/* Subclasses can override */
|
||||
|
||||
fire() {
|
||||
this.onKeyDown(this)
|
||||
}
|
||||
|
||||
unfire() {
|
||||
this.onKeyUp(this)
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,17 @@ export default class IMouseClickDrag extends IPointing {
|
||||
case this.options.clickButton:
|
||||
// Either doesn't matter or consider the click only when clicking on the parent, not descandants
|
||||
if (!this.options.strictTarget || e.target == e.currentTarget) {
|
||||
if (this.options.consumeEvent) {
|
||||
if (this.consumeEvent) {
|
||||
e.stopImmediatePropagation() // Captured, don't call anyone else
|
||||
}
|
||||
// Attach the listeners
|
||||
this.#movementListenedElement.addEventListener("mousemove", this.#mouseStartedMovingHandler)
|
||||
document.addEventListener("mouseup", this.#mouseUpHandler)
|
||||
this.clickedPosition = this.locationFromEvent(e)
|
||||
this.blueprint.mousePosition[0] = this.clickedPosition[0]
|
||||
this.blueprint.mousePosition[1] = this.clickedPosition[1]
|
||||
this.setLocationFromEvent(e)
|
||||
this.clickedPosition[0] = this.location[0]
|
||||
this.clickedPosition[1] = this.location[1]
|
||||
this.blueprint.mousePosition[0] = this.location[0]
|
||||
this.blueprint.mousePosition[1] = this.location[1]
|
||||
if (this.target instanceof IDraggableElement) {
|
||||
this.clickedOffset = [
|
||||
this.clickedPosition[0] - this.target.locationX,
|
||||
@@ -49,7 +51,7 @@ export default class IMouseClickDrag extends IPointing {
|
||||
|
||||
/** @param {MouseEvent} e */
|
||||
#mouseStartedMovingHandler = e => {
|
||||
if (this.options.consumeEvent) {
|
||||
if (this.consumeEvent) {
|
||||
e.stopImmediatePropagation() // Captured, don't call anyone else
|
||||
}
|
||||
// Delegate from now on to this.#mouseMoveHandler
|
||||
@@ -58,19 +60,19 @@ export default class IMouseClickDrag extends IPointing {
|
||||
// Handler calls e.preventDefault() when it receives the event, this means dispatchEvent returns false
|
||||
const dragEvent = this.getEvent(Configuration.trackingMouseEventName.begin)
|
||||
this.#trackingMouse = this.target.dispatchEvent(dragEvent) == false
|
||||
const location = this.locationFromEvent(e)
|
||||
this.setLocationFromEvent(e)
|
||||
// Do actual actions
|
||||
this.lastLocation = Utility.snapToGrid(this.clickedPosition[0], this.clickedPosition[1], this.stepSize)
|
||||
this.startDrag(location)
|
||||
this.startDrag(this.location)
|
||||
this.started = true
|
||||
}
|
||||
|
||||
/** @param {MouseEvent} e */
|
||||
#mouseMoveHandler = e => {
|
||||
if (this.options.consumeEvent) {
|
||||
if (this.consumeEvent) {
|
||||
e.stopImmediatePropagation() // Captured, don't call anyone else
|
||||
}
|
||||
const location = this.locationFromEvent(e)
|
||||
const location = this.setLocationFromEvent(e)
|
||||
const movement = [e.movementX, e.movementY]
|
||||
this.dragTo(location, movement)
|
||||
if (this.#trackingMouse) {
|
||||
@@ -104,7 +106,7 @@ export default class IMouseClickDrag extends IPointing {
|
||||
/** @param {MouseEvent} e */
|
||||
#mouseUpHandler = e => {
|
||||
if (!this.options.exitAnyButton || e.button == this.options.clickButton) {
|
||||
if (this.options.consumeEvent) {
|
||||
if (this.consumeEvent) {
|
||||
e.stopImmediatePropagation() // Captured, don't call anyone else
|
||||
}
|
||||
// Remove the handlers of "mousemove" and "mouseup"
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
import IInput from "../IInput.js"
|
||||
import Utility from "../../Utility.js"
|
||||
|
||||
/** @typedef {import("../keyboard/KeyboardShortcut.js").default} KeyboardShortcut */
|
||||
|
||||
/**
|
||||
* @template {HTMLElement} T
|
||||
* @template {Element} T
|
||||
* @extends {IInput<T>}
|
||||
*/
|
||||
export default class IPointing extends IInput {
|
||||
|
||||
#location = [0, 0]
|
||||
get location() {
|
||||
return this.#location
|
||||
}
|
||||
|
||||
/** @type {KeyboardShortcut?} */
|
||||
#enablerKey
|
||||
get enablerKey() {
|
||||
return this.#enablerKey
|
||||
}
|
||||
#enablerActivated = true
|
||||
get enablerActivated() {
|
||||
return this.#enablerActivated
|
||||
}
|
||||
|
||||
constructor(target, blueprint, options = {}) {
|
||||
options.ignoreTranslateCompensate ??= false
|
||||
options.ignoreScale ??= false
|
||||
@@ -14,17 +31,28 @@ export default class IPointing extends IInput {
|
||||
super(target, blueprint, options)
|
||||
/** @type {HTMLElement} */
|
||||
this.movementSpace = options.movementSpace
|
||||
if (options.enablerKey) {
|
||||
this.#enablerKey = options.enablerKey
|
||||
this.#enablerKey.onKeyDown = () => this.#enablerActivated = true
|
||||
this.#enablerKey.onKeyUp = () => this.#enablerActivated = false
|
||||
this.#enablerKey.consumeEvent = false
|
||||
this.#enablerKey.listenEvents()
|
||||
this.#enablerActivated = false
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {MouseEvent} mouseEvent */
|
||||
locationFromEvent(mouseEvent) {
|
||||
const location = Utility.convertLocation(
|
||||
setLocationFromEvent(mouseEvent) {
|
||||
let location = Utility.convertLocation(
|
||||
[mouseEvent.clientX, mouseEvent.clientY],
|
||||
this.movementSpace,
|
||||
this.options.ignoreScale
|
||||
)
|
||||
return this.options.ignoreTranslateCompensate
|
||||
location = this.options.ignoreTranslateCompensate
|
||||
? location
|
||||
: this.blueprint.compensateTranslation(location[0], location[1])
|
||||
this.#location[0] = location[0]
|
||||
this.#location[1] = location[1]
|
||||
return this.#location
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,40 @@ import Configuration from "../../Configuration.js"
|
||||
import IPointing from "./IPointing.js"
|
||||
|
||||
/**
|
||||
* @template {HTMLElement} T
|
||||
* @typedef {import("../../Blueprint.js").default} Blueprint
|
||||
* @typedef {import("../keyboard/KeyboardShortcut.js").default} KeyboardShortcut
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {Element} T
|
||||
* @extends {IPointing<T>}
|
||||
*/
|
||||
export default class MouseClick extends IPointing {
|
||||
|
||||
static #ignoreEvent =
|
||||
/** @param {MouseClick} self */
|
||||
self => { }
|
||||
|
||||
/** @param {MouseEvent} e */
|
||||
#mouseDownHandler = e => {
|
||||
this.blueprint.setFocused(true)
|
||||
if (this.enablerKey && !this.enablerActivated) {
|
||||
return
|
||||
}
|
||||
switch (e.button) {
|
||||
case this.options.clickButton:
|
||||
// Either doesn't matter or consider the click only when clicking on the target, not descandants
|
||||
if (!this.options.strictTarget || e.target === e.currentTarget) {
|
||||
if (this.options.consumeEvent) {
|
||||
if (this.consumeEvent) {
|
||||
e.stopImmediatePropagation() // Captured, don't call anyone else
|
||||
}
|
||||
// Attach the listeners
|
||||
document.addEventListener("mouseup", this.#mouseUpHandler)
|
||||
this.clickedPosition = this.locationFromEvent(e)
|
||||
this.blueprint.mousePosition[0] = this.clickedPosition[0]
|
||||
this.blueprint.mousePosition[1] = this.clickedPosition[1]
|
||||
this.setLocationFromEvent(e)
|
||||
this.clickedPosition[0] = this.location[0]
|
||||
this.clickedPosition[1] = this.location[1]
|
||||
this.blueprint.mousePosition[0] = this.location[0]
|
||||
this.blueprint.mousePosition[1] = this.location[1]
|
||||
this.clicked(this.clickedPosition)
|
||||
}
|
||||
break
|
||||
@@ -36,7 +50,7 @@ export default class MouseClick extends IPointing {
|
||||
/** @param {MouseEvent} e */
|
||||
#mouseUpHandler = e => {
|
||||
if (!this.options.exitAnyButton || e.button == this.options.clickButton) {
|
||||
if (this.options.consumeEvent) {
|
||||
if (this.consumeEvent) {
|
||||
e.stopImmediatePropagation() // Captured, don't call anyone else
|
||||
}
|
||||
// Remove the handlers of "mousemove" and "mouseup"
|
||||
@@ -47,12 +61,25 @@ export default class MouseClick extends IPointing {
|
||||
|
||||
clickedPosition = [0, 0]
|
||||
|
||||
constructor(target, blueprint, options = {}) {
|
||||
/**
|
||||
* @param {T} target
|
||||
* @param {Blueprint} blueprint
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(
|
||||
target,
|
||||
blueprint,
|
||||
options = {},
|
||||
onClick = MouseClick.#ignoreEvent,
|
||||
onUnclick = MouseClick.#ignoreEvent,
|
||||
) {
|
||||
options.clickButton ??= Configuration.mouseClickButton
|
||||
options.consumeEvent ??= true
|
||||
options.exitAnyButton ??= true
|
||||
options.strictTarget ??= false
|
||||
super(target, blueprint, options)
|
||||
this.onClick = onClick
|
||||
this.onUnclick = onUnclick
|
||||
this.listenEvents()
|
||||
}
|
||||
|
||||
@@ -69,8 +96,10 @@ export default class MouseClick extends IPointing {
|
||||
|
||||
/* Subclasses will override the following methods */
|
||||
clicked(location) {
|
||||
this.onClick(this)
|
||||
}
|
||||
|
||||
unclicked(location) {
|
||||
this.onUnclick(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ export default class MouseDbClick extends IPointing {
|
||||
/** @param {MouseEvent} e */
|
||||
#mouseDbClickHandler = e => {
|
||||
if (!this.options.strictTarget || e.target === e.currentTarget) {
|
||||
if (this.options.consumeEvent) {
|
||||
if (this.consumeEvent) {
|
||||
e.stopImmediatePropagation() // Captured, don't call anyone else
|
||||
}
|
||||
this.clickedPosition = this.locationFromEvent(e)
|
||||
this.clickedPosition = this.setLocationFromEvent(e)
|
||||
this.blueprint.mousePosition[0] = this.clickedPosition[0]
|
||||
this.blueprint.mousePosition[1] = this.clickedPosition[1]
|
||||
this.dbclicked(this.clickedPosition)
|
||||
|
||||
@@ -9,7 +9,9 @@ export default class MouseTracking extends IPointing {
|
||||
/** @param {MouseEvent} e */
|
||||
#mousemoveHandler= e => {
|
||||
e.preventDefault()
|
||||
this.blueprint.mousePosition = this.locationFromEvent(e)
|
||||
this.setLocationFromEvent(e)
|
||||
this.blueprint.mousePosition[0] = this.location[0]
|
||||
this.blueprint.mousePosition[1] = this.location[1]
|
||||
}
|
||||
|
||||
/** @param {CustomEvent} e */
|
||||
|
||||
@@ -2,13 +2,26 @@ import IPointing from "./IPointing.js"
|
||||
|
||||
/** @typedef {import("../../Blueprint.js").default} Blueprint */
|
||||
|
||||
export default class IMouseWheel extends IPointing {
|
||||
export default class MouseWheel extends IPointing {
|
||||
|
||||
static #ignoreEvent =
|
||||
/** @param {MouseWheel} self */
|
||||
self => { }
|
||||
|
||||
#variation = 0
|
||||
get variation() {
|
||||
return this.#variation
|
||||
}
|
||||
|
||||
/** @param {WheelEvent} e */
|
||||
#mouseWheelHandler = e => {
|
||||
if (this.enablerKey && !this.enablerActivated) {
|
||||
return
|
||||
}
|
||||
e.preventDefault()
|
||||
const location = this.locationFromEvent(e)
|
||||
this.wheel(e.deltaY, location)
|
||||
this.#variation = e.deltaY
|
||||
this.setLocationFromEvent(e)
|
||||
this.wheel()
|
||||
}
|
||||
|
||||
/** @param {WheelEvent} e */
|
||||
@@ -19,11 +32,17 @@ export default class IMouseWheel extends IPointing {
|
||||
* @param {Blueprint} blueprint
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(target, blueprint, options = {}) {
|
||||
constructor(
|
||||
target,
|
||||
blueprint,
|
||||
options = {},
|
||||
onWheel = MouseWheel.#ignoreEvent,
|
||||
) {
|
||||
options.listenOnFocus = true
|
||||
options.strictTarget ??= false
|
||||
super(target, blueprint, options)
|
||||
this.strictTarget = options.strictTarget
|
||||
this.onWheel = onWheel
|
||||
}
|
||||
|
||||
listenEvents() {
|
||||
@@ -36,7 +55,8 @@ export default class IMouseWheel extends IPointing {
|
||||
this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler)
|
||||
}
|
||||
|
||||
/* Subclasses will override the following method */
|
||||
wheel(variation, location) {
|
||||
/* Subclasses can override */
|
||||
wheel() {
|
||||
this.onWheel(this)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import Configuration from "../../Configuration.js"
|
||||
import IMouseWheel from "./IMouseWheel.js"
|
||||
import MouseWheel from "./MouseWheel.js"
|
||||
|
||||
export default class Zoom extends IMouseWheel {
|
||||
export default class Zoom extends MouseWheel {
|
||||
|
||||
#accumulatedVariation = 0
|
||||
|
||||
@@ -16,19 +16,17 @@ export default class Zoom extends IMouseWheel {
|
||||
this.#enableZoonIn = value
|
||||
}
|
||||
|
||||
wheel(variation, location) {
|
||||
this.#accumulatedVariation += -variation
|
||||
variation = this.#accumulatedVariation
|
||||
wheel() {
|
||||
this.#accumulatedVariation += -this.variation
|
||||
if (Math.abs(this.#accumulatedVariation) < Configuration.mouseWheelZoomThreshold) {
|
||||
return
|
||||
} else {
|
||||
this.#accumulatedVariation = 0
|
||||
}
|
||||
let zoomLevel = this.blueprint.getZoom()
|
||||
if (!this.enableZoonIn && zoomLevel == 0 && variation > 0) {
|
||||
if (!this.enableZoonIn && zoomLevel == 0 && this.#accumulatedVariation > 0) {
|
||||
return
|
||||
}
|
||||
zoomLevel += Math.sign(variation)
|
||||
this.blueprint.setZoom(zoomLevel, location)
|
||||
zoomLevel += Math.sign(this.#accumulatedVariation)
|
||||
this.blueprint.setZoom(zoomLevel, this.location)
|
||||
this.#accumulatedVariation = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,13 @@ export default class Grammar {
|
||||
)
|
||||
static symbol = P.regex(Grammar.Regex.Symbol)
|
||||
static attributeName = P.regex(Grammar.Regex.DotSeparatedSymbols)
|
||||
static attributeNameOptQuotes = Grammar.regexMap(
|
||||
new RegExp(
|
||||
"(" + Grammar.Regex.DotSeparatedSymbols.source + ")"
|
||||
+ '|"' + "(" + Grammar.Regex.DotSeparatedSymbols.source + ")" + '"'
|
||||
),
|
||||
([_0, a, b]) => a ?? b
|
||||
)
|
||||
static guid = P.regex(new RegExp(`${Grammar.Regex.HexDigit.source}{32}`))
|
||||
static commaSeparation = P.regex(/\s*,\s*(?!\))/)
|
||||
static equalSeparation = P.regex(/\s*=\s*/)
|
||||
@@ -338,9 +345,9 @@ export default class Grammar {
|
||||
return result
|
||||
}
|
||||
|
||||
static createAttributeGrammar(entityType, valueSeparator = this.equalSeparation) {
|
||||
static createAttributeGrammar(entityType, attributeName = this.attributeName, valueSeparator = this.equalSeparation) {
|
||||
return P.seq(
|
||||
this.attributeName,
|
||||
attributeName,
|
||||
valueSeparator,
|
||||
).chain(([attributeName, _1]) => {
|
||||
const attributeKey = attributeName.split(Configuration.keysSeparator)
|
||||
@@ -698,7 +705,7 @@ export default class Grammar {
|
||||
P.whitespace,
|
||||
P.alt(
|
||||
this.customProperty,
|
||||
this.createAttributeGrammar(ObjectEntity),
|
||||
this.createAttributeGrammar(ObjectEntity, this.attributeNameOptQuotes),
|
||||
this.inlinedArrayEntry,
|
||||
this.subObjectEntity
|
||||
)
|
||||
|
||||
@@ -144,7 +144,7 @@ export default function initializeSerializerFactory() {
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
InvariantTextEntity,
|
||||
new Serializer(InvariantTextEntity, (entity, v) => `${InvariantTextEntity.lookbehind}(${v})`, ", ", false, "", _ => "")
|
||||
new Serializer(InvariantTextEntity, (entity, v) => `${InvariantTextEntity.lookbehind}(${v})`, ", ", false, "", () => "")
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
@@ -159,7 +159,7 @@ export default function initializeSerializerFactory() {
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
LocalizedTextEntity,
|
||||
new Serializer(LocalizedTextEntity, (entity, v) => `${LocalizedTextEntity.lookbehind}(${v})`, ", ", false, "", _ => "")
|
||||
new Serializer(LocalizedTextEntity, (entity, v) => `${LocalizedTextEntity.lookbehind}(${v})`, ", ", false, "", () => "")
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
@@ -209,7 +209,7 @@ export default function initializeSerializerFactory() {
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
PinReferenceEntity,
|
||||
new Serializer(PinReferenceEntity, undefined, " ", false, "", _ => "")
|
||||
new Serializer(PinReferenceEntity, undefined, " ", false, "", () => "")
|
||||
)
|
||||
|
||||
SerializerFactory.registerSerializer(
|
||||
|
||||
@@ -3,15 +3,13 @@ import Configuration from "../Configuration.js"
|
||||
import Copy from "../input/common/Copy.js"
|
||||
import Cut from "../input/common/Cut.js"
|
||||
import ITemplate from "./ITemplate.js"
|
||||
import KeyboardCanc from "../input/keybaord/KeyboardCanc.js"
|
||||
import KeyboardEnableZoom from "../input/keybaord/KeyboardEnableZoom.js"
|
||||
import KeyboardSelectAll from "../input/keybaord/KeyboardSelectAll.js"
|
||||
import KeyboardShortcutAction from "../input/keybaord/KeyboardShortcutAction.js"
|
||||
import KeyboardEnableZoom from "../input/keyboard/KeyboardEnableZoom.js"
|
||||
import KeyboardShortcut from "../input/keyboard/KeyboardShortcut.js"
|
||||
import MouseScrollGraph from "../input/mouse/MouseScrollGraph.js"
|
||||
import MouseTracking from "../input/mouse/MouseTracking.js"
|
||||
import Paste from "../input/common/Paste.js"
|
||||
import Select from "../input/mouse/Select.js"
|
||||
import Shortcut from "../Shortcut.js"
|
||||
import Shortcuts from "../Shortcuts.js"
|
||||
import Unfocus from "../input/mouse/Unfocus.js"
|
||||
import Utility from "../Utility.js"
|
||||
import Zoom from "../input/mouse/Zoom.js"
|
||||
@@ -47,6 +45,12 @@ export default class BlueprintTemplate extends ITemplate {
|
||||
this.viewportSize[1] = size.blockSize
|
||||
}
|
||||
})
|
||||
/** @type {Copy} */
|
||||
#copyInputObject
|
||||
/** @type {Paste} */
|
||||
#pasteInputObject
|
||||
/** @type {Zoom} */
|
||||
#zoomInputObject
|
||||
|
||||
/** @type {HTMLElement} */ headerElement
|
||||
/** @type {HTMLElement} */ overlayElement
|
||||
@@ -102,34 +106,42 @@ export default class BlueprintTemplate extends ITemplate {
|
||||
}
|
||||
|
||||
createInputObjects() {
|
||||
const gridElement = this.element.getGridDOMElement()
|
||||
this.#copyInputObject = new Copy(gridElement, this.blueprint)
|
||||
this.#pasteInputObject = new Paste(gridElement, this.blueprint)
|
||||
this.#zoomInputObject = new Zoom(gridElement, this.blueprint)
|
||||
return [
|
||||
...super.createInputObjects(),
|
||||
new Copy(this.element.getGridDOMElement(), this.element),
|
||||
new Paste(this.element.getGridDOMElement(), this.element),
|
||||
new Cut(this.element.getGridDOMElement(), this.element),
|
||||
new KeyboardShortcutAction(this.element.getGridDOMElement(), this.element, {
|
||||
activationKeys: Shortcut.duplicateNodes
|
||||
this.#copyInputObject,
|
||||
this.#pasteInputObject,
|
||||
this.#zoomInputObject,
|
||||
new Cut(gridElement, this.blueprint),
|
||||
new KeyboardShortcut(gridElement, this.blueprint, {
|
||||
activationKeys: Shortcuts.duplicateNodes
|
||||
}, () =>
|
||||
this.blueprint.template.getPasteInputObject().pasted(
|
||||
this.blueprint.template.getCopyInputObject().copied()
|
||||
)
|
||||
),
|
||||
new KeyboardCanc(this.element.getGridDOMElement(), this.element),
|
||||
new KeyboardSelectAll(this.element.getGridDOMElement(), this.element),
|
||||
new Zoom(this.element.getGridDOMElement(), this.element),
|
||||
new Select(this.element.getGridDOMElement(), this.element, {
|
||||
new KeyboardShortcut(gridElement, this.blueprint, {
|
||||
activationKeys: Shortcuts.deleteNodes
|
||||
}, () => this.blueprint.removeGraphElement(...this.blueprint.getNodes(true))),
|
||||
new KeyboardShortcut(gridElement, this.blueprint, {
|
||||
activationKeys: Shortcuts.selectAllNodes
|
||||
}, () => this.blueprint.selectAll()),
|
||||
new Select(gridElement, this.blueprint, {
|
||||
clickButton: Configuration.mouseClickButton,
|
||||
exitAnyButton: true,
|
||||
moveEverywhere: true,
|
||||
}),
|
||||
new MouseScrollGraph(this.element.getGridDOMElement(), this.element, {
|
||||
new MouseScrollGraph(gridElement, this.blueprint, {
|
||||
clickButton: Configuration.mouseRightClickButton,
|
||||
exitAnyButton: false,
|
||||
moveEverywhere: true,
|
||||
}),
|
||||
new Unfocus(this.element.getGridDOMElement(), this.element),
|
||||
new MouseTracking(this.element.getGridDOMElement(), this.element),
|
||||
new KeyboardEnableZoom(this.element.getGridDOMElement(), this.element),
|
||||
new Unfocus(gridElement, this.blueprint),
|
||||
new MouseTracking(gridElement, this.blueprint),
|
||||
new KeyboardEnableZoom(gridElement, this.blueprint),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -219,11 +231,15 @@ export default class BlueprintTemplate extends ITemplate {
|
||||
}
|
||||
|
||||
getCopyInputObject() {
|
||||
return this.getInputObject(Copy)
|
||||
return this.#copyInputObject
|
||||
}
|
||||
|
||||
getPasteInputObject() {
|
||||
return this.getInputObject(Paste)
|
||||
return this.#pasteInputObject
|
||||
}
|
||||
|
||||
getZoomInputObject() {
|
||||
return this.#zoomInputObject
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import ITemplate from "./ITemplate.js"
|
||||
import KeyboardShortcutAction from "../input/keybaord/KeyboardShortcutAction.js"
|
||||
import KeyboardShortcut from "../input/keyboard/KeyboardShortcut.js"
|
||||
import MouseMoveDraggable from "../input/mouse/MouseMoveDraggable.js"
|
||||
|
||||
/** @typedef {import("../element/IDraggableElement.js").default} IDraggableElement */
|
||||
@@ -25,7 +25,7 @@ export default class IDraggableTemplate extends ITemplate {
|
||||
return [
|
||||
...super.createInputObjects(),
|
||||
this.createDraggableObject(),
|
||||
new KeyboardShortcutAction(
|
||||
new KeyboardShortcut(
|
||||
this.element,
|
||||
this.blueprint,
|
||||
{
|
||||
|
||||
@@ -31,14 +31,6 @@ export default class ITemplate {
|
||||
return /** @type {IInput[]} */([])
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {IInput} T
|
||||
* @param {new (...any) => T} type
|
||||
*/
|
||||
getInputObject(type) {
|
||||
return /** @type {T} */(this.inputObjects.find(object => object.constructor == type))
|
||||
}
|
||||
|
||||
setup() {
|
||||
this.#inputObjects.forEach(v => v.setup())
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@ import { html, nothing } from "lit"
|
||||
import Configuration from "../Configuration.js"
|
||||
import ElementFactory from "../element/ElementFactory.js"
|
||||
import IFromToPositionedTemplate from "./IFromToPositionedTemplate.js"
|
||||
import KeyboardShortcut from "../input/keyboard/KeyboardShortcut.js"
|
||||
import KnotEntity from "../entity/objects/KnotEntity.js"
|
||||
import MouseClick from "../input/mouse/MouseClick.js"
|
||||
import MouseDbClick from "../input/mouse/MouseDbClick.js"
|
||||
import Shortcuts from "../Shortcuts.js"
|
||||
import Utility from "../Utility.js"
|
||||
|
||||
/**
|
||||
@@ -85,10 +88,11 @@ export default class LinkTemplate extends IFromToPositionedTemplate {
|
||||
}
|
||||
|
||||
createInputObjects() {
|
||||
const linkArea = this.element.querySelector(".ueb-link-area")
|
||||
return [
|
||||
...super.createInputObjects(),
|
||||
new MouseDbClick(
|
||||
this.element.querySelector(".ueb-link-area"),
|
||||
linkArea,
|
||||
this.blueprint,
|
||||
undefined,
|
||||
/** @param {[Number, Number]} location */
|
||||
@@ -97,8 +101,18 @@ export default class LinkTemplate extends IFromToPositionedTemplate {
|
||||
location[1] += Configuration.knotOffset[1]
|
||||
location = Utility.snapToGrid(location[0], location[1], Configuration.gridSize)
|
||||
this.#createKnot(location)
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
new MouseClick(
|
||||
linkArea,
|
||||
this.blueprint,
|
||||
{
|
||||
enablerKey: new KeyboardShortcut(this.blueprint, this.blueprint, {
|
||||
activationKeys: Shortcuts.enableLinkDelete,
|
||||
})
|
||||
},
|
||||
() => this.blueprint.removeGraphElement(this.element),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export default class BoolPinTemplate extends PinTemplate {
|
||||
/** @type {HTMLInputElement?} */
|
||||
#input
|
||||
|
||||
#onChangeHandler = _ => this.element.setDefaultValue(this.#input.checked)
|
||||
#onChangeHandler = () => this.element.setDefaultValue(this.#input.checked)
|
||||
|
||||
/** @param {PropertyValues} changedProperties */
|
||||
firstUpdated(changedProperties) {
|
||||
|
||||
Reference in New Issue
Block a user