import 'dart:math'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:go_router/go_router.dart'; import 'package:latlong2/latlong.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:oc_front/core/services/specialized_services/datacenter_service.dart'; import 'package:oc_front/main.dart'; import 'package:oc_front/models/resources/compute.dart'; import 'package:oc_front/models/resources/resources.dart'; import 'package:oc_front/models/resources/storage.dart'; import 'package:oc_front/pages/abstract_page.dart'; import 'package:oc_front/widgets/items/item_row.dart'; class MapFactory implements AbstractFactory { @override GlobalKey getKey() { return key; } @override String? getSearch() { return ""; } @override void back(BuildContext context) { } static GlobalKey key = GlobalKey(); @override bool searchFill() { return false; } @override Widget factory(GoRouterState state, List args) { return MapPageWidget(); } @override void search(BuildContext context, bool special) { } } double menuSize = 0; class MapPageWidget extends StatefulWidget { bool isShowed = false; final DatacenterService _service = DatacenterService(); MapPageWidget(): super(key: MapFactory.key); @override MapPageWidgetState createState() => MapPageWidgetState(); static void search(BuildContext context) { } static Widget factory() { return MapPageWidget(); } } class MapPageWidgetState extends State { double currentZoom = 2.0; LatLng currentCenter = const LatLng(51.5, -0.09); static final MapController _mapController = MapController(); bool selected = true; @override Widget build(BuildContext context) { return FutureBuilder(future: widget._service.all(context), builder: (BuildContext context, AsyncSnapshot snapshot) { Map> coordinates = {}; List markerCoordinates = []; if (snapshot.data != null&& snapshot.data!.data != null) { for (var element in snapshot.data!.data!.values) { if (element["type"] == "storage") { StorageItem resource = StorageItem().deserialize(element); var instance = resource.getSelectedInstance(); if (instance == null || instance.location == null) { continue; } if (coordinates[resource.topic] == null) { coordinates[resource.topic] = {}; } coordinates[resource.topic]![resource] = LatLng(instance.location!.latitude ?? 0, instance.location!.longitude ?? 0); } else if (element["type"] == "compute") { ComputeItem resource = ComputeItem().deserialize(element); var instance = resource.getSelectedInstance(); if (instance == null || instance.location == null) { continue; } if (coordinates[resource.topic] == null) { coordinates[resource.topic] = {}; } var lr = Random().nextInt(max(1, 100)) / 10000; var lfr = Random().nextInt(max(1, 100)) / 10000; coordinates[resource.topic]![resource] = LatLng((instance.location!.latitude ?? 0) + lr, (instance.location!.longitude ?? 0) + lfr); } } for (var topic in coordinates.keys) { for (var coord in coordinates[topic]!.keys) { markerCoordinates.add(Marker( width: 25, height: 30, point: coordinates[topic]![coord]!, child: HoverMenu( width: 110, title: Container( alignment: Alignment.center, constraints: const BoxConstraints( maxHeight: 100, maxWidth: 100 ), child: Icon(FontAwesomeIcons.locationDot, shadows: const [Shadow(color: Color.fromRGBO(0, 0, 0, 1), blurRadius: 10.0)], color: getColor(topic)) ), items: [ Container(color: Colors.white, child: ItemRowWidget(low: true, contextWidth: 290, item: coord)) ] ), )); } } Rect rect = Rect.fromCenter( center: MediaQuery.of(context).size.center(Offset.zero), width: selected ? menuSize : 0, height: (getMainHeight(context) - 50) > 0 ? (getMainHeight(context) - 50) : 0); return Expanded( child : FlutterMap( mapController: _mapController, options: MapOptions( initialCenter: currentCenter, initialZoom: currentZoom, ), children: [ TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', userAgentPackageName: 'dev.fleaflet.flutter_map.example', ), MarkerLayer( markers: markerCoordinates, ), ], ), ); } else { return const CircularProgressIndicator(); } } ); } } class HoverMenuController { HoverMenuState? currentState; void hideSubMenu() { currentState?.hideSubMenu(); } } class HoverMenu extends StatefulWidget { final Widget title; final double? width; final List items; final HoverMenuController? controller; bool isHovered = false; HoverMenu({ Key? key, required this.title, this.items = const [], this.width, this.controller, }) : super(key: key); @override HoverMenuState createState() => HoverMenuState(); } class HoverMenuState extends State { OverlayEntry? _overlayEntry; final _focusNode = FocusNode(); @override void initState() { super.initState(); _focusNode.addListener(_onFocusChanged); if (widget.controller != null) { widget.controller?.currentState = this; } } @override void dispose() { _focusNode.dispose(); super.dispose(); } void _onFocusChanged() { if (_focusNode.hasFocus) { _overlayEntry = _createOverlayEntry(); Overlay.of(context).insert(_overlayEntry!); } else { _overlayEntry?.remove(); _removeOverlay(); } } void _removeOverlay() { widget.isHovered = false; } void hideSubMenu() { _focusNode.unfocus(); } @override Widget build(BuildContext context) { return TextButton( style: TextButton.styleFrom( elevation: 0.0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)), splashFactory: NoSplash.splashFactory, padding: EdgeInsets.zero, foregroundColor: Colors.transparent, surfaceTintColor: Colors.transparent, backgroundColor: Colors.transparent, shadowColor: Colors.transparent, ), focusNode: _focusNode, onHover: (isHovered) { if (isHovered && !widget.isHovered) { _focusNode.requestFocus(); isHovered = true; } else { _focusNode.unfocus(); widget.isHovered = false; } }, onPressed: () {}, child: widget.title, ); } OverlayEntry _createOverlayEntry() { final renderBox = context.findRenderObject() as RenderBox; final size = renderBox.size; final offset = renderBox.localToGlobal(Offset.zero); return OverlayEntry( maintainState: true, builder: (context) => Positioned( left: offset.dx - 300, top: offset.dy + size.height, width: 300, child: TextButton( style: TextButton.styleFrom( padding: EdgeInsets.zero, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)), splashFactory: NoSplash.splashFactory, backgroundColor: Colors.transparent, ), onPressed: () {}, child: ListView( padding: EdgeInsets.zero, shrinkWrap: true, children: widget.items)), ), ); } }