1 module rpui.events_observer;
2 
3 import rpui.events;
4 import std.algorithm.mutation;
5 import std.algorithm.searching;
6 import std.variant;
7 
8 interface Subscriber {
9     void onEventReceived(in Variant event);
10 }
11 
12 final class ListenerSubscriber(T) : Subscriber {
13     private void delegate(in T event) listener = null;
14     private void delegate() listenerWithoutEvent = null;
15 
16     this(void delegate(in T event) listener) {
17         this.listener = listener;
18     }
19 
20     this(void delegate() listener) {
21         this.listenerWithoutEvent = listener;
22     }
23 
24     override void onEventReceived(in Variant event) {
25         if (event.type != typeid(const(T)))
26             return;
27 
28         if (listener !is null) {
29             listener(event.get!(const(T)));
30         } else {
31             listenerWithoutEvent();
32         }
33     }
34 }
35 
36 class EventsSubscriber(T) : Subscriber {
37     private T listener;
38 
39     this(T listener) {
40         this.listener = listener;
41     }
42 
43     override void onEventReceived(in Variant event) {
44         static immutable events = [
45             "KeyPressed",
46             "KeyReleased",
47             "TextEntered",
48             "MouseDown",
49             "MouseUp",
50             "DblClick",
51             "TripleClick",
52             "MouseMove",
53             "MouseWheel",
54             "WindowResize",
55             "WindowExposed",
56         ];
57         onEventReceivedFor!(events)(event);
58     }
59 
60     protected void onEventReceivedFor(string[] events)(in Variant event) {
61         static foreach (eventName; events) {
62             {
63                 mixin("alias eventType = " ~ eventName ~ "Event;");
64 
65                 if (event.type == typeid(const(eventType))) {
66                     const e = event.get!(const(eventType));
67                     mixin("listener.on" ~ eventName ~ "(e);");
68                 }
69             }
70         }
71     }
72 }
73 
74 class EventsObserver {
75     private bool[EventsObserver] observerIsActive;
76     private const(TypeInfo)[][EventsObserver] observerJoinedEvents;
77     private EventsObserver[] joinedObservers;
78     private Subscriber[] subscribers;
79 
80     bool subscriberIsNotifiable(Subscriber subscriber) {
81         return true;
82     }
83 
84 final:
85     Subscriber subscribe(Subscriber subscriber) {
86         subscribers ~= subscriber;
87         return subscriber;
88     }
89 
90     Subscriber subscribe(T)(void delegate(in T event) listener) {
91         return subscribe(new ListenerSubscriber!(T)(listener));
92     }
93 
94     Subscriber subscribe(T)(void delegate() listener) {
95         return subscribe(new ListenerSubscriber!(T)(listener));
96     }
97 
98     Subscriber subscribe(EventsListener subscriber) {
99         return subscribe(new EventsSubscriber!(EventsListener)(subscriber));
100     }
101 
102     void unsubscribe(Subscriber subscriber) {
103         subscribers = subscribers.remove!(a => a == subscriber);
104     }
105 
106     void notify(T)(in T event) {
107         foreach (subscriber; subscribers) {
108             if (subscriberIsNotifiable(subscriber))
109                 subscriber.onEventReceived(Variant(event));
110         }
111 
112         foreach (observer; joinedObservers) {
113             const shouldNotify = observerJoinedEvents[observer].length == 0 ||
114                 observerJoinedEvents[observer].canFind(typeid(T));
115 
116             if (observerIsActive[observer] && shouldNotify)
117                 observer.notify(event);
118         }
119     }
120 
121     void join(EventsObserver observer, in TypeInfo[] events) {
122         joinedObservers ~= observer;
123         observerIsActive[observer] = true;
124         observerJoinedEvents[observer] = events;
125     }
126 
127     void join(EventsObserver observer) {
128         joinedObservers ~= observer;
129         observerIsActive[observer] = true;
130 
131         // when list is empty, then join to all events
132         observerJoinedEvents[observer] = [];
133     }
134 
135     void unjoin(EventsObserver observer) {
136         observerIsActive.remove(observer);
137         joinedObservers = joinedObservers.remove!(a => a == observer);
138     }
139 
140     void silent(EventsObserver observer) {
141         observerIsActive[observer] = false;
142     }
143 
144     void unsilent(EventsObserver observer) {
145         observerIsActive[observer] = true;
146     }
147 }