192 lines
7.6 KiB
C#
192 lines
7.6 KiB
C#
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<GraphNode> _nodes = new();
|
||
private readonly List<GraphConnection> _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;
|
||
}
|
||
}
|
||
}
|