fix(settings): Reset button not showing properly + unify game settings across Cluster and Global launcher pages
This commit is contained in:
parent
7ef1774767
commit
3f30a41c5a
3 changed files with 315 additions and 439 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import type { JSX } from 'solid-js';
|
||||
import { createSignal, createUniqueId, splitProps } from 'solid-js';
|
||||
import { createSignal, createUniqueId, onMount, splitProps } from 'solid-js';
|
||||
import styles from './TextField.module.scss';
|
||||
|
||||
type TextFieldProps = {
|
||||
|
|
@ -16,11 +16,13 @@ function TextField(props: TextFieldProps) {
|
|||
const [isValid, setIsValid] = createSignal(true);
|
||||
const id = createUniqueId();
|
||||
|
||||
function validate(e: Event & { currentTarget: HTMLInputElement }) {
|
||||
let ref!: HTMLInputElement;
|
||||
|
||||
function validate() {
|
||||
if (!split.inputFilter)
|
||||
return;
|
||||
|
||||
const value = e.currentTarget.value;
|
||||
const value = ref.value;
|
||||
const valid = split.inputFilter(value);
|
||||
setIsValid(valid);
|
||||
|
||||
|
|
@ -28,17 +30,22 @@ function TextField(props: TextFieldProps) {
|
|||
split.onValidInput(value);
|
||||
}
|
||||
|
||||
function onSubmit(e: Event & { currentTarget: HTMLInputElement }) {
|
||||
function onSubmit() {
|
||||
if (isValid() && split.onValidSubmit)
|
||||
split.onValidSubmit(e.currentTarget.value);
|
||||
split.onValidSubmit(ref.value);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
validate();
|
||||
});
|
||||
|
||||
return (
|
||||
<label for={id} class={`${styles.textfield} ${isValid() ? '' : styles.invalid} ${split.labelClass || ''}`}>
|
||||
{split.iconLeft && <span class={styles.icon}>{split.iconLeft}</span>}
|
||||
<input
|
||||
id={id}
|
||||
type="text"
|
||||
ref={ref}
|
||||
onInput={validate}
|
||||
onChange={onSubmit}
|
||||
{...rest}
|
||||
|
|
@ -48,11 +55,35 @@ function TextField(props: TextFieldProps) {
|
|||
);
|
||||
}
|
||||
|
||||
TextField.Number = (props: TextFieldProps) => (
|
||||
<TextField
|
||||
inputFilter={value => /^\d*$/.test(value)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
type NumberTextFieldProps = TextFieldProps & {
|
||||
min?: number;
|
||||
max?: number;
|
||||
};
|
||||
|
||||
TextField.Number = (props: NumberTextFieldProps) => {
|
||||
const [split, rest] = splitProps(props, ['min', 'max', 'inputFilter']);
|
||||
|
||||
return (
|
||||
<TextField
|
||||
{...rest}
|
||||
inputFilter={(value) => {
|
||||
const check_pattern = /^\d*$/.test(value);
|
||||
if (!check_pattern)
|
||||
return false;
|
||||
|
||||
const check_min = split.min === undefined || Number(value) >= split.min;
|
||||
const check_max = split.max === undefined || Number(value) <= split.max;
|
||||
|
||||
if (!check_min || !check_max)
|
||||
return false;
|
||||
|
||||
if (split.inputFilter)
|
||||
return split.inputFilter(value);
|
||||
|
||||
return true;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextField;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,11 @@
|
|||
import { useBeforeLeave } from '@solidjs/router';
|
||||
import { type Accessor, Show, splitProps } from 'solid-js';
|
||||
import { CpuChip01Icon, Database01Icon, FilePlus02Icon, FileX02Icon, LayoutTopIcon, Maximize01Icon, ParagraphWrapIcon, VariableIcon, XIcon } from '@untitled-theme/icons-solid';
|
||||
import { createSetting } from '../settings/game/SettingsMinecraft';
|
||||
import { type Accessor, Show } from 'solid-js';
|
||||
import { GameSettings, JvmSettings, LauncherSettings, ProcessSettings, createSetting } from '../settings/game/SettingsMinecraft';
|
||||
import ScrollableContainer from '~ui/components/ScrollableContainer';
|
||||
import type { SettingsRowProps } from '~ui/components/SettingsRow';
|
||||
import Sidebar from '~ui/components/Sidebar';
|
||||
import useClusterContext from '~ui/hooks/useCluster';
|
||||
import BaseSettingsRow from '~ui/components/SettingsRow';
|
||||
import type { Cluster } from '~bindings';
|
||||
import useSettingsContext from '~ui/hooks/useSettings';
|
||||
import Toggle from '~ui/components/base/Toggle';
|
||||
import TextField from '~ui/components/base/TextField';
|
||||
import { asEnvVariables } from '~utils';
|
||||
import { tryResult } from '~ui/hooks/useCommand';
|
||||
import { bridge } from '~imports';
|
||||
|
||||
|
|
@ -20,7 +14,7 @@ function ClusterSettings() {
|
|||
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<h1>Overview</h1>
|
||||
<h1>Game Settings</h1>
|
||||
<ScrollableContainer>
|
||||
<Show when={cluster() !== undefined}>
|
||||
{PageSettings(() => cluster()!)}
|
||||
|
|
@ -30,30 +24,6 @@ function ClusterSettings() {
|
|||
);
|
||||
}
|
||||
|
||||
function SettingsRow(props: SettingsRowProps & {
|
||||
reset: () => any;
|
||||
isGlobal: Accessor<boolean>;
|
||||
}) {
|
||||
const [split, rest] = splitProps(props, ['children', 'reset', 'isGlobal']);
|
||||
|
||||
// TODO: Reset button doesn't show up immediately after changing a setting
|
||||
|
||||
return (
|
||||
<BaseSettingsRow {...rest}>
|
||||
<Show when={split.isGlobal() === false}>
|
||||
<div
|
||||
class="px-2 py-1 text-xs text-white bg-brand hover:bg-brand-hover active:bg-brand-pressed rounded-md font-italic font-medium"
|
||||
onClick={() => split.reset()}
|
||||
>
|
||||
Reset
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{split.children}
|
||||
</BaseSettingsRow>
|
||||
);
|
||||
}
|
||||
|
||||
function PageSettings(cluster: Accessor<Cluster>) {
|
||||
const { settings } = useSettingsContext();
|
||||
|
||||
|
|
@ -97,207 +67,12 @@ function PageSettings(cluster: Accessor<Cluster>) {
|
|||
});
|
||||
});
|
||||
|
||||
const GameSettings = () => (
|
||||
<>
|
||||
<BaseSettingsRow.Header>Game</BaseSettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Force Fullscreen"
|
||||
description="Force Minecraft to start in fullscreen mode."
|
||||
icon={<Maximize01Icon />}
|
||||
isGlobal={fullscreen.isGlobal}
|
||||
reset={fullscreen.resetToFallback}
|
||||
>
|
||||
<Toggle
|
||||
checked={fullscreen.get}
|
||||
onChecked={fullscreen.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Resolution"
|
||||
description="The game window resolution in pixels."
|
||||
icon={<LayoutTopIcon />}
|
||||
isGlobal={resolution.isGlobal}
|
||||
reset={resolution.resetToFallback}
|
||||
>
|
||||
<div class="grid grid-justify-center grid-items-center gap-2 grid-cols-[70px_16px_70px]">
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
value={resolution.get()[0]}
|
||||
onValidSubmit={(value) => {
|
||||
resolution.set([Number.parseInt(value), resolution.get()[1]]);
|
||||
}}
|
||||
/>
|
||||
<XIcon class="w-4 h-4" />
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
value={resolution.get()[1]}
|
||||
onValidSubmit={(value) => {
|
||||
resolution.set([resolution.get()[0], Number.parseInt(value)]);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Memory"
|
||||
description="The amount of memory in megabytes allocated for the game."
|
||||
icon={<Database01Icon />}
|
||||
isGlobal={memory.isGlobal}
|
||||
reset={memory.resetToFallback}
|
||||
>
|
||||
<div class="flex flex-justify-center items-center gap-x-4">
|
||||
<div class="flex flex-row items-center gap-x-2">
|
||||
<span>Min:</span>
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
labelClass="w-[70px]!"
|
||||
value={memory.get().minimum}
|
||||
onValidSubmit={(value) => {
|
||||
memory.set({ minimum: Number.parseInt(value), maximum: memory.get().maximum });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center gap-x-2">
|
||||
<span>Max:</span>
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
labelClass="w-[70px]!"
|
||||
value={memory.get().maximum}
|
||||
onValidSubmit={(value) => {
|
||||
memory.set({ minimum: memory.get().minimum, maximum: Number.parseInt(value) });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
|
||||
const LauncherSettings = () => (
|
||||
<>
|
||||
{/* <BaseSettingsRow.Header>Launcher</BaseSettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Hide On Launch"
|
||||
description="Hide the launcher whenever you start a game."
|
||||
icon={<EyeIcon />}
|
||||
>
|
||||
<Toggle
|
||||
checked={hideOnLaunch.get}
|
||||
onChecked={hideOnLaunch.set}
|
||||
/>
|
||||
</SettingsRow> */}
|
||||
</>
|
||||
);
|
||||
|
||||
const ProcessSettings = () => (
|
||||
<>
|
||||
<BaseSettingsRow.Header>Process</BaseSettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Pre-Launch Command"
|
||||
description="Command to run before launching the game."
|
||||
icon={<FilePlus02Icon />}
|
||||
isGlobal={preCommand.isGlobal}
|
||||
reset={preCommand.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder="echo 'Game started'"
|
||||
value={preCommand.get()}
|
||||
onValidSubmit={preCommand.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Wrapper Command"
|
||||
description="Command to run when launching the game."
|
||||
icon={<ParagraphWrapIcon />}
|
||||
isGlobal={wrapperCommand.isGlobal}
|
||||
reset={wrapperCommand.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder="gamescope"
|
||||
value={wrapperCommand.get()}
|
||||
onValidSubmit={wrapperCommand.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Post-Exit Command"
|
||||
description="Command to run after exiting the game."
|
||||
icon={<FileX02Icon />}
|
||||
isGlobal={postCommand.isGlobal}
|
||||
reset={postCommand.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder="echo 'Game exited'"
|
||||
value={postCommand.get()}
|
||||
onValidSubmit={postCommand.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
|
||||
const JvmSettings = () => (
|
||||
<>
|
||||
<BaseSettingsRow.Header>Java</BaseSettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Version"
|
||||
description="Choose the JRE (Java Runtime Environment) used for the game."
|
||||
icon={<CpuChip01Icon />}
|
||||
isGlobal={() => true}
|
||||
reset={() => {}}
|
||||
/>
|
||||
|
||||
<SettingsRow
|
||||
title="Arguments"
|
||||
description="Additional arguments passed to the JVM (Java Virtual Machine)."
|
||||
icon={<FilePlus02Icon />}
|
||||
isGlobal={javaArgs.isGlobal}
|
||||
reset={javaArgs.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder='"-Xmx3G"'
|
||||
value={javaArgs.get().join(' ')}
|
||||
onValidSubmit={(value) => {
|
||||
javaArgs.set(value.split(' '));
|
||||
}}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Environment Variables"
|
||||
description="Additional environment variables passed to the JVM (Java Virtual Machine)."
|
||||
icon={<VariableIcon />}
|
||||
isGlobal={envVars.isGlobal}
|
||||
reset={envVars.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder='"JAVA_HOME=/path/to/java"'
|
||||
value={envVars.get().join(' ')}
|
||||
onValidSubmit={(value) => {
|
||||
envVars.set(asEnvVariables(value));
|
||||
}}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<GameSettings />
|
||||
<LauncherSettings />
|
||||
<ProcessSettings />
|
||||
<JvmSettings />
|
||||
<GameSettings {...{ fullscreen, memory, resolution }} />
|
||||
<LauncherSettings {...{ hideOnLaunch: undefined }} />
|
||||
<ProcessSettings {...{ preCommand, wrapperCommand, postCommand }} />
|
||||
<JvmSettings {...{ javaArgs, envVars }} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
import { CpuChip01Icon, Database01Icon, EyeIcon, FilePlus02Icon, FileX02Icon, LayoutTopIcon, Maximize01Icon, ParagraphWrapIcon, VariableIcon, XIcon } from '@untitled-theme/icons-solid';
|
||||
import { type Accessor, type Setter, createSignal, onMount, untrack } from 'solid-js';
|
||||
import { type Accessor, type Setter, Show, createSignal, onMount, splitProps, untrack } from 'solid-js';
|
||||
import type { Memory, Resolution } from '~bindings';
|
||||
import TextField from '~ui/components/base/TextField';
|
||||
import Toggle from '~ui/components/base/Toggle';
|
||||
import ScrollableContainer from '~ui/components/ScrollableContainer';
|
||||
import SettingsRow from '~ui/components/SettingsRow';
|
||||
import BaseSettingsRow, { type SettingsRowProps } from '~ui/components/SettingsRow';
|
||||
import Sidebar from '~ui/components/Sidebar';
|
||||
import useSettingsContext from '~ui/hooks/useSettings';
|
||||
import { asEnvVariables } from '~utils';
|
||||
|
||||
// TODO: REFACTOR THIS ENTIRE FILE AND POSSIBLY `apps/frontend/src/ui/pages/cluster/ClusterSettings.tsx` TOO
|
||||
|
||||
function SettingsMinecraft() {
|
||||
return (
|
||||
<Sidebar.Page>
|
||||
<h1>Global Minecraft Settings</h1>
|
||||
<h1>Global Game Settings</h1>
|
||||
<ScrollableContainer>
|
||||
<PageSettings />
|
||||
</ScrollableContainer>
|
||||
|
|
@ -25,20 +24,24 @@ interface CreateSetting<T> {
|
|||
get: Accessor<T>;
|
||||
getRaw: () => T;
|
||||
set: Setter<T>;
|
||||
isGlobal: Accessor<boolean>;
|
||||
isGlobal: Accessor<boolean | null>;
|
||||
resetToFallback: (raw?: any) => void;
|
||||
};
|
||||
|
||||
export function createSetting<T>(initial: T | undefined | null, fallback?: T): CreateSetting<T> {
|
||||
const [value, setValue] = createSignal<T>(initial || fallback as T);
|
||||
const [value, setValue] = createSignal<T>((initial === undefined || initial === null) ? fallback as T : initial);
|
||||
const [raw, setRaw] = createSignal<T>(initial as T);
|
||||
const [isGlobal, setIsGlobal] = createSignal(true);
|
||||
const [isGlobal, setIsGlobal] = createSignal<boolean | null>(true);
|
||||
|
||||
const checkGlobal = () => {
|
||||
const check_one = untrack(() => value()) === undefined || untrack(() => value()) === null;
|
||||
const check_two = initial === undefined || initial === null;
|
||||
// const check_three = untrack(() => raw()) === undefined || untrack(() => raw()) === null;
|
||||
if (fallback === undefined || fallback === null) {
|
||||
setIsGlobal(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsGlobal(check_one || check_two);
|
||||
const check_one = untrack(() => raw()) === undefined || untrack(() => raw()) === null;
|
||||
|
||||
setIsGlobal(check_one);
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
|
|
@ -67,17 +70,261 @@ export function createSetting<T>(initial: T | undefined | null, fallback?: T): C
|
|||
checkGlobal();
|
||||
};
|
||||
|
||||
const getRaw = () => raw();
|
||||
|
||||
return {
|
||||
get: value,
|
||||
getRaw,
|
||||
getRaw: raw,
|
||||
set,
|
||||
isGlobal,
|
||||
resetToFallback,
|
||||
};
|
||||
}
|
||||
|
||||
function SettingsRow(props: SettingsRowProps & {
|
||||
reset: () => any;
|
||||
isGlobal: Accessor<boolean | null>;
|
||||
}) {
|
||||
const [split, rest] = splitProps(props, ['children', 'reset', 'isGlobal']);
|
||||
|
||||
return (
|
||||
<BaseSettingsRow {...rest}>
|
||||
<Show when={split.isGlobal() === false}>
|
||||
<div
|
||||
class="px-2 py-1 text-xs text-white bg-brand hover:bg-brand-hover active:bg-brand-pressed rounded-md font-italic font-medium"
|
||||
onClick={() => split.reset()}
|
||||
>
|
||||
Reset
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{split.children}
|
||||
</BaseSettingsRow>
|
||||
);
|
||||
}
|
||||
|
||||
export function GameSettings(props: {
|
||||
fullscreen: CreateSetting<boolean>;
|
||||
resolution: CreateSetting<Resolution>;
|
||||
memory: CreateSetting<Memory>;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BaseSettingsRow.Header>Game</BaseSettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Force Fullscreen"
|
||||
description="Force Minecraft to start in fullscreen mode."
|
||||
icon={<Maximize01Icon />}
|
||||
isGlobal={props.fullscreen.isGlobal}
|
||||
reset={props.fullscreen.resetToFallback}
|
||||
>
|
||||
<Toggle
|
||||
checked={props.fullscreen.get}
|
||||
onChecked={props.fullscreen.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Resolution"
|
||||
description="The game window resolution in pixels."
|
||||
icon={<LayoutTopIcon />}
|
||||
isGlobal={props.resolution.isGlobal}
|
||||
reset={props.resolution.resetToFallback}
|
||||
>
|
||||
<div class="grid grid-justify-center grid-items-center gap-2 grid-cols-[70px_16px_70px]">
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
value={props.resolution.get()[0]}
|
||||
onValidSubmit={(value) => {
|
||||
props.resolution.set([Number.parseInt(value), props.resolution.get()[1]]);
|
||||
}}
|
||||
/>
|
||||
<XIcon class="w-4 h-4" />
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
value={props.resolution.get()[1]}
|
||||
onValidSubmit={(value) => {
|
||||
props.resolution.set([props.resolution.get()[0], Number.parseInt(value)]);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Memory"
|
||||
description="The amount of memory in megabytes allocated for the game."
|
||||
icon={<Database01Icon />}
|
||||
isGlobal={props.memory.isGlobal}
|
||||
reset={props.memory.resetToFallback}
|
||||
>
|
||||
<div class="flex flex-justify-center items-center gap-x-4">
|
||||
<div class="flex flex-row items-center gap-x-2">
|
||||
<span>Min:</span>
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
labelClass="w-[70px]!"
|
||||
value={props.memory.get().minimum}
|
||||
min={1}
|
||||
max={props.memory.get().maximum}
|
||||
onValidSubmit={(value) => {
|
||||
props.memory.set({ minimum: Number.parseInt(value), maximum: props.memory.get().maximum });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center gap-x-2">
|
||||
<span>Max:</span>
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
labelClass="w-[70px]!"
|
||||
value={props.memory.get().maximum}
|
||||
min={props.memory.get().minimum}
|
||||
max={Number.MAX_SAFE_INTEGER}
|
||||
onValidSubmit={(value) => {
|
||||
props.memory.set({ minimum: props.memory.get().minimum, maximum: Number.parseInt(value) });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function LauncherSettings(props: {
|
||||
hideOnLaunch: CreateSetting<boolean> | undefined;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Show when={props.hideOnLaunch !== undefined}>
|
||||
<BaseSettingsRow.Header>Launcher</BaseSettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Hide On Launch"
|
||||
description="Hide the launcher whenever you start a game."
|
||||
icon={<EyeIcon />}
|
||||
isGlobal={props.hideOnLaunch!.isGlobal}
|
||||
reset={props.hideOnLaunch!.resetToFallback}
|
||||
>
|
||||
<Toggle
|
||||
checked={props.hideOnLaunch!.get}
|
||||
onChecked={props.hideOnLaunch!.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function ProcessSettings(props: {
|
||||
preCommand: CreateSetting<string>;
|
||||
wrapperCommand: CreateSetting<string>;
|
||||
postCommand: CreateSetting<string>;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BaseSettingsRow.Header>Process</BaseSettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Pre-Launch Command"
|
||||
description="Command to run before launching the game."
|
||||
icon={<FilePlus02Icon />}
|
||||
isGlobal={props.preCommand.isGlobal}
|
||||
reset={props.preCommand.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder="echo 'Game started'"
|
||||
value={props.preCommand.get()}
|
||||
onValidSubmit={props.preCommand.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Wrapper Command"
|
||||
description="Command to run when launching the game."
|
||||
icon={<ParagraphWrapIcon />}
|
||||
isGlobal={props.wrapperCommand.isGlobal}
|
||||
reset={props.wrapperCommand.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder="gamescope"
|
||||
value={props.wrapperCommand.get()}
|
||||
onValidSubmit={props.wrapperCommand.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Post-Exit Command"
|
||||
description="Command to run after exiting the game."
|
||||
icon={<FileX02Icon />}
|
||||
isGlobal={props.postCommand.isGlobal}
|
||||
reset={props.postCommand.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder="echo 'Game exited'"
|
||||
value={props.postCommand.get()}
|
||||
onValidSubmit={props.postCommand.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function JvmSettings(props: {
|
||||
javaArgs: CreateSetting<string[]>;
|
||||
envVars: CreateSetting<[string, string][]>;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<BaseSettingsRow.Header>Java</BaseSettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Version"
|
||||
description="Choose the JRE (Java Runtime Environment) used for the game."
|
||||
icon={<CpuChip01Icon />}
|
||||
isGlobal={() => true}
|
||||
reset={() => {}}
|
||||
/>
|
||||
|
||||
<SettingsRow
|
||||
title="Arguments"
|
||||
description="Additional arguments passed to the JVM (Java Virtual Machine)."
|
||||
icon={<FilePlus02Icon />}
|
||||
isGlobal={props.javaArgs.isGlobal}
|
||||
reset={props.javaArgs.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder='"-Xmx3G"'
|
||||
value={props.javaArgs.get().join(' ')}
|
||||
onValidSubmit={(value) => {
|
||||
props.javaArgs.set(value.split(' '));
|
||||
}}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Environment Variables"
|
||||
description="Additional environment variables passed to the JVM (Java Virtual Machine)."
|
||||
icon={<VariableIcon />}
|
||||
isGlobal={props.envVars.isGlobal}
|
||||
reset={props.envVars.resetToFallback}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder='"JAVA_HOME=/path/to/java"'
|
||||
value={props.envVars.get().join(' ')}
|
||||
onValidSubmit={(value) => {
|
||||
props.envVars.set(asEnvVariables(value));
|
||||
}}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function PageSettings() {
|
||||
const { settings, saveOnLeave } = useSettingsContext();
|
||||
|
||||
|
|
@ -119,189 +366,12 @@ function PageSettings() {
|
|||
custom_env_args: envVars.get(),
|
||||
}));
|
||||
|
||||
const GameSettings = () => (
|
||||
<>
|
||||
<SettingsRow.Header>Game</SettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Force Fullscreen"
|
||||
description="Force Minecraft to start in fullscreen mode."
|
||||
icon={<Maximize01Icon />}
|
||||
>
|
||||
<Toggle
|
||||
checked={fullscreen.get}
|
||||
onChecked={fullscreen.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Resolution"
|
||||
description="The game window resolution in pixels."
|
||||
icon={<LayoutTopIcon />}
|
||||
>
|
||||
<div class="grid grid-justify-center grid-items-center gap-2 grid-cols-[70px_16px_70px]">
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
value={resolution.get()[0]}
|
||||
onValidSubmit={(value) => {
|
||||
resolution.set([Number.parseInt(value), resolution.get()[1]]);
|
||||
}}
|
||||
/>
|
||||
<XIcon class="w-4 h-4" />
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
value={resolution.get()[1]}
|
||||
onValidSubmit={(value) => {
|
||||
resolution.set([resolution.get()[0], Number.parseInt(value)]);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Memory"
|
||||
description="The amount of memory in megabytes allocated for the game."
|
||||
icon={<Database01Icon />}
|
||||
>
|
||||
<div class="flex flex-justify-center items-center gap-x-4">
|
||||
<div class="flex flex-row items-center gap-x-2">
|
||||
<span>Min:</span>
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
labelClass="w-[70px]!"
|
||||
value={memory.get().minimum}
|
||||
onValidSubmit={(value) => {
|
||||
memory.set({ minimum: Number.parseInt(value), maximum: memory.get().maximum });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center gap-x-2">
|
||||
<span>Max:</span>
|
||||
<TextField.Number
|
||||
class="text-center"
|
||||
labelClass="w-[70px]!"
|
||||
value={memory.get().maximum}
|
||||
onValidSubmit={(value) => {
|
||||
memory.set({ minimum: memory.get().minimum, maximum: Number.parseInt(value) });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
|
||||
const LauncherSettings = () => (
|
||||
<>
|
||||
<SettingsRow.Header>Launcher</SettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Hide On Launch"
|
||||
description="Hide the launcher whenever you start a game."
|
||||
icon={<EyeIcon />}
|
||||
>
|
||||
<Toggle
|
||||
checked={hideOnLaunch.get}
|
||||
onChecked={hideOnLaunch.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
|
||||
const ProcessSettings = () => (
|
||||
<>
|
||||
<SettingsRow.Header>Process</SettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Pre-Launch Command"
|
||||
description="Command to run before launching the game."
|
||||
icon={<FilePlus02Icon />}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder="echo 'Game started'"
|
||||
value={preCommand.get()}
|
||||
onValidSubmit={preCommand.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Wrapper Command"
|
||||
description="Command to run when launching the game."
|
||||
icon={<ParagraphWrapIcon />}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder="gamescope"
|
||||
value={wrapperCommand.get()}
|
||||
onValidSubmit={wrapperCommand.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Post-Exit Command"
|
||||
description="Command to run after exiting the game."
|
||||
icon={<FileX02Icon />}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder="echo 'Game exited'"
|
||||
value={postCommand.get()}
|
||||
onValidSubmit={postCommand.set}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
|
||||
const JvmSettings = () => (
|
||||
<>
|
||||
<SettingsRow.Header>Java</SettingsRow.Header>
|
||||
|
||||
<SettingsRow
|
||||
title="Version"
|
||||
description="Choose the JRE (Java Runtime Environment) used for the game."
|
||||
icon={<CpuChip01Icon />}
|
||||
/>
|
||||
|
||||
<SettingsRow
|
||||
title="Arguments"
|
||||
description="Additional arguments passed to the JVM (Java Virtual Machine)."
|
||||
icon={<FilePlus02Icon />}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder='"-Xmx3G"'
|
||||
value={javaArgs.get().join(' ')}
|
||||
onValidSubmit={(value) => {
|
||||
javaArgs.set(value.split(' '));
|
||||
}}
|
||||
/>
|
||||
</SettingsRow>
|
||||
|
||||
<SettingsRow
|
||||
title="Environment Variables"
|
||||
description="Additional environment variables passed to the JVM (Java Virtual Machine)."
|
||||
icon={<VariableIcon />}
|
||||
>
|
||||
<TextField
|
||||
labelClass="w-full! min-w-[260px]!"
|
||||
placeholder='"JAVA_HOME=/path/to/java"'
|
||||
value={envVars.get().join(' ')}
|
||||
onValidSubmit={(value) => {
|
||||
envVars.set(asEnvVariables(value));
|
||||
}}
|
||||
/>
|
||||
</SettingsRow>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<GameSettings />
|
||||
<LauncherSettings />
|
||||
<ProcessSettings />
|
||||
<JvmSettings />
|
||||
<GameSettings {...{ fullscreen, memory, resolution }} />
|
||||
<LauncherSettings {...{ hideOnLaunch }} />
|
||||
<ProcessSettings {...{ preCommand, wrapperCommand, postCommand }} />
|
||||
<JvmSettings {...{ javaArgs, envVars }} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue