From 3ac4e6ae0cf80dd3d51509c53b136c77bc523fe4 Mon Sep 17 00:00:00 2001 From: Arty Face Date: Wed, 18 Feb 2026 11:38:18 -0500 Subject: [PATCH] add T key for toggling the image fill mode --- rSlide/ConfigManager.cs | 133 ++++++++++++----- rSlide/ImageManager.cs | 200 ++++++++++++++++++-------- rSlide/MainWindow.xaml | 6 +- rSlide/MainWindow.xaml.cs | 272 +++++++++++++++++++++++++---------- rSlide/PlaybackController.cs | 146 ++++++++++++++----- rSlide/TransitionEngine.cs | 254 +++++++++++++++++++++++--------- 6 files changed, 740 insertions(+), 271 deletions(-) diff --git a/rSlide/ConfigManager.cs b/rSlide/ConfigManager.cs index 754123e..231311b 100644 --- a/rSlide/ConfigManager.cs +++ b/rSlide/ConfigManager.cs @@ -10,16 +10,22 @@ public class Config public Config() { ImagePaths = new List(); - DisplayDuration = 8.0; - TransitionDurationPercent = 20.0; + DisplayDuration = 2.0; + TransitionDurationPercent = 0.0; PlaybackMode = "Random"; LastImageIndex = 0; EnableKenBurns = true; KenBurnsDuration = 10.0; KenBurnsMaxZoom = 1.3; + StretchMode = "Uniform"; // Default: all transitions enabled - ActiveTransitions = new List { - "Crossfade", "MorphZoom", "SoftWipe", "ParallaxReveal", "ScaleDissolve", + ActiveTransitions = new List + { + "Crossfade", + "MorphZoom", + "SoftWipe", + "ParallaxReveal", + "ScaleDissolve", "KenBurns" }; } @@ -32,6 +38,8 @@ public Config() public bool EnableKenBurns { get; set; } public double KenBurnsDuration { get; set; } public double KenBurnsMaxZoom { get; set; } + public string StretchMode { get; set; } + /// /// List of active transition/effect names. One transition is picked /// randomly each time. KenBurns is also listed here as an effect toggle. @@ -45,8 +53,13 @@ public static class ConfigManager private static readonly CultureInfo CI = CultureInfo.InvariantCulture; // Transition names that appear in the pool - private static readonly string[] AllTransitions = { - "Crossfade", "MorphZoom", "SoftWipe", "ParallaxReveal", "ScaleDissolve" + private static readonly string[] AllTransitions = + { + "Crossfade", + "MorphZoom", + "SoftWipe", + "ParallaxReveal", + "ScaleDissolve" }; // KenBurns key and its parameter keys @@ -85,8 +98,18 @@ private static List BuildConfigLines(Config config, bool isDefault) lines.Add("[Display]"); lines.Add("; DisplayDuration: seconds each image is shown (1-60)"); lines.Add(string.Format(CI, "DisplayDuration={0:F1}", config.DisplayDuration)); - lines.Add("; TransitionDurationPercent: transition length as % of DisplayDuration (0-100)"); - lines.Add(string.Format(CI, "TransitionDurationPercent={0:F1}", config.TransitionDurationPercent)); + lines.Add( + "; TransitionDurationPercent: transition length as % of DisplayDuration (0-100)" + ); + lines.Add( + string.Format( + CI, + "TransitionDurationPercent={0:F1}", + config.TransitionDurationPercent + ) + ); + lines.Add("; StretchMode: Uniform | UniformToFill | Fill | None"); + lines.Add(string.Format(CI, "StretchMode={0}", config.StretchMode)); lines.Add(""); // --- Playback --- @@ -113,7 +136,10 @@ private static List BuildConfigLines(Config config, bool isDefault) lines.Add("; Effects (not transitions -- applied during image display):"); lines.Add("; KenBurns - slow pan & zoom during image display"); - var active = new HashSet(config.ActiveTransitions, StringComparer.OrdinalIgnoreCase); + var active = new HashSet( + config.ActiveTransitions, + StringComparer.OrdinalIgnoreCase + ); // Write transition entries foreach (var t in AllTransitions) @@ -135,10 +161,22 @@ private static List BuildConfigLines(Config config, bool isDefault) lines.Add("; KenBurns parameters (uncomment to change defaults):"); bool durationDefault = Math.Abs(config.KenBurnsDuration - 10.0) < 0.01; bool zoomDefault = Math.Abs(config.KenBurnsMaxZoom - 1.3) < 0.01; - lines.Add(string.Format(CI, "{0}KenBurnsDuration={1:F1}", - durationDefault ? "; " : "", config.KenBurnsDuration)); - lines.Add(string.Format(CI, "{0}KenBurnsMaxZoom={1:F1}", - zoomDefault ? "; " : "", config.KenBurnsMaxZoom)); + lines.Add( + string.Format( + CI, + "{0}KenBurnsDuration={1:F1}", + durationDefault ? "; " : "", + config.KenBurnsDuration + ) + ); + lines.Add( + string.Format( + CI, + "{0}KenBurnsMaxZoom={1:F1}", + zoomDefault ? "; " : "", + config.KenBurnsMaxZoom + ) + ); lines.Add(""); // --- Controls --- @@ -176,6 +214,9 @@ private static List BuildConfigLines(Config config, bool isDefault) lines.Add("; Display"); lines.Add("; I Toggle info overlay"); lines.Add("; D Show desktop (minimize other windows)"); + lines.Add( + "; T Toggle fill mode (Uniform / UniformToFill / Fill / None)" + ); lines.Add(";"); lines.Add("; Application"); lines.Add("; Escape Exit rSlide"); @@ -197,7 +238,8 @@ public static Config LoadConfig(string path) var config = new Config(); config.ActiveTransitions = new List(); // start empty, populate from file config.EnableKenBurns = false; // default off; set true if KenBurns line is found - if (!File.Exists(path)) return config; + if (!File.Exists(path)) + return config; var lines = File.ReadAllLines(path); string section = ""; @@ -205,7 +247,8 @@ public static Config LoadConfig(string path) foreach (var line in lines) { var t = line.Trim(); - if (string.IsNullOrWhiteSpace(t)) continue; + if (string.IsNullOrWhiteSpace(t)) + continue; if (t.StartsWith("[") && t.EndsWith("]")) { @@ -213,7 +256,8 @@ public static Config LoadConfig(string path) continue; } - if (t.StartsWith(";")) continue; + if (t.StartsWith(";")) + continue; // --- Paths: bare lines (no = sign) are folder paths --- if (section == "Paths") @@ -242,8 +286,12 @@ public static Config LoadConfig(string path) config.EnableKenBurns = true; config.ActiveTransitions.Add(KenBurnsKey); } - else if (Array.Exists(AllTransitions, - x => x.Equals(name, StringComparison.OrdinalIgnoreCase))) + else if ( + Array.Exists( + AllTransitions, + x => x.Equals(name, StringComparison.OrdinalIgnoreCase) + ) + ) { config.ActiveTransitions.Add(name); } @@ -265,25 +313,35 @@ public static Config LoadConfig(string path) // --- Key=Value sections --- { var eq = t.IndexOf('='); - if (eq < 0) continue; + if (eq < 0) + continue; var key = t.Substring(0, eq).Trim(); var val = t.Substring(eq + 1).Trim(); switch (section) { case "Display": - if (key == "DisplayDuration") config.DisplayDuration = PD(val, 8.0); - else if (key == "TransitionDurationPercent") config.TransitionDurationPercent = PD(val, 20.0); + if (key == "DisplayDuration") + config.DisplayDuration = PD(val, 8.0); + else if (key == "TransitionDurationPercent") + config.TransitionDurationPercent = PD(val, 20.0); + else if (key == "StretchMode") + config.StretchMode = val; break; case "Playback": - if (key == "PlaybackMode") config.PlaybackMode = val; - else if (key == "LastImageIndex") config.LastImageIndex = PI(val, 0); + if (key == "PlaybackMode") + config.PlaybackMode = val; + else if (key == "LastImageIndex") + config.LastImageIndex = PI(val, 0); break; // Legacy [KenBurns] section support case "KenBurns": - if (key == "EnableKenBurns") config.EnableKenBurns = PB(val, true); - else if (key == "Duration") config.KenBurnsDuration = PD(val, 10.0); - else if (key == "MaxZoom") config.KenBurnsMaxZoom = PD(val, 1.3); + if (key == "EnableKenBurns") + config.EnableKenBurns = PB(val, true); + else if (key == "Duration") + config.KenBurnsDuration = PD(val, 10.0); + else if (key == "MaxZoom") + config.KenBurnsMaxZoom = PD(val, 1.3); break; } } @@ -297,11 +355,22 @@ public static void SaveConfig(string path, Config config) File.WriteAllLines(path, BuildConfigLines(config, false)); } - private static double PD(string v, double d) { - double r; return double.TryParse(v, NumberStyles.Float, CI, out r) ? r : d; } - private static int PI(string v, int d) { - int r; return int.TryParse(v, NumberStyles.Integer, CI, out r) ? r : d; } - private static bool PB(string v, bool d) { - bool r; return bool.TryParse(v, out r) ? r : d; } + private static double PD(string v, double d) + { + double r; + return double.TryParse(v, NumberStyles.Float, CI, out r) ? r : d; + } + + private static int PI(string v, int d) + { + int r; + return int.TryParse(v, NumberStyles.Integer, CI, out r) ? r : d; + } + + private static bool PB(string v, bool d) + { + bool r; + return bool.TryParse(v, out r) ? r : d; + } } } diff --git a/rSlide/ImageManager.cs b/rSlide/ImageManager.cs index a0077ea..63c606d 100644 --- a/rSlide/ImageManager.cs +++ b/rSlide/ImageManager.cs @@ -5,9 +5,9 @@ using System.Linq; using System.Text; using System.Threading; +using System.Threading.Tasks; using System.Windows.Media; using System.Windows.Media.Imaging; -using System.Threading.Tasks; namespace rSlide { @@ -38,15 +38,39 @@ public class ImageManager : IDisposable private string cachedFilePath; // All formats WIC can decode natively on Windows 10/11 - private static readonly HashSet SupportedImageExtensions = - new HashSet(StringComparer.OrdinalIgnoreCase) - { ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tif", ".tiff", - ".wdp", ".jxr", ".ico", ".webp", ".heic", ".heif", ".avif" }; + private static readonly HashSet SupportedImageExtensions = new HashSet( + StringComparer.OrdinalIgnoreCase + ) + { + ".jpg", + ".jpeg", + ".png", + ".bmp", + ".gif", + ".tif", + ".tiff", + ".wdp", + ".jxr", + ".ico", + ".webp", + ".heic", + ".heif", + ".avif" + }; // Video formats supported by Windows Media Foundation (no extra codecs) - private static readonly HashSet SupportedVideoExtensions = - new HashSet(StringComparer.OrdinalIgnoreCase) - { ".mp4", ".wmv", ".avi", ".mov", ".m4v", ".mkv", ".webm" }; + private static readonly HashSet SupportedVideoExtensions = new HashSet( + StringComparer.OrdinalIgnoreCase + ) + { + ".mp4", + ".wmv", + ".avi", + ".mov", + ".m4v", + ".mkv", + ".webm" + }; // Combined set for directory scanning private static readonly HashSet SupportedExtensions; @@ -54,7 +78,9 @@ public class ImageManager : IDisposable static ImageManager() { SupportedExtensions = new HashSet( - SupportedImageExtensions, StringComparer.OrdinalIgnoreCase); + SupportedImageExtensions, + StringComparer.OrdinalIgnoreCase + ); foreach (var ext in SupportedVideoExtensions) SupportedExtensions.Add(ext); } @@ -62,7 +88,8 @@ static ImageManager() /// Returns true if the given file path is a video format. public static bool IsVideo(string path) { - if (string.IsNullOrEmpty(path)) return false; + if (string.IsNullOrEmpty(path)) + return false; return SupportedVideoExtensions.Contains(System.IO.Path.GetExtension(path)); } @@ -73,9 +100,18 @@ public bool IsCurrentVideo() return IsVideo(path); } - public int TotalImages { get { return playbackOrder != null ? playbackOrder.Count : 0; } } - public int CurrentIndex { get { return currentIndex; } } - public string CurrentMode { get { return currentMode; } } + public int TotalImages + { + get { return playbackOrder != null ? playbackOrder.Count : 0; } + } + public int CurrentIndex + { + get { return currentIndex; } + } + public string CurrentMode + { + get { return currentMode; } + } /// /// Synchronous constructor — does NOT scan files. Call InitAsync() @@ -113,7 +149,13 @@ public Task InitAsync(List imagePaths, Action progressCallback try { - foreach (var f in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)) + foreach ( + var f in Directory.EnumerateFiles( + path, + "*.*", + SearchOption.AllDirectories + ) + ) { if (SupportedExtensions.Contains(Path.GetExtension(f))) { @@ -128,7 +170,8 @@ public Task InitAsync(List imagePaths, Action progressCallback catch (Exception ex) { System.Diagnostics.Debug.WriteLine( - string.Format("Error scanning {0}: {1}", path, ex.Message)); + string.Format("Error scanning {0}: {1}", path, ex.Message) + ); } } @@ -158,7 +201,10 @@ private List BuildPlaybackOrder(List source, string mode) for (int i = 0; i < source.Count; i++) { long ticks = 0; - try { ticks = File.GetLastWriteTimeUtc(source[i]).Ticks; } + try + { + ticks = File.GetLastWriteTimeUtc(source[i]).Ticks; + } catch { } withDates[i] = new KeyValuePair(source[i], ticks); } @@ -175,7 +221,10 @@ private List BuildPlaybackOrder(List source, string mode) for (int i = 0; i < source.Count; i++) { long ticks = 0; - try { ticks = File.GetLastWriteTimeUtc(source[i]).Ticks; } + try + { + ticks = File.GetLastWriteTimeUtc(source[i]).Ticks; + } catch { } withDates[i] = new KeyValuePair(source[i], ticks); } @@ -188,9 +237,7 @@ private List BuildPlaybackOrder(List source, string mode) case "Sequential": default: - return source - .OrderBy(f => f, StringComparer.OrdinalIgnoreCase) - .ToList(); + return source.OrderBy(f => f, StringComparer.OrdinalIgnoreCase).ToList(); } } @@ -207,13 +254,15 @@ private void ShuffleInPlace(List list) public string GetCurrentImagePath() { - if (playbackOrder.Count == 0) return null; + if (playbackOrder.Count == 0) + return null; return playbackOrder[currentIndex]; } public BitmapSource GetCurrentImage() { - if (playbackOrder.Count == 0) return null; + if (playbackOrder.Count == 0) + return null; BitmapSource cached; if (imageCache.TryGetValue(currentIndex, out cached)) @@ -231,7 +280,8 @@ public BitmapSource GetCurrentImage() public void Next() { - if (playbackOrder.Count == 0) return; + if (playbackOrder.Count == 0) + return; currentIndex = (currentIndex + 1) % playbackOrder.Count; @@ -245,7 +295,8 @@ public void Next() public void Previous() { - if (playbackOrder.Count == 0) return; + if (playbackOrder.Count == 0) + return; currentIndex--; if (currentIndex < 0) @@ -258,7 +309,8 @@ public void Previous() public void JumpTo(int index) { - if (playbackOrder.Count == 0) return; + if (playbackOrder.Count == 0) + return; currentIndex = Math.Max(0, Math.Min(index, playbackOrder.Count - 1)); CacheFileInfo(); @@ -268,7 +320,8 @@ public void JumpTo(int index) public void Skip(int count) { - if (playbackOrder.Count == 0) return; + if (playbackOrder.Count == 0) + return; currentIndex = (currentIndex + count) % playbackOrder.Count; if (currentIndex < 0) @@ -281,7 +334,8 @@ public void Skip(int count) public void RemoveCurrent() { - if (playbackOrder.Count == 0) return; + if (playbackOrder.Count == 0) + return; var path = playbackOrder[currentIndex]; @@ -305,7 +359,8 @@ public void RemoveCurrent() public void SetMode(string mode) { - if (currentMode == mode) return; + if (currentMode == mode) + return; var currentPath = GetCurrentImagePath(); currentMode = mode; @@ -324,7 +379,8 @@ public void SetMode(string mode) private void PreloadImages() { - if (playbackOrder.Count == 0) return; + if (playbackOrder.Count == 0) + return; // Cancel any in-flight preload CancellationTokenSource newCts; @@ -344,34 +400,39 @@ private void PreloadImages() var order = playbackOrder; var token = newCts.Token; - Task.Run(() => - { - for (int i = 0; i <= PRELOAD_COUNT; i++) + Task.Run( + () => { - if (token.IsCancellationRequested) return; + for (int i = 0; i <= PRELOAD_COUNT; i++) + { + if (token.IsCancellationRequested) + return; - int idx = (baseIndex + i) % count; - if (imageCache.ContainsKey(idx)) - continue; + int idx = (baseIndex + i) % count; + if (imageCache.ContainsKey(idx)) + continue; - try - { - if (idx < order.Count) + try { - var image = LoadImage(order[idx]); - if (image != null && !token.IsCancellationRequested) - imageCache.TryAdd(idx, image); + if (idx < order.Count) + { + var image = LoadImage(order[idx]); + if (image != null && !token.IsCancellationRequested) + imageCache.TryAdd(idx, image); + } } + catch { } } - catch { } - } - }, token); + }, + token + ); } private void CleanupCache() { int count = playbackOrder.Count; - if (count == 0) return; + if (count == 0) + return; foreach (var key in imageCache.Keys) { @@ -400,12 +461,22 @@ private BitmapSource LoadImage(string path) { try { - using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) + using ( + var stream = new FileStream( + path, + FileMode.Open, + FileAccess.Read, + FileShare.Read + ) + ) { - var decoder = BitmapDecoder.Create(stream, + var decoder = BitmapDecoder.Create( + stream, BitmapCreateOptions.IgnoreColorProfile, - BitmapCacheOption.OnLoad); - if (decoder.Frames.Count == 0) return null; + BitmapCacheOption.OnLoad + ); + if (decoder.Frames.Count == 0) + return null; var frame = decoder.Frames[0]; @@ -413,8 +484,7 @@ private BitmapSource LoadImage(string path) if (frame.PixelWidth > decodePixelWidth) { double scale = (double)decodePixelWidth / frame.PixelWidth; - source = new TransformedBitmap(frame, - new ScaleTransform(scale, scale)); + source = new TransformedBitmap(frame, new ScaleTransform(scale, scale)); } else { @@ -466,24 +536,35 @@ private static BitmapSource ApplyOrientation(BitmapSource source, ushort orienta Transform transform; switch (orientation) { - case 2: transform = new ScaleTransform(-1, 1); break; - case 3: transform = new RotateTransform(180); break; - case 4: transform = new ScaleTransform(1, -1); break; + case 2: + transform = new ScaleTransform(-1, 1); + break; + case 3: + transform = new RotateTransform(180); + break; + case 4: + transform = new ScaleTransform(1, -1); + break; case 5: var tg5 = new TransformGroup(); tg5.Children.Add(new ScaleTransform(-1, 1)); tg5.Children.Add(new RotateTransform(270)); transform = tg5; break; - case 6: transform = new RotateTransform(90); break; + case 6: + transform = new RotateTransform(90); + break; case 7: var tg7 = new TransformGroup(); tg7.Children.Add(new ScaleTransform(-1, 1)); tg7.Children.Add(new RotateTransform(90)); transform = tg7; break; - case 8: transform = new RotateTransform(270); break; - default: return source; + case 8: + transform = new RotateTransform(270); + break; + default: + return source; } var transformed = new TransformedBitmap(source, transform); @@ -501,7 +582,8 @@ private void CacheFileInfo() cachedFileDate = DateTime.MinValue; cachedFilePath = null; - if (playbackOrder.Count == 0) return; + if (playbackOrder.Count == 0) + return; var path = playbackOrder[currentIndex]; cachedFilePath = path; diff --git a/rSlide/MainWindow.xaml b/rSlide/MainWindow.xaml index ddb3b27..a18182f 100644 --- a/rSlide/MainWindow.xaml +++ b/rSlide/MainWindow.xaml @@ -13,7 +13,7 @@ UpdateInfo(); @@ -92,10 +114,12 @@ private async void MainWindow_Loaded(object sender, RoutedEventArgs e) int imageCount = await playbackController.InitAsync(count => { // Progress callback from background thread — marshal to UI - Dispatcher.BeginInvoke(new Action(() => - { - LoadingText.Text = string.Format("Scanning... {0} images", count); - })); + Dispatcher.BeginInvoke( + new Action(() => + { + LoadingText.Text = string.Format("Scanning... {0} images", count); + }) + ); }); LoadingText.Visibility = Visibility.Collapsed; @@ -103,14 +127,15 @@ private async void MainWindow_Loaded(object sender, RoutedEventArgs e) if (imageCount == 0) { MessageBox.Show( - "No images found in the configured paths.\n\n" + - "Please check that:\n" + - "1. Paths in rSlide.ini are correct\n" + - "2. Paths contain supported image files\n" + - "3. You have permission to access the folders", + "No images found in the configured paths.\n\n" + + "Please check that:\n" + + "1. Paths in rSlide.ini are correct\n" + + "2. Paths contain supported image files\n" + + "3. You have permission to access the folders", "rSlide - No Images", MessageBoxButton.OK, - MessageBoxImage.Warning); + MessageBoxImage.Warning + ); Close(); return; } @@ -130,7 +155,8 @@ private async void MainWindow_Loaded(object sender, RoutedEventArgs e) "Error initializing slideshow:\n\n" + ex.Message, "rSlide - Error", MessageBoxButton.OK, - MessageBoxImage.Error); + MessageBoxImage.Error + ); Close(); } } @@ -143,15 +169,24 @@ private void MinimizeOtherWindows() if (shellType != null) { var shell = Activator.CreateInstance(shellType); - shellType.InvokeMember("MinimizeAll", - System.Reflection.BindingFlags.InvokeMethod, null, shell, null); + shellType.InvokeMember( + "MinimizeAll", + System.Reflection.BindingFlags.InvokeMethod, + null, + shell, + null + ); } } catch { } var t = new DispatcherTimer(); t.Interval = TimeSpan.FromMilliseconds(600); - t.Tick += (s, ev) => { t.Stop(); SendToBottom(); }; + t.Tick += (s, ev) => + { + t.Stop(); + SendToBottom(); + }; t.Start(); } @@ -162,30 +197,44 @@ private void SetupTrayIcon() trayIcon.Icon = CreateTrayIcon(); var menu = new System.Windows.Forms.ContextMenuStrip(); - menu.Items.Add("Pause / Resume", null, (s, ev) => - { - if (playbackController != null) + menu.Items.Add( + "Pause / Resume", + null, + (s, ev) => { - playbackController.TogglePause(); - Dispatcher.Invoke(() => UpdatePausedOverlay()); + if (playbackController != null) + { + playbackController.TogglePause(); + Dispatcher.Invoke(() => UpdatePausedOverlay()); + } } - }); - menu.Items.Add("Next Image", null, (s, ev) => - { - if (playbackController != null) - Dispatcher.Invoke(() => playbackController.NextImage()); - }); - menu.Items.Add("Previous Image", null, (s, ev) => - { - if (playbackController != null) - Dispatcher.Invoke(() => playbackController.PreviousImage()); - }); + ); + menu.Items.Add( + "Next Image", + null, + (s, ev) => + { + if (playbackController != null) + Dispatcher.Invoke(() => playbackController.NextImage()); + } + ); + menu.Items.Add( + "Previous Image", + null, + (s, ev) => + { + if (playbackController != null) + Dispatcher.Invoke(() => playbackController.PreviousImage()); + } + ); menu.Items.Add("-"); - menu.Items.Add("Show Desktop", null, (s, ev) => - Dispatcher.Invoke(() => MinimizeOtherWindows())); + menu.Items.Add( + "Show Desktop", + null, + (s, ev) => Dispatcher.Invoke(() => MinimizeOtherWindows()) + ); menu.Items.Add("-"); - menu.Items.Add("Exit", null, (s, ev) => - Dispatcher.Invoke(() => Close())); + menu.Items.Add("Exit", null, (s, ev) => Dispatcher.Invoke(() => Close())); trayIcon.ContextMenuStrip = menu; trayIcon.Visible = true; @@ -201,10 +250,15 @@ private static System.Drawing.Icon CreateTrayIcon() using (var b = new SolidBrush(Color.FromArgb(100, 180, 255))) g.FillRectangle(b, 2, 2, 12, 12); using (var b = new SolidBrush(Color.FromArgb(50, 120, 50))) - g.FillPolygon(b, new[] { - new System.Drawing.Point(2, 14), - new System.Drawing.Point(8, 6), - new System.Drawing.Point(14, 14) }); + g.FillPolygon( + b, + new[] + { + new System.Drawing.Point(2, 14), + new System.Drawing.Point(8, 6), + new System.Drawing.Point(14, 14) + } + ); using (var b = new SolidBrush(Color.FromArgb(255, 220, 60))) g.FillEllipse(b, 9, 3, 4, 4); } @@ -220,19 +274,33 @@ private void SendToBottom() { var hwnd = new System.Windows.Interop.WindowInteropHelper(this).Handle; if (hwnd != IntPtr.Zero) - SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + SetWindowPos( + hwnd, + HWND_BOTTOM, + 0, + 0, + 0, + 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE + ); } private void Window_KeyDown(object sender, KeyEventArgs e) { - if (playbackController == null) return; + if (playbackController == null) + return; bool ctrl = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control; switch (e.Key) { - case Key.Escape: Close(); break; - case Key.Space: playbackController.TogglePause(); UpdatePausedOverlay(); break; + case Key.Escape: + Close(); + break; + case Key.Space: + playbackController.TogglePause(); + UpdatePausedOverlay(); + break; case Key.Left: if (ctrl) playbackController.SeekVideo(-5); @@ -245,19 +313,55 @@ private void Window_KeyDown(object sender, KeyEventArgs e) else playbackController.NextImage(); break; - case Key.Up: playbackController.IncreaseDisplayDuration(); UpdateInfo(); break; - case Key.Down: playbackController.DecreaseDisplayDuration(); UpdateInfo(); break; - case Key.Home: playbackController.FirstImage(); break; - case Key.End: playbackController.LastImage(); break; - case Key.PageUp: playbackController.SkipForward(10); break; - case Key.PageDown: playbackController.SkipBackward(10); break; - case Key.R: playbackController.SetPlaybackMode("Random"); UpdateInfo(); break; - case Key.N: playbackController.SetPlaybackMode("NewestFirst"); UpdateInfo(); break; - case Key.S: playbackController.SetPlaybackMode("Sequential"); UpdateInfo(); break; - case Key.K: playbackController.ToggleKenBurns(); UpdateInfo(); break; - case Key.I: ToggleInfo(); break; - case Key.D: MinimizeOtherWindows(); break; - case Key.O: OpenImageInExplorer(); break; + case Key.Up: + playbackController.IncreaseDisplayDuration(); + UpdateInfo(); + break; + case Key.Down: + playbackController.DecreaseDisplayDuration(); + UpdateInfo(); + break; + case Key.Home: + playbackController.FirstImage(); + break; + case Key.End: + playbackController.LastImage(); + break; + case Key.PageUp: + playbackController.SkipForward(10); + break; + case Key.PageDown: + playbackController.SkipBackward(10); + break; + case Key.R: + playbackController.SetPlaybackMode("Random"); + UpdateInfo(); + break; + case Key.N: + playbackController.SetPlaybackMode("NewestFirst"); + UpdateInfo(); + break; + case Key.S: + playbackController.SetPlaybackMode("Sequential"); + UpdateInfo(); + break; + case Key.K: + playbackController.ToggleKenBurns(); + UpdateInfo(); + break; + case Key.I: + ToggleInfo(); + break; + case Key.D: + MinimizeOtherWindows(); + break; + case Key.O: + OpenImageInExplorer(); + break; + case Key.T: + var mode = playbackController.CycleStretchMode(); + UpdateInfo(); + break; case Key.Delete: if (ctrl) @@ -271,7 +375,8 @@ private void Window_KeyDown(object sender, KeyEventArgs e) private void DeleteImage(bool confirm) { var path = playbackController.GetCurrentImagePath(); - if (path == null) return; + if (path == null) + return; if (confirm) { @@ -279,7 +384,8 @@ private void DeleteImage(bool confirm) "Delete this image?\n\n" + path, "rSlide - Delete Image", MessageBoxButton.YesNo, - MessageBoxImage.Warning); + MessageBoxImage.Warning + ); if (result != MessageBoxResult.Yes) return; @@ -293,7 +399,8 @@ private void DeleteImage(bool confirm) private void OpenImageInExplorer() { var path = playbackController.GetCurrentImagePath(); - if (path == null || !System.IO.File.Exists(path)) return; + if (path == null || !System.IO.File.Exists(path)) + return; try { @@ -310,7 +417,10 @@ private void OpenImageInExplorer() catch { // Fallback - try { System.Diagnostics.Process.Start("explorer.exe", "/select,\"" + path + "\""); } + try + { + System.Diagnostics.Process.Start("explorer.exe", "/select,\"" + path + "\""); + } catch { } } } @@ -318,14 +428,16 @@ private void OpenImageInExplorer() private void UpdatePausedOverlay() { PausedOverlay.Visibility = playbackController.IsPaused - ? Visibility.Visible : Visibility.Collapsed; + ? Visibility.Visible + : Visibility.Collapsed; } private void ToggleInfo() { infoVisible = !infoVisible; InfoOverlay.Visibility = infoVisible ? Visibility.Visible : Visibility.Collapsed; - if (infoVisible) UpdateInfo(); + if (infoVisible) + UpdateInfo(); } private void UpdateInfo() @@ -343,9 +455,15 @@ private void MainWindow_Closing(object sender, System.ComponentModel.CancelEvent protected override void OnClosed(EventArgs e) { base.OnClosed(e); - if (bottomTimer != null) bottomTimer.Stop(); - if (trayIcon != null) { trayIcon.Visible = false; trayIcon.Dispose(); } - if (playbackController != null) playbackController.Dispose(); + if (bottomTimer != null) + bottomTimer.Stop(); + if (trayIcon != null) + { + trayIcon.Visible = false; + trayIcon.Dispose(); + } + if (playbackController != null) + playbackController.Dispose(); } } } diff --git a/rSlide/PlaybackController.cs b/rSlide/PlaybackController.cs index b739926..1d79888 100644 --- a/rSlide/PlaybackController.cs +++ b/rSlide/PlaybackController.cs @@ -26,45 +26,74 @@ public class PlaybackController : IDisposable private bool firstImage = true; private bool videoPlaying; - public bool IsPaused { get { return isPaused; } } - public bool IsVideoPlaying { get { return videoPlaying; } } - public int ImageCount { get { return imageManager != null ? imageManager.TotalImages : 0; } } + public bool IsPaused + { + get { return isPaused; } + } + public bool IsVideoPlaying + { + get { return videoPlaying; } + } + public int ImageCount + { + get { return imageManager != null ? imageManager.TotalImages : 0; } + } /// Fires whenever the displayed image changes. public event EventHandler ImageChanged; + private readonly Image image1; + private readonly Image image2; + private static readonly Stretch[] StretchModes = + { + Stretch.Uniform, + Stretch.UniformToFill, + Stretch.Fill, + Stretch.None + }; public PlaybackController( - Image image1, Image image2, - ScaleTransform scale1, ScaleTransform scale2, - TranslateTransform translate1, TranslateTransform translate2, + Image img1, + Image img2, + ScaleTransform scale1, + ScaleTransform scale2, + TranslateTransform translate1, + TranslateTransform translate2, MediaElement video, - Config cfg) + Config cfg + ) { config = cfg; + image1 = img1; + image2 = img2; displayDuration = config.DisplayDuration; transitionDurationPercent = config.TransitionDurationPercent; videoPlayer = video; - configPath = Path.Combine( - AppDomain.CurrentDomain.BaseDirectory, - "rSlide.ini"); + configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "rSlide.ini"); imageManager = new ImageManager( config.PlaybackMode, config.LastImageIndex, - config.KenBurnsMaxZoom); + config.KenBurnsMaxZoom + ); transitionEngine = new TransitionEngine( - image1, image2, - scale1, scale2, - translate1, translate2, - video) + img1, + img2, + scale1, + scale2, + translate1, + translate2, + video + ) { EnableKenBurns = config.EnableKenBurns, KenBurnsMaxZoom = config.KenBurnsMaxZoom, TransitionPool = ParseTransitionPool(config.ActiveTransitions) }; + ApplyStretchMode(ParseStretch(config.StretchMode)); + timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromSeconds(displayDuration); timer.Tick += Timer_Tick; @@ -118,7 +147,8 @@ private void ShowCurrentImage() if (isVideo) { var path = imageManager.GetCurrentImagePath(); - if (path == null) return; + if (path == null) + return; var uri = new Uri(path); videoPlaying = true; @@ -138,7 +168,8 @@ private void ShowCurrentImage() else { var image = imageManager.GetCurrentImage(); - if (image == null) return; + if (image == null) + return; videoPlaying = false; @@ -204,7 +235,8 @@ public void SkipBackward(int count) public string DeleteCurrentImage() { var path = imageManager.GetCurrentImagePath(); - if (path == null) return null; + if (path == null) + return null; try { @@ -251,13 +283,17 @@ public void TogglePause() /// public void SeekVideo(double seconds) { - if (!videoPlaying || videoPlayer == null) return; - if (!videoPlayer.NaturalDuration.HasTimeSpan) return; + if (!videoPlaying || videoPlayer == null) + return; + if (!videoPlayer.NaturalDuration.HasTimeSpan) + return; var pos = videoPlayer.Position + TimeSpan.FromSeconds(seconds); - if (pos < TimeSpan.Zero) pos = TimeSpan.Zero; + if (pos < TimeSpan.Zero) + pos = TimeSpan.Zero; var max = videoPlayer.NaturalDuration.TimeSpan; - if (pos > max) pos = max; + if (pos > max) + pos = max; videoPlayer.Position = pos; } @@ -301,6 +337,30 @@ public void ToggleKenBurns() config.EnableKenBurns = transitionEngine.EnableKenBurns; } + public string CycleStretchMode() + { + var current = image1.Stretch; + int next = (Array.IndexOf(StretchModes, current) + 1) % StretchModes.Length; + var mode = StretchModes[next]; + ApplyStretchMode(mode); + config.StretchMode = mode.ToString(); + return mode.ToString(); + } + + private void ApplyStretchMode(Stretch mode) + { + image1.Stretch = mode; + image2.Stretch = mode; + if (videoPlayer != null) + videoPlayer.Stretch = mode; + } + + private static Stretch ParseStretch(string val) + { + Stretch result; + return Enum.TryParse(val, true, out result) ? result : Stretch.Uniform; + } + public string GetInfoText() { var ci = CultureInfo.InvariantCulture; @@ -311,15 +371,22 @@ public string GetInfoText() // Playback status string status = isPaused ? "PAUSED" : (videoPlaying ? "VIDEO" : "PLAYING"); - sb.AppendFormat(ci, "\n\nStatus: {0} | Mode: {1}", - status, imageManager.CurrentMode); - sb.AppendFormat(ci, "\nDisplay: {0:F1}s | Transition: {1:F1}s", - displayDuration, displayDuration * transitionDurationPercent / 100.0); + sb.AppendFormat(ci, "\n\nStatus: {0} | Mode: {1}", status, imageManager.CurrentMode); + sb.AppendFormat(ci, "\nStretch: {0}", image1.Stretch); + sb.AppendFormat( + ci, + "\nDisplay: {0:F1}s | Transition: {1:F1}s", + displayDuration, + displayDuration * transitionDurationPercent / 100.0 + ); // Last transition used var last = transitionEngine.LastTransition; - sb.AppendFormat(ci, "\nLast transition: {0}", - last == TransitionType.None ? "None (instant)" : last.ToString()); + sb.AppendFormat( + ci, + "\nLast transition: {0}", + last == TransitionType.None ? "None (instant)" : last.ToString() + ); // Effects pool — transitions + KenBurns unified sb.Append("\nEffects: "); @@ -328,14 +395,16 @@ public string GetInfoText() { foreach (var t in transitionEngine.TransitionPool) { - if (any) sb.Append(", "); + if (any) + sb.Append(", "); sb.Append(t.ToString()); any = true; } } if (transitionEngine.EnableKenBurns) { - if (any) sb.Append(", "); + if (any) + sb.Append(", "); sb.Append("KenBurns"); any = true; } @@ -352,8 +421,9 @@ public void SaveState() // Serialize transition pool back to config if (transitionEngine.TransitionPool != null) { - config.ActiveTransitions = transitionEngine.TransitionPool - .Select(t => t.ToString()).ToList(); + config.ActiveTransitions = transitionEngine + .TransitionPool.Select(t => t.ToString()) + .ToList(); // Include KenBurns in the list if enabled if (transitionEngine.EnableKenBurns) config.ActiveTransitions.Add("KenBurns"); @@ -363,20 +433,24 @@ public void SaveState() public void Dispose() { - if (timer != null) timer.Stop(); - if (videoPlayer != null) videoPlayer.MediaEnded -= VideoPlayer_MediaEnded; + if (timer != null) + timer.Stop(); + if (videoPlayer != null) + videoPlayer.MediaEnded -= VideoPlayer_MediaEnded; if (transitionEngine != null) { transitionEngine.StopAllAnimations(); transitionEngine.StopVideo(); } - if (imageManager != null) imageManager.Dispose(); + if (imageManager != null) + imageManager.Dispose(); } private static List ParseTransitionPool(List names) { var pool = new List(); - if (names == null) return pool; + if (names == null) + return pool; foreach (var name in names) { // KenBurns is an effect toggle, not a transition type diff --git a/rSlide/TransitionEngine.cs b/rSlide/TransitionEngine.cs index 117f204..6a75fec 100644 --- a/rSlide/TransitionEngine.cs +++ b/rSlide/TransitionEngine.cs @@ -23,8 +23,10 @@ public class TransitionEngine { private readonly Image image1; private readonly Image image2; - private readonly ScaleTransform scale1, scale2; - private readonly TranslateTransform translate1, translate2; + private readonly ScaleTransform scale1, + scale2; + private readonly TranslateTransform translate1, + translate2; private readonly MediaElement videoPlayer; private bool image1IsActive = true; @@ -35,10 +37,14 @@ public class TransitionEngine // Tracks whether a transition is in progress so we can cancel private bool transitionInProgress; + // Store refs for the completion callback - private Image pendingInImg, pendingOutImg; - private ScaleTransform pendingInSc, pendingOutSc; - private TranslateTransform pendingInTr, pendingOutTr; + private Image pendingInImg, + pendingOutImg; + private ScaleTransform pendingInSc, + pendingOutSc; + private TranslateTransform pendingInTr, + pendingOutTr; private double pendingDisplayDur; // Single reusable completion timer — never allocate a new one @@ -60,11 +66,14 @@ public class TransitionEngine static TransitionEngine() { - var q = new QuadraticEase { EasingMode = EasingMode.EaseInOut }; q.Freeze(); + var q = new QuadraticEase { EasingMode = EasingMode.EaseInOut }; + q.Freeze(); EaseInOut = q; - var c = new CubicEase { EasingMode = EasingMode.EaseInOut }; c.Freeze(); + var c = new CubicEase { EasingMode = EasingMode.EaseInOut }; + c.Freeze(); CubicInOut = c; - var p = new PowerEase { EasingMode = EasingMode.EaseInOut, Power = 1.5 }; p.Freeze(); + var p = new PowerEase { EasingMode = EasingMode.EaseInOut, Power = 1.5 }; + p.Freeze(); GentleEase = p; var h = new DoubleAnimation(1, TimeSpan.Zero); @@ -74,10 +83,14 @@ static TransitionEngine() } public TransitionEngine( - Image img1, Image img2, - ScaleTransform s1, ScaleTransform s2, - TranslateTransform t1, TranslateTransform t2, - MediaElement video) + Image img1, + Image img2, + ScaleTransform s1, + ScaleTransform s2, + TranslateTransform t1, + TranslateTransform t2, + MediaElement video + ) { image1 = img1; image2 = img2; @@ -89,7 +102,8 @@ public TransitionEngine( EnableKenBurns = true; KenBurnsMaxZoom = 1.3; - TransitionPool = new List { + TransitionPool = new List + { TransitionType.Crossfade, TransitionType.MorphZoom, TransitionType.SoftWipe, @@ -109,11 +123,15 @@ private void CompletionTimer_Tick(object sender, EventArgs e) } /// Whether the video layer is currently the active display. - public bool VideoIsActive { get { return videoIsActive; } } + public bool VideoIsActive + { + get { return videoIsActive; } + } public void ShowFirstImage(BitmapSource bitmap, double displayDuration) { - if (bitmap == null) return; + if (bitmap == null) + return; CancelTransition(); ClearAll(); @@ -143,8 +161,10 @@ public void ShowFirstVideo(Uri videoUri) LastTransition = TransitionType.None; // Hide both image layers - image1.Opacity = 0; image1.Source = null; - image2.Opacity = 0; image2.Source = null; + image1.Opacity = 0; + image1.Source = null; + image2.Opacity = 0; + image2.Source = null; // Show and play video videoPlayer.BeginAnimation(UIElement.OpacityProperty, null); @@ -169,8 +189,10 @@ public void TransitionToVideo(Uri videoUri, double transitionDur) { // Instant cut ClearAll(); - image1.Opacity = 0; image1.Source = null; - image2.Opacity = 0; image2.Source = null; + image1.Opacity = 0; + image1.Source = null; + image2.Opacity = 0; + image2.Source = null; videoPlayer.Opacity = 1; videoIsActive = true; LastTransition = TransitionType.None; @@ -191,8 +213,12 @@ public void TransitionToVideo(Uri videoUri, double transitionDur) // Use the completion timer to clean up image layers once the fade completes transitionInProgress = true; - pendingInImg = null; pendingInSc = null; pendingInTr = null; - pendingOutImg = outImg; pendingOutSc = ActiveScale(); pendingOutTr = ActiveTranslate(); + pendingInImg = null; + pendingInSc = null; + pendingInTr = null; + pendingOutImg = outImg; + pendingOutSc = ActiveScale(); + pendingOutTr = ActiveTranslate(); pendingDisplayDur = 0; completionTimer.Interval = TimeSpan.FromSeconds(transitionDur + 0.02); @@ -205,7 +231,8 @@ public void TransitionToVideo(Uri videoUri, double transitionDur) /// public void HideVideoImmediate() { - if (videoPlayer == null) return; + if (videoPlayer == null) + return; videoPlayer.BeginAnimation(UIElement.OpacityProperty, null); videoPlayer.Stop(); videoPlayer.Source = null; @@ -216,14 +243,20 @@ public void HideVideoImmediate() /// Stop video playback without changing visibility (for Dispose). public void StopVideo() { - if (videoPlayer == null) return; + if (videoPlayer == null) + return; videoPlayer.Stop(); videoPlayer.Source = null; } - public void TransitionToImage(BitmapSource newImage, double transitionDur, double displayDuration) + public void TransitionToImage( + BitmapSource newImage, + double transitionDur, + double displayDuration + ) { - if (newImage == null) return; + if (newImage == null) + return; CancelTransition(); @@ -242,8 +275,10 @@ public void TransitionToImage(BitmapSource newImage, double transitionDur, doubl // Prepare incoming (invisible, safe to hard-reset) ClearTransformAnims(inSc, inTr); inImg.BeginAnimation(UIElement.OpacityProperty, null); - inSc.ScaleX = 1; inSc.ScaleY = 1; - inTr.X = 0; inTr.Y = 0; + inSc.ScaleX = 1; + inSc.ScaleY = 1; + inTr.X = 0; + inTr.Y = 0; inImg.Opacity = 0; inImg.Source = newImage; @@ -253,11 +288,16 @@ public void TransitionToImage(BitmapSource newImage, double transitionDur, doubl if (type == TransitionType.None || transitionDur < 0.05) { ClearAll(); - outImg.Opacity = 0; outImg.Source = null; - outSc.ScaleX = 1; outSc.ScaleY = 1; outTr.X = 0; outTr.Y = 0; + outImg.Opacity = 0; + outImg.Source = null; + outSc.ScaleX = 1; + outSc.ScaleY = 1; + outTr.X = 0; + outTr.Y = 0; inImg.Opacity = 1; image1IsActive = !image1IsActive; - if (EnableKenBurns) StartKenBurns(inSc, inTr, displayDuration); + if (EnableKenBurns) + StartKenBurns(inSc, inTr, displayDuration); return; } @@ -285,15 +325,33 @@ public void TransitionToImage(BitmapSource newImage, double transitionDur, doubl case TransitionType.ParallaxReveal: double drift = 60; bool goRight = random.Next(2) == 0; - RunUI(inImg, UIElement.OpacityProperty, 0, 1, - TimeSpan.FromSeconds(transitionDur * 0.4), null); - Run(inTr, TranslateTransform.XProperty, - goRight ? -drift : drift, 0, dur, GentleEase); + RunUI( + inImg, + UIElement.OpacityProperty, + 0, + 1, + TimeSpan.FromSeconds(transitionDur * 0.4), + null + ); + Run( + inTr, + TranslateTransform.XProperty, + goRight ? -drift : drift, + 0, + dur, + GentleEase + ); FadeOut(outImg, dur, CubicInOut); Run(outSc, ScaleTransform.ScaleXProperty, outSc.ScaleX, 1.1, dur, CubicInOut); Run(outSc, ScaleTransform.ScaleYProperty, outSc.ScaleY, 1.1, dur, CubicInOut); - Run(outTr, TranslateTransform.XProperty, outTr.X, - goRight ? 120 : -120, dur, CubicInOut); + Run( + outTr, + TranslateTransform.XProperty, + outTr.X, + goRight ? 120 : -120, + dur, + CubicInOut + ); break; case TransitionType.ScaleDissolve: @@ -311,8 +369,12 @@ public void TransitionToImage(BitmapSource newImage, double transitionDur, doubl // Store state for the completion callback transitionInProgress = true; - pendingInImg = inImg; pendingInSc = inSc; pendingInTr = inTr; - pendingOutImg = outImg; pendingOutSc = outSc; pendingOutTr = outTr; + pendingInImg = inImg; + pendingInSc = inSc; + pendingInTr = inTr; + pendingOutImg = outImg; + pendingOutSc = outSc; + pendingOutTr = outTr; pendingDisplayDur = displayDuration; // Reuse the single timer @@ -322,7 +384,8 @@ public void TransitionToImage(BitmapSource newImage, double transitionDur, doubl private void OnTransitionCompleted() { - if (!transitionInProgress) return; + if (!transitionInProgress) + return; transitionInProgress = false; // Video-transition completion: just clean up the outgoing image layer @@ -334,8 +397,10 @@ private void OnTransitionCompleted() ClearTransformAnims(pendingOutSc, pendingOutTr); pendingOutImg.Opacity = 0; pendingOutImg.Source = null; - pendingOutSc.ScaleX = 1; pendingOutSc.ScaleY = 1; - pendingOutTr.X = 0; pendingOutTr.Y = 0; + pendingOutSc.ScaleX = 1; + pendingOutSc.ScaleY = 1; + pendingOutTr.X = 0; + pendingOutTr.Y = 0; } return; } @@ -353,8 +418,10 @@ private void OnTransitionCompleted() ClearTransformAnims(capOutSc, capOutTr); capOutImg.Opacity = 0; capOutImg.Source = null; - capOutSc.ScaleX = 1; capOutSc.ScaleY = 1; - capOutTr.X = 0; capOutTr.Y = 0; + capOutSc.ScaleX = 1; + capOutSc.ScaleY = 1; + capOutTr.X = 0; + capOutTr.Y = 0; // Incoming is fully visible — set base then hold capInImg.Opacity = 1; @@ -369,8 +436,10 @@ private void OnTransitionCompleted() else { ClearTransformAnims(capInSc, capInTr); - capInSc.ScaleX = 1; capInSc.ScaleY = 1; - capInTr.X = 0; capInTr.Y = 0; + capInSc.ScaleX = 1; + capInSc.ScaleY = 1; + capInTr.X = 0; + capInTr.Y = 0; } } @@ -378,7 +447,11 @@ private void OnTransitionCompleted() // KEN BURNS // --------------------------------------------------------------- - private void StartKenBurns(ScaleTransform scale, TranslateTransform translate, double duration) + private void StartKenBurns( + ScaleTransform scale, + TranslateTransform translate, + double duration + ) { double dur = Math.Max(duration, 2.0); var ts = TimeSpan.FromSeconds(dur); @@ -391,14 +464,26 @@ private void StartKenBurns(ScaleTransform scale, TranslateTransform translate, d bool zoomIn = random.Next(2) == 0; double s1 = zoomIn ? KenBurnsMaxZoom : 1.0; - double maxPan = 40; - double x1 = 0, y1 = 0; + double extraZoom = (s1 - 1.0); + double maxPan = + (extraZoom > 0) ? SystemParameters.PrimaryScreenWidth * extraZoom * 0.3 : 0; + + double x1 = 0, + y1 = 0; switch (random.Next(5)) { - case 0: x1 = -maxPan; break; - case 1: x1 = maxPan; break; - case 2: y1 = -maxPan; break; - case 3: y1 = maxPan; break; + case 0: + x1 = -maxPan; + break; + case 1: + x1 = maxPan; + break; + case 2: + y1 = -maxPan; + break; + case 3: + y1 = maxPan; + break; } Run(scale, ScaleTransform.ScaleXProperty, curScale, s1, ts, GentleEase); @@ -438,8 +523,14 @@ public void StopAllAnimations() { CancelTransition(); ClearAll(); - scale1.ScaleX = 1; scale1.ScaleY = 1; translate1.X = 0; translate1.Y = 0; - scale2.ScaleX = 1; scale2.ScaleY = 1; translate2.X = 0; translate2.Y = 0; + scale1.ScaleX = 1; + scale1.ScaleY = 1; + translate1.X = 0; + translate1.Y = 0; + scale2.ScaleX = 1; + scale2.ScaleY = 1; + translate2.X = 0; + translate2.Y = 0; HideVideoImmediate(); } @@ -447,12 +538,35 @@ public void StopAllAnimations() // ANIMATION HELPERS // --------------------------------------------------------------- - private Image ActiveImage() { return image1IsActive ? image1 : image2; } - private Image StandbyImage() { return image1IsActive ? image2 : image1; } - private ScaleTransform ActiveScale() { return image1IsActive ? scale1 : scale2; } - private ScaleTransform StandbyScale() { return image1IsActive ? scale2 : scale1; } - private TranslateTransform ActiveTranslate() { return image1IsActive ? translate1 : translate2; } - private TranslateTransform StandbyTranslate() { return image1IsActive ? translate2 : translate1; } + private Image ActiveImage() + { + return image1IsActive ? image1 : image2; + } + + private Image StandbyImage() + { + return image1IsActive ? image2 : image1; + } + + private ScaleTransform ActiveScale() + { + return image1IsActive ? scale1 : scale2; + } + + private ScaleTransform StandbyScale() + { + return image1IsActive ? scale2 : scale1; + } + + private TranslateTransform ActiveTranslate() + { + return image1IsActive ? translate1 : translate2; + } + + private TranslateTransform StandbyTranslate() + { + return image1IsActive ? translate2 : translate1; + } private static void ClearTransformAnims(ScaleTransform sc, TranslateTransform tr) { @@ -463,8 +577,14 @@ private static void ClearTransformAnims(ScaleTransform sc, TranslateTransform tr } /// Animate a Freezable property (ScaleTransform, TranslateTransform). - private static void Run(Animatable target, DependencyProperty prop, - double from, double to, TimeSpan dur, IEasingFunction ease) + private static void Run( + Animatable target, + DependencyProperty prop, + double from, + double to, + TimeSpan dur, + IEasingFunction ease + ) { var anim = new DoubleAnimation { @@ -479,8 +599,14 @@ private static void Run(Animatable target, DependencyProperty prop, } /// Animate a UIElement property (Opacity). - private static void RunUI(UIElement target, DependencyProperty prop, - double from, double to, TimeSpan dur, IEasingFunction ease) + private static void RunUI( + UIElement target, + DependencyProperty prop, + double from, + double to, + TimeSpan dur, + IEasingFunction ease + ) { var anim = new DoubleAnimation {