From 21333908ece6ddbb73f29547498c6d0b3d259831 Mon Sep 17 00:00:00 2001 From: isark Date: Tue, 13 Feb 2024 21:19:26 +0100 Subject: [PATCH] (Unreleased) Should now ignore next/prev hotkeys if the overlay isn't visible. Also improved binding/unbinding to be more rxjs-ish --- src/app/_services/overlay.service.ts | 22 +++---- src/app/_services/shortcut.service.ts | 58 ++++++++++-------- src/app/editor/editor.component.html | 5 +- src/app/editor/editor.component.ts | 12 +++- .../plan-display/plan-display.component.ts | 61 +++++++++++++++---- 5 files changed, 105 insertions(+), 53 deletions(-) diff --git a/src/app/_services/overlay.service.ts b/src/app/_services/overlay.service.ts index b15fcfa..360a4dd 100644 --- a/src/app/_services/overlay.service.ts +++ b/src/app/_services/overlay.service.ts @@ -5,6 +5,7 @@ import { Event } from "@tauri-apps/api/event"; import { invoke } from '@tauri-apps/api'; import { ConfigService } from './config.service'; import { appWindow } from '@tauri-apps/api/window'; +import { Subscription } from 'rxjs'; export class StateEvent { Visible?: any; @@ -23,11 +24,18 @@ export class OverlayService { constructor(private shortcuts: ShortcutService, private events: EventsService, private configService: ConfigService) { if (appWindow.label == "Overlay") { - this.shortcuts.register(this.configService.config.toggleOverlay, this.onToggleOverlay.bind(this)); + + this.registerInitialBinds(); + this.events.listen("OverlayStateChange").subscribe(this.onOverlayStateChange.bind(this)); } this.isOverlay = appWindow.label === "Overlay"; + } + registerInitialBinds() { + this.shortcuts.register(this.configService.config.toggleOverlay).subscribe((_shortcut) => { + this.onToggleOverlay() + }); } onOverlayStateChange(event: Event) { @@ -48,16 +56,4 @@ export class OverlayService { 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; - } } diff --git a/src/app/_services/shortcut.service.ts b/src/app/_services/shortcut.service.ts index eef0e8d..2bff581 100644 --- a/src/app/_services/shortcut.service.ts +++ b/src/app/_services/shortcut.service.ts @@ -1,45 +1,53 @@ import { Injectable, NgZone } from '@angular/core'; import { ShortcutHandler, register, unregister } from '@tauri-apps/api/globalShortcut'; -import { EMPTY, from } from 'rxjs'; +import { EMPTY, Observable, Subscriber, TeardownLogic, from } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ShortcutService { - bound: Map = new Map(); + private internalHandlers: Map, () => void]> = new Map, () => void]>(); constructor(private zone: NgZone) { } - register(shortcut: string, handler: ShortcutHandler) { - this.bound.set(handler, shortcut); - return from(register(shortcut, (s) => { - this.zone.run(() => handler(s)); - })); - } + register(shortcut: string) { + return new Observable((subscriber) => { + + let originalHandler: ShortcutHandler = (s) => this.zone.run(() => subscriber.next(s)); + + const teardown = () => { + unregister(shortcut); + this.internalHandlers.delete(shortcut); + }; - unregister(handler: ShortcutHandler) { - const shortcut = this.bound.get(handler); - this.bound.delete(handler); + this.internalHandlers.set(shortcut, [originalHandler, subscriber, teardown]); - return shortcut ? from(unregister(shortcut)) : EMPTY; + register(shortcut, originalHandler) + + return teardown; + }); } - rebind(shortcut: string, handler: ShortcutHandler) { - const prevShortcut = this.bound.get(handler); - this.register(shortcut, handler).subscribe( - { - error: (_err) => { - if (prevShortcut) { - this.register(prevShortcut, handler); - } - return EMPTY; - } - }); + unregister(shortcut: string) { + this.internalHandlers.get(shortcut)?.[1].complete(); + this.internalHandlers.delete(shortcut); } + rebind_from_to(previousShortcut: string, nextShortcut: string) { - const oldHandler = [...this.bound.entries()].find((entry: [ShortcutHandler, string]) => entry[1] === previousShortcut)?.[0]; - this.rebind(nextShortcut, oldHandler!); + let [oldHandler, subscriber, teardown] = this.internalHandlers.get(previousShortcut)!; + + subscriber.remove(teardown); + teardown(); + + teardown = () => { + unregister(nextShortcut); + this.internalHandlers.delete(nextShortcut); + }; + + register(nextShortcut, oldHandler); + + this.internalHandlers.set(nextShortcut, [oldHandler, subscriber, teardown]); } } diff --git a/src/app/editor/editor.component.html b/src/app/editor/editor.component.html index 6ed148f..29b5f9e 100644 --- a/src/app/editor/editor.component.html +++ b/src/app/editor/editor.component.html @@ -74,8 +74,9 @@

