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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:CtrEditor.PopUps" xmlns:local="clr-namespace:CtrEditor.PopUps"
xmlns:ObjetosSim="clr-namespace:CtrEditor.ObjetosSim" xmlns:ObjetosSim="clr-namespace:CtrEditor.ObjetosSim"
Title="Biblioteca de Objetos Simulables" Height="700" Width="1200" xmlns:ctr="clr-namespace:CtrEditor"
WindowStartupLocation="CenterOwner" ResizeMode="CanResize" Title="Biblioteca de Objetos Simulables"
Loaded="Window_Loaded"> 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> <Window.InputBindings>
<KeyBinding Key="C" Modifiers="Ctrl" Command="{Binding CopyObjectCommand}" /> <KeyBinding Key="C" Modifiers="Ctrl" Command="{Binding CopyObjectCommand}" />
@ -17,16 +55,16 @@
<Grid> <Grid>
<Grid.ColumnDefinitions> <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="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="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="5" />
<ColumnDefinition Width="*" MinWidth="300" /> <ColumnDefinition Width="*" MinWidth="300" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Primera columna: Gestión de directorios de biblioteca --> <!-- Primera columna: TreeView jerárquico de directorios y bibliotecas -->
<Grid Grid.Column="0"> <Grid Grid.Column="0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@ -34,16 +72,45 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </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" <TreeView Grid.Row="1" Name="LibraryTreeView"
ItemsSource="{Binding LibraryDirectories}" ItemsSource="{Binding LibraryTreeNodes}"
SelectedItem="{Binding SelectedLibraryDirectory, Mode=TwoWay}" SelectedItemChanged="LibraryTreeView_SelectedItemChanged">
DisplayMemberPath="DisplayName" /> <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"> <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">
<Button Content="Agregar" Command="{Binding AddLibraryDirectoryCommand}" Margin="2" Padding="5,2" /> <Button Content="Agregar Directorio" Command="{Binding AddLibraryDirectoryCommand}" Margin="2" Padding="5,2" />
<Button Content="Eliminar" Command="{Binding RemoveLibraryDirectoryCommand}" Margin="2" Padding="5,2" />
</StackPanel> </StackPanel>
</Grid> </Grid>
@ -51,35 +118,25 @@
<GridSplitter Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" <GridSplitter Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"
Background="LightGray" Width="5" /> Background="LightGray" Width="5" />
<!-- Segunda columna: Bibliotecas --> <!-- Segunda columna: Filtros -->
<Grid Grid.Column="2"> <Grid Grid.Column="2">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Label Grid.Row="0" Content="Bibliotecas" FontWeight="Bold" /> <Label Grid.Row="0" Content="Filtros" FontWeight="Bold" />
<TreeView Grid.Row="1" Name="LibrariesTreeView"
ItemsSource="{Binding Libraries}" <Expander Grid.Row="1" Header="Configuración de Filtros" IsExpanded="True">
SelectedItemChanged="LibrariesTreeView_SelectedItemChanged"> <controls:osVisFilter x:Name="ObjectFilter" Margin="5"/>
<TreeView.ItemTemplate> </Expander>
<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>
</Grid> </Grid>
<!-- GridSplitter 2 --> <!-- GridSplitter 2 -->
<GridSplitter Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Stretch" <GridSplitter Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Stretch"
Background="LightGray" Width="5" /> Background="LightGray" Width="5" />
<!-- Tercera columna: Objetos --> <!-- Tercera columna: Objetos con selección múltiple -->
<Grid Grid.Column="4"> <Grid Grid.Column="4">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@ -88,44 +145,44 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<!-- Filtros --> <Label Grid.Row="0" Content="Objetos" FontWeight="Bold" />
<Expander Grid.Row="0" Header="Filtros" IsExpanded="True">
<controls:osVisFilter x:Name="ObjectFilter" Margin="5"/>
</Expander>
<Label Grid.Row="1" Content="Objetos" FontWeight="Bold" /> <!-- Controles de selección múltiple -->
<TreeView Grid.Row="2" Name="ObjectsTreeView" <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="5">
ItemsSource="{Binding FilteredObjectsByType}" <Button Content="Seleccionar Todo" Click="SelectAllObjects_Click" Margin="2" Padding="5,2"/>
SelectedItemChanged="ObjectsTreeView_SelectedItemChanged"> <Button Content="Deseleccionar Todo" Click="UnselectAllObjects_Click" Margin="2" Padding="5,2"/>
<TreeView.Resources> </StackPanel>
<!-- Template for object type groups -->
<HierarchicalDataTemplate DataType="{x:Type local:ObjectTypeGroup}" <!-- Listbox para objetos con checkboxes -->
ItemsSource="{Binding Objects}"> <ListBox Grid.Row="2" Name="SelectableObjectsList"
<TextBlock Text="{Binding TypeName}" FontWeight="Bold" /> ItemsSource="{Binding SelectableObjects}"
</HierarchicalDataTemplate> SelectionMode="Extended">
<ListBox.ItemTemplate>
<!-- Template for individual objects --> <DataTemplate>
<DataTemplate DataType="{x:Type ObjetosSim:osBase}"> <StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Nombre}"> <CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"
<TextBlock.Style> Margin="0,0,5,0" VerticalAlignment="Center"/>
<Style TargetType="TextBlock"> <TextBlock Text="{Binding Object.Nombre}" VerticalAlignment="Center">
<Style.Triggers> <TextBlock.Style>
<DataTrigger Binding="{Binding Enable_On_All_Pages}" Value="True"> <Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue" /> <Style.Triggers>
</DataTrigger> <DataTrigger Binding="{Binding Object.Enable_On_All_Pages}" Value="True">
<DataTrigger Binding="{Binding Cloned}" Value="True"> <Setter Property="Foreground" Value="Blue" />
<Setter Property="Foreground" Value="Orange" /> </DataTrigger>
</DataTrigger> <DataTrigger Binding="{Binding Object.Cloned}" Value="True">
<DataTrigger Binding="{Binding AutoCreated}" Value="True"> <Setter Property="Foreground" Value="Orange" />
<Setter Property="Foreground" Value="Green" /> </DataTrigger>
</DataTrigger> <DataTrigger Binding="{Binding Object.AutoCreated}" Value="True">
</Style.Triggers> <Setter Property="Foreground" Value="Green" />
</Style> </DataTrigger>
</TextBlock.Style> </Style.Triggers>
</TextBlock> </Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</DataTemplate> </DataTemplate>
</TreeView.Resources> </ListBox.ItemTemplate>
</TreeView> </ListBox>
<!-- Botones de acción --> <!-- Botones de acción -->
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5"> <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">

