Reworked to reduce some unnecessary re-rendering but still laggy on linux!!

main 1.8.5
isark 1 year ago
parent e1cdbd566a
commit 84695203ea

@ -8,7 +8,7 @@
},
"package": {
"productName": "Nothing",
"version": "1.8.4"
"version": "1.8.5"
},
"tauri": {
"systemTray": {

@ -34,7 +34,13 @@ export class Plan {
private selfSaveSubject: Subject<void> = new Subject<void>();
constructor(plan: PlanInterface) {
constructor(plan?: PlanInterface) {
if(!plan) {
this.plan = [];
this.current = 0;
return;
};
this.plan = plan.plan;
this.current = plan.current;
if (plan.stored_path) {

@ -5,7 +5,7 @@
<button mat-raised-button color="primary" (click)="loadBasePlan()">Load base plan preset</button>
</div>
<div class="d-flex flex-column overflow-hidden p-4">
<div class="d-flex flex-column overflow-hidden p-4 grow">
<div class="row">
<div class="col-6">
<div class="row">
@ -28,13 +28,13 @@
<div class="row">
<mat-form-field>
<mat-label>Area filter</mat-label>
<input matInput type="text" [(ngModel)]="planSearchString">
<input matInput type="text" [ngModel]="planSearchString" (ngModelChange)="planSearchStringChange($event)">
</mat-form-field>
</div>
<div class="row">
<mat-form-field>
<mat-label>Act filter</mat-label>
<mat-select [(value)]="planFilterAct">
<mat-select [value]="planFilterAct" (valueChange)="planFilterActChange($event)">
<mat-option *ngFor="let item of acts" [value]="item">{{item.name}}</mat-option>
</mat-select>
</mat-form-field>
@ -45,7 +45,8 @@
Auto scroll to latest
</mat-slide-toggle>
<mat-slide-toggle class="col-4" color="accent" [(ngModel)]="reverseDisplay">
<mat-slide-toggle class="col-4" color="accent" [ngModel]="reverseDisplay"
(ngModelChange)="reverseDisplayChange($event)">
Reverse display
</mat-slide-toggle>
<div class="col-3 d-flex justify-content-end ">
@ -56,26 +57,31 @@
</div>
<div cdkDropListGroup *ngIf="areas" class="row overflow-hidden">
<div cdkDropListGroup class="row h-100 overflow-hidden">
<div class="col-6 d-flex flex-column h-100">
<div class="col-6 d-flex flex-column h-100 grow">
<h2>Campaign zones</h2>
<div cdkDropList [cdkDropListData]="filterAreas()" class="list areas" cdkDropListSortingDisabled
<div cdkDropList [cdkDropListData]="filterAreas()" class="list h-100 areas" cdkDropListSortingDisabled
(cdkDropListDropped)="dropHandler($event)">
<div class="box" *ngFor="let item of filterAreas()" cdkDrag (dblclick)="doubleClickArea(item)">
<div class="box" *ngFor="let item of filterAreas(); index as boxindex" cdkDrag
(dblclick)="doubleClickArea(item)">
<div class="zone-name">{{item.name}}</div>
<div class="act">Act {{item.act}}</div>
</div>
</div>
</div>
<div class="col-6 d-flex flex-column h-100">
<div class="col-6 d-flex flex-column h-100 grow">
<h2>Plan</h2>
<div cdkDropList #planList [cdkDropListData]="filterPlanElements()" class="list"
<div appendOnly #planListElement cdkDropList [cdkDropListData]="(this.latestList | async)!" class="list h-100"
(cdkDropListDropped)="dropHandler($event)" [cdkDropListDisabled]="disabledPlanDD"
[cdkDropListEnterPredicate]="canDrop" [cdkDropListSortPredicate]="sortPredicate.bind(this)">
<div class="box" *ngFor="let item of filterPlanElements(); index as boxIndex" cdkDrag
<div class="box" *ngFor="let item of latestList | async; index as boxIndex" cdkDrag
[cdkDragDisabled]="(!!this.planFilterAct.value) && boxIndex == 0" (contextmenu)="addNote($event, item)">
<div class="content">
<div class="content-left">
@ -93,7 +99,8 @@
<div class="delete" (click)="remove(item)">+</div>
</div>
</div>
<div cdkDropList [cdkDropListData]="planInEditing.plan" class="list end"
<div cdkDropList [cdkDropListData]="(this.latestList | async) !" class="list end"
style="position: relative;display: flex; flex-direction: column; justify-content: center;"
(cdkDropListDropped)="dropEndHandler($event)">
<span style="position: absolute; ">Place at end of list</span>

@ -30,6 +30,11 @@
width: 100%;
}
.grow {
flex-grow: 1;
}
.box {
position: relative;
border-bottom: solid 1px map.get(palette.$nothing-dark-map, 50);
@ -44,9 +49,12 @@
padding: 20px 20px 20px 5px;
width: 100%;
overflow-x: hidden;
&:hover {
background-color: rgba(0, 0, 0, 0.1);
}
height: 80px;
}
.buttons {
@ -70,8 +78,8 @@
width: 15px;
height: 15px;
overflow: visible;
&:hover {
&:hover {
cursor: pointer;
}
}
@ -106,6 +114,7 @@
.list.cdk-drop-list-dragging .box:not(.cdk-drag-placeholder) {
transition: transform 125ms cubic-bezier(0, 0, 0.2, 1);
}
.right-settings {
gap: 8px;
}

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, OnChanges, OnInit, QueryList, SimpleChanges, ViewChild, ViewChildren } from '@angular/core';
import {
CdkDrag,
@ -14,7 +14,7 @@ import { Plan, PlanElement } from '../_models/plan';
import { WorldAreaService } from '../_services/world-area.service';
import { FormsModule } from '@angular/forms';
import { Fuzzr } from '../fuzzr/fuzzr';
import { from } from 'rxjs';
import { BehaviorSubject, first, from, skip } from 'rxjs';
import { save } from '@tauri-apps/api/dialog';
import { PlanService } from '../_services/plan.service';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
@ -47,12 +47,11 @@ interface Act {
MatInputModule,
MatSelectModule,
MatButtonModule,
MatSlideToggleModule
MatSlideToggleModule,
],
providers: []
})
export class EditorComponent implements OnInit {
planInEditing: Plan;
areas?: WorldArea[];
planAreas: WorldArea[];
areasMap?: Map<String, WorldArea>;
@ -62,22 +61,22 @@ export class EditorComponent implements OnInit {
filterAct: Act;
planFilterAct: Act;
acts: Act[];
@ViewChild('planList') planListElement!: ElementRef;
@ViewChild('planListElement') planListElement!: ElementRef;
autoScrollToEnd: boolean;
reverseDisplay: boolean;
disabledPlanDD: boolean;
original: PlanElement[] = [];
latestList: BehaviorSubject<PlanElement[]> = new BehaviorSubject<PlanElement[]>([]);
constructor(public worldAreaService: WorldAreaService, private cdr: ChangeDetectorRef, private planService: PlanService, public dialog: MatDialog) {
this.planInEditing = new Plan({
plan: [],
current: 0
});
this.disabledPlanDD = false;
this.autoScrollToEnd = false;
this.planFuzzer = new Fuzzr(this.planInEditing.plan, {
this.latestList = new BehaviorSubject<any[]>([]);
this.planFuzzer = new Fuzzr(this.original, {
toString: (e: PlanElement) => {
return this.areasMap?.get(e.area_key)?.name;
}
@ -100,6 +99,19 @@ export class EditorComponent implements OnInit {
this.planFilterAct = this.acts[0];
}
planSearchStringChange(value: string) {
this.planSearchString = value;
this.filterPlanElements();
}
planFilterActChange(value: Act) {
this.planFilterAct = value;
this.filterPlanElements();
}
reverseDisplayChange(value: boolean) {
this.reverseDisplay = value;
this.filterPlanElements();
}
ngOnInit(): void {
this.worldAreaService.getWorldAreas().subscribe(worldAreas => {
this.areas = [...worldAreas.values()];
@ -113,32 +125,45 @@ export class EditorComponent implements OnInit {
dropHandler(event: CdkDragDrop<WorldArea[]> | CdkDragDrop<PlanElement[]>) {
if (event.previousContainer === event.container && !isWorldAreaEvent(event)) {
const realCurrent = this.planInEditing.plan.indexOf(event.previousContainer.data[event.currentIndex]);
const realPrev = this.planInEditing.plan.indexOf(event.previousContainer.data[event.previousIndex]);
moveItemInArray(this.planInEditing.plan, realPrev, realCurrent);
} else
if (this.planInEditing && this.areas && isWorldAreaEvent(event)) {
const realCurrent = this.original.indexOf(event.previousContainer.data[event.currentIndex]);
const realPrev = this.original.indexOf(event.previousContainer.data[event.previousIndex]);
moveItemInArray(this.original, realPrev, realCurrent);
} else {
if (this.areas && isWorldAreaEvent(event)) {
if (event.container.data.length > 0 && 'connections_world_areas_keys' in event.container.data[0]) {
return;
}
const bounds = this.planElemFilterBounds();
let index = event.currentIndex;
let index = event.item.data;
if (bounds) {
index += bounds[0];
}
this.planInEditing.plan.splice(index, 0, this.planItemFromArea(event.previousContainer.data[event.previousIndex]));
this.original.splice(index, 0, this.planItemFromArea(event.previousContainer.data[event.previousIndex]));
}
}
this.latestList.pipe(skip(1)).pipe(first()).subscribe(() => {
this.scrollToEnd();
});
this.filterPlanElements();
}
dropEndHandler(event: CdkDragDrop<WorldArea[]> | CdkDragDrop<PlanElement[]>) {
dropEndHandler(event: CdkDragDrop<WorldArea[]> | CdkDragDrop<PlanElement[]> | null) {
if (event == null) return;
if (isWorldAreaEvent(event) && this.areas) {
this.planInEditing.plan.splice(this.getEnd(), 0, this.planItemFromArea(event.previousContainer.data[event.previousIndex]));
this.original.splice(this.getEnd(), 0, this.planItemFromArea(event.previousContainer.data[event.previousIndex]));
} else {
moveItemInArray(this.planInEditing.plan, event.previousIndex, this.getEnd());
moveItemInArray(this.original, event.previousIndex, this.getEnd());
}
this.scrollToEnd();
this.latestList.pipe(skip(1)).pipe(first()).subscribe(() => {
this.scrollToEnd();
});
this.filterPlanElements();
}
getEnd() {
@ -146,12 +171,14 @@ export class EditorComponent implements OnInit {
if (bounds) {
return bounds[1];
} else {
return this.planInEditing.plan.length;
return this.original.length;
}
}
remove(item: PlanElement) {
this.planInEditing.plan.splice(this.planIndexOf(item), 1);
this.original.splice(this.planIndexOf(item), 1);
this.filterPlanElements();
}
canDrop = () => {
@ -181,28 +208,36 @@ export class EditorComponent implements OnInit {
if (!this.autoScrollToEnd) {
return;
}
this.cdr.detectChanges();
if (!this.reverseDisplay) {
this.planListElement.nativeElement.scrollTop = this.planListElement.nativeElement.scrollHeight;
} else {
this.planListElement.nativeElement.scrollTop = 0;
}
}
doubleClickArea(item: WorldArea) {
this.planInEditing.plan.splice(this.planInEditing.plan.length, 0, this.planItemFromArea(item));
this.scrollToEnd();
this.original.splice(this.original.length, 0, this.planItemFromArea(item));
this.latestList.pipe(skip(1)).pipe(first()).subscribe((_) => {
this.scrollToEnd();
});
this.filterPlanElements();
}
planElemFilterBounds() {
if (this.planFilterAct.value !== 0) {
let bounds = this.planInEditing.plan.filter(item => item.anchor_act === this.planFilterAct.value || this.planFilterAct.value + 1 === item.anchor_act).map((value) => this.planIndexOf(value));
let bounds = this.original.filter(item => item.anchor_act === this.planFilterAct.value || this.planFilterAct.value + 1 === item.anchor_act).map((value) => this.planIndexOf(value));
if (bounds.length == 2) {
return bounds;
}
if (bounds.length == 1 && this.planFilterAct.value == 10) {
bounds[1] = this.planInEditing.plan.length;
bounds[1] = this.original.length;
return bounds;
}
}
@ -218,7 +253,6 @@ export class EditorComponent implements OnInit {
this.disabledPlanDD = false;
}
if (this.planSearchString !== "" || this.planFilterAct.value != 0) {
let bounds = this.planElemFilterBounds();
@ -237,24 +271,22 @@ export class EditorComponent implements OnInit {
}
} else {
return this.planInEditing.plan;
return this.original;
}
}
if (this.reverseDisplay) {
return value().slice().reverse();
} else {
return value();
}
this.latestList.next([... (this.reverseDisplay ? value().slice().reverse() : value())]);
}
planIndexOf(planElement: PlanElement) {
const index = this.planInEditing.plan.indexOf(planElement);
return index;
return this.original.indexOf(planElement);
}
clearPlan() {
this.planInEditing.plan.length = 0;
while (this.original.length > 0) {
this.original.pop();
}
this.filterPlanElements();
this.cdr.detectChanges();
}
@ -266,7 +298,9 @@ export class EditorComponent implements OnInit {
}]
})).subscribe(file => {
if (file) {
this.planService.savePlanAtPath(file, this.planInEditing).subscribe();
const plan = new Plan();
plan.plan = [...this.original];
this.planService.savePlanAtPath(file, plan).subscribe();
}
});
}
@ -284,8 +318,8 @@ export class EditorComponent implements OnInit {
if (file) {
// We disallow multiple but interface still says it can be multiple, thus the cast.
this.planService.loadPlanFromPath(file as string, false).subscribe(plan => {
this.planInEditing.plan.length = 0;
plan.plan.forEach(p => this.planInEditing.plan.push(p));
plan.plan.forEach(item => this.original.push(item));
this.filterPlanElements();
});
}
});
@ -293,8 +327,8 @@ export class EditorComponent implements OnInit {
loadBasePlan() {
this.planService.getBasePlan().subscribe(plan => {
this.planInEditing.plan.length = 0;
plan.plan.forEach(p => this.planInEditing.plan.push(p));
plan.plan.forEach(item => this.original.push(item));
this.filterPlanElements();
})
}

@ -1,17 +1,15 @@
<div class="NOTES_COMPONENT">
<div class="container">
<span>Edit note </span><span style="color: grey; font-size: 0.9em;">(supports markdown)</span>
<div class="left">
<textarea [(ngModel)]="note" cols="50" rows="10"></textarea>
</div>
<span><span>Preview </span><span style="color: grey; font-size: 0.9em;">(Unscaled)</span></span>
<div class="right">
<notes [note]="note"></notes>
</div>
<div class="container">
<span>Edit note </span><span style="color: grey; font-size: 0.9em;">(supports markdown)</span>
<div>
<textarea [(ngModel)]="note" cols="50" rows="10"></textarea>
</div>
<div mat-dialog-actions>
<button mat-button color="warn" (click)="cancel()">Cancel</button>
<button mat-button (click)="save()" cdkFocusInitial>Save</button>
<span><span>Preview </span><span style="color: grey; font-size: 0.9em;">(Unscaled)</span></span>
<div class="w-100">
<div class="display-component" [innerHTML]="md.render(note ?? '')"></div>
</div>
</div>
<div mat-dialog-actions>
<button mat-button color="warn" (click)="cancel()">Cancel</button>
<button mat-button (click)="save()" cdkFocusInitial>Save</button>
</div>

@ -0,0 +1,24 @@
.container {
display: flex;
flex-direction: column;
height: 700px;
width: 500px;
align-items: center;
}
img {
display: block;
width: 100%;
height: 100%;
max-height: 100%;
object-fit: contain;
}
.display-component {
max-height: 100%;
height: 100%;
display: grid;
grid-auto-flow: row;
grid-template-rows: repeat(auto-fit, minmax(50px, 1fr));
width: 100%;
}

@ -1 +1 @@
<div #ref *ngIf="note" class="NOTES_COMPONENT display-component" [innerHTML]="md.render(note)"></div>
<div #ref *ngIf="note" class="display-component" [innerHTML]="md.render(this.note)"></div>

@ -1,30 +1,30 @@
.NOTES_COMPONENT {
.container {
display: flex;
flex-direction: column;
}
img {
display: block;
width: 100%;
height: 100%;
max-height: 100%;
object-fit: contain;
}
.container {
display: flex;
flex-direction: column;
height: 800px;
width: 1200px;
}
&.display-component {
max-height: 100%;
height: 100%;
display: grid;
grid-auto-flow: row;
grid-template-rows: repeat(auto-fit, minmax(50px, 1fr));
}
img {
display: block;
width: 100%;
height: 100%;
max-height: 100%;
object-fit: contain;
}
& {
font-size: 1.3em;
}
.display-component {
max-height: 100%;
height: 100%;
display: grid;
grid-auto-flow: row;
grid-template-rows: repeat(auto-fit, minmax(50px, 1fr));
font-size: 1.3em;
}
.note-preview {
min-height: 400px;
}
.note-preview {
min-height: 400px;
max-height: 400px;
min-width: 600px;
max-width: 600px;
}

@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, Inject, Input, ViewChild, ViewEncapsulation } from '@angular/core';
import { Component, ElementRef, Inject, Input, ViewChild, } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
@ -19,26 +19,25 @@ interface DialogData {
styleUrls: ['./notes.component.scss'],
standalone: true,
imports: [CommonModule, FormsModule, MatButtonModule],
encapsulation: ViewEncapsulation.None,
})
export class NotesComponent implements AfterViewInit {
export class NotesComponent {
@Input()
note?: string;
@ViewChild("ref")
ref?: ElementRef
constructor(public md: MarkdownService) {}
ngAfterViewInit(): void {
}
realValue: any;
constructor(public md: MarkdownService) {}
}
@Component({
selector: 'notes-editor',
templateUrl: 'edit-notes.component.html',
styleUrls: ['./edit-notes.component.scss'],
standalone: true,
imports: [CommonModule, FormsModule, MatDialogModule, MatFormFieldModule, MatInputModule, FormsModule, MatButtonModule, NotesComponent, ScalableComponent],
encapsulation: ViewEncapsulation.None,
})
export class EditNotesComponentDialog {
note?: string;
@ -46,6 +45,7 @@ export class EditNotesComponentDialog {
constructor(
public dialogRef: MatDialogRef<EditNotesComponentDialog>,
@Inject(MAT_DIALOG_DATA) public data: DialogData,
public md: MarkdownService
) {
if (data.note) {
this.note = `${data.note}`;

Loading…
Cancel
Save