1 module nodes.input_map_chain;
2 
3 import fluid;
4 
5 @safe:
6 
7 class ActionTester : InputNode!Node {
8 
9     mixin enableInputActions;
10 
11     int pressed;
12     int submitted;
13     int broken;
14     int frameCalls;
15 
16     bool disableBreaking;
17 
18     override void resizeImpl(Vector2 space) { }
19     override void drawImpl(Rectangle, Rectangle) { }
20 
21     @(ActionIO.CoreAction.frame, WhileHeld)
22     void frame() {
23         frameCalls++;
24     }
25 
26     @(FluidInputAction.press)
27     bool press() {
28         pressed++;
29         return true;
30     }
31 
32     @(FluidInputAction.submit)
33     bool submit() {
34         submitted++;
35         return true;
36     }
37 
38     @(FluidInputAction.breakLine)
39     bool breakLine() {
40 
41         if (disableBreaking) return false;
42 
43         broken++;
44         return true;
45 
46     }
47 
48     bool runInputAction(immutable InputActionID actionID, bool isActive, int) {
49 
50         return super.runInputAction(actionID, isActive);
51 
52     }
53 
54 }
55 
56 alias actionTester = nodeBuilder!ActionTester;
57 
58 @("InputMapChain can trigger input events")
59 unittest {
60 
61     // Create bindings
62     auto map = InputMapping();
63     map.bindNew!(FluidInputAction.press)(KeyboardIO.codes.space);
64     map.bindNew!(FluidInputAction.submit)(KeyboardIO.codes.enter);
65     map.bindNew!(FluidInputAction.breakLine)(KeyboardIO.codes.enter);
66     map.bindNew!(FluidInputAction.submit)(KeyboardIO.codes.leftControl, KeyboardIO.codes.enter);
67 
68     auto tester = actionTester();
69     auto root = inputMapChain(map, tester);
70 
71     root.draw();
72     assert(tester.pressed == 0);
73 
74     // Press the button using an event
75     root.emitEvent(
76         KeyboardIO.createEvent(KeyboardIO.Key.space, true),
77         0,
78         &tester.runInputAction,
79     );
80     root.draw();
81     assert(tester.pressed == 1);
82 
83     // Break line
84     root.emitEvent(
85         KeyboardIO.createEvent(KeyboardIO.Key.enter, true),
86         0,
87         &tester.runInputAction,
88     );
89     root.draw();
90     assert(tester.pressed   == 1);
91     assert(tester.broken    == 1);
92     assert(tester.submitted == 0, "Enter doesn't submit as breakLine takes priority");
93 
94     // Submit
95     tester.disableBreaking = true;
96     root.emitEvent(
97         KeyboardIO.createEvent(KeyboardIO.Key.enter, true),
98         0,
99         &tester.runInputAction,
100     );
101     root.draw();
102     assert(tester.pressed   == 1);
103     assert(tester.broken    == 1);
104     assert(tester.submitted == 1);
105 
106 }
107 
108 @("InputMapChain emits a fallback event")
109 unittest {
110 
111     auto map = InputMapping();
112     map.bindNew!(FluidInputAction.press)(KeyboardIO.codes.space);
113 
114     auto tester = actionTester();
115     auto root = inputMapChain(map, tester);
116 
117     root.draw();
118     assert(tester.frameCalls == 0);
119 
120     // Emit frame event and expect it to be handled
121     root.emitEvent(ActionIO.frameEvent, 0, &tester.runInputAction);
122     assert(tester.frameCalls == 0);
123     root.draw();
124     assert(tester.frameCalls == 1);
125 
126     // Emit frame event alongside a space press event
127     root.emitEvent(ActionIO.frameEvent, 0, &tester.runInputAction);
128     root.emitEvent(KeyboardIO.press.space, 0, &tester.runInputAction);
129     assert(tester.frameCalls == 1);
130     assert(tester.pressed == 0);
131     root.draw();
132     assert(tester.pressed == 1);
133 
134 }
135 
136 @("Bindings are evaluated in a first-in-last-out manner")
137 unittest {
138 
139     auto map = InputMapping();
140     map.bindNew!(FluidInputAction.press)(KeyboardIO.codes.enter);
141 
142     auto tester = actionTester();
143     auto root = inputMapChain(map, tester);
144 
145     root.draw();
146     assert(tester.pressed == 0);
147     assert(tester.broken  == 0);
148 
149     root.emitEvent(KeyboardIO.press.enter, 0, &tester.runInputAction);
150     root.draw();
151     assert(tester.pressed == 1);
152     assert(tester.broken  == 0);
153 
154     map.bindNew!(FluidInputAction.breakLine)(KeyboardIO.codes.enter);
155     root.emitEvent(KeyboardIO.press.enter, 0, &tester.runInputAction);
156     root.draw();
157     assert(tester.pressed == 1);
158     assert(tester.broken  == 1);
159 
160 }