Controller customization feature

This commit is contained in:
redphx
2024-12-22 17:17:03 +07:00
parent 8ef5a95c88
commit 7b60ba3a3e
89 changed files with 3286 additions and 1188 deletions

View File

@@ -1,12 +1,10 @@
import { GamepadKey } from "@/enums/gamepad";
import { PrefKey } from "@/enums/pref-keys";
import { VIRTUAL_GAMEPAD_ID } from "@/modules/mkb/mkb-handler";
import { BxEvent } from "@/utils/bx-event";
import { BxEventBus } from "@/utils/bx-event-bus";
import { BxLogger } from "@/utils/bx-logger";
import { CE, isElementVisible } from "@/utils/html";
import { calculateSelectBoxes, CE, isElementVisible } from "@/utils/html";
import { setNearby } from "@/utils/navigation-utils";
import { getPref } from "@/utils/settings-storages/global-settings-storage";
export enum NavigationDirection {
UP = 1,
@@ -21,10 +19,10 @@ export type NavigationNearbyElements = Partial<{
focus: NavigationElement | (() => boolean),
loop: ((direction: NavigationDirection) => boolean),
[NavigationDirection.UP]: NavigationElement | (() => void) | 'previous' | 'next',
[NavigationDirection.DOWN]: NavigationElement | (() => void) | 'previous' | 'next',
[NavigationDirection.LEFT]: NavigationElement | (() => void) | 'previous' | 'next',
[NavigationDirection.RIGHT]: NavigationElement | (() => void) | 'previous' | 'next',
[NavigationDirection.UP]: NavigationElement,
[NavigationDirection.DOWN]: NavigationElement,
[NavigationDirection.LEFT]: NavigationElement,
[NavigationDirection.RIGHT]: NavigationElement,
}>;
export interface NavigationElement extends HTMLElement {
@@ -107,16 +105,18 @@ export class NavigationDialogManager {
private static readonly GAMEPAD_POLLING_INTERVAL = 50;
private static readonly GAMEPAD_KEYS = [
GamepadKey.UP,
GamepadKey.DOWN,
GamepadKey.LEFT,
GamepadKey.RIGHT,
GamepadKey.A,
GamepadKey.B,
GamepadKey.LB,
GamepadKey.RB,
GamepadKey.LT,
GamepadKey.RT,
GamepadKey.A, GamepadKey.B,
GamepadKey.X, GamepadKey.Y,
GamepadKey.UP, GamepadKey.RIGHT,
GamepadKey.DOWN, GamepadKey.LEFT,
GamepadKey.LB, GamepadKey.RB,
GamepadKey.LT, GamepadKey.RT,
GamepadKey.L3, GamepadKey.R3,
GamepadKey.SELECT, GamepadKey.START,
];
private static readonly GAMEPAD_DIRECTION_MAP = {
@@ -172,61 +172,21 @@ export class NavigationDialogManager {
window.addEventListener(BxEvent.XCLOUD_GUIDE_MENU_SHOWN, e => this.hide());
// Calculate minimum width of controller-friendly <select> elements
if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
const observer = new MutationObserver(mutationList => {
if (mutationList.length === 0 || mutationList[0].addedNodes.length === 0) {
return;
}
// Get dialog
const $dialog = mutationList[0].addedNodes[0];
if (!$dialog || !($dialog instanceof HTMLElement)) {
return;
}
// Find un-calculated <select> elements
this.calculateSelectBoxes($dialog);
});
observer.observe(this.$container, { childList: true });
}
}
calculateSelectBoxes($root: HTMLElement) {
const selects = Array.from($root.querySelectorAll('.bx-select:not([data-calculated]) select'));
for (const $select of selects) {
const $parent = $select.parentElement! as HTMLElement;
// Don't apply to select.bx-full-width elements
if ($parent.classList.contains('bx-full-width')) {
$parent.dataset.calculated = 'true';
const observer = new MutationObserver(mutationList => {
if (mutationList.length === 0 || mutationList[0].addedNodes.length === 0) {
return;
}
const rect = $select.getBoundingClientRect();
let $label: HTMLElement;
let width = Math.ceil(rect.width);
if (!width) {
// Get dialog
const $dialog = mutationList[0].addedNodes[0];
if (!$dialog || !($dialog instanceof HTMLElement)) {
return;
}
if (($select as HTMLSelectElement).multiple) {
$label = $parent.querySelector<HTMLElement>('.bx-select-value')!;
width += 20; // Add checkbox's width
} else {
$label = $parent.querySelector<HTMLElement>('div')!;
}
// Reduce width if it has <optgroup>
if ($select.querySelector('optgroup')) {
width -= 15;
}
// Set min-width
$label.style.minWidth = width + 'px';
$parent.dataset.calculated = 'true';
};
// Find un-calculated <select> elements
calculateSelectBoxes($dialog);
});
observer.observe(this.$container, { childList: true });
}
private updateActiveInput(input: 'keyboard' | 'gamepad' | 'mouse') {
@@ -369,7 +329,7 @@ export class NavigationDialogManager {
}
this.clearGamepadHoldingInterval();
}, 200);
}, 100);
}
continue;
}
@@ -579,6 +539,16 @@ export class NavigationDialogManager {
const nearby = ($target as NavigationElement).nearby || {};
const orientation = this.getOrientation($target)!;
if (nearby[NavigationDirection.UP] && direction === NavigationDirection.UP) {
return nearby[NavigationDirection.UP];
} else if (nearby[NavigationDirection.DOWN] && direction === NavigationDirection.DOWN) {
return nearby[NavigationDirection.DOWN];
} else if (nearby[NavigationDirection.LEFT] && direction === NavigationDirection.LEFT) {
return nearby[NavigationDirection.LEFT];
} else if (nearby[NavigationDirection.RIGHT] && direction === NavigationDirection.RIGHT) {
return nearby[NavigationDirection.RIGHT];
}
// @ts-ignore
let siblingProperty = (NavigationDialogManager.SIBLING_PROPERTY_MAP[orientation])[direction];
if (siblingProperty) {

View File

@@ -5,6 +5,7 @@ import { t } from "@/utils/translation";
import type { AllPresets, PresetRecord } from "@/types/presets";
import type { BasePresetsTable } from "@/utils/local-db/base-presets-table";
import { BxSelectElement } from "@/web-components/bx-select";
import { BxEvent } from "@/utils/bx-event";
export abstract class BaseProfileManagerDialog<T extends PresetRecord> extends NavigationDialog {
$container!: HTMLElement;
@@ -12,7 +13,8 @@ export abstract class BaseProfileManagerDialog<T extends PresetRecord> extends N
private title: string;
protected presetsDb: BasePresetsTable<T>;
protected allPresets!: AllPresets<T>;
protected currentPresetId: number = 0;
protected currentPresetId: number | null = null;
protected activatedPresetId: number | null = null;
private $presets!: HTMLSelectElement;
private $header!: HTMLElement;
@@ -34,7 +36,7 @@ export abstract class BaseProfileManagerDialog<T extends PresetRecord> extends N
protected abstract switchPreset(id: number): void;
protected updateButtonStates() {
const isDefaultPreset = this.currentPresetId <= 0;
const isDefaultPreset = this.currentPresetId === null || this.currentPresetId <= 0;
this.$btnRename.disabled = isDefaultPreset;
this.$btnDelete.disabled = isDefaultPreset;
this.$defaultNote.classList.toggle('bx-gone', !isDefaultPreset);
@@ -42,7 +44,11 @@ export abstract class BaseProfileManagerDialog<T extends PresetRecord> extends N
private async renderPresetsList() {
this.allPresets = await this.presetsDb.getPresets();
renderPresetsList<T>(this.$presets, this.allPresets, this.currentPresetId, { selectedIndicator: true });
if (this.currentPresetId === null) {
this.currentPresetId = this.allPresets.default[0];
}
renderPresetsList<T>(this.$presets, this.allPresets, this.activatedPresetId, { selectedIndicator: true });
}
private promptNewName(action: string,value='') {
@@ -59,10 +65,12 @@ export abstract class BaseProfileManagerDialog<T extends PresetRecord> extends N
};
private async renderDialog() {
this.$presets = CE<HTMLSelectElement>('select', { tabindex: -1 });
this.$presets = CE('select', {
class: 'bx-full-width',
tabindex: -1,
});
const $select = BxSelectElement.create(this.$presets);
$select.classList.add('bx-full-width');
$select.addEventListener('input', e => {
this.switchPreset(parseInt(($select as HTMLSelectElement).value));
});
@@ -82,7 +90,7 @@ export abstract class BaseProfileManagerDialog<T extends PresetRecord> extends N
icon: BxIcon.CURSOR_TEXT,
style: ButtonStyle.FOCUSABLE,
onClick: async () => {
const preset = this.allPresets.data[this.currentPresetId];
const preset = this.allPresets.data[this.currentPresetId!];
const newName = this.promptNewName(t('rename'), preset.name);
if (!newName) {
@@ -107,8 +115,8 @@ export abstract class BaseProfileManagerDialog<T extends PresetRecord> extends N
return;
}
await this.presetsDb.deletePreset(this.currentPresetId);
delete this.allPresets.data[this.currentPresetId];
await this.presetsDb.deletePreset(this.currentPresetId!);
delete this.allPresets.data[this.currentPresetId!];
this.currentPresetId = parseInt(Object.keys(this.allPresets.data)[0]);
await this.refresh();
@@ -140,7 +148,7 @@ export abstract class BaseProfileManagerDialog<T extends PresetRecord> extends N
title: t('copy'),
style: ButtonStyle.FOCUSABLE | ButtonStyle.PRIMARY,
onClick: async (e) => {
const preset = this.allPresets.data[this.currentPresetId];
const preset = this.allPresets.data[this.currentPresetId!];
const newName = this.promptNewName(t('copy'), `${preset.name} (2)`);
if (!newName) {
@@ -176,12 +184,26 @@ export abstract class BaseProfileManagerDialog<T extends PresetRecord> extends N
async refresh() {
await this.renderPresetsList();
this.switchPreset(this.currentPresetId);
this.$presets.value = this.currentPresetId!.toString();
BxEvent.dispatch(this.$presets, 'input', { manualTrigger: true });
}
async onBeforeMount(configs:{ id?: number }={}) {
if (configs?.id) {
this.currentPresetId = configs.id;
await this.renderPresetsList();
let valid = false;
if (typeof configs?.id === 'number') {
if (configs.id in this.allPresets.data) {
this.currentPresetId = configs.id;
this.activatedPresetId = configs.id;
valid = true;
}
}
// Invalid selected ID => get default ID;
if (!valid) {
this.currentPresetId = this.allPresets.default[0];
this.activatedPresetId = null;
}
// Select first preset

View File

@@ -0,0 +1,371 @@
import type { ControllerCustomizationPresetData, ControllerCustomizationPresetRecord } from "@/types/presets";
import { BaseProfileManagerDialog } from "./base-profile-manager-dialog";
import { ControllerCustomizationsTable } from "@/utils/local-db/controller-customizations-table";
import { t } from "@/utils/translation";
import { GamepadKey, GamepadKeyName } from "@/enums/gamepad";
import { ButtonStyle, CE, createButton, createSettingRow } from "@/utils/html";
import { BxSelectElement } from "@/web-components/bx-select";
import { PrefKey } from "@/enums/pref-keys";
import { getPref } from "@/utils/settings-storages/global-settings-storage";
import { BxEvent } from "@/utils/bx-event";
import { deepClone } from "@/utils/global";
import { StreamSettings } from "@/utils/stream-settings";
import { BxDualNumberStepper } from "@/web-components/bx-dual-number-stepper";
import { NavigationDirection, type NavigationElement } from "../navigation-dialog";
import { setNearby } from "@/utils/navigation-utils";
import type { DualNumberStepperParams } from "@/types/setting-definition";
import { BxNumberStepper } from "@/web-components/bx-number-stepper";
export class ControllerCustomizationsManagerDialog extends BaseProfileManagerDialog<ControllerCustomizationPresetRecord> {
private static instance: ControllerCustomizationsManagerDialog;
public static getInstance = () => ControllerCustomizationsManagerDialog.instance ?? (ControllerCustomizationsManagerDialog.instance = new ControllerCustomizationsManagerDialog(t('controller-customization')));
declare protected $content: HTMLElement;
private $vibrationIntensity!: BxNumberStepper;
private $leftTriggerRange!: BxDualNumberStepper;
private $rightTriggerRange!: BxDualNumberStepper;
private $leftStickDeadzone!: BxDualNumberStepper;
private $rightStickDeadzone!: BxDualNumberStepper;
private $btnDetect!: HTMLButtonElement;
protected BLANK_PRESET_DATA = {
mapping: {},
settings: {
leftTriggerRange: [0, 100],
rightTriggerRange: [0, 100],
leftStickDeadzone: [0, 100],
rightStickDeadzone: [0, 100],
vibrationIntensity: 100,
},
} satisfies ControllerCustomizationPresetData;
private selectsMap: Partial<Record<GamepadKey, HTMLSelectElement>> = {};
private selectsOrder: GamepadKey[] = [];
private isDetectingButton: boolean = false;
private detectIntervalId: number | null = null;
private readonly BUTTONS_ORDER = [
GamepadKey.A, GamepadKey.B,
GamepadKey.X, GamepadKey.Y,
GamepadKey.UP, GamepadKey.RIGHT,
GamepadKey.DOWN, GamepadKey.LEFT,
GamepadKey.LB, GamepadKey.RB,
GamepadKey.LT, GamepadKey.RT,
GamepadKey.SELECT, GamepadKey.START,
GamepadKey.L3, GamepadKey.R3,
GamepadKey.LS, GamepadKey.RS,
GamepadKey.SHARE,
];
constructor(title: string) {
super(title, ControllerCustomizationsTable.getInstance());
this.render();
}
private render() {
const isControllerFriendly = getPref(PrefKey.UI_CONTROLLER_FRIENDLY);
const $rows = CE('div', { class: 'bx-buttons-grid' });
const $baseSelect = CE('select', { class: 'bx-full-width' },
CE('option', { value: '' }, '---'),
CE('option', { value: 'false', _dataset: { label: '🚫' } }, isControllerFriendly ? '🚫' : t('off')),
);
const $baseButtonSelect = $baseSelect.cloneNode(true);
const $baseStickSelect = $baseSelect.cloneNode(true);
const onButtonChanged = (e: Event) => {
// Update preset
if (!(e as any).ignoreOnChange) {
this.updatePreset();
}
};
const boundUpdatePreset = this.updatePreset.bind(this);
for (const gamepadKey of this.BUTTONS_ORDER) {
if (gamepadKey === GamepadKey.SHARE) {
continue;
}
const name = GamepadKeyName[gamepadKey][isControllerFriendly ? 1 : 0];
const $target = (gamepadKey === GamepadKey.LS || gamepadKey === GamepadKey.RS) ? $baseStickSelect : $baseButtonSelect;
$target.appendChild(CE('option', {
value: gamepadKey,
_dataset: { label: GamepadKeyName[gamepadKey][1] },
}, name));
}
for (const gamepadKey of this.BUTTONS_ORDER) {
const [buttonName, buttonPrompt] = GamepadKeyName[gamepadKey];
const $sourceSelect = (gamepadKey === GamepadKey.LS || gamepadKey === GamepadKey.RS) ? $baseStickSelect : $baseButtonSelect;
// Remove current button from selection
const $clonedSelect = $sourceSelect.cloneNode(true) as HTMLSelectElement;
$clonedSelect.querySelector(`option[value="${gamepadKey}"]`)?.remove();
const $select = BxSelectElement.create($clonedSelect);
$select.dataset.index = gamepadKey.toString();
$select.addEventListener('input', onButtonChanged);
this.selectsMap[gamepadKey] = $select;
this.selectsOrder.push(gamepadKey);
const $row = CE('div', {
class: 'bx-controller-key-row',
_nearby: { orientation: 'horizontal' },
},
CE('label', { title: buttonName }, buttonPrompt),
$select,
);
$rows.append($row);
}
// Map nearby elenemts for controller-friendly UI
if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
for (let i = 0; i < this.selectsOrder.length; i++) {
const $select = this.selectsMap[this.selectsOrder[i] as unknown as GamepadKey] as NavigationElement;
const directions = {
[NavigationDirection.UP]: i - 2,
[NavigationDirection.DOWN]: i + 2,
[NavigationDirection.LEFT]: i - 1,
[NavigationDirection.RIGHT]: i + 1,
};
for (const dir in directions) {
const idx = directions[dir as unknown as NavigationDirection];
if (typeof this.selectsOrder[idx] === 'undefined') {
continue;
}
const $targetSelect = this.selectsMap[this.selectsOrder[idx] as unknown as GamepadKey];
setNearby($select, {
[dir]: $targetSelect,
});
}
}
}
const params: DualNumberStepperParams = {
min: 0,
minDiff: 1,
max: 100,
steps: 1,
};
this.$content = CE('div', { class: 'bx-controller-customizations-container' },
// Detect button
this.$btnDetect = createButton({
label: t('detect-controller-button'),
classes: ['bx-btn-detect'],
style: ButtonStyle.NORMAL_CASE | ButtonStyle.FOCUSABLE | ButtonStyle.FULL_WIDTH,
onClick: () => {
this.startDetectingButton();
},
}),
// Mapping
$rows,
// Vibration intensity
createSettingRow(t('vibration-intensity'),
this.$vibrationIntensity = BxNumberStepper.create('controller_vibration_intensity', 50, 0, 100, {
steps: 10,
suffix: '%',
exactTicks: 20,
customTextValue: (value: any) => {
value = parseInt(value);
return value === 0 ? t('off') : value + '%';
},
}, boundUpdatePreset),
),
// Range settings
createSettingRow(t('left-trigger-range'),
this.$leftTriggerRange = BxDualNumberStepper.create('left-trigger-range', this.BLANK_PRESET_DATA.settings.leftTriggerRange, params, boundUpdatePreset),
),
createSettingRow(t('right-trigger-range'),
this.$rightTriggerRange = BxDualNumberStepper.create('right-trigger-range', this.BLANK_PRESET_DATA.settings.rightTriggerRange, params, boundUpdatePreset),
),
createSettingRow(t('left-stick-deadzone'),
this.$leftStickDeadzone = BxDualNumberStepper.create('left-stick-deadzone', this.BLANK_PRESET_DATA.settings.leftStickDeadzone, params, boundUpdatePreset),
),
createSettingRow(t('right-stick-deadzone'),
this.$rightStickDeadzone = BxDualNumberStepper.create('right-stick-deadzone', this.BLANK_PRESET_DATA.settings.rightStickDeadzone, params, boundUpdatePreset),
),
);
}
private startDetectingButton() {
this.isDetectingButton = true;
const { $btnDetect } = this;
$btnDetect.classList.add('bx-monospaced', 'bx-blink-me');
$btnDetect.disabled = true;
let count = 4;
$btnDetect.textContent = `[${count}] ${t('press-any-button')}`;
this.detectIntervalId = window.setInterval(() => {
count -= 1;
if (count === 0) {
this.stopDetectingButton();
// Re-focus the Detect button
$btnDetect.focus();
return;
}
$btnDetect.textContent = `[${count}] ${t('press-any-button')}`;
}, 1000);
}
private stopDetectingButton() {
const { $btnDetect } = this;
$btnDetect.classList.remove('bx-monospaced', 'bx-blink-me');
$btnDetect.textContent = t('detect-controller-button');
$btnDetect.disabled = false;
this.isDetectingButton = false;
this.detectIntervalId && window.clearInterval(this.detectIntervalId);
this.detectIntervalId = null;
}
async onBeforeMount() {
this.stopDetectingButton();
super.onBeforeMount(...arguments);
}
onBeforeUnmount(): void {
this.stopDetectingButton();
StreamSettings.refreshControllerSettings();
super.onBeforeUnmount();
}
handleGamepad(button: GamepadKey): boolean {
if (!this.isDetectingButton) {
return super.handleGamepad(button);
}
if (button in this.BUTTONS_ORDER) {
this.stopDetectingButton();
const $select = this.selectsMap[button]!;
const $label = $select.previousElementSibling!;
$label.addEventListener('animationend', () => {
$label.classList.remove('bx-horizontal-shaking');
}, { once: true });
$label.classList.add('bx-horizontal-shaking');
// Focus select
if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
this.dialogManager.focus($select);
}
}
return true;
}
protected switchPreset(id: number): void {
const preset = this.allPresets.data[id];
if (!preset) {
this.currentPresetId = 0;
return;
}
const {
$btnDetect,
$vibrationIntensity,
$leftStickDeadzone,
$rightStickDeadzone,
$leftTriggerRange,
$rightTriggerRange,
selectsMap,
} = this;
const presetData = preset.data;
this.currentPresetId = id;
const isDefaultPreset = id <= 0;
this.updateButtonStates();
// Show/hide Detect button
$btnDetect.classList.toggle('bx-gone', isDefaultPreset);
// Set mappings
let buttonIndex: unknown;
for (buttonIndex in selectsMap) {
buttonIndex = buttonIndex as GamepadKey;
const $select = selectsMap[buttonIndex as GamepadKey];
if (!$select) {
continue;
}
const mappedButton = presetData.mapping[buttonIndex as GamepadKey];
$select.value = typeof mappedButton === 'undefined' ? '' : mappedButton.toString();
$select.disabled = isDefaultPreset;
BxEvent.dispatch($select, 'input', {
ignoreOnChange: true,
manualTrigger: true,
});
}
// Add missing settings
presetData.settings = Object.assign(this.BLANK_PRESET_DATA.settings, presetData.settings);
// Vibration intensity
$vibrationIntensity.value = presetData.settings.vibrationIntensity.toString();
$vibrationIntensity.dataset.disabled = isDefaultPreset.toString();
// Set extra settings
$leftStickDeadzone.dataset.disabled = $rightStickDeadzone.dataset.disabled = $leftTriggerRange.dataset.disabled = $rightTriggerRange.dataset.disabled = isDefaultPreset.toString();
$leftStickDeadzone.setValue(presetData.settings.leftStickDeadzone);
$rightStickDeadzone.setValue(presetData.settings.rightStickDeadzone);
$leftTriggerRange.setValue(presetData.settings.leftTriggerRange);
$rightTriggerRange.setValue(presetData.settings.rightTriggerRange);
}
private updatePreset() {
const newData: ControllerCustomizationPresetData = deepClone(this.BLANK_PRESET_DATA);
// Set mappings
let gamepadKey: unknown;
for (gamepadKey in this.selectsMap) {
const $select = this.selectsMap[gamepadKey as GamepadKey]!;
const value = $select.value;
if (!value) {
continue;
}
const mapTo = (value === 'false') ? false : parseInt(value);
newData.mapping[gamepadKey as GamepadKey] = mapTo;
}
// Set extra settings
Object.assign(newData.settings, {
vibrationIntensity: parseInt(this.$vibrationIntensity.value),
leftStickDeadzone: this.$leftStickDeadzone.getValue(),
rightStickDeadzone: this.$rightStickDeadzone.getValue(),
leftTriggerRange: this.$leftTriggerRange.getValue(),
rightTriggerRange: this.$rightTriggerRange.getValue(),
} satisfies typeof newData.settings);
// Update preset
const preset = this.allPresets.data[this.currentPresetId!];
preset.data = newData;
this.presetsDb.updatePreset(preset);
}
}

View File

@@ -2,12 +2,10 @@ import { t } from "@/utils/translation";
import { BaseProfileManagerDialog } from "./base-profile-manager-dialog";
import { CE } from "@/utils/html";
import { GamepadKey, GamepadKeyName } from "@/enums/gamepad";
import { PrefKey } from "@/enums/pref-keys";
import { PrompFont } from "@/enums/prompt-font";
import { ShortcutAction } from "@/enums/shortcut-actions";
import { deepClone } from "@/utils/global";
import { setNearby } from "@/utils/navigation-utils";
import { getPref } from "@/utils/settings-storages/global-settings-storage";
import { BxSelectElement } from "@/web-components/bx-select";
import type { ControllerShortcutPresetData, ControllerShortcutPresetRecord } from "@/types/presets";
import { ControllerShortcutsTable } from "@/utils/local-db/controller-shortcuts-table";
@@ -21,7 +19,7 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
// private readonly LOG_TAG = 'ControllerShortcutsManagerDialog';
protected $content: HTMLElement;
private selectActions: Partial<Record<GamepadKey, [HTMLSelectElement, HTMLSelectElement | null]>> = {};
private selectActions: Partial<Record<GamepadKey, HTMLSelectElement>> = {};
protected readonly BLANK_PRESET_DATA = {
mapping: {},
@@ -39,19 +37,18 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
constructor(title: string) {
super(title, ControllerShortcutsTable.getInstance());
const PREF_CONTROLLER_FRIENDLY_UI = getPref(PrefKey.UI_CONTROLLER_FRIENDLY);
const $baseSelect = CE('select', {
class: 'bx-full-width',
autocomplete: 'off',
}, CE('option', { value: '' }, '---'));
// Read actions from localStorage
// ControllerShortcut.ACTIONS = ControllerShortcut.getActionsFromStorage();
const $baseSelect = CE<HTMLSelectElement>('select', { autocomplete: 'off' }, CE('option', { value: '' }, '---'));
for (const groupLabel in SHORTCUT_ACTIONS) {
const items = SHORTCUT_ACTIONS[groupLabel];
if (!items) {
continue;
}
const $optGroup = CE<HTMLOptGroupElement>('optgroup', { label: groupLabel });
const $optGroup = CE('optgroup', { label: groupLabel });
for (const action in items) {
const crumbs = items[action as keyof typeof items];
if (!crumbs) {
@@ -59,7 +56,7 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
}
const label = crumbs.join(' ');
const $option = CE<HTMLOptionElement>('option', { value: action }, label);
const $option = CE('option', { value: action }, label);
$optGroup.appendChild($option);
}
@@ -71,23 +68,6 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
});
const onActionChanged = (e: Event) => {
const $target = e.target as HTMLSelectElement;
// const profile = $selectProfile.value;
// const button: unknown = $target.dataset.button;
const action = $target.value as ShortcutAction;
if (!PREF_CONTROLLER_FRIENDLY_UI) {
const $fakeSelect = $target.previousElementSibling! as HTMLSelectElement;
let fakeText = '---';
if (action) {
const $selectedOption = $target.options[$target.selectedIndex];
const $optGroup = $selectedOption.parentElement as HTMLOptGroupElement;
fakeText = $optGroup.label + ' ' + $selectedOption.text;
}
($fakeSelect.firstElementChild as HTMLOptionElement).text = fakeText;
}
// Update preset
if (!(e as any).ignoreOnChange) {
this.updatePreset();
@@ -110,30 +90,18 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
},
});
const $label = CE('label', { class: 'bx-prompt' }, `${PrompFont.HOME}${prompt}`);
const $div = CE('div', { class: 'bx-shortcut-actions' });
let $fakeSelect: HTMLSelectElement | null = null;
if (!PREF_CONTROLLER_FRIENDLY_UI) {
$fakeSelect = CE<HTMLSelectElement>('select', { autocomplete: 'off' },
CE('option', {}, '---'),
);
$div.appendChild($fakeSelect);
}
const $select = BxSelectElement.create($baseSelect.cloneNode(true) as HTMLSelectElement);
$select.dataset.button = button.toString();
$select.classList.add('bx-full-width');
$select.addEventListener('input', onActionChanged);
this.selectActions[button] = [$select, $fakeSelect];
this.selectActions[button] = $select;
$div.appendChild($select);
setNearby($row, {
focus: $select,
});
$row.append($label, $div);
$row.append($label, $select);
fragment.appendChild($row);
}
@@ -156,10 +124,9 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
// Reset selects' values
let button: unknown;
for (button in this.selectActions) {
const [$select, $fakeSelect] = this.selectActions[button as GamepadKey]!;
const $select = this.selectActions[button as GamepadKey]!;
$select.value = actions.mapping[button as GamepadKey] || '';
$select.disabled = isDefaultPreset;
$fakeSelect && ($fakeSelect.disabled = isDefaultPreset);
BxEvent.dispatch($select, 'input', {
ignoreOnChange: true,
@@ -175,8 +142,7 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
let button: unknown;
for (button in this.selectActions) {
const [$select, _] = this.selectActions[button as GamepadKey]!;
const $select = this.selectActions[button as GamepadKey]!;
const action = $select.value;
if (!action) {
continue;
@@ -185,10 +151,13 @@ export class ControllerShortcutsManagerDialog extends BaseProfileManagerDialog<C
newData.mapping[button as GamepadKey] = action as ShortcutAction;
}
const preset = this.allPresets.data[this.currentPresetId];
const preset = this.allPresets.data[this.currentPresetId!];
preset.data = newData;
this.presetsDb.updatePreset(preset);
}
onBeforeUnmount() {
StreamSettings.refreshControllerSettings();
super.onBeforeUnmount();
}
}

View File

@@ -37,7 +37,7 @@ export class KeyboardShortcutsManagerDialog extends BaseProfileManagerDialog<Key
continue;
}
const $fieldSet = CE<HTMLFieldSetElement>('fieldset', {}, CE('legend', {}, groupLabel));
const $fieldSet = CE('fieldset', {}, CE('legend', {}, groupLabel));
for (const action in items) {
const crumbs = items[action as keyof typeof items];
if (!crumbs) {
@@ -144,15 +144,19 @@ export class KeyboardShortcutsManagerDialog extends BaseProfileManagerDialog<Key
}
}
const oldPreset = this.allPresets.data[this.currentPresetId];
const oldPreset = this.allPresets.data[this.currentPresetId!];
const newPreset = {
id: this.currentPresetId,
id: this.currentPresetId!,
name: oldPreset.name,
data: presetData,
};
this.presetsDb.updatePreset(newPreset);
this.allPresets.data[this.currentPresetId] = newPreset;
this.allPresets.data[this.currentPresetId!] = newPreset;
}
onBeforeUnmount(): void {
StreamSettings.refreshKeyboardShortcuts();
super.onBeforeUnmount();
}
}

View File

@@ -121,9 +121,7 @@ export class MkbMappingManagerDialog extends BaseProfileManagerDialog<MkbPresetR
const $keyRow = CE('div', {
class: 'bx-mkb-key-row',
_nearby: {
orientation: 'horizontal',
},
_nearby: { orientation: 'horizontal' },
},
CE('label', { title: buttonName }, buttonPrompt),
$fragment,
@@ -244,15 +242,19 @@ export class MkbMappingManagerDialog extends BaseProfileManagerDialog<MkbPresetR
mouse.sensitivityY = parseInt(this.$mouseSensitivityY.value);
mouse.deadzoneCounterweight = parseInt(this.$mouseDeadzone.value);
const oldPreset = this.allPresets.data[this.currentPresetId];
const oldPreset = this.allPresets.data[this.currentPresetId!];
const newPreset = {
id: this.currentPresetId,
id: this.currentPresetId!,
name: oldPreset.name,
data: presetData,
};
this.presetsDb.updatePreset(newPreset);
this.allPresets.data[this.currentPresetId] = newPreset;
this.allPresets.data[this.currentPresetId!] = newPreset;
}
onBeforeUnmount() {
StreamSettings.refreshMkbSettings();
super.onBeforeUnmount();
}
}

View File

@@ -37,10 +37,10 @@ export class RemotePlayDialog extends NavigationDialog {
const $settingNote = CE('p', {});
const currentResolution = getPref(PrefKey.REMOTE_PLAY_STREAM_RESOLUTION);
let $resolutions : HTMLSelectElement | NavigationElement = CE<HTMLSelectElement>('select', {},
let $resolutions : HTMLSelectElement | NavigationElement = CE('select', {},
CE('option', { value: StreamResolution.DIM_720P }, '720p'),
CE('option', { value: StreamResolution.DIM_1080P }, '1080p'),
// CE('option', { value: StreamResolution.DIM_1080P_HQ }, `1080p (HQ) ${t('experimental')}`),
// CE('option', { value: StreamResolution.DIM_1080P_HQ }, `1080p (HQ)`),
);
$resolutions = BxSelectElement.create($resolutions as HTMLSelectElement);

View File

@@ -1,7 +1,7 @@
import { isFullVersion } from "@macros/build" with { type: "macro" };
import { limitVideoPlayerFps, onChangeVideoPlayerType, updateVideoPlayer } from "@/modules/stream/stream-settings-utils";
import { ButtonStyle, CE, createButton, createSettingRow, createSvgIcon, escapeCssSelector, type BxButtonOptions } from "@/utils/html";
import { ButtonStyle, calculateSelectBoxes, CE, createButton, createSettingRow, createSvgIcon, escapeCssSelector, type BxButtonOptions } from "@/utils/html";
import { NavigationDialog, NavigationDirection } from "./navigation-dialog";
import { SoundShortcut } from "@/modules/shortcuts/sound-shortcut";
import { StreamStats } from "@/modules/stream/stream-stats";
@@ -37,7 +37,7 @@ type SettingTabSectionItem = Partial<{
pref: PrefKey;
multiLines: boolean;
label: string;
note: string | (() => HTMLElement);
note: string | (() => HTMLElement) | HTMLElement;
experimental: string;
content: HTMLElement | (() => HTMLElement);
options: { [key: string]: string };
@@ -56,7 +56,7 @@ type SettingTabSection = {
| 'stats';
label?: string;
unsupported?: boolean;
unsupportedNote?: string | Text | null;
unsupportedNote?: HTMLElement | string | Text | null;
helpUrl?: string;
content?: HTMLElement;
lazyContent?: boolean | (() => HTMLElement);
@@ -86,7 +86,7 @@ export class SettingsDialog extends NavigationDialog {
private $btnReload!: HTMLElement;
private $btnGlobalReload!: HTMLButtonElement;
private $noteGlobalReload!: HTMLElement;
private $btnSuggestion!: HTMLButtonElement;
private $btnSuggestion!: HTMLDivElement;
private renderFullSettings: boolean;
@@ -106,7 +106,7 @@ export class SettingsDialog extends NavigationDialog {
items: [
// Top buttons
($parent) => {
const PREF_LATEST_VERSION = getPref<VersionLatest>(PrefKey.VERSION_LATEST);
const PREF_LATEST_VERSION = getPref(PrefKey.VERSION_LATEST);
const topButtons = [];
// "New version available" button
@@ -322,7 +322,7 @@ export class SettingsDialog extends NavigationDialog {
onCreated: (setting, $control) => {
const defaultUserAgent = window.navigator.orgUserAgent || window.navigator.userAgent;
const $inpCustomUserAgent = CE<HTMLInputElement>('input', {
const $inpCustomUserAgent = CE('input', {
type: 'text',
placeholder: defaultUserAgent,
autocomplete: 'off',
@@ -494,20 +494,7 @@ export class SettingsDialog extends NavigationDialog {
}],
}];
private readonly TAB_CONTROLLER_ITEMS: Array<SettingTabSection | HTMLElement | false> = [isFullVersion() && STATES.browser.capabilities.deviceVibration && {
group: 'device',
label: t('device'),
items: [{
pref: PrefKey.DEVICE_VIBRATION_MODE,
multiLines: true,
unsupported: !STATES.browser.capabilities.deviceVibration,
onChange: () => StreamSettings.refreshControllerSettings(),
}, {
pref: PrefKey.DEVICE_VIBRATION_INTENSITY,
unsupported: !STATES.browser.capabilities.deviceVibration,
onChange: () => StreamSettings.refreshControllerSettings(),
}],
}, {
private readonly TAB_CONTROLLER_ITEMS: Array<SettingTabSection | HTMLElement | false> = [{
group: 'controller',
label: t('controller'),
helpUrl: 'https://better-xcloud.github.io/ingame-features/#controller',
@@ -576,6 +563,19 @@ export class SettingsDialog extends NavigationDialog {
});
},
}],
}, isFullVersion() && STATES.browser.capabilities.deviceVibration && {
group: 'device',
label: t('device'),
items: [{
pref: PrefKey.DEVICE_VIBRATION_MODE,
multiLines: true,
unsupported: !STATES.browser.capabilities.deviceVibration,
onChange: () => StreamSettings.refreshControllerSettings(),
}, {
pref: PrefKey.DEVICE_VIBRATION_INTENSITY,
unsupported: !STATES.browser.capabilities.deviceVibration,
onChange: () => StreamSettings.refreshControllerSettings(),
}],
}];
private readonly TAB_MKB_ITEMS: (() => Array<SettingTabSection | false>) = () => [
@@ -764,9 +764,7 @@ export class SettingsDialog extends NavigationDialog {
$child.classList.remove('bx-gone');
// Calculate size of controller-friendly select boxes
if (getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
this.dialogManager.calculateSelectBoxes($child as HTMLElement);
}
calculateSelectBoxes($child as HTMLElement);
} else {
// Hide tab content
$child.classList.add('bx-gone');
@@ -804,7 +802,7 @@ export class SettingsDialog extends NavigationDialog {
}
private renderServerSetting(setting: SettingTabSectionItem): HTMLElement {
let selectedValue = getPref<ServerRegionName>(PrefKey.SERVER_REGION);
let selectedValue = getPref(PrefKey.SERVER_REGION);
const continents: Record<ServerContinent, {
label: string,
@@ -830,9 +828,8 @@ export class SettingsDialog extends NavigationDialog {
},
};
const $control = CE<HTMLSelectElement>('select', {
const $control = CE('select', {
id: `bx_setting_${escapeCssSelector(setting.pref!)}`,
title: setting.label,
tabindex: 0,
});
$control.name = $control.id;
@@ -859,7 +856,7 @@ export class SettingsDialog extends NavigationDialog {
setting.options[value] = label;
const $option = CE<HTMLOptionElement>('option', { value }, label);
const $option = CE('option', { value }, label);
const continent = continents[region.contintent];
if (!continent.children) {
continent.children = [];

View File

@@ -1,14 +1,16 @@
import { BxEvent } from "@/utils/bx-event";
import { getUniqueGamepadNames } from "@/utils/gamepad";
import { CE, removeChildElements, createButton, ButtonStyle, createSettingRow, renderPresetsList } from "@/utils/html";
import { CE, removeChildElements, createButton, ButtonStyle, createSettingRow, renderPresetsList, calculateSelectBoxes } from "@/utils/html";
import { t } from "@/utils/translation";
import { BxSelectElement } from "@/web-components/bx-select";
import { ControllerShortcutsManagerDialog } from "../profile-manger/controller-shortcuts-manager-dialog";
import type { SettingsDialog } from "../settings-dialog";
import { ControllerShortcutsTable } from "@/utils/local-db/controller-shortcuts-table";
import { BxNumberStepper } from "@/web-components/bx-number-stepper";
import { ControllerSettingsTable } from "@/utils/local-db/controller-settings-table";
import { StreamSettings } from "@/utils/stream-settings";
import { ControllerCustomizationsTable } from "@/utils/local-db/controller-customizations-table";
import { ControllerCustomizationsManagerDialog } from "../profile-manger/controller-customizations-manager-dialog";
import { BxIcon } from "@/utils/bx-icon";
export class ControllerExtraSettings extends HTMLElement {
currentControllerId!: string;
@@ -16,7 +18,7 @@ export class ControllerExtraSettings extends HTMLElement {
$selectControllers!: BxSelectElement;
$selectShortcuts!: BxSelectElement;
$vibrationIntensity!: BxNumberStepper;
$selectCustomization!: BxSelectElement;
updateLayout!: () => void;
switchController!: (id: string) => void;
@@ -24,16 +26,17 @@ export class ControllerExtraSettings extends HTMLElement {
saveSettings!: () => void;
static renderSettings(this: SettingsDialog): HTMLElement {
const $container = CE<ControllerExtraSettings>('label', {
const $container = CE('label', {
class: 'bx-settings-row bx-controller-extra-settings',
});
}) as unknown as ControllerExtraSettings;
$container.updateLayout = ControllerExtraSettings.updateLayout.bind($container);
$container.switchController = ControllerExtraSettings.switchController.bind($container);
$container.getCurrentControllerId = ControllerExtraSettings.getCurrentControllerId.bind($container);
$container.saveSettings = ControllerExtraSettings.saveSettings.bind($container);
const $selectControllers = BxSelectElement.create(CE<HTMLSelectElement>('select', {
const $selectControllers = BxSelectElement.create(CE('select', {
class: 'bx-full-width',
autocomplete: 'off',
_on: {
input: (e: Event) => {
@@ -41,24 +44,16 @@ export class ControllerExtraSettings extends HTMLElement {
},
},
}));
$selectControllers.classList.add('bx-full-width');
const $selectShortcuts = BxSelectElement.create(CE<HTMLSelectElement>('select', {
const $selectShortcuts = BxSelectElement.create(CE('select', {
autocomplete: 'off',
_on: {
input: $container.saveSettings,
},
_on: { input: $container.saveSettings },
}));
const $vibrationIntensity = BxNumberStepper.create('controller_vibration_intensity', 50, 0, 100, {
steps: 10,
suffix: '%',
exactTicks: 20,
customTextValue: (value: any) => {
value = parseInt(value);
return value === 0 ? t('off') : value + '%';
},
}, $container.saveSettings);
const $selectCustomization = BxSelectElement.create(CE('select', {
autocomplete: 'off',
_on: { input: $container.saveSettings },
}));
$container.append(
CE('span', {}, t('no-controllers-connected')),
@@ -67,17 +62,16 @@ export class ControllerExtraSettings extends HTMLElement {
CE('div', { class: 'bx-sub-content-box' },
createSettingRow(
t('controller-shortcuts-in-game'),
t('in-game-controller-shortcuts'),
CE('div', {
class: 'bx-preset-row',
_nearby: {
orientation: 'horizontal',
},
_nearby: { orientation: 'horizontal' },
},
$selectShortcuts,
createButton({
label: t('manage'),
style: ButtonStyle.FOCUSABLE,
title: t('manage'),
icon: BxIcon.MANAGE,
style: ButtonStyle.FOCUSABLE | ButtonStyle.PRIMARY | ButtonStyle.AUTO_HEIGHT,
onClick: () => ControllerShortcutsManagerDialog.getInstance().show({
id: parseInt($container.$selectShortcuts.value),
}),
@@ -87,8 +81,25 @@ export class ControllerExtraSettings extends HTMLElement {
),
createSettingRow(
t('vibration-intensity'),
$vibrationIntensity,
t('in-game-controller-customization'),
CE('div', {
class: 'bx-preset-row',
_nearby: { orientation: 'horizontal' },
},
$selectCustomization,
createButton({
title: t('manage'),
icon: BxIcon.MANAGE,
style: ButtonStyle.FOCUSABLE | ButtonStyle.PRIMARY | ButtonStyle.AUTO_HEIGHT,
onClick: () => ControllerCustomizationsManagerDialog.getInstance().show({
id: $container.$selectCustomization.value ? parseInt($container.$selectCustomization.value) : null,
}),
}),
),
{
multiLines: true,
$note: CE('div', { class: 'bx-settings-dialog-note' }, 'ⓘ ' + t('slightly-increase-input-latency')),
},
),
),
),
@@ -96,7 +107,7 @@ export class ControllerExtraSettings extends HTMLElement {
$container.$selectControllers = $selectControllers;
$container.$selectShortcuts = $selectShortcuts;
$container.$vibrationIntensity = $vibrationIntensity;
$container.$selectCustomization = $selectCustomization;
$container.updateLayout();
@@ -128,7 +139,7 @@ export class ControllerExtraSettings extends HTMLElement {
// Render controller list
for (const name of this.controllerIds) {
const $option = CE<HTMLOptionElement>('option', { value: name }, name);
const $option = CE('option', { value: name }, name);
$fragment.appendChild($option);
}
@@ -138,12 +149,17 @@ export class ControllerExtraSettings extends HTMLElement {
const allShortcutPresets = await ControllerShortcutsTable.getInstance().getPresets();
renderPresetsList(this.$selectShortcuts, allShortcutPresets, null, { addOffValue: true });
// Render customization presets
const allCustomizationPresets = await ControllerCustomizationsTable.getInstance().getPresets();
renderPresetsList(this.$selectCustomization, allCustomizationPresets, null, { addOffValue: true });
for (const name of this.controllerIds) {
const $option = CE<HTMLOptionElement>('option', { value: name }, name);
const $option = CE('option', { value: name }, name);
$fragment.appendChild($option);
}
BxEvent.dispatch(this.$selectControllers, 'input');
calculateSelectBoxes(this);
}
private static async switchController(this: ControllerExtraSettings, id: string) {
@@ -156,7 +172,7 @@ export class ControllerExtraSettings extends HTMLElement {
// Update UI
this.$selectShortcuts.value = controllerSettings.shortcutPresetId.toString();
this.$vibrationIntensity.value = controllerSettings.vibrationIntensity.toString();
this.$selectCustomization.value = controllerSettings.customizationPresetId.toString();
}
private static getCurrentControllerId(this: ControllerExtraSettings) {
@@ -190,7 +206,7 @@ export class ControllerExtraSettings extends HTMLElement {
id: this.currentControllerId,
data: {
shortcutPresetId: parseInt(this.$selectShortcuts.value),
vibrationIntensity: parseInt(this.$vibrationIntensity.value),
customizationPresetId: parseInt(this.$selectCustomization.value),
},
};

View File

@@ -12,6 +12,7 @@ import { KeyboardShortcutsTable } from "@/utils/local-db/keyboard-shortcuts-tabl
import { SettingElement } from "@/utils/setting-element";
import { STORAGE } from "@/utils/global";
import { EmulatedMkbHandler } from "@/modules/mkb/mkb-handler";
import { BxIcon } from "@/utils/bx-icon";
export class MkbExtraSettings extends HTMLElement {
private $mappingPresets!: BxSelectElement;
@@ -28,14 +29,14 @@ export class MkbExtraSettings extends HTMLElement {
$container.saveMkbSettings = MkbExtraSettings.saveMkbSettings.bind($container);
$container.saveShortcutsSettings = MkbExtraSettings.saveShortcutsSettings.bind($container);
const $mappingPresets = BxSelectElement.create(CE<HTMLSelectElement>('select', {
const $mappingPresets = BxSelectElement.create(CE('select', {
autocomplete: 'off',
_on: {
input: $container.saveMkbSettings,
},
}));
const $shortcutsPresets = BxSelectElement.create(CE<HTMLSelectElement>('select', {
const $shortcutsPresets = BxSelectElement.create(CE('select', {
autocomplete: 'off',
_on: {
input: $container.saveShortcutsSettings,
@@ -54,8 +55,9 @@ export class MkbExtraSettings extends HTMLElement {
},
$mappingPresets,
createButton({
label: t('manage'),
style: ButtonStyle.FOCUSABLE,
title: t('manage'),
icon: BxIcon.MANAGE,
style: ButtonStyle.FOCUSABLE | ButtonStyle.PRIMARY | ButtonStyle.AUTO_HEIGHT,
onClick: () => MkbMappingManagerDialog.getInstance().show({
id: parseInt($container.$mappingPresets.value),
}),
@@ -73,7 +75,7 @@ export class MkbExtraSettings extends HTMLElement {
] : []),
createSettingRow(
t('keyboard-shortcuts-in-game'),
t('in-game-keyboard-shortcuts'),
CE('div', {
class: 'bx-preset-row',
_nearby: {
@@ -82,8 +84,9 @@ export class MkbExtraSettings extends HTMLElement {
},
$shortcutsPresets,
createButton({
label: t('manage'),
style: ButtonStyle.FOCUSABLE,
title: t('manage'),
icon: BxIcon.MANAGE,
style: ButtonStyle.FOCUSABLE | ButtonStyle.PRIMARY | ButtonStyle.AUTO_HEIGHT,
onClick: () => KeyboardShortcutsManagerDialog.getInstance().show({
id: parseInt($container.$shortcutsPresets.value),
}),
@@ -108,23 +111,23 @@ export class MkbExtraSettings extends HTMLElement {
private static async updateLayout(this: MkbExtraSettings) {
// Render shortcut presets
const mappingPresets = await MkbMappingPresetsTable.getInstance().getPresets();
renderPresetsList(this.$mappingPresets, mappingPresets, getPref<MkbPresetId>(PrefKey.MKB_P1_MAPPING_PRESET_ID));
renderPresetsList(this.$mappingPresets, mappingPresets, getPref(PrefKey.MKB_P1_MAPPING_PRESET_ID));
// Render shortcut presets
const shortcutsPresets = await KeyboardShortcutsTable.getInstance().getPresets();
renderPresetsList(this.$shortcutsPresets, shortcutsPresets, getPref<MkbPresetId>(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID), { addOffValue: true });
renderPresetsList(this.$shortcutsPresets, shortcutsPresets, getPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID), { addOffValue: true });
}
private static async saveMkbSettings(this: MkbExtraSettings) {
const presetId = parseInt(this.$mappingPresets.value);
setPref<MkbPresetId>(PrefKey.MKB_P1_MAPPING_PRESET_ID, presetId);
setPref(PrefKey.MKB_P1_MAPPING_PRESET_ID, presetId);
StreamSettings.refreshMkbSettings();
}
private static async saveShortcutsSettings(this: MkbExtraSettings) {
const presetId = parseInt(this.$shortcutsPresets.value);
setPref<KeyboardShortcutsPresetId>(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, presetId);
setPref(PrefKey.KEYBOARD_SHORTCUTS_IN_GAME_PRESET_ID, presetId);
StreamSettings.refreshKeyboardShortcuts();
}

View File

@@ -92,7 +92,7 @@ export class SuggestionsSetting {
// Start rendering
const $suggestedSettings = CE('div', { class: 'bx-suggest-wrapper' });
const $select = CE<HTMLSelectElement>('select', {},
const $select = CE('select', {},
hasRecommendedSettings && CE('option', { value: 'recommended' }, t('recommended')),
!hasRecommendedSettings && CE('option', { value: 'highest' }, t('highest-quality')),
CE('option', { value: 'default' }, t('default')),
@@ -126,6 +126,7 @@ export class SuggestionsSetting {
suggestedValue = settings[prefKey];
}
// @ts-ignore
const currentValue = getPref(prefKey, false);
const currentValueText = STORAGE.Global.getValueText(prefKey, currentValue);
const isSameValue = currentValue === suggestedValue;
@@ -233,7 +234,7 @@ export class SuggestionsSetting {
orientation: 'vertical',
}
},
BxSelectElement.create($select, true),
BxSelectElement.create($select),
$suggestedSettings,
$btnApply,

View File

@@ -115,7 +115,7 @@ export class GuideMenu {
});
// Set TV tag
if (STATES.userAgent.isTv || getPref<UiLayout>(PrefKey.UI_LAYOUT) === UiLayout.TV) {
if (STATES.userAgent.isTv || getPref(PrefKey.UI_LAYOUT) === UiLayout.TV) {
document.body.dataset.bxMediaType = 'tv';
}

View File

@@ -50,7 +50,7 @@ export class HeaderSection {
return;
}
const PREF_LATEST_VERSION = getPref<VersionLatest>(PrefKey.VERSION_LATEST);
const PREF_LATEST_VERSION = getPref(PrefKey.VERSION_LATEST);
// Setup Settings button
const $btnSettings = this.$btnSettings;

View File

@@ -9,7 +9,7 @@ export function localRedirect(path: string) {
return;
}
const $anchor = CE<HTMLAnchorElement>('a', {
const $anchor = CE('a', {
href: url,
class: 'bx-hidden bx-offscreen',
}, '');