TrackViewDopeSheetBase.cpp 112 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "EditorDefs.h"
  9. #include "TrackViewDopeSheetBase.h"
  10. // Qt
  11. #include <QMenu>
  12. #include <QPainter>
  13. #include <QScrollBar>
  14. #include <QTimer>
  15. #include <QToolTip>
  16. // AzFramework
  17. #include <AzCore/std/sort.h>
  18. // AzQtComponents
  19. #include <AzQtComponents/Components/Widgets/ColorPicker.h>
  20. #include <AzQtComponents/Utilities/Conversions.h>
  21. // CryCommon
  22. #include <CryCommon/Maestro/Types/AnimValueType.h>
  23. #include <CryCommon/Maestro/Types/AnimParamType.h>
  24. #include <CryCommon/Maestro/Types/AssetBlendKey.h>
  25. // Editor
  26. #include "Controls/ReflectedPropertyControl/ReflectedPropertyCtrl.h"
  27. #include "Clipboard.h"
  28. #include "Util/fastlib.h"
  29. #include "TrackView/TrackViewNodes.h"
  30. #include "TVCustomizeTrackColorsDlg.h"
  31. #include "TrackView/TrackViewKeyPropertiesDlg.h"
  32. #define EDIT_DISABLE_GRAY_COLOR QColor(128, 128, 128)
  33. #define KEY_TEXT_COLOR QColor(0, 0, 50)
  34. #define INACTIVE_TEXT_COLOR QColor(128, 128, 128)
  35. namespace
  36. {
  37. const int kMarginForMagnetSnapping = 10;
  38. const unsigned int kDefaultTrackHeight = 16;
  39. }
  40. enum ETVMouseMode
  41. {
  42. eTVMouseMode_None = 0,
  43. eTVMouseMode_Select = 1,
  44. eTVMouseMode_Move,
  45. eTVMouseMode_Clone,
  46. eTVMouseMode_DragTime,
  47. eTVMouseMode_DragStartMarker,
  48. eTVMouseMode_DragEndMarker,
  49. eTVMouseMode_Paste,
  50. eTVMouseMode_SelectWithinTime,
  51. eTVMouseMode_StartTimeAdjust,
  52. eTVMouseMode_EndTimeAdjust
  53. };
  54. //////////////////////////////////////////////////////////////////////////
  55. CTrackViewDopeSheetBase::CTrackViewDopeSheetBase(QWidget* parent)
  56. : QWidget(parent)
  57. {
  58. m_bkgrBrush = palette().color(QPalette::Window);
  59. m_bkgrBrushEmpty = QColor(190, 190, 190);
  60. m_timeBkgBrush = QColor(0xE0, 0xE0, 0xE0);
  61. m_timeHighlightBrush = QColor(0xFF, 0x0, 0x0);
  62. m_selectedBrush = QColor(200, 200, 230);
  63. m_visibilityBrush = QColor(120, 120, 255);
  64. m_selectTrackBrush = QColor(100, 190, 255);
  65. m_timeScale = 1.0f;
  66. m_ticksStep = 10;
  67. m_bZoomDrag = false;
  68. m_bMoveDrag = false;
  69. m_leftOffset = 30;
  70. m_scrollOffset = QPoint(0, 0);
  71. m_mouseMode = eTVMouseMode_None;
  72. m_currentTime = 0.0f;
  73. m_storedTime = m_currentTime;
  74. m_rcSelect = QRect(0, 0, 0, 0);
  75. m_rubberBand = nullptr;
  76. m_scrollBar = new QScrollBar(Qt::Horizontal, this);
  77. connect(m_scrollBar, &QScrollBar::valueChanged, this, &CTrackViewDopeSheetBase::OnHScroll);
  78. m_keyTimeOffset = 0;
  79. m_currCursor = QCursor(Qt::ArrowCursor);
  80. m_mouseActionMode = eTVActionMode_MoveKey;
  81. m_scrollMin = 0;
  82. m_scrollMax = 1000;
  83. m_descriptionFont = QFont(QStringLiteral("Verdana"), 7);
  84. m_bCursorWasInKey = false;
  85. m_bJustSelected = false;
  86. m_snappingMode = eSnappingMode_SnapNone;
  87. m_snapFrameTime = 0.033333f;
  88. m_bMouseMovedAfterRButtonDown = false;
  89. m_stashedRecordModeWhileTimeDragging = false;
  90. m_tickDisplayMode = eTVTickMode_InSeconds;
  91. m_bEditLock = false;
  92. m_bFastRedraw = false;
  93. m_pLastTrackSelectedOnSpot = nullptr;
  94. m_wndPropsOnSpot = nullptr;
  95. #ifdef DEBUG
  96. m_redrawCount = 0;
  97. #endif
  98. m_bKeysMoved = false;
  99. ComputeFrameSteps(GetVisibleRange());
  100. m_crsLeftRight = Qt::SizeHorCursor;
  101. m_crsAddKey = CMFCUtils::LoadCursor(IDC_ARROW_ADDKEY);
  102. m_crsCross = CMFCUtils::LoadCursor(IDC_POINTER_OBJHIT);
  103. m_crsAdjustLR = CMFCUtils::LoadCursor(IDC_LEFTRIGHT);
  104. setMouseTracking(true);
  105. setFocusPolicy(Qt::StrongFocus);
  106. m_colorUpdateTrack = nullptr;
  107. m_colorUpdateKeyTime = 0;
  108. }
  109. //////////////////////////////////////////////////////////////////////////
  110. CTrackViewDopeSheetBase::~CTrackViewDopeSheetBase()
  111. {
  112. HideKeyPropertyCtrlOnSpot();
  113. GetIEditor()->GetAnimation()->RemoveListener(this);
  114. }
  115. //////////////////////////////////////////////////////////////////////////
  116. int CTrackViewDopeSheetBase::TimeToClient(float time) const
  117. {
  118. return static_cast<int>(m_leftOffset - m_scrollOffset.x() + (time * m_timeScale));
  119. }
  120. //////////////////////////////////////////////////////////////////////////
  121. Range CTrackViewDopeSheetBase::GetVisibleRange() const
  122. {
  123. Range r;
  124. r.start = (m_scrollOffset.x() - m_leftOffset) / m_timeScale;
  125. r.end = r.start + (m_rcClient.width()) / m_timeScale;
  126. Range extendedTimeRange(0.0f, m_timeRange.end);
  127. r = extendedTimeRange & r;
  128. return r;
  129. }
  130. //////////////////////////////////////////////////////////////////////////
  131. Range CTrackViewDopeSheetBase::GetTimeRange(const QRect& rc) const
  132. {
  133. Range r;
  134. r.start = (rc.left() - m_leftOffset + m_scrollOffset.x()) / m_timeScale;
  135. r.end = r.start + (rc.width()) / m_timeScale;
  136. r.start = TickSnap(r.start);
  137. r.end = TickSnap(r.end);
  138. // Intersect range with global time range.
  139. r = m_timeRange & r;
  140. return r;
  141. }
  142. //////////////////////////////////////////////////////////////////////////
  143. void CTrackViewDopeSheetBase::SetTimeRange(float start, float end)
  144. {
  145. if (m_timeMarked.start < start)
  146. {
  147. m_timeMarked.start = start;
  148. }
  149. if (m_timeMarked.end > end)
  150. {
  151. m_timeMarked.end = end;
  152. }
  153. m_timeRange.Set(start, end);
  154. SetHorizontalExtent(-m_leftOffset, static_cast<int>(m_timeRange.end * m_timeScale - m_leftOffset));
  155. }
  156. //////////////////////////////////////////////////////////////////////////
  157. void CTrackViewDopeSheetBase::SetTimeScale(float timeScale, float fAnchorTime)
  158. {
  159. const double fOldOffset = -fAnchorTime * m_timeScale;
  160. timeScale = std::max(timeScale, 0.001f);
  161. timeScale = std::min(timeScale, 100000.0f);
  162. m_timeScale = timeScale;
  163. int steps = 0;
  164. if (GetTickDisplayMode() == eTVTickMode_InSeconds)
  165. {
  166. m_ticksStep = 10;
  167. }
  168. else if (GetTickDisplayMode() == eTVTickMode_InFrames)
  169. {
  170. m_ticksStep = 1 / m_snapFrameTime;
  171. }
  172. else
  173. {
  174. assert(0);
  175. }
  176. double fPixelsPerTick;
  177. do
  178. {
  179. fPixelsPerTick = (1.0 / m_ticksStep) * (double)m_timeScale;
  180. if (fPixelsPerTick < 6.0)
  181. {
  182. m_ticksStep /= 2;
  183. }
  184. if (m_ticksStep <= 0)
  185. {
  186. m_ticksStep = 1;
  187. break;
  188. }
  189. steps++;
  190. }
  191. while (fPixelsPerTick < 6.0 && steps < 100);
  192. steps = 0;
  193. do
  194. {
  195. fPixelsPerTick = (1.0 / m_ticksStep) * (double)m_timeScale;
  196. if (fPixelsPerTick >= 12.0)
  197. {
  198. m_ticksStep *= 2;
  199. }
  200. if (m_ticksStep <= 0)
  201. {
  202. m_ticksStep = 1;
  203. break;
  204. }
  205. steps++;
  206. }
  207. while (fPixelsPerTick >= 12.0 && steps < 100);
  208. float fCurrentOffset = -fAnchorTime * m_timeScale;
  209. m_scrollOffset.rx() += static_cast<int>(fOldOffset - fCurrentOffset);
  210. m_scrollBar->setValue(m_scrollOffset.x());
  211. update();
  212. SetHorizontalExtent(-m_leftOffset, static_cast<int>(m_timeRange.end * m_timeScale));
  213. ComputeFrameSteps(GetVisibleRange());
  214. OnHScroll();
  215. }
  216. void CTrackViewDopeSheetBase::showEvent(QShowEvent* event)
  217. {
  218. QWidget::showEvent(event);
  219. GetIEditor()->GetAnimation()->AddListener(this);
  220. }
  221. //////////////////////////////////////////////////////////////////////////
  222. void CTrackViewDopeSheetBase::resizeEvent(QResizeEvent* event)
  223. {
  224. QWidget::resizeEvent(event);
  225. m_rcClient = rect();
  226. m_offscreenBitmap = QPixmap(m_rcClient.width(), m_rcClient.height());
  227. m_offscreenBitmap.fill(Qt::transparent);
  228. m_rcTimeline = rect();
  229. m_rcTimeline.setHeight(kDefaultTrackHeight);
  230. m_rcSummary = m_rcTimeline;
  231. m_rcSummary.setTop(m_rcTimeline.bottom());
  232. m_rcSummary.setBottom(m_rcSummary.top() + 8);
  233. SetHorizontalExtent(m_scrollMin, m_scrollMax);
  234. m_scrollBar->setGeometry(0, height() - m_scrollBar->sizeHint().height(), width(), m_scrollBar->sizeHint().height());
  235. QToolTip::hideText();
  236. }
  237. //////////////////////////////////////////////////////////////////////////
  238. void CTrackViewDopeSheetBase::wheelEvent(QWheelEvent* event)
  239. {
  240. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  241. if (!pSequence)
  242. {
  243. event->ignore();
  244. return;
  245. }
  246. float z = (event->angleDelta().y() > 0) ? (m_timeScale * 1.25f) : (m_timeScale * 0.8f);
  247. // Use m_mouseOverPos to get the local position in the timeline view instead of
  248. // event->pos() which seems to include the variable left panel of the view that
  249. // lists the tracks.
  250. float fAnchorTime = TimeFromPointUnsnapped(m_mouseOverPos);
  251. SetTimeScale(z, fAnchorTime);
  252. event->accept();
  253. }
  254. //////////////////////////////////////////////////////////////////////////
  255. void CTrackViewDopeSheetBase::OnHScroll()
  256. {
  257. // Get the current position of scroll box.
  258. int curpos = m_scrollBar->value();
  259. m_scrollOffset.setX(curpos);
  260. update();
  261. }
  262. int CTrackViewDopeSheetBase::GetScrollPos() const
  263. {
  264. return m_scrollBar->value();
  265. }
  266. //////////////////////////////////////////////////////////////////////////
  267. double CTrackViewDopeSheetBase::GetTickTime() const
  268. {
  269. if (GetTickDisplayMode() == eTVTickMode_InFrames)
  270. {
  271. return m_fFrameTickStep;
  272. }
  273. else
  274. {
  275. return 1.0f / m_ticksStep;
  276. }
  277. }
  278. //////////////////////////////////////////////////////////////////////////
  279. float CTrackViewDopeSheetBase::TickSnap(float time) const
  280. {
  281. double tickTime = GetTickTime();
  282. double t = floor(((double)time / tickTime) + 0.5);
  283. t *= tickTime;
  284. return static_cast<float>(t);
  285. }
  286. //////////////////////////////////////////////////////////////////////////
  287. float CTrackViewDopeSheetBase::TimeFromPoint(const QPoint& point) const
  288. {
  289. int x = point.x() - m_leftOffset + m_scrollOffset.x();
  290. float t = static_cast<float>(x) / m_timeScale;
  291. return TickSnap(t);
  292. }
  293. //////////////////////////////////////////////////////////////////////////
  294. float CTrackViewDopeSheetBase::TimeFromPointUnsnapped(const QPoint& point) const
  295. {
  296. int x = point.x() - m_leftOffset + m_scrollOffset.x();
  297. double t = (double)x / m_timeScale;
  298. return static_cast<float>(t);
  299. }
  300. void CTrackViewDopeSheetBase::mousePressEvent(QMouseEvent* event)
  301. {
  302. switch (event->button())
  303. {
  304. case Qt::LeftButton:
  305. OnLButtonDown(event->modifiers(), event->pos());
  306. break;
  307. case Qt::RightButton:
  308. OnRButtonDown(event->modifiers(), event->pos());
  309. break;
  310. case Qt::MiddleButton:
  311. OnMButtonDown(event->modifiers(), event->pos());
  312. break;
  313. default:
  314. break;
  315. }
  316. }
  317. void CTrackViewDopeSheetBase::mouseReleaseEvent(QMouseEvent* event)
  318. {
  319. switch (event->button())
  320. {
  321. case Qt::LeftButton:
  322. OnLButtonUp(event->modifiers(), event->pos());
  323. break;
  324. case Qt::RightButton:
  325. OnRButtonUp(event->modifiers(), event->pos());
  326. break;
  327. case Qt::MiddleButton:
  328. OnMButtonUp(event->modifiers(), event->pos());
  329. break;
  330. default:
  331. break;
  332. }
  333. }
  334. void CTrackViewDopeSheetBase::mouseDoubleClickEvent(QMouseEvent* event)
  335. {
  336. switch (event->button())
  337. {
  338. case Qt::LeftButton:
  339. OnLButtonDblClk(event->modifiers(), event->pos());
  340. break;
  341. default:
  342. break;
  343. }
  344. }
  345. //////////////////////////////////////////////////////////////////////////
  346. void CTrackViewDopeSheetBase::OnLButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
  347. {
  348. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  349. if (!sequence)
  350. {
  351. return;
  352. }
  353. HideKeyPropertyCtrlOnSpot();
  354. if (m_rcTimeline.contains(point))
  355. {
  356. m_mouseDownPos = point;
  357. // Clicked inside timeline.
  358. m_mouseMode = eTVMouseMode_DragTime;
  359. // If mouse over selected key, change cursor to left-right arrows.
  360. SetMouseCursor(m_crsLeftRight);
  361. m_stashedRecordModeWhileTimeDragging = GetIEditor()->GetAnimation()->IsRecordMode();
  362. GetIEditor()->GetAnimation()->SetRecording(false); // disable recording while dragging time
  363. SetCurrTime(TimeFromPoint(point));
  364. return;
  365. }
  366. if (m_bEditLock)
  367. {
  368. m_mouseDownPos = point;
  369. return;
  370. }
  371. if (m_mouseMode == eTVMouseMode_Paste)
  372. {
  373. m_mouseMode = eTVMouseMode_None;
  374. CTrackViewAnimNode* animNode = GetAnimNodeFromPoint(m_mouseOverPos);
  375. CTrackViewTrack* pTrack = GetTrackFromPoint(m_mouseOverPos);
  376. if (animNode)
  377. {
  378. AzToolsFramework::ScopedUndoBatch undoBatch("Paste Keys");
  379. sequence->DeselectAllKeys();
  380. sequence->PasteKeysFromClipboard(animNode, pTrack, ComputeSnappedMoveOffset());
  381. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  382. }
  383. SetMouseCursor(Qt::ArrowCursor);
  384. OnCaptureChanged();
  385. return;
  386. }
  387. m_mouseDownPos = point;
  388. // The summary region is used for moving already selected keys.
  389. if (m_rcSummary.contains(point))
  390. {
  391. CTrackViewKeyBundle selectedKeys = sequence->GetSelectedKeys();
  392. if (selectedKeys.GetKeyCount() > 0)
  393. {
  394. // Move/Clone Key Undo Begin
  395. GetIEditor()->BeginUndo();
  396. StoreMementoForTracksWithSelectedKeys();
  397. m_keyTimeOffset = 0;
  398. m_mouseMode = eTVMouseMode_Move;
  399. SetMouseCursor(m_crsLeftRight);
  400. return;
  401. }
  402. }
  403. bool bStart = false;
  404. CTrackViewKeyHandle keyHandle = CheckCursorOnStartEndTimeAdjustBar(point, bStart);
  405. if (keyHandle.IsValid())
  406. {
  407. return LButtonDownOnTimeAdjustBar(point, keyHandle, bStart);
  408. }
  409. keyHandle = FirstKeyFromPoint(point);
  410. if (!keyHandle.IsValid())
  411. {
  412. keyHandle = DurationKeyFromPoint(point);
  413. }
  414. if (keyHandle.IsValid())
  415. {
  416. return LButtonDownOnKey(point, keyHandle, modifiers);
  417. }
  418. if (m_mouseActionMode == eTVActionMode_AddKeys)
  419. {
  420. AddKeys(point, modifiers & Qt::ShiftModifier);
  421. return;
  422. }
  423. if (modifiers & Qt::ShiftModifier)
  424. {
  425. m_mouseMode = eTVMouseMode_SelectWithinTime;
  426. }
  427. else
  428. {
  429. m_mouseMode = eTVMouseMode_Select;
  430. }
  431. }
  432. //////////////////////////////////////////////////////////////////////////
  433. void CTrackViewDopeSheetBase::OnLButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
  434. {
  435. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  436. if (!pSequence)
  437. {
  438. return;
  439. }
  440. if (m_mouseMode == eTVMouseMode_Select)
  441. {
  442. // Check if any key are selected.
  443. m_rcSelect.translate(-m_scrollOffset);
  444. SelectKeys(m_rcSelect, modifiers & Qt::ControlModifier);
  445. m_rcSelect = QRect();
  446. m_rubberBand->deleteLater();
  447. m_rubberBand = nullptr;
  448. }
  449. else if (m_mouseMode == eTVMouseMode_SelectWithinTime)
  450. {
  451. m_rcSelect.translate(-m_scrollOffset);
  452. SelectAllKeysWithinTimeFrame(m_rcSelect, modifiers & Qt::ControlModifier);
  453. m_rcSelect = QRect();
  454. m_rubberBand->deleteLater();
  455. m_rubberBand = nullptr;
  456. }
  457. else if (m_mouseMode == eTVMouseMode_DragTime)
  458. {
  459. SetMouseCursor(Qt::ArrowCursor);
  460. // Notify that time was explicitly set
  461. GetIEditor()->GetAnimation()->TimeChanged(TimeFromPoint(point));
  462. if (m_stashedRecordModeWhileTimeDragging)
  463. {
  464. GetIEditor()->GetAnimation()->SetRecording(true); // re-enable recording that was disabled while dragging time
  465. m_stashedRecordModeWhileTimeDragging = false; // reset stashed value
  466. }
  467. }
  468. else if (m_mouseMode == eTVMouseMode_Paste)
  469. {
  470. SetMouseCursor(Qt::ArrowCursor);
  471. }
  472. OnCaptureChanged();
  473. m_keyTimeOffset = 0;
  474. m_keyForTimeAdjust = CTrackViewKeyHandle();
  475. AcceptUndo();
  476. update();
  477. }
  478. //////////////////////////////////////////////////////////////////////////
  479. void CTrackViewDopeSheetBase::OnLButtonDblClk(Qt::KeyboardModifiers modifiers, const QPoint& point)
  480. {
  481. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  482. if (!sequence || m_rcTimeline.contains(point) || m_bEditLock)
  483. {
  484. return;
  485. }
  486. CTrackViewKeyHandle keyHandle = FirstKeyFromPoint(point);
  487. if (!keyHandle.IsValid())
  488. {
  489. keyHandle = DurationKeyFromPoint(point);
  490. }
  491. else
  492. {
  493. CTrackViewTrack* pTrack = GetTrackFromPoint(point);
  494. if (pTrack)
  495. {
  496. CTrackViewSequenceNotificationContext context(sequence);
  497. AzToolsFramework::ScopedUndoBatch undoBatch("Select key");
  498. const std::vector<bool> beforeKeyState = sequence->SaveKeyStates();
  499. sequence->DeselectAllKeys();
  500. keyHandle.Select(true);
  501. const std::vector<bool> afterKeyState = sequence->SaveKeyStates();
  502. if (beforeKeyState != afterKeyState)
  503. {
  504. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  505. }
  506. m_keyTimeOffset = 0;
  507. if (pTrack->GetValueType() == AnimValueType::RGB)
  508. {
  509. // bring up color picker
  510. EditSelectedColorKey(pTrack);
  511. }
  512. else if (pTrack->GetValueType() != AnimValueType::Bool)
  513. {
  514. // Edit On Spot is blank (not useful) for boolean tracks so we disable dbl-clicking to bring it up for boolean tracks
  515. const QPoint p = QCursor::pos();
  516. bool bKeyChangeInSameTrack = m_pLastTrackSelectedOnSpot && pTrack == m_pLastTrackSelectedOnSpot;
  517. m_pLastTrackSelectedOnSpot = pTrack;
  518. ShowKeyPropertyCtrlOnSpot(p.x(), p.y(), sequence->GetSelectedKeys().GetKeyCount() > 1, bKeyChangeInSameTrack);
  519. }
  520. }
  521. return;
  522. }
  523. const bool bTryAddKeysInGroup = modifiers & Qt::ShiftModifier;
  524. AddKeys(point, bTryAddKeysInGroup);
  525. m_mouseMode = eTVMouseMode_None;
  526. }
  527. //////////////////////////////////////////////////////////////////////////
  528. void CTrackViewDopeSheetBase::OnMButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
  529. {
  530. OnRButtonDown(modifiers, point);
  531. }
  532. //////////////////////////////////////////////////////////////////////////
  533. void CTrackViewDopeSheetBase::OnMButtonUp(Qt::KeyboardModifiers modifiers, const QPoint& point)
  534. {
  535. OnRButtonUp(modifiers, point);
  536. }
  537. //////////////////////////////////////////////////////////////////////////
  538. void CTrackViewDopeSheetBase::OnRButtonDown(Qt::KeyboardModifiers modifiers, const QPoint& point)
  539. {
  540. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  541. if (!pSequence)
  542. {
  543. return;
  544. }
  545. HideKeyPropertyCtrlOnSpot();
  546. m_bCursorWasInKey = false;
  547. m_bMouseMovedAfterRButtonDown = false;
  548. if (m_rcTimeline.contains(point))
  549. {
  550. // Clicked inside timeline.
  551. // adjust markers.
  552. int nMarkerStart = TimeToClient(m_timeMarked.start);
  553. int nMarkerEnd = TimeToClient(m_timeMarked.end);
  554. if ((abs(point.x() - nMarkerStart)) < (abs(point.x() - nMarkerEnd)))
  555. {
  556. SetStartMarker(TimeFromPoint(point));
  557. m_mouseMode = eTVMouseMode_DragStartMarker;
  558. }
  559. else
  560. {
  561. SetEndMarker(TimeFromPoint(point));
  562. m_mouseMode = eTVMouseMode_DragEndMarker;
  563. }
  564. return;
  565. }
  566. m_mouseDownPos = point;
  567. if (modifiers & Qt::ShiftModifier) // alternative zoom
  568. {
  569. m_bZoomDrag = true;
  570. return;
  571. }
  572. CTrackViewKeyHandle keyHandle = FirstKeyFromPoint(point);
  573. if (!keyHandle.IsValid())
  574. {
  575. keyHandle = DurationKeyFromPoint(point);
  576. }
  577. if (keyHandle.IsValid())
  578. {
  579. m_bCursorWasInKey = true;
  580. CTrackViewNode* pNode = GetNodeFromPoint(point);
  581. CTrackViewTrack* pTrack = static_cast<CTrackViewTrack*>(pNode);
  582. keyHandle.Select(true);
  583. m_keyTimeOffset = 0;
  584. update();
  585. // Show a little pop-up menu for copy & delete.
  586. QMenu menu;
  587. CTrackViewKeyBundle selectedKeys = pSequence->GetSelectedKeys();
  588. const bool bEnableEditOnSpot = ((pTrack && pTrack->GetValueType() != AnimValueType::Bool) &&
  589. (selectedKeys.GetKeyCount() > 0 && selectedKeys.AreAllKeysOfSameType()));
  590. QAction* actionEditOnSpot = menu.addAction(tr("Edit On Spot"));
  591. actionEditOnSpot->setEnabled(bEnableEditOnSpot);
  592. menu.addSeparator();
  593. QAction* actionCopy = menu.addAction(tr("Copy"));
  594. menu.addSeparator();
  595. QAction* actionDelete = menu.addAction(tr("Delete"));
  596. const QPoint p = QCursor::pos();
  597. QAction* action = menu.exec(p);
  598. if (action == actionEditOnSpot)
  599. {
  600. bool bKeyChangeInSameTrack
  601. = m_pLastTrackSelectedOnSpot
  602. && selectedKeys.GetKeyCount() == 1
  603. && selectedKeys.GetKey(0).GetTrack() == m_pLastTrackSelectedOnSpot;
  604. if (selectedKeys.GetKeyCount() == 1)
  605. {
  606. m_pLastTrackSelectedOnSpot = selectedKeys.GetKey(0).GetTrack();
  607. }
  608. else
  609. {
  610. m_pLastTrackSelectedOnSpot = nullptr;
  611. }
  612. ShowKeyPropertyCtrlOnSpot(p.x(), p.y(), selectedKeys.GetKeyCount() > 1, bKeyChangeInSameTrack);
  613. }
  614. else if (action == actionCopy)
  615. {
  616. pSequence->CopyKeysToClipboard(true, false);
  617. }
  618. else if (action == actionDelete)
  619. {
  620. CUndo undo("Delete Keys");
  621. pSequence->DeleteSelectedKeys();
  622. }
  623. }
  624. else
  625. {
  626. m_bMoveDrag = true;
  627. }
  628. }
  629. //////////////////////////////////////////////////////////////////////////
  630. void CTrackViewDopeSheetBase::OnRButtonUp([[maybe_unused]] Qt::KeyboardModifiers modifiers, [[maybe_unused]] const QPoint& point)
  631. {
  632. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  633. if (!pSequence)
  634. {
  635. return;
  636. }
  637. m_bZoomDrag = false;
  638. m_bMoveDrag = false;
  639. OnCaptureChanged();
  640. m_mouseMode = eTVMouseMode_None;
  641. if (!m_bCursorWasInKey)
  642. {
  643. const bool bHasCopiedKey = (GetKeysInClickboard() != nullptr);
  644. if (bHasCopiedKey && m_bMouseMovedAfterRButtonDown == false) // Once moved, it means the user wanted to scroll, so no paste pop-up.
  645. {
  646. // Show a little pop-up menu for paste.
  647. QMenu menu;
  648. QAction* actionPaste = menu.addAction(tr("Paste"));
  649. QAction* action = menu.exec(QCursor::pos());
  650. if (action == actionPaste)
  651. {
  652. StartPasteKeys();
  653. }
  654. }
  655. }
  656. }
  657. //////////////////////////////////////////////////////////////////////////
  658. void CTrackViewDopeSheetBase::CancelDrag()
  659. {
  660. AcceptUndo();
  661. m_mouseMode = eTVMouseMode_None;
  662. }
  663. //////////////////////////////////////////////////////////////////////////
  664. void CTrackViewDopeSheetBase::mouseMoveEvent(QMouseEvent* event)
  665. {
  666. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  667. if (!pSequence)
  668. {
  669. return;
  670. }
  671. // To prevent the key moving while selecting
  672. if (m_bJustSelected)
  673. {
  674. m_bJustSelected = false;
  675. return;
  676. }
  677. // For some drags, make sure the left mouse button is still down.
  678. // If you drag off the window, and press the right mouse button,
  679. // and *then* release the left mouse button, QT will never tell us
  680. // about the release event.
  681. bool leftButtonPressed = event->buttons() & Qt::LeftButton;
  682. m_bMouseMovedAfterRButtonDown = true;
  683. m_mouseOverPos = event->pos();
  684. if (m_bZoomDrag && (event->modifiers() & Qt::ShiftModifier))
  685. {
  686. float fAnchorTime = TimeFromPointUnsnapped(m_mouseDownPos);
  687. SetTimeScale(m_timeScale * (1.0f + (event->pos().x() - m_mouseDownPos.x()) * 0.0025f), fAnchorTime);
  688. m_mouseDownPos = event->pos();
  689. return;
  690. }
  691. else
  692. {
  693. m_bZoomDrag = false;
  694. }
  695. if (m_bMoveDrag)
  696. {
  697. m_scrollOffset.setX(qBound(m_scrollMin, m_scrollOffset.x() + m_mouseDownPos.x() - event->pos().x(), m_scrollMax));
  698. m_mouseDownPos = event->pos();
  699. // Set the new position of the thumb (scroll box).
  700. m_scrollBar->setValue(m_scrollOffset.x());
  701. update();
  702. SetMouseCursor(m_crsLeftRight);
  703. return;
  704. }
  705. if (m_mouseMode == eTVMouseMode_Select
  706. || m_mouseMode == eTVMouseMode_SelectWithinTime)
  707. {
  708. MouseMoveSelect(event->pos());
  709. }
  710. else if (m_mouseMode == eTVMouseMode_Move)
  711. {
  712. if (leftButtonPressed)
  713. {
  714. MouseMoveMove(event->pos(), event->modifiers());
  715. }
  716. else
  717. {
  718. CancelDrag();
  719. }
  720. }
  721. else if (m_mouseMode == eTVMouseMode_Clone)
  722. {
  723. pSequence->CloneSelectedKeys();
  724. m_mouseMode = eTVMouseMode_Move;
  725. }
  726. else if (m_mouseMode == eTVMouseMode_DragTime)
  727. {
  728. if (leftButtonPressed)
  729. {
  730. MouseMoveDragTime(event->pos(), event->modifiers());
  731. }
  732. else
  733. {
  734. CancelDrag();
  735. }
  736. }
  737. else if (m_mouseMode == eTVMouseMode_DragStartMarker)
  738. {
  739. if (leftButtonPressed)
  740. {
  741. MouseMoveDragStartMarker(event->pos(), event->modifiers());
  742. }
  743. else
  744. {
  745. CancelDrag();
  746. }
  747. }
  748. else if (m_mouseMode == eTVMouseMode_DragEndMarker)
  749. {
  750. if (leftButtonPressed)
  751. {
  752. MouseMoveDragEndMarker(event->pos(), event->modifiers());
  753. }
  754. else
  755. {
  756. CancelDrag();
  757. }
  758. }
  759. else if (m_mouseMode == eTVMouseMode_Paste)
  760. {
  761. update();
  762. }
  763. else if (m_mouseMode == eTVMouseMode_StartTimeAdjust)
  764. {
  765. if (leftButtonPressed)
  766. {
  767. MouseMoveStartEndTimeAdjust(event->pos(), true);
  768. }
  769. else
  770. {
  771. CancelDrag();
  772. }
  773. }
  774. else if (m_mouseMode == eTVMouseMode_EndTimeAdjust)
  775. {
  776. if (leftButtonPressed)
  777. {
  778. MouseMoveStartEndTimeAdjust(event->pos(), false);
  779. }
  780. else
  781. {
  782. CancelDrag();
  783. }
  784. }
  785. else
  786. {
  787. //////////////////////////////////////////////////////////////////////////
  788. if (m_mouseActionMode == eTVActionMode_AddKeys)
  789. {
  790. SetMouseCursor(m_crsAddKey);
  791. }
  792. else
  793. {
  794. MouseMoveOver(event->pos());
  795. }
  796. }
  797. }
  798. //////////////////////////////////////////////////////////////////////////
  799. void CTrackViewDopeSheetBase::paintEvent(QPaintEvent* event)
  800. {
  801. QPainter painter(this);
  802. {
  803. // In case of the fast-redraw mode, just draw the saved bitmap.
  804. // Otherwise, actually redraw all things.
  805. // This mode is helpful when playing a sequence if the sequence has a lot of keys.
  806. if (!m_bFastRedraw)
  807. {
  808. QLinearGradient gradient(rect().topLeft(), rect().bottomLeft());
  809. gradient.setColorAt(0, QColor(250, 250, 250));
  810. gradient.setColorAt(1, QColor(220, 220, 220));
  811. painter.fillRect(rect(), gradient);
  812. if (GetIEditor()->GetAnimation()->GetSequence())
  813. {
  814. if (m_bEditLock)
  815. {
  816. painter.fillRect(event->rect(), EDIT_DISABLE_GRAY_COLOR);
  817. }
  818. DrawControl(&painter, event->rect());
  819. }
  820. }
  821. }
  822. if (GetIEditor()->GetAnimation()->GetSequence())
  823. {
  824. // Drawing the timeline is handled separately. In other words, it's not saved to the 'm_offscreenBitmap'.
  825. // This is for the fast-redraw mode mentioned above.
  826. DrawTimeline(&painter, event->rect());
  827. }
  828. #ifdef DEBUG
  829. painter.setFont(m_descriptionFont);
  830. painter.setPen(QColor(255, 255, 255));
  831. painter.setBrush(QColor(0, 0, 0));
  832. const QString redrawCountStr = QString::fromLatin1("Redraw Count: %1").arg(m_redrawCount);
  833. QRect redrawCountRect(0, 0, 150, 20);
  834. QRect bounds;
  835. painter.drawText(redrawCountRect, Qt::AlignLeft | Qt::TextSingleLine, redrawCountStr, &bounds);
  836. painter.fillRect(bounds, Qt::black);
  837. painter.drawText(redrawCountRect, Qt::AlignLeft | Qt::TextSingleLine, redrawCountStr);
  838. ++m_redrawCount;
  839. #endif
  840. }
  841. //////////////////////////////////////////////////////////////////////////
  842. void CTrackViewDopeSheetBase::SelectAllKeysWithinTimeFrame(const QRect& rc, const bool bMultiSelection)
  843. {
  844. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  845. if (!sequence)
  846. {
  847. return;
  848. }
  849. AzToolsFramework::ScopedUndoBatch undoBatch("Select keys");
  850. const std::vector<bool> beforeKeyState = sequence->SaveKeyStates();
  851. if (!bMultiSelection)
  852. {
  853. sequence->DeselectAllKeys();
  854. }
  855. // put selection rectangle from client to track space.
  856. QRect trackRect = rc;
  857. trackRect.translate(m_scrollOffset);
  858. Range selTime = GetTimeRange(trackRect);
  859. CTrackViewTrackBundle tracks = sequence->GetAllTracks();
  860. CTrackViewSequenceNotificationContext context(sequence);
  861. for (unsigned int i = 0; i < tracks.GetCount(); ++i)
  862. {
  863. CTrackViewTrack* pTrack = tracks.GetTrack(i);
  864. // Check which keys we intersect.
  865. for (unsigned int j = 0; j < pTrack->GetKeyCount(); j++)
  866. {
  867. CTrackViewKeyHandle keyHandle = pTrack->GetKey(j);
  868. const float time = keyHandle.GetTime();
  869. if (selTime.IsInside(time))
  870. {
  871. keyHandle.Select(true);
  872. }
  873. }
  874. }
  875. const std::vector<bool> afterKeyState = sequence->SaveKeyStates();
  876. if (beforeKeyState != afterKeyState)
  877. {
  878. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  879. }
  880. }
  881. //////////////////////////////////////////////////////////////////////////
  882. void CTrackViewDopeSheetBase::SetMouseCursor(const QCursor& cursor)
  883. {
  884. m_currCursor = cursor;
  885. setCursor(m_currCursor);
  886. }
  887. //////////////////////////////////////////////////////////////////////////
  888. void CTrackViewDopeSheetBase::SetCurrTime(float time)
  889. {
  890. if (time < m_timeRange.start)
  891. {
  892. time = m_timeRange.start;
  893. }
  894. if (time > m_timeRange.end)
  895. {
  896. time = m_timeRange.end;
  897. }
  898. GetIEditor()->GetAnimation()->SetTime(time);
  899. }
  900. //////////////////////////////////////////////////////////////////////////
  901. void CTrackViewDopeSheetBase::OnTimeChanged(float newTime)
  902. {
  903. int x1 = TimeToClient(m_currentTime);
  904. int x2 = TimeToClient(newTime);
  905. m_currentTime = newTime;
  906. m_bFastRedraw = true;
  907. const QRect rc(QPoint(x1 - 3, m_rcClient.top()), QPoint(x1 + 4, m_rcClient.bottom()));
  908. update(rc);
  909. const QRect rc1(QPoint(x2 - 3, m_rcClient.top()), QPoint(x2 + 4, m_rcClient.bottom()));
  910. update(rc1);
  911. m_bFastRedraw = false;
  912. }
  913. //////////////////////////////////////////////////////////////////////////
  914. void CTrackViewDopeSheetBase::SetStartMarker(float fTime)
  915. {
  916. m_timeMarked.start = fTime;
  917. if (m_timeMarked.start < m_timeRange.start)
  918. {
  919. m_timeMarked.start = m_timeRange.start;
  920. }
  921. if (m_timeMarked.start > m_timeRange.end)
  922. {
  923. m_timeMarked.start = m_timeRange.end;
  924. }
  925. if (m_timeMarked.start > m_timeMarked.end)
  926. {
  927. m_timeMarked.end = m_timeMarked.start;
  928. }
  929. GetIEditor()->GetAnimation()->SetMarkers(m_timeMarked);
  930. update();
  931. }
  932. //////////////////////////////////////////////////////////////////////////
  933. void CTrackViewDopeSheetBase::SetEndMarker(float fTime)
  934. {
  935. m_timeMarked.end = fTime;
  936. if (m_timeMarked.end < m_timeRange.start)
  937. {
  938. m_timeMarked.end = m_timeRange.start;
  939. }
  940. if (m_timeMarked.end > m_timeRange.end)
  941. {
  942. m_timeMarked.end = m_timeRange.end;
  943. }
  944. if (m_timeMarked.start > m_timeMarked.end)
  945. {
  946. m_timeMarked.start = m_timeMarked.end;
  947. }
  948. GetIEditor()->GetAnimation()->SetMarkers(m_timeMarked);
  949. update();
  950. }
  951. //////////////////////////////////////////////////////////////////////////
  952. void CTrackViewDopeSheetBase::SetMouseActionMode(ETVActionMode mode)
  953. {
  954. m_mouseActionMode = mode;
  955. if (mode == eTVActionMode_AddKeys)
  956. {
  957. setCursor(m_crsAddKey);
  958. }
  959. }
  960. //////////////////////////////////////////////////////////////////////////
  961. CTrackViewNode* CTrackViewDopeSheetBase::GetNodeFromPointRec(CTrackViewNode* pCurrentNode, const QPoint& point)
  962. {
  963. QRect currentNodeRect = GetNodeRect(pCurrentNode);
  964. if (currentNodeRect.top() > point.y())
  965. {
  966. return nullptr;
  967. }
  968. if (currentNodeRect.bottom() >= point.y())
  969. {
  970. return pCurrentNode;
  971. }
  972. if (pCurrentNode->GetExpanded())
  973. {
  974. unsigned int childCount = pCurrentNode->GetChildCount();
  975. for (unsigned int i = 0; i < childCount; ++i)
  976. {
  977. CTrackViewNode* pFoundNode = GetNodeFromPointRec(pCurrentNode->GetChild(i), point);
  978. if (pFoundNode)
  979. {
  980. return pFoundNode;
  981. }
  982. }
  983. }
  984. return nullptr;
  985. }
  986. //////////////////////////////////////////////////////////////////////////
  987. CTrackViewNode* CTrackViewDopeSheetBase::GetNodeFromPoint(const QPoint& point)
  988. {
  989. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  990. return GetNodeFromPointRec(pSequence, point);
  991. }
  992. //////////////////////////////////////////////////////////////////////////
  993. CTrackViewAnimNode* CTrackViewDopeSheetBase::GetAnimNodeFromPoint(const QPoint& point)
  994. {
  995. CTrackViewNode* pNode = GetNodeFromPoint(point);
  996. if (pNode)
  997. {
  998. if (pNode->GetNodeType() == eTVNT_Track)
  999. {
  1000. CTrackViewTrack* pTrack = static_cast<CTrackViewTrack*>(pNode);
  1001. return static_cast<CTrackViewAnimNode*>(pTrack->GetAnimNode());
  1002. }
  1003. else if (pNode->GetNodeType() == eTVNT_AnimNode)
  1004. {
  1005. return static_cast<CTrackViewAnimNode*>(pNode);
  1006. }
  1007. }
  1008. return nullptr;
  1009. }
  1010. //////////////////////////////////////////////////////////////////////////
  1011. CTrackViewTrack* CTrackViewDopeSheetBase::GetTrackFromPoint(const QPoint& point)
  1012. {
  1013. CTrackViewNode* pNode = GetNodeFromPoint(point);
  1014. if (pNode && pNode->GetNodeType() == eTVNT_Track)
  1015. {
  1016. return static_cast<CTrackViewTrack*>(pNode);
  1017. }
  1018. return nullptr;
  1019. }
  1020. //////////////////////////////////////////////////////////////////////////
  1021. void CTrackViewDopeSheetBase::SetHorizontalExtent(int min, int max)
  1022. {
  1023. m_scrollMin = min;
  1024. m_scrollMax = max;
  1025. m_scrollBar->setPageStep(m_rcClient.width() / 2);
  1026. m_scrollBar->setRange(min, max - m_scrollBar->pageStep() * 2 + m_leftOffset);
  1027. };
  1028. //////////////////////////////////////////////////////////////////////////
  1029. XmlNodeRef CTrackViewDopeSheetBase::GetKeysInClickboard()
  1030. {
  1031. CClipboard clip(this);
  1032. if (clip.IsEmpty())
  1033. {
  1034. return nullptr;
  1035. }
  1036. if (clip.GetTitle() != "Track view keys")
  1037. {
  1038. return nullptr;
  1039. }
  1040. XmlNodeRef copyNode = clip.Get();
  1041. if (copyNode == nullptr || strcmp(copyNode->getTag(), "CopyKeysNode"))
  1042. {
  1043. return nullptr;
  1044. }
  1045. int nNumTracksToPaste = copyNode->getChildCount();
  1046. if (nNumTracksToPaste == 0)
  1047. {
  1048. return nullptr;
  1049. }
  1050. return copyNode;
  1051. }
  1052. //////////////////////////////////////////////////////////////////////////
  1053. void CTrackViewDopeSheetBase::StartPasteKeys()
  1054. {
  1055. m_clipboardKeys = GetKeysInClickboard();
  1056. if (m_clipboardKeys)
  1057. {
  1058. m_mouseMode = eTVMouseMode_Paste;
  1059. // If mouse over selected key, change cursor to left-right arrows.
  1060. SetMouseCursor(m_crsLeftRight);
  1061. m_mouseDownPos = m_mouseOverPos;
  1062. }
  1063. }
  1064. void CTrackViewDopeSheetBase::keyPressEvent(QKeyEvent* event)
  1065. {
  1066. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1067. if (!sequence)
  1068. {
  1069. return;
  1070. }
  1071. // HAVE TO INCLUDE CASES FOR THESE IN THE ShortcutOverride handler in ::event() below
  1072. if (event->matches(QKeySequence::Delete))
  1073. {
  1074. CUndo undo("Delete Keys");
  1075. sequence->DeleteSelectedKeys();
  1076. return;
  1077. }
  1078. if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down || event->key() == Qt::Key_Right || event->key() == Qt::Key_Left)
  1079. {
  1080. CTrackViewKeyBundle keyBundle = sequence->GetSelectedKeys();
  1081. CTrackViewKeyHandle keyHandle = keyBundle.GetSingleSelectedKey();
  1082. if (keyHandle.IsValid())
  1083. {
  1084. switch (event->key())
  1085. {
  1086. case Qt::Key_Up:
  1087. keyHandle = keyHandle.GetAboveKey();
  1088. break;
  1089. case Qt::Key_Down:
  1090. keyHandle = keyHandle.GetBelowKey();
  1091. break;
  1092. case Qt::Key_Right:
  1093. keyHandle = keyHandle.GetNextKey();
  1094. break;
  1095. case Qt::Key_Left:
  1096. keyHandle = keyHandle.GetPrevKey();
  1097. break;
  1098. }
  1099. if (keyHandle.IsValid())
  1100. {
  1101. CTrackViewSequenceNotificationContext context(sequence);
  1102. const std::vector<bool> beforeKeyState = sequence->SaveKeyStates();
  1103. AzToolsFramework::ScopedUndoBatch undoBatch("Select Key");
  1104. sequence->DeselectAllKeys();
  1105. keyHandle.Select(true);
  1106. const std::vector<bool> afterKeyState = sequence->SaveKeyStates();
  1107. if (beforeKeyState != afterKeyState)
  1108. {
  1109. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1110. }
  1111. }
  1112. }
  1113. return;
  1114. }
  1115. if (event->matches(QKeySequence::Copy))
  1116. {
  1117. sequence->CopyKeysToClipboard(true, false);
  1118. }
  1119. else if (event->matches(QKeySequence::Paste))
  1120. {
  1121. StartPasteKeys();
  1122. }
  1123. else if (event->matches(QKeySequence::Undo))
  1124. {
  1125. GetIEditor()->Undo();
  1126. }
  1127. else if (event->matches(QKeySequence::Redo))
  1128. {
  1129. GetIEditor()->Redo();
  1130. }
  1131. else
  1132. {
  1133. return QWidget::keyPressEvent(event);
  1134. }
  1135. }
  1136. bool CTrackViewDopeSheetBase::event(QEvent* e)
  1137. {
  1138. if (e->type() == QEvent::ShortcutOverride)
  1139. {
  1140. // since we respond to the following things, let Qt know so that shortcuts don't override us
  1141. bool respondsToEvent = false;
  1142. QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
  1143. switch (keyEvent->key())
  1144. {
  1145. case Qt::Key_Delete:
  1146. case Qt::Key_Up:
  1147. case Qt::Key_Down:
  1148. case Qt::Key_Left:
  1149. case Qt::Key_Right:
  1150. respondsToEvent = true;
  1151. break;
  1152. default:
  1153. respondsToEvent =
  1154. keyEvent->matches(QKeySequence::Copy) ||
  1155. keyEvent->matches(QKeySequence::Paste) ||
  1156. keyEvent->matches(QKeySequence::Undo) ||
  1157. keyEvent->matches(QKeySequence::Redo);
  1158. break;
  1159. }
  1160. if (respondsToEvent)
  1161. {
  1162. e->accept();
  1163. return true;
  1164. }
  1165. }
  1166. return QWidget::event(e);
  1167. }
  1168. //////////////////////////////////////////////////////////////////////////
  1169. void CTrackViewDopeSheetBase::ShowKeyTooltip(CTrackViewKeyHandle& keyHandle, const QPoint& point)
  1170. {
  1171. if (m_lastTooltipPos == point)
  1172. {
  1173. return;
  1174. }
  1175. m_lastTooltipPos = point;
  1176. const float time = keyHandle.GetTime();
  1177. const char* desc = keyHandle.GetDescription();
  1178. QString tipText;
  1179. if (GetTickDisplayMode() == eTVTickMode_InSeconds)
  1180. {
  1181. tipText = tr("%1, {%2}").arg(time, 0, 'f', 3).arg(desc);
  1182. }
  1183. else
  1184. {
  1185. tipText = tr("%1, {%2}").arg(ftoi(time / m_snapFrameTime)).arg(desc);
  1186. }
  1187. QToolTip::showText(point, tipText);
  1188. }
  1189. //////////////////////////////////////////////////////////////////////////
  1190. void CTrackViewDopeSheetBase::OnCaptureChanged()
  1191. {
  1192. AcceptUndo();
  1193. m_bZoomDrag = false;
  1194. m_bMoveDrag = false;
  1195. }
  1196. //////////////////////////////////////////////////////////////////////////
  1197. bool CTrackViewDopeSheetBase::IsOkToAddKeyHere(const CTrackViewTrack* pTrack, float time) const
  1198. {
  1199. for (unsigned int i = 0; i < pTrack->GetKeyCount(); ++i)
  1200. {
  1201. const CTrackViewKeyConstHandle& keyHandle = pTrack->GetKey(i);
  1202. if (keyHandle.GetTime() == time)
  1203. {
  1204. return false;
  1205. }
  1206. }
  1207. return true;
  1208. }
  1209. //////////////////////////////////////////////////////////////////////////
  1210. void CTrackViewDopeSheetBase::MouseMoveSelect(const QPoint& point)
  1211. {
  1212. SetMouseCursor(Qt::ArrowCursor);
  1213. QRect rc(m_mouseDownPos, point);
  1214. rc = rc.normalized();
  1215. QRect rcClient = rect();
  1216. rc = rc.intersected(rcClient);
  1217. if (m_rubberBand == nullptr)
  1218. {
  1219. m_rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
  1220. }
  1221. m_rubberBand->show();
  1222. if (m_mouseMode == eTVMouseMode_SelectWithinTime)
  1223. {
  1224. rc.setTop(m_rcClient.top());
  1225. rc.setBottom(m_rcClient.bottom());
  1226. }
  1227. m_rcSelect = rc;
  1228. m_rubberBand->setGeometry(m_rcSelect);
  1229. }
  1230. //////////////////////////////////////////////////////////////////////////
  1231. void CTrackViewDopeSheetBase::MouseMoveStartEndTimeAdjust(const QPoint& p, bool bStart)
  1232. {
  1233. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1234. if (!sequence)
  1235. {
  1236. return;
  1237. }
  1238. SetMouseCursor(m_crsAdjustLR);
  1239. const QPoint point(qBound(m_rcClient.left(), p.x(), m_rcClient.right()), p.y());
  1240. const QPoint ofs = point - m_mouseDownPos;
  1241. CTrackViewKeyHandle& keyHandle = m_keyForTimeAdjust;
  1242. // TODO: Refactor this Time Range Key stuff.
  1243. ICharacterKey characterKey;
  1244. AZ::IAssetBlendKey assetBlendKey;
  1245. ITimeRangeKey* timeRangeKey = nullptr;
  1246. if (keyHandle.GetTrack()->GetValueType() == AnimValueType::AssetBlend)
  1247. {
  1248. keyHandle.GetKey(&assetBlendKey);
  1249. timeRangeKey = &assetBlendKey;
  1250. }
  1251. else
  1252. {
  1253. // This will work for both character & time range keys because
  1254. // ICharacterKey is derived from ITimeRangeKey. Not the most beautiful code.
  1255. keyHandle.GetKey(&characterKey);
  1256. timeRangeKey = &characterKey;
  1257. }
  1258. float& timeToAdjust = bStart ? timeRangeKey->m_startTime : timeRangeKey->m_endTime;
  1259. // Undo the last offset.
  1260. timeToAdjust += -m_keyTimeOffset;
  1261. // Apply a new offset.
  1262. m_keyTimeOffset = (ofs.x() / m_timeScale) * timeRangeKey->m_speed;
  1263. timeToAdjust += m_keyTimeOffset;
  1264. // Check the validity.
  1265. if (bStart)
  1266. {
  1267. if (timeToAdjust < 0)
  1268. {
  1269. timeToAdjust = 0;
  1270. }
  1271. else if (timeToAdjust > timeRangeKey->GetValidEndTime())
  1272. {
  1273. timeToAdjust = timeRangeKey->GetValidEndTime();
  1274. }
  1275. }
  1276. else
  1277. {
  1278. float endTime = AZStd::min(timeRangeKey->GetValidEndTime(), timeRangeKey->m_duration);
  1279. if (timeToAdjust < timeRangeKey->m_startTime)
  1280. {
  1281. timeToAdjust = timeRangeKey->m_startTime;
  1282. }
  1283. else if (timeToAdjust > endTime)
  1284. {
  1285. timeToAdjust = endTime;
  1286. }
  1287. }
  1288. keyHandle.SetKey(timeRangeKey);
  1289. update();
  1290. }
  1291. //////////////////////////////////////////////////////////////////////////
  1292. void CTrackViewDopeSheetBase::MouseMoveMove(const QPoint& p, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
  1293. {
  1294. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  1295. CTrackViewSequenceNotificationContext context(pSequence);
  1296. SetMouseCursor(m_crsLeftRight);
  1297. const QPoint point(qBound(m_rcClient.left(), p.x(), m_rcClient.right()), p.y());
  1298. // Reset tracks to their initial state before starting the move
  1299. for (auto iter = m_trackMementos.begin(); iter != m_trackMementos.end(); ++iter)
  1300. {
  1301. CTrackViewTrack* pTrack = iter->first;
  1302. const TrackMemento& trackMemento = iter->second;
  1303. pTrack->RestoreFromMemento(trackMemento.m_memento);
  1304. const unsigned int numKeys = static_cast<unsigned int>(trackMemento.m_keySelectionStates.size());
  1305. for (unsigned int i = 0; i < numKeys; ++i)
  1306. {
  1307. pTrack->GetKey(i).Select(trackMemento.m_keySelectionStates[i]);
  1308. }
  1309. }
  1310. CTrackViewKeyHandle keyHandle = FirstKeyFromPoint(m_mouseDownPos);
  1311. if (!keyHandle.IsValid())
  1312. {
  1313. keyHandle = DurationKeyFromPoint(m_mouseDownPos);
  1314. }
  1315. float oldTime;
  1316. if (keyHandle.IsValid())
  1317. {
  1318. oldTime = keyHandle.GetTime();
  1319. }
  1320. else
  1321. {
  1322. oldTime = TimeFromPointUnsnapped(m_mouseDownPos);
  1323. }
  1324. QPoint ofs = point - m_mouseDownPos;
  1325. float timeOffset = ofs.x() / m_timeScale;
  1326. float newTime = oldTime + timeOffset;
  1327. // Snap it, if necessary.
  1328. ESnappingMode snappingMode = GetKeyModifiedSnappingMode();
  1329. if (snappingMode == eSnappingMode_SnapFrame)
  1330. {
  1331. snappingMode = m_snappingMode;
  1332. }
  1333. if (snappingMode == eSnappingMode_SnapMagnet)
  1334. {
  1335. newTime = MagnetSnap(newTime, GetAnimNodeFromPoint(m_mouseOverPos));
  1336. }
  1337. else if (snappingMode == eSnappingMode_SnapTick)
  1338. {
  1339. newTime = TickSnap(newTime);
  1340. }
  1341. else if (snappingMode == eSnappingMode_SnapFrame)
  1342. {
  1343. newTime = FrameSnap(newTime);
  1344. }
  1345. Range extendedTimeRange(0.0f, m_timeRange.end);
  1346. extendedTimeRange.ClipValue(newTime);
  1347. timeOffset = newTime - oldTime; // Re-compute the time offset using snapped & clipped 'newTime'.
  1348. if (timeOffset == 0.0f)
  1349. {
  1350. return;
  1351. }
  1352. m_bKeysMoved = true;
  1353. if (m_mouseActionMode == eTVActionMode_ScaleKey)
  1354. {
  1355. float tscale = 0.005f;
  1356. float tofs = ofs.x() * tscale;
  1357. tofs = pSequence->ClipTimeOffsetForScaling(1 + tofs) - 1;
  1358. // Offset all selected keys by this offset.
  1359. pSequence->ScaleSelectedKeys(1 + tofs);
  1360. m_keyTimeOffset = tofs;
  1361. }
  1362. else
  1363. {
  1364. // Offset all selected keys by this offset.
  1365. if (m_mouseActionMode == eTVActionMode_SlideKey)
  1366. {
  1367. timeOffset = pSequence->ClipTimeOffsetForSliding(timeOffset);
  1368. pSequence->SlideKeys(timeOffset);
  1369. }
  1370. else
  1371. {
  1372. timeOffset = pSequence->ClipTimeOffsetForOffsetting(timeOffset);
  1373. pSequence->OffsetSelectedKeys(timeOffset);
  1374. }
  1375. if (CheckVirtualKey(Qt::Key_Menu))
  1376. {
  1377. CTrackViewKeyBundle selectedKeys = pSequence->GetSelectedKeys();
  1378. CTrackViewKeyHandle selectedKey = selectedKeys.GetSingleSelectedKey();
  1379. if (selectedKey.IsValid())
  1380. {
  1381. GetIEditor()->GetAnimation()->SetTime(selectedKey.GetTime());
  1382. }
  1383. }
  1384. m_keyTimeOffset = timeOffset;
  1385. }
  1386. // The time of the selected keys has likely just changed. OnKeySelectionChanged() so the
  1387. // UI elements of the key properties control will update.
  1388. pSequence->OnKeySelectionChanged();
  1389. }
  1390. void CTrackViewDopeSheetBase::MouseMoveDragTime(const QPoint& point, Qt::KeyboardModifiers modifiers)
  1391. {
  1392. const QPoint p(qBound(m_rcClient.left(), point.x(), m_rcClient.right()),
  1393. qBound(m_rcClient.top(), point.y(), m_rcClient.bottom()));
  1394. float time = TimeFromPointUnsnapped(p);
  1395. m_timeRange.ClipValue(time);
  1396. bool bSnap = (modifiers & Qt::ControlModifier);
  1397. if (bSnap)
  1398. {
  1399. time = TickSnap(time);
  1400. }
  1401. SetCurrTime(time);
  1402. }
  1403. void CTrackViewDopeSheetBase::MouseMoveDragStartMarker(const QPoint& point, Qt::KeyboardModifiers modifiers)
  1404. {
  1405. const QPoint p(qBound(m_rcClient.left(), point.x(), m_rcClient.right()),
  1406. qBound(m_rcClient.top(), point.y(), m_rcClient.bottom()));
  1407. bool bNoSnap = (modifiers & Qt::ControlModifier);
  1408. float time = TimeFromPointUnsnapped(p);
  1409. m_timeRange.ClipValue(time);
  1410. if (!bNoSnap)
  1411. {
  1412. time = TickSnap(time);
  1413. }
  1414. SetStartMarker(time);
  1415. }
  1416. void CTrackViewDopeSheetBase::MouseMoveDragEndMarker(const QPoint& point, Qt::KeyboardModifiers modifiers)
  1417. {
  1418. const QPoint p(qBound(m_rcClient.left(), point.x(), m_rcClient.right()),
  1419. qBound(m_rcClient.top(), point.y(), m_rcClient.bottom()));
  1420. bool bNoSnap = (modifiers & Qt::ControlModifier);
  1421. float time = TimeFromPointUnsnapped(p);
  1422. m_timeRange.ClipValue(time);
  1423. if (!bNoSnap)
  1424. {
  1425. time = TickSnap(time);
  1426. }
  1427. SetEndMarker(time);
  1428. }
  1429. void CTrackViewDopeSheetBase::MouseMoveOver(const QPoint& point)
  1430. {
  1431. // No mouse mode.
  1432. SetMouseCursor(Qt::ArrowCursor);
  1433. bool bStart = false;
  1434. CTrackViewKeyHandle keyHandle = CheckCursorOnStartEndTimeAdjustBar(point, bStart);
  1435. if (keyHandle.IsValid())
  1436. {
  1437. SetMouseCursor(m_crsAdjustLR);
  1438. return;
  1439. }
  1440. keyHandle = FirstKeyFromPoint(point);
  1441. if (!keyHandle.IsValid())
  1442. {
  1443. keyHandle = DurationKeyFromPoint(point);
  1444. }
  1445. if (keyHandle.IsValid())
  1446. {
  1447. CTrackViewTrack* pTrack = GetTrackFromPoint(point);
  1448. if (pTrack && keyHandle.IsSelected())
  1449. {
  1450. // If mouse over selected key, change cursor to left-right arrows.
  1451. SetMouseCursor(m_crsLeftRight);
  1452. }
  1453. else
  1454. {
  1455. SetMouseCursor(m_crsCross);
  1456. }
  1457. if (pTrack)
  1458. {
  1459. ShowKeyTooltip(keyHandle, mapToGlobal(point));
  1460. }
  1461. }
  1462. else
  1463. {
  1464. QToolTip::hideText();
  1465. }
  1466. }
  1467. float CTrackViewDopeSheetBase::MagnetSnap(float newTime, const CTrackViewAnimNode* pNode) const
  1468. {
  1469. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  1470. if (!pSequence)
  1471. {
  1472. return newTime;
  1473. }
  1474. CTrackViewKeyBundle keys = pSequence->GetKeysInTimeRange(newTime - kMarginForMagnetSnapping / m_timeScale,
  1475. newTime + kMarginForMagnetSnapping / m_timeScale);
  1476. if (keys.GetKeyCount() > 0)
  1477. {
  1478. // By default, just use the first key that belongs to the time range as a magnet.
  1479. newTime = keys.GetKey(0).GetTime();
  1480. // But if there is an in-range key in a sibling track, use it instead.
  1481. // Here a 'sibling' means a track that belongs to a same node.
  1482. for (unsigned int i = 0; i < keys.GetKeyCount(); ++i)
  1483. {
  1484. CTrackViewKeyHandle keyHandle = keys.GetKey(i);
  1485. if (keyHandle.GetTrack()->GetAnimNode() == pNode)
  1486. {
  1487. newTime = keyHandle.GetTime();
  1488. break;
  1489. }
  1490. }
  1491. }
  1492. return newTime;
  1493. }
  1494. //////////////////////////////////////////////////////////////////////////
  1495. float CTrackViewDopeSheetBase::FrameSnap(float time) const
  1496. {
  1497. double t = floor((double)time / m_snapFrameTime + 0.5);
  1498. t = t * m_snapFrameTime;
  1499. return static_cast<float>(t);
  1500. }
  1501. //////////////////////////////////////////////////////////////////////////
  1502. void CTrackViewDopeSheetBase::ShowKeyPropertyCtrlOnSpot(int x, int y, [[maybe_unused]] bool bMultipleKeysSelected, bool bKeyChangeInSameTrack)
  1503. {
  1504. if (m_keyPropertiesDlg == nullptr)
  1505. {
  1506. return;
  1507. }
  1508. if (m_wndPropsOnSpot == nullptr)
  1509. {
  1510. m_wndPropsOnSpot = new ReflectedPropertyControl(this);
  1511. m_wndPropsOnSpot->Setup(true, 150);
  1512. m_wndPropsOnSpot->setWindowFlags(Qt::CustomizeWindowHint | Qt::Popup | Qt::WindowStaysOnTopHint);
  1513. m_wndPropsOnSpot->SetStoreUndoByItems(false);
  1514. bKeyChangeInSameTrack = false;
  1515. }
  1516. if (bKeyChangeInSameTrack)
  1517. {
  1518. m_wndPropsOnSpot->ClearSelection();
  1519. m_wndPropsOnSpot->ReloadValues();
  1520. }
  1521. else
  1522. {
  1523. m_keyPropertiesDlg->PopulateVariables(m_wndPropsOnSpot);
  1524. }
  1525. m_wndPropsOnSpot->show();
  1526. m_wndPropsOnSpot->move(x, y);
  1527. m_wndPropsOnSpot->ExpandAll();
  1528. QTimer::singleShot(0, [this]() {
  1529. m_wndPropsOnSpot->resize(m_wndPropsOnSpot->sizeHint());
  1530. });
  1531. }
  1532. //////////////////////////////////////////////////////////////////////////
  1533. void CTrackViewDopeSheetBase::HideKeyPropertyCtrlOnSpot()
  1534. {
  1535. if (m_wndPropsOnSpot)
  1536. {
  1537. m_wndPropsOnSpot->hide();
  1538. m_wndPropsOnSpot->ClearSelection();
  1539. }
  1540. }
  1541. //////////////////////////////////////////////////////////////////////////
  1542. void CTrackViewDopeSheetBase::SetScrollOffset(int hpos)
  1543. {
  1544. m_scrollBar->setValue(hpos);
  1545. m_scrollOffset.setX(hpos);
  1546. update();
  1547. }
  1548. //////////////////////////////////////////////////////////////////////////
  1549. void CTrackViewDopeSheetBase::LButtonDownOnTimeAdjustBar([[maybe_unused]] const QPoint& point, CTrackViewKeyHandle& keyHandle, bool bStart)
  1550. {
  1551. m_keyTimeOffset = 0;
  1552. m_keyForTimeAdjust = keyHandle;
  1553. GetIEditor()->BeginUndo();
  1554. if (bStart)
  1555. {
  1556. m_mouseMode = eTVMouseMode_StartTimeAdjust;
  1557. }
  1558. else
  1559. {
  1560. // TODO: Refactor this Time Range Key stuff.
  1561. ICharacterKey characterKey;
  1562. AZ::IAssetBlendKey assetBlendKey;
  1563. ITimeRangeKey* timeRangeKey = nullptr;
  1564. if (keyHandle.GetTrack()->GetValueType() == AnimValueType::AssetBlend)
  1565. {
  1566. keyHandle.GetKey(&assetBlendKey);
  1567. timeRangeKey = &assetBlendKey;
  1568. }
  1569. else
  1570. {
  1571. // This will work for both character & time range keys because
  1572. // ICharacterKey is derived from ITimeRangeKey. Not the most beautiful code.
  1573. keyHandle.GetKey(&characterKey);
  1574. timeRangeKey = &characterKey;
  1575. }
  1576. // In case of the end time, make it have a valid (not zero)
  1577. // end time, first.
  1578. if (timeRangeKey->m_endTime == 0)
  1579. {
  1580. timeRangeKey->m_endTime = timeRangeKey->m_duration;
  1581. keyHandle.SetKey(timeRangeKey);
  1582. }
  1583. m_mouseMode = eTVMouseMode_EndTimeAdjust;
  1584. }
  1585. SetMouseCursor(m_crsAdjustLR);
  1586. }
  1587. //////////////////////////////////////////////////////////////////////////
  1588. void CTrackViewDopeSheetBase::LButtonDownOnKey([[maybe_unused]] const QPoint& point, CTrackViewKeyHandle& keyHandle, Qt::KeyboardModifiers modifiers)
  1589. {
  1590. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1591. AZ_Assert(sequence, "Expected a valid sequence.");
  1592. if (!sequence)
  1593. {
  1594. return;
  1595. }
  1596. if (!keyHandle.IsSelected() && !(modifiers & Qt::ControlModifier))
  1597. {
  1598. CTrackViewSequenceNotificationContext context(sequence);
  1599. AzToolsFramework::ScopedUndoBatch undoBatch("Select keys");
  1600. const std::vector<bool> beforeKeyState = sequence->SaveKeyStates();
  1601. sequence->DeselectAllKeys();
  1602. m_bJustSelected = true;
  1603. m_keyTimeOffset = 0;
  1604. keyHandle.Select(true);
  1605. ChangeSequenceTrackSelection(sequence, keyHandle.GetTrack());
  1606. const std::vector<bool> afterKeyState = sequence->SaveKeyStates();
  1607. if (beforeKeyState != afterKeyState)
  1608. {
  1609. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1610. }
  1611. }
  1612. else
  1613. {
  1614. GetIEditor()->CancelUndo();
  1615. }
  1616. // Move/Clone Key Undo Begin
  1617. GetIEditor()->BeginUndo();
  1618. StoreMementoForTracksWithSelectedKeys();
  1619. if (modifiers & Qt::ShiftModifier)
  1620. {
  1621. m_mouseMode = eTVMouseMode_Clone;
  1622. SetMouseCursor(m_crsLeftRight);
  1623. }
  1624. else
  1625. {
  1626. m_mouseMode = eTVMouseMode_Move;
  1627. SetMouseCursor(m_crsLeftRight);
  1628. }
  1629. update();
  1630. }
  1631. //////////////////////////////////////////////////////////////////////////
  1632. void CTrackViewDopeSheetBase::ChangeSequenceTrackSelection(CTrackViewSequence* sequenceWithTrack, CTrackViewTrack* trackToSelect) const
  1633. {
  1634. // Deselect all currently selected tracks that aren't the keyHandle's Track, then ensure the trackToSelect is selected
  1635. CTrackViewTrackBundle prevSelectedTracks;
  1636. prevSelectedTracks = sequenceWithTrack->GetSelectedTracks();
  1637. for (unsigned int i = 0; i < prevSelectedTracks.GetCount(); i++)
  1638. {
  1639. CTrackViewTrack* prevSelectedTrack = prevSelectedTracks.GetTrack(i);
  1640. if (prevSelectedTrack != trackToSelect)
  1641. {
  1642. prevSelectedTrack->SetSelected(false);
  1643. }
  1644. }
  1645. trackToSelect->SetSelected(true);
  1646. }
  1647. //////////////////////////////////////////////////////////////////////////
  1648. void CTrackViewDopeSheetBase::ChangeSequenceTrackSelection(CTrackViewSequence* sequence, CTrackViewTrackBundle tracksToSelect, bool multiTrackSelection) const
  1649. {
  1650. if (!multiTrackSelection)
  1651. {
  1652. // Deselect any tracks not in the tracksToSelect bundle
  1653. CTrackViewTrackBundle prevSelectedTracks;
  1654. prevSelectedTracks = sequence->GetSelectedTracks();
  1655. for (int i = prevSelectedTracks.GetCount(); --i >= 0; )
  1656. {
  1657. bool deselectTrack = true;
  1658. CTrackViewTrack* prevSelectedTrack = prevSelectedTracks.GetTrack(i);
  1659. for (int j = tracksToSelect.GetCount(); --j >= 0; )
  1660. {
  1661. CTrackViewTrack* selectTrackCandidate = tracksToSelect.GetTrack(j);
  1662. if (selectTrackCandidate == prevSelectedTrack)
  1663. {
  1664. // selectTrackCandidate is already selected
  1665. tracksToSelect.RemoveTrack(selectTrackCandidate);
  1666. deselectTrack = false;
  1667. break;
  1668. }
  1669. }
  1670. if (deselectTrack)
  1671. {
  1672. prevSelectedTrack->SetSelected(false);
  1673. }
  1674. }
  1675. }
  1676. // Add remaining tracks in tracksToSelect bundle to track selection
  1677. for (int j = tracksToSelect.GetCount(); --j >= 0; )
  1678. {
  1679. tracksToSelect.GetTrack(j)->SetSelected(true);
  1680. }
  1681. }
  1682. //////////////////////////////////////////////////////////////////////////
  1683. bool CTrackViewDopeSheetBase::CreateColorKey(CTrackViewTrack* pTrack, float keyTime)
  1684. {
  1685. bool keyCreated = false;
  1686. AZ::Vector3 vColor(0, 0, 0);
  1687. pTrack->GetValue(keyTime, vColor);
  1688. const AZ::Color defaultColor(
  1689. clamp_tpl<AZ::u8>(static_cast<AZ::u8>(FloatToIntRet(vColor.GetX())), 0, 255),
  1690. clamp_tpl<AZ::u8>(static_cast<AZ::u8>(FloatToIntRet(vColor.GetY())), 0, 255),
  1691. clamp_tpl<AZ::u8>(static_cast<AZ::u8>(FloatToIntRet(vColor.GetZ())), 0, 255),
  1692. 255);
  1693. AzQtComponents::ColorPicker dlg(AzQtComponents::ColorPicker::Configuration::RGB, QString(), this);
  1694. dlg.setWindowTitle(tr("Select Color"));
  1695. dlg.setCurrentColor(defaultColor);
  1696. dlg.setSelectedColor(defaultColor);
  1697. if (dlg.exec() == QDialog::Accepted)
  1698. {
  1699. const AZ::Color col = dlg.currentColor();
  1700. ColorF colArray(col.GetR8(), col.GetG8(), col.GetB8(), col.GetA8());
  1701. CTrackViewSequence* sequence = pTrack->GetSequence();
  1702. if (nullptr != sequence)
  1703. {
  1704. CTrackViewSequenceNotificationContext context(sequence);
  1705. AzToolsFramework::ScopedUndoBatch undoBatch("Set Key");
  1706. const unsigned int numChildNodes = pTrack->GetChildCount();
  1707. for (unsigned int i = 0; i < numChildNodes; ++i)
  1708. {
  1709. CTrackViewTrack* subTrack = static_cast<CTrackViewTrack*>(pTrack->GetChild(i));
  1710. if (IsOkToAddKeyHere(subTrack, keyTime))
  1711. {
  1712. CTrackViewKeyHandle newKey = subTrack->CreateKey(keyTime);
  1713. I2DBezierKey bezierKey;
  1714. newKey.GetKey(&bezierKey);
  1715. bezierKey.value = Vec2(keyTime, colArray[i]);
  1716. newKey.SetKey(&bezierKey);
  1717. keyCreated = true;
  1718. }
  1719. }
  1720. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1721. }
  1722. }
  1723. return keyCreated;
  1724. }
  1725. void CTrackViewDopeSheetBase::OnCurrentColorChange(const AZ::Color& color)
  1726. {
  1727. // This is while the color picker is up
  1728. // so we want to update the property but not store an undo
  1729. UpdateColorKey(AzQtComponents::toQColor(color), false);
  1730. }
  1731. void CTrackViewDopeSheetBase::UpdateColorKey(const QColor& color, bool addToUndo)
  1732. {
  1733. ColorF colArray(static_cast<f32>(color.red()), static_cast<f32>(color.green()), static_cast<f32>(color.blue()), static_cast<f32>(color.alpha()));
  1734. CTrackViewSequence* sequence = m_colorUpdateTrack->GetSequence();
  1735. if (nullptr != sequence)
  1736. {
  1737. CTrackViewSequenceNotificationContext context(sequence);
  1738. if (addToUndo)
  1739. {
  1740. AzToolsFramework::ScopedUndoBatch undoBatch("Set Key");
  1741. UpdateColorKeyHelper(colArray);
  1742. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1743. }
  1744. else
  1745. {
  1746. UpdateColorKeyHelper(colArray);
  1747. }
  1748. // We want this to take affect now
  1749. if (!addToUndo)
  1750. {
  1751. GetIEditor()->GetAnimation()->ForceAnimation();
  1752. }
  1753. }
  1754. }
  1755. void CTrackViewDopeSheetBase::UpdateColorKeyHelper(const ColorF& color)
  1756. {
  1757. const unsigned int numChildNodes = m_colorUpdateTrack->GetChildCount();
  1758. for (unsigned int i = 0; i < numChildNodes; ++i)
  1759. {
  1760. CTrackViewTrack* subTrack = static_cast<CTrackViewTrack*>(m_colorUpdateTrack->GetChild(i));
  1761. CTrackViewKeyHandle subTrackKey = subTrack->GetKeyByTime(m_colorUpdateKeyTime);
  1762. I2DBezierKey bezierKey;
  1763. if (subTrackKey.IsValid())
  1764. {
  1765. subTrackKey.GetKey(&bezierKey);
  1766. }
  1767. else
  1768. {
  1769. // no valid key found at this time - create Key
  1770. subTrackKey = subTrack->CreateKey(m_colorUpdateKeyTime);
  1771. subTrackKey.GetKey(&bezierKey);
  1772. }
  1773. bezierKey.value.x = m_colorUpdateKeyTime;
  1774. bezierKey.value.y = color[i];
  1775. subTrackKey.SetKey(&bezierKey);
  1776. }
  1777. }
  1778. void CTrackViewDopeSheetBase::EditSelectedColorKey(CTrackViewTrack* pTrack)
  1779. {
  1780. if (pTrack->IsCompoundTrack())
  1781. {
  1782. CTrackViewKeyBundle selectedKeyBundle = pTrack->GetSelectedKeys();
  1783. if (selectedKeyBundle.GetKeyCount())
  1784. {
  1785. m_colorUpdateTrack = pTrack;
  1786. // init with the first selected key color
  1787. m_colorUpdateKeyTime = selectedKeyBundle.GetKey(0).GetTime();
  1788. AZ::Vector3 color;
  1789. pTrack->GetValue(m_colorUpdateKeyTime, color);
  1790. const AZ::Color defaultColor(
  1791. clamp_tpl(static_cast<AZ::u8>(FloatToIntRet(color.GetX())), AZ::u8(0), AZ::u8(255)),
  1792. clamp_tpl(static_cast<AZ::u8>(FloatToIntRet(color.GetY())), AZ::u8(0), AZ::u8(255)),
  1793. clamp_tpl(static_cast<AZ::u8>(FloatToIntRet(color.GetZ())), AZ::u8(0), AZ::u8(255)),
  1794. 255);
  1795. AzQtComponents::ColorPicker picker(AzQtComponents::ColorPicker::Configuration::RGB);
  1796. picker.setWindowTitle(tr("Select Color"));
  1797. picker.setCurrentColor(defaultColor);
  1798. picker.setSelectedColor(defaultColor);
  1799. QObject::connect(&picker, &AzQtComponents::ColorPicker::currentColorChanged, this, &CTrackViewDopeSheetBase::OnCurrentColorChange);
  1800. if (picker.exec() == QDialog::Accepted)
  1801. {
  1802. const AZ::Color col = picker.currentColor();
  1803. // Moved bulk of method into helper to handle matching logic in QT callback and undo redo cases
  1804. UpdateColorKey(AzQtComponents::toQColor(col), true);
  1805. }
  1806. else
  1807. {
  1808. // We canceled out of the color picker, revert to color held before opening it
  1809. UpdateColorKey(AzQtComponents::toQColor(defaultColor), false);
  1810. }
  1811. }
  1812. }
  1813. }
  1814. //////////////////////////////////////////////////////////////////////////
  1815. void CTrackViewDopeSheetBase::AcceptUndo()
  1816. {
  1817. if (CUndo::IsRecording())
  1818. {
  1819. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1820. if (m_mouseMode == eTVMouseMode_Paste)
  1821. {
  1822. GetIEditor()->CancelUndo();
  1823. }
  1824. else if (m_mouseMode == eTVMouseMode_Move || m_mouseMode == eTVMouseMode_Clone)
  1825. {
  1826. if (sequence && m_bKeysMoved)
  1827. {
  1828. GetIEditor()->CancelUndo();
  1829. // Keys Moved, mark the sequence dirty to get an AZ undo event.
  1830. AzToolsFramework::ScopedUndoBatch undoBatch("Move/Clone Keys");
  1831. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1832. }
  1833. else
  1834. {
  1835. GetIEditor()->CancelUndo();
  1836. }
  1837. }
  1838. else if (m_mouseMode == eTVMouseMode_StartTimeAdjust || m_mouseMode == eTVMouseMode_EndTimeAdjust)
  1839. {
  1840. if (sequence)
  1841. {
  1842. GetIEditor()->CancelUndo();
  1843. AzToolsFramework::ScopedUndoBatch undoBatch("Adjust Start/End Time of an Animation Key");
  1844. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1845. }
  1846. else
  1847. {
  1848. GetIEditor()->CancelUndo();
  1849. }
  1850. }
  1851. }
  1852. m_mouseMode = eTVMouseMode_None;
  1853. m_trackMementos.clear();
  1854. }
  1855. //////////////////////////////////////////////////////////////////////////
  1856. float CTrackViewDopeSheetBase::ComputeSnappedMoveOffset()
  1857. {
  1858. // Compute time offset
  1859. const QPoint currentMousePos(qBound(m_rcClient.left(), m_mouseOverPos.x(), m_rcClient.right()), m_mouseOverPos.y());
  1860. float time0 = TimeFromPointUnsnapped(m_mouseDownPos);
  1861. float time = TimeFromPointUnsnapped(currentMousePos);
  1862. if (GetKeyModifiedSnappingMode() == eSnappingMode_SnapTick)
  1863. {
  1864. time0 = TickSnap(time0);
  1865. time = TickSnap(time);
  1866. }
  1867. return time - time0;
  1868. }
  1869. //////////////////////////////////////////////////////////////////////////
  1870. void CTrackViewDopeSheetBase::AddKeys(const QPoint& point, const bool bTryAddKeysInGroup)
  1871. {
  1872. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  1873. if (!sequence)
  1874. {
  1875. return;
  1876. }
  1877. // Add keys here.
  1878. CTrackViewTrack* pTrack = GetTrackFromPoint(point);
  1879. if (!pTrack)
  1880. {
  1881. return;
  1882. }
  1883. CTrackViewSequenceNotificationContext context(sequence);
  1884. CTrackViewAnimNode* pNode = pTrack->GetAnimNode();
  1885. float keyTime = TimeFromPoint(point);
  1886. bool inRange = m_timeRange.IsInside(keyTime);
  1887. if (pTrack && inRange)
  1888. {
  1889. if (bTryAddKeysInGroup && pNode->GetParentNode()) // Add keys in group
  1890. {
  1891. CTrackViewTrackBundle tracksInGroup = pNode->GetTracksByParam(pTrack->GetParameterType());
  1892. for (int i = 0; i < (int)tracksInGroup.GetCount(); ++i)
  1893. {
  1894. CTrackViewTrack* pCurrTrack = tracksInGroup.GetTrack(i);
  1895. if (pCurrTrack->GetChildCount() == 0) // A simple track
  1896. {
  1897. if (IsOkToAddKeyHere(pCurrTrack, keyTime))
  1898. {
  1899. AzToolsFramework::ScopedUndoBatch undoBatch("Create Key");
  1900. pCurrTrack->CreateKey(keyTime);
  1901. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1902. }
  1903. }
  1904. else // A compound track
  1905. {
  1906. for (unsigned int k = 0; k < pCurrTrack->GetChildCount(); ++k)
  1907. {
  1908. CTrackViewTrack* pSubTrack = static_cast<CTrackViewTrack*>(pCurrTrack->GetChild(k));
  1909. if (IsOkToAddKeyHere(pSubTrack, keyTime))
  1910. {
  1911. AzToolsFramework::ScopedUndoBatch undoBatch("Create Key");
  1912. pSubTrack->CreateKey(keyTime);
  1913. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1914. }
  1915. }
  1916. }
  1917. }
  1918. }
  1919. else if (pTrack->GetChildCount() == 0) // A simple track
  1920. {
  1921. if (IsOkToAddKeyHere(pTrack, keyTime))
  1922. {
  1923. AzToolsFramework::ScopedUndoBatch undoBatch("Create Key");
  1924. pTrack->CreateKey(keyTime);
  1925. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1926. }
  1927. }
  1928. else // A compound track
  1929. {
  1930. if (pTrack->GetValueType() == AnimValueType::RGB)
  1931. {
  1932. CreateColorKey(pTrack, keyTime);
  1933. }
  1934. else
  1935. {
  1936. AzToolsFramework::ScopedUndoBatch undoBatch("Create Key");
  1937. for (unsigned int i = 0; i < pTrack->GetChildCount(); ++i)
  1938. {
  1939. CTrackViewTrack* pSubTrack = static_cast<CTrackViewTrack*>(pTrack->GetChild(i));
  1940. if (IsOkToAddKeyHere(pSubTrack, keyTime))
  1941. {
  1942. pSubTrack->CreateKey(keyTime);
  1943. }
  1944. }
  1945. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  1946. }
  1947. }
  1948. }
  1949. }
  1950. //////////////////////////////////////////////////////////////////////////
  1951. void CTrackViewDopeSheetBase::DrawControl(QPainter* painter, const QRect& rcUpdate)
  1952. {
  1953. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  1954. DrawNodesRecursive(pSequence, painter, rcUpdate);
  1955. DrawSummary(painter, rcUpdate);
  1956. DrawSelectedKeyIndicators(painter);
  1957. if (m_mouseMode == eTVMouseMode_Paste)
  1958. {
  1959. // If in paste mode draw keys that are in clipboard
  1960. DrawClipboardKeys(painter, QRect());
  1961. }
  1962. }
  1963. //////////////////////////////////////////////////////////////////////////
  1964. void CTrackViewDopeSheetBase::DrawNodesRecursive(CTrackViewNode* pNode, QPainter* painter, const QRect& rcUpdate)
  1965. {
  1966. const QRect rect = GetNodeRect(pNode);
  1967. if (!rect.isEmpty())
  1968. {
  1969. switch (pNode->GetNodeType())
  1970. {
  1971. case eTVNT_AnimNode:
  1972. DrawNodeTrack(static_cast<CTrackViewAnimNode*>(pNode), painter, rect);
  1973. break;
  1974. case eTVNT_Track:
  1975. DrawTrack(static_cast<CTrackViewTrack*>(pNode), painter, rect);
  1976. break;
  1977. }
  1978. }
  1979. if (pNode->GetExpanded())
  1980. {
  1981. unsigned int numChildren = pNode->GetChildCount();
  1982. for (unsigned int i = 0; i < numChildren; ++i)
  1983. {
  1984. DrawNodesRecursive(pNode->GetChild(i), painter, rcUpdate);
  1985. }
  1986. }
  1987. }
  1988. //////////////////////////////////////////////////////////////////////////
  1989. void CTrackViewDopeSheetBase::DrawTicks(QPainter* painter, const QRect& rc, Range& timeRange)
  1990. {
  1991. // Draw time ticks every tick step seconds.
  1992. const QPen dkgray(QColor(90, 90, 90));
  1993. const QPen ltgray(QColor(120, 120, 120));
  1994. const QPen prevPen = painter->pen();
  1995. painter->setPen(dkgray);
  1996. Range VisRange = GetVisibleRange();
  1997. int nNumberTicks = 10;
  1998. if (GetTickDisplayMode() == eTVTickMode_InFrames)
  1999. {
  2000. nNumberTicks = 8;
  2001. }
  2002. float start = TickSnap(timeRange.start);
  2003. float step = 1.0f / static_cast<float>(m_ticksStep);
  2004. for (float t = 0.0f; t <= timeRange.end + step; t += step)
  2005. {
  2006. float st = TickSnap(t);
  2007. if (st > timeRange.end)
  2008. {
  2009. st = timeRange.end;
  2010. }
  2011. if (st < VisRange.start)
  2012. {
  2013. continue;
  2014. }
  2015. if (st > VisRange.end)
  2016. {
  2017. break;
  2018. }
  2019. int x = TimeToClient(st);
  2020. if (x < 0)
  2021. {
  2022. continue;
  2023. }
  2024. int k = RoundFloatToInt(st * static_cast<float>(m_ticksStep));
  2025. if (k % nNumberTicks == 0)
  2026. {
  2027. if (st >= start)
  2028. {
  2029. painter->setPen(Qt::black);
  2030. }
  2031. else
  2032. {
  2033. painter->setPen(dkgray);
  2034. }
  2035. painter->drawLine(x, rc.bottom() - 1, x, rc.bottom() - 5);
  2036. painter->setPen(dkgray);
  2037. }
  2038. else
  2039. {
  2040. if (st >= start)
  2041. {
  2042. painter->setPen(dkgray);
  2043. }
  2044. else
  2045. {
  2046. painter->setPen(ltgray);
  2047. }
  2048. painter->drawLine(x, rc.bottom() - 1, x, rc.bottom() - 3);
  2049. }
  2050. }
  2051. painter->setPen(prevPen);
  2052. }
  2053. //////////////////////////////////////////////////////////////////////////
  2054. void CTrackViewDopeSheetBase::DrawTrack(CTrackViewTrack* pTrack, QPainter* painter, const QRect& trackRect)
  2055. {
  2056. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  2057. const QPen prevPen = painter->pen();
  2058. painter->setPen(QColor(120, 120, 120));
  2059. painter->drawLine(trackRect.bottomLeft(), trackRect.bottomRight());
  2060. painter->setPen(prevPen);
  2061. QRect rcInner = trackRect;
  2062. rcInner.setLeft(max(trackRect.left(), m_leftOffset - m_scrollOffset.x()));
  2063. rcInner.setRight(min(trackRect.right(), (m_scrollMax + m_scrollMin) - m_scrollOffset.x() + m_leftOffset * 2));
  2064. bool bLightAnimationSetActive = pSequence->GetFlags() & IAnimSequence::eSeqFlags_LightAnimationSet;
  2065. if (bLightAnimationSetActive && pTrack->GetKeyCount() > 0)
  2066. {
  2067. // In the case of the light animation set, the time of of the last key
  2068. // determines the end of the track.
  2069. float lastKeyTime = pTrack->GetKey(pTrack->GetKeyCount() - 1).GetTime();
  2070. rcInner.setRight(min(rcInner.right(), TimeToClient(lastKeyTime)));
  2071. }
  2072. QRect rcInnerDraw(QPoint(rcInner.left() - 6, rcInner.top()), QPoint(rcInner.right() + 6, rcInner.bottom()));
  2073. QColor trackColor = CTVCustomizeTrackColorsDlg::GetTrackColor(pTrack->GetParameterType());
  2074. if (pTrack->HasCustomColor())
  2075. {
  2076. ColorB customColor = pTrack->GetCustomColor();
  2077. trackColor = QColor(customColor.r, customColor.g, customColor.b);
  2078. }
  2079. // For the case of tracks belonging to an inactive director node,
  2080. // changes the track color to a custom one.
  2081. const QColor colorForDisabled = CTVCustomizeTrackColorsDlg::GetColorForDisabledTracks();
  2082. const QColor colorForMuted = CTVCustomizeTrackColorsDlg::GetColorForMutedTracks();
  2083. CTrackViewAnimNode* pDirectorNode = pTrack->GetDirector();
  2084. if (!pDirectorNode->IsActiveDirector())
  2085. {
  2086. trackColor = colorForDisabled;
  2087. }
  2088. // A disabled/muted track or any track in a disabled node also uses a custom color.
  2089. CTrackViewAnimNode* animNode = pTrack->GetAnimNode();
  2090. bool bTrackDisabled = pTrack->GetFlags() & IAnimTrack::eAnimTrackFlags_Disabled;
  2091. bool bTrackMuted = pTrack->GetFlags() & IAnimTrack::eAnimTrackFlags_Muted;
  2092. bool bTrackInvalid = !pTrack->IsSubTrack() && !animNode->IsParamValid(pTrack->GetParameterType());
  2093. bool bTrackInDisabledNode = animNode->AreFlagsSetOnNodeOrAnyParent(eAnimNodeFlags_Disabled);
  2094. if (bTrackDisabled || bTrackInDisabledNode || bTrackInvalid)
  2095. {
  2096. trackColor = colorForDisabled;
  2097. }
  2098. else if (bTrackMuted)
  2099. {
  2100. trackColor = colorForMuted;
  2101. }
  2102. const QRect rc = rcInnerDraw.adjusted(0, 1, 0, 0);
  2103. const EAnimCurveType trackType = pTrack->GetCurveType();
  2104. if (trackType == eAnimCurveType_TCBFloat || trackType == eAnimCurveType_TCBQuat || trackType == eAnimCurveType_TCBVector)
  2105. {
  2106. trackColor = QColor(245, 80, 70);
  2107. }
  2108. if (pTrack->IsSelected())
  2109. {
  2110. QLinearGradient gradient(rc.topLeft(), rc.bottomLeft());
  2111. gradient.setColorAt(0, trackColor);
  2112. gradient.setColorAt(1, QColor(trackColor.red() / 2, trackColor.green() / 2, trackColor.blue() / 2));
  2113. painter->fillRect(rc, gradient);
  2114. }
  2115. else if (pTrack->GetValueType() == AnimValueType::RGB && pTrack->GetKeyCount() > 0)
  2116. {
  2117. DrawColorGradient(painter, rc, pTrack);
  2118. }
  2119. else
  2120. {
  2121. painter->fillRect(rc, trackColor);
  2122. }
  2123. // Left outside
  2124. QRect rcOutside = trackRect;
  2125. rcOutside.setRight(rcInnerDraw.left() - 1);
  2126. rcOutside.adjust(1, 1, -1, 0);
  2127. QLinearGradient gradient(rcOutside.topLeft(), rcOutside.bottomLeft());
  2128. gradient.setColorAt(0, QColor(210, 210, 210));
  2129. gradient.setColorAt(1, QColor(180, 180, 180));
  2130. painter->fillRect(rcOutside, gradient);
  2131. // Right outside.
  2132. rcOutside = trackRect;
  2133. rcOutside.setLeft(rcInnerDraw.right() + 1);
  2134. rcOutside.adjust(1, 1, -1, 0);
  2135. gradient = QLinearGradient(rcOutside.topLeft(), rcOutside.bottomLeft());
  2136. gradient.setColorAt(0, QColor(210, 210, 210));
  2137. gradient.setColorAt(1, QColor(180, 180, 180));
  2138. painter->fillRect(rcOutside, gradient);
  2139. // Get time range of update rectangle.
  2140. Range timeRange = GetTimeRange(trackRect);
  2141. // Draw tick marks in time range.
  2142. DrawTicks(painter, rcInner, timeRange);
  2143. // Draw special track features
  2144. AnimValueType trackValueType = pTrack->GetValueType();
  2145. CAnimParamType trackParamType = pTrack->GetParameterType();
  2146. if (trackValueType == AnimValueType::Bool)
  2147. {
  2148. // If this track is bool Track draw bars where track is true
  2149. DrawBoolTrack(timeRange, painter, pTrack, rc);
  2150. }
  2151. else if (trackValueType == AnimValueType::Select)
  2152. {
  2153. // If this track is Select Track draw bars to show where selection is active.
  2154. DrawSelectTrack(timeRange, painter, pTrack, rc);
  2155. }
  2156. else if (trackParamType == AnimParamType::Sequence)
  2157. {
  2158. // If this track is Sequence Track draw bars to show where sequence is active.
  2159. DrawSequenceTrack(timeRange, painter, pTrack, rc);
  2160. }
  2161. else if (trackParamType == AnimParamType::Goto)
  2162. {
  2163. // if this track is GoTo Track, draw an arrow to indicate jump position.
  2164. DrawGoToTrackArrow(pTrack, painter, rc);
  2165. }
  2166. // Draw keys in time range.
  2167. DrawKeys(pTrack, painter, rcInner, timeRange);
  2168. }
  2169. //////////////////////////////////////////////////////////////////////////
  2170. void CTrackViewDopeSheetBase::DrawSelectTrack(const Range& timeRange, QPainter* painter, CTrackViewTrack* pTrack, const QRect& rc)
  2171. {
  2172. const QBrush prevBrush = painter->brush();
  2173. painter->setBrush(m_selectTrackBrush);
  2174. const int numKeys = pTrack->GetKeyCount();
  2175. for (int i = 0; i < numKeys; ++i)
  2176. {
  2177. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
  2178. ISelectKey selectKey;
  2179. keyHandle.GetKey(&selectKey);
  2180. if (!selectKey.szSelection.empty() || selectKey.cameraAzEntityId.IsValid())
  2181. {
  2182. float time = keyHandle.GetTime();
  2183. float nextTime = timeRange.end;
  2184. if (i < numKeys - 1)
  2185. {
  2186. nextTime = pTrack->GetKey(i + 1).GetTime();
  2187. }
  2188. time = clamp_tpl(time, timeRange.start, timeRange.end);
  2189. nextTime = clamp_tpl(nextTime, timeRange.start, timeRange.end);
  2190. int x0_2 = TimeToClient(time);
  2191. float fBlendTime = selectKey.fBlendTime;
  2192. int blendTimeEnd = 0;
  2193. if (fBlendTime > 0.0f && fBlendTime < (nextTime - time))
  2194. {
  2195. blendTimeEnd = TimeToClient(nextTime);
  2196. nextTime -= fBlendTime;
  2197. }
  2198. int x = TimeToClient(nextTime);
  2199. if (x != x0_2)
  2200. {
  2201. QLinearGradient gradient(x0_2, rc.top() + 1, x0_2, rc.bottom());
  2202. gradient.setColorAt(0, Qt::white);
  2203. gradient.setColorAt(1, QColor(100, 190, 255));
  2204. painter->fillRect(QRect(QPoint(x0_2, rc.top() + 1), QPoint(x, rc.bottom())), gradient);
  2205. }
  2206. if (fBlendTime > 0.0f)
  2207. {
  2208. QLinearGradient gradient(x, rc.top() + 1, x, rc.bottom());
  2209. gradient.setColorAt(0, Qt::white);
  2210. gradient.setColorAt(1, QColor(0, 115, 230));
  2211. painter->fillRect(QRect(QPoint(x, rc.top() + 1), QPoint(blendTimeEnd, rc.bottom())), gradient);
  2212. }
  2213. }
  2214. }
  2215. painter->setBrush(prevBrush);
  2216. }
  2217. //////////////////////////////////////////////////////////////////////////
  2218. void CTrackViewDopeSheetBase::DrawBoolTrack(const Range& timeRange, QPainter* painter, CTrackViewTrack* pTrack, const QRect& rc)
  2219. {
  2220. int x0 = TimeToClient(timeRange.start);
  2221. const QBrush prevBrush = painter->brush();
  2222. painter->setBrush(m_visibilityBrush);
  2223. const int numKeys = pTrack->GetKeyCount();
  2224. for (int i = 0; i < numKeys; ++i)
  2225. {
  2226. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
  2227. const float time = keyHandle.GetTime();
  2228. if (time < timeRange.start)
  2229. {
  2230. continue;
  2231. }
  2232. if (time > timeRange.end)
  2233. {
  2234. break;
  2235. }
  2236. int x = TimeToClient(time);
  2237. bool val = false;
  2238. pTrack->GetValue(time - 0.001f, val);
  2239. if (val)
  2240. {
  2241. QLinearGradient gradient(x0, rc.top() + 4, x0, rc.bottom() - 4);
  2242. gradient.setColorAt(0, QColor(250, 250, 250));
  2243. gradient.setColorAt(1, QColor(0, 80, 255));
  2244. painter->fillRect(QRect(QPoint(x0, rc.top() + 4), QPoint(x, rc.bottom() - 4)), gradient);
  2245. }
  2246. x0 = x;
  2247. }
  2248. int x = TimeToClient(timeRange.end);
  2249. bool val = false;
  2250. pTrack->GetValue(timeRange.end - 0.001f, val);
  2251. if (val)
  2252. {
  2253. QLinearGradient gradient(x0, rc.top() + 4, x0, rc.bottom() - 4);
  2254. gradient.setColorAt(0, QColor(250, 250, 250));
  2255. gradient.setColorAt(1, QColor(0, 80, 255));
  2256. painter->fillRect(QRect(QPoint(x0, rc.top() + 4), QPoint(x, rc.bottom() - 4)), gradient);
  2257. }
  2258. painter->setBrush(prevBrush);
  2259. }
  2260. //////////////////////////////////////////////////////////////////////////
  2261. void CTrackViewDopeSheetBase::DrawSequenceTrack(const Range& timeRange, QPainter* painter, CTrackViewTrack* pTrack, const QRect& rc)
  2262. {
  2263. const QBrush prevBrush = painter->brush();
  2264. painter->setBrush(m_selectTrackBrush);
  2265. const int numKeys = pTrack->GetKeyCount();
  2266. for (int i = 0; i < numKeys - 1; ++i)
  2267. {
  2268. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
  2269. ISequenceKey sequenceKey;
  2270. keyHandle.GetKey(&sequenceKey);
  2271. if (sequenceKey.sequenceEntityId.IsValid())
  2272. {
  2273. float time = keyHandle.GetTime();
  2274. float nextTime = timeRange.end;
  2275. if (i < numKeys - 1)
  2276. {
  2277. nextTime = pTrack->GetKey(i + 1).GetTime();
  2278. }
  2279. time = clamp_tpl(time, timeRange.start, timeRange.end);
  2280. nextTime = clamp_tpl(nextTime, timeRange.start, timeRange.end);
  2281. int x0_2 = TimeToClient(time);
  2282. int x = TimeToClient(nextTime);
  2283. if (x != x0_2)
  2284. {
  2285. const QColor startColour(100, 190, 255);
  2286. const QColor endColour(250, 250, 250);
  2287. QLinearGradient gradient(x0_2, rc.top() + 1, x0_2, rc.bottom());
  2288. gradient.setColorAt(0, startColour);
  2289. gradient.setColorAt(1, endColour);
  2290. painter->fillRect(QRect(QPoint(x0_2, rc.top() + 1), QPoint(x, rc.bottom())), gradient);
  2291. }
  2292. }
  2293. }
  2294. painter->setBrush(prevBrush);
  2295. }
  2296. //////////////////////////////////////////////////////////////////////////
  2297. bool CTrackViewDopeSheetBase::CompareKeyHandleByTime(const CTrackViewKeyHandle &a, const CTrackViewKeyHandle &b)
  2298. {
  2299. return a.GetTime() < b.GetTime();
  2300. }
  2301. //////////////////////////////////////////////////////////////////////////
  2302. void CTrackViewDopeSheetBase::DrawKeys(CTrackViewTrack* pTrack, QPainter* painter, QRect& rect, [[maybe_unused]] Range& timeRange)
  2303. {
  2304. int numKeys = pTrack->GetKeyCount();
  2305. const QFont prevFont = painter->font();
  2306. painter->setFont(m_descriptionFont);
  2307. painter->setPen(KEY_TEXT_COLOR);
  2308. int prevKeyPixel = -10000;
  2309. const int kDefaultWidthForDescription = 200;
  2310. const int kSmallMargin = 10;
  2311. AZStd::vector<CTrackViewKeyHandle> sortedKeys;
  2312. sortedKeys.reserve(numKeys);
  2313. for (int i = 0; i < numKeys; ++i)
  2314. {
  2315. sortedKeys.push_back(pTrack->GetKey(i));
  2316. }
  2317. AZStd::sort(sortedKeys.begin(), sortedKeys.end(), CompareKeyHandleByTime);
  2318. // Draw keys.
  2319. for (int i = 0; i < numKeys; ++i)
  2320. {
  2321. CTrackViewKeyHandle keyHandle = sortedKeys[i];
  2322. const float time = keyHandle.GetTime();
  2323. int x = TimeToClient(time);
  2324. if (x - kSmallMargin > rect.right())
  2325. {
  2326. continue;
  2327. }
  2328. int x1 = x + kDefaultWidthForDescription;
  2329. int nextKeyIndex = i + 1;
  2330. // Skip over next keys that have the same time as the current key.
  2331. // If they have the same time it means they are keys from sub tracks
  2332. // in a compound track at the same time.
  2333. while (nextKeyIndex < numKeys && AZ::IsClose(sortedKeys[nextKeyIndex].GetTime(), time, AZ::Constants::FloatEpsilon))
  2334. {
  2335. nextKeyIndex++;
  2336. }
  2337. if (nextKeyIndex < numKeys)
  2338. {
  2339. CTrackViewKeyHandle nextKey2 = sortedKeys[nextKeyIndex];
  2340. x1 = TimeToClient(nextKey2.GetTime()) - kSmallMargin;
  2341. }
  2342. if (x1 > x + kSmallMargin) // Enough space for description text or duration bar
  2343. {
  2344. // Get info about that key.
  2345. const char* pDescription = keyHandle.GetDescription();
  2346. const float duration = keyHandle.GetDuration();
  2347. int xlast = x;
  2348. if (duration > 0)
  2349. {
  2350. xlast = TimeToClient(time + duration);
  2351. }
  2352. if (xlast + kSmallMargin < rect.left())
  2353. {
  2354. continue;
  2355. }
  2356. if (duration > 0)
  2357. {
  2358. DrawKeyDuration(pTrack, painter, rect, i);
  2359. }
  2360. if (pDescription && pDescription[0] != 0)
  2361. {
  2362. char keydesc[1024];
  2363. bool bSelectedAndBeingMoved = m_mouseMode == eTVMouseMode_Move && keyHandle.IsSelected();
  2364. if (bSelectedAndBeingMoved)
  2365. {
  2366. // Show its time or frame number additionally.
  2367. if (GetTickDisplayMode() == eTVTickMode_InSeconds)
  2368. {
  2369. sprintf_s(keydesc, "%.3f, {", time);
  2370. }
  2371. else
  2372. {
  2373. sprintf_s(keydesc, "%d, {", ftoi(time / m_snapFrameTime));
  2374. }
  2375. }
  2376. else
  2377. {
  2378. azstrcpy(keydesc, AZ_ARRAY_SIZE(keydesc), "{");
  2379. }
  2380. azstrcat(keydesc, AZ_ARRAY_SIZE(keydesc), pDescription);
  2381. azstrcat(keydesc, AZ_ARRAY_SIZE(keydesc), "}");
  2382. // Draw key description text.
  2383. // Find next key.
  2384. const QRect textRect(QPoint(x + 10, rect.top()), QPoint(x1, rect.bottom()));
  2385. painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, painter->fontMetrics().elidedText(keydesc, Qt::ElideRight, textRect.width()));
  2386. }
  2387. }
  2388. if (x < 0)
  2389. {
  2390. continue;
  2391. }
  2392. if (pTrack->GetChildCount() == 0 // At compound tracks, keys are all green.
  2393. && abs(x - prevKeyPixel) < 2)
  2394. {
  2395. // If multiple keys on the same time.
  2396. painter->drawPixmap(QPoint(x - 6, rect.top() + 2), QPixmap(":/Trackview/trackview_keys_02.png"));
  2397. }
  2398. else
  2399. {
  2400. if (keyHandle.IsSelected())
  2401. {
  2402. painter->drawPixmap(QPoint(x - 6, rect.top() + 2), QPixmap(":/Trackview/trackview_keys_01.png"));
  2403. }
  2404. else
  2405. {
  2406. painter->drawPixmap(QPoint(x - 6, rect.top() + 2), QPixmap(":/Trackview/trackview_keys_00.png"));
  2407. }
  2408. }
  2409. prevKeyPixel = x;
  2410. }
  2411. painter->setFont(prevFont);
  2412. }
  2413. //////////////////////////////////////////////////////////////////////////
  2414. void CTrackViewDopeSheetBase::DrawClipboardKeys(QPainter* painter, [[maybe_unused]] const QRect& rc)
  2415. {
  2416. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  2417. const float timeOffset = ComputeSnappedMoveOffset();
  2418. // Get node & track under cursor
  2419. CTrackViewAnimNode* animNode = GetAnimNodeFromPoint(m_mouseOverPos);
  2420. CTrackViewTrack* pTrack = GetTrackFromPoint(m_mouseOverPos);
  2421. auto matchedLocations = pSequence->GetMatchedPasteLocations(m_clipboardKeys, animNode, pTrack);
  2422. for (size_t i = 0; i < matchedLocations.size(); ++i)
  2423. {
  2424. auto& matchedLocation = matchedLocations[i];
  2425. CTrackViewTrack* pMatchedTrack = matchedLocation.first;
  2426. XmlNodeRef trackNode = matchedLocation.second;
  2427. if (pMatchedTrack->IsCompoundTrack())
  2428. {
  2429. // Both child counts should be the same, but make sure
  2430. const unsigned int numSubTrack = std::min(pMatchedTrack->GetChildCount(), (unsigned int)trackNode->getChildCount());
  2431. for (unsigned int subTrackIndex = 0; subTrackIndex < numSubTrack; ++subTrackIndex)
  2432. {
  2433. CTrackViewTrack* pSubTrack = static_cast<CTrackViewTrack*>(pMatchedTrack->GetChild(subTrackIndex));
  2434. XmlNodeRef subTrackNode = trackNode->getChild(subTrackIndex);
  2435. DrawTrackClipboardKeys(painter, pSubTrack, subTrackNode, timeOffset);
  2436. // Also draw to parent track. This is intentional
  2437. DrawTrackClipboardKeys(painter, pMatchedTrack, subTrackNode, timeOffset);
  2438. }
  2439. }
  2440. else
  2441. {
  2442. DrawTrackClipboardKeys(painter, pMatchedTrack, trackNode, timeOffset);
  2443. }
  2444. }
  2445. }
  2446. //////////////////////////////////////////////////////////////////////////
  2447. void CTrackViewDopeSheetBase::DrawTrackClipboardKeys(QPainter* painter, CTrackViewTrack* pTrack, XmlNodeRef trackNode, const float timeOffset)
  2448. {
  2449. const QPen prevPen = painter->pen();
  2450. painter->setPen(Qt::green);
  2451. const QRect trackRect = GetNodeRect(pTrack);
  2452. const int numKeysToPaste = trackNode->getChildCount();
  2453. for (int i = 0; i < numKeysToPaste; ++i)
  2454. {
  2455. XmlNodeRef keyNode = trackNode->getChild(i);
  2456. float time;
  2457. if (keyNode->getAttr("time", time))
  2458. {
  2459. int x = TimeToClient(time + timeOffset);
  2460. painter->drawPixmap(QPoint(x - 6, trackRect.top() + 2), QPixmap(":/Trackview/trackview_keys_03.png"));
  2461. painter->drawLine(x, m_rcClient.top(), x, m_rcClient.bottom());
  2462. }
  2463. }
  2464. painter->setPen(prevPen);
  2465. }
  2466. //////////////////////////////////////////////////////////////////////////
  2467. CTrackViewKeyHandle CTrackViewDopeSheetBase::FirstKeyFromPoint(const QPoint& point)
  2468. {
  2469. CTrackViewTrack* pTrack = GetTrackFromPoint(point);
  2470. if (!pTrack)
  2471. {
  2472. return CTrackViewKeyHandle();
  2473. }
  2474. float t1 = TimeFromPointUnsnapped(QPoint(point.x() - 4, point.y()));
  2475. float t2 = TimeFromPointUnsnapped(QPoint(point.x() + 4, point.y()));
  2476. int numKeys = pTrack->GetKeyCount();
  2477. for (int i = 0; i < numKeys; ++i)
  2478. {
  2479. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
  2480. float time = keyHandle.GetTime();
  2481. if (time >= t1 && time <= t2)
  2482. {
  2483. return keyHandle;
  2484. }
  2485. }
  2486. return CTrackViewKeyHandle();
  2487. }
  2488. //////////////////////////////////////////////////////////////////////////
  2489. CTrackViewKeyHandle CTrackViewDopeSheetBase::DurationKeyFromPoint(const QPoint& point)
  2490. {
  2491. CTrackViewTrack* pTrack = GetTrackFromPoint(point);
  2492. if (!pTrack)
  2493. {
  2494. return CTrackViewKeyHandle();
  2495. }
  2496. float t = TimeFromPointUnsnapped(point);
  2497. int numKeys = pTrack->GetKeyCount();
  2498. // Iterate in a reverse order to prioritize later nodes.
  2499. for (int i = numKeys - 1; i >= 0; --i)
  2500. {
  2501. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
  2502. const float time = keyHandle.GetTime();
  2503. const float duration = keyHandle.GetDuration();
  2504. if (t >= time && t <= time + duration)
  2505. {
  2506. return keyHandle;
  2507. }
  2508. }
  2509. return CTrackViewKeyHandle();
  2510. }
  2511. //////////////////////////////////////////////////////////////////////////
  2512. CTrackViewKeyHandle CTrackViewDopeSheetBase::CheckCursorOnStartEndTimeAdjustBar(const QPoint& point, bool& bStart)
  2513. {
  2514. CTrackViewTrack* pTrack = GetTrackFromPoint(point);
  2515. if (!pTrack || (pTrack->GetParameterType() != AnimParamType::Animation &&
  2516. pTrack->GetParameterType() != AnimParamType::TimeRanges &&
  2517. pTrack->GetValueType() != AnimValueType::CharacterAnim &&
  2518. pTrack->GetValueType() != AnimValueType::AssetBlend))
  2519. {
  2520. return CTrackViewKeyHandle();
  2521. }
  2522. int numKeys = pTrack->GetKeyCount();
  2523. for (int i = 0; i < numKeys; ++i)
  2524. {
  2525. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
  2526. if (!keyHandle.IsSelected())
  2527. {
  2528. continue;
  2529. }
  2530. const float time = keyHandle.GetTime();
  2531. const float duration = keyHandle.GetDuration();
  2532. if (duration == 0)
  2533. {
  2534. continue;
  2535. }
  2536. // TODO: Refactor this Time Range Key stuff.
  2537. ICharacterKey characterKey;
  2538. AZ::IAssetBlendKey assetBlendKey;
  2539. ITimeRangeKey* timeRangeKey = nullptr;
  2540. if (pTrack->GetValueType() == AnimValueType::AssetBlend)
  2541. {
  2542. keyHandle.GetKey(&assetBlendKey);
  2543. timeRangeKey = &assetBlendKey;
  2544. }
  2545. else
  2546. {
  2547. // This will work for both character & time range keys because
  2548. // ICharacterKey is derived from ITimeRangeKey. Not the most beautiful code.
  2549. keyHandle.GetKey(&characterKey);
  2550. timeRangeKey = &characterKey;
  2551. }
  2552. int stime = TimeToClient(time);
  2553. int etime = TimeToClient(time + AZStd::min(timeRangeKey->GetValidEndTime(), timeRangeKey->m_duration));
  2554. if (point.x() >= stime - 3 && point.x() <= stime)
  2555. {
  2556. bStart = true;
  2557. return keyHandle;
  2558. }
  2559. else if (point.x() >= etime && point.x() <= etime + 3)
  2560. {
  2561. bStart = false;
  2562. return keyHandle;
  2563. }
  2564. }
  2565. return CTrackViewKeyHandle();
  2566. }
  2567. //////////////////////////////////////////////////////////////////////////
  2568. int CTrackViewDopeSheetBase::NumKeysFromPoint(const QPoint& point)
  2569. {
  2570. CTrackViewTrack* pTrack = GetTrackFromPoint(point);
  2571. if (!pTrack)
  2572. {
  2573. return -1;
  2574. }
  2575. float t1 = TimeFromPointUnsnapped(QPoint(point.x() - 4, point.y()));
  2576. float t2 = TimeFromPointUnsnapped(QPoint(point.x() + 4, point.y()));
  2577. int count = 0;
  2578. int numKeys = pTrack->GetKeyCount();
  2579. for (int i = 0; i < numKeys; ++i)
  2580. {
  2581. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
  2582. const float time = keyHandle.GetTime();
  2583. if (time >= t1 && time <= t2)
  2584. {
  2585. ++count;
  2586. }
  2587. }
  2588. return count;
  2589. }
  2590. //////////////////////////////////////////////////////////////////////////
  2591. void CTrackViewDopeSheetBase::SelectKeys(const QRect& rc, const bool bMultiSelection)
  2592. {
  2593. CTrackViewSequence* sequence = GetIEditor()->GetAnimation()->GetSequence();
  2594. AZ_Assert(sequence != nullptr, "sequence should never be nullptr here");
  2595. if (!sequence)
  2596. {
  2597. return;
  2598. }
  2599. AzToolsFramework::ScopedUndoBatch undoBatch("Select Keys");
  2600. const std::vector<bool> beforeKeyState = sequence->SaveKeyStates();
  2601. CTrackViewSequenceNotificationContext context(sequence);
  2602. if (!bMultiSelection)
  2603. {
  2604. sequence->DeselectAllKeys();
  2605. }
  2606. // put selection rectangle from client to track space.
  2607. const QRect rci = rc.translated(m_scrollOffset);
  2608. Range selTime = GetTimeRange(rci);
  2609. CTrackViewTrackBundle tracks = sequence->GetAllTracks();
  2610. // note the tracks to select for the keyHandles selected
  2611. CTrackViewTrackBundle tracksToSelect;
  2612. for (unsigned int i = 0; i < tracks.GetCount(); ++i)
  2613. {
  2614. CTrackViewTrack* pTrack = tracks.GetTrack(i);
  2615. QRect trackRect = GetNodeRect(pTrack);
  2616. // Decrease item rectangle a bit.
  2617. trackRect.adjust(4, 4, -4, -4);
  2618. // Check if item rectangle intersects with selection rectangle in y axis.
  2619. if ((trackRect.top() >= rc.top() && trackRect.top() <= rc.bottom()) ||
  2620. (trackRect.bottom() >= rc.top() && trackRect.bottom() <= rc.bottom()) ||
  2621. (rc.top() >= trackRect.top() && rc.top() <= trackRect.bottom()) ||
  2622. (rc.bottom() >= trackRect.top() && rc.bottom() <= trackRect.bottom()))
  2623. {
  2624. // Check which keys we intersect.
  2625. for (unsigned int j = 0; j < pTrack->GetKeyCount(); j++)
  2626. {
  2627. CTrackViewKeyHandle keyHandle = pTrack->GetKey(j);
  2628. const float time = keyHandle.GetTime();
  2629. if (selTime.IsInside(time))
  2630. {
  2631. keyHandle.Select(true);
  2632. tracksToSelect.AppendTrack(pTrack);
  2633. }
  2634. }
  2635. }
  2636. }
  2637. ChangeSequenceTrackSelection(sequence, tracksToSelect, bMultiSelection);
  2638. const std::vector<bool> afterKeyState = sequence->SaveKeyStates();
  2639. if (beforeKeyState != afterKeyState)
  2640. {
  2641. undoBatch.MarkEntityDirty(sequence->GetSequenceComponentEntityId());
  2642. }
  2643. }
  2644. //////////////////////////////////////////////////////////////////////////
  2645. void CTrackViewDopeSheetBase::SetTickDisplayMode(ETVTickMode mode)
  2646. {
  2647. m_tickDisplayMode = mode;
  2648. SetTimeScale(GetTimeScale(), 0); // for refresh
  2649. }
  2650. //////////////////////////////////////////////////////////////////////////
  2651. void CTrackViewDopeSheetBase::SetSnapFPS(UINT fps)
  2652. {
  2653. m_snapFrameTime = (fps == 0) ? 0.033333f : (1.0f / float(fps));
  2654. }
  2655. //////////////////////////////////////////////////////////////////////////
  2656. ESnappingMode CTrackViewDopeSheetBase::GetKeyModifiedSnappingMode()
  2657. {
  2658. ESnappingMode snappingMode = m_snappingMode;
  2659. if (qApp->keyboardModifiers() & Qt::ControlModifier)
  2660. {
  2661. snappingMode = eSnappingMode_SnapNone;
  2662. }
  2663. else if (qApp->keyboardModifiers() & Qt::ShiftModifier)
  2664. {
  2665. snappingMode = eSnappingMode_SnapMagnet;
  2666. }
  2667. else if (qApp->keyboardModifiers() & Qt::AltModifier)
  2668. {
  2669. snappingMode = eSnappingMode_SnapFrame;
  2670. }
  2671. return snappingMode;
  2672. }
  2673. //////////////////////////////////////////////////////////////////////////
  2674. void CTrackViewDopeSheetBase::DrawSelectedKeyIndicators(QPainter* painter)
  2675. {
  2676. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  2677. const QPen prevPen = painter->pen();
  2678. painter->setPen(Qt::green);
  2679. CTrackViewKeyBundle keys = pSequence->GetSelectedKeys();
  2680. for (unsigned int i = 0; i < keys.GetKeyCount(); ++i)
  2681. {
  2682. const CTrackViewKeyHandle& keyHandle = keys.GetKey(i);
  2683. int x = TimeToClient(keyHandle.GetTime());
  2684. painter->drawLine(x, m_rcClient.top(), x, m_rcClient.bottom());
  2685. }
  2686. painter->setPen(prevPen);
  2687. }
  2688. //////////////////////////////////////////////////////////////////////////
  2689. void CTrackViewDopeSheetBase::ComputeFrameSteps(const Range& visRange)
  2690. {
  2691. float fNbFrames = fabsf ((visRange.end - visRange.start) / m_snapFrameTime);
  2692. float afStepTable [4] = { 1.0f, 0.5f, 0.2f, 0.1f };
  2693. bool bDone = false;
  2694. float fFact = 1.0f;
  2695. unsigned int nStepIdx = 0;
  2696. for (unsigned int nAttempts = 0; nAttempts < 10 && !bDone; ++nAttempts)
  2697. {
  2698. bool bLess = true;
  2699. for (nStepIdx = 0; nStepIdx < 4; ++nStepIdx)
  2700. {
  2701. float fFactNbFrames = fNbFrames / (afStepTable[nStepIdx] * fFact);
  2702. if (fFactNbFrames >= 3 && fFactNbFrames <= 9)
  2703. {
  2704. bDone = true;
  2705. break;
  2706. }
  2707. else
  2708. {
  2709. bLess = (fFactNbFrames < 3);
  2710. }
  2711. }
  2712. if (!bDone)
  2713. {
  2714. fFact *= (bLess) ? 0.1f : 10.0f;
  2715. }
  2716. }
  2717. float nBIntermediateTicks = 5;
  2718. m_fFrameLabelStep = fFact * afStepTable[nStepIdx];
  2719. if (TimeToClient(static_cast<float>(m_fFrameLabelStep)) - TimeToClient(0.0f) > 1300)
  2720. {
  2721. nBIntermediateTicks = 10;
  2722. }
  2723. m_fFrameTickStep = m_fFrameLabelStep * double (m_snapFrameTime) / double(nBIntermediateTicks);
  2724. }
  2725. void CTrackViewDopeSheetBase::DrawTimeLineInFrames(QPainter* painter, const QRect& rc, [[maybe_unused]] const QColor& lineCol, const QColor& textCol, [[maybe_unused]] double step)
  2726. {
  2727. float fFramesPerSec = 1.0f / m_snapFrameTime;
  2728. float fInvFrameLabelStep = 1.0f / static_cast<float>(m_fFrameLabelStep);
  2729. Range VisRange = GetVisibleRange();
  2730. const Range& timeRange = m_timeRange;
  2731. const QPen ltgray(QColor(90, 90, 90));
  2732. const QPen black(textCol);
  2733. for (float t = TickSnap(timeRange.start); t <= timeRange.end + static_cast<float>(m_fFrameTickStep); t += static_cast<float>(m_fFrameTickStep))
  2734. {
  2735. float st = t;
  2736. if (st > timeRange.end)
  2737. {
  2738. st = timeRange.end;
  2739. }
  2740. if (st < VisRange.start)
  2741. {
  2742. continue;
  2743. }
  2744. if (st > VisRange.end)
  2745. {
  2746. break;
  2747. }
  2748. if (st < m_timeRange.start || st > m_timeRange.end)
  2749. {
  2750. continue;
  2751. }
  2752. const int x = TimeToClient(st);
  2753. float fFrame = st * fFramesPerSec;
  2754. float fFrameScaled = fFrame * fInvFrameLabelStep;
  2755. if (fabsf(fFrameScaled - RoundFloatToInt(fFrameScaled)) < 0.001f)
  2756. {
  2757. painter->setPen(black);
  2758. painter->drawLine(x, rc.bottom() - 2, x, rc.bottom() - 14);
  2759. painter->drawText(x + 2, rc.top(), QString::number(fFrame));
  2760. painter->setPen(ltgray);
  2761. }
  2762. else
  2763. {
  2764. painter->drawLine(x, rc.bottom() - 2, x, rc.bottom() - 6);
  2765. }
  2766. }
  2767. }
  2768. void CTrackViewDopeSheetBase::DrawTimeLineInSeconds(QPainter* painter, const QRect& rc, [[maybe_unused]] const QColor& lineCol, const QColor& textCol, double step)
  2769. {
  2770. Range VisRange = GetVisibleRange();
  2771. const Range& timeRange = m_timeRange;
  2772. int nNumberTicks = 10;
  2773. const QPen ltgray(QColor(90, 90, 90));
  2774. const QPen black(textCol);
  2775. for (float t = TickSnap(timeRange.start); t <= timeRange.end + static_cast<float>(step); t += static_cast<float>(step))
  2776. {
  2777. float st = TickSnap(t);
  2778. if (st > timeRange.end)
  2779. {
  2780. st = timeRange.end;
  2781. }
  2782. if (st < VisRange.start)
  2783. {
  2784. continue;
  2785. }
  2786. if (st > VisRange.end)
  2787. {
  2788. break;
  2789. }
  2790. if (st < m_timeRange.start || st > m_timeRange.end)
  2791. {
  2792. continue;
  2793. }
  2794. int x = TimeToClient(st);
  2795. int k = RoundFloatToInt(st * static_cast<float>(m_ticksStep));
  2796. if (k % nNumberTicks == 0)
  2797. {
  2798. painter->setPen(black);
  2799. painter->drawLine(x, rc.bottom() - 2, x, rc.bottom() - 14);
  2800. painter->drawText(x + 2, rc.top(), QString::number(st));
  2801. painter->setPen(ltgray);
  2802. }
  2803. else
  2804. {
  2805. painter->drawLine(x, rc.bottom() - 2, x, rc.bottom() - 6);
  2806. }
  2807. }
  2808. }
  2809. //////////////////////////////////////////////////////////////////////////
  2810. void CTrackViewDopeSheetBase::DrawTimeline(QPainter* painter, const QRect& rcUpdate)
  2811. {
  2812. bool recording = GetIEditor()->GetAnimation()->IsRecording();
  2813. QColor lineCol(255, 0, 255);
  2814. const QColor textCol(Qt::black);
  2815. const QColor dkgrayCol(90, 90, 90);
  2816. const QColor ltgrayCol(150, 150, 150);
  2817. if (recording)
  2818. {
  2819. lineCol = Qt::red;
  2820. }
  2821. // Draw vertical line showing current time.
  2822. {
  2823. int x = TimeToClient(m_currentTime);
  2824. if (x > m_rcClient.left() && x < m_rcClient.right())
  2825. {
  2826. const QPen prevPen = painter->pen();
  2827. painter->setPen(lineCol);
  2828. painter->drawLine(x, 0, x, m_rcClient.bottom());
  2829. painter->setPen(prevPen);
  2830. }
  2831. }
  2832. const QRect rc = m_rcTimeline;
  2833. if (!rc.intersects(rcUpdate))
  2834. {
  2835. return;
  2836. }
  2837. QLinearGradient gradient(rc.topLeft(), rc.bottomLeft());
  2838. gradient.setColorAt(0, QColor(250, 250, 250));
  2839. gradient.setColorAt(1, QColor(180, 180, 180));
  2840. painter->fillRect(rc, gradient);
  2841. const QPen prevPen = painter->pen();
  2842. const QPen dkgray(dkgrayCol);
  2843. const QPen ltgray(ltgrayCol);
  2844. const QPen black(textCol);
  2845. const QPen redpen(lineCol);
  2846. // Draw time ticks every tick step seconds.
  2847. painter->setPen(dkgray);
  2848. double step = 1.0 / double(m_ticksStep);
  2849. if (GetTickDisplayMode() == eTVTickMode_InFrames)
  2850. {
  2851. DrawTimeLineInFrames(painter, rc, lineCol, textCol, step);
  2852. }
  2853. else if (GetTickDisplayMode() == eTVTickMode_InSeconds)
  2854. {
  2855. DrawTimeLineInSeconds(painter, rc, lineCol, textCol, step);
  2856. }
  2857. else
  2858. {
  2859. assert (0);
  2860. }
  2861. // Draw time markers.
  2862. int x;
  2863. x = TimeToClient(m_timeMarked.start);
  2864. painter->drawPixmap(QPoint(x, m_rcTimeline.bottom() - 9), QPixmap(":/Trackview/marker/bmp00016_01.png"));
  2865. x = TimeToClient(m_timeMarked.end);
  2866. painter->drawPixmap(QPoint(x - 7, m_rcTimeline.bottom() - 9), QPixmap(":/Trackview/marker/bmp00016_00.png"));
  2867. painter->setPen(redpen);
  2868. x = TimeToClient(m_currentTime);
  2869. painter->setBrush(Qt::NoBrush);
  2870. painter->drawRect(QRect(QPoint(x - 3, rc.top()), QPoint(x + 3, rc.bottom())));
  2871. painter->setPen(redpen);
  2872. painter->drawLine(x, rc.top(), x, rc.bottom());
  2873. painter->setPen(prevPen);
  2874. }
  2875. //////////////////////////////////////////////////////////////////////////
  2876. void CTrackViewDopeSheetBase::DrawSummary(QPainter* painter, const QRect& rcUpdate)
  2877. {
  2878. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  2879. const QColor lineCol = Qt::black;
  2880. const QColor fillCol(150, 100, 220);
  2881. const QRect rc = m_rcSummary;
  2882. if (!rc.intersects(rcUpdate))
  2883. {
  2884. return;
  2885. }
  2886. painter->fillRect(rc, fillCol);
  2887. const QPen prevPen = painter->pen();
  2888. painter->setPen(QPen(lineCol, 3));
  2889. // Draw a short thick line at each place where there is a key in any tracks.
  2890. CTrackViewKeyBundle keys = pSequence->GetAllKeys();
  2891. for (unsigned int i = 0; i < keys.GetKeyCount(); ++i)
  2892. {
  2893. const CTrackViewKeyHandle& keyHandle = keys.GetKey(i);
  2894. int x = TimeToClient(keyHandle.GetTime());
  2895. painter->drawLine(x, rc.bottom() - 2, x, rc.top() + 2);
  2896. }
  2897. painter->setPen(prevPen);
  2898. }
  2899. //////////////////////////////////////////////////////////////////////////
  2900. void CTrackViewDopeSheetBase::DrawNodeTrack(CTrackViewAnimNode* animNode, QPainter* painter, const QRect& trackRect)
  2901. {
  2902. const QFont prevFont = painter->font();
  2903. painter->setFont(m_descriptionFont);
  2904. CTrackViewAnimNode* pDirectorNode = animNode->GetDirector();
  2905. if (pDirectorNode->GetNodeType() != eTVNT_Sequence && !pDirectorNode->IsActiveDirector())
  2906. {
  2907. painter->setPen(INACTIVE_TEXT_COLOR);
  2908. }
  2909. else
  2910. {
  2911. painter->setPen(KEY_TEXT_COLOR);
  2912. }
  2913. const QRect textRect = trackRect.adjusted(4, 0, -4, 0);
  2914. QString sAnimNodeName = QString::fromUtf8(animNode->GetName().c_str());
  2915. const bool hasObsoleteTrack = animNode->HasObsoleteTrack();
  2916. if (hasObsoleteTrack)
  2917. {
  2918. painter->setPen(QColor(245, 80, 70));
  2919. sAnimNodeName += tr(": Some of the sub-tracks contains obsoleted TCB splines (marked in red), thus cannot be copied or pasted.");
  2920. }
  2921. painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, painter->fontMetrics().elidedText(sAnimNodeName, Qt::ElideRight, textRect.width()));
  2922. painter->setFont(prevFont);
  2923. }
  2924. //////////////////////////////////////////////////////////////////////////
  2925. void CTrackViewDopeSheetBase::DrawGoToTrackArrow(CTrackViewTrack* pTrack, QPainter* painter, const QRect& rc)
  2926. {
  2927. int numKeys = pTrack->GetKeyCount();
  2928. const QColor colorLine(150, 150, 150);
  2929. const QColor colorHeader(50, 50, 50);
  2930. const int tickness = 2;
  2931. const int halfMargin = (rc.height() - tickness) / 2;
  2932. for (int i = 0; i < numKeys; ++i)
  2933. {
  2934. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(i);
  2935. IDiscreteFloatKey discreteFloatKey;
  2936. keyHandle.GetKey(&discreteFloatKey);
  2937. int arrowStart = TimeToClient(discreteFloatKey.time);
  2938. int arrowEnd = TimeToClient(discreteFloatKey.m_fValue);
  2939. if (discreteFloatKey.m_fValue < 0.f)
  2940. {
  2941. continue;
  2942. }
  2943. // draw arrow body line
  2944. if (arrowStart < arrowEnd)
  2945. {
  2946. painter->fillRect(QRect(QPoint(arrowStart, rc.top() + halfMargin), QPoint(arrowEnd, rc.bottom() - halfMargin)), colorLine);
  2947. }
  2948. else if (arrowStart > arrowEnd)
  2949. {
  2950. painter->fillRect(QRect(QPoint(arrowEnd, rc.top() + halfMargin), QPoint(arrowStart, rc.bottom() - halfMargin)), colorLine);
  2951. }
  2952. // draw arrow head
  2953. if (arrowStart != arrowEnd)
  2954. {
  2955. painter->fillRect(QRect(QPoint(arrowEnd, rc.top() + 2), QPoint(arrowEnd + 1, rc.bottom() - 2)), colorHeader);
  2956. }
  2957. }
  2958. }
  2959. //////////////////////////////////////////////////////////////////////////
  2960. void CTrackViewDopeSheetBase::DrawKeyDuration(CTrackViewTrack* pTrack, QPainter* painter, const QRect& rc, int keyIndex)
  2961. {
  2962. const CTrackViewKeyHandle& keyHandle = pTrack->GetKey(keyIndex);
  2963. const float time = keyHandle.GetTime();
  2964. const float duration = keyHandle.GetDuration();
  2965. int x = TimeToClient(time);
  2966. // Draw key duration.
  2967. float endt = min(time + duration, m_timeRange.end);
  2968. int x1 = TimeToClient(endt);
  2969. if (x1 < 0)
  2970. {
  2971. if (x > 0)
  2972. {
  2973. x1 = rc.right();
  2974. }
  2975. }
  2976. const QBrush prevBrush = painter->brush();
  2977. painter->setBrush(m_visibilityBrush);
  2978. QColor colorFrom(120, 120, 255);
  2979. if (pTrack->GetParameterType() == AnimParamType::Sound) // If it is a sound key
  2980. {
  2981. ISoundKey soundKey;
  2982. keyHandle.GetKey(&soundKey);
  2983. colorFrom.setRgbF(soundKey.customColor.x, soundKey.customColor.y, soundKey.customColor.z);
  2984. }
  2985. QLinearGradient gradient(x, rc.top() + 3, x, rc.bottom() - 3);
  2986. gradient.setColorAt(0, colorFrom);
  2987. gradient.setColorAt(1, QColor(250, 250, 250));
  2988. const int width = x1 + 1 - x;
  2989. painter->fillRect(QRect(x, rc.top() + 3, width, rc.height() - 3), gradient);
  2990. painter->setBrush(prevBrush);
  2991. painter->drawLine(x1, rc.top(), x1, rc.bottom());
  2992. bool typeHasAnimBox = pTrack->GetParameterType() == AnimParamType::Animation ||
  2993. pTrack->GetParameterType() == AnimParamType::TimeRanges ||
  2994. pTrack->GetValueType() == AnimValueType::CharacterAnim ||
  2995. pTrack->GetValueType() == AnimValueType::AssetBlend;
  2996. // If it is a selected animation track, draw the whole animation box (in green)
  2997. // and two adjust bars (in red) for start/end time each, too.
  2998. if (keyHandle.IsSelected() && typeHasAnimBox)
  2999. {
  3000. // Draw the whole animation box.
  3001. // TODO: Refactor this Time Range Key stuff.
  3002. ICharacterKey characterKey;
  3003. AZ::IAssetBlendKey assetBlendKey;
  3004. ITimeRangeKey* timeRangeKey = nullptr;
  3005. if (pTrack->GetValueType() == AnimValueType::AssetBlend)
  3006. {
  3007. keyHandle.GetKey(&assetBlendKey);
  3008. timeRangeKey = &assetBlendKey;
  3009. }
  3010. else
  3011. {
  3012. // This will work for both character & time range keys because
  3013. // ICharacterKey is derived from ITimeRangeKey. Not the most beautiful code.
  3014. keyHandle.GetKey(&characterKey);
  3015. timeRangeKey = &characterKey;
  3016. }
  3017. int startX = TimeToClient(time - timeRangeKey->m_startTime / timeRangeKey->m_speed);
  3018. int endX = TimeToClient(time + (timeRangeKey->m_duration - timeRangeKey->m_startTime) / timeRangeKey->m_speed);
  3019. const QPen prevPen = painter->pen();
  3020. painter->setPen(Qt::green);
  3021. painter->drawLine(startX, rc.top(), endX, rc.top());
  3022. painter->drawLine(endX, rc.top(), endX, rc.bottom());
  3023. painter->drawLine(endX, rc.bottom(), startX, rc.bottom());
  3024. painter->drawLine(startX, rc.bottom(), startX, rc.top());
  3025. // Draw two adjust bars.
  3026. int durationX = TimeToClient(time + AZStd::min(timeRangeKey->GetValidEndTime(), timeRangeKey->m_duration));
  3027. painter->setPen(QPen(Qt::red, 3));
  3028. painter->drawLine(x - 2, rc.top(), x - 2, rc.bottom());
  3029. painter->drawLine(durationX + 2, rc.top(), durationX + 2, rc.bottom());
  3030. painter->setPen(prevPen);
  3031. }
  3032. }
  3033. //////////////////////////////////////////////////////////////////////////
  3034. void CTrackViewDopeSheetBase::DrawColorGradient(QPainter* painter, const QRect& rc, const CTrackViewTrack* pTrack)
  3035. {
  3036. const QPen pOldPen = painter->pen();
  3037. for (int x = rc.left(); x < rc.right(); ++x)
  3038. {
  3039. // This is really slow. Is there a better way?
  3040. AZ::Vector3 vColor(0, 0, 0);
  3041. pTrack->GetValue(TimeFromPointUnsnapped(QPoint(x, rc.top())), vColor);
  3042. painter->setPen(ColorToQColor(AZ::Color(vColor).ToU32LinearToGamma()));
  3043. painter->drawLine(x, rc.top(), x, rc.bottom());
  3044. }
  3045. painter->setPen(pOldPen);
  3046. }
  3047. //////////////////////////////////////////////////////////////////////////
  3048. QRect CTrackViewDopeSheetBase::GetNodeRect(const CTrackViewNode* pNode) const
  3049. {
  3050. CTrackViewNodesCtrl::CRecord* pRecord = m_pNodesCtrl->GetNodeRecord(pNode);
  3051. if (pRecord && pRecord->IsVisible())
  3052. {
  3053. QRect recordRect = pRecord->GetRect();
  3054. return QRect(0, recordRect.top(), m_rcClient.width(), recordRect.height());
  3055. }
  3056. return QRect();
  3057. }
  3058. //////////////////////////////////////////////////////////////////////////
  3059. void CTrackViewDopeSheetBase::StoreMementoForTracksWithSelectedKeys()
  3060. {
  3061. CTrackViewSequence* pSequence = GetIEditor()->GetAnimation()->GetSequence();
  3062. CTrackViewKeyBundle selectedKeys = pSequence->GetSelectedKeys();
  3063. m_trackMementos.clear();
  3064. // Construct the set of tracks that have selected keys
  3065. std::set<CTrackViewTrack*> tracks;
  3066. const unsigned int numKeys = selectedKeys.GetKeyCount();
  3067. for (unsigned int keyIndex = 0; keyIndex < numKeys; ++keyIndex)
  3068. {
  3069. CTrackViewKeyHandle keyHandle = selectedKeys.GetKey(keyIndex);
  3070. tracks.insert(keyHandle.GetTrack());
  3071. }
  3072. // For each of those tracks store an undo object
  3073. for (auto iter = tracks.begin(); iter != tracks.end(); ++iter)
  3074. {
  3075. CTrackViewTrack* pTrack = *iter;
  3076. TrackMemento trackMemento;
  3077. trackMemento.m_memento = pTrack->GetMemento();
  3078. const unsigned int numKeys2 = pTrack->GetKeyCount();
  3079. for (unsigned int i = 0; i < numKeys2; ++i)
  3080. {
  3081. trackMemento.m_keySelectionStates.push_back(pTrack->GetKey(i).IsSelected());
  3082. }
  3083. m_trackMementos[pTrack] = trackMemento;
  3084. }
  3085. }