mirror of
https://github.com/fluxerapp/fluxer.git
synced 2026-09-03 05:10:25 +03:00
fix(app): make click-through the default and move animation pausing to Accessibility (#1722)
This commit is contained in:
@@ -16,11 +16,6 @@
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:global(html.platform-native.window-focus-activation-guard:not(.first-click-passthrough-when-unfocused))
|
||||
:local(.appContainer) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:global(html.is-standalone:not(.auth-page)) :local(.appContainer) {
|
||||
padding-top: env(safe-area-inset-top);
|
||||
}
|
||||
@@ -86,12 +81,6 @@
|
||||
--skip-link-titlebar-offset: var(--native-titlebar-height);
|
||||
--skip-link-traffic-lights-offset: 4.5rem;
|
||||
}
|
||||
:global(html.platform-native.window-focus-activation-guard:not(.first-click-passthrough-when-unfocused))
|
||||
:local(.overlayScope)
|
||||
> :not([data-overlay-pass-through]) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.quickSwitcherPortal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
|
||||
@@ -45,15 +45,9 @@ import {getActivePortalHost, setActivePortalHost} from '@app/features/ui/overlay
|
||||
import MobileLayout from '@app/features/ui/state/MobileLayout';
|
||||
import Modal from '@app/features/ui/state/Modal';
|
||||
import Popout from '@app/features/ui/state/Popout';
|
||||
import {
|
||||
getDesktopWindowBehaviorSettings,
|
||||
setDesktopWindowBehaviorSettings,
|
||||
} from '@app/features/ui/utils/DesktopWindowBehaviorUtils';
|
||||
import {getDesktopWindowBehaviorSettings} from '@app/features/ui/utils/DesktopWindowBehaviorUtils';
|
||||
import {attachExternalLinkInterceptor, isDesktop} from '@app/features/ui/utils/NativeUtils';
|
||||
import {
|
||||
FIRST_CLICK_PASSTHROUGH_WHEN_UNFOCUSED_CLASS,
|
||||
UNFOCUSED_FULLY_INTERACTIVE_CLASS,
|
||||
} from '@app/features/ui/utils/WindowFocusInteractionGuard';
|
||||
import {UNFOCUSED_FULLY_INTERACTIVE_CLASS} from '@app/features/ui/utils/WindowFocusInteractionGuard';
|
||||
import UserSettings from '@app/features/user/state/UserSettings';
|
||||
import {IncomingCallManager} from '@app/features/voice/components/IncomingCallManager';
|
||||
import {VoiceLiveKitRoot} from '@app/features/voice/components/VoiceLiveKitRoot';
|
||||
@@ -86,7 +80,6 @@ export const AppWrapper = observer(({children}: AppWrapperProps) => {
|
||||
const {i18n} = useLingui();
|
||||
const reducedMotion = Accessibility.useReducedMotion;
|
||||
const stayInteractiveWhenUnfocused = Accessibility.stayInteractiveWhenUnfocused;
|
||||
const firstClickPassThroughWhenUnfocused = Accessibility.firstClickPassThroughWhenUnfocused;
|
||||
const {platform, isNative} = useNativePlatform();
|
||||
const useSystemTitleBar = useNativeTitleBar();
|
||||
const messageDisplayCompact = UserSettings.getMessageDisplayCompact();
|
||||
@@ -163,11 +156,6 @@ export const AppWrapper = observer(({children}: AppWrapperProps) => {
|
||||
useDocumentClassToggle('reduced-motion', reducedMotion);
|
||||
useDocumentClassToggle('mobile-layout', MobileLayout.platformMobileDetected || MobileLayout.enabled);
|
||||
useDocumentClassToggle(UNFOCUSED_FULLY_INTERACTIVE_CLASS, stayInteractiveWhenUnfocused);
|
||||
useDocumentClassToggle(FIRST_CLICK_PASSTHROUGH_WHEN_UNFOCUSED_CLASS, firstClickPassThroughWhenUnfocused);
|
||||
useEffect(() => {
|
||||
if (!isNative) return;
|
||||
void setDesktopWindowBehaviorSettings({firstClickPassThroughWhenUnfocused});
|
||||
}, [isNative, firstClickPassThroughWhenUnfocused]);
|
||||
useDesktopAllowTransparency(isNative);
|
||||
useWindowEventListeners({preventDocumentScroll: !isNative});
|
||||
useRemScaleTracking();
|
||||
|
||||
@@ -600,7 +600,6 @@ export interface AccessibilitySettings {
|
||||
hdrDisplayMode: HdrDisplayMode;
|
||||
preserveEditDraft: boolean;
|
||||
stayInteractiveWhenUnfocused: boolean;
|
||||
firstClickPassThroughWhenUnfocused: boolean;
|
||||
scrollToBottomOnMessageSend: boolean;
|
||||
sequentialFileSend: boolean;
|
||||
showNeko: boolean;
|
||||
@@ -718,7 +717,6 @@ class Accessibility {
|
||||
hdrDisplayMode = HdrDisplayMode.FULL;
|
||||
preserveEditDraft = false;
|
||||
stayInteractiveWhenUnfocused = false;
|
||||
firstClickPassThroughWhenUnfocused = false;
|
||||
scrollToBottomOnMessageSend = true;
|
||||
sequentialFileSend = false;
|
||||
showNeko = false;
|
||||
@@ -829,7 +827,6 @@ class Accessibility {
|
||||
'hdrDisplayMode',
|
||||
'preserveEditDraft',
|
||||
'stayInteractiveWhenUnfocused',
|
||||
'firstClickPassThroughWhenUnfocused',
|
||||
'scrollToBottomOnMessageSend',
|
||||
'sequentialFileSend',
|
||||
],
|
||||
@@ -890,7 +887,6 @@ class Accessibility {
|
||||
hdrDisplayMode: HDR_TO_PROTO[s.hdrDisplayMode],
|
||||
preserveEditDraft: s.preserveEditDraft,
|
||||
stayInteractiveWhenUnfocused: s.stayInteractiveWhenUnfocused,
|
||||
firstClickPassThroughWhenUnfocused: s.firstClickPassThroughWhenUnfocused,
|
||||
scrollToBottomOnMessageSend: s.scrollToBottomOnMessageSend,
|
||||
sequentialFileSend: s.sequentialFileSend,
|
||||
}),
|
||||
@@ -980,8 +976,6 @@ class Accessibility {
|
||||
if (m.preserveEditDraft !== undefined) s.preserveEditDraft = m.preserveEditDraft;
|
||||
if (m.stayInteractiveWhenUnfocused !== undefined)
|
||||
s.stayInteractiveWhenUnfocused = m.stayInteractiveWhenUnfocused;
|
||||
if (m.firstClickPassThroughWhenUnfocused !== undefined)
|
||||
s.firstClickPassThroughWhenUnfocused = m.firstClickPassThroughWhenUnfocused;
|
||||
if (m.scrollToBottomOnMessageSend !== undefined) s.scrollToBottomOnMessageSend = m.scrollToBottomOnMessageSend;
|
||||
if (m.sequentialFileSend !== undefined) s.sequentialFileSend = m.sequentialFileSend;
|
||||
},
|
||||
@@ -1316,8 +1310,6 @@ class Accessibility {
|
||||
if (validated.preserveEditDraft !== undefined) this.preserveEditDraft = validated.preserveEditDraft;
|
||||
if (validated.stayInteractiveWhenUnfocused !== undefined)
|
||||
this.stayInteractiveWhenUnfocused = validated.stayInteractiveWhenUnfocused;
|
||||
if (validated.firstClickPassThroughWhenUnfocused !== undefined)
|
||||
this.firstClickPassThroughWhenUnfocused = validated.firstClickPassThroughWhenUnfocused;
|
||||
if (validated.scrollToBottomOnMessageSend !== undefined)
|
||||
this.scrollToBottomOnMessageSend = validated.scrollToBottomOnMessageSend;
|
||||
if (validated.sequentialFileSend !== undefined) this.sequentialFileSend = validated.sequentialFileSend;
|
||||
@@ -1418,8 +1410,6 @@ class Accessibility {
|
||||
hdrDisplayMode: data.hdrDisplayMode ?? this.hdrDisplayMode,
|
||||
preserveEditDraft: data.preserveEditDraft ?? this.preserveEditDraft,
|
||||
stayInteractiveWhenUnfocused: data.stayInteractiveWhenUnfocused ?? this.stayInteractiveWhenUnfocused,
|
||||
firstClickPassThroughWhenUnfocused:
|
||||
data.firstClickPassThroughWhenUnfocused ?? this.firstClickPassThroughWhenUnfocused,
|
||||
scrollToBottomOnMessageSend: data.scrollToBottomOnMessageSend ?? this.scrollToBottomOnMessageSend,
|
||||
sequentialFileSend: data.sequentialFileSend ?? this.sequentialFileSend,
|
||||
showNeko: data.showNeko ?? this.showNeko,
|
||||
|
||||
@@ -67,7 +67,6 @@ import {Popout} from '@app/features/ui/popover/PopoverPopout';
|
||||
import ContextMenu from '@app/features/ui/state/ContextMenu';
|
||||
import KeyboardMode from '@app/features/ui/state/KeyboardMode';
|
||||
import {Tooltip} from '@app/features/ui/tooltip/Tooltip';
|
||||
import {canUseWindowFocusedActivationClick} from '@app/features/ui/utils/WindowFocusInteractionGuard';
|
||||
import UserSettings from '@app/features/user/state/UserSettings';
|
||||
import * as AvatarUtils from '@app/features/user/utils/AvatarUtils';
|
||||
import {MessageStates} from '@fluxer/constants/src/ChannelConstants';
|
||||
@@ -187,14 +186,6 @@ const useShiftKey = (enabled: boolean) => {
|
||||
}, [enabled]);
|
||||
return useSyncExternalStore(subscribe, getSnapshot, shiftKeyManager.getServerSnapshot);
|
||||
};
|
||||
const suppressBlockedActivationClick = (event: React.SyntheticEvent): boolean => {
|
||||
if (canUseWindowFocusedActivationClick()) {
|
||||
return false;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return true;
|
||||
};
|
||||
|
||||
interface MessageActionBarButtonProps {
|
||||
label: string;
|
||||
@@ -211,9 +202,6 @@ const MessageActionBarButton = React.forwardRef<HTMLButtonElement, MessageAction
|
||||
({label, icon, onClick, onPointerDownCapture, danger, isActive, hidden, dataAction}, ref) => {
|
||||
const handleClick = useCallback(
|
||||
(event: React.MouseEvent | React.KeyboardEvent) => {
|
||||
if (suppressBlockedActivationClick(event)) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
onClick?.(event);
|
||||
@@ -222,9 +210,6 @@ const MessageActionBarButton = React.forwardRef<HTMLButtonElement, MessageAction
|
||||
);
|
||||
const handlePointerDownCapture = useCallback(
|
||||
(event: React.PointerEvent) => {
|
||||
if (suppressBlockedActivationClick(event)) {
|
||||
return;
|
||||
}
|
||||
onPointerDownCapture?.(event);
|
||||
},
|
||||
[onPointerDownCapture],
|
||||
@@ -275,9 +260,6 @@ const QuickReactionButton = observer(
|
||||
const {url: displayUrl} = getEmojiDisplayData(emoji);
|
||||
const handleClick = useCallback(
|
||||
(event: React.MouseEvent | React.KeyboardEvent) => {
|
||||
if (suppressBlockedActivationClick(event)) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
EmojiPickerCommands.trackEmojiUsage(emoji);
|
||||
@@ -700,7 +682,6 @@ export const MessageActionBarCore: React.FC<MessageActionBarCoreProps> = observe
|
||||
uniqueId={`emoji_picker-actionbar-${message.id}`}
|
||||
shouldAutoUpdate={false}
|
||||
animationType="none"
|
||||
shouldOpenOnClick={() => canUseWindowFocusedActivationClick()}
|
||||
onOpen={handleEmojiPickerOpen}
|
||||
onClose={handleEmojiPickerClose}
|
||||
data-flx="channel.message-action-bar.message-action-bar-core.popout"
|
||||
|
||||
@@ -76,7 +76,6 @@ export interface DesktopWindowBehaviorSettings {
|
||||
activeSmoothScrolling: boolean;
|
||||
middleClickAutoscroll: boolean;
|
||||
activeMiddleClickAutoscroll: boolean;
|
||||
firstClickPassThroughWhenUnfocused: boolean;
|
||||
}
|
||||
|
||||
export interface ThemeLocalFileReference {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
export const WINDOW_FOCUSED_CLASS = 'window-focused';
|
||||
export const WINDOW_FOCUS_ACTIVATION_GUARD_CLASS = 'window-focus-activation-guard';
|
||||
export const UNFOCUSED_FULLY_INTERACTIVE_CLASS = 'unfocused-fully-interactive';
|
||||
export const FIRST_CLICK_PASSTHROUGH_WHEN_UNFOCUSED_CLASS = 'first-click-passthrough-when-unfocused';
|
||||
export const WINDOW_HOVER_CONTROLS_CHANGE_EVENT = 'fluxer-window-hover-controls-change';
|
||||
const DEFAULT_GUARD_TIMEOUT_MS = 250;
|
||||
const DEFAULT_RELEASE_CLEAR_DELAY_MS = 50;
|
||||
@@ -32,16 +31,6 @@ export function isUnfocusedFullyInteractive(root: HTMLElement = document.documen
|
||||
return root.classList.contains(UNFOCUSED_FULLY_INTERACTIVE_CLASS);
|
||||
}
|
||||
|
||||
export function shouldBlockActivationClick(root: HTMLElement = document.documentElement): boolean {
|
||||
return (
|
||||
isWindowFocusActivationGuardActive(root) && !root.classList.contains(FIRST_CLICK_PASSTHROUGH_WHEN_UNFOCUSED_CLASS)
|
||||
);
|
||||
}
|
||||
|
||||
export function canUseWindowFocusedActivationClick(root: HTMLElement = document.documentElement): boolean {
|
||||
return !shouldBlockActivationClick(root);
|
||||
}
|
||||
|
||||
export function canUseWindowFocusedHoverControls(root: HTMLElement = document.documentElement): boolean {
|
||||
if (isUnfocusedFullyInteractive(root)) {
|
||||
return !isWindowFocusActivationGuardActive(root);
|
||||
@@ -132,20 +121,12 @@ export function createWindowFocusInteractionGuard({
|
||||
clearActivationGuard();
|
||||
}, releaseClearDelayMs);
|
||||
};
|
||||
const suppressActivationEvent = (event: Event) => {
|
||||
if (!shouldBlockActivationClick(root)) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
};
|
||||
const handlePointerStart: EventListener = (event) => {
|
||||
const handlePointerStart: EventListener = () => {
|
||||
if (wasBlurred || !focused || isWindowFocusActivationGuardActive(root)) {
|
||||
beginActivationGuard(true);
|
||||
}
|
||||
suppressActivationEvent(event);
|
||||
};
|
||||
const handlePointerEnd: EventListener = (event) => {
|
||||
suppressActivationEvent(event);
|
||||
const handlePointerEnd: EventListener = () => {
|
||||
scheduleClearAfterActivationClick();
|
||||
};
|
||||
windowTarget.addEventListener('pointerdown', handlePointerStart, true);
|
||||
|
||||
+4
-11
@@ -3,6 +3,7 @@
|
||||
import {
|
||||
MiddleClickAutoscrollControl,
|
||||
SmoothScrollingControl,
|
||||
StayInteractiveUnfocusedControl,
|
||||
TextSelectionControl,
|
||||
VideoSeekThumbnailsControl,
|
||||
} from '@app/features/user/components/modals/tabs/advanced_settings_tab/AdvancedAccessibilityControls';
|
||||
@@ -34,10 +35,8 @@ import {
|
||||
} from '@app/features/user/components/modals/tabs/advanced_settings_tab/AdvancedChatControls';
|
||||
import {DeveloperModeControl} from '@app/features/user/components/modals/tabs/advanced_settings_tab/AdvancedClientDeveloperControls';
|
||||
import {
|
||||
FirstClickPassThroughControl,
|
||||
HardwareAccelerationControl,
|
||||
NativeTitleBarControl,
|
||||
StayInteractiveUnfocusedControl,
|
||||
} from '@app/features/user/components/modals/tabs/advanced_settings_tab/AdvancedDesktopControls';
|
||||
import {UnreadBadgeCustomizationControl} from '@app/features/user/components/modals/tabs/advanced_settings_tab/AdvancedExperimentalControls';
|
||||
import {
|
||||
@@ -90,8 +89,7 @@ export const DIRECT_CONTROL_ITEM_IDS = new Set([
|
||||
'voice-video-screen-share-encoder-controls',
|
||||
'advanced-unread-badge-customization',
|
||||
'client-developer-mode',
|
||||
'advanced-stay-interactive-unfocused',
|
||||
'first-click-pass-through',
|
||||
'accessibility-stay-interactive-unfocused',
|
||||
'advanced-native-title-bar',
|
||||
'advanced-hardware-acceleration',
|
||||
]);
|
||||
@@ -126,8 +124,7 @@ export const COMPACT_SWITCH_CONTROL_ITEM_IDS = new Set([
|
||||
'voice-video-openh264-codec',
|
||||
'advanced-unread-badge-customization',
|
||||
'client-developer-mode',
|
||||
'advanced-stay-interactive-unfocused',
|
||||
'first-click-pass-through',
|
||||
'accessibility-stay-interactive-unfocused',
|
||||
'advanced-native-title-bar',
|
||||
'advanced-hardware-acceleration',
|
||||
]);
|
||||
@@ -298,14 +295,10 @@ export const AdvancedSettingControl = observer(({item}: {item: SearchableSetting
|
||||
return (
|
||||
<DeveloperModeControl data-flx="user.advanced-setting-direct-controls.advanced-setting-control.developer-mode-control" />
|
||||
);
|
||||
case 'advanced-stay-interactive-unfocused':
|
||||
case 'accessibility-stay-interactive-unfocused':
|
||||
return (
|
||||
<StayInteractiveUnfocusedControl data-flx="user.advanced-setting-direct-controls.advanced-setting-control.stay-interactive-unfocused-control" />
|
||||
);
|
||||
case 'first-click-pass-through':
|
||||
return (
|
||||
<FirstClickPassThroughControl data-flx="user.advanced-setting-direct-controls.advanced-setting-control.first-click-pass-through-control" />
|
||||
);
|
||||
case 'advanced-native-title-bar':
|
||||
return (
|
||||
<NativeTitleBarControl data-flx="user.advanced-setting-direct-controls.advanced-setting-control.native-title-bar-control" />
|
||||
|
||||
@@ -32,6 +32,14 @@ const CONTENT_ANIMATIONS_STAY_IN_ANIMATION_TAB_DESCRIPTOR = msg({
|
||||
message: 'Animated emojis, GIFs and stickers stay under your control in the Animation tab.',
|
||||
comment: 'Extra hint shown beneath the reduce motion toggle while reduced motion is active.',
|
||||
});
|
||||
const STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'Stay fully interactive when unfocused',
|
||||
comment: 'Label in the motion tab.',
|
||||
});
|
||||
const KEEP_ANIMATIONS_GIF_PLAYBACK_HOVER_EFFECTS_AND_TOOLTIPS_RUNNING_DESCRIPTOR = msg({
|
||||
message: 'Keep animations, GIF playback, hover effects, and tooltips running while the window is unfocused.',
|
||||
comment: 'Description text in the motion tab.',
|
||||
});
|
||||
export const MotionTabContent: React.FC = observer(() => {
|
||||
const {i18n} = useLingui();
|
||||
const syncReducedMotionWithSystem = Accessibility.syncReducedMotionWithSystem;
|
||||
@@ -60,6 +68,13 @@ export const MotionTabContent: React.FC = observer(() => {
|
||||
onChange={(value) => AccessibilityCommands.update({reducedMotionOverride: value})}
|
||||
data-flx="user.accessibility-tab.motion-tab.motion-tab-content.switch.update--2"
|
||||
/>
|
||||
<Switch
|
||||
label={i18n._(STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR)}
|
||||
description={i18n._(KEEP_ANIMATIONS_GIF_PLAYBACK_HOVER_EFFECTS_AND_TOOLTIPS_RUNNING_DESCRIPTOR)}
|
||||
value={Accessibility.stayInteractiveWhenUnfocused}
|
||||
onChange={(value) => AccessibilityCommands.update({stayInteractiveWhenUnfocused: value})}
|
||||
data-flx="user.accessibility-tab.motion-tab.motion-tab-content.switch.update--3"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
+17
@@ -32,6 +32,10 @@ const SMOOTH_SCROLLING_DESCRIPTOR = msg({
|
||||
message: 'Smooth scrolling',
|
||||
comment: 'Short label for an advanced desktop scrolling toggle.',
|
||||
});
|
||||
const STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'Stay fully interactive when unfocused',
|
||||
comment: 'Short label for an advanced app interaction toggle.',
|
||||
});
|
||||
const USE_AUTOSCROLLING_DESCRIPTOR = msg({
|
||||
message: 'Use autoscrolling',
|
||||
comment: 'Short label for an advanced desktop scrolling toggle.',
|
||||
@@ -135,6 +139,19 @@ export const TextSelectionControl = observer(() => {
|
||||
);
|
||||
});
|
||||
|
||||
export const StayInteractiveUnfocusedControl = observer(() => {
|
||||
const {i18n} = useLingui();
|
||||
return (
|
||||
<Switch
|
||||
ariaLabel={i18n._(STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR)}
|
||||
value={Accessibility.stayInteractiveWhenUnfocused}
|
||||
onChange={(value) => AccessibilityCommands.update({stayInteractiveWhenUnfocused: value})}
|
||||
compact
|
||||
data-flx="user.advanced-settings-tab.switch.stay-interactive-unfocused"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const VideoSeekThumbnailsControl = observer(() => {
|
||||
const {i18n} = useLingui();
|
||||
return (
|
||||
|
||||
-36
@@ -1,7 +1,5 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import * as AccessibilityCommands from '@app/features/accessibility/commands/AccessibilityCommands';
|
||||
import Accessibility from '@app/features/accessibility/state/Accessibility';
|
||||
import {ConfirmModal} from '@app/features/app/components/dialogs/ConfirmModal';
|
||||
import {PRODUCT_NAME} from '@app/features/app/config/I18nDisplayConstants';
|
||||
import {
|
||||
@@ -25,14 +23,6 @@ import {Trans, useLingui} from '@lingui/react/macro';
|
||||
import {observer} from 'mobx-react-lite';
|
||||
import {useCallback, useLayoutEffect, useState} from 'react';
|
||||
|
||||
const STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'Stay fully interactive when unfocused',
|
||||
comment: 'Short label for an advanced desktop preference.',
|
||||
});
|
||||
const FIRST_CLICK_PASS_THROUGH_WHEN_UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'First click pass-through when unfocused',
|
||||
comment: 'Short label for an advanced desktop preference.',
|
||||
});
|
||||
const USE_NATIVE_TITLE_BAR_DESCRIPTOR = msg({
|
||||
message: 'Use native title bar',
|
||||
comment: 'Short label for an advanced desktop preference.',
|
||||
@@ -128,32 +118,6 @@ function useDesktopTroubleshootingSettings() {
|
||||
};
|
||||
}
|
||||
|
||||
export const StayInteractiveUnfocusedControl = observer(() => {
|
||||
const {i18n} = useLingui();
|
||||
return (
|
||||
<Switch
|
||||
ariaLabel={i18n._(STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR)}
|
||||
value={Accessibility.stayInteractiveWhenUnfocused}
|
||||
onChange={(value) => AccessibilityCommands.update({stayInteractiveWhenUnfocused: value})}
|
||||
compact
|
||||
data-flx="user.advanced-settings-tab.switch.stay-interactive-unfocused"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const FirstClickPassThroughControl = observer(() => {
|
||||
const {i18n} = useLingui();
|
||||
return (
|
||||
<Switch
|
||||
ariaLabel={i18n._(FIRST_CLICK_PASS_THROUGH_WHEN_UNFOCUSED_DESCRIPTOR)}
|
||||
value={Accessibility.firstClickPassThroughWhenUnfocused}
|
||||
onChange={(value) => AccessibilityCommands.update({firstClickPassThroughWhenUnfocused: value})}
|
||||
compact
|
||||
data-flx="user.advanced-settings-tab.switch.first-click-pass-through"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const NativeTitleBarControl = observer(() => {
|
||||
const {i18n} = useLingui();
|
||||
const {desktopWindowBehavior, desktopWindowBehaviorBusy, updateDesktopWindowBehavior} =
|
||||
|
||||
+55
@@ -342,6 +342,43 @@ const CONTROL_MIDDLE_CLICK_AUTOSCROLLING_DESCRIPTOR = msg({
|
||||
message: 'Control Chromium middle-click autoscrolling in the desktop app',
|
||||
comment: 'Settings search entry description. One-line summary of what the settings search entry controls.',
|
||||
});
|
||||
const STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'Stay fully interactive when unfocused',
|
||||
comment: 'Settings search entry label. Names the settings search entry in the settings UI.',
|
||||
});
|
||||
const UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'Unfocused',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const WINDOW_FOCUS_DESCRIPTOR = msg({
|
||||
message: 'Window focus',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const ANIMATIONS_WHILE_UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'Animations while unfocused',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const ANIMATION_PLAYBACK_DESCRIPTOR = msg({
|
||||
message: 'Animation playback',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const HOVER_EFFECTS_DESCRIPTOR = msg({
|
||||
message: 'Hover effects',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const TOOLTIPS_DESCRIPTOR = msg({
|
||||
message: 'Tooltips',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const POWER_SAVING_DESCRIPTOR = msg({
|
||||
message: 'Power saving',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const KEEP_ANIMATIONS_GIF_PLAYBACK_HOVER_EFFECTS_AND_TOOLTIPS_DESCRIPTOR = msg({
|
||||
message: 'Keep animations, GIF playback, hover effects, and tooltips running while the window is unfocused.',
|
||||
comment: 'Settings search entry description. One-line summary of what the settings search entry controls.',
|
||||
});
|
||||
|
||||
export const accessibilityIndex: Array<SearchableSettingDescriptor> = [
|
||||
{
|
||||
id: 'accessibility-confirm-calls',
|
||||
@@ -486,6 +523,24 @@ export const accessibilityIndex: Array<SearchableSettingDescriptor> = [
|
||||
],
|
||||
description: DISPLAY_A_VISIBLE_FOCUS_INDICATOR_AROUND_THE_MESSAGE_DESCRIPTOR,
|
||||
},
|
||||
{
|
||||
id: 'accessibility-stay-interactive-unfocused',
|
||||
tabType: 'accessibility',
|
||||
sectionId: 'motion',
|
||||
label: STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR,
|
||||
keywords: [
|
||||
UNFOCUSED_DESCRIPTOR,
|
||||
WINDOW_FOCUS_DESCRIPTOR,
|
||||
ANIMATIONS_WHILE_UNFOCUSED_DESCRIPTOR,
|
||||
ANIMATION_PLAYBACK_DESCRIPTOR,
|
||||
HOVER_EFFECTS_DESCRIPTOR,
|
||||
TOOLTIPS_DESCRIPTOR,
|
||||
POWER_SAVING_DESCRIPTOR,
|
||||
],
|
||||
description: KEEP_ANIMATIONS_GIF_PLAYBACK_HOVER_EFFECTS_AND_TOOLTIPS_DESCRIPTOR,
|
||||
audience: 'advanced',
|
||||
tags: ['accessibility'],
|
||||
},
|
||||
{
|
||||
id: 'accessibility-sync-reduced-motion',
|
||||
tabType: 'accessibility',
|
||||
|
||||
-94
@@ -7,66 +7,6 @@ import type {SearchableSettingDescriptor} from '@app/features/user/components/se
|
||||
import {BACKGROUND_DESCRIPTOR} from '@app/features/user/components/settings_utils/search_index/SharedDescriptors';
|
||||
import {msg} from '@lingui/core/macro';
|
||||
|
||||
const FIRST_CLICK_PASS_THROUGH_WHEN_UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'First click pass-through when unfocused',
|
||||
comment: 'Settings search entry label. Names the settings search entry in the settings UI.',
|
||||
});
|
||||
const FIRST_CLICK_DESCRIPTOR = msg({
|
||||
message: 'First click',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const CLICK_THROUGH_DESCRIPTOR = msg({
|
||||
message: 'Click through',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'Unfocused',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const WINDOW_FOCUS_DESCRIPTOR = msg({
|
||||
message: 'Window focus',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const FOCUS_CLICK_DESCRIPTOR = msg({
|
||||
message: 'Focus click',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const ACTIVATION_CLICK_DESCRIPTOR = msg({
|
||||
message: 'Activation click',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const LET_THE_CLICK_THAT_FOCUSES_ALSO_ACTIVATE_THE_DESCRIPTOR = msg({
|
||||
message: 'Let focus clicks activate controls.',
|
||||
comment: 'Settings search entry description. One-line summary of what the settings search entry controls.',
|
||||
});
|
||||
const STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'Stay fully interactive when unfocused',
|
||||
comment: 'Settings search entry label. Names the settings search entry in the settings UI.',
|
||||
});
|
||||
const ANIMATIONS_WHILE_UNFOCUSED_DESCRIPTOR = msg({
|
||||
message: 'Animations while unfocused',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const ANIMATION_PLAYBACK_DESCRIPTOR = msg({
|
||||
message: 'Animation playback',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const HOVER_EFFECTS_DESCRIPTOR = msg({
|
||||
message: 'Hover effects',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const TOOLTIPS_DESCRIPTOR = msg({
|
||||
message: 'Tooltips',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const POWER_SAVING_DESCRIPTOR = msg({
|
||||
message: 'Power saving',
|
||||
comment: 'Settings search synonym. Used to match this term when the user types it in the settings search bar.',
|
||||
});
|
||||
const KEEP_ANIMATIONS_GIF_PLAYBACK_HOVER_EFFECTS_AND_TOOLTIPS_DESCRIPTOR = msg({
|
||||
message: 'Keep animations, GIFs, hover, and tooltips running unfocused',
|
||||
comment: 'Settings search entry description. One-line summary of what the settings search entry controls.',
|
||||
});
|
||||
const HARDWARE_ACCELERATION_DESCRIPTOR = msg({
|
||||
message: 'Hardware acceleration',
|
||||
comment: 'Settings search entry label. Names the settings search entry in the settings UI.',
|
||||
@@ -280,40 +220,6 @@ const KEEP_RUNNING_WHEN_THE_WINDOW_IS_CLOSED_DESCRIPTOR = msg({
|
||||
comment: 'Settings search entry description. One-line summary of what the settings search entry controls.',
|
||||
});
|
||||
export const desktopSettingsIndex: Array<SearchableSettingDescriptor> = [
|
||||
{
|
||||
id: 'first-click-pass-through',
|
||||
tabType: 'desktop_settings',
|
||||
label: FIRST_CLICK_PASS_THROUGH_WHEN_UNFOCUSED_DESCRIPTOR,
|
||||
keywords: [
|
||||
FIRST_CLICK_DESCRIPTOR,
|
||||
CLICK_THROUGH_DESCRIPTOR,
|
||||
UNFOCUSED_DESCRIPTOR,
|
||||
WINDOW_FOCUS_DESCRIPTOR,
|
||||
FOCUS_CLICK_DESCRIPTOR,
|
||||
ACTIVATION_CLICK_DESCRIPTOR,
|
||||
],
|
||||
description: LET_THE_CLICK_THAT_FOCUSES_ALSO_ACTIVATE_THE_DESCRIPTOR,
|
||||
audience: 'advanced',
|
||||
tags: ['desktop'],
|
||||
isVisible: isDesktop,
|
||||
},
|
||||
{
|
||||
id: 'advanced-stay-interactive-unfocused',
|
||||
tabType: 'desktop_settings',
|
||||
label: STAY_FULLY_INTERACTIVE_WHEN_UNFOCUSED_DESCRIPTOR,
|
||||
keywords: [
|
||||
UNFOCUSED_DESCRIPTOR,
|
||||
WINDOW_FOCUS_DESCRIPTOR,
|
||||
ANIMATIONS_WHILE_UNFOCUSED_DESCRIPTOR,
|
||||
ANIMATION_PLAYBACK_DESCRIPTOR,
|
||||
HOVER_EFFECTS_DESCRIPTOR,
|
||||
TOOLTIPS_DESCRIPTOR,
|
||||
POWER_SAVING_DESCRIPTOR,
|
||||
],
|
||||
description: KEEP_ANIMATIONS_GIF_PLAYBACK_HOVER_EFFECTS_AND_TOOLTIPS_DESCRIPTOR,
|
||||
tags: ['desktop'],
|
||||
isVisible: isDesktop,
|
||||
},
|
||||
{
|
||||
id: 'advanced-hardware-acceleration',
|
||||
tabType: 'desktop_settings',
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
contain: layout style;
|
||||
}
|
||||
|
||||
:global(html.platform-native.window-focus-activation-guard:not(.first-click-passthrough-when-unfocused)) .container {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.container:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
Vendored
-1
@@ -93,7 +93,6 @@ export interface DesktopWindowBehaviorSettings {
|
||||
activeSmoothScrolling: boolean;
|
||||
middleClickAutoscroll: boolean;
|
||||
activeMiddleClickAutoscroll: boolean;
|
||||
firstClickPassThroughWhenUnfocused: boolean;
|
||||
}
|
||||
|
||||
export interface ThemeLocalFileReference {
|
||||
|
||||
@@ -31,7 +31,6 @@ interface PersistedDesktopWindowBehaviorSettings {
|
||||
allowTransparency?: boolean;
|
||||
smoothScrolling?: boolean;
|
||||
middleClickAutoscroll?: boolean;
|
||||
firstClickPassThroughWhenUnfocused?: boolean;
|
||||
}
|
||||
|
||||
interface PersistedDesktopTroubleshootingSettings {
|
||||
@@ -62,7 +61,6 @@ function getDefaultDesktopWindowBehaviorSettings(): DesktopWindowBehaviorSetting
|
||||
activeSmoothScrolling: true,
|
||||
middleClickAutoscroll: false,
|
||||
activeMiddleClickAutoscroll: false,
|
||||
firstClickPassThroughWhenUnfocused: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -95,9 +93,6 @@ function sanitizePersistedDesktopWindowBehaviorSettings(
|
||||
if (typeof value.middleClickAutoscroll === 'boolean') {
|
||||
settings.middleClickAutoscroll = value.middleClickAutoscroll;
|
||||
}
|
||||
if (typeof value.firstClickPassThroughWhenUnfocused === 'boolean') {
|
||||
settings.firstClickPassThroughWhenUnfocused = value.firstClickPassThroughWhenUnfocused;
|
||||
}
|
||||
const minimizeToTrayV2 = value[MINIMIZE_TO_TRAY_STORAGE_KEY_V2];
|
||||
if (typeof minimizeToTrayV2 === 'boolean') {
|
||||
settings.minimizeToTrayV2 = minimizeToTrayV2;
|
||||
@@ -238,10 +233,6 @@ function normalizeDesktopWindowBehaviorSettings(
|
||||
: typeof normalizedSettings?.middleClickAutoscroll === 'boolean'
|
||||
? normalizedSettings.middleClickAutoscroll
|
||||
: defaults.middleClickAutoscroll,
|
||||
firstClickPassThroughWhenUnfocused:
|
||||
typeof normalizedSettings?.firstClickPassThroughWhenUnfocused === 'boolean'
|
||||
? normalizedSettings.firstClickPassThroughWhenUnfocused
|
||||
: defaults.firstClickPassThroughWhenUnfocused,
|
||||
};
|
||||
if (!normalized.showTrayIcon) {
|
||||
normalized.minimizeToTray = false;
|
||||
@@ -260,7 +251,6 @@ function serializeDesktopWindowBehaviorSettings(
|
||||
allowTransparency: settings.allowTransparency,
|
||||
smoothScrolling: settings.smoothScrolling,
|
||||
middleClickAutoscroll: settings.middleClickAutoscroll,
|
||||
firstClickPassThroughWhenUnfocused: settings.firstClickPassThroughWhenUnfocused,
|
||||
[MINIMIZE_TO_TRAY_STORAGE_KEY_V2]: settings.minimizeToTray,
|
||||
[CLOSE_TO_TRAY_STORAGE_KEY_V2]: settings.closeToTray,
|
||||
};
|
||||
|
||||
@@ -120,7 +120,6 @@ export interface DesktopWindowBehaviorSettings {
|
||||
activeSmoothScrolling: boolean;
|
||||
middleClickAutoscroll: boolean;
|
||||
activeMiddleClickAutoscroll: boolean;
|
||||
firstClickPassThroughWhenUnfocused: boolean;
|
||||
}
|
||||
|
||||
export interface ThemeLocalFileReference {
|
||||
|
||||
@@ -322,7 +322,6 @@ function formatWindowBehavior(settings: DesktopWindowBehaviorSettings): string {
|
||||
`activeSmoothScrolling=${settings.activeSmoothScrolling}`,
|
||||
`middleClickAutoscroll=${settings.middleClickAutoscroll}`,
|
||||
`activeMiddleClickAutoscroll=${settings.activeMiddleClickAutoscroll}`,
|
||||
`firstClickPassThroughWhenUnfocused=${settings.firstClickPassThroughWhenUnfocused}`,
|
||||
].join(', ');
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,6 @@ import {
|
||||
import {
|
||||
clearSavedWindowBounds,
|
||||
closeThemeStudioPopoutWindow,
|
||||
desktopFirstClickPassThroughPendingRestart,
|
||||
desktopTransparencyPendingRestart,
|
||||
desktopUseNativeTitleBarPendingRestart,
|
||||
focusThemeStudioPopoutWindow,
|
||||
@@ -123,9 +122,6 @@ function normalizeDesktopWindowBehaviorUpdate(value: unknown): Partial<DesktopWi
|
||||
if (typeof value.middleClickAutoscroll === 'boolean') {
|
||||
update.middleClickAutoscroll = value.middleClickAutoscroll;
|
||||
}
|
||||
if (typeof value.firstClickPassThroughWhenUnfocused === 'boolean') {
|
||||
update.firstClickPassThroughWhenUnfocused = value.firstClickPassThroughWhenUnfocused;
|
||||
}
|
||||
return update;
|
||||
}
|
||||
|
||||
@@ -255,7 +251,6 @@ export function registerIpcHandlers(): void {
|
||||
ipcMain.handle('desktop-window-behavior-pending-restart', (): boolean => {
|
||||
return (
|
||||
desktopTrayChangePendingRestart() ||
|
||||
desktopFirstClickPassThroughPendingRestart() ||
|
||||
desktopUseNativeTitleBarPendingRestart() ||
|
||||
desktopTransparencyPendingRestart()
|
||||
);
|
||||
|
||||
@@ -145,7 +145,6 @@ interface CreateWindowOptions {
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
let windowStateFile: string;
|
||||
let isQuitting = false;
|
||||
let initialAcceptFirstMouseOnFocus: boolean | null = null;
|
||||
let initialUseNativeTitleBar: boolean | null = null;
|
||||
let initialAllowTransparency: boolean | null = null;
|
||||
let themeStudioPopoutWindow: BrowserWindow | null = null;
|
||||
@@ -331,12 +330,6 @@ export function getMainWindow(): BrowserWindow | null {
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
export function desktopFirstClickPassThroughPendingRestart(): boolean {
|
||||
if (process.platform !== 'darwin') return false;
|
||||
if (initialAcceptFirstMouseOnFocus === null) return false;
|
||||
return getDesktopWindowBehaviorSettings().firstClickPassThroughWhenUnfocused !== initialAcceptFirstMouseOnFocus;
|
||||
}
|
||||
|
||||
export function getActiveUseNativeTitleBar(): boolean {
|
||||
return initialUseNativeTitleBar ?? false;
|
||||
}
|
||||
@@ -730,8 +723,7 @@ export function createWindow(options: CreateWindowOptions = {}): BrowserWindow {
|
||||
const desktopWindowBehavior = getDesktopWindowBehaviorSettings();
|
||||
const allowTransparency = desktopWindowBehavior.allowTransparency;
|
||||
const useNativeTitleBar = getEffectiveUseNativeTitleBar(desktopWindowBehavior);
|
||||
const acceptFirstMouseOnFocus = isMac && desktopWindowBehavior.firstClickPassThroughWhenUnfocused;
|
||||
initialAcceptFirstMouseOnFocus = acceptFirstMouseOnFocus;
|
||||
const acceptFirstMouseOnFocus = isMac;
|
||||
initialUseNativeTitleBar = useNativeTitleBar;
|
||||
initialAllowTransparency = allowTransparency;
|
||||
const appUrl = getAppUrl();
|
||||
|
||||
Reference in New Issue
Block a user