View File

@ -1,6 +1,9 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using CtrEditor.ObjetosSim; using CtrEditor.ObjetosSim;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Data;
namespace CtrEditor.PopUps namespace CtrEditor.PopUps
{ {
@ -12,13 +15,57 @@ namespace CtrEditor.PopUps
public LibraryWindow() public LibraryWindow()
{ {
InitializeComponent(); 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) 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) private void CloseButton_Click(object sender, RoutedEventArgs e)
{ {
this.Close(); this.Close();
@ -57,5 +126,30 @@ namespace CtrEditor.PopUps
viewModel.SetObjectFilter(ObjectFilter); 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] [ObservableProperty]
private osBase selectedObject; 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 // Commands
public ICommand AddLibraryDirectoryCommand { get; } public ICommand AddLibraryDirectoryCommand { get; }
public ICommand RemoveLibraryDirectoryCommand { get; } public ICommand RemoveLibraryDirectoryCommand { get; }
@ -46,6 +57,10 @@ namespace CtrEditor.PopUps
public ICommand DeleteObjectCommand { get; } public ICommand DeleteObjectCommand { get; }
public ICommand CreateNewLibraryCommand { get; } public ICommand CreateNewLibraryCommand { get; }
// Nuevos comandos para menú contextual
public ICommand CreateLibraryInDirectoryCommand { get; }
public ICommand RemoveDirectoryCommand { get; }
public LibraryWindowViewModel(MainViewModel mainViewModel) public LibraryWindowViewModel(MainViewModel mainViewModel)
{ {
_mainViewModel = mainViewModel; _mainViewModel = mainViewModel;
@ -53,17 +68,57 @@ namespace CtrEditor.PopUps
LibraryDirectories = new ObservableCollection<LibraryDirectoryItem>(); LibraryDirectories = new ObservableCollection<LibraryDirectoryItem>();
Libraries = new ObservableCollection<LibraryItem>(); Libraries = new ObservableCollection<LibraryItem>();
FilteredObjectsByType = new ObservableCollection<ObjectTypeGroup>(); FilteredObjectsByType = new ObservableCollection<ObjectTypeGroup>();
LibraryTreeNodes = new ObservableCollection<LibraryTreeNode>();
SelectableObjects = new ObservableCollection<SelectableObjectWrapper>();
// Initialize commands // Initialize commands
AddLibraryDirectoryCommand = new RelayCommand(AddLibraryDirectory); AddLibraryDirectoryCommand = new RelayCommand(AddLibraryDirectory);
RemoveLibraryDirectoryCommand = new RelayCommand(RemoveLibraryDirectory, () => SelectedLibraryDirectory != null); 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()); 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); 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(); 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) partial void OnSelectedLibraryDirectoryChanged(LibraryDirectoryItem value)
@ -96,7 +151,7 @@ namespace CtrEditor.PopUps
LibraryDirectories.Add(new LibraryDirectoryItem LibraryDirectories.Add(new LibraryDirectoryItem
{ {
Path = _mainViewModel.directorioTrabajo, Path = _mainViewModel.directorioTrabajo,
DisplayName = "Proyecto Actual", DisplayName = $"Proyecto Actual ({_mainViewModel.directorioTrabajo})",
IsCurrentProject = true IsCurrentProject = true
}); });
@ -108,7 +163,7 @@ namespace CtrEditor.PopUps
LibraryDirectories.Add(new LibraryDirectoryItem LibraryDirectories.Add(new LibraryDirectoryItem
{ {
Path = directory, Path = directory,
DisplayName = Path.GetFileName(directory), DisplayName = directory, // Show full path instead of just folder name
IsCurrentProject = false IsCurrentProject = false
}); });
} }
@ -191,11 +246,27 @@ namespace CtrEditor.PopUps
}; };
string json = File.ReadAllText(filePath); 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) catch (Exception ex)
@ -209,9 +280,16 @@ namespace CtrEditor.PopUps
private void LoadObjectsFromLibrary() private void LoadObjectsFromLibrary()
{ {
FilteredObjectsByType.Clear(); FilteredObjectsByType.Clear();
SelectableObjects.Clear();
if (SelectedLibrary == null) return; 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 // Update filter types and tags when library changes
if (_objectFilter != null) if (_objectFilter != null)
{ {
@ -374,15 +452,13 @@ namespace CtrEditor.PopUps
private void CopyObject() private void CopyObject()
{ {
if (SelectedObject != null) var objectsToCopy = GetSelectedObjects();
if (objectsToCopy.Any())
{ {
try try
{ {
_clipboardObjects.Clear(); _clipboardObjects.Clear();
// Prepare object for serialization
SelectedObject.SalvarDatosNoSerializables();
var settings = new JsonSerializerSettings var settings = new JsonSerializerSettings
{ {
Formatting = Formatting.Indented, Formatting = Formatting.Indented,
@ -390,23 +466,30 @@ namespace CtrEditor.PopUps
TypeNameHandling = TypeNameHandling.All TypeNameHandling = TypeNameHandling.All
}; };
// Serialize and deserialize to create a deep copy foreach (var obj in objectsToCopy)
var serializedData = JsonConvert.SerializeObject(SelectedObject, settings);
var copiedObject = JsonConvert.DeserializeObject<osBase>(serializedData, settings);
if (copiedObject != null)
{ {
_clipboardObjects.Add(copiedObject); // Prepare object for serialization
MessageBox.Show("Objeto copiado al portapapeles.", "Información", MessageBoxButton.OK, MessageBoxImage.Information); 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 MessageBox.Show($"{_clipboardObjects.Count} objeto(s) copiado(s) al portapapeles.", "Información", MessageBoxButton.OK, MessageBoxImage.Information);
SelectedObject.RestaurarDatosNoSerializables();
CommandManager.InvalidateRequerySuggested(); CommandManager.InvalidateRequerySuggested();
} }
catch (Exception ex) 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() private void DeleteObject()
{ {
if (SelectedObject != null && SelectedLibrary != null && !SelectedLibrary.IsCurrentProject) var objectsToDelete = GetSelectedObjects();
if (objectsToDelete.Any() && SelectedLibrary != null && !SelectedLibrary.IsCurrentProject)
{ {
var result = MessageBox.Show( var message = objectsToDelete.Count == 1
$"¿Está seguro de que desea eliminar el objeto '{SelectedObject.Nombre}'?", ? $"¿Está seguro de que desea eliminar el objeto '{objectsToDelete.First().Nombre}'?"
"Confirmar eliminación", : $"¿Está seguro de que desea eliminar {objectsToDelete.Count} objetos?";
MessageBoxButton.YesNo,
MessageBoxImage.Question); var result = MessageBox.Show(message, "Confirmar eliminación", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes) if (result == MessageBoxResult.Yes)
{ {
try try
{ {
SelectedLibrary.Objects.Remove(SelectedObject); foreach (var obj in objectsToDelete)
{
SelectedLibrary.Objects.Remove(obj);
}
SaveLibraryToFile(SelectedLibrary); SaveLibraryToFile(SelectedLibrary);
LoadObjectsFromLibrary(); LoadObjectsFromLibrary();
SelectedObject = null; 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) 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); 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 // Support classes
public class LibraryDirectoryItem public class LibraryDirectoryItem
{ {

View File

@ -22,6 +22,18 @@ namespace CtrEditor
public string TargetColumn { get; set; } 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 internal class EstadoPersistente
{ {
// Ruta donde se guardará el estado // Ruta donde se guardará el estado
@ -44,6 +56,8 @@ namespace CtrEditor
public List<string> LibraryDirectories { get; set; } = new List<string>(); 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 // Propiedad pública con get y set para controlar el acceso a _strDirectorioTrabajo
public string directorio public string directorio
{ {
@ -76,6 +90,7 @@ namespace CtrEditor
{ {
_strDirectorioTrabajo = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); _strDirectorioTrabajo = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
RecentDirectories = new List<string>(); RecentDirectories = new List<string>();
LibraryWindow = new LibraryWindowSettings();
return this; return this;
} }
@ -114,7 +129,11 @@ namespace CtrEditor
if (File.Exists(_filePath)) if (File.Exists(_filePath))
{ {
string json = File.ReadAllText(_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 return new EstadoPersistente().Inizializar(); // Devuelve una nueva instancia si no existe el archivo
} }