//--------------------------------------------------------------------- // Static members /// <summary> /// Loads individual frames to be displayed in an icon animation. These /// are expected to be files named like <b>PREFIX-#.ico</b> in the specified /// directory. The method will start loading <b>PREFIX-0.ico</b> and keep /// incrementing the index until no more frame files are found. /// </summary> /// <param name="folder">The source folder path.</param> /// <param name="prefix">The icon file name prefix.</param> /// <param name="frameRate">The desired frame rate.</param> /// <returns>The <see cref="AnimatedIcon"/>.</returns> /// <exception cref="FileNotFoundException">Thrown if no matching icon files were found.</exception> public static AnimatedIcon Load(string folder, string prefix, double frameRate) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(folder)); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(prefix)); Covenant.Requires <ArgumentException>(frameRate > 0); var animatedIcon = new AnimatedIcon() { FrameRate = frameRate }; for (var i = 0; ; i++) { var path = Path.Combine(folder, $"{prefix}-{i}.ico"); if (!File.Exists(path)) { break; } animatedIcon.frames.Add(new Icon(path)); } if (animatedIcon.frames.Count == 0) { throw new FileNotFoundException($"Could not locate any icon files with prefix: {prefix}"); } return(animatedIcon); }
/// <summary> /// Starts a notify icon animation. /// </summary> /// <param name="animatedIcon">The icon animation.</param> /// <param name="balloonText">Optional text to be displayed in the balloon during the animation.</param> /// <param name="isTransient"> /// Optionally indicates that the state is not associated with /// an operation indicating an error or other transient status. /// </param> /// <param name="isError"> /// Optionally indicates that the application is in the error state. /// This implies <see cref="IsTransient"/><c>=true</c>. /// </param> /// <remarks> /// Calls to this method may be recursed and should be matched /// with a call to <see cref="StopNotifyAnimation"/>. The /// amimation will actually stop when the last matching /// <see cref="StartNotifyAnimation"/> call was matched with /// the last <see cref="StopNotifyAnimation"/>. /// </remarks> private void StartNotifyAnimation(AnimatedIcon animatedIcon, string balloonText = null, bool isTransient = false, bool isError = false) { Covenant.Requires <ArgumentNullException>(animatedIcon != null); InvokeOnUIThread( () => { notifyStack.Push(new NotifyState(animatedIcon, balloonText, isTransient, isError)); if (!string.IsNullOrEmpty(balloonText)) { SetBalloonText(balloonText); } if (animatedIcon != null) { animationTimer.Stop(); animationTimer.Interval = (int)TimeSpan.FromSeconds(1 / animatedIcon.FrameRate).TotalMilliseconds; animationTimer.Tick += (s, a) => { notifyIcon.Icon = animatedIcon.GetNextFrame(); }; animationTimer.Start(); } }); }
/// <summary> /// Constructor. /// </summary> /// <param name="animatedIcon">The optional animation.</param> /// <param name="balloonText">The optional balloon text.</param> /// <param name="isTransient"> /// Optionally indicates that the state is not associated with /// an operation indicating an error or other transient status. /// </param> /// <param name="isError"> /// Optionally indicates that the application is in the error state. /// This implies <see cref="IsTransient"/><c>=true</c>. /// </param> public NotifyState(AnimatedIcon animatedIcon, string balloonText = null, bool isTransient = false, bool isError = false) { this.AnimatedIcon = animatedIcon; this.BalloonText = balloonText; this.IsTransient = isTransient || isError; this.IsError = isError; }
/// <summary> /// Indicates that an operation is starting by optionally displaying a working /// animation and optionally displaying a status toast. /// </summary> /// <param name="animatedIcon">The optional notify icon animation.</param> /// <param name="toastText">The optional toast text.</param> private void StartOperation(AnimatedIcon animatedIcon = null, string toastText = null) { InvokeOnUIThread( () => { operationInProgress = true; if (animatedIcon != null) { StartNotifyAnimation(animatedIcon); } if (!string.IsNullOrEmpty(toastText)) { ShowToast(toastText); } }); }
/// <summary> /// Constructor. /// </summary> public MainForm() { MainForm.Current = this; InitializeComponent(); Load += MainForm_Load; Shown += (s, a) => Visible = false; // The main form should always be hidden // Ensure that temporary files are written to the users temporary folder because // there's a decent chance that this folder will be encrypted at rest. TempFile.Root = KubeHelper.TempFolder; TempFolder.Root = KubeHelper.TempFolder; // Preload the notification icons and animations for better performance. appIcon = new Icon(@"Images\app.ico"); connectedIcon = new Icon(@"Images\connected.ico"); disconnectedIcon = new Icon(@"Images\disconnected.ico"); errorIcon = new Icon(@"Images\error.ico"); connectingAnimation = AnimatedIcon.Load("Images", "connecting", animationFrameRate); workingAnimation = AnimatedIcon.Load("Images", "working", animationFrameRate); errorAnimation = AnimatedIcon.Load("Images", "error", animationFrameRate); notifyStack = new Stack <NotifyState>(); // Initialize the cluster hosting provider components. HostingLoader.Initialize(); // Initialize the client state. proxies = new List <ReverseProxy>(); portForwards = new List <PortForward>(); Headend = new HeadendClient(); KubeHelper.LoadClientConfig(); }