Skip to content

Commit

Permalink
Merge pull request #213 from NakulManchanda/start-menu-keybindings
Browse files Browse the repository at this point in the history
zowe desktop start menu keybindings
  • Loading branch information
1000TurquoisePogs authored Apr 23, 2020
2 parents 0fb4231 + 6550e1e commit 6fadb5f
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 17 deletions.
20 changes: 18 additions & 2 deletions virtual-desktop/src/app/context-menu/context-menu.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
ViewChild,
ViewChildren,
AfterViewInit,
QueryList
QueryList,
} from '@angular/core';

import { ContextMenuItem } from 'pluginlib/inject-resources';
Expand Down Expand Up @@ -136,7 +136,7 @@ export class ContextMenuComponent implements AfterViewInit {

constructor(
private elementRef: ElementRef,
private sanitizer:DomSanitizer
private sanitizer:DomSanitizer,
) {
this.complete = new EventEmitter<void>();
}
Expand Down Expand Up @@ -318,13 +318,17 @@ export class ContextMenuComponent implements AfterViewInit {
newIndex = mod(newIndex - 1, this.menuItems.length)
}
this.activeIndex = newIndex;
event.stopPropagation();
event.preventDefault();
break;
case 'ArrowDown':
newIndex = mod(this.activeIndex + 1, this.menuItems.length)
while (this.menuItems[newIndex].disabled) {
newIndex = mod(newIndex + 1, this.menuItems.length)
}
this.activeIndex = newIndex;
event.stopPropagation();
event.preventDefault();
break;
case 'ArrowRight':
if (this._propagateChildLeft) {
Expand All @@ -341,6 +345,8 @@ export class ContextMenuComponent implements AfterViewInit {
}, 0)
}
}
event.stopPropagation();
event.preventDefault();
break;
case 'ArrowLeft':
if (this._propagateChildLeft) {
Expand All @@ -357,6 +363,8 @@ export class ContextMenuComponent implements AfterViewInit {
this.setActiveIndex(-1);
}
}
event.stopPropagation();
event.preventDefault();
break;
case 'Enter':
let item = this.menuItems[this.activeIndex];
Expand All @@ -366,10 +374,18 @@ export class ContextMenuComponent implements AfterViewInit {
if (!item.preventCloseMenu) {
this.closeContextMenu();
}
event.stopPropagation();
event.preventDefault();
break;
case 'Escape':
this.closeContextMenu();
event.stopPropagation();
event.preventDefault();
break;
}
}
}

}


Expand Down
3 changes: 0 additions & 3 deletions virtual-desktop/src/app/context-menu/context-menu.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@ import { CommonModule } from '@angular/common';
],
exports: [
ContextMenuComponent
],
providers: [
]
})
export class ContextMenuModule {

}


/*
This program and the accompanying materials are
made available under the terms of the Eclipse Public License v2.0 which accompanies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@
left: 45px;
}

.launch-widget-row.active {
background-color: #a6a6a6;
}

/*
This program and the accompanying materials are
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,23 @@
</div>

<ng-template [ngIf]="isActive">
<div class="launch-widget-popup">
<div class="launch-widget-popup" (click)="setSearchFocus();">
<div style="cursor: pointer" class="refresh-plugins" (click)="refresh()">
{{translation.translate("Refresh Applications")}}
<i style="padding: 5px" class="fa fa-refresh" (click)="refresh()"></i>
</div>
<div style="margin: 20px"></div>
<div class="launch-menu-scroller">
<div class="launch-widget-row" *ngFor="let item of displayItems | sortBy: 'label'" (click)="clicked(item)" [title]="item.tooltip" (contextmenu)="onRightClick($event, item)">
<img class="icon" [src]="item.image">
<p>{{item.label}}</p>
</div>
<div #menudiv class="launch-menu-scroller">
<div class="launch-widget-row" *ngFor="let item of displayItems | sortBy: 'label'; let i = index;"
(click)="clicked(item)" [title]="item.tooltip" (contextmenu)="onRightClick($event, item)"
[class.active]="i==this.activeIndex"
(mouseover)="this.activeIndex=i" >
<img class="icon" [src]="item.image">
<p>{{item.label}}</p>
</div>
</div>
<div class="launch-menu-search">
<input style="width:95%" type="text" placeholder="{{translation.translate('Search')}}" [(ngModel)]="appFilter" (input)="filterMenuItems()">
<input #searchapp style="width:95%" type="text" placeholder="{{translation.translate('Search')}}" [(ngModel)]="appFilter" (input)="filterMenuItems()" >
</div>
</div>
<div class="launch-widget-caret caret-down"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
Copyright Contributors to the Zowe Project.
*/

import { Component, ElementRef, HostListener, Input, Output, EventEmitter, Injector } from '@angular/core';
import { Component, ElementRef, HostListener, Input, Output, EventEmitter, Injector, ViewChild } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { PluginsDataService } from '../../services/plugins-data.service';
import { LaunchbarItem } from '../shared/launchbar-item';
Expand All @@ -37,6 +37,9 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{
this.filterMenuItems();
}

@ViewChild('searchapp') searchAppInputRef: ElementRef;
@ViewChild('menudiv') menuDivRef: ElementRef;

