forked from reactiveui/ReactiveUI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInput.cs
More file actions
184 lines (152 loc) · 5.91 KB
/
Input.cs
File metadata and controls
184 lines (152 loc) · 5.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reactive.Linq;
using System.Reactive.Disposables;
using System.Linq.Expressions;
using Splat;
namespace ReactiveUI
{
public interface IInputCommand : IReactiveCommand
{
string Shortcut { get; }
string Description { get; }
}
public interface IInputCommand<T> : IReactiveCommand<T>, IInputCommand { }
public sealed class InputSection
{
public string SectionHeader { get; set; }
public bool IgnoreWhileTyping { get; set; }
internal ReactiveList<IObservable<IInputCommand>> CommandObservables { get; private set; }
public InputSection(string sectionHeader, params IObservable<IInputCommand>[] commands) : this(sectionHeader, true, commands) { }
public InputSection(string sectionHeader, bool ignoreWhileTyping, params IObservable<IInputCommand>[] commands)
{
SectionHeader = sectionHeader;
IgnoreWhileTyping = ignoreWhileTyping;
CommandObservables = new ReactiveList<IObservable<IInputCommand>>(commands);
}
public IEnumerable<IInputCommand> Commands {
get {
// NB: Same deal as below, we know that all of the CommandObservables
// return an initial value
return CommandObservables
.Select(x => x.Take(1))
.Concat()
.Aggregate(new List<IInputCommand>(), (acc, x) => { acc.Add(x); return acc; })
.First();
}
}
}
public interface IKeyboardManager
{
IEnumerable<InputSection> RegisteredSections { get; }
IDisposable RegisterScope(params InputSection[] sections);
void InvokeShortcut(string shortcut);
}
public sealed class KeyboardManager : IKeyboardManager
{
readonly ReactiveList<InputSection> registeredSections = new ReactiveList<InputSection>();
public static IKeyboardManager Current {
get { return Locator.Current.GetService<IKeyboardManager>(); }
}
internal KeyboardManager() { }
public IEnumerable<InputSection> RegisteredSections {
get { return registeredSections; }
}
public IDisposable RegisterScope(params InputSection[] sections)
{
var currentSize = registeredSections.Count;
var lengthToRemove = sections.Length;
registeredSections.AddRange(sections);
// NB: This is to hold the RefCount open in AsInputCommand until we
// drop the input section scope
var disp = sections
.SelectMany(x => x.CommandObservables).Merge()
.Subscribe();
return Disposable.Create(() => {
registeredSections.RemoveRange(currentSize, lengthToRemove);
disp.Dispose();
});
}
public void InvokeShortcut(string shortcut)
{
// NB: This looks asynchronous, but it will always run synchronously
// because we only add BehaviorSubjects to the Commands list
registeredSections.Reverse().ToObservable()
.Select(x => x.CommandObservables.ToObservable()).Concat()
.Merge()
.Where(x => x != null && x.Shortcut == shortcut && x.CanExecute(null))
.Take(1)
.Subscribe(x => x.Execute(null));
}
}
public static class InputMixins
{
public static IInputCommand<T> AsInputCommand<T>(this IReactiveCommand<T> This, string shortcut, string description = null)
{
return InputCommand.CreateWith(This, shortcut, description);
}
public static IObservable<IInputCommand<TCmd>> GetInputCommand<TTarget, TCmd>(this TTarget This, Expression<Func<TTarget, IReactiveCommand<TCmd>>> property, string shortcut, string description = null)
{
return This.WhenAnyValue(property)
.Select(x => x.AsInputCommand(shortcut, description))
.Publish(null)
.RefCount();
}
}
public static class InputCommand
{
public static IInputCommand<T> CreateWith<T>(IReactiveCommand<T> innerCommand, string shortcut, string description)
{
return new InputCommand<T>(innerCommand, shortcut, description);
}
}
class InputCommand<T> : IInputCommand<T>
{
IReactiveCommand<T> inner;
public InputCommand(IReactiveCommand<T> innerCommand, string shortcut, string description)
{
inner = innerCommand;
Shortcut = shortcut;
Description = description;
}
public string Shortcut { get; protected set; }
public string Description { get; protected set; }
#region Boring Code
public IObservable<bool> CanExecuteObservable {
get { return inner.CanExecuteObservable; }
}
public IObservable<bool> IsExecuting {
get { return inner.IsExecuting; }
}
public IObservable<Exception> ThrownExceptions {
get { return inner.ThrownExceptions; }
}
public IDisposable Subscribe(IObserver<T> observer)
{
return inner.Subscribe(observer);
}
public bool CanExecute(object parameter)
{
return inner.CanExecute(parameter);
}
public event EventHandler CanExecuteChanged {
add { inner.CanExecuteChanged += value; }
remove { inner.CanExecuteChanged -= value; }
}
public void Execute(object parameter)
{
inner.Execute(parameter);
}
public IObservable<T> ExecuteAsync(object parameter = null)
{
return inner.ExecuteAsync(parameter);
}
public void Dispose()
{
inner.Dispose();
}
#endregion
}
}