Se añadió la clase LibraryWindowSettings para gestionar la configuración de la ventana de la biblioteca, incluyendo propiedades para dimensiones y posición. Se implementó la persistencia de estas configuraciones al abrir y cerrar la ventana. Además, se mejoró la interfaz de usuario con un TreeView jerárquico para la gestión de bibliotecas y se añadieron comandos para crear y eliminar directorios de bibliotecas. Se implementó la selección múltiple de objetos en la ventana de la biblioteca, mejorando la experiencia del usuario al gestionar objetos.

This commit is contained in:
Miguel 2025-06-18 02:11:38 +02:00
parent c353f6c6ea
commit c03f6970d8
5 changed files with 512 additions and 101 deletions

View File

@ -5,9 +5,47 @@
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:CtrEditor.PopUps"
xmlns:ObjetosSim="clr-namespace:CtrEditor.ObjetosSim"
Title="Biblioteca de Objetos Simulables" Height="700" Width="1200"
WindowStartupLocation="CenterOwner" ResizeMode="CanResize"
Loaded="Window_Loaded">
xmlns:ctr="clr-namespace:CtrEditor"
Title="Biblioteca de Objetos Simulables"
Height="{Binding Source={x:Static ctr:EstadoPersistente.Instance}, Path=LibraryWindow.Height, Mode=TwoWay}"
Width="{Binding Source={x:Static ctr:EstadoPersistente.Instance}, Path=LibraryWindow.Width, Mode=TwoWay}"
Left="{Binding Source={x:Static ctr:EstadoPersistente.Instance}, Path=LibraryWindow.Left, Mode=TwoWay}"
Top="{Binding Source={x:Static ctr:EstadoPersistente.Instance}, Path=LibraryWindow.Top, Mode=TwoWay}"
WindowStartupLocation="Manual" ResizeMode="CanResize"
Loaded="Window_Loaded" Closing="Window_Closing">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter"/>
<!-- Estilo para nombres largos con wrapping -->
<Style x:Key="WrappingTextBlockStyle" TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="MaxWidth" Value="200"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- Estilo para nombres largos con formato especial -->
<Style x:Key="WrappingProjectTextBlockStyle" TargetType="TextBlock" BasedOn="{StaticResource WrappingTextBlockStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsCurrentProject}" Value="True">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
<!-- Menú contextual para directorios -->
<ContextMenu x:Key="DirectoryContextMenu">
<MenuItem Header="Nueva Biblioteca" Command="{Binding CreateLibraryInDirectoryCommand}"/>
<Separator/>
<MenuItem Header="Eliminar Directorio" Command="{Binding RemoveDirectoryCommand}"/>
</ContextMenu>
<!-- Estilo para TreeViewItem de directorios -->
<Style x:Key="DirectoryTreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="ContextMenu" Value="{StaticResource DirectoryContextMenu}"/>
</Style>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Key="C" Modifiers="Ctrl" Command="{Binding CopyObjectCommand}" />
@ -17,16 +55,16 @@
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" MinWidth="200" />
<ColumnDefinition Width="{Binding Source={x:Static ctr:EstadoPersistente.Instance}, Path=LibraryWindow.Column0Width, Mode=TwoWay}" MinWidth="200" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="300" MinWidth="250" />
<ColumnDefinition Width="{Binding Source={x:Static ctr:EstadoPersistente.Instance}, Path=LibraryWindow.Column2Width, Mode=TwoWay}" MinWidth="250" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="350" MinWidth="250" />
<ColumnDefinition Width="{Binding Source={x:Static ctr:EstadoPersistente.Instance}, Path=LibraryWindow.Column4Width, Mode=TwoWay}" MinWidth="250" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="*" MinWidth="300" />
</Grid.ColumnDefinitions>
<!-- Primera columna: Gestión de directorios de biblioteca -->
<!-- Primera columna: TreeView jerárquico de directorios y bibliotecas -->
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@ -34,16 +72,45 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Directorios de Biblioteca" FontWeight="Bold" />
<Label Grid.Row="0" Content="Bibliotecas" FontWeight="Bold" />
<ListBox Grid.Row="1" Name="LibraryDirectoriesList"
ItemsSource="{Binding LibraryDirectories}"
SelectedItem="{Binding SelectedLibraryDirectory, Mode=TwoWay}"
DisplayMemberPath="DisplayName" />
<TreeView Grid.Row="1" Name="LibraryTreeView"
ItemsSource="{Binding LibraryTreeNodes}"
SelectedItemChanged="LibraryTreeView_SelectedItemChanged">
<TreeView.ItemContainerStyleSelector>
<local:LibraryTreeItemStyleSelector DirectoryStyle="{StaticResource DirectoryTreeViewItemStyle}"/>
</TreeView.ItemContainerStyleSelector>
<TreeView.Resources>
<!-- Template for directory and library nodes -->
<HierarchicalDataTemplate DataType="{x:Type local:LibraryTreeNode}">
<HierarchicalDataTemplate.ItemsSource>
<Binding Path="Children"/>
</HierarchicalDataTemplate.ItemsSource>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="📁" Margin="0,0,5,0" VerticalAlignment="Center"
Visibility="{Binding IsDirectory, Converter={StaticResource BoolToVisConverter}}"/>
<TextBlock Text="📄" Margin="0,0,5,0" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsDirectory}" Value="False">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="{Binding DisplayName}" Style="{StaticResource WrappingProjectTextBlockStyle}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">
<Button Content="Agregar" Command="{Binding AddLibraryDirectoryCommand}" Margin="2" Padding="5,2" />
<Button Content="Eliminar" Command="{Binding RemoveLibraryDirectoryCommand}" Margin="2" Padding="5,2" />
<Button Content="Agregar Directorio" Command="{Binding AddLibraryDirectoryCommand}" Margin="2" Padding="5,2" />
</StackPanel>
</Grid>
@ -51,35 +118,25 @@
<GridSplitter Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"
Background="LightGray" Width="5" />
<!-- Segunda columna: Bibliotecas -->
<!-- Segunda columna: Filtros -->
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Bibliotecas" FontWeight="Bold" />
<TreeView Grid.Row="1" Name="LibrariesTreeView"
ItemsSource="{Binding Libraries}"
SelectedItemChanged="LibrariesTreeView_SelectedItemChanged">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">
<Button Content="Nueva Biblioteca" Command="{Binding CreateNewLibraryCommand}" Margin="2" Padding="5,2" />
</StackPanel>
<Label Grid.Row="0" Content="Filtros" FontWeight="Bold" />
<Expander Grid.Row="1" Header="Configuración de Filtros" IsExpanded="True">
<controls:osVisFilter x:Name="ObjectFilter" Margin="5"/>
</Expander>
</Grid>
<!-- GridSplitter 2 -->
<GridSplitter Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Stretch"
Background="LightGray" Width="5" />
<!-- Tercera columna: Objetos -->
<!-- Tercera columna: Objetos con selección múltiple -->
<Grid Grid.Column="4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@ -88,44 +145,44 @@
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Filtros -->
<Expander Grid.Row="0" Header="Filtros" IsExpanded="True">
<controls:osVisFilter x:Name="ObjectFilter" Margin="5"/>
</Expander>
<Label Grid.Row="0" Content="Objetos" FontWeight="Bold" />
<Label Grid.Row="1" Content="Objetos" FontWeight="Bold" />
<TreeView Grid.Row="2" Name="ObjectsTreeView"
ItemsSource="{Binding FilteredObjectsByType}"
SelectedItemChanged="ObjectsTreeView_SelectedItemChanged">
<TreeView.Resources>
<!-- Template for object type groups -->
<HierarchicalDataTemplate DataType="{x:Type local:ObjectTypeGroup}"
ItemsSource="{Binding Objects}">
<TextBlock Text="{Binding TypeName}" FontWeight="Bold" />
</HierarchicalDataTemplate>
<!-- Template for individual objects -->
<DataTemplate DataType="{x:Type ObjetosSim:osBase}">
<TextBlock Text="{Binding Nombre}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Enable_On_All_Pages}" Value="True">
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
<DataTrigger Binding="{Binding Cloned}" Value="True">
<Setter Property="Foreground" Value="Orange" />
</DataTrigger>
<DataTrigger Binding="{Binding AutoCreated}" Value="True">
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- Controles de selección múltiple -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5">
<Button Content="Seleccionar Todo" Click="SelectAllObjects_Click" Margin="2" Padding="5,2"/>
<Button Content="Deseleccionar Todo" Click="UnselectAllObjects_Click" Margin="2" Padding="5,2"/>
</StackPanel>
<!-- Listbox para objetos con checkboxes -->
<ListBox Grid.Row="2" Name="SelectableObjectsList"
ItemsSource="{Binding SelectableObjects}"
SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"
Margin="0,0,5,0" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Object.Nombre}" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Object.Enable_On_All_Pages}" Value="True">
<Setter Property="Foreground" Value="Blue" />
</DataTrigger>
<DataTrigger Binding="{Binding Object.Cloned}" Value="True">
<Setter Property="Foreground" Value="Orange" />
</DataTrigger>
<DataTrigger Binding="{Binding Object.AutoCreated}" Value="True">
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
</ListBox.ItemTemplate>
</ListBox>
<!-- Botones de acción -->
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">

