Se implementó un sistema de filtrado por etiquetas en la interfaz de usuario, permitiendo a los usuarios buscar y seleccionar objetos basados en etiquetas personalizadas. Se añadieron nuevas propiedades y métodos en la clase osBase para gestionar etiquetas, así como mejoras en la lógica de actualización de filtros en función de los objetos disponibles. Además, se realizaron ajustes en la visualización y manejo de los filtros en el control osVisFilter.
This commit is contained in:
parent
99248e9112
commit
67fa5eef3d
|
@ -46,6 +46,24 @@
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
|
|
||||||
|
<!-- Separator between types and tags -->
|
||||||
|
<Separator Style="{StaticResource FilterSeparatorStyle}" />
|
||||||
|
|
||||||
|
<!-- Tag Filter Options -->
|
||||||
|
<TextBlock Text="Tags" Style="{StaticResource FilterHeaderStyle}" />
|
||||||
|
<TextBox Margin="0,2,0,5"
|
||||||
|
Text="{Binding SearchTags, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
ToolTip="Buscar etiquetas (use # antes de cada etiqueta, ej: #motor #bomba)"
|
||||||
|
FontSize="11" />
|
||||||
|
<ItemsControl ItemsSource="{Binding TagFilters}">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<CheckBox Content="{Binding DisplayName}" IsChecked="{Binding IsSelected, Mode=TwoWay}"
|
||||||
|
Style="{StaticResource FilterCheckBoxStyle}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
|
@ -46,6 +46,14 @@ namespace CtrEditor.Controls
|
||||||
{
|
{
|
||||||
FilterViewModel.UpdateTypeFilters(types);
|
FilterViewModel.UpdateTypeFilters(types);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the tag filters based on the provided objects
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateAvailableTags(IEnumerable<osBase> objects)
|
||||||
|
{
|
||||||
|
FilterViewModel.UpdateTagFilters(objects);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -71,6 +79,12 @@ namespace CtrEditor.Controls
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private ObservableCollection<TypeFilterItem> typeFilters = new ObservableCollection<TypeFilterItem>();
|
private ObservableCollection<TypeFilterItem> typeFilters = new ObservableCollection<TypeFilterItem>();
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<TagFilterItem> tagFilters = new ObservableCollection<TagFilterItem>();
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string searchTags = "";
|
||||||
|
|
||||||
partial void OnShowAllChanged(bool value)
|
partial void OnShowAllChanged(bool value)
|
||||||
{
|
{
|
||||||
NotifyFilterChanged();
|
NotifyFilterChanged();
|
||||||
|
@ -113,6 +127,28 @@ namespace CtrEditor.Controls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partial void OnTagFiltersChanged(ObservableCollection<TagFilterItem> value)
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
foreach (var filter in value)
|
||||||
|
{
|
||||||
|
filter.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(TagFilterItem.IsSelected))
|
||||||
|
{
|
||||||
|
NotifyFilterChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnSearchTagsChanged(string value)
|
||||||
|
{
|
||||||
|
NotifyFilterChanged();
|
||||||
|
}
|
||||||
|
|
||||||
private void NotifyFilterChanged()
|
private void NotifyFilterChanged()
|
||||||
{
|
{
|
||||||
if (this.Parent is osVisFilter filter)
|
if (this.Parent is osVisFilter filter)
|
||||||
|
@ -120,7 +156,7 @@ namespace CtrEditor.Controls
|
||||||
filter.OnFilterChanged();
|
filter.OnFilterChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public osVisFilter Parent { get; set; }
|
public osVisFilter Parent { get; set; }
|
||||||
|
|
||||||
|
@ -165,6 +201,44 @@ namespace CtrEditor.Controls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the tag filters based on the provided objects
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateTagFilters(IEnumerable<osBase> objects)
|
||||||
|
{
|
||||||
|
// Get all unique tags from all objects
|
||||||
|
var allTags = objects
|
||||||
|
.SelectMany(obj => obj.ListaEtiquetas)
|
||||||
|
.Distinct()
|
||||||
|
.OrderBy(tag => tag)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Remove tags that are no longer present
|
||||||
|
var tagsToRemove = TagFilters
|
||||||
|
.Where(tf => !allTags.Contains(tf.TagName))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var item in tagsToRemove)
|
||||||
|
{
|
||||||
|
UnsubscribeFromTagFilter(item);
|
||||||
|
TagFilters.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new tags that aren't already in the list
|
||||||
|
foreach (var tag in allTags)
|
||||||
|
{
|
||||||
|
if (!TagFilters.Any(tf => tf.TagName == tag))
|
||||||
|
{
|
||||||
|
var newFilter = new TagFilterItem(tag)
|
||||||
|
{
|
||||||
|
IsSelected = true
|
||||||
|
};
|
||||||
|
SubscribeToTagFilter(newFilter);
|
||||||
|
TagFilters.Add(newFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SubscribeToTypeFilter(TypeFilterItem filter)
|
private void SubscribeToTypeFilter(TypeFilterItem filter)
|
||||||
{
|
{
|
||||||
filter.PropertyChanged += TypeFilter_PropertyChanged;
|
filter.PropertyChanged += TypeFilter_PropertyChanged;
|
||||||
|
@ -175,6 +249,16 @@ namespace CtrEditor.Controls
|
||||||
filter.PropertyChanged -= TypeFilter_PropertyChanged;
|
filter.PropertyChanged -= TypeFilter_PropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SubscribeToTagFilter(TagFilterItem filter)
|
||||||
|
{
|
||||||
|
filter.PropertyChanged += TagFilter_PropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnsubscribeFromTagFilter(TagFilterItem filter)
|
||||||
|
{
|
||||||
|
filter.PropertyChanged -= TagFilter_PropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
private void TypeFilter_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
private void TypeFilter_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PropertyName == nameof(TypeFilterItem.IsSelected))
|
if (e.PropertyName == nameof(TypeFilterItem.IsSelected))
|
||||||
|
@ -183,6 +267,14 @@ namespace CtrEditor.Controls
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TagFilter_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(TagFilterItem.IsSelected))
|
||||||
|
{
|
||||||
|
NotifyFilterChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the display name for a type, using the NombreClase static method if available
|
/// Gets the display name for a type, using the NombreClase static method if available
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -220,4 +312,26 @@ namespace CtrEditor.Controls
|
||||||
OnPropertyChanged(nameof(IsSelected));
|
OnPropertyChanged(nameof(IsSelected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a tag filter item with selection state
|
||||||
|
/// </summary>
|
||||||
|
public partial class TagFilterItem : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool isSelected;
|
||||||
|
|
||||||
|
public string TagName { get; }
|
||||||
|
public string DisplayName => "#" + TagName;
|
||||||
|
|
||||||
|
public TagFilterItem(string tagName)
|
||||||
|
{
|
||||||
|
TagName = tagName;
|
||||||
|
}
|
||||||
|
|
||||||
|
partial void OnIsSelectedChanged(bool value)
|
||||||
|
{
|
||||||
|
// Could add logic here if needed when selection changes
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
161
MainViewModel.cs
161
MainViewModel.cs
|
@ -127,7 +127,7 @@ namespace CtrEditor
|
||||||
private ObjectManipulationManager _objectManager; // Add this line
|
private ObjectManipulationManager _objectManager; // Add this line
|
||||||
|
|
||||||
public MainWindow MainWindow
|
public MainWindow MainWindow
|
||||||
{
|
{
|
||||||
get => mainWindow;
|
get => mainWindow;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
@ -250,10 +250,10 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
if (HasUnsavedChanges && !inhibitSaveChangesControl)
|
if (HasUnsavedChanges && !inhibitSaveChangesControl)
|
||||||
{
|
{
|
||||||
var result = MessageBox.Show("There are unsaved changes. Do you want to save them?",
|
var result = MessageBox.Show("There are unsaved changes. Do you want to save them?",
|
||||||
"Save Changes",
|
"Save Changes",
|
||||||
MessageBoxButton.YesNo);
|
MessageBoxButton.YesNo);
|
||||||
|
|
||||||
if (result == MessageBoxResult.Yes)
|
if (result == MessageBoxResult.Yes)
|
||||||
{
|
{
|
||||||
SaveStateObjetosSimulables();
|
SaveStateObjetosSimulables();
|
||||||
|
@ -283,7 +283,7 @@ namespace CtrEditor
|
||||||
habilitarEliminarUserControl = _objectManager.SelectedObjects.Count > 0;
|
habilitarEliminarUserControl = _objectManager.SelectedObjects.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private TipoSimulable selectedItem;
|
private TipoSimulable selectedItem;
|
||||||
|
|
||||||
|
@ -317,6 +317,7 @@ namespace CtrEditor
|
||||||
private void UpdateVisFilterTypes()
|
private void UpdateVisFilterTypes()
|
||||||
{
|
{
|
||||||
MainWindow?.VisFilter?.UpdateAvailableTypes(ObjetosSimulables.Select(o => o.GetType()).Distinct());
|
MainWindow?.VisFilter?.UpdateAvailableTypes(ObjetosSimulables.Select(o => o.GetType()).Distinct());
|
||||||
|
MainWindow?.VisFilter?.UpdateAvailableTags(ObjetosSimulables);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -331,9 +332,9 @@ namespace CtrEditor
|
||||||
datosDeTrabajo = new DatosDeTrabajo();
|
datosDeTrabajo = new DatosDeTrabajo();
|
||||||
|
|
||||||
// Initialize ObjetosSimulables first
|
// Initialize ObjetosSimulables first
|
||||||
ObjetosSimulables = new ObservableCollection<osBase>();
|
ObjetosSimulables = new ObservableCollection<osBase>();
|
||||||
|
|
||||||
ObjetosSimulables = new ObservableCollection<osBase>();
|
ObjetosSimulables = new ObservableCollection<osBase>();
|
||||||
|
|
||||||
ListaOsBase = new ObservableCollection<TipoSimulable>();
|
ListaOsBase = new ObservableCollection<TipoSimulable>();
|
||||||
|
|
||||||
|
@ -342,7 +343,7 @@ namespace CtrEditor
|
||||||
|
|
||||||
_timerPLCUpdate = new DispatcherTimer();
|
_timerPLCUpdate = new DispatcherTimer();
|
||||||
_timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(10); // Restaurado a 10ms
|
_timerPLCUpdate.Interval = TimeSpan.FromMilliseconds(10); // Restaurado a 10ms
|
||||||
_timerPLCUpdate.Tick += OnRefreshEvent;
|
_timerPLCUpdate.Tick += OnRefreshEvent;
|
||||||
|
|
||||||
InitializeTipoSimulableList();
|
InitializeTipoSimulableList();
|
||||||
|
|
||||||
|
@ -363,7 +364,7 @@ namespace CtrEditor
|
||||||
|
|
||||||
TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning);
|
TBStartSimulationCommand = new RelayCommand(StartSimulation, () => !IsSimulationRunning);
|
||||||
TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning);
|
TBStopSimulationCommand = new RelayCommand(StopSimulation, () => IsSimulationRunning);
|
||||||
|
|
||||||
// Inicializar simulación de fluidos
|
// Inicializar simulación de fluidos
|
||||||
FluidSimulation = new SimulationFluidsViewModel(this);
|
FluidSimulation = new SimulationFluidsViewModel(this);
|
||||||
TBStartFluidSimulationCommand = new RelayCommand(StartFluidSimulation, () => !FluidSimulation.IsFluidSimulationRunning);
|
TBStartFluidSimulationCommand = new RelayCommand(StartFluidSimulation, () => !FluidSimulation.IsFluidSimulationRunning);
|
||||||
|
@ -412,16 +413,46 @@ namespace CtrEditor
|
||||||
isVisible = false;
|
isVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check tag filters
|
||||||
|
if (filter.TagFilters.Any() && filter.TagFilters.Any(tf => tf.IsSelected))
|
||||||
|
{
|
||||||
|
var selectedTags = filter.TagFilters.Where(tf => tf.IsSelected).Select(tf => tf.TagName).ToList();
|
||||||
|
bool hasMatchingTag = obj.ListaEtiquetas.Any(tag => selectedTags.Contains(tag));
|
||||||
|
if (!hasMatchingTag)
|
||||||
|
{
|
||||||
|
isVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check search tags
|
||||||
|
if (!string.IsNullOrWhiteSpace(filter.SearchTags))
|
||||||
|
{
|
||||||
|
var searchTags = filter.SearchTags
|
||||||
|
.Split(' ', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Where(tag => tag.StartsWith("#") && tag.Length > 1)
|
||||||
|
.Select(tag => tag.Substring(1).ToLowerInvariant())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (searchTags.Any())
|
||||||
|
{
|
||||||
|
bool hasMatchingSearchTag = searchTags.Any(searchTag => obj.ListaEtiquetas.Contains(searchTag));
|
||||||
|
if (!hasMatchingSearchTag)
|
||||||
|
{
|
||||||
|
isVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check other filters
|
// Check other filters
|
||||||
if (filter.ShowCloned && !obj.Cloned)
|
if (filter.ShowCloned && !obj.Cloned)
|
||||||
isVisible = false;
|
isVisible = false;
|
||||||
|
|
||||||
if (filter.ShowAutoCreated && !obj.AutoCreated)
|
if (filter.ShowAutoCreated && !obj.AutoCreated)
|
||||||
isVisible = false;
|
isVisible = false;
|
||||||
|
|
||||||
if (filter.ShowEnableOnAllPages && !obj.Enable_On_All_Pages)
|
if (filter.ShowEnableOnAllPages && !obj.Enable_On_All_Pages)
|
||||||
isVisible = false;
|
isVisible = false;
|
||||||
|
|
||||||
if (filter.ShowOnThisPage && !obj.Show_On_This_Page)
|
if (filter.ShowOnThisPage && !obj.Show_On_This_Page)
|
||||||
isVisible = false;
|
isVisible = false;
|
||||||
}
|
}
|
||||||
|
@ -441,13 +472,13 @@ namespace CtrEditor
|
||||||
if (SelectedImage != null)
|
if (SelectedImage != null)
|
||||||
{
|
{
|
||||||
_stateSerializer.LoadState(SelectedImage);
|
_stateSerializer.LoadState(SelectedImage);
|
||||||
|
|
||||||
// Aplicar los filtros actuales a los objetos recién cargados
|
// Aplicar los filtros actuales a los objetos recién cargados
|
||||||
if (MainWindow?.VisFilter?.FilterViewModel != null)
|
if (MainWindow?.VisFilter?.FilterViewModel != null)
|
||||||
{
|
{
|
||||||
OnVisFilterChanged(MainWindow.VisFilter.FilterViewModel);
|
OnVisFilterChanged(MainWindow.VisFilter.FilterViewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limpiar historial de undo al cargar un estado desde archivo
|
// Limpiar historial de undo al cargar un estado desde archivo
|
||||||
MainWindow?.ClearUndoHistory();
|
MainWindow?.ClearUndoHistory();
|
||||||
}
|
}
|
||||||
|
@ -458,7 +489,7 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
// Suponiendo que "SelectedImage" es una propiedad que al establecerse dispara "ImageSelected"
|
// Suponiendo que "SelectedImage" es una propiedad que al establecerse dispara "ImageSelected"
|
||||||
directorioTrabajo = EstadoPersistente.Instance.directorio;
|
directorioTrabajo = EstadoPersistente.Instance.directorio;
|
||||||
|
|
||||||
// Limpiar historial de undo al cargar datos iniciales
|
// Limpiar historial de undo al cargar datos iniciales
|
||||||
MainWindow?.ClearUndoHistory();
|
MainWindow?.ClearUndoHistory();
|
||||||
}
|
}
|
||||||
|
@ -538,17 +569,17 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
// Create a copy of the selected objects to avoid issues during iteration
|
// Create a copy of the selected objects to avoid issues during iteration
|
||||||
var objectsToDuplicate = _objectManager.SelectedObjects.ToList();
|
var objectsToDuplicate = _objectManager.SelectedObjects.ToList();
|
||||||
|
|
||||||
// Clear current selection before duplicating
|
// Clear current selection before duplicating
|
||||||
_objectManager.ClearSelection();
|
_objectManager.ClearSelection();
|
||||||
|
|
||||||
// Track all newly created objects
|
// Track all newly created objects
|
||||||
var newObjects = new List<osBase>();
|
var newObjects = new List<osBase>();
|
||||||
|
|
||||||
// Duplicate each object with a small offset
|
// Duplicate each object with a small offset
|
||||||
float offsetX = 0.5f;
|
float offsetX = 0.5f;
|
||||||
float offsetY = 0.5f;
|
float offsetY = 0.5f;
|
||||||
|
|
||||||
foreach (var objToDuplicate in objectsToDuplicate)
|
foreach (var objToDuplicate in objectsToDuplicate)
|
||||||
{
|
{
|
||||||
var newObj = DuplicarObjeto(objToDuplicate, offsetX, offsetY);
|
var newObj = DuplicarObjeto(objToDuplicate, offsetX, offsetY);
|
||||||
|
@ -557,10 +588,10 @@ namespace CtrEditor
|
||||||
newObjects.Add(newObj);
|
newObjects.Add(newObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force a complete layout update to ensure all controls are positioned
|
// Force a complete layout update to ensure all controls are positioned
|
||||||
MainWindow.ImagenEnTrabajoCanvas.UpdateLayout();
|
MainWindow.ImagenEnTrabajoCanvas.UpdateLayout();
|
||||||
|
|
||||||
// Use a dispatcher to delay the selection until the UI has had time to fully render
|
// Use a dispatcher to delay the selection until the UI has had time to fully render
|
||||||
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
|
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
|
||||||
{
|
{
|
||||||
|
@ -571,7 +602,7 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
double left = Canvas.GetLeft(newObj.VisualRepresentation);
|
double left = Canvas.GetLeft(newObj.VisualRepresentation);
|
||||||
double top = Canvas.GetTop(newObj.VisualRepresentation);
|
double top = Canvas.GetTop(newObj.VisualRepresentation);
|
||||||
|
|
||||||
// Only add to selection if the object has valid coordinates
|
// Only add to selection if the object has valid coordinates
|
||||||
if (!double.IsNaN(left) && !double.IsNaN(top) && !double.IsInfinity(left) && !double.IsInfinity(top))
|
if (!double.IsNaN(left) && !double.IsNaN(top) && !double.IsInfinity(left) && !double.IsInfinity(top))
|
||||||
{
|
{
|
||||||
|
@ -579,17 +610,17 @@ namespace CtrEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force another layout update before updating selection visuals
|
// Force another layout update before updating selection visuals
|
||||||
MainWindow.ImagenEnTrabajoCanvas.UpdateLayout();
|
MainWindow.ImagenEnTrabajoCanvas.UpdateLayout();
|
||||||
|
|
||||||
// Update SelectedItemOsList if there are newly created objects
|
// Update SelectedItemOsList if there are newly created objects
|
||||||
if (newObjects.Count > 0)
|
if (newObjects.Count > 0)
|
||||||
{
|
{
|
||||||
// Set to the last duplicated object so it's visible in the property panel
|
// Set to the last duplicated object so it's visible in the property panel
|
||||||
SelectedItemOsList = newObjects.LastOrDefault();
|
SelectedItemOsList = newObjects.LastOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now update selection visuals
|
// Now update selection visuals
|
||||||
_objectManager.UpdateSelectionVisuals();
|
_objectManager.UpdateSelectionVisuals();
|
||||||
}));
|
}));
|
||||||
|
@ -665,7 +696,7 @@ namespace CtrEditor
|
||||||
.Where(o => o.Show_On_This_Page && o.AutoCreated)
|
.Where(o => o.Show_On_This_Page && o.AutoCreated)
|
||||||
.ToList();
|
.ToList();
|
||||||
foreach (var obj in osAutoCreated_List)
|
foreach (var obj in osAutoCreated_List)
|
||||||
RemoverObjetoSimulable(obj);
|
RemoverObjetoSimulable(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EliminarClonedCommand()
|
private void EliminarClonedCommand()
|
||||||
|
@ -674,7 +705,7 @@ namespace CtrEditor
|
||||||
.Where(o => o.Show_On_This_Page && o.Cloned)
|
.Where(o => o.Show_On_This_Page && o.Cloned)
|
||||||
.ToList();
|
.ToList();
|
||||||
foreach (var obj in osCloned_List)
|
foreach (var obj in osCloned_List)
|
||||||
RemoverObjetoSimulable(obj);
|
RemoverObjetoSimulable(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AssingPagesCommand()
|
private void AssingPagesCommand()
|
||||||
|
@ -765,10 +796,10 @@ namespace CtrEditor
|
||||||
|
|
||||||
private void StartSimulation()
|
private void StartSimulation()
|
||||||
{
|
{
|
||||||
// Detener simulación de fluidos si está ejecutándose
|
// Detener simulación de fluidos si está ejecutándose
|
||||||
StopFluidSimulation();
|
StopFluidSimulation();
|
||||||
|
|
||||||
IsSimulationRunning = true;
|
IsSimulationRunning = true;
|
||||||
|
|
||||||
// Ocultar rectángulos de selección antes de iniciar la simulación
|
// Ocultar rectángulos de selección antes de iniciar la simulación
|
||||||
_objectManager.UpdateSelectionVisuals();
|
_objectManager.UpdateSelectionVisuals();
|
||||||
|
@ -795,36 +826,36 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
simulationManager.Debug_ClearSimulationShapes();
|
simulationManager.Debug_ClearSimulationShapes();
|
||||||
Debug_SimulacionCreado = false;
|
Debug_SimulacionCreado = false;
|
||||||
}
|
}
|
||||||
_timerSimulacion.Stop();
|
_timerSimulacion.Stop();
|
||||||
|
|
||||||
// Restaurar los rectángulos de selección si hay objetos seleccionados
|
// Restaurar los rectángulos de selección si hay objetos seleccionados
|
||||||
_objectManager.UpdateSelectionVisuals();
|
_objectManager.UpdateSelectionVisuals();
|
||||||
|
|
||||||
// Limpiar historial de undo al detener la simulación
|
|
||||||
MainWindow?.ClearUndoHistory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
// Limpiar historial de undo al detener la simulación
|
||||||
/// Inicia la simulación de fluidos independiente
|
MainWindow?.ClearUndoHistory();
|
||||||
/// </summary>
|
}
|
||||||
public void StartFluidSimulation()
|
|
||||||
{
|
|
||||||
// Detener simulación física si está ejecutándose
|
|
||||||
StopSimulation();
|
|
||||||
|
|
||||||
FluidSimulation.StartFluidSimulation();
|
|
||||||
CommandManager.InvalidateRequerySuggested();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Detiene la simulación de fluidos independiente
|
/// Inicia la simulación de fluidos independiente
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void StopFluidSimulation()
|
public void StartFluidSimulation()
|
||||||
{
|
{
|
||||||
FluidSimulation.StopFluidSimulation();
|
// Detener simulación física si está ejecutándose
|
||||||
CommandManager.InvalidateRequerySuggested();
|
StopSimulation();
|
||||||
}
|
|
||||||
|
FluidSimulation.StartFluidSimulation();
|
||||||
|
CommandManager.InvalidateRequerySuggested();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detiene la simulación de fluidos independiente
|
||||||
|
/// </summary>
|
||||||
|
public void StopFluidSimulation()
|
||||||
|
{
|
||||||
|
FluidSimulation.StopFluidSimulation();
|
||||||
|
CommandManager.InvalidateRequerySuggested();
|
||||||
|
}
|
||||||
|
|
||||||
private void OnTickSimulacion(object sender, EventArgs e)
|
private void OnTickSimulacion(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
@ -833,7 +864,7 @@ namespace CtrEditor
|
||||||
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
|
// Detener el cronómetro y obtener el tiempo transcurrido en milisegundos
|
||||||
var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimModel_last;
|
var elapsedMilliseconds = stopwatch_Sim.Elapsed.TotalMilliseconds - stopwatch_SimModel_last;
|
||||||
stopwatch_SimModel_last = stopwatch_Sim.Elapsed.TotalMilliseconds;
|
stopwatch_SimModel_last = stopwatch_Sim.Elapsed.TotalMilliseconds;
|
||||||
|
|
||||||
// Acumular tiempo para el promedio
|
// Acumular tiempo para el promedio
|
||||||
accumulatedSimTime += elapsedMilliseconds;
|
accumulatedSimTime += elapsedMilliseconds;
|
||||||
simSampleCount++;
|
simSampleCount++;
|
||||||
|
@ -920,7 +951,7 @@ namespace CtrEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
stopwatch.Stop(); // Stop measuring time
|
stopwatch.Stop(); // Stop measuring time
|
||||||
// Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms");
|
// Debug.WriteLine($"OnRefreshEvent: {stopwatch.Elapsed.TotalMilliseconds} ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenWorkDirectory()
|
private void OpenWorkDirectory()
|
||||||
|
@ -956,16 +987,16 @@ namespace CtrEditor
|
||||||
{
|
{
|
||||||
// Remove the path if it already exists
|
// Remove the path if it already exists
|
||||||
RecentDirectories.Remove(path);
|
RecentDirectories.Remove(path);
|
||||||
|
|
||||||
// Add the new path at the beginning
|
// Add the new path at the beginning
|
||||||
RecentDirectories.Insert(0, path);
|
RecentDirectories.Insert(0, path);
|
||||||
|
|
||||||
// Keep only the last 10 entries
|
// Keep only the last 10 entries
|
||||||
while (RecentDirectories.Count > 10)
|
while (RecentDirectories.Count > 10)
|
||||||
{
|
{
|
||||||
RecentDirectories.RemoveAt(RecentDirectories.Count - 1);
|
RecentDirectories.RemoveAt(RecentDirectories.Count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateRecentDirectories();
|
UpdateRecentDirectories();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1103,9 +1134,9 @@ namespace CtrEditor
|
||||||
var objectsList = selectedObjects.ToList();
|
var objectsList = selectedObjects.ToList();
|
||||||
// Crear una clave única basada en los IDs de los objetos seleccionados
|
// Crear una clave única basada en los IDs de los objetos seleccionados
|
||||||
string key = string.Join("_", objectsList.Select(o => o.Id.Value).OrderBy(id => id));
|
string key = string.Join("_", objectsList.Select(o => o.Id.Value).OrderBy(id => id));
|
||||||
|
|
||||||
// Verificar si ya existe una ventana para esta selección
|
// Verificar si ya existe una ventana para esta selección
|
||||||
if (_propertyEditorWindows.TryGetValue(key, out var existingWindow) &&
|
if (_propertyEditorWindows.TryGetValue(key, out var existingWindow) &&
|
||||||
existingWindow.IsVisible)
|
existingWindow.IsVisible)
|
||||||
{
|
{
|
||||||
existingWindow.Activate();
|
existingWindow.Activate();
|
||||||
|
@ -1116,7 +1147,7 @@ namespace CtrEditor
|
||||||
var window = new Windows.MultiPropertyEditorWindow(objectsList, MainWindow);
|
var window = new Windows.MultiPropertyEditorWindow(objectsList, MainWindow);
|
||||||
window.Closed += (s, e) => _propertyEditorWindows.Remove(key);
|
window.Closed += (s, e) => _propertyEditorWindows.Remove(key);
|
||||||
_propertyEditorWindows[key] = window;
|
_propertyEditorWindows[key] = window;
|
||||||
|
|
||||||
window.Show();
|
window.Show();
|
||||||
HasUnsavedChanges = true;
|
HasUnsavedChanges = true;
|
||||||
_objectManager.UpdateSelectionVisuals();
|
_objectManager.UpdateSelectionVisuals();
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using CtrEditor.PopUps;
|
||||||
|
using Xceed.Wpf.Toolkit.PropertyGrid;
|
||||||
|
using Xceed.Wpf.Toolkit.PropertyGrid.Editors;
|
||||||
|
|
||||||
|
namespace CtrEditor.ObjetosSim
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Atributo para marcar propiedades que deben usar el editor de etiquetas
|
||||||
|
/// </summary>
|
||||||
|
public class TagEditorAttribute : Attribute
|
||||||
|
{
|
||||||
|
public TagEditorAttribute() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Editor personalizado para etiquetas en PropertyGrid
|
||||||
|
/// </summary>
|
||||||
|
public class TagPropertyEditor : ITypeEditor
|
||||||
|
{
|
||||||
|
public FrameworkElement ResolveEditor(PropertyItem propertyItem)
|
||||||
|
{
|
||||||
|
var grid = new Grid();
|
||||||
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||||
|
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||||
|
|
||||||
|
// TextBox para mostrar/editar etiquetas directamente
|
||||||
|
var textBox = new TextBox();
|
||||||
|
textBox.SetBinding(TextBox.TextProperty, new Binding("Value")
|
||||||
|
{
|
||||||
|
Source = propertyItem,
|
||||||
|
Mode = BindingMode.TwoWay,
|
||||||
|
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
|
||||||
|
});
|
||||||
|
Grid.SetColumn(textBox, 0);
|
||||||
|
|
||||||
|
// Botón para abrir el editor modal
|
||||||
|
var button = new Button
|
||||||
|
{
|
||||||
|
Content = "...",
|
||||||
|
Width = 25,
|
||||||
|
Margin = new Thickness(2, 0, 0, 0),
|
||||||
|
ToolTip = "Abrir editor de etiquetas"
|
||||||
|
};
|
||||||
|
|
||||||
|
button.Click += (sender, e) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Obtener el objeto que se está editando
|
||||||
|
if (propertyItem.Instance is osBase osObject)
|
||||||
|
{
|
||||||
|
// Obtener el MainWindow para acceder a todos los objetos
|
||||||
|
var mainWindow = Application.Current.Windows
|
||||||
|
.OfType<MainWindow>()
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (mainWindow?.DataContext is MainViewModel mainViewModel)
|
||||||
|
{
|
||||||
|
// Abrir el editor de etiquetas
|
||||||
|
var tagEditor = new TagEditorWindow(osObject, mainViewModel.ObjetosSimulables);
|
||||||
|
tagEditor.Owner = mainWindow;
|
||||||
|
|
||||||
|
if (tagEditor.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
// La ventana ya actualiza el objeto directamente
|
||||||
|
// Actualizar el textbox con el nuevo valor
|
||||||
|
textBox.GetBindingExpression(TextBox.TextProperty)?.UpdateTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Error al abrir el editor de etiquetas: {ex.Message}",
|
||||||
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Grid.SetColumn(button, 1);
|
||||||
|
|
||||||
|
grid.Children.Add(textBox);
|
||||||
|
grid.Children.Add(button);
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -106,8 +106,8 @@ namespace CtrEditor.ObjetosSim
|
||||||
LeftChanging(oldValue, newValue);
|
LeftChanging(oldValue, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void LeftChanged(float value)
|
public virtual void LeftChanged(float value)
|
||||||
{
|
{
|
||||||
// Actualizar posición relativa si el movimiento no viene del FramePlate
|
// Actualizar posición relativa si el movimiento no viene del FramePlate
|
||||||
UpdateFramePlateRelativePosition();
|
UpdateFramePlateRelativePosition();
|
||||||
}
|
}
|
||||||
|
@ -128,8 +128,8 @@ namespace CtrEditor.ObjetosSim
|
||||||
TopChanging(oldValue, newValue);
|
TopChanging(oldValue, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void TopChanged(float value)
|
public virtual void TopChanged(float value)
|
||||||
{
|
{
|
||||||
// Actualizar posición relativa si el movimiento no viene del FramePlate
|
// Actualizar posición relativa si el movimiento no viene del FramePlate
|
||||||
UpdateFramePlateRelativePosition();
|
UpdateFramePlateRelativePosition();
|
||||||
}
|
}
|
||||||
|
@ -176,6 +176,52 @@ namespace CtrEditor.ObjetosSim
|
||||||
public virtual void AnguloChanged(float value) { }
|
public virtual void AnguloChanged(float value) { }
|
||||||
public virtual void AnguloChanging(float oldValue, float newValue) { }
|
public virtual void AnguloChanging(float oldValue, float newValue) { }
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
[property: Description("Etiquetas para clasificar el objeto. Use # antes de cada etiqueta (ej: #motor #bomba #critico)")]
|
||||||
|
[property: Category("General:")]
|
||||||
|
[property: Editor(typeof(TagPropertyEditor), typeof(TagPropertyEditor))]
|
||||||
|
[property: TagEditor]
|
||||||
|
private string etiquetas = "";
|
||||||
|
|
||||||
|
partial void OnEtiquetasChanged(string value)
|
||||||
|
{
|
||||||
|
EtiquetasChanged(value);
|
||||||
|
// Update the visibility filters when tags change
|
||||||
|
_mainViewModel?.MainWindow?.VisFilter?.UpdateAvailableTags(_mainViewModel.ObjetosSimulables);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void EtiquetasChanged(string value) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtiene la lista de etiquetas parseadas desde el string
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public List<string> ListaEtiquetas
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(Etiquetas))
|
||||||
|
return new List<string>();
|
||||||
|
|
||||||
|
return Etiquetas
|
||||||
|
.Split(' ', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Where(tag => tag.StartsWith("#") && tag.Length > 1)
|
||||||
|
.Select(tag => tag.Substring(1).ToLowerInvariant())
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verifica si el objeto tiene una etiqueta específica
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="etiqueta">La etiqueta a buscar (sin el #)</param>
|
||||||
|
/// <returns>True si el objeto tiene la etiqueta</returns>
|
||||||
|
public bool TieneEtiqueta(string etiqueta)
|
||||||
|
{
|
||||||
|
return ListaEtiquetas.Contains(etiqueta.ToLowerInvariant());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Resize(double width, double height)
|
public void Resize(double width, double height)
|
||||||
{
|
{
|
||||||
|
@ -334,7 +380,7 @@ namespace CtrEditor.ObjetosSim
|
||||||
{
|
{
|
||||||
FramePlate.PropertyChanged += OnFramePlatePropertyChanged;
|
FramePlate.PropertyChanged += OnFramePlatePropertyChanged;
|
||||||
UpdateZIndex(FramePlate.Zindex_FramePlate);
|
UpdateZIndex(FramePlate.Zindex_FramePlate);
|
||||||
|
|
||||||
// Calcular punto de pivot y posición relativa inicial
|
// Calcular punto de pivot y posición relativa inicial
|
||||||
var pivotPoint = FramePlate.GetPivotPoint();
|
var pivotPoint = FramePlate.GetPivotPoint();
|
||||||
FramePlate_PivotX = pivotPoint.X;
|
FramePlate_PivotX = pivotPoint.X;
|
||||||
|
@ -373,7 +419,7 @@ namespace CtrEditor.ObjetosSim
|
||||||
if (e.PropertyName == nameof(osFramePlate.Nombre))
|
if (e.PropertyName == nameof(osFramePlate.Nombre))
|
||||||
Group_Panel = ((osFramePlate)sender).Nombre;
|
Group_Panel = ((osFramePlate)sender).Nombre;
|
||||||
|
|
||||||
if (e.PropertyName == nameof(osFramePlate.Top) ||
|
if (e.PropertyName == nameof(osFramePlate.Top) ||
|
||||||
e.PropertyName == nameof(osFramePlate.Left))
|
e.PropertyName == nameof(osFramePlate.Left))
|
||||||
{
|
{
|
||||||
UpdateOrbitalPosition();
|
UpdateOrbitalPosition();
|
||||||
|
@ -387,7 +433,7 @@ namespace CtrEditor.ObjetosSim
|
||||||
UpdateOrbitalPosition();
|
UpdateOrbitalPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.PropertyName == nameof(osFramePlate.PivotCenterX) ||
|
if (e.PropertyName == nameof(osFramePlate.PivotCenterX) ||
|
||||||
e.PropertyName == nameof(osFramePlate.PivotCenterY))
|
e.PropertyName == nameof(osFramePlate.PivotCenterY))
|
||||||
{
|
{
|
||||||
// Cuando cambia el pivot, recalcular posición relativa
|
// Cuando cambia el pivot, recalcular posición relativa
|
||||||
|
@ -407,32 +453,32 @@ namespace CtrEditor.ObjetosSim
|
||||||
|
|
||||||
// Actualizar punto de pivot actual (puede haber cambiado por los checkboxes)
|
// Actualizar punto de pivot actual (puede haber cambiado por los checkboxes)
|
||||||
var currentPivot = FramePlate.GetPivotPoint();
|
var currentPivot = FramePlate.GetPivotPoint();
|
||||||
|
|
||||||
// Calcular el ángulo de rotación total desde la posición inicial
|
// Calcular el ángulo de rotación total desde la posición inicial
|
||||||
float deltaAngle = FramePlate.Angulo - FramePlate_InitialAngle;
|
float deltaAngle = FramePlate.Angulo - FramePlate_InitialAngle;
|
||||||
|
|
||||||
// Convertir ángulo a radianes
|
// Convertir ángulo a radianes
|
||||||
float angleRad = deltaAngle * (float)Math.PI / 180.0f;
|
float angleRad = deltaAngle * (float)Math.PI / 180.0f;
|
||||||
|
|
||||||
// Calcular la nueva posición orbital usando rotación de matriz
|
// Calcular la nueva posición orbital usando rotación de matriz
|
||||||
float cosAngle = (float)Math.Cos(angleRad);
|
float cosAngle = (float)Math.Cos(angleRad);
|
||||||
float sinAngle = (float)Math.Sin(angleRad);
|
float sinAngle = (float)Math.Sin(angleRad);
|
||||||
|
|
||||||
// Rotar la posición relativa
|
// Rotar la posición relativa
|
||||||
float rotatedX = cosAngle * FramePlate_RelativeX - sinAngle * FramePlate_RelativeY;
|
float rotatedX = cosAngle * FramePlate_RelativeX - sinAngle * FramePlate_RelativeY;
|
||||||
float rotatedY = sinAngle * FramePlate_RelativeX + cosAngle * FramePlate_RelativeY;
|
float rotatedY = sinAngle * FramePlate_RelativeX + cosAngle * FramePlate_RelativeY;
|
||||||
|
|
||||||
// Calcular nueva posición absoluta usando el punto de pivot actual
|
// Calcular nueva posición absoluta usando el punto de pivot actual
|
||||||
float newLeft = currentPivot.X + rotatedX;
|
float newLeft = currentPivot.X + rotatedX;
|
||||||
float newTop = currentPivot.Y + rotatedY;
|
float newTop = currentPivot.Y + rotatedY;
|
||||||
|
|
||||||
// Actualizar directamente los campos sin disparar eventos de cambio
|
// Actualizar directamente los campos sin disparar eventos de cambio
|
||||||
SetProperty(ref left, newLeft);
|
SetProperty(ref left, newLeft);
|
||||||
SetProperty(ref top, newTop);
|
SetProperty(ref top, newTop);
|
||||||
|
|
||||||
// Forzar actualización de la posición visual
|
// Forzar actualización de la posición visual
|
||||||
ActualizarLeftTop();
|
ActualizarLeftTop();
|
||||||
|
|
||||||
// Llamar a OnMoveResizeRotate para actualizar la física
|
// Llamar a OnMoveResizeRotate para actualizar la física
|
||||||
OnMoveResizeRotate();
|
OnMoveResizeRotate();
|
||||||
}
|
}
|
||||||
|
@ -444,20 +490,20 @@ namespace CtrEditor.ObjetosSim
|
||||||
{
|
{
|
||||||
// Obtener punto de pivot actual
|
// Obtener punto de pivot actual
|
||||||
var currentPivot = FramePlate.GetPivotPoint();
|
var currentPivot = FramePlate.GetPivotPoint();
|
||||||
|
|
||||||
// Recalcular posición relativa considerando la rotación actual del FramePlate
|
// Recalcular posición relativa considerando la rotación actual del FramePlate
|
||||||
float deltaAngle = FramePlate.Angulo - FramePlate_InitialAngle;
|
float deltaAngle = FramePlate.Angulo - FramePlate_InitialAngle;
|
||||||
float angleRad = deltaAngle * (float)Math.PI / 180.0f;
|
float angleRad = deltaAngle * (float)Math.PI / 180.0f;
|
||||||
|
|
||||||
// Calcular la posición relativa actual respecto al pivot del FramePlate
|
// Calcular la posición relativa actual respecto al pivot del FramePlate
|
||||||
float currentRelativeX = Left - currentPivot.X;
|
float currentRelativeX = Left - currentPivot.X;
|
||||||
float currentRelativeY = Top - currentPivot.Y;
|
float currentRelativeY = Top - currentPivot.Y;
|
||||||
|
|
||||||
// Si el FramePlate está rotado, necesitamos "desrotar" la posición para obtener
|
// Si el FramePlate está rotado, necesitamos "desrotar" la posición para obtener
|
||||||
// la posición relativa en el sistema de coordenadas original
|
// la posición relativa en el sistema de coordenadas original
|
||||||
float cosAngle = (float)Math.Cos(-angleRad);
|
float cosAngle = (float)Math.Cos(-angleRad);
|
||||||
float sinAngle = (float)Math.Sin(-angleRad);
|
float sinAngle = (float)Math.Sin(-angleRad);
|
||||||
|
|
||||||
FramePlate_RelativeX = cosAngle * currentRelativeX - sinAngle * currentRelativeY;
|
FramePlate_RelativeX = cosAngle * currentRelativeX - sinAngle * currentRelativeY;
|
||||||
FramePlate_RelativeY = sinAngle * currentRelativeX + cosAngle * currentRelativeY;
|
FramePlate_RelativeY = sinAngle * currentRelativeX + cosAngle * currentRelativeY;
|
||||||
}
|
}
|
||||||
|
@ -470,23 +516,23 @@ namespace CtrEditor.ObjetosSim
|
||||||
// Obtener posición absoluta actual del objeto (antes del cambio de pivot)
|
// Obtener posición absoluta actual del objeto (antes del cambio de pivot)
|
||||||
float currentLeft = Left;
|
float currentLeft = Left;
|
||||||
float currentTop = Top;
|
float currentTop = Top;
|
||||||
|
|
||||||
// Obtener nuevo punto de pivot
|
// Obtener nuevo punto de pivot
|
||||||
var newPivot = FramePlate.GetPivotPoint();
|
var newPivot = FramePlate.GetPivotPoint();
|
||||||
|
|
||||||
// Calcular nueva posición relativa respecto al nuevo pivot
|
// Calcular nueva posición relativa respecto al nuevo pivot
|
||||||
// Considerar la rotación actual para obtener posición relativa en sistema original
|
// Considerar la rotación actual para obtener posición relativa en sistema original
|
||||||
float deltaAngle = FramePlate.Angulo - FramePlate_InitialAngle;
|
float deltaAngle = FramePlate.Angulo - FramePlate_InitialAngle;
|
||||||
float angleRad = deltaAngle * (float)Math.PI / 180.0f;
|
float angleRad = deltaAngle * (float)Math.PI / 180.0f;
|
||||||
|
|
||||||
// Posición relativa actual respecto al nuevo pivot
|
// Posición relativa actual respecto al nuevo pivot
|
||||||
float currentRelativeX = currentLeft - newPivot.X;
|
float currentRelativeX = currentLeft - newPivot.X;
|
||||||
float currentRelativeY = currentTop - newPivot.Y;
|
float currentRelativeY = currentTop - newPivot.Y;
|
||||||
|
|
||||||
// "Desrotar" para obtener coordenadas en el sistema original
|
// "Desrotar" para obtener coordenadas en el sistema original
|
||||||
float cosAngle = (float)Math.Cos(-angleRad);
|
float cosAngle = (float)Math.Cos(-angleRad);
|
||||||
float sinAngle = (float)Math.Sin(-angleRad);
|
float sinAngle = (float)Math.Sin(-angleRad);
|
||||||
|
|
||||||
FramePlate_RelativeX = cosAngle * currentRelativeX - sinAngle * currentRelativeY;
|
FramePlate_RelativeX = cosAngle * currentRelativeX - sinAngle * currentRelativeY;
|
||||||
FramePlate_RelativeY = sinAngle * currentRelativeX + cosAngle * currentRelativeY;
|
FramePlate_RelativeY = sinAngle * currentRelativeX + cosAngle * currentRelativeY;
|
||||||
}
|
}
|
||||||
|
@ -666,7 +712,8 @@ namespace CtrEditor.ObjetosSim
|
||||||
// Filtrar texto por lista de caracteres permitidos
|
// Filtrar texto por lista de caracteres permitidos
|
||||||
var filteredBlocks = result.TextBlocks
|
var filteredBlocks = result.TextBlocks
|
||||||
.Where(block => block.Score > 0.5) // Solo bloques con confianza > 50%
|
.Where(block => block.Score > 0.5) // Solo bloques con confianza > 50%
|
||||||
.Select(block => new {
|
.Select(block => new
|
||||||
|
{
|
||||||
Text = new string(block.Text.Where(c => allowedChars.Contains(c)).ToArray()),
|
Text = new string(block.Text.Where(c => allowedChars.Contains(c)).ToArray()),
|
||||||
Score = block.Score
|
Score = block.Score
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
<Window x:Class="CtrEditor.PopUps.TagEditorWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="Editor de Etiquetas" Height="400" Width="500"
|
||||||
|
WindowStartupLocation="CenterOwner" ResizeMode="CanResize">
|
||||||
|
|
||||||
|
<Window.Resources>
|
||||||
|
<Style x:Key="TagButtonStyle" TargetType="Button">
|
||||||
|
<Setter Property="Margin" Value="2"/>
|
||||||
|
<Setter Property="Padding" Value="8,4"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="BorderBrush" Value="LightGray"/>
|
||||||
|
<Setter Property="Background" Value="LightBlue"/>
|
||||||
|
<Setter Property="Foreground" Value="DarkBlue"/>
|
||||||
|
<Setter Property="FontWeight" Value="Bold"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="SelectedTagStyle" TargetType="Button" BasedOn="{StaticResource TagButtonStyle}">
|
||||||
|
<Setter Property="Background" Value="Navy"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="AvailableTagStyle" TargetType="Button" BasedOn="{StaticResource TagButtonStyle}">
|
||||||
|
<Setter Property="Background" Value="LightGray"/>
|
||||||
|
<Setter Property="Foreground" Value="Black"/>
|
||||||
|
</Style>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Grid Margin="10">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<TextBlock Grid.Row="0" Text="Editor de Etiquetas" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<!-- Nueva etiqueta -->
|
||||||
|
<Grid Grid.Row="1" Margin="0,0,0,10">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Column="0" Text="Nueva etiqueta:" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||||
|
<TextBox Grid.Column="1" x:Name="txtNuevaEtiqueta"
|
||||||
|
Text="{Binding NuevaEtiqueta, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
KeyDown="TxtNuevaEtiqueta_KeyDown"/>
|
||||||
|
<Button Grid.Column="2" Content="Agregar"
|
||||||
|
Command="{Binding AgregarEtiquetaCommand}"
|
||||||
|
Margin="10,0,0,0" MinWidth="70"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Etiquetas del objeto actual -->
|
||||||
|
<TextBlock Grid.Row="2" Text="Etiquetas del objeto:" FontWeight="Bold" Margin="0,0,0,5"/>
|
||||||
|
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto" MaxHeight="120">
|
||||||
|
<ItemsControl ItemsSource="{Binding EtiquetasObjeto}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Button Content="{Binding DisplayName}"
|
||||||
|
Style="{StaticResource SelectedTagStyle}"
|
||||||
|
Command="{Binding DataContext.RemoverEtiquetaCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||||
|
CommandParameter="{Binding TagName}"
|
||||||
|
ToolTip="Click para remover"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<!-- Etiquetas disponibles -->
|
||||||
|
<TextBlock Grid.Row="4" Text="Etiquetas disponibles:" FontWeight="Bold" Margin="0,10,0,5"/>
|
||||||
|
<ScrollViewer Grid.Row="5" VerticalScrollBarVisibility="Auto" MaxHeight="120">
|
||||||
|
<ItemsControl ItemsSource="{Binding EtiquetasDisponibles}">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<WrapPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Button Content="{Binding DisplayName}"
|
||||||
|
Style="{StaticResource AvailableTagStyle}"
|
||||||
|
Command="{Binding DataContext.AgregarEtiquetaExistenteCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||||
|
CommandParameter="{Binding TagName}"
|
||||||
|
ToolTip="Click para agregar"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
</ScrollViewer>
|
||||||
|
|
||||||
|
<!-- Botones -->
|
||||||
|
<StackPanel Grid.Row="6" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0">
|
||||||
|
<Button Content="Aplicar" Command="{Binding AplicarCommand}" Margin="0,0,10,0" MinWidth="70"/>
|
||||||
|
<Button Content="Cancelar" Command="{Binding CancelarCommand}" MinWidth="70"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
|
@ -0,0 +1,215 @@
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using CtrEditor.ObjetosSim;
|
||||||
|
|
||||||
|
namespace CtrEditor.PopUps
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for TagEditorWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class TagEditorWindow : Window
|
||||||
|
{
|
||||||
|
public TagEditorWindow(osBase objeto, IEnumerable<osBase> todosLosObjetos)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = new TagEditorViewModel(objeto, todosLosObjetos, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TxtNuevaEtiqueta_KeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter)
|
||||||
|
{
|
||||||
|
if (DataContext is TagEditorViewModel viewModel)
|
||||||
|
{
|
||||||
|
viewModel.AgregarEtiquetaCommand.Execute(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TagInfo
|
||||||
|
{
|
||||||
|
public string TagName { get; set; }
|
||||||
|
public string DisplayName => "#" + TagName;
|
||||||
|
|
||||||
|
public TagInfo(string tagName)
|
||||||
|
{
|
||||||
|
TagName = tagName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class TagEditorViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly osBase _objeto;
|
||||||
|
private readonly IEnumerable<osBase> _todosLosObjetos;
|
||||||
|
private readonly TagEditorWindow _window;
|
||||||
|
private readonly List<string> _etiquetasOriginales;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string nuevaEtiqueta = "";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<TagInfo> etiquetasObjeto = new();
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private ObservableCollection<TagInfo> etiquetasDisponibles = new();
|
||||||
|
|
||||||
|
public ICommand AgregarEtiquetaCommand { get; }
|
||||||
|
public ICommand AgregarEtiquetaExistenteCommand { get; }
|
||||||
|
public ICommand RemoverEtiquetaCommand { get; }
|
||||||
|
public ICommand AplicarCommand { get; }
|
||||||
|
public ICommand CancelarCommand { get; }
|
||||||
|
|
||||||
|
public TagEditorViewModel(osBase objeto, IEnumerable<osBase> todosLosObjetos, TagEditorWindow window)
|
||||||
|
{
|
||||||
|
_objeto = objeto;
|
||||||
|
_todosLosObjetos = todosLosObjetos;
|
||||||
|
_window = window;
|
||||||
|
|
||||||
|
// Guardar las etiquetas originales para poder cancelar
|
||||||
|
_etiquetasOriginales = objeto.ListaEtiquetas.ToList();
|
||||||
|
|
||||||
|
// Inicializar comandos
|
||||||
|
AgregarEtiquetaCommand = new RelayCommand(AgregarEtiqueta, () => !string.IsNullOrWhiteSpace(NuevaEtiqueta));
|
||||||
|
AgregarEtiquetaExistenteCommand = new RelayCommand<string>(AgregarEtiquetaExistente);
|
||||||
|
RemoverEtiquetaCommand = new RelayCommand<string>(RemoverEtiqueta);
|
||||||
|
AplicarCommand = new RelayCommand(Aplicar);
|
||||||
|
CancelarCommand = new RelayCommand(Cancelar);
|
||||||
|
|
||||||
|
// Cargar datos iniciales
|
||||||
|
CargarEtiquetasObjeto();
|
||||||
|
CargarEtiquetasDisponibles();
|
||||||
|
|
||||||
|
// Suscribirse al cambio de NuevaEtiqueta para actualizar el comando
|
||||||
|
PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(NuevaEtiqueta))
|
||||||
|
{
|
||||||
|
(AgregarEtiquetaCommand as IRelayCommand)?.NotifyCanExecuteChanged();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CargarEtiquetasObjeto()
|
||||||
|
{
|
||||||
|
EtiquetasObjeto.Clear();
|
||||||
|
foreach (var tag in _objeto.ListaEtiquetas.OrderBy(t => t))
|
||||||
|
{
|
||||||
|
EtiquetasObjeto.Add(new TagInfo(tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CargarEtiquetasDisponibles()
|
||||||
|
{
|
||||||
|
EtiquetasDisponibles.Clear();
|
||||||
|
|
||||||
|
// Obtener todas las etiquetas únicas de todos los objetos
|
||||||
|
var todasLasEtiquetas = _todosLosObjetos
|
||||||
|
.SelectMany(obj => obj.ListaEtiquetas)
|
||||||
|
.Distinct()
|
||||||
|
.OrderBy(tag => tag)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Mostrar solo las que no están en el objeto actual
|
||||||
|
var etiquetasObjetoActual = _objeto.ListaEtiquetas;
|
||||||
|
foreach (var tag in todasLasEtiquetas.Except(etiquetasObjetoActual))
|
||||||
|
{
|
||||||
|
EtiquetasDisponibles.Add(new TagInfo(tag));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AgregarEtiqueta()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(NuevaEtiqueta))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Limpiar la etiqueta (remover # si existe y convertir a minúsculas)
|
||||||
|
string tagLimpia = NuevaEtiqueta.Trim();
|
||||||
|
if (tagLimpia.StartsWith("#"))
|
||||||
|
tagLimpia = tagLimpia.Substring(1);
|
||||||
|
|
||||||
|
tagLimpia = tagLimpia.ToLowerInvariant();
|
||||||
|
|
||||||
|
// Validar que no esté vacía después de limpiar
|
||||||
|
if (string.IsNullOrWhiteSpace(tagLimpia))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Verificar que no existe ya
|
||||||
|
if (_objeto.ListaEtiquetas.Contains(tagLimpia))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Agregar la etiqueta al objeto
|
||||||
|
var etiquetasActuales = string.IsNullOrWhiteSpace(_objeto.Etiquetas) ?
|
||||||
|
new List<string>() :
|
||||||
|
_objeto.Etiquetas.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
|
|
||||||
|
etiquetasActuales.Add("#" + tagLimpia);
|
||||||
|
_objeto.Etiquetas = string.Join(" ", etiquetasActuales);
|
||||||
|
|
||||||
|
// Actualizar las listas
|
||||||
|
CargarEtiquetasObjeto();
|
||||||
|
CargarEtiquetasDisponibles();
|
||||||
|
|
||||||
|
// Limpiar el campo
|
||||||
|
NuevaEtiqueta = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AgregarEtiquetaExistente(string tagName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(tagName) || _objeto.ListaEtiquetas.Contains(tagName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Agregar la etiqueta al objeto
|
||||||
|
var etiquetasActuales = string.IsNullOrWhiteSpace(_objeto.Etiquetas) ?
|
||||||
|
new List<string>() :
|
||||||
|
_objeto.Etiquetas.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||||
|
|
||||||
|
etiquetasActuales.Add("#" + tagName);
|
||||||
|
_objeto.Etiquetas = string.Join(" ", etiquetasActuales);
|
||||||
|
|
||||||
|
// Actualizar las listas
|
||||||
|
CargarEtiquetasObjeto();
|
||||||
|
CargarEtiquetasDisponibles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoverEtiqueta(string tagName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(tagName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Remover la etiqueta del objeto
|
||||||
|
var etiquetasActuales = _objeto.Etiquetas
|
||||||
|
.Split(' ', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Where(tag => !tag.Equals("#" + tagName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
_objeto.Etiquetas = string.Join(" ", etiquetasActuales);
|
||||||
|
|
||||||
|
// Actualizar las listas
|
||||||
|
CargarEtiquetasObjeto();
|
||||||
|
CargarEtiquetasDisponibles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Aplicar()
|
||||||
|
{
|
||||||
|
_window.DialogResult = true;
|
||||||
|
_window.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancelar()
|
||||||
|
{
|
||||||
|
// Restaurar las etiquetas originales
|
||||||
|
var etiquetasOriginalesString = _etiquetasOriginales.Any() ?
|
||||||
|
string.Join(" ", _etiquetasOriginales.Select(tag => "#" + tag)) :
|
||||||
|
"";
|
||||||
|
_objeto.Etiquetas = etiquetasOriginalesString;
|
||||||
|
|
||||||
|
_window.DialogResult = false;
|
||||||
|
_window.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue