1 module sbylib.event.mouseevent; 2 3 import std; 4 import sbylib.math; 5 import sbylib.wrapper.glfw; 6 import sbylib.event; 7 8 private alias MouseEnterCallback = void delegate(bool); 9 private alias MousePosCallback = void delegate(double[2]); 10 private alias MouseScrollCallback = void delegate(double[2]); 11 private alias MouseButtonCallback = void delegate(MouseButton, ButtonState, BitFlags!ModKeyButton); 12 13 private struct MouseEnterNotification { Window window; } 14 private struct MouseLeaveNotification { Window window; } 15 private struct MousePosNotification { Window window; } 16 private struct MouseScrollNotification { Window window; } 17 private struct MouseButtonNotification { 18 Window window; 19 MouseButton button; 20 ButtonState state; 21 } 22 private struct MousePos {} 23 24 private struct Mouse { 25 MouseEnterNotification entered() { return MouseEnterNotification(); } 26 MouseLeaveNotification left() { return MouseLeaveNotification(); } 27 MousePosNotification moved() { return MousePosNotification(); } 28 MouseScrollNotification scrolled() { return MouseScrollNotification(); } 29 MousePos pos() { return MousePos(); } 30 } 31 32 static foreach (T; AliasSeq!(MouseEnterNotification, MouseLeaveNotification, MousePosNotification, MouseScrollNotification)) { 33 T on(T t, Window window) { 34 t.window = window; 35 return t; 36 } 37 } 38 39 vec2 on(MousePos, Window window) { 40 return vec2(window.mousePos()); 41 } 42 43 Mouse mouse() { return Mouse(); } 44 45 MouseButtonNotification pressed(MouseButton mouse) { 46 return MouseButtonNotification(null, mouse, ButtonState.Press); 47 } 48 49 MouseButtonNotification released(MouseButton mouse) { 50 return MouseButtonNotification(null, mouse, ButtonState.Release); 51 } 52 53 struct MousePressing { 54 MouseButton button; 55 } 56 57 MousePressing pressing(MouseButton button) { 58 return MousePressing(button); 59 } 60 61 MouseButtonNotification on(MouseButtonNotification notification, Window window) { 62 notification.window = window; 63 return notification; 64 } 65 66 auto on(MousePressing m, Window window) { 67 return FrameNotification(() => window.getMouse(m.button) == ButtonState.Press); 68 } 69 70 auto when(MouseEnterNotification notification) { 71 import sbylib.event : when; 72 73 auto event = new Event!(); 74 auto cb = MouseEventWatcher.add(notification.window, (bool entered) { 75 if (!entered) return; 76 event.fire(); 77 }); 78 when(event.finish).then({ 79 MouseEventWatcher.remove(notification.window, cb); 80 }); 81 return event; 82 } 83 84 auto when(MouseLeaveNotification notification) { 85 import sbylib.event : when; 86 87 auto event = new Event!(); 88 auto cb = MouseEventWatcher.add(notification.window, (bool entered) { 89 if (entered) return; 90 event.fire(); 91 }); 92 when(event.finish).then({ 93 MouseEventWatcher.remove(notification.window, cb); 94 }); 95 return event; 96 } 97 98 auto when(MousePosNotification notification) { 99 import sbylib.event : when; 100 101 auto event = new Event!(vec2); 102 auto cb = MouseEventWatcher.add(notification.window, (double[2] pos) { 103 event.fire(vec2(pos)); 104 }); 105 when(event.finish).then({ 106 MouseEventWatcher.remove(notification.window, cb); 107 }); 108 return event; 109 } 110 111 auto when(MouseScrollNotification notification) { 112 import sbylib.event : when; 113 114 auto event = new Event!(vec2); 115 auto cb = MouseEventWatcher.addScroll(notification.window, (double[2] scroll) { 116 event.fire(vec2(scroll)); 117 }); 118 when(event.finish).then({ 119 MouseEventWatcher.removeScroll(notification.window, cb); 120 }); 121 return event; 122 } 123 124 auto when(MouseButtonNotification notification) { 125 import sbylib.event : when; 126 127 auto event = new Event!(BitFlags!ModKeyButton); 128 auto cb = MouseEventWatcher.add(notification.window, 129 (MouseButton button, ButtonState state, BitFlags!ModKeyButton mods) { 130 if (button != notification.button) return; 131 if (state != notification.state) return; 132 event.fire(mods); 133 }); 134 when(event.finish).then({ 135 MouseEventWatcher.remove(notification.window, cb); 136 }); 137 return event; 138 } 139 140 private class MouseEventWatcher { 141 static: 142 private Array!MouseEnterCallback[Window] enterCallbackList; 143 private Array!MousePosCallback[Window] posCallbackList; 144 private Array!MouseScrollCallback[Window] scrollCallbackList; 145 private Array!MouseButtonCallback[Window] buttonCallbackList; 146 private Array!Window registered; 147 148 private void use(Window window) { 149 foreach (r; registered) { 150 if (r == window) return; 151 } 152 registered ~= window; 153 154 alias errorHandler = (Exception e) { assert(false, e.toString()); }; 155 window.setMouseEnterCallback!(enterCallback, errorHandler); 156 window.setMousePosCallback!(posCallback, errorHandler); 157 window.setScrollCallback!(scrollCallback, errorHandler); 158 window.setMouseButtonCallback!(buttonCallback, errorHandler); 159 160 enterCallbackList[window] = Array!MouseEnterCallback(); 161 posCallbackList[window] = Array!MousePosCallback(); 162 scrollCallbackList[window] = Array!MouseScrollCallback(); 163 buttonCallbackList[window] = Array!MouseButtonCallback(); 164 } 165 166 private MouseEnterCallback add(Window window, MouseEnterCallback callback) { 167 use(window); 168 enterCallbackList[window] ~= callback; 169 return callback; 170 } 171 172 private void remove(Window window, MouseEnterCallback callback) { 173 auto target = enterCallbackList[window]; 174 target.linearRemove(target[].find(callback).take(1)); 175 } 176 177 void enterCallback(Window window, bool enter) { 178 foreach (cb; enterCallbackList[window]) { 179 cb(enter); 180 } 181 } 182 183 private MousePosCallback add(Window window, MousePosCallback callback) { 184 use(window); 185 posCallbackList[window] ~= callback; 186 return callback; 187 } 188 189 private void remove(Window window, MousePosCallback callback) { 190 auto target = posCallbackList[window]; 191 target.linearRemove(target[].find(callback).take(1)); 192 } 193 194 void posCallback(Window window, double[2] pos) { 195 foreach (cb; posCallbackList[window]) { 196 cb(pos); 197 } 198 } 199 200 private MouseScrollCallback addScroll(Window window, MouseScrollCallback callback) { 201 use(window); 202 scrollCallbackList[window] ~= callback; 203 return callback; 204 } 205 206 private void removeScroll(Window window, MouseScrollCallback callback) { 207 auto target = scrollCallbackList[window]; 208 target.linearRemove(target[].find(callback).take(1)); 209 } 210 211 void scrollCallback(Window window, double[2] scroll) { 212 foreach (cb; scrollCallbackList[window]) { 213 cb(scroll); 214 } 215 } 216 217 private MouseButtonCallback add(Window window, MouseButtonCallback callback) { 218 use(window); 219 buttonCallbackList[window] ~= callback; 220 return callback; 221 } 222 223 private void remove(Window window, MouseButtonCallback callback) { 224 auto target = buttonCallbackList[window]; 225 target.linearRemove(target[].find(callback).take(1)); 226 } 227 228 void buttonCallback(Window window, MouseButton button, ButtonState state, BitFlags!ModKeyButton mods) { 229 foreach (cb; buttonCallbackList[window]) { 230 cb(button, state, mods); 231 } 232 } 233 }