Se implementó un sistema de gestión de historial de deshacer (undo) en la aplicación, permitiendo capturar y restaurar estados de objetos seleccionados. Se añadieron métodos para limpiar el historial y se mejoró la interfaz de usuario para mostrar información sobre el estado del historial de deshacer. Además, se realizaron ajustes en la lógica de manipulación de objetos para asegurar la correcta captura de estados antes de movimientos y redimensionamientos.
This commit is contained in:
parent
20467c88ae
commit
94b11cf068
|
@ -447,6 +447,9 @@ namespace CtrEditor
|
|||
{
|
||||
OnVisFilterChanged(MainWindow.VisFilter.FilterViewModel);
|
||||
}
|
||||
|
||||
// Limpiar historial de undo al cargar un estado desde archivo
|
||||
MainWindow?.ClearUndoHistory();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,6 +458,9 @@ namespace CtrEditor
|
|||
{
|
||||
// Suponiendo que "SelectedImage" es una propiedad que al establecerse dispara "ImageSelected"
|
||||
directorioTrabajo = EstadoPersistente.Instance.directorio;
|
||||
|
||||
// Limpiar historial de undo al cargar datos iniciales
|
||||
MainWindow?.ClearUndoHistory();
|
||||
}
|
||||
|
||||
// Crear un nuevo Objeto
|
||||
|
@ -794,6 +800,9 @@ namespace CtrEditor
|
|||
|
||||
// Restaurar los rectángulos de selección si hay objetos seleccionados
|
||||
_objectManager.UpdateSelectionVisuals();
|
||||
|
||||
// Limpiar historial de undo al detener la simulación
|
||||
MainWindow?.ClearUndoHistory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -869,6 +878,8 @@ namespace CtrEditor
|
|||
foreach (var objetoSimulable in ObjetosSimulables)
|
||||
objetoSimulable.SetPLC(null);
|
||||
|
||||
// Limpiar historial de undo al desconectar del PLC
|
||||
MainWindow?.ClearUndoHistory();
|
||||
}
|
||||
|
||||
private List<osBase> objetosSimulablesLlamados = new List<osBase>();
|
||||
|
|
|
@ -413,6 +413,9 @@ namespace CtrEditor
|
|||
|
||||
private void MoveSelectedObjects(float deltaX, float deltaY)
|
||||
{
|
||||
// Capturar estado antes de mover
|
||||
_objectManager.CaptureUndoState();
|
||||
|
||||
// Mover todos los objetos primero
|
||||
foreach (var obj in _objectManager.SelectedObjects)
|
||||
{
|
||||
|
@ -521,6 +524,11 @@ namespace CtrEditor
|
|||
debugWindow.Show();
|
||||
}
|
||||
|
||||
public void ClearUndoHistory()
|
||||
{
|
||||
_objectManager.ClearUndoHistory();
|
||||
}
|
||||
|
||||
private void Canvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// Aceptar el evento si viene del canvas o de la imagen de fondo
|
||||
|
@ -672,8 +680,11 @@ namespace CtrEditor
|
|||
contextMenu.Items.Add(pasteReplaceMenuItem);
|
||||
|
||||
contextMenu.Items.Add(new Separator());
|
||||
}
|
||||
|
||||
// Opciones de bloqueo/desbloqueo
|
||||
// Opciones de bloqueo/desbloqueo - mostrar siempre que haya objetos seleccionados
|
||||
if (_objectManager.SelectedObjects.Count > 0)
|
||||
{
|
||||
var lockSubmenu = new MenuItem { Header = "Bloqueo" };
|
||||
|
||||
// Verificar el estado actual de los objetos
|
||||
|
@ -702,6 +713,17 @@ namespace CtrEditor
|
|||
contextMenu.Items.Add(lockSubmenu);
|
||||
contextMenu.Items.Add(new Separator());
|
||||
}
|
||||
|
||||
// Agregar información del sistema de undo
|
||||
var undoHistoryCount = _objectManager.GetUndoHistoryCount();
|
||||
var canUndo = _objectManager.CanUndo();
|
||||
var undoInfoItem = new MenuItem
|
||||
{
|
||||
Header = $"Undo: {undoHistoryCount}/3 estados ({(canUndo ? "Ctrl+Z disponible" : "No disponible")})",
|
||||
IsEnabled = false,
|
||||
FontStyle = FontStyles.Italic
|
||||
};
|
||||
contextMenu.Items.Add(undoInfoItem);
|
||||
|
||||
contextMenu.IsOpen = true;
|
||||
contextMenu.PlacementTarget = ImagenEnTrabajoCanvas;
|
||||
|
@ -755,6 +777,11 @@ namespace CtrEditor
|
|||
PasteObjectsFromJson();
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.Z)
|
||||
{
|
||||
_objectManager.PerformUndo();
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (_objectManager.SelectedObjects.Any())
|
||||
{
|
||||
const float moveDistance = 0.01f;
|
||||
|
|
|
@ -10,6 +10,58 @@ using Color = System.Windows.Media.Color;
|
|||
|
||||
namespace CtrEditor
|
||||
{
|
||||
// Clase para almacenar el estado básico de un objeto para undo
|
||||
public class ObjectState
|
||||
{
|
||||
public int ObjectId { get; set; }
|
||||
public float Left { get; set; }
|
||||
public float Top { get; set; }
|
||||
public float Angle { get; set; }
|
||||
public float Width { get; set; }
|
||||
public float Height { get; set; }
|
||||
|
||||
public ObjectState(osBase obj)
|
||||
{
|
||||
ObjectId = obj.Id.Value;
|
||||
Left = obj.Left;
|
||||
Top = obj.Top;
|
||||
Angle = obj.Angulo;
|
||||
Width = obj.Ancho;
|
||||
Height = obj.Alto;
|
||||
}
|
||||
|
||||
public ObjectState(ObjectState other)
|
||||
{
|
||||
ObjectId = other.ObjectId;
|
||||
Left = other.Left;
|
||||
Top = other.Top;
|
||||
Angle = other.Angle;
|
||||
Width = other.Width;
|
||||
Height = other.Height;
|
||||
}
|
||||
}
|
||||
|
||||
// Clase para almacenar un estado completo del workspace
|
||||
public class UndoState
|
||||
{
|
||||
public List<ObjectState> ObjectStates { get; set; }
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
public UndoState()
|
||||
{
|
||||
ObjectStates = new List<ObjectState>();
|
||||
Timestamp = DateTime.Now;
|
||||
}
|
||||
|
||||
public UndoState(IEnumerable<osBase> objects) : this()
|
||||
{
|
||||
foreach (var obj in objects)
|
||||
{
|
||||
ObjectStates.Add(new ObjectState(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum AlignmentType
|
||||
{
|
||||
Left,
|
||||
|
@ -76,6 +128,11 @@ namespace CtrEditor
|
|||
private bool _isRectangleSelectionActive;
|
||||
private bool _selectedObjectsAreVisible;
|
||||
|
||||
// Sistema de Undo
|
||||
private Stack<UndoState> _undoHistory = new Stack<UndoState>();
|
||||
private const int MaxUndoSteps = 3;
|
||||
private bool _isApplyingUndo = false;
|
||||
|
||||
public bool IsRectangleSelectionActive
|
||||
{
|
||||
get => _isRectangleSelectionActive;
|
||||
|
@ -615,6 +672,9 @@ namespace CtrEditor
|
|||
return;
|
||||
}
|
||||
|
||||
// Capturar estado antes de iniciar redimensionamiento
|
||||
CaptureUndoState();
|
||||
|
||||
_currentDraggingRectangle = sender as Rectangle;
|
||||
_lastMousePosition = e.GetPosition(_canvas);
|
||||
_currentDraggingRectangle.CaptureMouse();
|
||||
|
@ -625,25 +685,49 @@ namespace CtrEditor
|
|||
}
|
||||
|
||||
var userControl = sender as UserControl;
|
||||
if (userControl != null)
|
||||
if (userControl != null && userControl.DataContext is osBase datos)
|
||||
{
|
||||
if (userControl.DataContext is osBase datos)
|
||||
bool isControlPressed = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
|
||||
|
||||
// Determinar qué objetos serán afectados por el movimiento
|
||||
List<osBase> objectsToMove = new List<osBase>();
|
||||
|
||||
if (!isControlPressed)
|
||||
{
|
||||
HandleObjectSelection(userControl, datos);
|
||||
// Si no está Ctrl presionado y vamos a hacer dragging
|
||||
if (_selectedObjects.Contains(datos))
|
||||
{
|
||||
// Si el objeto ya está seleccionado, vamos a mover todos los seleccionados
|
||||
objectsToMove.AddRange(_selectedObjects);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Si no está seleccionado, solo vamos a mover este objeto
|
||||
objectsToMove.Add(datos);
|
||||
}
|
||||
|
||||
// Actualizar el estado de objetos bloqueados después de la selección
|
||||
hasLockedObjects = _selectedObjects.Any(obj => obj.Lock_movement);
|
||||
// Verificar si alguno de los objetos a mover está bloqueado
|
||||
bool willMoveLockedObjects = objectsToMove.Any(obj => obj.Lock_movement);
|
||||
|
||||
if (!willMoveLockedObjects)
|
||||
{
|
||||
// Capturar estado antes de iniciar movimiento
|
||||
CaptureUndoStateForObjects(objectsToMove);
|
||||
|
||||
userControl.CaptureMouse();
|
||||
_currentDraggingControl = userControl;
|
||||
_isMovingUserControl = true;
|
||||
InitializeDrag(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Solo iniciar el arrastre si no se presionó Ctrl y no hay objetos bloqueados
|
||||
if (!(Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && !hasLockedObjects)
|
||||
{
|
||||
userControl.CaptureMouse();
|
||||
_currentDraggingControl = userControl;
|
||||
_isMovingUserControl = true;
|
||||
InitializeDrag(e);
|
||||
}
|
||||
else if (hasLockedObjects)
|
||||
|
||||
// Manejar la selección después de capturar el estado
|
||||
HandleObjectSelection(userControl, datos);
|
||||
|
||||
// Actualizar el estado de objetos bloqueados después de la selección
|
||||
hasLockedObjects = _selectedObjects.Any(obj => obj.Lock_movement);
|
||||
|
||||
if (hasLockedObjects && !isControlPressed)
|
||||
{
|
||||
// Si hay objetos bloqueados, no permitir el inicio del arrastre
|
||||
e.Handled = true;
|
||||
|
@ -929,6 +1013,9 @@ namespace CtrEditor
|
|||
return; // No alinear si hay objetos bloqueados
|
||||
}
|
||||
|
||||
// Capturar estado antes de alinear
|
||||
CaptureUndoState();
|
||||
|
||||
var alignment = new ObjectAlignment(_selectedObjects, _selectedObjects.FirstOrDefault());
|
||||
|
||||
switch (alignmentType)
|
||||
|
@ -1090,5 +1177,149 @@ namespace CtrEditor
|
|||
UpdateSelectionRectangle(currentPoint);
|
||||
}
|
||||
}
|
||||
|
||||
// Métodos del sistema de Undo
|
||||
public void CaptureUndoState()
|
||||
{
|
||||
// Solo capturar estado si no estamos aplicando un undo y si es permitido
|
||||
if (_isApplyingUndo || !CanPerformUndo())
|
||||
return;
|
||||
|
||||
// Solo capturar estado si hay objetos seleccionados
|
||||
if (_selectedObjects.Count == 0)
|
||||
return;
|
||||
|
||||
if (_mainWindow.DataContext is MainViewModel viewModel)
|
||||
{
|
||||
// Capturar solo el estado de los objetos seleccionados
|
||||
var undoState = new UndoState(_selectedObjects);
|
||||
|
||||
// Mantener solo los últimos MaxUndoSteps estados
|
||||
if (_undoHistory.Count >= MaxUndoSteps)
|
||||
{
|
||||
// Convertir a lista, remover el más antiguo, y volver a crear el stack
|
||||
var tempList = _undoHistory.ToList();
|
||||
tempList.Reverse(); // Invertir para mantener el orden correcto
|
||||
tempList.RemoveAt(0); // Remover el más antiguo
|
||||
_undoHistory.Clear();
|
||||
foreach (var state in tempList)
|
||||
{
|
||||
_undoHistory.Push(state);
|
||||
}
|
||||
}
|
||||
|
||||
_undoHistory.Push(undoState);
|
||||
Console.WriteLine($"Estado capturado para undo de {_selectedObjects.Count} objetos. Historial: {_undoHistory.Count} estados");
|
||||
}
|
||||
}
|
||||
|
||||
// Método para capturar el estado de objetos específicos
|
||||
public void CaptureUndoStateForObjects(IEnumerable<osBase> objects)
|
||||
{
|
||||
// Solo capturar estado si no estamos aplicando un undo y si es permitido
|
||||
if (_isApplyingUndo || !CanPerformUndo())
|
||||
return;
|
||||
|
||||
var objectsList = objects.ToList();
|
||||
if (objectsList.Count == 0)
|
||||
return;
|
||||
|
||||
if (_mainWindow.DataContext is MainViewModel viewModel)
|
||||
{
|
||||
// Capturar solo el estado de los objetos especificados
|
||||
var undoState = new UndoState(objectsList);
|
||||
|
||||
// Mantener solo los últimos MaxUndoSteps estados
|
||||
if (_undoHistory.Count >= MaxUndoSteps)
|
||||
{
|
||||
// Convertir a lista, remover el más antiguo, y volver a crear el stack
|
||||
var tempList = _undoHistory.ToList();
|
||||
tempList.Reverse(); // Invertir para mantener el orden correcto
|
||||
tempList.RemoveAt(0); // Remover el más antiguo
|
||||
_undoHistory.Clear();
|
||||
foreach (var state in tempList)
|
||||
{
|
||||
_undoHistory.Push(state);
|
||||
}
|
||||
}
|
||||
|
||||
_undoHistory.Push(undoState);
|
||||
Console.WriteLine($"Estado capturado para undo de {objectsList.Count} objetos específicos. Historial: {_undoHistory.Count} estados");
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanPerformUndo()
|
||||
{
|
||||
if (_mainWindow.DataContext is MainViewModel viewModel)
|
||||
{
|
||||
// Solo permitir undo si no estamos en simulación ni conectados
|
||||
return !viewModel.IsSimulationRunning && !viewModel.IsConnected;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanUndo()
|
||||
{
|
||||
return CanPerformUndo() && _undoHistory.Count > 0;
|
||||
}
|
||||
|
||||
public void PerformUndo()
|
||||
{
|
||||
if (!CanUndo())
|
||||
return;
|
||||
|
||||
if (_mainWindow.DataContext is MainViewModel viewModel)
|
||||
{
|
||||
_isApplyingUndo = true;
|
||||
|
||||
try
|
||||
{
|
||||
var undoState = _undoHistory.Pop();
|
||||
Console.WriteLine($"Aplicando undo. Estados restantes: {_undoHistory.Count}");
|
||||
|
||||
// Aplicar el estado a cada objeto
|
||||
foreach (var objectState in undoState.ObjectStates)
|
||||
{
|
||||
var obj = viewModel.ObjetosSimulables.FirstOrDefault(o => o.Id.Value == objectState.ObjectId);
|
||||
if (obj != null)
|
||||
{
|
||||
// Aplicar solo los parámetros básicos
|
||||
obj.Left = objectState.Left;
|
||||
obj.Top = objectState.Top;
|
||||
obj.Angulo = objectState.Angle;
|
||||
obj.Ancho = objectState.Width;
|
||||
obj.Alto = objectState.Height;
|
||||
|
||||
// Actualizar la posición visual
|
||||
obj.OnMoveResizeRotate();
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar los visuales de selección
|
||||
UpdateSelectionVisuals();
|
||||
|
||||
// Marcar como cambios sin guardar
|
||||
viewModel.HasUnsavedChanges = true;
|
||||
|
||||
Console.WriteLine("Undo aplicado exitosamente");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isApplyingUndo = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearUndoHistory()
|
||||
{
|
||||
_undoHistory.Clear();
|
||||
Console.WriteLine("Historial de undo limpiado");
|
||||
}
|
||||
|
||||
// Método de conveniencia para obtener información del historial
|
||||
public int GetUndoHistoryCount()
|
||||
{
|
||||
return _undoHistory.Count;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,12 +5,13 @@
|
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:vm="clr-namespace:CtrEditor.ObjetosSim"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.DataContext>
|
||||
<vm:osFramePlate Color_Titulo="Black" Alto_Titulo="0.1" Color="WhiteSmoke" />
|
||||
</UserControl.DataContext>
|
||||
|
||||
<Grid>
|
||||
<Grid.RenderTransform>
|
||||
<RotateTransform Angle="{Binding Angulo}" />
|
||||
</Grid.RenderTransform>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
|
@ -24,6 +25,5 @@
|
|||
Fill="{Binding Color, Converter={StaticResource ColorToBrushConverter}}" Stroke="Blue"
|
||||
StrokeThickness="0.2"
|
||||
Visibility="{Binding ShowPlate, Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
Loading…
Reference in New Issue