mirror of
https://github.com/viarotel-org/escrcpy.git
synced 2024-11-27 17:00:53 +01:00
feat: ✨ Support edge hiding function
This commit is contained in:
parent
ae7859bac3
commit
14a81de211
@ -228,10 +228,11 @@ Windows 及 Linux 端内部集成了 Gnirehtet, 用于提供 PC 到安卓设
|
|||||||
23. 浮动操作栏 ✅
|
23. 浮动操作栏 ✅
|
||||||
24. 增强录制功能 ✅
|
24. 增强录制功能 ✅
|
||||||
25. 启动APP(多线程) ✅
|
25. 启动APP(多线程) ✅
|
||||||
26. 改进历史设备连接体验 🚧
|
26. 主窗口贴边隐藏 ✅
|
||||||
27. 文件管理支持上传目录及进度展示🚧
|
27. 改进历史设备连接体验 🚧
|
||||||
28. 对设备进行分组 🚧
|
28. 文件管理支持上传目录及进度展示🚧
|
||||||
29. 游戏键位映射 🚧
|
29. 对设备进行分组 🚧
|
||||||
|
30. 游戏键位映射 🚧
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
|
@ -226,10 +226,11 @@ Refer to [scrcpy/doc/shortcuts](https://github.com/Genymobile/scrcpy/blob/master
|
|||||||
23. Floating control bar ✅
|
23. Floating control bar ✅
|
||||||
24. Enhanced recording ✅
|
24. Enhanced recording ✅
|
||||||
25. Start APP(Multi-threaded) ✅
|
25. Start APP(Multi-threaded) ✅
|
||||||
26. Improved history device connection experience 🚧
|
26. Main window edge hidden ✅
|
||||||
27. File management supports upload directory and progress display 🚧
|
27. Improved history device connection experience 🚧
|
||||||
28. Device grouping 🚧
|
28. File management supports upload directory and progress display 🚧
|
||||||
29. Game key mapping 🚧
|
29. Device grouping 🚧
|
||||||
|
30. Game key mapping 🚧
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
|
599
electron/helpers/edger/index.js
Normal file
599
electron/helpers/edger/index.js
Normal file
@ -0,0 +1,599 @@
|
|||||||
|
import { screen } from 'electron'
|
||||||
|
|
||||||
|
export class Edger {
|
||||||
|
constructor(window) {
|
||||||
|
if (!window) {
|
||||||
|
throw new Error('Window instance is required')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.window = window
|
||||||
|
this.isHidden = false
|
||||||
|
this.dockEdge = null
|
||||||
|
this.originalBounds = null
|
||||||
|
this.animationTimer = null
|
||||||
|
this.showDebounceTimer = null
|
||||||
|
this.hideDebounceTimer = null
|
||||||
|
this.isDragging = false
|
||||||
|
this.isAnimating = false
|
||||||
|
this.lastMousePosition = null
|
||||||
|
this.mouseMovementBuffer = []
|
||||||
|
this.lastAnimationTime = 0
|
||||||
|
|
||||||
|
// Animation configs
|
||||||
|
this.animationDuration = 300
|
||||||
|
this.animationSteps = 30
|
||||||
|
this.visiblePortion = 2
|
||||||
|
this.mouseBufferSize = 5
|
||||||
|
this.mouseVelocityThreshold = 50
|
||||||
|
this.animationCooldown = 100
|
||||||
|
|
||||||
|
// Thresholds
|
||||||
|
this.snapThreshold = 10
|
||||||
|
this.undockThreshold = 20
|
||||||
|
this.showHideThreshold = 50
|
||||||
|
this.stablePositionThreshold = 3
|
||||||
|
|
||||||
|
// Window topping
|
||||||
|
this.wasAlwaysOnTop = window.isAlwaysOnTop()
|
||||||
|
this.handleWindowBlur = this.handleWindowBlur.bind(this)
|
||||||
|
this.handleWindowFocus = this.handleWindowFocus.bind(this)
|
||||||
|
|
||||||
|
this.initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
easeOutCubic(t) {
|
||||||
|
return 1 - (1 - t) ** 3
|
||||||
|
}
|
||||||
|
|
||||||
|
easeInCubic(t) {
|
||||||
|
return t * t * t
|
||||||
|
}
|
||||||
|
|
||||||
|
easeInOutCubic(t) {
|
||||||
|
return t < 0.5
|
||||||
|
? 4 * t * t * t
|
||||||
|
: 1 - (-2 * t + 2) ** 3 / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
addBounceEffect(finalBounds) {
|
||||||
|
const bounceSteps = 15
|
||||||
|
const bounceDistance = 5
|
||||||
|
let step = 0
|
||||||
|
|
||||||
|
const bounceBounds = { ...finalBounds }
|
||||||
|
switch (this.dockEdge) {
|
||||||
|
case 'right':
|
||||||
|
bounceBounds.x -= bounceDistance
|
||||||
|
break
|
||||||
|
case 'left':
|
||||||
|
bounceBounds.x += bounceDistance
|
||||||
|
break
|
||||||
|
case 'top':
|
||||||
|
bounceBounds.y += bounceDistance
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const bounceTimer = setInterval(() => {
|
||||||
|
step++
|
||||||
|
const progress = step / bounceSteps
|
||||||
|
const easeProgress = this.easeInOutCubic(progress)
|
||||||
|
|
||||||
|
const currentBounds = {
|
||||||
|
x: Math.round(bounceBounds.x + (finalBounds.x - bounceBounds.x) * easeProgress),
|
||||||
|
y: Math.round(bounceBounds.y + (finalBounds.y - bounceBounds.y) * easeProgress),
|
||||||
|
width: finalBounds.width,
|
||||||
|
height: finalBounds.height,
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.window.setBounds(currentBounds)
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error('Failed to set window bounds during bounce:', err)
|
||||||
|
clearInterval(bounceTimer)
|
||||||
|
this.isAnimating = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step >= bounceSteps) {
|
||||||
|
clearInterval(bounceTimer)
|
||||||
|
this.window.setBounds(finalBounds)
|
||||||
|
this.isAnimating = false
|
||||||
|
}
|
||||||
|
}, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
showWindow() {
|
||||||
|
if (!this.isHidden || this.isAnimating)
|
||||||
|
return
|
||||||
|
clearTimeout(this.hideDebounceTimer)
|
||||||
|
|
||||||
|
if (this.showDebounceTimer)
|
||||||
|
return
|
||||||
|
|
||||||
|
this.showDebounceTimer = setTimeout(() => {
|
||||||
|
this.animateWindow(this.originalBounds, false)
|
||||||
|
this.isHidden = false
|
||||||
|
this.showDebounceTimer = null
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
hideWindow() {
|
||||||
|
if (this.isHidden || this.isAnimating)
|
||||||
|
return
|
||||||
|
clearTimeout(this.showDebounceTimer)
|
||||||
|
|
||||||
|
if (this.hideDebounceTimer)
|
||||||
|
return
|
||||||
|
|
||||||
|
this.hideDebounceTimer = setTimeout(() => {
|
||||||
|
const hiddenBounds = this.getHiddenBounds()
|
||||||
|
this.animateWindow(hiddenBounds, true)
|
||||||
|
this.isHidden = true
|
||||||
|
this.hideDebounceTimer = null
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
// Track window movement
|
||||||
|
this.window.on('move', () => {
|
||||||
|
if (!this.isHidden) {
|
||||||
|
this.checkEdgeSnap()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Track drag start
|
||||||
|
this.window.on('will-move', () => {
|
||||||
|
this.isDragging = true
|
||||||
|
if (this.dockEdge) {
|
||||||
|
this.checkUndock()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Track drag end
|
||||||
|
this.window.on('moved', () => {
|
||||||
|
this.isDragging = false
|
||||||
|
})
|
||||||
|
|
||||||
|
// Track mouse position
|
||||||
|
this.startMouseTracking()
|
||||||
|
|
||||||
|
// Add window focus event listening
|
||||||
|
this.window.on('blur', this.handleWindowBlur)
|
||||||
|
this.window.on('focus', this.handleWindowFocus)
|
||||||
|
|
||||||
|
// Check initial status
|
||||||
|
if (this.window.isAlwaysOnTop()) {
|
||||||
|
this.wasAlwaysOnTop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleWindowBlur() {
|
||||||
|
if (this.isHidden) {
|
||||||
|
this.setAlwaysOnTop(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleWindowFocus() {
|
||||||
|
if (!this.isHidden && !this.wasAlwaysOnTop) {
|
||||||
|
this.setAlwaysOnTop(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setAlwaysOnTop(value) {
|
||||||
|
try {
|
||||||
|
// 某些系统上可能需要特定的参数
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
this.window.setAlwaysOnTop(value, 'floating')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.window.setAlwaysOnTop(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error('Failed to set always on top:', err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 改进获取隐藏位置的方法,确保窗口边缘始终可见
|
||||||
|
getHiddenBounds() {
|
||||||
|
const currentBounds = this.window.getBounds()
|
||||||
|
const display = screen.getDisplayNearestPoint({
|
||||||
|
x: currentBounds.x,
|
||||||
|
y: currentBounds.y,
|
||||||
|
})
|
||||||
|
const screenBounds = display.workArea
|
||||||
|
|
||||||
|
let hiddenBounds = { ...currentBounds }
|
||||||
|
const minVisiblePixels = 3 // 确保至少有3个像素可见
|
||||||
|
|
||||||
|
switch (this.dockEdge) {
|
||||||
|
case 'right':
|
||||||
|
hiddenBounds.x = screenBounds.x + screenBounds.width - minVisiblePixels
|
||||||
|
break
|
||||||
|
case 'left':
|
||||||
|
hiddenBounds.x = screenBounds.x - currentBounds.width + minVisiblePixels
|
||||||
|
break
|
||||||
|
case 'top':
|
||||||
|
hiddenBounds.y = screenBounds.y - currentBounds.height + minVisiblePixels
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保窗口不会完全隐藏
|
||||||
|
hiddenBounds = {
|
||||||
|
x: Math.round(hiddenBounds.x),
|
||||||
|
y: Math.round(hiddenBounds.y),
|
||||||
|
width: currentBounds.width,
|
||||||
|
height: currentBounds.height,
|
||||||
|
}
|
||||||
|
|
||||||
|
return hiddenBounds
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加窗口位置恢复方法
|
||||||
|
restoreWindowPosition() {
|
||||||
|
if (!this.dockEdge || !this.originalBounds)
|
||||||
|
return
|
||||||
|
|
||||||
|
const display = screen.getDisplayNearestPoint({
|
||||||
|
x: this.originalBounds.x,
|
||||||
|
y: this.originalBounds.y,
|
||||||
|
})
|
||||||
|
const screenBounds = display.workArea
|
||||||
|
|
||||||
|
// 确保窗口在屏幕范围内
|
||||||
|
const restoredBounds = { ...this.originalBounds }
|
||||||
|
|
||||||
|
switch (this.dockEdge) {
|
||||||
|
case 'right':
|
||||||
|
restoredBounds.x = Math.min(
|
||||||
|
restoredBounds.x,
|
||||||
|
screenBounds.x + screenBounds.width - restoredBounds.width,
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case 'left':
|
||||||
|
restoredBounds.x = Math.max(restoredBounds.x, screenBounds.x)
|
||||||
|
break
|
||||||
|
case 'top':
|
||||||
|
restoredBounds.y = Math.max(restoredBounds.y, screenBounds.y)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
this.window.setBounds(restoredBounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.cleanupAnimation()
|
||||||
|
if (this.showDebounceTimer) {
|
||||||
|
clearTimeout(this.showDebounceTimer)
|
||||||
|
}
|
||||||
|
if (this.hideDebounceTimer) {
|
||||||
|
clearTimeout(this.hideDebounceTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理事件监听
|
||||||
|
if (this.window) {
|
||||||
|
this.window.removeListener('blur', this.handleWindowBlur)
|
||||||
|
this.window.removeListener('focus', this.handleWindowFocus)
|
||||||
|
this.window.removeAllListeners()
|
||||||
|
|
||||||
|
// 恢复原始置顶状态
|
||||||
|
if (this.window.isAlwaysOnTop() !== this.wasAlwaysOnTop) {
|
||||||
|
this.setAlwaysOnTop(this.wasAlwaysOnTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mouseMovementBuffer = []
|
||||||
|
}
|
||||||
|
|
||||||
|
startMouseTracking() {
|
||||||
|
const trackMouse = () => {
|
||||||
|
if (!this.dockEdge)
|
||||||
|
return
|
||||||
|
|
||||||
|
const currentTime = Date.now()
|
||||||
|
const mousePos = screen.getCursorScreenPoint()
|
||||||
|
|
||||||
|
// Update mouse movement buffer
|
||||||
|
this.updateMouseBuffer(mousePos, currentTime)
|
||||||
|
|
||||||
|
const windowBounds = this.window.getBounds()
|
||||||
|
const display = screen.getDisplayNearestPoint(mousePos)
|
||||||
|
const screenBounds = display.workArea
|
||||||
|
|
||||||
|
// Check that the mouse is stable
|
||||||
|
if (this.isMouseStable()) {
|
||||||
|
if (this.isMouseNearEdge(mousePos, windowBounds, screenBounds)) {
|
||||||
|
this.showWindow()
|
||||||
|
}
|
||||||
|
else if (this.isMouseOutsideWindow(mousePos, windowBounds)) {
|
||||||
|
this.hideWindow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastMousePosition = mousePos
|
||||||
|
}
|
||||||
|
|
||||||
|
setInterval(trackMouse, 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMouseBuffer(mousePos, currentTime) {
|
||||||
|
this.mouseMovementBuffer.push({
|
||||||
|
x: mousePos.x,
|
||||||
|
y: mousePos.y,
|
||||||
|
time: currentTime,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.mouseMovementBuffer.length > this.mouseBufferSize) {
|
||||||
|
this.mouseMovementBuffer.shift()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isMouseStable() {
|
||||||
|
if (this.mouseMovementBuffer.length < this.mouseBufferSize)
|
||||||
|
return true
|
||||||
|
|
||||||
|
const recentMovements = this.mouseMovementBuffer.slice(-this.stablePositionThreshold)
|
||||||
|
const firstPos = recentMovements[0]
|
||||||
|
|
||||||
|
return recentMovements.every(pos =>
|
||||||
|
Math.abs(pos.x - firstPos.x) <= this.stablePositionThreshold
|
||||||
|
&& Math.abs(pos.y - firstPos.y) <= this.stablePositionThreshold,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateMouseVelocity() {
|
||||||
|
if (this.mouseMovementBuffer.length < 2)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
const latest = this.mouseMovementBuffer[this.mouseMovementBuffer.length - 1]
|
||||||
|
const previous = this.mouseMovementBuffer[this.mouseMovementBuffer.length - 2]
|
||||||
|
const timeDiff = latest.time - previous.time
|
||||||
|
|
||||||
|
if (timeDiff === 0)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
const distance = Math.sqrt(
|
||||||
|
(latest.x - previous.x) ** 2
|
||||||
|
+ (latest.y - previous.y) ** 2,
|
||||||
|
)
|
||||||
|
|
||||||
|
return distance / timeDiff
|
||||||
|
}
|
||||||
|
|
||||||
|
isMouseNearEdge(mousePos, windowBounds, screenBounds) {
|
||||||
|
const threshold = this.snapThreshold
|
||||||
|
|
||||||
|
// Consider the offset of the window position
|
||||||
|
const offset = 5
|
||||||
|
const velocity = this.calculateMouseVelocity()
|
||||||
|
|
||||||
|
// If the mouse moves too fast, the display is not triggered
|
||||||
|
if (velocity > this.mouseVelocityThreshold)
|
||||||
|
return false
|
||||||
|
|
||||||
|
switch (this.dockEdge) {
|
||||||
|
case 'right': {
|
||||||
|
const rightEdge = screenBounds.x + screenBounds.width
|
||||||
|
return mousePos.x >= rightEdge - threshold
|
||||||
|
&& mousePos.y >= windowBounds.y - offset
|
||||||
|
&& mousePos.y <= windowBounds.y + windowBounds.height + offset
|
||||||
|
}
|
||||||
|
case 'left': {
|
||||||
|
return mousePos.x <= screenBounds.x + threshold
|
||||||
|
&& mousePos.y >= windowBounds.y - offset
|
||||||
|
&& mousePos.y <= windowBounds.y + windowBounds.height + offset
|
||||||
|
}
|
||||||
|
case 'top': {
|
||||||
|
return mousePos.y <= screenBounds.y + threshold
|
||||||
|
&& mousePos.x >= windowBounds.x - offset
|
||||||
|
&& mousePos.x <= windowBounds.x + windowBounds.width + offset
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isMouseOutsideWindow(mousePos, windowBounds) {
|
||||||
|
const margin = 10 // Add edge tolerance
|
||||||
|
return mousePos.x < windowBounds.x - margin
|
||||||
|
|| mousePos.x > windowBounds.x + windowBounds.width + margin
|
||||||
|
|| mousePos.y < windowBounds.y - margin
|
||||||
|
|| mousePos.y > windowBounds.y + windowBounds.height + margin
|
||||||
|
}
|
||||||
|
|
||||||
|
animateWindow(targetBounds, isHiding = false) {
|
||||||
|
const currentTime = Date.now()
|
||||||
|
if (currentTime - this.lastAnimationTime < this.animationCooldown) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.animationTimer) {
|
||||||
|
clearInterval(this.animationTimer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isAnimating)
|
||||||
|
return
|
||||||
|
|
||||||
|
this.isAnimating = true
|
||||||
|
this.lastAnimationTime = currentTime
|
||||||
|
|
||||||
|
const startBounds = this.window.getBounds()
|
||||||
|
// Make sure the start and destination positions are integers
|
||||||
|
const sanitizedTargetBounds = {
|
||||||
|
x: Math.round(targetBounds.x),
|
||||||
|
y: Math.round(targetBounds.y),
|
||||||
|
width: Math.round(targetBounds.width),
|
||||||
|
height: Math.round(targetBounds.height),
|
||||||
|
}
|
||||||
|
|
||||||
|
let step = 0
|
||||||
|
let lastBounds = startBounds
|
||||||
|
|
||||||
|
const animate = () => {
|
||||||
|
step++
|
||||||
|
const progress = step / this.animationSteps
|
||||||
|
const easeProgress = isHiding
|
||||||
|
? this.easeInCubic(progress)
|
||||||
|
: this.easeOutCubic(progress)
|
||||||
|
|
||||||
|
const currentBounds = {
|
||||||
|
x: Math.round(startBounds.x + (sanitizedTargetBounds.x - startBounds.x) * easeProgress),
|
||||||
|
y: Math.round(startBounds.y + (sanitizedTargetBounds.y - startBounds.y) * easeProgress),
|
||||||
|
width: startBounds.width,
|
||||||
|
height: startBounds.height,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent the same location from being set repeatedly
|
||||||
|
if (this.boundsEqual(currentBounds, lastBounds)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.window.setBounds(currentBounds)
|
||||||
|
lastBounds = currentBounds
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error('Animation error:', err)
|
||||||
|
this.cleanupAnimation()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step >= this.animationSteps) {
|
||||||
|
this.window.setBounds(sanitizedTargetBounds)
|
||||||
|
this.cleanupAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.animationTimer = setInterval(animate, this.animationDuration / this.animationSteps)
|
||||||
|
}
|
||||||
|
|
||||||
|
boundsEqual(bounds1, bounds2) {
|
||||||
|
return bounds1.x === bounds2.x
|
||||||
|
&& bounds1.y === bounds2.y
|
||||||
|
&& bounds1.width === bounds2.width
|
||||||
|
&& bounds1.height === bounds2.height
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupAnimation() {
|
||||||
|
clearInterval(this.animationTimer)
|
||||||
|
this.animationTimer = null
|
||||||
|
this.isAnimating = false
|
||||||
|
}
|
||||||
|
|
||||||
|
checkUndock() {
|
||||||
|
if (!this.dockEdge || !this.isDragging)
|
||||||
|
return
|
||||||
|
|
||||||
|
const windowBounds = this.window.getBounds()
|
||||||
|
const display = screen.getDisplayNearestPoint({
|
||||||
|
x: windowBounds.x,
|
||||||
|
y: windowBounds.y,
|
||||||
|
})
|
||||||
|
const screenBounds = display.workArea
|
||||||
|
|
||||||
|
const distanceFromRight = Math.abs(windowBounds.x + windowBounds.width - screenBounds.x - screenBounds.width)
|
||||||
|
const distanceFromLeft = Math.abs(windowBounds.x - screenBounds.x)
|
||||||
|
const distanceFromTop = Math.abs(windowBounds.y - screenBounds.y)
|
||||||
|
|
||||||
|
let shouldUndock = false
|
||||||
|
|
||||||
|
switch (this.dockEdge) {
|
||||||
|
case 'right':
|
||||||
|
shouldUndock = distanceFromRight > this.undockThreshold
|
||||||
|
break
|
||||||
|
case 'left':
|
||||||
|
shouldUndock = distanceFromLeft > this.undockThreshold
|
||||||
|
break
|
||||||
|
case 'top':
|
||||||
|
shouldUndock = distanceFromTop > this.undockThreshold
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldUndock) {
|
||||||
|
this.undock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
undock() {
|
||||||
|
this.dockEdge = null
|
||||||
|
this.originalBounds = null
|
||||||
|
this.isHidden = false
|
||||||
|
if (this.animationTimer) {
|
||||||
|
clearInterval(this.animationTimer)
|
||||||
|
this.animationTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkEdgeSnap() {
|
||||||
|
if (this.isDragging && this.dockEdge) {
|
||||||
|
this.checkUndock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const windowBounds = this.window.getBounds()
|
||||||
|
const display = screen.getDisplayNearestPoint({
|
||||||
|
x: windowBounds.x,
|
||||||
|
y: windowBounds.y,
|
||||||
|
})
|
||||||
|
const screenBounds = display.workArea
|
||||||
|
|
||||||
|
const distanceFromRight = Math.abs(windowBounds.x + windowBounds.width - screenBounds.x - screenBounds.width)
|
||||||
|
const distanceFromLeft = Math.abs(windowBounds.x - screenBounds.x)
|
||||||
|
const distanceFromTop = Math.abs(windowBounds.y - screenBounds.y)
|
||||||
|
|
||||||
|
// Check right edge
|
||||||
|
if (distanceFromRight < this.snapThreshold) {
|
||||||
|
this.dockToEdge('right', windowBounds)
|
||||||
|
}
|
||||||
|
// Check left edge
|
||||||
|
else if (distanceFromLeft < this.snapThreshold) {
|
||||||
|
this.dockToEdge('left', windowBounds)
|
||||||
|
}
|
||||||
|
// Check top edge
|
||||||
|
else if (distanceFromTop < this.snapThreshold) {
|
||||||
|
this.dockToEdge('top', windowBounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dockToEdge(edge, bounds) {
|
||||||
|
this.dockEdge = edge
|
||||||
|
this.originalBounds = bounds
|
||||||
|
|
||||||
|
const display = screen.getDisplayNearestPoint({
|
||||||
|
x: bounds.x,
|
||||||
|
y: bounds.y,
|
||||||
|
})
|
||||||
|
const screenBounds = display.workArea
|
||||||
|
|
||||||
|
// Snap to exact position
|
||||||
|
switch (edge) {
|
||||||
|
case 'right':
|
||||||
|
this.window.setBounds({
|
||||||
|
x: screenBounds.x + screenBounds.width - bounds.width,
|
||||||
|
y: bounds.y,
|
||||||
|
width: bounds.width,
|
||||||
|
height: bounds.height,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'left':
|
||||||
|
this.window.setBounds({
|
||||||
|
x: screenBounds.x,
|
||||||
|
y: bounds.y,
|
||||||
|
width: bounds.width,
|
||||||
|
height: bounds.height,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'top':
|
||||||
|
this.window.setBounds({
|
||||||
|
x: bounds.x,
|
||||||
|
y: screenBounds.y,
|
||||||
|
width: bounds.width,
|
||||||
|
height: bounds.height,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Edger
|
@ -20,6 +20,8 @@ import control from '$control/electron/main.js'
|
|||||||
|
|
||||||
import { loadPage } from './helpers/index.js'
|
import { loadPage } from './helpers/index.js'
|
||||||
|
|
||||||
|
import { Edger } from './helpers/edger/index.js'
|
||||||
|
|
||||||
const require = createRequire(import.meta.url)
|
const require = createRequire(import.meta.url)
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
@ -76,6 +78,8 @@ function createWindow() {
|
|||||||
remote.enable(mainWindow.webContents)
|
remote.enable(mainWindow.webContents)
|
||||||
remote.initialize()
|
remote.initialize()
|
||||||
|
|
||||||
|
new Edger(mainWindow);
|
||||||
|
|
||||||
mainWindow.on('ready-to-show', () => {
|
mainWindow.on('ready-to-show', () => {
|
||||||
mainWindow.show()
|
mainWindow.show()
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user