EditorKit.cs 127 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Xml;
  7. using UnityEngine;
  8. using UnityEngine.SceneManagement;
  9. #if UNITY_EDITOR
  10. using System.IO;
  11. using UnityEditor;
  12. namespace QFramework
  13. {
  14. [InitializeOnLoad]
  15. public sealed class EasyIMGUI
  16. {
  17. public static ILabel Label()
  18. {
  19. return new Label();
  20. }
  21. public static IButton Button()
  22. {
  23. return new IMGUIButton();
  24. }
  25. public static ISpace Space()
  26. {
  27. return new Space();
  28. }
  29. public static IFlexibleSpace FlexibleSpace()
  30. {
  31. return new FlexibleSpace();
  32. }
  33. public static ITextField TextField()
  34. {
  35. return new TextField();
  36. }
  37. public static ITextArea TextArea()
  38. {
  39. return new TextArea();
  40. }
  41. public static ICustom Custom()
  42. {
  43. return new CustomView();
  44. }
  45. public static IToggle Toggle()
  46. {
  47. return new IMGUIToggle();
  48. }
  49. public static IBox Box()
  50. {
  51. return new BoxView();
  52. }
  53. public static IToolbar Toolbar()
  54. {
  55. return new ToolbarView();
  56. }
  57. public static IVerticalLayout Vertical()
  58. {
  59. return new VerticalLayout();
  60. }
  61. public static IHorizontalLayout Horizontal()
  62. {
  63. return new HorizontalLayout();
  64. }
  65. public static IScrollLayout Scroll()
  66. {
  67. return new ScrollLayout();
  68. }
  69. public static IAreaLayout Area()
  70. {
  71. return new AreaLayout();
  72. }
  73. public static IXMLView XMLView()
  74. {
  75. return new XMLView();
  76. }
  77. public static ILabelWithRect LabelWithRect()
  78. {
  79. return new LabelWithRect();
  80. }
  81. public static IBoxWithRect BoxWithRect()
  82. {
  83. return new BoxWithRect();
  84. }
  85. static EasyIMGUI()
  86. {
  87. XMLKit.Get.SystemLayer.Get<IXMLToObjectConvertSystem>()
  88. .AddModule("EasyIMGUI", new EasyIMGUIXMLModule());
  89. }
  90. }
  91. public interface IMGUILayoutRoot
  92. {
  93. VerticalLayout Layout { get; set; }
  94. RenderEndCommandExecutor RenderEndCommandExecutor { get; set; }
  95. }
  96. public interface IBox : IMGUIView, IHasText<IBox>
  97. {
  98. }
  99. public class BoxView : View, IBox
  100. {
  101. public BoxView()
  102. {
  103. mStyleProperty = new GUIStyleProperty(() =>
  104. {
  105. // Box 的颜色保持和文本的颜色一致
  106. var boxStyle = new GUIStyle(GUI.skin.box) {normal = {textColor = GUI.skin.label.normal.textColor}};
  107. return boxStyle;
  108. });
  109. }
  110. protected override void OnGUI()
  111. {
  112. GUILayout.Box(mText, mStyleProperty.Value, LayoutStyles);
  113. }
  114. private string mText = string.Empty;
  115. public IBox Text(string labelText)
  116. {
  117. mText = labelText;
  118. return this;
  119. }
  120. }
  121. public class GUIStyleProperty
  122. {
  123. private readonly Func<GUIStyle> mCreator;
  124. private Action<GUIStyle> mOperations = (style) => { };
  125. public GUIStyleProperty Set(Action<GUIStyle> operation)
  126. {
  127. mOperations += operation;
  128. return this;
  129. }
  130. public GUIStyleProperty(Func<GUIStyle> creator)
  131. {
  132. mCreator = creator;
  133. }
  134. private GUIStyle mValue = null;
  135. public GUIStyle Value
  136. {
  137. get
  138. {
  139. if (mValue != null) return mValue;
  140. mValue = mCreator.Invoke();
  141. mOperations(mValue);
  142. return mValue;
  143. }
  144. set { mValue = value; }
  145. }
  146. }
  147. public interface IBoxWithRect : IMGUIView, IHasRect<IBoxWithRect>
  148. {
  149. IBoxWithRect Text(string text);
  150. }
  151. public class BoxWithRect : View, IBoxWithRect
  152. {
  153. public BoxWithRect()
  154. {
  155. mStyleProperty = new GUIStyleProperty(() => GUI.skin.box);
  156. }
  157. private Rect mRect = new Rect(0, 0, 200, 100);
  158. private string mText = string.Empty;
  159. protected override void OnGUI()
  160. {
  161. GUI.Box(mRect, mText, mStyleProperty.Value);
  162. }
  163. public IBoxWithRect Text(string labelText)
  164. {
  165. mText = labelText;
  166. return this;
  167. }
  168. public T Convert<T>(XmlNode node) where T : class
  169. {
  170. throw new System.NotImplementedException();
  171. }
  172. public IBoxWithRect Rect(Rect rect)
  173. {
  174. mRect = rect;
  175. return this;
  176. }
  177. public IBoxWithRect Position(Vector2 position)
  178. {
  179. mRect.position = position;
  180. return this;
  181. }
  182. public IBoxWithRect Position(float x, float y)
  183. {
  184. mRect.x = x;
  185. mRect.y = y;
  186. return this;
  187. }
  188. public IBoxWithRect Size(float width, float height)
  189. {
  190. mRect.width = width;
  191. mRect.height = height;
  192. return this;
  193. }
  194. public IBoxWithRect Size(Vector2 size)
  195. {
  196. mRect.size = size;
  197. return this;
  198. }
  199. public IBoxWithRect Width(float width)
  200. {
  201. mRect.width = width;
  202. return this;
  203. }
  204. public IBoxWithRect Height(float height)
  205. {
  206. mRect.height = height;
  207. return this;
  208. }
  209. }
  210. public interface ILabelWithRect : IMGUIView, IHasText<ILabelWithRect>, IXMLToObjectConverter,
  211. IHasRect<ILabelWithRect>
  212. {
  213. }
  214. public class LabelWithRect : View, ILabelWithRect
  215. {
  216. public LabelWithRect()
  217. {
  218. mStyleProperty = new GUIStyleProperty(() => new GUIStyle(GUI.skin.label));
  219. }
  220. private string mText = string.Empty;
  221. private Rect mRect = new Rect(0, 0, 200, 100);
  222. protected override void OnGUI()
  223. {
  224. GUI.Label(mRect, mText, mStyleProperty.Value);
  225. }
  226. public ILabelWithRect Text(string labelText)
  227. {
  228. mText = labelText;
  229. return this;
  230. }
  231. public T Convert<T>(XmlNode node) where T : class
  232. {
  233. throw new System.NotImplementedException();
  234. }
  235. public ILabelWithRect Rect(Rect rect)
  236. {
  237. mRect = rect;
  238. return this;
  239. }
  240. public ILabelWithRect Position(Vector2 position)
  241. {
  242. mRect.position = position;
  243. return this;
  244. }
  245. public ILabelWithRect Position(float x, float y)
  246. {
  247. mRect.x = x;
  248. mRect.y = y;
  249. return this;
  250. }
  251. public ILabelWithRect Size(float width, float height)
  252. {
  253. mRect.width = width;
  254. mRect.height = height;
  255. return this;
  256. }
  257. public ILabelWithRect Size(Vector2 size)
  258. {
  259. mRect.size = size;
  260. return this;
  261. }
  262. public ILabelWithRect Width(float width)
  263. {
  264. mRect.width = width;
  265. return this;
  266. }
  267. public ILabelWithRect Height(float height)
  268. {
  269. mRect.height = height;
  270. return this;
  271. }
  272. }
  273. public class ImageButtonView : View
  274. {
  275. private Texture2D mTexture2D { get; set; }
  276. private Action mOnClick { get; set; }
  277. public ImageButtonView(string texturePath, Action onClick)
  278. {
  279. mTexture2D = Resources.Load<Texture2D>(texturePath);
  280. mOnClick = onClick;
  281. //Style = new GUIStyle(GUI.skin.button);
  282. }
  283. protected override void OnGUI()
  284. {
  285. if (GUILayout.Button(mTexture2D, LayoutStyles))
  286. {
  287. mOnClick.Invoke();
  288. }
  289. }
  290. }
  291. public interface IToolbar : IMGUIView
  292. {
  293. IToolbar Menus(List<string> menuNames);
  294. IToolbar AddMenu(string name, Action<string> onMenuSelected = null);
  295. BindableProperty<int> IndexProperty { get; }
  296. IToolbar Index(int index);
  297. }
  298. internal class ToolbarView : View, IToolbar
  299. {
  300. public ToolbarView()
  301. {
  302. IndexProperty = new BindableProperty<int>(0);
  303. IndexProperty.Register(index =>
  304. {
  305. if (MenuSelected.Count > index)
  306. {
  307. MenuSelected[index].Invoke(MenuNames[index]);
  308. }
  309. });
  310. Style = new GUIStyleProperty(() => GUI.skin.button);
  311. }
  312. public IToolbar Menus(List<string> menuNames)
  313. {
  314. this.MenuNames = menuNames;
  315. // empty
  316. this.MenuSelected = MenuNames.Select(menuName => new Action<string>((str => { }))).ToList();
  317. return this;
  318. }
  319. public IToolbar AddMenu(string name, Action<string> onMenuSelected = null)
  320. {
  321. MenuNames.Add(name);
  322. if (onMenuSelected == null)
  323. {
  324. MenuSelected.Add((item) => { });
  325. }
  326. else
  327. {
  328. MenuSelected.Add(onMenuSelected);
  329. }
  330. return this;
  331. }
  332. List<string> MenuNames = new List<string>();
  333. List<Action<string>> MenuSelected = new List<Action<string>>();
  334. public BindableProperty<int> IndexProperty { get; private set; }
  335. public IToolbar Index(int index)
  336. {
  337. IndexProperty.Value = index;
  338. return this;
  339. }
  340. protected override void OnGUI()
  341. {
  342. IndexProperty.Value = GUILayout.Toolbar(IndexProperty.Value, MenuNames.ToArray(), Style.Value, LayoutStyles);
  343. }
  344. }
  345. public interface IToggle : IMGUIView,IHasText<IToggle>
  346. {
  347. BindableProperty<bool> ValueProperty { get; }
  348. IToggle IsOn(bool isOn);
  349. }
  350. internal class IMGUIToggle : View,IToggle
  351. {
  352. private string mText { get; set; }
  353. public IMGUIToggle()
  354. {
  355. ValueProperty = new BindableProperty<bool>(false);
  356. Style = new GUIStyleProperty(() => GUI.skin.toggle);
  357. }
  358. public BindableProperty<bool> ValueProperty { get; private set; }
  359. public IToggle IsOn(bool isOn)
  360. {
  361. ValueProperty.Value = isOn;
  362. return this;
  363. }
  364. protected override void OnGUI()
  365. {
  366. ValueProperty.Value = GUILayout.Toggle(ValueProperty.Value, mText ?? string.Empty, Style.Value, LayoutStyles);
  367. }
  368. public IToggle Text(string text)
  369. {
  370. mText = text;
  371. return this;
  372. }
  373. }
  374. [Serializable]
  375. public abstract class ReorderableArray<T> : ICloneable, IList<T>, ICollection<T>, IEnumerable<T> {
  376. [SerializeField]
  377. private List<T> array = new List<T>();
  378. public ReorderableArray()
  379. : this(0) {
  380. }
  381. public ReorderableArray(int length) {
  382. array = new List<T>(length);
  383. }
  384. public T this[int index] {
  385. get { return array[index]; }
  386. set { array[index] = value; }
  387. }
  388. public int Length {
  389. get { return array.Count; }
  390. }
  391. public bool IsReadOnly {
  392. get { return false; }
  393. }
  394. public int Count {
  395. get { return array.Count; }
  396. }
  397. public object Clone() {
  398. return new List<T>(array);
  399. }
  400. public void CopyFrom(IEnumerable<T> value) {
  401. array.Clear();
  402. array.AddRange(value);
  403. }
  404. public bool Contains(T value) {
  405. return array.Contains(value);
  406. }
  407. public int IndexOf(T value) {
  408. return array.IndexOf(value);
  409. }
  410. public void Insert(int index, T item) {
  411. array.Insert(index, item);
  412. }
  413. public void RemoveAt(int index) {
  414. array.RemoveAt(index);
  415. }
  416. public void Add(T item) {
  417. array.Add(item);
  418. }
  419. public void Clear() {
  420. array.Clear();
  421. }
  422. public void CopyTo(T[] array, int arrayIndex) {
  423. this.array.CopyTo(array, arrayIndex);
  424. }
  425. public bool Remove(T item) {
  426. return array.Remove(item);
  427. }
  428. public T[] ToArray() {
  429. return array.ToArray();
  430. }
  431. public IEnumerator<T> GetEnumerator() {
  432. return array.GetEnumerator();
  433. }
  434. IEnumerator IEnumerable.GetEnumerator() {
  435. return array.GetEnumerator();
  436. }
  437. }
  438. public class ReorderableAttribute : PropertyAttribute {
  439. public bool add;
  440. public bool remove;
  441. public bool draggable;
  442. public bool singleLine;
  443. public string elementNameProperty;
  444. public string elementNameOverride;
  445. public string elementIconPath;
  446. public ReorderableAttribute()
  447. : this(null) {
  448. }
  449. public ReorderableAttribute(string elementNameProperty)
  450. : this(true, true, true, elementNameProperty, null, null) {
  451. }
  452. public ReorderableAttribute(string elementNameProperty, string elementIconPath)
  453. : this(true, true, true, elementNameProperty, null, elementIconPath) {
  454. }
  455. public ReorderableAttribute(string elementNameProperty, string elementNameOverride, string elementIconPath)
  456. : this(true, true, true, elementNameProperty, elementNameOverride, elementIconPath) {
  457. }
  458. public ReorderableAttribute(bool add, bool remove, bool draggable, string elementNameProperty = null, string elementIconPath = null)
  459. : this(add, remove, draggable, elementNameProperty, null, elementIconPath) {
  460. }
  461. public ReorderableAttribute(bool add, bool remove, bool draggable, string elementNameProperty = null, string elementNameOverride = null, string elementIconPath = null) {
  462. this.add = add;
  463. this.remove = remove;
  464. this.draggable = draggable;
  465. this.elementNameProperty = elementNameProperty;
  466. this.elementNameOverride = elementNameOverride;
  467. this.elementIconPath = elementIconPath;
  468. }
  469. }
  470. public interface ITextArea : IMGUIView, IHasText<ITextArea>, IXMLToObjectConverter
  471. {
  472. BindableProperty<string> Content { get; }
  473. }
  474. internal class TextArea : View, ITextArea
  475. {
  476. public TextArea()
  477. {
  478. Content = new BindableProperty<string>(string.Empty);
  479. mStyleProperty = new GUIStyleProperty(() => GUI.skin.textArea);
  480. }
  481. public BindableProperty<string> Content { get; private set; }
  482. protected override void OnGUI()
  483. {
  484. Content.Value = CrossPlatformGUILayout.TextArea(Content.Value, mStyleProperty.Value, LayoutStyles);
  485. }
  486. public ITextArea Text(string labelText)
  487. {
  488. Content.Value = labelText;
  489. return this;
  490. }
  491. public T Convert<T>(XmlNode node) where T : class
  492. {
  493. var textArea = EasyIMGUI.TextArea();
  494. foreach (XmlAttribute nodeAttribute in node.Attributes)
  495. {
  496. if (nodeAttribute.Name == "Id")
  497. {
  498. textArea.Id = nodeAttribute.Value;
  499. }
  500. else if (nodeAttribute.Name == "Text")
  501. {
  502. textArea.Text(nodeAttribute.Value);
  503. }
  504. }
  505. return textArea as T;
  506. }
  507. }
  508. public interface ITextField : IMGUIView, IHasText<ITextField>,IXMLToObjectConverter
  509. {
  510. BindableProperty<string> Content { get; }
  511. ITextField PasswordMode();
  512. }
  513. public class TextField : View,ITextField
  514. {
  515. public TextField()
  516. {
  517. Content = new BindableProperty<string>(string.Empty);
  518. mStyleProperty = new GUIStyleProperty(() => GUI.skin.textField);
  519. }
  520. public BindableProperty<string> Content { get; private set; }
  521. protected override void OnGUI()
  522. {
  523. if (mPasswordMode)
  524. {
  525. Content.Value = CrossPlatformGUILayout.PasswordField(Content.Value, Style.Value, LayoutStyles);
  526. }
  527. else
  528. {
  529. Content.Value = CrossPlatformGUILayout.TextField(Content.Value, Style.Value, LayoutStyles);
  530. }
  531. }
  532. private bool mPasswordMode = false;
  533. public ITextField PasswordMode()
  534. {
  535. mPasswordMode = true;
  536. return this;
  537. }
  538. public ITextField Text(string labelText)
  539. {
  540. Content.Value = labelText;
  541. return this;
  542. }
  543. public T Convert<T>(XmlNode node) where T : class
  544. {
  545. var textArea = EasyIMGUI.TextField();
  546. foreach (XmlAttribute nodeAttribute in node.Attributes)
  547. {
  548. if (nodeAttribute.Name == "Id")
  549. {
  550. textArea.Id = nodeAttribute.Value;
  551. }
  552. else if (nodeAttribute.Name == "Text")
  553. {
  554. textArea.Text(nodeAttribute.Value);
  555. }
  556. }
  557. return textArea as T;
  558. }
  559. }
  560. public interface IFlexibleSpace : IMGUIView, IXMLToObjectConverter
  561. {
  562. }
  563. internal class FlexibleSpace : View, IFlexibleSpace
  564. {
  565. protected override void OnGUI()
  566. {
  567. GUILayout.FlexibleSpace();
  568. }
  569. public T Convert<T>(XmlNode node) where T : class
  570. {
  571. var flexibleSpace = EasyIMGUI.FlexibleSpace();
  572. foreach (XmlAttribute childNodeAttribute in node.Attributes)
  573. {
  574. if (childNodeAttribute.Name == "Id")
  575. {
  576. flexibleSpace.Id = childNodeAttribute.Value;
  577. }
  578. }
  579. return flexibleSpace as T;
  580. }
  581. }
  582. public interface ISpace : IMGUIView,IXMLToObjectConverter
  583. {
  584. ISpace Pixel(int pixel);
  585. }
  586. internal class Space : View,ISpace
  587. {
  588. private int mPixel = 10;
  589. protected override void OnGUI()
  590. {
  591. GUILayout.Space(mPixel);
  592. }
  593. public ISpace Pixel(int pixel)
  594. {
  595. mPixel = pixel;
  596. return this;
  597. }
  598. public T Convert<T>(XmlNode node) where T : class
  599. {
  600. var space = EasyIMGUI.Space();
  601. foreach (XmlAttribute nodeAttribute in node.Attributes)
  602. {
  603. if (nodeAttribute.Name == "Id")
  604. {
  605. space.Id = nodeAttribute.Value;
  606. }
  607. else if (nodeAttribute.Name == "Pixel")
  608. {
  609. space.Pixel(int.Parse(nodeAttribute.Value));
  610. }
  611. }
  612. return space as T;
  613. }
  614. }
  615. public interface ILabel : IMGUIView, IHasText<ILabel>, IHasTextGetter<ILabel>, IXMLToObjectConverter
  616. {
  617. }
  618. internal class Label : View, ILabel
  619. {
  620. public string Content { get; set; }
  621. public Label()
  622. {
  623. mStyleProperty = new GUIStyleProperty(() => new GUIStyle(GUI.skin.label));
  624. }
  625. protected override void OnGUI()
  626. {
  627. GUILayout.Label(mTextGetter == null ? Content : mTextGetter(), Style.Value, LayoutStyles);
  628. }
  629. public ILabel Text(string labelText)
  630. {
  631. Content = labelText;
  632. return this;
  633. }
  634. public ILabel TextGetter(Func<string> textGetter)
  635. {
  636. mTextGetter = textGetter;
  637. return this;
  638. }
  639. private Func<string> mTextGetter;
  640. public T Convert<T>(XmlNode node) where T : class
  641. {
  642. var label = EasyIMGUI.Label();
  643. foreach (XmlAttribute childNodeAttribute in node.Attributes)
  644. {
  645. if (childNodeAttribute.Name == "Id")
  646. {
  647. label.Id = childNodeAttribute.Value;
  648. }
  649. else if (childNodeAttribute.Name == "Text")
  650. {
  651. label.Text(childNodeAttribute.Value);
  652. }
  653. else if (childNodeAttribute.Name == "FontBold")
  654. {
  655. label.FontBold();
  656. }
  657. else if (childNodeAttribute.Name == "FontSize")
  658. {
  659. label.FontSize(int.Parse(childNodeAttribute.Value));
  660. }
  661. }
  662. return label as T;
  663. }
  664. }
  665. public interface ICustom : IMGUIView,IXMLToObjectConverter
  666. {
  667. ICustom OnGUI(Action onGUI);
  668. }
  669. internal class CustomView : View,ICustom
  670. {
  671. private Action mOnGUIAction { get; set; }
  672. protected override void OnGUI()
  673. {
  674. mOnGUIAction.Invoke();
  675. }
  676. public ICustom OnGUI(Action onGUI)
  677. {
  678. mOnGUIAction = onGUI;
  679. return this;
  680. }
  681. public T Convert<T>(XmlNode node) where T : class
  682. {
  683. var custom = EasyIMGUI.Custom();
  684. foreach (XmlAttribute nodeAttribute in node.Attributes)
  685. {
  686. if (nodeAttribute.Name == "Id")
  687. {
  688. custom.Id = nodeAttribute.Value;
  689. }
  690. }
  691. return custom as T;
  692. }
  693. }
  694. public interface IButton : IMGUIView,
  695. IHasText<IButton>,
  696. IHasTextGetter<IButton>,
  697. ICanClick<IButton>,
  698. IXMLToObjectConverter
  699. {
  700. }
  701. internal class IMGUIButton : View, IButton
  702. {
  703. private string mLabelText = string.Empty;
  704. private Action mOnClick = () => { };
  705. protected override void OnGUI()
  706. {
  707. if (GUILayout.Button(mTextGetter == null ? mLabelText : mTextGetter(), GUI.skin.button, LayoutStyles))
  708. {
  709. mOnClick.Invoke();
  710. // GUIUtility.ExitGUI();
  711. }
  712. }
  713. public IButton Text(string labelText)
  714. {
  715. mLabelText = labelText;
  716. return this;
  717. }
  718. public IButton OnClick(Action action)
  719. {
  720. mOnClick = action;
  721. return this;
  722. }
  723. public T Convert<T>(XmlNode node) where T : class
  724. {
  725. var button = EasyIMGUI.Button();
  726. foreach (XmlAttribute childNodeAttribute in node.Attributes)
  727. {
  728. if (childNodeAttribute.Name == "Id")
  729. {
  730. button.Id = childNodeAttribute.Value;
  731. }
  732. else if (childNodeAttribute.Name == "Text")
  733. {
  734. button.Text(childNodeAttribute.Value);
  735. }
  736. else if (childNodeAttribute.Name == "Width")
  737. {
  738. button.Width(int.Parse(childNodeAttribute.Value));
  739. }
  740. }
  741. return button as T;
  742. }
  743. private Func<string> mTextGetter;
  744. public IButton TextGetter(Func<string> textGetter)
  745. {
  746. mTextGetter = textGetter;
  747. return this;
  748. }
  749. }
  750. public interface IVerticalLayout : IMGUILayout,IXMLToObjectConverter
  751. {
  752. IVerticalLayout Box();
  753. }
  754. public class VerticalLayout : Layout,IVerticalLayout
  755. {
  756. public VerticalLayout(){}
  757. public string VerticalStyle { get; set; }
  758. public VerticalLayout(string verticalStyle = null)
  759. {
  760. VerticalStyle = verticalStyle;
  761. }
  762. protected override void OnGUIBegin()
  763. {
  764. if (string.IsNullOrEmpty(VerticalStyle))
  765. {
  766. GUILayout.BeginVertical(LayoutStyles);
  767. }
  768. else
  769. {
  770. GUILayout.BeginVertical(VerticalStyle, LayoutStyles);
  771. }
  772. }
  773. protected override void OnGUIEnd()
  774. {
  775. GUILayout.EndVertical();
  776. }
  777. public IVerticalLayout Box()
  778. {
  779. VerticalStyle = "box";
  780. return this;
  781. }
  782. public T Convert<T>(XmlNode node) where T : class
  783. {
  784. var scroll = EasyIMGUI.Vertical();
  785. foreach (XmlAttribute childNodeAttribute in node.Attributes)
  786. {
  787. if (childNodeAttribute.Name == "Id")
  788. {
  789. scroll.Id = childNodeAttribute.Value;
  790. }
  791. }
  792. return scroll as T;
  793. }
  794. }
  795. public interface IScrollLayout : IMGUILayout,IXMLToObjectConverter
  796. {
  797. }
  798. internal class ScrollLayout : Layout,IScrollLayout
  799. {
  800. Vector2 mScrollPos = Vector2.zero;
  801. protected override void OnGUIBegin()
  802. {
  803. mScrollPos = GUILayout.BeginScrollView(mScrollPos, LayoutStyles);
  804. }
  805. protected override void OnGUIEnd()
  806. {
  807. GUILayout.EndScrollView();
  808. }
  809. public T Convert<T>(XmlNode node) where T : class
  810. {
  811. var scroll = EasyIMGUI.Scroll();
  812. foreach (XmlAttribute childNodeAttribute in node.Attributes)
  813. {
  814. if (childNodeAttribute.Name == "Id")
  815. {
  816. scroll.Id = childNodeAttribute.Value;
  817. }
  818. }
  819. return scroll as T;
  820. }
  821. }
  822. public interface IHorizontalLayout : IMGUILayout, IXMLToObjectConverter
  823. {
  824. IHorizontalLayout Box();
  825. }
  826. public class HorizontalLayout : Layout, IHorizontalLayout
  827. {
  828. public string HorizontalStyle { get; set; }
  829. protected override void OnGUIBegin()
  830. {
  831. if (string.IsNullOrEmpty(HorizontalStyle))
  832. {
  833. GUILayout.BeginHorizontal();
  834. }
  835. else
  836. {
  837. GUILayout.BeginHorizontal(HorizontalStyle);
  838. }
  839. }
  840. protected override void OnGUIEnd()
  841. {
  842. GUILayout.EndHorizontal();
  843. }
  844. public IHorizontalLayout Box()
  845. {
  846. HorizontalStyle = "box";
  847. return this;
  848. }
  849. public T Convert<T>(XmlNode node) where T : class
  850. {
  851. var horizontal = EasyIMGUI.Horizontal();
  852. foreach (XmlAttribute childNodeAttribute in node.Attributes)
  853. {
  854. if (childNodeAttribute.Name == "Id")
  855. {
  856. horizontal.Id = childNodeAttribute.Value;
  857. }
  858. }
  859. return horizontal as T;
  860. }
  861. }
  862. public interface IAreaLayout : IMGUILayout
  863. {
  864. IAreaLayout WithRect(Rect rect);
  865. IAreaLayout WithRectGetter(Func<Rect> rectGetter);
  866. }
  867. public class AreaLayout : Layout, IAreaLayout
  868. {
  869. private Rect mRect;
  870. private Func<Rect> mRectGetter;
  871. public IAreaLayout WithRect(Rect rect)
  872. {
  873. mRect = rect;
  874. return this;
  875. }
  876. public IAreaLayout WithRectGetter(Func<Rect> rectGetter)
  877. {
  878. mRectGetter = rectGetter;
  879. return this;
  880. }
  881. protected override void OnGUIBegin()
  882. {
  883. GUILayout.BeginArea(mRectGetter == null ? mRect : mRectGetter());
  884. }
  885. protected override void OnGUIEnd()
  886. {
  887. GUILayout.EndArea();
  888. }
  889. }
  890. public abstract class View : IMGUIView
  891. {
  892. private bool mVisible = true;
  893. public string Id { get; set; }
  894. public bool Visible
  895. {
  896. get { return VisibleCondition == null ? mVisible : VisibleCondition(); }
  897. set { mVisible = value; }
  898. }
  899. public Func<bool> VisibleCondition { get; set; }
  900. private readonly List<GUILayoutOption> mPrivateLayoutOptions = new List<GUILayoutOption>();
  901. private List<GUILayoutOption> mLayoutOptions
  902. {
  903. get { return mPrivateLayoutOptions; }
  904. }
  905. protected GUILayoutOption[] LayoutStyles { get; private set; }
  906. protected GUIStyleProperty mStyleProperty = new GUIStyleProperty(() => new GUIStyle());
  907. public GUIStyleProperty Style
  908. {
  909. get { return mStyleProperty; }
  910. protected set { mStyleProperty = value; }
  911. }
  912. private Color mBackgroundColor = GUI.backgroundColor;
  913. public Color BackgroundColor
  914. {
  915. get { return mBackgroundColor; }
  916. set { mBackgroundColor = value; }
  917. }
  918. public void RefreshNextFrame()
  919. {
  920. this.PushCommand(Refresh);
  921. }
  922. public void AddLayoutOption(GUILayoutOption option)
  923. {
  924. mLayoutOptions.Add(option);
  925. }
  926. public void Show()
  927. {
  928. Visible = true;
  929. OnShow();
  930. }
  931. protected virtual void OnShow()
  932. {
  933. }
  934. public void Hide()
  935. {
  936. Visible = false;
  937. OnHide();
  938. }
  939. protected virtual void OnHide()
  940. {
  941. }
  942. private Color mPreviousBackgroundColor;
  943. public void DrawGUI()
  944. {
  945. BeforeDraw();
  946. if (Visible)
  947. {
  948. mPreviousBackgroundColor = GUI.backgroundColor;
  949. GUI.backgroundColor = BackgroundColor;
  950. OnGUI();
  951. GUI.backgroundColor = mPreviousBackgroundColor;
  952. }
  953. if (mCommands.Count > 0)
  954. {
  955. mCommands.Dequeue().Invoke();
  956. }
  957. }
  958. protected void PushCommand(Action command)
  959. {
  960. mCommands.Enqueue(command);
  961. }
  962. Queue<Action> mCommands = new Queue<Action>();
  963. private bool mBeforeDrawCalled = false;
  964. void BeforeDraw()
  965. {
  966. if (!mBeforeDrawCalled)
  967. {
  968. OnBeforeDraw();
  969. LayoutStyles = mLayoutOptions.ToArray();
  970. mBeforeDrawCalled = true;
  971. }
  972. }
  973. protected virtual void OnBeforeDraw()
  974. {
  975. }
  976. public IMGUILayout Parent { get; set; }
  977. public void RemoveFromParent()
  978. {
  979. Parent.RemoveChild(this);
  980. }
  981. public virtual void Refresh()
  982. {
  983. OnRefresh();
  984. }
  985. protected virtual void OnRefresh()
  986. {
  987. }
  988. protected abstract void OnGUI();
  989. public void Dispose()
  990. {
  991. OnDisposed();
  992. }
  993. protected virtual void OnDisposed()
  994. {
  995. }
  996. }
  997. public abstract class Layout : View, IMGUILayout
  998. {
  999. protected List<IMGUIView> Children = new List<IMGUIView>();
  1000. public IMGUILayout AddChild(IMGUIView view)
  1001. {
  1002. Children.Add(view);
  1003. view.Parent = this;
  1004. return this;
  1005. }
  1006. public void RemoveChild(IMGUIView view)
  1007. {
  1008. this.PushCommand(() =>
  1009. {
  1010. Children.Remove(view);
  1011. view.Parent = null;
  1012. });
  1013. view.Dispose();
  1014. }
  1015. public void Clear()
  1016. {
  1017. this.Children.ForEach(view =>
  1018. {
  1019. view.Parent = null;
  1020. view.Dispose();
  1021. });
  1022. this.Children.Clear();
  1023. }
  1024. public override void Refresh()
  1025. {
  1026. Children.ForEach(view => view.Refresh());
  1027. base.Refresh();
  1028. }
  1029. protected override void OnGUI()
  1030. {
  1031. OnGUIBegin();
  1032. foreach (var child in Children)
  1033. {
  1034. child.DrawGUI();
  1035. }
  1036. OnGUIEnd();
  1037. }
  1038. protected virtual void OnGUIBegin(){}
  1039. protected virtual void OnGUIEnd(){}
  1040. }
  1041. public interface IMGUIView : IDisposable
  1042. {
  1043. string Id { get; set; }
  1044. bool Visible { get; set; }
  1045. Func<bool> VisibleCondition { get; set; }
  1046. void DrawGUI();
  1047. IMGUILayout Parent { get; set; }
  1048. GUIStyleProperty Style { get; }
  1049. Color BackgroundColor { get; set; }
  1050. void RefreshNextFrame();
  1051. void AddLayoutOption(GUILayoutOption option);
  1052. void RemoveFromParent();
  1053. void Refresh();
  1054. void Hide();
  1055. void Show();
  1056. }
  1057. public interface IMGUILayout : IMGUIView
  1058. {
  1059. IMGUILayout AddChild(IMGUIView view);
  1060. void RemoveChild(IMGUIView view);
  1061. void Clear();
  1062. }
  1063. public interface ICanClick<T>
  1064. {
  1065. T OnClick(Action action);
  1066. }
  1067. public interface IHasRect<T>
  1068. {
  1069. T Rect(Rect rect);
  1070. T Position(Vector2 position);
  1071. T Position(float x, float y);
  1072. T Size(float width, float height);
  1073. T Size(Vector2 size);
  1074. T Width(float width);
  1075. T Height(float height);
  1076. }
  1077. public interface IHasText<T>
  1078. {
  1079. T Text(string labelText);
  1080. }
  1081. public interface IHasTextGetter<T>
  1082. {
  1083. T TextGetter(Func<string> textGetter);
  1084. }
  1085. public static class IMGUILayoutRootExtension
  1086. {
  1087. public static IMGUILayout GetLayout(this IMGUILayoutRoot self)
  1088. {
  1089. if (self.Layout == null)
  1090. {
  1091. self.Layout = new VerticalLayout();
  1092. }
  1093. return self.Layout;
  1094. }
  1095. public static void AddChild(this IMGUILayoutRoot self, IMGUIView child)
  1096. {
  1097. self.GetLayout().AddChild(child);
  1098. }
  1099. public static void RemoveChild(this IMGUILayoutRoot self, IMGUIView child)
  1100. {
  1101. self.GetLayout().RemoveChild(child);
  1102. }
  1103. public static RenderEndCommandExecutor GetCommandExecutor(this IMGUILayoutRoot self)
  1104. {
  1105. if (self.RenderEndCommandExecutor == null)
  1106. {
  1107. self.RenderEndCommandExecutor = new RenderEndCommandExecutor();
  1108. }
  1109. return self.RenderEndCommandExecutor;
  1110. }
  1111. public static void PushRenderEndCommand(this IMGUILayoutRoot self, Action command)
  1112. {
  1113. self.GetCommandExecutor().Push(command);
  1114. }
  1115. public static void ExecuteRenderEndCommand(this IMGUILayoutRoot self)
  1116. {
  1117. self.GetCommandExecutor().Execute();
  1118. }
  1119. }
  1120. public static class LayoutExtension
  1121. {
  1122. public static T Parent<T>(this T view, IMGUILayout parent) where T : IMGUIView
  1123. {
  1124. parent.AddChild(view);
  1125. return view;
  1126. }
  1127. }
  1128. public static class ViewExtension
  1129. {
  1130. public static TView Self<TView>(this TView self, Action<TView> onDo) where TView : IMGUIView
  1131. {
  1132. onDo(self);
  1133. return self;
  1134. }
  1135. public static T Width<T>(this T view, float width) where T : IMGUIView
  1136. {
  1137. view.AddLayoutOption(GUILayout.Width(width));
  1138. return view;
  1139. }
  1140. public static T Height<T>(this T view, float height) where T : IMGUIView
  1141. {
  1142. view.AddLayoutOption(GUILayout.Height(height));
  1143. return view;
  1144. }
  1145. public static T MaxHeight<T>(this T view, float height) where T : IMGUIView
  1146. {
  1147. view.AddLayoutOption(GUILayout.MaxHeight(height));
  1148. return view;
  1149. }
  1150. public static T MinHeight<T>(this T view, float height) where T : IMGUIView
  1151. {
  1152. view.AddLayoutOption(GUILayout.MinHeight(height));
  1153. return view;
  1154. }
  1155. public static T ExpandHeight<T>(this T view) where T : IMGUIView
  1156. {
  1157. view.AddLayoutOption(GUILayout.ExpandHeight(true));
  1158. return view;
  1159. }
  1160. public static T TextMiddleLeft<T>(this T view) where T : IMGUIView
  1161. {
  1162. view.Style.Set(style => style.alignment = TextAnchor.MiddleLeft);
  1163. return view;
  1164. }
  1165. public static T TextMiddleRight<T>(this T view) where T : IMGUIView
  1166. {
  1167. view.Style.Set(style => style.alignment = TextAnchor.MiddleRight);
  1168. return view;
  1169. }
  1170. public static T TextLowerRight<T>(this T view) where T : IMGUIView
  1171. {
  1172. view.Style.Set(style => style.alignment = TextAnchor.LowerRight);
  1173. return view;
  1174. }
  1175. public static T TextMiddleCenter<T>(this T view) where T : IMGUIView
  1176. {
  1177. view.Style.Set(style => style.alignment = TextAnchor.MiddleCenter);
  1178. return view;
  1179. }
  1180. public static T TextLowerCenter<T>(this T view) where T : IMGUIView
  1181. {
  1182. view.Style.Set(style => style.alignment = TextAnchor.LowerCenter);
  1183. return view;
  1184. }
  1185. public static T Color<T>(this T view, Color color) where T : IMGUIView
  1186. {
  1187. view.BackgroundColor = color;
  1188. return view;
  1189. }
  1190. public static T FontColor<T>(this T view, Color color) where T : IMGUIView
  1191. {
  1192. view.Style.Set(style => style.normal.textColor = color);
  1193. return view;
  1194. }
  1195. public static T FontBold<T>(this T view) where T : IMGUIView
  1196. {
  1197. view.Style.Set(style => style.fontStyle = FontStyle.Bold);
  1198. return view;
  1199. }
  1200. public static T FontNormal<T>(this T view) where T : IMGUIView
  1201. {
  1202. view.Style.Set(style => style.fontStyle = FontStyle.Normal);
  1203. return view;
  1204. }
  1205. public static T FontSize<T>(this T view, int fontSize) where T : IMGUIView
  1206. {
  1207. view.Style.Set(style => style.fontSize = fontSize);
  1208. return view;
  1209. }
  1210. public static T Visible<T>(this T view, bool visible) where T : IMGUIView
  1211. {
  1212. view.Visible = visible;
  1213. return view;
  1214. }
  1215. public static T WithVisibleCondition<T>(this T view, Func<bool> visibleCondition) where T : IMGUIView
  1216. {
  1217. view.VisibleCondition = visibleCondition;
  1218. return view;
  1219. }
  1220. }
  1221. public class CrossPlatformGUILayout
  1222. {
  1223. public static string PasswordField(string value, GUIStyle style, GUILayoutOption[] options)
  1224. {
  1225. #if UNITY_EDITOR
  1226. return EditorGUILayout.PasswordField(value, style, options);
  1227. #else
  1228. return GUILayout.PasswordField(value, '*', style, options);
  1229. #endif
  1230. }
  1231. public static string TextField(string value, GUIStyle style, GUILayoutOption[] options)
  1232. {
  1233. #if UNITY_EDITOR
  1234. return EditorGUILayout.TextField(value, style, options);
  1235. #else
  1236. return GUILayout.TextField(value, style, options);
  1237. #endif
  1238. }
  1239. public static string TextArea(string value, GUIStyle style, GUILayoutOption[] options)
  1240. {
  1241. #if UNITY_EDITOR
  1242. return EditorGUILayout.TextArea(value, style, options);
  1243. #else
  1244. return GUILayout.TextArea(value, style, options);
  1245. #endif
  1246. }
  1247. }
  1248. public class RenderEndCommandExecutor
  1249. {
  1250. // 全局的
  1251. private static RenderEndCommandExecutor mGlobal = new RenderEndCommandExecutor();
  1252. private Queue<System.Action> mPrivateCommands = new Queue<System.Action>();
  1253. private Queue<System.Action> mCommands
  1254. {
  1255. get { return mPrivateCommands; }
  1256. }
  1257. public static void PushCommand(System.Action command)
  1258. {
  1259. mGlobal.Push(command);
  1260. }
  1261. public static void ExecuteCommand()
  1262. {
  1263. mGlobal.Execute();
  1264. }
  1265. public void Push(System.Action command)
  1266. {
  1267. mCommands.Enqueue(command);
  1268. }
  1269. public void Execute()
  1270. {
  1271. while (mCommands.Count > 0)
  1272. {
  1273. mCommands.Dequeue().Invoke();
  1274. }
  1275. }
  1276. }
  1277. // ReSharper disable once InconsistentNaming
  1278. public class EasyIMGUIXMLModule : IConvertModule
  1279. {
  1280. private Dictionary<string, IXMLToObjectConverter> mConverters = new Dictionary<string, IXMLToObjectConverter>();
  1281. public EasyIMGUIXMLModule()
  1282. {
  1283. mConverters.Add("IButton", new IMGUIButton());
  1284. mConverters.Add("ILabel", new Label());
  1285. mConverters.Add("IFlexibleSpace", new FlexibleSpace());
  1286. mConverters.Add("IHorizontal", new HorizontalLayout());
  1287. mConverters.Add("IVertical", new VerticalLayout());
  1288. mConverters.Add("ICustom", new CustomView());
  1289. mConverters.Add("ISpace", new Space());
  1290. mConverters.Add("ITextField", new TextField());
  1291. mConverters.Add("ITextArea", new TextArea());
  1292. }
  1293. public IXMLToObjectConverter GetConverter(string name)
  1294. {
  1295. return mConverters[name];
  1296. }
  1297. public void RegisterConverter(string name, IXMLToObjectConverter converter)
  1298. {
  1299. if (mConverters.ContainsKey(name))
  1300. {
  1301. mConverters[name] = converter;
  1302. }
  1303. else
  1304. {
  1305. mConverters.Add(name, converter);
  1306. }
  1307. }
  1308. }
  1309. public interface IXMLView : IVerticalLayout
  1310. {
  1311. T GetById<T>(string id) where T : class, IMGUIView;
  1312. IXMLView LoadXML(string xmlFilePath);
  1313. IXMLView LoadXMLContent(string xmlContent);
  1314. }
  1315. internal class XMLView : VerticalLayout, IXMLView
  1316. {
  1317. private readonly Dictionary<string, IMGUIView> mIdIndex = new Dictionary<string, IMGUIView>();
  1318. public T GetById<T>(string id) where T : class, IMGUIView
  1319. {
  1320. return mIdIndex[id] as T;
  1321. }
  1322. public IXMLView LoadXML(string xmlFilePath)
  1323. {
  1324. var content = File.ReadAllText(xmlFilePath);
  1325. var xmlDocument = new XmlDocument();
  1326. xmlDocument.LoadXml(content);
  1327. var node = xmlDocument.FirstChild;
  1328. GenView(this, node);
  1329. return this;
  1330. }
  1331. public IXMLView LoadXMLContent(string xmlContent)
  1332. {
  1333. var xmlDocument = new XmlDocument();
  1334. xmlDocument.LoadXml(xmlContent);
  1335. var node = xmlDocument.FirstChild;
  1336. GenView(this, node);
  1337. return this;
  1338. }
  1339. void GenView(IMGUIView parentLayout, XmlNode parentNode)
  1340. {
  1341. foreach (XmlNode childNode in parentNode)
  1342. {
  1343. var converter = Converter.GetConverter(childNode.Name);
  1344. var view = converter.Convert<IMGUIView>(childNode);
  1345. var layout = parentLayout as IMGUILayout;
  1346. if (layout != null)
  1347. {
  1348. layout.AddChild(view);
  1349. }
  1350. else
  1351. {
  1352. break;
  1353. }
  1354. if (!string.IsNullOrEmpty(view.Id))
  1355. {
  1356. mIdIndex.Add(view.Id, view);
  1357. }
  1358. GenView(view, childNode);
  1359. }
  1360. }
  1361. IConvertModule Converter
  1362. {
  1363. get
  1364. {
  1365. return mConverter ?? (mConverter = XMLKit.Get.SystemLayer.Get<IXMLToObjectConvertSystem>()
  1366. .GetConvertModule("EasyIMGUI"));
  1367. }
  1368. }
  1369. private IConvertModule mConverter = null;
  1370. }
  1371. public interface IConvertModule
  1372. {
  1373. IXMLToObjectConverter GetConverter(string name);
  1374. void RegisterConverter(string name, IXMLToObjectConverter converter);
  1375. }
  1376. public interface IXMLToObjectConverter
  1377. {
  1378. T Convert<T>(XmlNode node) where T : class;
  1379. }
  1380. public class CustomXMLToObjectConverter<T> : IXMLToObjectConverter where T : class
  1381. {
  1382. private readonly Func<XmlNode, T> mConverter;
  1383. public CustomXMLToObjectConverter(Func<XmlNode, T> converter)
  1384. {
  1385. mConverter = converter;
  1386. }
  1387. public T1 Convert<T1>(XmlNode node) where T1 : class
  1388. {
  1389. return mConverter(node) as T1;
  1390. }
  1391. }
  1392. public interface IXMLToObjectConvertSystem
  1393. {
  1394. IConvertModule GetConvertModule(string moduleName);
  1395. void AddModule(string key, IConvertModule module);
  1396. bool ContainsModule(string key);
  1397. }
  1398. internal class XMLToObjectConvertSystem :IXMLToObjectConvertSystem
  1399. {
  1400. private Dictionary<string, IConvertModule> mModules =
  1401. new Dictionary<string,IConvertModule>();
  1402. public IConvertModule GetConvertModule(string moduleName)
  1403. {
  1404. return mModules[moduleName];
  1405. }
  1406. public void AddModule(string key, IConvertModule module)
  1407. {
  1408. if (mModules.ContainsKey(key))
  1409. {
  1410. mModules[key] = module;
  1411. }
  1412. else
  1413. {
  1414. mModules.Add(key, module);
  1415. }
  1416. }
  1417. public bool ContainsModule(string key)
  1418. {
  1419. return mModules.ContainsKey(key);
  1420. }
  1421. }
  1422. public class XMLKit
  1423. {
  1424. private static Lazy<XMLKit> mInstance = new Lazy<XMLKit>(() =>
  1425. {
  1426. var xmlKit = new XMLKit();
  1427. xmlKit.Init();
  1428. return xmlKit;
  1429. });
  1430. public static XMLKit Get => mInstance.Value;
  1431. public readonly EditorKitIOCContainer SystemLayer = new EditorKitIOCContainer();
  1432. private void Init()
  1433. {
  1434. SystemLayer.Register<IXMLToObjectConvertSystem>(new XMLToObjectConvertSystem());
  1435. }
  1436. }
  1437. public class EditorKitIOCContainer
  1438. {
  1439. private Dictionary<Type, object> mInstances = new Dictionary<Type, object>();
  1440. public void Register<T>(T instance)
  1441. {
  1442. var key = typeof(T);
  1443. if (mInstances.ContainsKey(key))
  1444. {
  1445. mInstances[key] = instance;
  1446. }
  1447. else
  1448. {
  1449. mInstances.Add(key, instance);
  1450. }
  1451. }
  1452. public T Get<T>() where T : class
  1453. {
  1454. var key = typeof(T);
  1455. if (mInstances.TryGetValue(key, out var retInstance))
  1456. {
  1457. return retInstance as T;
  1458. }
  1459. return null;
  1460. }
  1461. }
  1462. public class EasyInspectorEditor : Editor,IMGUILayoutRoot
  1463. {
  1464. VerticalLayout IMGUILayoutRoot.Layout { get; set; }
  1465. RenderEndCommandExecutor IMGUILayoutRoot.RenderEndCommandExecutor { get; set; }
  1466. protected void Save()
  1467. {
  1468. EditorUtility.SetDirty(target);
  1469. UnityEditor.SceneManagement.EditorSceneManager
  1470. .MarkSceneDirty(SceneManager.GetActiveScene());
  1471. GUIUtility.ExitGUI();
  1472. }
  1473. }
  1474. public class TreeNode : VerticalLayout
  1475. {
  1476. public BindableProperty<bool> Spread = null;
  1477. public string Content;
  1478. private readonly IHorizontalLayout mFirstLine = EasyIMGUI.Horizontal();
  1479. private VerticalLayout mSpreadView = new VerticalLayout();
  1480. public TreeNode(bool spread, string content, int indent = 0, bool autosaveSpreadState = false)
  1481. {
  1482. if (autosaveSpreadState)
  1483. {
  1484. spread = EditorPrefs.GetBool(content, spread);
  1485. }
  1486. Content = content;
  1487. Spread = new BindableProperty<bool>(spread);
  1488. Style = new GUIStyleProperty(() => EditorStyles.foldout);
  1489. mFirstLine.Parent(this);
  1490. mFirstLine.AddChild(EasyIMGUI.Space().Pixel(indent));
  1491. if (autosaveSpreadState)
  1492. {
  1493. Spread.Register(value => EditorPrefs.SetBool(content, value));
  1494. }
  1495. EasyIMGUI.Custom().OnGUI(() => { Spread.Value = EditorGUILayout.Foldout(Spread.Value, Content, true, Style.Value); })
  1496. .Parent(mFirstLine);
  1497. EasyIMGUI.Custom().OnGUI(() =>
  1498. {
  1499. if (Spread.Value)
  1500. {
  1501. mSpreadView.DrawGUI();
  1502. }
  1503. }).Parent(this);
  1504. }
  1505. public TreeNode Add2FirstLine(IMGUIView view)
  1506. {
  1507. view.Parent(mFirstLine);
  1508. return this;
  1509. }
  1510. public TreeNode FirstLineBox()
  1511. {
  1512. mFirstLine.Box();
  1513. return this;
  1514. }
  1515. public TreeNode SpreadBox()
  1516. {
  1517. mSpreadView.VerticalStyle = "box";
  1518. return this;
  1519. }
  1520. public TreeNode Add2Spread(IMGUIView view)
  1521. {
  1522. view.Parent(mSpreadView);
  1523. return this;
  1524. }
  1525. }
  1526. public abstract class EasyEditorWindow : EditorWindow,IMGUILayoutRoot
  1527. {
  1528. public static T Create<T>(bool utility, string title = null) where T : EasyEditorWindow
  1529. {
  1530. return string.IsNullOrEmpty(title) ? GetWindow<T>(utility) : GetWindow<T>(utility, title);
  1531. }
  1532. public bool Openning { get; set; }
  1533. public void Open()
  1534. {
  1535. Openning = true;
  1536. EditorApplication.update += OnUpdate;
  1537. Show();
  1538. }
  1539. public new void Close()
  1540. {
  1541. Openning = false;
  1542. base.Close();
  1543. }
  1544. public void RemoveAllChildren()
  1545. {
  1546. this.GetLayout().Clear();
  1547. }
  1548. public abstract void OnClose();
  1549. public abstract void OnUpdate();
  1550. private void OnDestroy()
  1551. {
  1552. EditorApplication.update -= OnUpdate;
  1553. OnClose();
  1554. }
  1555. protected abstract void Init();
  1556. private bool mInited = false;
  1557. public virtual void OnGUI()
  1558. {
  1559. if (!mInited)
  1560. {
  1561. Init();
  1562. mInited = true;
  1563. }
  1564. this.GetLayout().DrawGUI();
  1565. }
  1566. VerticalLayout IMGUILayoutRoot.Layout { get; set; }
  1567. RenderEndCommandExecutor IMGUILayoutRoot.RenderEndCommandExecutor { get; set; }
  1568. }
  1569. public interface IGenericMenu
  1570. {
  1571. }
  1572. public class GenericMenuView : IGenericMenu
  1573. {
  1574. protected GenericMenuView()
  1575. {
  1576. }
  1577. public static GenericMenuView Create()
  1578. {
  1579. return new GenericMenuView();
  1580. }
  1581. private GenericMenu mMenu = new GenericMenu();
  1582. public GenericMenuView Separator()
  1583. {
  1584. mMenu.AddSeparator(string.Empty);
  1585. return this;
  1586. }
  1587. public GenericMenuView AddMenu(string menuPath, GenericMenu.MenuFunction click)
  1588. {
  1589. mMenu.AddItem(new GUIContent(menuPath), false, click);
  1590. return this;
  1591. }
  1592. public void Show()
  1593. {
  1594. mMenu.ShowAsContext();
  1595. }
  1596. }
  1597. internal interface IPopup : IMGUIView
  1598. {
  1599. IPopup WithIndexAndMenus(int index, params string[] menus);
  1600. IPopup OnIndexChanged(Action<int> indexChanged);
  1601. IPopup ToolbarStyle();
  1602. BindableProperty<int> IndexProperty { get; }
  1603. IPopup Menus(List<string> value);
  1604. }
  1605. internal class PopupView : View, IPopup
  1606. {
  1607. protected PopupView()
  1608. {
  1609. mStyleProperty = new GUIStyleProperty(() => EditorStyles.popup);
  1610. }
  1611. public static IPopup Create()
  1612. {
  1613. return new PopupView();
  1614. }
  1615. private BindableProperty<int> mIndexProperty = new BindableProperty<int>(0);
  1616. public BindableProperty<int> IndexProperty
  1617. {
  1618. get { return mIndexProperty; }
  1619. }
  1620. public IPopup Menus(List<string> menus)
  1621. {
  1622. mMenus = menus.ToArray();
  1623. return this;
  1624. }
  1625. private string[] mMenus = { };
  1626. protected override void OnGUI()
  1627. {
  1628. IndexProperty.Value =
  1629. EditorGUILayout.Popup(IndexProperty.Value, mMenus, mStyleProperty.Value, LayoutStyles);
  1630. }
  1631. public IPopup WithIndexAndMenus(int index, params string[] menus)
  1632. {
  1633. IndexProperty.Value = index;
  1634. mMenus = menus;
  1635. return this;
  1636. }
  1637. public IPopup OnIndexChanged(Action<int> indexChanged)
  1638. {
  1639. IndexProperty.Register(indexChanged);
  1640. return this;
  1641. }
  1642. public IPopup ToolbarStyle()
  1643. {
  1644. mStyleProperty = new GUIStyleProperty(() => EditorStyles.toolbarPopup);
  1645. return this;
  1646. }
  1647. }
  1648. public class AssetTree
  1649. {
  1650. private TreeNode<AssetData> _root;
  1651. public AssetTree()
  1652. {
  1653. _root = new TreeNode<AssetData>(null);
  1654. }
  1655. public TreeNode<AssetData> Root
  1656. {
  1657. get { return _root; }
  1658. }
  1659. public void Clear()
  1660. {
  1661. _root.Clear();
  1662. }
  1663. public void AddAsset(string guid, HashSet<string> incluedPathes)
  1664. {
  1665. if (string.IsNullOrEmpty(guid)) return;
  1666. TreeNode<AssetData> node = _root;
  1667. string assetPath = AssetDatabase.GUIDToAssetPath(guid);
  1668. if (assetPath.StartsWith("Packages")) return;
  1669. int startIndex = 0, length = assetPath.Length;
  1670. var isSelected = incluedPathes.Contains(assetPath);
  1671. var isExpanded = incluedPathes.Any(path => path.Contains(assetPath));
  1672. while (startIndex < length)
  1673. {
  1674. int endIndex = assetPath.IndexOf('/', startIndex);
  1675. int subLength = endIndex == -1 ? length - startIndex : endIndex - startIndex;
  1676. string directory = assetPath.Substring(startIndex, subLength);
  1677. var pathNode = new AssetData(endIndex == -1 ? guid : null, directory,
  1678. assetPath.Substring(0, endIndex == -1 ? length : endIndex), node.Level == 0 || isExpanded,
  1679. isSelected);
  1680. var child = node.FindInChildren(pathNode);
  1681. if (child == null) child = node.AddChild(pathNode);
  1682. node = child;
  1683. startIndex += subLength + 1;
  1684. }
  1685. }
  1686. }
  1687. public class AssetData : ITreeIMGUIData
  1688. {
  1689. public readonly string guid;
  1690. public readonly string path;
  1691. public readonly string fullPath;
  1692. public bool isExpanded { get; set; }
  1693. public bool isSelected { get; set; }
  1694. public AssetData(string guid, string path, string fullPath, bool isExpanded, bool isSelected)
  1695. {
  1696. this.guid = guid;
  1697. this.path = path;
  1698. this.fullPath = fullPath;
  1699. this.isExpanded = isExpanded;
  1700. this.isSelected = isSelected;
  1701. }
  1702. public override string ToString()
  1703. {
  1704. return path;
  1705. }
  1706. public override int GetHashCode()
  1707. {
  1708. return path.GetHashCode() + 10;
  1709. }
  1710. public override bool Equals(object obj)
  1711. {
  1712. AssetData node = obj as AssetData;
  1713. return node != null && node.path == path;
  1714. }
  1715. public bool Equals(AssetData node)
  1716. {
  1717. return node.path == path;
  1718. }
  1719. }
  1720. public class AssetTreeIMGUI : TreeIMGUI<AssetData>
  1721. {
  1722. public AssetTreeIMGUI(TreeNode<AssetData> root) : base(root)
  1723. {
  1724. }
  1725. protected override void OnDrawTreeNode(Rect rect, TreeNode<AssetData> node, bool selected, bool focus)
  1726. {
  1727. GUIContent labelContent = new GUIContent(node.Data.path, AssetDatabase.GetCachedIcon(node.Data.fullPath));
  1728. if (!node.IsLeaf)
  1729. {
  1730. node.Data.isExpanded = EditorGUI.Foldout(new Rect(rect.x - 12, rect.y, 12, rect.height),
  1731. node.Data.isExpanded, GUIContent.none);
  1732. }
  1733. EditorGUI.BeginChangeCheck();
  1734. node.Data.isSelected = EditorGUI.ToggleLeft(rect, labelContent, node.Data.isSelected);
  1735. }
  1736. }
  1737. public class TreeIMGUI<T> where T : ITreeIMGUIData
  1738. {
  1739. private readonly TreeNode<T> _root;
  1740. private Rect _controlRect;
  1741. private float _drawY;
  1742. private float _height;
  1743. private TreeNode<T> _selected;
  1744. private int _controlID;
  1745. public TreeIMGUI(TreeNode<T> root)
  1746. {
  1747. _root = root;
  1748. }
  1749. public void DrawTreeLayout()
  1750. {
  1751. _height = 0;
  1752. _drawY = 0;
  1753. _root.Traverse(OnGetLayoutHeight);
  1754. _controlRect = EditorGUILayout.GetControlRect(false, _height);
  1755. _controlID = GUIUtility.GetControlID(FocusType.Passive, _controlRect);
  1756. _root.Traverse(OnDrawRow);
  1757. }
  1758. protected virtual float GetRowHeight(TreeNode<T> node)
  1759. {
  1760. return EditorGUIUtility.singleLineHeight;
  1761. }
  1762. protected virtual bool OnGetLayoutHeight(TreeNode<T> node)
  1763. {
  1764. if (node.Data == null) return true;
  1765. _height += GetRowHeight(node);
  1766. return node.Data.isExpanded;
  1767. }
  1768. protected virtual bool OnDrawRow(TreeNode<T> node)
  1769. {
  1770. if (node.Data == null) return true;
  1771. float rowIndent = 14 * node.Level;
  1772. float rowHeight = GetRowHeight(node);
  1773. Rect rowRect = new Rect(0, _controlRect.y + _drawY, _controlRect.width, rowHeight);
  1774. Rect indentRect = new Rect(rowIndent, _controlRect.y + _drawY, _controlRect.width - rowIndent, rowHeight);
  1775. // render
  1776. if (_selected == node)
  1777. {
  1778. EditorGUI.DrawRect(rowRect, Color.gray);
  1779. }
  1780. OnDrawTreeNode(indentRect, node, _selected == node, false);
  1781. // test for events
  1782. EventType eventType = Event.current.GetTypeForControl(_controlID);
  1783. if (eventType == EventType.MouseUp && rowRect.Contains(Event.current.mousePosition))
  1784. {
  1785. _selected = node;
  1786. GUI.changed = true;
  1787. Event.current.Use();
  1788. }
  1789. _drawY += rowHeight;
  1790. return node.Data.isExpanded;
  1791. }
  1792. protected virtual void OnDrawTreeNode(Rect rect, TreeNode<T> node, bool selected, bool focus)
  1793. {
  1794. GUIContent labelContent = new GUIContent(node.Data.ToString());
  1795. if (!node.IsLeaf)
  1796. {
  1797. node.Data.isExpanded = EditorGUI.Foldout(new Rect(rect.x - 12, rect.y, 12, rect.height),
  1798. node.Data.isExpanded, GUIContent.none);
  1799. }
  1800. EditorGUI.LabelField(rect, labelContent, selected ? EditorStyles.whiteLabel : EditorStyles.label);
  1801. }
  1802. }
  1803. public interface ITreeIMGUIData
  1804. {
  1805. bool isExpanded { get; set; }
  1806. }
  1807. public class TreeNode<T>
  1808. {
  1809. public delegate bool TraversalDataDelegate(T data);
  1810. public delegate bool TraversalNodeDelegate(TreeNode<T> node);
  1811. private readonly T _data;
  1812. private readonly TreeNode<T> _parent;
  1813. private readonly int _level;
  1814. private readonly List<TreeNode<T>> _children;
  1815. public TreeNode(T data)
  1816. {
  1817. _data = data;
  1818. _children = new List<TreeNode<T>>();
  1819. _level = 0;
  1820. }
  1821. public TreeNode(T data, TreeNode<T> parent) : this(data)
  1822. {
  1823. _parent = parent;
  1824. _level = _parent!=null ? _parent.Level+1 : 0;
  1825. }
  1826. public int Level { get { return _level; } }
  1827. public int Count { get { return _children.Count; }}
  1828. public bool IsRoot { get { return _parent==null; }}
  1829. public bool IsLeaf { get { return _children.Count==0; }}
  1830. public T Data { get { return _data; }}
  1831. public TreeNode<T> Parent { get { return _parent; }}
  1832. public TreeNode<T> this[int key]
  1833. {
  1834. get { return _children[key]; }
  1835. }
  1836. public void Clear()
  1837. {
  1838. _children.Clear();
  1839. }
  1840. public TreeNode<T> AddChild(T value)
  1841. {
  1842. TreeNode<T> node = new TreeNode<T>(value,this);
  1843. _children.Add(node);
  1844. return node;
  1845. }
  1846. public bool HasChild(T data)
  1847. {
  1848. return FindInChildren(data)!=null;
  1849. }
  1850. public TreeNode<T> FindInChildren(T data)
  1851. {
  1852. int i = 0, l = Count;
  1853. for(; i<l; ++i){
  1854. TreeNode<T> child = _children[i];
  1855. if(child.Data.Equals(data)) return child;
  1856. }
  1857. return null;
  1858. }
  1859. public bool RemoveChild(TreeNode<T> node)
  1860. {
  1861. return _children.Remove(node);
  1862. }
  1863. public void Traverse(TraversalDataDelegate handler)
  1864. {
  1865. if(handler(_data)){
  1866. int i = 0, l = Count;
  1867. for(; i<l; ++i) _children[i].Traverse(handler);
  1868. }
  1869. }
  1870. public void Traverse(TraversalNodeDelegate handler)
  1871. {
  1872. if(handler(this)){
  1873. int i = 0, l = Count;
  1874. for(; i<l; ++i) _children[i].Traverse(handler);
  1875. }
  1876. }
  1877. }
  1878. [CustomPropertyDrawer(typeof(ReorderableAttribute))]
  1879. public class ReorderableDrawer : PropertyDrawer {
  1880. private static Dictionary<int, ReorderableList> lists = new Dictionary<int, ReorderableList>();
  1881. public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
  1882. ReorderableList list = GetList(property, attribute as ReorderableAttribute);
  1883. return list != null ? list.GetHeight() : EditorGUIUtility.singleLineHeight;
  1884. }
  1885. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
  1886. ReorderableList list = GetList(property, attribute as ReorderableAttribute);
  1887. if (list != null) {
  1888. list.DoList(EditorGUI.IndentedRect(position), label);
  1889. }
  1890. else {
  1891. GUI.Label(position, "Array must extend from ReorderableArray", EditorStyles.label);
  1892. }
  1893. }
  1894. public static int GetListId(SerializedProperty property) {
  1895. if (property != null) {
  1896. int h1 = property.serializedObject.targetObject.GetHashCode();
  1897. int h2 = property.propertyPath.GetHashCode();
  1898. return (((h1 << 5) + h1) ^ h2);
  1899. }
  1900. return 0;
  1901. }
  1902. public static ReorderableList GetList(SerializedProperty property) {
  1903. return GetList(property, null, GetListId(property));
  1904. }
  1905. public static ReorderableList GetList(SerializedProperty property, ReorderableAttribute attrib) {
  1906. return GetList(property, attrib, GetListId(property));
  1907. }
  1908. public static ReorderableList GetList(SerializedProperty property, int id) {
  1909. return GetList(property, null, id);
  1910. }
  1911. public static ReorderableList GetList(SerializedProperty property, ReorderableAttribute attrib, int id) {
  1912. if (property == null) {
  1913. return null;
  1914. }
  1915. ReorderableList list = null;
  1916. SerializedProperty array = property.FindPropertyRelative("array");
  1917. if (array != null && array.isArray) {
  1918. if (!lists.TryGetValue(id, out list)) {
  1919. if (attrib != null) {
  1920. Texture icon = !string.IsNullOrEmpty(attrib.elementIconPath) ? AssetDatabase.GetCachedIcon(attrib.elementIconPath) : null;
  1921. ReorderableList.ElementDisplayType displayType = attrib.singleLine ? ReorderableList.ElementDisplayType.SingleLine : ReorderableList.ElementDisplayType.Auto;
  1922. list = new ReorderableList(array, attrib.add, attrib.remove, attrib.draggable, displayType, attrib.elementNameProperty, attrib.elementNameOverride, icon);
  1923. }
  1924. else {
  1925. list = new ReorderableList(array, true, true, true);
  1926. }
  1927. lists.Add(id, list);
  1928. }
  1929. else {
  1930. list.List = array;
  1931. }
  1932. }
  1933. return list;
  1934. }
  1935. }
  1936. public class ReorderableList {
  1937. #if UNITY_2019_3_OR_NEWER
  1938. private const float ELEMENT_EDGE_TOP = 1;
  1939. private const float ELEMENT_EDGE_BOT = 2;
  1940. #else
  1941. private const float ELEMENT_EDGE_TOP = 1;
  1942. private const float ELEMENT_EDGE_BOT = 3;
  1943. #endif
  1944. private const float ELEMENT_HEIGHT_OFFSET = ELEMENT_EDGE_TOP + ELEMENT_EDGE_BOT;
  1945. private static int selectionHash = "ReorderableListSelection".GetHashCode();
  1946. private static int dragAndDropHash = "ReorderableListDragAndDrop".GetHashCode();
  1947. private const string EMPTY_LABEL = "List is Empty";
  1948. private const string ARRAY_ERROR = "{0} is not an Array!";
  1949. public enum ElementDisplayType {
  1950. Auto,
  1951. Expandable,
  1952. SingleLine
  1953. }
  1954. public delegate void DrawHeaderDelegate(Rect rect, GUIContent label);
  1955. public delegate void DrawFooterDelegate(Rect rect);
  1956. public delegate void DrawElementDelegate(Rect rect, SerializedProperty element, GUIContent label, bool selected, bool focused);
  1957. public delegate void ActionDelegate(ReorderableList list);
  1958. public delegate bool ActionBoolDelegate(ReorderableList list);
  1959. public delegate void AddDropdownDelegate(Rect buttonRect, ReorderableList list);
  1960. public delegate UnityEngine.Object DragDropReferenceDelegate(UnityEngine.Object[] references, ReorderableList list);
  1961. public delegate void DragDropAppendDelegate(UnityEngine.Object reference, ReorderableList list);
  1962. public delegate float GetElementHeightDelegate(SerializedProperty element);
  1963. public delegate float GetElementsHeightDelegate(ReorderableList list);
  1964. public delegate string GetElementNameDelegate(SerializedProperty element);
  1965. public delegate GUIContent GetElementLabelDelegate(SerializedProperty element);
  1966. public delegate void SurrogateCallback(SerializedProperty element, UnityEngine.Object objectReference, ReorderableList list);
  1967. public event DrawHeaderDelegate drawHeaderCallback;
  1968. public event DrawFooterDelegate drawFooterCallback;
  1969. public event DrawElementDelegate drawElementCallback;
  1970. public event DrawElementDelegate drawElementBackgroundCallback;
  1971. public event GetElementHeightDelegate getElementHeightCallback;
  1972. public event GetElementsHeightDelegate getElementsHeightCallback;
  1973. public event GetElementNameDelegate getElementNameCallback;
  1974. public event GetElementLabelDelegate getElementLabelCallback;
  1975. public event DragDropReferenceDelegate onValidateDragAndDropCallback;
  1976. public event DragDropAppendDelegate onAppendDragDropCallback;
  1977. public event ActionDelegate onReorderCallback;
  1978. public event ActionDelegate onSelectCallback;
  1979. public event ActionDelegate onAddCallback;
  1980. public event AddDropdownDelegate onAddDropdownCallback;
  1981. public event ActionDelegate onRemoveCallback;
  1982. public event ActionDelegate onMouseUpCallback;
  1983. public event ActionBoolDelegate onCanRemoveCallback;
  1984. public event ActionDelegate onChangedCallback;
  1985. public bool canAdd;
  1986. public bool canRemove;
  1987. public bool draggable;
  1988. public bool sortable;
  1989. public bool expandable;
  1990. public bool multipleSelection;
  1991. public GUIContent label;
  1992. public float headerHeight;
  1993. public float footerHeight;
  1994. public float paginationHeight;
  1995. public float slideEasing;
  1996. public float verticalSpacing;
  1997. public bool showDefaultBackground;
  1998. public ElementDisplayType elementDisplayType;
  1999. public string elementNameProperty;
  2000. public string elementNameOverride;
  2001. public bool elementLabels;
  2002. public Texture elementIcon;
  2003. public Surrogate surrogate;
  2004. public bool paginate {
  2005. get { return pagination.enabled; }
  2006. set { pagination.enabled = value; }
  2007. }
  2008. public int pageSize {
  2009. get { return pagination.fixedPageSize; }
  2010. set { pagination.fixedPageSize = value; }
  2011. }
  2012. internal readonly int id;
  2013. private SerializedProperty list;
  2014. private int controlID = -1;
  2015. private Rect[] elementRects;
  2016. private GUIContent elementLabel;
  2017. private GUIContent pageInfoContent;
  2018. private GUIContent pageSizeContent;
  2019. private ListSelection selection;
  2020. private SlideGroup slideGroup;
  2021. private int pressIndex;
  2022. private bool doPagination {
  2023. get { return pagination.enabled && !list.serializedObject.isEditingMultipleObjects; }
  2024. }
  2025. private float elementSpacing {
  2026. get { return Mathf.Max(0, verticalSpacing - 2); }
  2027. }
  2028. private bool dragging;
  2029. private float pressPosition;
  2030. private float dragPosition;
  2031. private int dragDirection;
  2032. private DragList dragList;
  2033. private ListSelection beforeDragSelection;
  2034. private Pagination pagination;
  2035. private int dragDropControlID = -1;
  2036. public ReorderableList(SerializedProperty list)
  2037. : this(list, true, true, true) {
  2038. }
  2039. public ReorderableList(SerializedProperty list, bool canAdd, bool canRemove, bool draggable)
  2040. : this(list, canAdd, canRemove, draggable, ElementDisplayType.Auto, null, null, null) {
  2041. }
  2042. public ReorderableList(SerializedProperty list, bool canAdd, bool canRemove, bool draggable, ElementDisplayType elementDisplayType, string elementNameProperty, Texture elementIcon)
  2043. : this(list, canAdd, canRemove, draggable, elementDisplayType, elementNameProperty, null, elementIcon) {
  2044. }
  2045. public ReorderableList(SerializedProperty list, bool canAdd, bool canRemove, bool draggable, ElementDisplayType elementDisplayType, string elementNameProperty, string elementNameOverride, Texture elementIcon) {
  2046. if (list == null) {
  2047. throw new MissingListExeption();
  2048. }
  2049. else if (!list.isArray) {
  2050. //check if user passed in a ReorderableArray, if so, that becomes the list object
  2051. SerializedProperty array = list.FindPropertyRelative("array");
  2052. if (array == null || !array.isArray) {
  2053. throw new InvalidListException();
  2054. }
  2055. this.list = array;
  2056. }
  2057. else {
  2058. this.list = list;
  2059. }
  2060. this.canAdd = canAdd;
  2061. this.canRemove = canRemove;
  2062. this.draggable = draggable;
  2063. this.elementDisplayType = elementDisplayType;
  2064. this.elementNameProperty = elementNameProperty;
  2065. this.elementNameOverride = elementNameOverride;
  2066. this.elementIcon = elementIcon;
  2067. id = GetHashCode();
  2068. list.isExpanded = true;
  2069. label = new GUIContent(list.displayName);
  2070. pageInfoContent = new GUIContent();
  2071. pageSizeContent = new GUIContent();
  2072. #if UNITY_5_6_OR_NEWER
  2073. verticalSpacing = EditorGUIUtility.standardVerticalSpacing;
  2074. #else
  2075. verticalSpacing = 2f;
  2076. #endif
  2077. slideEasing = 0.15f;
  2078. expandable = true;
  2079. elementLabels = true;
  2080. showDefaultBackground = true;
  2081. multipleSelection = true;
  2082. pagination = new Pagination();
  2083. elementLabel = new GUIContent();
  2084. dragList = new DragList(0);
  2085. selection = new ListSelection();
  2086. slideGroup = new SlideGroup();
  2087. elementRects = new Rect[0];
  2088. //We can't access Style information yet as GUISkin hasn't loaded, so hard code the values
  2089. #if UNITY_2019_3_OR_NEWER
  2090. headerHeight = 20f;
  2091. footerHeight = 20f;
  2092. paginationHeight = 18f;
  2093. #else
  2094. headerHeight = 20f;
  2095. footerHeight = 13f;
  2096. paginationHeight = 20f;
  2097. #endif
  2098. }
  2099. //
  2100. // -- PROPERTIES --
  2101. //
  2102. public SerializedProperty List {
  2103. get { return list; }
  2104. set { list = value; }
  2105. }
  2106. public bool HasList {
  2107. get { return list != null && list.isArray; }
  2108. }
  2109. public int Length {
  2110. get {
  2111. if (!HasList) {
  2112. return 0;
  2113. }
  2114. else if (!list.hasMultipleDifferentValues) {
  2115. return list.arraySize;
  2116. }
  2117. //When multiple objects are selected, because of a Unity bug, list.arraySize is never guranteed to actually be the smallest
  2118. //array size. So we have to find it. Not that great since we're creating SerializedObjects here. There has to be a better way!
  2119. int smallerArraySize = list.arraySize;
  2120. foreach (UnityEngine.Object targetObject in list.serializedObject.targetObjects) {
  2121. SerializedObject serializedObject = new SerializedObject(targetObject);
  2122. SerializedProperty property = serializedObject.FindProperty(list.propertyPath);
  2123. smallerArraySize = Mathf.Min(property.arraySize, smallerArraySize);
  2124. }
  2125. return smallerArraySize;
  2126. }
  2127. }
  2128. public int VisibleLength {
  2129. get { return pagination.GetVisibleLength(Length); }
  2130. }
  2131. public int[] Selected {
  2132. get { return selection.ToArray(); }
  2133. set { selection = new ListSelection(value); }
  2134. }
  2135. public int Index {
  2136. get { return selection.First; }
  2137. set { selection.Select(value); }
  2138. }
  2139. public bool IsDragging {
  2140. get { return dragging; }
  2141. }
  2142. //
  2143. // -- PUBLIC --
  2144. //
  2145. public float GetHeight() {
  2146. if (HasList) {
  2147. float topHeight = doPagination ? headerHeight + paginationHeight : headerHeight;
  2148. return list.isExpanded ? topHeight + GetElementsHeight() + footerHeight : headerHeight;
  2149. }
  2150. else {
  2151. return EditorGUIUtility.singleLineHeight;
  2152. }
  2153. }
  2154. public float GetElementHeight(int index) {
  2155. return index >= 0 && index < Length ? GetElementHeight(list.GetArrayElementAtIndex(index)) : 0;
  2156. }
  2157. public void DoLayoutList() {
  2158. Rect position = EditorGUILayout.GetControlRect(false, GetHeight(), EditorStyles.largeLabel);
  2159. DoList(EditorGUI.IndentedRect(position), label);
  2160. }
  2161. public void DoList(Rect rect, GUIContent label) {
  2162. int indent = EditorGUI.indentLevel;
  2163. EditorGUI.indentLevel = 0;
  2164. Rect headerRect = rect;
  2165. headerRect.height = headerHeight;
  2166. if (!HasList) {
  2167. DrawEmpty(headerRect, string.Format(ARRAY_ERROR, label.text), GUIStyle.none, EditorStyles.helpBox);
  2168. }
  2169. else {
  2170. controlID = GUIUtility.GetControlID(selectionHash, FocusType.Keyboard, rect);
  2171. dragDropControlID = GUIUtility.GetControlID(dragAndDropHash, FocusType.Passive, rect);
  2172. DrawHeader(headerRect, label);
  2173. if (list.isExpanded) {
  2174. if (doPagination) {
  2175. Rect paginateHeaderRect = headerRect;
  2176. paginateHeaderRect.y += headerRect.height;
  2177. paginateHeaderRect.height = paginationHeight;
  2178. DrawPaginationHeader(paginateHeaderRect);
  2179. #if UNITY_2019_3_OR_NEWER
  2180. headerRect.yMax = paginateHeaderRect.yMax;
  2181. #else
  2182. headerRect.yMax = paginateHeaderRect.yMax - 1;
  2183. #endif
  2184. }
  2185. Rect elementBackgroundRect = rect;
  2186. elementBackgroundRect.yMin = headerRect.yMax;
  2187. elementBackgroundRect.yMax = rect.yMax - footerHeight;
  2188. Event evt = Event.current;
  2189. if (selection.Length > 1) {
  2190. if (evt.type == EventType.ContextClick && CanSelect(evt.mousePosition)) {
  2191. HandleMultipleContextClick(evt);
  2192. }
  2193. }
  2194. if (Length > 0) {
  2195. //update element rects if not dragging. Dragging caches draw rects so no need to update
  2196. if (!dragging) {
  2197. UpdateElementRects(elementBackgroundRect, evt);
  2198. }
  2199. if (elementRects.Length > 0) {
  2200. int start, end;
  2201. pagination.GetVisibleRange(elementRects.Length, out start, out end);
  2202. Rect selectableRect = elementBackgroundRect;
  2203. selectableRect.yMin = elementRects[start].yMin;
  2204. selectableRect.yMax = elementRects[end - 1].yMax;
  2205. HandlePreSelection(selectableRect, evt);
  2206. DrawElements(elementBackgroundRect, evt);
  2207. HandlePostSelection(selectableRect, evt);
  2208. }
  2209. }
  2210. else {
  2211. DrawEmpty(elementBackgroundRect, EMPTY_LABEL, Style.boxBackground, Style.verticalLabel);
  2212. }
  2213. Rect footerRect = rect;
  2214. footerRect.yMin = elementBackgroundRect.yMax;
  2215. footerRect.xMin = rect.xMax - 58;
  2216. footerRect.height = footerHeight;
  2217. DrawFooter(footerRect);
  2218. }
  2219. }
  2220. EditorGUI.indentLevel = indent;
  2221. }
  2222. public SerializedProperty AddItem<T>(T item) where T : UnityEngine.Object {
  2223. SerializedProperty property = AddItem();
  2224. if (property != null) {
  2225. property.objectReferenceValue = item;
  2226. }
  2227. return property;
  2228. }
  2229. public SerializedProperty AddItem() {
  2230. if (HasList) {
  2231. list.arraySize++;
  2232. selection.Select(list.arraySize - 1);
  2233. SetPageByIndex(list.arraySize - 1);
  2234. DispatchChange();
  2235. return list.GetArrayElementAtIndex(selection.Last);
  2236. }
  2237. else {
  2238. throw new InvalidListException();
  2239. }
  2240. }
  2241. public void Remove(int[] selection) {
  2242. System.Array.Sort(selection);
  2243. int i = selection.Length;
  2244. while (--i > -1) {
  2245. RemoveItem(selection[i]);
  2246. }
  2247. }
  2248. public void RemoveItem(int index) {
  2249. if (index >= 0 && index < Length) {
  2250. SerializedProperty property = list.GetArrayElementAtIndex(index);
  2251. if (property.propertyType == SerializedPropertyType.ObjectReference && property.objectReferenceValue) {
  2252. property.objectReferenceValue = null;
  2253. }
  2254. list.DeleteArrayElementAtIndex(index);
  2255. selection.Remove(index);
  2256. if (Length > 0) {
  2257. selection.Select(Mathf.Max(0, index - 1));
  2258. }
  2259. DispatchChange();
  2260. }
  2261. }
  2262. public SerializedProperty GetItem(int index) {
  2263. if (index >= 0 && index < Length) {
  2264. return list.GetArrayElementAtIndex(index);
  2265. }
  2266. else {
  2267. return null;
  2268. }
  2269. }
  2270. public int IndexOf(SerializedProperty element) {
  2271. if (element != null) {
  2272. int i = Length;
  2273. while (--i > -1) {
  2274. if (SerializedProperty.EqualContents(element, list.GetArrayElementAtIndex(i))) {
  2275. return i;
  2276. }
  2277. }
  2278. }
  2279. return -1;
  2280. }
  2281. public void GrabKeyboardFocus() {
  2282. GUIUtility.keyboardControl = id;
  2283. }
  2284. public bool HasKeyboardControl() {
  2285. return GUIUtility.keyboardControl == id;
  2286. }
  2287. public void ReleaseKeyboardFocus() {
  2288. if (GUIUtility.keyboardControl == id) {
  2289. GUIUtility.keyboardControl = 0;
  2290. }
  2291. }
  2292. public void SetPage(int page) {
  2293. if (doPagination) {
  2294. pagination.page = page;
  2295. }
  2296. }
  2297. public void SetPageByIndex(int index) {
  2298. if (doPagination) {
  2299. pagination.page = pagination.GetPageForIndex(index);
  2300. }
  2301. }
  2302. public int GetPage(int index) {
  2303. return doPagination ? pagination.page : 0;
  2304. }
  2305. public int GetPageByIndex(int index) {
  2306. return doPagination ? pagination.GetPageForIndex(index) : 0;
  2307. }
  2308. //
  2309. // -- PRIVATE --
  2310. //
  2311. private float GetElementsHeight() {
  2312. if (getElementsHeightCallback != null) {
  2313. return getElementsHeightCallback(this);
  2314. }
  2315. int i, len = Length;
  2316. if (len == 0) {
  2317. return 28;
  2318. }
  2319. float totalHeight = 0;
  2320. float spacing = elementSpacing;
  2321. int start, end;
  2322. pagination.GetVisibleRange(len, out start, out end);
  2323. for (i = start; i < end; i++) {
  2324. totalHeight += GetElementHeight(list.GetArrayElementAtIndex(i)) + spacing;
  2325. }
  2326. return totalHeight + 7 - spacing;
  2327. }
  2328. private float GetElementHeight(SerializedProperty element) {
  2329. if (getElementHeightCallback != null) {
  2330. return getElementHeightCallback(element) + ELEMENT_HEIGHT_OFFSET;
  2331. }
  2332. else {
  2333. return EditorGUI.GetPropertyHeight(element, GetElementLabel(element, elementLabels), IsElementExpandable(element)) + ELEMENT_HEIGHT_OFFSET;
  2334. }
  2335. }
  2336. private Rect GetElementDrawRect(int index, Rect desiredRect) {
  2337. if (slideEasing <= 0) {
  2338. return desiredRect;
  2339. }
  2340. else {
  2341. //lerp the drag easing toward slide easing, this creates a stronger easing at the start then slower at the end
  2342. //when dealing with large lists, we can
  2343. return dragging ? slideGroup.GetRect(dragList[index].startIndex, desiredRect, slideEasing) : slideGroup.SetRect(index, desiredRect);
  2344. }
  2345. }
  2346. /*
  2347. private Rect GetElementHeaderRect(SerializedProperty element, Rect elementRect) {
  2348. Rect rect = elementRect;
  2349. rect.height = EditorGUIUtility.singleLineHeight + verticalSpacing;
  2350. return rect;
  2351. }
  2352. */
  2353. private Rect GetElementRenderRect(SerializedProperty element, Rect elementRect) {
  2354. float offset = draggable ? 20 : 5;
  2355. Rect rect = elementRect;
  2356. rect.xMin += IsElementExpandable(element) ? offset + 10 : offset;
  2357. rect.xMax -= 5;
  2358. rect.yMin += ELEMENT_EDGE_TOP;
  2359. rect.yMax -= ELEMENT_EDGE_BOT;
  2360. return rect;
  2361. }
  2362. private void DrawHeader(Rect rect, GUIContent label) {
  2363. if (showDefaultBackground && Event.current.type == EventType.Repaint) {
  2364. Style.headerBackground.Draw(rect, false, false, false, false);
  2365. }
  2366. HandleDragAndDrop(rect, Event.current);
  2367. bool multiline = elementDisplayType != ElementDisplayType.SingleLine;
  2368. Rect titleRect = rect;
  2369. titleRect.xMin += 6f;
  2370. titleRect.xMax -= multiline ? 95f : 55f;
  2371. label = EditorGUI.BeginProperty(titleRect, label, list);
  2372. if (drawHeaderCallback != null) {
  2373. drawHeaderCallback(titleRect, label);
  2374. }
  2375. else if (expandable) {
  2376. titleRect.xMin += 10;
  2377. EditorGUI.BeginChangeCheck();
  2378. bool isExpanded = EditorGUI.Foldout(titleRect, list.isExpanded, label, true);
  2379. if (EditorGUI.EndChangeCheck()) {
  2380. list.isExpanded = isExpanded;
  2381. }
  2382. }
  2383. else {
  2384. GUI.Label(titleRect, label, EditorStyles.label);
  2385. }
  2386. EditorGUI.EndProperty();
  2387. if (multiline) {
  2388. Rect bRect1 = rect;
  2389. bRect1.xMin = rect.xMax - 25;
  2390. bRect1.xMax = rect.xMax - 5;
  2391. if (GUI.Button(bRect1, Style.expandButton, Style.preButtonStretch)) {
  2392. ExpandElements(true);
  2393. }
  2394. Rect bRect2 = rect;
  2395. bRect2.xMin = bRect1.xMin - 20;
  2396. bRect2.xMax = bRect1.xMin;
  2397. if (GUI.Button(bRect2, Style.collapseButton, Style.preButtonStretch)) {
  2398. ExpandElements(false);
  2399. }
  2400. rect.xMax = bRect2.xMin + 5;
  2401. }
  2402. //draw sorting options
  2403. if (sortable) {
  2404. Rect sortRect1 = rect;
  2405. sortRect1.xMin = rect.xMax - 25;
  2406. sortRect1.xMax = rect.xMax;
  2407. Rect sortRect2 = rect;
  2408. sortRect2.xMin = sortRect1.xMin - 20;
  2409. sortRect2.xMax = sortRect1.xMin;
  2410. if (EditorGUI.DropdownButton(sortRect1, Style.sortAscending, FocusType.Passive, Style.preButtonStretch)) {
  2411. SortElements(sortRect1, false);
  2412. }
  2413. if (EditorGUI.DropdownButton(sortRect2, Style.sortDescending, FocusType.Passive, Style.preButtonStretch)) {
  2414. SortElements(sortRect2, true);
  2415. }
  2416. }
  2417. }
  2418. private void ExpandElements(bool expand) {
  2419. if (!list.isExpanded && expand) {
  2420. list.isExpanded = true;
  2421. }
  2422. int i, len = Length;
  2423. for (i = 0; i < len; i++) {
  2424. list.GetArrayElementAtIndex(i).isExpanded = expand;
  2425. }
  2426. }
  2427. private void SortElements(Rect rect, bool descending) {
  2428. int total = Length;
  2429. //no point in sorting a list with 1 element!
  2430. if (total <= 1) {
  2431. return;
  2432. }
  2433. //the first property tells us what type of items are in the list
  2434. //if generic, then we give the user a list of properties to sort on
  2435. SerializedProperty prop = list.GetArrayElementAtIndex(0);
  2436. if (prop.propertyType == SerializedPropertyType.Generic) {
  2437. GenericMenu menu = new GenericMenu();
  2438. SerializedProperty property = prop.Copy();
  2439. SerializedProperty end = property.GetEndProperty();
  2440. bool enterChildren = true;
  2441. while (property.NextVisible(enterChildren) && !SerializedProperty.EqualContents(property, end)) {
  2442. menu.AddItem(new GUIContent(property.name), false, userData => {
  2443. //sort based on the property selected then apply the changes
  2444. ListSort.SortOnProperty(list, total, descending, (string)userData);
  2445. ApplyReorder();
  2446. HandleUtility.Repaint();
  2447. }, property.name);
  2448. enterChildren = false;
  2449. }
  2450. menu.DropDown(rect);
  2451. }
  2452. else {
  2453. //list is not generic, so we just sort directly on the type then apply the changes
  2454. ListSort.SortOnType(list, total, descending, prop.propertyType);
  2455. ApplyReorder();
  2456. }
  2457. }
  2458. private void DrawEmpty(Rect rect, string label, GUIStyle backgroundStyle, GUIStyle labelStyle) {
  2459. if (showDefaultBackground && Event.current.type == EventType.Repaint) {
  2460. backgroundStyle.Draw(rect, false, false, false, false);
  2461. }
  2462. EditorGUI.LabelField(rect, label, labelStyle);
  2463. }
  2464. private void UpdateElementRects(Rect rect, Event evt) {
  2465. //resize array if elements changed
  2466. int i, len = Length;
  2467. if (len != elementRects.Length) {
  2468. System.Array.Resize(ref elementRects, len);
  2469. }
  2470. if (evt.type == EventType.Repaint) {
  2471. //start rect
  2472. Rect elementRect = rect;
  2473. elementRect.yMin = elementRect.yMax = rect.yMin + 2;
  2474. float spacing = elementSpacing;
  2475. int start, end;
  2476. pagination.GetVisibleRange(len, out start, out end);
  2477. for (i = start; i < end; i++) {
  2478. SerializedProperty element = list.GetArrayElementAtIndex(i);
  2479. //update the elementRects value for this object. Grab the last elementRect for startPosition
  2480. elementRect.y = elementRect.yMax;
  2481. elementRect.height = GetElementHeight(element);
  2482. elementRects[i] = elementRect;
  2483. elementRect.yMax += spacing;
  2484. }
  2485. }
  2486. }
  2487. private void DrawElements(Rect rect, Event evt) {
  2488. //draw list background
  2489. if (showDefaultBackground && evt.type == EventType.Repaint) {
  2490. Style.boxBackground.Draw(rect, false, false, false, false);
  2491. }
  2492. //if not dragging, draw elements as usual
  2493. if (!dragging) {
  2494. int start, end;
  2495. pagination.GetVisibleRange(Length, out start, out end);
  2496. for (int i = start; i < end; i++) {
  2497. bool selected = selection.Contains(i);
  2498. DrawElement(list.GetArrayElementAtIndex(i), GetElementDrawRect(i, elementRects[i]), selected, selected && GUIUtility.keyboardControl == controlID);
  2499. }
  2500. }
  2501. else if (evt.type == EventType.Repaint) {
  2502. //draw dragging elements only when repainting
  2503. int i, s, len = dragList.Length;
  2504. int sLen = selection.Length;
  2505. //first, find the rects of the selected elements, we need to use them for overlap queries
  2506. for (i = 0; i < sLen; i++) {
  2507. DragElement element = dragList[i];
  2508. //update the element desiredRect if selected. Selected elements appear first in the dragList, so other elements later in iteration will have rects to compare
  2509. element.desiredRect.y = dragPosition - element.dragOffset;
  2510. dragList[i] = element;
  2511. }
  2512. //draw elements, start from the bottom of the list as first elements are the ones selected, so should be drawn last
  2513. i = len;
  2514. while (--i > -1) {
  2515. DragElement element = dragList[i];
  2516. //draw dragging elements last as the loop is backwards
  2517. if (element.selected) {
  2518. DrawElement(element.property, element.desiredRect, true, true);
  2519. continue;
  2520. }
  2521. //loop over selection and see what overlaps
  2522. //if dragging down we start from the bottom of the selection
  2523. //otherwise we start from the top. This helps to cover multiple selected objects
  2524. Rect elementRect = element.rect;
  2525. int elementIndex = element.startIndex;
  2526. int start = dragDirection > 0 ? sLen - 1 : 0;
  2527. int end = dragDirection > 0 ? -1 : sLen;
  2528. for (s = start; s != end; s -= dragDirection) {
  2529. DragElement selected = dragList[s];
  2530. if (selected.Overlaps(elementRect, elementIndex, dragDirection)) {
  2531. elementRect.y -= selected.rect.height * dragDirection;
  2532. elementIndex += dragDirection;
  2533. }
  2534. }
  2535. //draw the element with the new rect
  2536. DrawElement(element.property, GetElementDrawRect(i, elementRect), false, false);
  2537. //reassign the element back into the dragList
  2538. element.desiredRect = elementRect;
  2539. dragList[i] = element;
  2540. }
  2541. }
  2542. }
  2543. private void DrawElement(SerializedProperty element, Rect rect, bool selected, bool focused) {
  2544. Rect backgroundRect = rect;
  2545. #if UNITY_2019_3_OR_NEWER
  2546. backgroundRect.xMin++;
  2547. backgroundRect.xMax--;
  2548. #endif
  2549. Event evt = Event.current;
  2550. if (drawElementBackgroundCallback != null) {
  2551. drawElementBackgroundCallback(backgroundRect, element, null, selected, focused);
  2552. }
  2553. else if (evt.type == EventType.Repaint) {
  2554. Style.elementBackground.Draw(backgroundRect, false, selected, selected, focused);
  2555. }
  2556. if (evt.type == EventType.Repaint && draggable) {
  2557. Style.draggingHandle.Draw(new Rect(rect.x + 5, rect.y + 6, 10, rect.height - (rect.height - 6)), false, false, false, false);
  2558. }
  2559. GUIContent label = GetElementLabel(element, elementLabels);
  2560. Rect renderRect = GetElementRenderRect(element, rect);
  2561. if (drawElementCallback != null) {
  2562. drawElementCallback(renderRect, element, label, selected, focused);
  2563. }
  2564. else {
  2565. EditorGUI.PropertyField(renderRect, element, label, true);
  2566. }
  2567. //handle context click
  2568. int controlId = GUIUtility.GetControlID(label, FocusType.Passive, rect);
  2569. switch (evt.GetTypeForControl(controlId)) {
  2570. case EventType.ContextClick:
  2571. if (rect.Contains(evt.mousePosition)) {
  2572. HandleSingleContextClick(evt, element);
  2573. }
  2574. break;
  2575. }
  2576. }
  2577. private GUIContent GetElementLabel(SerializedProperty element, bool allowElementLabel) {
  2578. if (!allowElementLabel) {
  2579. return GUIContent.none;
  2580. }
  2581. else if (getElementLabelCallback != null) {
  2582. return getElementLabelCallback(element);
  2583. }
  2584. string name;
  2585. if (getElementNameCallback != null) {
  2586. name = getElementNameCallback(element);
  2587. }
  2588. else {
  2589. name = GetElementName(element, elementNameProperty, elementNameOverride);
  2590. }
  2591. elementLabel.text = !string.IsNullOrEmpty(name) ? name : element.displayName;
  2592. elementLabel.tooltip = element.tooltip;
  2593. elementLabel.image = elementIcon;
  2594. return elementLabel;
  2595. }
  2596. private static string GetElementName(SerializedProperty element, string nameProperty, string nameOverride) {
  2597. if (!string.IsNullOrEmpty(nameOverride)) {
  2598. string path = element.propertyPath;
  2599. const string arrayEndDelimeter = "]";
  2600. const char arrayStartDelimeter = '[';
  2601. if (path.EndsWith(arrayEndDelimeter)) {
  2602. int startIndex = path.LastIndexOf(arrayStartDelimeter) + 1;
  2603. return string.Format("{0} {1}", nameOverride, path.Substring(startIndex, path.Length - startIndex - 1));
  2604. }
  2605. return nameOverride;
  2606. }
  2607. else if (string.IsNullOrEmpty(nameProperty)) {
  2608. return null;
  2609. }
  2610. else if (element.propertyType == SerializedPropertyType.ObjectReference && nameProperty == "name") {
  2611. return element.objectReferenceValue ? element.objectReferenceValue.name : null;
  2612. }
  2613. SerializedProperty prop = element.FindPropertyRelative(nameProperty);
  2614. if (prop != null) {
  2615. switch (prop.propertyType) {
  2616. case SerializedPropertyType.ObjectReference:
  2617. return prop.objectReferenceValue ? prop.objectReferenceValue.name : null;
  2618. case SerializedPropertyType.Enum:
  2619. return prop.enumDisplayNames[prop.enumValueIndex];
  2620. case SerializedPropertyType.Integer:
  2621. case SerializedPropertyType.Character:
  2622. return prop.intValue.ToString();
  2623. case SerializedPropertyType.LayerMask:
  2624. return GetLayerMaskName(prop.intValue);
  2625. case SerializedPropertyType.String:
  2626. return prop.stringValue;
  2627. case SerializedPropertyType.Float:
  2628. return prop.floatValue.ToString();
  2629. }
  2630. return prop.displayName;
  2631. }
  2632. return null;
  2633. }
  2634. private static string GetLayerMaskName(int mask) {
  2635. if (mask == 0) {
  2636. return "Nothing";
  2637. }
  2638. else if (mask < 0) {
  2639. return "Everything";
  2640. }
  2641. string name = string.Empty;
  2642. int n = 0;
  2643. for (int i = 0; i < 32; i++) {
  2644. if (((1 << i) & mask) != 0) {
  2645. if (n == 4) {
  2646. return "Mixed ...";
  2647. }
  2648. name += (n > 0 ? ", " : string.Empty) + LayerMask.LayerToName(i);
  2649. n++;
  2650. }
  2651. }
  2652. return name;
  2653. }
  2654. private void DrawFooter(Rect rect) {
  2655. if (drawFooterCallback != null) {
  2656. drawFooterCallback(rect);
  2657. return;
  2658. }
  2659. if (Event.current.type == EventType.Repaint) {
  2660. Style.footerBackground.Draw(rect, false, false, false, false);
  2661. }
  2662. Rect addRect = new Rect(rect.xMin + 4f, rect.y, 25f, Style.preButton.fixedHeight);
  2663. Rect subRect = new Rect(rect.xMax - 29f, rect.y, 25f, Style.preButton.fixedHeight);
  2664. EditorGUI.BeginDisabledGroup(!canAdd);
  2665. if (GUI.Button(addRect, onAddDropdownCallback != null ? Style.iconToolbarPlusMore : Style.iconToolbarPlus, Style.preButton)) {
  2666. if (onAddDropdownCallback != null) {
  2667. onAddDropdownCallback(addRect, this);
  2668. }
  2669. else if (onAddCallback != null) {
  2670. onAddCallback(this);
  2671. }
  2672. else {
  2673. AddItem();
  2674. }
  2675. }
  2676. EditorGUI.EndDisabledGroup();
  2677. EditorGUI.BeginDisabledGroup(!CanSelect(selection) || !canRemove || (onCanRemoveCallback != null && !onCanRemoveCallback(this)));
  2678. if (GUI.Button(subRect, Style.iconToolbarMinus, Style.preButton)) {
  2679. if (onRemoveCallback != null) {
  2680. onRemoveCallback(this);
  2681. }
  2682. else {
  2683. Remove(selection.ToArray());
  2684. }
  2685. }
  2686. EditorGUI.EndDisabledGroup();
  2687. }
  2688. private void DrawPaginationHeader(Rect rect) {
  2689. int total = Length;
  2690. int pages = pagination.GetPageCount(total);
  2691. int page = Mathf.Clamp(pagination.page, 0, pages - 1);
  2692. //some actions may have reduced the page count, so we need to check the current page against the clamped one
  2693. //if different, we need to change and repaint
  2694. if (page != pagination.page) {
  2695. pagination.page = page;
  2696. HandleUtility.Repaint();
  2697. }
  2698. Rect prevRect = new Rect(rect.xMin + 4f, rect.y, 17f, rect.height - 1);
  2699. Rect popupRect = new Rect(prevRect.xMax, rect.y, 14f, rect.height - 1);
  2700. Rect nextRect = new Rect(popupRect.xMax, rect.y, 17f, rect.height - 1);
  2701. if (Event.current.type == EventType.Repaint) {
  2702. Style.paginationHeader.Draw(rect, false, true, true, false);
  2703. }
  2704. pageInfoContent.text = string.Format(Style.PAGE_INFO_FORMAT, pagination.page + 1, pages);
  2705. Rect pageInfoRect = rect;
  2706. pageInfoRect.xMin = rect.xMax - Style.paginationText.CalcSize(pageInfoContent).x - 7;
  2707. //draw page info
  2708. GUI.Label(pageInfoRect, pageInfoContent, Style.paginationText);
  2709. //draw page buttons and page popup
  2710. if (GUI.Button(prevRect, Style.iconPagePrev, Style.preButtonStretch)) {
  2711. pagination.page = Mathf.Max(0, pagination.page - 1);
  2712. }
  2713. if (EditorGUI.DropdownButton(popupRect, Style.iconPagePopup, FocusType.Passive, Style.preButtonStretch)) {
  2714. GenericMenu menu = new GenericMenu();
  2715. for (int i = 0; i < pages; i++) {
  2716. int pageIndex = i;
  2717. menu.AddItem(new GUIContent(string.Format("Page {0}", i + 1)), i == pagination.page, OnPageDropDownSelect, pageIndex);
  2718. }
  2719. menu.DropDown(popupRect);
  2720. }
  2721. if (GUI.Button(nextRect, Style.iconPageNext, Style.preButtonStretch)) {
  2722. pagination.page = Mathf.Min(pages - 1, pagination.page + 1);
  2723. }
  2724. //if we're allowed to control the page size manually, show an editor
  2725. bool useFixedPageSize = pagination.fixedPageSize > 0;
  2726. int currentPageSize = useFixedPageSize ? pagination.fixedPageSize : pagination.customPageSize;
  2727. EditorGUI.BeginDisabledGroup(useFixedPageSize);
  2728. pageSizeContent.text = currentPageSize.ToString();
  2729. GUIStyle style = Style.pageSizeTextField;
  2730. Texture icon = Style.listIcon.image;
  2731. float labelWidth = icon.width + 2;
  2732. float width = style.CalcSize(pageSizeContent).x + 50;
  2733. Rect pageSizeRect = rect;
  2734. pageSizeRect.x = rect.center.x - (width - labelWidth) / 2;
  2735. pageSizeRect.width = width;
  2736. EditorGUI.BeginChangeCheck();
  2737. EditorGUIUtility.labelWidth = labelWidth;
  2738. EditorGUIUtility.SetIconSize(new Vector2(icon.width, icon.height));
  2739. int newPageSize = EditorGUI.DelayedIntField(pageSizeRect, Style.listIcon, currentPageSize, style);
  2740. EditorGUIUtility.labelWidth = 0;
  2741. EditorGUIUtility.SetIconSize(Vector2.zero);
  2742. if (EditorGUI.EndChangeCheck()) {
  2743. pagination.customPageSize = Mathf.Clamp(newPageSize, 0, total);
  2744. pagination.page = Mathf.Min(pagination.GetPageCount(total) - 1, pagination.page);
  2745. }
  2746. EditorGUI.EndDisabledGroup();
  2747. }
  2748. private void OnPageDropDownSelect(object userData) {
  2749. pagination.page = (int)userData;
  2750. }
  2751. private void DispatchChange() {
  2752. if (onChangedCallback != null) {
  2753. onChangedCallback(this);
  2754. }
  2755. }
  2756. private void HandleSingleContextClick(Event evt, SerializedProperty element) {
  2757. selection.Select(IndexOf(element));
  2758. GenericMenu menu = new GenericMenu();
  2759. if (element.isInstantiatedPrefab) {
  2760. menu.AddItem(new GUIContent(string.Format("Revert {0} to Prefab", GetElementLabel(element, true).text)), false, selection.RevertValues, list);
  2761. menu.AddSeparator(string.Empty);
  2762. }
  2763. HandleSharedContextClick(evt, menu, "Duplicate Array Element", "Delete Array Element", "Move Array Element");
  2764. }
  2765. private void HandleMultipleContextClick(Event evt) {
  2766. GenericMenu menu = new GenericMenu();
  2767. if (selection.CanRevert(list)) {
  2768. menu.AddItem(new GUIContent("Revert Values to Prefab"), false, selection.RevertValues, list);
  2769. menu.AddSeparator(string.Empty);
  2770. }
  2771. HandleSharedContextClick(evt, menu, "Duplicate Array Elements", "Delete Array Elements", "Move Array Elements");
  2772. }
  2773. private void HandleSharedContextClick(Event evt, GenericMenu menu, string duplicateLabel, string deleteLabel, string moveLabel) {
  2774. menu.AddItem(new GUIContent(duplicateLabel), false, HandleDuplicate, list);
  2775. menu.AddItem(new GUIContent(deleteLabel), false, HandleDelete, list);
  2776. if (doPagination) {
  2777. int pages = pagination.GetPageCount(Length);
  2778. if (pages > 1) {
  2779. for (int i = 0; i < pages; i++) {
  2780. string label = string.Format("{0}/Page {1}", moveLabel, i + 1);
  2781. menu.AddItem(new GUIContent(label), i == pagination.page, HandleMoveElement, i);
  2782. }
  2783. }
  2784. }
  2785. menu.ShowAsContext();
  2786. evt.Use();
  2787. }
  2788. private void HandleMoveElement(object userData) {
  2789. int toPage = (int)userData;
  2790. int fromPage = pagination.page;
  2791. int size = pagination.pageSize;
  2792. int offset = (toPage * size) - (fromPage * size);
  2793. int direction = offset > 0 ? 1 : -1;
  2794. int total = Length;
  2795. //We need to find the actually positions things will move to and not clamp the index
  2796. //because sometimes something wants to move to a negative index, or beyond the length
  2797. //we need to find this overlow and adjust the move offsets based on that
  2798. int overflow = 0;
  2799. for (int i = 0; i < selection.Length; i++) {
  2800. int desiredIndex = selection[i] + offset;
  2801. overflow = direction < 0 ? Mathf.Min(overflow, desiredIndex) : Mathf.Max(overflow, desiredIndex - total);
  2802. }
  2803. offset -= overflow;
  2804. //copy the current list to prepare for moving
  2805. UpdateDragList(0, 0, total);
  2806. //create a list that will act as our new order
  2807. List<DragElement> orderedList = new List<DragElement>(dragList.Elements.Where(t => !selection.Contains(t.startIndex)));
  2808. //go through the selection and insert them into the new order based on the page offset
  2809. selection.Sort();
  2810. for (int i = 0; i < selection.Length; i++) {
  2811. int selIndex = selection[i];
  2812. int oldIndex = dragList.GetIndexFromSelection(selIndex);
  2813. int newIndex = Mathf.Clamp(selIndex + offset, 0, orderedList.Count);
  2814. orderedList.Insert(newIndex, dragList[oldIndex]);
  2815. }
  2816. //finally, perform the re-order
  2817. dragList.Elements = orderedList.ToArray();
  2818. ReorderDraggedElements(direction, 0, null);
  2819. //assume we still want to view these items
  2820. pagination.page = toPage;
  2821. HandleUtility.Repaint();
  2822. }
  2823. private void HandleDelete(object userData) {
  2824. selection.Delete(userData as SerializedProperty);
  2825. DispatchChange();
  2826. }
  2827. private void HandleDuplicate(object userData) {
  2828. selection.Duplicate(userData as SerializedProperty);
  2829. DispatchChange();
  2830. }
  2831. private void HandleDragAndDrop(Rect rect, Event evt) {
  2832. switch (evt.GetTypeForControl(dragDropControlID)) {
  2833. case EventType.DragUpdated:
  2834. case EventType.DragPerform:
  2835. if (GUI.enabled && rect.Contains(evt.mousePosition)) {
  2836. UnityEngine.Object[] objectReferences = DragAndDrop.objectReferences;
  2837. UnityEngine.Object[] references = new UnityEngine.Object[1];
  2838. bool acceptDrag = false;
  2839. foreach (UnityEngine.Object object1 in objectReferences) {
  2840. references[0] = object1;
  2841. UnityEngine.Object object2 = ValidateObjectDragAndDrop(references);
  2842. if (object2 != null) {
  2843. DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
  2844. if (evt.type == EventType.DragPerform) {
  2845. AppendDragAndDropValue(object2);
  2846. acceptDrag = true;
  2847. DragAndDrop.activeControlID = 0;
  2848. }
  2849. else {
  2850. DragAndDrop.activeControlID = dragDropControlID;
  2851. }
  2852. }
  2853. }
  2854. if (acceptDrag) {
  2855. GUI.changed = true;
  2856. DragAndDrop.AcceptDrag();
  2857. }
  2858. }
  2859. break;
  2860. case EventType.DragExited:
  2861. if (GUI.enabled) {
  2862. HandleUtility.Repaint();
  2863. }
  2864. break;
  2865. }
  2866. }
  2867. private UnityEngine.Object ValidateObjectDragAndDrop(UnityEngine.Object[] references) {
  2868. if (onValidateDragAndDropCallback != null) {
  2869. return onValidateDragAndDropCallback(references, this);
  2870. }
  2871. else if (surrogate.HasType) {
  2872. //if we have a surrogate type, then validate using the surrogate type rather than the list
  2873. return Internals.ValidateObjectDragAndDrop(references, null, surrogate.type, surrogate.exactType);
  2874. }
  2875. return Internals.ValidateObjectDragAndDrop(references, list, null, false);
  2876. }
  2877. private void AppendDragAndDropValue(UnityEngine.Object obj) {
  2878. if (onAppendDragDropCallback != null) {
  2879. onAppendDragDropCallback(obj, this);
  2880. }
  2881. else {
  2882. //check if we have a surrogate type. If so use that for appending
  2883. if (surrogate.HasType) {
  2884. surrogate.Invoke(AddItem(), obj, this);
  2885. }
  2886. else {
  2887. Internals.AppendDragAndDropValue(obj, list);
  2888. }
  2889. }
  2890. DispatchChange();
  2891. }
  2892. private void HandlePreSelection(Rect rect, Event evt) {
  2893. if (evt.type == EventType.MouseDrag && draggable && GUIUtility.hotControl == controlID) {
  2894. if (selection.Length > 0 && UpdateDragPosition(evt.mousePosition, rect, dragList)) {
  2895. GUIUtility.keyboardControl = controlID;
  2896. dragging = true;
  2897. }
  2898. evt.Use();
  2899. }
  2900. }
  2901. private void HandlePostSelection(Rect rect, Event evt) {
  2902. switch (evt.GetTypeForControl(controlID)) {
  2903. case EventType.MouseDown:
  2904. if (rect.Contains(evt.mousePosition) && IsSelectionButton(evt)) {
  2905. int index = GetSelectionIndex(evt.mousePosition);
  2906. if (CanSelect(index)) {
  2907. DoSelection(index, GUIUtility.keyboardControl == 0 || GUIUtility.keyboardControl == controlID || evt.button == 2, evt);
  2908. }
  2909. else {
  2910. selection.Clear();
  2911. }
  2912. HandleUtility.Repaint();
  2913. }
  2914. break;
  2915. case EventType.MouseUp:
  2916. if (!draggable) {
  2917. //select the single object if no selection modifier is being performed
  2918. selection.SelectWhenNoAction(pressIndex, evt);
  2919. if (onMouseUpCallback != null && IsPositionWithinElement(evt.mousePosition, selection.Last)) {
  2920. onMouseUpCallback(this);
  2921. }
  2922. }
  2923. else if (GUIUtility.hotControl == controlID) {
  2924. evt.Use();
  2925. if (dragging) {
  2926. dragging = false;
  2927. //move elements in list
  2928. ReorderDraggedElements(dragDirection, dragList.StartIndex, () => dragList.SortByPosition());
  2929. }
  2930. else {
  2931. //if we didn't drag, then select the original pressed object
  2932. selection.SelectWhenNoAction(pressIndex, evt);
  2933. if (onMouseUpCallback != null) {
  2934. onMouseUpCallback(this);
  2935. }
  2936. }
  2937. GUIUtility.hotControl = 0;
  2938. }
  2939. HandleUtility.Repaint();
  2940. break;
  2941. case EventType.KeyDown:
  2942. if (GUIUtility.keyboardControl == controlID) {
  2943. if (evt.keyCode == KeyCode.DownArrow && !dragging) {
  2944. selection.Select(Mathf.Min(selection.Last + 1, Length - 1));
  2945. evt.Use();
  2946. }
  2947. else if (evt.keyCode == KeyCode.UpArrow && !dragging) {
  2948. selection.Select(Mathf.Max(selection.Last - 1, 0));
  2949. evt.Use();
  2950. }
  2951. else if (evt.keyCode == KeyCode.Escape && GUIUtility.hotControl == controlID) {
  2952. GUIUtility.hotControl = 0;
  2953. if (dragging) {
  2954. dragging = false;
  2955. selection = beforeDragSelection;
  2956. }
  2957. evt.Use();
  2958. }
  2959. }
  2960. break;
  2961. }
  2962. }
  2963. private bool IsSelectionButton(Event evt) {
  2964. return evt.button == 0 || evt.button == 2;
  2965. }
  2966. private void DoSelection(int index, bool setKeyboardControl, Event evt) {
  2967. //append selections based on action, this may be a additive (ctrl) or range (shift) selection
  2968. if (multipleSelection) {
  2969. selection.AppendWithAction(pressIndex = index, evt);
  2970. }
  2971. else {
  2972. selection.Select(pressIndex = index);
  2973. }
  2974. if (onSelectCallback != null) {
  2975. onSelectCallback(this);
  2976. }
  2977. if (draggable) {
  2978. dragging = false;
  2979. dragPosition = pressPosition = evt.mousePosition.y;
  2980. int start, end;
  2981. pagination.GetVisibleRange(Length, out start, out end);
  2982. UpdateDragList(dragPosition, start, end);
  2983. selection.Trim(start, end);
  2984. beforeDragSelection = selection.Clone();
  2985. GUIUtility.hotControl = controlID;
  2986. }
  2987. if (setKeyboardControl) {
  2988. GUIUtility.keyboardControl = controlID;
  2989. }
  2990. evt.Use();
  2991. }
  2992. private void UpdateDragList(float dragPosition, int start, int end) {
  2993. dragList.Resize(start, end - start);
  2994. for (int i = start; i < end; i++) {
  2995. SerializedProperty property = list.GetArrayElementAtIndex(i);
  2996. Rect elementRect = elementRects[i];
  2997. DragElement dragElement = new DragElement() {
  2998. property = property,
  2999. dragOffset = dragPosition - elementRect.y,
  3000. rect = elementRect,
  3001. desiredRect = elementRect,
  3002. selected = selection.Contains(i),
  3003. startIndex = i
  3004. };
  3005. dragList[i - start] = dragElement;
  3006. }
  3007. //finally, sort the dragList by selection, selected objects appear first in the list
  3008. //selection order is preserved as well
  3009. dragList.SortByIndex();
  3010. }
  3011. private bool UpdateDragPosition(Vector2 position, Rect bounds, DragList dragList) {
  3012. //find new drag position
  3013. int startIndex = 0;
  3014. int endIndex = selection.Length - 1;
  3015. float minOffset = dragList[startIndex].dragOffset;
  3016. float maxOffset = dragList[endIndex].rect.height - dragList[endIndex].dragOffset;
  3017. dragPosition = Mathf.Clamp(position.y, bounds.yMin + minOffset, bounds.yMax - maxOffset);
  3018. if (Mathf.Abs(dragPosition - pressPosition) > 1) {
  3019. dragDirection = (int)Mathf.Sign(dragPosition - pressPosition);
  3020. return true;
  3021. }
  3022. return false;
  3023. }
  3024. private void ReorderDraggedElements(int direction, int offset, System.Action sortList) {
  3025. //save the current expanded states on all elements. I don't see any other way to do this
  3026. //MoveArrayElement does not move the foldout states, so... fun.
  3027. dragList.RecordState();
  3028. if (sortList != null) {
  3029. sortList();
  3030. }
  3031. selection.Sort((a, b) => {
  3032. int d1 = dragList.GetIndexFromSelection(a);
  3033. int d2 = dragList.GetIndexFromSelection(b);
  3034. return direction > 0 ? d1.CompareTo(d2) : d2.CompareTo(d1);
  3035. });
  3036. //swap the selected elements in the List
  3037. int s = selection.Length;
  3038. while (--s > -1) {
  3039. int newIndex = dragList.GetIndexFromSelection(selection[s]);
  3040. int listIndex = newIndex + offset;
  3041. selection[s] = listIndex;
  3042. list.MoveArrayElement(dragList[newIndex].startIndex, listIndex);
  3043. }
  3044. //restore expanded states on items
  3045. dragList.RestoreState(list);
  3046. //apply and update
  3047. ApplyReorder();
  3048. }
  3049. private void ApplyReorder() {
  3050. list.serializedObject.ApplyModifiedProperties();
  3051. list.serializedObject.Update();
  3052. if (onReorderCallback != null) {
  3053. onReorderCallback(this);
  3054. }
  3055. DispatchChange();
  3056. }
  3057. private int GetSelectionIndex(Vector2 position) {
  3058. int start, end;
  3059. pagination.GetVisibleRange(elementRects.Length, out start, out end);
  3060. for (int i = start; i < end; i++) {
  3061. Rect rect = elementRects[i];
  3062. if (rect.Contains(position) || (i == 0 && position.y <= rect.yMin) || (i == end - 1 && position.y >= rect.yMax)) {
  3063. return i;
  3064. }
  3065. }
  3066. return -1;
  3067. }
  3068. private bool CanSelect(ListSelection selection) {
  3069. return selection.Length > 0 ? selection.All(s => CanSelect(s)) : false;
  3070. }
  3071. private bool CanSelect(int index) {
  3072. return index >= 0 && index < Length;
  3073. }
  3074. private bool CanSelect(Vector2 position) {
  3075. return selection.Length > 0 ? selection.Any(s => IsPositionWithinElement(position, s)) : false;
  3076. }
  3077. private bool IsPositionWithinElement(Vector2 position, int index) {
  3078. return CanSelect(index) ? elementRects[index].Contains(position) : false;
  3079. }
  3080. private bool IsElementExpandable(SerializedProperty element) {
  3081. switch (elementDisplayType) {
  3082. case ElementDisplayType.Auto:
  3083. return element.hasVisibleChildren && IsTypeExpandable(element.propertyType);
  3084. case ElementDisplayType.Expandable: return true;
  3085. case ElementDisplayType.SingleLine: return false;
  3086. }
  3087. return false;
  3088. }
  3089. private bool IsTypeExpandable(SerializedPropertyType type) {
  3090. switch (type) {
  3091. case SerializedPropertyType.Generic:
  3092. case SerializedPropertyType.Vector4:
  3093. case SerializedPropertyType.Quaternion:
  3094. case SerializedPropertyType.ArraySize:
  3095. #if UNITY_2019_3_OR_NEWER
  3096. case SerializedPropertyType.ManagedReference:
  3097. #endif
  3098. return true;
  3099. default:
  3100. return false;
  3101. }
  3102. }
  3103. //
  3104. // -- LIST STYLE --
  3105. //
  3106. static class Style {
  3107. internal const string PAGE_INFO_FORMAT = "{0} / {1}";
  3108. internal static GUIContent iconToolbarPlus;
  3109. internal static GUIContent iconToolbarPlusMore;
  3110. internal static GUIContent iconToolbarMinus;
  3111. internal static GUIContent iconPagePrev;
  3112. internal static GUIContent iconPageNext;
  3113. internal static GUIContent iconPagePopup;
  3114. internal static GUIStyle paginationText;
  3115. internal static GUIStyle pageSizeTextField;
  3116. internal static GUIStyle draggingHandle;
  3117. internal static GUIStyle headerBackground;
  3118. internal static GUIStyle footerBackground;
  3119. internal static GUIStyle paginationHeader;
  3120. internal static GUIStyle boxBackground;
  3121. internal static GUIStyle preButton;
  3122. internal static GUIStyle preButtonStretch;
  3123. internal static GUIStyle elementBackground;
  3124. internal static GUIStyle verticalLabel;
  3125. internal static GUIContent expandButton;
  3126. internal static GUIContent collapseButton;
  3127. internal static GUIContent sortAscending;
  3128. internal static GUIContent sortDescending;
  3129. internal static GUIContent listIcon;
  3130. static Style() {
  3131. iconToolbarPlus = EditorGUIUtility.IconContent("Toolbar Plus", "Add to list");
  3132. iconToolbarPlusMore = EditorGUIUtility.IconContent("Toolbar Plus More", "Choose to add to list");
  3133. iconToolbarMinus = EditorGUIUtility.IconContent("Toolbar Minus", "Remove selection from list");
  3134. iconPagePrev = EditorGUIUtility.IconContent("Animation.PrevKey", "Previous page");
  3135. iconPageNext = EditorGUIUtility.IconContent("Animation.NextKey", "Next page");
  3136. #if UNITY_2018_3_OR_NEWER
  3137. iconPagePopup = EditorGUIUtility.IconContent("PopupCurveEditorDropDown", "Select page");
  3138. #else
  3139. iconPagePopup = EditorGUIUtility.IconContent("MiniPopupNoBg", "Select page");
  3140. #endif
  3141. paginationText = new GUIStyle();
  3142. paginationText.margin = new RectOffset(2, 2, 0, 0);
  3143. paginationText.fontSize = EditorStyles.miniTextField.fontSize;
  3144. paginationText.font = EditorStyles.miniFont;
  3145. paginationText.normal.textColor = EditorStyles.miniTextField.normal.textColor;
  3146. paginationText.alignment = TextAnchor.MiddleLeft;
  3147. paginationText.clipping = TextClipping.Clip;
  3148. #if UNITY_2019_3_OR_NEWER
  3149. pageSizeTextField = new GUIStyle("RL Background");
  3150. #else
  3151. pageSizeTextField = new GUIStyle("RL Footer");
  3152. pageSizeTextField.overflow = new RectOffset(0, 0, -2, -3);
  3153. pageSizeTextField.contentOffset = new Vector2(0, -1);
  3154. #endif
  3155. pageSizeTextField.alignment = TextAnchor.MiddleLeft;
  3156. pageSizeTextField.clipping = TextClipping.Clip;
  3157. pageSizeTextField.fixedHeight = 0;
  3158. pageSizeTextField.padding = new RectOffset(3, 0, 0, 0);
  3159. pageSizeTextField.font = EditorStyles.miniFont;
  3160. pageSizeTextField.fontSize = EditorStyles.miniTextField.fontSize;
  3161. pageSizeTextField.fontStyle = FontStyle.Normal;
  3162. pageSizeTextField.wordWrap = false;
  3163. draggingHandle = new GUIStyle("RL DragHandle");
  3164. headerBackground = new GUIStyle("RL Header");
  3165. footerBackground = new GUIStyle("RL Footer");
  3166. #if UNITY_2019_3_OR_NEWER
  3167. paginationHeader = new GUIStyle("TimeRulerBackground");
  3168. paginationHeader.fixedHeight = 0;
  3169. #else
  3170. paginationHeader = new GUIStyle("RL Element");
  3171. paginationHeader.border = new RectOffset(2, 3, 2, 3);
  3172. #endif
  3173. elementBackground = new GUIStyle("RL Element");
  3174. elementBackground.border = new RectOffset(2, 3, 2, 3);
  3175. verticalLabel = new GUIStyle(EditorStyles.label);
  3176. verticalLabel.alignment = TextAnchor.UpperLeft;
  3177. verticalLabel.contentOffset = new Vector2(10, 3);
  3178. boxBackground = new GUIStyle("RL Background");
  3179. boxBackground.border = new RectOffset(6, 3, 3, 6);
  3180. #if UNITY_2019_3_OR_NEWER
  3181. preButton = new GUIStyle("RL FooterButton");
  3182. #else
  3183. preButton = new GUIStyle("RL FooterButton");
  3184. preButton.contentOffset = new Vector2(0, -4);
  3185. #endif
  3186. preButtonStretch = new GUIStyle("RL FooterButton");
  3187. preButtonStretch.fixedHeight = 0;
  3188. preButtonStretch.stretchHeight = true;
  3189. expandButton = EditorGUIUtility.IconContent("winbtn_win_max");
  3190. expandButton.tooltip = "Expand All Elements";
  3191. collapseButton = EditorGUIUtility.IconContent("winbtn_win_min");
  3192. collapseButton.tooltip = "Collapse All Elements";
  3193. sortAscending = EditorGUIUtility.IconContent("align_vertically_bottom");
  3194. sortAscending.tooltip = "Sort Ascending";
  3195. sortDescending = EditorGUIUtility.IconContent("align_vertically_top");
  3196. sortDescending.tooltip = "Sort Descending";
  3197. listIcon = EditorGUIUtility.IconContent("align_horizontally_right");
  3198. }
  3199. }
  3200. //
  3201. // -- DRAG LIST --
  3202. //
  3203. struct DragList {
  3204. private int startIndex;
  3205. private DragElement[] elements;
  3206. private int length;
  3207. internal DragList(int length) {
  3208. this.length = length;
  3209. startIndex = 0;
  3210. elements = new DragElement[length];
  3211. }
  3212. internal int StartIndex {
  3213. get { return startIndex; }
  3214. }
  3215. internal int Length {
  3216. get { return length; }
  3217. }
  3218. internal DragElement[] Elements {
  3219. get { return elements; }
  3220. set { elements = value; }
  3221. }
  3222. internal DragElement this[int index] {
  3223. get { return elements[index]; }
  3224. set { elements[index] = value; }
  3225. }
  3226. internal void Resize(int start, int length) {
  3227. startIndex = start;
  3228. this.length = length;
  3229. if (elements.Length != length) {
  3230. System.Array.Resize(ref elements, length);
  3231. }
  3232. }
  3233. internal void SortByIndex() {
  3234. System.Array.Sort(elements, (a, b) => {
  3235. if (b.selected) {
  3236. return a.selected ? a.startIndex.CompareTo(b.startIndex) : 1;
  3237. }
  3238. else if (a.selected) {
  3239. return b.selected ? b.startIndex.CompareTo(a.startIndex) : -1;
  3240. }
  3241. return a.startIndex.CompareTo(b.startIndex);
  3242. });
  3243. }
  3244. internal void RecordState() {
  3245. for (int i = 0; i < length; i++) {
  3246. elements[i].RecordState();
  3247. }
  3248. }
  3249. internal void RestoreState(SerializedProperty list) {
  3250. for (int i = 0; i < length; i++) {
  3251. elements[i].RestoreState(list.GetArrayElementAtIndex(i + startIndex));
  3252. }
  3253. }
  3254. internal void SortByPosition() {
  3255. System.Array.Sort(elements, (a, b) => a.desiredRect.center.y.CompareTo(b.desiredRect.center.y));
  3256. }
  3257. internal int GetIndexFromSelection(int index) {
  3258. return System.Array.FindIndex(elements, t => t.startIndex == index);
  3259. }
  3260. }
  3261. //
  3262. // -- DRAG ELEMENT --
  3263. //
  3264. struct DragElement {
  3265. internal SerializedProperty property;
  3266. internal int startIndex;
  3267. internal float dragOffset;
  3268. internal bool selected;
  3269. internal Rect rect;
  3270. internal Rect desiredRect;
  3271. private bool isExpanded;
  3272. private Dictionary<int, bool> states;
  3273. internal bool Overlaps(Rect value, int index, int direction) {
  3274. if (direction < 0 && index < startIndex) {
  3275. return desiredRect.yMin < value.center.y;
  3276. }
  3277. else if (direction > 0 && index > startIndex) {
  3278. return desiredRect.yMax > value.center.y;
  3279. }
  3280. return false;
  3281. }
  3282. internal void RecordState() {
  3283. states = new Dictionary<int, bool>();
  3284. isExpanded = property.isExpanded;
  3285. Iterate(this, property, (DragElement e, SerializedProperty p, int index) => {
  3286. e.states[index] = p.isExpanded;
  3287. });
  3288. }
  3289. internal void RestoreState(SerializedProperty property) {
  3290. property.isExpanded = isExpanded;
  3291. Iterate(this, property, (DragElement e, SerializedProperty p, int index) => {
  3292. p.isExpanded = e.states[index];
  3293. });
  3294. }
  3295. private static void Iterate(DragElement element, SerializedProperty property, System.Action<DragElement, SerializedProperty, int> action) {
  3296. SerializedProperty copy = property.Copy();
  3297. SerializedProperty end = copy.GetEndProperty();
  3298. int index = 0;
  3299. while (copy.NextVisible(true) && !SerializedProperty.EqualContents(copy, end)) {
  3300. if (copy.hasVisibleChildren) {
  3301. action(element, copy, index);
  3302. index++;
  3303. }
  3304. }
  3305. }
  3306. }
  3307. //
  3308. // -- SLIDE GROUP --
  3309. //
  3310. class SlideGroup {
  3311. private Dictionary<int, Rect> animIDs;
  3312. public SlideGroup() {
  3313. animIDs = new Dictionary<int, Rect>();
  3314. }
  3315. public Rect GetRect(int id, Rect r, float easing) {
  3316. if (Event.current.type != EventType.Repaint) {
  3317. return r;
  3318. }
  3319. if (!animIDs.ContainsKey(id)) {
  3320. animIDs.Add(id, r);
  3321. return r;
  3322. }
  3323. else {
  3324. Rect rect = animIDs[id];
  3325. if (rect.y != r.y) {
  3326. float delta = r.y - rect.y;
  3327. float absDelta = Mathf.Abs(delta);
  3328. //if the distance between current rect and target is too large, then move the element towards the target rect so it reaches the destination faster
  3329. if (absDelta > (rect.height * 2)) {
  3330. r.y = delta > 0 ? r.y - rect.height : r.y + rect.height;
  3331. }
  3332. else if (absDelta > 0.5) {
  3333. r.y = Mathf.Lerp(rect.y, r.y, easing);
  3334. }
  3335. animIDs[id] = r;
  3336. HandleUtility.Repaint();
  3337. }
  3338. return r;
  3339. }
  3340. }
  3341. public Rect SetRect(int id, Rect rect) {
  3342. if (animIDs.ContainsKey(id)) {
  3343. animIDs[id] = rect;
  3344. }
  3345. else {
  3346. animIDs.Add(id, rect);
  3347. }
  3348. return rect;
  3349. }
  3350. }
  3351. //
  3352. // -- PAGINATION --
  3353. //
  3354. struct Pagination {
  3355. internal bool enabled;
  3356. internal int fixedPageSize;
  3357. internal int customPageSize;
  3358. internal int page;
  3359. internal bool usePagination {
  3360. get { return enabled && pageSize > 0; }
  3361. }
  3362. internal int pageSize {
  3363. get { return fixedPageSize > 0 ? fixedPageSize : customPageSize; }
  3364. }
  3365. internal int GetVisibleLength(int total) {
  3366. int start, end;
  3367. if (GetVisibleRange(total, out start, out end)) {
  3368. return end - start;
  3369. }
  3370. return total;
  3371. }
  3372. internal int GetPageForIndex(int index) {
  3373. return usePagination ? Mathf.FloorToInt(index / (float)pageSize) : 0;
  3374. }
  3375. internal int GetPageCount(int total) {
  3376. return usePagination ? Mathf.CeilToInt(total / (float)pageSize) : 1;
  3377. }
  3378. internal bool GetVisibleRange(int total, out int start, out int end) {
  3379. if (usePagination) {
  3380. int size = pageSize;
  3381. start = Mathf.Clamp(page * size, 0, total - 1);
  3382. end = Mathf.Min(start + size, total);
  3383. return true;
  3384. }
  3385. start = 0;
  3386. end = total;
  3387. return false;
  3388. }
  3389. }
  3390. //
  3391. // -- SELECTION --
  3392. //
  3393. class ListSelection : IEnumerable<int> {
  3394. private List<int> indexes;
  3395. internal int? firstSelected;
  3396. public ListSelection() {
  3397. indexes = new List<int>();
  3398. }
  3399. public ListSelection(int[] indexes) {
  3400. this.indexes = new List<int>(indexes);
  3401. }
  3402. public int First {
  3403. get { return indexes.Count > 0 ? indexes[0] : -1; }
  3404. }
  3405. public int Last {
  3406. get { return indexes.Count > 0 ? indexes[indexes.Count - 1] : -1; }
  3407. }
  3408. public int Length {
  3409. get { return indexes.Count; }
  3410. }
  3411. public int this[int index] {
  3412. get { return indexes[index]; }
  3413. set {
  3414. int oldIndex = indexes[index];
  3415. indexes[index] = value;
  3416. if (oldIndex == firstSelected) {
  3417. firstSelected = value;
  3418. }
  3419. }
  3420. }
  3421. public bool Contains(int index) {
  3422. return indexes.Contains(index);
  3423. }
  3424. public void Clear() {
  3425. indexes.Clear();
  3426. firstSelected = null;
  3427. }
  3428. public void SelectWhenNoAction(int index, Event evt) {
  3429. if (!EditorGUI.actionKey && !evt.shift) {
  3430. Select(index);
  3431. }
  3432. }
  3433. public void Select(int index) {
  3434. indexes.Clear();
  3435. indexes.Add(index);
  3436. firstSelected = index;
  3437. }
  3438. public void Remove(int index) {
  3439. if (indexes.Contains(index)) {
  3440. indexes.Remove(index);
  3441. }
  3442. }
  3443. public void AppendWithAction(int index, Event evt) {
  3444. if (EditorGUI.actionKey) {
  3445. if (Contains(index)) {
  3446. Remove(index);
  3447. }
  3448. else {
  3449. Append(index);
  3450. firstSelected = index;
  3451. }
  3452. }
  3453. else if (evt.shift && indexes.Count > 0 && firstSelected.HasValue) {
  3454. indexes.Clear();
  3455. AppendRange(firstSelected.Value, index);
  3456. }
  3457. else if (!Contains(index)) {
  3458. Select(index);
  3459. }
  3460. }
  3461. public void Sort() {
  3462. if (indexes.Count > 0) {
  3463. indexes.Sort();
  3464. }
  3465. }
  3466. public void Sort(System.Comparison<int> comparison) {
  3467. if (indexes.Count > 0) {
  3468. indexes.Sort(comparison);
  3469. }
  3470. }
  3471. public int[] ToArray() {
  3472. return indexes.ToArray();
  3473. }
  3474. public ListSelection Clone() {
  3475. ListSelection clone = new ListSelection(ToArray());
  3476. clone.firstSelected = firstSelected;
  3477. return clone;
  3478. }
  3479. internal void Trim(int min, int max) {
  3480. int i = indexes.Count;
  3481. while (--i > -1) {
  3482. int index = indexes[i];
  3483. if (index < min || index >= max) {
  3484. if (index == firstSelected && i > 0) {
  3485. firstSelected = indexes[i - 1];
  3486. }
  3487. indexes.RemoveAt(i);
  3488. }
  3489. }
  3490. }
  3491. internal bool CanRevert(SerializedProperty list) {
  3492. if (list.serializedObject.targetObjects.Length == 1) {
  3493. for (int i = 0; i < Length; i++) {
  3494. if (list.GetArrayElementAtIndex(this[i]).isInstantiatedPrefab) {
  3495. return true;
  3496. }
  3497. }
  3498. }
  3499. return false;
  3500. }
  3501. internal void RevertValues(object userData) {
  3502. SerializedProperty list = userData as SerializedProperty;
  3503. for (int i = 0; i < Length; i++) {
  3504. SerializedProperty property = list.GetArrayElementAtIndex(this[i]);
  3505. if (property.isInstantiatedPrefab) {
  3506. property.prefabOverride = false;
  3507. }
  3508. }
  3509. list.serializedObject.ApplyModifiedProperties();
  3510. list.serializedObject.Update();
  3511. HandleUtility.Repaint();
  3512. }
  3513. internal void Duplicate(SerializedProperty list) {
  3514. int offset = 0;
  3515. for (int i = 0; i < Length; i++) {
  3516. this[i] += offset;
  3517. list.GetArrayElementAtIndex(this[i]).DuplicateCommand();
  3518. list.serializedObject.ApplyModifiedProperties();
  3519. list.serializedObject.Update();
  3520. offset++;
  3521. }
  3522. HandleUtility.Repaint();
  3523. }
  3524. internal void Delete(SerializedProperty list) {
  3525. Sort();
  3526. int i = Length;
  3527. while (--i > -1) {
  3528. list.GetArrayElementAtIndex(this[i]).DeleteCommand();
  3529. }
  3530. Clear();
  3531. list.serializedObject.ApplyModifiedProperties();
  3532. list.serializedObject.Update();
  3533. HandleUtility.Repaint();
  3534. }
  3535. private void Append(int index) {
  3536. if (index >= 0 && !indexes.Contains(index)) {
  3537. indexes.Add(index);
  3538. }
  3539. }
  3540. private void AppendRange(int from, int to) {
  3541. int dir = (int)Mathf.Sign(to - from);
  3542. if (dir != 0) {
  3543. for (int i = from; i != to; i += dir) {
  3544. Append(i);
  3545. }
  3546. }
  3547. Append(to);
  3548. }
  3549. public IEnumerator<int> GetEnumerator() {
  3550. return ((IEnumerable<int>)indexes).GetEnumerator();
  3551. }
  3552. IEnumerator IEnumerable.GetEnumerator() {
  3553. return ((IEnumerable<int>)indexes).GetEnumerator();
  3554. }
  3555. }
  3556. //
  3557. // -- SORTING --
  3558. //
  3559. static class ListSort {
  3560. private delegate int SortComparision(SerializedProperty p1, SerializedProperty p2);
  3561. internal static void SortOnProperty(SerializedProperty list, int length, bool descending, string propertyName) {
  3562. BubbleSort(list, length, (p1, p2) => {
  3563. SerializedProperty a = p1.FindPropertyRelative(propertyName);
  3564. SerializedProperty b = p2.FindPropertyRelative(propertyName);
  3565. if (a != null && b != null && a.propertyType == b.propertyType) {
  3566. int comparison = Compare(a, b, descending, a.propertyType);
  3567. return descending ? -comparison : comparison;
  3568. }
  3569. return 0;
  3570. });
  3571. }
  3572. internal static void SortOnType(SerializedProperty list, int length, bool descending, SerializedPropertyType type) {
  3573. BubbleSort(list, length, (p1, p2) => {
  3574. int comparision = Compare(p1, p2, descending, type);
  3575. return descending ? -comparision : comparision;
  3576. });
  3577. }
  3578. //
  3579. // -- PRIVATE --
  3580. //
  3581. private static void BubbleSort(SerializedProperty list, int length, SortComparision comparision) {
  3582. for (int i = 0; i < length; i++) {
  3583. SerializedProperty p1 = list.GetArrayElementAtIndex(i);
  3584. for (int j = i + 1; j < length; j++) {
  3585. SerializedProperty p2 = list.GetArrayElementAtIndex(j);
  3586. if (comparision(p1, p2) > 0) {
  3587. list.MoveArrayElement(j, i);
  3588. }
  3589. }
  3590. }
  3591. }
  3592. private static int Compare(SerializedProperty p1, SerializedProperty p2, bool descending, SerializedPropertyType type) {
  3593. if (p1 == null || p2 == null) {
  3594. return 0;
  3595. }
  3596. switch (type) {
  3597. case SerializedPropertyType.Boolean:
  3598. return p1.boolValue.CompareTo(p2.boolValue);
  3599. case SerializedPropertyType.Character:
  3600. case SerializedPropertyType.Enum:
  3601. case SerializedPropertyType.Integer:
  3602. case SerializedPropertyType.LayerMask:
  3603. return p1.longValue.CompareTo(p2.longValue);
  3604. case SerializedPropertyType.Color:
  3605. return p1.colorValue.grayscale.CompareTo(p2.colorValue.grayscale);
  3606. case SerializedPropertyType.ExposedReference:
  3607. return CompareObjects(p1.exposedReferenceValue, p2.exposedReferenceValue, descending);
  3608. case SerializedPropertyType.Float:
  3609. return p1.doubleValue.CompareTo(p2.doubleValue);
  3610. case SerializedPropertyType.ObjectReference:
  3611. return CompareObjects(p1.objectReferenceValue, p2.objectReferenceValue, descending);
  3612. case SerializedPropertyType.String:
  3613. return p1.stringValue.CompareTo(p2.stringValue);
  3614. default:
  3615. return 0;
  3616. }
  3617. }
  3618. private static int CompareObjects(UnityEngine.Object obj1, UnityEngine.Object obj2, bool descending) {
  3619. if (obj1 && obj2) {
  3620. return obj1.name.CompareTo(obj2.name);
  3621. }
  3622. else if (obj1) {
  3623. return descending ? 1 : -1;
  3624. }
  3625. return descending ? -1 : 1;
  3626. }
  3627. }
  3628. //
  3629. // -- SURROGATE --
  3630. //
  3631. public struct Surrogate {
  3632. public System.Type type;
  3633. public bool exactType;
  3634. public SurrogateCallback callback;
  3635. internal bool enabled;
  3636. public bool HasType {
  3637. get { return enabled && type != null; }
  3638. }
  3639. public Surrogate(System.Type type)
  3640. : this(type, null) {
  3641. }
  3642. public Surrogate(System.Type type, SurrogateCallback callback) {
  3643. this.type = type;
  3644. this.callback = callback;
  3645. enabled = true;
  3646. exactType = false;
  3647. }
  3648. public void Invoke(SerializedProperty element, UnityEngine.Object objectReference, ReorderableList list) {
  3649. if (element != null && callback != null) {
  3650. callback.Invoke(element, objectReference, list);
  3651. }
  3652. }
  3653. }
  3654. //
  3655. // -- EXCEPTIONS --
  3656. //
  3657. class InvalidListException : System.InvalidOperationException {
  3658. public InvalidListException() : base("ReorderableList serializedProperty must be an array") {
  3659. }
  3660. }
  3661. class MissingListExeption : System.ArgumentNullException {
  3662. public MissingListExeption() : base("ReorderableList serializedProperty is null") {
  3663. }
  3664. }
  3665. //
  3666. // -- INTERNAL --
  3667. //
  3668. static class Internals {
  3669. private static MethodInfo dragDropValidation;
  3670. private static object[] dragDropValidationParams;
  3671. private static MethodInfo appendDragDrop;
  3672. private static object[] appendDragDropParams;
  3673. static Internals() {
  3674. dragDropValidation = System.Type.GetType("UnityEditor.EditorGUI, UnityEditor").GetMethod("ValidateObjectFieldAssignment", BindingFlags.NonPublic | BindingFlags.Static);
  3675. appendDragDrop = typeof(SerializedProperty).GetMethod("AppendFoldoutPPtrValue", BindingFlags.NonPublic | BindingFlags.Instance);
  3676. }
  3677. internal static UnityEngine.Object ValidateObjectDragAndDrop(UnityEngine.Object[] references, SerializedProperty property, System.Type type, bool exactType) {
  3678. #if UNITY_2017_1_OR_NEWER
  3679. dragDropValidationParams = GetParams(ref dragDropValidationParams, 4);
  3680. dragDropValidationParams[0] = references;
  3681. dragDropValidationParams[1] = type;
  3682. dragDropValidationParams[2] = property;
  3683. dragDropValidationParams[3] = exactType ? 1 : 0;
  3684. #else
  3685. dragDropValidationParams = GetParams(ref dragDropValidationParams, 3);
  3686. dragDropValidationParams[0] = references;
  3687. dragDropValidationParams[1] = type;
  3688. dragDropValidationParams[2] = property;
  3689. #endif
  3690. return dragDropValidation.Invoke(null, dragDropValidationParams) as UnityEngine.Object;
  3691. }
  3692. internal static void AppendDragAndDropValue(UnityEngine.Object obj, SerializedProperty list) {
  3693. appendDragDropParams = GetParams(ref appendDragDropParams, 1);
  3694. appendDragDropParams[0] = obj;
  3695. appendDragDrop.Invoke(list, appendDragDropParams);
  3696. }
  3697. private static object[] GetParams(ref object[] parameters, int count) {
  3698. if (parameters == null) {
  3699. parameters = new object[count];
  3700. }
  3701. return parameters;
  3702. }
  3703. }
  3704. }
  3705. [CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
  3706. public class ReadOnlyAttributeDrawer : PropertyDrawer
  3707. {
  3708. // Necessary since some properties tend to collapse smaller than their content
  3709. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  3710. {
  3711. return EditorGUI.GetPropertyHeight(property, label, true);
  3712. }
  3713. // Draw a disabled property field
  3714. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  3715. {
  3716. GUI.enabled = false; // Disable fields
  3717. EditorGUI.PropertyField(position, property, label, true);
  3718. GUI.enabled = true; // Enable fields
  3719. }
  3720. }
  3721. }
  3722. #endif