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 _isScrollingBlocked = 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 OnEditTextClicked(object sender, EventArgs e) { // Проверяем, выбрана ли нода if (_selectedNode == null) { await DisplayAlert("Ошибка", "Сначала выберите ноду", "OK"); return; } // Показываем поле для редактирования текста и заполняем его текущим текстом ноды textEditor.IsVisible = true; textEditor.Text = _selectedNode.Text; // Ставим фокус на текстовое поле для ввода textEditor.Focus(); } // Обработчик завершения редактирования текста private void OnTextEditorCompleted(object sender, EventArgs e) { if (_selectedNode != null) { // Обновляем текст ноды с введенным значением _selectedNode.Text = textEditor.Text; // Скрываем поле для редактирования текста textEditor.IsVisible = false; // Перерисовываем канву для обновления отображения текста canvasView.Invalidate(); } } 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) { // Запоминаем начальную позицию касания и ноды _initialTouchPosition = new Point(e.TotalX, e.TotalY); _initialNodePosition = _selectedNode.Position; } // Перемещение else if (e.StatusType == GestureStatus.Running) { var deltaX = e.TotalX - _initialTouchPosition.X; // Разница с начальной точкой var deltaY = e.TotalY - _initialTouchPosition.Y; // Перемещаем ноду относительно её начальной позиции _selectedNode.Position = new Point( _initialNodePosition.X + deltaX, _initialNodePosition.Y + deltaY ); canvasView.Invalidate(); // Перерисовываем канву } // Завершение перетаскивания else if (e.StatusType == GestureStatus.Completed) { // Можно добавить логику на завершение перетаскивания, например, включить прокрутку scrollView.IsEnabled = true; } } 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; } } }