Plan

-
+ [cdkDropListEnterPredicate]="canDrop" + [cdkDropListSortPredicate]="sortPredicate.bind(this)"> +
{{areasMap?.get(item.area_key)?.name}}
Act {{areasMap?.get(item.area_key)?.act}}
diff --git a/src/app/editor/editor.component.ts b/src/app/editor/editor.component.ts index 56f688b..974fa2d 100644 --- a/src/app/editor/editor.component.ts +++ b/src/app/editor/editor.component.ts @@ -107,6 +107,10 @@ export class EditorComponent implements OnInit { }); } + sortPredicate(index: number, _item: CdkDrag | CdkDrag) { + return !(this.planElemFilterBounds() && index == 0) + } + dropHandler(event: CdkDragDrop | CdkDragDrop) { if (event.previousContainer === event.container && !isWorldAreaEvent(event)) { 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]) { 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])); } } diff --git a/src/app/plan-display/plan-display.component.ts b/src/app/plan-display/plan-display.component.ts index 8e53f8c..a6a5387 100644 --- a/src/app/plan-display/plan-display.component.ts +++ b/src/app/plan-display/plan-display.component.ts @@ -9,11 +9,13 @@ import { PlanService } from '../_services/plan.service'; import { Plan, PlanElement } from '../_models/plan'; import { WorldAreaService } from '../_services/world-area.service'; import { WorldArea } from '../_models/world-area'; -import { from } from 'rxjs'; +import { Observable, Subscription, from } from 'rxjs'; 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 { EventsService } from '../_services/events.service'; +import { UnlistenFn } from '@tauri-apps/api/event'; +import { Event } from '@tauri-apps/api/event'; @Component({ selector: 'plan-display', @@ -35,6 +37,11 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { init: 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) { window.addEventListener("resize", () => { this.zone.run(() => { @@ -42,6 +49,11 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { }) }); + + + + + appWindow.listen("entered", (entered) => { if (this.planService.currentPlan) { const current = this.planService.currentPlan.current; @@ -65,7 +77,23 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { ngOnInit() { this.worldAreaService.getWorldAreas().subscribe(a => this.worldAreaMap = a); + this.overlayStateChangeHandle = this.events.listen("OverlayStateChange").subscribe(this.onOverlayStateChange.bind(this)); + } + onOverlayStateChange(event: Event) { + 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) { @@ -151,26 +179,35 @@ export class PlanDisplayComponent implements AfterViewInit, OnInit { 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) { this.currentSlides = carousel; this.currentSlides.setIndex(this.slideIndex); - if (this.currentSlides) { - this.shortcut.register(this.configService.config.prev, this.prev.bind(this)); - this.shortcut.register(this.configService.config.next, this.next.bind(this)); - } + this.setupBinds(); } next() { - this.planService.currentPlan!.next(); - this.currentSlides?.next(); - this.zoneSlides?.next(); + if(this.overlayService.visible) { + this.planService.currentPlan!.next(); + this.currentSlides?.next(); + this.zoneSlides?.next(); + } } prev() { - this.planService.currentPlan!.prev(); - this.currentSlides?.prev(); - this.zoneSlides?.prev(); + if(this.overlayService.visible) { + this.planService.currentPlan!.prev(); + this.currentSlides?.prev(); + this.zoneSlides?.prev(); + } } setIndex(index: number) {