Completed first iteration of drawings management on v2

This commit is contained in:
Davide Passoni
2025-02-26 09:39:30 +01:00
parent 1a93ee68d0
commit b2477112b1
17 changed files with 1041 additions and 17 deletions

View File

@@ -1,9 +1,9 @@
import React, { useEffect, useState } from "react";
import { Menu } from "./components/menu";
import { FaArrowDown, FaArrowUp, FaTrash } from "react-icons/fa";
import { FaArrowDown, FaArrowUp, FaChevronRight, FaTrash } from "react-icons/fa";
import { getApp } from "../../olympusapp";
import { OlStateButton } from "../components/olstatebutton";
import { faDrawPolygon } from "@fortawesome/free-solid-svg-icons";
import { faDrawPolygon, faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
import { faCircle } from "@fortawesome/free-regular-svg-icons";
import { CoalitionPolygon } from "../../map/coalitionarea/coalitionpolygon";
import { OlCoalitionToggle } from "../components/olcoalitiontoggle";
@@ -13,9 +13,12 @@ import { Coalition } from "../../types/types";
import { OlRangeSlider } from "../components/olrangeslider";
import { CoalitionCircle } from "../../map/coalitionarea/coalitioncircle";
import { DrawSubState, ERAS_ORDER, IADSTypes, NO_SUBSTATE, OlympusState, OlympusSubState } from "../../constants/constants";
import { AppStateChangedEvent, CoalitionAreasChangedEvent, CoalitionAreaSelectedEvent } from "../../events";
import { AppStateChangedEvent, CoalitionAreasChangedEvent, CoalitionAreaSelectedEvent, DrawingsInitEvent, DrawingsUpdatedEvent } from "../../events";
import { FaXmark } from "react-icons/fa6";
import { deepCopyTable } from "../../other/utils";
import { DCSDrawingsContainer, DCSEmptyLayer } from "../../map/drawings/drawingsmanager";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OlSearchBar } from "../components/olsearchbar";
export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
@@ -30,11 +33,21 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
const [erasSelection, setErasSelection] = useState({});
const [rangesSelection, setRangesSelection] = useState({});
const [openContainers, setOpenContainers] = useState([] as DCSDrawingsContainer[]);
const [mainDrawingsContainer, setDrawingsContainer] = useState({ container: null } as { container: null | DCSDrawingsContainer });
const [searchString, setSearchString] = useState("");
useEffect(() => {
AppStateChangedEvent.on((state, subState) => {
setAppState(state);
setAppSubState(subState);
});
DrawingsInitEvent.on((drawingContainer) => {
setDrawingsContainer({ container: drawingContainer });
});
DrawingsUpdatedEvent.on(() => {
setDrawingsContainer({ container: getApp().getDrawingsManager().getDrawingsContainer() });
});
}, []);
/* Get all the unique types and eras for groundunits */
@@ -50,6 +63,85 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
CoalitionAreasChangedEvent.on((coalitionAreas) => setCoalitionAreas([...coalitionAreas]));
}, []);
function renderDrawingsContainerControls(container: DCSDrawingsContainer) {
if (container.hasSearchString(searchString)) {
return (
<div className="ml-2 flex flex-col gap-2">
<div className="flex flex-col gap-2">
<div className="flex justify-between gap-2">
<FaChevronRight
className={`
my-auto
${openContainers.includes(container) && `rotate-90`}
cursor-pointer text-gray-400 transition-transform
`}
onClick={() => {
if (openContainers.includes(container)) {
let index = openContainers.indexOf(container);
openContainers.splice(index, 1);
} else {
openContainers.push(container);
}
setOpenContainers([...openContainers]);
}}
></FaChevronRight>
<FontAwesomeIcon
icon={container.getVisibility() ? faEye : faEyeSlash}
className={`
my-auto w-6 cursor-pointer text-gray-400 transition-transform
hover:scale-125 hover:text-gray-200
`}
onClick={() => {
container.setVisibility(!container.getVisibility(), true);
}}
/>
<div
className={`
w-40 w-max-40 overflow-hidden text-ellipsis text-nowrap bg-
`}
>
{container.getName()}
</div>
<OlRangeSlider
value={container.getOpacity() * 100}
min={0}
max={100}
onChange={(ev) => {
container.setOpacity(Number(ev.currentTarget.value) / 100);
}}
className={`my-auto ml-auto max-w-32`}
></OlRangeSlider>
</div>
</div>
{openContainers.includes(container) && container.getSubContainers().map((container) => renderDrawingsContainerControls(container))}
{openContainers.includes(container) &&
container.getDrawings().map((drawing) => {
if (drawing instanceof DCSEmptyLayer) return <></>;
return (
<div className="ml-4 flex justify-start gap-2">
<FontAwesomeIcon
icon={drawing.getVisibility() ? faEye : faEyeSlash}
className={`
my-auto w-6 cursor-pointer text-gray-400
transition-transform
hover:scale-125 hover:text-gray-200
`}
onClick={() => {
drawing.setVisibility(!drawing.getVisibility());
}}
/>
<div className={`overflow-hidden text-ellipsis text-nowrap`}>{drawing.getName()}</div>
</div>
);
})}
</div>
);
} else {
return <></>;
}
}
return (
<Menu
open={props.open}
@@ -132,6 +224,14 @@ export function DrawingMenu(props: { open: boolean; onClose: () => void }) {
<div className="text-sm">Add circle</div>
</OlStateButton>
</div>
<div>
<div className="flex flex-col gap-2 p-6">
<div className="text-sm text-gray-400">Mission drawings</div>
<OlSearchBar onChange={(search) => setSearchString(search)} text={searchString || ""}></OlSearchBar>
<div className="flex flex-col gap-2">{mainDrawingsContainer.container && renderDrawingsContainerControls(mainDrawingsContainer.container)}</div>
</div>
</div>
</div>
)}
</>

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from "react";
import { OlRoundStateButton, OlStateButton, OlLockStateButton } from "../components/olstatebutton";
import { faSkull, faCamera, faFlag, faVolumeHigh, faDownload, faUpload } from "@fortawesome/free-solid-svg-icons";
import { faSkull, faCamera, faFlag, faVolumeHigh, faDownload, faUpload, faDrawPolygon } from "@fortawesome/free-solid-svg-icons";
import { OlDropdownItem, OlDropdown } from "../components/oldropdown";
import { OlLabelToggle } from "../components/ollabeltoggle";
import { getApp, IP } from "../../olympusapp";
@@ -16,8 +16,24 @@ import {
olButtonsVisibilityOlympus,
} from "../components/olicons";
import { FaChevronLeft, FaChevronRight, FaFloppyDisk } from "react-icons/fa6";
import { CommandModeOptionsChangedEvent, ConfigLoadedEvent, HiddenTypesChangedEvent, MapOptionsChangedEvent, MapSourceChangedEvent, SessionDataChangedEvent, SessionDataSavedEvent } from "../../events";
import { BLUE_COMMANDER, COMMAND_MODE_OPTIONS_DEFAULTS, ImportExportSubstate, MAP_HIDDEN_TYPES_DEFAULTS, MAP_OPTIONS_DEFAULTS, OlympusState, RED_COMMANDER } from "../../constants/constants";
import {
CommandModeOptionsChangedEvent,
ConfigLoadedEvent,
HiddenTypesChangedEvent,
MapOptionsChangedEvent,
MapSourceChangedEvent,
SessionDataChangedEvent,
SessionDataSavedEvent,
} from "../../events";
import {
BLUE_COMMANDER,
COMMAND_MODE_OPTIONS_DEFAULTS,
ImportExportSubstate,
MAP_HIDDEN_TYPES_DEFAULTS,
MAP_OPTIONS_DEFAULTS,
OlympusState,
RED_COMMANDER,
} from "../../constants/constants";
import { OlympusConfig } from "../../interfaces";
import { FaCheck, FaSave, FaSpinner } from "react-icons/fa";
@@ -116,15 +132,40 @@ export function Header() {
{IP}
</div>
</div>
{savingSessionData ? <div className="text-white"><FaSpinner className={`
animate-spin text-2xl
`}/></div> : <div className={`relative text-white`}><FaFloppyDisk className={`
absolute -top-3 text-2xl
`}/><FaCheck className={`
absolute left-[9px] top-[-6px] text-2xl text-olympus-900
`}/><FaCheck className={`absolute left-3 top-0 text-green-500`}/></div>}
<OlStateButton className="ml-8" icon={faDownload} onClick={() => {getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.EXPORT)}} checked={false}/>
<OlStateButton icon={faUpload} onClick={() => {getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.IMPORT)}} checked={false}/>
{savingSessionData ? (
<div className="text-white">
<FaSpinner
className={`animate-spin text-2xl`}
/>
</div>
) : (
<div className={`relative text-white`}>
<FaFloppyDisk
className={`absolute -top-3 text-2xl`}
/>
<FaCheck
className={`
absolute left-[9px] top-[-6px] text-2xl text-olympus-900
`}
/>
<FaCheck className={`absolute left-3 top-0 text-green-500`} />
</div>
)}
<OlStateButton
className="ml-8"
icon={faDownload}
onClick={() => {
getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.EXPORT);
}}
checked={false}
/>
<OlStateButton
icon={faUpload}
onClick={() => {
getApp().setState(OlympusState.IMPORT_EXPORT, ImportExportSubstate.IMPORT);
}}
checked={false}
/>
</div>
{commandModeOptions.commandMode === BLUE_COMMANDER && (
@@ -138,6 +179,14 @@ export function Header() {
</div>
)}
<div className={`flex h-fit flex-row items-center justify-start gap-1`}>
<OlRoundStateButton
icon={faDrawPolygon}
checked={mapOptions.showMissionDrawings}
onClick={() => {
getApp().getMap().setOption("showMissionDrawings", !mapOptions.showMissionDrawings);
}}
tooltip="Show/Hide mission drawings"
/>
<OlLockStateButton
checked={!mapOptions.protectDCSUnits}
onClick={() => {