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