| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328 |
- /*==============================================================================================================
- * Copyright (c) 2020 John Jackson
- * File: gs_gui.h
- * Github: https://github.com/MrFrenik/gunslinger
- * All Rights Reserved
- * MIT License
- * May all those that this source may reach be blessed by the LORD and find peace and joy in life.
- * Everyone who drinks of this water will be thirsty again; but whoever drinks of the water
- * that I will give him shall never thirst; John 4:13-14
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
- * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- * The above copyright, blessing, biblical verse, notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
- * TO THE WARRANTIES OF MECHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
-
- =================================================================================================================*/
- /*
- USAGE: (IMPORTANT)
- =================================================================================================================
- Before including, define the gunslinger immediate gui implementation like this:
- #define GS_GUI_IMPL
- in EXACTLY ONE C or C++ file that includes this header, BEFORE the
- include, like this:
- #define GS_GUI_IMPL
- #include "gs_gui.h"
- All other files should just #include "gs_gui.h" without the #define.
- MUST include "gs.h" and "gs_idraw.h" and declare GS_IMPL and GS_IMMEDIATE_DRAW_IMPL BEFORE this file, since this file relies on those:
- #define GS_IMPL
- #include "gs.h"
- #define GS_IMMEDIATE_DRAW_IMPL
- #include "gs_idraw.h"
- #define GS_GUI_IMPL
- #include "gs_gui.h"
- ================================================================================================================
- */
- #ifndef GS_GUI_H
- #define GS_GUI_H
- // Directly modified from microui implementation: https://github.com/rxi/microui
- #define GS_GUI_SPLIT_SIZE 2.f
- #define GS_GUI_MAX_CNT 48
- #define GS_GUI_COMMANDLIST_SIZE (256 * 1024)
- #define GS_GUI_ROOTLIST_SIZE 32
- #define GS_GUI_CONTAINERSTACK_SIZE 32
- #define GS_GUI_CLIPSTACK_SIZE 32
- #define GS_GUI_IDSTACK_SIZE 32
- #define GS_GUI_LAYOUTSTACK_SIZE 16
- #define GS_GUI_CONTAINERPOOL_SIZE 48
- #define GS_GUI_TREENODEPOOL_SIZE 48
- #define GS_GUI_GS_GUI_SPLIT_SIZE 32
- #define GS_GUI_GS_GUI_TAB_SIZE 32
- #define GS_GUI_MAX_WIDTHS 16
- #define GS_GUI_REAL float
- #define GS_GUI_REAL_FMT "%.3g"
- #define GS_GUI_SLIDER_FMT "%.2f"
- #define GS_GUI_MAX_FMT 127
- #define GS_GUI_TAB_ITEM_MAX 24
- #define GS_GUI_CLS_SELECTOR_MAX 4
- #define gs_gui_stack(T, n) struct {int32_t idx; T items[n];}
- enum {
- GS_GUI_CLIP_PART = 1,
- GS_GUI_CLIP_ALL
- };
- enum {
- GS_GUI_COMMAND_JUMP = 1,
- GS_GUI_COMMAND_CLIP,
- GS_GUI_COMMAND_SHAPE,
- GS_GUI_COMMAND_TEXT,
- GS_GUI_COMMAND_ICON,
- GS_GUI_COMMAND_IMAGE,
- GS_GUI_COMMAND_CUSTOM,
- GS_GUI_COMMAND_PIPELINE,
- GS_GUI_COMMAND_UNIFORMS,
- GS_GUI_COMMAND_MAX
- };
- enum {
- GS_GUI_SHAPE_RECT = 1,
- GS_GUI_SHAPE_CIRCLE,
- GS_GUI_SHAPE_TRIANGLE,
- GS_GUI_SHAPE_LINE
- };
- enum {
- GS_GUI_TRANSFORM_WORLD = 0x00,
- GS_GUI_TRANSFORM_LOCAL
- };
- enum {
- GS_GUI_GIZMO_TRANSLATE = 0x00,
- GS_GUI_GIZMO_ROTATE,
- GS_GUI_GIZMO_SCALE
- };
- enum {
- GS_GUI_COLOR_BACKGROUND = 0x00,
- GS_GUI_COLOR_CONTENT,
- GS_GUI_COLOR_BORDER,
- GS_GUI_COLOR_SHADOW,
- GS_GUI_COLOR_CONTENT_BACKGROUND,
- GS_GUI_COLOR_CONTENT_SHADOW,
- GS_GUI_COLOR_CONTENT_BORDER,
- GS_GUI_COLOR_MAX
- };
- enum {
- GS_GUI_ICON_CLOSE = 1,
- GS_GUI_ICON_CHECK,
- GS_GUI_ICON_COLLAPSED,
- GS_GUI_ICON_EXPANDED,
- GS_GUI_ICON_MAX
- };
- enum {
- GS_GUI_RES_ACTIVE = (1 << 0),
- GS_GUI_RES_SUBMIT = (1 << 1),
- GS_GUI_RES_CHANGE = (1 << 2)
- };
- typedef enum gs_gui_alt_drag_mode_type {
- GS_GUI_ALT_DRAG_QUAD = 0x00, // Quadrants
- GS_GUI_ALT_DRAG_NINE, // Nine splice the window
- GS_GUI_ALT_DRAG_SINGLE // Single window drag (controls the width/height, leaving x/y position of window in tact)
- } gs_gui_alt_drag_mode_type;
- enum {
- GS_GUI_OPT_NOSTYLESHADOW = (1ULL << 0),
- GS_GUI_OPT_NOSTYLEBORDER = (1ULL << 1),
- GS_GUI_OPT_NOINTERACT = (1ULL << 2),
- GS_GUI_OPT_NOFRAME = (1ULL << 3),
- GS_GUI_OPT_NORESIZE = (1ULL << 4),
- GS_GUI_OPT_NOSCROLL = (1ULL << 5),
- GS_GUI_OPT_NOCLOSE = (1ULL << 6),
- GS_GUI_OPT_NOTITLE = (1ULL << 7),
- GS_GUI_OPT_HOLDFOCUS = (1ULL << 8),
- GS_GUI_OPT_AUTOSIZE = (1ULL << 9),
- GS_GUI_OPT_POPUP = (1ULL << 10),
- GS_GUI_OPT_CLOSED = (1ULL << 11),
- GS_GUI_OPT_EXPANDED = (1ULL << 12),
- GS_GUI_OPT_NOHOVER = (1ULL << 13),
- GS_GUI_OPT_FORCESETRECT = (1ULL << 14),
- GS_GUI_OPT_NOFOCUS = (1ULL << 15),
- GS_GUI_OPT_FORCEFOCUS = (1ULL << 16),
- GS_GUI_OPT_NOMOVE = (1ULL << 17),
- GS_GUI_OPT_NOCLIP = (1ULL << 18),
- GS_GUI_OPT_NODOCK = (1ULL << 19),
- GS_GUI_OPT_FULLSCREEN = (1ULL << 20),
- GS_GUI_OPT_DOCKSPACE = (1ULL << 21),
- GS_GUI_OPT_NOBRINGTOFRONT = (1ULL << 22),
- GS_GUI_OPT_LEFTCLICKONLY = (1ULL << 23),
- GS_GUI_OPT_NOSWITCHSTATE = (1ULL << 24),
- GS_GUI_OPT_NOBORDER = (1ULL << 25),
- GS_GUI_OPT_ISCONTENT = (1ULL << 26),
- GS_GUI_OPT_NOCARET = (1ULL << 27),
- GS_GUI_OPT_NOSCROLLHORIZONTAL = (1ULL << 28),
- GS_GUI_OPT_NOSCROLLVERTICAL = (1ULL << 29),
- GS_GUI_OPT_NOSTYLEBACKGROUND = (1ULL << 30),
- GS_GUI_OPT_PARSEIDTAGONLY = (1ULL << 31),
- };
- enum {
- GS_GUI_MOUSE_LEFT = (1 << 0),
- GS_GUI_MOUSE_RIGHT = (1 << 1),
- GS_GUI_MOUSE_MIDDLE = (1 << 2)
- };
- enum {
- GS_GUI_KEY_SHIFT = (1 << 0),
- GS_GUI_KEY_CTRL = (1 << 1),
- GS_GUI_KEY_ALT = (1 << 2),
- GS_GUI_KEY_BACKSPACE = (1 << 3),
- GS_GUI_KEY_RETURN = (1 << 4)
- };
- #define GS_GUI_OPT_NOSTYLING (GS_GUI_OPT_NOSTYLEBORDER | GS_GUI_OPT_NOSTYLEBACKGROUND | GS_GUI_OPT_NOSTYLESHADOW)
- typedef struct gs_gui_context_t gs_gui_context_t;
- typedef uint32_t gs_gui_id;
- typedef GS_GUI_REAL gs_gui_real;
- // Shapes
- typedef struct {float x, y, w, h;} gs_gui_rect_t;
- typedef struct {float radius; gs_vec2 center;} gs_gui_circle_t;
- typedef struct {gs_vec2 points[3];} gs_gui_triangle_t;
- typedef struct {gs_vec2 start; gs_vec2 end;} gs_gui_line_t;
- typedef struct {gs_gui_id id; int32_t last_update;} gs_gui_pool_item_t;
- typedef struct
- {
- int32_t type, size;
- } gs_gui_basecommand_t;
- typedef struct
- {
- gs_gui_basecommand_t base;
- void *dst;
- } gs_gui_jumpcommand_t;
- typedef struct
- {
- gs_gui_basecommand_t base;
- gs_gui_rect_t rect;
- } gs_gui_clipcommand_t;
- typedef struct
- {
- gs_gui_basecommand_t base;
- gs_asset_font_t* font;
- gs_vec2 pos;
- gs_color_t color;
- char str[1];
- } gs_gui_textcommand_t;
- typedef struct
- {
- gs_gui_basecommand_t base;
- gs_handle(gs_graphics_pipeline_t) pipeline;
- gsi_layout_type layout_type;
- void* layout;
- size_t layout_sz;
- } gs_gui_pipelinecommand_t;
- typedef struct
- {
- gs_gui_basecommand_t base;
- void* data;
- size_t sz;
- } gs_gui_binduniformscommand_t;
- typedef struct
- {
- gs_gui_basecommand_t base;
- gs_gui_rect_t rect;
- gs_handle(gs_graphics_texture_t) hndl;
- gs_vec4 uvs;
- gs_color_t color;
- } gs_gui_imagecommand_t;
- struct gs_gui_customcommand_t;
- // Draw Callback
- typedef void (* gs_gui_draw_callback_t)(gs_gui_context_t* ctx, struct gs_gui_customcommand_t* cmd);
- typedef struct gs_gui_customcommand_t
- {
- gs_gui_basecommand_t base;
- gs_gui_rect_t clip;
- gs_gui_rect_t viewport;
- gs_gui_id hash;
- gs_gui_id hover;
- gs_gui_id focus;
- gs_gui_draw_callback_t cb;
- void* data;
- size_t sz;
- } gs_gui_customcommand_t;
- typedef struct
- {
- gs_gui_basecommand_t base;
- uint32_t type;
- union
- {
- gs_gui_rect_t rect;
- gs_gui_circle_t circle;
- gs_gui_triangle_t triangle;
- gs_gui_line_t line;
- };
- gs_color_t color;
- } gs_gui_shapecommand_t;
- // NOTE(john): This is wasteful, given how I'm pushing into the byte buffer anyway for heterogenous types
- typedef union
- {
- int32_t type;
- gs_gui_basecommand_t base;
- gs_gui_jumpcommand_t jump;
- gs_gui_clipcommand_t clip;
- gs_gui_shapecommand_t shape;
- gs_gui_textcommand_t text;
- gs_gui_imagecommand_t image;
- gs_gui_customcommand_t custom;
- gs_gui_pipelinecommand_t pipeline;
- gs_gui_binduniformscommand_t uniforms;
- } gs_gui_command_t;
- struct gs_gui_context_t;
- typedef void (* gs_gui_on_draw_button_callback)(struct gs_gui_context_t* ctx, gs_gui_rect_t rect,
- gs_gui_id id, bool hovered, bool focused, uint64_t opt, const char* label, int32_t icon);
- typedef enum {
- GS_GUI_LAYOUT_ANCHOR_TOPLEFT = 0x00,
- GS_GUI_LAYOUT_ANCHOR_TOPCENTER,
- GS_GUI_LAYOUT_ANCHOR_TOPRIGHT,
- GS_GUI_LAYOUT_ANCHOR_LEFT,
- GS_GUI_LAYOUT_ANCHOR_CENTER,
- GS_GUI_LAYOUT_ANCHOR_RIGHT,
- GS_GUI_LAYOUT_ANCHOR_BOTTOMLEFT,
- GS_GUI_LAYOUT_ANCHOR_BOTTOMCENTER,
- GS_GUI_LAYOUT_ANCHOR_BOTTOMRIGHT
- } gs_gui_layout_anchor_type;
- typedef enum {
- GS_GUI_ALIGN_START = 0x00,
- GS_GUI_ALIGN_CENTER,
- GS_GUI_ALIGN_END
- } gs_gui_align_type;
- typedef enum {
- GS_GUI_JUSTIFY_START = 0x00,
- GS_GUI_JUSTIFY_CENTER,
- GS_GUI_JUSTIFY_END
- } gs_gui_justification_type;
- typedef enum {
- GS_GUI_DIRECTION_COLUMN = 0x00,
- GS_GUI_DIRECTION_ROW,
- GS_GUI_DIRECTION_COLUMN_REVERSE,
- GS_GUI_DIRECTION_ROW_REVERSE
- } gs_gui_direction;
- typedef struct gs_gui_layout_t
- {
- gs_gui_rect_t body;
- gs_gui_rect_t next;
- gs_vec2 position;
- gs_vec2 size;
- gs_vec2 max;
- int32_t padding[4];
- int32_t widths[GS_GUI_MAX_WIDTHS];
- int32_t items;
- int32_t item_index;
- int32_t next_row;
- int32_t next_type;
- int32_t indent;
- // flex direction / justification / alignment
- int32_t direction;
- int32_t justify_content;
- int32_t align_content;
- } gs_gui_layout_t;
- // Forward decl.
- struct gs_gui_container_t;
- typedef enum gs_gui_split_node_type
- {
- GS_GUI_SPLIT_NODE_CONTAINER = 0x00,
- GS_GUI_SPLIT_NODE_SPLIT
- } gs_gui_split_node_type;
- enum {
- GS_GUI_SPLIT_NODE_CHILD = 0x00,
- GS_GUI_SPLIT_NODE_PARENT
- };
- typedef struct gs_gui_split_node_t
- {
- gs_gui_split_node_type type;
- union
- {
- uint32_t split;
- struct gs_gui_container_t* container;
- };
- } gs_gui_split_node_t;
- typedef enum gs_gui_split_type
- {
- GS_GUI_SPLIT_LEFT = 0x00,
- GS_GUI_SPLIT_RIGHT,
- GS_GUI_SPLIT_TOP,
- GS_GUI_SPLIT_BOTTOM,
- GS_GUI_SPLIT_TAB
- } gs_gui_split_type;
- typedef struct gs_gui_split_t
- {
- gs_gui_split_type type; // GS_GUI_SPLIT_LEFT, GS_GUI_SPLIT_RIGHT, GS_GUI_SPLIT_TAB, GS_GUI_SPLIT_BOTTOM, GS_GUI_SPLIT_TOP
- float ratio; // Split ratio between children [0.f, 1.f], (left node = ratio), right node = (1.f - ratio)
- gs_gui_rect_t rect;
- gs_gui_rect_t prev_rect;
- gs_gui_split_node_t children[2];
- uint32_t parent;
- uint32_t id;
- uint32_t zindex;
- int32_t frame;
- } gs_gui_split_t;
- typedef enum gs_gui_window_flags {
- GS_GUI_WINDOW_FLAGS_VISIBLE = (1 << 0),
- GS_GUI_WINDOW_FLAGS_FIRST_INIT = (1 << 1),
- GS_GUI_WINDOW_FLAGS_PUSH_ID = (1 << 2)
- } gs_gui_window_flags;
- // Equidistantly sized tabs, based on rect of window
- typedef struct gs_gui_tab_item_t
- {
- gs_gui_id tab_bar;
- uint32_t zindex; // Sorting index in tab bar
- void* data; // User set data pointer for this item
- uint32_t idx; // Internal index
- } gs_gui_tab_item_t;
- typedef struct gs_gui_tab_bar_t
- {
- gs_gui_tab_item_t items[GS_GUI_TAB_ITEM_MAX];
- uint32_t size; // Current number of items in tab bar
- gs_gui_rect_t rect; // Cached sized for tab bar
- uint32_t focus; // Focused item in tab bar
- } gs_gui_tab_bar_t;
- typedef struct gs_gui_container_t
- {
- gs_gui_command_t *head, *tail;
- gs_gui_rect_t rect;
- gs_gui_rect_t body;
- gs_vec2 content_size;
- gs_vec2 scroll;
- int32_t zindex;
- int32_t open;
- gs_gui_id id;
- gs_gui_id split; // If container is docked, then will have owning split to get sizing (0x00 for NULL)
- uint32_t tab_bar;
- uint32_t tab_item;
- struct gs_gui_container_t* parent; // Owning parent (for tabbing)
- uint64_t opt;
- uint32_t frame;
- uint32_t visible;
- int32_t flags;
- char name[256];
- } gs_gui_container_t;
- typedef enum {
- GS_GUI_ELEMENT_STATE_NEG = -1,
- GS_GUI_ELEMENT_STATE_DEFAULT = 0x00,
- GS_GUI_ELEMENT_STATE_HOVER,
- GS_GUI_ELEMENT_STATE_FOCUS,
- GS_GUI_ELEMENT_STATE_COUNT,
- GS_GUI_ELEMENT_STATE_ON_HOVER,
- GS_GUI_ELEMENT_STATE_ON_FOCUS,
- GS_GUI_ELEMENT_STATE_OFF_HOVER,
- GS_GUI_ELEMENT_STATE_OFF_FOCUS
- } gs_gui_element_state;
- typedef enum gs_gui_element_type
- {
- GS_GUI_ELEMENT_CONTAINER = 0x00,
- GS_GUI_ELEMENT_LABEL,
- GS_GUI_ELEMENT_TEXT,
- GS_GUI_ELEMENT_PANEL,
- GS_GUI_ELEMENT_INPUT,
- GS_GUI_ELEMENT_BUTTON,
- GS_GUI_ELEMENT_SCROLL,
- GS_GUI_ELEMENT_IMAGE,
- GS_GUI_ELEMENT_COUNT
- } gs_gui_element_type;
- typedef enum {
- GS_GUI_PADDING_LEFT = 0x00,
- GS_GUI_PADDING_RIGHT,
- GS_GUI_PADDING_TOP,
- GS_GUI_PADDING_BOTTOM
- } gs_gui_padding_type;
- typedef enum {
- GS_GUI_MARGIN_LEFT = 0x00,
- GS_GUI_MARGIN_RIGHT,
- GS_GUI_MARGIN_TOP,
- GS_GUI_MARGIN_BOTTOM
- } gs_gui_margin_type;
- typedef enum {
- // Width/Height
- GS_GUI_STYLE_WIDTH = 0x00,
- GS_GUI_STYLE_HEIGHT,
- // Padding
- GS_GUI_STYLE_PADDING,
- GS_GUI_STYLE_PADDING_LEFT,
- GS_GUI_STYLE_PADDING_RIGHT,
- GS_GUI_STYLE_PADDING_TOP,
- GS_GUI_STYLE_PADDING_BOTTOM,
- GS_GUI_STYLE_MARGIN, // Can set margin for all at once, if -1.f then will assume 'auto' to simulate standard css
- GS_GUI_STYLE_MARGIN_LEFT,
- GS_GUI_STYLE_MARGIN_RIGHT,
- GS_GUI_STYLE_MARGIN_TOP,
- GS_GUI_STYLE_MARGIN_BOTTOM,
- // Border Radius
- GS_GUI_STYLE_BORDER_RADIUS,
- GS_GUI_STYLE_BORDER_RADIUS_LEFT,
- GS_GUI_STYLE_BORDER_RADIUS_RIGHT,
- GS_GUI_STYLE_BORDER_RADIUS_TOP,
- GS_GUI_STYLE_BORDER_RADIUS_BOTTOM,
- // Border Width
- GS_GUI_STYLE_BORDER_WIDTH,
- GS_GUI_STYLE_BORDER_WIDTH_LEFT,
- GS_GUI_STYLE_BORDER_WIDTH_RIGHT,
- GS_GUI_STYLE_BORDER_WIDTH_TOP,
- GS_GUI_STYLE_BORDER_WIDTH_BOTTOM,
- // Text
- GS_GUI_STYLE_TEXT_ALIGN,
- // Flex
- GS_GUI_STYLE_DIRECTION,
- GS_GUI_STYLE_ALIGN_CONTENT,
- GS_GUI_STYLE_JUSTIFY_CONTENT, // Justify runs parallel to direction (ex. for row, left to right)
- // Shadow
- GS_GUI_STYLE_SHADOW_X,
- GS_GUI_STYLE_SHADOW_Y,
- // Colors
- GS_GUI_STYLE_COLOR_BACKGROUND,
- GS_GUI_STYLE_COLOR_BORDER,
- GS_GUI_STYLE_COLOR_SHADOW,
- GS_GUI_STYLE_COLOR_CONTENT,
- GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND,
- GS_GUI_STYLE_COLOR_CONTENT_BORDER,
- GS_GUI_STYLE_COLOR_CONTENT_SHADOW,
- // Font
- GS_GUI_STYLE_FONT,
- GS_GUI_STYLE_COUNT
- } gs_gui_style_element_type;
- enum {
- GS_GUI_ANIMATION_DIRECTION_FORWARD = 0x00,
- GS_GUI_ANIMATION_DIRECTION_BACKWARD
- };
-
- typedef struct {
- gs_gui_style_element_type type;
- union {
- int32_t value;
- gs_color_t color;
- gs_asset_font_t* font;
- };
- } gs_gui_style_element_t;
- typedef struct gs_gui_animation_t
- {
- int16_t max; // max time
- int16_t time; // current time
- int16_t delay; // delay
- int16_t curve; // curve type
- int16_t direction; // current direction
- int16_t playing; // whether or not active
- int16_t iterations; // number of iterations to play the animation
- int16_t focus_state; // cached focus_state from frame (want to delete this somehow)
- int16_t hover_state; // cached hover_state from frame (want to delete this somehow)
- int16_t start_state; // starting state for animation blend
- int16_t end_state; // ending state for animation blend
- int32_t frame; // current frame (to match)
- } gs_gui_animation_t;
- typedef struct gs_gui_style_t
- {
- // font
- gs_asset_font_t* font;
- // dimensions
- float size[2];
- int16_t spacing; // get rid of (use padding)
- int16_t indent; // get rid of (use margin)
- int16_t title_height; // get rid of (use title_bar style)
- int16_t scrollbar_size; // get rid of (use scroll style)
- int16_t thumb_size; // get rid of (use various styles)
- // colors
- gs_color_t colors[GS_GUI_COLOR_MAX];
- // padding/margin
- int32_t padding[4];
- int16_t margin[4];
- // border
- int16_t border_width[4];
- int16_t border_radius[4];
- // flex direction / justification / alignment
- int16_t direction;
- int16_t justify_content;
- int16_t align_content;
- // shadow amount each direction
- int16_t shadow_x;
- int16_t shadow_y;
- } gs_gui_style_t;
- // Keep animation properties lists within style sheet to look up
- typedef struct gs_gui_animation_property_t
- {
- gs_gui_style_element_type type;
- int16_t time;
- int16_t delay;
- } gs_gui_animation_property_t;
- typedef struct gs_gui_animation_property_list_t
- {
- gs_dyn_array(gs_gui_animation_property_t) properties[3];
- } gs_gui_animation_property_list_t;
- /*
- element type
- classes
- id
- gs_gui_button(gui, "Text##.cls#id");
- gs_gui_label(gui, "Title###title");
- button .class #id : hover { // All of these styles get concat into one?
- }
- */
- typedef struct {
- gs_dyn_array(gs_gui_style_element_t) styles[3];
- } gs_gui_style_list_t;
- typedef struct gs_gui_style_sheet_t {
- gs_gui_style_t styles[GS_GUI_ELEMENT_COUNT][3]; // default | hovered | focused
- gs_hash_table(gs_gui_element_type, gs_gui_animation_property_list_t) animations;
-
- gs_hash_table(uint64_t, gs_gui_style_list_t) cid_styles;
- gs_hash_table(uint64_t, gs_gui_animation_property_list_t) cid_animations;
- } gs_gui_style_sheet_t;
- typedef struct gs_gui_style_sheet_element_desc_t {
- struct {
- struct {
- gs_gui_style_element_t* data;
- size_t size;
- } style;
- struct {
- gs_gui_animation_property_t* data;
- size_t size;
- } animation;
- } all;
- struct {
- struct {
- gs_gui_style_element_t* data;
- size_t size;
- } style;
- struct {
- gs_gui_animation_property_t* data;
- size_t size;
- } animation;
- } def;
- struct {
- struct {
- gs_gui_style_element_t* data;
- size_t size;
- } style;
- struct {
- gs_gui_animation_property_t* data;
- size_t size;
- } animation;
- } hover;
- struct {
- struct {
- gs_gui_style_element_t* data;
- size_t size;
- } style;
- struct {
- gs_gui_animation_property_t* data;
- size_t size;
- } animation;
- } focus;
- } gs_gui_style_sheet_element_desc_t;
- typedef gs_gui_style_sheet_element_desc_t gs_gui_inline_style_desc_t;
- typedef struct gs_gui_style_sheet_desc_t {
- gs_gui_style_sheet_element_desc_t container;
- gs_gui_style_sheet_element_desc_t button;
- gs_gui_style_sheet_element_desc_t panel;
- gs_gui_style_sheet_element_desc_t input;
- gs_gui_style_sheet_element_desc_t text;
- gs_gui_style_sheet_element_desc_t label;
- gs_gui_style_sheet_element_desc_t scroll;
- gs_gui_style_sheet_element_desc_t tab;
- gs_gui_style_sheet_element_desc_t menu;
- gs_gui_style_sheet_element_desc_t title;
- gs_gui_style_sheet_element_desc_t image;
- } gs_gui_style_sheet_desc_t;
- typedef enum gs_gui_request_type
- {
- GS_GUI_SPLIT_NEW = 0x01,
- GS_GUI_CNT_MOVE,
- GS_GUI_CNT_FOCUS,
- GS_GUI_SPLIT_MOVE,
- GS_GUI_SPLIT_RESIZE_SW,
- GS_GUI_SPLIT_RESIZE_SE,
- GS_GUI_SPLIT_RESIZE_NW,
- GS_GUI_SPLIT_RESIZE_NE,
- GS_GUI_SPLIT_RESIZE_W,
- GS_GUI_SPLIT_RESIZE_E,
- GS_GUI_SPLIT_RESIZE_N,
- GS_GUI_SPLIT_RESIZE_S,
- GS_GUI_SPLIT_RESIZE_CENTER,
- GS_GUI_SPLIT_RATIO,
- GS_GUI_SPLIT_RESIZE_INVALID,
- GS_GUI_TAB_SWAP_LEFT,
- GS_GUI_TAB_SWAP_RIGHT
- } gs_gui_request_type;
- typedef struct gs_gui_request_t
- {
- gs_gui_request_type type;
- union
- {
- gs_gui_split_type split_type;
- gs_gui_split_t* split;
- gs_gui_container_t* cnt;
- };
- uint32_t frame;
- } gs_gui_request_t;
- typedef struct gs_gui_inline_style_stack_t
- {
- gs_dyn_array(gs_gui_style_element_t) styles[3];
- gs_dyn_array(gs_gui_animation_property_t) animations[3];
- gs_dyn_array(uint32_t) style_counts; // amount of styles to pop off at "top of stack" for each state
- gs_dyn_array(uint32_t) animation_counts; // amount of animations to pop off at "top of stack" for each state
- } gs_gui_inline_style_stack_t;
- typedef struct gs_gui_context_t
- {
- // Core state
- gs_gui_style_t* style; // Active style
- gs_gui_style_sheet_t* style_sheet; // Active style sheet
- gs_gui_id hover;
- gs_gui_id focus;
- gs_gui_id last_id;
- gs_gui_id state_switch_id; // Id that had a state switch
- int32_t switch_state;
- gs_gui_id lock_focus;
- int32_t last_hover_state;
- int32_t last_focus_state;
- gs_gui_id prev_hover;
- gs_gui_id prev_focus;
- gs_gui_rect_t last_rect;
- int32_t last_zindex;
- int32_t updated_focus;
- int32_t frame;
- gs_vec2 framebuffer_size;
- gs_gui_rect_t viewport;
- gs_gui_container_t* active_root;
- gs_gui_container_t* hover_root;
- gs_gui_container_t* next_hover_root;
- gs_gui_container_t* scroll_target;
- gs_gui_container_t* focus_root;
- gs_gui_container_t* next_focus_root;
- gs_gui_container_t* dockable_root;
- gs_gui_container_t* prev_dockable_root;
- gs_gui_container_t* docked_root;
- gs_gui_container_t* undock_root;
- gs_gui_split_t* focus_split;
- gs_gui_split_t* next_hover_split;
- gs_gui_split_t* hover_split;
- gs_gui_id next_lock_hover_id;
- gs_gui_id lock_hover_id;
- char number_edit_buf[GS_GUI_MAX_FMT];
- gs_gui_id number_edit;
- gs_gui_alt_drag_mode_type alt_drag_mode;
- gs_dyn_array(gs_gui_request_t) requests;
- // Stacks
- gs_gui_stack(uint8_t, GS_GUI_COMMANDLIST_SIZE) command_list;
- gs_gui_stack(gs_gui_container_t*, GS_GUI_ROOTLIST_SIZE) root_list;
- gs_gui_stack(gs_gui_container_t*, GS_GUI_CONTAINERSTACK_SIZE) container_stack;
- gs_gui_stack(gs_gui_rect_t, GS_GUI_CLIPSTACK_SIZE) clip_stack;
- gs_gui_stack(gs_gui_id, GS_GUI_IDSTACK_SIZE) id_stack;
- gs_gui_stack(gs_gui_layout_t, GS_GUI_LAYOUTSTACK_SIZE) layout_stack;
- // Style sheet element stacks
- gs_hash_table(gs_gui_element_type, gs_gui_inline_style_stack_t) inline_styles;
- // Retained state pools
- gs_gui_pool_item_t container_pool[GS_GUI_CONTAINERPOOL_SIZE];
- gs_gui_container_t containers[GS_GUI_CONTAINERPOOL_SIZE];
- gs_gui_pool_item_t treenode_pool[GS_GUI_TREENODEPOOL_SIZE];
- gs_slot_array(gs_gui_split_t) splits;
- gs_slot_array(gs_gui_tab_bar_t) tab_bars;
- // Input state
- gs_vec2 mouse_pos;
- gs_vec2 last_mouse_pos;
- gs_vec2 mouse_delta;
- gs_vec2 scroll_delta;
- int32_t mouse_down;
- int32_t mouse_pressed;
- int32_t key_down;
- int32_t key_pressed;
- char input_text[32];
- // Backend resources
- uint32_t window_hndl;
- gs_immediate_draw_t gsi;
- gs_immediate_draw_t overlay_draw_list;
- // Active Transitions
- gs_hash_table(gs_gui_id, gs_gui_animation_t) animations;
- // Font stash
- gs_hash_table(uint64_t, gs_asset_font_t*) font_stash;
- // Callbacks
- struct {
- gs_gui_on_draw_button_callback button;
- } callbacks;
- } gs_gui_context_t;
- typedef struct
- {
- // Core state
- gs_gui_style_t* style; // Active style
- gs_gui_style_sheet_t* style_sheet; // Active style sheet
- gs_gui_id hover;
- gs_gui_id focus;
- gs_gui_id last_id;
- gs_gui_id lock_focus;
- gs_gui_rect_t last_rect;
- int32_t last_zindex;
- int32_t updated_focus;
- int32_t frame;
- gs_gui_container_t* hover_root;
- gs_gui_container_t* next_hover_root;
- gs_gui_container_t* scroll_target;
- gs_gui_container_t* focus_root;
- gs_gui_container_t* next_focus_root;
- gs_gui_container_t* dockable_root;
- gs_gui_container_t* prev_dockable_root;
- gs_gui_container_t* docked_root;
- gs_gui_container_t* undock_root;
- gs_gui_split_t* focus_split;
- gs_gui_split_t* next_hover_split;
- gs_gui_split_t* hover_split;
- gs_gui_id next_lock_hover_id;
- gs_gui_id lock_hover_id;
- char number_edit_buf[GS_GUI_MAX_FMT];
- gs_gui_id number_edit;
- gs_gui_alt_drag_mode_type alt_drag_mode;
- gs_dyn_array(gs_gui_request_t) requests;
- // Stacks
- gs_gui_stack(gs_gui_container_t*, GS_GUI_CONTAINERSTACK_SIZE) container_stack;
- gs_dyn_array(uint8_t) command_list;
- gs_dyn_array(gs_gui_container_t*) root_list;
- gs_dyn_array(gs_gui_rect_t) clip_stack;
- gs_dyn_array(gs_gui_id) id_stack;
- gs_dyn_array(gs_gui_layout_t) layout_stack;
- // Retained state pools
- gs_gui_pool_item_t container_pool[GS_GUI_CONTAINERPOOL_SIZE];
- gs_gui_pool_item_t treenode_pool[GS_GUI_TREENODEPOOL_SIZE];
- gs_slot_array(gs_gui_split_t) splits;
- gs_slot_array(gs_gui_tab_bar_t) tab_bars;
- // Input state
- gs_vec2 mouse_pos;
- gs_vec2 last_mouse_pos;
- gs_vec2 mouse_delta;
- gs_vec2 scroll_delta;
- int16_t mouse_down;
- int16_t mouse_pressed;
- int16_t key_down;
- int16_t key_pressed;
- char input_text[32];
- // Backend resources
- uint32_t window_hndl;
- gs_immediate_draw_t gsi;
- gs_immediate_draw_t overlay_draw_list;
- // Active Transitions
- gs_hash_table(gs_gui_id, gs_gui_animation_t) animations;
- // Callbacks
- struct {
- gs_gui_on_draw_button_callback button;
- } callbacks;
- } gs_gui_context_pruned_t;
- typedef struct {
- const char* key;
- gs_asset_font_t* font;
- } gs_gui_font_desc_t;
- typedef struct {
- gs_gui_font_desc_t* fonts;
- size_t size;
- } gs_gui_font_stash_desc_t;
- typedef struct
- {
- const char* id; // Id selector
- const char* classes[GS_GUI_CLS_SELECTOR_MAX]; // Class selectors
- } gs_gui_selector_desc_t;
- enum
- {
- GS_GUI_HINT_FLAG_NO_SCALE_BIAS_MOUSE = (1 << 0),
- GS_GUI_HINT_FLAG_NO_INVERT_Y = (1 << 1)
- };
- typedef struct gs_gui_hints_s
- {
- gs_vec2 framebuffer_size; // Overall framebuffer size
- gs_gui_rect_t viewport; // Viewport within framebuffer for gui context
- int32_t flags; // Flags for hints
- } gs_gui_hints_t;
- GS_API_DECL gs_gui_rect_t gs_gui_rect(float x, float y, float w, float h);
- //=== Context ===//
- GS_API_DECL gs_gui_context_t gs_gui_new(uint32_t window_hndl);
- GS_API_DECL void gs_gui_init(gs_gui_context_t *ctx, uint32_t window_hndl);
- GS_API_DECL void gs_gui_init_font_stash(gs_gui_context_t *ctx, gs_gui_font_stash_desc_t* desc);
- GS_API_DECL gs_gui_context_t gs_gui_context_new(uint32_t window_hndl);
- GS_API_DECL void gs_gui_free(gs_gui_context_t* ctx);
- GS_API_DECL void gs_gui_begin(gs_gui_context_t *ctx, const gs_gui_hints_t* hints);
- GS_API_DECL void gs_gui_end(gs_gui_context_t *ctx);
- GS_API_DECL void gs_gui_render(gs_gui_context_t* ctx, gs_command_buffer_t* cb);
- //=== Util ===//
- GS_API_DECL void gs_gui_renderpass_submit(gs_gui_context_t* ctx, gs_command_buffer_t* cb, gs_color_t clear);
- GS_API_DECL void gs_gui_renderpass_submit_ex(gs_gui_context_t* ctx, gs_command_buffer_t* cb, gs_graphics_clear_action_t* action);
- GS_API_DECL void gs_gui_parse_id_tag(gs_gui_context_t* ctx, const char* str, char* buffer, size_t sz, uint64_t opt);
- GS_API_DECL void gs_gui_parse_label_tag(gs_gui_context_t* ctx, const char* str, char* buffer, size_t sz);
- //=== Main API ===//
- GS_API_DECL void gs_gui_set_focus(gs_gui_context_t *ctx, gs_gui_id id);
- GS_API_DECL void gs_gui_set_hover(gs_gui_context_t *ctx, gs_gui_id id);
- GS_API_DECL gs_gui_id gs_gui_get_id(gs_gui_context_t *ctx, const void *data, int32_t size);
- GS_API_DECL gs_gui_id gs_gui_push_id(gs_gui_context_t *ctx, const void *data, int32_t size);
- GS_API_DECL void gs_gui_pop_id(gs_gui_context_t *ctx);
- GS_API_DECL void gs_gui_push_clip_rect(gs_gui_context_t *ctx, gs_gui_rect_t rect);
- GS_API_DECL void gs_gui_pop_clip_rect(gs_gui_context_t *ctx);
- GS_API_DECL gs_gui_rect_t gs_gui_get_clip_rect(gs_gui_context_t *ctx);
- GS_API_DECL int32_t gs_gui_check_clip(gs_gui_context_t *ctx, gs_gui_rect_t r);
- GS_API_DECL int32_t gs_gui_mouse_over(gs_gui_context_t* ctx, gs_gui_rect_t rect);
- GS_API_DECL void gs_gui_update_control(gs_gui_context_t* ctx, gs_gui_id id, gs_gui_rect_t rect, uint64_t opt);
- //=== Conatiner ===//
- GS_API_DECL gs_gui_container_t* gs_gui_get_current_container(gs_gui_context_t *ctx);
- GS_API_DECL gs_gui_container_t* gs_gui_get_container(gs_gui_context_t *ctx, const char *name);
- GS_API_DECL gs_gui_container_t* gs_gui_get_top_most_container(gs_gui_context_t* ctx, gs_gui_split_t* split);
- GS_API_DECL gs_gui_container_t* gs_gui_get_container_ex(gs_gui_context_t *ctx, gs_gui_id id, uint64_t opt);
- GS_API_DECL void gs_gui_bring_to_front(gs_gui_context_t *ctx, gs_gui_container_t *cnt);
- GS_API_DECL void gs_gui_bring_split_to_front(gs_gui_context_t* ctx, gs_gui_split_t* split);
- GS_API_DECL gs_gui_split_t* gs_gui_get_split(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
- GS_API_DECL gs_gui_tab_bar_t* gs_gui_get_tab_bar(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
- GS_API_DECL void gs_gui_tab_item_swap(gs_gui_context_t* ctx, gs_gui_container_t* cnt, int32_t direction);
- GS_API_DECL gs_gui_container_t* gs_gui_get_root_container(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
- GS_API_DECL gs_gui_container_t* gs_gui_get_root_container_from_split(gs_gui_context_t* ctx, gs_gui_split_t* split);
- GS_API_DECL gs_gui_container_t* gs_gui_get_parent(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
- GS_API_DECL void gs_gui_current_container_close(gs_gui_context_t* ctx);
- //=== Animation ===//
- GS_API_DECL gs_gui_animation_t* gs_gui_get_animation(gs_gui_context_t* ctx, gs_gui_id id,
- const gs_gui_selector_desc_t* desc, int32_t elementid);
- GS_API_DECL gs_gui_style_t gs_gui_animation_get_blend_style(gs_gui_context_t* ctx, gs_gui_animation_t* anim,
- const gs_gui_selector_desc_t* desc, int32_t elementid);
- //=== Style Sheet ===//
- GS_API_DECL gs_gui_style_sheet_t gs_gui_style_sheet_create(gs_gui_context_t* ctx, gs_gui_style_sheet_desc_t* desc);
- GS_API_DECL void gs_gui_style_sheet_destroy(gs_gui_style_sheet_t* ss);
- GS_API_DECL void gs_gui_set_element_style(gs_gui_context_t* ctx, gs_gui_element_type element, gs_gui_element_state state, gs_gui_style_element_t* style, size_t size);
- GS_API_DECL void gs_gui_style_sheet_set_element_styles(gs_gui_style_sheet_t* style_sheet, gs_gui_element_type element, gs_gui_element_state state, gs_gui_style_element_t* styles, size_t size);
- GS_API_DECL void gs_gui_set_style_sheet(gs_gui_context_t* ctx, gs_gui_style_sheet_t* style_sheet);
- GS_API_DECL void gs_gui_push_inline_style(gs_gui_context_t* ctx, gs_gui_element_type elementid, gs_gui_inline_style_desc_t* desc);
- GS_API_DECL void gs_gui_pop_inline_style(gs_gui_context_t* ctx, gs_gui_element_type elementid);
- //=== Resource Loading ===//
- GS_API_DECL gs_gui_style_sheet_t gs_gui_style_sheet_load_from_file(gs_gui_context_t* ctx, const char* file_path);
- GS_API_DECL gs_gui_style_sheet_t gs_gui_style_sheet_load_from_memory(gs_gui_context_t* ctx, const char* memory, size_t sz, bool* success);
- //=== Pools ===//
- GS_API_DECL int32_t gs_gui_pool_init(gs_gui_context_t *ctx, gs_gui_pool_item_t *items, int32_t len, gs_gui_id id);
- GS_API_DECL int32_t gs_gui_pool_get(gs_gui_context_t *ctx, gs_gui_pool_item_t *items, int32_t len, gs_gui_id id);
- GS_API_DECL void gs_gui_pool_update(gs_gui_context_t *ctx, gs_gui_pool_item_t *items, int32_t idx);
- //=== Input ===//
- GS_API_DECL void gs_gui_input_mousemove(gs_gui_context_t *ctx, int32_t x, int32_t y);
- GS_API_DECL void gs_gui_input_mousedown(gs_gui_context_t *ctx, int32_t x, int32_t y, int32_t btn);
- GS_API_DECL void gs_gui_input_mouseup(gs_gui_context_t *ctx, int32_t x, int32_t y, int32_t btn);
- GS_API_DECL void gs_gui_input_scroll(gs_gui_context_t *ctx, int32_t x, int32_t y);
- GS_API_DECL void gs_gui_input_keydown(gs_gui_context_t *ctx, int32_t key);
- GS_API_DECL void gs_gui_input_keyup(gs_gui_context_t *ctx, int32_t key);
- GS_API_DECL void gs_gui_input_text(gs_gui_context_t *ctx, const char *text);
- //=== Commands ===//
- GS_API_DECL gs_gui_command_t* gs_gui_push_command(gs_gui_context_t* ctx, int32_t type, int32_t size);
- GS_API_DECL int32_t gs_gui_next_command(gs_gui_context_t* ctx, gs_gui_command_t** cmd);
- GS_API_DECL void gs_gui_set_clip(gs_gui_context_t* ctx, gs_gui_rect_t rect);
- GS_API_DECL void gs_gui_set_pipeline(gs_gui_context_t* ctx, gs_handle(gs_graphics_pipeline_t) pip,
- void* layout, size_t layout_sz, gsi_layout_type layout_type);
- GS_API_DECL void gs_gui_bind_uniforms(gs_gui_context_t* ctx, gs_graphics_bind_uniform_desc_t* uniforms,
- size_t uniforms_sz);
- //=== Drawing ===//
- GS_API_DECL void gs_gui_draw_rect(gs_gui_context_t* ctx, gs_gui_rect_t rect, gs_color_t color);
- GS_API_DECL void gs_gui_draw_circle(gs_gui_context_t* ctx, gs_vec2 position, float radius, gs_color_t color);
- GS_API_DECL void gs_gui_draw_triangle(gs_gui_context_t* ctx, gs_vec2 a, gs_vec2 b, gs_vec2 c, gs_color_t color);
- GS_API_DECL void gs_gui_draw_box(gs_gui_context_t* ctx, gs_gui_rect_t rect, int16_t* width, gs_color_t color);
- GS_API_DECL void gs_gui_draw_line(gs_gui_context_t* ctx, gs_vec2 start, gs_vec2 end, gs_color_t color);
- GS_API_DECL void gs_gui_draw_text(gs_gui_context_t* ctx, gs_asset_font_t* font, const char *str, int32_t len, gs_vec2 pos, gs_color_t color, int32_t shadow_x, int32_t shadow_y, gs_color_t shadow_color);
- GS_API_DECL void gs_gui_draw_image(gs_gui_context_t* ctx, gs_handle(gs_graphics_texture_t) hndl, gs_gui_rect_t rect, gs_vec2 uv0, gs_vec2 uv1, gs_color_t color);
- GS_API_DECL void gs_gui_draw_nine_rect(gs_gui_context_t* ctx, gs_handle(gs_graphics_texture_t) hndl, gs_gui_rect_t rect, gs_vec2 uv0, gs_vec2 uv1, uint32_t left, uint32_t right, uint32_t top, uint32_t bottom, gs_color_t color);
- GS_API_DECL void gs_gui_draw_control_frame(gs_gui_context_t* ctx, gs_gui_id id, gs_gui_rect_t rect, int32_t elementid, uint64_t opt);
- GS_API_DECL void gs_gui_draw_control_text(gs_gui_context_t* ctx, const char *str, gs_gui_rect_t rect, const gs_gui_style_t* style, uint64_t opt);
- GS_API_DECL void gs_gui_draw_custom(gs_gui_context_t* ctx, gs_gui_rect_t rect, gs_gui_draw_callback_t cb, void* data, size_t sz);
- //=== Layout ===//
- GS_API_DECL gs_gui_layout_t* gs_gui_get_layout(gs_gui_context_t* ctx);
- GS_API_DECL void gs_gui_layout_row(gs_gui_context_t *ctx, int32_t items, const int32_t *widths, int32_t height);
- GS_API_DECL void gs_gui_layout_row_ex(gs_gui_context_t *ctx, int32_t items, const int32_t *widths, int32_t height, int32_t justification);
- GS_API_DECL void gs_gui_layout_width(gs_gui_context_t *ctx, int32_t width);
- GS_API_DECL void gs_gui_layout_height(gs_gui_context_t *ctx, int32_t height);
- GS_API_DECL void gs_gui_layout_column_begin(gs_gui_context_t *ctx);
- GS_API_DECL void gs_gui_layout_column_end(gs_gui_context_t *ctx);
- GS_API_DECL void gs_gui_layout_set_next(gs_gui_context_t *ctx, gs_gui_rect_t r, int32_t relative);
- GS_API_DECL gs_gui_rect_t gs_gui_layout_peek_next(gs_gui_context_t *ctx);
- GS_API_DECL gs_gui_rect_t gs_gui_layout_next(gs_gui_context_t *ctx);
- GS_API_DECL gs_gui_rect_t gs_gui_layout_anchor(const gs_gui_rect_t* parent, int32_t width, int32_t height, int32_t xoff, int32_t yoff, gs_gui_layout_anchor_type type);
- //=== Elements ===//
- #define gs_gui_button(_CTX, _LABEL) gs_gui_button_ex((_CTX), (_LABEL), NULL, GS_GUI_OPT_LEFTCLICKONLY)
- #define gs_gui_text(_CTX, _TXT) gs_gui_text_ex((_CTX), (_TXT), 1, NULL, 0x00)
- #define gs_gui_textbox(_CTX, _BUF, _BUFSZ) gs_gui_textbox_ex((_CTX), (_BUF), (_BUFSZ), NULL, 0x00)
- #define gs_gui_slider(_CTX, _VALUE, _LO, _HI) gs_gui_slider_ex((_CTX), (_VALUE), (_LO), (_HI), 0, GS_GUI_SLIDER_FMT, NULL, 0x00)
- #define gs_gui_number(_CTX, _VALUE, _STEP) gs_gui_number_ex((_CTX), (_VALUE), (_STEP), GS_GUI_SLIDER_FMT, NULL, 0x00)
- #define gs_gui_header(_CTX, _LABEL) gs_gui_header_ex((_CTX), (_LABEL), NULL, 0x00)
- #define gs_gui_checkbox(_CTX, _LABEL, _STATE) gs_gui_checkbox_ex((_CTX), (_LABEL), (_STATE), NULL, GS_GUI_OPT_LEFTCLICKONLY)
- #define gs_gui_treenode_begin(_CTX, _LABEL) gs_gui_treenode_begin_ex((_CTX), (_LABEL), NULL, 0x00)
- #define gs_gui_window_begin(_CTX, _TITLE, _RECT) gs_gui_window_begin_ex((_CTX), (_TITLE), (_RECT), 0, NULL, 0x00)
- #define gs_gui_popup_begin(_CTX, _TITLE, _RECT) gs_gui_popup_begin_ex((_CTX), (_TITLE), (_RECT), NULL, 0x00)
- #define gs_gui_panel_begin(_CTX, _NAME) gs_gui_panel_begin_ex((_CTX), (_NAME), NULL, 0x00)
- #define gs_gui_image(_CTX, _HNDL) gs_gui_image_ex((_CTX), (_HNDL), gs_v2s(0.f), gs_v2s(1.f), NULL, 0x00)
- #define gs_gui_combo_begin(_CTX, _ID, _ITEM, _MAX) gs_gui_combo_begin_ex((_CTX), (_ID), (_ITEM), (_MAX), NULL, 0x00)
- #define gs_gui_combo_item(_CTX, _NAME) gs_gui_combo_item_ex((_CTX), (_NAME), NULL, 0x00)
- #define gs_gui_dock(_CTX, _DST, _SRC, _TYPE) gs_gui_dock_ex((_CTX), (_DST), (_SRC), (_TYPE), 0.5f)
- #define gs_gui_undock(_CTX, _NAME) gs_gui_undock_ex((_CTX), (_NAME))
- #define gs_gui_label(_CTX, _FMT, ...)\
- (\
- gs_snprintf((_CTX)->number_edit_buf, sizeof((_CTX)->number_edit_buf), _FMT, ## __VA_ARGS__),\
- gs_gui_label_ex((_CTX), (_CTX)->number_edit_buf, NULL, 0x00)\
- )
- //=== Elements (Extended) ===//
- GS_API_DECL int32_t gs_gui_image_ex(gs_gui_context_t* ctx, gs_handle(gs_graphics_texture_t) hndl, gs_vec2 uv0, gs_vec2 uv1, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_text_ex(gs_gui_context_t* ctx, const char* text, int32_t text_wrap, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_label_ex(gs_gui_context_t* ctx, const char* text, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_button_ex(gs_gui_context_t* ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_checkbox_ex(gs_gui_context_t* ctx, const char* label, int32_t* state, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_textbox_raw(gs_gui_context_t* ctx, char* buf, int32_t bufsz, gs_gui_id id, gs_gui_rect_t r, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_textbox_ex(gs_gui_context_t* ctx, char* buf, int32_t bufsz, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_slider_ex(gs_gui_context_t* ctx, gs_gui_real* value, gs_gui_real low, gs_gui_real high, gs_gui_real step,
- const char* fmt, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_number_ex(gs_gui_context_t* ctx, gs_gui_real* value, gs_gui_real step, const char* fmt, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_header_ex(gs_gui_context_t* ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL int32_t gs_gui_treenode_begin_ex(gs_gui_context_t * ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL void gs_gui_treenode_end(gs_gui_context_t* ctx);
- GS_API_DECL int32_t gs_gui_window_begin_ex(gs_gui_context_t * ctx, const char* title, gs_gui_rect_t rect, bool* open, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL void gs_gui_window_end(gs_gui_context_t* ctx);
- GS_API_DECL void gs_gui_popup_open(gs_gui_context_t* ctx, const char* name);
- GS_API_DECL int32_t gs_gui_popup_begin_ex(gs_gui_context_t* ctx, const char* name, gs_gui_rect_t r, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL void gs_gui_popup_end(gs_gui_context_t* ctx);
- GS_API_DECL void gs_gui_panel_begin_ex(gs_gui_context_t* ctx, const char* name, const gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL void gs_gui_panel_end(gs_gui_context_t* ctx);
- GS_API_DECL int32_t gs_gui_combo_begin_ex(gs_gui_context_t* ctx, const char* id, const char* current_item, int32_t max_items, gs_gui_selector_desc_t* desc, uint64_t opt);
- GS_API_DECL void gs_gui_combo_end(gs_gui_context_t* ctx);
- GS_API_DECL int32_t gs_gui_combo_item_ex(gs_gui_context_t* ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt);
- //=== Demos ===//
- GS_API_DECL int32_t gs_gui_style_editor(gs_gui_context_t* ctx, gs_gui_style_sheet_t* style_sheet, gs_gui_rect_t rect, bool* open);
- GS_API_DECL int32_t gs_gui_demo_window(gs_gui_context_t* ctx, gs_gui_rect_t rect, bool* open);
- //=== Docking ===//
- GS_API_DECL void gs_gui_dock_ex(gs_gui_context_t* ctx, const char* dst, const char* src, int32_t split_type, float ratio);
- GS_API_DECL void gs_gui_undock_ex(gs_gui_context_t* ctx, const char* name);
- GS_API_DECL void gs_gui_dock_ex_cnt(gs_gui_context_t* ctx, gs_gui_container_t* dst, gs_gui_container_t* src, int32_t split_type, float ratio);
- GS_API_DECL void gs_gui_undock_ex_cnt(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
- //=== Gizmo ===//
- GS_API_DECL int32_t gs_gui_gizmo(gs_gui_context_t* ctx, gs_camera_t* camera, gs_vqs* model, gs_gui_rect_t viewport,
- bool invert_view_y, float snap, int32_t op, int32_t mode, uint64_t opt);
- //=== Implementation ===//
- #ifdef GS_GUI_IMPL
- #ifndef GS_PHYSICS_IMPL
- #define GS_PHYSICS_IMPL
- #include "gs_physics.h"
- #endif
- #define gs_gui_unused(x) ((void) (x))
- #define gs_gui_expect(x)\
- do { \
- if (!(x)) { \
- gs_log_error("Fatal error: assertion '%s' failed\n", \
- #x); \
- abort(); \
- } \
- } while (0)
- #define gs_gui_stack_push(stk, val)\
- do { \
- gs_gui_expect((stk).idx < (int32_t) (sizeof((stk).items) / sizeof(*(stk).items))); \
- (stk).items[(stk).idx] = (val); \
- (stk).idx++; /* incremented after incase `val` uses this value */ \
- } while (0)
- #define gs_gui_stack_pop(stk)\
- do { \
- gs_gui_expect((stk).idx > 0); \
- (stk).idx--; \
- } while (0)
- /* 32bit fnv-1a hash */
- #define GS_GUI_HASH_INITIAL 2166136261
- static void gs_gui_hash(gs_gui_id *hash, const void *data, int32_t size)
- {
- const unsigned char *p = (const unsigned char*)data;
- while (size--)
- {
- *hash = (*hash ^ *p++) * 16777619;
- }
- }
- static gs_gui_rect_t gs_gui_unclipped_rect = { 0, 0, 0x1000000, 0x1000000 };
- // Default styles
- static gs_gui_style_t gs_gui_default_container_style[3] = gs_default_val();
- static gs_gui_style_t gs_gui_default_button_style[3] = gs_default_val();
- static gs_gui_style_t gs_gui_default_text_style[3] = gs_default_val();
- static gs_gui_style_t gs_gui_default_label_style[3] = gs_default_val();
- static gs_gui_style_t gs_gui_default_panel_style[3] = gs_default_val();
- static gs_gui_style_t gs_gui_default_input_style[3] = gs_default_val();
- static gs_gui_style_t gs_gui_default_scroll_style[3] = gs_default_val();
- static gs_gui_style_t gs_gui_default_image_style[3] = gs_default_val();
- static gs_gui_style_t gs_gui_default_style =
- {
- // font | size | spacing | indent | title_height | scroll_width | thumb_width
- NULL, {68, 18}, 2, 10, 20, 5, 5,
- // colors
- {
- {25, 25, 25, 255}, // GS_GUI_COLOR_BACKGROUND
- {255, 255, 255, 255}, // GS_GUI_COLOR_CONTENT
- {29, 29, 29, 76}, // GS_GUI_COLOR_BORDER
- {0, 0, 0, 31}, // GS_GUI_COLOR_SHADOW
- {0, 0, 0, 0}, // GS_GUI_COLOR_CONTENT_BACKGROUND
- {0, 0, 0, 0}, // GS_GUI_COLOR_CONTENT_SHADOW
- {0, 0, 0, 0} // GS_GUI_COLOR_CONTENT_BORDER
- },
- // padding (left, right, top, bottom)
- {2, 2, 2, 2},
- // margin (left, right, top, bottom)
- {2, 2, 2, 2},
- // border width (left, right, top, bottom)
- {1, 1, 1, 1},
- // border radius (left, right, top, bottom)
- {0, 0, 0, 0},
- // flex direction / justification / alignment / shrink / grow
- GS_GUI_DIRECTION_COLUMN,
- GS_GUI_JUSTIFY_START,
- GS_GUI_ALIGN_CENTER,
- // shadow x, y
- 1, 1
- };
- static gs_gui_style_sheet_t gs_gui_default_style_sheet = gs_default_val();
- static gs_gui_style_t gs_gui_get_current_element_style(gs_gui_context_t* ctx, const gs_gui_selector_desc_t* desc,
- int32_t elementid, int32_t state)
- {
-
- #define GS_GUI_APPLY_STYLE(SE)\
- do {\
- switch ((SE)->type)\
- {\
- case GS_GUI_STYLE_WIDTH: style.size[0] = (float)(SE)->value; break;\
- case GS_GUI_STYLE_HEIGHT: style.size[1] = (float)(SE)->value; break;\
- \
- case GS_GUI_STYLE_PADDING: {\
- style.padding[GS_GUI_PADDING_LEFT] = (int32_t)(SE)->value;\
- style.padding[GS_GUI_PADDING_TOP] = (int32_t)(SE)->value;\
- style.padding[GS_GUI_PADDING_RIGHT] = (int32_t)(SE)->value;\
- style.padding[GS_GUI_PADDING_BOTTOM] = (int32_t)(SE)->value;\
- }\
- \
- case GS_GUI_STYLE_PADDING_LEFT: style.padding[GS_GUI_PADDING_LEFT] = (int32_t)(SE)->value; break;\
- case GS_GUI_STYLE_PADDING_TOP: style.padding[GS_GUI_PADDING_TOP] = (int32_t)(SE)->value; break;\
- case GS_GUI_STYLE_PADDING_RIGHT: style.padding[GS_GUI_PADDING_RIGHT] = (int32_t)(SE)->value; break;\
- case GS_GUI_STYLE_PADDING_BOTTOM: style.padding[GS_GUI_PADDING_BOTTOM] = (int32_t)(SE)->value; break;\
- \
- case GS_GUI_STYLE_MARGIN: {\
- style.margin[GS_GUI_MARGIN_LEFT] = (int32_t)(SE)->value;\
- style.margin[GS_GUI_MARGIN_TOP] = (int32_t)(SE)->value;\
- style.margin[GS_GUI_MARGIN_RIGHT] = (int32_t)(SE)->value;\
- style.margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)(SE)->value;\
- } break;\
- \
- case GS_GUI_STYLE_MARGIN_LEFT: style.margin[GS_GUI_MARGIN_LEFT] = (int32_t)(SE)->value; break;\
- case GS_GUI_STYLE_MARGIN_TOP: style.margin[GS_GUI_MARGIN_TOP] = (int32_t)(SE)->value; break;\
- case GS_GUI_STYLE_MARGIN_RIGHT: style.margin[GS_GUI_MARGIN_RIGHT] = (int32_t)(SE)->value; break;\
- case GS_GUI_STYLE_MARGIN_BOTTOM: style.margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)(SE)->value; break;\
- \
- case GS_GUI_STYLE_BORDER_RADIUS: {\
- style.border_radius[0] = (SE)->value;\
- style.border_radius[1] = (SE)->value;\
- style.border_radius[2] = (SE)->value;\
- style.border_radius[3] = (SE)->value;\
- } break;\
- \
- case GS_GUI_STYLE_BORDER_RADIUS_LEFT: style.border_radius[0] = (SE)->value; break;\
- case GS_GUI_STYLE_BORDER_RADIUS_RIGHT: style.border_radius[1] = (SE)->value; break;\
- case GS_GUI_STYLE_BORDER_RADIUS_TOP: style.border_radius[2] = (SE)->value; break;\
- case GS_GUI_STYLE_BORDER_RADIUS_BOTTOM: style.border_radius[3] = (SE)->value; break;\
- \
- case GS_GUI_STYLE_BORDER_WIDTH: {\
- style.border_width[0] = (SE)->value;\
- style.border_width[1] = (SE)->value;\
- style.border_width[2] = (SE)->value;\
- style.border_width[3] = (SE)->value;\
- } break;\
- \
- case GS_GUI_STYLE_BORDER_WIDTH_LEFT: style.border_width[0] = (SE)->value; break;\
- case GS_GUI_STYLE_BORDER_WIDTH_RIGHT: style.border_width[1] = (SE)->value; break;\
- case GS_GUI_STYLE_BORDER_WIDTH_TOP: style.border_width[2] = (SE)->value; break;\
- case GS_GUI_STYLE_BORDER_WIDTH_BOTTOM: style.border_width[3] = (SE)->value; break;\
- \
- case GS_GUI_STYLE_DIRECTION: style.direction = (int32_t)(SE)->value; break;\
- case GS_GUI_STYLE_ALIGN_CONTENT: style.align_content = (int32_t)(SE)->value; break;\
- case GS_GUI_STYLE_JUSTIFY_CONTENT: style.justify_content = (int32_t)(SE)->value; break;\
- \
- case GS_GUI_STYLE_SHADOW_X: style.shadow_x = (int32_t)(SE)->value; break;\
- case GS_GUI_STYLE_SHADOW_Y: style.shadow_y = (int32_t)(SE)->value; break;\
- \
- case GS_GUI_STYLE_COLOR_BACKGROUND: style.colors[GS_GUI_COLOR_BACKGROUND] = (SE)->color; break;\
- case GS_GUI_STYLE_COLOR_BORDER: style.colors[GS_GUI_COLOR_BORDER] = (SE)->color; break;\
- case GS_GUI_STYLE_COLOR_SHADOW: style.colors[GS_GUI_COLOR_SHADOW] = (SE)->color; break;\
- case GS_GUI_STYLE_COLOR_CONTENT: style.colors[GS_GUI_COLOR_CONTENT] = (SE)->color; break;\
- case GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND: style.colors[GS_GUI_COLOR_CONTENT_BACKGROUND] = (SE)->color; break;\
- case GS_GUI_STYLE_COLOR_CONTENT_BORDER: style.colors[GS_GUI_COLOR_CONTENT_BORDER] = (SE)->color; break;\
- case GS_GUI_STYLE_COLOR_CONTENT_SHADOW: style.colors[GS_GUI_COLOR_CONTENT_SHADOW] = (SE)->color; break;\
- \
- case GS_GUI_STYLE_FONT: style.font = (SE)->font; break;\
- }\
- } while (0)
- gs_gui_style_t style = ctx->style_sheet->styles[elementid][state];
- // Look for id tag style
- gs_gui_style_list_t* id_styles = NULL;
- gs_gui_style_list_t* cls_styles[GS_GUI_CLS_SELECTOR_MAX] = gs_default_val();
- if (desc)
- {
- char TMP[256] = gs_default_val();
- // ID selector
- gs_snprintf(TMP, sizeof(TMP), "#%s", desc->id);
- const uint64_t id_hash = gs_hash_str64(TMP);
- id_styles = gs_hash_table_exists(ctx->style_sheet->cid_styles, id_hash) ?
- gs_hash_table_getp(ctx->style_sheet->cid_styles, id_hash) : NULL;
- // Class selectors
- for (uint32_t i = 0; i < GS_GUI_CLS_SELECTOR_MAX; ++i)
- {
- if (desc->classes[i]) {
- gs_snprintf(TMP, sizeof(TMP), ".%s", desc->classes[i]);
- const uint64_t cls_hash = gs_hash_str64(TMP);
- cls_styles[i] = gs_hash_table_exists(ctx->style_sheet->cid_styles, cls_hash) ?
- gs_hash_table_getp(ctx->style_sheet->cid_styles, cls_hash) : NULL;
- }
- else break;
- }
- }
- // Override with class styles
- if (*cls_styles)
- {
- for (uint32_t i = 0; i < GS_GUI_CLS_SELECTOR_MAX; ++i)
- {
- if (!cls_styles[i]) break;
- for (uint32_t s = 0; s < gs_dyn_array_size(cls_styles[i]->styles[state]); ++s) {
- gs_gui_style_element_t* se = &cls_styles[i]->styles[state][s];
- GS_GUI_APPLY_STYLE(se);
- }
- }
- }
- // Override with id styles
- if (id_styles)
- {
- for (uint32_t i = 0; i < gs_dyn_array_size(id_styles->styles[state]); ++i) {
- gs_gui_style_element_t* se = &id_styles->styles[state][i];
- GS_GUI_APPLY_STYLE(se);
- }
- }
- if (gs_hash_table_exists(ctx->inline_styles, (gs_gui_element_type)elementid))
- {
- gs_gui_inline_style_stack_t* iss = gs_hash_table_getp(ctx->inline_styles,
- (gs_gui_element_type)elementid);
- if (gs_dyn_array_size(iss->styles[state]))
- {
- // Get last size to apply for styles for this state
- const uint32_t scz = gs_dyn_array_size(iss->style_counts);
- const uint32_t ct = state == 0x00 ? iss->style_counts[scz - 3] :
- state == 0x01 ? iss->style_counts[scz - 2] :
- iss->style_counts[scz - 1];
- const uint32_t ssz = gs_dyn_array_size(iss->styles[state]);
- for (uint32_t i = 0; i < ct; ++i) {
- uint32_t idx = (ssz - ct + i);
- gs_gui_style_element_t* se = &iss->styles[state][idx];
- GS_GUI_APPLY_STYLE(se);
- }
- }
- }
- return style;
- }
- GS_API_DECL gs_gui_style_t gs_gui_animation_get_blend_style(gs_gui_context_t* ctx, gs_gui_animation_t* anim,
- const gs_gui_selector_desc_t* desc, int32_t elementid)
- {
- gs_gui_style_t ret = gs_default_val();
- int32_t focus_state = anim->focus_state;
- int32_t hover_state = anim->hover_state;
- gs_gui_style_t s0 = gs_gui_get_current_element_style(ctx, desc, elementid, anim->start_state);
- gs_gui_style_t s1 = gs_gui_get_current_element_style(ctx, desc, elementid, anim->end_state);
- gs_gui_inline_style_stack_t* iss = NULL;
- if (gs_hash_table_exists(ctx->inline_styles, (gs_gui_element_type)elementid)) {
- iss = gs_hash_table_getp(ctx->inline_styles, (gs_gui_element_type)elementid);
- }
- if (anim->direction == GS_GUI_ANIMATION_DIRECTION_FORWARD) {ret = s1;}
- else {ret = s0;}
- const gs_gui_animation_property_list_t* list = NULL;
- if (gs_hash_table_exists(ctx->style_sheet->animations, (gs_gui_element_type)elementid)) {
- list = gs_hash_table_getp(ctx->style_sheet->animations, (gs_gui_element_type)elementid);
- }
- const gs_gui_animation_property_list_t* id_list = NULL;
- const gs_gui_animation_property_list_t* cls_list[GS_GUI_CLS_SELECTOR_MAX] = gs_default_val();
- bool has_class_animations = false;
- if (desc)
- {
- char TMP[256] = gs_default_val();
- // ID animations
- if (desc->id)
- {
- gs_snprintf(TMP, sizeof(TMP), "#%s", desc->id);
- const uint64_t id_hash = gs_hash_str64(TMP);
- if (gs_hash_table_exists(ctx->style_sheet->cid_animations, id_hash)) {
- id_list = gs_hash_table_getp(ctx->style_sheet->cid_animations, id_hash);
- }
- }
- // Class animations
- if (*desc->classes)
- {
- for (uint32_t i = 0; i < GS_GUI_CLS_SELECTOR_MAX; ++i)
- {
- if (!desc->classes[i]) break;
- gs_snprintf(TMP, sizeof(TMP), ".%s", desc->classes[i]);
- const uint64_t cls_hash = gs_hash_str64(TMP);
- if (cls_hash && gs_hash_table_exists(ctx->style_sheet->cid_animations, cls_hash)) {
- cls_list[i] = gs_hash_table_getp(ctx->style_sheet->cid_animations, cls_hash);
- has_class_animations = true;
- }
- }
- }
- }
- #define GS_GUI_BLEND_COLOR(TYPE)\
- do {\
- gs_color_t* c0 = &s0.colors[TYPE];\
- gs_color_t* c1 = &s1.colors[TYPE];\
- float r = 255.f * gs_interp_smoothstep((float)c0->r / 255.f, (float)c1->r / 255.f, t);\
- float g = 255.f * gs_interp_smoothstep((float)c0->g / 255.f, (float)c1->g / 255.f, t);\
- float b = 255.f * gs_interp_smoothstep((float)c0->b / 255.f, (float)c1->b / 255.f, t);\
- float a = 255.f * gs_interp_smoothstep((float)c0->a / 255.f, (float)c1->a / 255.f, t);\
- ret.colors[TYPE] = gs_color((u8)r, (u8)g, (u8)b, (u8)a);\
- } while (0)
- #define GS_GUI_BLEND_VALUE(FIELD, TYPE)\
- do {\
- float v0 = (float)s0.FIELD;\
- float v1 = (float)s1.FIELD;\
- ret.FIELD = (TYPE)gs_interp_smoothstep(v0, v1, t);\
- } while (0)
- #define GS_GUI_BLEND_PROPERTIES(LIST)\
- do {\
- for (uint32_t i = 0; i < gs_dyn_array_size(LIST); ++i)\
- {\
- const gs_gui_animation_property_t* prop = &LIST[i];\
- float t = 0.f;\
- switch (anim->direction)\
- {\
- default:\
- case GS_GUI_ANIMATION_DIRECTION_FORWARD:\
- {\
- t = gs_clamp(gs_map_range((float)prop->delay, (float)prop->time + (float)prop->delay, 0.f, 1.f, (float)anim->time), 0.f, 1.f);\
- } break;\
- case GS_GUI_ANIMATION_DIRECTION_BACKWARD:\
- {\
- if (prop->time <= 0.f)\
- t = 1.f;\
- else\
- t = gs_clamp(gs_map_range((float)0.f, (float)anim->max - (float)prop->delay, 0.f, 1.f, (float)anim->time), 0.f, 1.f);\
- } break;\
- }\
- \
- switch (prop->type)\
- {\
- case GS_GUI_STYLE_COLOR_BACKGROUND: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_BACKGROUND);} break;\
- case GS_GUI_STYLE_COLOR_SHADOW: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_SHADOW);} break;\
- case GS_GUI_STYLE_COLOR_BORDER: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_BORDER);} break;\
- case GS_GUI_STYLE_COLOR_CONTENT: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_CONTENT);} break;\
- case GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_CONTENT_BACKGROUND);} break;\
- case GS_GUI_STYLE_COLOR_CONTENT_SHADOW: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_CONTENT_SHADOW);} break;\
- case GS_GUI_STYLE_COLOR_CONTENT_BORDER: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_CONTENT_BORDER);} break;\
- case GS_GUI_STYLE_WIDTH: {GS_GUI_BLEND_VALUE(size[0], float);} break;\
- case GS_GUI_STYLE_HEIGHT: {GS_GUI_BLEND_VALUE(size[1], float);} break;\
- case GS_GUI_STYLE_BORDER_WIDTH: {\
- GS_GUI_BLEND_VALUE(border_width[0], int16_t);\
- GS_GUI_BLEND_VALUE(border_width[1], int16_t);\
- GS_GUI_BLEND_VALUE(border_width[2], int16_t);\
- GS_GUI_BLEND_VALUE(border_width[3], int16_t);\
- } break;\
- case GS_GUI_STYLE_BORDER_WIDTH_LEFT: {GS_GUI_BLEND_VALUE(border_width[0], int16_t);} break;\
- case GS_GUI_STYLE_BORDER_WIDTH_RIGHT: {GS_GUI_BLEND_VALUE(border_width[1], int16_t);} break;\
- case GS_GUI_STYLE_BORDER_WIDTH_TOP: {GS_GUI_BLEND_VALUE(border_width[2], int16_t);} break;\
- case GS_GUI_STYLE_BORDER_WIDTH_BOTTOM: {GS_GUI_BLEND_VALUE(border_width[3], int16_t);} break;\
- case GS_GUI_STYLE_BORDER_RADIUS: {\
- GS_GUI_BLEND_VALUE(border_radius[0], int16_t);\
- GS_GUI_BLEND_VALUE(border_radius[1], int16_t);\
- GS_GUI_BLEND_VALUE(border_radius[2], int16_t);\
- GS_GUI_BLEND_VALUE(border_radius[3], int16_t);\
- } break;\
- case GS_GUI_STYLE_BORDER_RADIUS_LEFT: {GS_GUI_BLEND_VALUE(border_radius[0], int16_t);} break;\
- case GS_GUI_STYLE_BORDER_RADIUS_RIGHT: {GS_GUI_BLEND_VALUE(border_radius[1], int16_t);} break;\
- case GS_GUI_STYLE_BORDER_RADIUS_TOP: {GS_GUI_BLEND_VALUE(border_radius[2], int16_t);} break;\
- case GS_GUI_STYLE_BORDER_RADIUS_BOTTOM: {GS_GUI_BLEND_VALUE(border_radius[3], int16_t);} break;\
- case GS_GUI_STYLE_MARGIN_BOTTOM: {GS_GUI_BLEND_VALUE(margin[GS_GUI_MARGIN_BOTTOM], int16_t);} break;\
- case GS_GUI_STYLE_MARGIN_TOP: {GS_GUI_BLEND_VALUE(margin[GS_GUI_MARGIN_TOP], int16_t);} break;\
- case GS_GUI_STYLE_MARGIN_LEFT: {GS_GUI_BLEND_VALUE(margin[GS_GUI_MARGIN_LEFT], int16_t);} break;\
- case GS_GUI_STYLE_MARGIN_RIGHT: {GS_GUI_BLEND_VALUE(margin[GS_GUI_MARGIN_RIGHT], int16_t);} break;\
- case GS_GUI_STYLE_MARGIN: {\
- GS_GUI_BLEND_VALUE(margin[0], int16_t);\
- GS_GUI_BLEND_VALUE(margin[1], int16_t);\
- GS_GUI_BLEND_VALUE(margin[2], int16_t);\
- GS_GUI_BLEND_VALUE(margin[3], int16_t);\
- } break;\
- case GS_GUI_STYLE_PADDING_BOTTOM: {GS_GUI_BLEND_VALUE(padding[GS_GUI_PADDING_BOTTOM], int32_t);} break;\
- case GS_GUI_STYLE_PADDING_TOP: {GS_GUI_BLEND_VALUE(padding[GS_GUI_PADDING_TOP], int32_t);} break;\
- case GS_GUI_STYLE_PADDING_LEFT: {GS_GUI_BLEND_VALUE(padding[GS_GUI_PADDING_LEFT], int32_t);} break;\
- case GS_GUI_STYLE_PADDING_RIGHT: {GS_GUI_BLEND_VALUE(padding[GS_GUI_PADDING_RIGHT], int32_t);} break;\
- case GS_GUI_STYLE_PADDING: {\
- GS_GUI_BLEND_VALUE(padding[0], int32_t);\
- GS_GUI_BLEND_VALUE(padding[1], int32_t);\
- GS_GUI_BLEND_VALUE(padding[2], int32_t);\
- GS_GUI_BLEND_VALUE(padding[3], int32_t);\
- } break;\
- case GS_GUI_STYLE_SHADOW_X: {GS_GUI_BLEND_VALUE(shadow_x, int16_t);} break;\
- case GS_GUI_STYLE_SHADOW_Y: {GS_GUI_BLEND_VALUE(shadow_y, int16_t);} break;\
- }\
- }\
- } while (0)
- // Get final blends
- if (list && !gs_dyn_array_empty(list->properties[anim->end_state])) {
- GS_GUI_BLEND_PROPERTIES(list->properties[anim->end_state]);
- }
- // Class list
- if (has_class_animations)
- {
- for (uint32_t c = 0; c < GS_GUI_CLS_SELECTOR_MAX; ++c)
- {
- if (!cls_list[c]) continue;
- if (!gs_dyn_array_empty(cls_list[c]->properties[anim->end_state])) {
- GS_GUI_BLEND_PROPERTIES(cls_list[c]->properties[anim->end_state]);
- }
- }
- }
- // Id list
- if (id_list && !gs_dyn_array_empty(id_list->properties[anim->end_state])) {
- GS_GUI_BLEND_PROPERTIES(id_list->properties[anim->end_state]);
- }
- if (iss) {
- GS_GUI_BLEND_PROPERTIES(iss->animations[anim->end_state]);
- }
- return ret;
- }
- static void _gs_gui_animation_get_time(gs_gui_context_t* ctx, gs_gui_id id, int32_t elementid,
- const gs_gui_selector_desc_t* desc, gs_gui_inline_style_stack_t* iss, int32_t state, gs_gui_animation_t* anim)
- {
- uint32_t act = 0, ssz = 0;
- if (iss && gs_dyn_array_size(iss->animations[state])) {
- const uint32_t scz = gs_dyn_array_size(iss->animation_counts);
- act = state == 0x00 ? iss->animation_counts[scz - 3] :
- state == 0x01 ? iss->animation_counts[scz - 2] :
- iss->animation_counts[scz - 1];
- ssz = gs_dyn_array_size(iss->animations[state]);
- }
- gs_gui_animation_property_list_t* cls_list[GS_GUI_CLS_SELECTOR_MAX] = gs_default_val();
- const gs_gui_animation_property_list_t* id_list = NULL;
- const gs_gui_animation_property_list_t* list = NULL;
- bool has_class_animations = false;
- if (desc)
- {
- char TMP[256] = gs_default_val();
-
- // Id animations
- gs_snprintf(TMP, sizeof(TMP), "#%s", desc->id);
- const uint64_t id_hash = gs_hash_str64(TMP);
- if (gs_hash_table_exists(ctx->style_sheet->cid_animations, id_hash)) {
- id_list = gs_hash_table_getp(ctx->style_sheet->cid_animations, id_hash);
- }
- // Class animations
- for (uint32_t i = 0; i < GS_GUI_CLS_SELECTOR_MAX; ++i)
- {
- if (!desc->classes[i]) break;
- gs_snprintf(TMP, sizeof(TMP), ".%s", desc->classes[i]);
- const uint64_t cls_hash = gs_hash_str64(TMP);
- if (gs_hash_table_exists(ctx->style_sheet->cid_animations, cls_hash)) {
- cls_list[i] = gs_hash_table_getp(ctx->style_sheet->cid_animations, cls_hash);
- has_class_animations = true;
- }
- }
- }
- // Element type animations
- if (gs_hash_table_exists(ctx->style_sheet->animations, (gs_gui_element_type)elementid)) {
- list = gs_hash_table_getp(ctx->style_sheet->animations, (gs_gui_element_type)elementid);
- }
- // Fill properties in order of specificity
- gs_gui_animation_property_t properties[GS_GUI_STYLE_COUNT] = gs_default_val();
- for (uint32_t i = 0; i < GS_GUI_STYLE_COUNT; ++i) {
- properties[i].type = (gs_gui_style_element_type)i;
- }
- #define GUI_SET_PROPERTY_TIMES(PROP_LIST)\
- do {\
- for (uint32_t p = 0; p < gs_dyn_array_size((PROP_LIST)); ++p)\
- {\
- gs_gui_animation_property_t* prop = &(PROP_LIST)[p];\
- properties[prop->type].time = prop->time;\
- properties[prop->type].delay = prop->delay;\
- }\
- } while (0)
- // Element type list
- if (list)
- {
- gs_dyn_array(gs_gui_animation_property_t) props = list->properties[state];
- GUI_SET_PROPERTY_TIMES(props);
- }
- // Class list
- if (has_class_animations)
- {
- for (uint32_t c = 0; c < GS_GUI_CLS_SELECTOR_MAX; ++c)
- {
- if (!cls_list[c]) continue;
- gs_dyn_array(gs_gui_animation_property_t) props = cls_list[c]->properties[state];
- GUI_SET_PROPERTY_TIMES(props);
- }
- }
- // Id list
- if (id_list)
- {
- gs_dyn_array(gs_gui_animation_property_t) props = id_list->properties[state];
- GUI_SET_PROPERTY_TIMES(props);
- }
- // Inline style list
- if (act && iss)
- {
- for ( uint32_t a = 0; a < act; ++a )
- {
- uint32_t idx = ssz - act + a;
- gs_gui_animation_property_t* ap = &iss->animations[state][idx];
- properties[ap->type].time = ap->time;
- properties[ap->type].delay = ap->delay;
- }
- }
- // Set max times
- for (uint32_t i = 0; i < GS_GUI_STYLE_COUNT; ++i)
- {
- if (properties[i].time > anim->max) anim->max = properties[i].time;
- if (properties[i].delay > anim->delay) anim->delay = properties[i].delay;
- }
- // Finalize time
- anim->max += anim->delay;
- anim->max = gs_max(anim->max, 5);
- }
- GS_API_DECL gs_gui_animation_t* gs_gui_get_animation(gs_gui_context_t* ctx, gs_gui_id id, const gs_gui_selector_desc_t* desc, int32_t elementid)
- {
- gs_gui_animation_t* anim = NULL;
- const bool32 valid_eid = (elementid >= 0 && elementid < GS_GUI_ELEMENT_COUNT);
- // Construct new animation if necessary to insert
- if (ctx->state_switch_id == id)
- {
- if (!gs_hash_table_exists(ctx->animations, id))
- {
- gs_gui_animation_t val = gs_default_val();
- gs_hash_table_insert(ctx->animations, id, val);
- }
- gs_gui_inline_style_stack_t* iss = NULL;
- if (gs_hash_table_exists(ctx->inline_styles, (gs_gui_element_type)elementid))
- {
- iss = gs_hash_table_getp(ctx->inline_styles, (gs_gui_element_type)elementid);
- }
- #define ANIM_GET_TIME(STATE)\
- anim = gs_hash_table_getp(ctx->animations, id);
- anim->playing = true;
- int16_t focus_state = 0x00;
- int16_t hover_state = 0x00;
- int16_t direction = 0x00;
- int16_t start_state = 0x00;
- int16_t end_state = 0x00;
- int16_t time_state = 0x00;
- switch (ctx->switch_state)
- {
- case GS_GUI_ELEMENT_STATE_OFF_FOCUS:
- {
- if (ctx->hover == id)
- {
- anim->direction = GS_GUI_ANIMATION_DIRECTION_BACKWARD;
- anim->start_state = GS_GUI_ELEMENT_STATE_HOVER;
- anim->end_state = GS_GUI_ELEMENT_STATE_FOCUS;
- time_state = GS_GUI_ELEMENT_STATE_HOVER;
- if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
- anim->time = anim->max;
- }
- else
- {
- anim->direction = GS_GUI_ANIMATION_DIRECTION_BACKWARD;
- anim->start_state = GS_GUI_ELEMENT_STATE_DEFAULT;
- anim->end_state = GS_GUI_ELEMENT_STATE_FOCUS;
- time_state = GS_GUI_ELEMENT_STATE_DEFAULT;
- if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
- anim->time = anim->max;
- }
- } break;
- case GS_GUI_ELEMENT_STATE_ON_FOCUS:
- {
- anim->direction = GS_GUI_ANIMATION_DIRECTION_FORWARD;
- anim->start_state = GS_GUI_ELEMENT_STATE_HOVER;
- anim->end_state = GS_GUI_ELEMENT_STATE_FOCUS;
- time_state = GS_GUI_ELEMENT_STATE_FOCUS;
- if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
- anim->time = 0;
- } break;
- case GS_GUI_ELEMENT_STATE_OFF_HOVER:
- {
- anim->direction = GS_GUI_ANIMATION_DIRECTION_BACKWARD;
- anim->start_state = GS_GUI_ELEMENT_STATE_DEFAULT;
- anim->end_state = GS_GUI_ELEMENT_STATE_HOVER;
- time_state = GS_GUI_ELEMENT_STATE_DEFAULT;
- if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
- anim->time = anim->max;
- } break;
- case GS_GUI_ELEMENT_STATE_ON_HOVER:
- {
- anim->direction = GS_GUI_ANIMATION_DIRECTION_FORWARD;
- anim->start_state = GS_GUI_ELEMENT_STATE_DEFAULT;
- anim->end_state = GS_GUI_ELEMENT_STATE_HOVER;
- time_state = GS_GUI_ELEMENT_STATE_HOVER;
- if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
- anim->time = 0;
- } break;
- }
- // Reset state switches and id
- ctx->state_switch_id = 0;
- ctx->switch_state = 0;
- return anim;
- }
- // Return if found
- if (gs_hash_table_exists(ctx->animations, id)) {
- anim = gs_hash_table_getp(ctx->animations, id);
- }
- if (anim && !anim->playing)
- {
- // This is causing a crash...
- gs_hash_table_erase(ctx->animations, id);
- anim = NULL;
- }
- return anim;
- }
- GS_API_DECL void gs_gui_animation_update(gs_gui_context_t* ctx, gs_gui_animation_t* anim)
- {
- if (ctx->frame == anim->frame) return;
- const int16_t dt = (int16_t)(gs_platform_delta_time() * 1000.f);
- if (anim->playing)
- {
- // Forward
- switch (anim->direction)
- {
- default:
- case (GS_GUI_ANIMATION_DIRECTION_FORWARD):
- {
- anim->time += dt;
- if (anim->time >= anim->max)
- {
- anim->time = anim->max;
- anim->playing = false;
- }
- } break;
- case (GS_GUI_ANIMATION_DIRECTION_BACKWARD):
- {
- anim->time -= dt;
- if (anim->time <= 0)
- {
- anim->time = 0;
- anim->playing = false;
- }
- } break;
- }
- }
- anim->frame = ctx->frame;
- }
- GS_API_DECL gs_gui_rect_t gs_gui_rect(float x, float y, float w, float h)
- {
- gs_gui_rect_t res;
- res.x = x; res.y = y; res.w = w; res.h = h;
- return res;
- }
- static gs_gui_rect_t gs_gui_expand_rect(gs_gui_rect_t rect, int16_t v[4])
- {
- return gs_gui_rect(rect.x - v[0],
- rect.y - v[2],
- rect.w + v[0] + v[1],
- rect.h + v[2] + v[3]);
- }
- static gs_gui_rect_t gs_gui_intersect_rects(gs_gui_rect_t r1, gs_gui_rect_t r2)
- {
- int32_t x1 = (int32_t)gs_max(r1.x, r2.x);
- int32_t y1 = (int32_t)gs_max(r1.y, r2.y);
- int32_t x2 = (int32_t)gs_min(r1.x + r1.w, r2.x + r2.w);
- int32_t y2 = (int32_t)gs_min(r1.y + r1.h, r2.y + r2.h);
- if (x2 < x1) {x2 = x1;}
- if (y2 < y1) {y2 = y1;}
- return gs_gui_rect((float)x1, (float)y1, (float)x2 - (float)x1, (float)y2 - (float)y1);
- }
- static int32_t gs_gui_rect_overlaps_vec2(gs_gui_rect_t r, gs_vec2 p)
- {
- return p.x >= r.x && p.x < r.x + r.w && p.y >= r.y && p.y < r.y + r.h;
- }
- GS_API_DECL gs_gui_container_t* gs_gui_get_top_most_container(gs_gui_context_t* ctx, gs_gui_split_t* split)
- {
- if (!split) return NULL;
- if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER) return split->children[0].container;
- if (split->children[1].type == GS_GUI_SPLIT_NODE_CONTAINER) return split->children[1].container;
- gs_gui_container_t* c0 = gs_gui_get_top_most_container(ctx, gs_slot_array_getp(ctx->splits, split->children[0].split));
- gs_gui_container_t* c1 = gs_gui_get_top_most_container(ctx, gs_slot_array_getp(ctx->splits, split->children[1].split));
- if (c0->zindex > c1->zindex) return c0;
- return c1;
- }
- GS_API_DECL void gs_gui_bring_split_to_front(gs_gui_context_t* ctx, gs_gui_split_t* split)
- {
- if (!split) return;
- if (!split->parent)
- {
- gs_snprintfc(TMP, 256, "!dockspace%zu", (size_t)split);
- gs_gui_id id = gs_gui_get_id(ctx, TMP, 256);
- gs_gui_container_t* cnt = gs_gui_get_container(ctx, TMP);
- // if (cnt) gs_gui_bring_to_front(ctx, cnt);
- // cnt->zindex = 0;
- }
-
- gs_gui_split_node_t* c0 = &split->children[0];
- gs_gui_split_node_t* c1 = &split->children[1];
- if (c0->type == GS_GUI_SPLIT_NODE_CONTAINER)
- {
- gs_gui_bring_to_front(ctx, c0->container);
- // ctx->hover = c0;
- }
- else
- {
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, c0->split);
- gs_gui_bring_split_to_front(ctx, s);
- }
- if (c1->type == GS_GUI_SPLIT_NODE_CONTAINER)
- {
- gs_gui_bring_to_front(ctx, c1->container);
- // ctx->hover = c1;
- }
- else
- {
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, c1->split);
- gs_gui_bring_split_to_front(ctx, s);
- }
- }
- static void gs_gui_update_split(gs_gui_context_t* ctx, gs_gui_split_t* split)
- {
- // Iterate through children, resize them based on size/position
- const gs_gui_rect_t* sr = &split->rect;
- const float ratio = split->ratio;
- switch (split->type)
- {
- case GS_GUI_SPLIT_LEFT:
- {
- if (split->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- // Update split
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_PARENT].split);
- s->rect = gs_gui_rect(sr->x + sr->w * ratio, sr->y, sr->w * (1.f - ratio), sr->h);
- gs_gui_update_split(ctx, s);
- }
- if (split->children[GS_GUI_SPLIT_NODE_CHILD].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_CHILD].split);
- s->rect = gs_gui_rect(sr->x, sr->y, sr->w * (ratio), sr->h);
- gs_gui_update_split(ctx, s);
- }
- } break;
- case GS_GUI_SPLIT_RIGHT:
- {
- if (split->children[GS_GUI_SPLIT_NODE_CHILD].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- // Update split
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_CHILD].split);
- s->rect = gs_gui_rect(sr->x + sr->w * (1.f - ratio), sr->y, sr->w * (ratio), sr->h);
- gs_gui_update_split(ctx, s);
- }
- if (split->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_PARENT].split);
- s->rect = gs_gui_rect(sr->x, sr->y, sr->w * (1.f - ratio), sr->h);
- gs_gui_update_split(ctx, s);
- }
- } break;
- case GS_GUI_SPLIT_TOP:
- {
- if (split->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- // Update split
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_PARENT].split);
- s->rect = gs_gui_rect(sr->x, sr->y + sr->h * (ratio), sr->w, sr->h * (1.f - ratio));
- gs_gui_update_split(ctx, s);
- }
- if (split->children[GS_GUI_SPLIT_NODE_CHILD].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_CHILD].split);
- s->rect = gs_gui_rect(sr->x, sr->y, sr->w, sr->h * (ratio));
- gs_gui_update_split(ctx, s);
- }
- } break;
- case GS_GUI_SPLIT_BOTTOM:
- {
- if (split->children[GS_GUI_SPLIT_NODE_CHILD].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- // Update split
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_CHILD].split);
- s->rect = gs_gui_rect(sr->x, sr->y + sr->h * (1.f - ratio), sr->w, sr->h * (ratio));
- gs_gui_update_split(ctx, s);
- }
- if (split->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_PARENT].split);
- s->rect = gs_gui_rect(sr->x, sr->y, sr->w, sr->h * (1.f - ratio));
- gs_gui_update_split(ctx, s);
- }
- } break;
- }
- }
- static gs_gui_split_t* gs_gui_get_root_split_from_split(gs_gui_context_t* ctx, gs_gui_split_t* split)
- {
- if (!split) return NULL;
- // Cache top root level split
- gs_gui_split_t* root_split = split && split->parent ? gs_slot_array_getp(ctx->splits, split->parent) : split ? split : NULL;
- while (root_split && root_split->parent)
- {
- root_split = gs_slot_array_getp(ctx->splits, root_split->parent);
- }
- return root_split;
- }
- static gs_gui_split_t* gs_gui_get_root_split(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
- {
- gs_gui_split_t* split = gs_gui_get_split(ctx, cnt);
- if (split) return gs_gui_get_root_split_from_split(ctx, split);
- else return NULL;
- }
- GS_API_DECL gs_gui_container_t* gs_gui_get_root_container_from_split(gs_gui_context_t* ctx, gs_gui_split_t* split)
- {
- gs_gui_split_t* root = gs_gui_get_root_split_from_split(ctx, split);
- gs_gui_split_t* s = root;
- gs_gui_container_t* c = NULL;
- while (s && !c)
- {
- if (s->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- s = gs_slot_array_getp(ctx->splits, s->children[GS_GUI_SPLIT_NODE_PARENT].split);
- }
- else if (s->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_CONTAINER)
- {
- c = s->children[GS_GUI_SPLIT_NODE_PARENT].container;
- }
- else
- {
- s = NULL;
- }
- }
- return c;
- }
- GS_API_DECL gs_gui_container_t* gs_gui_get_root_container(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
- {
- gs_gui_container_t* parent = gs_gui_get_parent(ctx, cnt);
- if (parent->split)
- {
- gs_gui_split_t* root = gs_gui_get_root_split(ctx, parent);
- gs_gui_split_t* s = root;
- gs_gui_container_t* c = NULL;
- while (s && !c)
- {
- if (s->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- s = gs_slot_array_getp(ctx->splits, s->children[GS_GUI_SPLIT_NODE_PARENT].split);
- }
- else if (s->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_CONTAINER)
- {
- c = s->children[GS_GUI_SPLIT_NODE_PARENT].container;
- }
- else
- {
- s = NULL;
- }
- }
- return c;
- }
- return parent;
- }
- GS_API_DECL gs_gui_tab_bar_t* gs_gui_get_tab_bar(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
- {
- return ((cnt->tab_bar && cnt->tab_bar< gs_slot_array_size(ctx->tab_bars)) ? gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar) : NULL);
- }
- GS_API_DECL gs_gui_split_t* gs_gui_get_split(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
- {
- gs_gui_tab_bar_t* tab_bar = gs_gui_get_tab_bar(ctx, cnt);
- gs_gui_tab_item_t* tab_item = tab_bar ? &tab_bar->items[cnt->tab_item] : NULL;
- gs_gui_split_t* split = cnt->split ? gs_slot_array_getp(ctx->splits, cnt->split) : NULL;
- // Look at split if in tab group
- if (!split && tab_bar)
- {
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (((gs_gui_container_t*)tab_bar->items[i].data)->split)
- {
- split = gs_slot_array_getp(ctx->splits, ((gs_gui_container_t*)tab_bar->items[i].data)->split);
- }
- }
- }
- return split;
- }
- static gs_gui_command_t* gs_gui_push_jump(gs_gui_context_t* ctx, gs_gui_command_t* dst)
- {
- gs_gui_command_t* cmd;
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_JUMP, sizeof(gs_gui_jumpcommand_t));
- cmd->jump.dst = dst;
- return cmd;
- }
- static void gs_gui_draw_frame(gs_gui_context_t* ctx, gs_gui_rect_t rect, gs_gui_style_t* style)
- {
- gs_gui_draw_rect(ctx, rect, style->colors[GS_GUI_COLOR_BACKGROUND]);
- // draw border
- if (style->colors[GS_GUI_COLOR_BORDER].a)
- {
- gs_gui_draw_box(ctx, gs_gui_expand_rect(rect, (int16_t*)style->border_width), (int16_t*)style->border_width, style->colors[GS_GUI_COLOR_BORDER]);
- }
- }
- static int32_t gs_gui_compare_zindex(const void *a, const void *b)
- {
- return (*(gs_gui_container_t**) a)->zindex - (*(gs_gui_container_t**) b)->zindex;
- }
- static gs_gui_style_t* gs_gui_push_style(gs_gui_context_t* ctx, gs_gui_style_t* style)
- {
- gs_gui_style_t* save = ctx->style;
- ctx->style = style;
- return save;
- }
- GS_API_DECL void gs_gui_push_inline_style(gs_gui_context_t* ctx, gs_gui_element_type elementid, gs_gui_inline_style_desc_t* desc)
- {
- if (elementid >= GS_GUI_ELEMENT_COUNT || !desc)
- {
- return;
- }
- if (!gs_hash_table_exists(ctx->inline_styles, elementid))
- {
- gs_gui_inline_style_stack_t v = gs_default_val();
- gs_hash_table_insert(ctx->inline_styles, elementid, v);
- }
- gs_gui_inline_style_stack_t* iss = gs_hash_table_getp(ctx->inline_styles, elementid);
- gs_assert(iss);
- // Counts to keep for popping off
- uint32_t style_ct[3] = gs_default_val(), anim_ct[3] = gs_default_val();
- if (desc->all.style.data && desc->all.style.size)
- {
- // Total amount to write for each section
- uint32_t ct = desc->all.style.size / sizeof(gs_gui_style_element_t);
- style_ct[0] += ct;
- style_ct[1] += ct;
- style_ct[2] += ct;
- // Iterate through all properties, then just push them back into style element list
- for (uint32_t i = 0; i < ct; ++i)
- {
- gs_dyn_array_push(iss->styles[0], desc->all.style.data[i]);
- gs_dyn_array_push(iss->styles[1], desc->all.style.data[i]);
- gs_dyn_array_push(iss->styles[2], desc->all.style.data[i]);
- }
- }
- if (desc->all.animation.data && desc->all.animation.size)
- {
- // Total amount to write for each section
- uint32_t ct = desc->all.animation.size / sizeof(gs_gui_animation_property_t);
- anim_ct[0] += ct;
- anim_ct[1] += ct;
- anim_ct[2] += ct;
- for (uint32_t i = 0; i < ct; ++i)
- {
- gs_dyn_array_push(iss->animations[0], desc->all.animation.data[i]);
- gs_dyn_array_push(iss->animations[1], desc->all.animation.data[i]);
- gs_dyn_array_push(iss->animations[2], desc->all.animation.data[i]);
- }
- }
- #define GS_GUI_COPY_INLINE_STYLE(TYPE, INDEX)\
- do {\
- if (desc->TYPE.style.data && desc->TYPE.style.size)\
- {\
- uint32_t ct = desc->TYPE.style.size / sizeof(gs_gui_style_element_t);\
- style_ct[INDEX] += ct;\
- for (uint32_t i = 0; i < ct; ++i)\
- {\
- gs_dyn_array_push(iss->styles[INDEX], desc->TYPE.style.data[i]);\
- }\
- }\
- if (desc->TYPE.animation.data && desc->TYPE.animation.size)\
- {\
- uint32_t ct = desc->TYPE.animation.size / sizeof(gs_gui_animation_property_t);\
- anim_ct[INDEX] += ct;\
- \
- for (uint32_t i = 0; i < ct; ++i)\
- {\
- gs_dyn_array_push(iss->animations[INDEX], desc->TYPE.animation.data[i]);\
- }\
- }\
- } while (0)
- // Copy remaining individual styles
- GS_GUI_COPY_INLINE_STYLE(def, 0);
- GS_GUI_COPY_INLINE_STYLE(hover, 1);
- GS_GUI_COPY_INLINE_STYLE(focus, 2);
- // Add final counts
- gs_dyn_array_push(iss->style_counts, style_ct[0]);
- gs_dyn_array_push(iss->style_counts, style_ct[1]);
- gs_dyn_array_push(iss->style_counts, style_ct[2]);
- gs_dyn_array_push(iss->animation_counts, anim_ct[0]);
- gs_dyn_array_push(iss->animation_counts, anim_ct[1]);
- gs_dyn_array_push(iss->animation_counts, anim_ct[2]);
- }
- GS_API_DECL void gs_gui_pop_inline_style(gs_gui_context_t* ctx, gs_gui_element_type elementid)
- {
- if (elementid >= GS_GUI_ELEMENT_COUNT)
- {
- return;
- }
- if (!gs_hash_table_exists(ctx->inline_styles, elementid))
- {
- return;
- }
- gs_gui_inline_style_stack_t* iss = gs_hash_table_getp(ctx->inline_styles, elementid);
- gs_assert(iss);
- if (gs_dyn_array_size(iss->style_counts) >= 3)
- {
- const uint32_t sz = gs_dyn_array_size(iss->style_counts);
- uint32_t c0 = iss->style_counts[sz - 3]; // default
- uint32_t c1 = iss->style_counts[sz - 2]; // hover
- uint32_t c2 = iss->style_counts[sz - 1]; // focus
-
- // Pop off elements
- if (iss->styles[0]) gs_dyn_array_head(iss->styles[0])->size -= c0;
- if (iss->styles[1]) gs_dyn_array_head(iss->styles[1])->size -= c1;
- if (iss->styles[2]) gs_dyn_array_head(iss->styles[2])->size -= c2;
- }
-
- if (gs_dyn_array_size(iss->animation_counts) >= 3)
- {
- const uint32_t sz = gs_dyn_array_size(iss->animation_counts);
- uint32_t c0 = iss->animation_counts[sz - 3]; // default
- uint32_t c1 = iss->animation_counts[sz - 2]; // hover
- uint32_t c2 = iss->animation_counts[sz - 1]; // focus
-
- // Pop off elements
- if (iss->animations[0]) gs_dyn_array_head(iss->animations[0])->size -= c0;
- if (iss->animations[1]) gs_dyn_array_head(iss->animations[1])->size -= c1;
- if (iss->animations[2]) gs_dyn_array_head(iss->animations[2])->size -= c2;
- }
- }
- static void gs_gui_pop_style(gs_gui_context_t* ctx, gs_gui_style_t* style)
- {
- ctx->style = style;
- }
- static void gs_gui_push_layout(gs_gui_context_t *ctx, gs_gui_rect_t body, gs_vec2 scroll)
- {
- gs_gui_layout_t layout;
- int32_t width = 0;
- memset(&layout, 0, sizeof(layout));
- layout.body = gs_gui_rect(body.x - scroll.x, body.y - scroll.y, body.w, body.h);
- layout.max = gs_v2(-0x1000000, -0x1000000);
- layout.direction = ctx->style->direction;
- layout.justify_content = ctx->style->justify_content;
- layout.align_content = ctx->style->align_content;
- memcpy(layout.padding, ctx->style->padding, sizeof(int32_t) * 4);
- gs_gui_stack_push(ctx->layout_stack, layout);
- gs_gui_layout_row(ctx, 1, &width, 0);
- }
- static void gs_gui_pop_layout(gs_gui_context_t* ctx)
- {
- gs_gui_stack_pop(ctx->layout_stack);
- }
- GS_API_DECL gs_gui_layout_t*
- gs_gui_get_layout(gs_gui_context_t *ctx)
- {
- return &ctx->layout_stack.items[ctx->layout_stack.idx - 1];
- }
- static void gs_gui_pop_container(gs_gui_context_t *ctx)
- {
- gs_gui_container_t *cnt = gs_gui_get_current_container(ctx);
- gs_gui_layout_t *layout = gs_gui_get_layout(ctx);
- cnt->content_size.x = layout->max.x - layout->body.x;
- cnt->content_size.y = layout->max.y - layout->body.y;
- /* pop container, layout and id */
- gs_gui_stack_pop(ctx->container_stack);
- gs_gui_stack_pop(ctx->layout_stack);
- gs_gui_pop_id(ctx);
- }
- #define gs_gui_scrollbar(ctx, cnt, b, cs, x, y, w, h) \
- do { \
- /* only add scrollbar if content size is larger than body */ \
- int32_t maxscroll = (int32_t)(cs.y - b->h); \
- \
- if (maxscroll > 0 && b->h > 0) { \
- gs_gui_rect_t base, thumb; \
- gs_gui_id id = gs_gui_get_id(ctx, "!scrollbar" #y, 11); \
- const int32_t elementid = GS_GUI_ELEMENT_SCROLL; \
- gs_gui_style_t style = gs_default_val(); \
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid); \
- \
- /* Update anim (keep states locally within animation, only way to do this)*/ \
- if (anim) \
- { \
- gs_gui_animation_update(ctx, anim); \
- \
- /* Get blended style based on animation*/ \
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid); \
- } \
- else \
- { \
- style = ctx->focus == id ? \
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) : \
- ctx->hover == id ? \
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) : \
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x00); \
- } \
- \
- int32_t sz = (int32_t)style.size[0]; \
- if (cs.y > cnt->body.h) {body->w -= sz;} \
- if (cs.x > cnt->body.w) {body->h -= sz;} \
- \
- /* get sizing / positioning */ \
- base = *b; \
- base.x = b->x + b->w; \
- base.w = style.size[0]; \
- \
- /* handle input */ \
- gs_gui_update_control(ctx, id, base, 0); \
- if (ctx->focus == id && ctx->mouse_down == GS_GUI_MOUSE_LEFT) { \
- cnt->scroll.y += ctx->mouse_delta.y * cs.y / base.h; \
- } \
- /* clamp scroll to limits */ \
- cnt->scroll.y = gs_clamp(cnt->scroll.y, 0, maxscroll); \
- int32_t state = ctx->focus == id ? GS_GUI_ELEMENT_STATE_FOCUS : \
- ctx->hover == id ? GS_GUI_ELEMENT_STATE_HOVER : 0x00; \
- \
- /* draw base and thumb */ \
- gs_gui_draw_rect(ctx, base, style.colors[GS_GUI_COLOR_BACKGROUND]); \
- /* draw border*/ \
- if (style.colors[GS_GUI_COLOR_BORDER].a) \
- { \
- gs_gui_draw_box(ctx, gs_gui_expand_rect(base, (int16_t*)style.border_width), \
- (int16_t*)style.border_width, style.colors[GS_GUI_COLOR_BORDER]); \
- } \
- float pl = ((float)style.padding[GS_GUI_PADDING_LEFT]); \
- float pr = ((float)style.padding[GS_GUI_PADDING_RIGHT]); \
- float pt = ((float)style.padding[GS_GUI_PADDING_TOP]); \
- float pb = ((float)style.padding[GS_GUI_PADDING_BOTTOM]); \
- float w = ((float)base.w - pr); \
- float x = (float)(base.x + pl); \
- thumb = base; \
- thumb.x = x; thumb.w = w; \
- thumb.h = gs_max(style.thumb_size, base.h * b->h / cs.y) - pb; \
- thumb.y += cnt->scroll.y * (base.h - thumb.h) / maxscroll + pt; \
- gs_gui_draw_rect(ctx, thumb, style.colors[GS_GUI_COLOR_CONTENT]); \
- /* draw border*/ \
- if (style.colors[GS_GUI_COLOR_BORDER].a) \
- { \
- gs_gui_draw_box(ctx, gs_gui_expand_rect(thumb, (int16_t*)style.border_width), \
- (int16_t*)style.border_width, style.colors[GS_GUI_COLOR_BORDER]); \
- } \
- \
- /* set this as the scroll_target (will get scrolled on mousewheel) */ \
- /* if the mouse is over it */ \
- if ( \
- gs_gui_mouse_over(ctx, *b) || \
- gs_gui_mouse_over(ctx, base) || \
- gs_gui_mouse_over(ctx, thumb) \
- ) \
- { \
- ctx->scroll_target = cnt; \
- } \
- } \
- } while (0)
- static void gs_gui_scrollbars(gs_gui_context_t* ctx, gs_gui_container_t* cnt, gs_gui_rect_t* body, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- int32_t sz = (int32_t)ctx->style_sheet->styles[GS_GUI_ELEMENT_SCROLL][0x00].size[0];
- gs_vec2 cs = cnt->content_size;
- cs.x += ctx->style->padding[GS_GUI_PADDING_LEFT] * 2;
- cs.y += ctx->style->padding[GS_GUI_PADDING_TOP] * 2;
- gs_gui_push_clip_rect(ctx, *body);
- /* resize body to make room for scrollbars */
- if (cs.y > cnt->body.h) { body->w -= sz; }
- if (cs.x > cnt->body.w) { body->h -= sz; }
- /* to create a horizontal or vertical scrollbar almost-identical code is
- ** used; only the references to `x|y` `w|h` need to be switched */
- gs_gui_scrollbar(ctx, cnt, body, cs, x, y, w, h);
- if (~opt & GS_GUI_OPT_NOSCROLLHORIZONTAL)
- {
- gs_gui_scrollbar(ctx, cnt, body, cs, y, x, h, w);
- }
- if (cs.y <= cnt->body.h) {cnt->scroll.y = 0;}
- if (cs.x <= cnt->body.w) {cnt->scroll.x = 0;}
- gs_gui_pop_clip_rect(ctx);
- }
- static void gs_gui_push_container_body(gs_gui_context_t *ctx, gs_gui_container_t *cnt, gs_gui_rect_t body, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- if (~opt & GS_GUI_OPT_NOSCROLL) {gs_gui_scrollbars(ctx, cnt, &body, desc, opt);}
- int32_t* padding = ctx->style->padding;
- float l = body.x + padding[GS_GUI_PADDING_LEFT];
- float t = body.y + padding[GS_GUI_PADDING_TOP];
- float r = body.x + body.w - padding[GS_GUI_PADDING_RIGHT];
- float b = body.y + body.h - padding[GS_GUI_PADDING_BOTTOM];
- gs_gui_rect_t rect = gs_gui_rect(l, t, r - l, b - t);
- gs_gui_push_layout(ctx, rect, cnt->scroll);
- cnt->body = body;
- }
- static void gs_gui_begin_root_container(gs_gui_context_t *ctx, gs_gui_container_t *cnt, uint64_t opt)
- {
- gs_gui_stack_push(ctx->container_stack, cnt);
- /* push container to roots list and push head command */
- gs_gui_stack_push(ctx->root_list, cnt);
- cnt->head = gs_gui_push_jump(ctx, NULL);
- /* set as hover root if the mouse is overlapping this container and it has a
- ** higher zindex than the current hover root */
- if (
- gs_gui_rect_overlaps_vec2(cnt->rect, ctx->mouse_pos) &&
- (!ctx->next_hover_root || cnt->zindex > ctx->next_hover_root->zindex) &&
- ~opt & GS_GUI_OPT_NOHOVER && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE
- )
- {
- ctx->next_hover_root = cnt;
- }
- /* clipping is reset here in case a root-container is made within
- ** another root-containers's begin/end block; this prevents the inner
- ** root-container being clipped to the outer */
- gs_gui_stack_push(ctx->clip_stack, gs_gui_unclipped_rect);
- }
- static void gs_gui_root_container_end(gs_gui_context_t *ctx)
- {
- /* push tail 'goto' jump command and set head 'skip' command. the final steps
- ** on initing these are done in gs_gui_end() */
- gs_gui_container_t *cnt = gs_gui_get_current_container(ctx);
- cnt->tail = gs_gui_push_jump(ctx, NULL);
- cnt->head->jump.dst = ctx->command_list.items + ctx->command_list.idx;
- /* pop base clip rect and container */
- gs_gui_pop_clip_rect(ctx);
- gs_gui_pop_container(ctx);
- }
- #define GS_GUI_COPY_STYLES(DST, SRC, ELEM)\
- do {\
- DST[ELEM][0x00] = SRC[ELEM][0x00];\
- DST[ELEM][0x01] = SRC[ELEM][0x01];\
- DST[ELEM][0x02] = SRC[ELEM][0x02];\
- } while (0)
- GS_API_DECL gs_gui_style_sheet_t gs_gui_style_sheet_create(gs_gui_context_t* ctx, gs_gui_style_sheet_desc_t* desc)
- {
- // Generate new style sheet based on default element styles
- gs_gui_style_sheet_t style_sheet = gs_default_val();
- // Copy all default styles
- GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_CONTAINER);
- GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_LABEL);
- GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_TEXT);
- GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_PANEL);
- GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_INPUT);
- GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_BUTTON);
- GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_SCROLL);
- GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_IMAGE);
- // GS_API_DECL void gs_gui_style_sheet_set_element_styles(gs_gui_style_sheet_t* style_sheet, gs_gui_element_type element, gs_gui_element_state state, gs_gui_style_element_t* styles, size_t size);
- #define GS_GUI_APPLY_STYLE_ELEMENT(ELEMENT, TYPE)\
- do {\
- if ((ELEMENT).all.style.data)\
- {\
- gs_gui_style_sheet_set_element_styles(&style_sheet, TYPE, GS_GUI_ELEMENT_STATE_NEG, (ELEMENT).all.style.data, (ELEMENT).all.style.size);\
- }\
- else if ((ELEMENT).def.style.data)\
- {\
- gs_gui_style_sheet_set_element_styles(&style_sheet, TYPE, GS_GUI_ELEMENT_STATE_DEFAULT, (ELEMENT).def.style.data, (ELEMENT).def.style.size);\
- }\
- if ((ELEMENT).hover.style.data)\
- {\
- gs_gui_style_sheet_set_element_styles(&style_sheet, TYPE, GS_GUI_ELEMENT_STATE_HOVER, (ELEMENT).hover.style.data, (ELEMENT).hover.style.size);\
- }\
- if ((ELEMENT).focus.style.data)\
- {\
- gs_gui_style_sheet_set_element_styles(&style_sheet, TYPE, GS_GUI_ELEMENT_STATE_FOCUS, (ELEMENT).focus.style.data, (ELEMENT).focus.style.size);\
- }\
- } while (0)
- // Iterate through descriptor
- if (desc)
- {
- GS_GUI_APPLY_STYLE_ELEMENT(desc->button, GS_GUI_ELEMENT_BUTTON);
- GS_GUI_APPLY_STYLE_ELEMENT(desc->container, GS_GUI_ELEMENT_CONTAINER);
- GS_GUI_APPLY_STYLE_ELEMENT(desc->panel, GS_GUI_ELEMENT_PANEL);
- GS_GUI_APPLY_STYLE_ELEMENT(desc->scroll, GS_GUI_ELEMENT_SCROLL);
- GS_GUI_APPLY_STYLE_ELEMENT(desc->image, GS_GUI_ELEMENT_IMAGE);
- GS_GUI_APPLY_STYLE_ELEMENT(desc->label, GS_GUI_ELEMENT_LABEL);
- GS_GUI_APPLY_STYLE_ELEMENT(desc->text, GS_GUI_ELEMENT_TEXT);
- }
- #define COPY_ANIM_DATA(TYPE, ELEMENT)\
- do {\
- /* Apply animations */\
- if (desc->TYPE.all.animation.data)\
- {\
- int32_t cnt = desc->TYPE.all.animation.size / sizeof(gs_gui_animation_property_t);\
- if (!gs_hash_table_exists(style_sheet.animations, ELEMENT)) {\
- gs_gui_animation_property_list_t v = gs_default_val();\
- gs_hash_table_insert(style_sheet.animations, ELEMENT, v);\
- }\
- gs_gui_animation_property_list_t* list = gs_hash_table_getp(style_sheet.animations, ELEMENT);\
- gs_assert(list);\
- /* Register animation properties for all */\
- for (uint32_t i = 0; i < 3; ++i)\
- {\
- for (uint32_t c = 0; c < cnt; ++c)\
- {\
- gs_dyn_array_push(list->properties[i], desc->TYPE.all.animation.data[c]);\
- }\
- }\
- }\
- } while (0)
- // Copy animations
- COPY_ANIM_DATA(button, GS_GUI_ELEMENT_BUTTON);
- COPY_ANIM_DATA(label, GS_GUI_ELEMENT_LABEL);
- COPY_ANIM_DATA(scroll, GS_GUI_ELEMENT_SCROLL);
- COPY_ANIM_DATA(image, GS_GUI_ELEMENT_IMAGE);
- COPY_ANIM_DATA(panel, GS_GUI_ELEMENT_PANEL);
- COPY_ANIM_DATA(text, GS_GUI_ELEMENT_TEXT);
- COPY_ANIM_DATA(container, GS_GUI_ELEMENT_CONTAINER);
- return style_sheet;
- }
- GS_API_DECL void
- gs_gui_style_sheet_destroy(gs_gui_style_sheet_t* ss)
- {
- // Need to free all animations
- if (!ss || !ss->animations) {
- gs_log_warning("Trying to destroy invalid style sheet");
- return;
- }
- for (
- gs_hash_table_iter it = gs_hash_table_iter_new(ss->animations);
- gs_hash_table_iter_valid(ss->animations, it);
- gs_hash_table_iter_advance(ss->animations, it)
- )
- {
- gs_gui_animation_property_list_t* list = gs_hash_table_iter_getp(ss->animations, it);
- for (uint32_t i = 0; i < 3; ++i)
- {
- gs_dyn_array_free(list->properties[i]);
- }
- }
- gs_hash_table_free(ss->animations);
- }
- GS_API_DECL void
- gs_gui_set_style_sheet(gs_gui_context_t* ctx, gs_gui_style_sheet_t* style_sheet)
- {
- ctx->style_sheet = style_sheet ? style_sheet : &gs_gui_default_style_sheet;
- }
- GS_API_DECL void
- gs_gui_style_sheet_set_element_styles(gs_gui_style_sheet_t* ss, gs_gui_element_type element, gs_gui_element_state state, gs_gui_style_element_t* styles, size_t size)
- {
- const uint32_t count = size / sizeof(gs_gui_style_element_t);
- uint32_t idx_cnt = 1;
- uint32_t idx = 0;
- // Switch on state
- switch (state)
- {
- // Do all
- default: idx_cnt = 3; break;
- case GS_GUI_ELEMENT_STATE_DEFAULT: idx = 0; break;
- case GS_GUI_ELEMENT_STATE_HOVER: idx = 1; break;
- case GS_GUI_ELEMENT_STATE_FOCUS: idx = 2; break;
- }
- for (uint32_t s = idx, c = 0; c < idx_cnt; ++s, ++c)
- {
- gs_gui_style_t* cs = &ss->styles[element][s];
- for (uint32_t i = 0; i < count; ++i)
- {
- gs_gui_style_element_t* se = &styles[i];
- switch (se->type)
- {
- // Width/Height
- case GS_GUI_STYLE_WIDTH: cs->size[0] = (float)se->value; break;
- case GS_GUI_STYLE_HEIGHT: cs->size[1] = (float)se->value; break;
- // Padding
- case GS_GUI_STYLE_PADDING: {
- cs->padding[GS_GUI_PADDING_LEFT] = (int32_t)se->value;
- cs->padding[GS_GUI_PADDING_TOP] = (int32_t)se->value;
- cs->padding[GS_GUI_PADDING_RIGHT] = (int32_t)se->value;
- cs->padding[GS_GUI_PADDING_BOTTOM] = (int32_t)se->value;
- }
- case GS_GUI_STYLE_PADDING_LEFT: cs->padding[GS_GUI_PADDING_LEFT] = (int32_t)se->value; break;
- case GS_GUI_STYLE_PADDING_TOP: cs->padding[GS_GUI_PADDING_TOP] = (int32_t)se->value; break;
- case GS_GUI_STYLE_PADDING_RIGHT: cs->padding[GS_GUI_PADDING_RIGHT] = (int32_t)se->value; break;
- case GS_GUI_STYLE_PADDING_BOTTOM: cs->padding[GS_GUI_PADDING_BOTTOM] = (int32_t)se->value; break;
- case GS_GUI_STYLE_MARGIN: {
- cs->margin[GS_GUI_MARGIN_LEFT] = (int32_t)se->value;
- cs->margin[GS_GUI_MARGIN_TOP] = (int32_t)se->value;
- cs->margin[GS_GUI_MARGIN_RIGHT] = (int32_t)se->value;
- cs->margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)se->value;
- } break;
- case GS_GUI_STYLE_MARGIN_LEFT: cs->margin[GS_GUI_MARGIN_LEFT] = (int32_t)se->value; break;
- case GS_GUI_STYLE_MARGIN_TOP: cs->margin[GS_GUI_MARGIN_TOP] = (int32_t)se->value; break;
- case GS_GUI_STYLE_MARGIN_RIGHT: cs->margin[GS_GUI_MARGIN_RIGHT] = (int32_t)se->value; break;
- case GS_GUI_STYLE_MARGIN_BOTTOM: cs->margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)se->value; break;
- // Border
- case GS_GUI_STYLE_BORDER_RADIUS: {
- cs->border_radius[0] = se->value;
- cs->border_radius[1] = se->value;
- cs->border_radius[2] = se->value;
- cs->border_radius[3] = se->value;
- } break;
- case GS_GUI_STYLE_BORDER_RADIUS_LEFT: cs->border_radius[0] = se->value; break;
- case GS_GUI_STYLE_BORDER_RADIUS_RIGHT: cs->border_radius[1] = se->value; break;
- case GS_GUI_STYLE_BORDER_RADIUS_TOP: cs->border_radius[2] = se->value; break;
- case GS_GUI_STYLE_BORDER_RADIUS_BOTTOM: cs->border_radius[3] = se->value; break;
- case GS_GUI_STYLE_BORDER_WIDTH: {
- cs->border_width[0] = se->value;
- cs->border_width[1] = se->value;
- cs->border_width[2] = se->value;
- cs->border_width[3] = se->value;
- } break;
- case GS_GUI_STYLE_BORDER_WIDTH_LEFT: cs->border_width[0] = se->value; break;
- case GS_GUI_STYLE_BORDER_WIDTH_RIGHT: cs->border_width[1] = se->value; break;
- case GS_GUI_STYLE_BORDER_WIDTH_TOP: cs->border_width[2] = se->value; break;
- case GS_GUI_STYLE_BORDER_WIDTH_BOTTOM: cs->border_width[3] = se->value; break;
- // Flex
- case GS_GUI_STYLE_DIRECTION: cs->direction = (int32_t)se->value; break;
- case GS_GUI_STYLE_ALIGN_CONTENT: cs->align_content = (int32_t)se->value; break;
- case GS_GUI_STYLE_JUSTIFY_CONTENT: cs->justify_content = (int32_t)se->value; break;
- // Shadow
- case GS_GUI_STYLE_SHADOW_X: cs->shadow_x = (int32_t)se->value; break;
- case GS_GUI_STYLE_SHADOW_Y: cs->shadow_y = (int32_t)se->value; break;
- // Colors
- case GS_GUI_STYLE_COLOR_BACKGROUND: cs->colors[GS_GUI_COLOR_BACKGROUND] = se->color; break;
- case GS_GUI_STYLE_COLOR_BORDER: cs->colors[GS_GUI_COLOR_BORDER] = se->color; break;
- case GS_GUI_STYLE_COLOR_SHADOW: cs->colors[GS_GUI_COLOR_SHADOW] = se->color; break;
- case GS_GUI_STYLE_COLOR_CONTENT: cs->colors[GS_GUI_COLOR_CONTENT] = se->color; break;
- case GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND: cs->colors[GS_GUI_COLOR_CONTENT_BACKGROUND] = se->color; break;
- case GS_GUI_STYLE_COLOR_CONTENT_BORDER: cs->colors[GS_GUI_COLOR_CONTENT_BORDER] = se->color; break;
- case GS_GUI_STYLE_COLOR_CONTENT_SHADOW: cs->colors[GS_GUI_COLOR_CONTENT_SHADOW] = se->color; break;
- // Font
- case GS_GUI_STYLE_FONT: cs->font = se->font; break;
- }
- }
- }
- }
- GS_API_DECL void gs_gui_set_element_style(gs_gui_context_t* ctx, gs_gui_element_type element, gs_gui_element_state state, gs_gui_style_element_t* style, size_t size)
- {
- const uint32_t count = size / sizeof(gs_gui_style_element_t);
- uint32_t idx_cnt = 1;
- uint32_t idx = 0;
- // Switch on state
- switch (state)
- {
- // Do all
- default: idx_cnt = 3; break;
- case GS_GUI_ELEMENT_STATE_DEFAULT: idx = 0; break;
- case GS_GUI_ELEMENT_STATE_HOVER: idx = 1; break;
- case GS_GUI_ELEMENT_STATE_FOCUS: idx = 2; break;
- }
- for (uint32_t s = idx, c = 0; c < idx_cnt; ++s, ++c)
- {
- gs_gui_style_t* cs = &ctx->style_sheet->styles[element][s];
- for (uint32_t i = 0; i < count; ++i)
- {
- gs_gui_style_element_t* se = &style[i];
- switch (se->type)
- {
- // Width/Height
- case GS_GUI_STYLE_WIDTH: cs->size[0] = (float)se->value; break;
- case GS_GUI_STYLE_HEIGHT: cs->size[1] = (float)se->value; break;
- // Padding
- case GS_GUI_STYLE_PADDING: {
- cs->padding[GS_GUI_PADDING_LEFT] = (int32_t)se->value;
- cs->padding[GS_GUI_PADDING_TOP] = (int32_t)se->value;
- cs->padding[GS_GUI_PADDING_RIGHT] = (int32_t)se->value;
- cs->padding[GS_GUI_PADDING_BOTTOM] = (int32_t)se->value;
- }
- case GS_GUI_STYLE_PADDING_LEFT: cs->padding[GS_GUI_PADDING_LEFT] = (int32_t)se->value; break;
- case GS_GUI_STYLE_PADDING_TOP: cs->padding[GS_GUI_PADDING_TOP] = (int32_t)se->value; break;
- case GS_GUI_STYLE_PADDING_RIGHT: cs->padding[GS_GUI_PADDING_RIGHT] = (int32_t)se->value; break;
- case GS_GUI_STYLE_PADDING_BOTTOM: cs->padding[GS_GUI_PADDING_BOTTOM] = (int32_t)se->value; break;
- case GS_GUI_STYLE_MARGIN: {
- cs->margin[GS_GUI_MARGIN_LEFT] = (int32_t)se->value;
- cs->margin[GS_GUI_MARGIN_TOP] = (int32_t)se->value;
- cs->margin[GS_GUI_MARGIN_RIGHT] = (int32_t)se->value;
- cs->margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)se->value;
- } break;
- case GS_GUI_STYLE_MARGIN_LEFT: cs->margin[GS_GUI_MARGIN_LEFT] = (int32_t)se->value; break;
- case GS_GUI_STYLE_MARGIN_TOP: cs->margin[GS_GUI_MARGIN_TOP] = (int32_t)se->value; break;
- case GS_GUI_STYLE_MARGIN_RIGHT: cs->margin[GS_GUI_MARGIN_RIGHT] = (int32_t)se->value; break;
- case GS_GUI_STYLE_MARGIN_BOTTOM: cs->margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)se->value; break;
- // Border
- case GS_GUI_STYLE_BORDER_RADIUS: {
- cs->border_radius[0] = se->value;
- cs->border_radius[1] = se->value;
- cs->border_radius[2] = se->value;
- cs->border_radius[3] = se->value;
- } break;
- case GS_GUI_STYLE_BORDER_RADIUS_LEFT: cs->border_radius[0] = se->value; break;
- case GS_GUI_STYLE_BORDER_RADIUS_RIGHT: cs->border_radius[1] = se->value; break;
- case GS_GUI_STYLE_BORDER_RADIUS_TOP: cs->border_radius[2] = se->value; break;
- case GS_GUI_STYLE_BORDER_RADIUS_BOTTOM: cs->border_radius[3] = se->value; break;
- case GS_GUI_STYLE_BORDER_WIDTH: {
- cs->border_width[0] = se->value;
- cs->border_width[1] = se->value;
- cs->border_width[2] = se->value;
- cs->border_width[3] = se->value;
- } break;
- case GS_GUI_STYLE_BORDER_WIDTH_LEFT: cs->border_width[0] = se->value; break;
- case GS_GUI_STYLE_BORDER_WIDTH_RIGHT: cs->border_width[1] = se->value; break;
- case GS_GUI_STYLE_BORDER_WIDTH_TOP: cs->border_width[2] = se->value; break;
- case GS_GUI_STYLE_BORDER_WIDTH_BOTTOM: cs->border_width[3] = se->value; break;
- // Flex
- case GS_GUI_STYLE_DIRECTION: cs->direction = (int32_t)se->value; break;
- case GS_GUI_STYLE_ALIGN_CONTENT: cs->align_content = (int32_t)se->value; break;
- case GS_GUI_STYLE_JUSTIFY_CONTENT: cs->justify_content = (int32_t)se->value; break;
- // Shadow
- case GS_GUI_STYLE_SHADOW_X: cs->shadow_x = (int32_t)se->value; break;
- case GS_GUI_STYLE_SHADOW_Y: cs->shadow_y = (int32_t)se->value; break;
- // Colors
- case GS_GUI_STYLE_COLOR_BACKGROUND: cs->colors[GS_GUI_COLOR_BACKGROUND] = se->color; break;
- case GS_GUI_STYLE_COLOR_BORDER: cs->colors[GS_GUI_COLOR_BORDER] = se->color; break;
- case GS_GUI_STYLE_COLOR_SHADOW: cs->colors[GS_GUI_COLOR_SHADOW] = se->color; break;
- case GS_GUI_STYLE_COLOR_CONTENT: cs->colors[GS_GUI_COLOR_CONTENT] = se->color; break;
- case GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND: cs->colors[GS_GUI_COLOR_CONTENT_BACKGROUND] = se->color; break;
- case GS_GUI_STYLE_COLOR_CONTENT_BORDER: cs->colors[GS_GUI_COLOR_CONTENT_BORDER] = se->color; break;
- case GS_GUI_STYLE_COLOR_CONTENT_SHADOW: cs->colors[GS_GUI_COLOR_CONTENT_SHADOW] = se->color; break;
- // Font
- case GS_GUI_STYLE_FONT: cs->font = se->font; break;
- }
- }
- }
- }
- GS_API_DECL gs_gui_container_t*
- gs_gui_get_container_ex(gs_gui_context_t* ctx, gs_gui_id id, uint64_t opt)
- {
- gs_gui_container_t *cnt;
- /* try to get existing container from pool */
- int32_t idx = gs_gui_pool_get(ctx, ctx->container_pool, GS_GUI_CONTAINERPOOL_SIZE, id);
- if (idx >= 0)
- {
- if (ctx->containers[idx].open || ~opt & GS_GUI_OPT_CLOSED)
- {
- gs_gui_pool_update(ctx, ctx->container_pool, idx);
- }
- return &ctx->containers[idx];
- }
- if (opt & GS_GUI_OPT_CLOSED) { return NULL; }
- /* container not found in pool: init new container */
- idx = gs_gui_pool_init(ctx, ctx->container_pool, GS_GUI_CONTAINERPOOL_SIZE, id);
- cnt = &ctx->containers[idx];
- memset(cnt, 0, sizeof(*cnt));
- cnt->open = 1;
- cnt->id = id;
- cnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE | GS_GUI_WINDOW_FLAGS_FIRST_INIT;
- gs_gui_bring_to_front(ctx, cnt);
- return cnt;
- }
- static int32_t
- gs_gui_text_width(gs_asset_font_t* font, const char* text, int32_t len)
- {
- gs_vec2 td = gs_asset_font_text_dimensions(font, text, len);
- return (int32_t)td.x;
- }
- static int32_t
- gs_gui_text_height(gs_asset_font_t* font, const char* text, int32_t len)
- {
- return (int32_t)gs_asset_font_max_height(font);
- gs_vec2 td = gs_asset_font_text_dimensions(font, text, len);
- return (int32_t)td.y;
- }
- // Grabs max height for a given font
- static int32_t
- gs_gui_font_height(gs_asset_font_t* font)
- {
- return (int32_t)gs_asset_font_max_height(font);
- }
- static gs_vec2
- gs_gui_text_dimensions(gs_asset_font_t* font, const char* text, int32_t len)
- {
- gs_vec2 td = gs_asset_font_text_dimensions(font, text, len);
- return td;
- }
- // =========================== //
- // ======== Docking ========== //
- // =========================== //
- GS_API_DECL void
- gs_gui_dock_ex(gs_gui_context_t* ctx, const char* dst, const char* src, int32_t split_type, float ratio)
- {
- gs_gui_hints_t hints = gs_default_val();
- hints.framebuffer_size = gs_platform_framebuffer_sizev(ctx->window_hndl);
- hints.viewport = gs_gui_rect(0.f, 0.f, 0.f, 0.f);
- uint32_t f = ctx->frame;
- if (!f) gs_gui_begin(ctx, &hints);
- gs_gui_container_t* dst_cnt = gs_gui_get_container(ctx, dst);
- gs_gui_container_t* src_cnt = gs_gui_get_container(ctx, src);
- gs_gui_dock_ex_cnt(ctx, dst_cnt, src_cnt, split_type, ratio);
- if (f != ctx->frame) gs_gui_end(ctx);
- }
- GS_API_DECL void gs_gui_undock_ex(gs_gui_context_t* ctx, const char* name)
- {
- gs_gui_container_t* cnt = gs_gui_get_container(ctx, name);
- gs_gui_undock_ex_cnt(ctx, cnt);
- }
- void gs_gui_set_split(gs_gui_context_t* ctx, gs_gui_container_t* cnt, uint32_t id)
- {
- cnt->split = id;
- gs_gui_tab_bar_t* tb = gs_gui_get_tab_bar(ctx, cnt);
- if (tb)
- {
- for (uint32_t i = 0; i < tb->size; ++i)
- {
- ((gs_gui_container_t*)tb->items[i].data)->split = id;
- }
- }
- }
- GS_API_DECL gs_gui_container_t* gs_gui_get_parent(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
- {
- return (cnt->parent ? cnt->parent : cnt);
- }
- GS_API_DECL void gs_gui_dock_ex_cnt(gs_gui_context_t* ctx, gs_gui_container_t* child, gs_gui_container_t* parent, int32_t split_type, float ratio)
- {
- // Get top-level parent windows
- parent = gs_gui_get_parent(ctx, parent);
- child = gs_gui_get_parent(ctx, child);
- if (!child || !parent)
- {
- return;
- }
- if (split_type == GS_GUI_SPLIT_TAB)
- {
- // If the parent window has a tab bar, then need to get that tab bar item and add it
- if (parent->tab_bar)
- {
- gs_println("add to tab bar");
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, parent->tab_bar);
- gs_assert(tab_bar);
-
- // Set all tab bar children to this as well, if has children, then release previous tab bar
- if (child->tab_bar)
- {
- uint32_t tbid = child->tab_bar;
- gs_gui_tab_bar_t* ctb = gs_slot_array_getp(ctx->tab_bars, child->tab_bar);
- for (uint32_t i = 0; i < ctb->size; ++i)
- {
- gs_gui_tab_item_t* cti = &tab_bar->items[tab_bar->size];
- gs_gui_container_t* c = (gs_gui_container_t*)ctb->items[i].data;
- cti->tab_bar = parent->tab_bar;
- cti->zindex = tab_bar->size++;
- cti->idx = cti->zindex;
- cti->data = c;
- c->tab_item = cti->idx;
- c->parent = parent;
- }
- // Free other tab bar
- // gs_slot_array_erase(ctx->tab_bars, tbid);
- }
- else
- {
- gs_gui_tab_item_t* tab_item = &tab_bar->items[tab_bar->size];
- tab_item->tab_bar = parent->tab_bar;
- tab_item->zindex = tab_bar->size++;
- tab_item->idx = tab_item->zindex;
- tab_bar->focus = tab_bar->size - 1;
- child->tab_item = tab_item->idx;
- }
- tab_bar->items[child->tab_item].data = child;
- child->rect = parent->rect;
- child->parent = parent;
- child->tab_bar = parent->tab_bar;
- }
- // Otherwise, create new tab bar
- else
- {
- gs_println("create tab bar");
- // Create tab bar
- gs_gui_tab_bar_t tb = gs_default_val();
- uint32_t hndl = gs_slot_array_insert(ctx->tab_bars, tb);
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, hndl);
- gs_assert(tab_bar);
- // Create tab items
- gs_gui_tab_item_t* parent_tab_item = &tab_bar->items[tab_bar->size];
- parent_tab_item->zindex = tab_bar->size++;
- parent_tab_item->tab_bar = hndl;
- // Set parent tab item
- parent->tab_item = 0;
- parent_tab_item->data = parent;
- uint32_t tbid = child->tab_bar;
- // Set all tab bar children to this as well, if has children, then release previous tab bar
- if (child->tab_bar)
- {
- gs_gui_tab_bar_t* ctb = gs_slot_array_getp(ctx->tab_bars, child->tab_bar);
- for (uint32_t i = 0; i < ctb->size; ++i)
- {
- gs_gui_tab_item_t* cti = &tab_bar->items[tab_bar->size];
- gs_gui_container_t* c = (gs_gui_container_t*)ctb->items[i].data;
- cti->tab_bar = hndl;
- cti->zindex = tab_bar->size++;
- cti->idx = cti->zindex;
- cti->data = c;
- c->tab_item = cti->idx;
- c->parent = parent;
- c->tab_bar = hndl;
- }
- // TODO(john): This erase is causing a crash.
- // gs_slot_array_erase(ctx->tab_bars, tbid);
- }
- else
- {
- gs_gui_tab_item_t* child_tab_item = &tab_bar->items[tab_bar->size];
- child_tab_item->zindex = tab_bar->size++;
- child_tab_item->idx = child_tab_item->zindex;
- child_tab_item->tab_bar = hndl;
- // Set child tab item
- child->tab_item = child_tab_item->idx;
- child_tab_item->data = child;
- }
- tab_bar->focus = 1;
- child->rect = parent->rect;
- child->parent = parent;
- tab_bar->rect = parent->rect;
- parent->tab_bar = hndl;
- child->tab_bar = hndl;
- // Bring everything to front...right?
- }
- gs_gui_split_t* root_split = gs_gui_get_root_split(ctx, parent);
- if (root_split)
- {
- gs_gui_update_split(ctx, root_split);
- gs_gui_bring_split_to_front(ctx, root_split);
- }
- }
- else
- {
- // Cache previous root splits
- gs_gui_split_t* ps = gs_gui_get_root_split(ctx, parent);
- gs_gui_split_t* cs = gs_gui_get_root_split(ctx, child);
- gs_gui_tab_bar_t* tab_bar = gs_gui_get_tab_bar(ctx, parent);
- gs_gui_split_t split = gs_default_val();
- split.type = (gs_gui_split_type)split_type;
- split.ratio = ratio;
- gs_gui_split_node_t c0 = gs_default_val();
- c0.type = GS_GUI_SPLIT_NODE_CONTAINER;
- c0.container = child;
- gs_gui_split_node_t c1 = gs_default_val();
- c1.type = GS_GUI_SPLIT_NODE_CONTAINER;
- c1.container = parent;
- split.children[GS_GUI_SPLIT_NODE_CHILD] = c0;
- split.children[GS_GUI_SPLIT_NODE_PARENT] = c1;
- split.rect = ps ? ps->rect : parent->rect;
- split.prev_rect = split.rect;
- // Add new split to array
- uint32_t hndl = gs_slot_array_insert(ctx->splits, split);
- // Get newly inserted split pointer
- gs_gui_split_t* sp = gs_slot_array_getp(ctx->splits, hndl);
- sp->id = hndl;
- // If both parents are null, creating a new split, new nodes, assigning to children's parents
- if (!cs && !ps)
- {
- gs_println("0");
- parent->split = hndl;
- child->split = hndl;
- }
- // Child has split
- else if (cs && !ps)
- {
- gs_println("1");
- // If child has split, then the split is different...
- sp->children[GS_GUI_SPLIT_NODE_CHILD].type = GS_GUI_SPLIT_NODE_SPLIT;
- sp->children[GS_GUI_SPLIT_NODE_CHILD].split = cs->id;
- // Child split needs to be set to this parent
- cs->parent = hndl;
- parent->split = hndl;
- }
- // Parent has split
- else if (ps && !cs)
- {
- gs_println("2");
- // No child to tree to assign, so we can get the raw parent split here
- ps = gs_slot_array_getp(ctx->splits, parent->split);
- // Assign parent split to previous
- sp->parent = ps->id;
- // Fix up references
- if (ps->children[GS_GUI_SPLIT_NODE_PARENT].container == parent)
- {
- ps->children[GS_GUI_SPLIT_NODE_PARENT].type = GS_GUI_SPLIT_NODE_SPLIT;
- ps->children[GS_GUI_SPLIT_NODE_PARENT].split = hndl;
- }
- else
- {
- ps->children[GS_GUI_SPLIT_NODE_CHILD].type = GS_GUI_SPLIT_NODE_SPLIT;
- ps->children[GS_GUI_SPLIT_NODE_CHILD].split = hndl;
- }
- parent->split = hndl;
- child->split = hndl;
- }
- // Both have splits
- else
- {
- gs_println("3");
- // Get parent split
- ps = gs_slot_array_getp(ctx->splits, parent->split);
- // Set parent id for new split to parent previous split
- sp->parent = ps->id;
- // Fix up references
- sp->children[GS_GUI_SPLIT_NODE_CHILD].type = GS_GUI_SPLIT_NODE_SPLIT;
- sp->children[GS_GUI_SPLIT_NODE_CHILD].split = cs->id;
- // Need to check which node to replace
- if (ps->children[GS_GUI_SPLIT_NODE_CHILD].container == parent)
- {
- ps->children[GS_GUI_SPLIT_NODE_CHILD].type = GS_GUI_SPLIT_NODE_SPLIT;
- ps->children[GS_GUI_SPLIT_NODE_CHILD].split = hndl;
- }
- else
- {
- ps->children[GS_GUI_SPLIT_NODE_PARENT].type = GS_GUI_SPLIT_NODE_SPLIT;
- ps->children[GS_GUI_SPLIT_NODE_PARENT].split = hndl;
- }
- cs->parent = hndl;
- parent->split = hndl;
- }
- gs_gui_split_t* root_split = gs_gui_get_root_split(ctx, parent);
- gs_gui_update_split(ctx, root_split);
- gs_gui_bring_split_to_front(ctx, root_split);
- }
- }
- GS_API_DECL void gs_gui_undock_ex_cnt(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
- {
- // If has a tab item idx, need to grab that
- gs_gui_split_t* split = gs_gui_get_split(ctx, cnt);
- // Get root split for container
- gs_gui_split_t* root_split = NULL;
- // Get parent split of this owning split
- gs_gui_split_t* ps = split && split->parent ? gs_slot_array_getp(ctx->splits, split->parent) : NULL;
- if (cnt->tab_bar)
- {
- // Get parent container for this container
- gs_gui_container_t* parent = gs_gui_get_parent(ctx, cnt);
- // Check if split
- if (parent->split)
- {
- // No split, so just do stuff normally...
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
- gs_gui_tab_item_t* tab_item = &tab_bar->items[cnt->tab_item];
- if (tab_bar->size > 2)
- {
- // Get index
- uint32_t idx = 0;
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (tab_bar->items[i].data == cnt)
- {
- idx = i;
- break;
- }
- }
- // Swap all the way down the chain
- for (uint32_t i = idx; i < tab_bar->size; ++i)
- {
- gs_gui_tab_item_swap(ctx, (gs_gui_container_t*)tab_bar->items[i].data, +1);
- }
- // Swap windows as well
- ((gs_gui_container_t*)(tab_item->data))->tab_item = tab_item->idx;
- // Set focus to first window
- tab_bar->focus = idx ? idx - 1 : idx;
- gs_assert(tab_bar->items[tab_bar->focus].data != cnt);
- // Set split for focus
- if (parent == cnt)
- {
- // Set parent for other containers (assuming parent was removed)
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- gs_gui_container_t* c = (gs_gui_container_t*)tab_bar->items[i].data;
- c->parent = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
- tab_bar->items[i].idx = i;
- tab_bar->items[i].zindex = i;
- }
- gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
- fcnt->split = parent->split;
- fcnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
- // Fix up split reference
- split = gs_slot_array_getp(ctx->splits, fcnt->split);
- if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[0].container == cnt)
- {
- split->children[0].container = fcnt;
- }
- else
- {
- split->children[1].container = fcnt;
- }
- }
- // Set size
- tab_bar->size--;
- }
- // Destroy tab
- else
- {
- uint32_t tbid = tab_item->tab_bar;
- // Get index
- uint32_t idx = 0;
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (tab_bar->items[i].data == cnt)
- {
- idx = i;
- break;
- }
- }
- // Swap all the way down the chain
- for (uint32_t i = idx; i < tab_bar->size; ++i)
- {
- gs_gui_tab_item_swap(ctx, (gs_gui_container_t*)tab_bar->items[i].data, +1);
- }
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[i].data;
- fcnt->rect = tab_bar->rect;
- fcnt->tab_item = 0x00;
- fcnt->tab_bar = 0x00;
- fcnt->parent = NULL;
- fcnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
- }
- // Fix up split reference
- if (parent == cnt)
- {
- tab_bar->focus = idx ? idx - 1 : idx;
- gs_assert((gs_gui_container_t*)tab_bar->items[tab_bar->focus].data != cnt);
- gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
- fcnt->split = parent->split;
- // Fix up split reference
- split = gs_slot_array_getp(ctx->splits, fcnt->split);
- if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[0].container == parent)
- {
- split->children[0].container = fcnt;
- }
- else
- {
- split->children[1].container = fcnt;
- }
- }
- // gs_slot_array_erase(ctx->tab_bars, tbid);
- }
- // Remove tab index from container
- cnt->tab_item = 0x00;
- cnt->tab_bar = 0x00;
- // Remove parent
- cnt->parent = NULL;
- // Set split to 0
- cnt->split = 0x00;
- gs_gui_bring_to_front(ctx, cnt);
- }
- else
- {
- // No split, so just do stuff normally...
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
- gs_gui_tab_item_t* tab_item = &tab_bar->items[cnt->tab_item];
- // Set next available window to visible/focused and rearrange all tab item zindices
- if (tab_bar->size > 2)
- {
- uint32_t idx = 0;
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (tab_bar->items[i].data == cnt)
- {
- idx = i;
- break;
- }
- }
- // Swap all the way down the chain
- for (uint32_t i = idx; i < tab_bar->size; ++i)
- {
- gs_gui_tab_item_swap(ctx, (gs_gui_container_t*)tab_bar->items[i].data, +1);
- }
- // Set size
- tab_bar->size--;
- // Set focus to first window
- tab_bar->focus = idx ? idx - 1 : idx;
- // Set parent for other containers
- if (parent == cnt)
- {
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- gs_gui_container_t* c = (gs_gui_container_t*)tab_bar->items[i].data;
- c->parent = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
- }
- }
- }
- // Only 2 windows left in tab bar
- else
- {
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[i].data;
- fcnt->rect = tab_bar->rect;
- fcnt->tab_item = 0x00;
- fcnt->tab_bar = 0x00;
- fcnt->parent = NULL;
- fcnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
- }
- tab_bar->size = 0;
- // Destroy tab bar, reset windows
- // gs_slot_array_erase(ctx->tab_bars, tab_item->tab_bar);
- }
- // Remove tab index from container
- cnt->tab_item = 0x00;
- cnt->tab_bar = 0x00;
- // Remove parent
- cnt->parent = NULL;
- gs_gui_bring_to_front(ctx, cnt);
- }
- }
- else
- {
- // Rmove split reference from container
- cnt->split = 0x00;
- gs_gui_split_node_t* remain_node =
- split->children[GS_GUI_SPLIT_NODE_CHILD].container == cnt? &split->children[GS_GUI_SPLIT_NODE_PARENT]:
- split->children[GS_GUI_SPLIT_NODE_PARENT].container == cnt? &split->children[GS_GUI_SPLIT_NODE_CHILD] :
- NULL;
- gs_assert(remain_node);
- // Set child split in prev container split to split container parent
- if (ps)
- {
- uint32_t node_id = ps->children[GS_GUI_SPLIT_NODE_CHILD].split == split->id ? GS_GUI_SPLIT_NODE_CHILD : GS_GUI_SPLIT_NODE_PARENT;
- gs_gui_split_node_t* fix_node = &ps->children[node_id];
- *fix_node = *remain_node;
- switch (fix_node->type)
- {
- case GS_GUI_SPLIT_NODE_CONTAINER:
- {
- gs_gui_container_t* remcnt = fix_node->container;
- remcnt->split = ps->id;
- } break;
- case GS_GUI_SPLIT_NODE_SPLIT:
- {
- gs_gui_split_t* remsplit = gs_slot_array_getp(ctx->splits, fix_node->split);
- remsplit->parent = ps->id;
- } break;
- }
- root_split = gs_gui_get_root_split_from_split(ctx, ps);
- }
- // Otherwise, we were root dock and have to treat that case for remaining nodes
- else
- {
- switch (remain_node->type)
- {
- case GS_GUI_SPLIT_NODE_CONTAINER:
- {
- gs_gui_container_t* remcnt = remain_node->container;
- remcnt->rect = split->rect;
- remcnt->split = 0x00;
- root_split = gs_gui_get_root_split(ctx, remcnt);
- } break;
- case GS_GUI_SPLIT_NODE_SPLIT:
- {
- gs_gui_split_t* remsplit = gs_slot_array_getp(ctx->splits, remain_node->split);
- remsplit->rect = split->rect;
- remsplit->parent = 0x00;
- root_split = gs_gui_get_root_split_from_split(ctx, remsplit);
- } break;
- }
- }
- // Erase split
- gs_slot_array_erase(ctx->splits, split->id);
- // Update
- if (root_split) gs_gui_update_split(ctx, root_split);
- if (root_split) gs_gui_bring_split_to_front(ctx, root_split);
- gs_gui_bring_to_front(ctx, cnt);
- }
- }
- // ============================= //
- // ========= Main API ========== //
- // ============================= //
- #define GS_GUI_COPY_STYLE(DST, SRC)\
- do {\
- DST[GS_GUI_ELEMENT_STATE_DEFAULT] = SRC;\
- DST[GS_GUI_ELEMENT_STATE_HOVER] = SRC;\
- DST[GS_GUI_ELEMENT_STATE_FOCUS] = SRC;\
- } while (0)
- static void gs_gui_init_default_styles(gs_gui_context_t* ctx)
- {
- // Set up main default style
- gs_gui_default_style.font = gsi_default_font();
- gs_gui_default_style.size[0] = 68.f;
- gs_gui_default_style.size[1] = 18.f;
- gs_gui_default_style.spacing = 2;
- gs_gui_default_style.indent = 10;
- gs_gui_default_style.title_height = 20;
- gs_gui_default_style.scrollbar_size = 5;
- gs_gui_default_style.thumb_size = 5;
- // Initialize all default styles
- GS_GUI_COPY_STYLE(gs_gui_default_container_style, gs_gui_default_style);
- GS_GUI_COPY_STYLE(gs_gui_default_button_style, gs_gui_default_style);
- GS_GUI_COPY_STYLE(gs_gui_default_text_style, gs_gui_default_style);
- GS_GUI_COPY_STYLE(gs_gui_default_label_style, gs_gui_default_style);
- GS_GUI_COPY_STYLE(gs_gui_default_panel_style, gs_gui_default_style);
- GS_GUI_COPY_STYLE(gs_gui_default_input_style, gs_gui_default_style);
- GS_GUI_COPY_STYLE(gs_gui_default_scroll_style, gs_gui_default_style);
- GS_GUI_COPY_STYLE(gs_gui_default_image_style, gs_gui_default_style);
- gs_gui_style_t* style = NULL;
- // button
- for (uint32_t i = 0; i < 3; ++i) {
- style = &gs_gui_default_button_style[i];
- style->justify_content = GS_GUI_JUSTIFY_CENTER;
- }
- gs_gui_default_button_style[GS_GUI_ELEMENT_STATE_DEFAULT].colors[GS_GUI_COLOR_BACKGROUND] = gs_color(35, 35, 35, 255);
- gs_gui_default_button_style[GS_GUI_ELEMENT_STATE_HOVER].colors[GS_GUI_COLOR_BACKGROUND] = gs_color(40, 40, 40, 255);
- gs_gui_default_button_style[GS_GUI_ELEMENT_STATE_FOCUS].colors[GS_GUI_COLOR_BACKGROUND] = gs_color(0, 214, 121, 255);
- // panel
- for (uint32_t i = 0; i < 3; ++i) {
- style = &gs_gui_default_panel_style[i];
- style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(30, 30, 30, 255);
- style->size[1] = 19;
- }
- // input
- for (uint32_t i = 0; i < 3; ++i) {
- style = &gs_gui_default_input_style[i];
- style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(20, 20, 20, 255);
- style->size[1] = 19;
- }
- // text
- for (uint32_t i = 0; i < 3; ++i) {
- style = &gs_gui_default_text_style[i];
- style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(0, 0, 0, 0);
- style->colors[GS_GUI_COLOR_CONTENT] = GS_COLOR_WHITE;
- }
- // label
- for (uint32_t i = 0; i < 3; ++i) {
- style = &gs_gui_default_label_style[i];
- style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(0, 0, 0, 0);
- style->colors[GS_GUI_COLOR_CONTENT] = GS_COLOR_WHITE;
- style->size[1] = 19;
- }
- // scroll
- for (uint32_t i = 0; i < 3; ++i) {
- style = &gs_gui_default_scroll_style[i];
- style->size[0] = 10;
- style->padding[GS_GUI_PADDING_RIGHT] = 4;
- }
- style = &gs_gui_default_scroll_style[GS_GUI_ELEMENT_STATE_DEFAULT];
- style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(22, 22, 22, 255);
- style->colors[GS_GUI_COLOR_CONTENT] = gs_color(255, 255, 255, 100);
- #define GS_GUI_COPY(DST, SRC)\
- do {\
- DST[0x00] = SRC[0x00];\
- DST[0x01] = SRC[0x01];\
- DST[0x02] = SRC[0x02];\
- } while (0)
- GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_CONTAINER], gs_gui_default_container_style);
- GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_LABEL], gs_gui_default_label_style);
- GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_TEXT], gs_gui_default_text_style);
- GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_PANEL], gs_gui_default_panel_style);
- GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_INPUT], gs_gui_default_input_style);
- GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_BUTTON], gs_gui_default_button_style);
- GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_SCROLL], gs_gui_default_scroll_style);
- GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_IMAGE], gs_gui_default_image_style);
- ctx->style_sheet = &gs_gui_default_style_sheet;
- ctx->style = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][0x00];
- }
- static char button_map[256] = gs_default_val();
- static char key_map[512] = gs_default_val();
- GS_API_DECL gs_gui_context_t gs_gui_new(uint32_t window_hndl)
- {
- gs_gui_context_t ctx = gs_default_val();
- gs_gui_init(&ctx, window_hndl);
- return ctx;
- }
- GS_API_DECL void gs_gui_init(gs_gui_context_t *ctx, uint32_t window_hndl)
- {
- memset(ctx, 0, sizeof(*ctx));
- ctx->gsi = gs_immediate_draw_new();
- ctx->overlay_draw_list = gs_immediate_draw_new();
- gs_gui_init_default_styles(ctx);
- ctx->window_hndl = window_hndl;
- ctx->last_zindex = 1000;
- gs_slot_array_reserve(ctx->splits, GS_GUI_GS_GUI_SPLIT_SIZE);
- gs_gui_split_t split = gs_default_val();
- gs_slot_array_insert(ctx->splits, split); // First item is set for 0x00 invalid
- gs_slot_array_reserve(ctx->tab_bars, GS_GUI_GS_GUI_TAB_SIZE);
- gs_gui_tab_bar_t tb = gs_default_val();
- gs_slot_array_insert(ctx->tab_bars, tb);
- button_map[GS_MOUSE_LBUTTON & 0xff] = GS_GUI_MOUSE_LEFT;
- button_map[GS_MOUSE_RBUTTON & 0xff] = GS_GUI_MOUSE_RIGHT;
- button_map[GS_MOUSE_MBUTTON & 0xff] = GS_GUI_MOUSE_MIDDLE;
- key_map[GS_KEYCODE_LEFT_SHIFT & 0xff] = GS_GUI_KEY_SHIFT;
- key_map[GS_KEYCODE_RIGHT_SHIFT & 0xff] = GS_GUI_KEY_SHIFT;
- key_map[GS_KEYCODE_LEFT_CONTROL & 0xff] = GS_GUI_KEY_CTRL;
- key_map[GS_KEYCODE_RIGHT_CONTROL & 0xff] = GS_GUI_KEY_CTRL;
- key_map[GS_KEYCODE_LEFT_ALT & 0xff] = GS_GUI_KEY_ALT;
- key_map[GS_KEYCODE_RIGHT_ALT & 0xff] = GS_GUI_KEY_ALT;
- key_map[GS_KEYCODE_ENTER & 0xff] = GS_GUI_KEY_RETURN;
- key_map[GS_KEYCODE_BACKSPACE & 0xff] = GS_GUI_KEY_BACKSPACE;
- }
- GS_API_DECL void
- gs_gui_init_font_stash(gs_gui_context_t* ctx, gs_gui_font_stash_desc_t* desc)
- {
- gs_hash_table_clear(ctx->font_stash);
- uint32_t ct = sizeof(gs_gui_font_desc_t) / desc->size;
- for (uint32_t i = 0; i < ct; ++i) {
- gs_hash_table_insert(ctx->font_stash, gs_hash_str64(desc->fonts[i].key), desc->fonts[i].font);
- }
- }
- GS_API_DECL gs_gui_context_t
- gs_gui_context_new(uint32_t window_hndl)
- {
- gs_gui_context_t gui = gs_default_val();
- gs_gui_init(&gui, window_hndl);
- return gui;
- }
- GS_API_DECL void
- gs_gui_free(gs_gui_context_t* ctx)
- {
- /*
- typedef struct gs_gui_context_t
- {
- // Core state
- gs_gui_style_t* style; // Active style
- gs_gui_style_sheet_t* style_sheet; // Active style sheet
- gs_gui_id hover;
- gs_gui_id focus;
- gs_gui_id last_id;
- gs_gui_id state_switch_id; // Id that had a state switch
- int32_t switch_state;
- gs_gui_id lock_focus;
- int32_t last_hover_state;
- int32_t last_focus_state;
- gs_gui_id prev_hover;
- gs_gui_id prev_focus;
- gs_gui_rect_t last_rect;
- int32_t last_zindex;
- int32_t updated_focus;
- int32_t frame;
- gs_vec2 framebuffer_size;
- gs_gui_rect_t viewport;
- gs_gui_container_t* active_root;
- gs_gui_container_t* hover_root;
- gs_gui_container_t* next_hover_root;
- gs_gui_container_t* scroll_target;
- gs_gui_container_t* focus_root;
- gs_gui_container_t* next_focus_root;
- gs_gui_container_t* dockable_root;
- gs_gui_container_t* prev_dockable_root;
- gs_gui_container_t* docked_root;
- gs_gui_container_t* undock_root;
- gs_gui_split_t* focus_split;
- gs_gui_split_t* next_hover_split;
- gs_gui_split_t* hover_split;
- gs_gui_id next_lock_hover_id;
- gs_gui_id lock_hover_id;
- char number_edit_buf[GS_GUI_MAX_FMT];
- gs_gui_id number_edit;
- gs_gui_alt_drag_mode_type alt_drag_mode;
- gs_dyn_array(gs_gui_request_t) requests;
- // Stacks
- gs_gui_stack(uint8_t, GS_GUI_COMMANDLIST_SIZE) command_list;
- gs_gui_stack(gs_gui_container_t*, GS_GUI_ROOTLIST_SIZE) root_list;
- gs_gui_stack(gs_gui_container_t*, GS_GUI_CONTAINERSTACK_SIZE) container_stack;
- gs_gui_stack(gs_gui_rect_t, GS_GUI_CLIPSTACK_SIZE) clip_stack;
- gs_gui_stack(gs_gui_id, GS_GUI_IDSTACK_SIZE) id_stack;
- gs_gui_stack(gs_gui_layout_t, GS_GUI_LAYOUTSTACK_SIZE) layout_stack;
- // Style sheet element stacks
- gs_hash_table(gs_gui_element_type, gs_gui_inline_style_stack_t) inline_styles;
- // Retained state pools
- gs_gui_pool_item_t container_pool[GS_GUI_CONTAINERPOOL_SIZE];
- gs_gui_container_t containers[GS_GUI_CONTAINERPOOL_SIZE];
- gs_gui_pool_item_t treenode_pool[GS_GUI_TREENODEPOOL_SIZE];
- gs_slot_array(gs_gui_split_t) splits;
- gs_slot_array(gs_gui_tab_bar_t) tab_bars;
- // Input state
- gs_vec2 mouse_pos;
- gs_vec2 last_mouse_pos;
- gs_vec2 mouse_delta;
- gs_vec2 scroll_delta;
- int32_t mouse_down;
- int32_t mouse_pressed;
- int32_t key_down;
- int32_t key_pressed;
- char input_text[32];
- // Backend resources
- uint32_t window_hndl;
- gs_immediate_draw_t gsi;
- gs_immediate_draw_t overlay_draw_list;
- // Active Transitions
- gs_hash_table(gs_gui_id, gs_gui_animation_t) animations;
- // Font stash
- gs_hash_table(uint64_t, gs_asset_font_t*) font_stash;
- // Callbacks
- struct {
- gs_gui_on_draw_button_callback button;
- } callbacks;
- } gs_gui_context_t;
- */
- gs_hash_table_free(ctx->font_stash);
- gs_immediate_draw_free(&ctx->gsi);
- gs_immediate_draw_free(&ctx->overlay_draw_list);
- gs_hash_table_free(ctx->animations);
- gs_slot_array_free(ctx->splits);
- gs_slot_array_free(ctx->tab_bars);
- gs_hash_table_free(ctx->inline_styles);
- // Inline style stacks
- for(
- gs_hash_table_iter it = gs_hash_table_iter_new(ctx->inline_styles);
- gs_hash_table_iter_valid(ctx->inline_styles, it);
- gs_hash_table_iter_advance(ctx->inline_styles, it)
- )
- {
- gs_gui_inline_style_stack_t* stack = gs_hash_table_iter_getp(ctx->inline_styles, it);
- for (uint32_t i = 0; i < 3; ++i) {
- gs_dyn_array_free(stack->styles[i]);
- gs_dyn_array_free(stack->animations[i]);
- }
- gs_dyn_array_free(stack->style_counts); // amount of styles to pop off at "top of stack" for each state
- gs_dyn_array_free(stack->animation_counts); // amount of animations to pop off at "top of stack" for each state
- }
- gs_dyn_array_free(ctx->requests);
- }
- static void gs_gui_draw_splits(gs_gui_context_t* ctx, gs_gui_split_t* split)
- {
- if (!split) return;
-
- gs_gui_split_t* root_split = gs_gui_get_root_split_from_split(ctx, split);
- // Draw split
- const gs_gui_rect_t* sr = &split->rect;
- gs_vec2 hd = gs_v2(sr->w * 0.5f, sr->h * 0.5f);
- gs_gui_rect_t r = gs_default_val();
- gs_color_t c = gs_color(0, 0, 0, 0);
- const float ratio = split->ratio;
- gs_gui_container_t* top = gs_gui_get_top_most_container(ctx, root_split);
- gs_gui_container_t* hover_cnt = ctx->hover_root ? ctx->hover_root :
- ctx->next_hover_root ? ctx->next_hover_root :
- NULL;
- bool valid_hover = hover_cnt && hover_cnt->zindex > top->zindex;
- valid_hover = false;
- bool valid = true;
- split->frame = ctx->frame;
- root_split->frame = ctx->frame;
- bool can_draw = true;
- for (uint32_t i = 0; i < 2; ++i)
- {
- if (
- split->children[i].type == GS_GUI_SPLIT_NODE_CONTAINER && can_draw
- )
- {
- gs_gui_container_t* cnt = split->children[i].container;
- // Don't draw split if this container belongs to a dockspace
- if (cnt->opt & GS_GUI_OPT_DOCKSPACE)
- {
- can_draw = false;
- continue;
- }
- switch (split->type)
- {
- case GS_GUI_SPLIT_LEFT:
- {
- r = gs_gui_rect(sr->x + sr->w * ratio - GS_GUI_SPLIT_SIZE * 0.5f, sr->y + GS_GUI_SPLIT_SIZE, GS_GUI_SPLIT_SIZE, sr->h - 2.f * GS_GUI_SPLIT_SIZE);
- } break;
- case GS_GUI_SPLIT_RIGHT:
- {
- r = gs_gui_rect(sr->x + sr->w * (1.f - ratio) - GS_GUI_SPLIT_SIZE * 0.5f, sr->y + GS_GUI_SPLIT_SIZE, GS_GUI_SPLIT_SIZE, sr->h - 2.f * GS_GUI_SPLIT_SIZE);
- } break;
- case GS_GUI_SPLIT_TOP:
- {
- r = gs_gui_rect(sr->x + GS_GUI_SPLIT_SIZE, sr->y + sr->h * (ratio) - GS_GUI_SPLIT_SIZE * 0.5f, sr->w - 2.f * GS_GUI_SPLIT_SIZE, GS_GUI_SPLIT_SIZE);
- } break;
- case GS_GUI_SPLIT_BOTTOM:
- {
- r = gs_gui_rect(sr->x + GS_GUI_SPLIT_SIZE, sr->y + sr->h * (1.f - ratio) - GS_GUI_SPLIT_SIZE * 0.5f, sr->w - 2.f * GS_GUI_SPLIT_SIZE, GS_GUI_SPLIT_SIZE);
- } break;
- }
- int16_t exp[4] = {1, 1, 1, 1};
- gs_gui_rect_t expand = gs_gui_expand_rect(r, exp);
- bool hover = !valid_hover && !ctx->focus && !ctx->prev_dockable_root && gs_gui_rect_overlaps_vec2(expand, ctx->mouse_pos) && !ctx->lock_hover_id;
- if (hover) ctx->next_hover_split = split;
- if (hover && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- if (!ctx->focus_split) ctx->focus_split = split;
- }
- bool active = ctx->focus_split == split;
- if (active && valid)
- {
- ctx->next_hover_root = top;
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_SPLIT_RATIO;
- req.split = split;
- gs_dyn_array_push(ctx->requests, req);
- }
- if (
- (hover && (ctx->focus_split == split)) ||
- (hover && !ctx->focus_split) ||
- active
- )
- {
- gs_color_t bc = ctx->focus_split == split ?
- ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_FOCUS].colors[GS_GUI_COLOR_BACKGROUND] :
- ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_HOVER].colors[GS_GUI_COLOR_BACKGROUND];
- gs_gui_draw_rect(ctx, r, bc);
- can_draw = false;
- }
- }
- else if (
- split->children[i].type == GS_GUI_SPLIT_NODE_SPLIT
- )
- {
- if (can_draw)
- {
- switch (split->type)
- {
- case GS_GUI_SPLIT_LEFT:
- {
- r = gs_gui_rect(sr->x + sr->w * ratio - GS_GUI_SPLIT_SIZE * 0.5f, sr->y + GS_GUI_SPLIT_SIZE, GS_GUI_SPLIT_SIZE, sr->h - 2.f * GS_GUI_SPLIT_SIZE);
- } break;
- case GS_GUI_SPLIT_RIGHT:
- {
- r = gs_gui_rect(sr->x + sr->w * (1.f - ratio) - GS_GUI_SPLIT_SIZE * 0.5f, sr->y + GS_GUI_SPLIT_SIZE, GS_GUI_SPLIT_SIZE, sr->h - 2.f * GS_GUI_SPLIT_SIZE);
- } break;
- case GS_GUI_SPLIT_TOP:
- {
- r = gs_gui_rect(sr->x + GS_GUI_SPLIT_SIZE, sr->y + sr->h * (ratio) - GS_GUI_SPLIT_SIZE * 0.5f, sr->w - 2.f * GS_GUI_SPLIT_SIZE, GS_GUI_SPLIT_SIZE);
- } break;
- case GS_GUI_SPLIT_BOTTOM:
- {
- r = gs_gui_rect(sr->x + GS_GUI_SPLIT_SIZE, sr->y + sr->h * (1.f - ratio) - GS_GUI_SPLIT_SIZE * 0.5f, sr->w - 2.f * GS_GUI_SPLIT_SIZE, GS_GUI_SPLIT_SIZE);
- } break;
- }
- int16_t exp[] = {1, 1, 1, 1};
- gs_gui_rect_t expand = gs_gui_expand_rect(r, exp);
- bool hover = !valid_hover && !ctx->focus && !ctx->prev_dockable_root && gs_gui_rect_overlaps_vec2(expand, ctx->mouse_pos);
- if (hover) ctx->next_hover_split = split;
- if (hover && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- if (!ctx->focus_split) ctx->focus_split = split;
- }
- bool active = ctx->focus_split == split;
- if (active)
- {
- ctx->next_hover_root = top;
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_SPLIT_RATIO;
- req.split = split;
- gs_dyn_array_push(ctx->requests, req);
- }
- if (
- (hover && (ctx->focus_split == split)) ||
- (hover && !ctx->focus_split) ||
- active
- )
- {
- gs_color_t bc = ctx->focus_split == split ?
- ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_FOCUS].colors[GS_GUI_COLOR_BACKGROUND] :
- ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_HOVER].colors[GS_GUI_COLOR_BACKGROUND];
- gs_gui_draw_rect(ctx, r, bc);
- can_draw = false;
- }
- }
- gs_gui_split_t* child = gs_slot_array_getp(ctx->splits, split->children[i].split);
- gs_gui_draw_splits(ctx, child);
- }
- }
- if (ctx->focus_split == split && ctx->mouse_down != GS_GUI_MOUSE_LEFT)
- {
- ctx->focus_split = NULL;
- }
- }
- static void gs_gui_get_split_lowest_zindex(gs_gui_context_t* ctx, gs_gui_split_t* split, int32_t* index)
- {
- if (!split) return;
- if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[0].container->zindex < *index)
- {
- *index = split->children[0].container->zindex;
- }
- if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[0].container->tab_bar)
- {
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, split->children[0].container->tab_bar);
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (((gs_gui_container_t*)tab_bar->items[i].data)->zindex < *index) *index = ((gs_gui_container_t*)tab_bar->items[i].data)->zindex;
- }
- }
- if (split->children[1].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[1].container->zindex < *index)
- {
- *index = split->children[1].container->zindex;
- }
- if (split->children[1].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[1].container->tab_bar)
- {
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, split->children[1].container->tab_bar);
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (((gs_gui_container_t*)tab_bar->items[i].data)->zindex < *index) *index = ((gs_gui_container_t*)tab_bar->items[i].data)->zindex;
- }
- }
- if (split->children[0].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- gs_gui_get_split_lowest_zindex(ctx, gs_slot_array_getp(ctx->splits, split->children[0].split), index);
- }
- if (split->children[1].type == GS_GUI_SPLIT_NODE_SPLIT)
- {
- gs_gui_get_split_lowest_zindex(ctx, gs_slot_array_getp(ctx->splits, split->children[1].split), index);
- }
- }
- GS_API_DECL void
- gs_gui_begin(gs_gui_context_t* ctx, const gs_gui_hints_t* hints)
- {
- gs_gui_hints_t default_hints = gs_default_val();
- default_hints.framebuffer_size = gs_platform_framebuffer_sizev(ctx->window_hndl);
- default_hints.viewport = gs_gui_rect(0.f, 0.f, default_hints.framebuffer_size.x, default_hints.framebuffer_size.y);
- gs_gui_hints_t hint = hints ? *hints : default_hints;
- // Set up viewport
- gs_gui_rect_t vp = hint.viewport;
- if (~hint.flags & GS_GUI_HINT_FLAG_NO_INVERT_Y) {
- vp.y = hint.viewport.y;
- } else {
- vp.y = hint.framebuffer_size.y - hint.viewport.h - hint.viewport.y;
- }
-
- // Capture event information
- gs_vec2 mouse_pos = gs_platform_mouse_positionv();
- // Check for scale and bias
- if (hint.flags & GS_GUI_HINT_FLAG_NO_SCALE_BIAS_MOUSE) {
- float px = (mouse_pos.x - vp.x) / vp.w;
- float py = (mouse_pos.y - vp.y) / vp.h;
- float xv = vp.w * px;
- float yv = vp.h * py;
- mouse_pos = gs_v2(xv, yv);
- }
- else {
- gs_vec2 fb_vp_ratio = gs_v2(hint.framebuffer_size.x / vp.w, hint.framebuffer_size.y / vp.h);
- float px = mouse_pos.x - (vp.x * fb_vp_ratio.x);
- float py = mouse_pos.y + (vp.y * fb_vp_ratio.y);
- float xv = px / fb_vp_ratio.x;
- float yv = py / fb_vp_ratio.y;
- mouse_pos = gs_v2(xv, yv);
- }
- gs_platform_event_t evt = gs_default_val();
- while (gs_platform_poll_events(&evt, false))
- {
- switch (evt.type)
- {
- case GS_PLATFORM_EVENT_MOUSE:
- {
- switch (evt.mouse.action)
- {
- case GS_PLATFORM_MOUSE_MOVE:
- {
- // ctx->mouse_pos = evt.mouse.move;
- } break;
- case GS_PLATFORM_MOUSE_WHEEL:
- {
- gs_gui_input_scroll(ctx, (int32_t)(-evt.mouse.wheel.x * 30.f), (int32_t)(-evt.mouse.wheel.y * 30.f));
- } break;
- case GS_PLATFORM_MOUSE_BUTTON_DOWN:
- case GS_PLATFORM_MOUSE_BUTTON_PRESSED: {
- int32_t code = 1 << evt.mouse.button;
- gs_gui_input_mousedown(ctx, (int32_t)mouse_pos.x, (int32_t)mouse_pos.y, code);
- } break;
- case GS_PLATFORM_MOUSE_BUTTON_RELEASED:
- {
- int32_t code = 1 << evt.mouse.button;
- gs_gui_input_mouseup(ctx, (int32_t)mouse_pos.x, (int32_t)mouse_pos.y, code);
- } break;
- case GS_PLATFORM_MOUSE_ENTER:
- {
- // If there are user callbacks, could trigger them here
- } break;
- case GS_PLATFORM_MOUSE_LEAVE:
- {
- // If there are user callbacks, could trigger them here
- } break;
- default: break;
- }
- } break;
- case GS_PLATFORM_EVENT_TEXT:
- {
- // Input text
- char txt[2] = {(char)(evt.text.codepoint & 255), 0};
- gs_gui_input_text(ctx, txt);
- } break;
- case GS_PLATFORM_EVENT_KEY:
- {
- switch (evt.key.action)
- {
- case GS_PLATFORM_KEY_DOWN:
- case GS_PLATFORM_KEY_PRESSED:
- {
- gs_gui_input_keydown(ctx, key_map[evt.key.keycode & 511]);
- } break;
- case GS_PLATFORM_KEY_RELEASED:
- {
- gs_gui_input_keyup(ctx, key_map[evt.key.keycode & 511]);
- } break;
- default: break;
- }
- } break;
- case GS_PLATFORM_EVENT_WINDOW:
- {
- switch (evt.window.action)
- {
- default: break;
- }
- } break;
- default: break;
- }
- }
- ctx->framebuffer_size = hint.framebuffer_size;
- ctx->viewport = gs_gui_rect(hint.viewport.x, hint.viewport.y, hint.viewport.w, hint.viewport.h);
- ctx->mouse_pos = mouse_pos;
- ctx->command_list.idx = 0;
- ctx->root_list.idx = 0;
- ctx->scroll_target = NULL;
- ctx->hover_root = ctx->next_hover_root;
- ctx->next_hover_root = NULL;
- ctx->focus_root = ctx->next_focus_root;
- ctx->next_focus_root = NULL;
- ctx->prev_dockable_root = ctx->dockable_root;
- ctx->dockable_root = NULL;
- ctx->hover_split = ctx->next_hover_split;
- ctx->next_hover_split = NULL;
- ctx->lock_hover_id = ctx->next_lock_hover_id;
- ctx->next_lock_hover_id = 0x00;
- ctx->mouse_delta.x = ctx->mouse_pos.x - ctx->last_mouse_pos.x;
- ctx->mouse_delta.y = ctx->mouse_pos.y - ctx->last_mouse_pos.y;
- ctx->frame++;
- // Set up overlay draw list
- gs_vec2 fbs = ctx->framebuffer_size;
- gsi_defaults(&ctx->overlay_draw_list);
- gsi_camera2D(&ctx->overlay_draw_list, (uint32_t)ctx->viewport.w, (uint32_t)ctx->viewport.h); // Need to pass in a viewport for this instead
- /*
- for (
- gs_slot_array_iter it = gs_slot_array_iter_new(ctx->splits);
- gs_slot_array_iter_valid(ctx->splits, it);
- gs_slot_array_iter_advance(ctx->splits, it)
- )
- {
- if (!it) continue;
- gs_gui_split_t* split = gs_slot_array_iter_getp(ctx->splits, it);
- // Root split
- if (!split->parent)
- {
- gs_gui_container_t* root_cnt = gs_gui_get_root_container_from_split(ctx, split);
- gs_gui_rect_t r = split->rect;
- r.x -= 10.f;
- r.w += 20.f;
- r.y -= 10.f;
- r.h += 20.f;
- gs_snprintfc(TMP, 256, "!dockspace%zu", (size_t)split);
- uint64_t opt = GS_GUI_OPT_NOFRAME |
- GS_GUI_OPT_FORCESETRECT |
- GS_GUI_OPT_NOMOVE |
- GS_GUI_OPT_NOTITLE |
- GS_GUI_OPT_NOSCROLL |
- GS_GUI_OPT_NOCLIP |
- GS_GUI_OPT_NODOCK |
- GS_GUI_OPT_DOCKSPACE |
- GS_GUI_OPT_NOBORDER;
- gs_gui_window_begin_ex(ctx, TMP, r, NULL, opt);
- {
- // Set zindex for sorting (always below the bottom most window in this split tree)
- gs_gui_container_t* ds = gs_gui_get_current_container(ctx);
- int32_t zindex = INT32_MAX - 1;
- gs_gui_get_split_lowest_zindex(ctx, split, &zindex);
- if (root_cnt->opt & GS_GUI_OPT_DOCKSPACE) ds->zindex = 0;
- else ds->zindex = gs_clamp((int32_t)zindex - 1, 0, INT32_MAX);
- gs_gui_rect_t fr = split->rect;
- fr.x += GS_GUI_SPLIT_SIZE; fr.y += GS_GUI_SPLIT_SIZE; fr.w -= 2.f * GS_GUI_SPLIT_SIZE; fr.h -= 2.f * GS_GUI_SPLIT_SIZE;
- // gs_gui_draw_frame(ctx, fr, &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][0x00]);
- // Draw splits
- gs_gui_draw_splits(ctx, split);
- // Do resize controls for dockspace
- gs_gui_container_t* top = gs_gui_get_top_most_container(ctx, split);
- const gs_gui_rect_t* sr = &split->rect;
- gs_gui_container_t* hover_cnt = ctx->hover_root ? ctx->hover_root :
- ctx->next_hover_root ? ctx->next_hover_root :
- NULL;
- bool valid_hover = hover_cnt && hover_cnt->zindex > top->zindex;
- // W
- {
- // Cache rect
- gs_gui_rect_t lr = gs_gui_rect(fr.x - 2.f * GS_GUI_SPLIT_SIZE, fr.y, GS_GUI_SPLIT_SIZE, fr.h);
- gs_gui_rect_t ex = lr;
- ex.x -= 10.f; ex.w += 20.f;
- gs_gui_id id = gs_gui_get_id(ctx, "!hov_l", 6);
- gs_gui_update_control(ctx, id, ex, opt);
- if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- gs_gui_draw_control_frame(ctx, id, lr, GS_GUI_ELEMENT_BUTTON, 0x00);
- ctx->next_hover_root = top;
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_SPLIT_RESIZE_W;
- req.split = split;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
-
- // E
- {
- // Cache rect
- gs_gui_rect_t rr = gs_gui_rect(fr.x + fr.w + GS_GUI_SPLIT_SIZE, fr.y, GS_GUI_SPLIT_SIZE, fr.h);
- gs_gui_rect_t ex = rr;
- ex.w += 20.f;
- gs_gui_id id = gs_gui_get_id(ctx, "!hov_r", 6);
- gs_gui_update_control(ctx, id, ex, opt);
- if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- gs_gui_draw_control_frame(ctx, id, rr, GS_GUI_ELEMENT_BUTTON, 0x00);
- ctx->next_hover_root = top;
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_SPLIT_RESIZE_E;
- req.split = split;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
- // N
- {
- // Cache rect
- gs_gui_rect_t tr = gs_gui_rect(fr.x, fr.y - 2.f * GS_GUI_SPLIT_SIZE, fr.w, GS_GUI_SPLIT_SIZE);
- gs_gui_rect_t ex = tr;
- ex.y -= 10.f;
- ex.h += 20.f;
- gs_gui_id id = gs_gui_get_id(ctx, "!hov_t", 6);
- gs_gui_update_control(ctx, id, ex, opt);
- if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- gs_gui_draw_control_frame(ctx, id, tr, GS_GUI_ELEMENT_BUTTON, 0x00);
- ctx->next_hover_root = top;
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_SPLIT_RESIZE_N;
- req.split = split;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
- // S
- {
- // Cache rect
- gs_gui_rect_t br = gs_gui_rect(fr.x, fr.y + fr.h + GS_GUI_SPLIT_SIZE, fr.w, GS_GUI_SPLIT_SIZE);
- gs_gui_rect_t ex = br;
- ex.h += 20.f;
- gs_gui_id id = gs_gui_get_id(ctx, "!hov_b", 6);
- gs_gui_update_control(ctx, id, ex, opt);
- if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- gs_gui_draw_control_frame(ctx, id, br, GS_GUI_ELEMENT_BUTTON, 0x00);
- ctx->next_hover_root = top;
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_SPLIT_RESIZE_S;
- req.split = split;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
- }
- gs_gui_window_end(ctx);
- }
- }
- */
- if (ctx->mouse_down != GS_GUI_MOUSE_LEFT)
- {
- ctx->lock_focus = 0x00;
- }
- }
- static void gs_gui_docking(gs_gui_context_t* ctx)
- {
- if (ctx->undock_root)
- {
- gs_gui_undock_ex_cnt(ctx, ctx->undock_root);
- }
- if (!ctx->focus_root || ctx->focus_root->opt & GS_GUI_OPT_NODOCK) return;
- if (ctx->dockable_root || ctx->prev_dockable_root)
- {
- gs_gui_container_t* cnt = ctx->dockable_root ? ctx->dockable_root : ctx->prev_dockable_root;
- if ( ctx->prev_dockable_root && !ctx->dockable_root && ctx->mouse_down != GS_GUI_MOUSE_LEFT )
- {
- int32_t b = 0;
- }
- // Cache hoverable tile information
- gs_vec2 c = gs_v2(cnt->rect.x + cnt->rect.w / 2.f, cnt->rect.y + cnt->rect.h / 2.f);
- const float sz = gs_clamp(gs_min(cnt->rect.w * 0.1f, cnt->rect.h * 0.1f), 15.f, 25.f);
- const float off = sz + sz * 0.2f;
- gs_color_t def_col = gs_color_alpha(ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_FOCUS].colors[GS_GUI_COLOR_BACKGROUND], 100);
- gs_color_t hov_col = gs_color_alpha(ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_FOCUS].colors[GS_GUI_COLOR_BACKGROUND], 150);
- gs_gui_rect_t center = gs_gui_rect(c.x, c.y, sz, sz);
- gs_gui_rect_t left = gs_gui_rect(c.x - off, c.y, sz, sz);
- gs_gui_rect_t right = gs_gui_rect(c.x + off, c.y, sz, sz);
- gs_gui_rect_t top = gs_gui_rect(c.x, c.y - off, sz, sz);
- gs_gui_rect_t bottom = gs_gui_rect(c.x, c.y + off, sz, sz);
- int32_t hov_c = (gs_gui_rect_overlaps_vec2(center, ctx->mouse_pos));
- int32_t hov_l = gs_gui_rect_overlaps_vec2(left, ctx->mouse_pos);
- int32_t hov_r = gs_gui_rect_overlaps_vec2(right, ctx->mouse_pos);
- int32_t hov_t = gs_gui_rect_overlaps_vec2(top, ctx->mouse_pos);
- int32_t hov_b = gs_gui_rect_overlaps_vec2(bottom, ctx->mouse_pos);
- int32_t hov_w = gs_gui_rect_overlaps_vec2(cnt->rect, ctx->mouse_pos);
- bool can_dock = true;
- // Can't dock one dockspace into another
- if (ctx->focus_root->opt & GS_GUI_OPT_DOCKSPACE)
- {
- can_dock = false;
- }
- if (ctx->focus_root->tab_bar)
- {
- gs_gui_container_t* tcmp = ctx->dockable_root ? ctx->dockable_root : ctx->prev_dockable_root;
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, ctx->focus_root->tab_bar);
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- gs_gui_container_t* tcnt = (gs_gui_container_t*)tab_bar->items[i].data;
- if (tcnt == tcmp)
- {
- can_dock = false;
- }
- }
- }
- // Need to make sure you CAN dock here first
- if (ctx->dockable_root && can_dock)
- {
- // Need to now grab overlay draw list, then draw rects into it
- gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
- bool is_dockspace = ctx->dockable_root->opt & GS_GUI_OPT_DOCKSPACE;
- // Draw center rect
- gsi_rectvd(dl, gs_v2(center.x, center.y), gs_v2(center.w, center.h), gs_v2s(0.f), gs_v2s(1.f), hov_c ? hov_col : def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- // gsi_rectvd(dl, gs_v2(center.x, center.y), gs_v2(center.w + 1, center.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- if (!is_dockspace)
- {
- gsi_rectvd(dl, gs_v2(left.x, left.y), gs_v2(left.w, left.h), gs_v2s(0.f), gs_v2s(1.f), hov_l ? hov_col : def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- // gsi_rectvd(dl, gs_v2(left.x, left.y), gs_v2(left.w, left.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_rectvd(dl, gs_v2(right.x, right.y), gs_v2(right.w, right.h), gs_v2s(0.f), gs_v2s(1.f), hov_r ? hov_col : def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- // gsi_rectvd(dl, gs_v2(right.x, right.y), gs_v2(right.w, right.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_rectvd(dl, gs_v2(top.x, top.y), gs_v2(top.w, top.h), gs_v2s(0.f), gs_v2s(1.f), hov_t ? hov_col : def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- // gsi_rectvd(dl, gs_v2(top.x, top.y), gs_v2(top.w, top.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_rectvd(dl, gs_v2(bottom.x, bottom.y), gs_v2(bottom.w, bottom.h), gs_v2s(0.f), gs_v2s(1.f), hov_b ? hov_col : def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- // gsi_rectvd(dl, gs_v2(bottom.x, bottom.y), gs_v2(bottom.w, bottom.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- }
- const float d = 0.5f;
- const float hs = sz * 0.5f;
- if (is_dockspace)
- {
- if (hov_c)
- {
- center = gs_gui_rect(cnt->rect.x, cnt->rect.y, cnt->rect.w, cnt->rect.h);
- gsi_rectvd(dl, gs_v2(center.x, center.y), gs_v2(center.w, center.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_rectvd(dl, gs_v2(center.x, center.y), gs_v2(center.w, center.h), gs_v2s(0.f), gs_v2s(1.f), def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- }
- }
- else
- {
- if (hov_c && !ctx->focus_root->split)
- {
- center = gs_gui_rect(cnt->rect.x, cnt->rect.y, cnt->rect.w, cnt->rect.h);
- gsi_rectvd(dl, gs_v2(center.x, center.y), gs_v2(center.w, center.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_rectvd(dl, gs_v2(center.x, center.y), gs_v2(center.w, center.h), gs_v2s(0.f), gs_v2s(1.f), def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- }
- else if (hov_l)
- {
- left = gs_gui_rect(cnt->rect.x, cnt->rect.y, cnt->rect.w * d + hs, cnt->rect.h);
- gsi_rectvd(dl, gs_v2(left.x, left.y), gs_v2(left.w, left.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_rectvd(dl, gs_v2(left.x, left.y), gs_v2(left.w, left.h), gs_v2s(0.f), gs_v2s(1.f), def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- }
- else if (hov_r)
- {
- right = gs_gui_rect(cnt->rect.x + cnt->rect.w * d + hs, cnt->rect.y, cnt->rect.w * (1.f - d) - hs, cnt->rect.h);
- gsi_rectvd(dl, gs_v2(right.x, right.y), gs_v2(right.w, right.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_rectvd(dl, gs_v2(right.x, right.y), gs_v2(right.w, right.h), gs_v2s(0.f), gs_v2s(1.f), def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- }
- else if (hov_b)
- {
- bottom = gs_gui_rect(cnt->rect.x, cnt->rect.y + cnt->rect.h * d + hs, cnt->rect.w, cnt->rect.h * (1.f - d) - hs);
- gsi_rectvd(dl, gs_v2(bottom.x, bottom.y), gs_v2(bottom.w, bottom.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_rectvd(dl, gs_v2(bottom.x, bottom.y), gs_v2(bottom.w, bottom.h), gs_v2s(0.f), gs_v2s(1.f), def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- }
- else if (hov_t)
- {
- top = gs_gui_rect(cnt->rect.x, cnt->rect.y, cnt->rect.w, cnt->rect.h * d + hs);
- gsi_rectvd(dl, gs_v2(top.x, top.y), gs_v2(top.w, top.h), gs_v2s(0.f), gs_v2s(1.f), hov_col, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_rectvd(dl, gs_v2(top.x, top.y), gs_v2(top.w, top.h), gs_v2s(0.f), gs_v2s(1.f), def_col, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- }
- }
- }
- // Handle docking
- if (ctx->prev_dockable_root && !ctx->dockable_root && ctx->mouse_down != GS_GUI_MOUSE_LEFT)
- {
- gs_gui_container_t* parent = ctx->prev_dockable_root;
- gs_gui_container_t* child = ctx->focus_root;
- bool is_dockspace = ctx->prev_dockable_root->opt & GS_GUI_OPT_DOCKSPACE;
- if (is_dockspace)
- {
- if (hov_c) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_TOP, 1.0f);
- }
- else
- {
- if (hov_c && !ctx->focus_root->split) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_TAB, 0.5f);
- else if (hov_l) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_LEFT, 0.5f);
- else if (hov_r) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_RIGHT, 0.5f);
- else if (hov_t) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_TOP, 0.5f);
- else if (hov_b) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_BOTTOM, 0.5f);
- }
- }
- }
- }
- GS_API_DECL void gs_gui_end(gs_gui_context_t *ctx)
- {
- int32_t i, n;
- // Check for docking, draw overlays
- gs_gui_docking(ctx);
- for (uint32_t r = 0; r < gs_dyn_array_size(ctx->requests); ++r)
- {
- const gs_gui_request_t* req = &ctx->requests[r];
- // If split moved, update position for next frame
- switch (req->type)
- {
- case GS_GUI_CNT_MOVE:
- {
- if (req->cnt)
- {
- req->cnt->rect.x += ctx->mouse_delta.x;
- req->cnt->rect.y += ctx->mouse_delta.y;
- if (req->cnt->tab_bar)
- {
- gs_gui_tab_bar_t* tb = gs_slot_array_getp(ctx->tab_bars, req->cnt->tab_bar);
- gs_assert(tb);
- tb->rect.x += ctx->mouse_delta.x;
- tb->rect.y += ctx->mouse_delta.y;
- }
- }
- } break;
- case GS_GUI_CNT_FOCUS:
- {
- if (!req->cnt) break;
- gs_gui_container_t* cnt = (gs_gui_container_t*)req->cnt;
- gs_assert(cnt);
- gs_gui_split_t* rs = gs_gui_get_root_split(ctx, cnt);
- if (cnt->tab_bar)
- {
- if (rs)
- {
- gs_gui_bring_split_to_front(ctx, rs);
- }
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
- gs_gui_tab_item_t* tab_item = &tab_bar->items[cnt->tab_item];
- gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
- fcnt->opt |= GS_GUI_OPT_NOHOVER;
- fcnt->opt |= GS_GUI_OPT_NOINTERACT;
- fcnt->flags &= ~GS_GUI_WINDOW_FLAGS_VISIBLE;
- tab_bar->focus = tab_item->idx;
- cnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
- // Bring all tab items to front
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- gs_gui_bring_to_front(ctx, (gs_gui_container_t*)tab_bar->items[i].data);
- }
- gs_gui_bring_to_front(ctx, cnt);
- }
- } break;
- case GS_GUI_SPLIT_MOVE:
- {
- if (req->split)
- {
- req->split->rect.x += ctx->mouse_delta.x;
- req->split->rect.y += ctx->mouse_delta.y;
- gs_gui_update_split(ctx, req->split);
- }
- } break;
- case GS_GUI_SPLIT_RESIZE_SE:
- {
- if (req->split)
- {
- gs_gui_rect_t* r = &req->split->rect;
- r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
- r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
- gs_gui_update_split(ctx, req->split);
- }
- } break;
- case GS_GUI_SPLIT_RESIZE_W:
- {
- if (req->split)
- {
- gs_gui_rect_t* r = &req->split->rect;
- float w = r->w;
- float max_x = r->x + r->w;
- r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
- if (fabsf(r->w - w) > 0.f)
- {
- r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
- }
- gs_gui_update_split(ctx, req->split);
- }
- } break;
- case GS_GUI_SPLIT_RESIZE_E:
- {
- if (req->split)
- {
- gs_gui_rect_t* r = &req->split->rect;
- r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
- gs_gui_update_split(ctx, req->split);
- }
- } break;
- case GS_GUI_SPLIT_RESIZE_N:
- {
- if (req->split)
- {
- gs_gui_rect_t* r = &req->split->rect;
- float h = r->h;
- float max_y = h + r->y;
- r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
- if (fabsf(r->h - h) > 0.f)
- {
- r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
- }
- gs_gui_update_split(ctx, req->split);
- }
- } break;
- case GS_GUI_SPLIT_RESIZE_NE:
- {
- if (req->split)
- {
- gs_gui_rect_t* r = &req->split->rect;
- r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
- float h = r->h;
- float max_y = h + r->y;
- r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
- if (fabsf(r->h - h) > 0.f)
- {
- r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
- }
- gs_gui_update_split(ctx, req->split);
- }
- } break;
- case GS_GUI_SPLIT_RESIZE_NW:
- {
- if (req->split)
- {
- gs_gui_rect_t* r = &req->split->rect;
- float h = r->h;
- float max_y = h + r->y;
- r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
- if (fabsf(r->h - h) > 0.f)
- {
- r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
- }
- float w = r->w;
- float max_x = r->x + r->w;
- r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
- if (fabsf(r->w - w) > 0.f)
- {
- r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
- }
- gs_gui_update_split(ctx, req->split);
- }
- } break;
- case GS_GUI_SPLIT_RESIZE_S:
- {
- if (req->split)
- {
- gs_gui_rect_t* r = &req->split->rect;
- r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
- gs_gui_update_split(ctx, req->split);
- }
- } break;
- case GS_GUI_SPLIT_RESIZE_SW:
- {
- if (req->split)
- {
- gs_gui_rect_t* r = &req->split->rect;
- float h = r->h;
- float max_y = h + r->y;
- r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
- float w = r->w;
- float max_x = r->x + r->w;
- r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
- if (fabsf(r->w - w) > 0.f)
- {
- r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
- }
- gs_gui_update_split(ctx, req->split);
- }
- } break;
- case GS_GUI_SPLIT_RATIO:
- {
- const float smin = 0.05f;
- const float smax = 1.f - smin;
- gs_gui_split_t* split = req->split;
- switch (split->type)
- {
- case GS_GUI_SPLIT_LEFT:
- {
- split->ratio = gs_clamp(split->ratio + ctx->mouse_delta.x / split->rect.w, smin, smax);
- gs_gui_update_split(ctx, split);
- } break;
- case GS_GUI_SPLIT_RIGHT:
- {
- split->ratio = gs_clamp(split->ratio - ctx->mouse_delta.x / split->rect.w, smin, smax);
- gs_gui_update_split(ctx, split);
- } break;
- case GS_GUI_SPLIT_TOP:
- {
- split->ratio = gs_clamp(split->ratio + ctx->mouse_delta.y / split->rect.h, smin, smax);
- gs_gui_update_split(ctx, split);
- } break;
- case GS_GUI_SPLIT_BOTTOM:
- {
- split->ratio = gs_clamp(split->ratio - ctx->mouse_delta.y / split->rect.h, smin, smax);
- gs_gui_update_split(ctx, split);
- } break;
- }
- // Bring to font
- gs_gui_bring_split_to_front(ctx, gs_gui_get_root_split_from_split(ctx, split));
- } break;
- case GS_GUI_TAB_SWAP_LEFT:
- {
- gs_gui_tab_item_swap(ctx, req->cnt, -1);
- } break;
-
- case GS_GUI_TAB_SWAP_RIGHT:
- {
- gs_gui_tab_item_swap(ctx, req->cnt, +1);
- } break;
- }
- }
- // Clear reqests
- gs_dyn_array_clear(ctx->requests);
- // Check stacks
- gs_gui_expect(ctx->container_stack.idx == 0);
- gs_gui_expect(ctx->clip_stack.idx == 0);
- gs_gui_expect(ctx->id_stack.idx == 0);
- gs_gui_expect(ctx->layout_stack.idx == 0);
- // Have to clear style stacks
- // Set previous frame ids
- // ctx->prev_hover = 0;
- // ctx->prev_focus = ctx->focus;
- // Handle scroll input
- if (ctx->scroll_target)
- {
- ctx->scroll_target->scroll.x += ctx->scroll_delta.x;
- ctx->scroll_target->scroll.y += ctx->scroll_delta.y;
- }
- // Unset focus if focus id was not touched this frame
- if (!ctx->updated_focus) { ctx->focus = 0; }
- ctx->updated_focus = 0;
- // Bring hover root to front if mouse was pressed
- if (ctx->mouse_pressed && ctx->next_hover_root &&
- ctx->next_hover_root->zindex < ctx->last_zindex &&
- ctx->next_hover_root->zindex >= 0
- )
- {
- // Root split
- gs_gui_split_t* split = gs_gui_get_root_split(ctx, ctx->next_hover_root);
- // Need to bring entire dockspace to front
- if (split)
- {
- gs_gui_bring_split_to_front(ctx, split);
- }
- else if (~ctx->next_hover_root->opt & GS_GUI_OPT_NOFOCUS)
- {
- gs_gui_bring_to_front(ctx, ctx->next_hover_root);
- }
- }
- if (ctx->mouse_pressed && (!ctx->next_hover_root || ctx->next_hover_root->opt & GS_GUI_OPT_NOFOCUS))
- {
- ctx->active_root = NULL;
- }
- // Reset state
- ctx->key_pressed = 0;
- ctx->input_text[0] = '\0';
- ctx->mouse_pressed = 0;
- ctx->scroll_delta = gs_v2(0, 0);
- ctx->last_mouse_pos = ctx->mouse_pos;
- ctx->undock_root = NULL;
- if (ctx->mouse_down != GS_GUI_MOUSE_LEFT)
- {
- gs_platform_set_cursor(ctx->window_hndl, GS_PLATFORM_CURSOR_ARROW);
- }
- // Sort root containers by zindex
- n = ctx->root_list.idx;
- qsort(ctx->root_list.items, n, sizeof(gs_gui_container_t*), gs_gui_compare_zindex);
- // Set root container jump commands
- for (i = 0; i < n; i++)
- {
- gs_gui_container_t *cnt = ctx->root_list.items[i];
- // If this is the first container then make the first command jump to it.
- // otherwise set the previous container's tail to jump to this one
- if (i == 0)
- {
- gs_gui_command_t *cmd = (gs_gui_command_t*) ctx->command_list.items;
- cmd->jump.dst = (char*) cnt->head + sizeof(gs_gui_jumpcommand_t);
- }
- else
- {
- gs_gui_container_t *prev = ctx->root_list.items[i - 1];
- prev->tail->jump.dst = (char*) cnt->head + sizeof(gs_gui_jumpcommand_t);
- }
- // Make the last container's tail jump to the end of command list
- if (i == n - 1)
- {
- cnt->tail->jump.dst = ctx->command_list.items + ctx->command_list.idx;
- }
- }
- }
- GS_API_DECL void
- gs_gui_render(gs_gui_context_t* ctx, gs_command_buffer_t* cb)
- {
- const gs_vec2 fb = ctx->framebuffer_size;
- const gs_gui_rect_t* viewport = &ctx->viewport;
- gs_immediate_draw_t* gsi = &ctx->gsi;
- gsi_defaults(&ctx->gsi);
- // gsi_camera2D(&ctx->gsi, (uint32_t)fb.x, (uint32_t)fb.y);
- gsi_camera2D(&ctx->gsi, (uint32_t)viewport->w, (uint32_t)viewport->h);
- gsi_blend_enabled(&ctx->gsi, true);
- gs_gui_rect_t clip = gs_gui_unclipped_rect;
- gs_gui_command_t* cmd = NULL;
- while (gs_gui_next_command(ctx, &cmd))
- {
- switch (cmd->type)
- {
- case GS_GUI_COMMAND_CUSTOM:
- {
- gsi_defaults(&ctx->gsi);
- gsi_set_view_scissor(&ctx->gsi,
- (int32_t)(cmd->custom.clip.x),
- (int32_t)(fb.y - cmd->custom.clip.h - cmd->custom.clip.y),
- (int32_t)(cmd->custom.clip.w),
- (int32_t)(cmd->custom.clip.h));
- if (cmd->custom.cb) {
- cmd->custom.cb(ctx, &cmd->custom);
- }
- gsi_defaults(&ctx->gsi);
- // gsi_camera2D(&ctx->gsi, (uint32_t)fb.x, (uint32_t)fb.y);
- gsi_camera2D(&ctx->gsi, (uint32_t)viewport->w, (uint32_t)viewport->h);
- gsi_blend_enabled(&ctx->gsi, true);
- // gs_graphics_set_viewport(&ctx->gsi.commands, 0, 0, (uint32_t)fb.x, (uint32_t)fb.y);
- gs_graphics_set_viewport(&ctx->gsi.commands, (uint32_t)viewport->x, (uint32_t)viewport->y, (uint32_t)viewport->w, (uint32_t)viewport->h);
- gsi_set_view_scissor(&ctx->gsi,
- (int32_t)(clip.x),
- (int32_t)(fb.y - clip.h - clip.y),
- (int32_t)(clip.w),
- (int32_t)(clip.h));
- } break;
- case GS_GUI_COMMAND_PIPELINE:
- {
- gsi_pipeline_set(&ctx->gsi, cmd->pipeline.pipeline);
- // Set layout if valid
- if (cmd->pipeline.layout_sz)
- {
- switch (cmd->pipeline.layout_type)
- {
- case GSI_LAYOUT_VATTR:
- {
- gsi_vattr_list(&ctx->gsi, cmd->pipeline.layout, cmd->pipeline.layout_sz);
- } break;
- case GSI_LAYOUT_MESH:
- {
- gsi_vattr_list_mesh(&ctx->gsi, cmd->pipeline.layout, cmd->pipeline.layout_sz);
- } break;
- }
- }
- // If not a valid pipeline, then set back to default gui pipeline
- if (!cmd->pipeline.pipeline.id)
- {
- gsi_blend_enabled(&ctx->gsi, true);
- }
- } break;
- case GS_GUI_COMMAND_UNIFORMS:
- {
- gs_graphics_bind_desc_t bind = gs_default_val();
- // Set uniform bind
- gs_graphics_bind_uniform_desc_t uniforms[1] = gs_default_val();
- bind.uniforms.desc = uniforms;
- bind.uniforms.size = sizeof(uniforms);
- // Treat as byte buffer, read data
- gs_byte_buffer_t buffer = gs_default_val();
- buffer.capacity = GS_GUI_COMMANDLIST_SIZE;
- buffer.data = cmd->uniforms.data;
- // Write count
- gs_byte_buffer_readc(&buffer, uint16_t, ct);
- // Iterate through all uniforms, memcpy data as needed for each uniform in list
- for (uint32_t i = 0; i < ct; ++i)
- {
- gs_byte_buffer_readc(&buffer, gs_handle(gs_graphics_uniform_t), hndl);
- gs_byte_buffer_readc(&buffer, size_t, sz);
- gs_byte_buffer_readc(&buffer, uint16_t, binding);
- void* udata = (buffer.data + buffer.position);
- gs_byte_buffer_advance_position(&buffer, sz);
- uniforms[0].uniform = hndl;
- uniforms[0].binding = binding;
- uniforms[0].data = udata;
- gs_graphics_apply_bindings(&ctx->gsi.commands, &bind);
- }
- } break;
- case GS_GUI_COMMAND_TEXT:
- {
- const gs_vec2* tp = &cmd->text.pos;
- const char* ts = cmd->text.str;
- const gs_color_t* tc = &cmd->text.color;
- const gs_asset_font_t* tf = cmd->text.font;
- gsi_text(&ctx->gsi, tp->x, tp->y, ts, tf, false, tc->r, tc->g, tc->b, tc->a);
- } break;
- case GS_GUI_COMMAND_SHAPE:
- {
- gsi_texture(&ctx->gsi, gs_handle_invalid(gs_graphics_texture_t));
- gs_color_t* c = &cmd->shape.color;
- switch (cmd->shape.type)
- {
- case GS_GUI_SHAPE_RECT:
- {
- gs_gui_rect_t* r = &cmd->shape.rect;
- gsi_rectvd(&ctx->gsi, gs_v2(r->x, r->y), gs_v2(r->w, r->h), gs_v2s(0.f), gs_v2s(1.f), *c, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- } break;
- case GS_GUI_SHAPE_CIRCLE:
- {
- gs_vec2* cp = &cmd->shape.circle.center;
- float* r = &cmd->shape.circle.radius;
- gsi_circle(&ctx->gsi, cp->x, cp->y, *r, 16, c->r, c->g, c->b, c->a, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- } break;
- case GS_GUI_SHAPE_TRIANGLE:
- {
- gs_vec2* pa = &cmd->shape.triangle.points[0];
- gs_vec2* pb = &cmd->shape.triangle.points[1];
- gs_vec2* pc = &cmd->shape.triangle.points[2];
- gsi_trianglev(&ctx->gsi, *pa, *pb, *pc, *c, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- } break;
- case GS_GUI_SHAPE_LINE:
- {
- gs_vec2* s = &cmd->shape.line.start;
- gs_vec2* e = &cmd->shape.line.end;
- gsi_linev(&ctx->gsi, *s, *e, *c);
- } break;
- }
-
- } break;
- case GS_GUI_COMMAND_IMAGE:
- {
- gsi_texture(&ctx->gsi, cmd->image.hndl);
- gs_color_t* c = &cmd->image.color;
- gs_gui_rect_t* r = &cmd->image.rect;
- gs_vec4* uvs = &cmd->image.uvs;
- gsi_rectvd(&ctx->gsi, gs_v2(r->x, r->y), gs_v2(r->w, r->h), gs_v2(uvs->x, uvs->y), gs_v2(uvs->z, uvs->w), *c, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
- } break;
- case GS_GUI_COMMAND_CLIP:
- {
- // Will project scissor/clipping rectangles into framebuffer space
- gs_vec2 clip_off = gs_v2s(0.f); // (0,0) unless using multi-viewports
- gs_vec2 clip_scale = gs_v2s(1.f); // (1,1) unless using retina display which are often (2,2)
- gs_gui_rect_t clip_rect;
- clip_rect.x = (cmd->clip.rect.x - clip_off.x) * clip_scale.x;
- clip_rect.y = (cmd->clip.rect.y - clip_off.y) * clip_scale.y;
- clip_rect.w = (cmd->clip.rect.w - clip_off.x) * clip_scale.x;
- clip_rect.h = (cmd->clip.rect.h - clip_off.y) * clip_scale.y;
- clip_rect.x = gs_max(clip_rect.x, 0.f);
- clip_rect.y = gs_max(clip_rect.y, 0.f);
- clip_rect.w = gs_max(clip_rect.w, 0.f);
- clip_rect.h = gs_max(clip_rect.h, 0.f);
- clip = clip_rect;
- gsi_set_view_scissor(&ctx->gsi,
- (int32_t)(clip_rect.x),
- (int32_t)(fb.y - clip_rect.h - clip_rect.y),
- (int32_t)(clip_rect.w),
- (int32_t)(clip_rect.h));
- } break;
- }
- }
- // Draw main list
- gsi_draw(&ctx->gsi, cb);
- // Draw overlay list
- gsi_draw(&ctx->overlay_draw_list, cb);
- }
- GS_API_DECL void gs_gui_renderpass_submit(gs_gui_context_t* ctx, gs_command_buffer_t* cb, gs_color_t c)
- {
- gs_vec2 fbs = ctx->framebuffer_size;
- gs_gui_rect_t* vp = &ctx->viewport;
- gs_graphics_clear_action_t action = gs_default_val();
- action.color[0] = (float)c.r / 255.f;
- action.color[1] = (float)c.g / 255.f;
- action.color[2] = (float)c.b / 255.f;
- action.color[3] = (float)c.a / 255.f;
- gs_graphics_clear_desc_t clear = gs_default_val();
- clear.actions = &action;
- gs_graphics_renderpass_begin(cb, GS_GRAPHICS_RENDER_PASS_DEFAULT);
- {
- gs_graphics_clear(cb, &clear);
- gs_graphics_set_viewport(cb, (uint32_t)vp->x, (uint32_t)vp->y, (uint32_t)vp->w, (uint32_t)vp->h);
- gs_gui_render(ctx, cb);
- }
- gs_graphics_renderpass_end(cb);
- }
- GS_API_DECL void
- gs_gui_renderpass_submit_ex(gs_gui_context_t* ctx, gs_command_buffer_t* cb, gs_graphics_clear_action_t* action)
- {
- gs_vec2 fbs = ctx->framebuffer_size;
- gs_gui_rect_t* vp = &ctx->viewport;
- gs_graphics_clear_desc_t clear = gs_default_val();
- clear.actions = action;
- gs_renderpass_t pass = gs_default_val();
- gs_graphics_renderpass_begin(cb, pass);
- gs_graphics_set_viewport(cb, (uint32_t)vp->x, (uint32_t)vp->y, (uint32_t)vp->w, (uint32_t)vp->h);
- gs_graphics_clear(cb, &clear);
- gs_gui_render(ctx, cb);
- gs_graphics_renderpass_end(cb);
- }
- GS_API_DECL void
- gs_gui_set_hover(gs_gui_context_t *ctx, gs_gui_id id)
- {
- ctx->prev_hover = ctx->hover;
- ctx->hover = id;
- }
- GS_API_DECL void
- gs_gui_set_focus(gs_gui_context_t* ctx, gs_gui_id id)
- {
- ctx->prev_focus = ctx->focus;
- ctx->focus = id;
- ctx->updated_focus = 1;
- }
- GS_API_DECL gs_gui_id
- gs_gui_get_id(gs_gui_context_t* ctx, const void* data, int32_t size)
- {
- int32_t idx = ctx->id_stack.idx;
- gs_gui_id res = (idx > 0) ? ctx->id_stack.items[idx - 1] : GS_GUI_HASH_INITIAL;
- gs_gui_hash(&res, data, size);
- ctx->last_id = res;
- return res;
- }
- GS_API_DECL gs_gui_id
- gs_gui_get_id_hash(gs_gui_context_t* ctx, const void* data, int32_t size, gs_gui_id hash)
- {
- gs_gui_id res = hash;
- gs_gui_hash(&res, data, size);
- ctx->last_id = res;
- return res;
- }
- GS_API_DECL gs_gui_id
- gs_gui_push_id(gs_gui_context_t* ctx, const void* data, int32_t size)
- {
- gs_gui_id id = gs_gui_get_id(ctx, data, size);
- gs_gui_stack_push(ctx->id_stack, id);
- return id;
- }
- GS_API_DECL void
- gs_gui_pop_id(gs_gui_context_t* ctx)
- {
- gs_gui_stack_pop(ctx->id_stack);
- }
- GS_API_DECL void
- gs_gui_push_clip_rect(gs_gui_context_t* ctx, gs_gui_rect_t rect)
- {
- gs_gui_rect_t last = gs_gui_get_clip_rect(ctx);
- gs_gui_stack_push(ctx->clip_stack, gs_gui_intersect_rects(rect, last));
- }
- GS_API_DECL void
- gs_gui_pop_clip_rect(gs_gui_context_t* ctx)
- {
- gs_gui_stack_pop(ctx->clip_stack);
- }
- GS_API_DECL gs_gui_rect_t
- gs_gui_get_clip_rect(gs_gui_context_t* ctx)
- {
- gs_gui_expect(ctx->clip_stack.idx > 0);
- return ctx->clip_stack.items[ctx->clip_stack.idx - 1];
- }
- GS_API_DECL int32_t
- gs_gui_check_clip(gs_gui_context_t* ctx, gs_gui_rect_t r)
- {
- gs_gui_rect_t cr = gs_gui_get_clip_rect(ctx);
- if (r.x > cr.x + cr.w || r.x + r.w < cr.x ||
- r.y > cr.y + cr.h || r.y + r.h < cr.y)
- {
- return GS_GUI_CLIP_ALL;
- }
- if (r.x >= cr.x && r.x + r.w <= cr.x + cr.w &&
- r.y >= cr.y && r.y + r.h <= cr.y + cr.h )
- {
- return 0;
- }
- return GS_GUI_CLIP_PART;
- }
- GS_API_DECL gs_gui_container_t*
- gs_gui_get_current_container(gs_gui_context_t* ctx)
- {
- gs_gui_expect(ctx->container_stack.idx > 0);
- return ctx->container_stack.items[ctx->container_stack.idx - 1];
- }
- GS_API_DECL void
- gs_gui_current_container_close(gs_gui_context_t* ctx)
- {
- gs_gui_container_t* cnt = gs_gui_get_current_container(ctx);
- cnt->open = false;
- }
- GS_API_DECL gs_gui_container_t*
- gs_gui_get_container(gs_gui_context_t* ctx, const char* name)
- {
- gs_gui_id id = gs_gui_get_id(ctx, name, strlen(name));
- return gs_gui_get_container_ex(ctx, id, 0);
- }
- GS_API_DECL void
- gs_gui_bring_to_front(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
- {
- gs_gui_container_t* root = gs_gui_get_root_container(ctx, cnt);
- if (root->opt & GS_GUI_OPT_NOBRINGTOFRONT)
- {
- if (cnt->opt & GS_GUI_OPT_DOCKSPACE) cnt->zindex = 0;
- else cnt->zindex = 2;
- if (cnt->tab_bar)
- {
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- ((gs_gui_container_t*)tab_bar->items[i].data)->zindex = cnt->zindex + i;
- }
- }
- }
- else
- {
- cnt->zindex = ++ctx->last_zindex;
- // If container is part of a tab item, then iterate and bring to front as well
- if (cnt->tab_bar)
- {
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- ((gs_gui_container_t*)tab_bar->items[i].data)->zindex = ++ctx->last_zindex;
- }
- }
- }
- }
- /*============================================================================
- ** Pool
- **============================================================================*/
- GS_API_DECL int32_t
- gs_gui_pool_init(gs_gui_context_t* ctx, gs_gui_pool_item_t* items, int32_t len, gs_gui_id id)
- {
- int32_t i, n = -1, f = ctx->frame;
- for (i = 0; i < len; i++)
- {
- if (items[i].last_update < f)
- {
- f = items[i].last_update;
- n = i;
- }
- }
- gs_gui_expect(n > -1);
- items[n].id = id;
- gs_gui_pool_update(ctx, items, n);
- return n;
- }
- GS_API_DECL int32_t
- gs_gui_pool_get(gs_gui_context_t* ctx, gs_gui_pool_item_t* items, int32_t len, gs_gui_id id)
- {
- // Note(john): This is a linear hash lookup. Could speed this up with a quadratic lookup.
- int32_t i;
- gs_gui_unused(ctx);
- for (i = 0; i < len; i++)
- {
- if (items[i].id == id)
- {
- return i;
- }
- }
- return -1;
- }
- GS_API_DECL void
- gs_gui_pool_update(gs_gui_context_t* ctx, gs_gui_pool_item_t* items, int32_t idx)
- {
- items[idx].last_update = ctx->frame;
- }
- /*============================================================================
- ** input handlers
- **============================================================================*/
- GS_API_DECL void
- gs_gui_input_mousemove(gs_gui_context_t* ctx, int32_t x, int32_t y)
- {
- ctx->mouse_pos = gs_v2((f32)x, (f32)y);
- }
- GS_API_DECL void
- gs_gui_input_mousedown(gs_gui_context_t* ctx, int32_t x, int32_t y, int32_t btn)
- {
- gs_gui_input_mousemove(ctx, x, y);
- ctx->mouse_down |= btn;
- ctx->mouse_pressed |= btn;
- }
- GS_API_DECL void
- gs_gui_input_mouseup(gs_gui_context_t* ctx, int32_t x, int32_t y, int32_t btn)
- {
- gs_gui_input_mousemove(ctx, x, y);
- ctx->mouse_down &= ~btn;
- }
- GS_API_DECL void
- gs_gui_input_scroll(gs_gui_context_t* ctx, int32_t x, int32_t y)
- {
- ctx->scroll_delta.x += x;
- ctx->scroll_delta.y += y;
- }
- GS_API_DECL void
- gs_gui_input_keydown(gs_gui_context_t* ctx, int32_t key)
- {
- ctx->key_pressed |= key;
- ctx->key_down |= key;
- }
- GS_API_DECL void
- gs_gui_input_keyup(gs_gui_context_t* ctx, int32_t key)
- {
- ctx->key_down &= ~key;
- }
- GS_API_DECL void
- gs_gui_input_text(gs_gui_context_t* ctx, const char* text)
- {
- int32_t len = strlen(ctx->input_text);
- int32_t size = strlen(text) + 1;
- if (len + size > (int32_t)sizeof(ctx->input_text)) return;
- memcpy(ctx->input_text + len, text, size);
- }
- /*============================================================================
- ** commandlist
- **============================================================================*/
- GS_API_DECL gs_gui_command_t*
- gs_gui_push_command(gs_gui_context_t* ctx, int32_t type, int32_t size)
- {
- gs_gui_command_t* cmd = (gs_gui_command_t*) (ctx->command_list.items + ctx->command_list.idx);
- gs_gui_expect(ctx->command_list.idx + size < GS_GUI_COMMANDLIST_SIZE);
- cmd->base.type = type;
- cmd->base.size = size;
- ctx->command_list.idx += size;
- return cmd;
- }
- GS_API_DECL int32_t
- gs_gui_next_command(gs_gui_context_t* ctx, gs_gui_command_t** cmd)
- {
- if (*cmd)
- {
- *cmd = (gs_gui_command_t*) (((char*) *cmd) + (*cmd)->base.size);
- }
- else
- {
- *cmd = (gs_gui_command_t*) ctx->command_list.items;
- }
- while ((uint8_t*) *cmd != (uint8_t*)(ctx->command_list.items + ctx->command_list.idx))
- {
- if ((*cmd)->type != GS_GUI_COMMAND_JUMP)
- {
- return 1;
- }
- *cmd = (gs_gui_command_t*)((*cmd)->jump.dst);
- }
- return 0;
- }
- GS_API_DECL void
- gs_gui_set_clip(gs_gui_context_t* ctx, gs_gui_rect_t rect)
- {
- gs_gui_command_t* cmd;
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_CLIP, sizeof(gs_gui_clipcommand_t));
- cmd->clip.rect = rect;
- }
- GS_API_DECL void
- gs_gui_set_pipeline(gs_gui_context_t* ctx, gs_handle(gs_graphics_pipeline_t) pip,
- void* layout, size_t sz, gsi_layout_type type)
- {
- gs_gui_command_t* cmd;
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_PIPELINE, sizeof(gs_gui_pipelinecommand_t));
- cmd->pipeline.pipeline = pip;
- cmd->pipeline.layout_type = type;
- cmd->pipeline.layout = ctx->command_list.items + ctx->command_list.idx;
- cmd->pipeline.layout_sz = sz;
- cmd->base.size += sz;
-
- // Copy data and move list forward
- memcpy(ctx->command_list.items + ctx->command_list.idx, layout, sz);
- ctx->command_list.idx += sz;
- }
- GS_API_DECL void
- gs_gui_bind_uniforms(gs_gui_context_t* ctx,
- gs_graphics_bind_uniform_desc_t* uniforms, size_t uniforms_sz)
- {
- gs_gui_command_t* cmd;
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_UNIFORMS, sizeof(gs_gui_binduniformscommand_t));
- cmd->uniforms.data = ctx->command_list.items + ctx->command_list.idx;
- // Treat as byte buffer, write into data then set size
- gs_byte_buffer_t buffer = gs_default_val();
- buffer.capacity = GS_GUI_COMMANDLIST_SIZE;
- buffer.data = cmd->uniforms.data;
- const uint16_t ct = uniforms_sz / sizeof(gs_graphics_bind_uniform_desc_t);
- // Write count
- gs_byte_buffer_write(&buffer, uint16_t, ct);
- // Iterate through all uniforms, memcpy data as needed for each uniform in list
- for (uint32_t i = 0; i < ct; ++i)
- {
- gs_graphics_bind_uniform_desc_t* decl = &uniforms[i];
- gs_handle(gs_graphics_uniform_t) hndl = decl->uniform;
- const size_t sz = gs_graphics_uniform_size_query(hndl);
- gs_byte_buffer_write(&buffer, gs_handle(gs_graphics_uniform_t), hndl);
- gs_byte_buffer_write(&buffer, size_t, sz);
- gs_byte_buffer_write(&buffer, uint16_t, (uint16_t)decl->binding);
- gs_byte_buffer_write_bulk(&buffer, decl->data, sz);
- }
- // Record final sizes
- const size_t sz = buffer.size;
- cmd->base.size += sz;
- ctx->command_list.idx += sz;
- }
- GS_API_DECL void
- gs_gui_draw_line(gs_gui_context_t* ctx, gs_vec2 start, gs_vec2 end, gs_color_t color)
- {
- gs_gui_command_t* cmd;
- gs_gui_rect_t rect = gs_default_val();
- gs_vec2 s = start.x < end.x ? start : end;
- gs_vec2 e = start.x < end.x ? end : start;
- gs_gui_rect(s.x, s.y, e.x - s.x, e.y - s.y);
- rect = gs_gui_intersect_rects(rect, gs_gui_get_clip_rect(ctx));
- // do clip command if the rect isn't fully contained within the cliprect
- int32_t clipped = gs_gui_check_clip(ctx, rect);
- if (clipped == GS_GUI_CLIP_ALL ) {return;}
- if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, gs_gui_get_clip_rect(ctx));}
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_SHAPE, sizeof(gs_gui_shapecommand_t));
- cmd->shape.type = GS_GUI_SHAPE_LINE;
- cmd->shape.line.start = s;
- cmd->shape.line.end = e;
- cmd->shape.color = color;
- // reset clipping if it was set
- if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
- }
- GS_API_DECL void
- gs_gui_draw_rect(gs_gui_context_t* ctx, gs_gui_rect_t rect, gs_color_t color)
- {
- gs_gui_command_t* cmd;
- rect = gs_gui_intersect_rects(rect, gs_gui_get_clip_rect(ctx));
- if (rect.w > 0 && rect.h > 0)
- {
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_SHAPE, sizeof(gs_gui_shapecommand_t));
- cmd->shape.type = GS_GUI_SHAPE_RECT;
- cmd->shape.rect = rect;
- cmd->shape.color = color;
- }
- }
- GS_API_DECL void
- gs_gui_draw_circle(gs_gui_context_t* ctx, gs_vec2 position, float radius, gs_color_t color)
- {
- gs_gui_command_t* cmd;
- gs_gui_rect_t rect = gs_gui_rect(position.x - radius, position.y - radius, 2.f * radius, 2.f * radius);
- // do clip command if the rect isn't fully contained within the cliprect
- int32_t clipped = gs_gui_check_clip(ctx, rect);
- if (clipped == GS_GUI_CLIP_ALL ) {return;}
- if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, gs_gui_get_clip_rect(ctx));}
- // do shape command
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_SHAPE, sizeof(gs_gui_shapecommand_t));
- cmd->shape.type = GS_GUI_SHAPE_CIRCLE;
- cmd->shape.circle.center = position;
- cmd->shape.circle.radius = radius;
- cmd->shape.color = color;
- // reset clipping if it was set
- if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
- }
- GS_API_DECL void
- gs_gui_draw_triangle(gs_gui_context_t* ctx, gs_vec2 a, gs_vec2 b,
- gs_vec2 c, gs_color_t color)
- {
- gs_gui_command_t* cmd;
- // Check each point against rect (if partially clipped, then good
- int32_t clipped = 0x00;
- gs_gui_rect_t clip = gs_gui_get_clip_rect(ctx);
- int32_t ca = gs_gui_rect_overlaps_vec2(clip, a);
- int32_t cb = gs_gui_rect_overlaps_vec2(clip, b);
- int32_t cc = gs_gui_rect_overlaps_vec2(clip, c);
- if (ca && cb && cc) clipped = 0x00; // No clip
- else if (!ca && !cb && !cc) clipped = GS_GUI_CLIP_ALL;
- else if (ca || cb || cc) clipped = GS_GUI_CLIP_PART;
- if (clipped == GS_GUI_CLIP_ALL) {return;}
- if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, clip);}
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_SHAPE, sizeof(gs_gui_shapecommand_t));
- cmd->shape.type = GS_GUI_SHAPE_TRIANGLE;
- cmd->shape.triangle.points[0] = a;
- cmd->shape.triangle.points[1] = b;
- cmd->shape.triangle.points[2] = c;
- cmd->shape.color = color;
- // Reset clipping if set
- if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
- }
- GS_API_DECL void
- gs_gui_draw_box(gs_gui_context_t* ctx, gs_gui_rect_t rect, int16_t* w, gs_color_t color)
- {
- gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
- // gsi_rectvd(dl, gs_v2(rect.x, rect.y), gs_v2(rect.w, rect.h), gs_v2s(0.f), gs_v2s(1.f), GS_COLOR_RED, GS_GRAPHICS_PRIMITIVE_LINES);
- const float l = (float)w[0], r = (float)w[1], t = (float)w[2], b = (float)w[3];
- gs_gui_draw_rect(ctx, gs_gui_rect(rect.x + l, rect.y, rect.w - r - l, t), color); // top
- gs_gui_draw_rect(ctx, gs_gui_rect(rect.x + l, rect.y + rect.h - b, rect.w - r - l, b), color); // bottom
- gs_gui_draw_rect(ctx, gs_gui_rect(rect.x, rect.y, l, rect.h), color); // left
- gs_gui_draw_rect(ctx, gs_gui_rect(rect.x + rect.w - r, rect.y, r, rect.h), color); // right
- }
- GS_API_DECL void
- gs_gui_draw_text(gs_gui_context_t* ctx, gs_asset_font_t* font, const char *str,
- int32_t len, gs_vec2 pos, gs_color_t color, int32_t shadow_x,
- int32_t shadow_y, gs_color_t shadow_color)
- {
- // Set to default font
- if (!font)
- {
- font = gsi_default_font();
- }
- #define DRAW_TEXT(TEXT, RECT, COLOR)\
- do\
- {\
- gs_gui_command_t* cmd;\
- gs_vec2 td = gs_gui_text_dimensions(font, TEXT, -1);\
- gs_gui_rect_t rect = (RECT);\
- int32_t clipped = gs_gui_check_clip(ctx, rect);\
- \
- if (clipped == GS_GUI_CLIP_ALL)\
- {\
- return;\
- }\
- \
- if (clipped == GS_GUI_CLIP_PART)\
- {\
- gs_gui_rect_t crect = gs_gui_get_clip_rect(ctx);\
- gs_gui_set_clip(ctx, crect);\
- }\
- \
- /* add command */\
- if (len < 0)\
- {\
- len = strlen(TEXT);\
- }\
- \
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_TEXT, sizeof(gs_gui_textcommand_t) + len);\
- memcpy(cmd->text.str, TEXT, len);\
- cmd->text.str[len] = '\0';\
- cmd->text.pos = gs_v2(rect.x, rect.y);\
- cmd->text.color = COLOR;\
- cmd->text.font = font;\
- \
- if (clipped)\
- {\
- gs_gui_set_clip(ctx, gs_gui_unclipped_rect);\
- }\
- } while (0)
- // Draw shadow
- if ((shadow_x || shadow_y) && shadow_color.a)
- {
- DRAW_TEXT(str, gs_gui_rect(pos.x + (float)shadow_x, pos.y + (float)shadow_y, td.x, td.y), shadow_color);
- }
-
- // Draw text
- {
- DRAW_TEXT(str, gs_gui_rect(pos.x, pos.y, td.x, td.y), color);
- }
- }
- GS_API_DECL void
- gs_gui_draw_image(gs_gui_context_t *ctx, gs_handle(gs_graphics_texture_t) hndl,
- gs_gui_rect_t rect, gs_vec2 uv0, gs_vec2 uv1, gs_color_t color)
- {
- gs_gui_command_t* cmd;
- /* do clip command if the rect isn't fully contained within the cliprect */
- int32_t clipped = gs_gui_check_clip(ctx, rect);
- if (clipped == GS_GUI_CLIP_ALL ) {return;}
- if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, gs_gui_get_clip_rect(ctx));}
- /* do image command */
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_IMAGE, sizeof(gs_gui_imagecommand_t));
- cmd->image.hndl = hndl;
- cmd->image.rect = rect;
- cmd->image.uvs = gs_v4(uv0.x, uv0.y, uv1.x, uv1.y);
- cmd->image.color = color;
- /* reset clipping if it was set */
- if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
- }
- GS_API_DECL void
- gs_gui_draw_custom(gs_gui_context_t* ctx, gs_gui_rect_t rect,
- gs_gui_draw_callback_t cb, void* data, size_t sz)
- {
- gs_gui_command_t* cmd;
- gs_gui_rect_t viewport = rect;
- rect = gs_gui_intersect_rects(rect, gs_gui_get_clip_rect(ctx));
- /* do clip command if the rect isn't fully contained within the cliprect */
- int32_t clipped = gs_gui_check_clip(ctx, rect);
- if (clipped == GS_GUI_CLIP_ALL ) {return;}
- if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, gs_gui_get_clip_rect(ctx));}
- int32_t idx = ctx->id_stack.idx;
- gs_gui_id res = (idx > 0) ? ctx->id_stack.items[idx - 1] : GS_GUI_HASH_INITIAL;
- /* do custom command */
- cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_CUSTOM, sizeof(gs_gui_customcommand_t));
- cmd->custom.clip = rect;
- cmd->custom.viewport = viewport;
- cmd->custom.cb = cb;
- cmd->custom.hover = ctx->hover;
- cmd->custom.focus = ctx->focus;
- cmd->custom.hash = res;
- cmd->custom.data = ctx->command_list.items + ctx->command_list.idx;
- cmd->custom.sz = sz;
- cmd->base.size += sz;
-
- // Copy data and move list forward
- memcpy(ctx->command_list.items + ctx->command_list.idx, data, sz);
- ctx->command_list.idx += sz;
- /* reset clipping if it was set */
- if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
- }
- GS_API_DECL void gs_gui_draw_nine_rect(gs_gui_context_t* ctx,
- gs_handle(gs_graphics_texture_t) hndl, gs_gui_rect_t rect,
- gs_vec2 uv0, gs_vec2 uv1, uint32_t left, uint32_t right, uint32_t top,
- uint32_t bottom, gs_color_t color)
- {
- // Draw images based on rect, slice image based on uvs (uv0, uv1), original texture dimensions (width, height) and control margins (left, right, top, bottom)
- gs_graphics_texture_desc_t desc = gs_default_val();
- gs_graphics_texture_desc_query(hndl, &desc);
- uint32_t width = desc.width;
- uint32_t height = desc.height;
- // tl
- {
- uint32_t w = left;
- uint32_t h = top;
- gs_gui_rect_t r = gs_gui_rect(rect.x, rect.y, (f32)w, (f32)h);
- gs_vec2 st0 = gs_v2(uv0.x, uv0.y);
- gs_vec2 st1 = gs_v2(uv0.x + ((float)left / (float)width), uv0.y + ((float)top / (float)height));
- gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
- }
- // tr
- {
- uint32_t w = right;
- uint32_t h = top;
- gs_gui_rect_t r = gs_gui_rect(rect.x + rect.w - w, rect.y, (f32)w, (f32)h);
- gs_vec2 st0 = gs_v2(uv1.x - ((float)right / (float)width), uv0.y);
- gs_vec2 st1 = gs_v2(uv1.x, uv0.y + ((float)top / (float)height));
- gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
- }
- // br
- {
- uint32_t w = right;
- uint32_t h = bottom;
- gs_gui_rect_t r = gs_gui_rect(rect.x + rect.w - (f32)w, rect.y + rect.h - (f32)h, (f32)w, (f32)h);
- gs_vec2 st0 = gs_v2(uv1.x - ((float)right / (float)width), uv1.y - ((float)bottom / (float)height));
- gs_vec2 st1 = gs_v2(uv1.x, uv1.y);
- gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
- }
- // bl
- {
- uint32_t w = left;
- uint32_t h = bottom;
- gs_gui_rect_t r = gs_gui_rect(rect.x, rect.y + rect.h - (f32)h, (f32)w, (f32)h);
- gs_vec2 st0 = gs_v2(uv0.x, uv1.y - ((float)bottom / (float)height));
- gs_vec2 st1 = gs_v2(uv0.x + ((float)left / (float)width), uv1.y);
- gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
- }
- // top
- {
- uint32_t w = (u32)rect.w - left - right;
- uint32_t h = top;
- gs_gui_rect_t r = gs_gui_rect(rect.x + left, rect.y, (f32)w, (f32)h);
- gs_vec2 st0 = gs_v2(uv0.x + ((float)left / (float)width), uv0.y);
- gs_vec2 st1 = gs_v2(uv1.x - ((float)right / (float)width), uv0.y + ((float)top / (float)height));
- gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
- }
- // bottom
- {
- uint32_t w = (u32)rect.w - left - right;
- uint32_t h = bottom;
- gs_gui_rect_t r = gs_gui_rect(rect.x + left, rect.y + rect.h - (f32)h, (f32)w, (f32)h);
- gs_vec2 st0 = gs_v2(uv0.x + ((float)left / (float)width), uv1.y - ((float)bottom / (float)height));
- gs_vec2 st1 = gs_v2(uv1.x - ((float)right / (float)width), uv1.y);
- gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
- }
- // left
- {
- uint32_t w = left;
- uint32_t h = (u32)rect.h - top - bottom;
- gs_gui_rect_t r = gs_gui_rect(rect.x, rect.y + top, (f32)w, (f32)h);
- gs_vec2 st0 = gs_v2(uv0.x, uv0.y + ((float)top / (float)height));
- gs_vec2 st1 = gs_v2(uv0.x + ((float)left / (float)width), uv1.y - ((float)bottom / (float)height));
- gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
- }
- // right
- {
- uint32_t w = right;
- uint32_t h = (u32)rect.h - top - bottom;
- gs_gui_rect_t r = gs_gui_rect(rect.x + rect.w - (f32)w, rect.y + top, (f32)w, (f32)h);
- gs_vec2 st0 = gs_v2(uv1.x - ((float)right / (float)width), uv0.y + ((float)top / (float)height));
- gs_vec2 st1 = gs_v2(uv1.x, uv1.y - ((float)bottom / (float)height));
- gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
- }
- // center
- {
- uint32_t w = (u32)rect.w - right - left;
- uint32_t h = (u32)rect.h - top - bottom;
- gs_gui_rect_t r = gs_gui_rect(rect.x + left, rect.y + top, (f32)w, (f32)h);
- gs_vec2 st0 = gs_v2(uv0.x + ((float)left / (float)width), uv0.y + ((float)top / (float)height));
- gs_vec2 st1 = gs_v2(uv1.x - ((float)right / (float)width), uv1.y - ((float)bottom / (float)height));
- gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
- }
- }
- /*============================================================================
- ** layout
- **============================================================================*/
- enum {
- GS_GUI_RELATIVE = 1,
- GS_GUI_ABSOLUTE = 2
- };
- GS_API_DECL gs_gui_rect_t
- gs_gui_layout_anchor(const gs_gui_rect_t* p, int32_t width, int32_t height,
- int32_t xoff, int32_t yoff, gs_gui_layout_anchor_type type)
- {
- float w = (float)width;
- float h = (float)height;
- gs_gui_rect_t r = gs_gui_rect(p->x, p->y, w, h);
- switch (type)
- {
- default:
- case GS_GUI_LAYOUT_ANCHOR_TOPLEFT:
- {
- } break;
- case GS_GUI_LAYOUT_ANCHOR_TOPCENTER:
- {
- r.x = p->x + (p->w - w) * 0.5f;
- } break;
- case GS_GUI_LAYOUT_ANCHOR_TOPRIGHT:
- {
- r.x = p->x + (p->w - w);
- } break;
- case GS_GUI_LAYOUT_ANCHOR_LEFT:
- {
- r.y = p->y + (p->h - h) * 0.5f;
- } break;
- case GS_GUI_LAYOUT_ANCHOR_CENTER:
- {
- r.x = p->x + (p->w - w) * 0.5f;
- r.y = p->y + (p->h - h) * 0.5f;
- } break;
- case GS_GUI_LAYOUT_ANCHOR_RIGHT:
- {
- r.x = p->x + (p->w - w);
- r.y = p->y + (p->h - h) * 0.5f;
- } break;
- case GS_GUI_LAYOUT_ANCHOR_BOTTOMLEFT:
- {
- r.y = p->y + (p->h - h);
- } break;
- case GS_GUI_LAYOUT_ANCHOR_BOTTOMCENTER:
- {
- r.x = p->x + (p->w - w) * 0.5f;
- r.y = p->y + (p->h - h);
- } break;
- case GS_GUI_LAYOUT_ANCHOR_BOTTOMRIGHT:
- {
- r.x = p->x + (p->w - w);
- r.y = p->y + (p->h - h);
- } break;
- }
- // Apply offset
- r.x += xoff;
- r.y += yoff;
- return r;
- }
- GS_API_DECL void gs_gui_layout_column_begin(gs_gui_context_t* ctx)
- {
- gs_gui_push_layout(ctx, gs_gui_layout_next(ctx), gs_v2(0, 0));
- }
- GS_API_DECL void gs_gui_layout_column_end(gs_gui_context_t* ctx)
- {
- gs_gui_layout_t *a, *b;
- b = gs_gui_get_layout(ctx);
- gs_gui_stack_pop(ctx->layout_stack);
- /* inherit position/next_row/max from child layout if they are greater */
- a = gs_gui_get_layout(ctx);
- a->position.x = gs_max(a->position.x, b->position.x + b->body.x - a->body.x);
- a->next_row = (int32_t)gs_max((f32)a->next_row, (f32)b->next_row + (f32)b->body.y - (f32)a->body.y);
- a->max.x = gs_max(a->max.x, b->max.x);
- a->max.y = gs_max(a->max.y, b->max.y);
- }
- GS_API_DECL void gs_gui_layout_row(gs_gui_context_t *ctx, int32_t items, const int32_t *widths, int32_t height)
- {
- gs_gui_style_t* style = ctx->style;
- gs_gui_layout_t* layout = gs_gui_get_layout(ctx);
- if (widths)
- {
- gs_gui_expect(items <= GS_GUI_MAX_WIDTHS);
- memcpy(layout->widths, widths, items * sizeof(widths[0]));
- }
- layout->items = items;
- layout->position = gs_v2((f32)layout->indent, (f32)layout->next_row);
- layout->size.y = (f32)height;
- layout->item_index = 0;
- }
- GS_API_DECL void gs_gui_layout_row_ex(gs_gui_context_t *ctx, int32_t items, const int32_t *widths, int32_t height, int32_t justification)
- {
- gs_gui_layout_row(ctx, items, widths, height);
- gs_gui_layout_t* layout = gs_gui_get_layout(ctx);
- switch (justification)
- {
- default: break;
- case GS_GUI_JUSTIFY_CENTER:
- {
- // Iterate through all widths, calculate total
- // X is center - tw/2
- float w = 0.f;
- for (uint32_t i = 0; i < items; ++i)
- {
- w += widths[i] > 0.f ? widths[i] : widths[i] == 0.f? layout->size.x : layout->body.w - widths[i];
- }
- layout->position.x = (layout->body.w - w) * 0.5f + layout->indent;
- } break;
- case GS_GUI_JUSTIFY_END:
- {
- float w = 0.f;
- for (uint32_t i = 0; i < items; ++i) {
- w += widths[i] > 0.f ? widths[i] : widths[i] == 0.f? layout->size.x : layout->body.w - widths[i];
- }
- layout->position.x = (layout->body.w - w);
- } break;
- }
- }
- GS_API_DECL void
- gs_gui_layout_width(gs_gui_context_t *ctx, int32_t width)
- {
- gs_gui_get_layout(ctx)->size.x = (f32)width;
- }
- GS_API_DECL void
- gs_gui_layout_height(gs_gui_context_t *ctx, int32_t height)
- {
- gs_gui_get_layout(ctx)->size.y = (f32)height;
- }
- GS_API_DECL void
- gs_gui_layout_set_next(gs_gui_context_t *ctx, gs_gui_rect_t r, int32_t relative)
- {
- gs_gui_layout_t *layout = gs_gui_get_layout(ctx);
- layout->next = r;
- layout->next_type = relative ? GS_GUI_RELATIVE : GS_GUI_ABSOLUTE;
- }
- GS_API_DECL gs_gui_rect_t
- gs_gui_layout_peek_next(gs_gui_context_t *ctx)
- {
- gs_gui_layout_t layout = *gs_gui_get_layout(ctx);
- gs_gui_style_t* style = ctx->style;
- gs_gui_rect_t res;
- if (layout.next_type)
- {
- /* handle rect set by `gs_gui_layout_set_next` */
- int32_t type = layout.next_type;
- res = layout.next;
- if (type == GS_GUI_ABSOLUTE)
- {
- return res;
- }
- }
- else
- {
- // handle next row
- if (layout.item_index == layout.items)
- {
- gs_gui_layout_row(ctx, layout.items, NULL, (s32)layout.size.y);
- }
- const int32_t items = layout.items;
- const int32_t idx = layout.item_index;
- int32_t ml = style->margin[GS_GUI_MARGIN_LEFT];
- int32_t mr = style->margin[GS_GUI_MARGIN_RIGHT];
- int32_t mt = style->margin[GS_GUI_MARGIN_TOP];
- int32_t mb = style->margin[GS_GUI_MARGIN_BOTTOM];
- // position
- res.x = layout.position.x + ml;
- res.y = layout.position.y + mt;
- // size
- res.w = layout.items > 0 ? layout.widths[layout.item_index] : layout.size.x;
- res.h = layout.size.y;
- // default fallbacks
- if (res.w == 0) { res.w = style->size[0]; }
- if (res.h == 0) { res.h = style->size[1]; }
- if (res.w < 0) { res.w += layout.body.w - res.x + 1; }
- if (res.h < 0) { res.h += layout.body.h - res.y + 1; }
- layout.item_index++;
- }
- /* update position */
- layout.position.x += res.w + style->margin[GS_GUI_MARGIN_RIGHT];
- layout.next_row = (s32)gs_max(layout.next_row, res.y + res.h + style->margin[GS_GUI_MARGIN_BOTTOM]);
- /* apply body offset */
- res.x += layout.body.x;
- res.y += layout.body.y;
- /* update max position */
- layout.max.x = gs_max(layout.max.x, res.x + res.w);
- layout.max.y = gs_max(layout.max.y, res.y + res.h);
- return res;
- }
- GS_API_DECL gs_gui_rect_t
- gs_gui_layout_next(gs_gui_context_t *ctx)
- {
- gs_gui_layout_t* layout = gs_gui_get_layout(ctx);
- gs_gui_style_t* style = ctx->style;
- gs_gui_rect_t res;
- if (layout->next_type)
- {
- /* handle rect set by `gs_gui_layout_set_next` */
- int32_t type = layout->next_type;
- layout->next_type = 0;
- res = layout->next;
- if (type == GS_GUI_ABSOLUTE)
- {
- return (ctx->last_rect = res);
- }
- }
- else
- {
- // handle next row
- if (layout->item_index == layout->items)
- {
- gs_gui_layout_row(ctx, layout->items, NULL, (s32)layout->size.y);
- }
- const int32_t items = layout->items;
- const int32_t idx = layout->item_index;
- int32_t ml = style->margin[GS_GUI_MARGIN_LEFT];
- int32_t mr = style->margin[GS_GUI_MARGIN_RIGHT];
- int32_t mt = style->margin[GS_GUI_MARGIN_TOP];
- int32_t mb = style->margin[GS_GUI_MARGIN_BOTTOM];
- // position
- res.x = layout->position.x + ml;
- res.y = layout->position.y + mt;
- // size
- res.w = layout->items > 0 ? layout->widths[layout->item_index] : layout->size.x;
- res.h = layout->size.y;
- // default fallbacks
- if (res.w == 0) { res.w = style->size[0]; }
- if (res.h == 0) { res.h = style->size[1]; }
- // Not sure about this... should probably iterate through the rest, figure out what's left, then
- // determine how much to divide up
- if (res.w < 0) { res.w += layout->body.w - res.x + 1; }
- if (res.h < 0) { res.h += layout->body.h - res.y + 1; }
- layout->item_index++;
- }
- /* update position */
- layout->position.x += res.w + style->margin[GS_GUI_MARGIN_RIGHT];
- layout->next_row = (s32)gs_max(layout->next_row, res.y + res.h + style->margin[GS_GUI_MARGIN_BOTTOM]);// + style->margin[GS_GUI_MARGIN_TOP] * 0.5f);
- /* apply body offset */
- res.x += layout->body.x;
- res.y += layout->body.y;
- /* update max position */
- layout->max.x = gs_max(layout->max.x, res.x + res.w);
- layout->max.y = gs_max(layout->max.y, res.y + res.h);
- return (ctx->last_rect = res);
- }
- /*============================================================================
- ** controls
- **============================================================================*/
- static int32_t gs_gui_in_hover_root(gs_gui_context_t *ctx)
- {
- int32_t i = ctx->container_stack.idx;
- while (i--)
- {
- if (ctx->container_stack.items[i] == ctx->hover_root) { return 1; }
- /* only root containers have their `head` field set; stop searching if we've
- ** reached the current root container */
- if (ctx->container_stack.items[i]->head) { break; }
- }
- return 0;
- }
- GS_API_DECL void gs_gui_draw_control_frame(gs_gui_context_t *ctx, gs_gui_id id, gs_gui_rect_t rect,
- int32_t elementid, uint64_t opt)
- {
- if (opt & GS_GUI_OPT_NOFRAME) {return;}
- int32_t state = ctx->focus == id ? GS_GUI_ELEMENT_STATE_FOCUS :
- ctx->hover == id ? GS_GUI_ELEMENT_STATE_HOVER :
- 0x00;
- gs_gui_draw_frame(ctx, rect, &ctx->style_sheet->styles[elementid][state]);
- }
- GS_API_DECL void gs_gui_draw_control_text(gs_gui_context_t *ctx, const char *str, gs_gui_rect_t rect,
- const gs_gui_style_t* style, uint64_t opt)
- {
- gs_vec2 pos = gs_v2(rect.x, rect.y);
- gs_asset_font_t* font = style->font;
- gs_vec2 td = gs_gui_text_dimensions(font, str, -1);
- int32_t tw = (int32_t)td.x;
- int32_t th = (int32_t)td.y;
- gs_gui_push_clip_rect(ctx, rect);
- // Grab stylings
- const int32_t padding_left = style->padding[GS_GUI_PADDING_LEFT];
- const int32_t padding_top = style->padding[GS_GUI_PADDING_TOP];
- const int32_t padding_right = style->padding[GS_GUI_PADDING_RIGHT];
- const int32_t padding_bottom = style->padding[GS_GUI_PADDING_BOTTOM];
- const int32_t align = style->align_content;
- const int32_t justify = style->justify_content;
- // Determine x placement based on justification
- switch (justify)
- {
- default:
- case GS_GUI_JUSTIFY_START:
- {
- pos.x = rect.x + padding_left;
- } break;
- case GS_GUI_JUSTIFY_CENTER:
- {
- pos.x = rect.x + (rect.w - tw) * 0.5f;
- } break;
- case GS_GUI_JUSTIFY_END:
- {
- pos.x = rect.x + (rect.w - tw) - padding_right;
- } break;
- }
- // Determine y placement based on alignment within rect
- switch (align)
- {
- default:
- case GS_GUI_ALIGN_START:
- {
- pos.y = rect.y + padding_top;
- } break;
- case GS_GUI_ALIGN_CENTER:
- {
- pos.y = rect.y + (rect.h - th) * 0.5f;
- } break;
- case GS_GUI_ALIGN_END:
- {
- pos.y = rect.y + (rect.h - th) - padding_bottom;
- } break;
- }
- bool is_content = (opt & GS_GUI_OPT_ISCONTENT);
- int32_t bg_color = is_content ? GS_GUI_COLOR_CONTENT_BACKGROUND :
- GS_GUI_COLOR_BACKGROUND;
- int32_t sh_color = is_content ? GS_GUI_COLOR_CONTENT_SHADOW :
- GS_GUI_COLOR_SHADOW;
- int32_t bd_color = is_content ? GS_GUI_COLOR_CONTENT_BORDER :
- GS_GUI_COLOR_BORDER;
- int32_t sx = style->shadow_x;
- int32_t sy = style->shadow_y;
- const gs_color_t* sc = &style->colors[sh_color];
- // Border
- const gs_color_t* bc = &style->colors[bd_color];
- if (bc->a && ~opt & GS_GUI_OPT_NOSTYLEBORDER)
- {
- gs_gui_pop_clip_rect(ctx);
- gs_gui_rect_t border_rect = gs_gui_expand_rect(rect, (int16_t*)style->border_width);
- gs_gui_push_clip_rect(ctx, border_rect);
- gs_gui_draw_box(ctx, border_rect, (int16_t*)style->border_width, *bc);
- }
- // Background
- if (~opt & GS_GUI_OPT_NOSTYLEBACKGROUND)
- {
- gs_gui_draw_rect(ctx, rect, style->colors[bg_color]);
- }
- // Text
- gs_gui_draw_text(ctx, font, str, -1, pos, style->colors[GS_GUI_COLOR_CONTENT], sx, sy, *sc);
- gs_gui_pop_clip_rect(ctx);
- }
- GS_API_DECL int32_t gs_gui_mouse_over(gs_gui_context_t *ctx, gs_gui_rect_t rect)
- {
- return gs_gui_rect_overlaps_vec2(rect, ctx->mouse_pos) && !ctx->hover_split && !ctx->lock_hover_id &&
- gs_gui_rect_overlaps_vec2(gs_gui_get_clip_rect(ctx), ctx->mouse_pos) &&
- gs_gui_in_hover_root(ctx);
- }
- GS_API_DECL void gs_gui_update_control(gs_gui_context_t *ctx, gs_gui_id id, gs_gui_rect_t rect, uint64_t opt)
- {
- int32_t mouseover = 0;
- gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
- gs_gui_id prev_hov = ctx->prev_hover;
- gs_gui_id prev_foc = ctx->prev_focus;
- // I should do updates in here
- if (opt & GS_GUI_OPT_FORCEFOCUS)
- {
- mouseover = gs_gui_rect_overlaps_vec2(gs_gui_get_clip_rect(ctx), ctx->mouse_pos);
- }
- else
- {
- mouseover = gs_gui_mouse_over(ctx, rect);
- }
- // Check for 'mouse-over' with id selection here
- if (ctx->focus == id) { ctx->updated_focus = 1; }
- if (opt & GS_GUI_OPT_NOINTERACT) { return; }
- // Check for hold focus here
- if (mouseover && !ctx->mouse_down) {
- gs_gui_set_hover(ctx, id);
- }
- if (ctx->focus == id)
- {
- gs_gui_set_focus(ctx, id);
- if (ctx->mouse_pressed && !mouseover) { gs_gui_set_focus(ctx, 0); }
- if (!ctx->mouse_down && ~opt & GS_GUI_OPT_HOLDFOCUS) { gs_gui_set_focus(ctx, 0); }
- }
- if (ctx->prev_hover == id && !mouseover) {ctx->prev_hover = ctx->hover;}
- if (ctx->hover == id)
- {
- if (ctx->mouse_pressed)
- {
- if ((opt & GS_GUI_OPT_LEFTCLICKONLY && ctx->mouse_pressed == GS_GUI_MOUSE_LEFT) || (~opt & GS_GUI_OPT_LEFTCLICKONLY))
- {
- gs_gui_set_focus(ctx, id);
- }
- }
- else if (!mouseover)
- {
- gs_gui_set_hover(ctx, 0);
- }
- }
- // Do state check
- if (~opt & GS_GUI_OPT_NOSWITCHSTATE)
- {
- if (ctx->focus == id)
- {
- if (ctx->prev_focus != id) ctx->last_focus_state = GS_GUI_ELEMENT_STATE_ON_FOCUS;
- else ctx->last_focus_state = GS_GUI_ELEMENT_STATE_FOCUS;
- }
- else
- {
- if (ctx->prev_focus == id) ctx->last_focus_state = GS_GUI_ELEMENT_STATE_OFF_FOCUS;
- else ctx->last_focus_state = GS_GUI_ELEMENT_STATE_DEFAULT;
- }
- if (ctx->hover == id)
- {
- if (ctx->prev_hover != id) ctx->last_hover_state = GS_GUI_ELEMENT_STATE_ON_HOVER;
- else ctx->last_hover_state = GS_GUI_ELEMENT_STATE_HOVER;
- }
- else
- {
- if (ctx->prev_hover == id) ctx->last_hover_state = GS_GUI_ELEMENT_STATE_OFF_HOVER;
- else ctx->last_hover_state = GS_GUI_ELEMENT_STATE_DEFAULT;
- }
- if (ctx->prev_focus == id && !ctx->mouse_down && ~opt & GS_GUI_OPT_HOLDFOCUS) {
- ctx->prev_focus = ctx->focus;
- }
- if (
- ctx->last_hover_state == GS_GUI_ELEMENT_STATE_ON_HOVER ||
- ctx->last_hover_state == GS_GUI_ELEMENT_STATE_OFF_HOVER ||
- ctx->last_focus_state == GS_GUI_ELEMENT_STATE_ON_FOCUS ||
- ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS
- )
- {
- // Don't have a hover state switch if focused
- ctx->switch_state = ctx->last_focus_state ? ctx->last_focus_state : ctx->focus != id ? ctx->last_hover_state : GS_GUI_ELEMENT_STATE_DEFAULT;
- switch (ctx->switch_state)
- {
- case GS_GUI_ELEMENT_STATE_OFF_HOVER:
- case GS_GUI_ELEMENT_STATE_ON_HOVER:
- {
- if (ctx->focus == id || ctx->prev_focus == id)
- {
- ctx->switch_state = 0x00;
- }
- } break;
- }
- if (ctx->switch_state && ctx->prev_focus != id) ctx->state_switch_id = id;
- }
- }
- else
- {
- ctx->prev_focus = prev_foc;
- ctx->prev_hover = prev_hov;
- }
- }
- GS_API_DECL int32_t
- gs_gui_text_ex(gs_gui_context_t* ctx, const char* text, int32_t wrap, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- int32_t res = 0, elementid = GS_GUI_ELEMENT_TEXT;
- gs_gui_id id = gs_gui_get_id(ctx, text, strlen(text));
- gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
- gs_gui_style_t style = gs_default_val();
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
- const char *start, *end, *p = text;
- int32_t width = -1;
- // Update anim (keep states locally within animation, only way to do this)
- if (anim)
- {
- gs_gui_animation_update(ctx, anim);
- // Get blended style based on animation
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
- }
- else
- {
- style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
- ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
- }
- gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
- gs_asset_font_t* font = ctx->style->font;
- gs_color_t* color = &ctx->style->colors[GS_GUI_COLOR_CONTENT];
- int32_t sx = ctx->style->shadow_x;
- int32_t sy = ctx->style->shadow_y;
- if (opt & GS_GUI_OPT_NOSTYLESHADOW){sx = 0; sy = 0;}
- gs_color_t* sc = &ctx->style->colors[GS_GUI_COLOR_SHADOW];
- int32_t th = gs_gui_font_height(font);
- gs_gui_layout_column_begin(ctx);
- gs_gui_layout_row(ctx, 1, &width, th);
- gs_gui_rect_t tr = gs_gui_layout_next(ctx);
- gs_gui_layout_set_next(ctx, tr, 0);
- gs_gui_rect_t r = gs_gui_layout_next(ctx);
- gs_gui_rect_t bg = r;
- do
- {
- int32_t w = 0;
- start = end = p;
- do
- {
- const char* word = p;
- while (*p && *p != ' ' && *p != '\n')
- {
- p++;
- }
- if (wrap) w += gs_gui_text_width(font, word, p - word);
- if (w > r.w && end != start)
- {
- break;
- }
- if (wrap) w += gs_gui_text_width(font, p, 1);
- end = p++;
- } while (*end && *end != '\n');
- if (r.w > tr.w) tr.w = r.w;
- tr.h = (r.y + r.h) - tr.y;
- gs_gui_rect_t txtrct = r;
- bg = r;
- if (*end)
- {
- r = gs_gui_layout_next(ctx);
- bg.h = r.y - bg.y;
- }
- else
- {
- int32_t th = gs_gui_text_height(font, start, end - start);
- bg.h = r.h + (float)th / 2.f;
- }
-
- // Draw frame here for background if applicable (need to do this to account for space between wrap)
- if (ctx->style->colors[GS_GUI_COLOR_BACKGROUND].a && ~opt & GS_GUI_OPT_NOSTYLEBACKGROUND)
- {
- gs_gui_draw_rect(ctx, bg, style.colors[GS_GUI_COLOR_BACKGROUND]);
- }
- // Draw text
- gs_gui_draw_text(ctx, font, start, end - start, gs_v2(txtrct.x, txtrct.y), *color, sx, sy, *sc);
- p = end + 1;
- } while (*end);
- // draw border
- if (style.colors[GS_GUI_COLOR_BORDER].a && ~opt & GS_GUI_OPT_NOSTYLEBORDER)
- {
- gs_gui_draw_box(ctx, gs_gui_expand_rect(tr, (int16_t*)style.border_width), (int16_t*)style.border_width, style.colors[GS_GUI_COLOR_BORDER]);
- }
- gs_gui_update_control(ctx, id, tr, 0x00);
- // handle click
- if (ctx->mouse_down != GS_GUI_MOUSE_LEFT && ctx->hover == id && ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS)
- {
- res |= GS_GUI_RES_SUBMIT;
- }
- gs_gui_layout_column_end(ctx);
- gs_gui_pop_style(ctx, save);
- return res;
- }
- GS_API_DECL int32_t
- gs_gui_label_ex(gs_gui_context_t* ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- // Want to push animations here for styles
- int32_t res = 0;
- int32_t elementid = GS_GUI_ELEMENT_LABEL;
- gs_gui_id id = gs_gui_get_id(ctx, label, gs_strlen(label));
- char id_tag[256] = gs_default_val();
- char label_tag[256] = gs_default_val();
- gs_gui_parse_id_tag(ctx, label, id_tag, sizeof(id_tag), opt);
- gs_gui_parse_label_tag(ctx, label, label_tag, sizeof(label_tag));
- gs_gui_push_id(ctx, id_tag, strlen(id_tag));
- gs_gui_style_t style = gs_default_val();
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
- if (anim)
- {
- gs_gui_animation_update(ctx, anim);
- // Get blended style based on animation
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
- }
- else
- {
- style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
- ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
- }
- gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
- gs_gui_rect_t r = gs_gui_layout_next(ctx);
- gs_gui_update_control(ctx, id, r, 0x00);
- gs_gui_draw_control_text(ctx, label_tag, r, &style, 0x00);
- gs_gui_pop_style(ctx, save);
- gs_gui_pop_id(ctx);
- /* handle click */
- if (
- ctx->mouse_down != GS_GUI_MOUSE_LEFT &&
- ctx->hover == id &&
- ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS
- )
- {
- res |= GS_GUI_RES_SUBMIT;
- }
- return res;
- }
- GS_API_DECL int32_t
- gs_gui_image_ex(gs_gui_context_t* ctx, gs_handle(gs_graphics_texture_t) hndl, gs_vec2 uv0, gs_vec2 uv1, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- int32_t res = 0;
- gs_gui_id id = gs_gui_get_id(ctx, &hndl, sizeof(hndl));
- const int32_t elementid = GS_GUI_ELEMENT_IMAGE;
- gs_gui_style_t style = gs_default_val();
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
- // Update anim (keep states locally within animation, only way to do this)
- if (anim)
- {
- gs_gui_animation_update(ctx, anim);
- // Get blended style based on animation
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
- }
- else
- {
- style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
- ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
- }
- // Temporary copy of style
- gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
- gs_gui_rect_t r = gs_gui_layout_next(ctx);
- gs_gui_update_control(ctx, id, r, opt);
- /* handle click */
- if (ctx->mouse_down != GS_GUI_MOUSE_LEFT && ctx->hover == id && ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS)
- {
- res |= GS_GUI_RES_SUBMIT;
- }
- // draw border
- if (style.colors[GS_GUI_COLOR_BORDER].a)
- {
- gs_gui_draw_box(ctx, gs_gui_expand_rect(r, (int16_t*)style.border_width),
- (int16_t*)style.border_width, style.colors[GS_GUI_COLOR_BORDER]);
- }
- gs_gui_draw_image(ctx, hndl, r, uv0, uv1, style.colors[GS_GUI_COLOR_CONTENT]);
- gs_gui_pop_style(ctx, save);
- return res;
- }
- GS_API_DECL int32_t
- gs_gui_combo_begin_ex(gs_gui_context_t* ctx, const char* id, const char* current_item,
- int32_t max_items, gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- int32_t res = 0;
- opt = GS_GUI_OPT_NOMOVE |
- GS_GUI_OPT_NORESIZE |
- GS_GUI_OPT_NOTITLE |
- GS_GUI_OPT_FORCESETRECT;
- if (gs_gui_button(ctx, current_item)) {
- gs_gui_popup_open(ctx, id);
- }
- int32_t ct = max_items > 0 ? max_items : 0;
- gs_gui_rect_t rect = ctx->last_rect;
- rect.y += rect.h;
- rect.h = ct ? (ct + 1) * ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x00].size[1] : rect.h;
- res = gs_gui_popup_begin_ex(ctx, id, rect, NULL, opt);
- return res;
- }
- GS_API_DECL int32_t
- gs_gui_combo_item_ex(gs_gui_context_t* ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- int32_t res = gs_gui_button_ex(ctx, label, desc, opt);
- if (res) {
- gs_gui_current_container_close(ctx);
- }
- return res;
- }
- GS_API_DECL void
- gs_gui_combo_end(gs_gui_context_t* ctx)
- {
- gs_gui_popup_end(ctx);
- }
- GS_API_DECL void
- gs_gui_parse_label_tag(gs_gui_context_t* ctx, const char* str, char* buffer, size_t sz)
- {
- gs_lexer_t lex = gs_lexer_c_ctor(str);
- while (gs_lexer_can_lex(&lex))
- {
- gs_token_t token = gs_lexer_next_token(&lex);
- switch (token.type)
- {
- case GS_TOKEN_HASH:
- {
- if (gs_lexer_peek(&lex).type == GS_TOKEN_HASH)
- {
- gs_token_t end = gs_lexer_current_token(&lex);
- // Determine len
- size_t len = gs_min(end.text - str, sz);
- memcpy(buffer, str, len);
- return;
- }
- } break;
- }
- }
- // Reached end, so just memcpy
- memcpy(buffer, str, gs_min(sz, strlen(str) + 1));
- }
- GS_API_DECL void gs_gui_parse_id_tag(gs_gui_context_t* ctx, const char* str, char* buffer, size_t sz, uint64_t opt)
- {
- if (opt & GS_GUI_OPT_PARSEIDTAGONLY) {
- gs_lexer_t lex = gs_lexer_c_ctor(str);
- while (gs_lexer_can_lex(&lex))
- {
- gs_token_t token = gs_lexer_next_token(&lex);
- switch (token.type)
- {
- case GS_TOKEN_HASH:
- {
- if (gs_lexer_peek(&lex).type == GS_TOKEN_HASH)
- {
- gs_token_t end = gs_lexer_next_token(&lex);
- end = gs_lexer_next_token(&lex);
- // Determine len
- size_t len = gs_min((str + strlen(str)) - end.text, sz);
- memcpy(buffer, end.text, len);
- return;
- }
- } break;
- }
- }
- } else {
- size_t str_sz = strlen(str);
- size_t actual_sz = gs_min(str_sz, sz-1);
- memcpy(buffer, str, actual_sz);
- buffer[actual_sz] = 0;
- }
- }
- GS_API_DECL int32_t
- gs_gui_button_ex(gs_gui_context_t* ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- // Note(john): clip out early here for performance
- int32_t res = 0;
- gs_gui_id id = gs_gui_get_id(ctx, label, strlen(label));
- gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
- char id_tag[256] = gs_default_val();
- char label_tag[256] = gs_default_val();
- gs_gui_parse_id_tag(ctx, label, id_tag, sizeof(id_tag), opt);
- gs_gui_parse_label_tag(ctx, label, label_tag, sizeof(label_tag));
- gs_gui_style_t style = gs_default_val();
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, GS_GUI_ELEMENT_BUTTON);
- // Push id if tag available
- gs_gui_push_id(ctx, id_tag, strlen(id_tag));
- // Update anim (keep states locally within animation, only way to do this)
- if (anim)
- {
- gs_gui_animation_update(ctx, anim);
- // Get blended style based on animation
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, GS_GUI_ELEMENT_BUTTON);
- }
- else
- {
- style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, GS_GUI_ELEMENT_BUTTON, 0x02) :
- ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, GS_GUI_ELEMENT_BUTTON, 0x01) :
- gs_gui_get_current_element_style(ctx, desc, GS_GUI_ELEMENT_BUTTON, 0x00);
- }
- // Temporary copy of style
- gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
- gs_gui_rect_t r = gs_gui_layout_next(ctx);
- gs_gui_update_control(ctx, id, r, opt);
- /* handle click or button press for submission */
- if (ctx->mouse_down != GS_GUI_MOUSE_LEFT && ctx->hover == id && ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS)
- {
- res |= GS_GUI_RES_SUBMIT;
- }
- // draw border
- if (style.colors[GS_GUI_COLOR_BORDER].a)
- {
- gs_gui_draw_box(ctx, gs_gui_expand_rect(r, (int16_t*)style.border_width), (int16_t*)style.border_width, style.colors[GS_GUI_COLOR_BORDER]);
- }
- opt |= GS_GUI_OPT_ISCONTENT;
- gs_gui_draw_rect(ctx, r, style.colors[GS_GUI_COLOR_BACKGROUND]);
- if (label) {gs_gui_draw_control_text(ctx, label_tag, r, &style, opt);}
- gs_gui_pop_style(ctx, save);
- gs_gui_pop_id(ctx);
- return res;
- }
- GS_API_DECL int32_t gs_gui_checkbox_ex(gs_gui_context_t* ctx, const char* label, int32_t* state, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- int32_t res = 0;
- gs_gui_id id = gs_gui_get_id(ctx, &state, sizeof(state));
- gs_gui_rect_t r = gs_gui_layout_next(ctx);
- gs_gui_rect_t box = gs_gui_rect(r.x, r.y, r.h, r.h);
- int32_t ox = (int32_t)(box.w * 0.2f), oy = (int32_t)(box.h * 0.2f);
- gs_gui_rect_t inner_box = gs_gui_rect(box.x + ox, box.y + oy, box.w - 2 * ox, box.h - 2 * oy);
- gs_gui_update_control(ctx, id, r, 0);
- int32_t elementid = GS_GUI_ELEMENT_BUTTON;
- gs_gui_style_t style = gs_default_val();
- style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
- ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
- /* handle click */
- if ((ctx->mouse_pressed == GS_GUI_MOUSE_LEFT || (ctx->mouse_pressed && ~opt & GS_GUI_OPT_LEFTCLICKONLY)) && ctx->focus == id)
- {
- res |= GS_GUI_RES_CHANGE;
- *state = !*state;
- }
- /* draw */
- gs_gui_draw_control_frame(ctx, id, box, GS_GUI_ELEMENT_INPUT, 0);
- if (*state)
- {
- // Draw in a filled rect
- gs_gui_draw_rect(ctx, inner_box, style.colors[GS_GUI_COLOR_BACKGROUND]);
- }
- r = gs_gui_rect(r.x + box.w, r.y, r.w - box.w, r.h);
- gs_gui_draw_control_text(ctx, label, r, &ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0], 0);
- return res;
- }
- GS_API_DECL int32_t gs_gui_textbox_raw(gs_gui_context_t* ctx, char* buf, int32_t bufsz, gs_gui_id id, gs_gui_rect_t rect,
- const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- int32_t res = 0;
- int32_t elementid = GS_GUI_ELEMENT_INPUT;
- gs_gui_style_t style = gs_default_val();
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
- // Update anim (keep states locally within animation, only way to do this)
- if (anim)
- {
- // Need to check that I haven't updated more than once this frame
- gs_gui_animation_update(ctx, anim);
- // Get blended style based on animation
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
- }
- else
- {
- style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
- ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
- }
- // Push temp style
- gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
- gs_gui_update_control(ctx, id, rect, opt | GS_GUI_OPT_HOLDFOCUS);
- if (ctx->focus == id)
- {
- /* handle text input */
- int32_t len = strlen(buf);
- int32_t n = gs_min(bufsz - len - 1, (int32_t) strlen(ctx->input_text));
- if (n > 0)
- {
- memcpy(buf + len, ctx->input_text, n);
- len += n;
- buf[len] = '\0';
- res |= GS_GUI_RES_CHANGE;
- }
- /* handle backspace */
- if (ctx->key_pressed & GS_GUI_KEY_BACKSPACE && len > 0)
- {
- if (ctx->key_down & GS_GUI_KEY_CTRL) {
- for (--len; len > 0 ;len--) {
- /* skip utf-8 continuation bytes */
- if ((buf[len-1] & 0xc0) == 0x80) continue;
- /* seek until seperator character */
- if (strchr(" ()[]{},.-+*=/\\^~|\"'&%#@!<>;:", buf[len-1])) break;
- }
- } else {
- /* skip utf-8 continuation bytes */
- while ((buf[--len] & 0xc0) == 0x80 && len > 0);
- }
- buf[len] = '\0';
- res |= GS_GUI_RES_CHANGE;
- }
- /* handle paste */
- if (gs_platform_key_pressed(GS_KEYCODE_V) && ctx->key_down & GS_GUI_KEY_CTRL)
- {
- const char* clipboard = gs_platform_window_get_clipboard(ctx->window_hndl);
- printf("%s --\n", clipboard);
- int32_t n = gs_min(bufsz - len - 1, (int32_t) strlen(clipboard));
- if (n > 0) {
- memcpy(buf + len, clipboard, n);
- len += n;
- buf[len] = '\0';
- res |= GS_GUI_RES_CHANGE;
- }
- }
- /* handle return */
- if (ctx->key_pressed & GS_GUI_KEY_RETURN)
- {
- gs_gui_set_focus(ctx, 0);
- res |= GS_GUI_RES_SUBMIT;
- }
- }
- /* draw */
- // Textbox border
- gs_gui_draw_box(ctx, gs_gui_expand_rect(rect, (int16_t*)style.border_width), (int16_t*)style.border_width, style.colors[GS_GUI_COLOR_BORDER]);
- // Textbox bg
- gs_gui_draw_control_frame(ctx, id, rect, GS_GUI_ELEMENT_INPUT, opt);
- // Text and carret
- if (ctx->focus == id)
- {
- gs_gui_style_t* sp = &style;
- gs_color_t* color = &sp->colors[GS_GUI_COLOR_CONTENT];
- int32_t sx = sp->shadow_x;
- int32_t sy = sp->shadow_y;
- gs_color_t* sc = &sp->colors[GS_GUI_COLOR_SHADOW];
- gs_asset_font_t* font = sp->font;
- int32_t textw = gs_gui_text_width(font, buf, -1);
- int32_t texth = gs_gui_font_height(font);
- int32_t ofx = (int32_t)(rect.w - sp->padding[GS_GUI_PADDING_RIGHT] - textw - 1);
- int32_t textx = (int32_t)(rect.x + gs_min(ofx, sp->padding[GS_GUI_PADDING_LEFT]));
- int32_t texty = (int32_t)(rect.y + (rect.h - texth) / 2);
- int32_t cary = (int32_t)(rect.y + 1);
- gs_gui_push_clip_rect(ctx, rect);
- // Draw text
- gs_gui_draw_control_text(ctx, buf, rect, &style, opt);
- // Draw caret (control alpha based on frame)
- static bool on = true;
- static float ct = 0.f;
- if (~opt & GS_GUI_OPT_NOCARET)
- {
- gs_vec2 pos = gs_v2(rect.x, rect.y);
- // Grab stylings
- const int32_t padding_left = sp->padding[GS_GUI_PADDING_LEFT];
- const int32_t padding_top = sp->padding[GS_GUI_PADDING_TOP];
- const int32_t padding_right = sp->padding[GS_GUI_PADDING_RIGHT];
- const int32_t padding_bottom = sp->padding[GS_GUI_PADDING_BOTTOM];
- const int32_t align = sp->align_content;
- const int32_t justify = sp->justify_content;
- // Determine x placement based on justification
- switch (justify)
- {
- default:
- case GS_GUI_JUSTIFY_START:
- {
- pos.x = rect.x + padding_left;
- } break;
- case GS_GUI_JUSTIFY_CENTER:
- {
- pos.x = rect.x + (rect.w - textw) * 0.5f;
- } break;
- case GS_GUI_JUSTIFY_END:
- {
- pos.x = rect.x + (rect.w - textw) - padding_right;
- } break;
- }
- // Determine caret position based on style justification
- gs_gui_rect_t cr = gs_gui_rect(pos.x + textw + padding_right,
- (f32)rect.y + 5.f, 1.f, (f32)rect.h - 10.f);
- if (ctx->last_focus_state == GS_GUI_ELEMENT_STATE_ON_FOCUS) {on = true; ct = 0.f;}
- ct += 0.1f;
- if (ct >= 3.f) {on = !on; ct = 0.f;}
- gs_color_t col = *color;
- col.a = on ? col.a : 0;
- gs_gui_draw_rect(ctx, cr, col);
- }
- gs_gui_pop_clip_rect(ctx);
- }
- else
- {
- gs_gui_style_t* sp = &style;
- gs_color_t* color = &sp->colors[GS_GUI_COLOR_CONTENT];
- gs_asset_font_t* font = sp->font;
- int32_t sx = sp->shadow_x;
- int32_t sy = sp->shadow_y;
- gs_color_t* sc = &sp->colors[GS_GUI_COLOR_SHADOW];
- int32_t textw = gs_gui_text_width(font, buf, -1);
- int32_t texth = gs_gui_text_height(font, buf, -1);
- int32_t textx = (int32_t)(rect.x + sp->padding[GS_GUI_PADDING_LEFT]);
- int32_t texty = (int32_t)(rect.y + (rect.h - texth) / 2);
- gs_gui_push_clip_rect(ctx, rect);
- gs_gui_draw_control_text(ctx, buf, rect, &style, opt);
- gs_gui_pop_clip_rect(ctx);
- }
- gs_gui_pop_style(ctx, save);
- return res;
- }
- static int32_t gs_gui_number_textbox(gs_gui_context_t *ctx, gs_gui_real *value, gs_gui_rect_t r, gs_gui_id id, const gs_gui_selector_desc_t* desc)
- {
- if (ctx->mouse_pressed == GS_GUI_MOUSE_LEFT && ctx->key_down & GS_GUI_KEY_SHIFT &&
- ctx->hover == id
- )
- {
- ctx->number_edit = id;
- gs_snprintf(ctx->number_edit_buf, GS_GUI_MAX_FMT, GS_GUI_REAL_FMT, *value);
- }
- if (ctx->number_edit == id)
- {
- // This is broken for some reason...
- int32_t res = gs_gui_textbox_raw(ctx, ctx->number_edit_buf,
- sizeof(ctx->number_edit_buf), id, r, desc, 0);
- if (res & GS_GUI_RES_SUBMIT || ctx->focus != id)
- {
- *value = strtod(ctx->number_edit_buf, NULL);
- ctx->number_edit = 0;
- }
- else
- {
- return 1;
- }
- }
- return 0;
- }
- GS_API_DECL int32_t gs_gui_textbox_ex(gs_gui_context_t* ctx, char* buf, int32_t bufsz, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- // Handle animation here...
- int32_t res = 0;
- gs_gui_id id = gs_gui_get_id(ctx, &buf, sizeof(buf));
- int32_t elementid = GS_GUI_ELEMENT_INPUT;
- gs_gui_style_t style = gs_default_val();
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
- // Update anim (keep states locally within animation, only way to do this)
- if (anim)
- {
- // Need to check that I haven't updated more than once this frame
- gs_gui_animation_update(ctx, anim);
- // Get blended style based on animation
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
- }
- else
- {
- style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
- ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
- }
- // Push temp style
- gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
- gs_gui_rect_t r = gs_gui_layout_next(ctx);
- gs_gui_update_control(ctx, id, r, opt | GS_GUI_OPT_HOLDFOCUS);
- res |= gs_gui_textbox_raw(ctx, buf, bufsz, id, r, desc, opt);
- gs_gui_pop_style(ctx, save);
- return res;
- }
- GS_API_DECL int32_t gs_gui_slider_ex(gs_gui_context_t* ctx, gs_gui_real* value, gs_gui_real low, gs_gui_real high, gs_gui_real step,
- const char* fmt, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- char buf[GS_GUI_MAX_FMT + 1];
- gs_gui_rect_t thumb;
- int32_t x, w, res = 0;
- gs_gui_real last = *value, v = last;
- gs_gui_id id = gs_gui_get_id(ctx, &value, sizeof(value));
- int32_t elementid = GS_GUI_ELEMENT_INPUT;
- gs_gui_style_t style = gs_default_val();
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
- int32_t state = ctx->focus == id ? GS_GUI_ELEMENT_STATE_FOCUS :
- ctx->hover == id ? GS_GUI_ELEMENT_STATE_HOVER :
- GS_GUI_ELEMENT_STATE_DEFAULT;
- // Update anim (keep states locally within animation, only way to do this)
- if (anim)
- {
- gs_gui_animation_update(ctx, anim);
- // Get blended style based on animation
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
- }
- else
- {
- style = gs_gui_get_current_element_style(ctx, desc, elementid, state);
- }
- // Temporary copy of style
- gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
- gs_gui_rect_t base = gs_gui_layout_next(ctx);
- /* handle text input mode */
- if (gs_gui_number_textbox(ctx, &v, base, id, desc)) {return res;}
- /* handle normal mode */
- gs_gui_update_control(ctx, id, base, opt);
- /* handle input */
- if (ctx->focus == id &&
- (ctx->mouse_down | ctx->mouse_pressed) == GS_GUI_MOUSE_LEFT)
- {
- v = low + (ctx->mouse_pos.x - base.x) * (high - low) / base.w;
- if (step) {v = (((v + step / 2) / step)) * step;}
- }
- /* clamp and store value, update res */
- *value = v = gs_clamp(v, low, high);
- if (last != v) {res |= GS_GUI_RES_CHANGE;}
- /* draw base */
- gs_gui_draw_control_frame(ctx, id, base, GS_GUI_ELEMENT_INPUT, opt);
- /* draw control */
- w = style.thumb_size; // Don't like this...
- x = (int32_t)((v - low) * (base.w - w) / (high - low));
- thumb = gs_gui_rect((f32)base.x + (f32)x, base.y, (f32)w, base.h);
- gs_gui_draw_control_frame(ctx, id, thumb, GS_GUI_ELEMENT_BUTTON, opt);
- /* draw text */
- style.colors[GS_GUI_COLOR_BACKGROUND] = ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][state].colors[GS_GUI_COLOR_BACKGROUND];
- gs_snprintf(buf, GS_GUI_MAX_FMT, fmt, v);
- gs_gui_draw_control_text(ctx, buf, base, &style, opt); // oh...bg
- // Pop style
- gs_gui_pop_style(ctx, save);
- return res;
- }
- GS_API_DECL int32_t gs_gui_number_ex(gs_gui_context_t* ctx, gs_gui_real* value, gs_gui_real step, const char* fmt,
- const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- char buf[GS_GUI_MAX_FMT + 1];
- int32_t res = 0;
- gs_gui_id id = gs_gui_get_id(ctx, &value, sizeof(value));
- int32_t elementid = GS_GUI_ELEMENT_INPUT;
- gs_gui_style_t style = gs_default_val();
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
- // Update anim (keep states locally within animation, only way to do this)
- if (anim)
- {
- gs_gui_animation_update(ctx, anim);
- // Get blended style based on animation
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
- }
- else
- {
- style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
- ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
- }
- // Temporary copy of style
- gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
- gs_gui_rect_t base = gs_gui_layout_next(ctx);
- gs_gui_real last = *value;
- /* handle text input mode */
- if (gs_gui_number_textbox(ctx, value, base, id, desc)) {
- gs_gui_pop_style(ctx, save);
- return res;
- }
- /* handle normal mode */
- gs_gui_update_control(ctx, id, base, opt);
- /* handle input */
- if (ctx->focus == id && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- *value += ctx->mouse_delta.x * step;
- }
- /* set flag if value changed */
- if (*value != last) { res |= GS_GUI_RES_CHANGE; }
- /* draw base */
- gs_gui_draw_control_frame(ctx, id, base, GS_GUI_ELEMENT_INPUT, opt);
- /* draw text */
- gs_snprintf(buf, GS_GUI_MAX_FMT, fmt, *value);
- gs_gui_draw_control_text(ctx, buf, base, &ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0], opt);
- gs_gui_pop_style(ctx, save);
- return res;
- }
- static int32_t _gs_gui_header(gs_gui_context_t *ctx, const char *label, int32_t istreenode,
- const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- gs_gui_rect_t r;
- int32_t active, expanded;
- int32_t width = -1;
- gs_gui_layout_row(ctx, 1, &width, 0);
- char id_tag[256] = gs_default_val();
- char label_tag[256] = gs_default_val();
- gs_gui_parse_id_tag(ctx, label, id_tag, sizeof(id_tag), opt);
- gs_gui_parse_label_tag(ctx, label, label_tag, sizeof(label_tag));
- gs_gui_id id = gs_gui_get_id(ctx, id_tag, strlen(id_tag));
- int32_t idx = gs_gui_pool_get(ctx, ctx->treenode_pool, GS_GUI_TREENODEPOOL_SIZE, id);
- gs_gui_push_id(ctx, id_tag, strlen(id_tag));
- active = (idx >= 0);
- expanded = (opt & GS_GUI_OPT_EXPANDED) ? !active : active;
- r = gs_gui_layout_next(ctx);
- gs_gui_update_control(ctx, id, r, 0);
- /* handle click */
- active ^= (ctx->mouse_pressed == GS_GUI_MOUSE_LEFT && ctx->focus == id);
- /* update pool ref */
- if (idx >= 0)
- {
- if (active)
- { gs_gui_pool_update(ctx, ctx->treenode_pool, idx);
- }
- else
- {
- memset(&ctx->treenode_pool[idx], 0, sizeof(gs_gui_pool_item_t));
- }
- }
- else if (active)
- {
- gs_gui_pool_init(ctx, ctx->treenode_pool, GS_GUI_TREENODEPOOL_SIZE, id);
- }
- /* draw */
- if (istreenode)
- {
- if (ctx->hover == id)
- {
- gs_gui_draw_frame(ctx, r, &ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_HOVER]);
- }
- }
- else
- {
- gs_gui_draw_control_frame(ctx, id, r, GS_GUI_ELEMENT_BUTTON, 0);
- }
- const float sz = 6.f;
- if (expanded)
- {
- gs_vec2 a = {r.x + sz / 2.f, r.y + (r.h - sz) / 2.f};
- gs_vec2 b = gs_vec2_add(a, gs_v2(sz, 0.f));
- gs_vec2 c = gs_vec2_add(a, gs_v2(sz / 2.f, sz));
- gs_gui_draw_triangle(ctx, a, b, c, ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0x00].colors[GS_GUI_COLOR_CONTENT]);
- }
- else
- {
- gs_vec2 a = {r.x + sz / 2.f, r.y + (r.h - sz) / 2.f};
- gs_vec2 b = gs_vec2_add(a, gs_v2(sz, sz / 2.f));
- gs_vec2 c = gs_vec2_add(a, gs_v2(0.f, sz));
- gs_gui_draw_triangle(ctx, a, b, c, ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0x00].colors[GS_GUI_COLOR_CONTENT]);
- }
- // Draw text for treenode
- r.x += r.h - ctx->style->padding[GS_GUI_PADDING_TOP];
- r.w -= r.h - ctx->style->padding[GS_GUI_PADDING_BOTTOM];
- gs_gui_draw_control_text(ctx, label_tag, r, &ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0x00], 0);
- gs_gui_pop_id(ctx);
- return expanded ? GS_GUI_RES_ACTIVE : 0;
- }
- GS_API_DECL int32_t
- gs_gui_header_ex(gs_gui_context_t* ctx, const char* label,
- const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- return _gs_gui_header(ctx, label, 0, desc, opt);
- }
- GS_API_DECL int32_t
- gs_gui_treenode_begin_ex(gs_gui_context_t * ctx, const char* label,
- const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- int32_t res = _gs_gui_header(ctx, label, 1, desc, opt);
- if (res & GS_GUI_RES_ACTIVE)
- {
- gs_gui_get_layout(ctx)->indent += ctx->style->indent;
- gs_gui_stack_push(ctx->id_stack, ctx->last_id);
- }
- return res;
- }
- GS_API_DECL void
- gs_gui_treenode_end(gs_gui_context_t *ctx)
- {
- gs_gui_get_layout(ctx)->indent -= ctx->style->indent;
- gs_gui_pop_id(ctx);
- }
- // -1 for left, + 1 for right
- GS_API_DECL void
- gs_gui_tab_item_swap(gs_gui_context_t* ctx, gs_gui_container_t* cnt, int32_t direction)
- {
- gs_gui_tab_bar_t* tab_bar = gs_gui_get_tab_bar(ctx, cnt);
- if (!tab_bar) return;
-
- int32_t item = (int32_t)cnt->tab_item;
- int32_t idx = gs_clamp(item + direction, 0, (int32_t)tab_bar->size - 1);
- gs_gui_container_t* scnt = (gs_gui_container_t*)tab_bar->items[idx].data;
- gs_gui_tab_item_t* cti = &tab_bar->items[cnt->tab_item];
- gs_gui_tab_item_t* sti = &tab_bar->items[idx];
- gs_gui_tab_item_t tmp = *cti;
- // Swap cti
- sti->data = cnt;
- cnt->tab_item = sti->idx;
- // Swap sti
- cti->data = scnt;
- scnt->tab_item = cti->idx;
- tab_bar->focus = sti->idx;
- }
- GS_API_DECL int32_t
- gs_gui_window_begin_ex(gs_gui_context_t * ctx, const char* title, gs_gui_rect_t rect, bool* open,
- const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- gs_gui_rect_t body;
- char id_tag[256] = gs_default_val();
- char label_tag[256] = gs_default_val();
- gs_gui_parse_id_tag(ctx, title, id_tag, sizeof(id_tag), opt);
- gs_gui_parse_label_tag(ctx, title, label_tag, sizeof(label_tag));
- gs_gui_id id = 0x00;
- /*
- if (*id_tag) {
- // id = gs_gui_push_id(ctx, id_tag, sizeof(id_tag));
- id = gs_gui_get_id(ctx, id_tag, sizeof(id_tag));
- }
- else {
- id = gs_gui_get_id(ctx, label_tag, sizeof(label_tag));
- // id = gs_gui_get_id(ctx, id_tag, gs_strlen(title));
- // id = gs_gui_get_id(ctx, id_tag, sizeof(id_tag));
- }
- */
- // id = gs_gui_get_id(ctx, title, strlen(title));
- id = gs_gui_get_id(ctx, id_tag, strlen(id_tag));
- gs_gui_container_t* cnt = gs_gui_get_container_ex(ctx, id, opt);
- if (cnt && open) {
- cnt->open = *open;
- }
- if (!cnt || !cnt->open) {
- // gs_gui_pop_id(ctx);
- return 0;
- }
- // Push id flag
- // cnt->flags |= GS_GUI_WINDOW_FLAGS_PUSH_ID;
- if (cnt->flags & GS_GUI_WINDOW_FLAGS_FIRST_INIT) {
- memcpy(cnt->name, label_tag, 256);
- }
- const int32_t title_max_size = 100;
- bool new_frame = cnt->frame != ctx->frame;
- int32_t state = ctx->active_root == cnt ? GS_GUI_ELEMENT_STATE_FOCUS :
- ctx->hover_root == cnt ? GS_GUI_ELEMENT_STATE_HOVER :
- GS_GUI_ELEMENT_STATE_DEFAULT;
- const float split_size = GS_GUI_SPLIT_SIZE;
- gs_gui_stack_push(ctx->id_stack, id);
- // Get splits
- gs_gui_split_t* split = gs_gui_get_split(ctx, cnt);
- gs_gui_split_t* root_split = gs_gui_get_root_split(ctx, cnt);
- // Get root container
- gs_gui_container_t* root_cnt = gs_gui_get_root_container(ctx, cnt);
- // Cache rect
- if ((cnt->rect.w == 0.f || opt & GS_GUI_OPT_FORCESETRECT || opt & GS_GUI_OPT_FULLSCREEN || cnt->flags & GS_GUI_WINDOW_FLAGS_FIRST_INIT) && new_frame)
- {
- if (opt & GS_GUI_OPT_FULLSCREEN)
- {
- gs_vec2 fb = ctx->framebuffer_size;
- cnt->rect = gs_gui_rect(0, 0, fb.x, fb.y);
- // Set root split rect size
- if (root_split)
- {
- root_split->rect = cnt->rect;
- gs_gui_update_split(ctx, root_split);
- }
- }
- else
- {
- // Set root split rect size
- if (root_split && root_cnt == cnt)
- {
- root_split->rect = rect;
- gs_gui_update_split(ctx, root_split);
- }
- else
- {
- cnt->rect = rect;
- }
- }
- cnt->flags = cnt->flags & ~GS_GUI_WINDOW_FLAGS_FIRST_INIT;
- }
- gs_gui_begin_root_container(ctx, cnt, opt);
- rect = body = cnt->rect;
- cnt->opt = opt;
- if (opt & GS_GUI_OPT_DOCKSPACE)
- {
- cnt->zindex = 0;
- }
- // If parent cannot move/resize, set to this opt as well
- if (root_cnt->opt & GS_GUI_OPT_NOMOVE)
- {
- cnt->opt |= GS_GUI_OPT_NOMOVE;
- }
- if (root_cnt->opt & GS_GUI_OPT_NORESIZE)
- {
- cnt->opt |= GS_GUI_OPT_NORESIZE;
- }
- // If in a tab view, then title has to be handled differently...
- gs_gui_tab_bar_t* tab_bar = cnt->tab_bar ? gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar) : NULL;
- gs_gui_tab_item_t* tab_item = tab_bar ? &tab_bar->items[cnt->tab_item] : NULL;
- if (tab_item && tab_item)
- {
- if (tab_bar->focus == tab_item->idx)
- {
- cnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
- cnt->opt &= ~GS_GUI_OPT_NOINTERACT;
- cnt->opt &= ~GS_GUI_OPT_NOHOVER;
- }
- else
- {
- cnt->flags &= ~GS_GUI_WINDOW_FLAGS_VISIBLE;
- cnt->opt |= GS_GUI_OPT_NOINTERACT;
- cnt->opt |= GS_GUI_OPT_NOHOVER;
- }
- }
- bool in_root = false;
- // If hovered root is in the tab group and moused over, then is hovered
- if (tab_bar)
- {
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (ctx->hover_root == (gs_gui_container_t*)tab_bar->items[i].data)
- {
- in_root = true;
- break;
- }
- }
- }
- gs_gui_container_t* s_cnt = cnt;
- if (tab_bar && split)
- {
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (((gs_gui_container_t*)tab_bar->items[i].data)->split)
- {
- s_cnt = (gs_gui_container_t*)tab_bar->items[i].data;
- }
- }
- }
- // Do split size/position
- if (split)
- {
- const gs_gui_style_t* cstyle = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state];
- const gs_gui_rect_t* sr = &split->rect;
- const float ratio = split->ratio;
- float shsz = split_size;
- const float omr = (1.f - ratio);
- switch (split->type)
- {
- case GS_GUI_SPLIT_LEFT:
- {
- if (split->children[GS_GUI_SPLIT_NODE_CHILD].container == s_cnt)
- {
- cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + shsz, sr->w * ratio - 2.f * shsz, sr->h - 2.f * shsz);
- }
- else
- {
- cnt->rect = gs_gui_rect(sr->x + sr->w * ratio + shsz, sr->y + shsz, sr->w * (1.f - ratio) - 2.f * shsz, sr->h - 2.f * shsz);
- }
- } break;
- case GS_GUI_SPLIT_RIGHT:
- {
- if (split->children[GS_GUI_SPLIT_NODE_PARENT].container == s_cnt)
- {
- cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + shsz, sr->w * (1.f - ratio) - 2.f * shsz, sr->h - 2.f * shsz);
- }
- else
- {
- cnt->rect = gs_gui_rect(sr->x + sr->w * (1.f - ratio) + shsz, sr->y + shsz, sr->w * ratio - 2.f * shsz, sr->h - 2.f * shsz);
- }
- } break;
- case GS_GUI_SPLIT_TOP:
- {
- if (split->children[GS_GUI_SPLIT_NODE_CHILD].container == s_cnt)
- {
- cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + shsz, sr->w - 2.f * shsz, sr->h * ratio - 2.f * shsz);
- }
- else
- {
- cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + sr->h * ratio + shsz, sr->w - 2.f * shsz, sr->h * (1.f - ratio) - 2.f * shsz);
- }
- } break;
- case GS_GUI_SPLIT_BOTTOM:
- {
- if (split->children[GS_GUI_SPLIT_NODE_CHILD].container == s_cnt)
- {
- cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + sr->h * (1.f - ratio) + shsz, sr->w - 2.f * shsz, sr->h * (ratio) - 2.f * shsz);
- }
- else
- {
- cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + shsz, sr->w - 2.f * shsz, sr->h * (1.f - ratio) - 2.f * shsz);
- }
- } break;
- }
- }
- // Calculate movement
- if (~cnt->opt & GS_GUI_OPT_NOTITLE && new_frame)
- {
- gs_gui_rect_t* rp = root_split ? &root_split->rect : &cnt->rect;
- // Cache rect
- gs_gui_rect_t tr = cnt->rect;
- tr.h = ctx->style->title_height;
- tr.x += split_size;
- gs_gui_id id = gs_gui_get_id(ctx, "!title", 6);
- gs_gui_update_control(ctx, id, tr, opt);
- // Need to move the entire thing
- if ((id == ctx->focus || id == ctx->hover) && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- // This lock id is what I need...
- ctx->active_root = cnt;
- if (tab_bar)
- {
- ctx->next_focus_root = (gs_gui_container_t*)(tab_bar->items[tab_bar->focus].data);
- gs_gui_bring_to_front(ctx, (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data);
- if (id == ctx->focus && tab_bar->focus != tab_item->idx) ctx->lock_focus = id;
- }
- else
- {
- ctx->next_focus_root = cnt;
- }
- if (root_split)
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_SPLIT_MOVE;
- req.split = root_split;
- gs_dyn_array_push(ctx->requests, req);
- }
- else
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_CNT_MOVE;
- req.cnt = cnt;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
- // Tab view
- int32_t tw = title_max_size;
- id = gs_gui_get_id(ctx, "!split_tab", 10);
- const float hp = 0.8f;
- tr.x += split_size;
- float h = tr.h * hp;
- float y = tr.y + tr.h * (1.f - hp);
- // Will update tab bar rect size with parent window rect
- if (tab_item)
- {
- // Get tab bar
- gs_gui_rect_t* r = &tab_bar->rect;
- // Determine width
- int32_t tab_width = (s32)gs_min(r->w / (float)tab_bar->size, title_max_size);
- tw = tab_item->zindex ? (s32)tab_width : (s32)(tab_width + 1.f);
- // Determine position (based on zindex and total width)
- float xoff = 0.f; //tab_item->zindex ? 2.f : 0.f;
- tr.x = tab_bar->rect.x + tab_width * tab_item->zindex + xoff;
- }
- gs_gui_rect_t r = gs_gui_rect(tr.x + split_size, y, (f32)tw, h);
- gs_gui_update_control(ctx, id, r, opt);
- // Need to move the entire thing
- if ((id == ctx->hover || id == ctx->focus) && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- gs_gui_set_focus(ctx, id);
- ctx->next_focus_root = cnt;
- ctx->active_root = cnt;
- // Don't move from tab bar
- if (tab_item)
- {
- // Handle break out
- if (ctx->mouse_pos.y < tr.y || ctx->mouse_pos.y > tr.y + tr.h)
- {
- ctx->undock_root = cnt;
- }
- if (tab_bar->focus != tab_item->idx)
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_CNT_FOCUS;
- req.cnt = cnt;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
- else if (root_split)
- {
- // Handle break out
- if (ctx->mouse_pos.y < tr.y || ctx->mouse_pos.y > tr.y + tr.h)
- {
- ctx->undock_root = cnt;
- }
- }
- else
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_CNT_MOVE;
- req.cnt = cnt;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
- }
- // Control frame for body movement
- if (
- ~root_cnt->opt & GS_GUI_OPT_NOMOVE &&
- ~cnt->opt & GS_GUI_OPT_NOMOVE &&
- ~cnt->opt & GS_GUI_OPT_NOINTERACT &&
- ~cnt->opt & GS_GUI_OPT_NOHOVER &&
- new_frame &&
- cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE
- )
- {
- // Cache rect
- gs_gui_rect_t br = cnt->rect;
- if (~cnt->opt & GS_GUI_OPT_NOTITLE)
- {
- br.y += ctx->style->title_height;
- br.h -= ctx->style->title_height;
- }
- gs_gui_id id = gs_gui_get_id(ctx, "!body", 5);
- // gs_gui_update_control(ctx, id, br, (opt | GS_GUI_OPT_NOSWITCHSTATE));
- if (ctx->hover_root == cnt && !ctx->focus_split && !ctx->lock_focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- ctx->active_root = cnt;
- ctx->next_focus_root = cnt;
- }
- // Need to move the entire thing
- if (ctx->hover_root == cnt && !ctx->focus_split && !ctx->focus && !ctx->lock_focus && !ctx->hover && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- if (root_split)
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_SPLIT_MOVE;
- req.split = root_split;
- gs_dyn_array_push(ctx->requests, req);
- }
- else if (tab_bar)
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_CNT_FOCUS;
- req.cnt = cnt;
- gs_dyn_array_push(ctx->requests, req);
- req.type = GS_GUI_CNT_MOVE;
- req.cnt = cnt;
- gs_dyn_array_push(ctx->requests, req);
- }
- else
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_CNT_MOVE;
- req.cnt = cnt;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
- }
- // Get parent window if in tab view, then set rect to it (will be a frame off though...)
- if (tab_item && tab_bar)
- {
- if (tab_bar->focus == tab_item->idx || split)
- {
- tab_bar->rect = cnt->rect;
- }
- else
- {
- cnt->rect = tab_bar->rect;
- }
- }
- // Cache body
- body = cnt->rect;
- if (split)
- {
- const float sh = split_size * 0.5f;
- }
- if (~opt & GS_GUI_OPT_NOTITLE)
- {
- gs_gui_rect_t tr = cnt->rect;
- tr.h = ctx->style->title_height;
- if (split)
- {
- const float sh = split_size * 0.5f;
- }
- body.y += tr.h;
- body.h -= tr.h;
- }
- int32_t zindex = INT32_MAX - 1;
- if (root_split) {
- gs_gui_get_split_lowest_zindex(ctx, root_split, &zindex);
- if (zindex == cnt->zindex) {
- gs_gui_style_t* style = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state];
- gs_gui_draw_rect(ctx, root_split->rect, style->colors[GS_GUI_COLOR_BACKGROUND]);
- gs_gui_draw_splits(ctx, root_split);
- }
- }
- // draw body frame
- if (~opt & GS_GUI_OPT_NOFRAME && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
- {
- gs_gui_style_t* style = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state];
- if (ctx->active_root == root_cnt)
- {
- int32_t f = 0;
- }
- gs_gui_draw_rect(ctx, body, style->colors[GS_GUI_COLOR_BACKGROUND]);
- // draw border (get root cnt and check state of that)
- if (split)
- {
- int32_t root_state = ctx->active_root == root_cnt ? GS_GUI_ELEMENT_STATE_FOCUS :
- ctx->hover_root == root_cnt ? GS_GUI_ELEMENT_STATE_HOVER :
- GS_GUI_ELEMENT_STATE_DEFAULT;
- bool share_split = ctx->active_root && gs_gui_get_root_container(ctx, ctx->active_root) == root_cnt ? true :
- false;
- // Have to look and see if hovered root shares split...
- gs_gui_style_t* root_style = style;
- if (share_split)
- {
- root_style = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][GS_GUI_ELEMENT_STATE_FOCUS];
- }
- else
- {
- root_style =
- state == GS_GUI_ELEMENT_STATE_FOCUS ? style :
- root_state == GS_GUI_ELEMENT_STATE_FOCUS ? &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][root_state] :
- root_state == GS_GUI_ELEMENT_STATE_HOVER ? &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][root_state] :
- style;
- }
- if (~opt & GS_GUI_OPT_NOBORDER && root_style->colors[GS_GUI_COLOR_BORDER].a) {
- gs_gui_draw_box(ctx, gs_gui_expand_rect(split->rect, (int16_t*)root_style->border_width), (int16_t*)root_style->border_width, root_style->colors[GS_GUI_COLOR_BORDER]);
- }
- }
- else
- {
- if (~opt & GS_GUI_OPT_NOBORDER && style->colors[GS_GUI_COLOR_BORDER].a) {
- gs_gui_draw_box(ctx, gs_gui_expand_rect(cnt->rect, (int16_t*)style->border_width), (int16_t*)style->border_width, style->colors[GS_GUI_COLOR_BORDER]);
- }
- }
- }
- if (split && ~opt & GS_GUI_OPT_NOCLIP && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
- {
- int16_t exp[] = {1, 1, 1, 1};
- gs_gui_push_clip_rect(ctx, gs_gui_expand_rect(cnt->rect, exp));
- }
- if (split)
- {
- const float sh = split_size * 0.5f;
- body.x += sh;
- body.w -= split_size;
- }
- // do title bar
- if (~opt & GS_GUI_OPT_NOTITLE)
- {
- gs_gui_style_t* cstyle = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state];
- gs_gui_rect_t tr = cnt->rect;
- tr.h = ctx->style->title_height;
- if (split)
- {
- const float sh = split_size * 0.5f;
- }
- // Don't draw this unless you're the bottom window or first frame in a tab group (if in dockspace)
- if (tab_bar)
- {
- bool lowest = true;
- {
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (cnt->zindex > ((gs_gui_container_t*)(tab_bar->items[i].data))->zindex)
- {
- lowest = false;
- break;
- }
- }
- if (lowest)
- {
- gs_gui_draw_frame(ctx, tr, &ctx->style_sheet->styles[GS_GUI_ELEMENT_PANEL][0x00]);
- // gs_gui_draw_box(ctx, gs_gui_expand_rect(tr, (int16_t*)cstyle->border_width), (int16_t*)cstyle->border_width, cstyle->colors[GS_GUI_COLOR_BORDER]);
- }
- }
- }
- else
- {
- gs_gui_draw_frame(ctx, tr, &ctx->style_sheet->styles[GS_GUI_ELEMENT_PANEL][0x00]);
- // gs_gui_draw_box(ctx, gs_gui_expand_rect(tr, (int16_t*)cstyle->border_width), cstyle->border_width, cstyle->colors[GS_GUI_COLOR_BORDER]);
- }
- // Draw tab control
- {
- // Tab view
- int32_t tw = title_max_size;
- id = gs_gui_get_id(ctx, "!split_tab", 10);
- const float hp = 0.8f;
- tr.x += split_size;
- float h = tr.h * hp;
- float y = tr.y + tr.h * (1.f - hp);
- // Will update tab bar rect size with parent window rect
- if (tab_item)
- {
- // Get tab bar
- gs_gui_rect_t* r = &tab_bar->rect;
- // Determine width
- int32_t tab_width = (s32)gs_min(r->w / (float)tab_bar->size, title_max_size);
- tw = (s32)(tab_width - 2.f);
- // Determine position (based on zindex and total width)
- float xoff = !tab_item->zindex ? split_size : 2.f; //tab_item->zindex ? 2.f : 0.f;
- tr.x = tab_bar->rect.x + tab_width * tab_item->zindex + xoff;
- }
- gs_gui_rect_t r = gs_gui_rect(tr.x + split_size, y, (f32)tw, h);
- bool hovered = false;
- if (in_root && gs_gui_rect_overlaps_vec2(r, ctx->mouse_pos))
- {
- gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
- // gsi_rectvd(dl, gs_v2(r.x, r.y), gs_v2(r.w, r.h), gs_v2s(0.f), gs_v2s(1.f), GS_COLOR_WHITE, GS_GRAPHICS_PRIMITIVE_LINES);
- hovered = true;
- }
- bool other_root_active = ctx->focus_root != cnt;
- if (tab_bar)
- {
- for (uint32_t i = 0; i < tab_bar->size; ++i)
- {
- if (tab_bar->items[i].data == ctx->focus_root)
- {
- other_root_active = false;
- }
- }
- }
- if (!other_root_active && hovered && ctx->mouse_down == GS_GUI_MOUSE_LEFT && !ctx->lock_focus)
- {
- // This is an issue...
- gs_gui_set_focus(ctx, id);
- ctx->lock_focus = id;
- if (tab_item && tab_bar->focus != tab_item->idx)
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_CNT_FOCUS;
- req.cnt = cnt;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
- if (!other_root_active && ctx->mouse_down == GS_GUI_MOUSE_LEFT && ctx->focus == id)
- {
- if (ctx->mouse_pos.x < r.x)
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_TAB_SWAP_LEFT;
- req.cnt = cnt;
- gs_dyn_array_push(ctx->requests, req);
- }
- if (ctx->mouse_pos.x > r.x + r.w)
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_TAB_SWAP_RIGHT;
- req.cnt = cnt;
- gs_dyn_array_push(ctx->requests, req);
- }
- }
- bool tab_focus = (!tab_bar || (tab_bar && tab_item && tab_bar->focus == tab_item->idx));
- gs_color_t def = ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x00].colors[GS_GUI_COLOR_BACKGROUND];
- gs_color_t hov = ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x01].colors[GS_GUI_COLOR_BACKGROUND];
- gs_color_t act = ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x02].colors[GS_GUI_COLOR_BACKGROUND];
- gs_color_t inactive = gs_color(10, 10, 10, 50);
- int16_t exp[] = {1, 1, 1, 1};
- gs_gui_push_clip_rect(ctx, gs_gui_expand_rect(cnt->rect, exp));
- gs_gui_push_clip_rect(ctx, r);
- gs_gui_draw_rect(ctx, r, id == ctx->focus ? act : hovered ? hov : tab_focus ? def : inactive);
- gs_gui_draw_control_text(ctx, label_tag, r, &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state], opt);
- gs_gui_pop_clip_rect(ctx);
- gs_gui_pop_clip_rect(ctx);
- }
- // do `close` button
- /*
- if (~opt & GS_GUI_OPT_NOCLOSE && false)
- {
- gs_gui_id id = gs_gui_get_id(ctx, "!close", 6);
- gs_gui_rect_t r = gs_gui_rect(tr.x + tr.w - tr.h, tr.y, tr.h, tr.h);
- tr.w -= r.w;
- gs_gui_draw_icon(ctx, GS_GUI_ICON_CLOSE, r, ctx->style->colors[GS_GUI_COLOR_TITLETEXT]);
- gs_gui_update_control(ctx, id, r, opt);
- if (ctx->mouse_pressed == GS_GUI_MOUSE_LEFT && id == ctx->focus)
- {
- cnt->open = 0;
- }
- }
- */
- }
- // resize to content size
- if (opt & GS_GUI_OPT_AUTOSIZE && !split)
- {
- /*
- gs_gui_rect_t r = gs_gui_get_layout(ctx)->body;
- cnt->rect.w = cnt->content_size.x + (cnt->rect.w - r.w);
- cnt->rect.h = cnt->content_size.y + (cnt->rect.h - r.h);
- */
- }
- if (split && ~opt & GS_GUI_OPT_NOCLIP && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
- {
- gs_gui_pop_clip_rect(ctx);
- }
- // Draw border
- if (~opt & GS_GUI_OPT_NOFRAME && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
- {
- const int* w = (int*)ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][0x00].border_width;
- const gs_color_t* bc = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][0x00].colors[GS_GUI_COLOR_BORDER];
- // gs_gui_draw_box(ctx, gs_gui_expand_rect(cnt->rect, w), w, *bc);
- }
- gs_gui_push_container_body(ctx, cnt, body, desc, opt);
- /* close if this is a popup window and elsewhere was clicked */
- if (opt & GS_GUI_OPT_POPUP && ctx->mouse_pressed && ctx->hover_root != cnt)
- {
- cnt->open = 0;
- }
- if (~opt & GS_GUI_OPT_NOCLIP)
- {
- if (cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
- {
- gs_gui_push_clip_rect(ctx, cnt->body);
- }
- else
- {
- gs_gui_push_clip_rect(ctx, gs_gui_rect(0, 0, 0, 0));
- }
- }
- return GS_GUI_RES_ACTIVE;
- }
- GS_API_DECL void
- gs_gui_window_end(gs_gui_context_t *ctx)
- {
- gs_gui_container_t* cnt = gs_gui_get_current_container(ctx);
- // Get root container
- gs_gui_container_t* root_cnt = gs_gui_get_root_container(ctx, cnt);
- // Get splits
- gs_gui_split_t* split = gs_gui_get_split(ctx, cnt);
- gs_gui_split_t* root_split = gs_gui_get_root_split(ctx, cnt);
- const bool new_frame = cnt->frame != ctx->frame;
- // Cache opt
- const uint64_t opt = cnt->opt;
- // Pop clip for rect
- if (~cnt->opt & GS_GUI_OPT_NOCLIP)
- {
- gs_gui_pop_clip_rect(ctx);
- }
- if (~cnt->opt & GS_GUI_OPT_NOCLIP)
- {
- gs_gui_push_clip_rect(ctx, cnt->rect);
- }
- // do `resize` handle
- if (~cnt->opt & GS_GUI_OPT_NORESIZE && ~root_cnt->opt &
- GS_GUI_OPT_NORESIZE && new_frame && ~cnt->opt & GS_GUI_OPT_DOCKSPACE)
- {
- int32_t sz = ctx->style->title_height;
- gs_gui_id id = gs_gui_get_id(ctx, "!resize", 7);
- gs_gui_rect_t r = gs_gui_rect(cnt->rect.x + cnt->rect.w - (f32)sz, cnt->rect.y + cnt->rect.h - (f32)sz, (f32)sz, (f32)sz);
- gs_gui_update_control(ctx, id, r, opt);
- if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
- {
- ctx->active_root = cnt;
- if (root_split)
- {
- gs_gui_request_t req = gs_default_val();
- req.type = GS_GUI_SPLIT_RESIZE_SE;
- req.split = root_split;
- gs_dyn_array_push(ctx->requests, req);
- }
- else
- {
- cnt->rect.w = gs_max(96, cnt->rect.w + ctx->mouse_delta.x);
- cnt->rect.h = gs_max(64, cnt->rect.h + ctx->mouse_delta.y);
- }
- }
- // Draw resize icon (this will also be a callback)
- const uint32_t grid = 5;
- const float w = r.w / (float)grid;
- const float h = r.h / (float)grid;
- const float m = 2.f;
- const float o = 5.f;
- gs_color_t col =
- ctx->focus == id ? ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x02].colors[GS_GUI_COLOR_BACKGROUND] :
- ctx->hover == id ? ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x01].colors[GS_GUI_COLOR_BACKGROUND] :
- ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x00].colors[GS_GUI_COLOR_BACKGROUND];
- gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * grid - o, r.y + h * (grid - 2) - o, w - m, h - m), col);
- gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * grid - o, r.y + h * (grid - 1) - o, w - m, h - m), col);
- gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * (grid - 1) - o, r.y + h * (grid - 1) - o, w - m, h - m), col);
- gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * grid - o, r.y + h * grid - o, w - m, h - m), col);
- gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * (grid - 1) - o, r.y + h * grid - o, w - m, h - m), col);
- gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * (grid - 2) - o, r.y + h * grid - o, w - m, h - m), col);
- }
-
- if (~cnt->opt & GS_GUI_OPT_NOCLIP)
- {
- gs_gui_pop_clip_rect(ctx);
- }
- // draw shadow
- if (~opt & GS_GUI_OPT_NOFRAME && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
- {
- gs_gui_rect_t* r = &cnt->rect;
- uint32_t ssz = (u32)(split ? GS_GUI_SPLIT_SIZE : 5);
- gs_gui_draw_rect(ctx, gs_gui_rect(r->x, r->y + r->h, r->w + 1, 1), ctx->style->colors[GS_GUI_COLOR_SHADOW]);
- gs_gui_draw_rect(ctx, gs_gui_rect(r->x, r->y + r->h, r->w + (f32)ssz, (f32)ssz), ctx->style->colors[GS_GUI_COLOR_SHADOW]);
- gs_gui_draw_rect(ctx, gs_gui_rect(r->x + r->w, r->y, 1, r->h), ctx->style->colors[GS_GUI_COLOR_SHADOW]);
- gs_gui_draw_rect(ctx, gs_gui_rect(r->x + r->w, r->y, (f32)ssz, r->h), ctx->style->colors[GS_GUI_COLOR_SHADOW]);
- }
- #define _gui_window_resize_ctrl(ID, RECT, MOUSE, SPLIT_TYPE, MOD_KEY, ...)\
- do {\
- if (ctx->key_down == (MOD_KEY))\
- {\
- gs_gui_id _ID = (ID);\
- gs_gui_rect_t _R = (RECT);\
- gs_gui_update_control(ctx, (ID), _R, opt);\
- \
- if (_ID == ctx->hover|| _ID == ctx->focus)\
- {\
- gs_gui_draw_rect(ctx, _R, GS_COLOR_WHITE);\
- }\
- \
- if (_ID == ctx->focus && ctx->mouse_down == (MOUSE))\
- {\
- gs_gui_draw_rect(ctx, _R, GS_COLOR_WHITE);\
- if (root_split)\
- {\
- gs_gui_request_t req = gs_default_val();\
- req.type = (SPLIT_TYPE);\
- req.split = root_split;\
- gs_dyn_array_push(ctx->requests, req);\
- }\
- else if (new_frame)\
- {\
- __VA_ARGS__\
- }\
- }\
- }\
- } while (0)\
- // Control frame for body resize
- if (~opt & GS_GUI_OPT_NORESIZE && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
- {
- // Cache main rect
- gs_gui_rect_t* r = root_split ? &root_split->rect : &cnt->rect;
- gs_gui_rect_t* cr = &cnt->rect;
- const float border_ratio = 0.333f;
- if (split)
- {
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!split_w", 8),
- gs_gui_rect(cr->x, cr->y + cr->h * border_ratio, cr->w * border_ratio, cr->h * (1.f - 2.f * border_ratio)),
- GS_GUI_MOUSE_RIGHT,
- GS_GUI_SPLIT_RESIZE_INVALID,
- GS_GUI_KEY_CTRL,
- {
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!split_e", 8),
- gs_gui_rect(cr->x + cr->w * (1.f - border_ratio), cr->y + cr->h * border_ratio, cr->w * border_ratio, cr->h * (1.f - 2.f * border_ratio)),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_INVALID,
- GS_GUI_KEY_CTRL,
- {
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!split_n", 8),
- gs_gui_rect(cr->x + cr->w * border_ratio, cr->y, cr->w * (1.f - 2.f * border_ratio), cr->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_INVALID,
- GS_GUI_KEY_CTRL,
- {
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!split_s", 8),
- gs_gui_rect(cr->x + cr->w * border_ratio, cr->y + cr->h * (1.f - border_ratio), cr->w * (1.f - 2.f * border_ratio), cr->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_INVALID,
- GS_GUI_KEY_CTRL,
- {
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!split_se", 9),
- gs_gui_rect(cr->x + cr->w - cr->w * border_ratio, cr->y + cr->h * (1.f - border_ratio), cr->w * border_ratio, cr->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_INVALID,
- GS_GUI_KEY_CTRL,
- {
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!split_ne", 9),
- gs_gui_rect(cr->x + cr->w - cr->w * border_ratio, cr->y, cr->w * border_ratio, cr->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_INVALID,
- GS_GUI_KEY_CTRL,
- {
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!split_nw", 9),
- gs_gui_rect(cr->x, cr->y, cr->w * border_ratio, cr->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_INVALID,
- GS_GUI_KEY_CTRL,
- {
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!split_sw", 9),
- gs_gui_rect(cr->x, cr->y + cr->h - cr->h * border_ratio, cr->w * border_ratio, cr->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_INVALID,
- GS_GUI_KEY_CTRL,
- {
- });
- }
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_w", 6),
- gs_gui_rect(r->x, r->y + r->h * border_ratio, r->w * border_ratio, r->h * (1.f - 2.f * border_ratio)),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_W,
- GS_GUI_KEY_ALT,
- {
- float w = r->w;
- float max_x = r->x + r->w;
- r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
- if (fabsf(r->w - w) > 0.f)
- {
- r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
- }
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_e", 6),
- gs_gui_rect(r->x + r->w * (1.f - border_ratio), r->y + r->h * border_ratio, r->w * border_ratio, r->h * (1.f - 2.f * border_ratio)),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_E,
- GS_GUI_KEY_ALT,
- {
- r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_n", 6),
- gs_gui_rect(r->x + r->w * border_ratio, r->y, r->w * (1.f - 2.f * border_ratio), r->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_N,
- GS_GUI_KEY_ALT,
- {
- float h = r->h;
- float max_y = h + r->y;
- r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
- if (fabsf(r->h - h) > 0.f)
- {
- r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
- }
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_s", 6),
- gs_gui_rect(r->x + r->w * border_ratio, r->y + r->h * (1.f - border_ratio), r->w * (1.f - 2.f * border_ratio), r->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_S,
- GS_GUI_KEY_ALT,
- {
- r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_se", 7),
- gs_gui_rect(r->x + r->w - r->w * border_ratio, r->y + r->h * (1.f - border_ratio), r->w * border_ratio, r->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_SE,
- GS_GUI_KEY_ALT,
- {
- r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
- r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_ne", 7),
- gs_gui_rect(r->x + r->w - r->w * border_ratio, r->y, r->w * border_ratio, r->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_NE,
- GS_GUI_KEY_ALT,
- {
- r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
- float h = r->h;
- float max_y = h + r->y;
- r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
- if (fabsf(r->h - h) > 0.f)
- {
- r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
- }
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_nw", 7),
- gs_gui_rect(r->x, r->y, r->w * border_ratio, r->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_NW,
- GS_GUI_KEY_ALT,
- {
- float h = r->h;
- float max_y = h + r->y;
- r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
- if (fabsf(r->h - h) > 0.f)
- {
- r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
- }
- float w = r->w;
- float max_x = r->x + r->w;
- r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
- if (fabsf(r->w - w) > 0.f)
- {
- r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
- }
- });
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_sw", 7),
- gs_gui_rect(r->x, r->y + r->h - r->h * border_ratio, r->w * border_ratio, r->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_RESIZE_SW,
- GS_GUI_KEY_ALT,
- {
- float h = r->h;
- float max_y = h + r->y;
- r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
- float w = r->w;
- float max_x = r->x + r->w;
- r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
- if (fabsf(r->w - w) > 0.f)
- {
- r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
- }
- });
- // move instead of resize?
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_c", 6),
- gs_gui_rect(r->x + r->w * border_ratio,
- r->y + r->h * border_ratio,
- r->w * border_ratio,
- r->h * border_ratio),
- GS_GUI_MOUSE_LEFT,
- GS_GUI_SPLIT_MOVE,
- GS_GUI_KEY_ALT,
- {
- ctx->next_focus_root = cnt;
- r->x += ctx->mouse_delta.x;
- r->y += ctx->mouse_delta.y;
- });
- static bool capture = false;
- static gs_vec2 mp = {0};
- static gs_gui_rect_t _rect = {0};
- /*
- _gui_window_resize_ctrl(
- gs_gui_get_id(ctx, "!res_c", 5),
- gs_gui_rect(r->x + r->w * border_ratio, r->y + r->h * border_ratio, r->w * border_ratio, r->h * border_ratio),
- GS_GUI_SPLIT_RESIZE_CENTER,
- {
- if (!capture)
- {
- capture = true;
- mp = ctx->mouse_pos;
- _rect = *r;
- }
- // Grow based on dist from center
- gs_vec2 c = gs_v2(r->x + r->w * 0.5f, r->y + r->h * 0.5f);
- gs_vec2 a = gs_vec2_sub(c, mp);
- gs_vec2 b = gs_vec2_sub(c, ctx->mouse_pos);
- gs_vec2 na = gs_vec2_norm(a);
- gs_vec2 nb = gs_vec2_norm(b);
- float dist = gs_vec2_len(gs_vec2_sub(b, a));
- float dot = gs_vec2_dot(na, nb);
- gs_println("len: %.2f, dot: %.2f", dist, dot);
- // Grow rect by dot product (scale dimensions)
- float sign = dot >= 0.f ? 1.f : -1.f;
- float factor = 1.f - dist / 1000.f;
- r->w = _rect.w * factor * sign;
- r->h = _rect.h * factor * sign;
- // Equidistant resize from middle (grow rect based on delta)
- float h = r->h;
- float max_y = h + r->y;
- r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
- if (fabsf(r->h - h) > 0.f)
- {
- r->y = gs_min(r->y - ctx->mouse_delta.y, max_y);
- }
- float w = r->w;
- float max_x = r->x + r->w;
- r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
- if (fabsf(r->w - w) > 0.f)
- {
- r->x = gs_min(r->x - ctx->mouse_delta.x, max_x);
- }
- });
- */
- if (ctx->mouse_down != GS_GUI_MOUSE_LEFT)
- {
- capture = false;
- mp = gs_v2s(0.f);
- }
- }
- // Determine if focus root in same tab group as current window for docking
- bool can_dock = true;
- if (cnt->tab_bar)
- {
- gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
- for (uint32_t t = 0; t < tab_bar->size; ++t)
- {
- if (tab_bar->items[t].data == ctx->focus_root)
- {
- can_dock = false;
- }
- }
- }
- // Do docking overlay (if enabled)
- if (
- can_dock &&
- ~cnt->opt & GS_GUI_OPT_NODOCK &&
- ctx->focus_root &&
- ctx->focus_root != cnt &&
- gs_gui_rect_overlaps_vec2(cnt->rect, ctx->mouse_pos) && // This is the incorrect part -
- // need to check if this container isn't being overlapped by another
- ctx->mouse_down == GS_GUI_MOUSE_LEFT &&
- ~cnt->opt & GS_GUI_OPT_NOHOVER &&
- cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
- {
- gs_gui_split_t* focus_split = gs_gui_get_root_split(ctx, ctx->focus_root);
- gs_gui_split_t* cnt_split = gs_gui_get_root_split(ctx, cnt);
- // NOTE(john): this is incorrect...
- if ((!focus_split && !cnt_split) || ((focus_split || cnt_split) && (focus_split != cnt_split)))
- {
- // Set dockable root container
- ctx->dockable_root = ctx->dockable_root && cnt->zindex > ctx->dockable_root->zindex ?
- cnt : ctx->dockable_root? ctx->dockable_root : cnt;
- }
- }
- // Set current frame
- cnt->frame = ctx->frame;
- // Pop root container
- gs_gui_root_container_end(ctx);
- }
- GS_API_DECL void
- gs_gui_popup_open(gs_gui_context_t* ctx, const char* name)
- {
- char id_tag[256] = gs_default_val();
- gs_gui_parse_id_tag(ctx, name, id_tag, sizeof(id_tag), 0);
- gs_gui_container_t *cnt = gs_gui_get_container(ctx, *id_tag ? id_tag : name);
- // Set as hover root so popup isn't closed in window_begin_ex()
- ctx->hover_root = ctx->next_hover_root = cnt;
- // position at mouse cursor, open and bring-to-front
- cnt->rect = gs_gui_rect(ctx->mouse_pos.x, ctx->mouse_pos.y, 100, 100);
- cnt->open = 1;
- gs_gui_bring_to_front(ctx, cnt);
- }
- GS_API_DECL int32_t
- gs_gui_popup_begin_ex(gs_gui_context_t* ctx, const char* name, gs_gui_rect_t r,
- const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- opt |= (GS_GUI_OPT_POPUP | GS_GUI_OPT_NODOCK | GS_GUI_OPT_CLOSED);
- return gs_gui_window_begin_ex(ctx, name, r, NULL, NULL, opt);
- }
- GS_API_DECL void
- gs_gui_popup_end(gs_gui_context_t *ctx)
- {
- gs_gui_window_end(ctx);
- }
- GS_API_DECL void
- gs_gui_panel_begin_ex(gs_gui_context_t* ctx, const char* name, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- gs_gui_container_t *cnt;
- const int32_t elementid = GS_GUI_ELEMENT_PANEL;
- char id_tag[256] = gs_default_val();
- gs_gui_parse_id_tag(ctx, name, id_tag, sizeof(id_tag), opt);
- // if (id_tag) gs_gui_push_id(ctx, id_tag, strlen(id_tag));
- // else gs_gui_push_id(ctx, name, strlen(name));
- gs_gui_push_id(ctx, name, strlen(name));
- cnt = gs_gui_get_container_ex(ctx, ctx->last_id, opt);
- cnt->rect = gs_gui_layout_next(ctx);
- const gs_gui_id id = gs_gui_get_id(ctx, name, strlen(name));
- gs_gui_style_t style = gs_default_val();
- gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
- // Update anim (keep states locally within animation, only way to do this)
- if (anim)
- {
- gs_gui_animation_update(ctx, anim);
- // Get blended style based on animation
- style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
- }
- else
- {
- style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
- ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
- gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
- }
-
- if (~opt & GS_GUI_OPT_NOFRAME) {
- gs_gui_draw_frame(ctx, cnt->rect, &style);
- }
- // Need a way to push/pop syles temp styles
- gs_gui_stack_push(ctx->container_stack, cnt);
- gs_gui_push_container_body(ctx, cnt, cnt->rect, desc, opt);
- gs_gui_push_clip_rect(ctx, cnt->body);
- }
- GS_API_DECL void
- gs_gui_panel_end(gs_gui_context_t *ctx)
- {
- gs_gui_pop_clip_rect(ctx);
- gs_gui_pop_container(ctx);
- }
- static uint8_t
- uint8_slider(gs_gui_context_t *ctx, unsigned char *value, int low, int high, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- static float tmp;
- gs_gui_push_id(ctx, &value, sizeof(value));
- tmp = (float)*value;
- int res = gs_gui_slider_ex(ctx, &tmp, (gs_gui_real)low, (gs_gui_real)high, 0, "%.0f", desc, opt);
- *value = (u8)tmp;
- gs_gui_pop_id(ctx);
- return res;
- }
- static int32_t
- int32_slider(gs_gui_context_t *ctx, int32_t* value, int32_t low, int32_t high, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- static float tmp;
- gs_gui_push_id(ctx, &value, sizeof(value));
- tmp = (float)*value;
- int res = gs_gui_slider_ex(ctx, &tmp, (gs_gui_real)low, (gs_gui_real)high, 0, "%.0f", desc, opt);
- *value = (int32_t)tmp;
- gs_gui_pop_id(ctx);
- return res;
- }
- static int16_t
- int16_slider(gs_gui_context_t *ctx, int16_t* value, int32_t low, int32_t high, const gs_gui_selector_desc_t* desc, uint64_t opt)
- {
- static float tmp;
- gs_gui_push_id(ctx, &value, sizeof(value));
- tmp = (float)*value;
- int res = gs_gui_slider_ex(ctx, &tmp, (gs_gui_real)low, (gs_gui_real)high, 0, "%.0f", desc, opt);
- *value = (int16_t)tmp;
- gs_gui_pop_id(ctx);
- return res;
- }
- //=== Gizmo ===//
- typedef struct
- {
- b32 hit;
- gs_vec3 point;
- } gs_gui_gizmo_line_intersection_result_t;
- enum {
- GS_GUI_AXIS_RIGHT = 0x01,
- GS_GUI_AXIS_UP,
- GS_GUI_AXIS_FORWARD
- };
- typedef struct {
- struct {
- gs_vqs model;
- union {
- gs_cylinder_t cylinder;
- gs_plane_t plane;
- } shape;
- } axis;
- struct {
- gs_vqs model;
- union {
- gs_cone_t cone;
- gs_aabb_t aabb;
- } shape;
- } cap;
- } gs_gui_axis_t;
- typedef struct {
- gs_gui_axis_t right;
- gs_gui_axis_t up;
- gs_gui_axis_t forward;
- } gs_gizmo_translate_t;
- typedef struct {
- gs_gui_axis_t right;
- gs_gui_axis_t up;
- gs_gui_axis_t forward;
- } gs_gizmo_scale_t;
- typedef struct {
- gs_gui_axis_t right;
- gs_gui_axis_t up;
- gs_gui_axis_t forward;
- } gs_gizmo_rotate_t;
- static gs_gizmo_scale_t
- gs_gizmo_scale(const gs_vqs* parent)
- {
- gs_gizmo_scale_t gizmo = gs_default_val();
- const gs_vec3 ax_scl = gs_v3(0.03f, 1.f, 0.03f);
- const gs_vec3 cap_scl = gs_vec3_scale(gs_v3s(0.05f), 2.f * gs_vec3_len(parent->scale));
- gs_vqs local = gs_vqs_default();
- gs_vqs abs = gs_vqs_default();
- #define GS_GUI_GIZMO_AXIS_DEFINE_SCALE(MEMBER, OFFSET, DEG, AXIS)\
- do {\
- /* Axis */\
- {\
- local = gs_vqs_ctor(\
- OFFSET,\
- gs_quat_angle_axis(gs_deg2rad(DEG), AXIS),\
- ax_scl\
- );\
- gizmo.MEMBER.axis.model = gs_vqs_absolute_transform(&local, parent);\
- gs_cylinder_t axis = gs_default_val();\
- axis.r = 1.f;\
- axis.base = gs_v3(0.f, 0.0f, 0.f);\
- axis.height = 1.f;\
- gizmo.MEMBER.axis.shape.cylinder = axis;\
- }\
- \
- /* Cap */\
- {\
- local = gs_vqs_ctor(\
- gs_v3(0.f, 0.5f, 0.f),\
- gs_quat_default(),\
- gs_v3s(1.f)\
- );\
- \
- gizmo.MEMBER.cap.model = gs_vqs_absolute_transform(&local, &gizmo.MEMBER.axis.model);\
- gizmo.MEMBER.cap.model.scale = cap_scl;\
- gs_aabb_t aabb = gs_default_val();\
- aabb.min = gs_v3s(-0.5f);\
- aabb.max = gs_v3s(0.5f);\
- gizmo.MEMBER.cap.shape.aabb = aabb;\
- }\
- } while (0)
- const float off = 0.6f;
- GS_GUI_GIZMO_AXIS_DEFINE_SCALE(right, gs_v3(-off, 0.f, 0.f), 90.f, GS_ZAXIS);
- GS_GUI_GIZMO_AXIS_DEFINE_SCALE(up, gs_v3(0.f, off, 0.f), 0.f, GS_YAXIS);
- GS_GUI_GIZMO_AXIS_DEFINE_SCALE(forward, gs_v3(0.f, 0.f, off), 90.f, GS_XAXIS);
- return gizmo;
- }
- static gs_gizmo_translate_t
- gs_gizmo_translate(const gs_vqs* parent)
- {
- gs_gizmo_translate_t trans = gs_default_val();
- const gs_vec3 ax_scl = gs_v3(0.03f, 1.f, 0.03f);
- const gs_vec3 cap_scl = gs_vec3_scale(gs_v3(0.02f, 0.05f, 0.02f), 2.f * gs_vec3_len(parent->scale));
- gs_vqs local = gs_vqs_default();
- gs_vqs abs = gs_vqs_default();
- #define GS_GUI_GIZMO_AXIS_DEFINE_TRANSLATE(MEMBER, OFFSET, DEG, AXIS)\
- do {\
- /* Axis */\
- {\
- local = gs_vqs_ctor(\
- OFFSET,\
- gs_quat_angle_axis(gs_deg2rad(DEG), AXIS),\
- ax_scl\
- );\
- trans.MEMBER.axis.model = gs_vqs_absolute_transform(&local, parent);\
- gs_cylinder_t axis = gs_default_val();\
- axis.r = 1.f;\
- axis.base = gs_v3(0.f, 0.0f, 0.f);\
- axis.height = 1.f;\
- trans.MEMBER.axis.shape.cylinder = axis;\
- }\
- \
- /* Cap */\
- {\
- local = gs_vqs_ctor(\
- gs_v3(0.f, 0.5f, 0.f),\
- gs_quat_default(),\
- gs_v3s(1.f)\
- );\
- \
- trans.MEMBER.cap.model = gs_vqs_absolute_transform(&local, &trans.MEMBER.axis.model);\
- trans.MEMBER.cap.model.scale = cap_scl;\
- gs_cone_t cap = gs_default_val();\
- cap.r = 1.f;\
- cap.base = gs_v3(0.f, 0.0f, 0.f);\
- cap.height = 1.0f;\
- trans.MEMBER.cap.shape.cone = cap;\
- }\
- } while (0)
- const float off = 0.6f;
- GS_GUI_GIZMO_AXIS_DEFINE_TRANSLATE(right, gs_v3(-off, 0.f, 0.f), 90.f, GS_ZAXIS);
- GS_GUI_GIZMO_AXIS_DEFINE_TRANSLATE(up, gs_v3(0.f, off, 0.f), 0.f, GS_YAXIS);
- GS_GUI_GIZMO_AXIS_DEFINE_TRANSLATE(forward, gs_v3(0.f, 0.f, off), 90.f, GS_XAXIS);
- return trans;
- }
- static gs_gizmo_rotate_t
- gs_gizmo_rotate(const gs_vqs* parent)
- {
- gs_gizmo_rotate_t gizmo = gs_default_val();
- const gs_vec3 ax_scl = gs_v3(1.f, 1.f, 1.f);
- gs_vqs local = gs_vqs_default();
- gs_vqs abs = gs_vqs_default();
- #define GS_GUI_GIZMO_AXIS_DEFINE_ROTATE(MEMBER, OFFSET, DEG, AXIS)\
- do {\
- /* Axis */\
- {\
- local = gs_vqs_ctor(\
- OFFSET,\
- gs_quat_angle_axis(gs_deg2rad(DEG), AXIS),\
- ax_scl\
- );\
- gizmo.MEMBER.axis.model = gs_vqs_absolute_transform(&local, parent);\
- gs_plane_t axis = gs_plane_from_pt_normal(gs_v3s(0.f), GS_ZAXIS);\
- gizmo.MEMBER.axis.shape.plane = axis;\
- }\
- } while (0)
- GS_GUI_GIZMO_AXIS_DEFINE_ROTATE(right, gs_v3(0.f, 0.f, 0.f), 90.f, GS_YAXIS);
- GS_GUI_GIZMO_AXIS_DEFINE_ROTATE(up, gs_v3(0.f, 0.f, 0.f), -90.f, GS_XAXIS);
- GS_GUI_GIZMO_AXIS_DEFINE_ROTATE(forward, gs_v3(0.f, 0.f, 0.f), 0.f, GS_ZAXIS);
- return gizmo;
- }
- typedef struct {
- int32_t op;
- int32_t mode;
- gs_vqs xform;
- gs_contact_info_t info;
- union {
- gs_gizmo_translate_t translate;
- gs_gizmo_scale_t scale;
- gs_gizmo_rotate_t rotate;
- } gizmo;
- int16_t hover;
- gs_camera_t camera;
- gs_gui_rect_t viewport;
- gs_gui_gizmo_line_intersection_result_t li;
- } gs_gizmo_desc_t;
- static void
- gs_gui_gizmo_render(gs_gui_context_t* ctx, gs_gui_customcommand_t* cmd)
- {
- const gs_vec2 fbs = ctx->framebuffer_size;
- const float t = gs_platform_elapsed_time();
- gs_immediate_draw_t* gsi = &ctx->gsi;
- gs_gizmo_desc_t* desc = (gs_gizmo_desc_t*)cmd->data;
- gs_gui_rect_t clip = cmd->clip;
- gs_gui_rect_t viewport = desc->viewport;
- gs_camera_t cam = desc->camera;
- const uint16_t segments = 4;
- const gs_gui_gizmo_line_intersection_result_t* li = &desc->li;
- const gs_contact_info_t* info = &desc->info;
- const uint8_t alpha = 150;
- gsi_defaults(gsi);
- gsi_depth_enabled(gsi, false);
- gsi_camera(gsi, &cam, (uint32_t)viewport.w, (uint32_t)viewport.h);
- gs_graphics_set_viewport(&gsi->commands, (u32)viewport.x, (u32)(fbs.y - viewport.h - viewport.y),
- (u32)viewport.w, (u32)viewport.h);
- gs_graphics_primitive_type primitive = GS_GRAPHICS_PRIMITIVE_TRIANGLES;
- #define GS_GUI_GIZMO_AXIS_TRANSLATE(ID, AXIS, COLOR)\
- do {\
- gs_gui_id id = gs_gui_get_id_hash(ctx, ID, strlen(ID), cmd->hash);\
- bool hover = cmd->hover == id;\
- bool focus = cmd->focus == id;\
- gs_color_t color = hover || focus ? GS_COLOR_YELLOW : COLOR;\
- /* Axis */\
- {\
- gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
- gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.translate.AXIS.axis.model));\
- {\
- gs_cylinder_t* axis = &desc->gizmo.translate.AXIS.axis.shape.cylinder;\
- gsi_cylinder(gsi, axis->base.x, axis->base.y, axis->base.z, axis->r,\
- axis->r, axis->height, segments, color.r, color.g, color.b, alpha, primitive);\
- }\
- gsi_pop_matrix(gsi);\
- }\
- \
- /* Cap */\
- {\
- gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
- gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.translate.AXIS.cap.model));\
- {\
- gs_cone_t* cap = &desc->gizmo.translate.AXIS.cap.shape.cone;\
- gsi_cone(gsi, cap->base.x, cap->base.y, cap->base.z, cap->r, cap->height, segments, color.r, color.g, color.b, alpha, primitive);\
- }\
- gsi_pop_matrix(gsi);\
- }\
- } while (0)
- #define GS_GUI_GIZMO_AXIS_SCALE(ID, AXIS, COLOR)\
- do {\
- gs_gui_id id = gs_gui_get_id_hash(ctx, ID, strlen(ID), cmd->hash);\
- bool hover = cmd->hover == id;\
- bool focus = cmd->focus == id;\
- gs_color_t color = hover || focus ? GS_COLOR_YELLOW : COLOR;\
- /* Axis */\
- {\
- gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
- gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.scale.AXIS.axis.model));\
- {\
- gs_cylinder_t* axis = &desc->gizmo.scale.AXIS.axis.shape.cylinder;\
- gsi_cylinder(gsi, axis->base.x, axis->base.y, axis->base.z, axis->r,\
- axis->r, axis->height, segments, color.r, color.g, color.b, alpha, primitive);\
- }\
- gsi_pop_matrix(gsi);\
- }\
- \
- /* Cap */\
- {\
- gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
- gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.scale.AXIS.cap.model));\
- {\
- gs_aabb_t* cap = &desc->gizmo.scale.AXIS.cap.shape.aabb;\
- gs_vec3 hd = gs_vec3_scale(gs_vec3_sub(cap->max, cap->min), 0.5f);\
- gs_vec3 c = gs_vec3_add(cap->min, hd);\
- gsi_box(gsi, c.x, c.y, c.z, hd.x, hd.y, hd.z, color.r, color.g, color.b, alpha, primitive);\
- }\
- gsi_pop_matrix(gsi);\
- }\
- } while (0)
- #define GS_GUI_GIZMO_AXIS_ROTATE(ID, AXIS, COLOR)\
- do {\
- gs_color_t def_color = (COLOR);\
- gs_gui_id id = gs_gui_get_id_hash(ctx, ID, strlen(ID), cmd->hash);\
- bool hover = cmd->hover == id;\
- bool focus = cmd->focus == id;\
- gs_color_t color = hover || focus ? GS_COLOR_YELLOW : def_color;\
- /* Axis */\
- {\
- gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
- gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.rotate.AXIS.axis.model));\
- {\
- gs_plane_t* axis = &desc->gizmo.rotate.AXIS.axis.shape.plane;\
- gsi_arc(gsi, 0.f, 0.f, 0.92f, 1.f, 0.f, 360.f, 48, color.r, color.g, color.b, color.a, GS_GRAPHICS_PRIMITIVE_TRIANGLES);\
- }\
- gsi_pop_matrix(gsi);\
- if (focus) {\
- gs_vec3 ls = desc->xform.translation;\
- gs_vec3 le = gs_vec3_add(ls, gs_vec3_scale(info->normal, 0.5f));\
- gsi_line3Dv(gsi, ls, le, GS_COLOR_BLUE);\
- }\
- }\
- } while (0)
- switch (desc->op)
- {
- case GS_GUI_GIZMO_TRANSLATE:
- {
- GS_GUI_GIZMO_AXIS_TRANSLATE("#gizmo_trans_right", right, GS_COLOR_RED);
- GS_GUI_GIZMO_AXIS_TRANSLATE("#gizmo_trans_up", up, GS_COLOR_GREEN);
- GS_GUI_GIZMO_AXIS_TRANSLATE("#gizmo_trans_forward", forward, GS_COLOR_BLUE);
- } break;
- case GS_GUI_GIZMO_SCALE:
- {
- GS_GUI_GIZMO_AXIS_SCALE("#gizmo_scale_right", right, GS_COLOR_RED);
- GS_GUI_GIZMO_AXIS_SCALE("#gizmo_scale_up", up, GS_COLOR_GREEN);
- GS_GUI_GIZMO_AXIS_SCALE("#gizmo_scale_forward", forward, GS_COLOR_BLUE);
- } break;
- case GS_GUI_GIZMO_ROTATE:
- {
- gs_gui_id id_r = gs_gui_get_id_hash(ctx, "#gizmo_rotate_right", strlen("#gizmo_rotate_right"), cmd->hash);
- gs_gui_id id_u = gs_gui_get_id_hash(ctx, "#gizmo_rotate_up", strlen("#gizmo_rotate_up"), cmd->hash);
- gs_gui_id id_f = gs_gui_get_id_hash(ctx, "#gizmo_rotate_forward", strlen("#gizmo_rotate_forward"), cmd->hash);
- GS_GUI_GIZMO_AXIS_ROTATE("#gizmo_rotate_right", right,
- (cmd->focus == id_u || cmd->focus == id_f) ? gs_color_alpha(GS_COLOR_RED, 25) :
- gs_color_alpha(GS_COLOR_RED, alpha));
- GS_GUI_GIZMO_AXIS_ROTATE("#gizmo_rotate_up", up,
- (cmd->focus == id_r || cmd->focus == id_f) ? gs_color_alpha(GS_COLOR_GREEN, 25) :
- gs_color_alpha(GS_COLOR_GREEN, alpha));
- GS_GUI_GIZMO_AXIS_ROTATE("#gizmo_rotate_forward", forward,
- (cmd->focus == id_r || cmd->focus == id_u) ? gs_color_alpha(GS_COLOR_BLUE, 25) :
- gs_color_alpha(GS_COLOR_BLUE, alpha));
- } break;
- }
- if (li->hit)
- {
- gsi_sphere(gsi, li->point.x, li->point.y, li->point.z, 0.005f, 255, 0, 0, 255, GS_GRAPHICS_PRIMITIVE_LINES);
- gsi_line3Dv(gsi, li->point, desc->xform.translation, gs_color(255, 0, 0, 255));
- }
- }
- static gs_gui_gizmo_line_intersection_result_t
- gs_gui_gizmo_get_line_intersection(const gs_vqs* model, gs_vec3 axis_a, gs_vec3 axis_b, gs_vec3 axis_c,
- const gs_camera_t* camera, const gs_ray_t* ray, gs_vec3 plane_normal_axis, bool compare_supporting_axes, bool override_axis)
- {
- gs_gui_gizmo_line_intersection_result_t res = gs_default_val();
- // Find absolute dot between cam forward and right axis
- gs_vec3 cf = gs_vec3_norm(gs_camera_forward(camera));
- gs_vec3 ta = gs_vec3_norm(gs_quat_rotate(model->rotation, axis_a));
- float cfdta = fabsf(gs_vec3_dot(cf, ta));
- // This doesn't really make sense. I want to project along the x/y or x/z planes.
- gs_plane_t intersection_plane = gs_default_val();
- gs_vec3 op = model->translation;
- if (compare_supporting_axes)
- {
- // Now determine appropriate axis to move along
- gs_vec3 tb = gs_vec3_norm(gs_quat_rotate(model->rotation, axis_b));
- gs_vec3 tc = gs_vec3_norm(gs_quat_rotate(model->rotation, axis_c));
- float cfdtb = fabsf(gs_vec3_dot(cf, tb));
- float cfdtc = fabsf(gs_vec3_dot(cf, tc));
- intersection_plane = cfdtb < cfdtc ? gs_plane_from_pt_normal(op, tc) :
- gs_plane_from_pt_normal(op, tb);
- }
- else
- {
- if (override_axis)
- {
- intersection_plane = gs_plane_from_pt_normal(op, plane_normal_axis);
- }
- else
- {
- intersection_plane = gs_plane_from_pt_normal(op, ta);
- }
- }
- // Get line intersection from ray and chosen intersection plane
- gs_plane_t* ip = &intersection_plane;
- float denom = gs_vec3_dot(gs_v3(ip->a, ip->b, ip->c), ray->d);
- if (fabsf(denom) >= GS_EPSILON)
- {
- float t = -(ip->a * ray->p.x + ip->b * ray->p.y + ip->c * ray->p.z + ip->d) / denom;
- res.hit = t >= 0.f ? true : false;
- res.point = gs_vec3_add(ray->p, gs_vec3_scale(ray->d, t));
- }
- return res;
- }
- static gs_vec3 s_intersection_start = gs_default_val();
- static gs_vqs s_delta = gs_default_val();
- static bool just_set_focus = false;
- GS_API_DECL int32_t
- gs_gui_gizmo(gs_gui_context_t* ctx, gs_camera_t* camera, gs_vqs* model, gs_gui_rect_t viewport,
- bool invert_view_y, float snap, int32_t op, int32_t mode, uint64_t opt)
- {
- int32_t res = 0;
- if (model->rotation.w == 0.f) model->rotation = gs_quat_default();
- if (gs_vec3_len(model->scale) == 0.f) model->scale = gs_v3s(1.f);
- const gs_vec2 fbs = ctx->framebuffer_size;
- const float t = gs_platform_elapsed_time();
- const bool in_hover_root = gs_gui_in_hover_root(ctx);
- gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
- // This doesn't actually work for the clip...
- gs_gui_rect_t clip = viewport;
- if (invert_view_y) {
- clip.y = fbs.y - clip.h - clip.y;
- }
- // Capture event information (might have to do something like this for smaller framebuffers scaled to higher viewports)
- /*
- gs_vec2 mc = gs_platform_mouse_positionv();
- // Check for scale and bias
- int32_t flags = 0x00;
- if (flags & GS_GUI_HINT_FLAG_NO_SCALE_BIAS_MOUSE) {
- float px = (mc.x - clip.x) / clip.w;
- float py = (mc.y - clip.y) / clip.h;
- float xv = clip.w * px;
- float yv = clip.h * py;
- mc = gs_v2(xv, yv);
- }
- else {
- gs_vec2 fb_vp_ratio = gs_v2(fbs.x / clip.w, fbs.y / clip.h);
- float px = mc.x - (clip.x * fb_vp_ratio.x);
- float py = mc.y + (clip.y * fb_vp_ratio.y);
- float xv = px / fb_vp_ratio.x;
- float yv = py / fb_vp_ratio.y;
- mc = gs_v2(xv, yv);
- }
- */
- gs_vec2 mc = gs_platform_mouse_positionv();
- mc = gs_vec2_sub(mc, gs_v2(clip.x, clip.y));
- // Project ray to world
- const float ray_len = 1000.f;
- gs_vec3 ms = gs_v3(mc.x, mc.y, 0.f);
- gs_vec3 me = gs_v3(mc.x, mc.y, -ray_len);
- gs_vec3 ro = gs_camera_screen_to_world(camera, ms, 0, 0, (int32_t)clip.w, (int32_t)clip.h);
- gs_vec3 rd = gs_camera_screen_to_world(camera, me, 0, 0, (int32_t)clip.w, (int32_t)clip.h);
- rd = gs_vec3_norm(gs_vec3_sub(ro, rd));
- gs_ray_t ray = gs_default_val();
- ray.p = ro;
- ray.d = rd;
- ray.len = ray_len;
- // Check for nan
- if (gs_vec3_nan(ray.p)) ray.p = gs_v3s(0.f);
- if (gs_vec3_nan(ray.d)) ray.d = gs_v3s(0.f);
- gs_gizmo_desc_t desc = gs_default_val();
- desc.op = op;
- desc.mode = mode;
- desc.camera = *camera;
- desc.info.depth = FLT_MAX;
- desc.viewport = clip;
- desc.xform = gs_vqs_default();
- desc.xform.translation = model->translation;
- desc.xform.rotation = (mode == GS_GUI_TRANSFORM_LOCAL || op == GS_GUI_GIZMO_SCALE) ? model->rotation : gs_quat_default(); // This depends on the mode (local/world)
- switch (camera->proj_type)
- {
- case GS_PROJECTION_TYPE_ORTHOGRAPHIC:
- {
- desc.xform.scale = gs_v3s(camera->ortho_scale * 0.2f);
- } break;
- case GS_PROJECTION_TYPE_PERSPECTIVE:
- {
- float dist_from_cam = gs_vec3_dist(desc.xform.translation, camera->transform.translation);
- desc.xform.scale = gs_v3s(dist_from_cam * 0.3f);
- } break;
- }
- #define UPDATE_GIZMO_CONTROL(ID, RAY, SHAPE, MODEL, FUNC, CAP_SHAPE, CAP_MODEL, CAP_FUNC, INFO)\
- do {\
- int32_t mouseover = 0;\
- gs_gui_id id = (ID);\
- gs_contact_info_t info0 = gs_default_val();\
- gs_contact_info_t info1 = gs_default_val();\
- if (in_hover_root) {\
- FUNC(&(SHAPE), &(MODEL), &(RAY), NULL, &info0);\
- info0.depth = gs_vec3_dist(info0.point, (RAY).p);\
- CAP_FUNC(&(CAP_SHAPE), &(CAP_MODEL), &(RAY), NULL, &info1);\
- info1.depth = gs_vec3_dist(info1.point, (RAY).p);\
- }\
- gs_contact_info_t* info = info0.depth < info1.depth ? &info0 : &info1;\
- mouseover = info->hit && info->depth <= INFO.depth && in_hover_root && !ctx->hover_split && !ctx->lock_hover_id;\
- if (ctx->focus == id) {ctx->updated_focus = 1;}\
- if (~opt & GS_GUI_OPT_NOINTERACT) {\
- /* Check for hold focus here */\
- if (mouseover && !ctx->mouse_down) {\
- gs_gui_set_hover(ctx, id);\
- INFO = *info;\
- }\
- \
- if (ctx->focus == id)\
- {\
- res |= GS_GUI_RES_ACTIVE;\
- just_set_focus = false;\
- gs_gui_set_focus(ctx, id);\
- if (ctx->mouse_pressed && !mouseover) {gs_gui_set_focus(ctx, 0);}\
- if (!ctx->mouse_down && ~opt & GS_GUI_OPT_HOLDFOCUS) {gs_gui_set_focus(ctx, 0);}\
- }\
- \
- if (ctx->prev_hover == id && !mouseover) {ctx->prev_hover = ctx->hover;}\
- \
- if (ctx->hover == id)\
- {\
- if (ctx->mouse_pressed)\
- {\
- if ((opt & GS_GUI_OPT_LEFTCLICKONLY && ctx->mouse_pressed == GS_GUI_MOUSE_LEFT) || (~opt & GS_GUI_OPT_LEFTCLICKONLY))\
- {\
- gs_gui_set_focus(ctx, id);\
- just_set_focus = true;\
- }\
- }\
- else if (!mouseover)\
- {\
- gs_gui_set_hover(ctx, 0);\
- }\
- }\
- }\
- } while (0)
- switch (op)
- {
- case GS_GUI_GIZMO_TRANSLATE:
- {
- // Construct translate gizmo for this frame based on given parent transform
- desc.gizmo.translate = gs_gizmo_translate(&desc.xform);
- gs_gui_id id_r = gs_gui_get_id(ctx, "#gizmo_trans_right", strlen("#gizmo_trans_right"));
- gs_gui_id id_u = gs_gui_get_id(ctx, "#gizmo_trans_up", strlen("#gizmo_trans_up"));
- gs_gui_id id_f = gs_gui_get_id(ctx, "#gizmo_trans_forward", strlen("#gizmo_trans_forward"));
- // Right
- UPDATE_GIZMO_CONTROL(id_r, ray,
- desc.gizmo.translate.right.axis.shape.cylinder, desc.gizmo.translate.right.axis.model, gs_cylinder_vs_ray,
- desc.gizmo.translate.right.cap.shape.cone, desc.gizmo.translate.right.cap.model, gs_cone_vs_ray,
- desc.info);
- // Up
- UPDATE_GIZMO_CONTROL(id_u, ray,
- desc.gizmo.translate.up.axis.shape.cylinder, desc.gizmo.translate.up.axis.model, gs_cylinder_vs_ray,
- desc.gizmo.translate.up.cap.shape.cone, desc.gizmo.translate.up.cap.model, gs_cone_vs_ray,
- desc.info);
- // Forward
- UPDATE_GIZMO_CONTROL(id_f, ray,
- desc.gizmo.translate.forward.axis.shape.cylinder, desc.gizmo.translate.forward.axis.model, gs_cylinder_vs_ray,
- desc.gizmo.translate.forward.cap.shape.cone, desc.gizmo.translate.forward.cap.model, gs_cone_vs_ray,
- desc.info);
-
- // Control
- if (ctx->focus == id_r)
- {
- desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_XAXIS, GS_YAXIS, GS_ZAXIS,
- camera, &ray, gs_v3s(0.f), true, false);
- if (just_set_focus) {
- s_intersection_start = desc.li.point;
- memset(&s_delta, 0, sizeof(s_delta));
- s_delta.rotation = gs_quat(
- model->translation.x,
- model->translation.y,
- model->translation.z,
- 0.f
- );
- }
- if (desc.li.hit)
- {
- gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_XAXIS));
- gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
- float udotn = gs_vec3_dot(u, axis);
- s_delta.translation = gs_vec3_scale(axis, udotn);
- s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
- if (gs_vec3_eq(axis, GS_XAXIS)) {
- s_delta.translation.y = 0.f;
- s_delta.translation.z = 0.f;
- }
- if (snap > 0.f)
- {
- s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
- float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
- gs_vec3 norm = gs_vec3_norm(s_delta.scale);
- gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
- gs_vec3 op = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
- model->translation = gs_vec3_add(op, delta);
- }
- else
- {
- // Set final translation
- model->translation = gs_vec3_add(desc.xform.translation, s_delta.translation);
- }
- }
- }
- else if (ctx->focus == id_u)
- {
- desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_YAXIS, GS_XAXIS, GS_ZAXIS,
- camera, &ray, gs_v3s(0.f), true, false);
- if (just_set_focus) {
- s_intersection_start = desc.li.point;
- memset(&s_delta, 0, sizeof(s_delta));
- s_delta.rotation = gs_quat(
- model->translation.x,
- model->translation.y,
- model->translation.z,
- 0.f
- );
- }
- if (desc.li.hit)
- {
- gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_YAXIS));
- gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
- float udotn = gs_vec3_dot(u, axis);
- s_delta.translation = gs_vec3_scale(axis, udotn);
- s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
- if (gs_vec3_eq(axis, GS_YAXIS)) {
- s_delta.translation.x = 0.f;
- s_delta.translation.z = 0.f;
- }
- if (snap > 0.f)
- {
- s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
- float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
- gs_vec3 norm = gs_vec3_norm(s_delta.scale);
- gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
- gs_vec3 op = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
- model->translation = gs_vec3_add(op, delta);
- }
- else
- {
- // Set final translation
- model->translation = gs_vec3_add(desc.xform.translation, s_delta.translation);
- }
- }
- }
- else if (ctx->focus == id_f)
- {
- desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_ZAXIS, GS_XAXIS, GS_YAXIS,
- camera, &ray, gs_v3s(0.f), true, false);
- if (just_set_focus) {
- s_intersection_start = desc.li.point;
- memset(&s_delta, 0, sizeof(s_delta));
- s_delta.rotation = gs_quat(
- model->translation.x,
- model->translation.y,
- model->translation.z,
- 0.f
- );
- }
- if (desc.li.hit)
- {
- gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_ZAXIS));
- gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
- float udotn = gs_vec3_dot(u, axis);
- s_delta.translation = gs_vec3_scale(axis, udotn);
- s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
- if (gs_vec3_eq(axis, GS_ZAXIS)) {
- s_delta.translation.x = 0.f;
- s_delta.translation.y = 0.f;
- }
- if (snap > 0.f)
- {
- s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
- float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
- gs_vec3 norm = gs_vec3_norm(s_delta.scale);
- gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
- gs_vec3 op = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
- model->translation = gs_vec3_add(op, delta);
- }
- else
- {
- // Set final translation
- model->translation = gs_vec3_add(desc.xform.translation, s_delta.translation);
- }
- }
- }
- } break;
- case GS_GUI_GIZMO_SCALE:
- {
- // Construct translate gizmo for this frame based on given parent transform
- desc.gizmo.scale = gs_gizmo_scale(&desc.xform);
- gs_gui_id id_r = gs_gui_get_id(ctx, "#gizmo_scale_right", strlen("#gizmo_scale_right"));
- gs_gui_id id_u = gs_gui_get_id(ctx, "#gizmo_scale_up", strlen("#gizmo_scale_up"));
- gs_gui_id id_f = gs_gui_get_id(ctx, "#gizmo_scale_forward", strlen("#gizmo_scale_forward"));
- // Right
- UPDATE_GIZMO_CONTROL(id_r, ray,
- desc.gizmo.scale.right.axis.shape.cylinder, desc.gizmo.scale.right.axis.model, gs_cylinder_vs_ray,
- desc.gizmo.scale.right.cap.shape.aabb, desc.gizmo.scale.right.cap.model, gs_aabb_vs_ray,
- desc.info);
- // Up
- UPDATE_GIZMO_CONTROL(id_u, ray,
- desc.gizmo.scale.up.axis.shape.cylinder, desc.gizmo.scale.up.axis.model, gs_cylinder_vs_ray,
- desc.gizmo.scale.up.cap.shape.aabb, desc.gizmo.scale.up.cap.model, gs_aabb_vs_ray,
- desc.info);
- // Forward
- UPDATE_GIZMO_CONTROL(id_f, ray,
- desc.gizmo.scale.forward.axis.shape.cylinder, desc.gizmo.scale.forward.axis.model, gs_cylinder_vs_ray,
- desc.gizmo.scale.forward.cap.shape.aabb, desc.gizmo.scale.forward.cap.model, gs_aabb_vs_ray,
- desc.info);
- // Control
- if (ctx->focus == id_r)
- {
- desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_XAXIS, GS_YAXIS, GS_ZAXIS,
- camera, &ray, gs_v3s(0.f), true, false);
- if (desc.li.hit)
- {
- if (just_set_focus) {
- s_intersection_start = desc.li.point;
- memset(&s_delta, 0, sizeof(s_delta));
- s_delta.rotation = gs_quat(
- model->scale.x,
- model->scale.y,
- model->scale.z,
- 0.f
- );
- }
- gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_XAXIS));
- gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
- float udotn = gs_vec3_dot(u, axis);
- float neg = gs_vec3_dot(axis, GS_XAXIS) < 0.f ? 1.f : -1.f;
- s_delta.translation = gs_vec3_scale(axis, udotn);
- s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
- s_delta.translation = gs_vec3_scale(s_delta.translation, neg);
- s_delta.translation.z = 0.f;
- s_delta.translation.y = 0.f;
- if (snap > 0.f)
- {
- s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
- float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
- gs_vec3 norm = gs_vec3_norm(s_delta.scale);
- gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
- gs_vec3 os = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
- model->scale = gs_vec3_add(os, delta);
- }
- else
- {
- model->scale = gs_vec3_add(model->scale, s_delta.translation);
- }
- }
- }
- else if (ctx->focus == id_u)
- {
- desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_YAXIS, GS_XAXIS, GS_ZAXIS,
- camera, &ray, gs_v3s(0.f), true, false);
- if (desc.li.hit)
- {
- if (just_set_focus) {
- s_intersection_start = desc.li.point;
- memset(&s_delta, 0, sizeof(s_delta));
- s_delta.rotation = gs_quat(
- model->scale.x,
- model->scale.y,
- model->scale.z,
- 0.f
- );
- }
- gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_YAXIS));
- gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
- float udotn = gs_vec3_dot(u, axis);
- s_delta.translation = gs_vec3_scale(axis, udotn);
- float neg = gs_vec3_dot(axis, GS_YAXIS) < 0.f ? -1.f : 1.f;
- s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
- s_delta.translation = gs_vec3_scale(s_delta.translation, neg);
- s_delta.translation.z = 0.f;
- s_delta.translation.x = 0.f;
- if (snap > 0.f)
- {
- s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
- float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
- gs_vec3 norm = gs_vec3_norm(s_delta.scale);
- gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
- gs_vec3 os = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
- model->scale = gs_vec3_add(os, delta);
- }
- else
- {
- model->scale = gs_vec3_add(model->scale, s_delta.translation);
- }
- }
- }
- else if (ctx->focus == id_f)
- {
- desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_ZAXIS, GS_XAXIS, GS_YAXIS,
- camera, &ray, gs_v3s(0.f), true, false);
- if (desc.li.hit)
- {
- if (just_set_focus) {
- s_intersection_start = desc.li.point;
- memset(&s_delta, 0, sizeof(s_delta));
- s_delta.rotation = gs_quat(
- model->scale.x,
- model->scale.y,
- model->scale.z,
- 0.f
- );
- }
- gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_ZAXIS));
- gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
- float udotn = gs_vec3_dot(u, axis);
- float neg = gs_vec3_dot(axis, GS_ZAXIS) < 0.f ? -1.f : 1.f;
- s_delta.translation = gs_vec3_scale(axis, udotn);
- s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
- s_delta.translation = gs_vec3_scale(s_delta.translation, neg);
- s_delta.translation.y = 0.f;
- s_delta.translation.x = 0.f;
- if (snap > 0.f)
- {
- s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
- float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
- gs_vec3 norm = gs_vec3_norm(s_delta.scale);
- gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
- gs_vec3 os = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
- model->scale = gs_vec3_add(os, delta);
- }
- else
- {
- model->scale = gs_vec3_add(model->scale, s_delta.translation);
- }
- }
- }
- } break;
- #define UPDATE_GIZMO_CONTROL_ROTATE(ID, RAY, SHAPE, MODEL, AXIS, INFO)\
- do {\
- int32_t mouseover = 0;\
- gs_gui_id id = (ID);\
- gs_contact_info_t info = gs_default_val();\
- gs_vec3 axis = gs_quat_rotate(desc.xform.rotation, AXIS);\
- info.normal = axis;\
- if (in_hover_root) {\
- gs_plane_t ip = gs_plane_from_pt_normal(desc.xform.translation, axis);\
- float denom = gs_vec3_dot(gs_v3(ip.a, ip.b, ip.c), ray.d);\
- denom = fabsf(denom) >= GS_EPSILON ? denom : 0.00001f;\
- info.depth = -(ip.a * ray.p.x + ip.b * ray.p.y + ip.c * ray.p.z + ip.d) / denom;\
- gs_gui_gizmo_line_intersection_result_t res = gs_default_val();\
- res.point = gs_vec3_add(ray.p, gs_vec3_scale(ray.d, info.depth));\
- float dist = gs_vec3_dist(res.point, model->translation);\
- float scl = gs_vec3_len(desc.xform.scale);\
- if (dist <= 0.6f * scl && dist >= 0.45f * scl) {\
- info.hit = true;\
- }\
- }\
- mouseover = info.hit && info.depth <= INFO.depth && in_hover_root && !ctx->hover_split && !ctx->lock_hover_id;\
- if (ctx->focus == id) {ctx->updated_focus = 1; INFO = info;}\
- if (~opt & GS_GUI_OPT_NOINTERACT) {\
- /* Check for hold focus here */\
- if (mouseover && !ctx->mouse_down) {\
- gs_gui_set_hover(ctx, id);\
- INFO = info;\
- }\
- \
- if (ctx->focus == id)\
- {\
- res |= GS_GUI_RES_ACTIVE;\
- just_set_focus = false;\
- gs_gui_set_focus(ctx, id);\
- if (ctx->mouse_pressed && !mouseover) {gs_gui_set_focus(ctx, 0);}\
- if (!ctx->mouse_down && ~opt & GS_GUI_OPT_HOLDFOCUS) {gs_gui_set_focus(ctx, 0);}\
- }\
- \
- if (ctx->prev_hover == id && !mouseover) {ctx->prev_hover = ctx->hover;}\
- \
- if (ctx->hover == id)\
- {\
- if (ctx->mouse_pressed)\
- {\
- if ((opt & GS_GUI_OPT_LEFTCLICKONLY && ctx->mouse_pressed == GS_GUI_MOUSE_LEFT) || (~opt & GS_GUI_OPT_LEFTCLICKONLY))\
- {\
- gs_gui_set_focus(ctx, id);\
- just_set_focus = true;\
- }\
- }\
- else if (!mouseover)\
- {\
- gs_gui_set_hover(ctx, 0);\
- }\
- }\
- }\
- } while (0)
- case GS_GUI_GIZMO_ROTATE:
- {
- // Construct translate gizmo for this frame based on given parent transform
- desc.gizmo.rotate = gs_gizmo_rotate(&desc.xform);
- gs_gui_id id_r = gs_gui_get_id(ctx, "#gizmo_rotate_right", strlen("#gizmo_rotate_right"));
- gs_gui_id id_u = gs_gui_get_id(ctx, "#gizmo_rotate_up", strlen("#gizmo_rotate_up"));
- gs_gui_id id_f = gs_gui_get_id(ctx, "#gizmo_rotate_forward", strlen("#gizmo_rotate_forward"));
- // Right
- UPDATE_GIZMO_CONTROL_ROTATE(id_r, ray, desc.gizmo.rotate.right.axis.shape.plane,
- desc.gizmo.rotate.right.axis.model, GS_XAXIS, desc.info);
- // Up
- UPDATE_GIZMO_CONTROL_ROTATE(id_u, ray, desc.gizmo.rotate.up.axis.shape.plane,
- desc.gizmo.rotate.up.axis.model, GS_YAXIS, desc.info);
- // Forward
- UPDATE_GIZMO_CONTROL_ROTATE(id_f, ray, desc.gizmo.rotate.forward.axis.shape.plane,
- desc.gizmo.rotate.forward.axis.model, GS_ZAXIS, desc.info);
- if (ctx->focus == id_r)
- {
- desc.li = op == GS_GUI_TRANSFORM_LOCAL ?
- gs_gui_gizmo_get_line_intersection(&desc.xform, GS_XAXIS, GS_YAXIS, GS_ZAXIS, camera, &ray, gs_v3s(0.f), false, false) :
- gs_gui_gizmo_get_line_intersection(&desc.xform, GS_XAXIS, GS_YAXIS, GS_ZAXIS, camera, &ray, gs_v3s(0.f), false, true);
- if (desc.li.hit)
- {
- if (just_set_focus) {
- s_intersection_start = desc.li.point;
- memset(&s_delta, 0, sizeof(s_delta));
- s_delta.translation = gs_v3(
- model->rotation.x,
- model->rotation.y,
- model->rotation.z
- );
- s_delta.scale.y = model->rotation.w;
- }
- float dist_from_cam = gs_vec3_dist(desc.xform.translation, camera->transform.translation);
- const float denom = dist_from_cam != 0.f ? dist_from_cam : 2.f;
- const gs_vec3 end_vector = gs_vec3_sub(desc.li.point, desc.xform.translation);
- const gs_vec3 start_norm = gs_vec3_norm(gs_vec3_sub(s_intersection_start, desc.xform.translation));
- const gs_vec3 end_norm = gs_vec3_norm(end_vector);
- const gs_vec3 rot_local = gs_quat_rotate(desc.xform.rotation, GS_XAXIS);
- float len = gs_vec3_len(end_vector) / denom;
- float angle = gs_vec3_angle_between_signed(start_norm, end_norm);
- if (len > 1.f) {
- angle *= len;
- }
- gs_vec3 cross = gs_vec3_cross(start_norm, end_norm);
- if (gs_vec3_dot(rot_local, cross) < 0.f) {
- angle *= -1.f;
- }
- s_intersection_start = desc.li.point;
- float delta = gs_rad2deg(angle);
- s_delta.scale.x += delta;
- s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(delta), GS_XAXIS);
- if (snap > 0.f)
- {
- float snap_delta = round(s_delta.scale.x / snap) * snap;
- s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(snap_delta), GS_XAXIS);
- gs_quat orot = gs_quat(s_delta.translation.x, s_delta.translation.y, s_delta.translation.z, s_delta.scale.y);
- switch (mode) {
- case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, orot); break;
- case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(orot, s_delta.rotation); break;
- }
- }
- else
- {
- switch (mode) {
- case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, model->rotation); break;
- case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(model->rotation, s_delta.rotation); break;
- }
- }
- }
- }
- else if (ctx->focus == id_u)
- {
- desc.li = op == GS_GUI_TRANSFORM_LOCAL ?
- gs_gui_gizmo_get_line_intersection(&desc.xform, GS_YAXIS, GS_XAXIS, GS_ZAXIS, camera, &ray, gs_v3s(0.f), false, false) :
- gs_gui_gizmo_get_line_intersection(&desc.xform, GS_YAXIS, GS_XAXIS, GS_ZAXIS, camera, &ray, gs_v3s(0.f), false, true);
- if (desc.li.hit)
- {
- if (just_set_focus) {
- s_intersection_start = desc.li.point;
- memset(&s_delta, 0, sizeof(s_delta));
- s_delta.translation = gs_v3(
- model->rotation.x,
- model->rotation.y,
- model->rotation.z
- );
- s_delta.scale.y = model->rotation.w;
- }
- float dist_from_cam = gs_vec3_dist(desc.xform.translation, camera->transform.translation);
- const float denom = dist_from_cam != 0.f ? dist_from_cam : 2.f;
- const gs_vec3 end_vector = gs_vec3_sub(desc.li.point, desc.xform.translation);
- const gs_vec3 start_norm = gs_vec3_norm(gs_vec3_sub(s_intersection_start, desc.xform.translation));
- const gs_vec3 end_norm = gs_vec3_norm(end_vector);
- const gs_vec3 rot_local = gs_quat_rotate(desc.xform.rotation, GS_YAXIS);
- float len = gs_vec3_len(end_vector) / denom;
- float angle = gs_vec3_angle_between_signed(start_norm, end_norm);
- if (len > 1.f) {
- angle *= len;
- }
- gs_vec3 cross = gs_vec3_cross(start_norm, end_norm);
- if (gs_vec3_dot(rot_local, cross) < 0.f) {
- angle *= -1.f;
- }
- s_intersection_start = desc.li.point;
- float delta = gs_rad2deg(angle);
- s_delta.scale.x += delta;
- s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(delta), GS_YAXIS);
- if (snap > 0.f)
- {
- float snap_delta = round(s_delta.scale.x / snap) * snap;
- s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(snap_delta), GS_YAXIS);
- gs_quat orot = gs_quat(s_delta.translation.x, s_delta.translation.y, s_delta.translation.z, s_delta.scale.y);
- switch (mode) {
- case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, orot); break;
- case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(orot, s_delta.rotation); break;
- }
- }
- else
- {
- switch (mode) {
- case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, model->rotation); break;
- case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(model->rotation, s_delta.rotation); break;
- }
- }
- }
- }
- else if (ctx->focus == id_f)
- {
- desc.li = op == GS_GUI_TRANSFORM_LOCAL ?
- gs_gui_gizmo_get_line_intersection(&desc.xform, GS_ZAXIS, GS_XAXIS, GS_YAXIS, camera, &ray, gs_v3s(0.f), false, false) :
- gs_gui_gizmo_get_line_intersection(&desc.xform, GS_ZAXIS, GS_XAXIS, GS_YAXIS, camera, &ray, gs_v3s(0.f), false, true);
- if (desc.li.hit)
- {
- if (just_set_focus) {
- s_intersection_start = desc.li.point;
- memset(&s_delta, 0, sizeof(s_delta));
- s_delta.translation = gs_v3(
- model->rotation.x,
- model->rotation.y,
- model->rotation.z
- );
- s_delta.scale.y = model->rotation.w;
- }
- float dist_from_cam = gs_vec3_dist(desc.xform.translation, camera->transform.translation);
- const float denom = dist_from_cam != 0.f ? dist_from_cam : 2.f;
- const gs_vec3 end_vector = gs_vec3_sub(desc.li.point, desc.xform.translation);
- const gs_vec3 start_norm = gs_vec3_norm(gs_vec3_sub(s_intersection_start, desc.xform.translation));
- const gs_vec3 end_norm = gs_vec3_norm(end_vector);
- const gs_vec3 rot_local = gs_quat_rotate(desc.xform.rotation, GS_ZAXIS);
- float len = gs_vec3_len(end_vector) / denom;
- float angle = gs_vec3_angle_between_signed(start_norm, end_norm);
- if (len > 1.f) {
- angle *= len;
- }
- gs_vec3 cross = gs_vec3_cross(start_norm, end_norm);
- if (gs_vec3_dot(rot_local, cross) < 0.f) {
- angle *= -1.f;
- }
- s_intersection_start = desc.li.point;
- float delta = gs_rad2deg(angle);
- s_delta.scale.x += delta;
- s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(delta), GS_ZAXIS);
- if (snap > 0.f)
- {
- float snap_delta = round(s_delta.scale.x / snap) * snap;
- s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(snap_delta), GS_ZAXIS);
- gs_quat orot = gs_quat(s_delta.translation.x, s_delta.translation.y, s_delta.translation.z, s_delta.scale.y);
- switch (mode) {
- case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, orot); break;
- case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(orot, s_delta.rotation); break;
- }
- }
- else
- {
- switch (mode) {
- case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, model->rotation); break;
- case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(model->rotation, s_delta.rotation); break;
- }
- }
- }
- }
- } break;
- }
- // Have to render the gizmo using view/projection (so a custom render command)
- gs_gui_draw_custom(ctx, clip, gs_gui_gizmo_render, &desc, sizeof(desc));
- return res;
- }
- //=== Demos ===//
- GS_API_DECL int32_t
- gs_gui_style_editor(gs_gui_context_t *ctx, gs_gui_style_sheet_t* style_sheet, gs_gui_rect_t rect, bool* open)
- {
- if (!style_sheet)
- {
- style_sheet = &gs_gui_default_style_sheet;
- }
- static struct {const char* label; int32_t idx;} elements[] = {
- {"container", GS_GUI_ELEMENT_CONTAINER},
- {"button", GS_GUI_ELEMENT_BUTTON} ,
- {"panel", GS_GUI_ELEMENT_PANEL},
- {"input", GS_GUI_ELEMENT_INPUT},
- {"label", GS_GUI_ELEMENT_LABEL},
- {"text", GS_GUI_ELEMENT_TEXT},
- {"scroll", GS_GUI_ELEMENT_SCROLL},
- {"image", GS_GUI_ELEMENT_IMAGE},
- {NULL}
- };
- static char* states[] = {
- "default",
- "hover",
- "focus"
- };
- static struct {const char* label; int32_t idx;} colors[] = {
- {"background", GS_GUI_COLOR_BACKGROUND},
- {"border", GS_GUI_COLOR_BORDER},
- {"shadow", GS_GUI_COLOR_SHADOW},
- {"content", GS_GUI_COLOR_CONTENT} ,
- {"content_shadow", GS_GUI_COLOR_CONTENT_SHADOW},
- {"content_background", GS_GUI_COLOR_CONTENT_BACKGROUND},
- {"content_border", GS_GUI_COLOR_CONTENT_BORDER},
- {NULL}
- };
- if (gs_gui_window_begin_ex(ctx, "Style_Editor", rect, open, NULL, 0x00))
- {
- for (uint32_t i = 0; elements[i].label; ++i)
- {
- int32_t idx = elements[i].idx;
- if (gs_gui_treenode_begin_ex(ctx, elements[i].label, NULL, 0x00))
- {
- for (uint32_t j = 0; j < GS_GUI_ELEMENT_STATE_COUNT; ++j)
- {
- gs_gui_push_id(ctx, &j, sizeof(j));
- gs_gui_style_t* s = &style_sheet->styles[idx][j];
- if (gs_gui_treenode_begin_ex(ctx, states[j], NULL, 0x00))
- {
- gs_gui_style_t* save = gs_gui_push_style(ctx, &ctx->style_sheet->styles[GS_GUI_ELEMENT_PANEL][0x00]);
- int32_t row[] = {-1};
- gs_gui_layout_row(ctx, 1, row, 300);
- gs_gui_panel_begin(ctx, states[j]);
- {
- gs_gui_layout_t* l = gs_gui_get_layout(ctx);
- gs_gui_rect_t* r = &l->body;
- const int32_t ls = 80;
- // size
- int32_t w = (int32_t)((l->body.w - ls) * 0.35f);
- {
- int32_t row[] = {ls, w, w};
- gs_gui_layout_row(ctx, 3, row, 0);
- }
- gs_gui_label(ctx, "size:");
- gs_gui_slider(ctx, &s->size[0], 0.f, 500.f);
- gs_gui_slider(ctx, &s->size[1], 0.f, 500.f);
- w = (int32_t)((l->body.w - ls) * 0.2f);
- {
- int32_t row[] = {ls, w, w, w, w};
- gs_gui_layout_row(ctx, 5, row, 0);
- }
- gs_gui_label(ctx, "border_width:");
- int16_slider(ctx, &s->border_width[0], 0, 100, NULL, 0x00);
- int16_slider(ctx, &s->border_width[1], 0, 100, NULL, 0x00);
- int16_slider(ctx, &s->border_width[2], 0, 100, NULL, 0x00);
- int16_slider(ctx, &s->border_width[3], 0, 100, NULL, 0x00);
- gs_gui_label(ctx, "border_radius:");
- int16_slider(ctx, &s->border_radius[0], 0, 100, NULL, 0x00);
- int16_slider(ctx, &s->border_radius[1], 0, 100, NULL, 0x00);
- int16_slider(ctx, &s->border_radius[2], 0, 100, NULL, 0x00);
- int16_slider(ctx, &s->border_radius[3], 0, 100, NULL, 0x00);
- // padding/margin
- gs_gui_label(ctx, "padding:");
- int32_slider(ctx, &s->padding[0], 0, 100, NULL, 0x00);
- int32_slider(ctx, &s->padding[1], 0, 100, NULL, 0x00);
- int32_slider(ctx, &s->padding[2], 0, 100, NULL, 0x00);
- int32_slider(ctx, &s->padding[3], 0, 100, NULL, 0x00);
- gs_gui_label(ctx, "margin:");
- int16_slider(ctx, &s->margin[0], 0, 100, NULL, 0x00);
- int16_slider(ctx, &s->margin[1], 0, 100, NULL, 0x00);
- int16_slider(ctx, &s->margin[2], 0, 100, NULL, 0x00);
- int16_slider(ctx, &s->margin[3], 0, 100, NULL, 0x00);
- // Colors
- int sw = (int32_t)(l->body.w * 0.14);
- {
- int32_t row[] = {80, sw, sw, sw, sw, -1};
- gs_gui_layout_row(ctx, 6, row, 0);
- }
- for (uint32_t c = 0; colors[c].label; ++c)
- {
- gs_gui_label(ctx, colors[c].label);
- uint8_slider(ctx, &s->colors[c].r, 0, 255, NULL, 0x00);
- uint8_slider(ctx, &s->colors[c].g, 0, 255, NULL, 0x00);
- uint8_slider(ctx, &s->colors[c].b, 0, 255, NULL, 0x00);
- uint8_slider(ctx, &s->colors[c].a, 0, 255, NULL, 0x00);
- gs_gui_draw_rect(ctx, gs_gui_layout_next(ctx), s->colors[c]);
- }
- }
- gs_gui_panel_end(ctx);
- gs_gui_pop_style(ctx, save);
- gs_gui_treenode_end(ctx);
- }
- gs_gui_pop_id(ctx);
- }
- gs_gui_treenode_end(ctx);
- }
- }
- gs_gui_window_end(ctx);
- }
- return 0x01;
- }
- GS_API_DECL int32_t gs_gui_demo_window(gs_gui_context_t* ctx, gs_gui_rect_t rect, bool* open)
- {
- /*
- Window for all of the available features for gs_gui:
- - Opts:
- - Widgets:
- - Label
- - Panel
- - Button
- - Textbox
- - Text
- Todo:
- - Menu / MenuItem
- - ColorPicker
- - Lists (with images / various style enums for item types (circle, rect, box)
- - Window Navigation
- - VIM controls eventually?
- - Docking
- - Menus
- - Tabs
- - Styles:
- - Inline styles
- - Style sheets
- */
- if (gs_gui_window_begin_ex(ctx, "Demo_Window", rect, open, NULL, 0x00))
- {
- gs_gui_container_t* win = gs_gui_get_current_container(ctx);
- if (gs_gui_treenode_begin(ctx, "Help"))
- {
- {
- int32_t row[] = {-10};
- gs_gui_layout_row(ctx, 1, row, 170);
- }
-
- gs_gui_panel_begin(ctx, "#!window_info");
- {
- {
- int32_t row[] = {-1};
- gs_gui_layout_row(ctx, 1, row, 0);
- }
- gs_gui_label(ctx, "ABOUT THIS DEMO:");
- gs_gui_text(ctx, " - Sections below are demonstrating many aspects of the util.");
- }
- gs_gui_panel_end(ctx);
- gs_gui_treenode_end(ctx);
- }
- if (gs_gui_treenode_begin(ctx, "Window Info"))
- {
- {
- int32_t row[] = {-10};
- gs_gui_layout_row(ctx, 1, row, 170);
- }
- gs_gui_panel_begin(ctx, "#!window_info");
- {
- char buf[64];
- {
- int32_t row[] = {65, -1};
- gs_gui_layout_row(ctx, 2, row, 0);
- }
- gs_gui_label(ctx,"Position:");
- gs_snprintf(buf, 64, "%.2f, %.2f", win->rect.x, win->rect.y); gs_gui_label(ctx, buf);
- gs_gui_label(ctx, "Size:");
- gs_snprintf(buf, 64, "%.2f, %.2f", win->rect.w, win->rect.h); gs_gui_label(ctx, buf);
- gs_gui_label(ctx, "Title:");
- gs_gui_label(ctx, win->name);
- gs_gui_label(ctx, "ID:");
- gs_snprintf(buf, 64, "%zu", win->id); gs_gui_label(ctx, buf);
- gs_gui_label(ctx, "Open:");
- gs_snprintf(buf, 64, "%s", win->open ? "true" : "close"); gs_gui_label(ctx, buf);
- }
- gs_gui_panel_end(ctx);
- gs_gui_treenode_end(ctx);
- }
- if (gs_gui_treenode_begin(ctx, "Context State"))
- {
- {
- int32_t row[] = {-10};
- gs_gui_layout_row(ctx, 1, row, 170);
- }
- gs_gui_panel_begin(ctx, "#!context_state");
- {
- char buf[64];
- {
- int32_t row[] = {80, -1};
- gs_gui_layout_row(ctx, 2, row, 0);
- }
- gs_gui_label(ctx,"Hovered:");
- gs_snprintf(buf, 64, "%s", ctx->hover_root ? ctx->hover_root->name : "NULL"); gs_gui_label(ctx, buf);
- gs_gui_label(ctx, "Focused:");
- gs_snprintf(buf, 64, "%s", ctx->focus_root ? ctx->focus_root->name : "NULL"); gs_gui_label(ctx, buf);
- gs_gui_label(ctx, "Active:");
- gs_snprintf(buf, 64, "%s", ctx->active_root ? ctx->active_root->name : "NULL"); gs_gui_label(ctx, buf);
- gs_gui_label(ctx, "Lock Focus:");
- gs_snprintf(buf, 64, "%zu", ctx->lock_focus); gs_gui_label(ctx, buf);
- }
- gs_gui_panel_end(ctx);
- gs_gui_treenode_end(ctx);
- }
- if (gs_gui_treenode_begin(ctx, "Widgets"))
- {
- {
- int32_t row[] = {-10};
- gs_gui_layout_row(ctx, 1, row, 170);
- }
- gs_gui_panel_begin(ctx, "#!widgets");
- {
- {
- int32_t row[] = {150, 50};
- gs_gui_layout_row(ctx, 2, row, 0);
- }
- gs_gui_layout_column_begin(ctx);
- {
- {
- int32_t row[] = {0};
- gs_gui_layout_row(ctx, 1, row, 0);
- }
- gs_gui_button(ctx, "Button");
- // Label
- gs_gui_label(ctx, "Label");
- // Text
- {
- int32_t row[] = {150};
- gs_gui_layout_row(ctx, 1, row, 0);
- }
- gs_gui_text(ctx, "This is some text");
- static char buf[64] = {0};
- gs_gui_textbox(ctx, buf, 64);
- }
- gs_gui_layout_column_end(ctx);
- gs_gui_layout_column_begin(ctx);
- {
- gs_gui_label(ctx, "(?)");
- if (ctx->hover == ctx->last_id) gs_println("HOVERED");
- }
- gs_gui_layout_column_end(ctx);
- }
- gs_gui_panel_end(ctx);
- gs_gui_treenode_end(ctx);
- }
- gs_gui_window_end(ctx);
- }
- return 0x01;
- }
- //==== Resource Loading ===//
- typedef enum
- {
- GS_GUI_SS_DEF_NUMBER = 0x00,
- GS_GUI_SS_DEF_ENUM,
- GS_GUI_SS_DEF_COLOR,
- GS_GUI_SS_DEF_STR
- } gs_gui_ss_var_def_type;
- typedef struct
- {
- gs_gui_ss_var_def_type type;
- union {
- int32_t number;
- gs_color_t color;
- char str[64];
- } val;
- } gs_gui_ss_var_def_t;
- typedef struct
- {
- gs_hash_table(uint64_t, gs_gui_ss_var_def_t) variables;
- } gs_gui_ss_variables_t;
- #define _GS_GUI_SS_GET_TO_VALUES(T0, T1)\
- do {\
- bool ret = gs_lexer_require_token_type(lex, T0);\
- ret &= gs_lexer_require_token_type(lex, T1);\
- if (!ret) {\
- gs_log_warning("Unidentified token.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- } while (0)
- bool _gs_gui_style_sheet_parse_attribute_transition(gs_gui_context_t* ctx, gs_lexer_t* lex,
- gs_gui_style_sheet_t* ss, const uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
- {
- // Name of enum attribute
- gs_token_t token = gs_lexer_current_token(lex);
- // gs_token_debug_print(&token);
- if (id_tag)
- {
- if (!gs_hash_table_exists(ss->cid_animations, id_tag))
- {
- gs_gui_animation_property_list_t sl = gs_default_val();
- gs_hash_table_insert(ss->cid_animations, id_tag, sl);
- }
- }
- #define PARSE_TRANSITION(T)\
- do {\
- uint32_t time_v = 0;\
- uint32_t delay_v = 0;\
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);\
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
- if (!ret) {\
- gs_log_warning("Transition: Unidentified token.");\
- return false;\
- }\
- gs_token_t time = gs_lexer_current_token(lex);\
- switch (time.type)\
- { \
- case GS_TOKEN_NUMBER:\
- {\
- gs_snprintfc(TIME, 32, "%.*s", time.len, time.text);\
- time_v = (uint32_t)atoi(TIME);\
- } break;\
- \
- case GS_TOKEN_DOLLAR:\
- {\
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER));\
- if (!ret) {\
- gs_log_warning("Transition: Variable missing identifier token.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- gs_snprintfc(VAR, 64, "%.*s", token.len, token.text);\
- uint64_t hash = gs_hash_str64(VAR);\
- if (gs_hash_table_exists(variables->variables, hash))\
- {\
- time_v = (uint32_t)(gs_hash_table_getp(variables->variables, hash))->val.number;\
- }\
- else\
- {\
- gs_log_warning("Transition: Variable not found: %s.", VAR);\
- return false;\
- }\
- } break;\
- }\
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
- if (!ret) {\
- gs_log_warning("Transition: Unidentified token.");\
- return false;\
- }\
- gs_token_t delay = gs_lexer_current_token(lex);\
- switch (delay.type)\
- {\
- case GS_TOKEN_NUMBER:\
- {\
- gs_snprintfc(DELAY, 32, "%.*s", delay.len, delay.text);\
- uint32_t delay_v = (uint32_t)atoi(DELAY);\
- } break;\
- \
- case GS_TOKEN_DOLLAR:\
- {\
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER));\
- if (!ret) {\
- gs_log_warning("Transition: Variable missing identifier token.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- gs_snprintfc(VAR, 64, "%.*s", token.len, token.text);\
- uint64_t hash = gs_hash_str64(VAR);\
- if (gs_hash_table_exists(variables->variables, hash))\
- {\
- delay_v = (uint32_t)(gs_hash_table_getp(variables->variables, hash))->val.number;\
- }\
- else\
- {\
- gs_log_warning("Transition: Variable not found: %s.", VAR);\
- return false;\
- }\
- } break;\
- }\
- gs_gui_animation_property_t prop = gs_default_val();\
- prop.type = T;\
- prop.time = (int16_t)time_v;\
- prop.delay = (int16_t)delay_v;\
- switch (state)\
- {\
- case GS_GUI_ELEMENT_STATE_DEFAULT:\
- {\
- for (uint32_t s = 0; s < 3; ++s)\
- {\
- gs_dyn_array_push(list->properties[s], prop);\
- }\
- } break;\
- default:\
- {\
- gs_dyn_array_push(list->properties[state], prop);\
- } break;\
- }\
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON));\
- if (!ret) {\
- gs_log_warning("Transition: Missing semicolon.");\
- return false;\
- }\
- } while (0)
- _GS_GUI_SS_GET_TO_VALUES(GS_TOKEN_COLON, GS_TOKEN_LBRACE);
- if (!gs_hash_table_exists(ss->animations, (gs_gui_element_type)elementid))
- {
- gs_gui_animation_property_list_t list = gs_default_val();
- gs_hash_table_insert(ss->animations, (gs_gui_element_type)elementid, list);
- }
- gs_gui_animation_property_list_t* list = id_tag ? gs_hash_table_getp(ss->cid_animations, id_tag) :
- gs_hash_table_getp(ss->animations, (gs_gui_element_type)elementid);
- int32_t bc = 1;
- while (gs_lexer_can_lex(lex) && bc)
- {
- token = gs_lexer_next_token(lex);
- switch (token.type)
- {
- case GS_TOKEN_LBRACE: bc++; break;
- case GS_TOKEN_RBRACE: bc--; break;
- case GS_TOKEN_IDENTIFIER:
- {
- if (gs_token_compare_text(&token, "color_background")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_BACKGROUND);
- else if (gs_token_compare_text(&token, "color_border")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_BORDER);
- else if (gs_token_compare_text(&token, "color_shadow")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_SHADOW);
- else if (gs_token_compare_text(&token, "color_content")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_CONTENT);
- else if (gs_token_compare_text(&token, "width")) PARSE_TRANSITION(GS_GUI_STYLE_WIDTH);
- else if (gs_token_compare_text(&token, "height")) PARSE_TRANSITION(GS_GUI_STYLE_HEIGHT);
- else if (gs_token_compare_text(&token, "padding")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING);
- else if (gs_token_compare_text(&token, "padding_left")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING_LEFT);
- else if (gs_token_compare_text(&token, "padding_right")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING_RIGHT);
- else if (gs_token_compare_text(&token, "padding_top")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING_TOP);
- else if (gs_token_compare_text(&token, "padding_bottom")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING_BOTTOM);
- else if (gs_token_compare_text(&token, "margin")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING);
- else if (gs_token_compare_text(&token, "margin_left")) PARSE_TRANSITION(GS_GUI_STYLE_MARGIN_LEFT);
- else if (gs_token_compare_text(&token, "margin_right")) PARSE_TRANSITION(GS_GUI_STYLE_MARGIN_RIGHT);
- else if (gs_token_compare_text(&token, "margin_top")) PARSE_TRANSITION(GS_GUI_STYLE_MARGIN_TOP);
- else if (gs_token_compare_text(&token, "margin_bottom")) PARSE_TRANSITION(GS_GUI_STYLE_MARGIN_BOTTOM);
- else if (gs_token_compare_text(&token, "shadow_x")) PARSE_TRANSITION(GS_GUI_STYLE_SHADOW_X);
- else if (gs_token_compare_text(&token, "shadow_y")) PARSE_TRANSITION(GS_GUI_STYLE_SHADOW_Y);
- else if (gs_token_compare_text(&token, "color_content_background")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND);
- else if (gs_token_compare_text(&token, "color_content_border")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_CONTENT_BORDER);
- else if (gs_token_compare_text(&token, "color_content_shadow")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_CONTENT_SHADOW);
- else
- {
- gs_log_warning("Unidentified attribute: %.*s.", token.len, token.text);
- return false;
- }
- } break;
- }
- }
- return true;
- }
- bool _gs_gui_style_sheet_parse_attribute_font(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
- uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
- {
- // Name of enum attribute
- gs_token_t token = gs_lexer_current_token(lex);
- // gs_token_debug_print(&token);
- //
- gs_gui_style_list_t* idsl = NULL;
- if (id_tag)
- {
- const uint64_t idhash = id_tag;
- if (!gs_hash_table_exists(ss->cid_styles, idhash))
- {
- gs_gui_style_list_t sl = gs_default_val();
- gs_hash_table_insert(ss->cid_styles, idhash, sl);
- }
- idsl = gs_hash_table_getp(ss->cid_styles, idhash);
- }
- #define SET_FONT(FONT)\
- do {\
- switch (state)\
- {\
- case GS_GUI_ELEMENT_STATE_DEFAULT:\
- {\
- se.font = FONT;\
- for (uint32_t s = 0; s < 3; ++s)\
- {\
- if (idsl) gs_dyn_array_push(idsl->styles[s], se);\
- else ss->styles[elementid][s].font = FONT;\
- }\
- } break;\
- default:\
- {\
- se.font = FONT;\
- if (idsl) gs_dyn_array_push(idsl->styles[state], se);\
- ss->styles[elementid][state].font = FONT;\
- } break;\
- }\
- } while (0)
- if (gs_token_compare_text(&token, "font"))
- {
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_STRING) ||
- gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR));
- if (!ret)
- {
- gs_log_warning("Missing either string value or variable.");
- return false;
- }
- gs_gui_style_element_t se = gs_default_val();
- se.type = GS_GUI_STYLE_FONT;
- token = gs_lexer_current_token(lex);
- char FONT[64] = gs_default_val();
- switch (token.type)
- {
- case GS_TOKEN_STRING:
- {
- gs_snprintf(FONT, sizeof(FONT), "%.*s", token.len - 2, token.text + 1);
- } break;
- case GS_TOKEN_DOLLAR:
- {
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);
- if (!ret) {
- gs_log_warning("Unidentified token.");
- return false;
- }\
- token = gs_lexer_current_token(lex);
- gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);
- uint64_t hash = gs_hash_str64(TMP);
- gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?
- gs_hash_table_getp(variables->variables, hash) : NULL;
- if (!var) {
- gs_log_warning("Variable not found: %s", TMP);
- return false;
- }
- memcpy(FONT, var->val.str, sizeof(FONT));
- ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);
- if (!ret) {
- gs_log_warning("Missing semicolon.");
- return false;
- }
- token = gs_lexer_current_token(lex);
- } break;
- }
- uint64_t hash = gs_hash_str64(FONT);
- bool found = false;
- for (
- gs_hash_table_iter it = gs_hash_table_iter_new(ctx->font_stash);
- gs_hash_table_iter_valid(ctx->font_stash, it);
- gs_hash_table_iter_advance(ctx->font_stash, it)
- )
- {
- uint64_t key = gs_hash_table_getk(ctx->font_stash, it);
- if (hash == key)
- {
- gs_asset_font_t* font = gs_hash_table_iter_get(ctx->font_stash, it);
- SET_FONT(font);
- found = true;
- break;
- }
- }
- if (!found)
- {
- gs_log_warning("Font not found in gui font stash: %s", FONT);
- }
- }
- else
- {
- gs_log_warning("Unidentified token.");
- return false;
- }
- return true;
- }
- bool _gs_gui_style_sheet_parse_attribute_enum(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
- uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
- {
- // Name of enum attribute
- gs_token_t token = gs_lexer_current_token(lex);
- // gs_token_debug_print(&token);
- gs_gui_style_list_t* idsl = NULL;
- if (id_tag)
- {
- const uint64_t idhash = id_tag;
- if (!gs_hash_table_exists(ss->cid_styles, idhash))
- {
- gs_gui_style_list_t sl = gs_default_val();
- gs_hash_table_insert(ss->cid_styles, idhash, sl);
- }
- idsl = gs_hash_table_getp(ss->cid_styles, idhash);
- }
- #define SET_ENUM(COMP, VAL)\
- do {\
- se.value = VAL;\
- switch (state)\
- {\
- case GS_GUI_ELEMENT_STATE_DEFAULT:\
- {\
- if (idsl)\
- {\
- for (uint32_t s = 0; s < 3; ++s) {\
- gs_dyn_array_push(idsl->styles[s], se);\
- }\
- }\
- else\
- {\
- for (uint32_t s = 0; s < 3; ++s) {\
- ss->styles[elementid][s].COMP = VAL;\
- }\
- }\
- } break;\
- default:\
- {\
- if (idsl) gs_dyn_array_push(idsl->styles[state], se);\
- else ss->styles[elementid][state].COMP = VAL;\
- } break;\
- }\
- } while (0)
- if (gs_token_compare_text(&token, "justify_content"))
- {
- gs_gui_style_element_t se = gs_default_val();
- se.type = GS_GUI_STYLE_JUSTIFY_CONTENT;
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER) ||
- gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR));
- if (!ret)
- {
- gs_log_warning("Missing either identifier value or variable.");
- return false;
- }
- token = gs_lexer_current_token(lex);
- switch (token.type)
- {
- case GS_TOKEN_IDENTIFIER:
- {
- if (gs_token_compare_text(&token, "start")) SET_ENUM(justify_content, GS_GUI_JUSTIFY_START);
- else if (gs_token_compare_text(&token, "end")) SET_ENUM(justify_content, GS_GUI_JUSTIFY_END);
- else if (gs_token_compare_text(&token, "center")) SET_ENUM(justify_content, GS_GUI_JUSTIFY_CENTER);
- } break;
- case GS_TOKEN_DOLLAR:
- {
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);
- if (!ret) {
- gs_log_warning("Unidentified token.");
- return false;
- }\
- token = gs_lexer_current_token(lex);
- gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);
- uint64_t hash = gs_hash_str64(TMP);
- gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?
- gs_hash_table_getp(variables->variables, hash) : NULL;
- if (!var) {
- gs_log_warning("Variable not found: %s", TMP);
- return false;
- }
- SET_ENUM(justify_content, var->val.number);
- ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);
- if (!ret) {
- gs_log_warning("Missing semicolon.");
- return false;
- }
- token = gs_lexer_current_token(lex);
- } break;
- }
- }
- else if (gs_token_compare_text(&token, "align_content"))
- {
- gs_gui_style_element_t se = gs_default_val();
- se.type = GS_GUI_STYLE_ALIGN_CONTENT;
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER) ||
- gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR));
- if (!ret)
- {
- gs_log_warning("Missing either identifier value or variable.");
- return false;
- }
- token = gs_lexer_current_token(lex);
- switch (token.type)
- {
- case GS_TOKEN_IDENTIFIER:
- {
- if (gs_token_compare_text(&token, "start")) SET_ENUM(align_content, GS_GUI_ALIGN_START);
- else if (gs_token_compare_text(&token, "end")) SET_ENUM(align_content, GS_GUI_ALIGN_END);
- else if (gs_token_compare_text(&token, "center")) SET_ENUM(align_content, GS_GUI_ALIGN_CENTER);
- } break;
- case GS_TOKEN_DOLLAR:
- {
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);
- if (!ret) {
- gs_log_warning("Unidentified token.");
- return false;
- }\
- token = gs_lexer_current_token(lex);
- gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);
- uint64_t hash = gs_hash_str64(TMP);
- gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?
- gs_hash_table_getp(variables->variables, hash) : NULL;
- if (!var) {
- gs_log_warning("Variable not found: %s", TMP);
- return false;
- }
- SET_ENUM(align_content, var->val.number);
- ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);
- if (!ret) {
- gs_log_warning("Missing semicolon.");
- return false;
- }
- token = gs_lexer_current_token(lex);
- } break;
- }
- }
- return true;
- }
- bool _gs_gui_style_sheet_parse_attribute_val(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
- uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
- {
- // Name of value attribute
- gs_token_t token = gs_lexer_current_token(lex);
- // gs_token_debug_print(&token);
- gs_gui_style_list_t* idsl = NULL;
- if (id_tag)
- {
- const uint64_t idhash = id_tag;
- if (!gs_hash_table_exists(ss->cid_styles, idhash))
- {
- gs_gui_style_list_t sl = gs_default_val();
- gs_hash_table_insert(ss->cid_styles, idhash, sl);
- }
- idsl = gs_hash_table_getp(ss->cid_styles, idhash);
- }
- #define SET_VAL4(COMP, T, SE)\
- do {\
- gs_gui_style_element_t se = gs_default_val();\
- se.type = SE;\
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);\
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
- token = gs_lexer_current_token(lex);\
- gs_token_debug_print(&token);\
- if (!ret) {\
- gs_log_warning("Unidentified token.");\
- return false;\
- }\
- switch (token.type)\
- {\
- case GS_TOKEN_NUMBER:\
- {\
- gs_snprintfc(TMP, 10, "%.*s", token.len, token.text);\
- uint32_t val = (uint32_t)atoi(TMP);\
- se.value = (T)val;\
- switch (state) {\
- case GS_GUI_ELEMENT_STATE_DEFAULT:\
- {\
- if (idsl) {\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se);\
- }\
- else {\
- for (uint32_t p = 0; p < 4; ++p) {\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP[p] = (T)val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP[p] = (T)val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP[p] = (T)val;\
- }\
- }\
- } break;\
- default:\
- {\
- if (idsl) gs_dyn_array_push(idsl->styles[state], se);\
- else {\
- for (uint32_t p = 0; p < 4; ++p) {\
- ss->styles[elementid][state].COMP[p] = (T)val;\
- }\
- }\
- } break;\
- }\
- } break;\
- case GS_TOKEN_DOLLAR:\
- {\
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);\
- if (!ret) {\
- gs_log_warning("Unidentified token.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);\
- uint64_t hash = gs_hash_str64(TMP);\
- gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?\
- gs_hash_table_getp(variables->variables, hash) : NULL;\
- if (!var) {\
- gs_log_warning("Variable not found: %s", TMP);\
- return false;\
- }\
- T val = var->val.number;\
- se.value = val;\
- switch (state) {\
- case GS_GUI_ELEMENT_STATE_DEFAULT:\
- {\
- if (idsl) {\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se);\
- }\
- else {\
- for (uint32_t p = 0; p < 4; ++p) {\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP[p] = val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP[p] = val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP[p] = val;\
- }\
- }\
- } break;\
- default:\
- {\
- if (idsl) gs_dyn_array_push(idsl->styles[state], se);\
- else {\
- for (uint32_t p = 0; p < 4; ++p) {\
- ss->styles[elementid][state].COMP[p] = val;\
- }\
- }\
- } break;\
- }\
- ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);\
- if (!ret) {\
- gs_log_warning("Missing semicolon.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- } break;\
- }\
- } while (0)
- #define SET_VAL2(COMP0, COMP1, T, SE0, SE1)\
- do {\
- gs_gui_style_element_t se0 = gs_default_val();\
- gs_gui_style_element_t se1 = gs_default_val();\
- se0.type = SE0;\
- se1.type = SE1;\
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);\
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
- if (!ret) {\
- gs_log_warning("Unidentified token.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- T val = 0;\
- switch (token.type)\
- {\
- case GS_TOKEN_NUMBER:\
- {\
- gs_snprintfc(TMP, 10, "%.*s", token.len, token.text);\
- val = (T)atoi(TMP);\
- } break;\
- case GS_TOKEN_DOLLAR:\
- {\
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);\
- if (!ret) {\
- gs_log_warning("Unidentified token.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);\
- uint64_t hash = gs_hash_str64(TMP);\
- gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?\
- gs_hash_table_getp(variables->variables, hash) : NULL;\
- if (!var) {\
- gs_log_warning("Variable not found: %s", TMP);\
- return false;\
- }\
- val = (T)var->val.number;\
- ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);\
- if (!ret) {\
- gs_log_warning("Missing semicolon.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- } break;\
- }\
- switch (state)\
- {\
- case GS_GUI_ELEMENT_STATE_DEFAULT:\
- {\
- if (idsl)\
- {\
- se0.value = val;\
- se1.value = val;\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se0);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se0);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se0);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se1);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se1);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se1);\
- }\
- else\
- {\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP0 = val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP0 = val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP0 = val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP1 = val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP1 = val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP1 = val;\
- }\
- } break;\
- default:\
- {\
- if (idsl)\
- {\
- se0.value = val;\
- se1.value = val;\
- gs_dyn_array_push(idsl->styles[state], se0);\
- gs_dyn_array_push(idsl->styles[state], se1);\
- }\
- else\
- {\
- ss->styles[elementid][state].COMP0 = val;\
- ss->styles[elementid][state].COMP1 = val;\
- }\
- } break;\
- }\
- } while (0)
- #define SET_VAL(COMP, T, SE)\
- do {\
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);\
- token = gs_lexer_current_token(lex);\
- ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
- token = gs_lexer_current_token(lex);\
- if (!ret) {\
- gs_log_warning("Unidentified token: %.*s", token.len, token.text);\
- return false;\
- }\
- gs_gui_style_element_t se = gs_default_val();\
- se.type = SE;\
- T val = 0;\
- switch (token.type)\
- {\
- case GS_TOKEN_NUMBER:\
- {\
- gs_snprintfc(TMP, 10, "%.*s", token.len, token.text);\
- val = (T)atoi(TMP);\
- } break;\
- \
- case GS_TOKEN_DOLLAR:\
- {\
- bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);\
- if (!ret) {\
- gs_log_warning("Unidentified token.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);\
- uint64_t hash = gs_hash_str64(TMP);\
- gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?\
- gs_hash_table_getp(variables->variables, hash) : NULL;\
- if (!var) {\
- gs_log_warning("Variable not found: %s", TMP);\
- return false;\
- }\
- val = (T)var->val.number;\
- ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);\
- if (!ret) {\
- gs_log_warning("Missing semicolon.");\
- return false;\
- }\
- token = gs_lexer_current_token(lex);\
- } break;\
- }\
- switch (state)\
- {\
- case GS_GUI_ELEMENT_STATE_DEFAULT:\
- {\
- if (idsl)\
- {\
- se.value = (int32_t)val;\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se);\
- gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se);\
- }\
- else\
- {\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP = val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP = val;\
- ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP = val;\
- }\
- } break;\
- default:\
- {\
- if (idsl)\
- {\
- se.value = (int32_t)val;\
- gs_dyn_array_push(idsl->styles[state], se);\
- }\
- else\
- {\
- ss->styles[elementid][state].COMP = val;\
- }\
- } break;\
- }\
- } while (0)
- if (gs_token_compare_text(&token, "width")) SET_VAL(size[0], float, GS_GUI_STYLE_WIDTH);
- else if (gs_token_compare_text(&token, "height")) SET_VAL(size[1], float, GS_GUI_STYLE_HEIGHT);
- else if (gs_token_compare_text(&token, "padding")) SET_VAL4(padding, int16_t, GS_GUI_STYLE_PADDING);
- else if (gs_token_compare_text(&token, "padding_left")) SET_VAL(padding[GS_GUI_PADDING_LEFT], int16_t, GS_GUI_STYLE_PADDING_LEFT);
- else if (gs_token_compare_text(&token, "padding_right")) SET_VAL(padding[GS_GUI_PADDING_RIGHT], int16_t, GS_GUI_STYLE_PADDING_RIGHT);
- else if (gs_token_compare_text(&token, "padding_top")) SET_VAL(padding[GS_GUI_PADDING_TOP], int16_t, GS_GUI_STYLE_PADDING_TOP);
- else if (gs_token_compare_text(&token, "padding_bottom")) SET_VAL(padding[GS_GUI_PADDING_BOTTOM], int16_t, GS_GUI_STYLE_PADDING_BOTTOM);
- else if (gs_token_compare_text(&token, "margin")) SET_VAL4(margin, int16_t, GS_GUI_STYLE_MARGIN);
- else if (gs_token_compare_text(&token, "margin_left")) SET_VAL(margin[GS_GUI_MARGIN_LEFT], int16_t, GS_GUI_STYLE_MARGIN_LEFT);
- else if (gs_token_compare_text(&token, "margin_right")) SET_VAL(margin[GS_GUI_MARGIN_RIGHT], int16_t, GS_GUI_STYLE_MARGIN_RIGHT);
- else if (gs_token_compare_text(&token, "margin_top")) SET_VAL(margin[GS_GUI_MARGIN_TOP], int16_t, GS_GUI_STYLE_MARGIN_TOP);
- else if (gs_token_compare_text(&token, "margin_bottom")) SET_VAL(margin[GS_GUI_MARGIN_BOTTOM], int16_t, GS_GUI_STYLE_MARGIN_BOTTOM);
- else if (gs_token_compare_text(&token, "border")) SET_VAL4(border_width, int16_t, GS_GUI_STYLE_BORDER_WIDTH);
- else if (gs_token_compare_text(&token, "border_left")) SET_VAL(border_width[0], int16_t, GS_GUI_STYLE_BORDER_WIDTH_LEFT);
- else if (gs_token_compare_text(&token, "border_right")) SET_VAL(border_width[1], int16_t, GS_GUI_STYLE_BORDER_WIDTH_RIGHT);
- else if (gs_token_compare_text(&token, "border_top")) SET_VAL(border_width[2], int16_t, GS_GUI_STYLE_BORDER_WIDTH_TOP);
- else if (gs_token_compare_text(&token, "border_bottom")) SET_VAL(border_width[3], int16_t, GS_GUI_STYLE_BORDER_WIDTH_BOTTOM);
- else if (gs_token_compare_text(&token, "shadow")) SET_VAL2(shadow_x, shadow_y, int16_t, GS_GUI_STYLE_SHADOW_X, GS_GUI_STYLE_SHADOW_Y);
- return true;
- }
- bool _gs_gui_style_sheet_parse_attribute_color(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
- uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
- {
- // Name of color attribute
- gs_token_t token = gs_lexer_current_token(lex);
- // gs_token_debug_print(&token);
- gs_gui_style_list_t* idsl = NULL;
- if (id_tag)
- {
- const uint64_t idhash = id_tag;
- if (!gs_hash_table_exists(ss->cid_styles, idhash))
- {
- gs_gui_style_list_t sl = gs_default_val();
- gs_hash_table_insert(ss->cid_styles, idhash, sl);
- }
- idsl = gs_hash_table_getp(ss->cid_styles, idhash);
- }
- gs_gui_style_element_type type = (gs_gui_style_element_type)0x00;
- int32_t color = GS_GUI_COLOR_BACKGROUND;
- if (gs_token_compare_text(&token, "color_background")) {color = GS_GUI_COLOR_BACKGROUND; type = GS_GUI_STYLE_COLOR_BACKGROUND;}
- else if (gs_token_compare_text(&token, "color_border")) {color = GS_GUI_COLOR_BORDER; type = GS_GUI_STYLE_COLOR_BORDER;}
- else if (gs_token_compare_text(&token, "color_shadow")) {color = GS_GUI_COLOR_SHADOW; type = GS_GUI_STYLE_COLOR_SHADOW;}
- else if (gs_token_compare_text(&token, "color_content")) {color = GS_GUI_COLOR_CONTENT; type = GS_GUI_STYLE_COLOR_CONTENT;}
- else if (gs_token_compare_text(&token, "color_content_background")) {color = GS_GUI_COLOR_CONTENT_BACKGROUND; type = GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND;}
- else if (gs_token_compare_text(&token, "color_content_border")) {color = GS_GUI_COLOR_CONTENT_BORDER; type = GS_GUI_STYLE_COLOR_CONTENT_BORDER;}
- else if (gs_token_compare_text(&token, "color_content_shadow")) {color = GS_GUI_COLOR_CONTENT_SHADOW; type = GS_GUI_STYLE_COLOR_CONTENT_SHADOW;}
- else
- {
- gs_log_warning("Unidentified attribute: %.*s.", token.len, token.text);
- return false;
- }
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_COLON)
- {
- gs_log_warning("Unidentified token. (Expected colon after attribute type).");
- gs_token_debug_print(&token);
- return false;
- }
- gs_gui_style_element_t se = gs_default_val();
- se.type = type;
- token = gs_lexer_next_token(lex);
- // gs_println("Parsing color: %.*s", token.len, token.text);
- if (gs_token_compare_text(&token, "rgba") ||
- gs_token_compare_text(&token, "rgb")
- )
- {
- int32_t i = 0;
- while (gs_lexer_can_lex(lex) && token.type != GS_TOKEN_RPAREN)
- {
- token = gs_lexer_next_token(lex);
- switch (token.type)
- {
- case GS_TOKEN_NUMBER:
- {
- if (i < 4)
- {
- gs_snprintfc(TMP, 10, "%.*s", token.len, token.text);
- uint8_t val = (uint8_t)atoi(TMP);
- if (idsl)
- {
- se.color.rgba[i] = val;
- }
- else
- {
- #define SET_COLOR(STATE)\
- do {\
- switch (i)\
- {\
- case 0: ss->styles[elementid][STATE].colors[color].r = val; break;\
- case 1: ss->styles[elementid][STATE].colors[color].g = val; break;\
- case 2: ss->styles[elementid][STATE].colors[color].b = val; break;\
- case 3: ss->styles[elementid][STATE].colors[color].a = val; break;\
- }\
- } while (0)
- switch (state)
- {
- case GS_GUI_ELEMENT_STATE_HOVER:
- case GS_GUI_ELEMENT_STATE_FOCUS:
- {
- SET_COLOR(state);
- } break;
- case GS_GUI_ELEMENT_STATE_DEFAULT:
- {
- for (uint32_t s = 0; s < 3; ++s) {
- SET_COLOR(s);
- }
- } break;
- }
- }
- i++;
- // gs_token_debug_print(&token);
- }
- } break;
- }
- }
- // Set alpha, if not provided
- if (i < 4)
- {
- ss->styles[elementid][state].colors[color].a = 255;
- se.color.a = 255;
- }
- // Push style element
- if (idsl)
- {
- switch (state)
- {
- case GS_GUI_ELEMENT_STATE_DEFAULT:
- {
- for (uint32_t s = 0; s < 3; ++s) {
- gs_dyn_array_push(idsl->styles[s], se);
- }
- } break;
- default:
- {
- gs_dyn_array_push(idsl->styles[state], se);
- } break;
- }
- }
- }
- else if (gs_token_compare_text(&token, "$"))
- {
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_IDENTIFIER)
- {
- gs_log_warning("Unidentified symbol found: %.*s. Expecting identifier for variable name.", token.len, token.text);
- return false;
- }
- gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);
- uint64_t hash = gs_hash_str64(TMP);
- gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ? gs_hash_table_getp(variables->variables, hash) : NULL;
- if (var) {
- se.color = var->val.color;
- switch (state)
- {
- default:
- {
- if (idsl) gs_dyn_array_push(idsl->styles[state], se);
- else ss->styles[elementid][state].colors[color] = var->val.color;
- } break;
- case GS_GUI_ELEMENT_STATE_DEFAULT:
- {
- for (uint32_t s = 0; s < 3; ++s) {
- if (idsl) gs_dyn_array_push(idsl->styles[s], se);
- else ss->styles[elementid][s].colors[color] = var->val.color;
- }
- } break;
- }
- }
- else
- {
- gs_log_warning("Variable not found: %.*s.", token.len, token.text);
- }
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_SEMICOLON)
- {
- gs_log_warning("Syntax error. Expecting semicolon, found: %.*s.", token.len, token.text);
- return false;
- }
- }
- else
- {
- gs_log_warning("Unidentified color type found: %.*s. (Expect either 'rgba' or 'rgb').", token.len, token.text);
- return false;
- }
-
- return true;
- }
- bool _gs_gui_style_sheet_parse_attribute(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
- uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
- {
- // Name of attribute
- gs_token_t token = gs_lexer_current_token(lex);
- if (gs_token_compare_text(&token, "color_background") ||
- gs_token_compare_text(&token, "color_border") ||
- gs_token_compare_text(&token, "color_shadow") ||
- gs_token_compare_text(&token, "color_content") ||
- gs_token_compare_text(&token, "color_content_background") ||
- gs_token_compare_text(&token, "color_content_border") ||
- gs_token_compare_text(&token, "color_content_shadow")
- )
- {
- if (!_gs_gui_style_sheet_parse_attribute_color(ctx, lex, ss, id_tag, elementid, state, variables))
- {
- gs_log_warning("Failed to parse color attribute.");
- return false;
- }
- }
- else if (gs_token_compare_text(&token, "width") ||
- gs_token_compare_text(&token, "height") ||
- gs_token_compare_text(&token, "padding") ||
- gs_token_compare_text(&token, "padding_left") ||
- gs_token_compare_text(&token, "padding_right") ||
- gs_token_compare_text(&token, "padding_top") ||
- gs_token_compare_text(&token, "padding_bottom") ||
- gs_token_compare_text(&token, "margin") ||
- gs_token_compare_text(&token, "margin_left") ||
- gs_token_compare_text(&token, "margin_right") ||
- gs_token_compare_text(&token, "margin_top") ||
- gs_token_compare_text(&token, "margin_bottom") ||
- gs_token_compare_text(&token, "border") ||
- gs_token_compare_text(&token, "border_left") ||
- gs_token_compare_text(&token, "border_right") ||
- gs_token_compare_text(&token, "border_top") ||
- gs_token_compare_text(&token, "border_bottom") ||
- gs_token_compare_text(&token, "shadow")
- )
- {
- if (!_gs_gui_style_sheet_parse_attribute_val(ctx, lex, ss, id_tag, elementid, state, variables))
- {
- gs_log_warning("Failed to parse value attribute.");
- return false;
- }
- }
- else if (gs_token_compare_text(&token, "justify_content") ||
- gs_token_compare_text(&token, "align_content")
- )
- {
- if (!_gs_gui_style_sheet_parse_attribute_enum(ctx, lex, ss, id_tag, elementid, state, variables))
- {
- gs_log_warning("Failed to parse enum attribute.");
- return false;
- }
- }
- else if (gs_token_compare_text(&token, "font"))
- {
- if (!_gs_gui_style_sheet_parse_attribute_font(ctx, lex, ss, id_tag, elementid, state, variables))
- {
- gs_log_warning("Failed to parse font attribute.");
- return false;
- }
- }
- else if (gs_token_compare_text(&token, "transition"))
- {
- if (!_gs_gui_style_sheet_parse_attribute_transition(ctx, lex, ss, id_tag, elementid, state, variables))
- {
- gs_log_warning("Failed to parse transition attribute.");
- return false;
- }
- }
- else
- {
- gs_log_warning("Unidentified attribute: %.*s.", token.len, token.text);
- return false;
- }
- return true;
- }
- bool _gs_gui_style_sheet_parse_element(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
- int32_t elementid, gs_gui_ss_variables_t* variables)
- {
- int32_t state = 0x00;
- int32_t bc = 0;
- gs_token_t token = gs_lexer_next_token(lex);
- if (token.type == GS_TOKEN_COLON)
- {
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_IDENTIFIER)
- {
- gs_log_warning("Unidentified Token. (Expected identifier after colon).");
- gs_token_debug_print(&token);
- return false;
- }
- if (gs_token_compare_text(&token, "focus")) state = GS_GUI_ELEMENT_STATE_FOCUS;
- else if (gs_token_compare_text(&token, "hover")) state = GS_GUI_ELEMENT_STATE_HOVER;
- else
- {
- gs_log_warning("Unidentified element state provided: %.*s", token.len, token.text);
- return false;
- }
- // Get rbrace
- token = gs_lexer_next_token(lex);
- }
- if (token.type != GS_TOKEN_LBRACE)
- {
- gs_log_warning("Unidentified token. (Expected brace after element declaration).");
- gs_token_debug_print(&token);
- return false;
- }
- bc = 1;
- while (gs_lexer_can_lex(lex) && bc)
- {
- token = gs_lexer_next_token(lex);
- switch (token.type)
- {
- case GS_TOKEN_LBRACE: bc++; break;
- case GS_TOKEN_RBRACE: bc--; break;
- case GS_TOKEN_IDENTIFIER:
- {
- // gs_println("Parsing attribute: %.*s", token.len, token.text);
- if (!_gs_gui_style_sheet_parse_attribute(ctx, lex, ss, 0, elementid, state, variables))
- {
- gs_log_warning("Unable to parse attribute");
- return false;
- }
- } break;
- }
- }
- return true;
- }
- bool _gs_gui_style_sheet_parse_cid_tag(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
- const uint64_t cid_tag, gs_gui_ss_variables_t* variables)
- {
- int32_t state = 0x00;
- int32_t bc = 0;
- gs_token_t token = gs_lexer_next_token(lex);
- if (token.type == GS_TOKEN_COLON)
- {
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_IDENTIFIER)
- {
- gs_log_warning("Unidentified Token. (Expected identifier after colon).");
- gs_token_debug_print(&token);
- return false;
- }
- if (gs_token_compare_text(&token, "focus")) state = GS_GUI_ELEMENT_STATE_FOCUS;
- else if (gs_token_compare_text(&token, "hover")) state = GS_GUI_ELEMENT_STATE_HOVER;
- else
- {
- gs_log_warning("Unidentified element state provided: %.*s", token.len, token.text);
- return false;
- }
- // Get rbrace
- token = gs_lexer_next_token(lex);
- }
- if (token.type != GS_TOKEN_LBRACE)
- {
- gs_log_warning("Unidentified token. (Expected brace after element declaration).");
- gs_token_debug_print(&token);
- return false;
- }
- bc = 1;
- while (gs_lexer_can_lex(lex) && bc)
- {
- token = gs_lexer_next_token(lex);
- switch (token.type)
- {
- case GS_TOKEN_LBRACE: bc++; break;
- case GS_TOKEN_RBRACE: bc--; break;
- case GS_TOKEN_IDENTIFIER:
- {
- // gs_println("Parsing attribute: %.*s", token.len, token.text);
- if (!_gs_gui_style_sheet_parse_attribute(ctx, lex, ss, cid_tag, GS_GUI_ELEMENT_COUNT, state, variables))
- {
- gs_log_warning("Unable to parse attribute");
- return false;
- }
- } break;
- }
- }
- return true;
- }
- GS_API_DECL gs_gui_style_sheet_t gs_gui_style_sheet_load_from_file(gs_gui_context_t* ctx, const char* file_path)
- {
- // Generate new style sheet based on default element styles
- gs_gui_style_sheet_t ss = gs_default_val();
- bool success = true;
- size_t sz = 0;
- char* fd = gs_platform_read_file_contents(file_path, "rb", &sz);
- if (!fd) {
- gs_log_warning("Cannot load file: %s", file_path);
- return ss;
- }
- ss = gs_gui_style_sheet_load_from_memory(ctx, fd, sz, &success);
- if (success) {
- gs_log_success("Successfully loaded style sheet %s.", file_path);
- }
- else {
- gs_log_warning("Failed to loaded style sheet %s.", file_path);
- }
- gs_free(fd);
- return ss;
- }
- static bool _gs_gui_style_sheet_parse_variable(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
- char* name_buf, size_t sz, gs_gui_ss_var_def_t* out)
- {
- // Get next token, needs to be identifier
- gs_token_t token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_IDENTIFIER)
- {
- gs_log_warning("Unidentified token. (Expected variable name after percent sign).");
- gs_token_debug_print(&token);
- return false;
- }
- // Copy name of variable
- memcpy(name_buf, token.text, gs_min(token.len, sz));
- // Expect colon
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_COLON)
- {
- gs_log_warning("Syntax error. (Expected colon name after variable name).");
- gs_token_debug_print(&token);
- return false;
- }
- // Now to get variable
- token = gs_lexer_next_token(lex);
- while (gs_lexer_can_lex(lex) && token.type != GS_TOKEN_SEMICOLON)
- {
- switch (token.type)
- {
- case GS_TOKEN_IDENTIFIER:
- {
- if (gs_token_compare_text(&token, "rgb"))
- {
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_LPAREN)
- {
- gs_log_warning("rgb: missing paren (", token.len, token.text);
- gs_token_debug_print(&token);
- return false;
- }
- out->type = GS_GUI_SS_DEF_COLOR;
- for (uint32_t i = 0; i < 3; ++i)
- {
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_NUMBER)
- {
- gs_log_warning("rgb expects numbers", token.len, token.text);
- gs_token_debug_print(&token);
- return false;
- }
- gs_snprintfc(VAL, 32, "%.*s", token.len, token.text);\
- uint8_t v = (uint8_t)atoi(VAL);\
- out->val.color.rgba[i] = v;
- }
- out->val.color.rgba[3] = 255;
- }
- else if (gs_token_compare_text(&token, "rgba"))
- {
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_LPAREN)
- {
- gs_log_warning("rgb: missing paren (", token.len, token.text);
- gs_token_debug_print(&token);
- return false;
- }
- out->type = GS_GUI_SS_DEF_COLOR;
- for (uint32_t i = 0; i < 4; ++i)
- {
- token = gs_lexer_next_token(lex);
- if (token.type != GS_TOKEN_NUMBER)
- {
- gs_log_warning("rgb expects numbers", token.len, token.text);
- gs_token_debug_print(&token);
- return false;
- }
- gs_snprintfc(VAL, 32, "%.*s", token.len, token.text);\
- uint8_t v = (uint8_t)atoi(VAL);\
- out->val.color.rgba[i] = v;
- }
- }
- else if (gs_token_compare_text(&token, "center"))
- {
- out->type = GS_GUI_SS_DEF_ENUM;
- out->val.number = (int32_t)GS_GUI_JUSTIFY_CENTER;
- }
- else if (gs_token_compare_text(&token, "start"))
- {
- out->type = GS_GUI_SS_DEF_ENUM;
- out->val.number = (int32_t)GS_GUI_JUSTIFY_START;
- }
- else if (gs_token_compare_text(&token, "end"))
- {
- out->type = GS_GUI_SS_DEF_ENUM;
- out->val.number = (int32_t)GS_GUI_JUSTIFY_END;
- }
- else
- {
- gs_log_warning("Variable value unknown: %.*s", token.len, token.text);
- gs_token_debug_print(&token);
- return false;
- }
- } break;
- case GS_TOKEN_NUMBER:
- {
- gs_snprintfc(VAL, 32, "%.*s", token.len, token.text);\
- int32_t v = (int32_t)atoi(VAL);\
- out->type = GS_GUI_SS_DEF_NUMBER;
- out->val.number = v;
- } break;
- case GS_TOKEN_STRING:
- {
- gs_snprintfc(VAL, 64, "%.*s", token.len - 2, token.text + 1);\
- out->type = GS_GUI_SS_DEF_STR;
- memcpy(out->val.str, VAL, 64);
- }
- }
- token = gs_lexer_next_token(lex);
- }
- return true;
- }
- // Going to require a lot of parsing
- GS_API_DECL gs_gui_style_sheet_t gs_gui_style_sheet_load_from_memory(gs_gui_context_t* ctx, const char* fd, size_t sz, bool* sp)
- {
- // Generate new style sheet based on default element styles
- gs_gui_style_sheet_t ss = gs_default_val();
- bool success = true;
- gs_gui_ss_variables_t variables = gs_default_val();
- // Copy all default styles
- GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_CONTAINER);
- GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_LABEL);
- GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_TEXT);
- GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_PANEL);
- GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_INPUT);
- GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_BUTTON);
- GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_SCROLL);
- GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_IMAGE);
- #define PARSE_ELEMENT(TYPE, TYPESTR)\
- do {\
- if (!_gs_gui_style_sheet_parse_element(ctx, &lex, &ss, TYPE, &variables))\
- {\
- gs_log_warning("Failed to parse element: %s", TYPESTR);\
- success = false;\
- break;\
- }\
- } while (0)
- // Parse style sheet for styles
- gs_lexer_t lex = gs_lexer_c_ctor(fd);
- while (success && gs_lexer_c_can_lex(&lex))
- {
- gs_token_t token = lex.next_token(&lex);
- switch (token.type)
- {
- case GS_TOKEN_IDENTIFIER:
- {
- if (gs_token_compare_text(&token, "button")) PARSE_ELEMENT(GS_GUI_ELEMENT_BUTTON, "button");
- else if (gs_token_compare_text(&token, "text")) PARSE_ELEMENT(GS_GUI_ELEMENT_TEXT, "text");
- else if (gs_token_compare_text(&token, "label")) PARSE_ELEMENT(GS_GUI_ELEMENT_LABEL, "label");
- else if (gs_token_compare_text(&token, "image")) PARSE_ELEMENT(GS_GUI_ELEMENT_IMAGE, "image");
- else if (gs_token_compare_text(&token, "scroll")) PARSE_ELEMENT(GS_GUI_ELEMENT_SCROLL, "scroll");
- else if (gs_token_compare_text(&token, "panel")) PARSE_ELEMENT(GS_GUI_ELEMENT_PANEL, "panel");
- else if (gs_token_compare_text(&token, "container")) PARSE_ELEMENT(GS_GUI_ELEMENT_CONTAINER, "container");
- else if (gs_token_compare_text(&token, "input")) PARSE_ELEMENT(GS_GUI_ELEMENT_INPUT, "input");
- else
- {
- gs_log_warning("Unidentified token. (Invalid element type found).");
- gs_token_debug_print(&token);
- break;
- }
- } break;
- case GS_TOKEN_PERIOD:
- {
- // Do single class for now
- gs_token_t cls_tag = gs_lexer_next_token(&lex);
- char CLS_TAG[256] = gs_default_val();
- CLS_TAG[0] = '.';
- memcpy(CLS_TAG + 1, cls_tag.text, cls_tag.len);
- uint64_t cls_hash = gs_hash_str64(CLS_TAG);
- if (!_gs_gui_style_sheet_parse_cid_tag(ctx, &lex, &ss, cls_hash, &variables))
- {
- gs_log_warning("Failed to parse id tag: %s", CLS_TAG);
- success = false;
- break;
- }
- } break;
- case GS_TOKEN_HASH:
- {
- gs_token_t id_tag = gs_lexer_next_token(&lex);
- char ID_TAG[256] = gs_default_val();
- ID_TAG[0] = '#';
- memcpy(ID_TAG + 1, id_tag.text, id_tag.len);
- uint64_t id_hash = gs_hash_str64(ID_TAG);
- if (!_gs_gui_style_sheet_parse_cid_tag(ctx, &lex, &ss, id_hash, &variables))
- {
- gs_log_warning("Failed to parse id tag: %s", ID_TAG);
- success = false;
- break;
- }
- } break;
- case GS_TOKEN_ASTERISK:
- {
- // Save token
- gs_token_t token = gs_lexer_next_token(&lex); gs_lexer_set_token(&lex, token);
- PARSE_ELEMENT(GS_GUI_ELEMENT_CONTAINER, "* (container)"); gs_lexer_set_token(&lex, token);
- PARSE_ELEMENT(GS_GUI_ELEMENT_TEXT, "* (text)"); gs_lexer_set_token(&lex, token);
- PARSE_ELEMENT(GS_GUI_ELEMENT_LABEL, "* (label)"); gs_lexer_set_token(&lex, token);
- PARSE_ELEMENT(GS_GUI_ELEMENT_IMAGE, "* (image)"); gs_lexer_set_token(&lex, token);
- PARSE_ELEMENT(GS_GUI_ELEMENT_BUTTON, "* (button)"); gs_lexer_set_token(&lex, token);
- PARSE_ELEMENT(GS_GUI_ELEMENT_PANEL, "* (panel)"); gs_lexer_set_token(&lex, token);
- PARSE_ELEMENT(GS_GUI_ELEMENT_INPUT, "* (input)"); gs_lexer_set_token(&lex, token);
- PARSE_ELEMENT(GS_GUI_ELEMENT_SCROLL, "* (scroll)");
- } break;
- case GS_TOKEN_DOLLAR:
- {
- gs_gui_ss_var_def_t variable = gs_default_val();
- char variable_name[256] = gs_default_val();
- if (!_gs_gui_style_sheet_parse_variable(ctx, &lex, &ss, variable_name, sizeof(variable_name), &variable))
- {
- gs_log_warning("Failed to parse variable: %s", variable_name);
- success = false;
- break;
- }
- else
- {
- gs_hash_table_insert(variables.variables, gs_hash_str64(variable_name), variable);
- }
- /*
- typedef enum
- {
- GS_GUI_SS_DEF_VALUE = 0x00,
- GS_GUI_SS_DEF_COLOR,
- GS_GUI_SS_DEF_STR
- } gs_gui_ss_var_def_type;
- typedef struct
- {
- gs_gui_ss_var_def_type type;
- union {
- int32_t value;
- gs_color_t color;
- char str[64];
- } val;
- } gs_gui_ss_var_def_t;
- */
- } break;
- }
- }
- if (!sp)
- {
- if (success) {
- gs_log_success("Successfully loaded style sheet from memory.");
- }
- else {
- gs_log_warning("Failed to loaded style sheet from memory.");
- }
- }
- else
- {
- *sp = success;
- }
- if (variables.variables) gs_hash_table_free(variables.variables);
- return ss;
- }
- #endif // GS_GUI_IMPL
- #endif // GS_GUI_H
|