Finalize menu position settings, replace eventBus.sendToTunnel() with eventBus.send()

This commit is contained in:
Tamius Han 2026-01-15 02:33:21 +01:00
parent 925a9fdea3
commit 35bb5b477b
12 changed files with 188 additions and 74 deletions

View File

@ -23,6 +23,6 @@ export enum MenuPosition {
export interface MenuConfig { export interface MenuConfig {
isGlobal?: boolean; isGlobal?: boolean;
ui: InPlayerUIOptions; ui: InPlayerUIOptions;
menuPosition: MenuPosition; options?: {forceShow?: boolean};
items: MenuItemConfig[]; items: MenuItemConfig[];
} }

View File

@ -9,6 +9,7 @@ import StretchType from '@src/common/enums/StretchType.enum'
import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum' import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum'
import { PlayerDetectionMode } from '@src/common/enums/PlayerDetectionMode.enum'; import { PlayerDetectionMode } from '@src/common/enums/PlayerDetectionMode.enum';
import { InputHandlingMode } from '@src/common/enums/InputHandlingMode.enum'; import { InputHandlingMode } from '@src/common/enums/InputHandlingMode.enum';
import { MenuPosition } from '@src/common/interfaces/ClientUiMenu.interface';
export enum ExtensionEnvironment { export enum ExtensionEnvironment {
Normal = ExtensionMode.All, Normal = ExtensionMode.All,
@ -309,14 +310,14 @@ interface DevSettings {
} }
export interface InPlayerUIOptions { export interface InPlayerUIOptions {
activatorAlignment: 'left' | 'right', activatorAlignment: MenuPosition,
minEnabledWidth: number, // don't show UI if player is narrower than % of screen width minEnabledWidth: number, // don't show UI if player is narrower than % of screen width
minEnabledHeight: number, // don't show UI if player is narrower than % of screen height minEnabledHeight: number, // don't show UI if player is narrower than % of screen height
activation: 'player' | 'player-ctrl' | 'trigger-zone' | 'distance' | 'none', // what needs to be hovered in order for UI to be visible activation: 'player' | 'player-ctrl' | 'trigger-zone' | 'distance' | 'none', // what needs to be hovered in order for UI to be visible
activationDistance: number, activationDistance: number,
activationDistanceUnits: '%' | 'px', activationDistanceUnits: '%' | 'px',
activatorPadding: 10, activatorPadding: {x: number, y: number}
activatorPaddingUnit: '%' | 'px', activatorPaddingUnit: {x: '%' | 'px', y: '%' | 'px'},
triggerZoneDimensions: { // how large the trigger zone is (relative to player size) triggerZoneDimensions: { // how large the trigger zone is (relative to player size)
width: number width: number
height: number, height: number,

View File

@ -267,8 +267,8 @@ const ExtensionConf: SettingsInterface = {
activationDistance: 100, activationDistance: 100,
activationDistanceUnits: '%', activationDistanceUnits: '%',
activatorAlignment: 'left', activatorAlignment: 'left',
activatorPadding: 10, activatorPadding: {x: 16, y: 16},
activatorPaddingUnit: '%', activatorPaddingUnit: {x: 'px', y: 'px'},
triggerZoneDimensions: { triggerZoneDimensions: {
width: 0.5, width: 0.5,
height: 0.5, height: 0.5,

View File

@ -19,6 +19,7 @@ export class ClientMenu {
private isHovered = false; private isHovered = false;
private forceShow = false;
private isWithinActivation = false; private isWithinActivation = false;
private lastMouseMove = performance.now(); private lastMouseMove = performance.now();
private idleTimer?: number; private idleTimer?: number;
@ -29,7 +30,11 @@ export class ClientMenu {
private onDocumentMouseLeave?: () => void; private onDocumentMouseLeave?: () => void;
private idleIntervalId?: number; private idleIntervalId?: number;
constructor(private config: MenuConfig) {} constructor(private config: MenuConfig) {
if (config.options?.forceShow !== undefined) {
this.forceShow = config.options.forceShow;
}
}
mount(anchorElement: HTMLElement) { mount(anchorElement: HTMLElement) {
this.buildMenuPositionClassList(); this.buildMenuPositionClassList();
@ -173,7 +178,8 @@ export class ClientMenu {
*/ */
private buildMenuPositionClassList() { private buildMenuPositionClassList() {
let classList; let classList;
switch (this.config.menuPosition) { console.log('BUILDING MENU POS:', MenuPosition[this.config.ui.activatorAlignment]);
switch (this.config.ui.activatorAlignment) {
case MenuPosition.TopLeft: case MenuPosition.TopLeft:
classList = ['uw-menu-left','uw-menu-top']; classList = ['uw-menu-left','uw-menu-top'];
break; break;
@ -209,10 +215,14 @@ export class ClientMenu {
private createMenu() { private createMenu() {
this.root = document.createElement('div'); this.root = document.createElement('div');
this.root.className = 'uw-menu-root uw-hidden'; this.root.className = 'uw-menu-root uw-hidden';
this.root.setAttribute(
'style',
`padding: ${this.config.ui.activatorPadding.y ?? 16}${this.config.ui.activatorPaddingUnit.y ?? 'px'} ${this.config.ui.activatorPadding.x ?? 16}${this.config.ui.activatorPaddingUnit.x ?? 'px'}`
);
const trigger = document.createElement('div'); const trigger = document.createElement('div');
trigger.classList = 'uw-menu-trigger uw-trigger'; trigger.classList = 'uw-menu-trigger uw-trigger';
trigger.style = `margin: ${this.config.ui.activatorPadding ?? 10} ${this.config.ui.activatorPaddingUnit ?? '%'}`;
trigger.textContent = 'Ultrawidify'; trigger.textContent = 'Ultrawidify';
this.trigger = trigger; this.trigger = trigger;
@ -364,7 +374,7 @@ export class ClientMenu {
} }
} }
private show() { show(options?: {forceShow?: boolean}) {
this.lastMouseMove = performance.now(); this.lastMouseMove = performance.now();
if (!this.visible) { if (!this.visible) {
@ -372,10 +382,18 @@ export class ClientMenu {
this.root.classList.add('uw-visible'); this.root.classList.add('uw-visible');
this.root.classList.remove('uw-hidden'); this.root.classList.remove('uw-hidden');
} }
if (options?.forceShow !== undefined) {
this.forceShow = options.forceShow;
}
} }
private hide() { hide(options?: {forceShow?: boolean}) {
if (this.visible) { if (options?.forceShow !== undefined) {
this.forceShow = options.forceShow;
}
if (this.visible && !this.forceShow) {
this.visible = false; this.visible = false;
this.root.classList.remove('uw-visible'); this.root.classList.remove('uw-visible');
this.root.classList.add('uw-hidden'); this.root.classList.add('uw-hidden');

View File

@ -68,6 +68,24 @@ class UI {
// UI will be initialized when setUiVisibility is called // UI will be initialized when setUiVisibility is called
if (!this.isGlobal) { if (!this.isGlobal) {
this.init(); this.init();
this.eventBus.subscribeMulti({
'reload-menu': {
function: () => {
this.createExtensionMenu({forceShow: true});
this.extensionMenu.show({forceShow: true});
}
},
'force-menu-activator-state': {
function: (commandData) => {
if (commandData.visibility) {
this.extensionMenu.show({forceShow: true});
} else {
this.extensionMenu.show({forceShow: false});
}
}
}
});
} }
} }
@ -164,7 +182,7 @@ class UI {
} }
} }
createExtensionMenu() { createExtensionMenu(options?: {forceShow?: boolean}) {
if (+this.siteSettings?.data.enableUI === ExtensionMode.Disabled || this.isGlobal) { if (+this.siteSettings?.data.enableUI === ExtensionMode.Disabled || this.isGlobal) {
return; // don't return; // don't
} }
@ -185,6 +203,7 @@ class UI {
const menuConfig = { const menuConfig = {
isGlobal: this.isGlobal, isGlobal: this.isGlobal,
ui: this.settings.active.ui.inPlayer, ui: this.settings.active.ui.inPlayer,
options,
items: [ items: [
{ {
customClassList: 'uw-site-info', customClassList: 'uw-site-info',
@ -339,7 +358,7 @@ class UI {
action: () => this.createSettingsWindow('about'), action: () => this.createSettingsWindow('about'),
} }
] ]
} };
this.extensionMenu = new ClientMenu(menuConfig); this.extensionMenu = new ClientMenu(menuConfig);
this.extensionMenu.mount(this.uiConfig.parentElement); this.extensionMenu.mount(this.uiConfig.parentElement);
@ -347,10 +366,13 @@ class UI {
* SETUP MENU INTERACTIONS * SETUP MENU INTERACTIONS
* *
* Interactions are needed for the following things: * Interactions are needed for the following things:
* 0. [ ] both sliders. Needs to read the state of the sliders and update the labels * 0. [X] both sliders. Needs to read the state of the sliders and update the labels
* 1. [ ] lock X/Y button needs to update icon state of the button and the linked bar * 1. [X] lock X/Y button needs to update icon state of the button and the linked bar
* 2. [ ] reset button just needs to reset, no icon changes necessary * 2. [X] reset button just needs to reset, no icon changes necessary
* 3. [X] alignment indicator also needs to update indicator state * 3. [X] alignment indicator also needs to update indicator state
* A
* |
* (yay done!)
*/ */
const menuElement = this.extensionMenu.root; const menuElement = this.extensionMenu.root;

View File

@ -206,7 +206,7 @@
<div class="flex flex-row w-full gap-2"> <div class="flex flex-row w-full gap-2">
<div class="grow flex flex-col gap-2"> <div class="grow flex flex-col gap-2">
<div> <div>
<button v-if="eventBus" @click="eventBus?.sendToTunnel('aard-enable-debug', true)">Show debug overlay</button> <button v-if="eventBus" @click="eventBus?.send('aard-enable-debug', true)">Show debug overlay</button>
</div> </div>
<div class="field"> <div class="field">
<div class="label">Show debug overlay on startup</div> <div class="label">Show debug overlay on startup</div>
@ -277,7 +277,7 @@ export default defineComponent({
); );
}, },
mounted() { mounted() {
this.eventBus?.sendToTunnel('get-aard-timing'); this.eventBus?.send('get-aard-timing');
// this.graphRefreshInterval = setInterval(() => this.eventBus.sendToTunnel('get-aard-timing'), 500); // this.graphRefreshInterval = setInterval(() => this.eventBus.sendToTunnel('get-aard-timing'), 500);
this.resetSettingsEditor(); this.resetSettingsEditor();
}, },
@ -302,7 +302,7 @@ export default defineComponent({
this.settings.saveWithoutReload(); this.settings.saveWithoutReload();
}, },
refreshGraph() { refreshGraph() {
this.eventBus.sendToTunnel('get-aard-timing'); this.eventBus.send('get-aard-timing');
}, },
handleConfigBroadcast(data) { handleConfigBroadcast(data) {
if (data.type === 'aard-performance-data') { if (data.type === 'aard-performance-data') {

View File

@ -183,11 +183,7 @@ export default defineComponent({
this.tutorialStep = 0; this.tutorialStep = 0;
}, },
getPlayerTree() { getPlayerTree() {
if (this.isPopup) { this.eventBus.send('get-player-tree');
this.eventBus.send('get-player-tree');
} else {
this.eventBus.sendToTunnel('get-player-tree');
}
}, },
handleElementStack(configBroadcast) { handleElementStack(configBroadcast) {
if (configBroadcast.type === 'player-tree') { if (configBroadcast.type === 'player-tree') {
@ -196,7 +192,7 @@ export default defineComponent({
} }
}, },
markElement(parentIndex, enable) { markElement(parentIndex, enable) {
this.eventBus.sendToTunnel('set-mark-element', {parentIndex, enable}); this.eventBus.send('set-mark-element', {parentIndex, enable});
}, },
async setPlayer(index) { async setPlayer(index) {
// yup. // yup.

View File

@ -139,11 +139,7 @@ export default({
this.tutorialStep = 0; this.tutorialStep = 0;
}, },
getPlayerTree() { getPlayerTree() {
if (this.isPopup) { this.eventBus.send('get-player-tree');
this.eventBus.send('get-player-tree');
} else {
this.eventBus.sendToTunnel('get-player-tree');
}
}, },
handleElementStack(configBroadcast) { handleElementStack(configBroadcast) {
if (configBroadcast.type === 'player-tree') { if (configBroadcast.type === 'player-tree') {
@ -152,7 +148,7 @@ export default({
} }
}, },
markElement(parentIndex, enable) { markElement(parentIndex, enable) {
this.eventBus.sendToTunnel('set-mark-element', {parentIndex, enable}); this.eventBus.send('set-mark-element', {parentIndex, enable});
}, },
async setPlayer(index) { async setPlayer(index) {
// yup. // yup.

View File

@ -9,35 +9,6 @@
<div <div
class="flex flex-col field-group compact-form gap-2" class="flex flex-col field-group compact-form gap-2"
> >
<div class="field">
<div class="label">
Popup activator position:
</div>
<div class="select">
<select
v-model="settings.active.ui.inPlayer.activatorAlignment"
@change="saveSettings()"
>
<option value="left">Left</option>
<option value="right">Right</option>
</select>
</div>
</div>
<div class="field">
<div class="label">
Popup activator padding:
</div>
<div class="select">
<select
v-model="settings.active.ui.inPlayer.activatorAlignment"
@change="saveSettings()"
>
<option value="left">Left</option>
<option value="right">Right</option>
</select>
</div>
</div>
<div class="field"> <div class="field">
<div class="label"> <div class="label">
@ -99,6 +70,92 @@
<button @click="startTriggerZoneEdit()">Edit</button> <button @click="startTriggerZoneEdit()">Edit</button>
</div> </div>
<div class="field">
<div class="label">
Menu activator position:
</div>
<div class="select">
<select
v-model="settings.active.ui.inPlayer.activatorAlignment"
@change="saveSettings()"
>
<optgroup label="Edges">
<option :value="MenuPosition.Left">Left</option>
<option :value="MenuPosition.Right">Right</option>
<option :value="MenuPosition.Top">Top</option>
<option :value="MenuPosition.Bottom">Bottom</option>
</optgroup>
<optgroup label="corners">
<option :value="MenuPosition.TopLeft">Top left</option>
<option :value="MenuPosition.BottomLeft">Bottom left</option>
<option :value="MenuPosition.TopRight">Top right"</option>
<option :value="MenuPosition.BottomRight">Bottom right"</option>
</optgroup>
</select>
</div>
</div>
<div class="field">
<div class="label">
Menu activator horizontal padding:
</div>
<div class="input range-input">
<input
v-model="settings.active.ui.inPlayer.activatorPadding.x"
class="slider"
type="range"
min="0"
max="100"
step="1"
@change="(event) => saveSettings()"
>
<input
style="margin-right: 0.6rem;"
v-model="settings.active.ui.inPlayer.activatorPadding.x"
@change="(event) => saveSettings(true)"
>
<select
class="unit-select !min-w-[72px]"
v-model="settings.active.ui.inPlayer.activatorPaddingUnit.x"
@change="(event) => saveSettings(true)"
>
<option value="%">%</option>
<option value="px">px</option>
</select>
</div>
</div>
<div class="field">
<div class="label">
Menu activator vertical padding:
</div>
<div class="input range-input">
<input
v-model="settings.active.ui.inPlayer.activatorPadding.y"
class="slider"
type="range"
min="0"
max="100"
step="1"
@change="(event) => saveSettings()"
>
<input
style="margin-right: 0.6rem;"
v-model="settings.active.ui.inPlayer.activatorPadding.y"
@change="(event) => saveSettings(true)"
>
<select
class="unit-select !min-w-[72px]"
v-model="settings.active.ui.inPlayer.activatorPaddingUnit.y"
@change="(event) => saveSettings(true)"
>
<option value="%">%</option>
<option value="px">px</option>
</select>
</div>
</div>
<div class="field"> <div class="field">
<div class="label"> <div class="label">
Do not show in-player UI when video player is narrower than Do not show in-player UI when video player is narrower than
@ -159,6 +216,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { MenuPosition } from '@src/common/interfaces/ClientUiMenu.interface';
import BrowserDetect from '@src/ext/conf/BrowserDetect'; import BrowserDetect from '@src/ext/conf/BrowserDetect';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
@ -167,6 +225,7 @@ export default defineComponent({
}, },
data() { data() {
return { return {
MenuPosition,
ghettoComputed: { } ghettoComputed: { }
} }
}, },
@ -181,7 +240,12 @@ export default defineComponent({
this.ghettoComputed = { this.ghettoComputed = {
minEnabledWidth: this.optionalToFixed(this.settings.active.ui.inPlayer.minEnabledWidth * 100, 0), minEnabledWidth: this.optionalToFixed(this.settings.active.ui.inPlayer.minEnabledWidth * 100, 0),
minEnabledHeight: this.optionalToFixed(this.settings.active.ui.inPlayer.minEnabledHeight * 100, 0), minEnabledHeight: this.optionalToFixed(this.settings.active.ui.inPlayer.minEnabledHeight * 100, 0),
} };
this.eventBus?.send('force-menu-activator-state', {visibility: true});
},
unmounted() {
this.eventBus?.send('force-menu-activator-state', {visibility: false});
}, },
methods: { methods: {
forcePositiveNumber(value) { forcePositiveNumber(value) {
@ -213,11 +277,13 @@ export default defineComponent({
this.ghettoComputed[key] = this.optionalToFixed(value * 100, 0); this.ghettoComputed[key] = this.optionalToFixed(value * 100, 0);
} }
}, },
saveSettings(forceRefresh) { async saveSettings(forceRefresh) {
this.settings.saveWithoutReload(); await this.settings.saveWithoutReload();
this.eventBus.send('reload-menu');
if (forceRefresh) { if (forceRefresh) {
this.$nextTick( () => this.$forceRefresh() ); this.$nextTick( () => this.$forceUpdate() );
} }
}, },
startTriggerZoneEdit() { startTriggerZoneEdit() {

View File

@ -173,7 +173,7 @@ export default defineComponent({
} }
} }
this.eventBus.sendToTunnel('get-ar'); this.eventBus.send('get-ar');
}, },
destroyed() { destroyed() {
this.eventBus.unsubscribeAll(this); this.eventBus.unsubscribeAll(this);
@ -201,7 +201,7 @@ export default defineComponent({
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'y'}); // this.eventBus.send('set-zoom', {zoom: 1, axis: 'y'});
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'x'}); // this.eventBus.send('set-zoom', {zoom: 1, axis: 'x'});
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1}); this.eventBus?.send('set-zoom', {zoom: 1});
}, },
changeZoom(logZoom: number, axis: 'x' | 'y') { changeZoom(logZoom: number, axis: 'x' | 'y') {
// we do not use logarithmic zoom elsewhere, therefore we need to convert // we do not use logarithmic zoom elsewhere, therefore we need to convert
@ -210,17 +210,17 @@ export default defineComponent({
if (this.zoomOptions.lockAr) { if (this.zoomOptions.lockAr) {
this.zoom.x = logZoom; this.zoom.x = logZoom;
this.zoom.y = logZoom; this.zoom.y = logZoom;
this.eventBus?.sendToTunnel('set-zoom', {zoom: linearZoom}); this.eventBus?.send('set-zoom', {zoom: linearZoom});
} else { } else {
this.zoom[axis] = logZoom; this.zoom[axis] = logZoom;
this.eventBus?.sendToTunnel('set-zoom', {zoom: {[axis]: linearZoom}}); this.eventBus?.send('set-zoom', {zoom: {[axis]: linearZoom}});
} }
}, },
isActiveZoom(command) { isActiveZoom(command) {
return false; return false;
}, },
align(alignmentX: VideoAlignmentType, alignmentY: VideoAlignmentType) { align(alignmentX: VideoAlignmentType, alignmentY: VideoAlignmentType) {
this.eventBus?.sendToTunnel('set-alignment', {x: alignmentX, y: alignmentY}) this.eventBus?.send('set-alignment', {x: alignmentX, y: alignmentY})
} }
} }

View File

@ -106,13 +106,28 @@
} }
&.uw-menu-center { &.uw-menu-center {
@apply left-1/2 -translate-x-1/2 flex-row; @apply flex-row;
left: 50%;
transform: translateX(-50%);
&.uw-menu-top { &.uw-menu-top {
@apply bottom-0; @apply top-full;
} }
&.uw-menu-bottom { &.uw-menu-bottom {
@apply top-0; @apply bottom-full;
}
.uw-menu-item {
@apply px-8 relative;
&:not(:first-child):after {
content: '·';
@apply absolute;
left: -1rem;
transform: translateX(50%);
}
} }
} }

View File

@ -15,7 +15,7 @@ export default {
*/ */
execAction(command) { execAction(command) {
const cmd = JSON.parse(JSON.stringify(command)); const cmd = JSON.parse(JSON.stringify(command));
this.eventBus?.sendToTunnel(cmd.action, cmd.arguments); this.eventBus?.send(cmd.action, cmd.arguments);
}, },
} }
} }