Хрен знает

This commit is contained in:
ARCHI86 2024-11-10 23:04:15 +03:00
parent 1303209095
commit af9466c0a6
3 changed files with 110 additions and 245 deletions

View file

@ -10,7 +10,7 @@
<!-- Главная страница приложения --> <!-- Главная страница приложения -->
<FlyoutItem Title="Главная" Icon="add.png"> <FlyoutItem Title="Главная" Icon="add.png">
<ShellContent ContentTemplate="{DataTemplate local:ResizableCanvasPage}" /> <ShellContent ContentTemplate="{DataTemplate local:GraphPage}" />
</FlyoutItem> </FlyoutItem>
<!-- Выпадающий список категорий --> <!-- Выпадающий список категорий -->

View file

@ -1,18 +1,13 @@
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
namespace NotesDataAnalyst namespace NotesDataAnalyst
{ {
public partial class GraphPage : ContentPage public partial class GraphPage : ContentPage
{ {
private PointF _initialNodePosition;
private PointF _initialNodePosition; // Õðàíèò íà÷àëüíóþ ïîçèöèþ íîäû private PointF _initialTouchPosition;
private PointF _initialTouchPosition; // Õðàíèò íà÷àëüíóþ ïîçèöèþ êàñàíèÿ private SizeF _initialNodeSize;
private SizeF _initialNodeSize; // Õðàíèò íà÷àëüíûé ðàçìåð íîäû
private DateTime tapStartTime;
private const int longPressDuration = 3000; // 3 ñåêóíäû â ìèëëèñåêóíäàõ
private readonly List<GraphNode> _nodes = new(); private readonly List<GraphNode> _nodes = new();
private readonly List<GraphConnection> _connections = new(); private readonly List<GraphConnection> _connections = new();
private GraphNode _selectedNode; private GraphNode _selectedNode;
@ -22,13 +17,9 @@ namespace NotesDataAnalyst
{ {
InitializeComponent(); InitializeComponent();
// Íàñòðàèâàåì Drawable äëÿ îòðèñîâêè óçëîâ è ñâÿçåé
var graphDrawable = new GraphDrawable(_nodes, _connections); var graphDrawable = new GraphDrawable(_nodes, _connections);
canvasView.Drawable = graphDrawable; canvasView.Drawable = graphDrawable;
// Äîáàâëÿåì æåñòû
var panGesture = new PanGestureRecognizer(); var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += OnPanUpdated; panGesture.PanUpdated += OnPanUpdated;
canvasView.GestureRecognizers.Add(panGesture); canvasView.GestureRecognizers.Add(panGesture);
@ -36,73 +27,6 @@ namespace NotesDataAnalyst
var tapGesture = new TapGestureRecognizer(); var tapGesture = new TapGestureRecognizer();
tapGesture.Tapped += OnCanvasTapped; tapGesture.Tapped += OnCanvasTapped;
canvasView.GestureRecognizers.Add(tapGesture); canvasView.GestureRecognizers.Add(tapGesture);
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += OnTapped;
canvasView.GestureRecognizers.Add(tapGestureRecognizer);
}
private void OnTapped(object sender, EventArgs e)
{
tapStartTime = DateTime.Now;
Device.StartTimer(TimeSpan.FromMilliseconds(longPressDuration), () =>
{
if ((DateTime.Now - tapStartTime).TotalMilliseconds >= longPressDuration)
{
// Çäåñü âûïîëíÿåòñÿ äåéñòâèå ïðè äëèòåëüíîì íàæàòèè
// OnNodeLongPressed();
}
return false; // Îñòàíàâëèâàåì òàéìåð
});
}
/*
private void OnNodeLongPressed()
{
if (_selectedNode != null)
{
// Äîáàâëÿåì äî÷åðíþþ íîäó ðÿäîì ñ àêòèâíîé
var childNode = new GraphNode
{
Text = "Äî÷åðíÿÿ íîäà",
Position = new Point(_selectedNode.Position.X + 50, _selectedNode.Position.Y + 50),
Size = new Size(150, 100)
};
_nodes.Add(childNode);
_connections.Add(new GraphConnection { FromNode = _selectedNode, ToNode = childNode });
canvasView.Invalidate(); // Ïåðåðèñîâûâàåì
}
// Çäåñü ðåàëèçóéòå äåéñòâèå ïðè äëèòåëüíîì íàæàòèè
// DisplayAlert("Äëèòåëüíîå íàæàòèå", "Âû íàæàëè è óäåðæèâàëè ýëåìåíò", "ÎÊ");
}*/
private void OnNodeTapped(object sender, TappedEventArgs e)
{
var node = _selectedNode;
Debug.WriteLine("Íàæàòà íîäà - {node}");
}
private void OnChildClicked(object sender, EventArgs e)
{
if (_selectedNode != null)
{
var child = new GraphNode
{
Text = "Child Node",
Position = new Point(_selectedNode.Position.X + 5, _selectedNode.Position.Y + 5),
Size = _selectedNode.Size,
BackgroundColor = Colors.Gray,
};
_nodes.Add(child);
_connections.Add(new GraphConnection { FromNode = _selectedNode, ToNode = child });
canvasView.Invalidate();
}
else { Debug.WriteLine("Ñíà÷àëà âûáèðèòå íîäó"); }
} }
private async void OnAddNodeClicked(object sender, EventArgs e) private async void OnAddNodeClicked(object sender, EventArgs e)
@ -110,7 +34,7 @@ namespace NotesDataAnalyst
var newNode = new GraphNode var newNode = new GraphNode
{ {
Text = "Íîâàÿ íîäà", Text = "Íîâàÿ íîäà",
Position = new Point(300, 200), Position = new Point(300, 200),
Size = new Size(200, 200), Size = new Size(200, 200),
@ -120,8 +44,28 @@ namespace NotesDataAnalyst
canvasView.Invalidate(); 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) private void OnCanvasTapped(object sender, TappedEventArgs e)
{ {
var tapPosition = e.GetPosition((View)sender); var tapPosition = e.GetPosition((View)sender);
@ -132,37 +76,27 @@ namespace NotesDataAnalyst
var touchPoint = new PointF((float)tapPosition.Value.X, (float)tapPosition.Value.Y); var touchPoint = new PointF((float)tapPosition.Value.X, (float)tapPosition.Value.Y);
_selectedNode = _nodes.FirstOrDefault(node => IsPointInsideNode(touchPoint, node)); _selectedNode = _nodes.FirstOrDefault(node => IsPointInsideNode(touchPoint, node));
// Ñáðîñ àêòèâíîñòè äëÿ âñåõ óçëîâ, êðîìå âûáðàííîãî
foreach (var node in _nodes) foreach (var node in _nodes)
node.IsActive = node == _selectedNode; node.IsActive = node == _selectedNode;
// Îáíîâëÿåì ýêðàí ïîñëå èçìåíåíèÿ ñîñòîÿíèÿ óçëîâ
canvasView.Invalidate(); canvasView.Invalidate();
} }
} }
private void OnPanUpdated(object sender, PanUpdatedEventArgs e) private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{ {
if (_selectedNode == null) return; if (_selectedNode == null) return;
if (e.StatusType == GestureStatus.Started) if (e.StatusType == GestureStatus.Started)
{ {
// Ñîõðàíÿåì íà÷àëüíóþ ïîçèöèþ íîäû è ðàçìåð
_initialNodePosition = _selectedNode.Position; _initialNodePosition = _selectedNode.Position;
_initialTouchPosition = new PointF((float)e.TotalX, (float)e.TotalY); _initialTouchPosition = new PointF((float)e.TotalX, (float)e.TotalY);
_initialNodeSize = _selectedNode.Size; // Ñîõðàíÿåì íà÷àëüíûé ðàçìåð _initialNodeSize = _selectedNode.Size;
// Ïðîâåðÿåì, íàõîäèìñÿ ëè ìû íà ðó÷êå èçìåíåíèÿ ðàçìåðà
_isResizing = IsOnRightResizeHandle(_selectedNode, _initialTouchPosition) ||
IsOnLeftResizeHandle(_selectedNode, _initialTouchPosition) ||
IsOnTopResizeHandle(_selectedNode, _initialTouchPosition) ||
IsOnBottomResizeHandle(_selectedNode, _initialTouchPosition);
_isResizing = IsOnResizeHandle(_selectedNode, _initialTouchPosition);
} }
else if (e.StatusType == GestureStatus.Running) else if (e.StatusType == GestureStatus.Running)
{ {
// Âû÷èñëÿåì ñìåùåíèå îò íà÷àëüíîé ïîçèöèè êàñàíèÿ
var deltaX = (float)e.TotalX - _initialTouchPosition.X; var deltaX = (float)e.TotalX - _initialTouchPosition.X;
var deltaY = (float)e.TotalY - _initialTouchPosition.Y; var deltaY = (float)e.TotalY - _initialTouchPosition.Y;
@ -172,7 +106,6 @@ namespace NotesDataAnalyst
} }
else else
{ {
// Ïåðåìåùåíèå
_selectedNode.Position = new PointF( _selectedNode.Position = new PointF(
_initialNodePosition.X + deltaX, _initialNodePosition.X + deltaX,
_initialNodePosition.Y + deltaY _initialNodePosition.Y + deltaY
@ -187,86 +120,32 @@ namespace NotesDataAnalyst
} }
} }
// Èçìåíåíèå ðàçìåðà óçëà
private void ResizeNode(GraphNode node, double deltaX, double deltaY) private void ResizeNode(GraphNode node, double deltaX, double deltaY)
{ {
const float minSize = 50f; // Óáèðàåì "f" äëÿ double const double minSize = 50.0;
// Óâåëè÷èâàåì èëè óìåíüøàåì ðàçìåð â çàâèñèìîñòè îò íàïðàâëåíèÿ
if (_isResizing) if (_isResizing)
{
// Åñëè ìû òÿíåì ïðàâóþ "ðó÷êó", óâåëè÷èâàåì øèðèíó
if (IsOnRightResizeHandle(node, _initialTouchPosition))
{ {
_selectedNode.Size = new Size( _selectedNode.Size = new Size(
Math.Max(minSize, node.Size.Width + deltaX), // double Math.Max(minSize, _initialNodeSize.Width + deltaX),
_selectedNode.Size.Height // double Math.Max(minSize, _initialNodeSize.Height + deltaY)
);
}
// Åñëè ìû òÿíåì ëåâóþ "ðó÷êó", óìåíüøàåì øèðèíó
else if (IsOnLeftResizeHandle(node, _initialTouchPosition))
{
_selectedNode.Size = new Size(
Math.Max(minSize, _selectedNode.Size.Width - deltaX), // double
_selectedNode.Size.Height // double
);
// Ïåðåìåùàåì íîäó âëåâî, ÷òîáû ñîõðàíèòü åå ïîçèöèþ
_selectedNode.Position = new Point(_selectedNode.Position.X + deltaX, _selectedNode.Position.Y); // double
}
// Åñëè ìû òÿíåì âåðõíþþ "ðó÷êó", óìåíüøàåì âûñîòó
else if (IsOnTopResizeHandle(node, _initialTouchPosition))
{
_selectedNode.Size = new Size(
_selectedNode.Size.Width, // double
Math.Max(minSize, _selectedNode.Size.Height - deltaY) // double
);
// Ïåðåìåùàåì íîäó ââåðõ, ÷òîáû ñîõðàíèòü åå ïîçèöèþ
_selectedNode.Position = new Point(_selectedNode.Position.X, _selectedNode.Position.Y + deltaY); // double
}
// Åñëè ìû òÿíåì íèæíþþ "ðó÷êó", óâåëè÷èâàåì âûñîòó
else if (IsOnBottomResizeHandle(node, _initialTouchPosition))
{
_selectedNode.Size = new Size(
_selectedNode.Size.Width, // double
Math.Max(minSize, _selectedNode.Size.Height + deltaY) // double
); );
} }
} }
private bool IsOnResizeHandle(GraphNode node, PointF point)
}
private bool IsOnRightResizeHandle(GraphNode node, PointF point)
{ {
const float handleSize = 10; const float handleSize = 20f;
return Math.Abs(point.X - (node.Position.X + node.Size.Width - 1)) <= handleSize;
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 IsOnLeftResizeHandle(GraphNode node, PointF point)
{
const float handleSize = 10;
return Math.Abs(point.X - node.Position.X) <= handleSize;
}
private bool IsOnTopResizeHandle(GraphNode node, PointF point)
{
const float handleSize = 10;
return Math.Abs(point.Y - node.Position.Y) <= handleSize;
}
private bool IsOnBottomResizeHandle(GraphNode node, PointF point)
{
const float handleSize = 10;
return Math.Abs(point.Y - (node.Position.Y + node.Size.Height)) <= handleSize;
}
// Ìåòîä äëÿ ïðîâåðêè, íàõîäèòñÿ ëè òî÷êà âíóòðè óçëà
private bool IsPointInsideNode(PointF point, GraphNode node) private bool IsPointInsideNode(PointF point, GraphNode node)
{ {
return point.X >= node.Position.X && return point.X >= node.Position.X &&
@ -274,42 +153,5 @@ namespace NotesDataAnalyst
point.Y >= node.Position.Y && point.Y >= node.Position.Y &&
point.Y <= node.Position.Y + node.Size.Height; point.Y <= node.Position.Y + node.Size.Height;
} }
// Ïðîâåðêà, íàõîäèòñÿ ëè òî÷êà íà "ðó÷êå" èçìåíåíèÿ ðàçìåðà
private bool IsOnResizeHandle(GraphNode node, PointF point)
{
const float handleSize = 20f; // Ðàçìåð ðó÷êè èçìåíåíèÿ ðàçìåðà
// Îïðåäåëÿåì ïðÿìîóãîëüíèêè äëÿ êàæäîé ðó÷êè
var rightHandleRect = new RectF(
(float)(node.Position.X + node.Size.Width - handleSize),
(float)(node.Position.Y + node.Size.Height - handleSize),
handleSize, handleSize);
var leftHandleRect = new RectF(
(float)(node.Position.X),
(float)(node.Position.Y + node.Size.Height - handleSize),
handleSize, handleSize);
var topHandleRect = new RectF(
(float)(node.Position.X + (node.Size.Width / 2) - (handleSize / 2)),
(float)(node.Position.Y),
handleSize, handleSize);
var bottomHandleRect = new RectF(
(float)(node.Position.X + (node.Size.Width / 2) - (handleSize / 2)),
(float)(node.Position.Y + node.Size.Height - handleSize),
handleSize, handleSize);
// Ïðîâåðÿåì, íàõîäèòñÿ ëè êóðñîð íà îäíîé èç ðó÷åê
return rightHandleRect.Contains(point) ||
leftHandleRect.Contains(point) ||
topHandleRect.Contains(point) ||
bottomHandleRect.Contains(point);
}
} }
} }

View file

@ -1,5 +1,6 @@
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Layouts;
using System; using System;
namespace NotesDataAnalyst namespace NotesDataAnalyst
@ -9,98 +10,120 @@ namespace NotesDataAnalyst
private const double InitialWidth = 150; private const double InitialWidth = 150;
private const double InitialHeight = 100; private const double InitialHeight = 100;
private double startX, startY; private double startX, startY;
private bool isResizing;
public ResizableCanvasPage() public ResizableCanvasPage()
{ {
InitializeComponent(); InitializeComponent();
} }
// Ìåòîä äëÿ äîáàâëåíèÿ îáúåêòà íà õîëñò
private void AddResizableObject(double x, double y) private void AddResizableObject(double x, double y)
{ {
// Ñîçäàíèå Frame äëÿ ãðàôè÷åñêîãî îáúåêòà // Ñîçäàåì îñíîâíîé êîíòåéíåð (Frame) äëÿ îáúåêòà è ðó÷êè
var resizableFrame = new Frame var resizableFrame = new Frame
{ {
BackgroundColor = Colors.LightBlue, BackgroundColor = Colors.LightBlue,
BorderColor = Colors.DarkBlue, BorderColor = Colors.DarkBlue,
CornerRadius = 8, CornerRadius = 8,
WidthRequest = InitialWidth, WidthRequest = InitialWidth,
HeightRequest = InitialHeight HeightRequest = InitialHeight,
Padding = 0,
HasShadow = true
}; };
// Îáðàáîòêà ïåðåìåùåíèÿ îáúåêòà var content = new Label
var panGesture = new PanGestureRecognizer(); {
panGesture.PanUpdated += (s, e) => OnDragPanUpdated(s, e, resizableFrame); Text = "Resizable Object",
resizableFrame.GestureRecognizers.Add(panGesture); HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
};
// Ñîçäàíèå ðó÷êè äëÿ èçìåíåíèÿ ðàçìåðà resizableFrame.Content = content;
// Ñîçäàåì ðó÷êó äëÿ èçìåíåíèÿ ðàçìåðà
var resizeHandle = new BoxView var resizeHandle = new BoxView
{ {
BackgroundColor = Colors.Gray, BackgroundColor = Colors.Gray,
WidthRequest = 20, WidthRequest = 20,
HeightRequest = 20 HeightRequest = 20,
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.End
}; };
// Îáðàáîòêà èçìåíåíèÿ ðàçìåðà îáúåêòà // Ñîçäàåì ñëîé AbsoluteLayout äëÿ ðó÷êè è ôðåéìà
var layout = new AbsoluteLayout();
AbsoluteLayout.SetLayoutBounds(resizableFrame, new Rect(0, 0, InitialWidth, InitialHeight));
layout.Children.Add(resizableFrame);
// Ïîçèöèîíèðóåì ðó÷êó
AbsoluteLayout.SetLayoutFlags(resizeHandle, AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(resizeHandle, new Rect(1, 1, 20, 20));
layout.Children.Add(resizeHandle);
// Äîáàâëÿåì îáðàáîò÷èê ïåðåìåùåíèÿ
var panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += (s, e) => OnDragPanUpdated(s, e, layout);
resizableFrame.GestureRecognizers.Add(panGesture);
// Äîáàâëÿåì îáðàáîò÷èê äëÿ èçìåíåíèÿ ðàçìåðà
var resizePanGesture = new PanGestureRecognizer(); var resizePanGesture = new PanGestureRecognizer();
resizePanGesture.PanUpdated += (s, e) => OnResizePanUpdated(s, e, resizableFrame, resizeHandle); resizePanGesture.PanUpdated += (s, e) => OnResizePanUpdated(s, e, layout);
resizeHandle.GestureRecognizers.Add(resizePanGesture); resizeHandle.GestureRecognizers.Add(resizePanGesture);
// Ïîçèöèîíèðîâàíèå îáúåêòà è ðó÷êè // Óñòàíàâëèâàåì íà÷àëüíóþ ïîçèöèþ
AbsoluteLayout.SetLayoutBounds(resizableFrame, new Rect(x, y, InitialWidth, InitialHeight)); AbsoluteLayout.SetLayoutBounds(layout, new Rect(x, y, InitialWidth, InitialHeight));
CanvasLayout.Children.Add(resizableFrame); CanvasLayout.Children.Add(layout);
CanvasLayout.Children.Add(resizeHandle);
UpdateResizeHandlePosition(resizableFrame, resizeHandle);
} }
// Îáðàáîòêà ïåðåìåùåíèÿ îáúåêòà // Ëîãèêà ïåðåìåùåíèÿ
private void OnDragPanUpdated(object sender, PanUpdatedEventArgs e, Frame frame) private void OnDragPanUpdated(object sender, PanUpdatedEventArgs e, AbsoluteLayout layout)
{ {
if (isResizing) return;
if (e.StatusType == GestureStatus.Started) if (e.StatusType == GestureStatus.Started)
{ {
startX = AbsoluteLayout.GetLayoutBounds(frame).X; startX = AbsoluteLayout.GetLayoutBounds(layout).X;
startY = AbsoluteLayout.GetLayoutBounds(frame).Y; startY = AbsoluteLayout.GetLayoutBounds(layout).Y;
} }
else if (e.StatusType == GestureStatus.Running) else if (e.StatusType == GestureStatus.Running)
{ {
double x = startX + e.TotalX; double x = startX + e.TotalX;
double y = startY + e.TotalY; double y = startY + e.TotalY;
AbsoluteLayout.SetLayoutBounds(frame, new Rect(x, y, frame.WidthRequest, frame.HeightRequest));
UpdateResizeHandlePosition(frame, CanvasLayout.Children[CanvasLayout.Children.IndexOf(frame) + 1] as BoxView); // Îáíîâëåíèå UI ÷åðåç Dispatcher
Dispatcher.Dispatch(() =>
{
AbsoluteLayout.SetLayoutBounds(layout, new Rect(x, y, layout.WidthRequest, layout.HeightRequest));
});
} }
} }
// Îáðàáîòêà èçìåíåíèÿ ðàçìåðà îáúåêòà // Ëîãèêà èçìåíåíèÿ ðàçìåðà
private void OnResizePanUpdated(object sender, PanUpdatedEventArgs e, Frame frame, BoxView resizeHandle) private void OnResizePanUpdated(object sender, PanUpdatedEventArgs e, AbsoluteLayout layout)
{ {
isResizing = true;
if (e.StatusType == GestureStatus.Started) if (e.StatusType == GestureStatus.Started)
{ {
startX = frame.WidthRequest; startX = layout.WidthRequest;
startY = frame.HeightRequest; startY = layout.HeightRequest;
} }
else if (e.StatusType == GestureStatus.Running) else if (e.StatusType == GestureStatus.Running)
{ {
double newWidth = Math.Max(50, startX + e.TotalX); double newWidth = Math.Max(50, startX + e.TotalX);
double newHeight = Math.Max(50, startY + e.TotalY); double newHeight = Math.Max(50, startY + e.TotalY);
frame.WidthRequest = newWidth;
frame.HeightRequest = newHeight;
UpdateResizeHandlePosition(frame, resizeHandle); Dispatcher.Dispatch(() =>
}
}
// Ìåòîä äëÿ ôèêñàöèè ïîçèöèè ðó÷êè â óãëó îáúåêòà
private void UpdateResizeHandlePosition(Frame frame, BoxView resizeHandle)
{ {
var frameBounds = AbsoluteLayout.GetLayoutBounds(frame); layout.WidthRequest = newWidth;
double x = frameBounds.X + frame.WidthRequest - resizeHandle.WidthRequest / 2; layout.HeightRequest = newHeight;
double y = frameBounds.Y + frame.HeightRequest - resizeHandle.HeightRequest / 2; });
}
AbsoluteLayout.SetLayoutBounds(resizeHandle, new Rect(x, y, resizeHandle.WidthRequest, resizeHandle.HeightRequest)); else if (e.StatusType == GestureStatus.Completed || e.StatusType == GestureStatus.Canceled)
{
isResizing = false;
}
} }
// Äîáàâëåíèå îáúåêòîâ íà õîëñò ïðè çàïóñêå
protected override void OnAppearing() protected override void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();