mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-06 07:37:19 +02:00
Add "Controller-friendly UI" option
This commit is contained in:
parent
66120d6970
commit
394dc68ece
55
dist/better-xcloud.user.js
vendored
55
dist/better-xcloud.user.js
vendored
File diff suppressed because one or more lines are too long
@ -83,24 +83,24 @@
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&.bx-focusable {
|
||||
position: relative;
|
||||
.bx-focusable {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
border: 2px solid transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
&::after {
|
||||
border: 2px solid transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&:focus::after {
|
||||
content: '';
|
||||
border-color: white;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
&:focus::after {
|
||||
content: '';
|
||||
border-color: white;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,11 +100,16 @@
|
||||
position: relative;
|
||||
|
||||
label {
|
||||
flex: 1;
|
||||
align-self: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bx-setting-control {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
|
||||
&:hover, &:focus-within {
|
||||
background-color: #242424;
|
||||
|
@ -1,19 +1,25 @@
|
||||
.bx-select {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
select {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: inline-block;
|
||||
> div, button.bx-select-value {
|
||||
min-width: 110px;
|
||||
text-align: center;
|
||||
margin: 0 10px;
|
||||
margin: 0 8px;
|
||||
line-height: 24px;
|
||||
vertical-align: middle;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
border-radius: 4px;
|
||||
padding: 2px 4px;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: inline-block;
|
||||
|
||||
input {
|
||||
display: inline-block;
|
||||
@ -22,23 +28,55 @@
|
||||
|
||||
label {
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
button.bx-select-value {
|
||||
border: none;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 0 4px;
|
||||
accent-color: var(--bx-primary-button-color);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
input {
|
||||
accent-color: var(--bx-danger-button-color);
|
||||
}
|
||||
|
||||
&::after {
|
||||
border-color: #4d4d4d !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.bx-button {
|
||||
border: none;
|
||||
height: 30px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
padding: 0;
|
||||
line-height: 30px;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
font-family: var(--bx-monospaced-font);
|
||||
|
||||
&.bx-inactive {
|
||||
pointer-events: none;
|
||||
opacity: 0.2;
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
span {
|
||||
|
@ -11,7 +11,6 @@ import { SoundShortcut } from "../shortcuts/shortcut-sound";
|
||||
import { TouchController } from "../touch-controller";
|
||||
import { VibrationManager } from "../vibration-manager";
|
||||
import { StreamStats } from "./stream-stats";
|
||||
import { BX_FLAGS } from "@/utils/bx-flags";
|
||||
import { BxSelectElement } from "@/web-components/bx-select";
|
||||
import { onChangeVideoPlayerType, updateVideoPlayer } from "./stream-settings-utils";
|
||||
|
||||
@ -362,7 +361,8 @@ export class StreamSettings {
|
||||
} else if (!setting.unsupported) {
|
||||
$control = toPrefElement(pref, setting.onChange, setting.params);
|
||||
|
||||
if ($control instanceof HTMLSelectElement && BX_FLAGS.ScriptUi === 'tv') {
|
||||
// Replace <select> with controller-friendly one
|
||||
if ($control instanceof HTMLSelectElement && getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
|
||||
$control = BxSelectElement.wrap($control);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import { getPref, Preferences, PrefKey, setPref, toPrefElement } from "@utils/pr
|
||||
import { t, Translations } from "@utils/translation";
|
||||
import { PatcherCache } from "../patcher";
|
||||
import { UserAgentProfile } from "@enums/user-agent";
|
||||
import { BX_FLAGS } from "@/utils/bx-flags";
|
||||
import { BxSelectElement } from "@/web-components/bx-select";
|
||||
import { StreamSettings } from "../stream/stream-settings";
|
||||
|
||||
@ -16,6 +15,7 @@ const SETTINGS_UI = {
|
||||
items: [
|
||||
PrefKey.BETTER_XCLOUD_LOCALE,
|
||||
PrefKey.SERVER_BYPASS_RESTRICTION,
|
||||
PrefKey.UI_CONTROLLER_FRIENDLY,
|
||||
PrefKey.REMOTE_PLAY_ENABLED,
|
||||
],
|
||||
},
|
||||
@ -218,8 +218,8 @@ export function setupSettingsUi() {
|
||||
Translations.refreshCurrentLocale();
|
||||
await Translations.updateTranslations();
|
||||
|
||||
// Don't refresh the page on TV
|
||||
if (BX_FLAGS.ScriptUi !== 'tv') {
|
||||
// Don't refresh the page when using controller-friendly UI
|
||||
if (!getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
|
||||
$btnReload.textContent = t('settings-reloading');
|
||||
$btnReload.click();
|
||||
}
|
||||
@ -388,15 +388,16 @@ export function setupSettingsUi() {
|
||||
|
||||
let $elm: HTMLElement;
|
||||
|
||||
if ($control instanceof HTMLSelectElement && BX_FLAGS.ScriptUi === 'tv') {
|
||||
if ($control instanceof HTMLSelectElement && getPref(PrefKey.UI_CONTROLLER_FRIENDLY)) {
|
||||
// Controller-friendly <select>
|
||||
$elm = CE('div', {'class': 'bx-settings-row'},
|
||||
$label,
|
||||
BxSelectElement.wrap($control),
|
||||
CE('div', {class: 'bx-setting-control', 'data-group': 0}, BxSelectElement.wrap($control)),
|
||||
);
|
||||
} else {
|
||||
$elm = CE('div', {'class': 'bx-settings-row'},
|
||||
$elm = CE('div', {'class': 'bx-settings-row', 'data-group': 0},
|
||||
$label,
|
||||
$control,
|
||||
CE('div', {class: 'bx-setting-control'}, $control),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,6 @@ type BxFlags = Partial<{
|
||||
ForceNativeMkbTitles: string[];
|
||||
FeatureGates: {[key: string]: boolean} | null,
|
||||
|
||||
ScriptUi: 'default' | 'tv',
|
||||
|
||||
IsSupportedTvBrowser: boolean,
|
||||
}>
|
||||
|
||||
@ -27,8 +25,6 @@ const DEFAULT_FLAGS: BxFlags = {
|
||||
|
||||
ForceNativeMkbTitles: [],
|
||||
FeatureGates: null,
|
||||
|
||||
ScriptUi: 'default',
|
||||
}
|
||||
|
||||
export const BX_FLAGS: BxFlags = Object.assign(DEFAULT_FLAGS, window.BX_FLAGS || {});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { CE } from "@utils/html";
|
||||
import { SUPPORTED_LANGUAGES, t} from "@utils/translation";
|
||||
import { SUPPORTED_LANGUAGES, t, ut} from "@utils/translation";
|
||||
import { SettingElement, SettingElementType } from "@utils/settings";
|
||||
import { UserAgent } from "@utils/user-agent";
|
||||
import { StreamStat } from "@modules/stream/stream-stats";
|
||||
@ -72,6 +72,7 @@ export enum PrefKey {
|
||||
UI_LOADING_SCREEN_WAIT_TIME = 'ui_loading_screen_wait_time',
|
||||
UI_LOADING_SCREEN_ROCKET = 'ui_loading_screen_rocket',
|
||||
|
||||
UI_CONTROLLER_FRIENDLY = 'ui_controller_friendly',
|
||||
UI_LAYOUT = 'ui_layout',
|
||||
UI_SCROLLBAR_HIDE = 'ui_scrollbar_hide',
|
||||
UI_HIDE_SECTIONS = 'ui_hide_sections',
|
||||
@ -553,6 +554,12 @@ export class Preferences {
|
||||
hide: t('rocket-always-hide'),
|
||||
},
|
||||
},
|
||||
|
||||
[PrefKey.UI_CONTROLLER_FRIENDLY]: {
|
||||
label: ut('Controller-friendly UI'),
|
||||
default: false,
|
||||
},
|
||||
|
||||
[PrefKey.UI_LAYOUT]: {
|
||||
label: t('layout'),
|
||||
default: 'default',
|
||||
|
@ -23,20 +23,32 @@ export class BxSelectElement {
|
||||
let $checkBox: HTMLInputElement;
|
||||
let $label: HTMLElement;
|
||||
|
||||
const $content = CE('div', {},
|
||||
$checkBox = CE('input', {type: 'checkbox', id: $select.id + '_checkbox'}),
|
||||
$label = CE('label', {for: $select.id + '_checkbox'}, ''),
|
||||
);
|
||||
let $content;
|
||||
|
||||
isMultiple && $checkBox.addEventListener('input', e => {
|
||||
const $option = getOptionAtIndex(visibleIndex);
|
||||
$option && ($option.selected = (e.target as HTMLInputElement).checked);
|
||||
if (isMultiple) {
|
||||
$content = CE('button', {
|
||||
class: 'bx-select-value bx-focusable',
|
||||
tabindex: 0,
|
||||
},
|
||||
$checkBox = CE('input', {type: 'checkbox'}),
|
||||
$label = CE('span', {}, ''),
|
||||
);
|
||||
|
||||
$select.dispatchEvent(new Event('input'));
|
||||
});
|
||||
$content.addEventListener('click', e => {
|
||||
$checkBox.click();
|
||||
});
|
||||
|
||||
// Only show checkbox in "multiple" <select>
|
||||
$checkBox.classList.toggle('bx-gone', !isMultiple);
|
||||
$checkBox.addEventListener('input', e => {
|
||||
const $option = getOptionAtIndex(visibleIndex);
|
||||
$option && ($option.selected = (e.target as HTMLInputElement).checked);
|
||||
|
||||
$select.dispatchEvent(new Event('input'));
|
||||
});
|
||||
} else {
|
||||
$content = CE('div', {},
|
||||
$label = CE('label', {for: $select.id + '_checkbox'}, ''),
|
||||
);
|
||||
}
|
||||
|
||||
const getOptionAtIndex = (index: number): HTMLOptionElement | undefined => {
|
||||
return $select.querySelector(`option:nth-of-type(${visibleIndex + 1})`) as HTMLOptionElement;
|
||||
@ -51,22 +63,28 @@ export class BxSelectElement {
|
||||
let content = '';
|
||||
if ($option) {
|
||||
content = $option.textContent || '';
|
||||
|
||||
if (content && $option.parentElement!.tagName === 'OPTGROUP') {
|
||||
content = ($option.parentElement as HTMLOptionElement).label + ' ❯ ' + content;
|
||||
}
|
||||
}
|
||||
|
||||
$label.textContent = content;
|
||||
|
||||
// Hide checkbox when the selection is empty
|
||||
isMultiple && ($checkBox.checked = $option?.selected || false);
|
||||
$checkBox.classList.toggle('bx-gone', !isMultiple || !content);
|
||||
if (isMultiple) {
|
||||
$checkBox.checked = $option?.selected || false;
|
||||
$checkBox.classList.toggle('bx-gone', !content);
|
||||
}
|
||||
|
||||
const disablePrev = visibleIndex <= 0;
|
||||
const disableNext = visibleIndex === $select.querySelectorAll('option').length - 1;
|
||||
|
||||
$btnPrev.classList.toggle('bx-inactive', disablePrev);
|
||||
disablePrev && $btnNext.focus();
|
||||
disablePrev && document.activeElement === $btnPrev && $btnNext.focus();
|
||||
|
||||
$btnNext.classList.toggle('bx-inactive', disableNext);
|
||||
disableNext && $btnPrev.focus();
|
||||
disableNext && document.activeElement === $btnNext &&$btnPrev.focus();
|
||||
}
|
||||
|
||||
const normalizeIndex = (index: number): number => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user