1 module unecht.core.components.editor.mouseControls;
2 
3 version(UEIncludeEditor):
4 
5 import unecht.ue;
6 import unecht.core.events;
7 import unecht.core.entity;
8 import unecht.core.component;
9 import unecht.core.components.internal.gui;
10 import unecht.core.components.editor.mousePicking;
11 import unecht.core.components.editor.gismo;
12 
13 import gl3n.linalg;
14 
15 ///
16 final class UEEditorMouseControls : UEComponent
17 {
18     mixin(UERegisterObject!());
19     
20     static immutable SPEED_NORMAL = 0.02f;
21     static immutable SPEED_FAST = 0.25f;
22     static immutable MOUSE_CLICK_TIMEOUT = 1.0f;
23     static immutable MOUSE_CLICK_DISTANCE_THRESHOLT = 2.0f;
24     
25     private struct MouseClickState
26     {
27         int mouseButtonDown;
28         float time = 0;
29         vec2 pos;
30     }
31 
32     private bool mouseDown;
33     private bool moveMode;
34     private vec2 lastMousePos;
35     private MouseClickState mouseClickState;
36     
37     override void onCreate() {
38         super.onCreate;
39         
40         registerEvent(UEEventType.mouseButton, &onMouseEvent);
41         registerEvent(UEEventType.mousePos, &onMouseEvent);
42         registerEvent(UEEventType.mouseScroll, &onMouseEvent);
43     }
44     
45     private void onMouseEvent(UEEvent _ev)
46     {
47         if(_ev.eventType == UEEventType.mouseButton)
48         {
49             if(_ev.mouseButtonEvent.isClick)
50             {
51                 onClick(_ev.mouseButtonEvent);
52                 return;
53             }
54 
55             if(!UEGui.capturesMouse)
56             {
57                 if(_ev.mouseButtonEvent.button == 0)
58                 {
59                     mouseDown = _ev.mouseButtonEvent.isDown;
60                     moveMode = !_ev.mouseButtonEvent.pos.mods.isModAlt;
61                 }
62 
63                 if(_ev.mouseButtonEvent.isDown)
64                 {
65                     mouseClickState.mouseButtonDown = _ev.mouseButtonEvent.button;
66                     mouseClickState.time = ue.tickTime;
67                     mouseClickState.pos = vec2(_ev.mouseButtonEvent.pos.x,_ev.mouseButtonEvent.pos.y);
68                 }
69                 else
70                 {
71                     auto mpos = vec2(_ev.mouseButtonEvent.pos.x,_ev.mouseButtonEvent.pos.y);
72                     auto delta = mouseClickState.pos - mpos;
73                     if(_ev.mouseButtonEvent.button == mouseClickState.mouseButtonDown && 
74                         ue.tickTime - mouseClickState.time < MOUSE_CLICK_TIMEOUT &&
75                         delta.length < MOUSE_CLICK_DISTANCE_THRESHOLT)
76                     {
77                         triggerMouseClick(mouseClickState.mouseButtonDown, mpos);
78                     }
79                 }
80             }
81         }
82         else if(_ev.eventType == UEEventType.mousePos)
83         {
84             auto curPos = vec2(_ev.mousePosEvent.x,_ev.mousePosEvent.y);
85             auto delta = curPos - lastMousePos;
86 
87             if(mouseDown)
88             {
89                 static bool ignoreNext = false;
90 
91                 const isInsideOfWindow = ue.application.mainWindow.isCursorPosInside(curPos.x,curPos.y);
92 
93                 if(isInsideOfWindow)
94                 {
95                     if(!ignoreNext)
96                         onDrag(delta,0,_ev.mousePosEvent.mods.isModShift);
97 
98                     ignoreNext = false;
99                 }
100                 else
101                 {
102                     ue.application.mainWindow.wrapAroundCursorPos(curPos.x, curPos.y);
103                     ignoreNext = true;
104                 }
105             }
106             
107             lastMousePos = curPos;
108         }
109         else if(_ev.eventType == UEEventType.mouseScroll)
110         {
111             if(!UEGui.capturesMouse)
112                 onDrag(vec2(0), _ev.mouseScrollEvent.yoffset,_ev.mouseScrollEvent.mods.isModShift);
113         }
114     }
115 
116     private static triggerMouseClick(int button, vec2 pos)
117     {
118         UEEvent ev;
119         ev.eventType = UEEventType.mouseButton;
120         ev.mouseButtonEvent.action = UEEvent.MouseButtonEvent.Action.click;
121         ev.mouseButtonEvent.button = button;
122         ev.mouseButtonEvent.pos.x = pos.x;
123         ev.mouseButtonEvent.pos.y = pos.y;
124 
125         ue.events.trigger(ev);
126     }
127     
128     private void onDrag(vec2 delta, double scroll=0, bool fastMode=false)
129     {
130         auto speedNow = fastMode?SPEED_FAST:SPEED_NORMAL;
131         
132         //TODO: only zoom like this in move mode and move cam in direction of the ray (eye, cursorPosDir-from-screen-to-frustum)
133         sceneNode.position = sceneNode.position + (sceneNode.forward * scroll * speedNow * 10.0f);
134         
135         if(moveMode)
136         {
137             sceneNode.position = sceneNode.position + (sceneNode.right * delta.x * -speedNow);
138             sceneNode.position = sceneNode.position + (sceneNode.up * delta.y * speedNow);
139         }
140         else
141         {
142             sceneNode.angles = sceneNode.angles + vec3(delta.y*speedNow*3,delta.x*-speedNow*3,0);
143         }
144     }
145 
146     private void onClick(UEEvent.MouseButtonEvent event)
147     {
148         auto pos = vec2(event.pos.x,event.pos.y);
149         import unecht.core.components._editor;
150 
151         auto ray = EditorRootComponent.camera.screenToRay(pos);
152      
153         MousePicking.onPick(ray);
154     }
155 }