OSDN Git Service

完了状態のTaskを生成するために Task.CompletedTask を使用する
[opentween/open-tween.git] / OpenTween / ShortcutCommand.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 using System;
23 using System.Collections.Generic;
24 using System.Linq;
25 using System.Text;
26 using System.Threading.Tasks;
27 using System.Windows.Forms;
28
29 namespace OpenTween
30 {
31     public enum FocusedControl
32     {
33         None,
34         ListTab,
35         StatusText,
36         PostBrowser,
37     }
38
39     /// <summary>
40     /// ショートカットキーの条件と動作を定義するクラス
41     /// </summary>
42     public class ShortcutCommand
43     {
44         private Func<bool> onlyWhen;
45         private Func<Task> command;
46
47         /// <summary>
48         /// ショートカットキーが動作する条件となるキー入力
49         /// </summary>
50         public Keys[] Shortcuts { get; private set; }
51
52         /// <summary>
53         /// ショートカットキーが動作する条件となるフォーカス状態
54         /// </summary>
55         public FocusedControl FocusedOn { get; private set; }
56
57         /// <summary>
58         /// ショートカットキーが動作する否定条件となるフォーカス状態
59         /// </summary>
60         public FocusedControl NotFocusedOn { get; private set; }
61
62         /// <summary>
63         /// コマンドを実行した後、コントロール既定の動作を無効化するか否か (デフォルトは true)
64         /// </summary>
65         public bool PreventDefault { get; private set; }
66
67         private ShortcutCommand()
68         {
69             this.Shortcuts = Array.Empty<Keys>();
70             this.command = () => Task.CompletedTask;
71             this.onlyWhen = () => true;
72             this.FocusedOn = FocusedControl.None;
73             this.NotFocusedOn = FocusedControl.None;
74             this.PreventDefault = true;
75         }
76
77         /// <summary>
78         /// コマンドを実行する条件を満たしているか判定します
79         /// </summary>
80         public bool IsMatch(Keys pressedKey, FocusedControl focusedOn)
81         {
82             if (!this.Shortcuts.Contains(pressedKey))
83                 return false;
84
85             if (this.FocusedOn != FocusedControl.None && this.FocusedOn != focusedOn)
86                 return false;
87
88             if (this.NotFocusedOn != FocusedControl.None && this.NotFocusedOn == focusedOn)
89                 return false;
90
91             if (!this.onlyWhen())
92                 return false;
93
94             return true;
95         }
96
97         /// <summary>
98         /// コマンドを実行します
99         /// </summary>
100         public async Task RunCommand()
101             => await this.command();
102
103         /// <summary>
104         /// 新規に ShortcutCommand インスタンスを作成するビルダーを返します
105         /// </summary>
106         public static ShortcutCommand.Builder Create(params Keys[] shortcuts)
107             => new Builder().Keys(shortcuts);
108
109         public class Builder
110         {
111             private readonly ShortcutCommand instance;
112
113             internal Builder()
114                 => this.instance = new ShortcutCommand();
115
116             /// <summary>
117             /// 指定されたキーが入力された時にショートカットを発動します
118             /// </summary>
119             public Builder Keys(params Keys[] shortcuts)
120             {
121                 this.instance.Shortcuts = shortcuts;
122                 return this;
123             }
124
125             /// <summary>
126             /// 指定されたコントロールにフォーカスが当たっている時のみショートカットを有効にします
127             /// </summary>
128             public Builder FocusedOn(FocusedControl focusedOn)
129             {
130                 this.instance.FocusedOn = focusedOn;
131                 return this;
132             }
133
134             /// <summary>
135             /// 指定されたコントロールにフォーカスが当たっている時はショートカットを有効にしません
136             /// </summary>
137             public Builder NotFocusedOn(FocusedControl notFocusedOn)
138             {
139                 this.instance.NotFocusedOn = notFocusedOn;
140                 return this;
141             }
142
143             /// <summary>
144             /// 指定された条件が true になる間のみショートカットを有効にします
145             /// </summary>
146             public Builder OnlyWhen(Func<bool> condition)
147             {
148                 this.instance.onlyWhen = condition;
149                 return this;
150             }
151
152             /// <summary>
153             /// ショートカットが入力された時に行う動作の内容
154             /// </summary>
155             public ShortcutCommand Do(Action action, bool preventDefault = true)
156                 => this.Do(SynchronousTask(action), preventDefault);
157
158             /// <summary>
159             /// ショートカットが入力された時に行う動作の内容
160             /// </summary>
161             public ShortcutCommand Do(Func<Task> action, bool preventDefault = true)
162             {
163                 this.instance.command = action;
164                 this.instance.PreventDefault = preventDefault;
165
166                 return this.instance;
167             }
168
169             /// <summary>
170             /// Action を Func&lt;Task&gt; に変換します
171             /// </summary>
172             private static Func<Task> SynchronousTask(Action action)
173                 => () => { action(); return Task.CompletedTask; };
174         }
175     }
176 }