mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-05-21 13:47:37 +08:00
Center node in the viewport
This commit is contained in:
@@ -10,6 +10,7 @@ import Utility from "./Utility"
|
||||
* @typedef {import("./entity/GuidEntity").default} GuidEntity
|
||||
* @typedef {import("./entity/PinReferenceEntity").default} PinReferenceEntity
|
||||
* @typedef {import("./template/node/CommentNodeTemplate").default} CommentNodeTemplate
|
||||
* @typedef {import("lit").PropertyValues} PropertyValues
|
||||
* @typedef {{
|
||||
* primaryInf: Number,
|
||||
* primarySup: Number,
|
||||
@@ -71,6 +72,7 @@ export default class Blueprint extends IElement {
|
||||
},
|
||||
}
|
||||
|
||||
#avoidScrolling = false
|
||||
/** @type {Map<String, Number>} */
|
||||
#nodeNameCounter = new Map()
|
||||
/** @type {NodeElement[]}" */
|
||||
@@ -121,39 +123,53 @@ export default class Blueprint extends IElement {
|
||||
}
|
||||
|
||||
/** @param {Number[]} param0 */
|
||||
setScroll([x, y], smooth = false) {
|
||||
setScroll([x, y]) {
|
||||
this.scrollX = x
|
||||
this.scrollY = y
|
||||
}
|
||||
|
||||
/** @param {Number[]} delta */
|
||||
scrollDelta(delta, smooth = false) {
|
||||
const maxScroll = [2 * Configuration.expandGridSize, 2 * Configuration.expandGridSize]
|
||||
let currentScroll = this.getScroll()
|
||||
let finalScroll = [
|
||||
currentScroll[0] + delta[0],
|
||||
currentScroll[1] + delta[1]
|
||||
]
|
||||
let expand = [0, 0]
|
||||
for (let i = 0; i < 2; ++i) {
|
||||
if (delta[i] < 0 && finalScroll[i] < Configuration.gridExpandThreshold * Configuration.expandGridSize) {
|
||||
// Expand left/top
|
||||
expand[i] = -1
|
||||
} else if (delta[i] > 0 && finalScroll[i]
|
||||
> maxScroll[i] - Configuration.gridExpandThreshold * Configuration.expandGridSize) {
|
||||
// Expand right/bottom
|
||||
expand[i] = 1
|
||||
if (smooth) {
|
||||
let previousScrollDelta = [0, 0]
|
||||
Utility.animate(0, delta[0], Configuration.smoothScrollTime, x => {
|
||||
this.scrollDelta([x - previousScrollDelta[0], 0], false)
|
||||
previousScrollDelta[0] = x
|
||||
})
|
||||
Utility.animate(0, delta[1], Configuration.smoothScrollTime, y => {
|
||||
this.scrollDelta([0, y - previousScrollDelta[1]], false)
|
||||
previousScrollDelta[1] = y
|
||||
})
|
||||
} else {
|
||||
const maxScroll = [2 * Configuration.expandGridSize, 2 * Configuration.expandGridSize]
|
||||
let currentScroll = this.getScroll()
|
||||
let finalScroll = [
|
||||
currentScroll[0] + delta[0],
|
||||
currentScroll[1] + delta[1]
|
||||
]
|
||||
let expand = [0, 0]
|
||||
for (let i = 0; i < 2; ++i) {
|
||||
if (finalScroll[i] < Configuration.gridExpandThreshold * Configuration.expandGridSize) {
|
||||
// Expand left/top
|
||||
expand[i] = -1
|
||||
} else if (
|
||||
finalScroll[i]
|
||||
> maxScroll[i] - Configuration.gridExpandThreshold * Configuration.expandGridSize
|
||||
) {
|
||||
// Expand right/bottom
|
||||
expand[i] = 1
|
||||
}
|
||||
}
|
||||
if (expand[0] != 0 || expand[1] != 0) {
|
||||
this.seamlessExpand(expand)
|
||||
}
|
||||
currentScroll = this.getScroll()
|
||||
finalScroll = [
|
||||
currentScroll[0] + delta[0],
|
||||
currentScroll[1] + delta[1]
|
||||
]
|
||||
this.setScroll(finalScroll)
|
||||
}
|
||||
if (expand[0] != 0 || expand[1] != 0) {
|
||||
this.seamlessExpand(expand)
|
||||
}
|
||||
currentScroll = this.getScroll()
|
||||
finalScroll = [
|
||||
currentScroll[0] + delta[0],
|
||||
currentScroll[1] + delta[1]
|
||||
]
|
||||
this.setScroll(finalScroll, smooth)
|
||||
}
|
||||
|
||||
scrollCenter() {
|
||||
|
||||
@@ -32,8 +32,9 @@ export default class Configuration {
|
||||
static defaultCommentHeight = 96
|
||||
static defaultCommentWidth = 400
|
||||
static deleteNodesKeyboardKey = "Delete"
|
||||
static dragGeneralEventName = "ueb-drag-general"
|
||||
static distanceThreshold = 5 // px
|
||||
static dragEventName = "ueb-drag"
|
||||
static dragGeneralEventName = "ueb-drag-general"
|
||||
static editTextEventName = {
|
||||
begin: "ueb-edit-text-begin",
|
||||
end: "ueb-edit-text-end",
|
||||
@@ -76,16 +77,16 @@ export default class Configuration {
|
||||
static gridAxisLineColor = css`black`
|
||||
static gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event
|
||||
static gridLineColor = css`#353535`
|
||||
static gridLineWidth = 1 // pixel
|
||||
static gridLineWidth = 1 // px
|
||||
static gridSet = 8
|
||||
static gridSetLineColor = css`#161616`
|
||||
static gridShrinkThreshold = 4 // exceding size factor threshold to cause a shrink event
|
||||
static gridSize = 16 // pixel
|
||||
static gridSize = 16 // px
|
||||
static hexColorRegex = /^\s*#(?<r>[0-9a-fA-F]{2})(?<g>[0-9a-fA-F]{2})(?<b>[0-9a-fA-F]{2})([0-9a-fA-F]{2})?|#(?<rs>[0-9a-fA-F])(?<gs>[0-9a-fA-F])(?<bs>[0-9a-fA-F])\s*$/
|
||||
static keysSeparator = "+"
|
||||
static linkCurveHeight = 15 // pixel
|
||||
static linkCurveWidth = 80 // pixel
|
||||
static linkMinWidth = 100 // pixel
|
||||
static linkCurveHeight = 15 // px
|
||||
static linkCurveWidth = 80 // px
|
||||
static linkMinWidth = 100 // px
|
||||
/**
|
||||
* @param {Number} start
|
||||
* @param {Number} c1
|
||||
@@ -102,7 +103,7 @@ export default class Configuration {
|
||||
static nodeDragGeneralEventName = "ueb-node-drag-general"
|
||||
static nodeDragEventName = "ueb-node-drag"
|
||||
static nodeName = (name, counter) => `${name}_${counter}`
|
||||
static nodeRadius = 8 // in pixel
|
||||
static nodeRadius = 8 // px
|
||||
static nodeReflowEventName = "ueb-node-reflow"
|
||||
static nodeType = {
|
||||
callFunction: "/Script/BlueprintGraph.K2Node_CallFunction",
|
||||
@@ -128,7 +129,7 @@ export default class Configuration {
|
||||
whileLoop: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:WhileLoop",
|
||||
}
|
||||
static selectAllKeyboardKey = "(bCtrl=True,Key=A)"
|
||||
static distanceThreshold = 5 // in pixel
|
||||
static smoothScrollTime = 1000 // ms
|
||||
static trackingMouseEventName = {
|
||||
begin: "ueb-tracking-mouse-begin",
|
||||
end: "ueb-tracking-mouse-end",
|
||||
|
||||
@@ -324,4 +324,22 @@ export default class Utility {
|
||||
event.clipboardData.setData("text", value)
|
||||
element.dispatchEvent(event)
|
||||
}
|
||||
|
||||
static animate(from, to, intervalSeconds, callback, timingFunction = x => {
|
||||
const v = x ** 3.5
|
||||
return v / (v + ((1 - x) ** 3.5))
|
||||
}) {
|
||||
const startTimestamp = performance.now()
|
||||
const doAnimation = currentTimestamp => {
|
||||
let delta = (currentTimestamp - startTimestamp) / intervalSeconds
|
||||
if (Utility.approximatelyEqual(delta, 1) || delta > 1) {
|
||||
delta = 1
|
||||
} else {
|
||||
requestAnimationFrame(doAnimation)
|
||||
}
|
||||
const currentValue = from + (to - from) * timingFunction(delta)
|
||||
callback(currentValue)
|
||||
}
|
||||
requestAnimationFrame(doAnimation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,14 @@ export default class BlueprintTemplate extends ITemplate {
|
||||
"--ueb-node-radius": `${Configuration.nodeRadius}px`,
|
||||
}
|
||||
|
||||
#resizeObserver = new ResizeObserver(entries => {
|
||||
const size = entries.find(entry => entry.target === this.viewportElement)?.devicePixelContentBoxSize?.[0]
|
||||
if (size) {
|
||||
this.viewportSize[0] = size.inlineSize
|
||||
this.viewportSize[1] = size.blockSize
|
||||
}
|
||||
})
|
||||
|
||||
/** @type {HTMLElement} */ headerElement
|
||||
/** @type {HTMLElement} */ overlayElement
|
||||
/** @type {HTMLElement} */ viewportElement
|
||||
@@ -44,11 +52,28 @@ export default class BlueprintTemplate extends ITemplate {
|
||||
/** @type {HTMLElement} */ gridElement
|
||||
/** @type {HTMLElement} */ linksContainerElement
|
||||
/** @type {HTMLElement} */ nodesContainerElement
|
||||
viewportSize = [0, 0]
|
||||
|
||||
/** @param {Blueprint} element */
|
||||
initialize(element) {
|
||||
super.initialize(element)
|
||||
this.element.style.cssText = Object.entries(BlueprintTemplate.styleVariables).map(([k, v]) => `${k}:${v};`).join("")
|
||||
this.element.style.cssText = Object.entries(BlueprintTemplate.styleVariables)
|
||||
.map(([k, v]) => `${k}:${v};`).join("")
|
||||
}
|
||||
|
||||
setup() {
|
||||
super.setup()
|
||||
this.#resizeObserver.observe(this.viewportElement, {
|
||||
box: "device-pixel-content-box",
|
||||
})
|
||||
const bounding = this.viewportElement.getBoundingClientRect()
|
||||
this.viewportSize[0] = bounding.width
|
||||
this.viewportSize[1] = bounding.height
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
super.cleanup()
|
||||
this.#resizeObserver.unobserve(this.viewportElement)
|
||||
}
|
||||
|
||||
createInputObjects() {
|
||||
@@ -99,14 +124,14 @@ export default class BlueprintTemplate extends ITemplate {
|
||||
/** @param {PropertyValues} changedProperties */
|
||||
firstUpdated(changedProperties) {
|
||||
super.firstUpdated(changedProperties)
|
||||
this.headerElement = /** @type {HTMLElement} */(this.element.querySelector('.ueb-viewport-header'))
|
||||
this.overlayElement = /** @type {HTMLElement} */(this.element.querySelector('.ueb-viewport-overlay'))
|
||||
this.viewportElement = /** @type {HTMLElement} */(this.element.querySelector('.ueb-viewport-body'))
|
||||
this.selectorElement = /** @type {SelectorElement} */(this.element.querySelector('ueb-selector'))
|
||||
this.gridElement = /** @type {HTMLElement} */(this.viewportElement.querySelector(".ueb-grid"))
|
||||
this.linksContainerElement = /** @type {HTMLElement} */(this.element.querySelector("[data-links]"))
|
||||
this.headerElement = this.element.querySelector('.ueb-viewport-header')
|
||||
this.overlayElement = this.element.querySelector('.ueb-viewport-overlay')
|
||||
this.viewportElement = this.element.querySelector('.ueb-viewport-body')
|
||||
this.selectorElement = this.element.querySelector('ueb-selector')
|
||||
this.gridElement = this.viewportElement.querySelector(".ueb-grid")
|
||||
this.linksContainerElement = this.element.querySelector("[data-links]")
|
||||
this.linksContainerElement.append(...this.element.getLinks())
|
||||
this.nodesContainerElement = /** @type {HTMLElement} */(this.element.querySelector("[data-nodes]"))
|
||||
this.nodesContainerElement = this.element.querySelector("[data-nodes]")
|
||||
this.nodesContainerElement.append(...this.element.getNodes())
|
||||
this.viewportElement.scroll(Configuration.expandGridSize, Configuration.expandGridSize)
|
||||
}
|
||||
@@ -165,4 +190,20 @@ export default class BlueprintTemplate extends ITemplate {
|
||||
isPointVisible(x, y) {
|
||||
return false
|
||||
}
|
||||
|
||||
gridTopVisibilityBoundary() {
|
||||
return this.blueprint.scrollY - this.blueprint.translateY
|
||||
}
|
||||
|
||||
gridRightVisibilityBoundary() {
|
||||
return this.gridLeftVisibilityBoundary() + this.viewportSize[0]
|
||||
}
|
||||
|
||||
gridBottomVisibilityBoundary() {
|
||||
return this.gridTopVisibilityBoundary() + this.viewportSize[1]
|
||||
}
|
||||
|
||||
gridLeftVisibilityBoundary() {
|
||||
return this.blueprint.scrollX - this.blueprint.translateX
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,4 +44,19 @@ export default class IDraggableTemplate extends ITemplate {
|
||||
leftBoundary(justSelectableArea = false) {
|
||||
return this.element.locationX
|
||||
}
|
||||
|
||||
centerInViewport() {
|
||||
const minMargin = Math.min(
|
||||
this.blueprint.template.viewportSize[0] / 10,
|
||||
this.blueprint.template.viewportSize[1] / 10
|
||||
)
|
||||
const dl = this.leftBoundary() - this.blueprint.template.gridLeftVisibilityBoundary()
|
||||
const dr = this.blueprint.template.gridRightVisibilityBoundary() - this.rightBoundary()
|
||||
let avgX = Math.max((dl + dr) / 2, minMargin)
|
||||
const dt = this.topBoundary() - this.blueprint.template.gridTopVisibilityBoundary()
|
||||
const db = this.blueprint.template.gridBottomVisibilityBoundary() - this.bottomBoundary()
|
||||
let avgY = Math.max((dt + db) / 2, minMargin)
|
||||
const delta = [dl - avgX, dt - avgY]
|
||||
this.blueprint.scrollDelta(delta, true)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user