fix(layout): keep header/footer transparent and fixed in scrolled mode, closes #4157 (#4168)

Remove the redundant "Apply also in Scrolled Mode" options for bars and
margins so scrolled mode renders the header/footer consistently with
paginated mode: transparent, fixed in position, and not obscuring content.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-05-15 16:30:43 +08:00
committed by GitHub
parent ab2def32dd
commit 7716f189c3
11 changed files with 25 additions and 65 deletions
@@ -586,7 +586,6 @@ describe('services/constants', () => {
it('has boolean display flags', () => {
expect(typeof DEFAULT_VIEW_CONFIG.showHeader).toBe('boolean');
expect(typeof DEFAULT_VIEW_CONFIG.showFooter).toBe('boolean');
expect(typeof DEFAULT_VIEW_CONFIG.showBarsOnScroll).toBe('boolean');
expect(typeof DEFAULT_VIEW_CONFIG.showRemainingTime).toBe('boolean');
expect(typeof DEFAULT_VIEW_CONFIG.showRemainingPages).toBe('boolean');
expect(typeof DEFAULT_VIEW_CONFIG.showProgressInfo).toBe('boolean');
@@ -595,7 +594,6 @@ describe('services/constants', () => {
expect(typeof DEFAULT_VIEW_CONFIG.showBatteryPercentage).toBe('boolean');
expect(typeof DEFAULT_VIEW_CONFIG.use24HourClock).toBe('boolean');
expect(typeof DEFAULT_VIEW_CONFIG.tapToToggleFooter).toBe('boolean');
expect(typeof DEFAULT_VIEW_CONFIG.showMarginsOnScroll).toBe('boolean');
expect(typeof DEFAULT_VIEW_CONFIG.showPaginationButtons).toBe('boolean');
});
@@ -104,10 +104,8 @@ const BooksGrid: React.FC<BooksGridProps> = ({ bookKeys, onCloseBook, onGoToLibr
bottom: gridInsets.bottom + viewInsets.bottom,
left: gridInsets.left + viewInsets.left,
};
const scrolled = viewSettings.scrolled;
const showBarsOnScroll = viewSettings.showBarsOnScroll;
const showHeader = viewSettings.showHeader && (scrolled ? showBarsOnScroll : true);
const showFooter = viewSettings.showFooter && (scrolled ? showBarsOnScroll : true);
const showHeader = viewSettings.showHeader;
const showFooter = viewSettings.showFooter;
return (
<div
@@ -632,11 +632,11 @@ const FoliateViewer: React.FC<{
const showDoubleBorderFooter = showDoubleBorder && viewSettings.showFooter;
const showTopHeader = viewSettings.showHeader && !viewSettings.vertical;
const showBottomFooter = viewSettings.showFooter && !viewSettings.vertical;
const moreTopInset = showTopHeader ? Math.max(0, 44 - insets.top) : 0;
const moreTopInset = showTopHeader ? Math.max(0, 16 - insets.top) : 0;
const ttsBarHeight =
viewState?.ttsEnabled && viewSettings.showTTSBar ? 52 + gridInsets.bottom * 0.33 : 0;
const moreBottomInset = showBottomFooter
? Math.max(0, Math.max(ttsBarHeight, 52) - insets.bottom)
? Math.max(0, Math.max(ttsBarHeight, 16) - insets.bottom)
: Math.max(0, ttsBarHeight);
const moreRightInset = showDoubleBorderHeader ? 32 : 0;
const moreLeftInset = showDoubleBorderFooter ? 32 : 0;
@@ -644,19 +644,17 @@ const FoliateViewer: React.FC<{
const rightMargin = insets.right + moreRightInset;
const bottomMargin = (showBottomFooter ? insets.bottom : viewInsets.bottom) + moreBottomInset;
const leftMargin = insets.left + moreLeftInset;
const viewMargins = viewSettings.showMarginsOnScroll && viewSettings.scrolled;
viewRef.current?.renderer.setAttribute('margin-top', `${viewMargins ? 0 : topMargin}px`);
viewRef.current?.renderer.setAttribute('margin-top', `${topMargin}px`);
viewRef.current?.renderer.setAttribute('margin-right', `${rightMargin}px`);
viewRef.current?.renderer.setAttribute('margin-bottom', `${viewMargins ? 0 : bottomMargin}px`);
viewRef.current?.renderer.setAttribute('margin-bottom', `${bottomMargin}px`);
viewRef.current?.renderer.setAttribute('margin-left', `${leftMargin}px`);
if (viewMargins) {
const showBarsOnScroll = viewSettings.showBarsOnScroll;
const headerVisible = showTopHeader && showBarsOnScroll;
const footerVisible = showBottomFooter && showBarsOnScroll;
if (viewSettings.scrolled) {
const headerVisible = showTopHeader;
const footerVisible = showBottomFooter;
const safeBottomPadding = appService?.hasSafeAreaInset ? gridInsets.bottom * 0.33 : 0;
const footerBarHeight = 52 + safeBottomPadding;
const scrollTop = headerVisible ? gridInsets.top + 44 : 0;
const footerBarHeight = safeBottomPadding + viewSettings.marginBottomPx;
const scrollTop = headerVisible ? gridInsets.top + viewSettings.marginTopPx : 0;
const scrollBottom = footerVisible ? Math.max(footerBarHeight, ttsBarHeight) : ttsBarHeight;
setScrollMargins({ top: scrollTop, bottom: scrollBottom });
} else {
@@ -754,8 +752,6 @@ const FoliateViewer: React.FC<{
viewSettings?.showHeader,
viewSettings?.showFooter,
viewSettings?.showTTSBar,
viewSettings?.showBarsOnScroll,
viewSettings?.showMarginsOnScroll,
viewSettings?.scrolled,
viewSettings?.noContinuousScroll,
viewState?.ttsEnabled,
@@ -37,7 +37,6 @@ const ProgressBar: React.FC<ProgressBarProps> = ({
const { section, pageinfo } = progress || {};
const showDoubleBorder = viewSettings.vertical && viewSettings.doubleBorder;
const isScrolled = viewSettings.scrolled;
const isVertical = viewSettings.vertical;
const isEink = viewSettings.isEink;
const { progressStyle: readingProgressStyle } = viewSettings;
@@ -173,7 +172,6 @@ const ProgressBar: React.FC<ProgressBarProps> = ({
'progressinfo absolute bottom-0 flex items-center justify-between font-sans',
isEink ? 'text-sm font-normal' : 'text-neutral-content text-xs font-extralight',
isVertical ? 'writing-vertical-rl' : 'w-full',
isScrolled && !isVertical && 'bg-base-100',
isMobile ? 'pointer-events-auto' : 'pointer-events-none',
)}
onClick={() => {
@@ -211,10 +209,8 @@ const ProgressBar: React.FC<ProgressBarProps> = ({
>
<div
aria-hidden='true'
className={clsx(
'flex items-center justify-between',
isVertical ? 'h-full' : 'h-[52px] w-full',
)}
className={clsx('flex items-center justify-between', isVertical ? 'h-full' : 'w-full')}
style={isVertical ? {} : { height: `${viewSettings.marginBottomPx}px` }}
>
{(progressBarMode === 'all' || progressBarMode.includes('remaining')) &&
hasRemainingInfo && (
@@ -32,8 +32,9 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
}) => {
const _ = useTranslation();
const { appService } = useEnv();
const { hoveredBookKey, getView, setHoveredBookKey } = useReaderStore();
const { hoveredBookKey, getView, getViewSettings, setHoveredBookKey } = useReaderStore();
const { systemUIVisible, statusBarHeight } = useThemeStore();
const viewSettings = getViewSettings(bookKey)!;
const topInset = Math.max(
gridInsets.top,
appService?.isAndroidApp && systemUIVisible ? statusBarHeight / 2 : 0,
@@ -69,8 +70,7 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
className={clsx(
'sectioninfo absolute flex items-center overflow-hidden font-sans',
isEink ? 'text-sm font-normal' : 'text-neutral-content text-xs font-light',
isVertical ? 'writing-vertical-rl max-h-[85%]' : 'top-0 h-[44px]',
isScrolled && !isVertical && 'bg-base-100',
isVertical ? 'writing-vertical-rl max-h-[85%]' : 'top-0',
)}
role='none'
tabIndex={-1}
@@ -89,6 +89,7 @@ const SectionInfo: React.FC<SectionInfoProps> = ({
top: `${topInset}px`,
paddingInline: `calc(${horizontalGap / 2}% + ${contentInsets.left / 2}px)`,
width: '100%',
height: `${viewSettings.marginTopPx}px`,
}
}
>
@@ -61,8 +61,8 @@ export const viewPagination = (
}
if (renderer.scrolled) {
const { size } = renderer;
const showHeader = viewSettings.showHeader && viewSettings.showBarsOnScroll;
const showFooter = viewSettings.showFooter && viewSettings.showBarsOnScroll;
const showHeader = viewSettings.showHeader;
const showFooter = viewSettings.showFooter;
const scrollingOverlap = viewSettings.scrollingOverlap;
const distance = size - scrollingOverlap - (showHeader ? 44 : 0) - (showFooter ? 44 : 0);
switch (mode) {
@@ -232,9 +232,8 @@ export const useTTSControl = ({ bookKey, onRequestHidePanel }: UseTTSControlProp
const showHeader = viewSettings.showHeader;
const showFooter = viewSettings.showFooter;
const showBarsOnScroll = viewSettings.showBarsOnScroll;
const headerScrollOverlap = showHeader && showBarsOnScroll ? 44 : 0;
const footerScrollOverlap = showFooter && showBarsOnScroll ? 44 : 0;
const headerScrollOverlap = showHeader ? viewSettings.marginTopPx : 0;
const footerScrollOverlap = showFooter ? viewSettings.marginBottomPx : 0;
const scrollingOverlap = viewSettings.scrollingOverlap;
const outOfView =
rangeBottom > end - footerScrollOverlap - scrollingOverlap ||
@@ -72,8 +72,6 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
const [borderColor, setBorderColor] = useState(viewSettings.borderColor);
const [showHeader, setShowHeader] = useState(viewSettings.showHeader);
const [showFooter, setShowFooter] = useState(viewSettings.showFooter);
const [showBarsOnScroll, setShowBarsOnScroll] = useState(viewSettings.showBarsOnScroll);
const [showMarginsOnScroll, setShowMarginsOnScroll] = useState(viewSettings.showMarginsOnScroll);
const [showRemainingTime, setShowRemainingTime] = useState(viewSettings.showRemainingTime);
const [showRemainingPages, setShowRemainingPages] = useState(viewSettings.showRemainingPages);
const [showProgressInfo, setShowProgressInfo] = useState(viewSettings.showProgressInfo);
@@ -118,7 +116,6 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
borderColor: setBorderColor,
showHeader: setShowHeader,
showFooter: setShowFooter,
showBarsOnScroll: setShowBarsOnScroll,
showRemainingTime: setShowRemainingTime,
showRemainingPages: setShowRemainingPages,
showProgressInfo: setShowProgressInfo,
@@ -127,7 +124,6 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
showCurrentBatteryStatus: setShowCurrentBatteryStatus,
showBatteryPercentage: setShowBatteryPercentage,
tapToToggleFooter: setTapToToggleFooter,
showMarginsOnScroll: setShowMarginsOnScroll,
});
};
@@ -340,16 +336,6 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [borderColor]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'showBarsOnScroll', showBarsOnScroll, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [showBarsOnScroll]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'showMarginsOnScroll', showMarginsOnScroll, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [showMarginsOnScroll]);
useEffect(() => {
saveViewSettings(envConfig, bookKey, 'showRemainingTime', showRemainingTime, false, false);
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -606,7 +592,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
value={showHeader && !isVertical ? marginTopPx : compactMarginTopPx}
onChange={showHeader && !isVertical ? setMarginTopPx : setCompactMarginTopPx}
min={
showHeader && !isVertical ? Math.max(0, Math.round((44 - gridInsets.top) / 4) * 4) : 0
showHeader && !isVertical ? Math.max(0, Math.round((16 - gridInsets.top) / 4) * 4) : 0
}
max={88}
step={4}
@@ -617,7 +603,7 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
onChange={showFooter && !isVertical ? setMarginBottomPx : setCompactMarginBottomPx}
min={
showFooter && !isVertical
? Math.max(0, Math.round((44 - gridInsets.bottom) / 4) * 4)
? Math.max(0, Math.round((16 - gridInsets.bottom) / 4) * 4)
: 0
}
max={88}
@@ -675,11 +661,6 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
step={50}
data-setting-id='settings.layout.maxBlockSize'
/>
<SettingsSwitchRow
label={_('Apply also in Scrolled Mode')}
checked={showMarginsOnScroll}
onChange={() => setShowMarginsOnScroll(!showMarginsOnScroll)}
/>
</BoxedList>
<BoxedList title={_('Header & Footer')} data-setting-id='settings.layout.showHeader'>
@@ -773,11 +754,6 @@ const LayoutPanel: React.FC<SettingsPanelPanelProp> = ({ bookKey, onRegisterRese
disabled={!showFooter}
onChange={() => setTapToToggleFooter(!tapToToggleFooter)}
/>
<SettingsSwitchRow
label={_('Apply also in Scrolled Mode')}
checked={showBarsOnScroll}
onChange={() => setShowBarsOnScroll(!showBarsOnScroll)}
/>
</BoxedList>
{appService?.hasOrientationLock && (
@@ -306,7 +306,6 @@ export const DEFAULT_VIEW_CONFIG: ViewConfig = {
showHeader: true,
showFooter: true,
showBarsOnScroll: false,
showRemainingTime: false,
showRemainingPages: false,
showProgressInfo: true,
@@ -315,7 +314,6 @@ export const DEFAULT_VIEW_CONFIG: ViewConfig = {
showBatteryPercentage: true,
use24HourClock: false,
tapToToggleFooter: false,
showMarginsOnScroll: false,
showPaginationButtons: false,
progressStyle: 'fraction',
progressInfoMode: 'all',
-2
View File
@@ -253,8 +253,6 @@ export interface ViewConfig {
showCurrentBatteryStatus: boolean;
showBatteryPercentage: boolean;
tapToToggleFooter: boolean;
showBarsOnScroll: boolean;
showMarginsOnScroll: boolean;
showPaginationButtons: boolean;
progressStyle: 'percentage' | 'fraction';
progressInfoMode: ProgressBarMode;