Files
STARTLINER/src/components/options/Display.vue
2025-04-22 21:34:55 +00:00

235 lines
7.9 KiB
Vue

<script setup lang="ts">
import { Ref, computed, ref } from 'vue';
import InputNumber from 'primevue/inputnumber';
import Select from 'primevue/select';
import SelectButton from 'primevue/selectbutton';
import ToggleSwitch from 'primevue/toggleswitch';
import OptionCategory from '../OptionCategory.vue';
import OptionRow from '../OptionRow.vue';
import { invoke } from '../../invoke';
import { usePrfStore } from '../../stores';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const capabilities: Ref<string[]> = ref([]);
const displayList: Ref<{ title: string; value: string }[]> = ref([
{
title: t('cfg.display.primary'),
value: 'default',
},
]);
const prf = usePrfStore();
const extraDisplayOptionsDisabled = computed(() => {
return prf.current?.data.display.target === 'default';
});
const loadDisplays = () => {
const newList = [
{
title: t('cfg.display.primary'),
value: 'default',
},
];
invoke('list_platform_capabilities')
.then(async (v: unknown) => {
let different = false;
if (Array.isArray(v)) {
capabilities.value.push(...v);
}
if (capabilities.value.includes('display')) {
for (const [devName, devString] of (await invoke(
'list_displays'
)) as Array<[string, string]>) {
newList.push({
title: `${devName.replace('\\\\.\\', '')} (${devString})`,
value: devName,
});
if (
displayList.value.find(
(item) => item.value === devName
) === undefined
) {
different = true;
}
}
}
if (displayList.value.length !== newList.length) {
different = true;
}
if (different) {
displayList.value = newList;
}
})
.catch(() => {});
};
loadDisplays();
const game = prf.current!.meta.game;
const isVertical = game === 'ongeki';
const adjustableRez = game === 'ongeki';
const canSkipPrimarySwitch = game === 'ongeki';
</script>
<template>
<OptionCategory title="Display">
<OptionRow
v-if="capabilities.includes('display')"
:title="t('cfg.display.target')"
>
<Select
v-model="prf.current!.data.display.target"
:options="displayList"
option-label="title"
option-value="value"
placeholder="(Disconnected)"
@show="loadDisplays"
></Select>
</OptionRow>
<OptionRow
class="number-input"
title="Game resolution"
v-if="adjustableRez"
>
<InputNumber
class="shrink"
size="small"
:min="480"
:max="9999"
:use-grouping="false"
v-model="prf.current!.data.display.rez[0]"
/>
x
<InputNumber
class="shrink"
size="small"
:min="640"
:max="9999"
:use-grouping="false"
v-model="prf.current!.data.display.rez[1]"
/>
</OptionRow>
<OptionRow :title="t('cfg.display.mode')">
<SelectButton
v-model="prf.current!.data.display.mode"
:options="[
{ title: 'Window', value: 'Window' },
{ title: 'Borderless window', value: 'Borderless' },
{ title: 'Fullscreen', value: 'Fullscreen' },
]"
:allow-empty="false"
option-label="title"
option-value="value"
/>
</OptionRow>
<OptionRow
:title="t('cfg.display.rotation')"
v-if="capabilities.includes('display')"
>
<SelectButton
v-model="prf.current!.data.display.rotation"
:options="
isVertical
? [
{ title: t('cfg.display.portrait'), value: 90 },
{
title: `${t('cfg.display.portrait')} (${t('cfg.display.flipped')})`,
value: 270,
},
]
: [
{ title: t('cfg.display.landscape'), value: 0 },
{
title: `${t('cfg.display.landscape')} (${t('cfg.display.flipped')})`,
value: 180,
},
]
"
:allow-empty="true"
option-label="title"
option-value="value"
:disabled="extraDisplayOptionsDisabled"
/>
</OptionRow>
<OptionRow
v-if="capabilities.includes('display')"
class="number-input"
:title="t('cfg.display.refreshRate')"
>
<InputNumber
v-if="game === 'ongeki'"
class="shrink"
size="small"
:min="60"
:max="999"
:use-grouping="false"
v-model="prf.current!.data.display.frequency"
:disabled="extraDisplayOptionsDisabled"
/>
<SelectButton
v-if="game === 'chunithm'"
v-model="prf.current!.data.display.frequency"
:options="[
{ title: '60Hz (CVT)', value: 60 },
{ title: '120Hz (SP)', value: 120 },
]"
:allow-empty="false"
option-label="title"
option-value="value"
:disabled="extraDisplayOptionsDisabled"
/>
</OptionRow>
<OptionRow
:title="t('cfg.display.borderlessFullscreen')"
v-if="capabilities.includes('display')"
:tooltip="t('cfg.display.borderlessFullscreenTooltip')"
>
<ToggleSwitch
:disabled="
extraDisplayOptionsDisabled ||
prf.current?.data.display.mode !== 'Borderless'
"
v-model="prf.current!.data.display.borderless_fullscreen"
/>
</OptionRow>
<OptionRow
:title="t('cfg.display.dontSwitchPrimary')"
v-if="
capabilities.includes('display') &&
prf.current?.data.display.target !== 'default' &&
(prf.current!.data.display.dont_switch_primary ||
displayList.length > 2) &&
canSkipPrimarySwitch
"
:dangerous-tooltip="t('cfg.display.dontSwitchPrimaryTooltip')"
>
<ToggleSwitch
:disabled="extraDisplayOptionsDisabled"
v-model="prf.current!.data.display.dont_switch_primary"
/>
</OptionRow>
<OptionRow
:title="t('cfg.display.index')"
class="number-input"
v-if="
capabilities.includes('display') &&
prf.current?.data.display.target !== 'default' &&
prf.current!.data.display.dont_switch_primary
"
>
<InputNumber
class="shrink"
size="small"
:min="game === 'chunithm' ? 0 : 1"
:max="32"
:use-grouping="false"
v-model="prf.current!.data.display.monitor_index_override"
:disabled="extraDisplayOptionsDisabled"
:allow-empty="true"
/>
</OptionRow>
</OptionCategory>
</template>