Always show window controls, and open Linux settings in dialog

This commit is contained in:
Gregory Schier
2024-10-10 06:22:11 -07:00
parent 16e090b520
commit 250625fc0e
7 changed files with 101 additions and 46 deletions

View File

@@ -1,27 +1,31 @@
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import classNames from 'classnames';
import type { HTMLAttributes, ReactNode } from 'react';
import { useIsFullscreen } from '../hooks/useIsFullscreen';
import { useOsInfo } from '../hooks/useOsInfo';
import React from 'react';
import { useSettings } from '../hooks/useSettings';
import { useStoplightsVisible } from '../hooks/useStoplightsVisible';
import { WINDOW_CONTROLS_WIDTH, WindowControls } from './WindowControls';
interface HeaderSizeProps extends HTMLAttributes<HTMLDivElement> {
children?: ReactNode;
size: 'md' | 'lg';
ignoreStoplights?: boolean;
ignoreControlsSpacing?: boolean;
onlyXWindowControl?: boolean;
}
export const HEADER_SIZE_MD = '27px';
export const HEADER_SIZE_LG = '38px';
export function HeaderSize({
className,
style,
size,
ignoreStoplights,
...props
ignoreControlsSpacing,
onlyXWindowControl,
children,
}: HeaderSizeProps) {
const settings = useSettings();
const platform = useOsInfo();
const fullscreen = useIsFullscreen();
const stoplightsVisible = platform?.osType === 'macos' && !fullscreen && !ignoreStoplights;
const stoplightsVisible = useStoplightsVisible();
return (
<div
data-tauri-drag-region
@@ -33,18 +37,23 @@ export function HeaderSize({
...style,
// Add padding for macOS stoplights, but keep it the same width (account for the interface scale)
paddingLeft: stoplightsVisible ? 72 / settings.interfaceScale : undefined,
...(size === 'md' ? { height: HEADER_SIZE_MD } : {}),
...(size === 'lg' ? { height: HEADER_SIZE_LG } : {}),
...(stoplightsVisible || ignoreControlsSpacing
? { paddingRight: '2px' }
: { paddingLeft: '2px', paddingRight: WINDOW_CONTROLS_WIDTH }),
}}
className={classNames(
className,
'select-none',
'select-none relative',
'pt-[1px] w-full border-b border-border-subtle min-w-0',
stoplightsVisible ? 'pr-1' : 'pl-1',
size === 'md' && 'h-[27px]',
size === 'lg' && 'h-[38px]',
)}
>
{/* NOTE: This needs display:grid or else the element shrinks (even though scrollable) */}
<div className="h-full w-full overflow-x-auto hide-scrollbars grid" {...props} />
<div className="pointer-events-none h-full w-full overflow-x-auto hide-scrollbars grid">
{children}
</div>
<WindowControls onlyX={onlyXWindowControl} />
</div>
);
}

View File

@@ -86,8 +86,7 @@ export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'classNa
hotkeyAction="request_switcher.toggle"
className={classNames(
className,
'max-w-[40vw]',
'text truncate pointer-events-auto',
'truncate pointer-events-auto',
activeRequest === null && 'text-text-subtlest italic',
)}
>

View File

