fix(app): make click-through the default and move animation pausing to Accessibility (#1722)

This commit is contained in:
Hampus
2026-08-18 19:40:35 +02:00
committed by GitHub
parent 9e89539f0a
commit 4bbfee4cec
19 changed files with 96 additions and 248 deletions
-11
View File
@@ -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;
+2 -14
View File
@@ -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);
@@ -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"
/>
</>
);
});
@@ -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 (
@@ -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} =
@@ -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',
@@ -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;
}
-1
View File
@@ -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,
};
-1
View File
@@ -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(', ');
}
-5
View File
@@ -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()
);
+1 -9
View File
@@ -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();