From 85bdd791b8955d999559678af227d5ba3e1acd9d Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Mon, 28 Aug 2023 15:48:46 +0200 Subject: [PATCH 1/2] Implemented long press right click menu --- client/src/constants/constants.ts | 3 -- client/src/map/map.ts | 81 ++++++++++++++++++++++--------- client/src/unit/unit.ts | 30 ++---------- 3 files changed, 62 insertions(+), 52 deletions(-) diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index 8d29bbb7..f4216afb 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -135,9 +135,6 @@ export const layers = { /* Map constants */ export const IDLE = "Idle"; export const MOVE_UNIT = "Move unit"; -export const BOMBING = "Bombing"; -export const CARPET_BOMBING = "Carpet bombing"; -export const FIRE_AT_AREA = "Fire at area"; export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area"; export const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"]; export const visibilityControlsTypes: string[][] = [["human"], ["dcs"], ["aircraft"], ["groundunit-sam", "groundunit-sam-radar", "groundunit-sam-launcher"], ["groundunit-other", "groundunit-ewr"], ["navyunit"], ["airbase"]]; diff --git a/client/src/map/map.ts b/client/src/map/map.ts index f727ce93..cfd3edc9 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -1,5 +1,5 @@ import * as L from "leaflet" -import { getMissionHandler, getUnitsManager } from ".."; +import { getInfoPopup, getMissionHandler, getUnitsManager } from ".."; import { BoxSelect } from "./boxselect"; import { MapContextMenu } from "../controls/mapcontextmenu"; import { UnitContextMenu } from "../controls/unitcontextmenu"; @@ -12,7 +12,7 @@ import { DestinationPreviewMarker } from "./destinationpreviewmarker"; import { TemporaryUnitMarker } from "./temporaryunitmarker"; import { ClickableMiniMap } from "./clickableminimap"; import { SVGInjector } from '@tanem/svg-injector' -import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTootlips, FIRE_AT_AREA, MOVE_UNIT, CARPET_BOMBING, BOMBING, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes } from "../constants/constants"; +import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTootlips, MOVE_UNIT, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, visibilityControlsTypes } from "../constants/constants"; import { TargetMarker } from "./targetmarker"; import { CoalitionArea } from "./coalitionarea"; import { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu"; @@ -54,6 +54,7 @@ export class Map extends L.Map { #targetCursor: TargetMarker = new TargetMarker(new L.LatLng(0, 0), { interactive: false }); #destinationPreviewCursors: DestinationPreviewMarker[] = []; #drawingCursor: DrawingCursor = new DrawingCursor(); + #longPressCounter: number = 0; #mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu"); #unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu"); @@ -466,6 +467,9 @@ export class Map extends L.Map { #onContextMenu(e: any) { this.hideMapContextMenu(); + + var isLongPress = (new Date().getTime()) - this.#longPressCounter > 250; + if (this.#state === IDLE) { if (this.#state == IDLE) { this.showMapContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng); @@ -484,26 +488,57 @@ export class Map extends L.Map { } } else if (this.#state === MOVE_UNIT) { - if (!e.originalEvent.ctrlKey) { - getUnitsManager().selectedUnitsClearDestinations(); + if (isLongPress) { + var options: { [key: string]: { text: string, tooltip: string } } = {}; + const selectedUnits = getUnitsManager().getSelectedUnits(); + const selectedUnitTypes = getUnitsManager().getSelectedUnitsTypes(); + + if (selectedUnitTypes.length === 1 && ["Aircraft", "Helicopter"].includes(selectedUnitTypes[0])) { + if (selectedUnits.every((unit: Unit) => { return unit.canFulfillRole(["CAS", "Strike"]) })) { + options["bomb"] = { text: "Precision bombing", tooltip: "Precision bombing of a specific point" }; + options["carpet-bomb"] = { text: "Carpet bombing", tooltip: "Carpet bombing close to a point" }; + } else { + getInfoPopup().setText(`Selected units can not perform point actions.`); + } + } + else if (selectedUnitTypes.length === 1 && ["GroundUnit", "NavyUnit"].includes(selectedUnitTypes[0])) { + if (selectedUnits.every((unit: Unit) => { return ["Gun Artillery", "Rocket Artillery", "Infantry", "IFV", "Tank", "Cruiser", "Destroyer", "Frigate"].includes(unit.getType()) })) + options["fire-at-area"] = { text: "Fire at area", tooltip: "Fire at a large area" }; + else + getInfoPopup().setText(`Selected units can not perform point actions.`); + } + else { + getInfoPopup().setText(`Multiple unit types selected, no common actions available.`); + } + + if (Object.keys(options).length > 0) { + this.showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng); + this.getUnitContextMenu().setOptions(options, (option: string) => { + this.hideUnitContextMenu(); + if (option === "bomb") { + getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); + getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates()); + } + else if (option === "carpet-bomb") { + getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); + getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates()); + } + else if (option === "fire-at-area") { + getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); + getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates()); + } + }); + } + } else { + if (!e.originalEvent.ctrlKey) { + getUnitsManager().selectedUnitsClearDestinations(); + } + getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation) } - getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation) this.#destinationGroupRotation = 0; this.#destinationRotationCenter = null; this.#computeDestinationRotation = false; } - else if (this.#state === BOMBING) { - getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); - getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates()); - } - else if (this.#state === CARPET_BOMBING) { - getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); - getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates()); - } - else if (this.#state === FIRE_AT_AREA) { - getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); - getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates()); - } else { this.setState(IDLE); } @@ -537,6 +572,8 @@ export class Map extends L.Map { this.#destinationRotationCenter = this.getMouseCoordinates(); } } + + this.#longPressCounter = new Date().getTime(); } #onMouseUp(e: any) { @@ -554,9 +591,9 @@ export class Map extends L.Map { this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng); this.#updateDestinationCursors(); } - else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) { - this.#targetCursor.setLatLng(this.getMouseCoordinates()); - } + //else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) { + // this.#targetCursor.setLatLng(this.getMouseCoordinates()); + //} else if (this.#state === COALITIONAREA_DRAW_POLYGON) { this.#drawingCursor.setLatLng(e.latlng); /* Update the polygon being drawn with the current position of the mouse cursor */ @@ -712,13 +749,13 @@ export class Map extends L.Map { /* Hide all the unnecessary cursors depending on the active state */ if (this.#state !== IDLE) this.#hideDefaultCursor(); if (this.#state !== MOVE_UNIT) this.#hideDestinationCursors(); - if (![BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#hideTargetCursor(); + //if (![BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#hideTargetCursor(); if (this.#state !== COALITIONAREA_DRAW_POLYGON) this.#hideDrawingCursor(); /* Show the active cursor depending on the active state */ if (this.#state === IDLE) this.#showDefaultCursor(); else if (this.#state === MOVE_UNIT) this.#showDestinationCursors(); - else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#showTargetCursor(); + //else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#showTargetCursor(); else if (this.#state === COALITIONAREA_DRAW_POLYGON) this.#showDrawingCursor(); } } diff --git a/client/src/unit/unit.ts b/client/src/unit/unit.ts index 409a4a14..59cc0035 100644 --- a/client/src/unit/unit.ts +++ b/client/src/unit/unit.ts @@ -6,7 +6,7 @@ import { CustomMarker } from '../map/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; import { UnitDatabase } from './unitdatabase'; import { TargetMarker } from '../map/targetmarker'; -import { BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_CONTACT_LINES, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; +import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_CONTACT_LINES, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, ObjectIconOptions } from '../@types/unit'; import { DataExtractor } from '../server/dataextractor'; import { groundUnitDatabase } from './groundunitdatabase'; @@ -747,24 +747,7 @@ export class Unit extends CustomMarker { options["refuel"] = { text: "Air to air refuel", tooltip: "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB." }; // TODO Add some way of knowing which aircraft can AAR } } - - if ((selectedUnits.length === 0 && this.getCategory() == "Aircraft") || (selectedUnitTypes.length === 1 && ["Aircraft"].includes(selectedUnitTypes[0]))) { - if (selectedUnits.concat([this]).every((unit: Unit) => { return unit.canFulfillRole(["CAS", "Strike"]) })) { - options["bomb"] = { text: "Precision bombing", tooltip: "Precision bombing of a specific point" }; - options["carpet-bomb"] = { text: "Carpet bombing", tooltip: "Carpet bombing close to a point" }; - } - } - - if ((selectedUnits.length === 0 && this.getCategory() == "GroundUnit") || selectedUnitTypes.length === 1 && ["GroundUnit"].includes(selectedUnitTypes[0])) { - if (selectedUnits.concat([this]).every((unit: Unit) => { return ["Gun Artillery", "Rocket Artillery", "Infantry", "IFV", "Tank"].includes(this.getType()) })) - options["fire-at-area"] = { text: "Fire at area", tooltip: "Fire at a large area" }; - } - - if ((selectedUnits.length === 0 && this.getCategory() == "NavyUnit") || selectedUnitTypes.length === 1 && ["NavyUnit"].includes(selectedUnitTypes[0])) { - if (selectedUnits.concat([this]).every((unit: Unit) => { return ["Cruiser", "Destroyer", "Frigate"].includes(this.getType()) })) - options["fire-at-area"] = { text: "Fire at area", tooltip: "Fire at a large area" }; - } - + if (selectedUnitTypes.length === 1 && ["NavyUnit", "GroundUnit"].includes(selectedUnitTypes[0]) && getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getCoalition()}) !== undefined) options["group"] = { text: "Create group", tooltip: "Create a group from the selected units." }; @@ -787,14 +770,7 @@ export class Unit extends CustomMarker { else if (action === "group") getUnitsManager().selectedUnitsCreateGroup(); else if (action === "follow") - this.#showFollowOptions(e); - else if (action === "bomb") - getMap().setState(BOMBING); - else if (action === "carpet-bomb") - getMap().setState(CARPET_BOMBING); - else if (action === "fire-at-area") - getMap().setState(FIRE_AT_AREA); - + this.#showFollowOptions(e); } #showFollowOptions(e: any) { From a10c113c423eee093fcc3a1673ce1772ebd69bda Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Wed, 30 Aug 2023 09:10:42 +0200 Subject: [PATCH 2/2] Right click long press menu completed --- client/src/controls/dropdown.ts | 8 +-- client/src/map/map.ts | 118 +++++++++++++++++--------------- 2 files changed, 65 insertions(+), 61 deletions(-) diff --git a/client/src/controls/dropdown.ts b/client/src/controls/dropdown.ts index e02b611b..0b1b9d58 100644 --- a/client/src/controls/dropdown.ts +++ b/client/src/controls/dropdown.ts @@ -14,13 +14,9 @@ export class Dropdown { this.#defaultValue = this.#value.innerText; this.#callback = callback; - if (options != null) { - this.setOptions(options); - } + if (options != null) this.setOptions(options); - this.#value.addEventListener("click", (ev) => { - this.#toggle(); - }); + this.#value.addEventListener("click", (ev) => { this.#toggle(); }); document.addEventListener("click", (ev) => { if (!(this.#value.contains(ev.target as Node) || this.#options.contains(ev.target as Node) || this.#element.contains(ev.target as Node))) { diff --git a/client/src/map/map.ts b/client/src/map/map.ts index cfd3edc9..6dfd0222 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -54,7 +54,8 @@ export class Map extends L.Map { #targetCursor: TargetMarker = new TargetMarker(new L.LatLng(0, 0), { interactive: false }); #destinationPreviewCursors: DestinationPreviewMarker[] = []; #drawingCursor: DrawingCursor = new DrawingCursor(); - #longPressCounter: number = 0; + #longPressHandled: boolean = false; + #longPressTimer: number = 0; #mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu"); #unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu"); @@ -134,7 +135,6 @@ export class Map extends L.Map { } }); - document.addEventListener("toggleCoalitionAreaDraw", (ev: CustomEventInit) => { this.getMapContextMenu().hide(); this.deselectAllCoalitionAreas(); @@ -466,14 +466,19 @@ export class Map extends L.Map { } #onContextMenu(e: any) { + /* A long press will show the point action context menu */ + window.clearInterval(this.#longPressTimer); + if (this.#longPressHandled) { + this.#longPressHandled = false; + return; + } + this.hideMapContextMenu(); - - var isLongPress = (new Date().getTime()) - this.#longPressCounter > 250; - if (this.#state === IDLE) { if (this.#state == IDLE) { this.showMapContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng); var clickedCoalitionArea = null; + /* Coalition areas are ordered in the #coalitionAreas array according to their zindex. Select the upper one */ for (let coalitionArea of this.#coalitionAreas) { if (coalitionArea.getBounds().contains(e.latlng)) { @@ -488,53 +493,11 @@ export class Map extends L.Map { } } else if (this.#state === MOVE_UNIT) { - if (isLongPress) { - var options: { [key: string]: { text: string, tooltip: string } } = {}; - const selectedUnits = getUnitsManager().getSelectedUnits(); - const selectedUnitTypes = getUnitsManager().getSelectedUnitsTypes(); - - if (selectedUnitTypes.length === 1 && ["Aircraft", "Helicopter"].includes(selectedUnitTypes[0])) { - if (selectedUnits.every((unit: Unit) => { return unit.canFulfillRole(["CAS", "Strike"]) })) { - options["bomb"] = { text: "Precision bombing", tooltip: "Precision bombing of a specific point" }; - options["carpet-bomb"] = { text: "Carpet bombing", tooltip: "Carpet bombing close to a point" }; - } else { - getInfoPopup().setText(`Selected units can not perform point actions.`); - } - } - else if (selectedUnitTypes.length === 1 && ["GroundUnit", "NavyUnit"].includes(selectedUnitTypes[0])) { - if (selectedUnits.every((unit: Unit) => { return ["Gun Artillery", "Rocket Artillery", "Infantry", "IFV", "Tank", "Cruiser", "Destroyer", "Frigate"].includes(unit.getType()) })) - options["fire-at-area"] = { text: "Fire at area", tooltip: "Fire at a large area" }; - else - getInfoPopup().setText(`Selected units can not perform point actions.`); - } - else { - getInfoPopup().setText(`Multiple unit types selected, no common actions available.`); - } - - if (Object.keys(options).length > 0) { - this.showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng); - this.getUnitContextMenu().setOptions(options, (option: string) => { - this.hideUnitContextMenu(); - if (option === "bomb") { - getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); - getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates()); - } - else if (option === "carpet-bomb") { - getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); - getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates()); - } - else if (option === "fire-at-area") { - getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); - getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates()); - } - }); - } - } else { - if (!e.originalEvent.ctrlKey) { - getUnitsManager().selectedUnitsClearDestinations(); - } - getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation) + if (!e.originalEvent.ctrlKey) { + getUnitsManager().selectedUnitsClearDestinations(); } + getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation) + this.#destinationGroupRotation = 0; this.#destinationRotationCenter = null; this.#computeDestinationRotation = false; @@ -573,7 +536,55 @@ export class Map extends L.Map { } } - this.#longPressCounter = new Date().getTime(); + this.#longPressTimer = window.setTimeout(() => { + if (e.originalEvent.button != 2) + return; + + this.hideMapContextMenu(); + this.#longPressHandled = true; + + var options: { [key: string]: { text: string, tooltip: string } } = {}; + const selectedUnits = getUnitsManager().getSelectedUnits(); + const selectedUnitTypes = getUnitsManager().getSelectedUnitsTypes(); + + if (selectedUnitTypes.length === 1 && ["Aircraft", "Helicopter"].includes(selectedUnitTypes[0])) { + if (selectedUnits.every((unit: Unit) => { return unit.canFulfillRole(["CAS", "Strike"]) })) { + options["bomb"] = { text: "Precision bombing", tooltip: "Precision bombing of a specific point" }; + options["carpet-bomb"] = { text: "Carpet bombing", tooltip: "Carpet bombing close to a point" }; + } else { + getInfoPopup().setText(`Selected units can not perform point actions.`); + } + } + else if (selectedUnitTypes.length === 1 && ["GroundUnit", "NavyUnit"].includes(selectedUnitTypes[0])) { + if (selectedUnits.every((unit: Unit) => { return ["Gun Artillery", "Rocket Artillery", "Infantry", "IFV", "Tank", "Cruiser", "Destroyer", "Frigate"].includes(unit.getType()) })) + options["fire-at-area"] = { text: "Fire at area", tooltip: "Fire at a large area" }; + else + getInfoPopup().setText(`Selected units can not perform point actions.`); + } + else { + getInfoPopup().setText(`Multiple unit types selected, no common actions available.`); + } + + if (Object.keys(options).length > 0) { + this.showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng); + this.getUnitContextMenu().setOptions(options, (option: string) => { + this.hideUnitContextMenu(); + if (option === "bomb") { + getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); + getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates()); + } + else if (option === "carpet-bomb") { + getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); + getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates()); + } + else if (option === "fire-at-area") { + getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE); + getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates()); + } + }); + } + }, 150); + this.#longPressHandled = false; } #onMouseUp(e: any) { @@ -591,9 +602,6 @@ export class Map extends L.Map { this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng); this.#updateDestinationCursors(); } - //else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) { - // this.#targetCursor.setLatLng(this.getMouseCoordinates()); - //} else if (this.#state === COALITIONAREA_DRAW_POLYGON) { this.#drawingCursor.setLatLng(e.latlng); /* Update the polygon being drawn with the current position of the mouse cursor */