@@ -7,11 +7,14 @@ import { capitalize } from '../../lib/capitalize';
import { HStack } from '../core/Stacks';
import { TabContent, Tabs } from '../core/Tabs/Tabs';
import { HeaderSize } from '../HeaderSize';
import { WindowControls } from '../WindowControls';
import { SettingsAppearance } from './SettingsAppearance';
import { SettingsGeneral } from './SettingsGeneral';
import { SettingsPlugins } from './SettingsPlugins';
interface Props {
hide?: () => void;
}
enum Tab {
General = 'general',
Appearance = 'appearance',
@@ -20,33 +23,45 @@ enum Tab {
const tabs = [Tab.General, Tab.Appearance, Tab.Plugins];
export default function Settings() {
export default function Settings({ hide }: Props) {
const osInfo = useOsInfo();
const [tab, setTab] = useState<string>(Tab.General);
// Close settings window on escape
// TODO: Could this be put in a better place? Eg. in Rust key listener when creating the window
useKeyPressEvent('Escape', () => getCurrentWebviewWindow().close());
useKeyPressEvent('Escape', async () => {
if (hide != null) {
// It's being shown in a dialog, so close the dialog
hide();
} else {
// It's being shown in a window, so close the window
await getCurrentWebviewWindow().close();
}
});
return (
<div className={classNames('grid grid-rows-[auto_minmax(0,1fr)] h-full')}>
<HeaderSize
data-tauri-drag-region
ignoreStoplights
size="md"
className="x-theme-appHeader bg-surface text-text-subtle flex items-center justify-center border-b border-border-subtle text-sm font-semibold"
>
<HStack
space={2}
justifyContent="center"
className="w-full h-full grid grid-cols-[1fr_auto] pointer-events-none"
{hide ? (
<span />
) : (
<HeaderSize
data-tauri-drag-region
ignoreControlsSpacing
onlyXWindowControl
size="md"
className="x-theme-appHeader bg-surface text-text-subtle flex items-center justify-center border-b border-border-subtle text-sm font-semibold"
>
<div className={classNames(osInfo?.osType === 'macos' ? 'text-center' : 'pl-2')}>
Settings
</div>
<WindowControls className="ml-auto" onlyX />
</HStack>
</HeaderSize>
<HStack
space={2}
justifyContent="center"
className="w-full h-full grid grid-cols-[1fr_auto] pointer-events-none"
>
<div className={classNames(osInfo?.osType === 'macos' ? 'text-center' : 'pl-2')}>
Settings
</div>
</HStack>
</HeaderSize>
)}
<Tabs
value={tab}
addBorders

View File

@@ -1,7 +1,7 @@
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
import classNames from 'classnames';
import React, { useState } from 'react';
import { useOsInfo } from '../hooks/useOsInfo';
import { useStoplightsVisible } from '../hooks/useStoplightsVisible';
import { Button } from './core/Button';
import { HStack } from './core/Stacks';
@@ -10,16 +10,21 @@ interface Props {
onlyX?: boolean;
}
export const WINDOW_CONTROLS_WIDTH = '10.5rem';
export function WindowControls({ className, onlyX }: Props) {
const [maximized, setMaximized] = useState<boolean>(false);
const osInfo = useOsInfo();
const shouldShow = osInfo?.osType === 'linux' || osInfo?.osType === 'windows';
if (!shouldShow) {
const stoplightsVisible = useStoplightsVisible();
if (stoplightsVisible) {
return null;
}
return (
<HStack className={classNames(className, 'ml-4 h-full')}>
<HStack
className={classNames(className, 'ml-4 absolute right-0 top-0 bottom-0')}
justifyContent="end"
style={{ width: WINDOW_CONTROLS_WIDTH }}
>
{!onlyX && (
<>
<Button
@@ -57,7 +62,7 @@ export function WindowControls({ className, onlyX }: Props) {
)}
<Button
color="custom"
className="!h-full px-4 text-text-subtle rounded-none hocus:bg-danger hocus:text"
className="!h-full px-4 text-text-subtle rounded-none hocus:bg-danger hocus:text-text"
onClick={() => getCurrentWebviewWindow().close()}
>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">

View File

@@ -10,7 +10,6 @@ import { ImportCurlButton } from './ImportCurlButton';
import { RecentRequestsDropdown } from './RecentRequestsDropdown';
import { SettingsDropdown } from './SettingsDropdown';
import { SidebarActions } from './SidebarActions';
import { WindowControls } from './WindowControls';
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
interface Props {
@@ -20,17 +19,22 @@ interface Props {
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
const togglePalette = useToggleCommandPalette();
return (
<HStack space={2} justifyContent="center" className={classNames(className, 'w-full h-full')}>
<div
className={classNames(
className,
'grid grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center w-full h-full',
)}
>
<HStack space={0.5} className="flex-1 pointer-events-none">
<SidebarActions />
<CookieDropdown />
<HStack>
<HStack className="min-w-0">
<WorkspaceActionsDropdown />
<Icon icon="chevron_right" className="text-text-subtle" />
<EnvironmentActionsDropdown className="w-auto pointer-events-auto" />
</HStack>
</HStack>
<div className="pointer-events-none">
<div className="pointer-events-none w-full max-w-[30vw] mx-auto">
<RecentRequestsDropdown />
</div>
<div className="flex-1 flex gap-1 items-center h-full justify-end pointer-events-none pr-0.5">
@@ -42,8 +46,7 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
onClick={togglePalette}
/>
<SettingsDropdown />
<WindowControls />
</div>
</HStack>
</div>
);
});

View File

@@ -1,16 +1,31 @@
import { useMutation } from '@tanstack/react-query';
import { useDialog } from '../components/DialogContext';
import Settings from '../components/Settings/Settings';
import { invokeCmd } from '../lib/tauri';
import { useActiveWorkspace } from './useActiveWorkspace';
import { useAppRoutes } from './useAppRoutes';
import { useOsInfo } from './useOsInfo';
export function useOpenSettings() {
const routes = useAppRoutes();
const workspace = useActiveWorkspace();
const dialog = useDialog();
const { osType } = useOsInfo();
return useMutation({
mutationKey: ['open_settings'],
mutationFn: async () => {
if (workspace == null) return;
// HACK: Show settings in dialog on Linux until this is fixed: https://github.com/tauri-apps/tauri/issues/11171
if (osType === 'linux') {
dialog.show({
id: 'settings',
size: 'lg',
render: ({ hide }) => <Settings hide={hide} />,
});
return;
}
await invokeCmd('cmd_new_child_window', {
url: routes.paths.workspaceSettings({ workspaceId: workspace.id }),
label: 'settings',

View File

@@ -0,0 +1,9 @@
import { useIsFullscreen } from './useIsFullscreen';
import { useOsInfo } from './useOsInfo';
export function useStoplightsVisible() {
const platform = useOsInfo();
const fullscreen = useIsFullscreen();
const stoplightsVisible = platform?.osType === 'macos' && !fullscreen;
return stoplightsVisible;
}