@Output() refreshClicked: EventEmitter<void>;
@Output() itemClicked: EventEmitter<LaunchbarItem>;
@Output() menuStateChanged: EventEmitter<boolean>;
Expand All @@ -47,6 +50,8 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{
propertyWindowPluginDef : DesktopPluginDefinitionImpl;
public authenticationManager : MVDHosting.AuthenticationManagerInterface;
public appFilter:string="";
public activeIndex:number;
private isContextMenuPresent:boolean;

constructor(
private elementRef: ElementRef,
Expand All @@ -55,7 +60,7 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{
private injector: Injector,
private translation: TranslationService,
private desktopComponent: DesktopComponent,
private appKeyboard: KeybindingService
private appKeyboard: KeybindingService,
) {
// Workaround for AoT problem with namespaces (see angular/angular#15613)
this.applicationManager = this.injector.get(MVDHosting.Tokens.ApplicationManagerToken);
Expand All @@ -65,6 +70,9 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{
this.refreshClicked = new EventEmitter();
this.menuStateChanged = new EventEmitter<boolean>();
this.authenticationManager.registerPostLoginAction(this);

this.activeIndex = 0;
this.isContextMenuPresent = false;
}

onLogin(plugins:any): boolean {
Expand Down Expand Up @@ -106,16 +114,31 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{

activeToggle(): void {
this.isActive = !this.isActive;
// gain focus and clear on toggle when active
if(this.isActive) {
setTimeout(() => {
this.searchAppInputRef.nativeElement.focus();
},0);
}
this.emitState();
}

setSearchFocus() {
this.searchAppInputRef.nativeElement.focus();
}

refresh(): void {
this.resetMenu();
this.refreshClicked.emit();
}

resetMenu(): void {
this.appFilter = '';
this.displayItems = this._menuItems;
this.refreshClicked.emit();
}

filterMenuItems(): void {
this.activeIndex = 0;
if (this.appFilter) {
let filter = this.appFilter.toLowerCase();
this.displayItems = this._menuItems.filter((item)=> {
Expand Down Expand Up @@ -146,9 +169,108 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{
@HostListener('document:mousedown', ['$event'])
onMouseDown(event: MouseEvent): void {
if (this.isActive && event && !this.elementRef.nativeElement.contains(event.target)) {
this.isActive = false;
this.emitState();
this.activeToggle();
}
}



@HostListener('keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
if(this.isContextMenuInDom()) {
this.keepSearchCursor();
return;
}

// eating one render cycle
if(this.isContextMenuPresent) {
this.isContextMenuPresent = false;
return;
}

if(!this.isSearchFocus()) return;

switch(event.which) {
case KeyCode.ESCAPE: {
// if(this.appFilter>'') {
// this.resetMenu();
// } else {
this.activeToggle();
//}
break;
}
case KeyCode.ENTER: {
if(this.activeIndex<this.displayItems.length) {
this.clicked(this.displayItems[this.activeIndex]);
}
break;
}
case KeyCode.RIGHT_ARROW: {
if(this.activeIndex<this.displayItems.length) {
this.getContextMenu(this.displayItems[this.activeIndex]);
}
break;
}
case KeyCode.UP_ARROW: {
this.keepSearchCursor();
if(this.activeIndex>0) {
this.activeIndex--;
} else {
this.activeIndex=0;
}
this.scrollToActiveMenuItem();
break;
}
case KeyCode.DOWN_ARROW: {
if(this.activeIndex < this.displayItems.length-1) {
this.activeIndex++;
}
this.scrollToActiveMenuItem();
break;
}
}
}

private getActiveMenuItem():any {
return this.menuDivRef.nativeElement.querySelectorAll('.launch-widget-row')[this.activeIndex];
}

private getContextMenu(item:LaunchbarItem):void {
const elm = this.getActiveMenuItem();
if(elm) {
const pos = this.getElementPosition(elm);
let menuItems: ContextMenuItem[] = generateInstanceActions(item, this.pluginsDataService, this.translation, this.applicationManager, this.windowManager);
this.windowManager.contextMenuRequested.next({ xPos: pos.x, yPos: pos.y - 20, items: menuItems });
this.isContextMenuPresent = true;
}
}

private getElementPosition(elm: any): any {
let x = window.scrollX + elm.getBoundingClientRect().left + 40;
let y = window.scrollY + elm.getBoundingClientRect().top + 50;
return {x:x, y:y};
}

private scrollToActiveMenuItem(): void {
const elm = this.getActiveMenuItem();
if(elm) {
elm.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'start' });
}
}

private keepSearchCursor(): void {
const el = this.searchAppInputRef.nativeElement;
if (typeof el.selectionStart == "number") {
el.selectionStart = el.selectionEnd = el.value.length;
}
}

private isContextMenuInDom(): boolean {
return document.querySelector('com-rs-mvd-context-menu') !== null;
}

private isSearchFocus(): boolean {
return document.activeElement === this.searchAppInputRef.nativeElement;
}

private emitState(): void {
Expand All @@ -158,6 +280,7 @@ export class LaunchbarMenuComponent implements MVDHosting.LoginActionInterface{
onRightClick(event: MouseEvent, item: LaunchbarItem): boolean {
let menuItems: ContextMenuItem[] = generateInstanceActions(item, this.pluginsDataService, this.translation, this.applicationManager, this.windowManager);
this.windowManager.contextMenuRequested.next({ xPos: event.clientX, yPos: event.clientY - 20, items: menuItems });
this.isContextMenuPresent = true;
return false;
}

Expand Down

0 comments on commit 6fadb5f

Please sign in to comment.