diff --git a/apps/readest-app/src-tauri/src/macos/traffic_light.rs b/apps/readest-app/src-tauri/src/macos/traffic_light.rs index d542c1bb..bd58bbec 100644 --- a/apps/readest-app/src-tauri/src/macos/traffic_light.rs +++ b/apps/readest-app/src-tauri/src/macos/traffic_light.rs @@ -365,26 +365,26 @@ pub fn setup_traffic_light_positioner(window: Window) { app_box: *mut c_void = app_box, toolbar: id = cocoa::base::nil, super_delegate: id = current_delegate, - (windowShouldClose:) => on_window_should_close as extern fn(&Object, Sel, id) -> BOOL, - (windowWillClose:) => on_window_will_close as extern fn(&Object, Sel, id), - (windowDidResize:) => on_window_did_resize:: as extern fn(&Object, Sel, id), - (windowDidMove:) => on_window_did_move as extern fn(&Object, Sel, id), - (windowDidChangeBackingProperties:) => on_window_did_change_backing_properties as extern fn(&Object, Sel, id), - (windowDidBecomeKey:) => on_window_did_become_key as extern fn(&Object, Sel, id), - (windowDidResignKey:) => on_window_did_resign_key as extern fn(&Object, Sel, id), - (draggingEntered:) => on_dragging_entered as extern fn(&Object, Sel, id) -> BOOL, - (prepareForDragOperation:) => on_prepare_for_drag_operation as extern fn(&Object, Sel, id) -> BOOL, - (performDragOperation:) => on_perform_drag_operation as extern fn(&Object, Sel, id) -> BOOL, - (concludeDragOperation:) => on_conclude_drag_operation as extern fn(&Object, Sel, id), - (draggingExited:) => on_dragging_exited as extern fn(&Object, Sel, id), - (window:willUseFullScreenPresentationOptions:) => on_window_will_use_full_screen_presentation_options as extern fn(&Object, Sel, id, NSUInteger) -> NSUInteger, - (windowDidEnterFullScreen:) => on_window_did_enter_full_screen:: as extern fn(&Object, Sel, id), - (windowWillEnterFullScreen:) => on_window_will_enter_full_screen:: as extern fn(&Object, Sel, id), - (windowDidExitFullScreen:) => on_window_did_exit_full_screen:: as extern fn(&Object, Sel, id), - (windowWillExitFullScreen:) => on_window_will_exit_full_screen:: as extern fn(&Object, Sel, id), - (windowDidFailToEnterFullScreen:) => on_window_did_fail_to_enter_full_screen as extern fn(&Object, Sel, id), - (effectiveAppearanceDidChange:) => on_effective_appearance_did_change as extern fn(&Object, Sel, id), - (effectiveAppearanceDidChangedOnMainThread:) => on_effective_appearance_did_changed_on_main_thread as extern fn(&Object, Sel, id) + (windowShouldClose:) => on_window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL, + (windowWillClose:) => on_window_will_close as extern "C" fn(&Object, Sel, id), + (windowDidResize:) => on_window_did_resize:: as extern "C" fn(&Object, Sel, id), + (windowDidMove:) => on_window_did_move as extern "C" fn(&Object, Sel, id), + (windowDidChangeBackingProperties:) => on_window_did_change_backing_properties as extern "C" fn(&Object, Sel, id), + (windowDidBecomeKey:) => on_window_did_become_key as extern "C" fn(&Object, Sel, id), + (windowDidResignKey:) => on_window_did_resign_key as extern "C" fn(&Object, Sel, id), + (draggingEntered:) => on_dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL, + (prepareForDragOperation:) => on_prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + (performDragOperation:) => on_perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, + (concludeDragOperation:) => on_conclude_drag_operation as extern "C" fn(&Object, Sel, id), + (draggingExited:) => on_dragging_exited as extern "C" fn(&Object, Sel, id), + (window:willUseFullScreenPresentationOptions:) => on_window_will_use_full_screen_presentation_options as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger, + (windowDidEnterFullScreen:) => on_window_did_enter_full_screen:: as extern "C" fn(&Object, Sel, id), + (windowWillEnterFullScreen:) => on_window_will_enter_full_screen:: as extern "C" fn(&Object, Sel, id), + (windowDidExitFullScreen:) => on_window_did_exit_full_screen:: as extern "C" fn(&Object, Sel, id), + (windowWillExitFullScreen:) => on_window_will_exit_full_screen:: as extern "C" fn(&Object, Sel, id), + (windowDidFailToEnterFullScreen:) => on_window_did_fail_to_enter_full_screen as extern "C" fn(&Object, Sel, id), + (effectiveAppearanceDidChange:) => on_effective_appearance_did_change as extern "C" fn(&Object, Sel, id), + (effectiveAppearanceDidChangedOnMainThread:) => on_effective_appearance_did_changed_on_main_thread as extern "C" fn(&Object, Sel, id) })) } } diff --git a/apps/readest-app/src/app/reader/components/BooksGrid.tsx b/apps/readest-app/src/app/reader/components/BooksGrid.tsx index 0acab5c2..96ce2f8a 100644 --- a/apps/readest-app/src/app/reader/components/BooksGrid.tsx +++ b/apps/readest-app/src/app/reader/components/BooksGrid.tsx @@ -158,6 +158,7 @@ const BooksGrid: React.FC = ({ bookKeys, onCloseBook }) => { )} {showHeader && ( = ({ const { appService } = useEnv(); const headerRef = useRef(null); const { - isTrafficLightVisible, + trafficLightInFullscreen, setTrafficLightVisibility, initializeTrafficLightStore, initializeTrafficLightListeners, @@ -70,12 +70,19 @@ const HeaderBar: React.FC = ({ useEffect(() => { if (!appService?.hasTrafficLight) return; + if (isSideBarVisible) return; - setTrafficLightVisibility(isSideBarVisible); + if (hoveredBookKey === bookKey && isTopLeft) { + setTrafficLightVisibility(true, { x: 10, y: 20 }); + } else if (!hoveredBookKey) { + setTrafficLightVisibility(false); + } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [appService, isSideBarVisible]); + }, [appService, isSideBarVisible, hoveredBookKey]); - const isVisible = hoveredBookKey === bookKey || isDropdownOpen; + const isHeaderVisible = hoveredBookKey === bookKey || isDropdownOpen; + const trafficLightInHeader = + appService?.hasTrafficLight && !trafficLightInFullscreen && !isSideBarVisible && isTopLeft; return (
= ({ className={clsx( 'bg-base-100 absolute left-0 right-0 top-0 z-10', appService?.hasRoundedWindow && 'rounded-window-top-right', - isVisible ? 'visible' : 'hidden', + isHeaderVisible ? 'visible' : 'hidden', )} style={{ height: systemUIVisible ? `${Math.max(gridInsets.top, statusBarHeight)}px` : '0px', @@ -104,11 +111,11 @@ const HeaderBar: React.FC = ({ className={clsx( `header-bar bg-base-100 absolute top-0 z-10 flex h-11 w-full items-center pr-4`, `shadow-xs transition-[opacity,margin-top] duration-300`, - isTrafficLightVisible && isTopLeft && !isSideBarVisible ? 'pl-16' : 'pl-4', + trafficLightInHeader ? 'pl-20' : 'pl-4', appService?.hasRoundedWindow && 'rounded-window-top-right', !isSideBarVisible && appService?.hasRoundedWindow && 'rounded-window-top-left', isHoveredAnim && 'hover-bar-anim', - isVisible ? 'pointer-events-auto visible' : 'pointer-events-none opacity-0', + isHeaderVisible ? 'pointer-events-auto visible' : 'pointer-events-none opacity-0', isDropdownOpen && 'header-bar-pinned', )} style={{ @@ -148,10 +155,14 @@ const HeaderBar: React.FC = ({ className='window-buttons flex h-full items-center' headerRef={headerRef} showMinimize={ - bookKeys.length == 1 && !isTrafficLightVisible && appService?.appPlatform !== 'web' + bookKeys.length == 1 && + !appService?.hasTrafficLight && + appService?.appPlatform !== 'web' } showMaximize={ - bookKeys.length == 1 && !isTrafficLightVisible && appService?.appPlatform !== 'web' + bookKeys.length == 1 && + !appService?.hasTrafficLight && + appService?.appPlatform !== 'web' } onClose={() => { setHoveredBookKey(null); diff --git a/apps/readest-app/src/app/reader/components/SectionInfo.tsx b/apps/readest-app/src/app/reader/components/SectionInfo.tsx index de726634..b1f15b0e 100644 --- a/apps/readest-app/src/app/reader/components/SectionInfo.tsx +++ b/apps/readest-app/src/app/reader/components/SectionInfo.tsx @@ -3,8 +3,10 @@ import React from 'react'; import { Insets } from '@/types/misc'; import { useEnv } from '@/context/EnvContext'; import { useThemeStore } from '@/store/themeStore'; +import { useReaderStore } from '@/store/readerStore'; interface SectionInfoProps { + bookKey: string; section?: string; showDoubleBorder: boolean; isScrolled: boolean; @@ -15,6 +17,7 @@ interface SectionInfoProps { } const SectionInfo: React.FC = ({ + bookKey, section, showDoubleBorder, isScrolled, @@ -24,6 +27,7 @@ const SectionInfo: React.FC = ({ gridInsets, }) => { const { appService } = useEnv(); + const { hoveredBookKey } = useReaderStore(); const { systemUIVisible, statusBarHeight } = useThemeStore(); const topInset = Math.max( gridInsets.top, @@ -68,6 +72,7 @@ const SectionInfo: React.FC = ({ className={clsx( 'text-neutral-content text-center font-sans text-xs font-light', isVertical ? '' : 'line-clamp-1', + !isVertical && hoveredBookKey == bookKey && 'hidden', )} > {section || ''} diff --git a/apps/readest-app/src/store/trafficLightStore.ts b/apps/readest-app/src/store/trafficLightStore.ts index 01bd6225..8f10618a 100644 --- a/apps/readest-app/src/store/trafficLightStore.ts +++ b/apps/readest-app/src/store/trafficLightStore.ts @@ -9,8 +9,9 @@ interface TrafficLightState { appService?: AppService; isTrafficLightVisible: boolean; shouldShowTrafficLight: boolean; + trafficLightInFullscreen: boolean; initializeTrafficLightStore: (appService: AppService) => void; - setTrafficLightVisibility: (visible: boolean) => void; + setTrafficLightVisibility: (visible: boolean, position?: { x: number; y: number }) => void; initializeTrafficLightListeners: () => Promise; cleanupTrafficLightListeners: () => void; unlistenEnterFullScreen?: () => void; @@ -22,6 +23,7 @@ export const useTrafficLightStore = create((set, get) => { appService: undefined, isTrafficLightVisible: false, shouldShowTrafficLight: false, + trafficLightInFullscreen: false, initializeTrafficLightStore: (appService: AppService) => { set({ @@ -31,15 +33,19 @@ export const useTrafficLightStore = create((set, get) => { }); }, - setTrafficLightVisibility: async (visible: boolean) => { + setTrafficLightVisibility: async (visible: boolean, position?: { x: number; y: number }) => { const { getCurrentWindow } = await import('@tauri-apps/api/window'); const currentWindow = getCurrentWindow(); const isFullscreen = await currentWindow.isFullscreen(); - set({ isTrafficLightVisible: !isFullscreen && visible, shouldShowTrafficLight: visible }); + set({ + isTrafficLightVisible: !isFullscreen && visible, + shouldShowTrafficLight: visible, + trafficLightInFullscreen: isFullscreen, + }); invoke('set_traffic_lights', { visible: visible, - x: WINDOW_CONTROL_PAD_X, - y: WINDOW_CONTROL_PAD_Y, + x: position?.x ?? WINDOW_CONTROL_PAD_X, + y: position?.y ?? WINDOW_CONTROL_PAD_Y, }); }, @@ -48,12 +54,12 @@ export const useTrafficLightStore = create((set, get) => { const currentWindow = getCurrentWindow(); const unlistenEnterFullScreen = await currentWindow.listen('will-enter-fullscreen', () => { - set({ isTrafficLightVisible: false }); + set({ isTrafficLightVisible: false, trafficLightInFullscreen: true }); }); const unlistenExitFullScreen = await currentWindow.listen('will-exit-fullscreen', () => { const { shouldShowTrafficLight } = get(); - set({ isTrafficLightVisible: shouldShowTrafficLight }); + set({ isTrafficLightVisible: shouldShowTrafficLight, trafficLightInFullscreen: false }); }); set({ unlistenEnterFullScreen, unlistenExitFullScreen });