import clsx from 'clsx'; import React, { useEffect, useRef } from 'react'; import { useEnv } from '@/context/EnvContext'; import { tauriHandleMinimize, tauriHandleToggleMaximize, tauriHandleClose } from '@/utils/window'; import { isTauriAppPlatform } from '@/services/environment'; interface WindowButtonsProps { className?: string; headerRef?: React.RefObject; showMinimize?: boolean; showMaximize?: boolean; showClose?: boolean; onMinimize?: () => void; onToggleMaximize?: () => void; onClose?: () => void; } interface WindowButtonProps { id: string; onClick: () => void; ariaLabel: string; children: React.ReactNode; } const WindowButton: React.FC = ({ onClick, ariaLabel, id, children }) => ( ); const WindowButtons: React.FC = ({ className, headerRef, showMinimize = true, showMaximize = true, showClose = true, onMinimize, onToggleMaximize, onClose, }) => { const parentRef = useRef(null); const { appService } = useEnv(); const touchState = useRef({ lastTouchTime: 0, touchStartPosition: { x: 0, y: 0 }, }); const isExcludedElement = (target: HTMLElement) => { return ( target.closest('.btn') || target.closest('.window-button') || target.closest('.dropdown-container') || target.closest('.exclude-title-bar-mousedown') ); }; const handleMouseDown = async (e: MouseEvent) => { const target = e.target as HTMLElement; if (isExcludedElement(target)) { return; } const { getCurrentWindow } = await import('@tauri-apps/api/window'); if (e.buttons === 1) { if (e.detail === 2) { getCurrentWindow().toggleMaximize(); } else { getCurrentWindow().startDragging(); } } }; const handleTouchStart = async (e: TouchEvent) => { const target = e.target as HTMLElement; const touch = e.touches[0]!; if (isExcludedElement(target)) { return; } const currentTime = Date.now(); const timeDiff = currentTime - touchState.current.lastTouchTime; touchState.current.touchStartPosition = { x: touch.clientX, y: touch.clientY, }; if (timeDiff < 300) { const { getCurrentWindow } = await import('@tauri-apps/api/window'); getCurrentWindow().toggleMaximize(); return; } touchState.current.lastTouchTime = currentTime; }; const handleTouchMove = async (e: TouchEvent) => { const target = e.target as HTMLElement; const touch = e.touches[0]!; if (isExcludedElement(target)) { return; } const deltaX = Math.abs(touch.clientX - touchState.current.touchStartPosition.x); const deltaY = Math.abs(touch.clientY - touchState.current.touchStartPosition.y); if (deltaX > 5 || deltaY > 5) { try { const { getCurrentWindow } = await import('@tauri-apps/api/window'); await getCurrentWindow().startDragging(); } catch (error) { console.warn('Failed to start window dragging:', error); } } }; useEffect(() => { if (!isTauriAppPlatform()) return; const headerElement = headerRef?.current; if (!headerElement) return; headerElement.addEventListener('mousedown', handleMouseDown); headerElement.addEventListener('touchstart', handleTouchStart, { passive: false }); headerElement.addEventListener('touchmove', handleTouchMove, { passive: false }); return () => { headerElement.removeEventListener('mousedown', handleMouseDown); headerElement.removeEventListener('touchstart', handleTouchStart); headerElement.removeEventListener('touchmove', handleTouchMove); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleMinimize = async () => { if (onMinimize) { onMinimize(); } else { tauriHandleMinimize(); } }; const handleMaximize = async () => { if (onToggleMaximize) { onToggleMaximize(); } else { tauriHandleToggleMaximize(); } }; const handleClose = async () => { if (onClose) { onClose(); } else { tauriHandleClose(); } }; return (
{showMinimize && appService?.hasWindowBar && ( )} {showMaximize && appService?.hasWindowBar && ( )} {showClose && (appService?.hasWindowBar || onClose) && ( )}
); }; export default WindowButtons;