diff --git a/assets/stylesheets/App.css b/assets/stylesheets/App.css index 88ba4f7..3614b88 100644 --- a/assets/stylesheets/App.css +++ b/assets/stylesheets/App.css @@ -190,6 +190,27 @@ html.macos-vibrancy .dark-mode .ant-layout.main-content-layout .ant-card { background: rgba(31, 31, 31, 0.5) !important; } +.redTrafficLight { + width: 12px; + height: 12px; + background-color: #fe5f57; + border-radius: 50%; +} + +.yellowTrafficLight { + width: 12px; + height: 12px; + background-color: #febc2e; + border-radius: 50%; +} + +.greenTrafficLight { + width: 12px; + height: 12px; + background-color: #28c840; + border-radius: 50%; +} + .electron-navigation { line-height: 40px; } diff --git a/assets/trafficlights/closecolored.svg b/assets/trafficlights/closecolored.svg new file mode 100644 index 0000000..7aead9a --- /dev/null +++ b/assets/trafficlights/closecolored.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/trafficlights/closedowncolored.svg b/assets/trafficlights/closedowncolored.svg new file mode 100644 index 0000000..707b68f --- /dev/null +++ b/assets/trafficlights/closedowncolored.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/trafficlights/closehovercolored.svg b/assets/trafficlights/closehovercolored.svg new file mode 100644 index 0000000..3c0db94 --- /dev/null +++ b/assets/trafficlights/closehovercolored.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/trafficlights/exitfullscreendowncolored.svg b/assets/trafficlights/exitfullscreendowncolored.svg new file mode 100644 index 0000000..62abef8 --- /dev/null +++ b/assets/trafficlights/exitfullscreendowncolored.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/trafficlights/exitfullscreenhovercolored.svg b/assets/trafficlights/exitfullscreenhovercolored.svg new file mode 100644 index 0000000..177210b --- /dev/null +++ b/assets/trafficlights/exitfullscreenhovercolored.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/assets/trafficlights/fullscreencolored.svg b/assets/trafficlights/fullscreencolored.svg new file mode 100644 index 0000000..c098917 --- /dev/null +++ b/assets/trafficlights/fullscreencolored.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/trafficlights/fullscreendowncolored.svg b/assets/trafficlights/fullscreendowncolored.svg new file mode 100644 index 0000000..e9dd466 --- /dev/null +++ b/assets/trafficlights/fullscreendowncolored.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/trafficlights/fullscreenhovercolored.svg b/assets/trafficlights/fullscreenhovercolored.svg new file mode 100644 index 0000000..38ef153 --- /dev/null +++ b/assets/trafficlights/fullscreenhovercolored.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/trafficlights/minimizecolored.svg b/assets/trafficlights/minimizecolored.svg new file mode 100644 index 0000000..480df43 --- /dev/null +++ b/assets/trafficlights/minimizecolored.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/trafficlights/minimizedowncolored.svg b/assets/trafficlights/minimizedowncolored.svg new file mode 100644 index 0000000..f2ff3e4 --- /dev/null +++ b/assets/trafficlights/minimizedowncolored.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/trafficlights/minimizehovercolored.svg b/assets/trafficlights/minimizehovercolored.svg new file mode 100644 index 0000000..6cad028 --- /dev/null +++ b/assets/trafficlights/minimizehovercolored.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/trafficlights/nofocus.svg b/assets/trafficlights/nofocus.svg new file mode 100644 index 0000000..79efe5e --- /dev/null +++ b/assets/trafficlights/nofocus.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/native/macos/window-effects.mm b/native/macos/window-effects.mm index 1898603..04ddaac 100644 --- a/native/macos/window-effects.mm +++ b/native/macos/window-effects.mm @@ -106,6 +106,7 @@ static const void *kElectrobunChromeObserverKey = &kElectrobunChromeObserverKey; static bool applyTrafficLightsPosition(NSWindow *window, CGFloat x, CGFloat yFromTop); static void refreshWindowChrome(NSWindow *window); +static BOOL isWindowFullScreen(NSWindow *window); @implementation ElectrobunWindowChromeObserver @@ -134,6 +135,10 @@ static void refreshWindowChrome(NSWindow *window); selector:@selector(refreshChrome) name:NSWindowDidBecomeKeyNotification object:window]; + [center addObserver:self + selector:@selector(refreshChrome) + name:NSWindowWillEnterFullScreenNotification + object:window]; } return self; } diff --git a/public/mainWindow.js b/public/mainWindow.js index 0dd67d2..223c7f8 100644 --- a/public/mainWindow.js +++ b/public/mainWindow.js @@ -265,6 +265,9 @@ export function setupMainWindowIPC() { case 'close': win.close() break + case 'fullscreen': + win.setFullScreen(!win.isFullScreen()) + break default: break } diff --git a/src/components/Dashboard/common/DashboardNavigation.jsx b/src/components/Dashboard/common/DashboardNavigation.jsx index 9a4c4c4..2965b9d 100644 --- a/src/components/Dashboard/common/DashboardNavigation.jsx +++ b/src/components/Dashboard/common/DashboardNavigation.jsx @@ -64,7 +64,7 @@ const DashboardNavigation = () => { icon: }) const isMobile = useMediaQuery({ maxWidth: 768 }) - const { platform, isElectron, isFullScreen, setSidebarViewMenu } = + const { platform, isElectron, setSidebarViewMenu } = useContext(ElectronContext) const { availableUpdate, checkForUpdates } = useAppUpdateContext() const mainMenuItems = useMemo( @@ -140,9 +140,7 @@ const DashboardNavigation = () => { setSidebarViewMenu(sections) }, [isElectron, setSidebarViewMenu]) - const showAppLogo = - (isElectron && platform == 'darwin' && isFullScreen == true) || - (isElectron && platform != 'darwin') + const showAppLogo = isElectron && platform != 'darwin' const isMacOSApp = isElectron && platform == 'darwin' const isOtherApp = isElectron && platform != 'darwin' diff --git a/src/components/Dashboard/common/DashboardWindowButtons.jsx b/src/components/Dashboard/common/DashboardWindowButtons.jsx index 157c52f..2a4a32e 100644 --- a/src/components/Dashboard/common/DashboardWindowButtons.jsx +++ b/src/components/Dashboard/common/DashboardWindowButtons.jsx @@ -5,6 +5,7 @@ import XMarkIcon from '../../Icons/XMarkIcon' import MinusIcon from '../../Icons/MinusIcon' import ContractIcon from '../../Icons/ContractIcon' import ExpandIcon from '../../Icons/ExpandIcon' +import MacOSTrafficLights from './MacOSTrafficLights' const DashboardWindowButtons = () => { const { isMaximized, handleWindowControl, platform, isFullScreen } = @@ -17,14 +18,21 @@ const DashboardWindowButtons = () => { onClick={() => handleWindowControl('close')} /> ) - const maximizeButton = ( + const minimizeButton = ( + ) +} + +TrafficLightButton.propTypes = { + action: PropTypes.string.isRequired, + icons: PropTypes.shape({ + colored: PropTypes.elementType.isRequired, + hover: PropTypes.elementType.isRequired, + down: PropTypes.elementType.isRequired, + noFocus: PropTypes.elementType.isRequired + }).isRequired, + isGroupHovered: PropTypes.bool.isRequired, + activeAction: PropTypes.string, + isEnabled: PropTypes.bool.isRequired, + isWindowFocused: PropTypes.bool.isRequired, + onActive: PropTypes.func.isRequired, + onAction: PropTypes.func.isRequired +} + +const MacOSTrafficLights = () => { + const { handleWindowControl, isFullScreen } = useContext(ElectronContext) + const [isGroupHovered, setIsGroupHovered] = useState(false) + const [activeAction, setActiveAction] = useState(null) + const [isWindowFocused, setIsWindowFocused] = useState( + () => document.hasFocus?.() ?? true + ) + + useEffect(() => { + const handleFocus = () => setIsWindowFocused(true) + const handleBlur = () => setIsWindowFocused(false) + + window.addEventListener('focus', handleFocus) + window.addEventListener('blur', handleBlur) + + return () => { + window.removeEventListener('focus', handleFocus) + window.removeEventListener('blur', handleBlur) + } + }, []) + + const fullscreenIcons = isFullScreen + ? { + colored: FullscreenColored, + hover: ExitFullscreenHoverColored, + down: ExitFullscreenDownColored, + noFocus: NoFocus + } + : { + colored: FullscreenColored, + hover: FullscreenHoverColored, + down: FullscreenDownColored, + noFocus: NoFocus + } + + const handleAction = (action) => { + handleWindowControl(action) + } + + return ( + setIsGroupHovered(true)} + onMouseLeave={() => { + setIsGroupHovered(false) + setActiveAction(null) + }} + > + + + + + ) +} + +export default MacOSTrafficLights diff --git a/src/desktop/window.js b/src/desktop/window.js index 119dd5b..d4be591 100644 --- a/src/desktop/window.js +++ b/src/desktop/window.js @@ -93,6 +93,7 @@ function setupWindowEvents(window) { // Electrobun emits resize/focus, not Electron's maximize/fullscreen events. window.on?.('resize', broadcastWindowState) window.on?.('focus', broadcastWindowState) + window.on?.('move', broadcastWindowState) } export function setupMainWindowMessaging(window = mainWindow) { @@ -229,6 +230,14 @@ export function handleWindowControl(action) { mainWindow.maximize?.() } break + case 'fullscreen': + if (mainWindow.isFullScreen?.()) { + mainWindow.setFullScreen?.(false) + } else { + mainWindow.setFullScreen?.(true) + } + setTimeout(broadcastWindowState, 100) + break case 'close': mainWindow.close?.() break