1+ using Avalonia . Controls . PanAndZoom ;
12using Avalonia . Controls . Selection ;
2- using Avalonia . Media . Imaging ;
33using Avalonia . Threading ;
44using Definitions . ObjectModels ;
55using DynamicData ;
@@ -30,20 +30,6 @@ public class ImageTableViewModel : ReactiveObject, IExtraContentViewModel
3030 [ Reactive ]
3131 public ColourRemapSwatch SelectedSecondarySwatch { get ; set ; } = ColourRemapSwatch . SecondaryRemap ;
3232
33- readonly DispatcherTimer animationTimer ;
34- int currentFrameIndex ;
35-
36- public IList < Bitmap > SelectedBitmaps { get ; set ; }
37-
38- [ Reactive ] public Bitmap SelectedBitmapPreview { get ; set ; }
39- public Avalonia . Size SelectedBitmapPreviewBorder
40- => SelectedBitmapPreview == null
41- ? new Avalonia . Size ( )
42- : new Avalonia . Size ( SelectedBitmapPreview . Size . Width + 2 , SelectedBitmapPreview . Size . Height + 2 ) ;
43-
44- [ Reactive ]
45- public int AnimationSpeed { get ; set ; } = 40 ;
46-
4733 [ Reactive ]
4834 public ICommand ReplaceImageCommand { get ; set ; }
4935
@@ -56,29 +42,33 @@ public Avalonia.Size SelectedBitmapPreviewBorder
5642 [ Reactive ]
5743 public ICommand CropAllImagesCommand { get ; set ; }
5844
45+ [ Reactive ]
46+ public ICommand ZeroOffsetAllImagesCommand { get ; set ; }
47+
48+ [ Reactive ]
49+ public ICommand CenterOffsetAllImagesCommand { get ; set ; }
50+
5951 // what is displaying on the ui
6052 [ Reactive ]
61- public ObservableCollection < Bitmap ? > Bitmaps { get ; set ; }
53+ public ObservableCollection < ImageViewModel > ImageViewModels { get ; set ; } = [ ] ;
6254
6355 [ Reactive ]
6456 public int SelectedImageIndex { get ; set ; } = - 1 ;
6557
6658 [ Reactive ]
67- public SelectionModel < Bitmap > SelectionModel { get ; set ; }
68-
69- public UIG1Element32 ? SelectedG1Element
70- => SelectedImageIndex == - 1 || Model . G1Provider . GraphicsElements . Count == 0 || SelectedImageIndex >= Model . G1Provider . GraphicsElements . Count
71- ? null
72- : new UIG1Element32 ( SelectedImageIndex , Model . GetImageName ( SelectedImageIndex ) , Model . G1Provider . GraphicsElements [ SelectedImageIndex ] ) ;
73-
74- public Avalonia . Point SelectedG1ElementOffset
75- => SelectedG1Element == null
76- ? new Avalonia . Point ( )
77- : new Avalonia . Point ( - SelectedG1Element ? . XOffset ?? 0 , - SelectedG1Element ? . YOffset ?? 0 ) ;
78- public Avalonia . Size SelectedG1ElementSize
79- => SelectedG1Element == null
80- ? new Avalonia . Size ( )
81- : new Avalonia . Size ( SelectedG1Element ? . Width ?? 0 , SelectedG1Element ? . Height ?? 0 ) ;
59+ public SelectionModel < ImageViewModel > SelectionModel { get ; set ; }
60+
61+ [ Reactive ]
62+ public ImageViewModel ? SelectedImage { get ; set ; }
63+
64+ readonly DispatcherTimer animationTimer ;
65+ int currentFrameIndex ;
66+
67+ [ Reactive ]
68+ public IList < ImageViewModel > SelectedBitmaps { get ; set ; }
69+
70+ [ Reactive ]
71+ public int AnimationSpeed { get ; set ; } = 40 ;
8272
8373 ImageTableModel Model { get ; init ; }
8474
@@ -95,17 +85,12 @@ public ImageTableViewModel(ImageTableModel model)
9585 . Subscribe ( _ => UpdateBitmaps ( ) ) ;
9686
9787 _ = this . WhenAnyValue ( o => o . SelectedImageIndex )
98- . Subscribe ( _ => this . RaisePropertyChanged ( nameof ( SelectedG1Element ) ) ) ; // disabling this line stops mem leak
99- _ = this . WhenAnyValue ( o => o . SelectedG1Element )
100- . Subscribe ( _ => this . RaisePropertyChanged ( nameof ( SelectedG1ElementOffset ) ) ) ;
101- _ = this . WhenAnyValue ( o => o . SelectedG1Element )
102- . Subscribe ( _ => this . RaisePropertyChanged ( nameof ( SelectedG1ElementSize ) ) ) ;
103- _ = this . WhenAnyValue ( o => o . SelectedG1Element )
104- . Subscribe ( _ => this . RaisePropertyChanged ( nameof ( SelectedBitmapPreview ) ) ) ;
105- _ = this . WhenAnyValue ( o => o . SelectedBitmapPreview )
106- . Subscribe ( _ => this . RaisePropertyChanged ( nameof ( SelectedBitmapPreviewBorder ) ) ) ;
88+ . Where ( index => index >= 0 && index < ImageViewModels ? . Count )
89+ . Subscribe ( _ => SelectedImage = ImageViewModels [ SelectedImageIndex ] ) ;
90+
10791 _ = this . WhenAnyValue ( o => o . AnimationSpeed )
108- . Subscribe ( _ => UpdateAnimationSpeed ( ) ) ;
92+ . Where ( _ => animationTimer != null )
93+ . Subscribe ( _ => animationTimer ! . Interval = TimeSpan . FromMilliseconds ( 1000 / AnimationSpeed ) ) ;
10994
11095 ImportImagesCommand = ReactiveCommand . CreateFromTask ( ImportImages ) ;
11196 ExportImagesCommand = ReactiveCommand . CreateFromTask ( ExportImages ) ;
@@ -116,6 +101,24 @@ public ImageTableViewModel(ImageTableModel model)
116101 UpdateBitmaps ( ) ;
117102 } ) ;
118103
104+ ZeroOffsetAllImagesCommand = ReactiveCommand . Create ( ( ) =>
105+ {
106+ foreach ( var ivm in ImageViewModels )
107+ {
108+ ivm . XOffset = 0 ;
109+ ivm . YOffset = 0 ;
110+ }
111+ } ) ;
112+
113+ CenterOffsetAllImagesCommand = ReactiveCommand . Create ( ( ) =>
114+ {
115+ foreach ( var ivm in ImageViewModels )
116+ {
117+ ivm . XOffset = ( short ) ( - ivm . Width / 2 ) ;
118+ ivm . YOffset = ( short ) ( - ivm . Height / 2 ) ;
119+ }
120+ } ) ;
121+
119122 UpdateBitmaps ( ) ;
120123
121124 // Set up the animation timer
@@ -129,7 +132,7 @@ public ImageTableViewModel(ImageTableModel model)
129132
130133 void SelectionChanged ( object sender , SelectionModelSelectionChangedEventArgs e )
131134 {
132- var sm = ( SelectionModel < Bitmap > ) sender ;
135+ var sm = ( SelectionModel < ImageViewModel > ) sender ;
133136
134137 if ( sm . SelectedIndexes . Count > 0 )
135138 {
@@ -142,17 +145,7 @@ void SelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e)
142145 }
143146
144147 // ... handle selection changed
145- SelectedBitmaps = [ .. sm . SelectedItems . Cast < Bitmap > ( ) ] ;
146- }
147-
148- void UpdateAnimationSpeed ( )
149- {
150- if ( animationTimer == null )
151- {
152- return ;
153- }
154-
155- animationTimer . Interval = TimeSpan . FromMilliseconds ( 1000 / AnimationSpeed ) ;
148+ SelectedBitmaps = [ .. sm . SelectedItems . Cast < ImageViewModel > ( ) ] ;
156149 }
157150
158151 void AnimationTimer_Tick ( object ? sender , EventArgs e )
@@ -167,8 +160,7 @@ void AnimationTimer_Tick(object? sender, EventArgs e)
167160 currentFrameIndex = 0 ;
168161 }
169162
170- // Update the displayed image
171- SelectedBitmapPreview = SelectedBitmaps [ currentFrameIndex ] ;
163+ // Update the displayed image viewmodel
172164 SelectedImageIndex = SelectionModel . SelectedIndexes [ currentFrameIndex ] ; // disabling this also makes the memory leaks stop
173165
174166 // Move to the next frame, looping back to the beginning if necessary
@@ -177,7 +169,7 @@ void AnimationTimer_Tick(object? sender, EventArgs e)
177169
178170 void CreateSelectionModel ( )
179171 {
180- SelectionModel = new SelectionModel < Bitmap >
172+ SelectionModel = new SelectionModel < ImageViewModel >
181173 {
182174 SingleSelect = false
183175 } ;
@@ -213,7 +205,15 @@ public async Task ImportImages()
213205 public void UpdateBitmaps ( )
214206 {
215207 Model . RecalcImages ( SelectedPrimarySwatch , SelectedSecondarySwatch ) ;
216- Bitmaps = [ .. G1ImageConversion . CreateAvaloniaImages ( Model . Images ) ] ;
208+ var newImages = G1ImageConversion . CreateAvaloniaImages ( Model . Images ) ;
209+
210+ ImageViewModels . Clear ( ) ;
211+ var i = 0 ;
212+ foreach ( var image in newImages )
213+ {
214+ ImageViewModels . Add ( new ImageViewModel ( i , Model . GetImageName ( i ) , Model . G1Provider . GraphicsElements [ i ] , image ) ) ;
215+ i ++ ;
216+ }
217217 }
218218
219219 public async Task ExportImages ( )
0 commit comments