(Unreleased) Should now ignore next/prev hotkeys if the overlay isn't visible. Also improved binding/unbinding to be more rxjs-ish

cleanup
isark 1 year ago
parent a8334cbd03
commit 21333908ec

@ -5,6 +5,7 @@ import { Event } from "@tauri-apps/api/event";
import { invoke } from '@tauri-apps/api'; import { invoke } from '@tauri-apps/api';
import { ConfigService } from './config.service'; import { ConfigService } from './config.service';
import { appWindow } from '@tauri-apps/api/window'; import { appWindow } from '@tauri-apps/api/window';
import { Subscription } from 'rxjs';
export class StateEvent { export class StateEvent {
Visible?: any; Visible?: any;
@ -23,11 +24,18 @@ export class OverlayService {
constructor(private shortcuts: ShortcutService, private events: EventsService, private configService: ConfigService) { constructor(private shortcuts: ShortcutService, private events: EventsService, private configService: ConfigService) {
if (appWindow.label == "Overlay") { if (appWindow.label == "Overlay") {
this.shortcuts.register(this.configService.config.toggleOverlay, this.onToggleOverlay.bind(this));
this.registerInitialBinds();
this.events.listen<StateEvent>("OverlayStateChange").subscribe(this.onOverlayStateChange.bind(this)); this.events.listen<StateEvent>("OverlayStateChange").subscribe(this.onOverlayStateChange.bind(this));
} }
this.isOverlay = appWindow.label === "Overlay"; this.isOverlay = appWindow.label === "Overlay";
}
registerInitialBinds() {
this.shortcuts.register(this.configService.config.toggleOverlay).subscribe((_shortcut) => {
this.onToggleOverlay()
});
} }
onOverlayStateChange(event: Event<StateEvent>) { onOverlayStateChange(event: Event<StateEvent>) {
@ -48,16 +56,4 @@ export class OverlayService {
invoke("set_interactable", { interactable: this.interactable }).then(); invoke("set_interactable", { interactable: this.interactable }).then();
} }
onBindToggleOverlayFinish(keys: string[]) {
this.isBinding = false;
let chord = keys.reduce((acc, curr) => {
if (acc === '') return curr;
return acc.concat('+').concat(curr);
}, '');
this.shortcuts.rebind(chord, this.onToggleOverlay);
this.configService.config.toggleOverlay = chord;
}
} }

@ -1,45 +1,53 @@
import { Injectable, NgZone } from '@angular/core'; import { Injectable, NgZone } from '@angular/core';
import { ShortcutHandler, register, unregister } from '@tauri-apps/api/globalShortcut'; import { ShortcutHandler, register, unregister } from '@tauri-apps/api/globalShortcut';
import { EMPTY, from } from 'rxjs'; import { EMPTY, Observable, Subscriber, TeardownLogic, from } from 'rxjs';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class ShortcutService { export class ShortcutService {
bound: Map<ShortcutHandler, string> = new Map<ShortcutHandler, string>(); private internalHandlers: Map<string, [ShortcutHandler, Subscriber<string>, () => void]> = new Map<string, [ShortcutHandler, Subscriber<string>, () => void]>();
constructor(private zone: NgZone) { constructor(private zone: NgZone) {
} }
register(shortcut: string, handler: ShortcutHandler) {
this.bound.set(handler, shortcut);
return from(register(shortcut, (s) => { register(shortcut: string) {
this.zone.run(() => handler(s)); return new Observable<string>((subscriber) => {
}));
} let originalHandler: ShortcutHandler = (s) => this.zone.run(() => subscriber.next(s));
const teardown = () => {
unregister(shortcut);
this.internalHandlers.delete(shortcut);
};
unregister(handler: ShortcutHandler) { this.internalHandlers.set(shortcut, [originalHandler, subscriber, teardown]);
const shortcut = this.bound.get(handler);
this.bound.delete(handler);
return shortcut ? from(unregister(shortcut)) : EMPTY; register(shortcut, originalHandler)
return teardown;
});
} }
rebind(shortcut: string, handler: ShortcutHandler) { unregister(shortcut: string) {
const prevShortcut = this.bound.get(handler); this.internalHandlers.get(shortcut)?.[1].complete();
this.register(shortcut, handler).subscribe( this.internalHandlers.delete(shortcut);
{
error: (_err) => {
if (prevShortcut) {
this.register(prevShortcut, handler);
}
return EMPTY;
}
});
} }
rebind_from_to(previousShortcut: string, nextShortcut: string) { rebind_from_to(previousShortcut: string, nextShortcut: string) {
const oldHandler = [...this.bound.entries()].find((entry: [ShortcutHandler, string]) => entry[1] === previousShortcut)?.[0]; let [oldHandler, subscriber, teardown] = this.internalHandlers.get(previousShortcut)!;
this.rebind(nextShortcut, oldHandler!);
subscriber.remove(teardown);
teardown();
teardown = () => {
unregister(nextShortcut);
this.internalHandlers.delete(nextShortcut);
};
register(nextShortcut, oldHandler);
this.internalHandlers.set(nextShortcut, [oldHandler, subscriber, teardown]);
} }
} }

@ -74,8 +74,9 @@
<h2>Plan</h2> <h2>Plan</h2>
<div cdkDropList #planList [cdkDropListData]="filterPlanElements()" class="list" <div cdkDropList #planList [cdkDropListData]="filterPlanElements()" class="list"
(cdkDropListDropped)="dropHandler($event)" [cdkDropListDisabled]="disabledPlanDD" (cdkDropListDropped)="dropHandler($event)" [cdkDropListDisabled]="disabledPlanDD"
[cdkDropListEnterPredicate]="canDrop"> [cdkDropListEnterPredicate]="canDrop"
<div class="box" *ngFor="let item of filterPlanElements()" cdkDrag (contextmenu)="addNote($event, item)"> [cdkDropListSortPredicate]="sortPredicate.bind(this)">
<div class="box" *ngFor="let item of filterPlanElements(); index as boxIndex" cdkDrag [cdkDragDisabled]="(!!this.planFilterAct.value) && boxIndex == 0" (contextmenu)="addNote($event, item)">
<div class="content"> <div class="content">
<div class="zone-name">{{areasMap?.get(item.area_key)?.name}}</div> <div class="zone-name">{{areasMap?.get(item.area_key)?.name}}</div>
<div class="act">Act {{areasMap?.get(item.area_key)?.act}}</div> <div class="act">Act {{areasMap?.get(item.area_key)?.act}}</div>

@ -107,6 +107,10 @@ export class EditorComponent implements OnInit {
}); });
} }
sortPredicate(index: number, _item: CdkDrag<WorldArea> | CdkDrag<PlanElement>) {
return !(this.planElemFilterBounds() && index == 0)
}
dropHandler(event: CdkDragDrop<WorldArea[]> | CdkDragDrop<PlanElement[]>) { dropHandler(event: CdkDragDrop<WorldArea[]> | CdkDragDrop<PlanElement[]>) {
if (event.previousContainer === event.container && !isWorldAreaEvent(event)) { if (event.previousContainer === event.container && !isWorldAreaEvent(event)) {
const realCurrent = this.plan.plan.indexOf(event.previousContainer.data[event.currentIndex]); const realCurrent = this.plan.plan.indexOf(event.previousContainer.data[event.currentIndex]);
@ -117,7 +121,13 @@ export class EditorComponent implements OnInit {
if (event.container.data.length > 0 && 'connections_world_areas_keys' in event.container.data[0]) { if (event.container.data.length > 0 && 'connections_world_areas_keys' in event.container.data[0]) {
return; return;
} }
this.plan.plan.splice(event.currentIndex, 0, this.planItemFromArea(event.previousContainer.data[event.previousIndex]));
const bounds = this.planElemFilterBounds();
let index = event.currentIndex;
if(bounds) {
index += bounds[0];
}
this.plan.plan.splice(index, 0, this.planItemFromArea(event.previousContainer.data[event.previousIndex]));
} }
} }

@ -9,11 +9,13 @@ import { PlanService } from '../_services/plan.service';
import { Plan, PlanElement } from '../_models/plan'; import { Plan, PlanElement } from '../_models/plan';
import { WorldAreaService } from '../_services/world-area.service'; import { WorldAreaService } from '../_services/world-area.service';
import { WorldArea } from '../_models/world-area'; import { WorldArea } from '../_models/world-area';
import { from } from 'rxjs'; import { Observable, Subscription, from } from 'rxjs';
import { open } from '@tauri-apps/api/dialog'; import { open } from '@tauri-apps/api/dialog';
import { OverlayService } from '../_services/overlay.service'; import { OverlayService, StateEvent } from '../_services/overlay.service';
import { appWindow } from '@tauri-apps/api/window'; import { appWindow } from '@tauri-apps/api/window';
import { EventsService } from '../_services/events.service'; import { EventsService } from '../_services/events.service';
import { UnlistenFn } from '@tauri-apps/api/event';
import { Event } from '@tauri-apps/api/event';
@Component({ @Component({
selector: 'plan-display', selector: 'plan-display',
@ -35,6 +37,11 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
init: boolean = false; init: boolean = false;
hasAttachedOnce: boolean = false; hasAttachedOnce: boolean = false;
overlayStateChangeHandle?: Subscription;
bindsAreSetup: boolean = false;
nextBind?: Subscription;
prevBind?: Subscription;
constructor(private events: EventsService, public configService: ConfigService, private cdr: ChangeDetectorRef, private shortcut: ShortcutService, public planService: PlanService, public worldAreaService: WorldAreaService, public overlayService: OverlayService, private zone: NgZone) { constructor(private events: EventsService, public configService: ConfigService, private cdr: ChangeDetectorRef, private shortcut: ShortcutService, public planService: PlanService, public worldAreaService: WorldAreaService, public overlayService: OverlayService, private zone: NgZone) {
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
this.zone.run(() => { this.zone.run(() => {
@ -42,6 +49,11 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
}) })
}); });
appWindow.listen("entered", (entered) => { appWindow.listen("entered", (entered) => {
if (this.planService.currentPlan) { if (this.planService.currentPlan) {
const current = this.planService.currentPlan.current; const current = this.planService.currentPlan.current;
@ -65,7 +77,23 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
ngOnInit() { ngOnInit() {
this.worldAreaService.getWorldAreas().subscribe(a => this.worldAreaMap = a); this.worldAreaService.getWorldAreas().subscribe(a => this.worldAreaMap = a);
this.overlayStateChangeHandle = this.events.listen<StateEvent>("OverlayStateChange").subscribe(this.onOverlayStateChange.bind(this));
}
onOverlayStateChange(event: Event<StateEvent>) {
if (event.payload.Hidden) {
this.destroyBinds();
} else {
this.setupBinds();
}
}
destroyBinds() {
if (this.bindsAreSetup) {
this.nextBind?.unsubscribe();
this.prevBind?.unsubscribe();
this.bindsAreSetup = false;
}
} }
abs(v: number) { abs(v: number) {
@ -151,26 +179,35 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit {
this.zoneSlides.setIndex(this.slideIndex); this.zoneSlides.setIndex(this.slideIndex);
} }
setupBinds() {
if (this.currentSlides && !this.bindsAreSetup) {
this.nextBind = this.shortcut.register(this.configService.config.prev).subscribe((_shortcut) => this.prev());
this.prevBind = this.shortcut.register(this.configService.config.next).subscribe((_shortcut) => this.next());
this.bindsAreSetup = true;
}
}
registerCurrentSlides(carousel: CarouselComponent<PlanElement>) { registerCurrentSlides(carousel: CarouselComponent<PlanElement>) {
this.currentSlides = carousel; this.currentSlides = carousel;
this.currentSlides.setIndex(this.slideIndex); this.currentSlides.setIndex(this.slideIndex);
if (this.currentSlides) { this.setupBinds();
this.shortcut.register(this.configService.config.prev, this.prev.bind(this));
this.shortcut.register(this.configService.config.next, this.next.bind(this));
}
} }
next() { next() {
this.planService.currentPlan!.next(); if(this.overlayService.visible) {
this.currentSlides?.next(); this.planService.currentPlan!.next();
this.zoneSlides?.next(); this.currentSlides?.next();
this.zoneSlides?.next();
}
} }
prev() { prev() {
this.planService.currentPlan!.prev(); if(this.overlayService.visible) {
this.currentSlides?.prev(); this.planService.currentPlan!.prev();
this.zoneSlides?.prev(); this.currentSlides?.prev();
this.zoneSlides?.prev();
}
} }
setIndex(index: number) { setIndex(index: number) {

Loading…
Cancel
Save