OSDN Git Service

選択中のタブの名前を変更するとSelectedTabのgetterがエラーになる不具合を修正
[opentween/open-tween.git] / OpenTween / WaitingDialog.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2015 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
10 // any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License
15 // for more details.
16 //
17 // You should have received a copy of the GNU General public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
21
22 #nullable enable
23
24 using System;
25 using System.Collections.Generic;
26 using System.ComponentModel;
27 using System.Linq;
28 using System.Text;
29 using System.Threading;
30 using System.Threading.Tasks;
31 using System.Windows.Forms;
32
33 namespace OpenTween
34 {
35     /// <summary>
36     /// タスクが待機中であることを表示するダイアログ
37     /// </summary>
38     /// <remarks>
39     /// 一定時間 (Timeout プロパティで指定する) 以上経ってもタスクが完了しない場合にダイアログを表示します。
40     /// EnableCancellation メソッドを使用することでキャンセル機能を提供することも可能です。
41     /// </remarks>
42     public partial class WaitingDialog : OTBaseForm
43     {
44         private readonly Lazy<CancellationTokenSource> cancellationTokenSource;
45
46         private bool cancellationEnabled = false;
47
48         /// <summary>
49         /// ダイアログを表示せずに待機する最短の待ち時間 (デフォルト0.5秒)
50         /// </summary>
51         public TimeSpan Timeout { get; set; }
52
53         /// <summary>
54         /// ダイアログに表示するメッセージ
55         /// </summary>
56         public string Message
57         {
58             get => this.labelMessage.Text;
59             set => this.labelMessage.Text = value;
60         }
61
62         public WaitingDialog()
63         {
64             this.InitializeComponent();
65
66             this.cancellationTokenSource = new Lazy<CancellationTokenSource>();
67
68             this.Timeout = TimeSpan.FromMilliseconds(500);
69         }
70
71         public WaitingDialog(string message)
72             : this()
73         {
74             this.Message = message;
75         }
76
77         /// <summary>
78         /// キャンセル機能を有効にし、キャンセルを通知するための CancellationToken を返します
79         /// </summary>
80         public CancellationToken EnableCancellation()
81         {
82             this.cancellationEnabled = true;
83             this.ControlBox = true;
84
85             var cts = this.cancellationTokenSource.Value;
86             return cts.Token;
87         }
88
89         public Task WaitForAsync(Task task)
90             => this.WaitForAsync(this.ConvertTaskWithValue(task));
91
92         public Task WaitForAsync(IWin32Window? owner, Task task)
93             => this.WaitForAsync(owner, this.ConvertTaskWithValue(task));
94
95         public Task<T> WaitForAsync<T>(Task<T> task)
96             => this.WaitForAsync(null, task);
97
98         /// <summary>
99         /// タスクを待機し、状況に応じて待機中ダイアログを表示します
100         /// </summary>
101         /// <param name="owner">ダイアログのオーナー</param>
102         /// <param name="task">待機するタスク</param>
103         public Task<T> WaitForAsync<T>(IWin32Window? owner, Task<T> task)
104         {
105             return Task.Run(async () =>
106             {
107                 // 指定された秒数以内で完了すればダイアログは表示しない
108                 var timeout = Task.Delay(this.Timeout);
109                 if (await Task.WhenAny(task, timeout) != timeout)
110                     return await task;
111
112                 var dialogTask = this.InvokeAsync(() => this.ShowDialog(owner));
113
114                 // キャンセルされずにタスクが先に完了したらダイアログを閉じる
115                 if (await Task.WhenAny(task, dialogTask) != dialogTask)
116                     await this.InvokeAsync(() => this.DialogResult = DialogResult.OK);
117
118                 return await task;
119             });
120         }
121
122         /// <summary>Task を Task&lt;T&gt; に変換したいだけ</summary>
123         private async Task<int> ConvertTaskWithValue(Task task)
124         {
125             await task.ConfigureAwait(false);
126             return 0;
127         }
128
129         private void ProgressDialog_FormClosing(object sender, FormClosingEventArgs e)
130         {
131             if (this.cancellationEnabled)
132             {
133                 if (e.CloseReason == CloseReason.UserClosing)
134                 {
135                     var cts = this.cancellationTokenSource.Value;
136                     cts.Cancel();
137                 }
138             }
139         }
140
141         private void ProgressDialog_KeyDown(object sender, KeyEventArgs e)
142         {
143             if (this.cancellationEnabled)
144             {
145                 if (e.KeyCode == Keys.Escape)
146                 {
147                     var cts = this.cancellationTokenSource.Value;
148                     cts.Cancel();
149
150                     this.DialogResult = DialogResult.Cancel;
151                 }
152             }
153         }
154     }
155 }