NoteMAUI/GraphPage.xaml.cs
2024-11-11 15:24:11 +03:00

260 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Diagnostics;
using System.Linq;
namespace NotesDataAnalyst
{
public partial class GraphPage : ContentPage
{
private Point _initialPanPosition;
private PointF _initialNodePosition;
private PointF _initialTouchPosition;
private SizeF _initialNodeSize;
private readonly List<GraphNode> _nodes = new();
private readonly List<GraphConnection> _connections = new();
private GraphNode _selectedNode;
private bool _isScrollingBlocked = false;
private double _scale = 1.0;
private double _initialScale = 1.0;
private bool _isNodeBeingMoved = false;
private const double SmoothFactor = 0.2; // Коэффициент сглаживания
private Point _lastTranslation;
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);
// Добавляем Pinch только один раз
if (!canvasView.GestureRecognizers.OfType<PinchGestureRecognizer>().Any())
{
var pinchGesture = new PinchGestureRecognizer();
pinchGesture.PinchUpdated += OnPinchUpdated;
canvasView.GestureRecognizers.Add(pinchGesture);
}
}
// Обработчик жеста перемещения (Pan) для перемещения канвы или ноды
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
if (_selectedNode != null)
{
// Начало перетаскивания
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;
}
}
else // Перемещение канвы при снятии выделения с ноды
{
if (e.StatusType == GestureStatus.Started)
{
_initialPanPosition = new Point(canvasView.TranslationX, canvasView.TranslationY);
_lastTranslation = _initialPanPosition;
}
else if (e.StatusType == GestureStatus.Running)
{
var deltaX = e.TotalX;
var deltaY = e.TotalY;
// Применяем сглаживание к движению
double smoothedX = _lastTranslation.X + SmoothFactor * (deltaX - _lastTranslation.X);
double smoothedY = _lastTranslation.Y + SmoothFactor * (deltaY - _lastTranslation.Y);
// Обновляем положение канвы
canvasView.TranslationX = _initialPanPosition.X + smoothedX;
canvasView.TranslationY = _initialPanPosition.Y + smoothedY;
// Сохраняем последнюю смягченную позицию для следующего цикла
_lastTranslation = new Point(smoothedX, smoothedY);
}
else if (e.StatusType == GestureStatus.Completed)
{
_initialPanPosition = new Point(canvasView.TranslationX, canvasView.TranslationY);
canvasView.Invalidate();
}
}
}
private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started)
{
// Сохраняем начальный масштаб
_initialScale = _scale;
}
else if (e.Status == GestureStatus.Running)
{
// Вычисляем новый масштаб
_scale = Math.Max(0.5, Math.Min(_initialScale * e.Scale, 3.0));
// Применяем масштаб к канве
canvasView.Scale = _scale;
}
}
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();
_selectedNode = childNode;
}
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 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;
}
}
}