View File

@ -1,6 +1,9 @@
using System.Windows;
using System.Windows.Controls;
using CtrEditor.ObjetosSim;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Data;
namespace CtrEditor.PopUps
{
@ -12,13 +15,57 @@ namespace CtrEditor.PopUps
public LibraryWindow()
{
InitializeComponent();
// Aplicar configuraciones persistentes
ApplyPersistedSettings();
// Suscribirse al evento StateChanged para persistir cambios de estado
StateChanged += LibraryWindow_StateChanged;
}
private void LibrariesTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
private void LibraryWindow_StateChanged(object sender, EventArgs e)
{
// Guardar el estado cuando cambia
EstadoPersistente.Instance.LibraryWindow.IsMaximized = WindowState == WindowState.Maximized;
EstadoPersistente.Instance.GuardarEstado();
}
private void ApplyPersistedSettings()
{
var settings = EstadoPersistente.Instance.LibraryWindow;
// Asegurar que la ventana esté dentro de los límites de la pantalla
var screenWidth = SystemParameters.WorkArea.Width;
var screenHeight = SystemParameters.WorkArea.Height;
if (settings.Left < 0 || settings.Left > screenWidth - 100)
settings.Left = 100;
if (settings.Top < 0 || settings.Top > screenHeight - 100)
settings.Top = 100;
if (settings.Width < 800 || settings.Width > screenWidth)
settings.Width = 1200;
if (settings.Height < 600 || settings.Height > screenHeight)
settings.Height = 700;
// Aplicar el estado de la ventana
WindowState = settings.IsMaximized ? WindowState.Maximized : WindowState.Normal;
}
private void LibraryTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (DataContext is LibraryWindowViewModel viewModel)
{
viewModel.SelectedLibrary = e.NewValue as LibraryItem;
if (e.NewValue is LibraryTreeNode selectedNode)
{
viewModel.SelectedTreeNode = selectedNode;
// Si es una biblioteca (no directorio), establecer como biblioteca seleccionada
if (!selectedNode.IsDirectory && selectedNode.Library != null)
{
viewModel.SelectedLibrary = selectedNode.Library;
}
}
}
}
@ -43,6 +90,28 @@ namespace CtrEditor.PopUps
}
}
private void SelectAllObjects_Click(object sender, RoutedEventArgs e)
{
if (DataContext is LibraryWindowViewModel viewModel)
{
foreach (var item in viewModel.SelectableObjects)
{
item.IsSelected = true;
}
}
}
private void UnselectAllObjects_Click(object sender, RoutedEventArgs e)
{
if (DataContext is LibraryWindowViewModel viewModel)
{
foreach (var item in viewModel.SelectableObjects)
{
item.IsSelected = false;
}
}
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
@ -57,5 +126,30 @@ namespace CtrEditor.PopUps
viewModel.SetObjectFilter(ObjectFilter);
}
}
private void Window_Closing(object sender, CancelEventArgs e)
{
// Guardar configuraciones de la ventana antes de cerrar
SaveWindowSettings();
}
private void SaveWindowSettings()
{
var settings = EstadoPersistente.Instance.LibraryWindow;
if (WindowState == WindowState.Normal)
{
settings.Width = ActualWidth;
settings.Height = ActualHeight;
settings.Left = Left;
settings.Top = Top;
}
settings.IsMaximized = WindowState == WindowState.Maximized;
EstadoPersistente.Instance.GuardarEstado();
}
}
}

