240 lines
14 KiB
Python
240 lines
14 KiB
Python
import re
|
|
|
|
path = "packages/tactical_app/lib/screens/tactical_map_screen.dart"
|
|
|
|
with open(path, "r") as f:
|
|
content = f.read()
|
|
|
|
# 1. Add imports
|
|
imports = """
|
|
import '../models/tactical_sheet_type.dart';
|
|
import '../widgets/persistent_tactical_sheet_wrapper.dart';
|
|
"""
|
|
content = content.replace("import '../widgets/tactical_viewshed_sheet.dart';", "import '../widgets/tactical_viewshed_sheet.dart';\n" + imports)
|
|
|
|
# 2. Add Persistent Sheet in Stack
|
|
# We will insert it right before the active navigation HUDs (around line 287)
|
|
# or just after the InteractiveMapPickerHud (around line 242).
|
|
# Let's find " // ── 5. Active Turn-by-Turn Navigation HUD ──────────────"
|
|
stack_insert = """
|
|
// ── 4c. Persistent Tactical Sheet ──────────────────────────
|
|
Positioned(
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0,
|
|
child: Obx(() {
|
|
final activeSheet = controller.activeSheet.value;
|
|
if (activeSheet == null || navState.isNavigating) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
|
|
Widget sheetContent = const SizedBox.shrink();
|
|
switch (activeSheet.type) {
|
|
case TacticalSheetType.route:
|
|
sheetContent = TacticalRoutePlannerSheet(
|
|
initialOrigin: controller.pickedRouteOrigin.value,
|
|
initialDestination: controller.pickedRouteDestination.value,
|
|
currentGps: controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
|
resectionFix: controller.resection.resectionResult.value != null
|
|
? LatLng(controller.resection.resectionResult.value!.lat, controller.resection.resectionResult.value!.lng)
|
|
: null,
|
|
onRouteConfirmed: (origin, destination, profile, autoStartNav) async {
|
|
final plan = await controller.navigation.calculateRoute(
|
|
origin: origin,
|
|
destination: destination,
|
|
profile: profile,
|
|
autoStart: autoStartNav,
|
|
mapController: controller.mapController,
|
|
);
|
|
if (plan != null && plan.polylinePoints.isNotEmpty) {
|
|
controller.fitBounds(plan.polylinePoints);
|
|
}
|
|
},
|
|
onPickOriginOnMap: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.routeOrigin);
|
|
},
|
|
onPickDestinationOnMap: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.routeDestination);
|
|
},
|
|
onClose: controller.closeSheet,
|
|
);
|
|
break;
|
|
case TacticalSheetType.los:
|
|
sheetContent = TacticalLosSheet(
|
|
observer: controller.los.observerPosition.value ?? const LatLng(31.9539, 35.9106),
|
|
target: controller.los.targetPosition.value ?? const LatLng(31.9800, 35.9500),
|
|
currentGps: controller.currentGpsPosition.value,
|
|
angleUnit: controller.angleUnit.value,
|
|
onUpdateLocations: (newObs, newTgt) {
|
|
controller.los.setObserver(newObs);
|
|
controller.los.setTarget(newTgt);
|
|
controller.fitBounds([newObs, newTgt]);
|
|
},
|
|
onVisibilityChanged: (_) {},
|
|
onReportGenerated: (_) {},
|
|
onPickObserverOnMap: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.losObserver);
|
|
},
|
|
onPickTargetOnMap: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.losTarget);
|
|
},
|
|
onClose: () {
|
|
controller.closeSheet();
|
|
controller.switchMode('nav');
|
|
},
|
|
);
|
|
break;
|
|
case TacticalSheetType.viewshed:
|
|
sheetContent = TacticalViewshedSheet(
|
|
observer: controller.viewshed.observerPosition.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
|
currentGps: controller.currentGpsPosition.value,
|
|
onUpdateObserver: controller.viewshed.setObserver,
|
|
onReportUpdated: (_) {},
|
|
onPickObserverOnMap: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.viewshedCenter);
|
|
},
|
|
onClose: controller.closeSheet,
|
|
);
|
|
break;
|
|
case TacticalSheetType.artillery:
|
|
sheetContent = TacticalArtillerySheet(
|
|
gunPosition: controller.artillery.gunPosition.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
|
targetPosition: controller.artillery.targetPosition.value,
|
|
angleUnit: controller.angleUnit.value,
|
|
onPickGun: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.artilleryGun);
|
|
},
|
|
onPickTarget: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.artilleryTarget);
|
|
},
|
|
onSwap: controller.artillery.swapPositions,
|
|
onSolutionCalculated: (sol) {
|
|
controller.fitBounds([sol.gunPosition, sol.targetPosition]);
|
|
},
|
|
onClose: controller.closeSheet,
|
|
);
|
|
break;
|
|
case TacticalSheetType.hlz:
|
|
sheetContent = TacticalHlzSheet(
|
|
selectedPosition: controller.hlz.selectedPosition.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
|
onPickLocation: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.hlzCenter);
|
|
},
|
|
onAssessmentCompleted: (_) {},
|
|
onClose: controller.closeSheet,
|
|
);
|
|
break;
|
|
case TacticalSheetType.minefield:
|
|
sheetContent = TacticalMinefieldSheet(
|
|
startPoint: controller.minefield.startPoint.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
|
endPoint: controller.minefield.endPoint.value,
|
|
onPickStart: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.minefieldStart);
|
|
},
|
|
onPickEnd: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.minefieldEnd);
|
|
},
|
|
onSwap: controller.minefield.swapPoints,
|
|
onZoneCalculated: (res) {
|
|
controller.fitBounds([res.startPoint, res.endPoint]);
|
|
},
|
|
onClose: controller.closeSheet,
|
|
);
|
|
break;
|
|
case TacticalSheetType.isochrone:
|
|
sheetContent = TacticalIsochroneSheet(
|
|
center: controller.isochrone.center.value ?? controller.currentGpsPosition.value ?? const LatLng(31.9539, 35.9106),
|
|
onPickCenter: () {
|
|
controller.isSheetMinimized.value = true;
|
|
controller.setPickerTarget(MapPickerTarget.isochroneCenter);
|
|
},
|
|
onIsochronesCalculated: (_) {},
|
|
onClose: controller.closeSheet,
|
|
);
|
|
break;
|
|
case TacticalSheetType.symbols:
|
|
sheetContent = TacticalSymbolsSheet(
|
|
activePlacementType: controller.symbols.activePlacementType.value,
|
|
placedSymbols: controller.symbols.placedSymbols,
|
|
onSelectSymbolType: (type) {
|
|
controller.symbols.selectSymbolForPlacement(type);
|
|
controller.isSheetMinimized.value = true;
|
|
Get.snackbar(
|
|
'الرموز العسكرية',
|
|
'انقر على الخريطة لتثبيت الرمز في الميدان 📍',
|
|
backgroundColor: const Color(0xFF0071E3),
|
|
colorText: Colors.white,
|
|
snackPosition: SnackPosition.BOTTOM,
|
|
);
|
|
},
|
|
onDeleteSymbol: (s) => controller.symbols.removeSymbol(s.id),
|
|
onClose: controller.closeSheet,
|
|
);
|
|
break;
|
|
case TacticalSheetType.overlays:
|
|
sheetContent = TacticalOverlaysSheet(
|
|
layers: controller.overlays.overlayLayers,
|
|
onToggleLayer: controller.overlays.toggleLayer,
|
|
onClose: controller.closeSheet,
|
|
);
|
|
break;
|
|
}
|
|
|
|
return PersistentTacticalSheetWrapper(
|
|
title: activeSheet.title,
|
|
icon: activeSheet.icon,
|
|
isMinimized: controller.isSheetMinimized.value,
|
|
onToggleMinimize: () => controller.isSheetMinimized.toggle(),
|
|
onClose: controller.closeSheet,
|
|
child: sheetContent,
|
|
);
|
|
}),
|
|
),
|
|
// ── 5. Active Turn-by-Turn Navigation HUD ──────────────
|
|
"""
|
|
|
|
content = content.replace(" // ── 5. Active Turn-by-Turn Navigation HUD ──────────────", stack_insert)
|
|
|
|
# 3. Replace the _openX methods to just open the sheet and not use showModalBottomSheet
|
|
replacements = {
|
|
"void _openRoutePlanner(BuildContext context) {": "void _openRoutePlanner(BuildContext context) {\n Navigator.pop(context);\n controller.openSheet(ActiveTacticalSheet(TacticalSheetType.route, 'مخطط مسار القوافل', Icons.route));\n }",
|
|
"void _openLosSheet(BuildContext context) {": "void _openLosSheet(BuildContext context) {\n Navigator.pop(context);\n controller.openSheet(ActiveTacticalSheet(TacticalSheetType.los, 'تحليل خط الرؤية (LOS)', Icons.visibility));\n }",
|
|
"void _openViewshed360Sheet(BuildContext context) {": "void _openViewshed360Sheet(BuildContext context) {\n Navigator.pop(context);\n controller.openSheet(ActiveTacticalSheet(TacticalSheetType.viewshed, 'رادار الرؤية 360 درجة', Icons.radar));\n }",
|
|
"void _openArtillerySheet(BuildContext context) {": "void _openArtillerySheet(BuildContext context) {\n Navigator.pop(context);\n controller.openSheet(ActiveTacticalSheet(TacticalSheetType.artillery, 'حاسبة المدفعية الميدانية', Icons.track_changes));\n }",
|
|
"void _openHlzSheet(BuildContext context) {": "void _openHlzSheet(BuildContext context) {\n Navigator.pop(context);\n controller.openSheet(ActiveTacticalSheet(TacticalSheetType.hlz, 'استطلاع المهابط (HLZ)', Icons.local_airport));\n }",
|
|
"void _openMinefieldSheet(BuildContext context) {": "void _openMinefieldSheet(BuildContext context) {\n Navigator.pop(context);\n controller.openSheet(ActiveTacticalSheet(TacticalSheetType.minefield, 'تأمين الممرات والألغام', Icons.warning_amber_rounded));\n }",
|
|
"void _openIsochroneSheet(BuildContext context) {": "void _openIsochroneSheet(BuildContext context) {\n Navigator.pop(context);\n controller.openSheet(ActiveTacticalSheet(TacticalSheetType.isochrone, 'نطاقات الحركة والإمداد', Icons.speed));\n }",
|
|
"void _openSymbolsSheet(BuildContext context) {": "void _openSymbolsSheet(BuildContext context) {\n Navigator.pop(context);\n controller.openSheet(ActiveTacticalSheet(TacticalSheetType.symbols, 'الرموز العسكرية (MIL-STD)', Icons.category));\n }",
|
|
"void _openOverlaysSheet(BuildContext context) {": "void _openOverlaysSheet(BuildContext context) {\n Navigator.pop(context);\n controller.openSheet(ActiveTacticalSheet(TacticalSheetType.overlays, 'شفافات العمليات (IPB)', Icons.layers));\n }"
|
|
}
|
|
|
|
# We need to remove the existing bodies of these methods.
|
|
for func_start, new_func in replacements.items():
|
|
# Find the start of the function
|
|
idx = content.find(func_start)
|
|
if idx == -1: continue
|
|
|
|
# Find the matching closing brace (very naive approach since it's at the end of the file)
|
|
end_idx = idx + len(func_start)
|
|
brace_count = 1
|
|
while brace_count > 0 and end_idx < len(content):
|
|
if content[end_idx] == '{': brace_count += 1
|
|
elif content[end_idx] == '}': brace_count -= 1
|
|
end_idx += 1
|
|
|
|
# Replace
|
|
content = content[:idx] + new_func + content[end_idx:]
|
|
|
|
with open(path, "w") as f:
|
|
f.write(content)
|