using System.Diagnostics; using System.Linq; namespace NotesDataAnalyst { public partial class GraphPage : ContentPage { private PointF _initialNodePosition; private PointF _initialTouchPosition; private SizeF _initialNodeSize; private readonly List _nodes = new(); private readonly List _connections = new(); private GraphNode _selectedNode; private bool _isResizing = false; public GraphPage() { InitializeComponent(); var graphDrawable = new GraphDrawable(_nodes, _connections); canvasView.Drawable = graphDrawable; var panGesture = new PanGestureRecognizer(); panGesture.PanUpdated += OnPanUpdated; canvasView.GestureRecognizers.Add(panGesture); var tapGesture = new TapGestureRecognizer(); tapGesture.Tapped += OnCanvasTapped; canvasView.GestureRecognizers.Add(tapGesture); } private async void OnResizeNodeClicked(object sender, EventArgs e) { // Проверяем, выбрана ли нода if (_selectedNode == null) { await DisplayAlert("Ошибка", "Сначала выберите ноду", "OK"); return; } // Открываем диалог для ввода новых размеров var result = await DisplayPromptAsync("Изменить размер", "Введите ширину и высоту", initialValue: $"{_selectedNode.Size.Width} x {_selectedNode.Size.Height}", keyboard: Keyboard.Numeric); if (string.IsNullOrEmpty(result)) return; // Разбиваем введенное значение на ширину и высоту var dimensions = result.Split('x'); if (dimensions.Length == 2 && double.TryParse(dimensions[0].Trim(), out double width) && double.TryParse(dimensions[1].Trim(), out double height)) { // Обновляем размеры ноды _selectedNode.Size = new Size(width, height); canvasView.Invalidate(); // Перерисовываем канву } else { await DisplayAlert("Ошибка", "Неверный формат. Введите значения в формате ширина x высота.", "OK"); } } private async void OnAddNodeClicked(object sender, EventArgs e) { var newNode = new GraphNode { Text = "Новая нода", Position = new Point(100, 200), Size = new Size(100, 50), }; _nodes.Add(newNode); canvasView.Invalidate(); } private void OnChildClicked(object sender, EventArgs e) { if (_selectedNode != null) { var childNode = new GraphNode { Text = "Child Node", Position = new Point(_selectedNode.Position.X + 100, _selectedNode.Position.Y + 70), Size = _selectedNode.Size, BackgroundColor = Colors.Gray, }; _nodes.Add(childNode); _connections.Add(new GraphConnection { FromNode = _selectedNode, ToNode = childNode }); canvasView.Invalidate(); } else { Debug.WriteLine("Сначала выберите ноду"); } } private void OnCanvasTapped(object sender, TappedEventArgs e) { var tapPosition = e.GetPosition((View)sender); _selectedNode = null; if (tapPosition.HasValue) { var touchPoint = new PointF((float)tapPosition.Value.X, (float)tapPosition.Value.Y); _selectedNode = _nodes.FirstOrDefault(node => IsPointInsideNode(touchPoint, node)); foreach (var node in _nodes) node.IsActive = node == _selectedNode; canvasView.Invalidate(); } } private void OnPanUpdated(object sender, PanUpdatedEventArgs e) { if (_selectedNode == null) return; if (e.StatusType == GestureStatus.Started) { _initialNodePosition = _selectedNode.Position; _initialTouchPosition = new PointF((float)e.TotalX, (float)e.TotalY); _initialNodeSize = _selectedNode.Size; _isResizing = IsOnResizeHandle(_selectedNode, _initialTouchPosition); } else if (e.StatusType == GestureStatus.Running) { var deltaX = (float)e.TotalX - _initialTouchPosition.X; var deltaY = (float)e.TotalY - _initialTouchPosition.Y; if (_isResizing) { ResizeNode(_selectedNode, deltaX, deltaY); } else { _selectedNode.Position = new PointF( _initialNodePosition.X + deltaX, _initialNodePosition.Y + deltaY ); } canvasView.Invalidate(); } else if (e.StatusType == GestureStatus.Completed) { _isResizing = false; } } private void ResizeNode(GraphNode node, double deltaX, double deltaY) { const double minSize = 50.0; if (_isResizing) { _selectedNode.Size = new Size( Math.Max(minSize, _initialNodeSize.Width + deltaX), Math.Max(minSize, _initialNodeSize.Height + deltaY) ); } } private bool IsOnResizeHandle(GraphNode node, PointF point) { const float handleSize = 20f; var resizeHandleRect = new RectF( (float)(node.Position.X + node.Size.Width - handleSize), (float)(node.Position.Y + node.Size.Height - handleSize), handleSize, handleSize ); return resizeHandleRect.Contains(point); } private bool IsPointInsideNode(PointF point, GraphNode node) { return point.X >= node.Position.X && point.X <= node.Position.X + node.Size.Width && point.Y >= node.Position.Y && point.Y <= node.Position.Y + node.Size.Height; } } }