OSDN Git Service

ショートカットキーの条件と動作を定義するShortcutCommandクラスを追加
[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 Keys[] shortcuts;
45         private FocusedControl focusedOn;
46         private FocusedControl notFocusedOn;
47         private Func<bool> onlyWhen;
48         private Func<Task> command;
49         private bool preventDefault;
50
51         /// <summary>
52         /// ショートカットキーが動作する条件となるキー入力
53         /// </summary>
54         public Keys[] Shortcuts
55         {
56             get { return this.shortcuts; }
57         }
58
59         /// <summary>
60         /// ショートカットキーが動作する条件となるフォーカス状態
61         /// </summary>
62         public FocusedControl FocusedOn
63         {
64             get { return this.focusedOn; }
65         }
66
67         /// <summary>
68         /// ショートカットキーが動作する否定条件となるフォーカス状態
69         /// </summary>
70         public FocusedControl NotFocusedOn
71         {
72             get { return this.notFocusedOn; }
73         }
74
75         /// <summary>
76         /// コマンドを実行した後、コントロール既定の動作を無効化するか否か (デフォルトは true)
77         /// </summary>
78         public bool PreventDefault
79         {
80             get { return this.preventDefault; }
81         }
82
83         private ShortcutCommand()
84         {
85             this.shortcuts = new Keys[0];
86             this.command = () => Task.FromResult(0);
87             this.onlyWhen = () => true;
88             this.focusedOn = FocusedControl.None;
89             this.notFocusedOn = FocusedControl.None;
90             this.preventDefault = true;
91         }
92
93         /// <summary>
94         /// コマンドを実行する条件を満たしているか判定します
95         /// </summary>
96         public bool IsMatch(Keys pressedKey, FocusedControl focusedOn)
97         {
98             if (!this.Shortcuts.Contains(pressedKey))
99                 return false;
100
101             if (this.FocusedOn != FocusedControl.None && this.FocusedOn != focusedOn)
102                 return false;
103
104             if (this.NotFocusedOn != FocusedControl.None && this.NotFocusedOn == focusedOn)
105                 return false;
106
107             if (!this.onlyWhen())
108                 return false;
109
110             return true;
111         }
112
113         /// <summary>
114         /// コマンドを実行します
115         /// </summary>
116         public async Task RunCommand()
117         {
118             await this.command();
119         }
120
121         /// <summary>
122         /// 新規に ShortcutCommand インスタンスを作成するビルダーを返します
123         /// </summary>
124         public static ShortcutCommand.Builder Create(params Keys[] shortcuts)
125         {
126             return new Builder().Keys(shortcuts);
127         }
128
129         public class Builder
130         {
131             private readonly ShortcutCommand instance;
132
133             internal Builder()
134             {
135                 this.instance = new ShortcutCommand();
136             }
137
138             /// <summary>
139             /// 指定されたキーが入力された時にショートカットを発動します
140             /// </summary>
141             public Builder Keys(params Keys[] shortcuts)
142             {
143                 this.instance.shortcuts = shortcuts;
144                 return this;
145             }
146
147             /// <summary>
148             /// 指定されたコントロールにフォーカスが当たっている時のみショートカットを有効にします
149             /// </summary>
150             public Builder FocusedOn(FocusedControl focusedOn)
151             {
152                 this.instance.focusedOn = focusedOn;
153                 return this;
154             }
155
156             /// <summary>
157             /// 指定されたコントロールにフォーカスが当たっている時はショートカットを有効にしません
158             /// </summary>
159             public Builder NotFocusedOn(FocusedControl notFocusedOn)
160             {
161                 this.instance.notFocusedOn = notFocusedOn;
162                 return this;
163             }
164
165             /// <summary>
166             /// 指定された条件が true になる間のみショートカットを有効にします
167             /// </summary>
168             public Builder OnlyWhen(Func<bool> condition)
169             {
170                 this.instance.onlyWhen = condition;
171                 return this;
172             }
173
174             /// <summary>
175             /// ショートカットが入力された時に行う動作の内容
176             /// </summary>
177             public ShortcutCommand Do(Action action, bool preventDefault = true)
178             {
179                 return this.Do(SynchronousTask(action), preventDefault);
180             }
181
182             /// <summary>
183             /// ショートカットが入力された時に行う動作の内容
184             /// </summary>
185             public ShortcutCommand Do(Func<Task> action, bool preventDefault = true)
186             {
187                 this.instance.command = action;
188                 this.instance.preventDefault = preventDefault;
189
190                 return this.instance;
191             }
192
193             /// <summary>何もしないタスク</summary>
194             private static Task noOpTask = Task.FromResult(0);
195
196             /// <summary>
197             /// Action を Func&lt;Task&gt; に変換します
198             /// </summary>
199             private static Func<Task> SynchronousTask(Action action)
200             {
201                 return () => { action(); return noOpTask; };
202             }
203         }
204     }
205 }