From 432c757b05a5a8db045fdab43671740cd70214f0 Mon Sep 17 00:00:00 2001 From: Gabriel Keenleyside Date: Tue, 17 Dec 2019 08:51:46 -0500 Subject: [PATCH 01/35] Force context menu to close when window is resized Signed-off-by: Gabriel Keenleyside --- .../context-menu/context-menu.component.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts index 6b9173005..00d7c94dd 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts @@ -98,6 +98,11 @@ export class ContextMenuComponent { this.closeContextMenu(); } } + + @HostListener('window:resize') + onResize(): void { + this.closeContextMenu(); + } } From 0e84cbe95db2c462901b53bcd890f991ffa1fb1d Mon Sep 17 00:00:00 2001 From: Gabriel Keenleyside Date: Tue, 17 Dec 2019 08:53:18 -0500 Subject: [PATCH 02/35] Remove focus from context menu items when cursor moves off menu (currently it's removed only if cursor moves over a different menu item) Signed-off-by: Gabriel Keenleyside --- .../context-menu/context-menu.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html index 6b5b59723..bd83a969a 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html @@ -15,12 +15,12 @@ - - + + + + + + + SPDX-License-Identifier: EPL-2.0 - \ No newline at end of file + Copyright Contributors to the Zowe Project. +--> + diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts index e662a9689..4f4181877 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.ts @@ -10,7 +10,20 @@ Copyright Contributors to the Zowe Project. */ -import { Input, Component, EventEmitter, Output, HostListener, ElementRef, ViewChild } from '@angular/core'; +import { + Input, + Component, + EventEmitter, + Output, + HostListener, + ElementRef, + ViewChild, + ViewChildren, + AfterViewInit, + OnInit, + OnDestroy, + QueryList +} from '@angular/core'; import { ContextMenuItem } from 'pluginlib/inject-resources'; @@ -19,25 +32,36 @@ import { BaseLogger } from 'virtual-desktop-logger'; @Component({ selector: 'com-rs-mvd-context-menu', templateUrl: './context-menu.component.html', - styleUrls: ['./context-menu.component.css'] + styleUrls: ['./context-menu.component.css'], }) -export class ContextMenuComponent { +export class ContextMenuComponent implements AfterViewInit, OnInit, OnDestroy { hovering: ContextMenuItem; - newX: number; // distance from left side of browser to left side of menu - newY: number; // distance from top of browser to top of menu + newX: number; + newY: number; + activeIndex: number; // Index of active item in menu. -1 if none active. + _isChildMenu: boolean; // True if menu is child of another menu, false otherwise. + _isParentActive: boolean; // True if parent item is active (ie. menu should be displayed), false otherwise. + isNavigable: boolean; // True if menu is navigable, false otherwise. + _parentText: string; // Text of parent menu item + children: { [key: string]: any }; @ViewChild('contextmenu') set menu(contextmenu: any) { contextmenu.nativeElement.style.opacity = 0; setTimeout(() => { - let menuHeight = contextmenu.nativeElement.clientHeight; // Get inital menu height - let menuWidth = contextmenu.nativeElement.clientWidth; // Het initial menu width + let menuHeight = contextmenu.nativeElement.clientHeight; + let menuWidth = contextmenu.nativeElement.clientWidth; this.newY = this.validateY(this.newY, menuHeight); this.newX = this.validateX(this.newX, menuWidth); contextmenu.nativeElement.style.opacity = 1; + this.activeIndex = -1; // By default, no item is selected. + this._isParentActive = false; // Since by default no item is selected, all parent items are inactive. + this.isNavigable = !this._isChildMenu; // By default, surface-depth menu is navigable, and all others are not. }, 0); } + @ViewChildren(ContextMenuComponent) _children: QueryList; + @Input() set xPos(xPos: number) { this.newX = xPos; }; @@ -45,47 +69,109 @@ export class ContextMenuComponent { @Input() set yPos(yPos: number) { this.newY = yPos; }; - + @Input() menuItems: ContextMenuItem[]; + + // Declare value of _isChildMenu based on input isChildMenu + // This is set on every child menu + @Input() set isChildMenu(isChildMenu: boolean) { + this._isChildMenu = isChildMenu; + }; + + // Set values of _isParentActive and activeIndex based on input isParentActive + // _isParentActive set to true by parent menu if activeIndex of parent menu corresponds to this child menu + // activeIndex set to existing value of activeIndex if parent is active (ie. child menu visible), -1 otherwise, so that any active item in child menu reset when child meny disappears, thus none selected when it reappears. + @Input() set isParentActive(isParentActive: boolean) { + this._isParentActive = isParentActive; // Set _isParentActive based on input + this.activeIndex = isParentActive ? this.activeIndex : -1; // If parent not active, reset this.activeIndex to -1 + }; + + // Set value of isNavigable based on input parentNavigable. + // If parent menu is navigable, child menu cannot be. + @Input() set parentNavigable(parentNavigable: boolean) { + if (parentNavigable) { + this.isNavigable = false; + } + }; + + @Input() set parentText(parentText: string) { + this._parentText = parentText; + }; + + @Output() complete: EventEmitter; + @Output() makeParentUnnavigable = new EventEmitter(); + + @Output() makeParentNavigable = new EventEmitter(); + + @Output() sendRefToParent = new EventEmitter(); + private readonly logger: ZLUX.ComponentLogger = BaseLogger; constructor( - private elementRef: ElementRef, + private elementRef: ElementRef //private logger: DesktopLogger - ) { + ) { this.complete = new EventEmitter(); } + // Make menu navigable, and emit makeParentUnnavigable event. + // Emitting makeParentUnnavigable event will trigger any parent menu to call makeUnnavigable(). + makeNavigable = () => { + // Make navigable + this.isNavigable = true; + // If menu has parent, un-focus parent + if (this._isChildMenu) { + this.makeParentUnnavigable.emit() + } + } + + // Make menu unnavigable, and emit makeParentUnnavigable event. + // Emitting makeParentUnnavigable event will recursively make all subsequent parent menus call makeUnnavigable(). + makeUnnavigable() { + // Make un-navigable + this.isNavigable = false; + // If menu has parent, un-focus parent + if (this._isChildMenu) { + this.makeParentUnnavigable.emit() + } + } + + ngOnInit() { + } + + ngOnDestroy() { + } + + ngAfterViewInit() { + this.elementRef.nativeElement.focus() + this.children = this._children.toArray().reduce((acc, curr) => ( + { + ...acc, + [curr._parentText]: curr + } + ), {}) + } + validateX(xPos: number, menuWidth: number): number { let menuRight = xPos + menuWidth; - let menuLeft = xPos; let screenWidth = window.innerWidth - 10; /* Gave a 10 pixel buffer so isn't right on the edge */ if (menuRight > screenWidth) { let difference = menuRight - screenWidth; xPos = xPos - difference } - if (menuLeft < 10) { - let difference = 10 - menuLeft; - xPos = xPos + difference; - } return xPos; } validateY(yPos: number, menuHeight: number): number { let menuBottom = menuHeight + yPos; - let menuTop = yPos; let screenHeight = window.innerHeight - 10; /* Gave a 10 pixel buffer so isn't right on the edge */ if (menuBottom > screenHeight) { let difference = menuBottom - screenHeight; yPos = yPos - difference; } - if (menuTop < 10) { - let difference = 10 - menuTop; - yPos = yPos + difference; - } return yPos; } @@ -102,6 +188,10 @@ export class ContextMenuComponent { this.complete.emit(); } + setActiveIndex = (index: number) => { + this.activeIndex = index; + } + @HostListener('document:mousedown', ['$event']) onMouseDown(event: MouseEvent): void { if (event && !this.elementRef.nativeElement.contains(event.target)) { @@ -109,9 +199,37 @@ export class ContextMenuComponent { } } - @HostListener('window:resize') - onResize(): void { - this.closeContextMenu(); + @HostListener('window:keydown', ['$event']) + onWindowKeyDown(event: KeyboardEvent) { + let mod = (x: number, n: number): number => ((x == -2 ? -1 : x % n) + n) % n; + let hasChildren; + if (this.activeIndex > -1) { + hasChildren = this.menuItems && this.menuItems[this.activeIndex].children ? true : false; + } + if (this.isNavigable) { + switch (event.key){ + case 'ArrowUp': + this.activeIndex = mod(this.activeIndex - 1, this.menuItems.length) + break; + case 'ArrowDown': + this.activeIndex = mod(this.activeIndex + 1, this.menuItems.length) + break; + case 'ArrowRight': + if (hasChildren) { + this.makeUnnavigable(); // Make un-navigable because navigability will transfer to child menu + setTimeout(() => { + this.children[this.menuItems[this.activeIndex].text].makeNavigable(); + this.children[this.menuItems[this.activeIndex].text].setActiveIndex(0); + }, 0) + } + break; + case 'ArrowLeft': + if (this.isNavigable) { + this.makeParentNavigable.emit(); + this.setActiveIndex(-1); + } + } + } } } From 58ec1325ad8c518ad24be7fc7c994443f64c8cdd Mon Sep 17 00:00:00 2001 From: Gabriel Keenleyside Date: Tue, 17 Dec 2019 12:29:15 -0500 Subject: [PATCH 06/35] Add functionality to allow icons for context menu items Signed-off-by: Gabriel Keenleyside --- .../context-menu/context-menu.component.css | 14 ++++++++++++++ .../context-menu/context-menu.component.html | 16 ++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.css b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.css index cae55c6dd..0fd85fc91 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.css +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.css @@ -97,6 +97,20 @@ border-bottom: none; } +.icon { + width: 19px; + margin-right: 5px; + background-size: contain; +} + +.icon-person { + background-image: url(../../../../assets/images/contextmenu/person.png); +} + +.icon-settings { + background-image: url(../../../../assets/images/contextmenu/settings.png); +} + /* This program and the accompanying materials are made available under the terms of the Eclipse Public License v2.0 which accompanies diff --git a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html index 545ac20fc..5f5aca1cf 100644 --- a/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html +++ b/virtual-desktop/src/app/window-manager/mvd-window-manager/context-menu/context-menu.component.html @@ -29,7 +29,12 @@ (click)='itemClicked(menuItem); $event.stopPropagation(); closeContextMenu()' (mouseover)="this.activeIndex=i; makeNavigable(); $event.stopPropagation();" > - {{menuItem.text}} +
+
+
{{menuItem.text}}
+
@@ -40,10 +45,13 @@ [class.bottom]="i==menuItems.length-1" (mouseover)="this.activeIndex=i; makeNavigable(); $event.stopPropagation();" > -
- {{menuItem.text}} - +
+
+
{{menuItem.text}}
+