mirror of
https://github.com/redphx/better-xcloud.git
synced 2025-06-06 15:47:18 +02:00
Refactor Game Bar
This commit is contained in:
parent
34159fad22
commit
1acb30e3af
@ -1,6 +1,8 @@
|
|||||||
import { BxEvent } from "@/utils/bx-event";
|
import { BxEvent } from "@/utils/bx-event";
|
||||||
|
|
||||||
export abstract class BaseGameBarAction {
|
export abstract class BaseGameBarAction {
|
||||||
|
abstract $content: HTMLElement;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
reset() {}
|
reset() {}
|
||||||
|
|
||||||
@ -8,5 +10,7 @@ export abstract class BaseGameBarAction {
|
|||||||
BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
|
BxEvent.dispatch(window, BxEvent.GAME_BAR_ACTION_ACTIVATED);
|
||||||
};
|
};
|
||||||
|
|
||||||
abstract render(): HTMLElement;
|
render(): HTMLElement {
|
||||||
|
return this.$content;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,6 @@ import { MicrophoneShortcut, MicrophoneState } from "../shortcuts/shortcut-micro
|
|||||||
export class MicrophoneAction extends BaseGameBarAction {
|
export class MicrophoneAction extends BaseGameBarAction {
|
||||||
$content: HTMLElement;
|
$content: HTMLElement;
|
||||||
|
|
||||||
visible: boolean = false;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@ -26,12 +24,7 @@ export class MicrophoneAction extends BaseGameBarAction {
|
|||||||
onClick: this.onClick.bind(this),
|
onClick: this.onClick.bind(this),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$content = CE('div', {},
|
this.$content = CE('div', {}, $btnMuted, $btnDefault);
|
||||||
$btnMuted,
|
|
||||||
$btnDefault,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
|
|
||||||
window.addEventListener(BxEvent.MICROPHONE_STATE_CHANGED, e => {
|
window.addEventListener(BxEvent.MICROPHONE_STATE_CHANGED, e => {
|
||||||
const microphoneState = (e as any).microphoneState;
|
const microphoneState = (e as any).microphoneState;
|
||||||
@ -49,12 +42,7 @@ export class MicrophoneAction extends BaseGameBarAction {
|
|||||||
this.$content.dataset.activated = enabled.toString();
|
this.$content.dataset.activated = enabled.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.visible = false;
|
|
||||||
this.$content.classList.add('bx-gone');
|
this.$content.classList.add('bx-gone');
|
||||||
this.$content.dataset.activated = 'false';
|
this.$content.dataset.activated = 'false';
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,7 @@ export class RendererAction extends BaseGameBarAction {
|
|||||||
classes: ['bx-activated'],
|
classes: ['bx-activated'],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$content = CE('div', {},
|
this.$content = CE('div', {}, $btnDefault, $btnActivated);
|
||||||
$btnDefault,
|
|
||||||
$btnActivated,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(e: Event) {
|
onClick(e: Event) {
|
||||||
@ -37,10 +32,6 @@ export class RendererAction extends BaseGameBarAction {
|
|||||||
this.$content.dataset.activated = (!isVisible).toString();
|
this.$content.dataset.activated = (!isVisible).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.$content.dataset.activated = 'false';
|
this.$content.dataset.activated = 'false';
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,4 @@ export class ScreenshotAction extends BaseGameBarAction {
|
|||||||
super.onClick(e);
|
super.onClick(e);
|
||||||
Screenshot.takeScreenshot();
|
Screenshot.takeScreenshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,7 @@ export class SpeakerAction extends BaseGameBarAction {
|
|||||||
classes: ['bx-activated'],
|
classes: ['bx-activated'],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$content = CE('div', {},
|
this.$content = CE('div', {}, $btnEnable, $btnMuted);
|
||||||
$btnEnable,
|
|
||||||
$btnMuted,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
|
|
||||||
window.addEventListener(BxEvent.SPEAKER_STATE_CHANGED, e => {
|
window.addEventListener(BxEvent.SPEAKER_STATE_CHANGED, e => {
|
||||||
const speakerState = (e as any).speakerState;
|
const speakerState = (e as any).speakerState;
|
||||||
@ -44,10 +39,6 @@ export class SpeakerAction extends BaseGameBarAction {
|
|||||||
SoundShortcut.muteUnmute();
|
SoundShortcut.muteUnmute();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.$content.dataset.activated = 'false';
|
this.$content.dataset.activated = 'false';
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,7 @@ export class TouchControlAction extends BaseGameBarAction {
|
|||||||
classes: ['bx-activated'],
|
classes: ['bx-activated'],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$content = CE('div', {},
|
this.$content = CE('div', {}, $btnEnable, $btnDisable);
|
||||||
$btnEnable,
|
|
||||||
$btnDisable,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(e: Event) {
|
onClick(e: Event) {
|
||||||
@ -39,10 +34,6 @@ export class TouchControlAction extends BaseGameBarAction {
|
|||||||
this.$content.dataset.activated = (!isVisible).toString();
|
this.$content.dataset.activated = (!isVisible).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
this.$content.dataset.activated = 'false';
|
this.$content.dataset.activated = 'false';
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { BxIcon } from "@/utils/bx-icon";
|
import { BxIcon } from "@/utils/bx-icon";
|
||||||
import { createButton, ButtonStyle } from "@/utils/html";
|
import { createButton, ButtonStyle } from "@/utils/html";
|
||||||
import { t } from "@/utils/translation";
|
|
||||||
import { BaseGameBarAction } from "./action-base";
|
import { BaseGameBarAction } from "./action-base";
|
||||||
import { TrueAchievements } from "@/utils/true-achievements";
|
import { TrueAchievements } from "@/utils/true-achievements";
|
||||||
|
|
||||||
@ -13,7 +12,6 @@ export class TrueAchievementsAction extends BaseGameBarAction {
|
|||||||
this.$content = createButton({
|
this.$content = createButton({
|
||||||
style: ButtonStyle.GHOST,
|
style: ButtonStyle.GHOST,
|
||||||
icon: BxIcon.TRUE_ACHIEVEMENTS,
|
icon: BxIcon.TRUE_ACHIEVEMENTS,
|
||||||
title: t('true-achievements'),
|
|
||||||
onClick: this.onClick.bind(this),
|
onClick: this.onClick.bind(this),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -22,8 +20,4 @@ export class TrueAchievementsAction extends BaseGameBarAction {
|
|||||||
super.onClick(e);
|
super.onClick(e);
|
||||||
TrueAchievements.open(false);
|
TrueAchievements.open(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): HTMLElement {
|
|
||||||
return this.$content;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import type { BaseGameBarAction } from "./action-base";
|
|||||||
import { STATES } from "@utils/global";
|
import { STATES } from "@utils/global";
|
||||||
import { MicrophoneAction } from "./action-microphone";
|
import { MicrophoneAction } from "./action-microphone";
|
||||||
import { PrefKey } from "@/enums/pref-keys";
|
import { PrefKey } from "@/enums/pref-keys";
|
||||||
import { getPref, StreamTouchController } from "@/utils/settings-storages/global-settings-storage";
|
import { getPref, StreamTouchController, type GameBarPosition } from "@/utils/settings-storages/global-settings-storage";
|
||||||
import { TrueAchievementsAction } from "./action-true-achievements";
|
import { TrueAchievementsAction } from "./action-true-achievements";
|
||||||
import { SpeakerAction } from "./action-speaker";
|
import { SpeakerAction } from "./action-speaker";
|
||||||
import { RendererAction } from "./action-renderer";
|
import { RendererAction } from "./action-renderer";
|
||||||
@ -15,13 +15,7 @@ import { RendererAction } from "./action-renderer";
|
|||||||
|
|
||||||
export class GameBar {
|
export class GameBar {
|
||||||
private static instance: GameBar;
|
private static instance: GameBar;
|
||||||
public static getInstance(): GameBar {
|
public static getInstance = () => GameBar.instance ?? (GameBar.instance = new GameBar());
|
||||||
if (!GameBar.instance) {
|
|
||||||
GameBar.instance = new GameBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
return GameBar.instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly VISIBLE_DURATION = 2000;
|
private static readonly VISIBLE_DURATION = 2000;
|
||||||
|
|
||||||
@ -35,12 +29,12 @@ export class GameBar {
|
|||||||
private constructor() {
|
private constructor() {
|
||||||
let $container;
|
let $container;
|
||||||
|
|
||||||
const position = getPref(PrefKey.GAME_BAR_POSITION);
|
const position = getPref(PrefKey.GAME_BAR_POSITION) as GameBarPosition;
|
||||||
|
|
||||||
const $gameBar = CE('div', {id: 'bx-game-bar', class: 'bx-gone', 'data-position': position},
|
const $gameBar = CE('div', {id: 'bx-game-bar', class: 'bx-gone', 'data-position': position},
|
||||||
$container = CE('div', {class: 'bx-game-bar-container bx-offscreen'}),
|
$container = CE('div', {class: 'bx-game-bar-container bx-offscreen'}),
|
||||||
createSvgIcon(position === 'bottom-left' ? BxIcon.CARET_RIGHT : BxIcon.CARET_LEFT),
|
createSvgIcon(position === 'bottom-left' ? BxIcon.CARET_RIGHT : BxIcon.CARET_LEFT),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.actions = [
|
this.actions = [
|
||||||
new ScreenshotAction(),
|
new ScreenshotAction(),
|
||||||
@ -78,11 +72,7 @@ export class GameBar {
|
|||||||
|
|
||||||
// Add animation when hiding game bar
|
// Add animation when hiding game bar
|
||||||
$container.addEventListener('transitionend', e => {
|
$container.addEventListener('transitionend', e => {
|
||||||
const classList = $container.classList;
|
$container.classList.replace('bx-hide', 'bx-offscreen');
|
||||||
if (classList.contains('bx-hide')) {
|
|
||||||
classList.remove('bx-hide');
|
|
||||||
classList.add('bx-offscreen');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.documentElement.appendChild($gameBar);
|
document.documentElement.appendChild($gameBar);
|
||||||
@ -106,9 +96,9 @@ export class GameBar {
|
|||||||
this.clearHideTimeout();
|
this.clearHideTimeout();
|
||||||
|
|
||||||
this.timeoutId = window.setTimeout(() => {
|
this.timeoutId = window.setTimeout(() => {
|
||||||
this.timeoutId = null;
|
this.timeoutId = null;
|
||||||
this.hideBar();
|
this.hideBar();
|
||||||
}, GameBar.VISIBLE_DURATION);
|
}, GameBar.VISIBLE_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearHideTimeout() {
|
private clearHideTimeout() {
|
||||||
@ -117,19 +107,15 @@ export class GameBar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {
|
||||||
this.$gameBar && this.$gameBar.classList.remove('bx-gone');
|
this.$gameBar.classList.remove('bx-gone');
|
||||||
}
|
}
|
||||||
|
|
||||||
disable() {
|
disable() {
|
||||||
this.hideBar();
|
this.hideBar();
|
||||||
this.$gameBar && this.$gameBar.classList.add('bx-gone');
|
this.$gameBar.classList.add('bx-gone');
|
||||||
}
|
}
|
||||||
|
|
||||||
showBar() {
|
showBar() {
|
||||||
if (!this.$container) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$container.classList.remove('bx-offscreen', 'bx-hide' , 'bx-gone');
|
this.$container.classList.remove('bx-offscreen', 'bx-hide' , 'bx-gone');
|
||||||
this.$container.classList.add('bx-show');
|
this.$container.classList.add('bx-show');
|
||||||
|
|
||||||
@ -142,18 +128,11 @@ export class GameBar {
|
|||||||
// Stop focusing Game Bar
|
// Stop focusing Game Bar
|
||||||
clearFocus();
|
clearFocus();
|
||||||
|
|
||||||
if (!this.$container) {
|
this.$container.classList.replace('bx-show', 'bx-hide');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$container.classList.remove('bx-show');
|
|
||||||
this.$container.classList.add('bx-hide');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset all states
|
// Reset all states
|
||||||
reset() {
|
reset() {
|
||||||
for (const action of this.actions) {
|
this.actions.forEach(action => action.reset());
|
||||||
action.reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,10 @@ export const enum ControllerDeviceVibration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type GameBarPosition = 'bottom-left' | 'bottom-right' | 'off';
|
||||||
|
export type GameBarPositionOptions = Record<GameBarPosition, string>;
|
||||||
|
|
||||||
|
|
||||||
function getSupportedCodecProfiles() {
|
function getSupportedCodecProfiles() {
|
||||||
const options: PartialRecord<CodecProfile, string> = {
|
const options: PartialRecord<CodecProfile, string> = {
|
||||||
default: t('default'),
|
default: t('default'),
|
||||||
@ -323,12 +327,12 @@ export class GlobalSettingsStorage extends BaseSettingsStorage {
|
|||||||
[PrefKey.GAME_BAR_POSITION]: {
|
[PrefKey.GAME_BAR_POSITION]: {
|
||||||
requiredVariants: 'full',
|
requiredVariants: 'full',
|
||||||
label: t('position'),
|
label: t('position'),
|
||||||
default: 'bottom-left',
|
default: 'bottom-left' satisfies GameBarPosition,
|
||||||
options: {
|
options: {
|
||||||
'bottom-left': t('bottom-left'),
|
'bottom-left': t('bottom-left'),
|
||||||
'bottom-right': t('bottom-right'),
|
'bottom-right': t('bottom-right'),
|
||||||
'off': t('off'),
|
'off': t('off'),
|
||||||
},
|
} satisfies GameBarPositionOptions,
|
||||||
},
|
},
|
||||||
|
|
||||||
[PrefKey.LOCAL_CO_OP_ENABLED]: {
|
[PrefKey.LOCAL_CO_OP_ENABLED]: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user