From f6fc00028753ddbd18660248e17635019287908a Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Sat, 14 Nov 2026 15:58:48 +0100 Subject: [PATCH] Implement mouse events --- src/editor/dialogs/addeventdialog.cpp | 72 ++++++++++++ src/editor/dialogs/addeventdialog.ui | 6 +- src/editor/icons/event-mouse.png | Bin 0 -> 14337 bytes src/editor/models/objecteventsmodel.cpp | 140 +++++++++++++++++++---- src/editor/resources_editor.qrc | 1 + src/engine/gameengine.cpp | 80 +++++++++++++ src/engine/gameengine.h | 4 + src/engine/gamescene.cpp | 142 ++++++++++++++++++++++++ src/engine/gamescene.h | 23 ++++ src/projectcontainer.h | 47 ++++++++ 10 files changed, 491 insertions(+), 24 deletions(-) create mode 100644 src/editor/icons/event-mouse.png diff --git a/src/editor/dialogs/addeventdialog.cpp b/src/editor/dialogs/addeventdialog.cpp index 0734da2..d088f09 100644 --- a/src/editor/dialogs/addeventdialog.cpp +++ b/src/editor/dialogs/addeventdialog.cpp @@ -82,6 +82,78 @@ AddEventDialog::AddEventDialog(ProjectTreeModel &projectModel, MainWindow &mainW m_ui->pushButtonCollision->setMenu(menu); } + { + auto menu = new QMenu; + menu->addAction(tr("Left button"), this, [this](){ m_eventType = Object::EventType::MouseLeftButton; accept(); }); + menu->addAction(tr("Right button"), this, [this](){ m_eventType = Object::EventType::MouseRightButton; accept(); }); + menu->addAction(tr("Middle button"), this, [this](){ m_eventType = Object::EventType::MouseMiddleButton; accept(); }); + menu->addSeparator(); + menu->addAction(tr("No button"), this, [this](){ m_eventType = Object::EventType::MouseNoButton; accept(); }); + menu->addSeparator(); + menu->addAction(tr("Left pressed"), this, [this](){ m_eventType = Object::EventType::MouseLeftPressed; accept(); }); + menu->addAction(tr("Right pressed"), this, [this](){ m_eventType = Object::EventType::MouseRightPressed; accept(); }); + menu->addAction(tr("Middle pressed"), this, [this](){ m_eventType = Object::EventType::MouseMiddlePressed; accept(); }); + menu->addSeparator(); + menu->addAction(tr("Left released"), this, [this](){ m_eventType = Object::EventType::MouseLeftReleased; accept(); }); + menu->addAction(tr("Right released"), this, [this](){ m_eventType = Object::EventType::MouseRightReleased; accept(); }); + menu->addAction(tr("Middle released"), this, [this](){ m_eventType = Object::EventType::MouseMiddleReleased; accept(); }); + menu->addSeparator(); + menu->addAction(tr("Mouse enter"), this, [this](){ m_eventType = Object::EventType::MouseEnter; accept(); }); + menu->addAction(tr("Mouse leave"), this, [this](){ m_eventType = Object::EventType::MouseLeave; accept(); }); + menu->addSeparator(); + menu->addAction(tr("Mouse wheel up"), this, [this](){ m_eventType = Object::EventType::MouseWheelUp; accept(); }); + menu->addAction(tr("Mouse wheel down"), this, [this](){ m_eventType = Object::EventType::MouseWheelDown; accept(); }); + menu->addSeparator(); + { + auto subMenu = menu->addMenu(tr("Global mouse")); + subMenu->addAction(tr("Global Left button"), this, [this](){ m_eventType = Object::EventType::MouseGlobalLeftButton; accept(); }); + subMenu->addAction(tr("Global Right button"), this, [this](){ m_eventType = Object::EventType::MouseGlobalRightButton; accept(); }); + subMenu->addAction(tr("Global Middle button"), this, [this](){ m_eventType = Object::EventType::MouseGlobalMiddleButton; accept(); }); + subMenu->addSeparator(); + subMenu->addAction(tr("Global Left pressed"), this, [this](){ m_eventType = Object::EventType::MouseGlobalLeftPressed; accept(); }); + subMenu->addAction(tr("Global Right pressed"), this, [this](){ m_eventType = Object::EventType::MouseGlobalRightPressed; accept(); }); + subMenu->addAction(tr("Global Middle pressed"), this, [this](){ m_eventType = Object::EventType::MouseGlobalMiddlePressed; accept(); }); + subMenu->addSeparator(); + subMenu->addAction(tr("Global Left released"), this, [this](){ m_eventType = Object::EventType::MouseGlobalLeftReleased; accept(); }); + subMenu->addAction(tr("Global Right released"), this, [this](){ m_eventType = Object::EventType::MouseGlobalRightReleased; accept(); }); + subMenu->addAction(tr("Global Middle released"), this, [this](){ m_eventType = Object::EventType::MouseGlobalMiddleReleased; accept(); }); + } + menu->addSeparator(); + { + auto subMenu = menu->addMenu(tr("Joystick 1")); + subMenu->addAction(tr("Left"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Left; accept(); }); + subMenu->addAction(tr("Right"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Right; accept(); }); + subMenu->addAction(tr("Up"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Up; accept(); }); + subMenu->addAction(tr("Down"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Down; accept(); }); + subMenu->addSeparator(); + subMenu->addAction(tr("Button 1"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button1; accept(); }); + subMenu->addAction(tr("Button 2"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button2; accept(); }); + subMenu->addAction(tr("Button 3"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button3; accept(); }); + subMenu->addAction(tr("Button 4"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button4; accept(); }); + subMenu->addAction(tr("Button 5"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button5; accept(); }); + subMenu->addAction(tr("Button 6"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button6; accept(); }); + subMenu->addAction(tr("Button 7"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button7; accept(); }); + subMenu->addAction(tr("Button 8"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button8; accept(); }); + } + { + auto subMenu = menu->addMenu(tr("Joystick 2")); + subMenu->addAction(tr("Left"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Left; accept(); }); + subMenu->addAction(tr("Right"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Right; accept(); }); + subMenu->addAction(tr("Up"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Up; accept(); }); + subMenu->addAction(tr("Down"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Down; accept(); }); + subMenu->addSeparator(); + subMenu->addAction(tr("Button 1"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button1; accept(); }); + subMenu->addAction(tr("Button 2"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button2; accept(); }); + subMenu->addAction(tr("Button 3"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button3; accept(); }); + subMenu->addAction(tr("Button 4"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button4; accept(); }); + subMenu->addAction(tr("Button 5"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button5; accept(); }); + subMenu->addAction(tr("Button 6"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button6; accept(); }); + subMenu->addAction(tr("Button 7"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button7; accept(); }); + subMenu->addAction(tr("Button 8"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button8; accept(); }); + } + m_ui->pushButtonMouse->setMenu(menu); + } + { auto menu = new QMenu; menu->addAction(tr("Add/Modify Triggers..."), this, [this](){ m_mainWindow.showDefineTriggers(); reject(); }); diff --git a/src/editor/dialogs/addeventdialog.ui b/src/editor/dialogs/addeventdialog.ui index beb6733..fa16330 100644 --- a/src/editor/dialogs/addeventdialog.ui +++ b/src/editor/dialogs/addeventdialog.ui @@ -32,6 +32,10 @@ &Mouse + + + :/qtgameengine/icons/event-mouse.png:/qtgameengine/icons/event-mouse.png + @@ -133,7 +137,7 @@ - QDialogButtonBox::Cancel + QDialogButtonBox::StandardButton::Cancel true diff --git a/src/editor/icons/event-mouse.png b/src/editor/icons/event-mouse.png new file mode 100644 index 0000000000000000000000000000000000000000..3c2b67676dde4f6ba9b009754b8530433b653098 GIT binary patch literal 14337 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o0X7B(hF?F|&ShX=U`coMb!1@J*w6hZk(Ggg zfw4Hq-HBn{IhmIX3=9g%9znhg3{`3j3=J&|48Ir{7#dzMFq9fFFuY1&V6d9Oz#v{Q zXIG#N0|NtFlDE4H!+#K5uy^@n1_lPs0*}aI1_o|n5N2eUHAey@SXSVe4srw-zxwiwNLzPtoW9*L~jq{_BE@_vZIaH*Yd} z{$}ra%inLx9@MLc8vK~fzb4PF#P8$6|J8OpuO1$Lkbfrq{j}@b_H28)_1pP*T7|2Q ztQ7jV|NWUeGj|$)ez7gwYj4o2sX?XcKmM&d;?lg(j_2py_QvX(7v-n^yyKPcnlI`9 zw*Bmk|C*8f)eQfgUp;1?`RcGzJ9qc3(pQHza39v=(XPGmz5J=&x5Sqk@^0V0C2}t3Z)UEQ2)N(Q zx40we*g8UO2m*XMRI{!adT;5WH^n^jopRYkJF~k4$TYuc&{g3Y`;$3%)6hyJ-pqXFTYKGcip33ov+p3 z)$b`jeNNYU`}g-6Z%Rz36JdY^J^=dGUIoimR+HrM?* z{o?*MW)aT1Zho2jGuM0nFt9k+uIlgpwf~j;)%{7Q(o5}De~z4+-nK3NR$b=rrF(;& z_i_K!d$Rk){T=6P-#zxPzI9n{%dW8NuNIhYX}i|2bem;cAw55BII z5WMhWrQ2L-+tBLg4w&3|;xdz4LJCW%%FStG>ZmhKH>3H+vUBr!xcMTqF{yP8f z$D6;l&tEGQyEWwMi)ZrD^CRS==bxzDz5DLncYK=y%}%*JP!ivHqMk)xbCQy#gUUpg zCY}k5MhqN^Edm>MMZ~VrOHhqI8D?~&vux2^jwchG`3kzD?=an6AYH=zLB;>mlH!oR z3w|knWlmakCc}F1?VhT{++(p)<&}4qu75vwP2r|rC#5!~*+t!K|7I#L_Akw@-0buJ z$BBmj67MthOw~Phs%)07$%jq+-hLSZ_Z9Xz*7R!1zy0^(eE&Dz{gI``SFeVeZ~ZaP z{^mhF%k`|Yma`sk65AkPae$GJfj85`d4cfU7c50iekV8=aD}d5Qkm>ybYar8Ei5;c zqEomFrtiMP`ZH?ZwDpd8r*(Jo8ab++U{RQzv&q5z)$Lz;AH>xyHLq*Mudi8HRCW5j z{fvJHukbJ9e;sYPwaDw~lU2I4pEU#@9#%*`Y;oq)^k;4Sg8beL{N9XRIsM6PipN%c zGf%I}NRmjbOYGx1cm7q~wLhEHoTaY#tgT#}=Jxvj+rn?v-!v{%MMb}Eu3I?&b;t$b zU(@CO@BjDRNo4oc2ip9=7!fX&#elk zES6!-D{N}85xc?%EbJgW-ew(e{T3fi!m#a-Kxzq4^ z-R5->;W;<+Yd&lJ>)~ha@qKLIE+;KFNk(oKufYZOX%}n4gXP7(|JrcouYP;o&qvdp ze_zQLzqT%A_k@4@tyfu}vdXf&KUMKl7Z2-6K^5^OJs}f=6zYO}m>MVUIr`dx$FPKX z)`cl)3g%Cj-C@vOow|ibMb9^8+76yi*PgFEAI-0tK7CGTk(kGtIg^VIO!s-LeL0e9?dag^d^`z=7b__e=zlq_-si<#E z3tI$O?mXb)bO=?_;QMCvyJ4@aU%AcQUtfebyuVwU|E5~{&#~XNkIea+wAezqR`EP_ zJ}s^<&y(^{jlbaWGJzB7Q<)Z+Eev6{R6i-iZFAb;7h~ZcSEda2x9bca)L*D?|GzKk zXwtLef8;hNIh<*fW3Q5VQTy=L!rpc>xnHWsHEQY%<*Uv*g(z3(-kSM#f3(8?FMJ6y zN_@rj-^{~`W{8RjiwTPbYi0-i_x!8=wZ7D6Dce-3(5fX5W!EmdD8*~zTV5%a;qPwA zTI6@mTYkdylodS8mb%NIsGNxPoUr-;+qKPdDck>O?RP%3uVqJz^M>mC^SGE*uZn(Iw=9z@BmeEfSckynyBvc}TsJ#< zXRVmVX1iGZi%Ev~+of|I<1bDx;b2+9wuDt>>I?CCvh!r;t=wGq`fK$1`&Dz_p7!)B zGAf^#|Bp4bN2$k*tKj;F*N^V&PN>gf*NFOg%=nqXyxj+`*GNd&t@*X#)!|k4+ZLzI zpS3spTi(|2)tRMh88-#gO`G4iZp&5ihKG9&MjvnzJ2ibl@K^7-Oh*KdTQ1;So5>kw z{W^0s^AovKcjxYAUUzm+*r!|m3(nuvziF#D<-*m4^-ZxyzimFOXl<=-X)SoE&SUmO zGjXPO?00wznva~~-N`jaajvw3((#=P5h4OyAxqgx&+M>cm{YOQ!Thz|Us;P2Y1;zV zzH8j&{`%k5e@^F@+D`u#^X>V0=^F>wF7f;?X0D0INaCzI`QZLO;XlD{4;VfdTi3k! zK83&Ab$aExGsUUDrtiMEb3w7=^Ed1Cci&m(6kZmPeRsjt(D4!tvoMiR%#`!moh$+ms zDRP*0DrY9=r#(E=(i=tcCa}B;j4obq<6@F~x2(oS%V*1H%ufnCzDK(_b={fXGhaU{ zzTiDPeX8A*@4WYK{khxpv%QSrW~xZSPJZTj9}+z}7mBt`laJbW^yZ3Jfu)O9*%}?{ z+j9EmQpZ0>{bLTi-g91B;rf$JN1FGoI$XkX!sBPaJ=VEvj7zyTw8zRYT79y3d$8_S z-Mx40lJC>^%>F25p(Jy$Pe@_))9{dHnO%=g||ai~RbUc5mFh zXSWDTozM^02UQOO<(5i)P~RBC@`po>+e5ikuEAA+^#G^kVaWvRL>Y#%|)_M7g|KtAk{z=*JD&px{DTO+v zM+~d`u3pUIt%~7)5wBqWbk!Wz*XG#^zj9ZES5B*#)?9YkeT!N_LvRke?W+ARGB14H z6u%b=!G60v!%fxdCk!WqW~zHP&bqRIEkn9C<~BoYhPV5Kx=&kp7*_?g zzH*Rs;$)k<`12R{3+ulI&*doFHaAbbG4FD7mB5c?-5jR23p4LI_-X}mRm@smDQ*(_ z{zdMF#apI@vCY}?#PGuDn9J1%YW_X3PEdOMldEFNHxu^1-uy2sFPxsWD*Z!iEt7-W z2?j})U9t5Sbrt-Lf^Io&J$N-~6>t8ph{FeY<8yATKQ-<0Q@`o9u~sXa`|WQQ*GBwU z8QAi!ZT@HZEe-$lqz`CJo3Wr{>jjMl&ovrxt9zIC-aU3%$=1N~L)?SC7fgR`+qd$4 z)4nCnr5rEL?}`a;G`qa)meiJIVOM4?s4Z#u#+19X|K;KZ$t7F%aa+0BU;Ey8{%4b> zgI?UHgX_|_Z;Ed0yTbX)EQ3{dWvYX9hJ3A@iRb%FsTZlgr_{>XOy8S1ui`@Ov5jBY z9!s!RY-M1aO&B-cKO;nON)FC=U2A4U18_FAfU@vvRK@<@ww!w%?qw>>2qt`cFFITMZnxD z6PC9N?ru>xnCSmg;z!%eKMcPX`d!paklm&i6@GBy1CMf*#`2ExhH{o0{kKftx;wmo zp}Lpl)(O3(Uk}QyJO8ra!?jN#AGa~M#~=UPw@=~yi2_MRl?fh31+yNZ1zI& zZ}5L6J)zT{36{5Sa=l5iuWnS(bXF;7F1h58{>o-9dtZtBw$Jkxmz>qy`s~`AOB&aX z&lgh5d6WO-kwK?U(xL?|wi%9pW8EFZjXyj;b6#S4e#6x-MVC+e>bvuEYDhr^}{IskgE$BdNZApCu9W$uWGbgZ2Lv>foxo&LB_&ot9eZpsD(OI zdAV20tf>xL_q1)nZAVGRxDDF3Mc?j9Sihxy+x14<19kV_vEEx+=KTJ8$w4XN$TYFlf@!;82J?oA3o%%6Lea*VC@YRcJR?VEXQgT7*)^%n~xy$EnVb0ih z>lF)cP;|4y>Fmh0j9(VCmocwhelbe=i{aYUrjGGf0)MF)O!J?~@GbDS`EQ0P75k2G ztKMrk@m@LHC6C4W^uEma<~Q>XH=!}0_Ahu?3D zm)g){xrXtbz%)@**j@b z$hwzlO7ZUdh5mYd(N?Y3{_3+b-RtX`O%BecA>GZtRyzN6@PFlL%Nw=e=PNed<5312tvieg^) zYr(6%tN0sVR9)27Hf_7|;>v?792~5*7Ui_4AL75U#QG<%htmm` zcg6EX67*L`^2~X<;~(EIE9DY~4|eknIcJ=6Io5pcx}_EWjAJEstn)5}-4ZHU<#(yS z;igUsn@99Zg|&*STtXs33QCi8+Gk|XpMTP8o0rtTS*y{ifs1Vp+4qn$)uP++*0gJaZP;tkssYxL3Wp2fjDI$MVE*)B65}1!cAEt2 z;}%?2uHvt@FK8}VV8{Ne!)~RoWB7}>Sl(S;^;e%RNHz63&AfT$L8S|pzt)~(*DGq{ z7T9K#lX_#-r|XNwTy?vmWD*ZOtvklCr6)b$jP5Fp{zw1k%54$6af;`Q|6&H$SvCvr zO{{W%CA%*C(B>nXk8DX zc_6+aEb{bP(*=>U*0DMmM+Nblgfwq4U9onN--U?1t2Qcpep>xg>WlB+nBz^g=j^iQ zw;XtKK(;Y5z;1U<+)o{je>N3PA5tdNZi*{o{3~(p^?~9q3#~V_ZPj7^8(DvC>w?az zNa4oo5+@B3OtXJVzIp#<=QpDZ*KUQ#vu?fWFS}sx*4cKfYm#mEKeSe{eUSUC_SA2S zCvxX(=Tu8w(q9(uV;`ATaC2?y_Gx#cwni~=suY|$;??zu?MRBq-{Zy=KmUg&@K>>S z7&7ykF`Ga1yM8KZclTSpZPriD?kWFt?^Lz7ok!%vO&Y11DSRht?1}|;7~hqA$MeDN z>Dwp0jHQ_!zZ%6ZUy|af^3#7IxncQj`J2fO!e86sm}1v(e|5jGx604IdES-eDv2vg z|Gi?dKJ;z#-39Wu-rrj65dN|#maocxeU{J%R$)ojH_tVy9p+!)yT!F-uHCv`hxM;` z_cH%#ty_P;@!U1mT_Rg{uw@utSa&PTnn`!dylG5#m;e4^e!;pbR=>ISs^2DKh4;(% z%>O*iIqT(>qLoV@JPS8hzdMKH`DkbC{l2}?0S)T z%N_Zm=g-eEho@gRUgszfVY#6wL6zeK&kmUhJnL-2x4kyAi0M;k=YHaEskd>#!KlE+ z4)3$~q%~c;zU-Eq#9@xMrhf~#OQl|j#67Nkpx6INMZjGt(t%a#g2;(SY~_s(MFkAc zZ=Pp;Ea}5`R=FV{pj|AfQ8qvQhWUZ&1G|szX5;b`jnlN@u6+J^-IKdbb8cErF5cJl zd-ePEyX^Uk>dybvO=tILikWULU}Ieo{lTUqMtrB)C)EwNqRy^St;^PG4dGh%q{AZp zNV1TJ%(1C`Kj+=3pUot)v_djGZK7bt%q8yo=f}R^b!^u?y=~VfKJX~J)3jixV|UXt z=Z%|=dWo&(xcBYexqHhG>^;2q;9j;ON>Sz4v=lxyg-%Ws$d8@5S%!-pjq)2+h#Gf!J*jB!+_#4ah%+Xp?c>ZDa6ZHo(-u^geD0ycwcb&!$MjOMIvoC#j+%`2gUA^h~~Dv2DiYQ(f1sqTFXk%D*W2>nF}2n(5+v!S~IDQic1U z*PLM#Gw7aqAmx~Wkjz_|UYW)R<>%hdxL?QdYwL}33w#sSPM^QF_kdW~CZ0FtZ@0gZ zThLw7`pfZQaVs-7yVF9EXChoL9+zC{OK`uX{>Fpjmw|!81=e*@>$KM?Kd^atrbMl= zRWxHN(@n+KPxU8g1^H_=@?D&Gi=Sh$h@u1gtcx6bJhvU@WB4umKH|N_hjri9eb8fi zFZW*Hebk3{3)g>R&z|yiqAB~Ad1_f!H(RPxc@TT>h`bJ!k&lB zrzI+rf=(Ry!TICs{Z$OEGqqi>Pnmx9z;%O(oIYE2-A;dd_?su!lT|U(xEH9tky*?1 zYsu6_z7En^&VS`nI?qg8dO$U4Cr{1chYp4&v4d?uey)eC6mYH6OEkbYxk zg8a7UH%}jkzVrIiug2|@wr|_6cf<5eR9WqXby1Ov9s5uD{Nze$J2PRugWieMojjYu z%uX3S;FMdgxM8lW4118r6TT1P%PqNLj(Q3dbfjBoxQ5jQZu`L4d|PhA9=RDzB6roi zB+r<{i%aUem+tAi&Tp}_{L7p;CLzx!dM{+ZUQW2sJ!=W)f`wU`3NJ3-imq;Wmd*c_ z`PfHJd8V5cCytBf=`w_EoZG7q{4{tbmsOV zvYOz@_w#^|m;wKi#ujha1w2zV_%AtGc_mLcyD9RIW0&`omgYH=XA5!H91_~cbZN&a z+Xq(9E!bHC=L8&>&*j3Le&e{MLVHBnBkhQtydlfjt~y-)vTRwyv&?N;6Ar|#+kEE3 z`6d0`J9#IhZ&+dTFMc_LtM+{j2DLt2->rMMT`qfbA;kA8%LBHJGK_22=&j|HS{293 zoV)1j7r_O_`k!njI2-w02>)6t%NFH)IwSHzOlh`|!OZd>0w(@@xo&)a>u{UN-P2vv zUE~D!CGWO|KL_o;HT^rMufyP1+_c-lSSpxz$HB=aY+i!CwF(mI2OZ1~)jjB1Z`9#4 zsgPN7&Oz8Hs8tpNTHv!=$(HVR?O`Y4pouvzk$^n&Fj`{h_>UF(^!8S<#K_9tK8Th~fXBINu=fSf=yZ zmAyBegsywC@rG?@W9Yx!e(u2QJts6SbXs}2H&46Zv6hSHxoaDvY=(n-g8lRx47(!2 zU$AYseOvbJw*~PlSFlb|XmL%Nut;dC2-oz?og4JzIsj ze225tKIIoe0xj(c3uFRa7^ zd*-OmjaHd>O6=^#m{Nv^`yM+rZ`mWpzIK6Nl-QQuvP)qHPV*#a7&KfR1$}(P@G5}Meu0f63uB!HCrg%~r{{#}Pb(!EivsnEWtPkcnQ}@&J9V82+f8Nj zRQ3-VyqPQ^i^VP~OkFz1r8!Q;+tz{c-yN2qld8No4##aft)wONY_l7Kf!^2uU&JdC zRwmYmTioD@5RVEBnS9asGj74$GwJxu-jURZkN+HkOE zvVY^tniJs;(Q!+Z111TzFe)~a9hfYQDMn~a`sm%ab2*^lIhiq{q>X9ZDE*~qLACDUNPmDLnzY`o)r^&JTvtG|nySem@QQ4C1+jd>` zKU(d3IxDi5?N#9Pss&4Z1Xj#0yJ&Xc`w*e1Ddz6vX^07Rm6BPjc|`Xk=J2 z$yKjl(&jrXGgU9AFd0mG*uyGvke6p6-`R#bo<;U23Mzf)vHkQ7JDsoK8Kj_jz+Wdx zcY=dVp@Qj>l_Bh}g1eVFFvjI95L(*FBDCX7>V@00)`R>ua?KYc*fj=C0J;qIsf1Qa8U*?}F;G9hwi&L0RR7L80H>#N|(+ildX2QrN!1BcQr^B&^ zcUSJ0sy{87f9~z%-zC!w+9S{C#bp1;*j8ui&aD0~mn&ci`?11>%WhB0HHzTcbotzp zxxU@LHxrNFh<&?GX5y`&=q%>dKifnPq`li9?D1G^=YrcMw$YC3U$Oq>exv`!^P9_s z=_M_FZ0<7-KmEXdC}ocigIv0HyQAMpPD|cv7TFCvOqHH-lin(LTd2Qo^fsCovq0=9 zC*w_Z>!%F+4)x!1kepNDIPC;OCHIm!S0*rQh>HHZW?_Q<#^tx~Gu^G2Q>PGrq3hg% znEC%3yj%oXOCGxZVqChyjq^dE++xNP+@DNr8!o?jsx`rhQ8K_{XMxv4b5`fcr&OkT zYfa7h!!WO$NB*|tKC!Jce!h)g#o&6fYu01u^_=YDG5&Ryl@Xs_d+x6gufAS+{buu; zm)BHY*JiI=_B!`0WADXJx7bbMFPjQS95b_J-W9q1h4_QogTLo|mn=9_TJw4aPf%Qp z5KEBXDeuw=VV6#1u>{36JvBL@rkQ<};idlD6uuLouMJPKG-djWB|JXL9Ta%V(?a0o zYPM<5mrgHXk6o-?svU85ZCjY-g|u7#)s0_wnI5WQfAjTQQZ;A5M58H;c@xgWPGJ1B zjECi<(E1w(jP@n7dT?uMUu{^Pq5RoV>VivIv(L0hBUY{?t}3xTlWv=BbWPfP#C>x6 z#@Tn@XqjwlxPFCQAooLKYqR_o!5{IW3O6+zzAuUMw4I#vu1s@Jbj4N!z5Pa$e@yUn zsFqvKd|{@V3G2OwvTe=pE`8m_W;1b~*F3KUqFeWwvEI}={zSlI$(0F33eQ!7&o!?Q znZPEbsVKy=C@AcZT97Dp=iO=c;?{yAGwwXxdAPVeeF^I?b~e^utP8(?op+D9 z*0K7P@dD6 zfvnW6o1z-NO#60;TP0x6B=$=ISzfUV1Wi})t4utWJAo_6a}^_Z#*v%_mXaDLgsvIL zF4?+es+a=nBLVgkygPNu9X~zoOKH}bqG}|p;-7a~c7jTu0JqOWaWUqFKZG-rc#R$R zJkqyRP=7LE4ujZ+iC-s(O)pb=>vU;>wYyONUCSbU2F4{9bOL^}9}W+8I`-a1q3Qxx z!DXH+8LO_};p1{Uez(iJ!+VGHjDK#wqF?48pU#yp8K=Id(DDm&ZLmG-?d<6rbvJOo z39yz>aa`xAWl<#S$!8wXAEmrjP9b#ZrYj7VO6@lpVkSGsFt2X>IwzD}CC1Ckazb!X z#J8r<>#GuVStAv+Q)LC*wK5$PL{~5isU8fH^Ehy}a)DBBC(FwGtDCc$V=mbc46zou-vRwhudP*?otqITrAijAs|3pW?Y8P@0s?eP?dhtIP(Ho9w_Nsw%yL80n(1riDWs}+tzw!y^I*z^-$F`!50qY}G{sC- zv1AI0JJrekq%CGr^Z~QFByEqkj>Q$^cB?PR-=po-tR6R8svR;r5am|w_W zR(?9aAoJzYwbQ2^+57TwcweS#bcWHIEhpqZJY}AC{r*!|yF~ni?M|(i3UNDiSruA? z?yO?_G`B{3wd1^ra-U@OtxGCYSUE}Mr$Y2o^KHz*KYKb4RF)O;sziq9OehKplw*w4 z$xgLAq3}|@x$$GqbLI&*Kh5=F`gOTePN8m+u2_oC**}KGnMI3lT;8~G%x zd=YckYzyO_Ao;0z4U?t*c2!vw&pV&QPndobV4tMIw~T}!;BJm$zuu|A-xS0sExFZ>D53B8-DuN%Kh9Pn1?_b{zw z`7yQjO^da1{`Y--tY6>pPJLS>nA*HracgS7OQXs!uk|VpULFP=`z=%i ze$3FU;oKDQ>~zcomYrJS4g5!rhAX5W)pmE7q#C}i$zn+h3*+n?CzK`>8ac(W>Ff&P zKcPN_L7_W(4y&f_!bv>8HrB10+z=G9Q0auBrS3)r%^-#E4q_+#H*uHSkd8a9DHd>^`==YD=`Zg$^Ip-lDganrW-H*FGr?G{;ZqB!(?`#poj3@h!<)ci{M;Q9Py!{MvaUve+3 zbNucn9CP{4uAR%+d{fqLnH=Y!b~cl_XBoGCL(e45Pn;i&t#5LC>bNrbn1cMt3`zE) zxKFC=GfuOaF?&axiFFVp2HSq*uT_4vS#Dyt!pOTRrvlyDbzvj zl*mt}CsSf3DJxVKg>Pz1o|9&uAlShDggsJgbz}0rPx+FHu}>r?NCyRJHL-XZax(8! z`&cBFGIgrrY=y5btF*tqc5FW zSYFcpkMq+E8^wM`bI-@V2?BecD0Os&vmA;4bcAhR`n;Q-ag#L_o~wB0HFWGyoXCu*c~5-zet)u8{e7udm;KepA>V`dmi-PtwR%d_%9Q5y zfAxOLe!2a&COpq;(H-_kwdxc@56P8Up$(b)*!qlo+D;lIaC%2F7`fR^IjNweRHyLp zk)U+Sx||872Rzt}_+Fgz{=&FYVH!)rlY+RI1BEkBlwMfBrF9$QtbqAp3ZbfAO=tWH zS)7u4jpv<~n7gU& z_k*|kx$n0fx2U@0%bU6{Q}(r~WkVKM?uj4wS1~->w<`4P5ud>1eU?)1zQ@1cesAl& zo%U<5zFvDT@9O`R%L2DA%5y(@-BrsYGLvIT(d>{3`l)?q7*5~k+ScUAc3Pm4FJ z^`uj+4O+r0rBjybOs+goGVO!y3B%6GmlZCjPOD+nNwf2I+&6W7s+EVWraG@<-)UV- zmZ-?pS+W+wwaYHeoX}kN`|-Eq@AgaVK7PFY&3xY7+kRirJM}~I=zX96A&XCJW^0!; z`5xo`zK2^<+~{1kg!1yH7BC?@ZNZ`K*9cmRwT=#2@D^ZUPqkLy0G_~)Lou$+OIaUH|<++KFfc@{*C*$ z?PvDAkS)bu=DvIDU8yh2?p`b8@;be&C;jcRPFg2$FAlY$f`S5AJYP$%STsW3a_R&z-5mKwGq z_fuYa9xHAHad}LU*~$DS$uzo==@2JlrqbJ`oD+Q6zB_z6s@-_NVb^6@mA=0+Y}-zW zl{M5TYNzlOOm|k9HTA#kiJX~=_DxStgRCf37 z*N^U9|F&<}_pO(o`7YFX{J4DfIr+HD{vY4J=Z|pt|D{{;7>9`Pxe`hF@o7m%4 ze~y}a{neKr1qFW|{5Z&&EZzE~b%*n(E>){ozfz`i>(1$&%X`36x~(LPF}=h1Y9zzC zg7h7mr7WZ^+bygcK2Q6+=d*^zrnHamf6@z3yLsFL{3R#pa9Yi~DEIv)4>xd^Y1} zb&ov5|9UTl|0SC<;+0Hy-S@d6!@Bd>_qq3KK7KlST73PD*&jV#C@<`1G3}|%d6Ya^ zZuX7e>fbiaZvMoSy3)ZlL2B_#t|bLiS1YW2qS?vFIZ4!rU&ZfH2W#0U>uSearvxNf z&ZOB0JH}1CcS-)kVv(bcNlzEWF#q)of0eaB@N0$43Mb!1j`P&!%PQPXSyjVXUk@4V0RQu2)|u25JNn0;^{o5v{r`GwQ~vRF zGQUsX*vfsl=JT#hF^655e*&I-`Z`O_>wjh5JNd=!e>0|JN@hzYUrDt|P*h`F6mosW zLPP!IJh^g)-=5vbPQL7ZzROCYROMB`*}%SyeGmJ-AC!F9Q^MA~dG_Y%o7FA4txsEc zHy9t$tZRJGQyf<~hvm3$`{_b)jlS6d0p5R2)AXlFB>&jDueW%)eH#`P^(%cQd`e?l0rL@>fM1vpjBj+ER4R-Z{;4 z3?Jyc*Ll2#{oL+z*5`5x<`v8<{>Rz(sV}OpKw;kJl%~9^_eGx%Wig+kG$QgU**fPi8XT6W^;}?cT6`{&&s%`hxZy-n&m%zggTb|F+=E z#=GCbds9El9B-eTIT7|7x5ar%PPk;gbahr$LR8qS zRrmH6|G)RQdeu(Po#DmFp?B5qRNws_ayRzQ+&kx2Jqtg>e}4U%S^ty&?fDbB>Ye4g zpYNWodS~%n@_XjhJvS@wKl2SfD>w0P(6`g8X5KZ5PTLmX8$EZMO>S$JQ`n)sEl=)z zJ{_e~T@tzZs_N*y!uITESmowv2&s_-D`x(!*^NGBD-KXP|m;3xniRz!QX2Ng9 z$hSVpXR_4lz>*{vO0I(4tiiu~5O zY3?bzi-jM1UK;P4XZW{!YoY1mtYcSqC8X<|yD{lw@WV%U<`@4z5xeO2-Pk*Z@7~RN zSa}W>%&ax(4P}1_l;|-qkP- zAeB}ST?|GBh6cI@hPpy$?Z| z=o{%7=;vgyt$icons/event-create.png icons/event-destroy.png icons/event-draw.png + icons/event-mouse.png icons/event-step.png icons/exit.png icons/export-resources.png diff --git a/src/engine/gameengine.cpp b/src/engine/gameengine.cpp index 54b4cea..778db73 100644 --- a/src/engine/gameengine.cpp +++ b/src/engine/gameengine.cpp @@ -3,6 +3,9 @@ #include #include +#include +#include +#include #include "closeeventfilter.h" #include "projectcontainer.h" @@ -22,6 +25,8 @@ GameEngine::GameEngine(const ProjectContainer &project, QObject *parent) : m_glGameWindow.setModality(Qt::WindowModal); m_vulkanGameWindow.setModality(Qt::WindowModal); + m_glGameWindow.installEventFilter(this); + m_vulkanGameWindow.installEventFilter(this); m_frameTimer.setTimerType(Qt::PreciseTimer); m_frameTimer.setSingleShot(true); @@ -41,6 +46,81 @@ GameEngine::GameEngine(const ProjectContainer &project, QObject *parent) : }); } +bool GameEngine::eventFilter(QObject *watched, QEvent *event) +{ + if (watched != &m_glGameWindow && watched != &m_vulkanGameWindow) + return QObject::eventFilter(watched, event); + + auto *window = static_cast(watched); + const auto roomPosition = [this, window](QPointF position) { + return QPointF{position.x() * m_scene.roomSize.width() / std::max(1, window->width()), + position.y() * m_scene.roomSize.height() / std::max(1, window->height())}; + }; + + switch (event->type()) + { + case QEvent::FocusIn: + if (m_mouseSource != window) + { + m_scene.resetMouseInput(); + m_mouseSource = window; + } + break; + case QEvent::FocusOut: + case QEvent::Hide: + if (m_mouseSource == window) + { + m_scene.resetMouseInput(); + m_mouseSource = nullptr; + } + break; + case QEvent::Enter: + if (!m_mouseSource) + m_mouseSource = window; + if (m_mouseSource == window) + m_scene.queueMouseMove(roomPosition(static_cast(event)->position())); + break; + case QEvent::Leave: + if (m_mouseSource == window) + m_scene.queueMouseLeave(); + break; + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + { + auto *mouse = static_cast(event); + if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) + { + if (m_mouseSource != window) + { + m_scene.resetMouseInput(); + m_mouseSource = window; + } + } + if (m_mouseSource != window) + break; + const QPointF position = roomPosition(mouse->position()); + if (event->type() == QEvent::MouseMove) + m_scene.queueMouseMove(position); + else + m_scene.queueMouseButton(position, mouse->button(), + event->type() != QEvent::MouseButtonRelease); + break; + } + case QEvent::Wheel: + if (m_mouseSource == window) + { + auto *wheel = static_cast(event); + m_scene.queueMouseWheel(roomPosition(wheel->position()), wheel->angleDelta().y()); + } + break; + default: + break; + } + return QObject::eventFilter(watched, event); +} + void GameEngine::run() { if (m_project.rooms.empty()) diff --git a/src/engine/gameengine.h b/src/engine/gameengine.h index eae0efa..da06c90 100644 --- a/src/engine/gameengine.h +++ b/src/engine/gameengine.h @@ -21,6 +21,9 @@ public: explicit GameEngine(const ProjectContainer &project, QObject *parent = nullptr); void run(); +protected: + bool eventFilter(QObject *watched, QEvent *event) override; + private: const ProjectContainer &m_project; GameScene m_scene; @@ -28,6 +31,7 @@ private: GlGameWindow m_glGameWindow; VulkanGameWindow m_vulkanGameWindow; bool m_vulkanAvailable{}; + QWindow *m_mouseSource{}; QTimer m_frameTimer; QElapsedTimer m_elapsed; qint64 m_nextFrameNanoseconds{}; diff --git a/src/engine/gamescene.cpp b/src/engine/gamescene.cpp index 93dd82d..bf7940c 100644 --- a/src/engine/gamescene.cpp +++ b/src/engine/gamescene.cpp @@ -26,6 +26,37 @@ QPointF motion(double degrees, double speed) return {speed * std::cos(radians), -speed * std::sin(radians)}; } +std::optional mouseButtonEvent(Qt::MouseButton button, bool pressed, bool global) +{ + using Event = Object::EventType; + switch (button) + { + case Qt::LeftButton: + return global ? (pressed ? Event::MouseGlobalLeftPressed : Event::MouseGlobalLeftReleased) + : (pressed ? Event::MouseLeftPressed : Event::MouseLeftReleased); + case Qt::RightButton: + return global ? (pressed ? Event::MouseGlobalRightPressed : Event::MouseGlobalRightReleased) + : (pressed ? Event::MouseRightPressed : Event::MouseRightReleased); + case Qt::MiddleButton: + return global ? (pressed ? Event::MouseGlobalMiddlePressed : Event::MouseGlobalMiddleReleased) + : (pressed ? Event::MouseMiddlePressed : Event::MouseMiddleReleased); + default: + return std::nullopt; + } +} + +std::optional mouseHeldEvent(Qt::MouseButton button, bool global) +{ + using Event = Object::EventType; + switch (button) + { + case Qt::LeftButton: return global ? Event::MouseGlobalLeftButton : Event::MouseLeftButton; + case Qt::RightButton: return global ? Event::MouseGlobalRightButton : Event::MouseRightButton; + case Qt::MiddleButton: return global ? Event::MouseGlobalMiddleButton : Event::MouseMiddleButton; + default: return std::nullopt; + } +} + } // namespace void ObjectInstance::executeActions(const ActionsContainer &actions, GameScene &scene, @@ -260,6 +291,115 @@ void GameScene::removeDestroyed() std::erase_if(instances, [](const Instance &instance) { return instance.pendingDestroy; }); } +void GameScene::queueMouseMove(QPointF position) +{ + m_mouseInputs.push_back({MouseInput::Kind::Move, position}); +} + +void GameScene::queueMouseButton(QPointF position, Qt::MouseButton button, bool pressed) +{ + m_mouseInputs.push_back({pressed ? MouseInput::Kind::Press : MouseInput::Kind::Release, + position, button}); +} + +void GameScene::queueMouseWheel(QPointF position, int angleDeltaY) +{ + m_mouseInputs.push_back({MouseInput::Kind::Wheel, position, Qt::NoButton, angleDeltaY}); +} + +void GameScene::queueMouseLeave() +{ + m_mouseInputs.push_back({MouseInput::Kind::Leave, {}}); +} + +void GameScene::resetMouseInput() +{ + m_mouseInputs.push_back({MouseInput::Kind::Reset, {}}); +} + +void GameScene::dispatchMouseEvent(Object::EventType type, QPointF position, bool global) +{ + for (Instance &instance : instances) + if (!instance.pendingDestroy && + (global || collisionRect(instance).contains(position))) + instance.executeEvent(type, *this, m_executor); +} + +void GameScene::updateMouseHover() +{ + for (Instance &instance : instances) + { + if (instance.pendingDestroy) + continue; + const bool over = m_mouseInside && collisionRect(instance).contains(m_mousePosition); + if (over == instance.mouseOver) + continue; + instance.mouseOver = over; + instance.executeEvent(over ? Object::EventType::MouseEnter : Object::EventType::MouseLeave, + *this, m_executor); + } +} + +void GameScene::processMouseInput() +{ + const auto inputs = std::exchange(m_mouseInputs, {}); + const QRectF room{QPointF{}, QSizeF{roomSize}}; + for (const MouseInput &input : inputs) + { + if (input.kind == MouseInput::Kind::Leave || input.kind == MouseInput::Kind::Reset) + { + m_mouseInside = false; + if (input.kind == MouseInput::Kind::Reset) + m_mouseButtons = Qt::NoButton; + updateMouseHover(); + continue; + } + + m_mousePosition = input.position; + m_mouseInside = room.contains(input.position); + updateMouseHover(); + if (input.kind == MouseInput::Kind::Press || input.kind == MouseInput::Kind::Release) + { + const bool pressed = input.kind == MouseInput::Kind::Press; + if (pressed) + m_mouseButtons |= input.button; + else + m_mouseButtons &= ~Qt::MouseButtons{input.button}; + if (const auto local = mouseButtonEvent(input.button, pressed, false); local && m_mouseInside) + dispatchMouseEvent(*local, input.position, false); + if (const auto global = mouseButtonEvent(input.button, pressed, true); global && m_mouseInside) + dispatchMouseEvent(*global, input.position, true); + } + else if (input.kind == MouseInput::Kind::Wheel && m_mouseInside) + { + m_wheelRemainder += input.wheelDelta; + while (m_wheelRemainder >= 120) + { + dispatchMouseEvent(Object::EventType::MouseWheelUp, input.position, true); + m_wheelRemainder -= 120; + } + while (m_wheelRemainder <= -120) + { + dispatchMouseEvent(Object::EventType::MouseWheelDown, input.position, true); + m_wheelRemainder += 120; + } + } + } + + updateMouseHover(); // Instances can also move under a stationary cursor. + if (!m_mouseInside) + return; + if (m_mouseButtons == Qt::NoButton) + dispatchMouseEvent(Object::EventType::MouseNoButton, m_mousePosition, false); + for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton}) + { + if (!(m_mouseButtons & button)) + continue; + dispatchMouseEvent(*mouseHeldEvent(button, false), m_mousePosition, false); + dispatchMouseEvent(*mouseHeldEvent(button, true), m_mousePosition, true); + } +} + void GameScene::advanceTo(qint64 elapsedMilliseconds) { const qint64 targetStep = elapsedMilliseconds * roomSpeed / 1000; @@ -284,6 +424,7 @@ void GameScene::advanceTo(qint64 elapsedMilliseconds) break; } } + processMouseInput(); for (Instance &instance : instances) if (!instance.pendingDestroy) instance.executeEvent(Object::EventType::Step, *this, m_executor); @@ -295,6 +436,7 @@ void GameScene::advanceTo(qint64 elapsedMilliseconds) for (Instance &instance : instances) if (!instance.pendingDestroy) instance.step(); + updateMouseHover(); // Detect every pair at its moved position before collision actions can // change either instance. A sprite-size rectangle is our first mask. diff --git a/src/engine/gamescene.h b/src/engine/gamescene.h index 96db84c..c30a692 100644 --- a/src/engine/gamescene.h +++ b/src/engine/gamescene.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "projectcontainer.h" @@ -39,6 +40,7 @@ struct ObjectInstance int depth{}; bool visible{true}; bool pendingDestroy{}; + bool mouseOver{}; std::array alarms; void step(); @@ -71,6 +73,11 @@ struct GameScene std::vector drawOrder() const; void start(const ActionExecutor &executor); void destroyInstance(Instance &instance); + void queueMouseMove(QPointF position); + void queueMouseButton(QPointF position, Qt::MouseButton button, bool pressed); + void queueMouseWheel(QPointF position, int angleDeltaY); + void queueMouseLeave(); + void resetMouseInput(); void advanceTo(qint64 elapsedMilliseconds); void draw(); @@ -82,7 +89,23 @@ struct GameScene std::vector instances; private: + struct MouseInput + { + enum class Kind { Move, Press, Release, Wheel, Leave, Reset } kind; + QPointF position; + Qt::MouseButton button{Qt::NoButton}; + int wheelDelta{}; + }; + qint64 m_stepsDone{}; ActionExecutor m_executor; + std::vector m_mouseInputs; + QPointF m_mousePosition; + Qt::MouseButtons m_mouseButtons{Qt::NoButton}; + bool m_mouseInside{}; + int m_wheelRemainder{}; void removeDestroyed(); + void updateMouseHover(); + void dispatchMouseEvent(Object::EventType type, QPointF position, bool global); + void processMouseInput(); }; diff --git a/src/projectcontainer.h b/src/projectcontainer.h index 9123673..ee378a9 100644 --- a/src/projectcontainer.h +++ b/src/projectcontainer.h @@ -262,6 +262,53 @@ struct Object BeginStep, EndStep, Draw, + MouseLeftButton, + MouseRightButton, + MouseMiddleButton, + MouseNoButton, + MouseLeftPressed, + MouseRightPressed, + MouseMiddlePressed, + MouseLeftReleased, + MouseRightReleased, + MouseMiddleReleased, + MouseEnter, + MouseLeave, + MouseWheelUp, + MouseWheelDown, + MouseGlobalLeftButton, + MouseGlobalRightButton, + MouseGlobalMiddleButton, + MouseGlobalLeftPressed, + MouseGlobalRightPressed, + MouseGlobalMiddlePressed, + MouseGlobalLeftReleased, + MouseGlobalRightReleased, + MouseGlobalMiddleReleased, + MouseJoystick1Left, + MouseJoystick1Right, + MouseJoystick1Up, + MouseJoystick1Down, + MouseJoystick1Button1, + MouseJoystick1Button2, + MouseJoystick1Button3, + MouseJoystick1Button4, + MouseJoystick1Button5, + MouseJoystick1Button6, + MouseJoystick1Button7, + MouseJoystick1Button8, + MouseJoystick2Left, + MouseJoystick2Right, + MouseJoystick2Up, + MouseJoystick2Down, + MouseJoystick2Button1, + MouseJoystick2Button2, + MouseJoystick2Button3, + MouseJoystick2Button4, + MouseJoystick2Button5, + MouseJoystick2Button6, + MouseJoystick2Button7, + MouseJoystick2Button8, }; using events_container_t = std::map;