safe-mode for input fields

This commit is contained in:
Michał Sieciechowicz 2025-08-05 22:13:56 +02:00
parent 88918c7a57
commit 48cc0e1fc1
5 changed files with 41 additions and 13 deletions

View File

@ -24,7 +24,9 @@ export class InputKeyboard extends InputDevice {
private catchKey(event: KeyboardEvent, value: number = 1, angle: number = 0): boolean {
const key = event.key;
super.setKey(key, value, angle);
event.preventDefault();
if (!InputDevice.IsKeyEventSafeModeEnabled) {
event.preventDefault();
}
return false;
}
}

View File

@ -1,6 +1,7 @@
import { InputDevice } from "./input-device";
import { InputScheme } from "./input-scheme";
export type ActionCallback = (value: number, data: unknown, angle?:number, timePressed?: number) => void;
export type ActionCallback = (value: number, data: unknown, angle?:number, timePressed?: number, safeMode?: boolean) => void;
export class InputAction {
@ -23,14 +24,15 @@ export class InputAction {
public fire(value: number, data: unknown = undefined, angle?: number, timePressed?: number) {
const isPressed = value > 0;
const wasPressed = this.currentValue > 0;
const safeMode = InputDevice.IsKeyEventSafeModeEnabled;
if (isPressed !== wasPressed) {
if (isPressed) {
this.downActions.forEach(action => action(value, data, angle, timePressed));
this.downActions.forEach(action => action(value, data, angle, timePressed, safeMode));
} else {
this.upActions.forEach(action => action(value, data, angle, timePressed));
this.upActions.forEach(action => action(value, data, angle, timePressed, safeMode));
}
} else {
this.changeActions.forEach(action => action(value, data, angle, timePressed));
this.changeActions.forEach(action => action(value, data, angle, timePressed, safeMode));
}
this.currentValue = value;

View File

@ -1,6 +1,7 @@
import { EventEmitter } from "@angular/core";
import { InputScheme } from "./input-scheme";
import { InputAction } from "./input-action";
import { InputService } from "../services/input.service";
export interface InputKeyState {
key: string | number;
@ -13,6 +14,18 @@ export abstract class InputDevice {
protected scheme?: InputScheme;
protected keyStates: InputKeyState[] = [];
public keyStateChanged = new EventEmitter<InputKeyState>();
public static KeyEventSafeModeEnabled = false;
public static get IsKeyEventSafeModeEnabled() {
if ((window as any).KeyEventSafeModeEnabled === true) {
return true;
}
if ((window as any).KeyEventSafeModeEnabled === false) {
return false;
}
return InputDevice.KeyEventSafeModeEnabled;
}
constructor(
public readonly name: string,

View File

@ -1,4 +1,4 @@
import { Directive, ElementRef, HostListener, Input, OnDestroy, OnInit, Optional, Host, EventEmitter, Output } from '@angular/core';
import { Directive, ElementRef, HostListener, Input, OnDestroy, OnInit, Optional, Host, EventEmitter, Output, SimpleChanges } from '@angular/core';
import { KeyItemGroupDirective } from './key-item-group.directive';
export interface ActionBindings {
@ -15,6 +15,7 @@ interface HTMLKeyItemEntryElement extends HTMLElement {
keyId: number;
active: string;
actionBindings: ActionBindings;
isActive: boolean;
activate: () => void;
deactivate: () => void;
focus: () => void;
@ -26,7 +27,7 @@ interface HTMLKeyItemEntryElement extends HTMLElement {
standalone: true
})
export class KeyItemDirective implements OnInit, OnDestroy {
@Input() isActive = true;
@Input() active = 'active';
@Input() actionBindings: ActionBindings = {};
@Output() activated = new EventEmitter<void>();
@ -57,13 +58,15 @@ export class KeyItemDirective implements OnInit, OnDestroy {
}
ngOnInit() {
const that = this;
this.element.active = this.active;
this.element.activate = () => this.activated.emit();
this.element.deactivate = () => this.deactivated.emit();
this.element.activate = () => that.activated.emit();
this.element.deactivate = () => that.deactivated.emit();
this.element.keyId = this.id;
this.element.actionBindings = this.actionBindings;
this.element.focus = () => this.focused.emit();
this.element.blur = () => this.blurred.emit();
this.element.isActive = this.isActive;
this.element.focus = () => that.focused.emit();
this.element.blur = () => that.blurred.emit();
const position = this.element.getBoundingClientRect();
const item = {
element: this.element,
@ -81,6 +84,12 @@ export class KeyItemDirective implements OnInit, OnDestroy {
}
}
ngOnChanges(changes: SimpleChanges) {
if (changes['isActive']) {
this.element.isActive = this.isActive;
}
}
ngOnDestroy() {
const index = KeyItemDirective.allItems.findIndex(item => item.element.keyId === this.id);
if (index > -1) {
@ -140,13 +149,14 @@ export class KeyItemDirective implements OnInit, OnDestroy {
}
}
if (current) {
const list = allItems.map(item => {
const list = allItems.filter(item => item.element.isActive).map(item => {
const distance = Math.sqrt(Math.pow(item.top - this.current!.top, 2) + Math.pow(item.left - this.current!.left, 2));
const angle = 90 + Math.atan2(item.top - this.current!.top, item.left - this.current!.left) * 180 / Math.PI;
return {
item,
distance,
angle,
isActive: item.element.isActive,
};
})
.filter(item => {
@ -169,12 +179,14 @@ export class KeyItemDirective implements OnInit, OnDestroy {
}
static SetCurrentItem(item?: KeyItemEntry) {
console.log('KeyItemDirective.SetCurrentItem', {item});
if (this.current?.element.active && this.current.element.classList.contains(this.current.element.active)) {
this.current.element.classList.remove(this.current.element.active);
}
this.current?.element.blur();
this.current = item;
this.current?.element.focus();
console.log({focus: this.current?.element.focus.toString()});
if (this.current?.element.active && !this.current.element.classList.contains(this.current.element.active)) {
this.current.element.classList.add(this.current.element.active);
}

View File

@ -12,7 +12,6 @@ export * from './lib/modules/input/models/devices/input-gamepad';
export * from './lib/modules/input/models/devices/input-keyboard';
export * from './lib/modules/navigation/navigation-module';
export * from './lib/modules/navigation/directives/action-binding.directive';