View File

@ -0,0 +1,48 @@
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;
namespace CtrEditor.PopUps
{
// StyleSelector para TreeViewItems
public class LibraryTreeItemStyleSelector : StyleSelector
{
public Style DirectoryStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is LibraryTreeNode node && node.IsDirectory)
{
return DirectoryStyle;
}
return null;
}
}
// Clase para nodos del TreeView jerárquico
public class LibraryTreeNode : ObservableObject
{
public string DisplayName { get; set; }
public bool IsDirectory { get; set; }
public bool IsCurrentProject { get; set; }
public string DirectoryPath { get; set; }
public LibraryItem Library { get; set; }
public ObservableCollection<LibraryTreeNode> Children { get; set; } = new ObservableCollection<LibraryTreeNode>();
}
// Wrapper para objetos seleccionables
public class SelectableObjectWrapper : ObservableObject
{
public CtrEditor.ObjetosSim.osBase Object { get; set; }
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set => SetProperty(ref _isSelected, value);
}
}
}

View File

@ -38,6 +38,17 @@ namespace CtrEditor.PopUps
[ObservableProperty]
private osBase selectedObject;
// Nueva propiedad para el TreeView jerárquico
[ObservableProperty]
private ObservableCollection<LibraryTreeNode> libraryTreeNodes;
[ObservableProperty]
private LibraryTreeNode selectedTreeNode;
// Nueva propiedad para objetos seleccionados múltiples
[ObservableProperty]
private ObservableCollection<SelectableObjectWrapper> selectableObjects;
// Commands
public ICommand AddLibraryDirectoryCommand { get; }
public ICommand RemoveLibraryDirectoryCommand { get; }
@ -46,6 +57,10 @@ namespace CtrEditor.PopUps
public ICommand DeleteObjectCommand { get; }
public ICommand CreateNewLibraryCommand { get; }
// Nuevos comandos para menú contextual
public ICommand CreateLibraryInDirectoryCommand { get; }
public ICommand RemoveDirectoryCommand { get; }
public LibraryWindowViewModel(MainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
@ -53,17 +68,57 @@ namespace CtrEditor.PopUps
LibraryDirectories = new ObservableCollection<LibraryDirectoryItem>();
Libraries = new ObservableCollection<LibraryItem>();
FilteredObjectsByType = new ObservableCollection<ObjectTypeGroup>();
LibraryTreeNodes = new ObservableCollection<LibraryTreeNode>();
SelectableObjects = new ObservableCollection<SelectableObjectWrapper>();
// Initialize commands
AddLibraryDirectoryCommand = new RelayCommand(AddLibraryDirectory);
RemoveLibraryDirectoryCommand = new RelayCommand(RemoveLibraryDirectory, () => SelectedLibraryDirectory != null);
CopyObjectCommand = new RelayCommand(CopyObject, () => SelectedObject != null);
CopyObjectCommand = new RelayCommand(CopyObject, () => HasSelectedObjects());
PasteObjectCommand = new RelayCommand(PasteObject, () => SelectedLibrary != null && _clipboardObjects.Any());
DeleteObjectCommand = new RelayCommand(DeleteObject, () => SelectedObject != null && SelectedLibrary != null && !SelectedLibrary.IsCurrentProject);
DeleteObjectCommand = new RelayCommand(DeleteObject, () => HasSelectedObjects() && SelectedLibrary != null && !SelectedLibrary.IsCurrentProject);
CreateNewLibraryCommand = new RelayCommand(CreateNewLibrary, () => SelectedLibraryDirectory != null);
// Nuevos comandos
CreateLibraryInDirectoryCommand = new RelayCommand(CreateLibraryInDirectory, () => SelectedTreeNode?.IsDirectory == true);
RemoveDirectoryCommand = new RelayCommand(RemoveDirectory, () => SelectedTreeNode?.IsDirectory == true && !SelectedTreeNode.IsCurrentProject);
LoadLibraryDirectories();
RefreshLibraries();
RefreshLibraryTree();
}
private bool HasSelectedObjects()
{
return SelectableObjects?.Any(o => o.IsSelected) == true || SelectedObject != null;
}
private List<osBase> GetSelectedObjects()
{
var selected = new List<osBase>();
// Primero agregar objetos seleccionados por checkbox
if (SelectableObjects != null)
{
selected.AddRange(SelectableObjects.Where(o => o.IsSelected).Select(o => o.Object));
}
// Si no hay objetos con checkbox seleccionados, usar la selección simple
if (!selected.Any() && SelectedObject != null)
{
selected.Add(SelectedObject);
}
return selected;
}
partial void OnSelectedTreeNodeChanged(LibraryTreeNode value)
{
if (value?.IsDirectory == false) // Es una biblioteca
{
SelectedLibrary = value.Library;
LoadObjectsFromLibrary();
}
CommandManager.InvalidateRequerySuggested();
}
partial void OnSelectedLibraryDirectoryChanged(LibraryDirectoryItem value)
@ -96,7 +151,7 @@ namespace CtrEditor.PopUps
LibraryDirectories.Add(new LibraryDirectoryItem
{
Path = _mainViewModel.directorioTrabajo,
DisplayName = "Proyecto Actual",
DisplayName = $"Proyecto Actual ({_mainViewModel.directorioTrabajo})",
IsCurrentProject = true
});
@ -108,7 +163,7 @@ namespace CtrEditor.PopUps
LibraryDirectories.Add(new LibraryDirectoryItem
{
Path = directory,
DisplayName = Path.GetFileName(directory),
DisplayName = directory, // Show full path instead of just folder name
IsCurrentProject = false
});
}
@ -191,11 +246,27 @@ namespace CtrEditor.PopUps
};
string json = File.ReadAllText(filePath);
var simulationData = JsonConvert.DeserializeObject<SimulationData>(json, settings);
if (simulationData?.ObjetosSimulables != null)
// Try to detect format: check if it starts with '[' (AllPages.json format) or '{' (normal format)
json = json.Trim();
if (json.StartsWith("["))
{
objects.AddRange(simulationData.ObjetosSimulables);
// AllPages.json format: direct array of objects
var directObjects = JsonConvert.DeserializeObject<List<osBase>>(json, settings);
if (directObjects != null)
{
objects.AddRange(directObjects);
}
}
else if (json.StartsWith("{"))
{
// Normal format: SimulationData wrapper
var simulationData = JsonConvert.DeserializeObject<SimulationData>(json, settings);
if (simulationData?.ObjetosSimulables != null)
{
objects.AddRange(simulationData.ObjetosSimulables);
}
}
}
catch (Exception ex)
@ -209,9 +280,16 @@ namespace CtrEditor.PopUps
private void LoadObjectsFromLibrary()
{
FilteredObjectsByType.Clear();
SelectableObjects.Clear();
if (SelectedLibrary == null) return;
// Crear objetos seleccionables con checkboxes
foreach (var obj in SelectedLibrary.Objects)
{
SelectableObjects.Add(new SelectableObjectWrapper { Object = obj, IsSelected = false });
}
// Update filter types and tags when library changes
if (_objectFilter != null)
{
@ -374,15 +452,13 @@ namespace CtrEditor.PopUps
private void CopyObject()
{
if (SelectedObject != null)
var objectsToCopy = GetSelectedObjects();
if (objectsToCopy.Any())
{
try
{
_clipboardObjects.Clear();
// Prepare object for serialization
SelectedObject.SalvarDatosNoSerializables();
var settings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
@ -390,23 +466,30 @@ namespace CtrEditor.PopUps
TypeNameHandling = TypeNameHandling.All
};
// Serialize and deserialize to create a deep copy
var serializedData = JsonConvert.SerializeObject(SelectedObject, settings);
var copiedObject = JsonConvert.DeserializeObject<osBase>(serializedData, settings);
if (copiedObject != null)
foreach (var obj in objectsToCopy)
{
_clipboardObjects.Add(copiedObject);
MessageBox.Show("Objeto copiado al portapapeles.", "Información", MessageBoxButton.OK, MessageBoxImage.Information);
// Prepare object for serialization
obj.SalvarDatosNoSerializables();
// Serialize and deserialize to create a deep copy
var serializedData = JsonConvert.SerializeObject(obj, settings);
var copiedObject = JsonConvert.DeserializeObject<osBase>(serializedData, settings);
if (copiedObject != null)
{
_clipboardObjects.Add(copiedObject);
}
// Restore object state
obj.RestaurarDatosNoSerializables();
}
// Restore object state
SelectedObject.RestaurarDatosNoSerializables();
MessageBox.Show($"{_clipboardObjects.Count} objeto(s) copiado(s) al portapapeles.", "Información", MessageBoxButton.OK, MessageBoxImage.Information);
CommandManager.InvalidateRequerySuggested();
}
catch (Exception ex)
{
MessageBox.Show($"Error al copiar objeto: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
MessageBox.Show($"Error al copiar objeto(s): {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
@ -456,27 +539,37 @@ namespace CtrEditor.PopUps
private void DeleteObject()
{
if (SelectedObject != null && SelectedLibrary != null && !SelectedLibrary.IsCurrentProject)
var objectsToDelete = GetSelectedObjects();
if (objectsToDelete.Any() && SelectedLibrary != null && !SelectedLibrary.IsCurrentProject)
{
var result = MessageBox.Show(
$"¿Está seguro de que desea eliminar el objeto '{SelectedObject.Nombre}'?",
"Confirmar eliminación",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
var message = objectsToDelete.Count == 1
? $"¿Está seguro de que desea eliminar el objeto '{objectsToDelete.First().Nombre}'?"
: $"¿Está seguro de que desea eliminar {objectsToDelete.Count} objetos?";
var result = MessageBox.Show(message, "Confirmar eliminación", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
try
{
SelectedLibrary.Objects.Remove(SelectedObject);
foreach (var obj in objectsToDelete)
{
SelectedLibrary.Objects.Remove(obj);
}
SaveLibraryToFile(SelectedLibrary);
LoadObjectsFromLibrary();
SelectedObject = null;
MessageBox.Show("Objeto eliminado.", "Información", MessageBoxButton.OK, MessageBoxImage.Information);
var successMessage = objectsToDelete.Count == 1
? "Objeto eliminado."
: $"{objectsToDelete.Count} objetos eliminados.";
MessageBox.Show(successMessage, "Información", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Error al eliminar objeto: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
MessageBox.Show($"Error al eliminar objeto(s): {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
@ -553,8 +646,108 @@ namespace CtrEditor.PopUps
MessageBox.Show($"Error al guardar biblioteca: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void RefreshLibraryTree()
{
LibraryTreeNodes.Clear();
foreach (var directory in LibraryDirectories)
{
var directoryNode = new LibraryTreeNode
{
DisplayName = directory.DisplayName,
IsDirectory = true,
IsCurrentProject = directory.IsCurrentProject,
DirectoryPath = directory.Path,
Children = new ObservableCollection<LibraryTreeNode>()
};
// Agregar bibliotecas como hijos del directorio
if (directory.IsCurrentProject)
{
// Agregar proyecto actual
directoryNode.Children.Add(new LibraryTreeNode
{
DisplayName = "Proyecto Actual",
IsDirectory = false,
Library = new LibraryItem
{
Name = "Proyecto Actual",
IsCurrentProject = true,
Objects = _mainViewModel.ObjetosSimulables.ToList()
}
});
}
// Agregar archivos JSON
if (Directory.Exists(directory.Path))
{
var jsonFiles = Directory.GetFiles(directory.Path, "*.json", SearchOption.AllDirectories);
foreach (var jsonFile in jsonFiles)
{
try
{
var displayName = Path.GetFileNameWithoutExtension(jsonFile);
directoryNode.Children.Add(new LibraryTreeNode
{
DisplayName = displayName,
IsDirectory = false,
Library = new LibraryItem
{
Name = displayName,
FilePath = jsonFile,
IsCurrentProject = false,
Objects = LoadObjectsFromJsonFile(jsonFile)
}
});
}
catch
{
// Ignorar archivos que no se pueden cargar
}
}
}
LibraryTreeNodes.Add(directoryNode);
}
}
private void CreateLibraryInDirectory()
{
if (SelectedTreeNode?.IsDirectory != true) return;
var oldSelectedDirectory = SelectedLibraryDirectory;
// Temporalmente cambiar el directorio seleccionado
SelectedLibraryDirectory = LibraryDirectories.FirstOrDefault(d => d.Path == SelectedTreeNode.DirectoryPath);
CreateNewLibrary();
RefreshLibraryTree();
// Restaurar el directorio seleccionado
SelectedLibraryDirectory = oldSelectedDirectory;
}
private void RemoveDirectory()
{
if (SelectedTreeNode?.IsDirectory != true || SelectedTreeNode.IsCurrentProject) return;
var result = MessageBox.Show($"¿Eliminar el directorio '{SelectedTreeNode.DisplayName}' de la lista de bibliotecas?",
"Confirmar eliminación", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
EstadoPersistente.Instance.LibraryDirectories.Remove(SelectedTreeNode.DirectoryPath);
EstadoPersistente.Instance.GuardarEstado();
LoadLibraryDirectories();
RefreshLibraryTree();
}
}
}
// Support classes
public class LibraryDirectoryItem
{

View File

@ -22,6 +22,18 @@ namespace CtrEditor
public string TargetColumn { get; set; }
}
public class LibraryWindowSettings
{
public double Width { get; set; } = 1200;
public double Height { get; set; } = 700;
public double Left { get; set; } = 100;
public double Top { get; set; } = 100;
public double Column0Width { get; set; } = 300;
public double Column2Width { get; set; } = 350;
public double Column4Width { get; set; } = 350;
public bool IsMaximized { get; set; } = false;
}
internal class EstadoPersistente
{
// Ruta donde se guardará el estado
@ -44,6 +56,8 @@ namespace CtrEditor
public List<string> LibraryDirectories { get; set; } = new List<string>();
public LibraryWindowSettings LibraryWindow { get; set; } = new LibraryWindowSettings();
// Propiedad pública con get y set para controlar el acceso a _strDirectorioTrabajo
public string directorio
{
@ -76,6 +90,7 @@ namespace CtrEditor
{
_strDirectorioTrabajo = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
RecentDirectories = new List<string>();
LibraryWindow = new LibraryWindowSettings();
return this;
}
@ -114,7 +129,11 @@ namespace CtrEditor
if (File.Exists(_filePath))
{
string json = File.ReadAllText(_filePath);
return JsonSerializer.Deserialize<EstadoPersistente>(json);
var estado = JsonSerializer.Deserialize<EstadoPersistente>(json);
// Asegurar que LibraryWindow esté inicializado
if (estado.LibraryWindow == null)
estado.LibraryWindow = new LibraryWindowSettings();
return estado;
}
return new EstadoPersistente().Inizializar(); // Devuelve una nueva instancia si no existe el archivo
}