Fix issues with grid key naviation

It should only navigate now when focussed.
Same logic applies to the gallery.
The focus is kept when changing between the gallery and
the grid.

TG-48 #closed

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 08632080cb
commit a86aaef510

@ -43,14 +43,12 @@ export class CoreComponent {
this.stateService.state.subscribe(state => { this.stateService.state.subscribe(state => {
this.appState = state; this.appState = state;
console.log("new state", state);
if (this.appState.tabs.value.length === 0) { if (this.appState.tabs.value.length === 0) {
this.addTab(); this.addTab();
} }
state.tabs.subscribe(tabs => { state.tabs.subscribe(tabs => {
console.log("new tabs", tabs);
this.tabs = tabs; this.tabs = tabs;
console.log(tabs);
if (this.tabs.length === 0) { if (this.tabs.length === 0) {
this.addTab(); this.addTab();
} }
@ -101,7 +99,6 @@ export class CoreComponent {
} }
public async onMouseClickTabLabel(tab: TabState, event: MouseEvent) { public async onMouseClickTabLabel(tab: TabState, event: MouseEvent) {
console.log(event);
if (event.button === 1) { // middle mouse button if (event.button === 1) { // middle mouse button
await this.closeTab(tab); await this.closeTab(tab);
} }

@ -15,6 +15,7 @@ import {MatMenuModule} from "@angular/material/menu";
import { import {
ContentAwareImageComponent ContentAwareImageComponent
} from "./content-aware-image/content-aware-image.component"; } from "./content-aware-image/content-aware-image.component";
import { InputReceiverDirective } from "./input-receiver/input-receiver.directive";
@NgModule({ @NgModule({
@ -23,12 +24,14 @@ import {
BusyIndicatorComponent, BusyIndicatorComponent,
ContextMenuComponent, ContextMenuComponent,
ContentAwareImageComponent, ContentAwareImageComponent,
InputReceiverDirective,
], ],
exports: [ exports: [
ConfirmDialogComponent, ConfirmDialogComponent,
BusyIndicatorComponent, BusyIndicatorComponent,
ContextMenuComponent, ContextMenuComponent,
ContentAwareImageComponent, ContentAwareImageComponent,
InputReceiverDirective,
], ],
imports: [ imports: [
CommonModule, CommonModule,

@ -0,0 +1,8 @@
import { InputReceiverDirective } from './input-receiver.directive';
describe('InputReceiverDirective', () => {
it('should create an instance', () => {
const directive = new InputReceiverDirective();
expect(directive).toBeTruthy();
});
});

@ -0,0 +1,32 @@
import {
Directive,
EventEmitter,
HostBinding,
HostListener,
Output
} from "@angular/core";
@Directive({
selector: "[appInputReceiver]"
})
export class InputReceiverDirective {
constructor() {
}
@Output() keyDownEvent = new EventEmitter<KeyboardEvent>();
@Output() keyUpEvent = new EventEmitter<KeyboardEvent>();
@HostBinding("tabindex") tabIndex = 1;
@HostListener("keydown", ["$event"])
onKeyDown(event: KeyboardEvent) {
this.keyDownEvent.emit(event);
}
@HostListener("keyup", ["$event"])
onKeyUp(event: KeyboardEvent) {
this.keyUpEvent.emit(event);
}
}

@ -1,4 +1,4 @@
<div class="gallery-container" fxLayout="column"> <div class="gallery-container" #inner fxLayout="column" appInputReceiver (keyDownEvent)="handleKeydownEvent($event)">
<button (click)="this.closeEvent.emit(this)" class="close-button" mat-icon-button> <button (click)="this.closeEvent.emit(this)" class="close-button" mat-icon-button>
<ng-icon name="mat-close"></ng-icon> <ng-icon name="mat-close"></ng-icon>
</button> </button>

@ -1,5 +1,6 @@
import { import {
Component, AfterContentInit, AfterViewInit,
Component, ElementRef,
EventEmitter, EventEmitter,
HostListener, HostListener,
Input, Input,
@ -21,7 +22,7 @@ import {TabService} from "../../../../../services/tab/tab.service";
templateUrl: "./file-gallery.component.html", templateUrl: "./file-gallery.component.html",
styleUrls: ["./file-gallery.component.scss"] styleUrls: ["./file-gallery.component.scss"]
}) })
export class FileGalleryComponent implements OnChanges, OnInit { export class FileGalleryComponent implements OnChanges, OnInit, AfterViewInit {
@Input() files: File[] = []; @Input() files: File[] = [];
@Input() preselectedFile: File | undefined; @Input() preselectedFile: File | undefined;
@ -31,6 +32,7 @@ export class FileGalleryComponent implements OnChanges, OnInit {
entries: Selectable<File>[] = []; entries: Selectable<File>[] = [];
@ViewChild("virtualScroll") virtualScroll!: CdkVirtualScrollViewport; @ViewChild("virtualScroll") virtualScroll!: CdkVirtualScrollViewport;
@ViewChild("inner") inner!: ElementRef<HTMLDivElement>;
public selectedFile: Selectable<File> | undefined; public selectedFile: Selectable<File> | undefined;
public fileContentUrl: SafeResourceUrl | undefined; public fileContentUrl: SafeResourceUrl | undefined;
@ -50,6 +52,10 @@ export class FileGalleryComponent implements OnChanges, OnInit {
} }
} }
public ngAfterViewInit(): void {
this.focus();
}
public async ngOnChanges(changes: SimpleChanges): Promise<void> { public async ngOnChanges(changes: SimpleChanges): Promise<void> {
if (changes["files"]) { if (changes["files"]) {
this.entries = this.files.map( this.entries = this.files.map(
@ -138,6 +144,24 @@ export class FileGalleryComponent implements OnChanges, OnInit {
} }
} }
public focus() {
this.inner.nativeElement.focus();
}
public async handleKeydownEvent(event: KeyboardEvent) {
switch (event.key) {
case "ArrowRight":
await this.nextItem();
break;
case "ArrowLeft":
await this.previousItem();
break;
case "Escape":
this.onEscapeClick();
break;
}
}
private scrollToSelection(): void { private scrollToSelection(): void {
if (this.selectedFile) { if (this.selectedFile) {
const selectedIndex = this.entries.indexOf(this.selectedFile); const selectedIndex = this.entries.indexOf(this.selectedFile);
@ -174,20 +198,4 @@ export class FileGalleryComponent implements OnChanges, OnInit {
setTimeout(() => this.escapeCount--, 500); setTimeout(() => this.escapeCount--, 500);
} }
} }
@HostListener("window:keydown", ["$event"])
private async handleKeydownEvent(event: KeyboardEvent) {
switch (event.key) {
case "ArrowRight":
await this.nextItem();
break;
case "ArrowLeft":
await this.previousItem();
break;
case "Escape":
this.onEscapeClick();
break;
}
}
} }

@ -1,4 +1,4 @@
<div class="file-gallery-inner"> <div class="file-gallery-inner" #inner appInputReceiver (keyDownEvent)="handleKeydownEvent($event)" (keyUpEvent)="handleKeyupEvent($event)">
<cdk-virtual-scroll-viewport #virtualScrollGrid class="file-scroll" itemSize="260" maxBufferPx="2000" <cdk-virtual-scroll-viewport #virtualScrollGrid class="file-scroll" itemSize="260" maxBufferPx="2000"
minBufferPx="500"> minBufferPx="500">
<div *cdkVirtualFor="let rowEntry of partitionedGridEntries"> <div *cdkVirtualFor="let rowEntry of partitionedGridEntries">
@ -15,7 +15,7 @@
</div> </div>
<app-file-context-menu #fileContextMenu> <app-file-context-menu #fileContextMenu>
<button (click)="this.fileOpenEvent.emit(fileContextMenu.file)" mat-menu-item content-before>Open</button> <button (click)="this.fileOpenEvent.emit(fileContextMenu.file)" mat-menu-item content-before="">Open</button>
<button (click)="this.regenerateThumbnail(fileContextMenu.file)" mat-menu-item>Regenerate thumbnail</button> <button (click)="this.regenerateThumbnail(fileContextMenu.file)" mat-menu-item>Regenerate thumbnail</button>
</app-file-context-menu> </app-file-context-menu>

@ -1,4 +1,5 @@
import { import {
AfterContentInit, AfterViewInit,
Component, Component,
ElementRef, ElementRef,
EventEmitter, EventEmitter,
@ -22,7 +23,7 @@ import {Selectable} from "../../../../../models/Selectable";
templateUrl: "./file-grid.component.html", templateUrl: "./file-grid.component.html",
styleUrls: ["./file-grid.component.scss"] styleUrls: ["./file-grid.component.scss"]
}) })
export class FileGridComponent implements OnChanges, OnInit { export class FileGridComponent implements OnChanges, OnInit, AfterViewInit {
@Input() files: File[] = []; @Input() files: File[] = [];
@Input() columns: number = 6; @Input() columns: number = 6;
@ -31,7 +32,7 @@ export class FileGridComponent implements OnChanges, OnInit {
@Output() fileSelectEvent = new EventEmitter<File[]>(); @Output() fileSelectEvent = new EventEmitter<File[]>();
@ViewChild("virtualScrollGrid") virtualScroll!: CdkVirtualScrollViewport; @ViewChild("virtualScrollGrid") virtualScroll!: CdkVirtualScrollViewport;
@ViewChild("galleryWrapper") galleryWrapper!: ElementRef<HTMLDivElement>; @ViewChild("inner") inner!: ElementRef<HTMLDivElement>;
selectedEntries: Selectable<File>[] = []; selectedEntries: Selectable<File>[] = [];
partitionedGridEntries: Selectable<File>[][] = []; partitionedGridEntries: Selectable<File>[][] = [];
@ -52,6 +53,10 @@ export class FileGridComponent implements OnChanges, OnInit {
this.setPartitionedGridEntries(); this.setPartitionedGridEntries();
} }
public ngAfterViewInit(): void {
this.focus();
}
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes["files"]) { if (changes["files"]) {
this.gridEntries = this.files.map( this.gridEntries = this.files.map(
@ -211,7 +216,6 @@ export class FileGridComponent implements OnChanges, OnInit {
const viewportSize = this.virtualScroll.getViewportSize(); const viewportSize = this.virtualScroll.getViewportSize();
let offsetTop = this.virtualScroll.measureScrollOffset("top"); let offsetTop = this.virtualScroll.measureScrollOffset("top");
const contentOffset = Math.floor(selectedIndex / this.columns) * 260; const contentOffset = Math.floor(selectedIndex / this.columns) * 260;
console.log(offsetTop, contentOffset, viewportSize);
if (contentOffset > offsetTop + viewportSize - 300 || contentOffset < offsetTop) { if (contentOffset > offsetTop + viewportSize - 300 || contentOffset < offsetTop) {
this.virtualScroll.scrollToIndex(Math.floor(selectedIndex / this.columns)); this.virtualScroll.scrollToIndex(Math.floor(selectedIndex / this.columns));
@ -224,22 +228,11 @@ export class FileGridComponent implements OnChanges, OnInit {
} }
} }
private pageDown() { public focus() {
if (this.virtualScroll) { this.inner.nativeElement.focus();
const offsetTop = this.virtualScroll.measureScrollOffset("top");
this.virtualScroll.scrollToOffset(offsetTop + this.virtualScroll.getViewportSize());
}
}
private pageUp() {
if (this.virtualScroll) {
const offsetTop = this.virtualScroll.measureScrollOffset("top");
this.virtualScroll.scrollToOffset(offsetTop - this.virtualScroll.getViewportSize());
}
} }
@HostListener("window:keydown", ["$event"]) public handleKeydownEvent(event: KeyboardEvent) {
private handleKeydownEvent(event: KeyboardEvent) {
this.shiftClicked ||= event.shiftKey; this.shiftClicked ||= event.shiftKey;
this.ctrlClicked ||= event.ctrlKey; this.ctrlClicked ||= event.ctrlKey;
@ -279,9 +272,22 @@ export class FileGridComponent implements OnChanges, OnInit {
} }
} }
@HostListener("window:keyup", ["$event"]) public handleKeyupEvent(event: KeyboardEvent) {
private handleKeyupEvent(event: KeyboardEvent) {
this.shiftClicked = event.shiftKey? false : this.shiftClicked; this.shiftClicked = event.shiftKey? false : this.shiftClicked;
this.ctrlClicked = event.ctrlKey? false : this.ctrlClicked; this.ctrlClicked = event.ctrlKey? false : this.ctrlClicked;
} }
private pageDown() {
if (this.virtualScroll) {
const offsetTop = this.virtualScroll.measureScrollOffset("top");
this.virtualScroll.scrollToOffset(offsetTop + this.virtualScroll.getViewportSize());
}
}
private pageUp() {
if (this.virtualScroll) {
const offsetTop = this.virtualScroll.measureScrollOffset("top");
this.virtualScroll.scrollToOffset(offsetTop - this.virtualScroll.getViewportSize());
}
}
} }

@ -1,7 +1,7 @@
<app-file-grid (fileOpenEvent)="this.onFileOpen($event)" (fileSelectEvent)="this.onFileSelect($event)" <app-file-grid #fileGrid (fileOpenEvent)="this.onFileOpen($event)" (fileSelectEvent)="this.onFileSelect($event)"
*ngIf="this.mode === 'grid'" *ngIf="this.mode === 'grid'"
[files]="this.files" [preselectedFile]="this.preselectedFile"></app-file-grid> [files]="this.files" [preselectedFile]="this.preselectedFile"></app-file-grid>
<app-file-gallery (closeEvent)="this.mode = 'grid'" (fileSelectEvent)="this.onSinglefileSelect($event)" <app-file-gallery #fileGallery (closeEvent)="this.mode = 'grid'" (fileSelectEvent)="this.onSinglefileSelect($event)"
*ngIf="this.mode === 'gallery'" *ngIf="this.mode === 'gallery'"
[files]="this.files" [files]="this.files"
[preselectedFile]="this.preselectedFile"></app-file-gallery> [preselectedFile]="this.preselectedFile"></app-file-gallery>

@ -1,5 +1,14 @@
import {Component, EventEmitter, Input, Output} from "@angular/core"; import {
Component,
ElementRef,
EventEmitter,
Input,
Output,
ViewChild
} from "@angular/core";
import {File} from "../../../../models/File"; import {File} from "../../../../models/File";
import {FileGalleryComponent} from "./file-gallery/file-gallery.component";
import {FileGridComponent} from "./file-grid/file-grid.component";
@Component({ @Component({
selector: "app-file-multiview", selector: "app-file-multiview",
@ -14,6 +23,9 @@ export class FileMultiviewComponent {
@Output() fileOpenEvent = new EventEmitter<File>(); @Output() fileOpenEvent = new EventEmitter<File>();
@Output() fileSelectEvent = new EventEmitter<File[]>(); @Output() fileSelectEvent = new EventEmitter<File[]>();
@ViewChild(FileGalleryComponent) fileGallery!: FileGalleryComponent;
@ViewChild(FileGridComponent) fileGrid!: FileGridComponent;
public selectedFiles: File[] = []; public selectedFiles: File[] = [];
public preselectedFile: File | undefined; public preselectedFile: File | undefined;

@ -20,7 +20,6 @@ export class FileService {
} }
public async findFiles(filters: FilterExpression[], sortBy: SortKey[]): Promise<File[]> { public async findFiles(filters: FilterExpression[], sortBy: SortKey[]): Promise<File[]> {
console.log(filters);
let backendFilters = filters.map(f => f.toBackendType()); let backendFilters = filters.map(f => f.toBackendType());
return await invoke<File[]>("plugin:mediarepo|find_files", return await invoke<File[]>("plugin:mediarepo|find_files",
{ {

Loading…
Cancel
Save