Add navigation gesture support in mainWindow and ElectronContext
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Implemented navigation gestures in mainWindow.js to handle swipe and app-command events for back and forward navigation. - Enhanced ElectronContext.jsx to manage navigation history through IPC events and added horizontal scroll detection for improved user experience. - Introduced a debounce mechanism for navigation actions to prevent rapid triggering.
This commit is contained in:
parent
f0571c9c5c
commit
36a3e81f47
@ -149,6 +149,33 @@ function attachKeyboardShortcuts(browserWindow) {
|
|||||||
// Keyboard shortcuts for the main window can be added here if needed
|
// Keyboard shortcuts for the main window can be added here if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendNavigationGesture(browserWindow, direction) {
|
||||||
|
if (!browserWindow || browserWindow.isDestroyed()) return
|
||||||
|
browserWindow.webContents.send('navigation-gesture', direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupNavigationGestures(browserWindow) {
|
||||||
|
if (!browserWindow) return
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
browserWindow.on('swipe', (_event, direction) => {
|
||||||
|
if (direction === 'left') {
|
||||||
|
sendNavigationGesture(browserWindow, 'back')
|
||||||
|
} else if (direction === 'right') {
|
||||||
|
sendNavigationGesture(browserWindow, 'forward')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
browserWindow.on('app-command', (_event, command) => {
|
||||||
|
if (command === 'browser-backward') {
|
||||||
|
sendNavigationGesture(browserWindow, 'back')
|
||||||
|
} else if (command === 'browser-forward') {
|
||||||
|
sendNavigationGesture(browserWindow, 'forward')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function setupWindowEvents() {
|
function setupWindowEvents() {
|
||||||
if (!win) return
|
if (!win) return
|
||||||
win.on('maximize', () => {
|
win.on('maximize', () => {
|
||||||
@ -199,6 +226,7 @@ export function createWindow() {
|
|||||||
|
|
||||||
setupWindowEvents()
|
setupWindowEvents()
|
||||||
attachKeyboardShortcuts(win)
|
attachKeyboardShortcuts(win)
|
||||||
|
setupNavigationGestures(win)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWindow() {
|
export function getWindow() {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { createContext, useCallback, useEffect, useState } from 'react'
|
import { createContext, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
|
||||||
@ -32,12 +32,43 @@ export function isElectron() {
|
|||||||
|
|
||||||
const ElectronContext = createContext()
|
const ElectronContext = createContext()
|
||||||
|
|
||||||
|
function isInHorizontalScrollContainer(element) {
|
||||||
|
let node = element
|
||||||
|
while (node && node !== document.body) {
|
||||||
|
const { overflowX } = window.getComputedStyle(node)
|
||||||
|
if (
|
||||||
|
(overflowX === 'auto' || overflowX === 'scroll') &&
|
||||||
|
node.scrollWidth > node.clientWidth
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
node = node.parentElement
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const ElectronProvider = ({ children }) => {
|
const ElectronProvider = ({ children }) => {
|
||||||
const [platform, setPlatform] = useState('unknown')
|
const [platform, setPlatform] = useState('unknown')
|
||||||
const [isMaximized, setIsMaximized] = useState(false)
|
const [isMaximized, setIsMaximized] = useState(false)
|
||||||
const [isFullScreen, setIsFullScreen] = useState(false)
|
const [isFullScreen, setIsFullScreen] = useState(false)
|
||||||
const [electronAvailable] = useState(isElectron())
|
const [electronAvailable] = useState(isElectron())
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const lastNavigationAtRef = useRef(0)
|
||||||
|
|
||||||
|
const navigateHistory = useCallback(
|
||||||
|
(direction) => {
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - lastNavigationAtRef.current < 300) return
|
||||||
|
lastNavigationAtRef.current = now
|
||||||
|
|
||||||
|
if (direction === 'back') {
|
||||||
|
navigate(-1)
|
||||||
|
} else if (direction === 'forward') {
|
||||||
|
navigate(1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[navigate]
|
||||||
|
)
|
||||||
|
|
||||||
// Function to open external URL via Electron
|
// Function to open external URL via Electron
|
||||||
const openExternalUrl = (url) => {
|
const openExternalUrl = (url) => {
|
||||||
@ -92,11 +123,47 @@ const ElectronProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
ipcRenderer.on('navigate', navigateHandler)
|
ipcRenderer.on('navigate', navigateHandler)
|
||||||
|
|
||||||
|
const navigationGestureHandler = (_event, direction) => {
|
||||||
|
navigateHistory(direction)
|
||||||
|
}
|
||||||
|
ipcRenderer.on('navigation-gesture', navigationGestureHandler)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
ipcRenderer.removeListener('navigate', navigateHandler)
|
ipcRenderer.removeListener('navigate', navigateHandler)
|
||||||
|
ipcRenderer.removeListener('navigation-gesture', navigationGestureHandler)
|
||||||
ipcRenderer.removeListener('window-state', windowStateHandler)
|
ipcRenderer.removeListener('window-state', windowStateHandler)
|
||||||
}
|
}
|
||||||
}, [navigate])
|
}, [navigate, navigateHistory])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!electronAvailable || platform !== 'darwin') return
|
||||||
|
|
||||||
|
let accumulatedDeltaX = 0
|
||||||
|
let resetTimer
|
||||||
|
|
||||||
|
const handleWheel = (event) => {
|
||||||
|
if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return
|
||||||
|
if (Math.abs(event.deltaX) < Math.abs(event.deltaY)) return
|
||||||
|
if (Math.abs(event.deltaX) < 2) return
|
||||||
|
if (isInHorizontalScrollContainer(event.target)) return
|
||||||
|
|
||||||
|
accumulatedDeltaX += event.deltaX
|
||||||
|
clearTimeout(resetTimer)
|
||||||
|
resetTimer = setTimeout(() => {
|
||||||
|
if (Math.abs(accumulatedDeltaX) > 60) {
|
||||||
|
navigateHistory(accumulatedDeltaX > 0 ? 'forward' : 'back')
|
||||||
|
}
|
||||||
|
accumulatedDeltaX = 0
|
||||||
|
}, 80)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('wheel', handleWheel, { passive: true })
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('wheel', handleWheel)
|
||||||
|
clearTimeout(resetTimer)
|
||||||
|
}
|
||||||
|
}, [electronAvailable, navigateHistory, platform])
|
||||||
|
|
||||||
// Window control handler
|
// Window control handler
|
||||||
const handleWindowControl = (action) => {
|
const handleWindowControl = (action) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user