gs_gui.h 420 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328
  1. /*==============================================================================================================
  2. * Copyright (c) 2020 John Jackson
  3. * File: gs_gui.h
  4. * Github: https://github.com/MrFrenik/gunslinger
  5. * All Rights Reserved
  6. * MIT License
  7. * May all those that this source may reach be blessed by the LORD and find peace and joy in life.
  8. * Everyone who drinks of this water will be thirsty again; but whoever drinks of the water
  9. * that I will give him shall never thirst; John 4:13-14
  10. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  11. * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
  12. * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
  13. * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  14. * The above copyright, blessing, biblical verse, notice and this permission notice shall be included in all
  15. * copies or substantial portions of the Software.
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  17. * TO THE WARRANTIES OF MECHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  18. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. * IN THE SOFTWARE.
  21. =================================================================================================================*/
  22. /*
  23. USAGE: (IMPORTANT)
  24. =================================================================================================================
  25. Before including, define the gunslinger immediate gui implementation like this:
  26. #define GS_GUI_IMPL
  27. in EXACTLY ONE C or C++ file that includes this header, BEFORE the
  28. include, like this:
  29. #define GS_GUI_IMPL
  30. #include "gs_gui.h"
  31. All other files should just #include "gs_gui.h" without the #define.
  32. 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:
  33. #define GS_IMPL
  34. #include "gs.h"
  35. #define GS_IMMEDIATE_DRAW_IMPL
  36. #include "gs_idraw.h"
  37. #define GS_GUI_IMPL
  38. #include "gs_gui.h"
  39. ================================================================================================================
  40. */
  41. #ifndef GS_GUI_H
  42. #define GS_GUI_H
  43. // Directly modified from microui implementation: https://github.com/rxi/microui
  44. #define GS_GUI_SPLIT_SIZE 2.f
  45. #define GS_GUI_MAX_CNT 48
  46. #define GS_GUI_COMMANDLIST_SIZE (256 * 1024)
  47. #define GS_GUI_ROOTLIST_SIZE 32
  48. #define GS_GUI_CONTAINERSTACK_SIZE 32
  49. #define GS_GUI_CLIPSTACK_SIZE 32
  50. #define GS_GUI_IDSTACK_SIZE 32
  51. #define GS_GUI_LAYOUTSTACK_SIZE 16
  52. #define GS_GUI_CONTAINERPOOL_SIZE 48
  53. #define GS_GUI_TREENODEPOOL_SIZE 48
  54. #define GS_GUI_GS_GUI_SPLIT_SIZE 32
  55. #define GS_GUI_GS_GUI_TAB_SIZE 32
  56. #define GS_GUI_MAX_WIDTHS 16
  57. #define GS_GUI_REAL float
  58. #define GS_GUI_REAL_FMT "%.3g"
  59. #define GS_GUI_SLIDER_FMT "%.2f"
  60. #define GS_GUI_MAX_FMT 127
  61. #define GS_GUI_TAB_ITEM_MAX 24
  62. #define GS_GUI_CLS_SELECTOR_MAX 4
  63. #define gs_gui_stack(T, n) struct {int32_t idx; T items[n];}
  64. enum {
  65. GS_GUI_CLIP_PART = 1,
  66. GS_GUI_CLIP_ALL
  67. };
  68. enum {
  69. GS_GUI_COMMAND_JUMP = 1,
  70. GS_GUI_COMMAND_CLIP,
  71. GS_GUI_COMMAND_SHAPE,
  72. GS_GUI_COMMAND_TEXT,
  73. GS_GUI_COMMAND_ICON,
  74. GS_GUI_COMMAND_IMAGE,
  75. GS_GUI_COMMAND_CUSTOM,
  76. GS_GUI_COMMAND_PIPELINE,
  77. GS_GUI_COMMAND_UNIFORMS,
  78. GS_GUI_COMMAND_MAX
  79. };
  80. enum {
  81. GS_GUI_SHAPE_RECT = 1,
  82. GS_GUI_SHAPE_CIRCLE,
  83. GS_GUI_SHAPE_TRIANGLE,
  84. GS_GUI_SHAPE_LINE
  85. };
  86. enum {
  87. GS_GUI_TRANSFORM_WORLD = 0x00,
  88. GS_GUI_TRANSFORM_LOCAL
  89. };
  90. enum {
  91. GS_GUI_GIZMO_TRANSLATE = 0x00,
  92. GS_GUI_GIZMO_ROTATE,
  93. GS_GUI_GIZMO_SCALE
  94. };
  95. enum {
  96. GS_GUI_COLOR_BACKGROUND = 0x00,
  97. GS_GUI_COLOR_CONTENT,
  98. GS_GUI_COLOR_BORDER,
  99. GS_GUI_COLOR_SHADOW,
  100. GS_GUI_COLOR_CONTENT_BACKGROUND,
  101. GS_GUI_COLOR_CONTENT_SHADOW,
  102. GS_GUI_COLOR_CONTENT_BORDER,
  103. GS_GUI_COLOR_MAX
  104. };
  105. enum {
  106. GS_GUI_ICON_CLOSE = 1,
  107. GS_GUI_ICON_CHECK,
  108. GS_GUI_ICON_COLLAPSED,
  109. GS_GUI_ICON_EXPANDED,
  110. GS_GUI_ICON_MAX
  111. };
  112. enum {
  113. GS_GUI_RES_ACTIVE = (1 << 0),
  114. GS_GUI_RES_SUBMIT = (1 << 1),
  115. GS_GUI_RES_CHANGE = (1 << 2)
  116. };
  117. typedef enum gs_gui_alt_drag_mode_type {
  118. GS_GUI_ALT_DRAG_QUAD = 0x00, // Quadrants
  119. GS_GUI_ALT_DRAG_NINE, // Nine splice the window
  120. GS_GUI_ALT_DRAG_SINGLE // Single window drag (controls the width/height, leaving x/y position of window in tact)
  121. } gs_gui_alt_drag_mode_type;
  122. enum {
  123. GS_GUI_OPT_NOSTYLESHADOW = (1ULL << 0),
  124. GS_GUI_OPT_NOSTYLEBORDER = (1ULL << 1),
  125. GS_GUI_OPT_NOINTERACT = (1ULL << 2),
  126. GS_GUI_OPT_NOFRAME = (1ULL << 3),
  127. GS_GUI_OPT_NORESIZE = (1ULL << 4),
  128. GS_GUI_OPT_NOSCROLL = (1ULL << 5),
  129. GS_GUI_OPT_NOCLOSE = (1ULL << 6),
  130. GS_GUI_OPT_NOTITLE = (1ULL << 7),
  131. GS_GUI_OPT_HOLDFOCUS = (1ULL << 8),
  132. GS_GUI_OPT_AUTOSIZE = (1ULL << 9),
  133. GS_GUI_OPT_POPUP = (1ULL << 10),
  134. GS_GUI_OPT_CLOSED = (1ULL << 11),
  135. GS_GUI_OPT_EXPANDED = (1ULL << 12),
  136. GS_GUI_OPT_NOHOVER = (1ULL << 13),
  137. GS_GUI_OPT_FORCESETRECT = (1ULL << 14),
  138. GS_GUI_OPT_NOFOCUS = (1ULL << 15),
  139. GS_GUI_OPT_FORCEFOCUS = (1ULL << 16),
  140. GS_GUI_OPT_NOMOVE = (1ULL << 17),
  141. GS_GUI_OPT_NOCLIP = (1ULL << 18),
  142. GS_GUI_OPT_NODOCK = (1ULL << 19),
  143. GS_GUI_OPT_FULLSCREEN = (1ULL << 20),
  144. GS_GUI_OPT_DOCKSPACE = (1ULL << 21),
  145. GS_GUI_OPT_NOBRINGTOFRONT = (1ULL << 22),
  146. GS_GUI_OPT_LEFTCLICKONLY = (1ULL << 23),
  147. GS_GUI_OPT_NOSWITCHSTATE = (1ULL << 24),
  148. GS_GUI_OPT_NOBORDER = (1ULL << 25),
  149. GS_GUI_OPT_ISCONTENT = (1ULL << 26),
  150. GS_GUI_OPT_NOCARET = (1ULL << 27),
  151. GS_GUI_OPT_NOSCROLLHORIZONTAL = (1ULL << 28),
  152. GS_GUI_OPT_NOSCROLLVERTICAL = (1ULL << 29),
  153. GS_GUI_OPT_NOSTYLEBACKGROUND = (1ULL << 30),
  154. GS_GUI_OPT_PARSEIDTAGONLY = (1ULL << 31),
  155. };
  156. enum {
  157. GS_GUI_MOUSE_LEFT = (1 << 0),
  158. GS_GUI_MOUSE_RIGHT = (1 << 1),
  159. GS_GUI_MOUSE_MIDDLE = (1 << 2)
  160. };
  161. enum {
  162. GS_GUI_KEY_SHIFT = (1 << 0),
  163. GS_GUI_KEY_CTRL = (1 << 1),
  164. GS_GUI_KEY_ALT = (1 << 2),
  165. GS_GUI_KEY_BACKSPACE = (1 << 3),
  166. GS_GUI_KEY_RETURN = (1 << 4)
  167. };
  168. #define GS_GUI_OPT_NOSTYLING (GS_GUI_OPT_NOSTYLEBORDER | GS_GUI_OPT_NOSTYLEBACKGROUND | GS_GUI_OPT_NOSTYLESHADOW)
  169. typedef struct gs_gui_context_t gs_gui_context_t;
  170. typedef uint32_t gs_gui_id;
  171. typedef GS_GUI_REAL gs_gui_real;
  172. // Shapes
  173. typedef struct {float x, y, w, h;} gs_gui_rect_t;
  174. typedef struct {float radius; gs_vec2 center;} gs_gui_circle_t;
  175. typedef struct {gs_vec2 points[3];} gs_gui_triangle_t;
  176. typedef struct {gs_vec2 start; gs_vec2 end;} gs_gui_line_t;
  177. typedef struct {gs_gui_id id; int32_t last_update;} gs_gui_pool_item_t;
  178. typedef struct
  179. {
  180. int32_t type, size;
  181. } gs_gui_basecommand_t;
  182. typedef struct
  183. {
  184. gs_gui_basecommand_t base;
  185. void *dst;
  186. } gs_gui_jumpcommand_t;
  187. typedef struct
  188. {
  189. gs_gui_basecommand_t base;
  190. gs_gui_rect_t rect;
  191. } gs_gui_clipcommand_t;
  192. typedef struct
  193. {
  194. gs_gui_basecommand_t base;
  195. gs_asset_font_t* font;
  196. gs_vec2 pos;
  197. gs_color_t color;
  198. char str[1];
  199. } gs_gui_textcommand_t;
  200. typedef struct
  201. {
  202. gs_gui_basecommand_t base;
  203. gs_handle(gs_graphics_pipeline_t) pipeline;
  204. gsi_layout_type layout_type;
  205. void* layout;
  206. size_t layout_sz;
  207. } gs_gui_pipelinecommand_t;
  208. typedef struct
  209. {
  210. gs_gui_basecommand_t base;
  211. void* data;
  212. size_t sz;
  213. } gs_gui_binduniformscommand_t;
  214. typedef struct
  215. {
  216. gs_gui_basecommand_t base;
  217. gs_gui_rect_t rect;
  218. gs_handle(gs_graphics_texture_t) hndl;
  219. gs_vec4 uvs;
  220. gs_color_t color;
  221. } gs_gui_imagecommand_t;
  222. struct gs_gui_customcommand_t;
  223. // Draw Callback
  224. typedef void (* gs_gui_draw_callback_t)(gs_gui_context_t* ctx, struct gs_gui_customcommand_t* cmd);
  225. typedef struct gs_gui_customcommand_t
  226. {
  227. gs_gui_basecommand_t base;
  228. gs_gui_rect_t clip;
  229. gs_gui_rect_t viewport;
  230. gs_gui_id hash;
  231. gs_gui_id hover;
  232. gs_gui_id focus;
  233. gs_gui_draw_callback_t cb;
  234. void* data;
  235. size_t sz;
  236. } gs_gui_customcommand_t;
  237. typedef struct
  238. {
  239. gs_gui_basecommand_t base;
  240. uint32_t type;
  241. union
  242. {
  243. gs_gui_rect_t rect;
  244. gs_gui_circle_t circle;
  245. gs_gui_triangle_t triangle;
  246. gs_gui_line_t line;
  247. };
  248. gs_color_t color;
  249. } gs_gui_shapecommand_t;
  250. // NOTE(john): This is wasteful, given how I'm pushing into the byte buffer anyway for heterogenous types
  251. typedef union
  252. {
  253. int32_t type;
  254. gs_gui_basecommand_t base;
  255. gs_gui_jumpcommand_t jump;
  256. gs_gui_clipcommand_t clip;
  257. gs_gui_shapecommand_t shape;
  258. gs_gui_textcommand_t text;
  259. gs_gui_imagecommand_t image;
  260. gs_gui_customcommand_t custom;
  261. gs_gui_pipelinecommand_t pipeline;
  262. gs_gui_binduniformscommand_t uniforms;
  263. } gs_gui_command_t;
  264. struct gs_gui_context_t;
  265. typedef void (* gs_gui_on_draw_button_callback)(struct gs_gui_context_t* ctx, gs_gui_rect_t rect,
  266. gs_gui_id id, bool hovered, bool focused, uint64_t opt, const char* label, int32_t icon);
  267. typedef enum {
  268. GS_GUI_LAYOUT_ANCHOR_TOPLEFT = 0x00,
  269. GS_GUI_LAYOUT_ANCHOR_TOPCENTER,
  270. GS_GUI_LAYOUT_ANCHOR_TOPRIGHT,
  271. GS_GUI_LAYOUT_ANCHOR_LEFT,
  272. GS_GUI_LAYOUT_ANCHOR_CENTER,
  273. GS_GUI_LAYOUT_ANCHOR_RIGHT,
  274. GS_GUI_LAYOUT_ANCHOR_BOTTOMLEFT,
  275. GS_GUI_LAYOUT_ANCHOR_BOTTOMCENTER,
  276. GS_GUI_LAYOUT_ANCHOR_BOTTOMRIGHT
  277. } gs_gui_layout_anchor_type;
  278. typedef enum {
  279. GS_GUI_ALIGN_START = 0x00,
  280. GS_GUI_ALIGN_CENTER,
  281. GS_GUI_ALIGN_END
  282. } gs_gui_align_type;
  283. typedef enum {
  284. GS_GUI_JUSTIFY_START = 0x00,
  285. GS_GUI_JUSTIFY_CENTER,
  286. GS_GUI_JUSTIFY_END
  287. } gs_gui_justification_type;
  288. typedef enum {
  289. GS_GUI_DIRECTION_COLUMN = 0x00,
  290. GS_GUI_DIRECTION_ROW,
  291. GS_GUI_DIRECTION_COLUMN_REVERSE,
  292. GS_GUI_DIRECTION_ROW_REVERSE
  293. } gs_gui_direction;
  294. typedef struct gs_gui_layout_t
  295. {
  296. gs_gui_rect_t body;
  297. gs_gui_rect_t next;
  298. gs_vec2 position;
  299. gs_vec2 size;
  300. gs_vec2 max;
  301. int32_t padding[4];
  302. int32_t widths[GS_GUI_MAX_WIDTHS];
  303. int32_t items;
  304. int32_t item_index;
  305. int32_t next_row;
  306. int32_t next_type;
  307. int32_t indent;
  308. // flex direction / justification / alignment
  309. int32_t direction;
  310. int32_t justify_content;
  311. int32_t align_content;
  312. } gs_gui_layout_t;
  313. // Forward decl.
  314. struct gs_gui_container_t;
  315. typedef enum gs_gui_split_node_type
  316. {
  317. GS_GUI_SPLIT_NODE_CONTAINER = 0x00,
  318. GS_GUI_SPLIT_NODE_SPLIT
  319. } gs_gui_split_node_type;
  320. enum {
  321. GS_GUI_SPLIT_NODE_CHILD = 0x00,
  322. GS_GUI_SPLIT_NODE_PARENT
  323. };
  324. typedef struct gs_gui_split_node_t
  325. {
  326. gs_gui_split_node_type type;
  327. union
  328. {
  329. uint32_t split;
  330. struct gs_gui_container_t* container;
  331. };
  332. } gs_gui_split_node_t;
  333. typedef enum gs_gui_split_type
  334. {
  335. GS_GUI_SPLIT_LEFT = 0x00,
  336. GS_GUI_SPLIT_RIGHT,
  337. GS_GUI_SPLIT_TOP,
  338. GS_GUI_SPLIT_BOTTOM,
  339. GS_GUI_SPLIT_TAB
  340. } gs_gui_split_type;
  341. typedef struct gs_gui_split_t
  342. {
  343. 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
  344. float ratio; // Split ratio between children [0.f, 1.f], (left node = ratio), right node = (1.f - ratio)
  345. gs_gui_rect_t rect;
  346. gs_gui_rect_t prev_rect;
  347. gs_gui_split_node_t children[2];
  348. uint32_t parent;
  349. uint32_t id;
  350. uint32_t zindex;
  351. int32_t frame;
  352. } gs_gui_split_t;
  353. typedef enum gs_gui_window_flags {
  354. GS_GUI_WINDOW_FLAGS_VISIBLE = (1 << 0),
  355. GS_GUI_WINDOW_FLAGS_FIRST_INIT = (1 << 1),
  356. GS_GUI_WINDOW_FLAGS_PUSH_ID = (1 << 2)
  357. } gs_gui_window_flags;
  358. // Equidistantly sized tabs, based on rect of window
  359. typedef struct gs_gui_tab_item_t
  360. {
  361. gs_gui_id tab_bar;
  362. uint32_t zindex; // Sorting index in tab bar
  363. void* data; // User set data pointer for this item
  364. uint32_t idx; // Internal index
  365. } gs_gui_tab_item_t;
  366. typedef struct gs_gui_tab_bar_t
  367. {
  368. gs_gui_tab_item_t items[GS_GUI_TAB_ITEM_MAX];
  369. uint32_t size; // Current number of items in tab bar
  370. gs_gui_rect_t rect; // Cached sized for tab bar
  371. uint32_t focus; // Focused item in tab bar
  372. } gs_gui_tab_bar_t;
  373. typedef struct gs_gui_container_t
  374. {
  375. gs_gui_command_t *head, *tail;
  376. gs_gui_rect_t rect;
  377. gs_gui_rect_t body;
  378. gs_vec2 content_size;
  379. gs_vec2 scroll;
  380. int32_t zindex;
  381. int32_t open;
  382. gs_gui_id id;
  383. gs_gui_id split; // If container is docked, then will have owning split to get sizing (0x00 for NULL)
  384. uint32_t tab_bar;
  385. uint32_t tab_item;
  386. struct gs_gui_container_t* parent; // Owning parent (for tabbing)
  387. uint64_t opt;
  388. uint32_t frame;
  389. uint32_t visible;
  390. int32_t flags;
  391. char name[256];
  392. } gs_gui_container_t;
  393. typedef enum {
  394. GS_GUI_ELEMENT_STATE_NEG = -1,
  395. GS_GUI_ELEMENT_STATE_DEFAULT = 0x00,
  396. GS_GUI_ELEMENT_STATE_HOVER,
  397. GS_GUI_ELEMENT_STATE_FOCUS,
  398. GS_GUI_ELEMENT_STATE_COUNT,
  399. GS_GUI_ELEMENT_STATE_ON_HOVER,
  400. GS_GUI_ELEMENT_STATE_ON_FOCUS,
  401. GS_GUI_ELEMENT_STATE_OFF_HOVER,
  402. GS_GUI_ELEMENT_STATE_OFF_FOCUS
  403. } gs_gui_element_state;
  404. typedef enum gs_gui_element_type
  405. {
  406. GS_GUI_ELEMENT_CONTAINER = 0x00,
  407. GS_GUI_ELEMENT_LABEL,
  408. GS_GUI_ELEMENT_TEXT,
  409. GS_GUI_ELEMENT_PANEL,
  410. GS_GUI_ELEMENT_INPUT,
  411. GS_GUI_ELEMENT_BUTTON,
  412. GS_GUI_ELEMENT_SCROLL,
  413. GS_GUI_ELEMENT_IMAGE,
  414. GS_GUI_ELEMENT_COUNT
  415. } gs_gui_element_type;
  416. typedef enum {
  417. GS_GUI_PADDING_LEFT = 0x00,
  418. GS_GUI_PADDING_RIGHT,
  419. GS_GUI_PADDING_TOP,
  420. GS_GUI_PADDING_BOTTOM
  421. } gs_gui_padding_type;
  422. typedef enum {
  423. GS_GUI_MARGIN_LEFT = 0x00,
  424. GS_GUI_MARGIN_RIGHT,
  425. GS_GUI_MARGIN_TOP,
  426. GS_GUI_MARGIN_BOTTOM
  427. } gs_gui_margin_type;
  428. typedef enum {
  429. // Width/Height
  430. GS_GUI_STYLE_WIDTH = 0x00,
  431. GS_GUI_STYLE_HEIGHT,
  432. // Padding
  433. GS_GUI_STYLE_PADDING,
  434. GS_GUI_STYLE_PADDING_LEFT,
  435. GS_GUI_STYLE_PADDING_RIGHT,
  436. GS_GUI_STYLE_PADDING_TOP,
  437. GS_GUI_STYLE_PADDING_BOTTOM,
  438. GS_GUI_STYLE_MARGIN, // Can set margin for all at once, if -1.f then will assume 'auto' to simulate standard css
  439. GS_GUI_STYLE_MARGIN_LEFT,
  440. GS_GUI_STYLE_MARGIN_RIGHT,
  441. GS_GUI_STYLE_MARGIN_TOP,
  442. GS_GUI_STYLE_MARGIN_BOTTOM,
  443. // Border Radius
  444. GS_GUI_STYLE_BORDER_RADIUS,
  445. GS_GUI_STYLE_BORDER_RADIUS_LEFT,
  446. GS_GUI_STYLE_BORDER_RADIUS_RIGHT,
  447. GS_GUI_STYLE_BORDER_RADIUS_TOP,
  448. GS_GUI_STYLE_BORDER_RADIUS_BOTTOM,
  449. // Border Width
  450. GS_GUI_STYLE_BORDER_WIDTH,
  451. GS_GUI_STYLE_BORDER_WIDTH_LEFT,
  452. GS_GUI_STYLE_BORDER_WIDTH_RIGHT,
  453. GS_GUI_STYLE_BORDER_WIDTH_TOP,
  454. GS_GUI_STYLE_BORDER_WIDTH_BOTTOM,
  455. // Text
  456. GS_GUI_STYLE_TEXT_ALIGN,
  457. // Flex
  458. GS_GUI_STYLE_DIRECTION,
  459. GS_GUI_STYLE_ALIGN_CONTENT,
  460. GS_GUI_STYLE_JUSTIFY_CONTENT, // Justify runs parallel to direction (ex. for row, left to right)
  461. // Shadow
  462. GS_GUI_STYLE_SHADOW_X,
  463. GS_GUI_STYLE_SHADOW_Y,
  464. // Colors
  465. GS_GUI_STYLE_COLOR_BACKGROUND,
  466. GS_GUI_STYLE_COLOR_BORDER,
  467. GS_GUI_STYLE_COLOR_SHADOW,
  468. GS_GUI_STYLE_COLOR_CONTENT,
  469. GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND,
  470. GS_GUI_STYLE_COLOR_CONTENT_BORDER,
  471. GS_GUI_STYLE_COLOR_CONTENT_SHADOW,
  472. // Font
  473. GS_GUI_STYLE_FONT,
  474. GS_GUI_STYLE_COUNT
  475. } gs_gui_style_element_type;
  476. enum {
  477. GS_GUI_ANIMATION_DIRECTION_FORWARD = 0x00,
  478. GS_GUI_ANIMATION_DIRECTION_BACKWARD
  479. };
  480. typedef struct {
  481. gs_gui_style_element_type type;
  482. union {
  483. int32_t value;
  484. gs_color_t color;
  485. gs_asset_font_t* font;
  486. };
  487. } gs_gui_style_element_t;
  488. typedef struct gs_gui_animation_t
  489. {
  490. int16_t max; // max time
  491. int16_t time; // current time
  492. int16_t delay; // delay
  493. int16_t curve; // curve type
  494. int16_t direction; // current direction
  495. int16_t playing; // whether or not active
  496. int16_t iterations; // number of iterations to play the animation
  497. int16_t focus_state; // cached focus_state from frame (want to delete this somehow)
  498. int16_t hover_state; // cached hover_state from frame (want to delete this somehow)
  499. int16_t start_state; // starting state for animation blend
  500. int16_t end_state; // ending state for animation blend
  501. int32_t frame; // current frame (to match)
  502. } gs_gui_animation_t;
  503. typedef struct gs_gui_style_t
  504. {
  505. // font
  506. gs_asset_font_t* font;
  507. // dimensions
  508. float size[2];
  509. int16_t spacing; // get rid of (use padding)
  510. int16_t indent; // get rid of (use margin)
  511. int16_t title_height; // get rid of (use title_bar style)
  512. int16_t scrollbar_size; // get rid of (use scroll style)
  513. int16_t thumb_size; // get rid of (use various styles)
  514. // colors
  515. gs_color_t colors[GS_GUI_COLOR_MAX];
  516. // padding/margin
  517. int32_t padding[4];
  518. int16_t margin[4];
  519. // border
  520. int16_t border_width[4];
  521. int16_t border_radius[4];
  522. // flex direction / justification / alignment
  523. int16_t direction;
  524. int16_t justify_content;
  525. int16_t align_content;
  526. // shadow amount each direction
  527. int16_t shadow_x;
  528. int16_t shadow_y;
  529. } gs_gui_style_t;
  530. // Keep animation properties lists within style sheet to look up
  531. typedef struct gs_gui_animation_property_t
  532. {
  533. gs_gui_style_element_type type;
  534. int16_t time;
  535. int16_t delay;
  536. } gs_gui_animation_property_t;
  537. typedef struct gs_gui_animation_property_list_t
  538. {
  539. gs_dyn_array(gs_gui_animation_property_t) properties[3];
  540. } gs_gui_animation_property_list_t;
  541. /*
  542. element type
  543. classes
  544. id
  545. gs_gui_button(gui, "Text##.cls#id");
  546. gs_gui_label(gui, "Title###title");
  547. button .class #id : hover { // All of these styles get concat into one?
  548. }
  549. */
  550. typedef struct {
  551. gs_dyn_array(gs_gui_style_element_t) styles[3];
  552. } gs_gui_style_list_t;
  553. typedef struct gs_gui_style_sheet_t {
  554. gs_gui_style_t styles[GS_GUI_ELEMENT_COUNT][3]; // default | hovered | focused
  555. gs_hash_table(gs_gui_element_type, gs_gui_animation_property_list_t) animations;
  556. gs_hash_table(uint64_t, gs_gui_style_list_t) cid_styles;
  557. gs_hash_table(uint64_t, gs_gui_animation_property_list_t) cid_animations;
  558. } gs_gui_style_sheet_t;
  559. typedef struct gs_gui_style_sheet_element_desc_t {
  560. struct {
  561. struct {
  562. gs_gui_style_element_t* data;
  563. size_t size;
  564. } style;
  565. struct {
  566. gs_gui_animation_property_t* data;
  567. size_t size;
  568. } animation;
  569. } all;
  570. struct {
  571. struct {
  572. gs_gui_style_element_t* data;
  573. size_t size;
  574. } style;
  575. struct {
  576. gs_gui_animation_property_t* data;
  577. size_t size;
  578. } animation;
  579. } def;
  580. struct {
  581. struct {
  582. gs_gui_style_element_t* data;
  583. size_t size;
  584. } style;
  585. struct {
  586. gs_gui_animation_property_t* data;
  587. size_t size;
  588. } animation;
  589. } hover;
  590. struct {
  591. struct {
  592. gs_gui_style_element_t* data;
  593. size_t size;
  594. } style;
  595. struct {
  596. gs_gui_animation_property_t* data;
  597. size_t size;
  598. } animation;
  599. } focus;
  600. } gs_gui_style_sheet_element_desc_t;
  601. typedef gs_gui_style_sheet_element_desc_t gs_gui_inline_style_desc_t;
  602. typedef struct gs_gui_style_sheet_desc_t {
  603. gs_gui_style_sheet_element_desc_t container;
  604. gs_gui_style_sheet_element_desc_t button;
  605. gs_gui_style_sheet_element_desc_t panel;
  606. gs_gui_style_sheet_element_desc_t input;
  607. gs_gui_style_sheet_element_desc_t text;
  608. gs_gui_style_sheet_element_desc_t label;
  609. gs_gui_style_sheet_element_desc_t scroll;
  610. gs_gui_style_sheet_element_desc_t tab;
  611. gs_gui_style_sheet_element_desc_t menu;
  612. gs_gui_style_sheet_element_desc_t title;
  613. gs_gui_style_sheet_element_desc_t image;
  614. } gs_gui_style_sheet_desc_t;
  615. typedef enum gs_gui_request_type
  616. {
  617. GS_GUI_SPLIT_NEW = 0x01,
  618. GS_GUI_CNT_MOVE,
  619. GS_GUI_CNT_FOCUS,
  620. GS_GUI_SPLIT_MOVE,
  621. GS_GUI_SPLIT_RESIZE_SW,
  622. GS_GUI_SPLIT_RESIZE_SE,
  623. GS_GUI_SPLIT_RESIZE_NW,
  624. GS_GUI_SPLIT_RESIZE_NE,
  625. GS_GUI_SPLIT_RESIZE_W,
  626. GS_GUI_SPLIT_RESIZE_E,
  627. GS_GUI_SPLIT_RESIZE_N,
  628. GS_GUI_SPLIT_RESIZE_S,
  629. GS_GUI_SPLIT_RESIZE_CENTER,
  630. GS_GUI_SPLIT_RATIO,
  631. GS_GUI_SPLIT_RESIZE_INVALID,
  632. GS_GUI_TAB_SWAP_LEFT,
  633. GS_GUI_TAB_SWAP_RIGHT
  634. } gs_gui_request_type;
  635. typedef struct gs_gui_request_t
  636. {
  637. gs_gui_request_type type;
  638. union
  639. {
  640. gs_gui_split_type split_type;
  641. gs_gui_split_t* split;
  642. gs_gui_container_t* cnt;
  643. };
  644. uint32_t frame;
  645. } gs_gui_request_t;
  646. typedef struct gs_gui_inline_style_stack_t
  647. {
  648. gs_dyn_array(gs_gui_style_element_t) styles[3];
  649. gs_dyn_array(gs_gui_animation_property_t) animations[3];
  650. gs_dyn_array(uint32_t) style_counts; // amount of styles to pop off at "top of stack" for each state
  651. gs_dyn_array(uint32_t) animation_counts; // amount of animations to pop off at "top of stack" for each state
  652. } gs_gui_inline_style_stack_t;
  653. typedef struct gs_gui_context_t
  654. {
  655. // Core state
  656. gs_gui_style_t* style; // Active style
  657. gs_gui_style_sheet_t* style_sheet; // Active style sheet
  658. gs_gui_id hover;
  659. gs_gui_id focus;
  660. gs_gui_id last_id;
  661. gs_gui_id state_switch_id; // Id that had a state switch
  662. int32_t switch_state;
  663. gs_gui_id lock_focus;
  664. int32_t last_hover_state;
  665. int32_t last_focus_state;
  666. gs_gui_id prev_hover;
  667. gs_gui_id prev_focus;
  668. gs_gui_rect_t last_rect;
  669. int32_t last_zindex;
  670. int32_t updated_focus;
  671. int32_t frame;
  672. gs_vec2 framebuffer_size;
  673. gs_gui_rect_t viewport;
  674. gs_gui_container_t* active_root;
  675. gs_gui_container_t* hover_root;
  676. gs_gui_container_t* next_hover_root;
  677. gs_gui_container_t* scroll_target;
  678. gs_gui_container_t* focus_root;
  679. gs_gui_container_t* next_focus_root;
  680. gs_gui_container_t* dockable_root;
  681. gs_gui_container_t* prev_dockable_root;
  682. gs_gui_container_t* docked_root;
  683. gs_gui_container_t* undock_root;
  684. gs_gui_split_t* focus_split;
  685. gs_gui_split_t* next_hover_split;
  686. gs_gui_split_t* hover_split;
  687. gs_gui_id next_lock_hover_id;
  688. gs_gui_id lock_hover_id;
  689. char number_edit_buf[GS_GUI_MAX_FMT];
  690. gs_gui_id number_edit;
  691. gs_gui_alt_drag_mode_type alt_drag_mode;
  692. gs_dyn_array(gs_gui_request_t) requests;
  693. // Stacks
  694. gs_gui_stack(uint8_t, GS_GUI_COMMANDLIST_SIZE) command_list;
  695. gs_gui_stack(gs_gui_container_t*, GS_GUI_ROOTLIST_SIZE) root_list;
  696. gs_gui_stack(gs_gui_container_t*, GS_GUI_CONTAINERSTACK_SIZE) container_stack;
  697. gs_gui_stack(gs_gui_rect_t, GS_GUI_CLIPSTACK_SIZE) clip_stack;
  698. gs_gui_stack(gs_gui_id, GS_GUI_IDSTACK_SIZE) id_stack;
  699. gs_gui_stack(gs_gui_layout_t, GS_GUI_LAYOUTSTACK_SIZE) layout_stack;
  700. // Style sheet element stacks
  701. gs_hash_table(gs_gui_element_type, gs_gui_inline_style_stack_t) inline_styles;
  702. // Retained state pools
  703. gs_gui_pool_item_t container_pool[GS_GUI_CONTAINERPOOL_SIZE];
  704. gs_gui_container_t containers[GS_GUI_CONTAINERPOOL_SIZE];
  705. gs_gui_pool_item_t treenode_pool[GS_GUI_TREENODEPOOL_SIZE];
  706. gs_slot_array(gs_gui_split_t) splits;
  707. gs_slot_array(gs_gui_tab_bar_t) tab_bars;
  708. // Input state
  709. gs_vec2 mouse_pos;
  710. gs_vec2 last_mouse_pos;
  711. gs_vec2 mouse_delta;
  712. gs_vec2 scroll_delta;
  713. int32_t mouse_down;
  714. int32_t mouse_pressed;
  715. int32_t key_down;
  716. int32_t key_pressed;
  717. char input_text[32];
  718. // Backend resources
  719. uint32_t window_hndl;
  720. gs_immediate_draw_t gsi;
  721. gs_immediate_draw_t overlay_draw_list;
  722. // Active Transitions
  723. gs_hash_table(gs_gui_id, gs_gui_animation_t) animations;
  724. // Font stash
  725. gs_hash_table(uint64_t, gs_asset_font_t*) font_stash;
  726. // Callbacks
  727. struct {
  728. gs_gui_on_draw_button_callback button;
  729. } callbacks;
  730. } gs_gui_context_t;
  731. typedef struct
  732. {
  733. // Core state
  734. gs_gui_style_t* style; // Active style
  735. gs_gui_style_sheet_t* style_sheet; // Active style sheet
  736. gs_gui_id hover;
  737. gs_gui_id focus;
  738. gs_gui_id last_id;
  739. gs_gui_id lock_focus;
  740. gs_gui_rect_t last_rect;
  741. int32_t last_zindex;
  742. int32_t updated_focus;
  743. int32_t frame;
  744. gs_gui_container_t* hover_root;
  745. gs_gui_container_t* next_hover_root;
  746. gs_gui_container_t* scroll_target;
  747. gs_gui_container_t* focus_root;
  748. gs_gui_container_t* next_focus_root;
  749. gs_gui_container_t* dockable_root;
  750. gs_gui_container_t* prev_dockable_root;
  751. gs_gui_container_t* docked_root;
  752. gs_gui_container_t* undock_root;
  753. gs_gui_split_t* focus_split;
  754. gs_gui_split_t* next_hover_split;
  755. gs_gui_split_t* hover_split;
  756. gs_gui_id next_lock_hover_id;
  757. gs_gui_id lock_hover_id;
  758. char number_edit_buf[GS_GUI_MAX_FMT];
  759. gs_gui_id number_edit;
  760. gs_gui_alt_drag_mode_type alt_drag_mode;
  761. gs_dyn_array(gs_gui_request_t) requests;
  762. // Stacks
  763. gs_gui_stack(gs_gui_container_t*, GS_GUI_CONTAINERSTACK_SIZE) container_stack;
  764. gs_dyn_array(uint8_t) command_list;
  765. gs_dyn_array(gs_gui_container_t*) root_list;
  766. gs_dyn_array(gs_gui_rect_t) clip_stack;
  767. gs_dyn_array(gs_gui_id) id_stack;
  768. gs_dyn_array(gs_gui_layout_t) layout_stack;
  769. // Retained state pools
  770. gs_gui_pool_item_t container_pool[GS_GUI_CONTAINERPOOL_SIZE];
  771. gs_gui_pool_item_t treenode_pool[GS_GUI_TREENODEPOOL_SIZE];
  772. gs_slot_array(gs_gui_split_t) splits;
  773. gs_slot_array(gs_gui_tab_bar_t) tab_bars;
  774. // Input state
  775. gs_vec2 mouse_pos;
  776. gs_vec2 last_mouse_pos;
  777. gs_vec2 mouse_delta;
  778. gs_vec2 scroll_delta;
  779. int16_t mouse_down;
  780. int16_t mouse_pressed;
  781. int16_t key_down;
  782. int16_t key_pressed;
  783. char input_text[32];
  784. // Backend resources
  785. uint32_t window_hndl;
  786. gs_immediate_draw_t gsi;
  787. gs_immediate_draw_t overlay_draw_list;
  788. // Active Transitions
  789. gs_hash_table(gs_gui_id, gs_gui_animation_t) animations;
  790. // Callbacks
  791. struct {
  792. gs_gui_on_draw_button_callback button;
  793. } callbacks;
  794. } gs_gui_context_pruned_t;
  795. typedef struct {
  796. const char* key;
  797. gs_asset_font_t* font;
  798. } gs_gui_font_desc_t;
  799. typedef struct {
  800. gs_gui_font_desc_t* fonts;
  801. size_t size;
  802. } gs_gui_font_stash_desc_t;
  803. typedef struct
  804. {
  805. const char* id; // Id selector
  806. const char* classes[GS_GUI_CLS_SELECTOR_MAX]; // Class selectors
  807. } gs_gui_selector_desc_t;
  808. enum
  809. {
  810. GS_GUI_HINT_FLAG_NO_SCALE_BIAS_MOUSE = (1 << 0),
  811. GS_GUI_HINT_FLAG_NO_INVERT_Y = (1 << 1)
  812. };
  813. typedef struct gs_gui_hints_s
  814. {
  815. gs_vec2 framebuffer_size; // Overall framebuffer size
  816. gs_gui_rect_t viewport; // Viewport within framebuffer for gui context
  817. int32_t flags; // Flags for hints
  818. } gs_gui_hints_t;
  819. GS_API_DECL gs_gui_rect_t gs_gui_rect(float x, float y, float w, float h);
  820. //=== Context ===//
  821. GS_API_DECL gs_gui_context_t gs_gui_new(uint32_t window_hndl);
  822. GS_API_DECL void gs_gui_init(gs_gui_context_t *ctx, uint32_t window_hndl);
  823. GS_API_DECL void gs_gui_init_font_stash(gs_gui_context_t *ctx, gs_gui_font_stash_desc_t* desc);
  824. GS_API_DECL gs_gui_context_t gs_gui_context_new(uint32_t window_hndl);
  825. GS_API_DECL void gs_gui_free(gs_gui_context_t* ctx);
  826. GS_API_DECL void gs_gui_begin(gs_gui_context_t *ctx, const gs_gui_hints_t* hints);
  827. GS_API_DECL void gs_gui_end(gs_gui_context_t *ctx);
  828. GS_API_DECL void gs_gui_render(gs_gui_context_t* ctx, gs_command_buffer_t* cb);
  829. //=== Util ===//
  830. GS_API_DECL void gs_gui_renderpass_submit(gs_gui_context_t* ctx, gs_command_buffer_t* cb, gs_color_t clear);
  831. 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);
  832. 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);
  833. GS_API_DECL void gs_gui_parse_label_tag(gs_gui_context_t* ctx, const char* str, char* buffer, size_t sz);
  834. //=== Main API ===//
  835. GS_API_DECL void gs_gui_set_focus(gs_gui_context_t *ctx, gs_gui_id id);
  836. GS_API_DECL void gs_gui_set_hover(gs_gui_context_t *ctx, gs_gui_id id);
  837. GS_API_DECL gs_gui_id gs_gui_get_id(gs_gui_context_t *ctx, const void *data, int32_t size);
  838. GS_API_DECL gs_gui_id gs_gui_push_id(gs_gui_context_t *ctx, const void *data, int32_t size);
  839. GS_API_DECL void gs_gui_pop_id(gs_gui_context_t *ctx);
  840. GS_API_DECL void gs_gui_push_clip_rect(gs_gui_context_t *ctx, gs_gui_rect_t rect);
  841. GS_API_DECL void gs_gui_pop_clip_rect(gs_gui_context_t *ctx);
  842. GS_API_DECL gs_gui_rect_t gs_gui_get_clip_rect(gs_gui_context_t *ctx);
  843. GS_API_DECL int32_t gs_gui_check_clip(gs_gui_context_t *ctx, gs_gui_rect_t r);
  844. GS_API_DECL int32_t gs_gui_mouse_over(gs_gui_context_t* ctx, gs_gui_rect_t rect);
  845. 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);
  846. //=== Conatiner ===//
  847. GS_API_DECL gs_gui_container_t* gs_gui_get_current_container(gs_gui_context_t *ctx);
  848. GS_API_DECL gs_gui_container_t* gs_gui_get_container(gs_gui_context_t *ctx, const char *name);
  849. GS_API_DECL gs_gui_container_t* gs_gui_get_top_most_container(gs_gui_context_t* ctx, gs_gui_split_t* split);
  850. GS_API_DECL gs_gui_container_t* gs_gui_get_container_ex(gs_gui_context_t *ctx, gs_gui_id id, uint64_t opt);
  851. GS_API_DECL void gs_gui_bring_to_front(gs_gui_context_t *ctx, gs_gui_container_t *cnt);
  852. GS_API_DECL void gs_gui_bring_split_to_front(gs_gui_context_t* ctx, gs_gui_split_t* split);
  853. GS_API_DECL gs_gui_split_t* gs_gui_get_split(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
  854. GS_API_DECL gs_gui_tab_bar_t* gs_gui_get_tab_bar(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
  855. GS_API_DECL void gs_gui_tab_item_swap(gs_gui_context_t* ctx, gs_gui_container_t* cnt, int32_t direction);
  856. GS_API_DECL gs_gui_container_t* gs_gui_get_root_container(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
  857. GS_API_DECL gs_gui_container_t* gs_gui_get_root_container_from_split(gs_gui_context_t* ctx, gs_gui_split_t* split);
  858. GS_API_DECL gs_gui_container_t* gs_gui_get_parent(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
  859. GS_API_DECL void gs_gui_current_container_close(gs_gui_context_t* ctx);
  860. //=== Animation ===//
  861. GS_API_DECL gs_gui_animation_t* gs_gui_get_animation(gs_gui_context_t* ctx, gs_gui_id id,
  862. const gs_gui_selector_desc_t* desc, int32_t elementid);
  863. GS_API_DECL gs_gui_style_t gs_gui_animation_get_blend_style(gs_gui_context_t* ctx, gs_gui_animation_t* anim,
  864. const gs_gui_selector_desc_t* desc, int32_t elementid);
  865. //=== Style Sheet ===//
  866. 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);
  867. GS_API_DECL void gs_gui_style_sheet_destroy(gs_gui_style_sheet_t* ss);
  868. 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);
  869. 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);
  870. GS_API_DECL void gs_gui_set_style_sheet(gs_gui_context_t* ctx, gs_gui_style_sheet_t* style_sheet);
  871. 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);
  872. GS_API_DECL void gs_gui_pop_inline_style(gs_gui_context_t* ctx, gs_gui_element_type elementid);
  873. //=== Resource Loading ===//
  874. GS_API_DECL gs_gui_style_sheet_t gs_gui_style_sheet_load_from_file(gs_gui_context_t* ctx, const char* file_path);
  875. 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);
  876. //=== Pools ===//
  877. 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);
  878. 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);
  879. GS_API_DECL void gs_gui_pool_update(gs_gui_context_t *ctx, gs_gui_pool_item_t *items, int32_t idx);
  880. //=== Input ===//
  881. GS_API_DECL void gs_gui_input_mousemove(gs_gui_context_t *ctx, int32_t x, int32_t y);
  882. GS_API_DECL void gs_gui_input_mousedown(gs_gui_context_t *ctx, int32_t x, int32_t y, int32_t btn);
  883. GS_API_DECL void gs_gui_input_mouseup(gs_gui_context_t *ctx, int32_t x, int32_t y, int32_t btn);
  884. GS_API_DECL void gs_gui_input_scroll(gs_gui_context_t *ctx, int32_t x, int32_t y);
  885. GS_API_DECL void gs_gui_input_keydown(gs_gui_context_t *ctx, int32_t key);
  886. GS_API_DECL void gs_gui_input_keyup(gs_gui_context_t *ctx, int32_t key);
  887. GS_API_DECL void gs_gui_input_text(gs_gui_context_t *ctx, const char *text);
  888. //=== Commands ===//
  889. GS_API_DECL gs_gui_command_t* gs_gui_push_command(gs_gui_context_t* ctx, int32_t type, int32_t size);
  890. GS_API_DECL int32_t gs_gui_next_command(gs_gui_context_t* ctx, gs_gui_command_t** cmd);
  891. GS_API_DECL void gs_gui_set_clip(gs_gui_context_t* ctx, gs_gui_rect_t rect);
  892. GS_API_DECL void gs_gui_set_pipeline(gs_gui_context_t* ctx, gs_handle(gs_graphics_pipeline_t) pip,
  893. void* layout, size_t layout_sz, gsi_layout_type layout_type);
  894. GS_API_DECL void gs_gui_bind_uniforms(gs_gui_context_t* ctx, gs_graphics_bind_uniform_desc_t* uniforms,
  895. size_t uniforms_sz);
  896. //=== Drawing ===//
  897. GS_API_DECL void gs_gui_draw_rect(gs_gui_context_t* ctx, gs_gui_rect_t rect, gs_color_t color);
  898. GS_API_DECL void gs_gui_draw_circle(gs_gui_context_t* ctx, gs_vec2 position, float radius, gs_color_t color);
  899. 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);
  900. 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);
  901. GS_API_DECL void gs_gui_draw_line(gs_gui_context_t* ctx, gs_vec2 start, gs_vec2 end, gs_color_t color);
  902. 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);
  903. 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);
  904. 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);
  905. 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);
  906. 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);
  907. 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);
  908. //=== Layout ===//
  909. GS_API_DECL gs_gui_layout_t* gs_gui_get_layout(gs_gui_context_t* ctx);
  910. GS_API_DECL void gs_gui_layout_row(gs_gui_context_t *ctx, int32_t items, const int32_t *widths, int32_t height);
  911. 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);
  912. GS_API_DECL void gs_gui_layout_width(gs_gui_context_t *ctx, int32_t width);
  913. GS_API_DECL void gs_gui_layout_height(gs_gui_context_t *ctx, int32_t height);
  914. GS_API_DECL void gs_gui_layout_column_begin(gs_gui_context_t *ctx);
  915. GS_API_DECL void gs_gui_layout_column_end(gs_gui_context_t *ctx);
  916. GS_API_DECL void gs_gui_layout_set_next(gs_gui_context_t *ctx, gs_gui_rect_t r, int32_t relative);
  917. GS_API_DECL gs_gui_rect_t gs_gui_layout_peek_next(gs_gui_context_t *ctx);
  918. GS_API_DECL gs_gui_rect_t gs_gui_layout_next(gs_gui_context_t *ctx);
  919. 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);
  920. //=== Elements ===//
  921. #define gs_gui_button(_CTX, _LABEL) gs_gui_button_ex((_CTX), (_LABEL), NULL, GS_GUI_OPT_LEFTCLICKONLY)
  922. #define gs_gui_text(_CTX, _TXT) gs_gui_text_ex((_CTX), (_TXT), 1, NULL, 0x00)
  923. #define gs_gui_textbox(_CTX, _BUF, _BUFSZ) gs_gui_textbox_ex((_CTX), (_BUF), (_BUFSZ), NULL, 0x00)
  924. #define gs_gui_slider(_CTX, _VALUE, _LO, _HI) gs_gui_slider_ex((_CTX), (_VALUE), (_LO), (_HI), 0, GS_GUI_SLIDER_FMT, NULL, 0x00)
  925. #define gs_gui_number(_CTX, _VALUE, _STEP) gs_gui_number_ex((_CTX), (_VALUE), (_STEP), GS_GUI_SLIDER_FMT, NULL, 0x00)
  926. #define gs_gui_header(_CTX, _LABEL) gs_gui_header_ex((_CTX), (_LABEL), NULL, 0x00)
  927. #define gs_gui_checkbox(_CTX, _LABEL, _STATE) gs_gui_checkbox_ex((_CTX), (_LABEL), (_STATE), NULL, GS_GUI_OPT_LEFTCLICKONLY)
  928. #define gs_gui_treenode_begin(_CTX, _LABEL) gs_gui_treenode_begin_ex((_CTX), (_LABEL), NULL, 0x00)
  929. #define gs_gui_window_begin(_CTX, _TITLE, _RECT) gs_gui_window_begin_ex((_CTX), (_TITLE), (_RECT), 0, NULL, 0x00)
  930. #define gs_gui_popup_begin(_CTX, _TITLE, _RECT) gs_gui_popup_begin_ex((_CTX), (_TITLE), (_RECT), NULL, 0x00)
  931. #define gs_gui_panel_begin(_CTX, _NAME) gs_gui_panel_begin_ex((_CTX), (_NAME), NULL, 0x00)
  932. #define gs_gui_image(_CTX, _HNDL) gs_gui_image_ex((_CTX), (_HNDL), gs_v2s(0.f), gs_v2s(1.f), NULL, 0x00)
  933. #define gs_gui_combo_begin(_CTX, _ID, _ITEM, _MAX) gs_gui_combo_begin_ex((_CTX), (_ID), (_ITEM), (_MAX), NULL, 0x00)
  934. #define gs_gui_combo_item(_CTX, _NAME) gs_gui_combo_item_ex((_CTX), (_NAME), NULL, 0x00)
  935. #define gs_gui_dock(_CTX, _DST, _SRC, _TYPE) gs_gui_dock_ex((_CTX), (_DST), (_SRC), (_TYPE), 0.5f)
  936. #define gs_gui_undock(_CTX, _NAME) gs_gui_undock_ex((_CTX), (_NAME))
  937. #define gs_gui_label(_CTX, _FMT, ...)\
  938. (\
  939. gs_snprintf((_CTX)->number_edit_buf, sizeof((_CTX)->number_edit_buf), _FMT, ## __VA_ARGS__),\
  940. gs_gui_label_ex((_CTX), (_CTX)->number_edit_buf, NULL, 0x00)\
  941. )
  942. //=== Elements (Extended) ===//
  943. 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);
  944. 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);
  945. 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);
  946. 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);
  947. 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);
  948. 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);
  949. 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);
  950. 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,
  951. const char* fmt, const gs_gui_selector_desc_t* desc, uint64_t opt);
  952. 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);
  953. 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);
  954. 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);
  955. GS_API_DECL void gs_gui_treenode_end(gs_gui_context_t* ctx);
  956. 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);
  957. GS_API_DECL void gs_gui_window_end(gs_gui_context_t* ctx);
  958. GS_API_DECL void gs_gui_popup_open(gs_gui_context_t* ctx, const char* name);
  959. 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);
  960. GS_API_DECL void gs_gui_popup_end(gs_gui_context_t* ctx);
  961. 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);
  962. GS_API_DECL void gs_gui_panel_end(gs_gui_context_t* ctx);
  963. 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);
  964. GS_API_DECL void gs_gui_combo_end(gs_gui_context_t* ctx);
  965. 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);
  966. //=== Demos ===//
  967. 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);
  968. GS_API_DECL int32_t gs_gui_demo_window(gs_gui_context_t* ctx, gs_gui_rect_t rect, bool* open);
  969. //=== Docking ===//
  970. 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);
  971. GS_API_DECL void gs_gui_undock_ex(gs_gui_context_t* ctx, const char* name);
  972. 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);
  973. GS_API_DECL void gs_gui_undock_ex_cnt(gs_gui_context_t* ctx, gs_gui_container_t* cnt);
  974. //=== Gizmo ===//
  975. 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,
  976. bool invert_view_y, float snap, int32_t op, int32_t mode, uint64_t opt);
  977. //=== Implementation ===//
  978. #ifdef GS_GUI_IMPL
  979. #ifndef GS_PHYSICS_IMPL
  980. #define GS_PHYSICS_IMPL
  981. #include "gs_physics.h"
  982. #endif
  983. #define gs_gui_unused(x) ((void) (x))
  984. #define gs_gui_expect(x)\
  985. do { \
  986. if (!(x)) { \
  987. gs_log_error("Fatal error: assertion '%s' failed\n", \
  988. #x); \
  989. abort(); \
  990. } \
  991. } while (0)
  992. #define gs_gui_stack_push(stk, val)\
  993. do { \
  994. gs_gui_expect((stk).idx < (int32_t) (sizeof((stk).items) / sizeof(*(stk).items))); \
  995. (stk).items[(stk).idx] = (val); \
  996. (stk).idx++; /* incremented after incase `val` uses this value */ \
  997. } while (0)
  998. #define gs_gui_stack_pop(stk)\
  999. do { \
  1000. gs_gui_expect((stk).idx > 0); \
  1001. (stk).idx--; \
  1002. } while (0)
  1003. /* 32bit fnv-1a hash */
  1004. #define GS_GUI_HASH_INITIAL 2166136261
  1005. static void gs_gui_hash(gs_gui_id *hash, const void *data, int32_t size)
  1006. {
  1007. const unsigned char *p = (const unsigned char*)data;
  1008. while (size--)
  1009. {
  1010. *hash = (*hash ^ *p++) * 16777619;
  1011. }
  1012. }
  1013. static gs_gui_rect_t gs_gui_unclipped_rect = { 0, 0, 0x1000000, 0x1000000 };
  1014. // Default styles
  1015. static gs_gui_style_t gs_gui_default_container_style[3] = gs_default_val();
  1016. static gs_gui_style_t gs_gui_default_button_style[3] = gs_default_val();
  1017. static gs_gui_style_t gs_gui_default_text_style[3] = gs_default_val();
  1018. static gs_gui_style_t gs_gui_default_label_style[3] = gs_default_val();
  1019. static gs_gui_style_t gs_gui_default_panel_style[3] = gs_default_val();
  1020. static gs_gui_style_t gs_gui_default_input_style[3] = gs_default_val();
  1021. static gs_gui_style_t gs_gui_default_scroll_style[3] = gs_default_val();
  1022. static gs_gui_style_t gs_gui_default_image_style[3] = gs_default_val();
  1023. static gs_gui_style_t gs_gui_default_style =
  1024. {
  1025. // font | size | spacing | indent | title_height | scroll_width | thumb_width
  1026. NULL, {68, 18}, 2, 10, 20, 5, 5,
  1027. // colors
  1028. {
  1029. {25, 25, 25, 255}, // GS_GUI_COLOR_BACKGROUND
  1030. {255, 255, 255, 255}, // GS_GUI_COLOR_CONTENT
  1031. {29, 29, 29, 76}, // GS_GUI_COLOR_BORDER
  1032. {0, 0, 0, 31}, // GS_GUI_COLOR_SHADOW
  1033. {0, 0, 0, 0}, // GS_GUI_COLOR_CONTENT_BACKGROUND
  1034. {0, 0, 0, 0}, // GS_GUI_COLOR_CONTENT_SHADOW
  1035. {0, 0, 0, 0} // GS_GUI_COLOR_CONTENT_BORDER
  1036. },
  1037. // padding (left, right, top, bottom)
  1038. {2, 2, 2, 2},
  1039. // margin (left, right, top, bottom)
  1040. {2, 2, 2, 2},
  1041. // border width (left, right, top, bottom)
  1042. {1, 1, 1, 1},
  1043. // border radius (left, right, top, bottom)
  1044. {0, 0, 0, 0},
  1045. // flex direction / justification / alignment / shrink / grow
  1046. GS_GUI_DIRECTION_COLUMN,
  1047. GS_GUI_JUSTIFY_START,
  1048. GS_GUI_ALIGN_CENTER,
  1049. // shadow x, y
  1050. 1, 1
  1051. };
  1052. static gs_gui_style_sheet_t gs_gui_default_style_sheet = gs_default_val();
  1053. static gs_gui_style_t gs_gui_get_current_element_style(gs_gui_context_t* ctx, const gs_gui_selector_desc_t* desc,
  1054. int32_t elementid, int32_t state)
  1055. {
  1056. #define GS_GUI_APPLY_STYLE(SE)\
  1057. do {\
  1058. switch ((SE)->type)\
  1059. {\
  1060. case GS_GUI_STYLE_WIDTH: style.size[0] = (float)(SE)->value; break;\
  1061. case GS_GUI_STYLE_HEIGHT: style.size[1] = (float)(SE)->value; break;\
  1062. \
  1063. case GS_GUI_STYLE_PADDING: {\
  1064. style.padding[GS_GUI_PADDING_LEFT] = (int32_t)(SE)->value;\
  1065. style.padding[GS_GUI_PADDING_TOP] = (int32_t)(SE)->value;\
  1066. style.padding[GS_GUI_PADDING_RIGHT] = (int32_t)(SE)->value;\
  1067. style.padding[GS_GUI_PADDING_BOTTOM] = (int32_t)(SE)->value;\
  1068. }\
  1069. \
  1070. case GS_GUI_STYLE_PADDING_LEFT: style.padding[GS_GUI_PADDING_LEFT] = (int32_t)(SE)->value; break;\
  1071. case GS_GUI_STYLE_PADDING_TOP: style.padding[GS_GUI_PADDING_TOP] = (int32_t)(SE)->value; break;\
  1072. case GS_GUI_STYLE_PADDING_RIGHT: style.padding[GS_GUI_PADDING_RIGHT] = (int32_t)(SE)->value; break;\
  1073. case GS_GUI_STYLE_PADDING_BOTTOM: style.padding[GS_GUI_PADDING_BOTTOM] = (int32_t)(SE)->value; break;\
  1074. \
  1075. case GS_GUI_STYLE_MARGIN: {\
  1076. style.margin[GS_GUI_MARGIN_LEFT] = (int32_t)(SE)->value;\
  1077. style.margin[GS_GUI_MARGIN_TOP] = (int32_t)(SE)->value;\
  1078. style.margin[GS_GUI_MARGIN_RIGHT] = (int32_t)(SE)->value;\
  1079. style.margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)(SE)->value;\
  1080. } break;\
  1081. \
  1082. case GS_GUI_STYLE_MARGIN_LEFT: style.margin[GS_GUI_MARGIN_LEFT] = (int32_t)(SE)->value; break;\
  1083. case GS_GUI_STYLE_MARGIN_TOP: style.margin[GS_GUI_MARGIN_TOP] = (int32_t)(SE)->value; break;\
  1084. case GS_GUI_STYLE_MARGIN_RIGHT: style.margin[GS_GUI_MARGIN_RIGHT] = (int32_t)(SE)->value; break;\
  1085. case GS_GUI_STYLE_MARGIN_BOTTOM: style.margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)(SE)->value; break;\
  1086. \
  1087. case GS_GUI_STYLE_BORDER_RADIUS: {\
  1088. style.border_radius[0] = (SE)->value;\
  1089. style.border_radius[1] = (SE)->value;\
  1090. style.border_radius[2] = (SE)->value;\
  1091. style.border_radius[3] = (SE)->value;\
  1092. } break;\
  1093. \
  1094. case GS_GUI_STYLE_BORDER_RADIUS_LEFT: style.border_radius[0] = (SE)->value; break;\
  1095. case GS_GUI_STYLE_BORDER_RADIUS_RIGHT: style.border_radius[1] = (SE)->value; break;\
  1096. case GS_GUI_STYLE_BORDER_RADIUS_TOP: style.border_radius[2] = (SE)->value; break;\
  1097. case GS_GUI_STYLE_BORDER_RADIUS_BOTTOM: style.border_radius[3] = (SE)->value; break;\
  1098. \
  1099. case GS_GUI_STYLE_BORDER_WIDTH: {\
  1100. style.border_width[0] = (SE)->value;\
  1101. style.border_width[1] = (SE)->value;\
  1102. style.border_width[2] = (SE)->value;\
  1103. style.border_width[3] = (SE)->value;\
  1104. } break;\
  1105. \
  1106. case GS_GUI_STYLE_BORDER_WIDTH_LEFT: style.border_width[0] = (SE)->value; break;\
  1107. case GS_GUI_STYLE_BORDER_WIDTH_RIGHT: style.border_width[1] = (SE)->value; break;\
  1108. case GS_GUI_STYLE_BORDER_WIDTH_TOP: style.border_width[2] = (SE)->value; break;\
  1109. case GS_GUI_STYLE_BORDER_WIDTH_BOTTOM: style.border_width[3] = (SE)->value; break;\
  1110. \
  1111. case GS_GUI_STYLE_DIRECTION: style.direction = (int32_t)(SE)->value; break;\
  1112. case GS_GUI_STYLE_ALIGN_CONTENT: style.align_content = (int32_t)(SE)->value; break;\
  1113. case GS_GUI_STYLE_JUSTIFY_CONTENT: style.justify_content = (int32_t)(SE)->value; break;\
  1114. \
  1115. case GS_GUI_STYLE_SHADOW_X: style.shadow_x = (int32_t)(SE)->value; break;\
  1116. case GS_GUI_STYLE_SHADOW_Y: style.shadow_y = (int32_t)(SE)->value; break;\
  1117. \
  1118. case GS_GUI_STYLE_COLOR_BACKGROUND: style.colors[GS_GUI_COLOR_BACKGROUND] = (SE)->color; break;\
  1119. case GS_GUI_STYLE_COLOR_BORDER: style.colors[GS_GUI_COLOR_BORDER] = (SE)->color; break;\
  1120. case GS_GUI_STYLE_COLOR_SHADOW: style.colors[GS_GUI_COLOR_SHADOW] = (SE)->color; break;\
  1121. case GS_GUI_STYLE_COLOR_CONTENT: style.colors[GS_GUI_COLOR_CONTENT] = (SE)->color; break;\
  1122. case GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND: style.colors[GS_GUI_COLOR_CONTENT_BACKGROUND] = (SE)->color; break;\
  1123. case GS_GUI_STYLE_COLOR_CONTENT_BORDER: style.colors[GS_GUI_COLOR_CONTENT_BORDER] = (SE)->color; break;\
  1124. case GS_GUI_STYLE_COLOR_CONTENT_SHADOW: style.colors[GS_GUI_COLOR_CONTENT_SHADOW] = (SE)->color; break;\
  1125. \
  1126. case GS_GUI_STYLE_FONT: style.font = (SE)->font; break;\
  1127. }\
  1128. } while (0)
  1129. gs_gui_style_t style = ctx->style_sheet->styles[elementid][state];
  1130. // Look for id tag style
  1131. gs_gui_style_list_t* id_styles = NULL;
  1132. gs_gui_style_list_t* cls_styles[GS_GUI_CLS_SELECTOR_MAX] = gs_default_val();
  1133. if (desc)
  1134. {
  1135. char TMP[256] = gs_default_val();
  1136. // ID selector
  1137. gs_snprintf(TMP, sizeof(TMP), "#%s", desc->id);
  1138. const uint64_t id_hash = gs_hash_str64(TMP);
  1139. id_styles = gs_hash_table_exists(ctx->style_sheet->cid_styles, id_hash) ?
  1140. gs_hash_table_getp(ctx->style_sheet->cid_styles, id_hash) : NULL;
  1141. // Class selectors
  1142. for (uint32_t i = 0; i < GS_GUI_CLS_SELECTOR_MAX; ++i)
  1143. {
  1144. if (desc->classes[i]) {
  1145. gs_snprintf(TMP, sizeof(TMP), ".%s", desc->classes[i]);
  1146. const uint64_t cls_hash = gs_hash_str64(TMP);
  1147. cls_styles[i] = gs_hash_table_exists(ctx->style_sheet->cid_styles, cls_hash) ?
  1148. gs_hash_table_getp(ctx->style_sheet->cid_styles, cls_hash) : NULL;
  1149. }
  1150. else break;
  1151. }
  1152. }
  1153. // Override with class styles
  1154. if (*cls_styles)
  1155. {
  1156. for (uint32_t i = 0; i < GS_GUI_CLS_SELECTOR_MAX; ++i)
  1157. {
  1158. if (!cls_styles[i]) break;
  1159. for (uint32_t s = 0; s < gs_dyn_array_size(cls_styles[i]->styles[state]); ++s) {
  1160. gs_gui_style_element_t* se = &cls_styles[i]->styles[state][s];
  1161. GS_GUI_APPLY_STYLE(se);
  1162. }
  1163. }
  1164. }
  1165. // Override with id styles
  1166. if (id_styles)
  1167. {
  1168. for (uint32_t i = 0; i < gs_dyn_array_size(id_styles->styles[state]); ++i) {
  1169. gs_gui_style_element_t* se = &id_styles->styles[state][i];
  1170. GS_GUI_APPLY_STYLE(se);
  1171. }
  1172. }
  1173. if (gs_hash_table_exists(ctx->inline_styles, (gs_gui_element_type)elementid))
  1174. {
  1175. gs_gui_inline_style_stack_t* iss = gs_hash_table_getp(ctx->inline_styles,
  1176. (gs_gui_element_type)elementid);
  1177. if (gs_dyn_array_size(iss->styles[state]))
  1178. {
  1179. // Get last size to apply for styles for this state
  1180. const uint32_t scz = gs_dyn_array_size(iss->style_counts);
  1181. const uint32_t ct = state == 0x00 ? iss->style_counts[scz - 3] :
  1182. state == 0x01 ? iss->style_counts[scz - 2] :
  1183. iss->style_counts[scz - 1];
  1184. const uint32_t ssz = gs_dyn_array_size(iss->styles[state]);
  1185. for (uint32_t i = 0; i < ct; ++i) {
  1186. uint32_t idx = (ssz - ct + i);
  1187. gs_gui_style_element_t* se = &iss->styles[state][idx];
  1188. GS_GUI_APPLY_STYLE(se);
  1189. }
  1190. }
  1191. }
  1192. return style;
  1193. }
  1194. GS_API_DECL gs_gui_style_t gs_gui_animation_get_blend_style(gs_gui_context_t* ctx, gs_gui_animation_t* anim,
  1195. const gs_gui_selector_desc_t* desc, int32_t elementid)
  1196. {
  1197. gs_gui_style_t ret = gs_default_val();
  1198. int32_t focus_state = anim->focus_state;
  1199. int32_t hover_state = anim->hover_state;
  1200. gs_gui_style_t s0 = gs_gui_get_current_element_style(ctx, desc, elementid, anim->start_state);
  1201. gs_gui_style_t s1 = gs_gui_get_current_element_style(ctx, desc, elementid, anim->end_state);
  1202. gs_gui_inline_style_stack_t* iss = NULL;
  1203. if (gs_hash_table_exists(ctx->inline_styles, (gs_gui_element_type)elementid)) {
  1204. iss = gs_hash_table_getp(ctx->inline_styles, (gs_gui_element_type)elementid);
  1205. }
  1206. if (anim->direction == GS_GUI_ANIMATION_DIRECTION_FORWARD) {ret = s1;}
  1207. else {ret = s0;}
  1208. const gs_gui_animation_property_list_t* list = NULL;
  1209. if (gs_hash_table_exists(ctx->style_sheet->animations, (gs_gui_element_type)elementid)) {
  1210. list = gs_hash_table_getp(ctx->style_sheet->animations, (gs_gui_element_type)elementid);
  1211. }
  1212. const gs_gui_animation_property_list_t* id_list = NULL;
  1213. const gs_gui_animation_property_list_t* cls_list[GS_GUI_CLS_SELECTOR_MAX] = gs_default_val();
  1214. bool has_class_animations = false;
  1215. if (desc)
  1216. {
  1217. char TMP[256] = gs_default_val();
  1218. // ID animations
  1219. if (desc->id)
  1220. {
  1221. gs_snprintf(TMP, sizeof(TMP), "#%s", desc->id);
  1222. const uint64_t id_hash = gs_hash_str64(TMP);
  1223. if (gs_hash_table_exists(ctx->style_sheet->cid_animations, id_hash)) {
  1224. id_list = gs_hash_table_getp(ctx->style_sheet->cid_animations, id_hash);
  1225. }
  1226. }
  1227. // Class animations
  1228. if (*desc->classes)
  1229. {
  1230. for (uint32_t i = 0; i < GS_GUI_CLS_SELECTOR_MAX; ++i)
  1231. {
  1232. if (!desc->classes[i]) break;
  1233. gs_snprintf(TMP, sizeof(TMP), ".%s", desc->classes[i]);
  1234. const uint64_t cls_hash = gs_hash_str64(TMP);
  1235. if (cls_hash && gs_hash_table_exists(ctx->style_sheet->cid_animations, cls_hash)) {
  1236. cls_list[i] = gs_hash_table_getp(ctx->style_sheet->cid_animations, cls_hash);
  1237. has_class_animations = true;
  1238. }
  1239. }
  1240. }
  1241. }
  1242. #define GS_GUI_BLEND_COLOR(TYPE)\
  1243. do {\
  1244. gs_color_t* c0 = &s0.colors[TYPE];\
  1245. gs_color_t* c1 = &s1.colors[TYPE];\
  1246. float r = 255.f * gs_interp_smoothstep((float)c0->r / 255.f, (float)c1->r / 255.f, t);\
  1247. float g = 255.f * gs_interp_smoothstep((float)c0->g / 255.f, (float)c1->g / 255.f, t);\
  1248. float b = 255.f * gs_interp_smoothstep((float)c0->b / 255.f, (float)c1->b / 255.f, t);\
  1249. float a = 255.f * gs_interp_smoothstep((float)c0->a / 255.f, (float)c1->a / 255.f, t);\
  1250. ret.colors[TYPE] = gs_color((u8)r, (u8)g, (u8)b, (u8)a);\
  1251. } while (0)
  1252. #define GS_GUI_BLEND_VALUE(FIELD, TYPE)\
  1253. do {\
  1254. float v0 = (float)s0.FIELD;\
  1255. float v1 = (float)s1.FIELD;\
  1256. ret.FIELD = (TYPE)gs_interp_smoothstep(v0, v1, t);\
  1257. } while (0)
  1258. #define GS_GUI_BLEND_PROPERTIES(LIST)\
  1259. do {\
  1260. for (uint32_t i = 0; i < gs_dyn_array_size(LIST); ++i)\
  1261. {\
  1262. const gs_gui_animation_property_t* prop = &LIST[i];\
  1263. float t = 0.f;\
  1264. switch (anim->direction)\
  1265. {\
  1266. default:\
  1267. case GS_GUI_ANIMATION_DIRECTION_FORWARD:\
  1268. {\
  1269. 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);\
  1270. } break;\
  1271. case GS_GUI_ANIMATION_DIRECTION_BACKWARD:\
  1272. {\
  1273. if (prop->time <= 0.f)\
  1274. t = 1.f;\
  1275. else\
  1276. 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);\
  1277. } break;\
  1278. }\
  1279. \
  1280. switch (prop->type)\
  1281. {\
  1282. case GS_GUI_STYLE_COLOR_BACKGROUND: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_BACKGROUND);} break;\
  1283. case GS_GUI_STYLE_COLOR_SHADOW: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_SHADOW);} break;\
  1284. case GS_GUI_STYLE_COLOR_BORDER: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_BORDER);} break;\
  1285. case GS_GUI_STYLE_COLOR_CONTENT: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_CONTENT);} break;\
  1286. case GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_CONTENT_BACKGROUND);} break;\
  1287. case GS_GUI_STYLE_COLOR_CONTENT_SHADOW: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_CONTENT_SHADOW);} break;\
  1288. case GS_GUI_STYLE_COLOR_CONTENT_BORDER: {GS_GUI_BLEND_COLOR(GS_GUI_COLOR_CONTENT_BORDER);} break;\
  1289. case GS_GUI_STYLE_WIDTH: {GS_GUI_BLEND_VALUE(size[0], float);} break;\
  1290. case GS_GUI_STYLE_HEIGHT: {GS_GUI_BLEND_VALUE(size[1], float);} break;\
  1291. case GS_GUI_STYLE_BORDER_WIDTH: {\
  1292. GS_GUI_BLEND_VALUE(border_width[0], int16_t);\
  1293. GS_GUI_BLEND_VALUE(border_width[1], int16_t);\
  1294. GS_GUI_BLEND_VALUE(border_width[2], int16_t);\
  1295. GS_GUI_BLEND_VALUE(border_width[3], int16_t);\
  1296. } break;\
  1297. case GS_GUI_STYLE_BORDER_WIDTH_LEFT: {GS_GUI_BLEND_VALUE(border_width[0], int16_t);} break;\
  1298. case GS_GUI_STYLE_BORDER_WIDTH_RIGHT: {GS_GUI_BLEND_VALUE(border_width[1], int16_t);} break;\
  1299. case GS_GUI_STYLE_BORDER_WIDTH_TOP: {GS_GUI_BLEND_VALUE(border_width[2], int16_t);} break;\
  1300. case GS_GUI_STYLE_BORDER_WIDTH_BOTTOM: {GS_GUI_BLEND_VALUE(border_width[3], int16_t);} break;\
  1301. case GS_GUI_STYLE_BORDER_RADIUS: {\
  1302. GS_GUI_BLEND_VALUE(border_radius[0], int16_t);\
  1303. GS_GUI_BLEND_VALUE(border_radius[1], int16_t);\
  1304. GS_GUI_BLEND_VALUE(border_radius[2], int16_t);\
  1305. GS_GUI_BLEND_VALUE(border_radius[3], int16_t);\
  1306. } break;\
  1307. case GS_GUI_STYLE_BORDER_RADIUS_LEFT: {GS_GUI_BLEND_VALUE(border_radius[0], int16_t);} break;\
  1308. case GS_GUI_STYLE_BORDER_RADIUS_RIGHT: {GS_GUI_BLEND_VALUE(border_radius[1], int16_t);} break;\
  1309. case GS_GUI_STYLE_BORDER_RADIUS_TOP: {GS_GUI_BLEND_VALUE(border_radius[2], int16_t);} break;\
  1310. case GS_GUI_STYLE_BORDER_RADIUS_BOTTOM: {GS_GUI_BLEND_VALUE(border_radius[3], int16_t);} break;\
  1311. case GS_GUI_STYLE_MARGIN_BOTTOM: {GS_GUI_BLEND_VALUE(margin[GS_GUI_MARGIN_BOTTOM], int16_t);} break;\
  1312. case GS_GUI_STYLE_MARGIN_TOP: {GS_GUI_BLEND_VALUE(margin[GS_GUI_MARGIN_TOP], int16_t);} break;\
  1313. case GS_GUI_STYLE_MARGIN_LEFT: {GS_GUI_BLEND_VALUE(margin[GS_GUI_MARGIN_LEFT], int16_t);} break;\
  1314. case GS_GUI_STYLE_MARGIN_RIGHT: {GS_GUI_BLEND_VALUE(margin[GS_GUI_MARGIN_RIGHT], int16_t);} break;\
  1315. case GS_GUI_STYLE_MARGIN: {\
  1316. GS_GUI_BLEND_VALUE(margin[0], int16_t);\
  1317. GS_GUI_BLEND_VALUE(margin[1], int16_t);\
  1318. GS_GUI_BLEND_VALUE(margin[2], int16_t);\
  1319. GS_GUI_BLEND_VALUE(margin[3], int16_t);\
  1320. } break;\
  1321. case GS_GUI_STYLE_PADDING_BOTTOM: {GS_GUI_BLEND_VALUE(padding[GS_GUI_PADDING_BOTTOM], int32_t);} break;\
  1322. case GS_GUI_STYLE_PADDING_TOP: {GS_GUI_BLEND_VALUE(padding[GS_GUI_PADDING_TOP], int32_t);} break;\
  1323. case GS_GUI_STYLE_PADDING_LEFT: {GS_GUI_BLEND_VALUE(padding[GS_GUI_PADDING_LEFT], int32_t);} break;\
  1324. case GS_GUI_STYLE_PADDING_RIGHT: {GS_GUI_BLEND_VALUE(padding[GS_GUI_PADDING_RIGHT], int32_t);} break;\
  1325. case GS_GUI_STYLE_PADDING: {\
  1326. GS_GUI_BLEND_VALUE(padding[0], int32_t);\
  1327. GS_GUI_BLEND_VALUE(padding[1], int32_t);\
  1328. GS_GUI_BLEND_VALUE(padding[2], int32_t);\
  1329. GS_GUI_BLEND_VALUE(padding[3], int32_t);\
  1330. } break;\
  1331. case GS_GUI_STYLE_SHADOW_X: {GS_GUI_BLEND_VALUE(shadow_x, int16_t);} break;\
  1332. case GS_GUI_STYLE_SHADOW_Y: {GS_GUI_BLEND_VALUE(shadow_y, int16_t);} break;\
  1333. }\
  1334. }\
  1335. } while (0)
  1336. // Get final blends
  1337. if (list && !gs_dyn_array_empty(list->properties[anim->end_state])) {
  1338. GS_GUI_BLEND_PROPERTIES(list->properties[anim->end_state]);
  1339. }
  1340. // Class list
  1341. if (has_class_animations)
  1342. {
  1343. for (uint32_t c = 0; c < GS_GUI_CLS_SELECTOR_MAX; ++c)
  1344. {
  1345. if (!cls_list[c]) continue;
  1346. if (!gs_dyn_array_empty(cls_list[c]->properties[anim->end_state])) {
  1347. GS_GUI_BLEND_PROPERTIES(cls_list[c]->properties[anim->end_state]);
  1348. }
  1349. }
  1350. }
  1351. // Id list
  1352. if (id_list && !gs_dyn_array_empty(id_list->properties[anim->end_state])) {
  1353. GS_GUI_BLEND_PROPERTIES(id_list->properties[anim->end_state]);
  1354. }
  1355. if (iss) {
  1356. GS_GUI_BLEND_PROPERTIES(iss->animations[anim->end_state]);
  1357. }
  1358. return ret;
  1359. }
  1360. static void _gs_gui_animation_get_time(gs_gui_context_t* ctx, gs_gui_id id, int32_t elementid,
  1361. const gs_gui_selector_desc_t* desc, gs_gui_inline_style_stack_t* iss, int32_t state, gs_gui_animation_t* anim)
  1362. {
  1363. uint32_t act = 0, ssz = 0;
  1364. if (iss && gs_dyn_array_size(iss->animations[state])) {
  1365. const uint32_t scz = gs_dyn_array_size(iss->animation_counts);
  1366. act = state == 0x00 ? iss->animation_counts[scz - 3] :
  1367. state == 0x01 ? iss->animation_counts[scz - 2] :
  1368. iss->animation_counts[scz - 1];
  1369. ssz = gs_dyn_array_size(iss->animations[state]);
  1370. }
  1371. gs_gui_animation_property_list_t* cls_list[GS_GUI_CLS_SELECTOR_MAX] = gs_default_val();
  1372. const gs_gui_animation_property_list_t* id_list = NULL;
  1373. const gs_gui_animation_property_list_t* list = NULL;
  1374. bool has_class_animations = false;
  1375. if (desc)
  1376. {
  1377. char TMP[256] = gs_default_val();
  1378. // Id animations
  1379. gs_snprintf(TMP, sizeof(TMP), "#%s", desc->id);
  1380. const uint64_t id_hash = gs_hash_str64(TMP);
  1381. if (gs_hash_table_exists(ctx->style_sheet->cid_animations, id_hash)) {
  1382. id_list = gs_hash_table_getp(ctx->style_sheet->cid_animations, id_hash);
  1383. }
  1384. // Class animations
  1385. for (uint32_t i = 0; i < GS_GUI_CLS_SELECTOR_MAX; ++i)
  1386. {
  1387. if (!desc->classes[i]) break;
  1388. gs_snprintf(TMP, sizeof(TMP), ".%s", desc->classes[i]);
  1389. const uint64_t cls_hash = gs_hash_str64(TMP);
  1390. if (gs_hash_table_exists(ctx->style_sheet->cid_animations, cls_hash)) {
  1391. cls_list[i] = gs_hash_table_getp(ctx->style_sheet->cid_animations, cls_hash);
  1392. has_class_animations = true;
  1393. }
  1394. }
  1395. }
  1396. // Element type animations
  1397. if (gs_hash_table_exists(ctx->style_sheet->animations, (gs_gui_element_type)elementid)) {
  1398. list = gs_hash_table_getp(ctx->style_sheet->animations, (gs_gui_element_type)elementid);
  1399. }
  1400. // Fill properties in order of specificity
  1401. gs_gui_animation_property_t properties[GS_GUI_STYLE_COUNT] = gs_default_val();
  1402. for (uint32_t i = 0; i < GS_GUI_STYLE_COUNT; ++i) {
  1403. properties[i].type = (gs_gui_style_element_type)i;
  1404. }
  1405. #define GUI_SET_PROPERTY_TIMES(PROP_LIST)\
  1406. do {\
  1407. for (uint32_t p = 0; p < gs_dyn_array_size((PROP_LIST)); ++p)\
  1408. {\
  1409. gs_gui_animation_property_t* prop = &(PROP_LIST)[p];\
  1410. properties[prop->type].time = prop->time;\
  1411. properties[prop->type].delay = prop->delay;\
  1412. }\
  1413. } while (0)
  1414. // Element type list
  1415. if (list)
  1416. {
  1417. gs_dyn_array(gs_gui_animation_property_t) props = list->properties[state];
  1418. GUI_SET_PROPERTY_TIMES(props);
  1419. }
  1420. // Class list
  1421. if (has_class_animations)
  1422. {
  1423. for (uint32_t c = 0; c < GS_GUI_CLS_SELECTOR_MAX; ++c)
  1424. {
  1425. if (!cls_list[c]) continue;
  1426. gs_dyn_array(gs_gui_animation_property_t) props = cls_list[c]->properties[state];
  1427. GUI_SET_PROPERTY_TIMES(props);
  1428. }
  1429. }
  1430. // Id list
  1431. if (id_list)
  1432. {
  1433. gs_dyn_array(gs_gui_animation_property_t) props = id_list->properties[state];
  1434. GUI_SET_PROPERTY_TIMES(props);
  1435. }
  1436. // Inline style list
  1437. if (act && iss)
  1438. {
  1439. for ( uint32_t a = 0; a < act; ++a )
  1440. {
  1441. uint32_t idx = ssz - act + a;
  1442. gs_gui_animation_property_t* ap = &iss->animations[state][idx];
  1443. properties[ap->type].time = ap->time;
  1444. properties[ap->type].delay = ap->delay;
  1445. }
  1446. }
  1447. // Set max times
  1448. for (uint32_t i = 0; i < GS_GUI_STYLE_COUNT; ++i)
  1449. {
  1450. if (properties[i].time > anim->max) anim->max = properties[i].time;
  1451. if (properties[i].delay > anim->delay) anim->delay = properties[i].delay;
  1452. }
  1453. // Finalize time
  1454. anim->max += anim->delay;
  1455. anim->max = gs_max(anim->max, 5);
  1456. }
  1457. 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)
  1458. {
  1459. gs_gui_animation_t* anim = NULL;
  1460. const bool32 valid_eid = (elementid >= 0 && elementid < GS_GUI_ELEMENT_COUNT);
  1461. // Construct new animation if necessary to insert
  1462. if (ctx->state_switch_id == id)
  1463. {
  1464. if (!gs_hash_table_exists(ctx->animations, id))
  1465. {
  1466. gs_gui_animation_t val = gs_default_val();
  1467. gs_hash_table_insert(ctx->animations, id, val);
  1468. }
  1469. gs_gui_inline_style_stack_t* iss = NULL;
  1470. if (gs_hash_table_exists(ctx->inline_styles, (gs_gui_element_type)elementid))
  1471. {
  1472. iss = gs_hash_table_getp(ctx->inline_styles, (gs_gui_element_type)elementid);
  1473. }
  1474. #define ANIM_GET_TIME(STATE)\
  1475. anim = gs_hash_table_getp(ctx->animations, id);
  1476. anim->playing = true;
  1477. int16_t focus_state = 0x00;
  1478. int16_t hover_state = 0x00;
  1479. int16_t direction = 0x00;
  1480. int16_t start_state = 0x00;
  1481. int16_t end_state = 0x00;
  1482. int16_t time_state = 0x00;
  1483. switch (ctx->switch_state)
  1484. {
  1485. case GS_GUI_ELEMENT_STATE_OFF_FOCUS:
  1486. {
  1487. if (ctx->hover == id)
  1488. {
  1489. anim->direction = GS_GUI_ANIMATION_DIRECTION_BACKWARD;
  1490. anim->start_state = GS_GUI_ELEMENT_STATE_HOVER;
  1491. anim->end_state = GS_GUI_ELEMENT_STATE_FOCUS;
  1492. time_state = GS_GUI_ELEMENT_STATE_HOVER;
  1493. if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
  1494. anim->time = anim->max;
  1495. }
  1496. else
  1497. {
  1498. anim->direction = GS_GUI_ANIMATION_DIRECTION_BACKWARD;
  1499. anim->start_state = GS_GUI_ELEMENT_STATE_DEFAULT;
  1500. anim->end_state = GS_GUI_ELEMENT_STATE_FOCUS;
  1501. time_state = GS_GUI_ELEMENT_STATE_DEFAULT;
  1502. if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
  1503. anim->time = anim->max;
  1504. }
  1505. } break;
  1506. case GS_GUI_ELEMENT_STATE_ON_FOCUS:
  1507. {
  1508. anim->direction = GS_GUI_ANIMATION_DIRECTION_FORWARD;
  1509. anim->start_state = GS_GUI_ELEMENT_STATE_HOVER;
  1510. anim->end_state = GS_GUI_ELEMENT_STATE_FOCUS;
  1511. time_state = GS_GUI_ELEMENT_STATE_FOCUS;
  1512. if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
  1513. anim->time = 0;
  1514. } break;
  1515. case GS_GUI_ELEMENT_STATE_OFF_HOVER:
  1516. {
  1517. anim->direction = GS_GUI_ANIMATION_DIRECTION_BACKWARD;
  1518. anim->start_state = GS_GUI_ELEMENT_STATE_DEFAULT;
  1519. anim->end_state = GS_GUI_ELEMENT_STATE_HOVER;
  1520. time_state = GS_GUI_ELEMENT_STATE_DEFAULT;
  1521. if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
  1522. anim->time = anim->max;
  1523. } break;
  1524. case GS_GUI_ELEMENT_STATE_ON_HOVER:
  1525. {
  1526. anim->direction = GS_GUI_ANIMATION_DIRECTION_FORWARD;
  1527. anim->start_state = GS_GUI_ELEMENT_STATE_DEFAULT;
  1528. anim->end_state = GS_GUI_ELEMENT_STATE_HOVER;
  1529. time_state = GS_GUI_ELEMENT_STATE_HOVER;
  1530. if (valid_eid) _gs_gui_animation_get_time(ctx, id, elementid, desc, iss, time_state, anim);
  1531. anim->time = 0;
  1532. } break;
  1533. }
  1534. // Reset state switches and id
  1535. ctx->state_switch_id = 0;
  1536. ctx->switch_state = 0;
  1537. return anim;
  1538. }
  1539. // Return if found
  1540. if (gs_hash_table_exists(ctx->animations, id)) {
  1541. anim = gs_hash_table_getp(ctx->animations, id);
  1542. }
  1543. if (anim && !anim->playing)
  1544. {
  1545. // This is causing a crash...
  1546. gs_hash_table_erase(ctx->animations, id);
  1547. anim = NULL;
  1548. }
  1549. return anim;
  1550. }
  1551. GS_API_DECL void gs_gui_animation_update(gs_gui_context_t* ctx, gs_gui_animation_t* anim)
  1552. {
  1553. if (ctx->frame == anim->frame) return;
  1554. const int16_t dt = (int16_t)(gs_platform_delta_time() * 1000.f);
  1555. if (anim->playing)
  1556. {
  1557. // Forward
  1558. switch (anim->direction)
  1559. {
  1560. default:
  1561. case (GS_GUI_ANIMATION_DIRECTION_FORWARD):
  1562. {
  1563. anim->time += dt;
  1564. if (anim->time >= anim->max)
  1565. {
  1566. anim->time = anim->max;
  1567. anim->playing = false;
  1568. }
  1569. } break;
  1570. case (GS_GUI_ANIMATION_DIRECTION_BACKWARD):
  1571. {
  1572. anim->time -= dt;
  1573. if (anim->time <= 0)
  1574. {
  1575. anim->time = 0;
  1576. anim->playing = false;
  1577. }
  1578. } break;
  1579. }
  1580. }
  1581. anim->frame = ctx->frame;
  1582. }
  1583. GS_API_DECL gs_gui_rect_t gs_gui_rect(float x, float y, float w, float h)
  1584. {
  1585. gs_gui_rect_t res;
  1586. res.x = x; res.y = y; res.w = w; res.h = h;
  1587. return res;
  1588. }
  1589. static gs_gui_rect_t gs_gui_expand_rect(gs_gui_rect_t rect, int16_t v[4])
  1590. {
  1591. return gs_gui_rect(rect.x - v[0],
  1592. rect.y - v[2],
  1593. rect.w + v[0] + v[1],
  1594. rect.h + v[2] + v[3]);
  1595. }
  1596. static gs_gui_rect_t gs_gui_intersect_rects(gs_gui_rect_t r1, gs_gui_rect_t r2)
  1597. {
  1598. int32_t x1 = (int32_t)gs_max(r1.x, r2.x);
  1599. int32_t y1 = (int32_t)gs_max(r1.y, r2.y);
  1600. int32_t x2 = (int32_t)gs_min(r1.x + r1.w, r2.x + r2.w);
  1601. int32_t y2 = (int32_t)gs_min(r1.y + r1.h, r2.y + r2.h);
  1602. if (x2 < x1) {x2 = x1;}
  1603. if (y2 < y1) {y2 = y1;}
  1604. return gs_gui_rect((float)x1, (float)y1, (float)x2 - (float)x1, (float)y2 - (float)y1);
  1605. }
  1606. static int32_t gs_gui_rect_overlaps_vec2(gs_gui_rect_t r, gs_vec2 p)
  1607. {
  1608. return p.x >= r.x && p.x < r.x + r.w && p.y >= r.y && p.y < r.y + r.h;
  1609. }
  1610. GS_API_DECL gs_gui_container_t* gs_gui_get_top_most_container(gs_gui_context_t* ctx, gs_gui_split_t* split)
  1611. {
  1612. if (!split) return NULL;
  1613. if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER) return split->children[0].container;
  1614. if (split->children[1].type == GS_GUI_SPLIT_NODE_CONTAINER) return split->children[1].container;
  1615. gs_gui_container_t* c0 = gs_gui_get_top_most_container(ctx, gs_slot_array_getp(ctx->splits, split->children[0].split));
  1616. gs_gui_container_t* c1 = gs_gui_get_top_most_container(ctx, gs_slot_array_getp(ctx->splits, split->children[1].split));
  1617. if (c0->zindex > c1->zindex) return c0;
  1618. return c1;
  1619. }
  1620. GS_API_DECL void gs_gui_bring_split_to_front(gs_gui_context_t* ctx, gs_gui_split_t* split)
  1621. {
  1622. if (!split) return;
  1623. if (!split->parent)
  1624. {
  1625. gs_snprintfc(TMP, 256, "!dockspace%zu", (size_t)split);
  1626. gs_gui_id id = gs_gui_get_id(ctx, TMP, 256);
  1627. gs_gui_container_t* cnt = gs_gui_get_container(ctx, TMP);
  1628. // if (cnt) gs_gui_bring_to_front(ctx, cnt);
  1629. // cnt->zindex = 0;
  1630. }
  1631. gs_gui_split_node_t* c0 = &split->children[0];
  1632. gs_gui_split_node_t* c1 = &split->children[1];
  1633. if (c0->type == GS_GUI_SPLIT_NODE_CONTAINER)
  1634. {
  1635. gs_gui_bring_to_front(ctx, c0->container);
  1636. // ctx->hover = c0;
  1637. }
  1638. else
  1639. {
  1640. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, c0->split);
  1641. gs_gui_bring_split_to_front(ctx, s);
  1642. }
  1643. if (c1->type == GS_GUI_SPLIT_NODE_CONTAINER)
  1644. {
  1645. gs_gui_bring_to_front(ctx, c1->container);
  1646. // ctx->hover = c1;
  1647. }
  1648. else
  1649. {
  1650. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, c1->split);
  1651. gs_gui_bring_split_to_front(ctx, s);
  1652. }
  1653. }
  1654. static void gs_gui_update_split(gs_gui_context_t* ctx, gs_gui_split_t* split)
  1655. {
  1656. // Iterate through children, resize them based on size/position
  1657. const gs_gui_rect_t* sr = &split->rect;
  1658. const float ratio = split->ratio;
  1659. switch (split->type)
  1660. {
  1661. case GS_GUI_SPLIT_LEFT:
  1662. {
  1663. if (split->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
  1664. {
  1665. // Update split
  1666. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_PARENT].split);
  1667. s->rect = gs_gui_rect(sr->x + sr->w * ratio, sr->y, sr->w * (1.f - ratio), sr->h);
  1668. gs_gui_update_split(ctx, s);
  1669. }
  1670. if (split->children[GS_GUI_SPLIT_NODE_CHILD].type == GS_GUI_SPLIT_NODE_SPLIT)
  1671. {
  1672. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_CHILD].split);
  1673. s->rect = gs_gui_rect(sr->x, sr->y, sr->w * (ratio), sr->h);
  1674. gs_gui_update_split(ctx, s);
  1675. }
  1676. } break;
  1677. case GS_GUI_SPLIT_RIGHT:
  1678. {
  1679. if (split->children[GS_GUI_SPLIT_NODE_CHILD].type == GS_GUI_SPLIT_NODE_SPLIT)
  1680. {
  1681. // Update split
  1682. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_CHILD].split);
  1683. s->rect = gs_gui_rect(sr->x + sr->w * (1.f - ratio), sr->y, sr->w * (ratio), sr->h);
  1684. gs_gui_update_split(ctx, s);
  1685. }
  1686. if (split->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
  1687. {
  1688. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_PARENT].split);
  1689. s->rect = gs_gui_rect(sr->x, sr->y, sr->w * (1.f - ratio), sr->h);
  1690. gs_gui_update_split(ctx, s);
  1691. }
  1692. } break;
  1693. case GS_GUI_SPLIT_TOP:
  1694. {
  1695. if (split->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
  1696. {
  1697. // Update split
  1698. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_PARENT].split);
  1699. s->rect = gs_gui_rect(sr->x, sr->y + sr->h * (ratio), sr->w, sr->h * (1.f - ratio));
  1700. gs_gui_update_split(ctx, s);
  1701. }
  1702. if (split->children[GS_GUI_SPLIT_NODE_CHILD].type == GS_GUI_SPLIT_NODE_SPLIT)
  1703. {
  1704. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_CHILD].split);
  1705. s->rect = gs_gui_rect(sr->x, sr->y, sr->w, sr->h * (ratio));
  1706. gs_gui_update_split(ctx, s);
  1707. }
  1708. } break;
  1709. case GS_GUI_SPLIT_BOTTOM:
  1710. {
  1711. if (split->children[GS_GUI_SPLIT_NODE_CHILD].type == GS_GUI_SPLIT_NODE_SPLIT)
  1712. {
  1713. // Update split
  1714. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_CHILD].split);
  1715. s->rect = gs_gui_rect(sr->x, sr->y + sr->h * (1.f - ratio), sr->w, sr->h * (ratio));
  1716. gs_gui_update_split(ctx, s);
  1717. }
  1718. if (split->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
  1719. {
  1720. gs_gui_split_t* s = gs_slot_array_getp(ctx->splits, split->children[GS_GUI_SPLIT_NODE_PARENT].split);
  1721. s->rect = gs_gui_rect(sr->x, sr->y, sr->w, sr->h * (1.f - ratio));
  1722. gs_gui_update_split(ctx, s);
  1723. }
  1724. } break;
  1725. }
  1726. }
  1727. static gs_gui_split_t* gs_gui_get_root_split_from_split(gs_gui_context_t* ctx, gs_gui_split_t* split)
  1728. {
  1729. if (!split) return NULL;
  1730. // Cache top root level split
  1731. gs_gui_split_t* root_split = split && split->parent ? gs_slot_array_getp(ctx->splits, split->parent) : split ? split : NULL;
  1732. while (root_split && root_split->parent)
  1733. {
  1734. root_split = gs_slot_array_getp(ctx->splits, root_split->parent);
  1735. }
  1736. return root_split;
  1737. }
  1738. static gs_gui_split_t* gs_gui_get_root_split(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
  1739. {
  1740. gs_gui_split_t* split = gs_gui_get_split(ctx, cnt);
  1741. if (split) return gs_gui_get_root_split_from_split(ctx, split);
  1742. else return NULL;
  1743. }
  1744. GS_API_DECL gs_gui_container_t* gs_gui_get_root_container_from_split(gs_gui_context_t* ctx, gs_gui_split_t* split)
  1745. {
  1746. gs_gui_split_t* root = gs_gui_get_root_split_from_split(ctx, split);
  1747. gs_gui_split_t* s = root;
  1748. gs_gui_container_t* c = NULL;
  1749. while (s && !c)
  1750. {
  1751. if (s->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
  1752. {
  1753. s = gs_slot_array_getp(ctx->splits, s->children[GS_GUI_SPLIT_NODE_PARENT].split);
  1754. }
  1755. else if (s->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_CONTAINER)
  1756. {
  1757. c = s->children[GS_GUI_SPLIT_NODE_PARENT].container;
  1758. }
  1759. else
  1760. {
  1761. s = NULL;
  1762. }
  1763. }
  1764. return c;
  1765. }
  1766. GS_API_DECL gs_gui_container_t* gs_gui_get_root_container(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
  1767. {
  1768. gs_gui_container_t* parent = gs_gui_get_parent(ctx, cnt);
  1769. if (parent->split)
  1770. {
  1771. gs_gui_split_t* root = gs_gui_get_root_split(ctx, parent);
  1772. gs_gui_split_t* s = root;
  1773. gs_gui_container_t* c = NULL;
  1774. while (s && !c)
  1775. {
  1776. if (s->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_SPLIT)
  1777. {
  1778. s = gs_slot_array_getp(ctx->splits, s->children[GS_GUI_SPLIT_NODE_PARENT].split);
  1779. }
  1780. else if (s->children[GS_GUI_SPLIT_NODE_PARENT].type == GS_GUI_SPLIT_NODE_CONTAINER)
  1781. {
  1782. c = s->children[GS_GUI_SPLIT_NODE_PARENT].container;
  1783. }
  1784. else
  1785. {
  1786. s = NULL;
  1787. }
  1788. }
  1789. return c;
  1790. }
  1791. return parent;
  1792. }
  1793. GS_API_DECL gs_gui_tab_bar_t* gs_gui_get_tab_bar(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
  1794. {
  1795. 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);
  1796. }
  1797. GS_API_DECL gs_gui_split_t* gs_gui_get_split(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
  1798. {
  1799. gs_gui_tab_bar_t* tab_bar = gs_gui_get_tab_bar(ctx, cnt);
  1800. gs_gui_tab_item_t* tab_item = tab_bar ? &tab_bar->items[cnt->tab_item] : NULL;
  1801. gs_gui_split_t* split = cnt->split ? gs_slot_array_getp(ctx->splits, cnt->split) : NULL;
  1802. // Look at split if in tab group
  1803. if (!split && tab_bar)
  1804. {
  1805. for (uint32_t i = 0; i < tab_bar->size; ++i)
  1806. {
  1807. if (((gs_gui_container_t*)tab_bar->items[i].data)->split)
  1808. {
  1809. split = gs_slot_array_getp(ctx->splits, ((gs_gui_container_t*)tab_bar->items[i].data)->split);
  1810. }
  1811. }
  1812. }
  1813. return split;
  1814. }
  1815. static gs_gui_command_t* gs_gui_push_jump(gs_gui_context_t* ctx, gs_gui_command_t* dst)
  1816. {
  1817. gs_gui_command_t* cmd;
  1818. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_JUMP, sizeof(gs_gui_jumpcommand_t));
  1819. cmd->jump.dst = dst;
  1820. return cmd;
  1821. }
  1822. static void gs_gui_draw_frame(gs_gui_context_t* ctx, gs_gui_rect_t rect, gs_gui_style_t* style)
  1823. {
  1824. gs_gui_draw_rect(ctx, rect, style->colors[GS_GUI_COLOR_BACKGROUND]);
  1825. // draw border
  1826. if (style->colors[GS_GUI_COLOR_BORDER].a)
  1827. {
  1828. 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]);
  1829. }
  1830. }
  1831. static int32_t gs_gui_compare_zindex(const void *a, const void *b)
  1832. {
  1833. return (*(gs_gui_container_t**) a)->zindex - (*(gs_gui_container_t**) b)->zindex;
  1834. }
  1835. static gs_gui_style_t* gs_gui_push_style(gs_gui_context_t* ctx, gs_gui_style_t* style)
  1836. {
  1837. gs_gui_style_t* save = ctx->style;
  1838. ctx->style = style;
  1839. return save;
  1840. }
  1841. 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)
  1842. {
  1843. if (elementid >= GS_GUI_ELEMENT_COUNT || !desc)
  1844. {
  1845. return;
  1846. }
  1847. if (!gs_hash_table_exists(ctx->inline_styles, elementid))
  1848. {
  1849. gs_gui_inline_style_stack_t v = gs_default_val();
  1850. gs_hash_table_insert(ctx->inline_styles, elementid, v);
  1851. }
  1852. gs_gui_inline_style_stack_t* iss = gs_hash_table_getp(ctx->inline_styles, elementid);
  1853. gs_assert(iss);
  1854. // Counts to keep for popping off
  1855. uint32_t style_ct[3] = gs_default_val(), anim_ct[3] = gs_default_val();
  1856. if (desc->all.style.data && desc->all.style.size)
  1857. {
  1858. // Total amount to write for each section
  1859. uint32_t ct = desc->all.style.size / sizeof(gs_gui_style_element_t);
  1860. style_ct[0] += ct;
  1861. style_ct[1] += ct;
  1862. style_ct[2] += ct;
  1863. // Iterate through all properties, then just push them back into style element list
  1864. for (uint32_t i = 0; i < ct; ++i)
  1865. {
  1866. gs_dyn_array_push(iss->styles[0], desc->all.style.data[i]);
  1867. gs_dyn_array_push(iss->styles[1], desc->all.style.data[i]);
  1868. gs_dyn_array_push(iss->styles[2], desc->all.style.data[i]);
  1869. }
  1870. }
  1871. if (desc->all.animation.data && desc->all.animation.size)
  1872. {
  1873. // Total amount to write for each section
  1874. uint32_t ct = desc->all.animation.size / sizeof(gs_gui_animation_property_t);
  1875. anim_ct[0] += ct;
  1876. anim_ct[1] += ct;
  1877. anim_ct[2] += ct;
  1878. for (uint32_t i = 0; i < ct; ++i)
  1879. {
  1880. gs_dyn_array_push(iss->animations[0], desc->all.animation.data[i]);
  1881. gs_dyn_array_push(iss->animations[1], desc->all.animation.data[i]);
  1882. gs_dyn_array_push(iss->animations[2], desc->all.animation.data[i]);
  1883. }
  1884. }
  1885. #define GS_GUI_COPY_INLINE_STYLE(TYPE, INDEX)\
  1886. do {\
  1887. if (desc->TYPE.style.data && desc->TYPE.style.size)\
  1888. {\
  1889. uint32_t ct = desc->TYPE.style.size / sizeof(gs_gui_style_element_t);\
  1890. style_ct[INDEX] += ct;\
  1891. for (uint32_t i = 0; i < ct; ++i)\
  1892. {\
  1893. gs_dyn_array_push(iss->styles[INDEX], desc->TYPE.style.data[i]);\
  1894. }\
  1895. }\
  1896. if (desc->TYPE.animation.data && desc->TYPE.animation.size)\
  1897. {\
  1898. uint32_t ct = desc->TYPE.animation.size / sizeof(gs_gui_animation_property_t);\
  1899. anim_ct[INDEX] += ct;\
  1900. \
  1901. for (uint32_t i = 0; i < ct; ++i)\
  1902. {\
  1903. gs_dyn_array_push(iss->animations[INDEX], desc->TYPE.animation.data[i]);\
  1904. }\
  1905. }\
  1906. } while (0)
  1907. // Copy remaining individual styles
  1908. GS_GUI_COPY_INLINE_STYLE(def, 0);
  1909. GS_GUI_COPY_INLINE_STYLE(hover, 1);
  1910. GS_GUI_COPY_INLINE_STYLE(focus, 2);
  1911. // Add final counts
  1912. gs_dyn_array_push(iss->style_counts, style_ct[0]);
  1913. gs_dyn_array_push(iss->style_counts, style_ct[1]);
  1914. gs_dyn_array_push(iss->style_counts, style_ct[2]);
  1915. gs_dyn_array_push(iss->animation_counts, anim_ct[0]);
  1916. gs_dyn_array_push(iss->animation_counts, anim_ct[1]);
  1917. gs_dyn_array_push(iss->animation_counts, anim_ct[2]);
  1918. }
  1919. GS_API_DECL void gs_gui_pop_inline_style(gs_gui_context_t* ctx, gs_gui_element_type elementid)
  1920. {
  1921. if (elementid >= GS_GUI_ELEMENT_COUNT)
  1922. {
  1923. return;
  1924. }
  1925. if (!gs_hash_table_exists(ctx->inline_styles, elementid))
  1926. {
  1927. return;
  1928. }
  1929. gs_gui_inline_style_stack_t* iss = gs_hash_table_getp(ctx->inline_styles, elementid);
  1930. gs_assert(iss);
  1931. if (gs_dyn_array_size(iss->style_counts) >= 3)
  1932. {
  1933. const uint32_t sz = gs_dyn_array_size(iss->style_counts);
  1934. uint32_t c0 = iss->style_counts[sz - 3]; // default
  1935. uint32_t c1 = iss->style_counts[sz - 2]; // hover
  1936. uint32_t c2 = iss->style_counts[sz - 1]; // focus
  1937. // Pop off elements
  1938. if (iss->styles[0]) gs_dyn_array_head(iss->styles[0])->size -= c0;
  1939. if (iss->styles[1]) gs_dyn_array_head(iss->styles[1])->size -= c1;
  1940. if (iss->styles[2]) gs_dyn_array_head(iss->styles[2])->size -= c2;
  1941. }
  1942. if (gs_dyn_array_size(iss->animation_counts) >= 3)
  1943. {
  1944. const uint32_t sz = gs_dyn_array_size(iss->animation_counts);
  1945. uint32_t c0 = iss->animation_counts[sz - 3]; // default
  1946. uint32_t c1 = iss->animation_counts[sz - 2]; // hover
  1947. uint32_t c2 = iss->animation_counts[sz - 1]; // focus
  1948. // Pop off elements
  1949. if (iss->animations[0]) gs_dyn_array_head(iss->animations[0])->size -= c0;
  1950. if (iss->animations[1]) gs_dyn_array_head(iss->animations[1])->size -= c1;
  1951. if (iss->animations[2]) gs_dyn_array_head(iss->animations[2])->size -= c2;
  1952. }
  1953. }
  1954. static void gs_gui_pop_style(gs_gui_context_t* ctx, gs_gui_style_t* style)
  1955. {
  1956. ctx->style = style;
  1957. }
  1958. static void gs_gui_push_layout(gs_gui_context_t *ctx, gs_gui_rect_t body, gs_vec2 scroll)
  1959. {
  1960. gs_gui_layout_t layout;
  1961. int32_t width = 0;
  1962. memset(&layout, 0, sizeof(layout));
  1963. layout.body = gs_gui_rect(body.x - scroll.x, body.y - scroll.y, body.w, body.h);
  1964. layout.max = gs_v2(-0x1000000, -0x1000000);
  1965. layout.direction = ctx->style->direction;
  1966. layout.justify_content = ctx->style->justify_content;
  1967. layout.align_content = ctx->style->align_content;
  1968. memcpy(layout.padding, ctx->style->padding, sizeof(int32_t) * 4);
  1969. gs_gui_stack_push(ctx->layout_stack, layout);
  1970. gs_gui_layout_row(ctx, 1, &width, 0);
  1971. }
  1972. static void gs_gui_pop_layout(gs_gui_context_t* ctx)
  1973. {
  1974. gs_gui_stack_pop(ctx->layout_stack);
  1975. }
  1976. GS_API_DECL gs_gui_layout_t*
  1977. gs_gui_get_layout(gs_gui_context_t *ctx)
  1978. {
  1979. return &ctx->layout_stack.items[ctx->layout_stack.idx - 1];
  1980. }
  1981. static void gs_gui_pop_container(gs_gui_context_t *ctx)
  1982. {
  1983. gs_gui_container_t *cnt = gs_gui_get_current_container(ctx);
  1984. gs_gui_layout_t *layout = gs_gui_get_layout(ctx);
  1985. cnt->content_size.x = layout->max.x - layout->body.x;
  1986. cnt->content_size.y = layout->max.y - layout->body.y;
  1987. /* pop container, layout and id */
  1988. gs_gui_stack_pop(ctx->container_stack);
  1989. gs_gui_stack_pop(ctx->layout_stack);
  1990. gs_gui_pop_id(ctx);
  1991. }
  1992. #define gs_gui_scrollbar(ctx, cnt, b, cs, x, y, w, h) \
  1993. do { \
  1994. /* only add scrollbar if content size is larger than body */ \
  1995. int32_t maxscroll = (int32_t)(cs.y - b->h); \
  1996. \
  1997. if (maxscroll > 0 && b->h > 0) { \
  1998. gs_gui_rect_t base, thumb; \
  1999. gs_gui_id id = gs_gui_get_id(ctx, "!scrollbar" #y, 11); \
  2000. const int32_t elementid = GS_GUI_ELEMENT_SCROLL; \
  2001. gs_gui_style_t style = gs_default_val(); \
  2002. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid); \
  2003. \
  2004. /* Update anim (keep states locally within animation, only way to do this)*/ \
  2005. if (anim) \
  2006. { \
  2007. gs_gui_animation_update(ctx, anim); \
  2008. \
  2009. /* Get blended style based on animation*/ \
  2010. style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid); \
  2011. } \
  2012. else \
  2013. { \
  2014. style = ctx->focus == id ? \
  2015. gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) : \
  2016. ctx->hover == id ? \
  2017. gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) : \
  2018. gs_gui_get_current_element_style(ctx, desc, elementid, 0x00); \
  2019. } \
  2020. \
  2021. int32_t sz = (int32_t)style.size[0]; \
  2022. if (cs.y > cnt->body.h) {body->w -= sz;} \
  2023. if (cs.x > cnt->body.w) {body->h -= sz;} \
  2024. \
  2025. /* get sizing / positioning */ \
  2026. base = *b; \
  2027. base.x = b->x + b->w; \
  2028. base.w = style.size[0]; \
  2029. \
  2030. /* handle input */ \
  2031. gs_gui_update_control(ctx, id, base, 0); \
  2032. if (ctx->focus == id && ctx->mouse_down == GS_GUI_MOUSE_LEFT) { \
  2033. cnt->scroll.y += ctx->mouse_delta.y * cs.y / base.h; \
  2034. } \
  2035. /* clamp scroll to limits */ \
  2036. cnt->scroll.y = gs_clamp(cnt->scroll.y, 0, maxscroll); \
  2037. int32_t state = ctx->focus == id ? GS_GUI_ELEMENT_STATE_FOCUS : \
  2038. ctx->hover == id ? GS_GUI_ELEMENT_STATE_HOVER : 0x00; \
  2039. \
  2040. /* draw base and thumb */ \
  2041. gs_gui_draw_rect(ctx, base, style.colors[GS_GUI_COLOR_BACKGROUND]); \
  2042. /* draw border*/ \
  2043. if (style.colors[GS_GUI_COLOR_BORDER].a) \
  2044. { \
  2045. gs_gui_draw_box(ctx, gs_gui_expand_rect(base, (int16_t*)style.border_width), \
  2046. (int16_t*)style.border_width, style.colors[GS_GUI_COLOR_BORDER]); \
  2047. } \
  2048. float pl = ((float)style.padding[GS_GUI_PADDING_LEFT]); \
  2049. float pr = ((float)style.padding[GS_GUI_PADDING_RIGHT]); \
  2050. float pt = ((float)style.padding[GS_GUI_PADDING_TOP]); \
  2051. float pb = ((float)style.padding[GS_GUI_PADDING_BOTTOM]); \
  2052. float w = ((float)base.w - pr); \
  2053. float x = (float)(base.x + pl); \
  2054. thumb = base; \
  2055. thumb.x = x; thumb.w = w; \
  2056. thumb.h = gs_max(style.thumb_size, base.h * b->h / cs.y) - pb; \
  2057. thumb.y += cnt->scroll.y * (base.h - thumb.h) / maxscroll + pt; \
  2058. gs_gui_draw_rect(ctx, thumb, style.colors[GS_GUI_COLOR_CONTENT]); \
  2059. /* draw border*/ \
  2060. if (style.colors[GS_GUI_COLOR_BORDER].a) \
  2061. { \
  2062. gs_gui_draw_box(ctx, gs_gui_expand_rect(thumb, (int16_t*)style.border_width), \
  2063. (int16_t*)style.border_width, style.colors[GS_GUI_COLOR_BORDER]); \
  2064. } \
  2065. \
  2066. /* set this as the scroll_target (will get scrolled on mousewheel) */ \
  2067. /* if the mouse is over it */ \
  2068. if ( \
  2069. gs_gui_mouse_over(ctx, *b) || \
  2070. gs_gui_mouse_over(ctx, base) || \
  2071. gs_gui_mouse_over(ctx, thumb) \
  2072. ) \
  2073. { \
  2074. ctx->scroll_target = cnt; \
  2075. } \
  2076. } \
  2077. } while (0)
  2078. 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)
  2079. {
  2080. int32_t sz = (int32_t)ctx->style_sheet->styles[GS_GUI_ELEMENT_SCROLL][0x00].size[0];
  2081. gs_vec2 cs = cnt->content_size;
  2082. cs.x += ctx->style->padding[GS_GUI_PADDING_LEFT] * 2;
  2083. cs.y += ctx->style->padding[GS_GUI_PADDING_TOP] * 2;
  2084. gs_gui_push_clip_rect(ctx, *body);
  2085. /* resize body to make room for scrollbars */
  2086. if (cs.y > cnt->body.h) { body->w -= sz; }
  2087. if (cs.x > cnt->body.w) { body->h -= sz; }
  2088. /* to create a horizontal or vertical scrollbar almost-identical code is
  2089. ** used; only the references to `x|y` `w|h` need to be switched */
  2090. gs_gui_scrollbar(ctx, cnt, body, cs, x, y, w, h);
  2091. if (~opt & GS_GUI_OPT_NOSCROLLHORIZONTAL)
  2092. {
  2093. gs_gui_scrollbar(ctx, cnt, body, cs, y, x, h, w);
  2094. }
  2095. if (cs.y <= cnt->body.h) {cnt->scroll.y = 0;}
  2096. if (cs.x <= cnt->body.w) {cnt->scroll.x = 0;}
  2097. gs_gui_pop_clip_rect(ctx);
  2098. }
  2099. 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)
  2100. {
  2101. if (~opt & GS_GUI_OPT_NOSCROLL) {gs_gui_scrollbars(ctx, cnt, &body, desc, opt);}
  2102. int32_t* padding = ctx->style->padding;
  2103. float l = body.x + padding[GS_GUI_PADDING_LEFT];
  2104. float t = body.y + padding[GS_GUI_PADDING_TOP];
  2105. float r = body.x + body.w - padding[GS_GUI_PADDING_RIGHT];
  2106. float b = body.y + body.h - padding[GS_GUI_PADDING_BOTTOM];
  2107. gs_gui_rect_t rect = gs_gui_rect(l, t, r - l, b - t);
  2108. gs_gui_push_layout(ctx, rect, cnt->scroll);
  2109. cnt->body = body;
  2110. }
  2111. static void gs_gui_begin_root_container(gs_gui_context_t *ctx, gs_gui_container_t *cnt, uint64_t opt)
  2112. {
  2113. gs_gui_stack_push(ctx->container_stack, cnt);
  2114. /* push container to roots list and push head command */
  2115. gs_gui_stack_push(ctx->root_list, cnt);
  2116. cnt->head = gs_gui_push_jump(ctx, NULL);
  2117. /* set as hover root if the mouse is overlapping this container and it has a
  2118. ** higher zindex than the current hover root */
  2119. if (
  2120. gs_gui_rect_overlaps_vec2(cnt->rect, ctx->mouse_pos) &&
  2121. (!ctx->next_hover_root || cnt->zindex > ctx->next_hover_root->zindex) &&
  2122. ~opt & GS_GUI_OPT_NOHOVER && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE
  2123. )
  2124. {
  2125. ctx->next_hover_root = cnt;
  2126. }
  2127. /* clipping is reset here in case a root-container is made within
  2128. ** another root-containers's begin/end block; this prevents the inner
  2129. ** root-container being clipped to the outer */
  2130. gs_gui_stack_push(ctx->clip_stack, gs_gui_unclipped_rect);
  2131. }
  2132. static void gs_gui_root_container_end(gs_gui_context_t *ctx)
  2133. {
  2134. /* push tail 'goto' jump command and set head 'skip' command. the final steps
  2135. ** on initing these are done in gs_gui_end() */
  2136. gs_gui_container_t *cnt = gs_gui_get_current_container(ctx);
  2137. cnt->tail = gs_gui_push_jump(ctx, NULL);
  2138. cnt->head->jump.dst = ctx->command_list.items + ctx->command_list.idx;
  2139. /* pop base clip rect and container */
  2140. gs_gui_pop_clip_rect(ctx);
  2141. gs_gui_pop_container(ctx);
  2142. }
  2143. #define GS_GUI_COPY_STYLES(DST, SRC, ELEM)\
  2144. do {\
  2145. DST[ELEM][0x00] = SRC[ELEM][0x00];\
  2146. DST[ELEM][0x01] = SRC[ELEM][0x01];\
  2147. DST[ELEM][0x02] = SRC[ELEM][0x02];\
  2148. } while (0)
  2149. 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)
  2150. {
  2151. // Generate new style sheet based on default element styles
  2152. gs_gui_style_sheet_t style_sheet = gs_default_val();
  2153. // Copy all default styles
  2154. GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_CONTAINER);
  2155. GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_LABEL);
  2156. GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_TEXT);
  2157. GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_PANEL);
  2158. GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_INPUT);
  2159. GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_BUTTON);
  2160. GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_SCROLL);
  2161. GS_GUI_COPY_STYLES(style_sheet.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_IMAGE);
  2162. // 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);
  2163. #define GS_GUI_APPLY_STYLE_ELEMENT(ELEMENT, TYPE)\
  2164. do {\
  2165. if ((ELEMENT).all.style.data)\
  2166. {\
  2167. gs_gui_style_sheet_set_element_styles(&style_sheet, TYPE, GS_GUI_ELEMENT_STATE_NEG, (ELEMENT).all.style.data, (ELEMENT).all.style.size);\
  2168. }\
  2169. else if ((ELEMENT).def.style.data)\
  2170. {\
  2171. gs_gui_style_sheet_set_element_styles(&style_sheet, TYPE, GS_GUI_ELEMENT_STATE_DEFAULT, (ELEMENT).def.style.data, (ELEMENT).def.style.size);\
  2172. }\
  2173. if ((ELEMENT).hover.style.data)\
  2174. {\
  2175. gs_gui_style_sheet_set_element_styles(&style_sheet, TYPE, GS_GUI_ELEMENT_STATE_HOVER, (ELEMENT).hover.style.data, (ELEMENT).hover.style.size);\
  2176. }\
  2177. if ((ELEMENT).focus.style.data)\
  2178. {\
  2179. gs_gui_style_sheet_set_element_styles(&style_sheet, TYPE, GS_GUI_ELEMENT_STATE_FOCUS, (ELEMENT).focus.style.data, (ELEMENT).focus.style.size);\
  2180. }\
  2181. } while (0)
  2182. // Iterate through descriptor
  2183. if (desc)
  2184. {
  2185. GS_GUI_APPLY_STYLE_ELEMENT(desc->button, GS_GUI_ELEMENT_BUTTON);
  2186. GS_GUI_APPLY_STYLE_ELEMENT(desc->container, GS_GUI_ELEMENT_CONTAINER);
  2187. GS_GUI_APPLY_STYLE_ELEMENT(desc->panel, GS_GUI_ELEMENT_PANEL);
  2188. GS_GUI_APPLY_STYLE_ELEMENT(desc->scroll, GS_GUI_ELEMENT_SCROLL);
  2189. GS_GUI_APPLY_STYLE_ELEMENT(desc->image, GS_GUI_ELEMENT_IMAGE);
  2190. GS_GUI_APPLY_STYLE_ELEMENT(desc->label, GS_GUI_ELEMENT_LABEL);
  2191. GS_GUI_APPLY_STYLE_ELEMENT(desc->text, GS_GUI_ELEMENT_TEXT);
  2192. }
  2193. #define COPY_ANIM_DATA(TYPE, ELEMENT)\
  2194. do {\
  2195. /* Apply animations */\
  2196. if (desc->TYPE.all.animation.data)\
  2197. {\
  2198. int32_t cnt = desc->TYPE.all.animation.size / sizeof(gs_gui_animation_property_t);\
  2199. if (!gs_hash_table_exists(style_sheet.animations, ELEMENT)) {\
  2200. gs_gui_animation_property_list_t v = gs_default_val();\
  2201. gs_hash_table_insert(style_sheet.animations, ELEMENT, v);\
  2202. }\
  2203. gs_gui_animation_property_list_t* list = gs_hash_table_getp(style_sheet.animations, ELEMENT);\
  2204. gs_assert(list);\
  2205. /* Register animation properties for all */\
  2206. for (uint32_t i = 0; i < 3; ++i)\
  2207. {\
  2208. for (uint32_t c = 0; c < cnt; ++c)\
  2209. {\
  2210. gs_dyn_array_push(list->properties[i], desc->TYPE.all.animation.data[c]);\
  2211. }\
  2212. }\
  2213. }\
  2214. } while (0)
  2215. // Copy animations
  2216. COPY_ANIM_DATA(button, GS_GUI_ELEMENT_BUTTON);
  2217. COPY_ANIM_DATA(label, GS_GUI_ELEMENT_LABEL);
  2218. COPY_ANIM_DATA(scroll, GS_GUI_ELEMENT_SCROLL);
  2219. COPY_ANIM_DATA(image, GS_GUI_ELEMENT_IMAGE);
  2220. COPY_ANIM_DATA(panel, GS_GUI_ELEMENT_PANEL);
  2221. COPY_ANIM_DATA(text, GS_GUI_ELEMENT_TEXT);
  2222. COPY_ANIM_DATA(container, GS_GUI_ELEMENT_CONTAINER);
  2223. return style_sheet;
  2224. }
  2225. GS_API_DECL void
  2226. gs_gui_style_sheet_destroy(gs_gui_style_sheet_t* ss)
  2227. {
  2228. // Need to free all animations
  2229. if (!ss || !ss->animations) {
  2230. gs_log_warning("Trying to destroy invalid style sheet");
  2231. return;
  2232. }
  2233. for (
  2234. gs_hash_table_iter it = gs_hash_table_iter_new(ss->animations);
  2235. gs_hash_table_iter_valid(ss->animations, it);
  2236. gs_hash_table_iter_advance(ss->animations, it)
  2237. )
  2238. {
  2239. gs_gui_animation_property_list_t* list = gs_hash_table_iter_getp(ss->animations, it);
  2240. for (uint32_t i = 0; i < 3; ++i)
  2241. {
  2242. gs_dyn_array_free(list->properties[i]);
  2243. }
  2244. }
  2245. gs_hash_table_free(ss->animations);
  2246. }
  2247. GS_API_DECL void
  2248. gs_gui_set_style_sheet(gs_gui_context_t* ctx, gs_gui_style_sheet_t* style_sheet)
  2249. {
  2250. ctx->style_sheet = style_sheet ? style_sheet : &gs_gui_default_style_sheet;
  2251. }
  2252. GS_API_DECL void
  2253. 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)
  2254. {
  2255. const uint32_t count = size / sizeof(gs_gui_style_element_t);
  2256. uint32_t idx_cnt = 1;
  2257. uint32_t idx = 0;
  2258. // Switch on state
  2259. switch (state)
  2260. {
  2261. // Do all
  2262. default: idx_cnt = 3; break;
  2263. case GS_GUI_ELEMENT_STATE_DEFAULT: idx = 0; break;
  2264. case GS_GUI_ELEMENT_STATE_HOVER: idx = 1; break;
  2265. case GS_GUI_ELEMENT_STATE_FOCUS: idx = 2; break;
  2266. }
  2267. for (uint32_t s = idx, c = 0; c < idx_cnt; ++s, ++c)
  2268. {
  2269. gs_gui_style_t* cs = &ss->styles[element][s];
  2270. for (uint32_t i = 0; i < count; ++i)
  2271. {
  2272. gs_gui_style_element_t* se = &styles[i];
  2273. switch (se->type)
  2274. {
  2275. // Width/Height
  2276. case GS_GUI_STYLE_WIDTH: cs->size[0] = (float)se->value; break;
  2277. case GS_GUI_STYLE_HEIGHT: cs->size[1] = (float)se->value; break;
  2278. // Padding
  2279. case GS_GUI_STYLE_PADDING: {
  2280. cs->padding[GS_GUI_PADDING_LEFT] = (int32_t)se->value;
  2281. cs->padding[GS_GUI_PADDING_TOP] = (int32_t)se->value;
  2282. cs->padding[GS_GUI_PADDING_RIGHT] = (int32_t)se->value;
  2283. cs->padding[GS_GUI_PADDING_BOTTOM] = (int32_t)se->value;
  2284. }
  2285. case GS_GUI_STYLE_PADDING_LEFT: cs->padding[GS_GUI_PADDING_LEFT] = (int32_t)se->value; break;
  2286. case GS_GUI_STYLE_PADDING_TOP: cs->padding[GS_GUI_PADDING_TOP] = (int32_t)se->value; break;
  2287. case GS_GUI_STYLE_PADDING_RIGHT: cs->padding[GS_GUI_PADDING_RIGHT] = (int32_t)se->value; break;
  2288. case GS_GUI_STYLE_PADDING_BOTTOM: cs->padding[GS_GUI_PADDING_BOTTOM] = (int32_t)se->value; break;
  2289. case GS_GUI_STYLE_MARGIN: {
  2290. cs->margin[GS_GUI_MARGIN_LEFT] = (int32_t)se->value;
  2291. cs->margin[GS_GUI_MARGIN_TOP] = (int32_t)se->value;
  2292. cs->margin[GS_GUI_MARGIN_RIGHT] = (int32_t)se->value;
  2293. cs->margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)se->value;
  2294. } break;
  2295. case GS_GUI_STYLE_MARGIN_LEFT: cs->margin[GS_GUI_MARGIN_LEFT] = (int32_t)se->value; break;
  2296. case GS_GUI_STYLE_MARGIN_TOP: cs->margin[GS_GUI_MARGIN_TOP] = (int32_t)se->value; break;
  2297. case GS_GUI_STYLE_MARGIN_RIGHT: cs->margin[GS_GUI_MARGIN_RIGHT] = (int32_t)se->value; break;
  2298. case GS_GUI_STYLE_MARGIN_BOTTOM: cs->margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)se->value; break;
  2299. // Border
  2300. case GS_GUI_STYLE_BORDER_RADIUS: {
  2301. cs->border_radius[0] = se->value;
  2302. cs->border_radius[1] = se->value;
  2303. cs->border_radius[2] = se->value;
  2304. cs->border_radius[3] = se->value;
  2305. } break;
  2306. case GS_GUI_STYLE_BORDER_RADIUS_LEFT: cs->border_radius[0] = se->value; break;
  2307. case GS_GUI_STYLE_BORDER_RADIUS_RIGHT: cs->border_radius[1] = se->value; break;
  2308. case GS_GUI_STYLE_BORDER_RADIUS_TOP: cs->border_radius[2] = se->value; break;
  2309. case GS_GUI_STYLE_BORDER_RADIUS_BOTTOM: cs->border_radius[3] = se->value; break;
  2310. case GS_GUI_STYLE_BORDER_WIDTH: {
  2311. cs->border_width[0] = se->value;
  2312. cs->border_width[1] = se->value;
  2313. cs->border_width[2] = se->value;
  2314. cs->border_width[3] = se->value;
  2315. } break;
  2316. case GS_GUI_STYLE_BORDER_WIDTH_LEFT: cs->border_width[0] = se->value; break;
  2317. case GS_GUI_STYLE_BORDER_WIDTH_RIGHT: cs->border_width[1] = se->value; break;
  2318. case GS_GUI_STYLE_BORDER_WIDTH_TOP: cs->border_width[2] = se->value; break;
  2319. case GS_GUI_STYLE_BORDER_WIDTH_BOTTOM: cs->border_width[3] = se->value; break;
  2320. // Flex
  2321. case GS_GUI_STYLE_DIRECTION: cs->direction = (int32_t)se->value; break;
  2322. case GS_GUI_STYLE_ALIGN_CONTENT: cs->align_content = (int32_t)se->value; break;
  2323. case GS_GUI_STYLE_JUSTIFY_CONTENT: cs->justify_content = (int32_t)se->value; break;
  2324. // Shadow
  2325. case GS_GUI_STYLE_SHADOW_X: cs->shadow_x = (int32_t)se->value; break;
  2326. case GS_GUI_STYLE_SHADOW_Y: cs->shadow_y = (int32_t)se->value; break;
  2327. // Colors
  2328. case GS_GUI_STYLE_COLOR_BACKGROUND: cs->colors[GS_GUI_COLOR_BACKGROUND] = se->color; break;
  2329. case GS_GUI_STYLE_COLOR_BORDER: cs->colors[GS_GUI_COLOR_BORDER] = se->color; break;
  2330. case GS_GUI_STYLE_COLOR_SHADOW: cs->colors[GS_GUI_COLOR_SHADOW] = se->color; break;
  2331. case GS_GUI_STYLE_COLOR_CONTENT: cs->colors[GS_GUI_COLOR_CONTENT] = se->color; break;
  2332. case GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND: cs->colors[GS_GUI_COLOR_CONTENT_BACKGROUND] = se->color; break;
  2333. case GS_GUI_STYLE_COLOR_CONTENT_BORDER: cs->colors[GS_GUI_COLOR_CONTENT_BORDER] = se->color; break;
  2334. case GS_GUI_STYLE_COLOR_CONTENT_SHADOW: cs->colors[GS_GUI_COLOR_CONTENT_SHADOW] = se->color; break;
  2335. // Font
  2336. case GS_GUI_STYLE_FONT: cs->font = se->font; break;
  2337. }
  2338. }
  2339. }
  2340. }
  2341. 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)
  2342. {
  2343. const uint32_t count = size / sizeof(gs_gui_style_element_t);
  2344. uint32_t idx_cnt = 1;
  2345. uint32_t idx = 0;
  2346. // Switch on state
  2347. switch (state)
  2348. {
  2349. // Do all
  2350. default: idx_cnt = 3; break;
  2351. case GS_GUI_ELEMENT_STATE_DEFAULT: idx = 0; break;
  2352. case GS_GUI_ELEMENT_STATE_HOVER: idx = 1; break;
  2353. case GS_GUI_ELEMENT_STATE_FOCUS: idx = 2; break;
  2354. }
  2355. for (uint32_t s = idx, c = 0; c < idx_cnt; ++s, ++c)
  2356. {
  2357. gs_gui_style_t* cs = &ctx->style_sheet->styles[element][s];
  2358. for (uint32_t i = 0; i < count; ++i)
  2359. {
  2360. gs_gui_style_element_t* se = &style[i];
  2361. switch (se->type)
  2362. {
  2363. // Width/Height
  2364. case GS_GUI_STYLE_WIDTH: cs->size[0] = (float)se->value; break;
  2365. case GS_GUI_STYLE_HEIGHT: cs->size[1] = (float)se->value; break;
  2366. // Padding
  2367. case GS_GUI_STYLE_PADDING: {
  2368. cs->padding[GS_GUI_PADDING_LEFT] = (int32_t)se->value;
  2369. cs->padding[GS_GUI_PADDING_TOP] = (int32_t)se->value;
  2370. cs->padding[GS_GUI_PADDING_RIGHT] = (int32_t)se->value;
  2371. cs->padding[GS_GUI_PADDING_BOTTOM] = (int32_t)se->value;
  2372. }
  2373. case GS_GUI_STYLE_PADDING_LEFT: cs->padding[GS_GUI_PADDING_LEFT] = (int32_t)se->value; break;
  2374. case GS_GUI_STYLE_PADDING_TOP: cs->padding[GS_GUI_PADDING_TOP] = (int32_t)se->value; break;
  2375. case GS_GUI_STYLE_PADDING_RIGHT: cs->padding[GS_GUI_PADDING_RIGHT] = (int32_t)se->value; break;
  2376. case GS_GUI_STYLE_PADDING_BOTTOM: cs->padding[GS_GUI_PADDING_BOTTOM] = (int32_t)se->value; break;
  2377. case GS_GUI_STYLE_MARGIN: {
  2378. cs->margin[GS_GUI_MARGIN_LEFT] = (int32_t)se->value;
  2379. cs->margin[GS_GUI_MARGIN_TOP] = (int32_t)se->value;
  2380. cs->margin[GS_GUI_MARGIN_RIGHT] = (int32_t)se->value;
  2381. cs->margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)se->value;
  2382. } break;
  2383. case GS_GUI_STYLE_MARGIN_LEFT: cs->margin[GS_GUI_MARGIN_LEFT] = (int32_t)se->value; break;
  2384. case GS_GUI_STYLE_MARGIN_TOP: cs->margin[GS_GUI_MARGIN_TOP] = (int32_t)se->value; break;
  2385. case GS_GUI_STYLE_MARGIN_RIGHT: cs->margin[GS_GUI_MARGIN_RIGHT] = (int32_t)se->value; break;
  2386. case GS_GUI_STYLE_MARGIN_BOTTOM: cs->margin[GS_GUI_MARGIN_BOTTOM] = (int32_t)se->value; break;
  2387. // Border
  2388. case GS_GUI_STYLE_BORDER_RADIUS: {
  2389. cs->border_radius[0] = se->value;
  2390. cs->border_radius[1] = se->value;
  2391. cs->border_radius[2] = se->value;
  2392. cs->border_radius[3] = se->value;
  2393. } break;
  2394. case GS_GUI_STYLE_BORDER_RADIUS_LEFT: cs->border_radius[0] = se->value; break;
  2395. case GS_GUI_STYLE_BORDER_RADIUS_RIGHT: cs->border_radius[1] = se->value; break;
  2396. case GS_GUI_STYLE_BORDER_RADIUS_TOP: cs->border_radius[2] = se->value; break;
  2397. case GS_GUI_STYLE_BORDER_RADIUS_BOTTOM: cs->border_radius[3] = se->value; break;
  2398. case GS_GUI_STYLE_BORDER_WIDTH: {
  2399. cs->border_width[0] = se->value;
  2400. cs->border_width[1] = se->value;
  2401. cs->border_width[2] = se->value;
  2402. cs->border_width[3] = se->value;
  2403. } break;
  2404. case GS_GUI_STYLE_BORDER_WIDTH_LEFT: cs->border_width[0] = se->value; break;
  2405. case GS_GUI_STYLE_BORDER_WIDTH_RIGHT: cs->border_width[1] = se->value; break;
  2406. case GS_GUI_STYLE_BORDER_WIDTH_TOP: cs->border_width[2] = se->value; break;
  2407. case GS_GUI_STYLE_BORDER_WIDTH_BOTTOM: cs->border_width[3] = se->value; break;
  2408. // Flex
  2409. case GS_GUI_STYLE_DIRECTION: cs->direction = (int32_t)se->value; break;
  2410. case GS_GUI_STYLE_ALIGN_CONTENT: cs->align_content = (int32_t)se->value; break;
  2411. case GS_GUI_STYLE_JUSTIFY_CONTENT: cs->justify_content = (int32_t)se->value; break;
  2412. // Shadow
  2413. case GS_GUI_STYLE_SHADOW_X: cs->shadow_x = (int32_t)se->value; break;
  2414. case GS_GUI_STYLE_SHADOW_Y: cs->shadow_y = (int32_t)se->value; break;
  2415. // Colors
  2416. case GS_GUI_STYLE_COLOR_BACKGROUND: cs->colors[GS_GUI_COLOR_BACKGROUND] = se->color; break;
  2417. case GS_GUI_STYLE_COLOR_BORDER: cs->colors[GS_GUI_COLOR_BORDER] = se->color; break;
  2418. case GS_GUI_STYLE_COLOR_SHADOW: cs->colors[GS_GUI_COLOR_SHADOW] = se->color; break;
  2419. case GS_GUI_STYLE_COLOR_CONTENT: cs->colors[GS_GUI_COLOR_CONTENT] = se->color; break;
  2420. case GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND: cs->colors[GS_GUI_COLOR_CONTENT_BACKGROUND] = se->color; break;
  2421. case GS_GUI_STYLE_COLOR_CONTENT_BORDER: cs->colors[GS_GUI_COLOR_CONTENT_BORDER] = se->color; break;
  2422. case GS_GUI_STYLE_COLOR_CONTENT_SHADOW: cs->colors[GS_GUI_COLOR_CONTENT_SHADOW] = se->color; break;
  2423. // Font
  2424. case GS_GUI_STYLE_FONT: cs->font = se->font; break;
  2425. }
  2426. }
  2427. }
  2428. }
  2429. GS_API_DECL gs_gui_container_t*
  2430. gs_gui_get_container_ex(gs_gui_context_t* ctx, gs_gui_id id, uint64_t opt)
  2431. {
  2432. gs_gui_container_t *cnt;
  2433. /* try to get existing container from pool */
  2434. int32_t idx = gs_gui_pool_get(ctx, ctx->container_pool, GS_GUI_CONTAINERPOOL_SIZE, id);
  2435. if (idx >= 0)
  2436. {
  2437. if (ctx->containers[idx].open || ~opt & GS_GUI_OPT_CLOSED)
  2438. {
  2439. gs_gui_pool_update(ctx, ctx->container_pool, idx);
  2440. }
  2441. return &ctx->containers[idx];
  2442. }
  2443. if (opt & GS_GUI_OPT_CLOSED) { return NULL; }
  2444. /* container not found in pool: init new container */
  2445. idx = gs_gui_pool_init(ctx, ctx->container_pool, GS_GUI_CONTAINERPOOL_SIZE, id);
  2446. cnt = &ctx->containers[idx];
  2447. memset(cnt, 0, sizeof(*cnt));
  2448. cnt->open = 1;
  2449. cnt->id = id;
  2450. cnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE | GS_GUI_WINDOW_FLAGS_FIRST_INIT;
  2451. gs_gui_bring_to_front(ctx, cnt);
  2452. return cnt;
  2453. }
  2454. static int32_t
  2455. gs_gui_text_width(gs_asset_font_t* font, const char* text, int32_t len)
  2456. {
  2457. gs_vec2 td = gs_asset_font_text_dimensions(font, text, len);
  2458. return (int32_t)td.x;
  2459. }
  2460. static int32_t
  2461. gs_gui_text_height(gs_asset_font_t* font, const char* text, int32_t len)
  2462. {
  2463. return (int32_t)gs_asset_font_max_height(font);
  2464. gs_vec2 td = gs_asset_font_text_dimensions(font, text, len);
  2465. return (int32_t)td.y;
  2466. }
  2467. // Grabs max height for a given font
  2468. static int32_t
  2469. gs_gui_font_height(gs_asset_font_t* font)
  2470. {
  2471. return (int32_t)gs_asset_font_max_height(font);
  2472. }
  2473. static gs_vec2
  2474. gs_gui_text_dimensions(gs_asset_font_t* font, const char* text, int32_t len)
  2475. {
  2476. gs_vec2 td = gs_asset_font_text_dimensions(font, text, len);
  2477. return td;
  2478. }
  2479. // =========================== //
  2480. // ======== Docking ========== //
  2481. // =========================== //
  2482. GS_API_DECL void
  2483. gs_gui_dock_ex(gs_gui_context_t* ctx, const char* dst, const char* src, int32_t split_type, float ratio)
  2484. {
  2485. gs_gui_hints_t hints = gs_default_val();
  2486. hints.framebuffer_size = gs_platform_framebuffer_sizev(ctx->window_hndl);
  2487. hints.viewport = gs_gui_rect(0.f, 0.f, 0.f, 0.f);
  2488. uint32_t f = ctx->frame;
  2489. if (!f) gs_gui_begin(ctx, &hints);
  2490. gs_gui_container_t* dst_cnt = gs_gui_get_container(ctx, dst);
  2491. gs_gui_container_t* src_cnt = gs_gui_get_container(ctx, src);
  2492. gs_gui_dock_ex_cnt(ctx, dst_cnt, src_cnt, split_type, ratio);
  2493. if (f != ctx->frame) gs_gui_end(ctx);
  2494. }
  2495. GS_API_DECL void gs_gui_undock_ex(gs_gui_context_t* ctx, const char* name)
  2496. {
  2497. gs_gui_container_t* cnt = gs_gui_get_container(ctx, name);
  2498. gs_gui_undock_ex_cnt(ctx, cnt);
  2499. }
  2500. void gs_gui_set_split(gs_gui_context_t* ctx, gs_gui_container_t* cnt, uint32_t id)
  2501. {
  2502. cnt->split = id;
  2503. gs_gui_tab_bar_t* tb = gs_gui_get_tab_bar(ctx, cnt);
  2504. if (tb)
  2505. {
  2506. for (uint32_t i = 0; i < tb->size; ++i)
  2507. {
  2508. ((gs_gui_container_t*)tb->items[i].data)->split = id;
  2509. }
  2510. }
  2511. }
  2512. GS_API_DECL gs_gui_container_t* gs_gui_get_parent(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
  2513. {
  2514. return (cnt->parent ? cnt->parent : cnt);
  2515. }
  2516. 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)
  2517. {
  2518. // Get top-level parent windows
  2519. parent = gs_gui_get_parent(ctx, parent);
  2520. child = gs_gui_get_parent(ctx, child);
  2521. if (!child || !parent)
  2522. {
  2523. return;
  2524. }
  2525. if (split_type == GS_GUI_SPLIT_TAB)
  2526. {
  2527. // If the parent window has a tab bar, then need to get that tab bar item and add it
  2528. if (parent->tab_bar)
  2529. {
  2530. gs_println("add to tab bar");
  2531. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, parent->tab_bar);
  2532. gs_assert(tab_bar);
  2533. // Set all tab bar children to this as well, if has children, then release previous tab bar
  2534. if (child->tab_bar)
  2535. {
  2536. uint32_t tbid = child->tab_bar;
  2537. gs_gui_tab_bar_t* ctb = gs_slot_array_getp(ctx->tab_bars, child->tab_bar);
  2538. for (uint32_t i = 0; i < ctb->size; ++i)
  2539. {
  2540. gs_gui_tab_item_t* cti = &tab_bar->items[tab_bar->size];
  2541. gs_gui_container_t* c = (gs_gui_container_t*)ctb->items[i].data;
  2542. cti->tab_bar = parent->tab_bar;
  2543. cti->zindex = tab_bar->size++;
  2544. cti->idx = cti->zindex;
  2545. cti->data = c;
  2546. c->tab_item = cti->idx;
  2547. c->parent = parent;
  2548. }
  2549. // Free other tab bar
  2550. // gs_slot_array_erase(ctx->tab_bars, tbid);
  2551. }
  2552. else
  2553. {
  2554. gs_gui_tab_item_t* tab_item = &tab_bar->items[tab_bar->size];
  2555. tab_item->tab_bar = parent->tab_bar;
  2556. tab_item->zindex = tab_bar->size++;
  2557. tab_item->idx = tab_item->zindex;
  2558. tab_bar->focus = tab_bar->size - 1;
  2559. child->tab_item = tab_item->idx;
  2560. }
  2561. tab_bar->items[child->tab_item].data = child;
  2562. child->rect = parent->rect;
  2563. child->parent = parent;
  2564. child->tab_bar = parent->tab_bar;
  2565. }
  2566. // Otherwise, create new tab bar
  2567. else
  2568. {
  2569. gs_println("create tab bar");
  2570. // Create tab bar
  2571. gs_gui_tab_bar_t tb = gs_default_val();
  2572. uint32_t hndl = gs_slot_array_insert(ctx->tab_bars, tb);
  2573. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, hndl);
  2574. gs_assert(tab_bar);
  2575. // Create tab items
  2576. gs_gui_tab_item_t* parent_tab_item = &tab_bar->items[tab_bar->size];
  2577. parent_tab_item->zindex = tab_bar->size++;
  2578. parent_tab_item->tab_bar = hndl;
  2579. // Set parent tab item
  2580. parent->tab_item = 0;
  2581. parent_tab_item->data = parent;
  2582. uint32_t tbid = child->tab_bar;
  2583. // Set all tab bar children to this as well, if has children, then release previous tab bar
  2584. if (child->tab_bar)
  2585. {
  2586. gs_gui_tab_bar_t* ctb = gs_slot_array_getp(ctx->tab_bars, child->tab_bar);
  2587. for (uint32_t i = 0; i < ctb->size; ++i)
  2588. {
  2589. gs_gui_tab_item_t* cti = &tab_bar->items[tab_bar->size];
  2590. gs_gui_container_t* c = (gs_gui_container_t*)ctb->items[i].data;
  2591. cti->tab_bar = hndl;
  2592. cti->zindex = tab_bar->size++;
  2593. cti->idx = cti->zindex;
  2594. cti->data = c;
  2595. c->tab_item = cti->idx;
  2596. c->parent = parent;
  2597. c->tab_bar = hndl;
  2598. }
  2599. // TODO(john): This erase is causing a crash.
  2600. // gs_slot_array_erase(ctx->tab_bars, tbid);
  2601. }
  2602. else
  2603. {
  2604. gs_gui_tab_item_t* child_tab_item = &tab_bar->items[tab_bar->size];
  2605. child_tab_item->zindex = tab_bar->size++;
  2606. child_tab_item->idx = child_tab_item->zindex;
  2607. child_tab_item->tab_bar = hndl;
  2608. // Set child tab item
  2609. child->tab_item = child_tab_item->idx;
  2610. child_tab_item->data = child;
  2611. }
  2612. tab_bar->focus = 1;
  2613. child->rect = parent->rect;
  2614. child->parent = parent;
  2615. tab_bar->rect = parent->rect;
  2616. parent->tab_bar = hndl;
  2617. child->tab_bar = hndl;
  2618. // Bring everything to front...right?
  2619. }
  2620. gs_gui_split_t* root_split = gs_gui_get_root_split(ctx, parent);
  2621. if (root_split)
  2622. {
  2623. gs_gui_update_split(ctx, root_split);
  2624. gs_gui_bring_split_to_front(ctx, root_split);
  2625. }
  2626. }
  2627. else
  2628. {
  2629. // Cache previous root splits
  2630. gs_gui_split_t* ps = gs_gui_get_root_split(ctx, parent);
  2631. gs_gui_split_t* cs = gs_gui_get_root_split(ctx, child);
  2632. gs_gui_tab_bar_t* tab_bar = gs_gui_get_tab_bar(ctx, parent);
  2633. gs_gui_split_t split = gs_default_val();
  2634. split.type = (gs_gui_split_type)split_type;
  2635. split.ratio = ratio;
  2636. gs_gui_split_node_t c0 = gs_default_val();
  2637. c0.type = GS_GUI_SPLIT_NODE_CONTAINER;
  2638. c0.container = child;
  2639. gs_gui_split_node_t c1 = gs_default_val();
  2640. c1.type = GS_GUI_SPLIT_NODE_CONTAINER;
  2641. c1.container = parent;
  2642. split.children[GS_GUI_SPLIT_NODE_CHILD] = c0;
  2643. split.children[GS_GUI_SPLIT_NODE_PARENT] = c1;
  2644. split.rect = ps ? ps->rect : parent->rect;
  2645. split.prev_rect = split.rect;
  2646. // Add new split to array
  2647. uint32_t hndl = gs_slot_array_insert(ctx->splits, split);
  2648. // Get newly inserted split pointer
  2649. gs_gui_split_t* sp = gs_slot_array_getp(ctx->splits, hndl);
  2650. sp->id = hndl;
  2651. // If both parents are null, creating a new split, new nodes, assigning to children's parents
  2652. if (!cs && !ps)
  2653. {
  2654. gs_println("0");
  2655. parent->split = hndl;
  2656. child->split = hndl;
  2657. }
  2658. // Child has split
  2659. else if (cs && !ps)
  2660. {
  2661. gs_println("1");
  2662. // If child has split, then the split is different...
  2663. sp->children[GS_GUI_SPLIT_NODE_CHILD].type = GS_GUI_SPLIT_NODE_SPLIT;
  2664. sp->children[GS_GUI_SPLIT_NODE_CHILD].split = cs->id;
  2665. // Child split needs to be set to this parent
  2666. cs->parent = hndl;
  2667. parent->split = hndl;
  2668. }
  2669. // Parent has split
  2670. else if (ps && !cs)
  2671. {
  2672. gs_println("2");
  2673. // No child to tree to assign, so we can get the raw parent split here
  2674. ps = gs_slot_array_getp(ctx->splits, parent->split);
  2675. // Assign parent split to previous
  2676. sp->parent = ps->id;
  2677. // Fix up references
  2678. if (ps->children[GS_GUI_SPLIT_NODE_PARENT].container == parent)
  2679. {
  2680. ps->children[GS_GUI_SPLIT_NODE_PARENT].type = GS_GUI_SPLIT_NODE_SPLIT;
  2681. ps->children[GS_GUI_SPLIT_NODE_PARENT].split = hndl;
  2682. }
  2683. else
  2684. {
  2685. ps->children[GS_GUI_SPLIT_NODE_CHILD].type = GS_GUI_SPLIT_NODE_SPLIT;
  2686. ps->children[GS_GUI_SPLIT_NODE_CHILD].split = hndl;
  2687. }
  2688. parent->split = hndl;
  2689. child->split = hndl;
  2690. }
  2691. // Both have splits
  2692. else
  2693. {
  2694. gs_println("3");
  2695. // Get parent split
  2696. ps = gs_slot_array_getp(ctx->splits, parent->split);
  2697. // Set parent id for new split to parent previous split
  2698. sp->parent = ps->id;
  2699. // Fix up references
  2700. sp->children[GS_GUI_SPLIT_NODE_CHILD].type = GS_GUI_SPLIT_NODE_SPLIT;
  2701. sp->children[GS_GUI_SPLIT_NODE_CHILD].split = cs->id;
  2702. // Need to check which node to replace
  2703. if (ps->children[GS_GUI_SPLIT_NODE_CHILD].container == parent)
  2704. {
  2705. ps->children[GS_GUI_SPLIT_NODE_CHILD].type = GS_GUI_SPLIT_NODE_SPLIT;
  2706. ps->children[GS_GUI_SPLIT_NODE_CHILD].split = hndl;
  2707. }
  2708. else
  2709. {
  2710. ps->children[GS_GUI_SPLIT_NODE_PARENT].type = GS_GUI_SPLIT_NODE_SPLIT;
  2711. ps->children[GS_GUI_SPLIT_NODE_PARENT].split = hndl;
  2712. }
  2713. cs->parent = hndl;
  2714. parent->split = hndl;
  2715. }
  2716. gs_gui_split_t* root_split = gs_gui_get_root_split(ctx, parent);
  2717. gs_gui_update_split(ctx, root_split);
  2718. gs_gui_bring_split_to_front(ctx, root_split);
  2719. }
  2720. }
  2721. GS_API_DECL void gs_gui_undock_ex_cnt(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
  2722. {
  2723. // If has a tab item idx, need to grab that
  2724. gs_gui_split_t* split = gs_gui_get_split(ctx, cnt);
  2725. // Get root split for container
  2726. gs_gui_split_t* root_split = NULL;
  2727. // Get parent split of this owning split
  2728. gs_gui_split_t* ps = split && split->parent ? gs_slot_array_getp(ctx->splits, split->parent) : NULL;
  2729. if (cnt->tab_bar)
  2730. {
  2731. // Get parent container for this container
  2732. gs_gui_container_t* parent = gs_gui_get_parent(ctx, cnt);
  2733. // Check if split
  2734. if (parent->split)
  2735. {
  2736. // No split, so just do stuff normally...
  2737. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
  2738. gs_gui_tab_item_t* tab_item = &tab_bar->items[cnt->tab_item];
  2739. if (tab_bar->size > 2)
  2740. {
  2741. // Get index
  2742. uint32_t idx = 0;
  2743. for (uint32_t i = 0; i < tab_bar->size; ++i)
  2744. {
  2745. if (tab_bar->items[i].data == cnt)
  2746. {
  2747. idx = i;
  2748. break;
  2749. }
  2750. }
  2751. // Swap all the way down the chain
  2752. for (uint32_t i = idx; i < tab_bar->size; ++i)
  2753. {
  2754. gs_gui_tab_item_swap(ctx, (gs_gui_container_t*)tab_bar->items[i].data, +1);
  2755. }
  2756. // Swap windows as well
  2757. ((gs_gui_container_t*)(tab_item->data))->tab_item = tab_item->idx;
  2758. // Set focus to first window
  2759. tab_bar->focus = idx ? idx - 1 : idx;
  2760. gs_assert(tab_bar->items[tab_bar->focus].data != cnt);
  2761. // Set split for focus
  2762. if (parent == cnt)
  2763. {
  2764. // Set parent for other containers (assuming parent was removed)
  2765. for (uint32_t i = 0; i < tab_bar->size; ++i)
  2766. {
  2767. gs_gui_container_t* c = (gs_gui_container_t*)tab_bar->items[i].data;
  2768. c->parent = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
  2769. tab_bar->items[i].idx = i;
  2770. tab_bar->items[i].zindex = i;
  2771. }
  2772. gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
  2773. fcnt->split = parent->split;
  2774. fcnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
  2775. // Fix up split reference
  2776. split = gs_slot_array_getp(ctx->splits, fcnt->split);
  2777. if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[0].container == cnt)
  2778. {
  2779. split->children[0].container = fcnt;
  2780. }
  2781. else
  2782. {
  2783. split->children[1].container = fcnt;
  2784. }
  2785. }
  2786. // Set size
  2787. tab_bar->size--;
  2788. }
  2789. // Destroy tab
  2790. else
  2791. {
  2792. uint32_t tbid = tab_item->tab_bar;
  2793. // Get index
  2794. uint32_t idx = 0;
  2795. for (uint32_t i = 0; i < tab_bar->size; ++i)
  2796. {
  2797. if (tab_bar->items[i].data == cnt)
  2798. {
  2799. idx = i;
  2800. break;
  2801. }
  2802. }
  2803. // Swap all the way down the chain
  2804. for (uint32_t i = idx; i < tab_bar->size; ++i)
  2805. {
  2806. gs_gui_tab_item_swap(ctx, (gs_gui_container_t*)tab_bar->items[i].data, +1);
  2807. }
  2808. for (uint32_t i = 0; i < tab_bar->size; ++i)
  2809. {
  2810. gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[i].data;
  2811. fcnt->rect = tab_bar->rect;
  2812. fcnt->tab_item = 0x00;
  2813. fcnt->tab_bar = 0x00;
  2814. fcnt->parent = NULL;
  2815. fcnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
  2816. }
  2817. // Fix up split reference
  2818. if (parent == cnt)
  2819. {
  2820. tab_bar->focus = idx ? idx - 1 : idx;
  2821. gs_assert((gs_gui_container_t*)tab_bar->items[tab_bar->focus].data != cnt);
  2822. gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
  2823. fcnt->split = parent->split;
  2824. // Fix up split reference
  2825. split = gs_slot_array_getp(ctx->splits, fcnt->split);
  2826. if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[0].container == parent)
  2827. {
  2828. split->children[0].container = fcnt;
  2829. }
  2830. else
  2831. {
  2832. split->children[1].container = fcnt;
  2833. }
  2834. }
  2835. // gs_slot_array_erase(ctx->tab_bars, tbid);
  2836. }
  2837. // Remove tab index from container
  2838. cnt->tab_item = 0x00;
  2839. cnt->tab_bar = 0x00;
  2840. // Remove parent
  2841. cnt->parent = NULL;
  2842. // Set split to 0
  2843. cnt->split = 0x00;
  2844. gs_gui_bring_to_front(ctx, cnt);
  2845. }
  2846. else
  2847. {
  2848. // No split, so just do stuff normally...
  2849. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
  2850. gs_gui_tab_item_t* tab_item = &tab_bar->items[cnt->tab_item];
  2851. // Set next available window to visible/focused and rearrange all tab item zindices
  2852. if (tab_bar->size > 2)
  2853. {
  2854. uint32_t idx = 0;
  2855. for (uint32_t i = 0; i < tab_bar->size; ++i)
  2856. {
  2857. if (tab_bar->items[i].data == cnt)
  2858. {
  2859. idx = i;
  2860. break;
  2861. }
  2862. }
  2863. // Swap all the way down the chain
  2864. for (uint32_t i = idx; i < tab_bar->size; ++i)
  2865. {
  2866. gs_gui_tab_item_swap(ctx, (gs_gui_container_t*)tab_bar->items[i].data, +1);
  2867. }
  2868. // Set size
  2869. tab_bar->size--;
  2870. // Set focus to first window
  2871. tab_bar->focus = idx ? idx - 1 : idx;
  2872. // Set parent for other containers
  2873. if (parent == cnt)
  2874. {
  2875. for (uint32_t i = 0; i < tab_bar->size; ++i)
  2876. {
  2877. gs_gui_container_t* c = (gs_gui_container_t*)tab_bar->items[i].data;
  2878. c->parent = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
  2879. }
  2880. }
  2881. }
  2882. // Only 2 windows left in tab bar
  2883. else
  2884. {
  2885. for (uint32_t i = 0; i < tab_bar->size; ++i)
  2886. {
  2887. gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[i].data;
  2888. fcnt->rect = tab_bar->rect;
  2889. fcnt->tab_item = 0x00;
  2890. fcnt->tab_bar = 0x00;
  2891. fcnt->parent = NULL;
  2892. fcnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
  2893. }
  2894. tab_bar->size = 0;
  2895. // Destroy tab bar, reset windows
  2896. // gs_slot_array_erase(ctx->tab_bars, tab_item->tab_bar);
  2897. }
  2898. // Remove tab index from container
  2899. cnt->tab_item = 0x00;
  2900. cnt->tab_bar = 0x00;
  2901. // Remove parent
  2902. cnt->parent = NULL;
  2903. gs_gui_bring_to_front(ctx, cnt);
  2904. }
  2905. }
  2906. else
  2907. {
  2908. // Rmove split reference from container
  2909. cnt->split = 0x00;
  2910. gs_gui_split_node_t* remain_node =
  2911. split->children[GS_GUI_SPLIT_NODE_CHILD].container == cnt? &split->children[GS_GUI_SPLIT_NODE_PARENT]:
  2912. split->children[GS_GUI_SPLIT_NODE_PARENT].container == cnt? &split->children[GS_GUI_SPLIT_NODE_CHILD] :
  2913. NULL;
  2914. gs_assert(remain_node);
  2915. // Set child split in prev container split to split container parent
  2916. if (ps)
  2917. {
  2918. uint32_t node_id = ps->children[GS_GUI_SPLIT_NODE_CHILD].split == split->id ? GS_GUI_SPLIT_NODE_CHILD : GS_GUI_SPLIT_NODE_PARENT;
  2919. gs_gui_split_node_t* fix_node = &ps->children[node_id];
  2920. *fix_node = *remain_node;
  2921. switch (fix_node->type)
  2922. {
  2923. case GS_GUI_SPLIT_NODE_CONTAINER:
  2924. {
  2925. gs_gui_container_t* remcnt = fix_node->container;
  2926. remcnt->split = ps->id;
  2927. } break;
  2928. case GS_GUI_SPLIT_NODE_SPLIT:
  2929. {
  2930. gs_gui_split_t* remsplit = gs_slot_array_getp(ctx->splits, fix_node->split);
  2931. remsplit->parent = ps->id;
  2932. } break;
  2933. }
  2934. root_split = gs_gui_get_root_split_from_split(ctx, ps);
  2935. }
  2936. // Otherwise, we were root dock and have to treat that case for remaining nodes
  2937. else
  2938. {
  2939. switch (remain_node->type)
  2940. {
  2941. case GS_GUI_SPLIT_NODE_CONTAINER:
  2942. {
  2943. gs_gui_container_t* remcnt = remain_node->container;
  2944. remcnt->rect = split->rect;
  2945. remcnt->split = 0x00;
  2946. root_split = gs_gui_get_root_split(ctx, remcnt);
  2947. } break;
  2948. case GS_GUI_SPLIT_NODE_SPLIT:
  2949. {
  2950. gs_gui_split_t* remsplit = gs_slot_array_getp(ctx->splits, remain_node->split);
  2951. remsplit->rect = split->rect;
  2952. remsplit->parent = 0x00;
  2953. root_split = gs_gui_get_root_split_from_split(ctx, remsplit);
  2954. } break;
  2955. }
  2956. }
  2957. // Erase split
  2958. gs_slot_array_erase(ctx->splits, split->id);
  2959. // Update
  2960. if (root_split) gs_gui_update_split(ctx, root_split);
  2961. if (root_split) gs_gui_bring_split_to_front(ctx, root_split);
  2962. gs_gui_bring_to_front(ctx, cnt);
  2963. }
  2964. }
  2965. // ============================= //
  2966. // ========= Main API ========== //
  2967. // ============================= //
  2968. #define GS_GUI_COPY_STYLE(DST, SRC)\
  2969. do {\
  2970. DST[GS_GUI_ELEMENT_STATE_DEFAULT] = SRC;\
  2971. DST[GS_GUI_ELEMENT_STATE_HOVER] = SRC;\
  2972. DST[GS_GUI_ELEMENT_STATE_FOCUS] = SRC;\
  2973. } while (0)
  2974. static void gs_gui_init_default_styles(gs_gui_context_t* ctx)
  2975. {
  2976. // Set up main default style
  2977. gs_gui_default_style.font = gsi_default_font();
  2978. gs_gui_default_style.size[0] = 68.f;
  2979. gs_gui_default_style.size[1] = 18.f;
  2980. gs_gui_default_style.spacing = 2;
  2981. gs_gui_default_style.indent = 10;
  2982. gs_gui_default_style.title_height = 20;
  2983. gs_gui_default_style.scrollbar_size = 5;
  2984. gs_gui_default_style.thumb_size = 5;
  2985. // Initialize all default styles
  2986. GS_GUI_COPY_STYLE(gs_gui_default_container_style, gs_gui_default_style);
  2987. GS_GUI_COPY_STYLE(gs_gui_default_button_style, gs_gui_default_style);
  2988. GS_GUI_COPY_STYLE(gs_gui_default_text_style, gs_gui_default_style);
  2989. GS_GUI_COPY_STYLE(gs_gui_default_label_style, gs_gui_default_style);
  2990. GS_GUI_COPY_STYLE(gs_gui_default_panel_style, gs_gui_default_style);
  2991. GS_GUI_COPY_STYLE(gs_gui_default_input_style, gs_gui_default_style);
  2992. GS_GUI_COPY_STYLE(gs_gui_default_scroll_style, gs_gui_default_style);
  2993. GS_GUI_COPY_STYLE(gs_gui_default_image_style, gs_gui_default_style);
  2994. gs_gui_style_t* style = NULL;
  2995. // button
  2996. for (uint32_t i = 0; i < 3; ++i) {
  2997. style = &gs_gui_default_button_style[i];
  2998. style->justify_content = GS_GUI_JUSTIFY_CENTER;
  2999. }
  3000. gs_gui_default_button_style[GS_GUI_ELEMENT_STATE_DEFAULT].colors[GS_GUI_COLOR_BACKGROUND] = gs_color(35, 35, 35, 255);
  3001. gs_gui_default_button_style[GS_GUI_ELEMENT_STATE_HOVER].colors[GS_GUI_COLOR_BACKGROUND] = gs_color(40, 40, 40, 255);
  3002. gs_gui_default_button_style[GS_GUI_ELEMENT_STATE_FOCUS].colors[GS_GUI_COLOR_BACKGROUND] = gs_color(0, 214, 121, 255);
  3003. // panel
  3004. for (uint32_t i = 0; i < 3; ++i) {
  3005. style = &gs_gui_default_panel_style[i];
  3006. style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(30, 30, 30, 255);
  3007. style->size[1] = 19;
  3008. }
  3009. // input
  3010. for (uint32_t i = 0; i < 3; ++i) {
  3011. style = &gs_gui_default_input_style[i];
  3012. style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(20, 20, 20, 255);
  3013. style->size[1] = 19;
  3014. }
  3015. // text
  3016. for (uint32_t i = 0; i < 3; ++i) {
  3017. style = &gs_gui_default_text_style[i];
  3018. style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(0, 0, 0, 0);
  3019. style->colors[GS_GUI_COLOR_CONTENT] = GS_COLOR_WHITE;
  3020. }
  3021. // label
  3022. for (uint32_t i = 0; i < 3; ++i) {
  3023. style = &gs_gui_default_label_style[i];
  3024. style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(0, 0, 0, 0);
  3025. style->colors[GS_GUI_COLOR_CONTENT] = GS_COLOR_WHITE;
  3026. style->size[1] = 19;
  3027. }
  3028. // scroll
  3029. for (uint32_t i = 0; i < 3; ++i) {
  3030. style = &gs_gui_default_scroll_style[i];
  3031. style->size[0] = 10;
  3032. style->padding[GS_GUI_PADDING_RIGHT] = 4;
  3033. }
  3034. style = &gs_gui_default_scroll_style[GS_GUI_ELEMENT_STATE_DEFAULT];
  3035. style->colors[GS_GUI_COLOR_BACKGROUND] = gs_color(22, 22, 22, 255);
  3036. style->colors[GS_GUI_COLOR_CONTENT] = gs_color(255, 255, 255, 100);
  3037. #define GS_GUI_COPY(DST, SRC)\
  3038. do {\
  3039. DST[0x00] = SRC[0x00];\
  3040. DST[0x01] = SRC[0x01];\
  3041. DST[0x02] = SRC[0x02];\
  3042. } while (0)
  3043. GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_CONTAINER], gs_gui_default_container_style);
  3044. GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_LABEL], gs_gui_default_label_style);
  3045. GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_TEXT], gs_gui_default_text_style);
  3046. GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_PANEL], gs_gui_default_panel_style);
  3047. GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_INPUT], gs_gui_default_input_style);
  3048. GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_BUTTON], gs_gui_default_button_style);
  3049. GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_SCROLL], gs_gui_default_scroll_style);
  3050. GS_GUI_COPY(gs_gui_default_style_sheet.styles[GS_GUI_ELEMENT_IMAGE], gs_gui_default_image_style);
  3051. ctx->style_sheet = &gs_gui_default_style_sheet;
  3052. ctx->style = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][0x00];
  3053. }
  3054. static char button_map[256] = gs_default_val();
  3055. static char key_map[512] = gs_default_val();
  3056. GS_API_DECL gs_gui_context_t gs_gui_new(uint32_t window_hndl)
  3057. {
  3058. gs_gui_context_t ctx = gs_default_val();
  3059. gs_gui_init(&ctx, window_hndl);
  3060. return ctx;
  3061. }
  3062. GS_API_DECL void gs_gui_init(gs_gui_context_t *ctx, uint32_t window_hndl)
  3063. {
  3064. memset(ctx, 0, sizeof(*ctx));
  3065. ctx->gsi = gs_immediate_draw_new();
  3066. ctx->overlay_draw_list = gs_immediate_draw_new();
  3067. gs_gui_init_default_styles(ctx);
  3068. ctx->window_hndl = window_hndl;
  3069. ctx->last_zindex = 1000;
  3070. gs_slot_array_reserve(ctx->splits, GS_GUI_GS_GUI_SPLIT_SIZE);
  3071. gs_gui_split_t split = gs_default_val();
  3072. gs_slot_array_insert(ctx->splits, split); // First item is set for 0x00 invalid
  3073. gs_slot_array_reserve(ctx->tab_bars, GS_GUI_GS_GUI_TAB_SIZE);
  3074. gs_gui_tab_bar_t tb = gs_default_val();
  3075. gs_slot_array_insert(ctx->tab_bars, tb);
  3076. button_map[GS_MOUSE_LBUTTON & 0xff] = GS_GUI_MOUSE_LEFT;
  3077. button_map[GS_MOUSE_RBUTTON & 0xff] = GS_GUI_MOUSE_RIGHT;
  3078. button_map[GS_MOUSE_MBUTTON & 0xff] = GS_GUI_MOUSE_MIDDLE;
  3079. key_map[GS_KEYCODE_LEFT_SHIFT & 0xff] = GS_GUI_KEY_SHIFT;
  3080. key_map[GS_KEYCODE_RIGHT_SHIFT & 0xff] = GS_GUI_KEY_SHIFT;
  3081. key_map[GS_KEYCODE_LEFT_CONTROL & 0xff] = GS_GUI_KEY_CTRL;
  3082. key_map[GS_KEYCODE_RIGHT_CONTROL & 0xff] = GS_GUI_KEY_CTRL;
  3083. key_map[GS_KEYCODE_LEFT_ALT & 0xff] = GS_GUI_KEY_ALT;
  3084. key_map[GS_KEYCODE_RIGHT_ALT & 0xff] = GS_GUI_KEY_ALT;
  3085. key_map[GS_KEYCODE_ENTER & 0xff] = GS_GUI_KEY_RETURN;
  3086. key_map[GS_KEYCODE_BACKSPACE & 0xff] = GS_GUI_KEY_BACKSPACE;
  3087. }
  3088. GS_API_DECL void
  3089. gs_gui_init_font_stash(gs_gui_context_t* ctx, gs_gui_font_stash_desc_t* desc)
  3090. {
  3091. gs_hash_table_clear(ctx->font_stash);
  3092. uint32_t ct = sizeof(gs_gui_font_desc_t) / desc->size;
  3093. for (uint32_t i = 0; i < ct; ++i) {
  3094. gs_hash_table_insert(ctx->font_stash, gs_hash_str64(desc->fonts[i].key), desc->fonts[i].font);
  3095. }
  3096. }
  3097. GS_API_DECL gs_gui_context_t
  3098. gs_gui_context_new(uint32_t window_hndl)
  3099. {
  3100. gs_gui_context_t gui = gs_default_val();
  3101. gs_gui_init(&gui, window_hndl);
  3102. return gui;
  3103. }
  3104. GS_API_DECL void
  3105. gs_gui_free(gs_gui_context_t* ctx)
  3106. {
  3107. /*
  3108. typedef struct gs_gui_context_t
  3109. {
  3110. // Core state
  3111. gs_gui_style_t* style; // Active style
  3112. gs_gui_style_sheet_t* style_sheet; // Active style sheet
  3113. gs_gui_id hover;
  3114. gs_gui_id focus;
  3115. gs_gui_id last_id;
  3116. gs_gui_id state_switch_id; // Id that had a state switch
  3117. int32_t switch_state;
  3118. gs_gui_id lock_focus;
  3119. int32_t last_hover_state;
  3120. int32_t last_focus_state;
  3121. gs_gui_id prev_hover;
  3122. gs_gui_id prev_focus;
  3123. gs_gui_rect_t last_rect;
  3124. int32_t last_zindex;
  3125. int32_t updated_focus;
  3126. int32_t frame;
  3127. gs_vec2 framebuffer_size;
  3128. gs_gui_rect_t viewport;
  3129. gs_gui_container_t* active_root;
  3130. gs_gui_container_t* hover_root;
  3131. gs_gui_container_t* next_hover_root;
  3132. gs_gui_container_t* scroll_target;
  3133. gs_gui_container_t* focus_root;
  3134. gs_gui_container_t* next_focus_root;
  3135. gs_gui_container_t* dockable_root;
  3136. gs_gui_container_t* prev_dockable_root;
  3137. gs_gui_container_t* docked_root;
  3138. gs_gui_container_t* undock_root;
  3139. gs_gui_split_t* focus_split;
  3140. gs_gui_split_t* next_hover_split;
  3141. gs_gui_split_t* hover_split;
  3142. gs_gui_id next_lock_hover_id;
  3143. gs_gui_id lock_hover_id;
  3144. char number_edit_buf[GS_GUI_MAX_FMT];
  3145. gs_gui_id number_edit;
  3146. gs_gui_alt_drag_mode_type alt_drag_mode;
  3147. gs_dyn_array(gs_gui_request_t) requests;
  3148. // Stacks
  3149. gs_gui_stack(uint8_t, GS_GUI_COMMANDLIST_SIZE) command_list;
  3150. gs_gui_stack(gs_gui_container_t*, GS_GUI_ROOTLIST_SIZE) root_list;
  3151. gs_gui_stack(gs_gui_container_t*, GS_GUI_CONTAINERSTACK_SIZE) container_stack;
  3152. gs_gui_stack(gs_gui_rect_t, GS_GUI_CLIPSTACK_SIZE) clip_stack;
  3153. gs_gui_stack(gs_gui_id, GS_GUI_IDSTACK_SIZE) id_stack;
  3154. gs_gui_stack(gs_gui_layout_t, GS_GUI_LAYOUTSTACK_SIZE) layout_stack;
  3155. // Style sheet element stacks
  3156. gs_hash_table(gs_gui_element_type, gs_gui_inline_style_stack_t) inline_styles;
  3157. // Retained state pools
  3158. gs_gui_pool_item_t container_pool[GS_GUI_CONTAINERPOOL_SIZE];
  3159. gs_gui_container_t containers[GS_GUI_CONTAINERPOOL_SIZE];
  3160. gs_gui_pool_item_t treenode_pool[GS_GUI_TREENODEPOOL_SIZE];
  3161. gs_slot_array(gs_gui_split_t) splits;
  3162. gs_slot_array(gs_gui_tab_bar_t) tab_bars;
  3163. // Input state
  3164. gs_vec2 mouse_pos;
  3165. gs_vec2 last_mouse_pos;
  3166. gs_vec2 mouse_delta;
  3167. gs_vec2 scroll_delta;
  3168. int32_t mouse_down;
  3169. int32_t mouse_pressed;
  3170. int32_t key_down;
  3171. int32_t key_pressed;
  3172. char input_text[32];
  3173. // Backend resources
  3174. uint32_t window_hndl;
  3175. gs_immediate_draw_t gsi;
  3176. gs_immediate_draw_t overlay_draw_list;
  3177. // Active Transitions
  3178. gs_hash_table(gs_gui_id, gs_gui_animation_t) animations;
  3179. // Font stash
  3180. gs_hash_table(uint64_t, gs_asset_font_t*) font_stash;
  3181. // Callbacks
  3182. struct {
  3183. gs_gui_on_draw_button_callback button;
  3184. } callbacks;
  3185. } gs_gui_context_t;
  3186. */
  3187. gs_hash_table_free(ctx->font_stash);
  3188. gs_immediate_draw_free(&ctx->gsi);
  3189. gs_immediate_draw_free(&ctx->overlay_draw_list);
  3190. gs_hash_table_free(ctx->animations);
  3191. gs_slot_array_free(ctx->splits);
  3192. gs_slot_array_free(ctx->tab_bars);
  3193. gs_hash_table_free(ctx->inline_styles);
  3194. // Inline style stacks
  3195. for(
  3196. gs_hash_table_iter it = gs_hash_table_iter_new(ctx->inline_styles);
  3197. gs_hash_table_iter_valid(ctx->inline_styles, it);
  3198. gs_hash_table_iter_advance(ctx->inline_styles, it)
  3199. )
  3200. {
  3201. gs_gui_inline_style_stack_t* stack = gs_hash_table_iter_getp(ctx->inline_styles, it);
  3202. for (uint32_t i = 0; i < 3; ++i) {
  3203. gs_dyn_array_free(stack->styles[i]);
  3204. gs_dyn_array_free(stack->animations[i]);
  3205. }
  3206. gs_dyn_array_free(stack->style_counts); // amount of styles to pop off at "top of stack" for each state
  3207. gs_dyn_array_free(stack->animation_counts); // amount of animations to pop off at "top of stack" for each state
  3208. }
  3209. gs_dyn_array_free(ctx->requests);
  3210. }
  3211. static void gs_gui_draw_splits(gs_gui_context_t* ctx, gs_gui_split_t* split)
  3212. {
  3213. if (!split) return;
  3214. gs_gui_split_t* root_split = gs_gui_get_root_split_from_split(ctx, split);
  3215. // Draw split
  3216. const gs_gui_rect_t* sr = &split->rect;
  3217. gs_vec2 hd = gs_v2(sr->w * 0.5f, sr->h * 0.5f);
  3218. gs_gui_rect_t r = gs_default_val();
  3219. gs_color_t c = gs_color(0, 0, 0, 0);
  3220. const float ratio = split->ratio;
  3221. gs_gui_container_t* top = gs_gui_get_top_most_container(ctx, root_split);
  3222. gs_gui_container_t* hover_cnt = ctx->hover_root ? ctx->hover_root :
  3223. ctx->next_hover_root ? ctx->next_hover_root :
  3224. NULL;
  3225. bool valid_hover = hover_cnt && hover_cnt->zindex > top->zindex;
  3226. valid_hover = false;
  3227. bool valid = true;
  3228. split->frame = ctx->frame;
  3229. root_split->frame = ctx->frame;
  3230. bool can_draw = true;
  3231. for (uint32_t i = 0; i < 2; ++i)
  3232. {
  3233. if (
  3234. split->children[i].type == GS_GUI_SPLIT_NODE_CONTAINER && can_draw
  3235. )
  3236. {
  3237. gs_gui_container_t* cnt = split->children[i].container;
  3238. // Don't draw split if this container belongs to a dockspace
  3239. if (cnt->opt & GS_GUI_OPT_DOCKSPACE)
  3240. {
  3241. can_draw = false;
  3242. continue;
  3243. }
  3244. switch (split->type)
  3245. {
  3246. case GS_GUI_SPLIT_LEFT:
  3247. {
  3248. 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);
  3249. } break;
  3250. case GS_GUI_SPLIT_RIGHT:
  3251. {
  3252. 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);
  3253. } break;
  3254. case GS_GUI_SPLIT_TOP:
  3255. {
  3256. 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);
  3257. } break;
  3258. case GS_GUI_SPLIT_BOTTOM:
  3259. {
  3260. 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);
  3261. } break;
  3262. }
  3263. int16_t exp[4] = {1, 1, 1, 1};
  3264. gs_gui_rect_t expand = gs_gui_expand_rect(r, exp);
  3265. bool hover = !valid_hover && !ctx->focus && !ctx->prev_dockable_root && gs_gui_rect_overlaps_vec2(expand, ctx->mouse_pos) && !ctx->lock_hover_id;
  3266. if (hover) ctx->next_hover_split = split;
  3267. if (hover && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  3268. {
  3269. if (!ctx->focus_split) ctx->focus_split = split;
  3270. }
  3271. bool active = ctx->focus_split == split;
  3272. if (active && valid)
  3273. {
  3274. ctx->next_hover_root = top;
  3275. gs_gui_request_t req = gs_default_val();
  3276. req.type = GS_GUI_SPLIT_RATIO;
  3277. req.split = split;
  3278. gs_dyn_array_push(ctx->requests, req);
  3279. }
  3280. if (
  3281. (hover && (ctx->focus_split == split)) ||
  3282. (hover && !ctx->focus_split) ||
  3283. active
  3284. )
  3285. {
  3286. gs_color_t bc = ctx->focus_split == split ?
  3287. ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_FOCUS].colors[GS_GUI_COLOR_BACKGROUND] :
  3288. ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_HOVER].colors[GS_GUI_COLOR_BACKGROUND];
  3289. gs_gui_draw_rect(ctx, r, bc);
  3290. can_draw = false;
  3291. }
  3292. }
  3293. else if (
  3294. split->children[i].type == GS_GUI_SPLIT_NODE_SPLIT
  3295. )
  3296. {
  3297. if (can_draw)
  3298. {
  3299. switch (split->type)
  3300. {
  3301. case GS_GUI_SPLIT_LEFT:
  3302. {
  3303. 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);
  3304. } break;
  3305. case GS_GUI_SPLIT_RIGHT:
  3306. {
  3307. 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);
  3308. } break;
  3309. case GS_GUI_SPLIT_TOP:
  3310. {
  3311. 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);
  3312. } break;
  3313. case GS_GUI_SPLIT_BOTTOM:
  3314. {
  3315. 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);
  3316. } break;
  3317. }
  3318. int16_t exp[] = {1, 1, 1, 1};
  3319. gs_gui_rect_t expand = gs_gui_expand_rect(r, exp);
  3320. bool hover = !valid_hover && !ctx->focus && !ctx->prev_dockable_root && gs_gui_rect_overlaps_vec2(expand, ctx->mouse_pos);
  3321. if (hover) ctx->next_hover_split = split;
  3322. if (hover && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  3323. {
  3324. if (!ctx->focus_split) ctx->focus_split = split;
  3325. }
  3326. bool active = ctx->focus_split == split;
  3327. if (active)
  3328. {
  3329. ctx->next_hover_root = top;
  3330. gs_gui_request_t req = gs_default_val();
  3331. req.type = GS_GUI_SPLIT_RATIO;
  3332. req.split = split;
  3333. gs_dyn_array_push(ctx->requests, req);
  3334. }
  3335. if (
  3336. (hover && (ctx->focus_split == split)) ||
  3337. (hover && !ctx->focus_split) ||
  3338. active
  3339. )
  3340. {
  3341. gs_color_t bc = ctx->focus_split == split ?
  3342. ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_FOCUS].colors[GS_GUI_COLOR_BACKGROUND] :
  3343. ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_HOVER].colors[GS_GUI_COLOR_BACKGROUND];
  3344. gs_gui_draw_rect(ctx, r, bc);
  3345. can_draw = false;
  3346. }
  3347. }
  3348. gs_gui_split_t* child = gs_slot_array_getp(ctx->splits, split->children[i].split);
  3349. gs_gui_draw_splits(ctx, child);
  3350. }
  3351. }
  3352. if (ctx->focus_split == split && ctx->mouse_down != GS_GUI_MOUSE_LEFT)
  3353. {
  3354. ctx->focus_split = NULL;
  3355. }
  3356. }
  3357. static void gs_gui_get_split_lowest_zindex(gs_gui_context_t* ctx, gs_gui_split_t* split, int32_t* index)
  3358. {
  3359. if (!split) return;
  3360. if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[0].container->zindex < *index)
  3361. {
  3362. *index = split->children[0].container->zindex;
  3363. }
  3364. if (split->children[0].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[0].container->tab_bar)
  3365. {
  3366. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, split->children[0].container->tab_bar);
  3367. for (uint32_t i = 0; i < tab_bar->size; ++i)
  3368. {
  3369. if (((gs_gui_container_t*)tab_bar->items[i].data)->zindex < *index) *index = ((gs_gui_container_t*)tab_bar->items[i].data)->zindex;
  3370. }
  3371. }
  3372. if (split->children[1].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[1].container->zindex < *index)
  3373. {
  3374. *index = split->children[1].container->zindex;
  3375. }
  3376. if (split->children[1].type == GS_GUI_SPLIT_NODE_CONTAINER && split->children[1].container->tab_bar)
  3377. {
  3378. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, split->children[1].container->tab_bar);
  3379. for (uint32_t i = 0; i < tab_bar->size; ++i)
  3380. {
  3381. if (((gs_gui_container_t*)tab_bar->items[i].data)->zindex < *index) *index = ((gs_gui_container_t*)tab_bar->items[i].data)->zindex;
  3382. }
  3383. }
  3384. if (split->children[0].type == GS_GUI_SPLIT_NODE_SPLIT)
  3385. {
  3386. gs_gui_get_split_lowest_zindex(ctx, gs_slot_array_getp(ctx->splits, split->children[0].split), index);
  3387. }
  3388. if (split->children[1].type == GS_GUI_SPLIT_NODE_SPLIT)
  3389. {
  3390. gs_gui_get_split_lowest_zindex(ctx, gs_slot_array_getp(ctx->splits, split->children[1].split), index);
  3391. }
  3392. }
  3393. GS_API_DECL void
  3394. gs_gui_begin(gs_gui_context_t* ctx, const gs_gui_hints_t* hints)
  3395. {
  3396. gs_gui_hints_t default_hints = gs_default_val();
  3397. default_hints.framebuffer_size = gs_platform_framebuffer_sizev(ctx->window_hndl);
  3398. default_hints.viewport = gs_gui_rect(0.f, 0.f, default_hints.framebuffer_size.x, default_hints.framebuffer_size.y);
  3399. gs_gui_hints_t hint = hints ? *hints : default_hints;
  3400. // Set up viewport
  3401. gs_gui_rect_t vp = hint.viewport;
  3402. if (~hint.flags & GS_GUI_HINT_FLAG_NO_INVERT_Y) {
  3403. vp.y = hint.viewport.y;
  3404. } else {
  3405. vp.y = hint.framebuffer_size.y - hint.viewport.h - hint.viewport.y;
  3406. }
  3407. // Capture event information
  3408. gs_vec2 mouse_pos = gs_platform_mouse_positionv();
  3409. // Check for scale and bias
  3410. if (hint.flags & GS_GUI_HINT_FLAG_NO_SCALE_BIAS_MOUSE) {
  3411. float px = (mouse_pos.x - vp.x) / vp.w;
  3412. float py = (mouse_pos.y - vp.y) / vp.h;
  3413. float xv = vp.w * px;
  3414. float yv = vp.h * py;
  3415. mouse_pos = gs_v2(xv, yv);
  3416. }
  3417. else {
  3418. gs_vec2 fb_vp_ratio = gs_v2(hint.framebuffer_size.x / vp.w, hint.framebuffer_size.y / vp.h);
  3419. float px = mouse_pos.x - (vp.x * fb_vp_ratio.x);
  3420. float py = mouse_pos.y + (vp.y * fb_vp_ratio.y);
  3421. float xv = px / fb_vp_ratio.x;
  3422. float yv = py / fb_vp_ratio.y;
  3423. mouse_pos = gs_v2(xv, yv);
  3424. }
  3425. gs_platform_event_t evt = gs_default_val();
  3426. while (gs_platform_poll_events(&evt, false))
  3427. {
  3428. switch (evt.type)
  3429. {
  3430. case GS_PLATFORM_EVENT_MOUSE:
  3431. {
  3432. switch (evt.mouse.action)
  3433. {
  3434. case GS_PLATFORM_MOUSE_MOVE:
  3435. {
  3436. // ctx->mouse_pos = evt.mouse.move;
  3437. } break;
  3438. case GS_PLATFORM_MOUSE_WHEEL:
  3439. {
  3440. gs_gui_input_scroll(ctx, (int32_t)(-evt.mouse.wheel.x * 30.f), (int32_t)(-evt.mouse.wheel.y * 30.f));
  3441. } break;
  3442. case GS_PLATFORM_MOUSE_BUTTON_DOWN:
  3443. case GS_PLATFORM_MOUSE_BUTTON_PRESSED: {
  3444. int32_t code = 1 << evt.mouse.button;
  3445. gs_gui_input_mousedown(ctx, (int32_t)mouse_pos.x, (int32_t)mouse_pos.y, code);
  3446. } break;
  3447. case GS_PLATFORM_MOUSE_BUTTON_RELEASED:
  3448. {
  3449. int32_t code = 1 << evt.mouse.button;
  3450. gs_gui_input_mouseup(ctx, (int32_t)mouse_pos.x, (int32_t)mouse_pos.y, code);
  3451. } break;
  3452. case GS_PLATFORM_MOUSE_ENTER:
  3453. {
  3454. // If there are user callbacks, could trigger them here
  3455. } break;
  3456. case GS_PLATFORM_MOUSE_LEAVE:
  3457. {
  3458. // If there are user callbacks, could trigger them here
  3459. } break;
  3460. default: break;
  3461. }
  3462. } break;
  3463. case GS_PLATFORM_EVENT_TEXT:
  3464. {
  3465. // Input text
  3466. char txt[2] = {(char)(evt.text.codepoint & 255), 0};
  3467. gs_gui_input_text(ctx, txt);
  3468. } break;
  3469. case GS_PLATFORM_EVENT_KEY:
  3470. {
  3471. switch (evt.key.action)
  3472. {
  3473. case GS_PLATFORM_KEY_DOWN:
  3474. case GS_PLATFORM_KEY_PRESSED:
  3475. {
  3476. gs_gui_input_keydown(ctx, key_map[evt.key.keycode & 511]);
  3477. } break;
  3478. case GS_PLATFORM_KEY_RELEASED:
  3479. {
  3480. gs_gui_input_keyup(ctx, key_map[evt.key.keycode & 511]);
  3481. } break;
  3482. default: break;
  3483. }
  3484. } break;
  3485. case GS_PLATFORM_EVENT_WINDOW:
  3486. {
  3487. switch (evt.window.action)
  3488. {
  3489. default: break;
  3490. }
  3491. } break;
  3492. default: break;
  3493. }
  3494. }
  3495. ctx->framebuffer_size = hint.framebuffer_size;
  3496. ctx->viewport = gs_gui_rect(hint.viewport.x, hint.viewport.y, hint.viewport.w, hint.viewport.h);
  3497. ctx->mouse_pos = mouse_pos;
  3498. ctx->command_list.idx = 0;
  3499. ctx->root_list.idx = 0;
  3500. ctx->scroll_target = NULL;
  3501. ctx->hover_root = ctx->next_hover_root;
  3502. ctx->next_hover_root = NULL;
  3503. ctx->focus_root = ctx->next_focus_root;
  3504. ctx->next_focus_root = NULL;
  3505. ctx->prev_dockable_root = ctx->dockable_root;
  3506. ctx->dockable_root = NULL;
  3507. ctx->hover_split = ctx->next_hover_split;
  3508. ctx->next_hover_split = NULL;
  3509. ctx->lock_hover_id = ctx->next_lock_hover_id;
  3510. ctx->next_lock_hover_id = 0x00;
  3511. ctx->mouse_delta.x = ctx->mouse_pos.x - ctx->last_mouse_pos.x;
  3512. ctx->mouse_delta.y = ctx->mouse_pos.y - ctx->last_mouse_pos.y;
  3513. ctx->frame++;
  3514. // Set up overlay draw list
  3515. gs_vec2 fbs = ctx->framebuffer_size;
  3516. gsi_defaults(&ctx->overlay_draw_list);
  3517. 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
  3518. /*
  3519. for (
  3520. gs_slot_array_iter it = gs_slot_array_iter_new(ctx->splits);
  3521. gs_slot_array_iter_valid(ctx->splits, it);
  3522. gs_slot_array_iter_advance(ctx->splits, it)
  3523. )
  3524. {
  3525. if (!it) continue;
  3526. gs_gui_split_t* split = gs_slot_array_iter_getp(ctx->splits, it);
  3527. // Root split
  3528. if (!split->parent)
  3529. {
  3530. gs_gui_container_t* root_cnt = gs_gui_get_root_container_from_split(ctx, split);
  3531. gs_gui_rect_t r = split->rect;
  3532. r.x -= 10.f;
  3533. r.w += 20.f;
  3534. r.y -= 10.f;
  3535. r.h += 20.f;
  3536. gs_snprintfc(TMP, 256, "!dockspace%zu", (size_t)split);
  3537. uint64_t opt = GS_GUI_OPT_NOFRAME |
  3538. GS_GUI_OPT_FORCESETRECT |
  3539. GS_GUI_OPT_NOMOVE |
  3540. GS_GUI_OPT_NOTITLE |
  3541. GS_GUI_OPT_NOSCROLL |
  3542. GS_GUI_OPT_NOCLIP |
  3543. GS_GUI_OPT_NODOCK |
  3544. GS_GUI_OPT_DOCKSPACE |
  3545. GS_GUI_OPT_NOBORDER;
  3546. gs_gui_window_begin_ex(ctx, TMP, r, NULL, opt);
  3547. {
  3548. // Set zindex for sorting (always below the bottom most window in this split tree)
  3549. gs_gui_container_t* ds = gs_gui_get_current_container(ctx);
  3550. int32_t zindex = INT32_MAX - 1;
  3551. gs_gui_get_split_lowest_zindex(ctx, split, &zindex);
  3552. if (root_cnt->opt & GS_GUI_OPT_DOCKSPACE) ds->zindex = 0;
  3553. else ds->zindex = gs_clamp((int32_t)zindex - 1, 0, INT32_MAX);
  3554. gs_gui_rect_t fr = split->rect;
  3555. 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;
  3556. // gs_gui_draw_frame(ctx, fr, &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][0x00]);
  3557. // Draw splits
  3558. gs_gui_draw_splits(ctx, split);
  3559. // Do resize controls for dockspace
  3560. gs_gui_container_t* top = gs_gui_get_top_most_container(ctx, split);
  3561. const gs_gui_rect_t* sr = &split->rect;
  3562. gs_gui_container_t* hover_cnt = ctx->hover_root ? ctx->hover_root :
  3563. ctx->next_hover_root ? ctx->next_hover_root :
  3564. NULL;
  3565. bool valid_hover = hover_cnt && hover_cnt->zindex > top->zindex;
  3566. // W
  3567. {
  3568. // Cache rect
  3569. gs_gui_rect_t lr = gs_gui_rect(fr.x - 2.f * GS_GUI_SPLIT_SIZE, fr.y, GS_GUI_SPLIT_SIZE, fr.h);
  3570. gs_gui_rect_t ex = lr;
  3571. ex.x -= 10.f; ex.w += 20.f;
  3572. gs_gui_id id = gs_gui_get_id(ctx, "!hov_l", 6);
  3573. gs_gui_update_control(ctx, id, ex, opt);
  3574. if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  3575. {
  3576. gs_gui_draw_control_frame(ctx, id, lr, GS_GUI_ELEMENT_BUTTON, 0x00);
  3577. ctx->next_hover_root = top;
  3578. gs_gui_request_t req = gs_default_val();
  3579. req.type = GS_GUI_SPLIT_RESIZE_W;
  3580. req.split = split;
  3581. gs_dyn_array_push(ctx->requests, req);
  3582. }
  3583. }
  3584. // E
  3585. {
  3586. // Cache rect
  3587. gs_gui_rect_t rr = gs_gui_rect(fr.x + fr.w + GS_GUI_SPLIT_SIZE, fr.y, GS_GUI_SPLIT_SIZE, fr.h);
  3588. gs_gui_rect_t ex = rr;
  3589. ex.w += 20.f;
  3590. gs_gui_id id = gs_gui_get_id(ctx, "!hov_r", 6);
  3591. gs_gui_update_control(ctx, id, ex, opt);
  3592. if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  3593. {
  3594. gs_gui_draw_control_frame(ctx, id, rr, GS_GUI_ELEMENT_BUTTON, 0x00);
  3595. ctx->next_hover_root = top;
  3596. gs_gui_request_t req = gs_default_val();
  3597. req.type = GS_GUI_SPLIT_RESIZE_E;
  3598. req.split = split;
  3599. gs_dyn_array_push(ctx->requests, req);
  3600. }
  3601. }
  3602. // N
  3603. {
  3604. // Cache rect
  3605. gs_gui_rect_t tr = gs_gui_rect(fr.x, fr.y - 2.f * GS_GUI_SPLIT_SIZE, fr.w, GS_GUI_SPLIT_SIZE);
  3606. gs_gui_rect_t ex = tr;
  3607. ex.y -= 10.f;
  3608. ex.h += 20.f;
  3609. gs_gui_id id = gs_gui_get_id(ctx, "!hov_t", 6);
  3610. gs_gui_update_control(ctx, id, ex, opt);
  3611. if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  3612. {
  3613. gs_gui_draw_control_frame(ctx, id, tr, GS_GUI_ELEMENT_BUTTON, 0x00);
  3614. ctx->next_hover_root = top;
  3615. gs_gui_request_t req = gs_default_val();
  3616. req.type = GS_GUI_SPLIT_RESIZE_N;
  3617. req.split = split;
  3618. gs_dyn_array_push(ctx->requests, req);
  3619. }
  3620. }
  3621. // S
  3622. {
  3623. // Cache rect
  3624. gs_gui_rect_t br = gs_gui_rect(fr.x, fr.y + fr.h + GS_GUI_SPLIT_SIZE, fr.w, GS_GUI_SPLIT_SIZE);
  3625. gs_gui_rect_t ex = br;
  3626. ex.h += 20.f;
  3627. gs_gui_id id = gs_gui_get_id(ctx, "!hov_b", 6);
  3628. gs_gui_update_control(ctx, id, ex, opt);
  3629. if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  3630. {
  3631. gs_gui_draw_control_frame(ctx, id, br, GS_GUI_ELEMENT_BUTTON, 0x00);
  3632. ctx->next_hover_root = top;
  3633. gs_gui_request_t req = gs_default_val();
  3634. req.type = GS_GUI_SPLIT_RESIZE_S;
  3635. req.split = split;
  3636. gs_dyn_array_push(ctx->requests, req);
  3637. }
  3638. }
  3639. }
  3640. gs_gui_window_end(ctx);
  3641. }
  3642. }
  3643. */
  3644. if (ctx->mouse_down != GS_GUI_MOUSE_LEFT)
  3645. {
  3646. ctx->lock_focus = 0x00;
  3647. }
  3648. }
  3649. static void gs_gui_docking(gs_gui_context_t* ctx)
  3650. {
  3651. if (ctx->undock_root)
  3652. {
  3653. gs_gui_undock_ex_cnt(ctx, ctx->undock_root);
  3654. }
  3655. if (!ctx->focus_root || ctx->focus_root->opt & GS_GUI_OPT_NODOCK) return;
  3656. if (ctx->dockable_root || ctx->prev_dockable_root)
  3657. {
  3658. gs_gui_container_t* cnt = ctx->dockable_root ? ctx->dockable_root : ctx->prev_dockable_root;
  3659. if ( ctx->prev_dockable_root && !ctx->dockable_root && ctx->mouse_down != GS_GUI_MOUSE_LEFT )
  3660. {
  3661. int32_t b = 0;
  3662. }
  3663. // Cache hoverable tile information
  3664. gs_vec2 c = gs_v2(cnt->rect.x + cnt->rect.w / 2.f, cnt->rect.y + cnt->rect.h / 2.f);
  3665. const float sz = gs_clamp(gs_min(cnt->rect.w * 0.1f, cnt->rect.h * 0.1f), 15.f, 25.f);
  3666. const float off = sz + sz * 0.2f;
  3667. 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);
  3668. 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);
  3669. gs_gui_rect_t center = gs_gui_rect(c.x, c.y, sz, sz);
  3670. gs_gui_rect_t left = gs_gui_rect(c.x - off, c.y, sz, sz);
  3671. gs_gui_rect_t right = gs_gui_rect(c.x + off, c.y, sz, sz);
  3672. gs_gui_rect_t top = gs_gui_rect(c.x, c.y - off, sz, sz);
  3673. gs_gui_rect_t bottom = gs_gui_rect(c.x, c.y + off, sz, sz);
  3674. int32_t hov_c = (gs_gui_rect_overlaps_vec2(center, ctx->mouse_pos));
  3675. int32_t hov_l = gs_gui_rect_overlaps_vec2(left, ctx->mouse_pos);
  3676. int32_t hov_r = gs_gui_rect_overlaps_vec2(right, ctx->mouse_pos);
  3677. int32_t hov_t = gs_gui_rect_overlaps_vec2(top, ctx->mouse_pos);
  3678. int32_t hov_b = gs_gui_rect_overlaps_vec2(bottom, ctx->mouse_pos);
  3679. int32_t hov_w = gs_gui_rect_overlaps_vec2(cnt->rect, ctx->mouse_pos);
  3680. bool can_dock = true;
  3681. // Can't dock one dockspace into another
  3682. if (ctx->focus_root->opt & GS_GUI_OPT_DOCKSPACE)
  3683. {
  3684. can_dock = false;
  3685. }
  3686. if (ctx->focus_root->tab_bar)
  3687. {
  3688. gs_gui_container_t* tcmp = ctx->dockable_root ? ctx->dockable_root : ctx->prev_dockable_root;
  3689. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, ctx->focus_root->tab_bar);
  3690. for (uint32_t i = 0; i < tab_bar->size; ++i)
  3691. {
  3692. gs_gui_container_t* tcnt = (gs_gui_container_t*)tab_bar->items[i].data;
  3693. if (tcnt == tcmp)
  3694. {
  3695. can_dock = false;
  3696. }
  3697. }
  3698. }
  3699. // Need to make sure you CAN dock here first
  3700. if (ctx->dockable_root && can_dock)
  3701. {
  3702. // Need to now grab overlay draw list, then draw rects into it
  3703. gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
  3704. bool is_dockspace = ctx->dockable_root->opt & GS_GUI_OPT_DOCKSPACE;
  3705. // Draw center rect
  3706. 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);
  3707. // 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);
  3708. if (!is_dockspace)
  3709. {
  3710. 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);
  3711. // 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);
  3712. 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);
  3713. // 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);
  3714. 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);
  3715. // 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);
  3716. 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);
  3717. // 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);
  3718. }
  3719. const float d = 0.5f;
  3720. const float hs = sz * 0.5f;
  3721. if (is_dockspace)
  3722. {
  3723. if (hov_c)
  3724. {
  3725. center = gs_gui_rect(cnt->rect.x, cnt->rect.y, cnt->rect.w, cnt->rect.h);
  3726. 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);
  3727. 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);
  3728. }
  3729. }
  3730. else
  3731. {
  3732. if (hov_c && !ctx->focus_root->split)
  3733. {
  3734. center = gs_gui_rect(cnt->rect.x, cnt->rect.y, cnt->rect.w, cnt->rect.h);
  3735. 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);
  3736. 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);
  3737. }
  3738. else if (hov_l)
  3739. {
  3740. left = gs_gui_rect(cnt->rect.x, cnt->rect.y, cnt->rect.w * d + hs, cnt->rect.h);
  3741. 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);
  3742. 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);
  3743. }
  3744. else if (hov_r)
  3745. {
  3746. 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);
  3747. 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);
  3748. 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);
  3749. }
  3750. else if (hov_b)
  3751. {
  3752. 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);
  3753. 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);
  3754. 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);
  3755. }
  3756. else if (hov_t)
  3757. {
  3758. top = gs_gui_rect(cnt->rect.x, cnt->rect.y, cnt->rect.w, cnt->rect.h * d + hs);
  3759. 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);
  3760. 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);
  3761. }
  3762. }
  3763. }
  3764. // Handle docking
  3765. if (ctx->prev_dockable_root && !ctx->dockable_root && ctx->mouse_down != GS_GUI_MOUSE_LEFT)
  3766. {
  3767. gs_gui_container_t* parent = ctx->prev_dockable_root;
  3768. gs_gui_container_t* child = ctx->focus_root;
  3769. bool is_dockspace = ctx->prev_dockable_root->opt & GS_GUI_OPT_DOCKSPACE;
  3770. if (is_dockspace)
  3771. {
  3772. if (hov_c) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_TOP, 1.0f);
  3773. }
  3774. else
  3775. {
  3776. if (hov_c && !ctx->focus_root->split) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_TAB, 0.5f);
  3777. else if (hov_l) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_LEFT, 0.5f);
  3778. else if (hov_r) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_RIGHT, 0.5f);
  3779. else if (hov_t) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_TOP, 0.5f);
  3780. else if (hov_b) gs_gui_dock_ex_cnt(ctx, child, parent, GS_GUI_SPLIT_BOTTOM, 0.5f);
  3781. }
  3782. }
  3783. }
  3784. }
  3785. GS_API_DECL void gs_gui_end(gs_gui_context_t *ctx)
  3786. {
  3787. int32_t i, n;
  3788. // Check for docking, draw overlays
  3789. gs_gui_docking(ctx);
  3790. for (uint32_t r = 0; r < gs_dyn_array_size(ctx->requests); ++r)
  3791. {
  3792. const gs_gui_request_t* req = &ctx->requests[r];
  3793. // If split moved, update position for next frame
  3794. switch (req->type)
  3795. {
  3796. case GS_GUI_CNT_MOVE:
  3797. {
  3798. if (req->cnt)
  3799. {
  3800. req->cnt->rect.x += ctx->mouse_delta.x;
  3801. req->cnt->rect.y += ctx->mouse_delta.y;
  3802. if (req->cnt->tab_bar)
  3803. {
  3804. gs_gui_tab_bar_t* tb = gs_slot_array_getp(ctx->tab_bars, req->cnt->tab_bar);
  3805. gs_assert(tb);
  3806. tb->rect.x += ctx->mouse_delta.x;
  3807. tb->rect.y += ctx->mouse_delta.y;
  3808. }
  3809. }
  3810. } break;
  3811. case GS_GUI_CNT_FOCUS:
  3812. {
  3813. if (!req->cnt) break;
  3814. gs_gui_container_t* cnt = (gs_gui_container_t*)req->cnt;
  3815. gs_assert(cnt);
  3816. gs_gui_split_t* rs = gs_gui_get_root_split(ctx, cnt);
  3817. if (cnt->tab_bar)
  3818. {
  3819. if (rs)
  3820. {
  3821. gs_gui_bring_split_to_front(ctx, rs);
  3822. }
  3823. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
  3824. gs_gui_tab_item_t* tab_item = &tab_bar->items[cnt->tab_item];
  3825. gs_gui_container_t* fcnt = (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data;
  3826. fcnt->opt |= GS_GUI_OPT_NOHOVER;
  3827. fcnt->opt |= GS_GUI_OPT_NOINTERACT;
  3828. fcnt->flags &= ~GS_GUI_WINDOW_FLAGS_VISIBLE;
  3829. tab_bar->focus = tab_item->idx;
  3830. cnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
  3831. // Bring all tab items to front
  3832. for (uint32_t i = 0; i < tab_bar->size; ++i)
  3833. {
  3834. gs_gui_bring_to_front(ctx, (gs_gui_container_t*)tab_bar->items[i].data);
  3835. }
  3836. gs_gui_bring_to_front(ctx, cnt);
  3837. }
  3838. } break;
  3839. case GS_GUI_SPLIT_MOVE:
  3840. {
  3841. if (req->split)
  3842. {
  3843. req->split->rect.x += ctx->mouse_delta.x;
  3844. req->split->rect.y += ctx->mouse_delta.y;
  3845. gs_gui_update_split(ctx, req->split);
  3846. }
  3847. } break;
  3848. case GS_GUI_SPLIT_RESIZE_SE:
  3849. {
  3850. if (req->split)
  3851. {
  3852. gs_gui_rect_t* r = &req->split->rect;
  3853. r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
  3854. r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
  3855. gs_gui_update_split(ctx, req->split);
  3856. }
  3857. } break;
  3858. case GS_GUI_SPLIT_RESIZE_W:
  3859. {
  3860. if (req->split)
  3861. {
  3862. gs_gui_rect_t* r = &req->split->rect;
  3863. float w = r->w;
  3864. float max_x = r->x + r->w;
  3865. r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
  3866. if (fabsf(r->w - w) > 0.f)
  3867. {
  3868. r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
  3869. }
  3870. gs_gui_update_split(ctx, req->split);
  3871. }
  3872. } break;
  3873. case GS_GUI_SPLIT_RESIZE_E:
  3874. {
  3875. if (req->split)
  3876. {
  3877. gs_gui_rect_t* r = &req->split->rect;
  3878. r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
  3879. gs_gui_update_split(ctx, req->split);
  3880. }
  3881. } break;
  3882. case GS_GUI_SPLIT_RESIZE_N:
  3883. {
  3884. if (req->split)
  3885. {
  3886. gs_gui_rect_t* r = &req->split->rect;
  3887. float h = r->h;
  3888. float max_y = h + r->y;
  3889. r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
  3890. if (fabsf(r->h - h) > 0.f)
  3891. {
  3892. r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
  3893. }
  3894. gs_gui_update_split(ctx, req->split);
  3895. }
  3896. } break;
  3897. case GS_GUI_SPLIT_RESIZE_NE:
  3898. {
  3899. if (req->split)
  3900. {
  3901. gs_gui_rect_t* r = &req->split->rect;
  3902. r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
  3903. float h = r->h;
  3904. float max_y = h + r->y;
  3905. r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
  3906. if (fabsf(r->h - h) > 0.f)
  3907. {
  3908. r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
  3909. }
  3910. gs_gui_update_split(ctx, req->split);
  3911. }
  3912. } break;
  3913. case GS_GUI_SPLIT_RESIZE_NW:
  3914. {
  3915. if (req->split)
  3916. {
  3917. gs_gui_rect_t* r = &req->split->rect;
  3918. float h = r->h;
  3919. float max_y = h + r->y;
  3920. r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
  3921. if (fabsf(r->h - h) > 0.f)
  3922. {
  3923. r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
  3924. }
  3925. float w = r->w;
  3926. float max_x = r->x + r->w;
  3927. r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
  3928. if (fabsf(r->w - w) > 0.f)
  3929. {
  3930. r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
  3931. }
  3932. gs_gui_update_split(ctx, req->split);
  3933. }
  3934. } break;
  3935. case GS_GUI_SPLIT_RESIZE_S:
  3936. {
  3937. if (req->split)
  3938. {
  3939. gs_gui_rect_t* r = &req->split->rect;
  3940. r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
  3941. gs_gui_update_split(ctx, req->split);
  3942. }
  3943. } break;
  3944. case GS_GUI_SPLIT_RESIZE_SW:
  3945. {
  3946. if (req->split)
  3947. {
  3948. gs_gui_rect_t* r = &req->split->rect;
  3949. float h = r->h;
  3950. float max_y = h + r->y;
  3951. r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
  3952. float w = r->w;
  3953. float max_x = r->x + r->w;
  3954. r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
  3955. if (fabsf(r->w - w) > 0.f)
  3956. {
  3957. r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
  3958. }
  3959. gs_gui_update_split(ctx, req->split);
  3960. }
  3961. } break;
  3962. case GS_GUI_SPLIT_RATIO:
  3963. {
  3964. const float smin = 0.05f;
  3965. const float smax = 1.f - smin;
  3966. gs_gui_split_t* split = req->split;
  3967. switch (split->type)
  3968. {
  3969. case GS_GUI_SPLIT_LEFT:
  3970. {
  3971. split->ratio = gs_clamp(split->ratio + ctx->mouse_delta.x / split->rect.w, smin, smax);
  3972. gs_gui_update_split(ctx, split);
  3973. } break;
  3974. case GS_GUI_SPLIT_RIGHT:
  3975. {
  3976. split->ratio = gs_clamp(split->ratio - ctx->mouse_delta.x / split->rect.w, smin, smax);
  3977. gs_gui_update_split(ctx, split);
  3978. } break;
  3979. case GS_GUI_SPLIT_TOP:
  3980. {
  3981. split->ratio = gs_clamp(split->ratio + ctx->mouse_delta.y / split->rect.h, smin, smax);
  3982. gs_gui_update_split(ctx, split);
  3983. } break;
  3984. case GS_GUI_SPLIT_BOTTOM:
  3985. {
  3986. split->ratio = gs_clamp(split->ratio - ctx->mouse_delta.y / split->rect.h, smin, smax);
  3987. gs_gui_update_split(ctx, split);
  3988. } break;
  3989. }
  3990. // Bring to font
  3991. gs_gui_bring_split_to_front(ctx, gs_gui_get_root_split_from_split(ctx, split));
  3992. } break;
  3993. case GS_GUI_TAB_SWAP_LEFT:
  3994. {
  3995. gs_gui_tab_item_swap(ctx, req->cnt, -1);
  3996. } break;
  3997. case GS_GUI_TAB_SWAP_RIGHT:
  3998. {
  3999. gs_gui_tab_item_swap(ctx, req->cnt, +1);
  4000. } break;
  4001. }
  4002. }
  4003. // Clear reqests
  4004. gs_dyn_array_clear(ctx->requests);
  4005. // Check stacks
  4006. gs_gui_expect(ctx->container_stack.idx == 0);
  4007. gs_gui_expect(ctx->clip_stack.idx == 0);
  4008. gs_gui_expect(ctx->id_stack.idx == 0);
  4009. gs_gui_expect(ctx->layout_stack.idx == 0);
  4010. // Have to clear style stacks
  4011. // Set previous frame ids
  4012. // ctx->prev_hover = 0;
  4013. // ctx->prev_focus = ctx->focus;
  4014. // Handle scroll input
  4015. if (ctx->scroll_target)
  4016. {
  4017. ctx->scroll_target->scroll.x += ctx->scroll_delta.x;
  4018. ctx->scroll_target->scroll.y += ctx->scroll_delta.y;
  4019. }
  4020. // Unset focus if focus id was not touched this frame
  4021. if (!ctx->updated_focus) { ctx->focus = 0; }
  4022. ctx->updated_focus = 0;
  4023. // Bring hover root to front if mouse was pressed
  4024. if (ctx->mouse_pressed && ctx->next_hover_root &&
  4025. ctx->next_hover_root->zindex < ctx->last_zindex &&
  4026. ctx->next_hover_root->zindex >= 0
  4027. )
  4028. {
  4029. // Root split
  4030. gs_gui_split_t* split = gs_gui_get_root_split(ctx, ctx->next_hover_root);
  4031. // Need to bring entire dockspace to front
  4032. if (split)
  4033. {
  4034. gs_gui_bring_split_to_front(ctx, split);
  4035. }
  4036. else if (~ctx->next_hover_root->opt & GS_GUI_OPT_NOFOCUS)
  4037. {
  4038. gs_gui_bring_to_front(ctx, ctx->next_hover_root);
  4039. }
  4040. }
  4041. if (ctx->mouse_pressed && (!ctx->next_hover_root || ctx->next_hover_root->opt & GS_GUI_OPT_NOFOCUS))
  4042. {
  4043. ctx->active_root = NULL;
  4044. }
  4045. // Reset state
  4046. ctx->key_pressed = 0;
  4047. ctx->input_text[0] = '\0';
  4048. ctx->mouse_pressed = 0;
  4049. ctx->scroll_delta = gs_v2(0, 0);
  4050. ctx->last_mouse_pos = ctx->mouse_pos;
  4051. ctx->undock_root = NULL;
  4052. if (ctx->mouse_down != GS_GUI_MOUSE_LEFT)
  4053. {
  4054. gs_platform_set_cursor(ctx->window_hndl, GS_PLATFORM_CURSOR_ARROW);
  4055. }
  4056. // Sort root containers by zindex
  4057. n = ctx->root_list.idx;
  4058. qsort(ctx->root_list.items, n, sizeof(gs_gui_container_t*), gs_gui_compare_zindex);
  4059. // Set root container jump commands
  4060. for (i = 0; i < n; i++)
  4061. {
  4062. gs_gui_container_t *cnt = ctx->root_list.items[i];
  4063. // If this is the first container then make the first command jump to it.
  4064. // otherwise set the previous container's tail to jump to this one
  4065. if (i == 0)
  4066. {
  4067. gs_gui_command_t *cmd = (gs_gui_command_t*) ctx->command_list.items;
  4068. cmd->jump.dst = (char*) cnt->head + sizeof(gs_gui_jumpcommand_t);
  4069. }
  4070. else
  4071. {
  4072. gs_gui_container_t *prev = ctx->root_list.items[i - 1];
  4073. prev->tail->jump.dst = (char*) cnt->head + sizeof(gs_gui_jumpcommand_t);
  4074. }
  4075. // Make the last container's tail jump to the end of command list
  4076. if (i == n - 1)
  4077. {
  4078. cnt->tail->jump.dst = ctx->command_list.items + ctx->command_list.idx;
  4079. }
  4080. }
  4081. }
  4082. GS_API_DECL void
  4083. gs_gui_render(gs_gui_context_t* ctx, gs_command_buffer_t* cb)
  4084. {
  4085. const gs_vec2 fb = ctx->framebuffer_size;
  4086. const gs_gui_rect_t* viewport = &ctx->viewport;
  4087. gs_immediate_draw_t* gsi = &ctx->gsi;
  4088. gsi_defaults(&ctx->gsi);
  4089. // gsi_camera2D(&ctx->gsi, (uint32_t)fb.x, (uint32_t)fb.y);
  4090. gsi_camera2D(&ctx->gsi, (uint32_t)viewport->w, (uint32_t)viewport->h);
  4091. gsi_blend_enabled(&ctx->gsi, true);
  4092. gs_gui_rect_t clip = gs_gui_unclipped_rect;
  4093. gs_gui_command_t* cmd = NULL;
  4094. while (gs_gui_next_command(ctx, &cmd))
  4095. {
  4096. switch (cmd->type)
  4097. {
  4098. case GS_GUI_COMMAND_CUSTOM:
  4099. {
  4100. gsi_defaults(&ctx->gsi);
  4101. gsi_set_view_scissor(&ctx->gsi,
  4102. (int32_t)(cmd->custom.clip.x),
  4103. (int32_t)(fb.y - cmd->custom.clip.h - cmd->custom.clip.y),
  4104. (int32_t)(cmd->custom.clip.w),
  4105. (int32_t)(cmd->custom.clip.h));
  4106. if (cmd->custom.cb) {
  4107. cmd->custom.cb(ctx, &cmd->custom);
  4108. }
  4109. gsi_defaults(&ctx->gsi);
  4110. // gsi_camera2D(&ctx->gsi, (uint32_t)fb.x, (uint32_t)fb.y);
  4111. gsi_camera2D(&ctx->gsi, (uint32_t)viewport->w, (uint32_t)viewport->h);
  4112. gsi_blend_enabled(&ctx->gsi, true);
  4113. // gs_graphics_set_viewport(&ctx->gsi.commands, 0, 0, (uint32_t)fb.x, (uint32_t)fb.y);
  4114. gs_graphics_set_viewport(&ctx->gsi.commands, (uint32_t)viewport->x, (uint32_t)viewport->y, (uint32_t)viewport->w, (uint32_t)viewport->h);
  4115. gsi_set_view_scissor(&ctx->gsi,
  4116. (int32_t)(clip.x),
  4117. (int32_t)(fb.y - clip.h - clip.y),
  4118. (int32_t)(clip.w),
  4119. (int32_t)(clip.h));
  4120. } break;
  4121. case GS_GUI_COMMAND_PIPELINE:
  4122. {
  4123. gsi_pipeline_set(&ctx->gsi, cmd->pipeline.pipeline);
  4124. // Set layout if valid
  4125. if (cmd->pipeline.layout_sz)
  4126. {
  4127. switch (cmd->pipeline.layout_type)
  4128. {
  4129. case GSI_LAYOUT_VATTR:
  4130. {
  4131. gsi_vattr_list(&ctx->gsi, cmd->pipeline.layout, cmd->pipeline.layout_sz);
  4132. } break;
  4133. case GSI_LAYOUT_MESH:
  4134. {
  4135. gsi_vattr_list_mesh(&ctx->gsi, cmd->pipeline.layout, cmd->pipeline.layout_sz);
  4136. } break;
  4137. }
  4138. }
  4139. // If not a valid pipeline, then set back to default gui pipeline
  4140. if (!cmd->pipeline.pipeline.id)
  4141. {
  4142. gsi_blend_enabled(&ctx->gsi, true);
  4143. }
  4144. } break;
  4145. case GS_GUI_COMMAND_UNIFORMS:
  4146. {
  4147. gs_graphics_bind_desc_t bind = gs_default_val();
  4148. // Set uniform bind
  4149. gs_graphics_bind_uniform_desc_t uniforms[1] = gs_default_val();
  4150. bind.uniforms.desc = uniforms;
  4151. bind.uniforms.size = sizeof(uniforms);
  4152. // Treat as byte buffer, read data
  4153. gs_byte_buffer_t buffer = gs_default_val();
  4154. buffer.capacity = GS_GUI_COMMANDLIST_SIZE;
  4155. buffer.data = cmd->uniforms.data;
  4156. // Write count
  4157. gs_byte_buffer_readc(&buffer, uint16_t, ct);
  4158. // Iterate through all uniforms, memcpy data as needed for each uniform in list
  4159. for (uint32_t i = 0; i < ct; ++i)
  4160. {
  4161. gs_byte_buffer_readc(&buffer, gs_handle(gs_graphics_uniform_t), hndl);
  4162. gs_byte_buffer_readc(&buffer, size_t, sz);
  4163. gs_byte_buffer_readc(&buffer, uint16_t, binding);
  4164. void* udata = (buffer.data + buffer.position);
  4165. gs_byte_buffer_advance_position(&buffer, sz);
  4166. uniforms[0].uniform = hndl;
  4167. uniforms[0].binding = binding;
  4168. uniforms[0].data = udata;
  4169. gs_graphics_apply_bindings(&ctx->gsi.commands, &bind);
  4170. }
  4171. } break;
  4172. case GS_GUI_COMMAND_TEXT:
  4173. {
  4174. const gs_vec2* tp = &cmd->text.pos;
  4175. const char* ts = cmd->text.str;
  4176. const gs_color_t* tc = &cmd->text.color;
  4177. const gs_asset_font_t* tf = cmd->text.font;
  4178. gsi_text(&ctx->gsi, tp->x, tp->y, ts, tf, false, tc->r, tc->g, tc->b, tc->a);
  4179. } break;
  4180. case GS_GUI_COMMAND_SHAPE:
  4181. {
  4182. gsi_texture(&ctx->gsi, gs_handle_invalid(gs_graphics_texture_t));
  4183. gs_color_t* c = &cmd->shape.color;
  4184. switch (cmd->shape.type)
  4185. {
  4186. case GS_GUI_SHAPE_RECT:
  4187. {
  4188. gs_gui_rect_t* r = &cmd->shape.rect;
  4189. 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);
  4190. } break;
  4191. case GS_GUI_SHAPE_CIRCLE:
  4192. {
  4193. gs_vec2* cp = &cmd->shape.circle.center;
  4194. float* r = &cmd->shape.circle.radius;
  4195. gsi_circle(&ctx->gsi, cp->x, cp->y, *r, 16, c->r, c->g, c->b, c->a, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
  4196. } break;
  4197. case GS_GUI_SHAPE_TRIANGLE:
  4198. {
  4199. gs_vec2* pa = &cmd->shape.triangle.points[0];
  4200. gs_vec2* pb = &cmd->shape.triangle.points[1];
  4201. gs_vec2* pc = &cmd->shape.triangle.points[2];
  4202. gsi_trianglev(&ctx->gsi, *pa, *pb, *pc, *c, GS_GRAPHICS_PRIMITIVE_TRIANGLES);
  4203. } break;
  4204. case GS_GUI_SHAPE_LINE:
  4205. {
  4206. gs_vec2* s = &cmd->shape.line.start;
  4207. gs_vec2* e = &cmd->shape.line.end;
  4208. gsi_linev(&ctx->gsi, *s, *e, *c);
  4209. } break;
  4210. }
  4211. } break;
  4212. case GS_GUI_COMMAND_IMAGE:
  4213. {
  4214. gsi_texture(&ctx->gsi, cmd->image.hndl);
  4215. gs_color_t* c = &cmd->image.color;
  4216. gs_gui_rect_t* r = &cmd->image.rect;
  4217. gs_vec4* uvs = &cmd->image.uvs;
  4218. 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);
  4219. } break;
  4220. case GS_GUI_COMMAND_CLIP:
  4221. {
  4222. // Will project scissor/clipping rectangles into framebuffer space
  4223. gs_vec2 clip_off = gs_v2s(0.f); // (0,0) unless using multi-viewports
  4224. gs_vec2 clip_scale = gs_v2s(1.f); // (1,1) unless using retina display which are often (2,2)
  4225. gs_gui_rect_t clip_rect;
  4226. clip_rect.x = (cmd->clip.rect.x - clip_off.x) * clip_scale.x;
  4227. clip_rect.y = (cmd->clip.rect.y - clip_off.y) * clip_scale.y;
  4228. clip_rect.w = (cmd->clip.rect.w - clip_off.x) * clip_scale.x;
  4229. clip_rect.h = (cmd->clip.rect.h - clip_off.y) * clip_scale.y;
  4230. clip_rect.x = gs_max(clip_rect.x, 0.f);
  4231. clip_rect.y = gs_max(clip_rect.y, 0.f);
  4232. clip_rect.w = gs_max(clip_rect.w, 0.f);
  4233. clip_rect.h = gs_max(clip_rect.h, 0.f);
  4234. clip = clip_rect;
  4235. gsi_set_view_scissor(&ctx->gsi,
  4236. (int32_t)(clip_rect.x),
  4237. (int32_t)(fb.y - clip_rect.h - clip_rect.y),
  4238. (int32_t)(clip_rect.w),
  4239. (int32_t)(clip_rect.h));
  4240. } break;
  4241. }
  4242. }
  4243. // Draw main list
  4244. gsi_draw(&ctx->gsi, cb);
  4245. // Draw overlay list
  4246. gsi_draw(&ctx->overlay_draw_list, cb);
  4247. }
  4248. GS_API_DECL void gs_gui_renderpass_submit(gs_gui_context_t* ctx, gs_command_buffer_t* cb, gs_color_t c)
  4249. {
  4250. gs_vec2 fbs = ctx->framebuffer_size;
  4251. gs_gui_rect_t* vp = &ctx->viewport;
  4252. gs_graphics_clear_action_t action = gs_default_val();
  4253. action.color[0] = (float)c.r / 255.f;
  4254. action.color[1] = (float)c.g / 255.f;
  4255. action.color[2] = (float)c.b / 255.f;
  4256. action.color[3] = (float)c.a / 255.f;
  4257. gs_graphics_clear_desc_t clear = gs_default_val();
  4258. clear.actions = &action;
  4259. gs_graphics_renderpass_begin(cb, GS_GRAPHICS_RENDER_PASS_DEFAULT);
  4260. {
  4261. gs_graphics_clear(cb, &clear);
  4262. gs_graphics_set_viewport(cb, (uint32_t)vp->x, (uint32_t)vp->y, (uint32_t)vp->w, (uint32_t)vp->h);
  4263. gs_gui_render(ctx, cb);
  4264. }
  4265. gs_graphics_renderpass_end(cb);
  4266. }
  4267. GS_API_DECL void
  4268. gs_gui_renderpass_submit_ex(gs_gui_context_t* ctx, gs_command_buffer_t* cb, gs_graphics_clear_action_t* action)
  4269. {
  4270. gs_vec2 fbs = ctx->framebuffer_size;
  4271. gs_gui_rect_t* vp = &ctx->viewport;
  4272. gs_graphics_clear_desc_t clear = gs_default_val();
  4273. clear.actions = action;
  4274. gs_renderpass_t pass = gs_default_val();
  4275. gs_graphics_renderpass_begin(cb, pass);
  4276. gs_graphics_set_viewport(cb, (uint32_t)vp->x, (uint32_t)vp->y, (uint32_t)vp->w, (uint32_t)vp->h);
  4277. gs_graphics_clear(cb, &clear);
  4278. gs_gui_render(ctx, cb);
  4279. gs_graphics_renderpass_end(cb);
  4280. }
  4281. GS_API_DECL void
  4282. gs_gui_set_hover(gs_gui_context_t *ctx, gs_gui_id id)
  4283. {
  4284. ctx->prev_hover = ctx->hover;
  4285. ctx->hover = id;
  4286. }
  4287. GS_API_DECL void
  4288. gs_gui_set_focus(gs_gui_context_t* ctx, gs_gui_id id)
  4289. {
  4290. ctx->prev_focus = ctx->focus;
  4291. ctx->focus = id;
  4292. ctx->updated_focus = 1;
  4293. }
  4294. GS_API_DECL gs_gui_id
  4295. gs_gui_get_id(gs_gui_context_t* ctx, const void* data, int32_t size)
  4296. {
  4297. int32_t idx = ctx->id_stack.idx;
  4298. gs_gui_id res = (idx > 0) ? ctx->id_stack.items[idx - 1] : GS_GUI_HASH_INITIAL;
  4299. gs_gui_hash(&res, data, size);
  4300. ctx->last_id = res;
  4301. return res;
  4302. }
  4303. GS_API_DECL gs_gui_id
  4304. gs_gui_get_id_hash(gs_gui_context_t* ctx, const void* data, int32_t size, gs_gui_id hash)
  4305. {
  4306. gs_gui_id res = hash;
  4307. gs_gui_hash(&res, data, size);
  4308. ctx->last_id = res;
  4309. return res;
  4310. }
  4311. GS_API_DECL gs_gui_id
  4312. gs_gui_push_id(gs_gui_context_t* ctx, const void* data, int32_t size)
  4313. {
  4314. gs_gui_id id = gs_gui_get_id(ctx, data, size);
  4315. gs_gui_stack_push(ctx->id_stack, id);
  4316. return id;
  4317. }
  4318. GS_API_DECL void
  4319. gs_gui_pop_id(gs_gui_context_t* ctx)
  4320. {
  4321. gs_gui_stack_pop(ctx->id_stack);
  4322. }
  4323. GS_API_DECL void
  4324. gs_gui_push_clip_rect(gs_gui_context_t* ctx, gs_gui_rect_t rect)
  4325. {
  4326. gs_gui_rect_t last = gs_gui_get_clip_rect(ctx);
  4327. gs_gui_stack_push(ctx->clip_stack, gs_gui_intersect_rects(rect, last));
  4328. }
  4329. GS_API_DECL void
  4330. gs_gui_pop_clip_rect(gs_gui_context_t* ctx)
  4331. {
  4332. gs_gui_stack_pop(ctx->clip_stack);
  4333. }
  4334. GS_API_DECL gs_gui_rect_t
  4335. gs_gui_get_clip_rect(gs_gui_context_t* ctx)
  4336. {
  4337. gs_gui_expect(ctx->clip_stack.idx > 0);
  4338. return ctx->clip_stack.items[ctx->clip_stack.idx - 1];
  4339. }
  4340. GS_API_DECL int32_t
  4341. gs_gui_check_clip(gs_gui_context_t* ctx, gs_gui_rect_t r)
  4342. {
  4343. gs_gui_rect_t cr = gs_gui_get_clip_rect(ctx);
  4344. if (r.x > cr.x + cr.w || r.x + r.w < cr.x ||
  4345. r.y > cr.y + cr.h || r.y + r.h < cr.y)
  4346. {
  4347. return GS_GUI_CLIP_ALL;
  4348. }
  4349. if (r.x >= cr.x && r.x + r.w <= cr.x + cr.w &&
  4350. r.y >= cr.y && r.y + r.h <= cr.y + cr.h )
  4351. {
  4352. return 0;
  4353. }
  4354. return GS_GUI_CLIP_PART;
  4355. }
  4356. GS_API_DECL gs_gui_container_t*
  4357. gs_gui_get_current_container(gs_gui_context_t* ctx)
  4358. {
  4359. gs_gui_expect(ctx->container_stack.idx > 0);
  4360. return ctx->container_stack.items[ctx->container_stack.idx - 1];
  4361. }
  4362. GS_API_DECL void
  4363. gs_gui_current_container_close(gs_gui_context_t* ctx)
  4364. {
  4365. gs_gui_container_t* cnt = gs_gui_get_current_container(ctx);
  4366. cnt->open = false;
  4367. }
  4368. GS_API_DECL gs_gui_container_t*
  4369. gs_gui_get_container(gs_gui_context_t* ctx, const char* name)
  4370. {
  4371. gs_gui_id id = gs_gui_get_id(ctx, name, strlen(name));
  4372. return gs_gui_get_container_ex(ctx, id, 0);
  4373. }
  4374. GS_API_DECL void
  4375. gs_gui_bring_to_front(gs_gui_context_t* ctx, gs_gui_container_t* cnt)
  4376. {
  4377. gs_gui_container_t* root = gs_gui_get_root_container(ctx, cnt);
  4378. if (root->opt & GS_GUI_OPT_NOBRINGTOFRONT)
  4379. {
  4380. if (cnt->opt & GS_GUI_OPT_DOCKSPACE) cnt->zindex = 0;
  4381. else cnt->zindex = 2;
  4382. if (cnt->tab_bar)
  4383. {
  4384. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
  4385. for (uint32_t i = 0; i < tab_bar->size; ++i)
  4386. {
  4387. ((gs_gui_container_t*)tab_bar->items[i].data)->zindex = cnt->zindex + i;
  4388. }
  4389. }
  4390. }
  4391. else
  4392. {
  4393. cnt->zindex = ++ctx->last_zindex;
  4394. // If container is part of a tab item, then iterate and bring to front as well
  4395. if (cnt->tab_bar)
  4396. {
  4397. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
  4398. for (uint32_t i = 0; i < tab_bar->size; ++i)
  4399. {
  4400. ((gs_gui_container_t*)tab_bar->items[i].data)->zindex = ++ctx->last_zindex;
  4401. }
  4402. }
  4403. }
  4404. }
  4405. /*============================================================================
  4406. ** Pool
  4407. **============================================================================*/
  4408. GS_API_DECL int32_t
  4409. gs_gui_pool_init(gs_gui_context_t* ctx, gs_gui_pool_item_t* items, int32_t len, gs_gui_id id)
  4410. {
  4411. int32_t i, n = -1, f = ctx->frame;
  4412. for (i = 0; i < len; i++)
  4413. {
  4414. if (items[i].last_update < f)
  4415. {
  4416. f = items[i].last_update;
  4417. n = i;
  4418. }
  4419. }
  4420. gs_gui_expect(n > -1);
  4421. items[n].id = id;
  4422. gs_gui_pool_update(ctx, items, n);
  4423. return n;
  4424. }
  4425. GS_API_DECL int32_t
  4426. gs_gui_pool_get(gs_gui_context_t* ctx, gs_gui_pool_item_t* items, int32_t len, gs_gui_id id)
  4427. {
  4428. // Note(john): This is a linear hash lookup. Could speed this up with a quadratic lookup.
  4429. int32_t i;
  4430. gs_gui_unused(ctx);
  4431. for (i = 0; i < len; i++)
  4432. {
  4433. if (items[i].id == id)
  4434. {
  4435. return i;
  4436. }
  4437. }
  4438. return -1;
  4439. }
  4440. GS_API_DECL void
  4441. gs_gui_pool_update(gs_gui_context_t* ctx, gs_gui_pool_item_t* items, int32_t idx)
  4442. {
  4443. items[idx].last_update = ctx->frame;
  4444. }
  4445. /*============================================================================
  4446. ** input handlers
  4447. **============================================================================*/
  4448. GS_API_DECL void
  4449. gs_gui_input_mousemove(gs_gui_context_t* ctx, int32_t x, int32_t y)
  4450. {
  4451. ctx->mouse_pos = gs_v2((f32)x, (f32)y);
  4452. }
  4453. GS_API_DECL void
  4454. gs_gui_input_mousedown(gs_gui_context_t* ctx, int32_t x, int32_t y, int32_t btn)
  4455. {
  4456. gs_gui_input_mousemove(ctx, x, y);
  4457. ctx->mouse_down |= btn;
  4458. ctx->mouse_pressed |= btn;
  4459. }
  4460. GS_API_DECL void
  4461. gs_gui_input_mouseup(gs_gui_context_t* ctx, int32_t x, int32_t y, int32_t btn)
  4462. {
  4463. gs_gui_input_mousemove(ctx, x, y);
  4464. ctx->mouse_down &= ~btn;
  4465. }
  4466. GS_API_DECL void
  4467. gs_gui_input_scroll(gs_gui_context_t* ctx, int32_t x, int32_t y)
  4468. {
  4469. ctx->scroll_delta.x += x;
  4470. ctx->scroll_delta.y += y;
  4471. }
  4472. GS_API_DECL void
  4473. gs_gui_input_keydown(gs_gui_context_t* ctx, int32_t key)
  4474. {
  4475. ctx->key_pressed |= key;
  4476. ctx->key_down |= key;
  4477. }
  4478. GS_API_DECL void
  4479. gs_gui_input_keyup(gs_gui_context_t* ctx, int32_t key)
  4480. {
  4481. ctx->key_down &= ~key;
  4482. }
  4483. GS_API_DECL void
  4484. gs_gui_input_text(gs_gui_context_t* ctx, const char* text)
  4485. {
  4486. int32_t len = strlen(ctx->input_text);
  4487. int32_t size = strlen(text) + 1;
  4488. if (len + size > (int32_t)sizeof(ctx->input_text)) return;
  4489. memcpy(ctx->input_text + len, text, size);
  4490. }
  4491. /*============================================================================
  4492. ** commandlist
  4493. **============================================================================*/
  4494. GS_API_DECL gs_gui_command_t*
  4495. gs_gui_push_command(gs_gui_context_t* ctx, int32_t type, int32_t size)
  4496. {
  4497. gs_gui_command_t* cmd = (gs_gui_command_t*) (ctx->command_list.items + ctx->command_list.idx);
  4498. gs_gui_expect(ctx->command_list.idx + size < GS_GUI_COMMANDLIST_SIZE);
  4499. cmd->base.type = type;
  4500. cmd->base.size = size;
  4501. ctx->command_list.idx += size;
  4502. return cmd;
  4503. }
  4504. GS_API_DECL int32_t
  4505. gs_gui_next_command(gs_gui_context_t* ctx, gs_gui_command_t** cmd)
  4506. {
  4507. if (*cmd)
  4508. {
  4509. *cmd = (gs_gui_command_t*) (((char*) *cmd) + (*cmd)->base.size);
  4510. }
  4511. else
  4512. {
  4513. *cmd = (gs_gui_command_t*) ctx->command_list.items;
  4514. }
  4515. while ((uint8_t*) *cmd != (uint8_t*)(ctx->command_list.items + ctx->command_list.idx))
  4516. {
  4517. if ((*cmd)->type != GS_GUI_COMMAND_JUMP)
  4518. {
  4519. return 1;
  4520. }
  4521. *cmd = (gs_gui_command_t*)((*cmd)->jump.dst);
  4522. }
  4523. return 0;
  4524. }
  4525. GS_API_DECL void
  4526. gs_gui_set_clip(gs_gui_context_t* ctx, gs_gui_rect_t rect)
  4527. {
  4528. gs_gui_command_t* cmd;
  4529. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_CLIP, sizeof(gs_gui_clipcommand_t));
  4530. cmd->clip.rect = rect;
  4531. }
  4532. GS_API_DECL void
  4533. gs_gui_set_pipeline(gs_gui_context_t* ctx, gs_handle(gs_graphics_pipeline_t) pip,
  4534. void* layout, size_t sz, gsi_layout_type type)
  4535. {
  4536. gs_gui_command_t* cmd;
  4537. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_PIPELINE, sizeof(gs_gui_pipelinecommand_t));
  4538. cmd->pipeline.pipeline = pip;
  4539. cmd->pipeline.layout_type = type;
  4540. cmd->pipeline.layout = ctx->command_list.items + ctx->command_list.idx;
  4541. cmd->pipeline.layout_sz = sz;
  4542. cmd->base.size += sz;
  4543. // Copy data and move list forward
  4544. memcpy(ctx->command_list.items + ctx->command_list.idx, layout, sz);
  4545. ctx->command_list.idx += sz;
  4546. }
  4547. GS_API_DECL void
  4548. gs_gui_bind_uniforms(gs_gui_context_t* ctx,
  4549. gs_graphics_bind_uniform_desc_t* uniforms, size_t uniforms_sz)
  4550. {
  4551. gs_gui_command_t* cmd;
  4552. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_UNIFORMS, sizeof(gs_gui_binduniformscommand_t));
  4553. cmd->uniforms.data = ctx->command_list.items + ctx->command_list.idx;
  4554. // Treat as byte buffer, write into data then set size
  4555. gs_byte_buffer_t buffer = gs_default_val();
  4556. buffer.capacity = GS_GUI_COMMANDLIST_SIZE;
  4557. buffer.data = cmd->uniforms.data;
  4558. const uint16_t ct = uniforms_sz / sizeof(gs_graphics_bind_uniform_desc_t);
  4559. // Write count
  4560. gs_byte_buffer_write(&buffer, uint16_t, ct);
  4561. // Iterate through all uniforms, memcpy data as needed for each uniform in list
  4562. for (uint32_t i = 0; i < ct; ++i)
  4563. {
  4564. gs_graphics_bind_uniform_desc_t* decl = &uniforms[i];
  4565. gs_handle(gs_graphics_uniform_t) hndl = decl->uniform;
  4566. const size_t sz = gs_graphics_uniform_size_query(hndl);
  4567. gs_byte_buffer_write(&buffer, gs_handle(gs_graphics_uniform_t), hndl);
  4568. gs_byte_buffer_write(&buffer, size_t, sz);
  4569. gs_byte_buffer_write(&buffer, uint16_t, (uint16_t)decl->binding);
  4570. gs_byte_buffer_write_bulk(&buffer, decl->data, sz);
  4571. }
  4572. // Record final sizes
  4573. const size_t sz = buffer.size;
  4574. cmd->base.size += sz;
  4575. ctx->command_list.idx += sz;
  4576. }
  4577. GS_API_DECL void
  4578. gs_gui_draw_line(gs_gui_context_t* ctx, gs_vec2 start, gs_vec2 end, gs_color_t color)
  4579. {
  4580. gs_gui_command_t* cmd;
  4581. gs_gui_rect_t rect = gs_default_val();
  4582. gs_vec2 s = start.x < end.x ? start : end;
  4583. gs_vec2 e = start.x < end.x ? end : start;
  4584. gs_gui_rect(s.x, s.y, e.x - s.x, e.y - s.y);
  4585. rect = gs_gui_intersect_rects(rect, gs_gui_get_clip_rect(ctx));
  4586. // do clip command if the rect isn't fully contained within the cliprect
  4587. int32_t clipped = gs_gui_check_clip(ctx, rect);
  4588. if (clipped == GS_GUI_CLIP_ALL ) {return;}
  4589. if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, gs_gui_get_clip_rect(ctx));}
  4590. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_SHAPE, sizeof(gs_gui_shapecommand_t));
  4591. cmd->shape.type = GS_GUI_SHAPE_LINE;
  4592. cmd->shape.line.start = s;
  4593. cmd->shape.line.end = e;
  4594. cmd->shape.color = color;
  4595. // reset clipping if it was set
  4596. if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
  4597. }
  4598. GS_API_DECL void
  4599. gs_gui_draw_rect(gs_gui_context_t* ctx, gs_gui_rect_t rect, gs_color_t color)
  4600. {
  4601. gs_gui_command_t* cmd;
  4602. rect = gs_gui_intersect_rects(rect, gs_gui_get_clip_rect(ctx));
  4603. if (rect.w > 0 && rect.h > 0)
  4604. {
  4605. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_SHAPE, sizeof(gs_gui_shapecommand_t));
  4606. cmd->shape.type = GS_GUI_SHAPE_RECT;
  4607. cmd->shape.rect = rect;
  4608. cmd->shape.color = color;
  4609. }
  4610. }
  4611. GS_API_DECL void
  4612. gs_gui_draw_circle(gs_gui_context_t* ctx, gs_vec2 position, float radius, gs_color_t color)
  4613. {
  4614. gs_gui_command_t* cmd;
  4615. gs_gui_rect_t rect = gs_gui_rect(position.x - radius, position.y - radius, 2.f * radius, 2.f * radius);
  4616. // do clip command if the rect isn't fully contained within the cliprect
  4617. int32_t clipped = gs_gui_check_clip(ctx, rect);
  4618. if (clipped == GS_GUI_CLIP_ALL ) {return;}
  4619. if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, gs_gui_get_clip_rect(ctx));}
  4620. // do shape command
  4621. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_SHAPE, sizeof(gs_gui_shapecommand_t));
  4622. cmd->shape.type = GS_GUI_SHAPE_CIRCLE;
  4623. cmd->shape.circle.center = position;
  4624. cmd->shape.circle.radius = radius;
  4625. cmd->shape.color = color;
  4626. // reset clipping if it was set
  4627. if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
  4628. }
  4629. GS_API_DECL void
  4630. gs_gui_draw_triangle(gs_gui_context_t* ctx, gs_vec2 a, gs_vec2 b,
  4631. gs_vec2 c, gs_color_t color)
  4632. {
  4633. gs_gui_command_t* cmd;
  4634. // Check each point against rect (if partially clipped, then good
  4635. int32_t clipped = 0x00;
  4636. gs_gui_rect_t clip = gs_gui_get_clip_rect(ctx);
  4637. int32_t ca = gs_gui_rect_overlaps_vec2(clip, a);
  4638. int32_t cb = gs_gui_rect_overlaps_vec2(clip, b);
  4639. int32_t cc = gs_gui_rect_overlaps_vec2(clip, c);
  4640. if (ca && cb && cc) clipped = 0x00; // No clip
  4641. else if (!ca && !cb && !cc) clipped = GS_GUI_CLIP_ALL;
  4642. else if (ca || cb || cc) clipped = GS_GUI_CLIP_PART;
  4643. if (clipped == GS_GUI_CLIP_ALL) {return;}
  4644. if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, clip);}
  4645. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_SHAPE, sizeof(gs_gui_shapecommand_t));
  4646. cmd->shape.type = GS_GUI_SHAPE_TRIANGLE;
  4647. cmd->shape.triangle.points[0] = a;
  4648. cmd->shape.triangle.points[1] = b;
  4649. cmd->shape.triangle.points[2] = c;
  4650. cmd->shape.color = color;
  4651. // Reset clipping if set
  4652. if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
  4653. }
  4654. GS_API_DECL void
  4655. gs_gui_draw_box(gs_gui_context_t* ctx, gs_gui_rect_t rect, int16_t* w, gs_color_t color)
  4656. {
  4657. gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
  4658. // 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);
  4659. const float l = (float)w[0], r = (float)w[1], t = (float)w[2], b = (float)w[3];
  4660. gs_gui_draw_rect(ctx, gs_gui_rect(rect.x + l, rect.y, rect.w - r - l, t), color); // top
  4661. gs_gui_draw_rect(ctx, gs_gui_rect(rect.x + l, rect.y + rect.h - b, rect.w - r - l, b), color); // bottom
  4662. gs_gui_draw_rect(ctx, gs_gui_rect(rect.x, rect.y, l, rect.h), color); // left
  4663. gs_gui_draw_rect(ctx, gs_gui_rect(rect.x + rect.w - r, rect.y, r, rect.h), color); // right
  4664. }
  4665. GS_API_DECL void
  4666. gs_gui_draw_text(gs_gui_context_t* ctx, gs_asset_font_t* font, const char *str,
  4667. int32_t len, gs_vec2 pos, gs_color_t color, int32_t shadow_x,
  4668. int32_t shadow_y, gs_color_t shadow_color)
  4669. {
  4670. // Set to default font
  4671. if (!font)
  4672. {
  4673. font = gsi_default_font();
  4674. }
  4675. #define DRAW_TEXT(TEXT, RECT, COLOR)\
  4676. do\
  4677. {\
  4678. gs_gui_command_t* cmd;\
  4679. gs_vec2 td = gs_gui_text_dimensions(font, TEXT, -1);\
  4680. gs_gui_rect_t rect = (RECT);\
  4681. int32_t clipped = gs_gui_check_clip(ctx, rect);\
  4682. \
  4683. if (clipped == GS_GUI_CLIP_ALL)\
  4684. {\
  4685. return;\
  4686. }\
  4687. \
  4688. if (clipped == GS_GUI_CLIP_PART)\
  4689. {\
  4690. gs_gui_rect_t crect = gs_gui_get_clip_rect(ctx);\
  4691. gs_gui_set_clip(ctx, crect);\
  4692. }\
  4693. \
  4694. /* add command */\
  4695. if (len < 0)\
  4696. {\
  4697. len = strlen(TEXT);\
  4698. }\
  4699. \
  4700. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_TEXT, sizeof(gs_gui_textcommand_t) + len);\
  4701. memcpy(cmd->text.str, TEXT, len);\
  4702. cmd->text.str[len] = '\0';\
  4703. cmd->text.pos = gs_v2(rect.x, rect.y);\
  4704. cmd->text.color = COLOR;\
  4705. cmd->text.font = font;\
  4706. \
  4707. if (clipped)\
  4708. {\
  4709. gs_gui_set_clip(ctx, gs_gui_unclipped_rect);\
  4710. }\
  4711. } while (0)
  4712. // Draw shadow
  4713. if ((shadow_x || shadow_y) && shadow_color.a)
  4714. {
  4715. DRAW_TEXT(str, gs_gui_rect(pos.x + (float)shadow_x, pos.y + (float)shadow_y, td.x, td.y), shadow_color);
  4716. }
  4717. // Draw text
  4718. {
  4719. DRAW_TEXT(str, gs_gui_rect(pos.x, pos.y, td.x, td.y), color);
  4720. }
  4721. }
  4722. GS_API_DECL void
  4723. gs_gui_draw_image(gs_gui_context_t *ctx, gs_handle(gs_graphics_texture_t) hndl,
  4724. gs_gui_rect_t rect, gs_vec2 uv0, gs_vec2 uv1, gs_color_t color)
  4725. {
  4726. gs_gui_command_t* cmd;
  4727. /* do clip command if the rect isn't fully contained within the cliprect */
  4728. int32_t clipped = gs_gui_check_clip(ctx, rect);
  4729. if (clipped == GS_GUI_CLIP_ALL ) {return;}
  4730. if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, gs_gui_get_clip_rect(ctx));}
  4731. /* do image command */
  4732. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_IMAGE, sizeof(gs_gui_imagecommand_t));
  4733. cmd->image.hndl = hndl;
  4734. cmd->image.rect = rect;
  4735. cmd->image.uvs = gs_v4(uv0.x, uv0.y, uv1.x, uv1.y);
  4736. cmd->image.color = color;
  4737. /* reset clipping if it was set */
  4738. if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
  4739. }
  4740. GS_API_DECL void
  4741. gs_gui_draw_custom(gs_gui_context_t* ctx, gs_gui_rect_t rect,
  4742. gs_gui_draw_callback_t cb, void* data, size_t sz)
  4743. {
  4744. gs_gui_command_t* cmd;
  4745. gs_gui_rect_t viewport = rect;
  4746. rect = gs_gui_intersect_rects(rect, gs_gui_get_clip_rect(ctx));
  4747. /* do clip command if the rect isn't fully contained within the cliprect */
  4748. int32_t clipped = gs_gui_check_clip(ctx, rect);
  4749. if (clipped == GS_GUI_CLIP_ALL ) {return;}
  4750. if (clipped == GS_GUI_CLIP_PART) {gs_gui_set_clip(ctx, gs_gui_get_clip_rect(ctx));}
  4751. int32_t idx = ctx->id_stack.idx;
  4752. gs_gui_id res = (idx > 0) ? ctx->id_stack.items[idx - 1] : GS_GUI_HASH_INITIAL;
  4753. /* do custom command */
  4754. cmd = gs_gui_push_command(ctx, GS_GUI_COMMAND_CUSTOM, sizeof(gs_gui_customcommand_t));
  4755. cmd->custom.clip = rect;
  4756. cmd->custom.viewport = viewport;
  4757. cmd->custom.cb = cb;
  4758. cmd->custom.hover = ctx->hover;
  4759. cmd->custom.focus = ctx->focus;
  4760. cmd->custom.hash = res;
  4761. cmd->custom.data = ctx->command_list.items + ctx->command_list.idx;
  4762. cmd->custom.sz = sz;
  4763. cmd->base.size += sz;
  4764. // Copy data and move list forward
  4765. memcpy(ctx->command_list.items + ctx->command_list.idx, data, sz);
  4766. ctx->command_list.idx += sz;
  4767. /* reset clipping if it was set */
  4768. if (clipped) {gs_gui_set_clip(ctx, gs_gui_unclipped_rect);}
  4769. }
  4770. GS_API_DECL void gs_gui_draw_nine_rect(gs_gui_context_t* ctx,
  4771. gs_handle(gs_graphics_texture_t) hndl, gs_gui_rect_t rect,
  4772. gs_vec2 uv0, gs_vec2 uv1, uint32_t left, uint32_t right, uint32_t top,
  4773. uint32_t bottom, gs_color_t color)
  4774. {
  4775. // Draw images based on rect, slice image based on uvs (uv0, uv1), original texture dimensions (width, height) and control margins (left, right, top, bottom)
  4776. gs_graphics_texture_desc_t desc = gs_default_val();
  4777. gs_graphics_texture_desc_query(hndl, &desc);
  4778. uint32_t width = desc.width;
  4779. uint32_t height = desc.height;
  4780. // tl
  4781. {
  4782. uint32_t w = left;
  4783. uint32_t h = top;
  4784. gs_gui_rect_t r = gs_gui_rect(rect.x, rect.y, (f32)w, (f32)h);
  4785. gs_vec2 st0 = gs_v2(uv0.x, uv0.y);
  4786. gs_vec2 st1 = gs_v2(uv0.x + ((float)left / (float)width), uv0.y + ((float)top / (float)height));
  4787. gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
  4788. }
  4789. // tr
  4790. {
  4791. uint32_t w = right;
  4792. uint32_t h = top;
  4793. gs_gui_rect_t r = gs_gui_rect(rect.x + rect.w - w, rect.y, (f32)w, (f32)h);
  4794. gs_vec2 st0 = gs_v2(uv1.x - ((float)right / (float)width), uv0.y);
  4795. gs_vec2 st1 = gs_v2(uv1.x, uv0.y + ((float)top / (float)height));
  4796. gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
  4797. }
  4798. // br
  4799. {
  4800. uint32_t w = right;
  4801. uint32_t h = bottom;
  4802. gs_gui_rect_t r = gs_gui_rect(rect.x + rect.w - (f32)w, rect.y + rect.h - (f32)h, (f32)w, (f32)h);
  4803. gs_vec2 st0 = gs_v2(uv1.x - ((float)right / (float)width), uv1.y - ((float)bottom / (float)height));
  4804. gs_vec2 st1 = gs_v2(uv1.x, uv1.y);
  4805. gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
  4806. }
  4807. // bl
  4808. {
  4809. uint32_t w = left;
  4810. uint32_t h = bottom;
  4811. gs_gui_rect_t r = gs_gui_rect(rect.x, rect.y + rect.h - (f32)h, (f32)w, (f32)h);
  4812. gs_vec2 st0 = gs_v2(uv0.x, uv1.y - ((float)bottom / (float)height));
  4813. gs_vec2 st1 = gs_v2(uv0.x + ((float)left / (float)width), uv1.y);
  4814. gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
  4815. }
  4816. // top
  4817. {
  4818. uint32_t w = (u32)rect.w - left - right;
  4819. uint32_t h = top;
  4820. gs_gui_rect_t r = gs_gui_rect(rect.x + left, rect.y, (f32)w, (f32)h);
  4821. gs_vec2 st0 = gs_v2(uv0.x + ((float)left / (float)width), uv0.y);
  4822. gs_vec2 st1 = gs_v2(uv1.x - ((float)right / (float)width), uv0.y + ((float)top / (float)height));
  4823. gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
  4824. }
  4825. // bottom
  4826. {
  4827. uint32_t w = (u32)rect.w - left - right;
  4828. uint32_t h = bottom;
  4829. gs_gui_rect_t r = gs_gui_rect(rect.x + left, rect.y + rect.h - (f32)h, (f32)w, (f32)h);
  4830. gs_vec2 st0 = gs_v2(uv0.x + ((float)left / (float)width), uv1.y - ((float)bottom / (float)height));
  4831. gs_vec2 st1 = gs_v2(uv1.x - ((float)right / (float)width), uv1.y);
  4832. gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
  4833. }
  4834. // left
  4835. {
  4836. uint32_t w = left;
  4837. uint32_t h = (u32)rect.h - top - bottom;
  4838. gs_gui_rect_t r = gs_gui_rect(rect.x, rect.y + top, (f32)w, (f32)h);
  4839. gs_vec2 st0 = gs_v2(uv0.x, uv0.y + ((float)top / (float)height));
  4840. gs_vec2 st1 = gs_v2(uv0.x + ((float)left / (float)width), uv1.y - ((float)bottom / (float)height));
  4841. gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
  4842. }
  4843. // right
  4844. {
  4845. uint32_t w = right;
  4846. uint32_t h = (u32)rect.h - top - bottom;
  4847. gs_gui_rect_t r = gs_gui_rect(rect.x + rect.w - (f32)w, rect.y + top, (f32)w, (f32)h);
  4848. gs_vec2 st0 = gs_v2(uv1.x - ((float)right / (float)width), uv0.y + ((float)top / (float)height));
  4849. gs_vec2 st1 = gs_v2(uv1.x, uv1.y - ((float)bottom / (float)height));
  4850. gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
  4851. }
  4852. // center
  4853. {
  4854. uint32_t w = (u32)rect.w - right - left;
  4855. uint32_t h = (u32)rect.h - top - bottom;
  4856. gs_gui_rect_t r = gs_gui_rect(rect.x + left, rect.y + top, (f32)w, (f32)h);
  4857. gs_vec2 st0 = gs_v2(uv0.x + ((float)left / (float)width), uv0.y + ((float)top / (float)height));
  4858. gs_vec2 st1 = gs_v2(uv1.x - ((float)right / (float)width), uv1.y - ((float)bottom / (float)height));
  4859. gs_gui_draw_image(ctx, hndl, r, st0, st1, color);
  4860. }
  4861. }
  4862. /*============================================================================
  4863. ** layout
  4864. **============================================================================*/
  4865. enum {
  4866. GS_GUI_RELATIVE = 1,
  4867. GS_GUI_ABSOLUTE = 2
  4868. };
  4869. GS_API_DECL gs_gui_rect_t
  4870. gs_gui_layout_anchor(const gs_gui_rect_t* p, int32_t width, int32_t height,
  4871. int32_t xoff, int32_t yoff, gs_gui_layout_anchor_type type)
  4872. {
  4873. float w = (float)width;
  4874. float h = (float)height;
  4875. gs_gui_rect_t r = gs_gui_rect(p->x, p->y, w, h);
  4876. switch (type)
  4877. {
  4878. default:
  4879. case GS_GUI_LAYOUT_ANCHOR_TOPLEFT:
  4880. {
  4881. } break;
  4882. case GS_GUI_LAYOUT_ANCHOR_TOPCENTER:
  4883. {
  4884. r.x = p->x + (p->w - w) * 0.5f;
  4885. } break;
  4886. case GS_GUI_LAYOUT_ANCHOR_TOPRIGHT:
  4887. {
  4888. r.x = p->x + (p->w - w);
  4889. } break;
  4890. case GS_GUI_LAYOUT_ANCHOR_LEFT:
  4891. {
  4892. r.y = p->y + (p->h - h) * 0.5f;
  4893. } break;
  4894. case GS_GUI_LAYOUT_ANCHOR_CENTER:
  4895. {
  4896. r.x = p->x + (p->w - w) * 0.5f;
  4897. r.y = p->y + (p->h - h) * 0.5f;
  4898. } break;
  4899. case GS_GUI_LAYOUT_ANCHOR_RIGHT:
  4900. {
  4901. r.x = p->x + (p->w - w);
  4902. r.y = p->y + (p->h - h) * 0.5f;
  4903. } break;
  4904. case GS_GUI_LAYOUT_ANCHOR_BOTTOMLEFT:
  4905. {
  4906. r.y = p->y + (p->h - h);
  4907. } break;
  4908. case GS_GUI_LAYOUT_ANCHOR_BOTTOMCENTER:
  4909. {
  4910. r.x = p->x + (p->w - w) * 0.5f;
  4911. r.y = p->y + (p->h - h);
  4912. } break;
  4913. case GS_GUI_LAYOUT_ANCHOR_BOTTOMRIGHT:
  4914. {
  4915. r.x = p->x + (p->w - w);
  4916. r.y = p->y + (p->h - h);
  4917. } break;
  4918. }
  4919. // Apply offset
  4920. r.x += xoff;
  4921. r.y += yoff;
  4922. return r;
  4923. }
  4924. GS_API_DECL void gs_gui_layout_column_begin(gs_gui_context_t* ctx)
  4925. {
  4926. gs_gui_push_layout(ctx, gs_gui_layout_next(ctx), gs_v2(0, 0));
  4927. }
  4928. GS_API_DECL void gs_gui_layout_column_end(gs_gui_context_t* ctx)
  4929. {
  4930. gs_gui_layout_t *a, *b;
  4931. b = gs_gui_get_layout(ctx);
  4932. gs_gui_stack_pop(ctx->layout_stack);
  4933. /* inherit position/next_row/max from child layout if they are greater */
  4934. a = gs_gui_get_layout(ctx);
  4935. a->position.x = gs_max(a->position.x, b->position.x + b->body.x - a->body.x);
  4936. a->next_row = (int32_t)gs_max((f32)a->next_row, (f32)b->next_row + (f32)b->body.y - (f32)a->body.y);
  4937. a->max.x = gs_max(a->max.x, b->max.x);
  4938. a->max.y = gs_max(a->max.y, b->max.y);
  4939. }
  4940. GS_API_DECL void gs_gui_layout_row(gs_gui_context_t *ctx, int32_t items, const int32_t *widths, int32_t height)
  4941. {
  4942. gs_gui_style_t* style = ctx->style;
  4943. gs_gui_layout_t* layout = gs_gui_get_layout(ctx);
  4944. if (widths)
  4945. {
  4946. gs_gui_expect(items <= GS_GUI_MAX_WIDTHS);
  4947. memcpy(layout->widths, widths, items * sizeof(widths[0]));
  4948. }
  4949. layout->items = items;
  4950. layout->position = gs_v2((f32)layout->indent, (f32)layout->next_row);
  4951. layout->size.y = (f32)height;
  4952. layout->item_index = 0;
  4953. }
  4954. 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)
  4955. {
  4956. gs_gui_layout_row(ctx, items, widths, height);
  4957. gs_gui_layout_t* layout = gs_gui_get_layout(ctx);
  4958. switch (justification)
  4959. {
  4960. default: break;
  4961. case GS_GUI_JUSTIFY_CENTER:
  4962. {
  4963. // Iterate through all widths, calculate total
  4964. // X is center - tw/2
  4965. float w = 0.f;
  4966. for (uint32_t i = 0; i < items; ++i)
  4967. {
  4968. w += widths[i] > 0.f ? widths[i] : widths[i] == 0.f? layout->size.x : layout->body.w - widths[i];
  4969. }
  4970. layout->position.x = (layout->body.w - w) * 0.5f + layout->indent;
  4971. } break;
  4972. case GS_GUI_JUSTIFY_END:
  4973. {
  4974. float w = 0.f;
  4975. for (uint32_t i = 0; i < items; ++i) {
  4976. w += widths[i] > 0.f ? widths[i] : widths[i] == 0.f? layout->size.x : layout->body.w - widths[i];
  4977. }
  4978. layout->position.x = (layout->body.w - w);
  4979. } break;
  4980. }
  4981. }
  4982. GS_API_DECL void
  4983. gs_gui_layout_width(gs_gui_context_t *ctx, int32_t width)
  4984. {
  4985. gs_gui_get_layout(ctx)->size.x = (f32)width;
  4986. }
  4987. GS_API_DECL void
  4988. gs_gui_layout_height(gs_gui_context_t *ctx, int32_t height)
  4989. {
  4990. gs_gui_get_layout(ctx)->size.y = (f32)height;
  4991. }
  4992. GS_API_DECL void
  4993. gs_gui_layout_set_next(gs_gui_context_t *ctx, gs_gui_rect_t r, int32_t relative)
  4994. {
  4995. gs_gui_layout_t *layout = gs_gui_get_layout(ctx);
  4996. layout->next = r;
  4997. layout->next_type = relative ? GS_GUI_RELATIVE : GS_GUI_ABSOLUTE;
  4998. }
  4999. GS_API_DECL gs_gui_rect_t
  5000. gs_gui_layout_peek_next(gs_gui_context_t *ctx)
  5001. {
  5002. gs_gui_layout_t layout = *gs_gui_get_layout(ctx);
  5003. gs_gui_style_t* style = ctx->style;
  5004. gs_gui_rect_t res;
  5005. if (layout.next_type)
  5006. {
  5007. /* handle rect set by `gs_gui_layout_set_next` */
  5008. int32_t type = layout.next_type;
  5009. res = layout.next;
  5010. if (type == GS_GUI_ABSOLUTE)
  5011. {
  5012. return res;
  5013. }
  5014. }
  5015. else
  5016. {
  5017. // handle next row
  5018. if (layout.item_index == layout.items)
  5019. {
  5020. gs_gui_layout_row(ctx, layout.items, NULL, (s32)layout.size.y);
  5021. }
  5022. const int32_t items = layout.items;
  5023. const int32_t idx = layout.item_index;
  5024. int32_t ml = style->margin[GS_GUI_MARGIN_LEFT];
  5025. int32_t mr = style->margin[GS_GUI_MARGIN_RIGHT];
  5026. int32_t mt = style->margin[GS_GUI_MARGIN_TOP];
  5027. int32_t mb = style->margin[GS_GUI_MARGIN_BOTTOM];
  5028. // position
  5029. res.x = layout.position.x + ml;
  5030. res.y = layout.position.y + mt;
  5031. // size
  5032. res.w = layout.items > 0 ? layout.widths[layout.item_index] : layout.size.x;
  5033. res.h = layout.size.y;
  5034. // default fallbacks
  5035. if (res.w == 0) { res.w = style->size[0]; }
  5036. if (res.h == 0) { res.h = style->size[1]; }
  5037. if (res.w < 0) { res.w += layout.body.w - res.x + 1; }
  5038. if (res.h < 0) { res.h += layout.body.h - res.y + 1; }
  5039. layout.item_index++;
  5040. }
  5041. /* update position */
  5042. layout.position.x += res.w + style->margin[GS_GUI_MARGIN_RIGHT];
  5043. layout.next_row = (s32)gs_max(layout.next_row, res.y + res.h + style->margin[GS_GUI_MARGIN_BOTTOM]);
  5044. /* apply body offset */
  5045. res.x += layout.body.x;
  5046. res.y += layout.body.y;
  5047. /* update max position */
  5048. layout.max.x = gs_max(layout.max.x, res.x + res.w);
  5049. layout.max.y = gs_max(layout.max.y, res.y + res.h);
  5050. return res;
  5051. }
  5052. GS_API_DECL gs_gui_rect_t
  5053. gs_gui_layout_next(gs_gui_context_t *ctx)
  5054. {
  5055. gs_gui_layout_t* layout = gs_gui_get_layout(ctx);
  5056. gs_gui_style_t* style = ctx->style;
  5057. gs_gui_rect_t res;
  5058. if (layout->next_type)
  5059. {
  5060. /* handle rect set by `gs_gui_layout_set_next` */
  5061. int32_t type = layout->next_type;
  5062. layout->next_type = 0;
  5063. res = layout->next;
  5064. if (type == GS_GUI_ABSOLUTE)
  5065. {
  5066. return (ctx->last_rect = res);
  5067. }
  5068. }
  5069. else
  5070. {
  5071. // handle next row
  5072. if (layout->item_index == layout->items)
  5073. {
  5074. gs_gui_layout_row(ctx, layout->items, NULL, (s32)layout->size.y);
  5075. }
  5076. const int32_t items = layout->items;
  5077. const int32_t idx = layout->item_index;
  5078. int32_t ml = style->margin[GS_GUI_MARGIN_LEFT];
  5079. int32_t mr = style->margin[GS_GUI_MARGIN_RIGHT];
  5080. int32_t mt = style->margin[GS_GUI_MARGIN_TOP];
  5081. int32_t mb = style->margin[GS_GUI_MARGIN_BOTTOM];
  5082. // position
  5083. res.x = layout->position.x + ml;
  5084. res.y = layout->position.y + mt;
  5085. // size
  5086. res.w = layout->items > 0 ? layout->widths[layout->item_index] : layout->size.x;
  5087. res.h = layout->size.y;
  5088. // default fallbacks
  5089. if (res.w == 0) { res.w = style->size[0]; }
  5090. if (res.h == 0) { res.h = style->size[1]; }
  5091. // Not sure about this... should probably iterate through the rest, figure out what's left, then
  5092. // determine how much to divide up
  5093. if (res.w < 0) { res.w += layout->body.w - res.x + 1; }
  5094. if (res.h < 0) { res.h += layout->body.h - res.y + 1; }
  5095. layout->item_index++;
  5096. }
  5097. /* update position */
  5098. layout->position.x += res.w + style->margin[GS_GUI_MARGIN_RIGHT];
  5099. 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);
  5100. /* apply body offset */
  5101. res.x += layout->body.x;
  5102. res.y += layout->body.y;
  5103. /* update max position */
  5104. layout->max.x = gs_max(layout->max.x, res.x + res.w);
  5105. layout->max.y = gs_max(layout->max.y, res.y + res.h);
  5106. return (ctx->last_rect = res);
  5107. }
  5108. /*============================================================================
  5109. ** controls
  5110. **============================================================================*/
  5111. static int32_t gs_gui_in_hover_root(gs_gui_context_t *ctx)
  5112. {
  5113. int32_t i = ctx->container_stack.idx;
  5114. while (i--)
  5115. {
  5116. if (ctx->container_stack.items[i] == ctx->hover_root) { return 1; }
  5117. /* only root containers have their `head` field set; stop searching if we've
  5118. ** reached the current root container */
  5119. if (ctx->container_stack.items[i]->head) { break; }
  5120. }
  5121. return 0;
  5122. }
  5123. GS_API_DECL void gs_gui_draw_control_frame(gs_gui_context_t *ctx, gs_gui_id id, gs_gui_rect_t rect,
  5124. int32_t elementid, uint64_t opt)
  5125. {
  5126. if (opt & GS_GUI_OPT_NOFRAME) {return;}
  5127. int32_t state = ctx->focus == id ? GS_GUI_ELEMENT_STATE_FOCUS :
  5128. ctx->hover == id ? GS_GUI_ELEMENT_STATE_HOVER :
  5129. 0x00;
  5130. gs_gui_draw_frame(ctx, rect, &ctx->style_sheet->styles[elementid][state]);
  5131. }
  5132. GS_API_DECL void gs_gui_draw_control_text(gs_gui_context_t *ctx, const char *str, gs_gui_rect_t rect,
  5133. const gs_gui_style_t* style, uint64_t opt)
  5134. {
  5135. gs_vec2 pos = gs_v2(rect.x, rect.y);
  5136. gs_asset_font_t* font = style->font;
  5137. gs_vec2 td = gs_gui_text_dimensions(font, str, -1);
  5138. int32_t tw = (int32_t)td.x;
  5139. int32_t th = (int32_t)td.y;
  5140. gs_gui_push_clip_rect(ctx, rect);
  5141. // Grab stylings
  5142. const int32_t padding_left = style->padding[GS_GUI_PADDING_LEFT];
  5143. const int32_t padding_top = style->padding[GS_GUI_PADDING_TOP];
  5144. const int32_t padding_right = style->padding[GS_GUI_PADDING_RIGHT];
  5145. const int32_t padding_bottom = style->padding[GS_GUI_PADDING_BOTTOM];
  5146. const int32_t align = style->align_content;
  5147. const int32_t justify = style->justify_content;
  5148. // Determine x placement based on justification
  5149. switch (justify)
  5150. {
  5151. default:
  5152. case GS_GUI_JUSTIFY_START:
  5153. {
  5154. pos.x = rect.x + padding_left;
  5155. } break;
  5156. case GS_GUI_JUSTIFY_CENTER:
  5157. {
  5158. pos.x = rect.x + (rect.w - tw) * 0.5f;
  5159. } break;
  5160. case GS_GUI_JUSTIFY_END:
  5161. {
  5162. pos.x = rect.x + (rect.w - tw) - padding_right;
  5163. } break;
  5164. }
  5165. // Determine y placement based on alignment within rect
  5166. switch (align)
  5167. {
  5168. default:
  5169. case GS_GUI_ALIGN_START:
  5170. {
  5171. pos.y = rect.y + padding_top;
  5172. } break;
  5173. case GS_GUI_ALIGN_CENTER:
  5174. {
  5175. pos.y = rect.y + (rect.h - th) * 0.5f;
  5176. } break;
  5177. case GS_GUI_ALIGN_END:
  5178. {
  5179. pos.y = rect.y + (rect.h - th) - padding_bottom;
  5180. } break;
  5181. }
  5182. bool is_content = (opt & GS_GUI_OPT_ISCONTENT);
  5183. int32_t bg_color = is_content ? GS_GUI_COLOR_CONTENT_BACKGROUND :
  5184. GS_GUI_COLOR_BACKGROUND;
  5185. int32_t sh_color = is_content ? GS_GUI_COLOR_CONTENT_SHADOW :
  5186. GS_GUI_COLOR_SHADOW;
  5187. int32_t bd_color = is_content ? GS_GUI_COLOR_CONTENT_BORDER :
  5188. GS_GUI_COLOR_BORDER;
  5189. int32_t sx = style->shadow_x;
  5190. int32_t sy = style->shadow_y;
  5191. const gs_color_t* sc = &style->colors[sh_color];
  5192. // Border
  5193. const gs_color_t* bc = &style->colors[bd_color];
  5194. if (bc->a && ~opt & GS_GUI_OPT_NOSTYLEBORDER)
  5195. {
  5196. gs_gui_pop_clip_rect(ctx);
  5197. gs_gui_rect_t border_rect = gs_gui_expand_rect(rect, (int16_t*)style->border_width);
  5198. gs_gui_push_clip_rect(ctx, border_rect);
  5199. gs_gui_draw_box(ctx, border_rect, (int16_t*)style->border_width, *bc);
  5200. }
  5201. // Background
  5202. if (~opt & GS_GUI_OPT_NOSTYLEBACKGROUND)
  5203. {
  5204. gs_gui_draw_rect(ctx, rect, style->colors[bg_color]);
  5205. }
  5206. // Text
  5207. gs_gui_draw_text(ctx, font, str, -1, pos, style->colors[GS_GUI_COLOR_CONTENT], sx, sy, *sc);
  5208. gs_gui_pop_clip_rect(ctx);
  5209. }
  5210. GS_API_DECL int32_t gs_gui_mouse_over(gs_gui_context_t *ctx, gs_gui_rect_t rect)
  5211. {
  5212. return gs_gui_rect_overlaps_vec2(rect, ctx->mouse_pos) && !ctx->hover_split && !ctx->lock_hover_id &&
  5213. gs_gui_rect_overlaps_vec2(gs_gui_get_clip_rect(ctx), ctx->mouse_pos) &&
  5214. gs_gui_in_hover_root(ctx);
  5215. }
  5216. 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)
  5217. {
  5218. int32_t mouseover = 0;
  5219. gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
  5220. gs_gui_id prev_hov = ctx->prev_hover;
  5221. gs_gui_id prev_foc = ctx->prev_focus;
  5222. // I should do updates in here
  5223. if (opt & GS_GUI_OPT_FORCEFOCUS)
  5224. {
  5225. mouseover = gs_gui_rect_overlaps_vec2(gs_gui_get_clip_rect(ctx), ctx->mouse_pos);
  5226. }
  5227. else
  5228. {
  5229. mouseover = gs_gui_mouse_over(ctx, rect);
  5230. }
  5231. // Check for 'mouse-over' with id selection here
  5232. if (ctx->focus == id) { ctx->updated_focus = 1; }
  5233. if (opt & GS_GUI_OPT_NOINTERACT) { return; }
  5234. // Check for hold focus here
  5235. if (mouseover && !ctx->mouse_down) {
  5236. gs_gui_set_hover(ctx, id);
  5237. }
  5238. if (ctx->focus == id)
  5239. {
  5240. gs_gui_set_focus(ctx, id);
  5241. if (ctx->mouse_pressed && !mouseover) { gs_gui_set_focus(ctx, 0); }
  5242. if (!ctx->mouse_down && ~opt & GS_GUI_OPT_HOLDFOCUS) { gs_gui_set_focus(ctx, 0); }
  5243. }
  5244. if (ctx->prev_hover == id && !mouseover) {ctx->prev_hover = ctx->hover;}
  5245. if (ctx->hover == id)
  5246. {
  5247. if (ctx->mouse_pressed)
  5248. {
  5249. if ((opt & GS_GUI_OPT_LEFTCLICKONLY && ctx->mouse_pressed == GS_GUI_MOUSE_LEFT) || (~opt & GS_GUI_OPT_LEFTCLICKONLY))
  5250. {
  5251. gs_gui_set_focus(ctx, id);
  5252. }
  5253. }
  5254. else if (!mouseover)
  5255. {
  5256. gs_gui_set_hover(ctx, 0);
  5257. }
  5258. }
  5259. // Do state check
  5260. if (~opt & GS_GUI_OPT_NOSWITCHSTATE)
  5261. {
  5262. if (ctx->focus == id)
  5263. {
  5264. if (ctx->prev_focus != id) ctx->last_focus_state = GS_GUI_ELEMENT_STATE_ON_FOCUS;
  5265. else ctx->last_focus_state = GS_GUI_ELEMENT_STATE_FOCUS;
  5266. }
  5267. else
  5268. {
  5269. if (ctx->prev_focus == id) ctx->last_focus_state = GS_GUI_ELEMENT_STATE_OFF_FOCUS;
  5270. else ctx->last_focus_state = GS_GUI_ELEMENT_STATE_DEFAULT;
  5271. }
  5272. if (ctx->hover == id)
  5273. {
  5274. if (ctx->prev_hover != id) ctx->last_hover_state = GS_GUI_ELEMENT_STATE_ON_HOVER;
  5275. else ctx->last_hover_state = GS_GUI_ELEMENT_STATE_HOVER;
  5276. }
  5277. else
  5278. {
  5279. if (ctx->prev_hover == id) ctx->last_hover_state = GS_GUI_ELEMENT_STATE_OFF_HOVER;
  5280. else ctx->last_hover_state = GS_GUI_ELEMENT_STATE_DEFAULT;
  5281. }
  5282. if (ctx->prev_focus == id && !ctx->mouse_down && ~opt & GS_GUI_OPT_HOLDFOCUS) {
  5283. ctx->prev_focus = ctx->focus;
  5284. }
  5285. if (
  5286. ctx->last_hover_state == GS_GUI_ELEMENT_STATE_ON_HOVER ||
  5287. ctx->last_hover_state == GS_GUI_ELEMENT_STATE_OFF_HOVER ||
  5288. ctx->last_focus_state == GS_GUI_ELEMENT_STATE_ON_FOCUS ||
  5289. ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS
  5290. )
  5291. {
  5292. // Don't have a hover state switch if focused
  5293. ctx->switch_state = ctx->last_focus_state ? ctx->last_focus_state : ctx->focus != id ? ctx->last_hover_state : GS_GUI_ELEMENT_STATE_DEFAULT;
  5294. switch (ctx->switch_state)
  5295. {
  5296. case GS_GUI_ELEMENT_STATE_OFF_HOVER:
  5297. case GS_GUI_ELEMENT_STATE_ON_HOVER:
  5298. {
  5299. if (ctx->focus == id || ctx->prev_focus == id)
  5300. {
  5301. ctx->switch_state = 0x00;
  5302. }
  5303. } break;
  5304. }
  5305. if (ctx->switch_state && ctx->prev_focus != id) ctx->state_switch_id = id;
  5306. }
  5307. }
  5308. else
  5309. {
  5310. ctx->prev_focus = prev_foc;
  5311. ctx->prev_hover = prev_hov;
  5312. }
  5313. }
  5314. GS_API_DECL int32_t
  5315. 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)
  5316. {
  5317. int32_t res = 0, elementid = GS_GUI_ELEMENT_TEXT;
  5318. gs_gui_id id = gs_gui_get_id(ctx, text, strlen(text));
  5319. gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
  5320. gs_gui_style_t style = gs_default_val();
  5321. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
  5322. const char *start, *end, *p = text;
  5323. int32_t width = -1;
  5324. // Update anim (keep states locally within animation, only way to do this)
  5325. if (anim)
  5326. {
  5327. gs_gui_animation_update(ctx, anim);
  5328. // Get blended style based on animation
  5329. style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
  5330. }
  5331. else
  5332. {
  5333. style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
  5334. ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
  5335. gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
  5336. }
  5337. gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
  5338. gs_asset_font_t* font = ctx->style->font;
  5339. gs_color_t* color = &ctx->style->colors[GS_GUI_COLOR_CONTENT];
  5340. int32_t sx = ctx->style->shadow_x;
  5341. int32_t sy = ctx->style->shadow_y;
  5342. if (opt & GS_GUI_OPT_NOSTYLESHADOW){sx = 0; sy = 0;}
  5343. gs_color_t* sc = &ctx->style->colors[GS_GUI_COLOR_SHADOW];
  5344. int32_t th = gs_gui_font_height(font);
  5345. gs_gui_layout_column_begin(ctx);
  5346. gs_gui_layout_row(ctx, 1, &width, th);
  5347. gs_gui_rect_t tr = gs_gui_layout_next(ctx);
  5348. gs_gui_layout_set_next(ctx, tr, 0);
  5349. gs_gui_rect_t r = gs_gui_layout_next(ctx);
  5350. gs_gui_rect_t bg = r;
  5351. do
  5352. {
  5353. int32_t w = 0;
  5354. start = end = p;
  5355. do
  5356. {
  5357. const char* word = p;
  5358. while (*p && *p != ' ' && *p != '\n')
  5359. {
  5360. p++;
  5361. }
  5362. if (wrap) w += gs_gui_text_width(font, word, p - word);
  5363. if (w > r.w && end != start)
  5364. {
  5365. break;
  5366. }
  5367. if (wrap) w += gs_gui_text_width(font, p, 1);
  5368. end = p++;
  5369. } while (*end && *end != '\n');
  5370. if (r.w > tr.w) tr.w = r.w;
  5371. tr.h = (r.y + r.h) - tr.y;
  5372. gs_gui_rect_t txtrct = r;
  5373. bg = r;
  5374. if (*end)
  5375. {
  5376. r = gs_gui_layout_next(ctx);
  5377. bg.h = r.y - bg.y;
  5378. }
  5379. else
  5380. {
  5381. int32_t th = gs_gui_text_height(font, start, end - start);
  5382. bg.h = r.h + (float)th / 2.f;
  5383. }
  5384. // Draw frame here for background if applicable (need to do this to account for space between wrap)
  5385. if (ctx->style->colors[GS_GUI_COLOR_BACKGROUND].a && ~opt & GS_GUI_OPT_NOSTYLEBACKGROUND)
  5386. {
  5387. gs_gui_draw_rect(ctx, bg, style.colors[GS_GUI_COLOR_BACKGROUND]);
  5388. }
  5389. // Draw text
  5390. gs_gui_draw_text(ctx, font, start, end - start, gs_v2(txtrct.x, txtrct.y), *color, sx, sy, *sc);
  5391. p = end + 1;
  5392. } while (*end);
  5393. // draw border
  5394. if (style.colors[GS_GUI_COLOR_BORDER].a && ~opt & GS_GUI_OPT_NOSTYLEBORDER)
  5395. {
  5396. 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]);
  5397. }
  5398. gs_gui_update_control(ctx, id, tr, 0x00);
  5399. // handle click
  5400. if (ctx->mouse_down != GS_GUI_MOUSE_LEFT && ctx->hover == id && ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS)
  5401. {
  5402. res |= GS_GUI_RES_SUBMIT;
  5403. }
  5404. gs_gui_layout_column_end(ctx);
  5405. gs_gui_pop_style(ctx, save);
  5406. return res;
  5407. }
  5408. GS_API_DECL int32_t
  5409. gs_gui_label_ex(gs_gui_context_t* ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt)
  5410. {
  5411. // Want to push animations here for styles
  5412. int32_t res = 0;
  5413. int32_t elementid = GS_GUI_ELEMENT_LABEL;
  5414. gs_gui_id id = gs_gui_get_id(ctx, label, gs_strlen(label));
  5415. char id_tag[256] = gs_default_val();
  5416. char label_tag[256] = gs_default_val();
  5417. gs_gui_parse_id_tag(ctx, label, id_tag, sizeof(id_tag), opt);
  5418. gs_gui_parse_label_tag(ctx, label, label_tag, sizeof(label_tag));
  5419. gs_gui_push_id(ctx, id_tag, strlen(id_tag));
  5420. gs_gui_style_t style = gs_default_val();
  5421. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
  5422. if (anim)
  5423. {
  5424. gs_gui_animation_update(ctx, anim);
  5425. // Get blended style based on animation
  5426. style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
  5427. }
  5428. else
  5429. {
  5430. style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
  5431. ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
  5432. gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
  5433. }
  5434. gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
  5435. gs_gui_rect_t r = gs_gui_layout_next(ctx);
  5436. gs_gui_update_control(ctx, id, r, 0x00);
  5437. gs_gui_draw_control_text(ctx, label_tag, r, &style, 0x00);
  5438. gs_gui_pop_style(ctx, save);
  5439. gs_gui_pop_id(ctx);
  5440. /* handle click */
  5441. if (
  5442. ctx->mouse_down != GS_GUI_MOUSE_LEFT &&
  5443. ctx->hover == id &&
  5444. ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS
  5445. )
  5446. {
  5447. res |= GS_GUI_RES_SUBMIT;
  5448. }
  5449. return res;
  5450. }
  5451. GS_API_DECL int32_t
  5452. 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)
  5453. {
  5454. int32_t res = 0;
  5455. gs_gui_id id = gs_gui_get_id(ctx, &hndl, sizeof(hndl));
  5456. const int32_t elementid = GS_GUI_ELEMENT_IMAGE;
  5457. gs_gui_style_t style = gs_default_val();
  5458. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
  5459. // Update anim (keep states locally within animation, only way to do this)
  5460. if (anim)
  5461. {
  5462. gs_gui_animation_update(ctx, anim);
  5463. // Get blended style based on animation
  5464. style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
  5465. }
  5466. else
  5467. {
  5468. style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
  5469. ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
  5470. gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
  5471. }
  5472. // Temporary copy of style
  5473. gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
  5474. gs_gui_rect_t r = gs_gui_layout_next(ctx);
  5475. gs_gui_update_control(ctx, id, r, opt);
  5476. /* handle click */
  5477. if (ctx->mouse_down != GS_GUI_MOUSE_LEFT && ctx->hover == id && ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS)
  5478. {
  5479. res |= GS_GUI_RES_SUBMIT;
  5480. }
  5481. // draw border
  5482. if (style.colors[GS_GUI_COLOR_BORDER].a)
  5483. {
  5484. gs_gui_draw_box(ctx, gs_gui_expand_rect(r, (int16_t*)style.border_width),
  5485. (int16_t*)style.border_width, style.colors[GS_GUI_COLOR_BORDER]);
  5486. }
  5487. gs_gui_draw_image(ctx, hndl, r, uv0, uv1, style.colors[GS_GUI_COLOR_CONTENT]);
  5488. gs_gui_pop_style(ctx, save);
  5489. return res;
  5490. }
  5491. GS_API_DECL int32_t
  5492. gs_gui_combo_begin_ex(gs_gui_context_t* ctx, const char* id, const char* current_item,
  5493. int32_t max_items, gs_gui_selector_desc_t* desc, uint64_t opt)
  5494. {
  5495. int32_t res = 0;
  5496. opt = GS_GUI_OPT_NOMOVE |
  5497. GS_GUI_OPT_NORESIZE |
  5498. GS_GUI_OPT_NOTITLE |
  5499. GS_GUI_OPT_FORCESETRECT;
  5500. if (gs_gui_button(ctx, current_item)) {
  5501. gs_gui_popup_open(ctx, id);
  5502. }
  5503. int32_t ct = max_items > 0 ? max_items : 0;
  5504. gs_gui_rect_t rect = ctx->last_rect;
  5505. rect.y += rect.h;
  5506. rect.h = ct ? (ct + 1) * ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x00].size[1] : rect.h;
  5507. res = gs_gui_popup_begin_ex(ctx, id, rect, NULL, opt);
  5508. return res;
  5509. }
  5510. GS_API_DECL int32_t
  5511. gs_gui_combo_item_ex(gs_gui_context_t* ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt)
  5512. {
  5513. int32_t res = gs_gui_button_ex(ctx, label, desc, opt);
  5514. if (res) {
  5515. gs_gui_current_container_close(ctx);
  5516. }
  5517. return res;
  5518. }
  5519. GS_API_DECL void
  5520. gs_gui_combo_end(gs_gui_context_t* ctx)
  5521. {
  5522. gs_gui_popup_end(ctx);
  5523. }
  5524. GS_API_DECL void
  5525. gs_gui_parse_label_tag(gs_gui_context_t* ctx, const char* str, char* buffer, size_t sz)
  5526. {
  5527. gs_lexer_t lex = gs_lexer_c_ctor(str);
  5528. while (gs_lexer_can_lex(&lex))
  5529. {
  5530. gs_token_t token = gs_lexer_next_token(&lex);
  5531. switch (token.type)
  5532. {
  5533. case GS_TOKEN_HASH:
  5534. {
  5535. if (gs_lexer_peek(&lex).type == GS_TOKEN_HASH)
  5536. {
  5537. gs_token_t end = gs_lexer_current_token(&lex);
  5538. // Determine len
  5539. size_t len = gs_min(end.text - str, sz);
  5540. memcpy(buffer, str, len);
  5541. return;
  5542. }
  5543. } break;
  5544. }
  5545. }
  5546. // Reached end, so just memcpy
  5547. memcpy(buffer, str, gs_min(sz, strlen(str) + 1));
  5548. }
  5549. 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)
  5550. {
  5551. if (opt & GS_GUI_OPT_PARSEIDTAGONLY) {
  5552. gs_lexer_t lex = gs_lexer_c_ctor(str);
  5553. while (gs_lexer_can_lex(&lex))
  5554. {
  5555. gs_token_t token = gs_lexer_next_token(&lex);
  5556. switch (token.type)
  5557. {
  5558. case GS_TOKEN_HASH:
  5559. {
  5560. if (gs_lexer_peek(&lex).type == GS_TOKEN_HASH)
  5561. {
  5562. gs_token_t end = gs_lexer_next_token(&lex);
  5563. end = gs_lexer_next_token(&lex);
  5564. // Determine len
  5565. size_t len = gs_min((str + strlen(str)) - end.text, sz);
  5566. memcpy(buffer, end.text, len);
  5567. return;
  5568. }
  5569. } break;
  5570. }
  5571. }
  5572. } else {
  5573. size_t str_sz = strlen(str);
  5574. size_t actual_sz = gs_min(str_sz, sz-1);
  5575. memcpy(buffer, str, actual_sz);
  5576. buffer[actual_sz] = 0;
  5577. }
  5578. }
  5579. GS_API_DECL int32_t
  5580. gs_gui_button_ex(gs_gui_context_t* ctx, const char* label, const gs_gui_selector_desc_t* desc, uint64_t opt)
  5581. {
  5582. // Note(john): clip out early here for performance
  5583. int32_t res = 0;
  5584. gs_gui_id id = gs_gui_get_id(ctx, label, strlen(label));
  5585. gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
  5586. char id_tag[256] = gs_default_val();
  5587. char label_tag[256] = gs_default_val();
  5588. gs_gui_parse_id_tag(ctx, label, id_tag, sizeof(id_tag), opt);
  5589. gs_gui_parse_label_tag(ctx, label, label_tag, sizeof(label_tag));
  5590. gs_gui_style_t style = gs_default_val();
  5591. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, GS_GUI_ELEMENT_BUTTON);
  5592. // Push id if tag available
  5593. gs_gui_push_id(ctx, id_tag, strlen(id_tag));
  5594. // Update anim (keep states locally within animation, only way to do this)
  5595. if (anim)
  5596. {
  5597. gs_gui_animation_update(ctx, anim);
  5598. // Get blended style based on animation
  5599. style = gs_gui_animation_get_blend_style(ctx, anim, desc, GS_GUI_ELEMENT_BUTTON);
  5600. }
  5601. else
  5602. {
  5603. style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, GS_GUI_ELEMENT_BUTTON, 0x02) :
  5604. ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, GS_GUI_ELEMENT_BUTTON, 0x01) :
  5605. gs_gui_get_current_element_style(ctx, desc, GS_GUI_ELEMENT_BUTTON, 0x00);
  5606. }
  5607. // Temporary copy of style
  5608. gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
  5609. gs_gui_rect_t r = gs_gui_layout_next(ctx);
  5610. gs_gui_update_control(ctx, id, r, opt);
  5611. /* handle click or button press for submission */
  5612. if (ctx->mouse_down != GS_GUI_MOUSE_LEFT && ctx->hover == id && ctx->last_focus_state == GS_GUI_ELEMENT_STATE_OFF_FOCUS)
  5613. {
  5614. res |= GS_GUI_RES_SUBMIT;
  5615. }
  5616. // draw border
  5617. if (style.colors[GS_GUI_COLOR_BORDER].a)
  5618. {
  5619. 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]);
  5620. }
  5621. opt |= GS_GUI_OPT_ISCONTENT;
  5622. gs_gui_draw_rect(ctx, r, style.colors[GS_GUI_COLOR_BACKGROUND]);
  5623. if (label) {gs_gui_draw_control_text(ctx, label_tag, r, &style, opt);}
  5624. gs_gui_pop_style(ctx, save);
  5625. gs_gui_pop_id(ctx);
  5626. return res;
  5627. }
  5628. 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)
  5629. {
  5630. int32_t res = 0;
  5631. gs_gui_id id = gs_gui_get_id(ctx, &state, sizeof(state));
  5632. gs_gui_rect_t r = gs_gui_layout_next(ctx);
  5633. gs_gui_rect_t box = gs_gui_rect(r.x, r.y, r.h, r.h);
  5634. int32_t ox = (int32_t)(box.w * 0.2f), oy = (int32_t)(box.h * 0.2f);
  5635. gs_gui_rect_t inner_box = gs_gui_rect(box.x + ox, box.y + oy, box.w - 2 * ox, box.h - 2 * oy);
  5636. gs_gui_update_control(ctx, id, r, 0);
  5637. int32_t elementid = GS_GUI_ELEMENT_BUTTON;
  5638. gs_gui_style_t style = gs_default_val();
  5639. style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
  5640. ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
  5641. gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
  5642. /* handle click */
  5643. if ((ctx->mouse_pressed == GS_GUI_MOUSE_LEFT || (ctx->mouse_pressed && ~opt & GS_GUI_OPT_LEFTCLICKONLY)) && ctx->focus == id)
  5644. {
  5645. res |= GS_GUI_RES_CHANGE;
  5646. *state = !*state;
  5647. }
  5648. /* draw */
  5649. gs_gui_draw_control_frame(ctx, id, box, GS_GUI_ELEMENT_INPUT, 0);
  5650. if (*state)
  5651. {
  5652. // Draw in a filled rect
  5653. gs_gui_draw_rect(ctx, inner_box, style.colors[GS_GUI_COLOR_BACKGROUND]);
  5654. }
  5655. r = gs_gui_rect(r.x + box.w, r.y, r.w - box.w, r.h);
  5656. gs_gui_draw_control_text(ctx, label, r, &ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0], 0);
  5657. return res;
  5658. }
  5659. 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,
  5660. const gs_gui_selector_desc_t* desc, uint64_t opt)
  5661. {
  5662. int32_t res = 0;
  5663. int32_t elementid = GS_GUI_ELEMENT_INPUT;
  5664. gs_gui_style_t style = gs_default_val();
  5665. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
  5666. // Update anim (keep states locally within animation, only way to do this)
  5667. if (anim)
  5668. {
  5669. // Need to check that I haven't updated more than once this frame
  5670. gs_gui_animation_update(ctx, anim);
  5671. // Get blended style based on animation
  5672. style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
  5673. }
  5674. else
  5675. {
  5676. style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
  5677. ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
  5678. gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
  5679. }
  5680. // Push temp style
  5681. gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
  5682. gs_gui_update_control(ctx, id, rect, opt | GS_GUI_OPT_HOLDFOCUS);
  5683. if (ctx->focus == id)
  5684. {
  5685. /* handle text input */
  5686. int32_t len = strlen(buf);
  5687. int32_t n = gs_min(bufsz - len - 1, (int32_t) strlen(ctx->input_text));
  5688. if (n > 0)
  5689. {
  5690. memcpy(buf + len, ctx->input_text, n);
  5691. len += n;
  5692. buf[len] = '\0';
  5693. res |= GS_GUI_RES_CHANGE;
  5694. }
  5695. /* handle backspace */
  5696. if (ctx->key_pressed & GS_GUI_KEY_BACKSPACE && len > 0)
  5697. {
  5698. if (ctx->key_down & GS_GUI_KEY_CTRL) {
  5699. for (--len; len > 0 ;len--) {
  5700. /* skip utf-8 continuation bytes */
  5701. if ((buf[len-1] & 0xc0) == 0x80) continue;
  5702. /* seek until seperator character */
  5703. if (strchr(" ()[]{},.-+*=/\\^~|\"'&%#@!<>;:", buf[len-1])) break;
  5704. }
  5705. } else {
  5706. /* skip utf-8 continuation bytes */
  5707. while ((buf[--len] & 0xc0) == 0x80 && len > 0);
  5708. }
  5709. buf[len] = '\0';
  5710. res |= GS_GUI_RES_CHANGE;
  5711. }
  5712. /* handle paste */
  5713. if (gs_platform_key_pressed(GS_KEYCODE_V) && ctx->key_down & GS_GUI_KEY_CTRL)
  5714. {
  5715. const char* clipboard = gs_platform_window_get_clipboard(ctx->window_hndl);
  5716. printf("%s --\n", clipboard);
  5717. int32_t n = gs_min(bufsz - len - 1, (int32_t) strlen(clipboard));
  5718. if (n > 0) {
  5719. memcpy(buf + len, clipboard, n);
  5720. len += n;
  5721. buf[len] = '\0';
  5722. res |= GS_GUI_RES_CHANGE;
  5723. }
  5724. }
  5725. /* handle return */
  5726. if (ctx->key_pressed & GS_GUI_KEY_RETURN)
  5727. {
  5728. gs_gui_set_focus(ctx, 0);
  5729. res |= GS_GUI_RES_SUBMIT;
  5730. }
  5731. }
  5732. /* draw */
  5733. // Textbox border
  5734. 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]);
  5735. // Textbox bg
  5736. gs_gui_draw_control_frame(ctx, id, rect, GS_GUI_ELEMENT_INPUT, opt);
  5737. // Text and carret
  5738. if (ctx->focus == id)
  5739. {
  5740. gs_gui_style_t* sp = &style;
  5741. gs_color_t* color = &sp->colors[GS_GUI_COLOR_CONTENT];
  5742. int32_t sx = sp->shadow_x;
  5743. int32_t sy = sp->shadow_y;
  5744. gs_color_t* sc = &sp->colors[GS_GUI_COLOR_SHADOW];
  5745. gs_asset_font_t* font = sp->font;
  5746. int32_t textw = gs_gui_text_width(font, buf, -1);
  5747. int32_t texth = gs_gui_font_height(font);
  5748. int32_t ofx = (int32_t)(rect.w - sp->padding[GS_GUI_PADDING_RIGHT] - textw - 1);
  5749. int32_t textx = (int32_t)(rect.x + gs_min(ofx, sp->padding[GS_GUI_PADDING_LEFT]));
  5750. int32_t texty = (int32_t)(rect.y + (rect.h - texth) / 2);
  5751. int32_t cary = (int32_t)(rect.y + 1);
  5752. gs_gui_push_clip_rect(ctx, rect);
  5753. // Draw text
  5754. gs_gui_draw_control_text(ctx, buf, rect, &style, opt);
  5755. // Draw caret (control alpha based on frame)
  5756. static bool on = true;
  5757. static float ct = 0.f;
  5758. if (~opt & GS_GUI_OPT_NOCARET)
  5759. {
  5760. gs_vec2 pos = gs_v2(rect.x, rect.y);
  5761. // Grab stylings
  5762. const int32_t padding_left = sp->padding[GS_GUI_PADDING_LEFT];
  5763. const int32_t padding_top = sp->padding[GS_GUI_PADDING_TOP];
  5764. const int32_t padding_right = sp->padding[GS_GUI_PADDING_RIGHT];
  5765. const int32_t padding_bottom = sp->padding[GS_GUI_PADDING_BOTTOM];
  5766. const int32_t align = sp->align_content;
  5767. const int32_t justify = sp->justify_content;
  5768. // Determine x placement based on justification
  5769. switch (justify)
  5770. {
  5771. default:
  5772. case GS_GUI_JUSTIFY_START:
  5773. {
  5774. pos.x = rect.x + padding_left;
  5775. } break;
  5776. case GS_GUI_JUSTIFY_CENTER:
  5777. {
  5778. pos.x = rect.x + (rect.w - textw) * 0.5f;
  5779. } break;
  5780. case GS_GUI_JUSTIFY_END:
  5781. {
  5782. pos.x = rect.x + (rect.w - textw) - padding_right;
  5783. } break;
  5784. }
  5785. // Determine caret position based on style justification
  5786. gs_gui_rect_t cr = gs_gui_rect(pos.x + textw + padding_right,
  5787. (f32)rect.y + 5.f, 1.f, (f32)rect.h - 10.f);
  5788. if (ctx->last_focus_state == GS_GUI_ELEMENT_STATE_ON_FOCUS) {on = true; ct = 0.f;}
  5789. ct += 0.1f;
  5790. if (ct >= 3.f) {on = !on; ct = 0.f;}
  5791. gs_color_t col = *color;
  5792. col.a = on ? col.a : 0;
  5793. gs_gui_draw_rect(ctx, cr, col);
  5794. }
  5795. gs_gui_pop_clip_rect(ctx);
  5796. }
  5797. else
  5798. {
  5799. gs_gui_style_t* sp = &style;
  5800. gs_color_t* color = &sp->colors[GS_GUI_COLOR_CONTENT];
  5801. gs_asset_font_t* font = sp->font;
  5802. int32_t sx = sp->shadow_x;
  5803. int32_t sy = sp->shadow_y;
  5804. gs_color_t* sc = &sp->colors[GS_GUI_COLOR_SHADOW];
  5805. int32_t textw = gs_gui_text_width(font, buf, -1);
  5806. int32_t texth = gs_gui_text_height(font, buf, -1);
  5807. int32_t textx = (int32_t)(rect.x + sp->padding[GS_GUI_PADDING_LEFT]);
  5808. int32_t texty = (int32_t)(rect.y + (rect.h - texth) / 2);
  5809. gs_gui_push_clip_rect(ctx, rect);
  5810. gs_gui_draw_control_text(ctx, buf, rect, &style, opt);
  5811. gs_gui_pop_clip_rect(ctx);
  5812. }
  5813. gs_gui_pop_style(ctx, save);
  5814. return res;
  5815. }
  5816. 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)
  5817. {
  5818. if (ctx->mouse_pressed == GS_GUI_MOUSE_LEFT && ctx->key_down & GS_GUI_KEY_SHIFT &&
  5819. ctx->hover == id
  5820. )
  5821. {
  5822. ctx->number_edit = id;
  5823. gs_snprintf(ctx->number_edit_buf, GS_GUI_MAX_FMT, GS_GUI_REAL_FMT, *value);
  5824. }
  5825. if (ctx->number_edit == id)
  5826. {
  5827. // This is broken for some reason...
  5828. int32_t res = gs_gui_textbox_raw(ctx, ctx->number_edit_buf,
  5829. sizeof(ctx->number_edit_buf), id, r, desc, 0);
  5830. if (res & GS_GUI_RES_SUBMIT || ctx->focus != id)
  5831. {
  5832. *value = strtod(ctx->number_edit_buf, NULL);
  5833. ctx->number_edit = 0;
  5834. }
  5835. else
  5836. {
  5837. return 1;
  5838. }
  5839. }
  5840. return 0;
  5841. }
  5842. 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)
  5843. {
  5844. // Handle animation here...
  5845. int32_t res = 0;
  5846. gs_gui_id id = gs_gui_get_id(ctx, &buf, sizeof(buf));
  5847. int32_t elementid = GS_GUI_ELEMENT_INPUT;
  5848. gs_gui_style_t style = gs_default_val();
  5849. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
  5850. // Update anim (keep states locally within animation, only way to do this)
  5851. if (anim)
  5852. {
  5853. // Need to check that I haven't updated more than once this frame
  5854. gs_gui_animation_update(ctx, anim);
  5855. // Get blended style based on animation
  5856. style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
  5857. }
  5858. else
  5859. {
  5860. style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
  5861. ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
  5862. gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
  5863. }
  5864. // Push temp style
  5865. gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
  5866. gs_gui_rect_t r = gs_gui_layout_next(ctx);
  5867. gs_gui_update_control(ctx, id, r, opt | GS_GUI_OPT_HOLDFOCUS);
  5868. res |= gs_gui_textbox_raw(ctx, buf, bufsz, id, r, desc, opt);
  5869. gs_gui_pop_style(ctx, save);
  5870. return res;
  5871. }
  5872. 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,
  5873. const char* fmt, const gs_gui_selector_desc_t* desc, uint64_t opt)
  5874. {
  5875. char buf[GS_GUI_MAX_FMT + 1];
  5876. gs_gui_rect_t thumb;
  5877. int32_t x, w, res = 0;
  5878. gs_gui_real last = *value, v = last;
  5879. gs_gui_id id = gs_gui_get_id(ctx, &value, sizeof(value));
  5880. int32_t elementid = GS_GUI_ELEMENT_INPUT;
  5881. gs_gui_style_t style = gs_default_val();
  5882. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
  5883. int32_t state = ctx->focus == id ? GS_GUI_ELEMENT_STATE_FOCUS :
  5884. ctx->hover == id ? GS_GUI_ELEMENT_STATE_HOVER :
  5885. GS_GUI_ELEMENT_STATE_DEFAULT;
  5886. // Update anim (keep states locally within animation, only way to do this)
  5887. if (anim)
  5888. {
  5889. gs_gui_animation_update(ctx, anim);
  5890. // Get blended style based on animation
  5891. style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
  5892. }
  5893. else
  5894. {
  5895. style = gs_gui_get_current_element_style(ctx, desc, elementid, state);
  5896. }
  5897. // Temporary copy of style
  5898. gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
  5899. gs_gui_rect_t base = gs_gui_layout_next(ctx);
  5900. /* handle text input mode */
  5901. if (gs_gui_number_textbox(ctx, &v, base, id, desc)) {return res;}
  5902. /* handle normal mode */
  5903. gs_gui_update_control(ctx, id, base, opt);
  5904. /* handle input */
  5905. if (ctx->focus == id &&
  5906. (ctx->mouse_down | ctx->mouse_pressed) == GS_GUI_MOUSE_LEFT)
  5907. {
  5908. v = low + (ctx->mouse_pos.x - base.x) * (high - low) / base.w;
  5909. if (step) {v = (((v + step / 2) / step)) * step;}
  5910. }
  5911. /* clamp and store value, update res */
  5912. *value = v = gs_clamp(v, low, high);
  5913. if (last != v) {res |= GS_GUI_RES_CHANGE;}
  5914. /* draw base */
  5915. gs_gui_draw_control_frame(ctx, id, base, GS_GUI_ELEMENT_INPUT, opt);
  5916. /* draw control */
  5917. w = style.thumb_size; // Don't like this...
  5918. x = (int32_t)((v - low) * (base.w - w) / (high - low));
  5919. thumb = gs_gui_rect((f32)base.x + (f32)x, base.y, (f32)w, base.h);
  5920. gs_gui_draw_control_frame(ctx, id, thumb, GS_GUI_ELEMENT_BUTTON, opt);
  5921. /* draw text */
  5922. style.colors[GS_GUI_COLOR_BACKGROUND] = ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][state].colors[GS_GUI_COLOR_BACKGROUND];
  5923. gs_snprintf(buf, GS_GUI_MAX_FMT, fmt, v);
  5924. gs_gui_draw_control_text(ctx, buf, base, &style, opt); // oh...bg
  5925. // Pop style
  5926. gs_gui_pop_style(ctx, save);
  5927. return res;
  5928. }
  5929. 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,
  5930. const gs_gui_selector_desc_t* desc, uint64_t opt)
  5931. {
  5932. char buf[GS_GUI_MAX_FMT + 1];
  5933. int32_t res = 0;
  5934. gs_gui_id id = gs_gui_get_id(ctx, &value, sizeof(value));
  5935. int32_t elementid = GS_GUI_ELEMENT_INPUT;
  5936. gs_gui_style_t style = gs_default_val();
  5937. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
  5938. // Update anim (keep states locally within animation, only way to do this)
  5939. if (anim)
  5940. {
  5941. gs_gui_animation_update(ctx, anim);
  5942. // Get blended style based on animation
  5943. style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
  5944. }
  5945. else
  5946. {
  5947. style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
  5948. ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
  5949. gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
  5950. }
  5951. // Temporary copy of style
  5952. gs_gui_style_t* save = gs_gui_push_style(ctx, &style);
  5953. gs_gui_rect_t base = gs_gui_layout_next(ctx);
  5954. gs_gui_real last = *value;
  5955. /* handle text input mode */
  5956. if (gs_gui_number_textbox(ctx, value, base, id, desc)) {
  5957. gs_gui_pop_style(ctx, save);
  5958. return res;
  5959. }
  5960. /* handle normal mode */
  5961. gs_gui_update_control(ctx, id, base, opt);
  5962. /* handle input */
  5963. if (ctx->focus == id && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  5964. {
  5965. *value += ctx->mouse_delta.x * step;
  5966. }
  5967. /* set flag if value changed */
  5968. if (*value != last) { res |= GS_GUI_RES_CHANGE; }
  5969. /* draw base */
  5970. gs_gui_draw_control_frame(ctx, id, base, GS_GUI_ELEMENT_INPUT, opt);
  5971. /* draw text */
  5972. gs_snprintf(buf, GS_GUI_MAX_FMT, fmt, *value);
  5973. gs_gui_draw_control_text(ctx, buf, base, &ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0], opt);
  5974. gs_gui_pop_style(ctx, save);
  5975. return res;
  5976. }
  5977. static int32_t _gs_gui_header(gs_gui_context_t *ctx, const char *label, int32_t istreenode,
  5978. const gs_gui_selector_desc_t* desc, uint64_t opt)
  5979. {
  5980. gs_gui_rect_t r;
  5981. int32_t active, expanded;
  5982. int32_t width = -1;
  5983. gs_gui_layout_row(ctx, 1, &width, 0);
  5984. char id_tag[256] = gs_default_val();
  5985. char label_tag[256] = gs_default_val();
  5986. gs_gui_parse_id_tag(ctx, label, id_tag, sizeof(id_tag), opt);
  5987. gs_gui_parse_label_tag(ctx, label, label_tag, sizeof(label_tag));
  5988. gs_gui_id id = gs_gui_get_id(ctx, id_tag, strlen(id_tag));
  5989. int32_t idx = gs_gui_pool_get(ctx, ctx->treenode_pool, GS_GUI_TREENODEPOOL_SIZE, id);
  5990. gs_gui_push_id(ctx, id_tag, strlen(id_tag));
  5991. active = (idx >= 0);
  5992. expanded = (opt & GS_GUI_OPT_EXPANDED) ? !active : active;
  5993. r = gs_gui_layout_next(ctx);
  5994. gs_gui_update_control(ctx, id, r, 0);
  5995. /* handle click */
  5996. active ^= (ctx->mouse_pressed == GS_GUI_MOUSE_LEFT && ctx->focus == id);
  5997. /* update pool ref */
  5998. if (idx >= 0)
  5999. {
  6000. if (active)
  6001. { gs_gui_pool_update(ctx, ctx->treenode_pool, idx);
  6002. }
  6003. else
  6004. {
  6005. memset(&ctx->treenode_pool[idx], 0, sizeof(gs_gui_pool_item_t));
  6006. }
  6007. }
  6008. else if (active)
  6009. {
  6010. gs_gui_pool_init(ctx, ctx->treenode_pool, GS_GUI_TREENODEPOOL_SIZE, id);
  6011. }
  6012. /* draw */
  6013. if (istreenode)
  6014. {
  6015. if (ctx->hover == id)
  6016. {
  6017. gs_gui_draw_frame(ctx, r, &ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][GS_GUI_ELEMENT_STATE_HOVER]);
  6018. }
  6019. }
  6020. else
  6021. {
  6022. gs_gui_draw_control_frame(ctx, id, r, GS_GUI_ELEMENT_BUTTON, 0);
  6023. }
  6024. const float sz = 6.f;
  6025. if (expanded)
  6026. {
  6027. gs_vec2 a = {r.x + sz / 2.f, r.y + (r.h - sz) / 2.f};
  6028. gs_vec2 b = gs_vec2_add(a, gs_v2(sz, 0.f));
  6029. gs_vec2 c = gs_vec2_add(a, gs_v2(sz / 2.f, sz));
  6030. gs_gui_draw_triangle(ctx, a, b, c, ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0x00].colors[GS_GUI_COLOR_CONTENT]);
  6031. }
  6032. else
  6033. {
  6034. gs_vec2 a = {r.x + sz / 2.f, r.y + (r.h - sz) / 2.f};
  6035. gs_vec2 b = gs_vec2_add(a, gs_v2(sz, sz / 2.f));
  6036. gs_vec2 c = gs_vec2_add(a, gs_v2(0.f, sz));
  6037. gs_gui_draw_triangle(ctx, a, b, c, ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0x00].colors[GS_GUI_COLOR_CONTENT]);
  6038. }
  6039. // Draw text for treenode
  6040. r.x += r.h - ctx->style->padding[GS_GUI_PADDING_TOP];
  6041. r.w -= r.h - ctx->style->padding[GS_GUI_PADDING_BOTTOM];
  6042. gs_gui_draw_control_text(ctx, label_tag, r, &ctx->style_sheet->styles[GS_GUI_ELEMENT_TEXT][0x00], 0);
  6043. gs_gui_pop_id(ctx);
  6044. return expanded ? GS_GUI_RES_ACTIVE : 0;
  6045. }
  6046. GS_API_DECL int32_t
  6047. gs_gui_header_ex(gs_gui_context_t* ctx, const char* label,
  6048. const gs_gui_selector_desc_t* desc, uint64_t opt)
  6049. {
  6050. return _gs_gui_header(ctx, label, 0, desc, opt);
  6051. }
  6052. GS_API_DECL int32_t
  6053. gs_gui_treenode_begin_ex(gs_gui_context_t * ctx, const char* label,
  6054. const gs_gui_selector_desc_t* desc, uint64_t opt)
  6055. {
  6056. int32_t res = _gs_gui_header(ctx, label, 1, desc, opt);
  6057. if (res & GS_GUI_RES_ACTIVE)
  6058. {
  6059. gs_gui_get_layout(ctx)->indent += ctx->style->indent;
  6060. gs_gui_stack_push(ctx->id_stack, ctx->last_id);
  6061. }
  6062. return res;
  6063. }
  6064. GS_API_DECL void
  6065. gs_gui_treenode_end(gs_gui_context_t *ctx)
  6066. {
  6067. gs_gui_get_layout(ctx)->indent -= ctx->style->indent;
  6068. gs_gui_pop_id(ctx);
  6069. }
  6070. // -1 for left, + 1 for right
  6071. GS_API_DECL void
  6072. gs_gui_tab_item_swap(gs_gui_context_t* ctx, gs_gui_container_t* cnt, int32_t direction)
  6073. {
  6074. gs_gui_tab_bar_t* tab_bar = gs_gui_get_tab_bar(ctx, cnt);
  6075. if (!tab_bar) return;
  6076. int32_t item = (int32_t)cnt->tab_item;
  6077. int32_t idx = gs_clamp(item + direction, 0, (int32_t)tab_bar->size - 1);
  6078. gs_gui_container_t* scnt = (gs_gui_container_t*)tab_bar->items[idx].data;
  6079. gs_gui_tab_item_t* cti = &tab_bar->items[cnt->tab_item];
  6080. gs_gui_tab_item_t* sti = &tab_bar->items[idx];
  6081. gs_gui_tab_item_t tmp = *cti;
  6082. // Swap cti
  6083. sti->data = cnt;
  6084. cnt->tab_item = sti->idx;
  6085. // Swap sti
  6086. cti->data = scnt;
  6087. scnt->tab_item = cti->idx;
  6088. tab_bar->focus = sti->idx;
  6089. }
  6090. GS_API_DECL int32_t
  6091. gs_gui_window_begin_ex(gs_gui_context_t * ctx, const char* title, gs_gui_rect_t rect, bool* open,
  6092. const gs_gui_selector_desc_t* desc, uint64_t opt)
  6093. {
  6094. gs_gui_rect_t body;
  6095. char id_tag[256] = gs_default_val();
  6096. char label_tag[256] = gs_default_val();
  6097. gs_gui_parse_id_tag(ctx, title, id_tag, sizeof(id_tag), opt);
  6098. gs_gui_parse_label_tag(ctx, title, label_tag, sizeof(label_tag));
  6099. gs_gui_id id = 0x00;
  6100. /*
  6101. if (*id_tag) {
  6102. // id = gs_gui_push_id(ctx, id_tag, sizeof(id_tag));
  6103. id = gs_gui_get_id(ctx, id_tag, sizeof(id_tag));
  6104. }
  6105. else {
  6106. id = gs_gui_get_id(ctx, label_tag, sizeof(label_tag));
  6107. // id = gs_gui_get_id(ctx, id_tag, gs_strlen(title));
  6108. // id = gs_gui_get_id(ctx, id_tag, sizeof(id_tag));
  6109. }
  6110. */
  6111. // id = gs_gui_get_id(ctx, title, strlen(title));
  6112. id = gs_gui_get_id(ctx, id_tag, strlen(id_tag));
  6113. gs_gui_container_t* cnt = gs_gui_get_container_ex(ctx, id, opt);
  6114. if (cnt && open) {
  6115. cnt->open = *open;
  6116. }
  6117. if (!cnt || !cnt->open) {
  6118. // gs_gui_pop_id(ctx);
  6119. return 0;
  6120. }
  6121. // Push id flag
  6122. // cnt->flags |= GS_GUI_WINDOW_FLAGS_PUSH_ID;
  6123. if (cnt->flags & GS_GUI_WINDOW_FLAGS_FIRST_INIT) {
  6124. memcpy(cnt->name, label_tag, 256);
  6125. }
  6126. const int32_t title_max_size = 100;
  6127. bool new_frame = cnt->frame != ctx->frame;
  6128. int32_t state = ctx->active_root == cnt ? GS_GUI_ELEMENT_STATE_FOCUS :
  6129. ctx->hover_root == cnt ? GS_GUI_ELEMENT_STATE_HOVER :
  6130. GS_GUI_ELEMENT_STATE_DEFAULT;
  6131. const float split_size = GS_GUI_SPLIT_SIZE;
  6132. gs_gui_stack_push(ctx->id_stack, id);
  6133. // Get splits
  6134. gs_gui_split_t* split = gs_gui_get_split(ctx, cnt);
  6135. gs_gui_split_t* root_split = gs_gui_get_root_split(ctx, cnt);
  6136. // Get root container
  6137. gs_gui_container_t* root_cnt = gs_gui_get_root_container(ctx, cnt);
  6138. // Cache rect
  6139. 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)
  6140. {
  6141. if (opt & GS_GUI_OPT_FULLSCREEN)
  6142. {
  6143. gs_vec2 fb = ctx->framebuffer_size;
  6144. cnt->rect = gs_gui_rect(0, 0, fb.x, fb.y);
  6145. // Set root split rect size
  6146. if (root_split)
  6147. {
  6148. root_split->rect = cnt->rect;
  6149. gs_gui_update_split(ctx, root_split);
  6150. }
  6151. }
  6152. else
  6153. {
  6154. // Set root split rect size
  6155. if (root_split && root_cnt == cnt)
  6156. {
  6157. root_split->rect = rect;
  6158. gs_gui_update_split(ctx, root_split);
  6159. }
  6160. else
  6161. {
  6162. cnt->rect = rect;
  6163. }
  6164. }
  6165. cnt->flags = cnt->flags & ~GS_GUI_WINDOW_FLAGS_FIRST_INIT;
  6166. }
  6167. gs_gui_begin_root_container(ctx, cnt, opt);
  6168. rect = body = cnt->rect;
  6169. cnt->opt = opt;
  6170. if (opt & GS_GUI_OPT_DOCKSPACE)
  6171. {
  6172. cnt->zindex = 0;
  6173. }
  6174. // If parent cannot move/resize, set to this opt as well
  6175. if (root_cnt->opt & GS_GUI_OPT_NOMOVE)
  6176. {
  6177. cnt->opt |= GS_GUI_OPT_NOMOVE;
  6178. }
  6179. if (root_cnt->opt & GS_GUI_OPT_NORESIZE)
  6180. {
  6181. cnt->opt |= GS_GUI_OPT_NORESIZE;
  6182. }
  6183. // If in a tab view, then title has to be handled differently...
  6184. gs_gui_tab_bar_t* tab_bar = cnt->tab_bar ? gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar) : NULL;
  6185. gs_gui_tab_item_t* tab_item = tab_bar ? &tab_bar->items[cnt->tab_item] : NULL;
  6186. if (tab_item && tab_item)
  6187. {
  6188. if (tab_bar->focus == tab_item->idx)
  6189. {
  6190. cnt->flags |= GS_GUI_WINDOW_FLAGS_VISIBLE;
  6191. cnt->opt &= ~GS_GUI_OPT_NOINTERACT;
  6192. cnt->opt &= ~GS_GUI_OPT_NOHOVER;
  6193. }
  6194. else
  6195. {
  6196. cnt->flags &= ~GS_GUI_WINDOW_FLAGS_VISIBLE;
  6197. cnt->opt |= GS_GUI_OPT_NOINTERACT;
  6198. cnt->opt |= GS_GUI_OPT_NOHOVER;
  6199. }
  6200. }
  6201. bool in_root = false;
  6202. // If hovered root is in the tab group and moused over, then is hovered
  6203. if (tab_bar)
  6204. {
  6205. for (uint32_t i = 0; i < tab_bar->size; ++i)
  6206. {
  6207. if (ctx->hover_root == (gs_gui_container_t*)tab_bar->items[i].data)
  6208. {
  6209. in_root = true;
  6210. break;
  6211. }
  6212. }
  6213. }
  6214. gs_gui_container_t* s_cnt = cnt;
  6215. if (tab_bar && split)
  6216. {
  6217. for (uint32_t i = 0; i < tab_bar->size; ++i)
  6218. {
  6219. if (((gs_gui_container_t*)tab_bar->items[i].data)->split)
  6220. {
  6221. s_cnt = (gs_gui_container_t*)tab_bar->items[i].data;
  6222. }
  6223. }
  6224. }
  6225. // Do split size/position
  6226. if (split)
  6227. {
  6228. const gs_gui_style_t* cstyle = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state];
  6229. const gs_gui_rect_t* sr = &split->rect;
  6230. const float ratio = split->ratio;
  6231. float shsz = split_size;
  6232. const float omr = (1.f - ratio);
  6233. switch (split->type)
  6234. {
  6235. case GS_GUI_SPLIT_LEFT:
  6236. {
  6237. if (split->children[GS_GUI_SPLIT_NODE_CHILD].container == s_cnt)
  6238. {
  6239. cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + shsz, sr->w * ratio - 2.f * shsz, sr->h - 2.f * shsz);
  6240. }
  6241. else
  6242. {
  6243. 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);
  6244. }
  6245. } break;
  6246. case GS_GUI_SPLIT_RIGHT:
  6247. {
  6248. if (split->children[GS_GUI_SPLIT_NODE_PARENT].container == s_cnt)
  6249. {
  6250. cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + shsz, sr->w * (1.f - ratio) - 2.f * shsz, sr->h - 2.f * shsz);
  6251. }
  6252. else
  6253. {
  6254. 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);
  6255. }
  6256. } break;
  6257. case GS_GUI_SPLIT_TOP:
  6258. {
  6259. if (split->children[GS_GUI_SPLIT_NODE_CHILD].container == s_cnt)
  6260. {
  6261. cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + shsz, sr->w - 2.f * shsz, sr->h * ratio - 2.f * shsz);
  6262. }
  6263. else
  6264. {
  6265. 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);
  6266. }
  6267. } break;
  6268. case GS_GUI_SPLIT_BOTTOM:
  6269. {
  6270. if (split->children[GS_GUI_SPLIT_NODE_CHILD].container == s_cnt)
  6271. {
  6272. 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);
  6273. }
  6274. else
  6275. {
  6276. cnt->rect = gs_gui_rect(sr->x + shsz, sr->y + shsz, sr->w - 2.f * shsz, sr->h * (1.f - ratio) - 2.f * shsz);
  6277. }
  6278. } break;
  6279. }
  6280. }
  6281. // Calculate movement
  6282. if (~cnt->opt & GS_GUI_OPT_NOTITLE && new_frame)
  6283. {
  6284. gs_gui_rect_t* rp = root_split ? &root_split->rect : &cnt->rect;
  6285. // Cache rect
  6286. gs_gui_rect_t tr = cnt->rect;
  6287. tr.h = ctx->style->title_height;
  6288. tr.x += split_size;
  6289. gs_gui_id id = gs_gui_get_id(ctx, "!title", 6);
  6290. gs_gui_update_control(ctx, id, tr, opt);
  6291. // Need to move the entire thing
  6292. if ((id == ctx->focus || id == ctx->hover) && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  6293. {
  6294. // This lock id is what I need...
  6295. ctx->active_root = cnt;
  6296. if (tab_bar)
  6297. {
  6298. ctx->next_focus_root = (gs_gui_container_t*)(tab_bar->items[tab_bar->focus].data);
  6299. gs_gui_bring_to_front(ctx, (gs_gui_container_t*)tab_bar->items[tab_bar->focus].data);
  6300. if (id == ctx->focus && tab_bar->focus != tab_item->idx) ctx->lock_focus = id;
  6301. }
  6302. else
  6303. {
  6304. ctx->next_focus_root = cnt;
  6305. }
  6306. if (root_split)
  6307. {
  6308. gs_gui_request_t req = gs_default_val();
  6309. req.type = GS_GUI_SPLIT_MOVE;
  6310. req.split = root_split;
  6311. gs_dyn_array_push(ctx->requests, req);
  6312. }
  6313. else
  6314. {
  6315. gs_gui_request_t req = gs_default_val();
  6316. req.type = GS_GUI_CNT_MOVE;
  6317. req.cnt = cnt;
  6318. gs_dyn_array_push(ctx->requests, req);
  6319. }
  6320. }
  6321. // Tab view
  6322. int32_t tw = title_max_size;
  6323. id = gs_gui_get_id(ctx, "!split_tab", 10);
  6324. const float hp = 0.8f;
  6325. tr.x += split_size;
  6326. float h = tr.h * hp;
  6327. float y = tr.y + tr.h * (1.f - hp);
  6328. // Will update tab bar rect size with parent window rect
  6329. if (tab_item)
  6330. {
  6331. // Get tab bar
  6332. gs_gui_rect_t* r = &tab_bar->rect;
  6333. // Determine width
  6334. int32_t tab_width = (s32)gs_min(r->w / (float)tab_bar->size, title_max_size);
  6335. tw = tab_item->zindex ? (s32)tab_width : (s32)(tab_width + 1.f);
  6336. // Determine position (based on zindex and total width)
  6337. float xoff = 0.f; //tab_item->zindex ? 2.f : 0.f;
  6338. tr.x = tab_bar->rect.x + tab_width * tab_item->zindex + xoff;
  6339. }
  6340. gs_gui_rect_t r = gs_gui_rect(tr.x + split_size, y, (f32)tw, h);
  6341. gs_gui_update_control(ctx, id, r, opt);
  6342. // Need to move the entire thing
  6343. if ((id == ctx->hover || id == ctx->focus) && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  6344. {
  6345. gs_gui_set_focus(ctx, id);
  6346. ctx->next_focus_root = cnt;
  6347. ctx->active_root = cnt;
  6348. // Don't move from tab bar
  6349. if (tab_item)
  6350. {
  6351. // Handle break out
  6352. if (ctx->mouse_pos.y < tr.y || ctx->mouse_pos.y > tr.y + tr.h)
  6353. {
  6354. ctx->undock_root = cnt;
  6355. }
  6356. if (tab_bar->focus != tab_item->idx)
  6357. {
  6358. gs_gui_request_t req = gs_default_val();
  6359. req.type = GS_GUI_CNT_FOCUS;
  6360. req.cnt = cnt;
  6361. gs_dyn_array_push(ctx->requests, req);
  6362. }
  6363. }
  6364. else if (root_split)
  6365. {
  6366. // Handle break out
  6367. if (ctx->mouse_pos.y < tr.y || ctx->mouse_pos.y > tr.y + tr.h)
  6368. {
  6369. ctx->undock_root = cnt;
  6370. }
  6371. }
  6372. else
  6373. {
  6374. gs_gui_request_t req = gs_default_val();
  6375. req.type = GS_GUI_CNT_MOVE;
  6376. req.cnt = cnt;
  6377. gs_dyn_array_push(ctx->requests, req);
  6378. }
  6379. }
  6380. }
  6381. // Control frame for body movement
  6382. if (
  6383. ~root_cnt->opt & GS_GUI_OPT_NOMOVE &&
  6384. ~cnt->opt & GS_GUI_OPT_NOMOVE &&
  6385. ~cnt->opt & GS_GUI_OPT_NOINTERACT &&
  6386. ~cnt->opt & GS_GUI_OPT_NOHOVER &&
  6387. new_frame &&
  6388. cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE
  6389. )
  6390. {
  6391. // Cache rect
  6392. gs_gui_rect_t br = cnt->rect;
  6393. if (~cnt->opt & GS_GUI_OPT_NOTITLE)
  6394. {
  6395. br.y += ctx->style->title_height;
  6396. br.h -= ctx->style->title_height;
  6397. }
  6398. gs_gui_id id = gs_gui_get_id(ctx, "!body", 5);
  6399. // gs_gui_update_control(ctx, id, br, (opt | GS_GUI_OPT_NOSWITCHSTATE));
  6400. if (ctx->hover_root == cnt && !ctx->focus_split && !ctx->lock_focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  6401. {
  6402. ctx->active_root = cnt;
  6403. ctx->next_focus_root = cnt;
  6404. }
  6405. // Need to move the entire thing
  6406. if (ctx->hover_root == cnt && !ctx->focus_split && !ctx->focus && !ctx->lock_focus && !ctx->hover && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  6407. {
  6408. if (root_split)
  6409. {
  6410. gs_gui_request_t req = gs_default_val();
  6411. req.type = GS_GUI_SPLIT_MOVE;
  6412. req.split = root_split;
  6413. gs_dyn_array_push(ctx->requests, req);
  6414. }
  6415. else if (tab_bar)
  6416. {
  6417. gs_gui_request_t req = gs_default_val();
  6418. req.type = GS_GUI_CNT_FOCUS;
  6419. req.cnt = cnt;
  6420. gs_dyn_array_push(ctx->requests, req);
  6421. req.type = GS_GUI_CNT_MOVE;
  6422. req.cnt = cnt;
  6423. gs_dyn_array_push(ctx->requests, req);
  6424. }
  6425. else
  6426. {
  6427. gs_gui_request_t req = gs_default_val();
  6428. req.type = GS_GUI_CNT_MOVE;
  6429. req.cnt = cnt;
  6430. gs_dyn_array_push(ctx->requests, req);
  6431. }
  6432. }
  6433. }
  6434. // Get parent window if in tab view, then set rect to it (will be a frame off though...)
  6435. if (tab_item && tab_bar)
  6436. {
  6437. if (tab_bar->focus == tab_item->idx || split)
  6438. {
  6439. tab_bar->rect = cnt->rect;
  6440. }
  6441. else
  6442. {
  6443. cnt->rect = tab_bar->rect;
  6444. }
  6445. }
  6446. // Cache body
  6447. body = cnt->rect;
  6448. if (split)
  6449. {
  6450. const float sh = split_size * 0.5f;
  6451. }
  6452. if (~opt & GS_GUI_OPT_NOTITLE)
  6453. {
  6454. gs_gui_rect_t tr = cnt->rect;
  6455. tr.h = ctx->style->title_height;
  6456. if (split)
  6457. {
  6458. const float sh = split_size * 0.5f;
  6459. }
  6460. body.y += tr.h;
  6461. body.h -= tr.h;
  6462. }
  6463. int32_t zindex = INT32_MAX - 1;
  6464. if (root_split) {
  6465. gs_gui_get_split_lowest_zindex(ctx, root_split, &zindex);
  6466. if (zindex == cnt->zindex) {
  6467. gs_gui_style_t* style = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state];
  6468. gs_gui_draw_rect(ctx, root_split->rect, style->colors[GS_GUI_COLOR_BACKGROUND]);
  6469. gs_gui_draw_splits(ctx, root_split);
  6470. }
  6471. }
  6472. // draw body frame
  6473. if (~opt & GS_GUI_OPT_NOFRAME && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
  6474. {
  6475. gs_gui_style_t* style = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state];
  6476. if (ctx->active_root == root_cnt)
  6477. {
  6478. int32_t f = 0;
  6479. }
  6480. gs_gui_draw_rect(ctx, body, style->colors[GS_GUI_COLOR_BACKGROUND]);
  6481. // draw border (get root cnt and check state of that)
  6482. if (split)
  6483. {
  6484. int32_t root_state = ctx->active_root == root_cnt ? GS_GUI_ELEMENT_STATE_FOCUS :
  6485. ctx->hover_root == root_cnt ? GS_GUI_ELEMENT_STATE_HOVER :
  6486. GS_GUI_ELEMENT_STATE_DEFAULT;
  6487. bool share_split = ctx->active_root && gs_gui_get_root_container(ctx, ctx->active_root) == root_cnt ? true :
  6488. false;
  6489. // Have to look and see if hovered root shares split...
  6490. gs_gui_style_t* root_style = style;
  6491. if (share_split)
  6492. {
  6493. root_style = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][GS_GUI_ELEMENT_STATE_FOCUS];
  6494. }
  6495. else
  6496. {
  6497. root_style =
  6498. state == GS_GUI_ELEMENT_STATE_FOCUS ? style :
  6499. root_state == GS_GUI_ELEMENT_STATE_FOCUS ? &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][root_state] :
  6500. root_state == GS_GUI_ELEMENT_STATE_HOVER ? &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][root_state] :
  6501. style;
  6502. }
  6503. if (~opt & GS_GUI_OPT_NOBORDER && root_style->colors[GS_GUI_COLOR_BORDER].a) {
  6504. 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]);
  6505. }
  6506. }
  6507. else
  6508. {
  6509. if (~opt & GS_GUI_OPT_NOBORDER && style->colors[GS_GUI_COLOR_BORDER].a) {
  6510. 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]);
  6511. }
  6512. }
  6513. }
  6514. if (split && ~opt & GS_GUI_OPT_NOCLIP && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
  6515. {
  6516. int16_t exp[] = {1, 1, 1, 1};
  6517. gs_gui_push_clip_rect(ctx, gs_gui_expand_rect(cnt->rect, exp));
  6518. }
  6519. if (split)
  6520. {
  6521. const float sh = split_size * 0.5f;
  6522. body.x += sh;
  6523. body.w -= split_size;
  6524. }
  6525. // do title bar
  6526. if (~opt & GS_GUI_OPT_NOTITLE)
  6527. {
  6528. gs_gui_style_t* cstyle = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state];
  6529. gs_gui_rect_t tr = cnt->rect;
  6530. tr.h = ctx->style->title_height;
  6531. if (split)
  6532. {
  6533. const float sh = split_size * 0.5f;
  6534. }
  6535. // Don't draw this unless you're the bottom window or first frame in a tab group (if in dockspace)
  6536. if (tab_bar)
  6537. {
  6538. bool lowest = true;
  6539. {
  6540. for (uint32_t i = 0; i < tab_bar->size; ++i)
  6541. {
  6542. if (cnt->zindex > ((gs_gui_container_t*)(tab_bar->items[i].data))->zindex)
  6543. {
  6544. lowest = false;
  6545. break;
  6546. }
  6547. }
  6548. if (lowest)
  6549. {
  6550. gs_gui_draw_frame(ctx, tr, &ctx->style_sheet->styles[GS_GUI_ELEMENT_PANEL][0x00]);
  6551. // 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]);
  6552. }
  6553. }
  6554. }
  6555. else
  6556. {
  6557. gs_gui_draw_frame(ctx, tr, &ctx->style_sheet->styles[GS_GUI_ELEMENT_PANEL][0x00]);
  6558. // gs_gui_draw_box(ctx, gs_gui_expand_rect(tr, (int16_t*)cstyle->border_width), cstyle->border_width, cstyle->colors[GS_GUI_COLOR_BORDER]);
  6559. }
  6560. // Draw tab control
  6561. {
  6562. // Tab view
  6563. int32_t tw = title_max_size;
  6564. id = gs_gui_get_id(ctx, "!split_tab", 10);
  6565. const float hp = 0.8f;
  6566. tr.x += split_size;
  6567. float h = tr.h * hp;
  6568. float y = tr.y + tr.h * (1.f - hp);
  6569. // Will update tab bar rect size with parent window rect
  6570. if (tab_item)
  6571. {
  6572. // Get tab bar
  6573. gs_gui_rect_t* r = &tab_bar->rect;
  6574. // Determine width
  6575. int32_t tab_width = (s32)gs_min(r->w / (float)tab_bar->size, title_max_size);
  6576. tw = (s32)(tab_width - 2.f);
  6577. // Determine position (based on zindex and total width)
  6578. float xoff = !tab_item->zindex ? split_size : 2.f; //tab_item->zindex ? 2.f : 0.f;
  6579. tr.x = tab_bar->rect.x + tab_width * tab_item->zindex + xoff;
  6580. }
  6581. gs_gui_rect_t r = gs_gui_rect(tr.x + split_size, y, (f32)tw, h);
  6582. bool hovered = false;
  6583. if (in_root && gs_gui_rect_overlaps_vec2(r, ctx->mouse_pos))
  6584. {
  6585. gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
  6586. // 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);
  6587. hovered = true;
  6588. }
  6589. bool other_root_active = ctx->focus_root != cnt;
  6590. if (tab_bar)
  6591. {
  6592. for (uint32_t i = 0; i < tab_bar->size; ++i)
  6593. {
  6594. if (tab_bar->items[i].data == ctx->focus_root)
  6595. {
  6596. other_root_active = false;
  6597. }
  6598. }
  6599. }
  6600. if (!other_root_active && hovered && ctx->mouse_down == GS_GUI_MOUSE_LEFT && !ctx->lock_focus)
  6601. {
  6602. // This is an issue...
  6603. gs_gui_set_focus(ctx, id);
  6604. ctx->lock_focus = id;
  6605. if (tab_item && tab_bar->focus != tab_item->idx)
  6606. {
  6607. gs_gui_request_t req = gs_default_val();
  6608. req.type = GS_GUI_CNT_FOCUS;
  6609. req.cnt = cnt;
  6610. gs_dyn_array_push(ctx->requests, req);
  6611. }
  6612. }
  6613. if (!other_root_active && ctx->mouse_down == GS_GUI_MOUSE_LEFT && ctx->focus == id)
  6614. {
  6615. if (ctx->mouse_pos.x < r.x)
  6616. {
  6617. gs_gui_request_t req = gs_default_val();
  6618. req.type = GS_GUI_TAB_SWAP_LEFT;
  6619. req.cnt = cnt;
  6620. gs_dyn_array_push(ctx->requests, req);
  6621. }
  6622. if (ctx->mouse_pos.x > r.x + r.w)
  6623. {
  6624. gs_gui_request_t req = gs_default_val();
  6625. req.type = GS_GUI_TAB_SWAP_RIGHT;
  6626. req.cnt = cnt;
  6627. gs_dyn_array_push(ctx->requests, req);
  6628. }
  6629. }
  6630. bool tab_focus = (!tab_bar || (tab_bar && tab_item && tab_bar->focus == tab_item->idx));
  6631. gs_color_t def = ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x00].colors[GS_GUI_COLOR_BACKGROUND];
  6632. gs_color_t hov = ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x01].colors[GS_GUI_COLOR_BACKGROUND];
  6633. gs_color_t act = ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x02].colors[GS_GUI_COLOR_BACKGROUND];
  6634. gs_color_t inactive = gs_color(10, 10, 10, 50);
  6635. int16_t exp[] = {1, 1, 1, 1};
  6636. gs_gui_push_clip_rect(ctx, gs_gui_expand_rect(cnt->rect, exp));
  6637. gs_gui_push_clip_rect(ctx, r);
  6638. gs_gui_draw_rect(ctx, r, id == ctx->focus ? act : hovered ? hov : tab_focus ? def : inactive);
  6639. gs_gui_draw_control_text(ctx, label_tag, r, &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][state], opt);
  6640. gs_gui_pop_clip_rect(ctx);
  6641. gs_gui_pop_clip_rect(ctx);
  6642. }
  6643. // do `close` button
  6644. /*
  6645. if (~opt & GS_GUI_OPT_NOCLOSE && false)
  6646. {
  6647. gs_gui_id id = gs_gui_get_id(ctx, "!close", 6);
  6648. gs_gui_rect_t r = gs_gui_rect(tr.x + tr.w - tr.h, tr.y, tr.h, tr.h);
  6649. tr.w -= r.w;
  6650. gs_gui_draw_icon(ctx, GS_GUI_ICON_CLOSE, r, ctx->style->colors[GS_GUI_COLOR_TITLETEXT]);
  6651. gs_gui_update_control(ctx, id, r, opt);
  6652. if (ctx->mouse_pressed == GS_GUI_MOUSE_LEFT && id == ctx->focus)
  6653. {
  6654. cnt->open = 0;
  6655. }
  6656. }
  6657. */
  6658. }
  6659. // resize to content size
  6660. if (opt & GS_GUI_OPT_AUTOSIZE && !split)
  6661. {
  6662. /*
  6663. gs_gui_rect_t r = gs_gui_get_layout(ctx)->body;
  6664. cnt->rect.w = cnt->content_size.x + (cnt->rect.w - r.w);
  6665. cnt->rect.h = cnt->content_size.y + (cnt->rect.h - r.h);
  6666. */
  6667. }
  6668. if (split && ~opt & GS_GUI_OPT_NOCLIP && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
  6669. {
  6670. gs_gui_pop_clip_rect(ctx);
  6671. }
  6672. // Draw border
  6673. if (~opt & GS_GUI_OPT_NOFRAME && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
  6674. {
  6675. const int* w = (int*)ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][0x00].border_width;
  6676. const gs_color_t* bc = &ctx->style_sheet->styles[GS_GUI_ELEMENT_CONTAINER][0x00].colors[GS_GUI_COLOR_BORDER];
  6677. // gs_gui_draw_box(ctx, gs_gui_expand_rect(cnt->rect, w), w, *bc);
  6678. }
  6679. gs_gui_push_container_body(ctx, cnt, body, desc, opt);
  6680. /* close if this is a popup window and elsewhere was clicked */
  6681. if (opt & GS_GUI_OPT_POPUP && ctx->mouse_pressed && ctx->hover_root != cnt)
  6682. {
  6683. cnt->open = 0;
  6684. }
  6685. if (~opt & GS_GUI_OPT_NOCLIP)
  6686. {
  6687. if (cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
  6688. {
  6689. gs_gui_push_clip_rect(ctx, cnt->body);
  6690. }
  6691. else
  6692. {
  6693. gs_gui_push_clip_rect(ctx, gs_gui_rect(0, 0, 0, 0));
  6694. }
  6695. }
  6696. return GS_GUI_RES_ACTIVE;
  6697. }
  6698. GS_API_DECL void
  6699. gs_gui_window_end(gs_gui_context_t *ctx)
  6700. {
  6701. gs_gui_container_t* cnt = gs_gui_get_current_container(ctx);
  6702. // Get root container
  6703. gs_gui_container_t* root_cnt = gs_gui_get_root_container(ctx, cnt);
  6704. // Get splits
  6705. gs_gui_split_t* split = gs_gui_get_split(ctx, cnt);
  6706. gs_gui_split_t* root_split = gs_gui_get_root_split(ctx, cnt);
  6707. const bool new_frame = cnt->frame != ctx->frame;
  6708. // Cache opt
  6709. const uint64_t opt = cnt->opt;
  6710. // Pop clip for rect
  6711. if (~cnt->opt & GS_GUI_OPT_NOCLIP)
  6712. {
  6713. gs_gui_pop_clip_rect(ctx);
  6714. }
  6715. if (~cnt->opt & GS_GUI_OPT_NOCLIP)
  6716. {
  6717. gs_gui_push_clip_rect(ctx, cnt->rect);
  6718. }
  6719. // do `resize` handle
  6720. if (~cnt->opt & GS_GUI_OPT_NORESIZE && ~root_cnt->opt &
  6721. GS_GUI_OPT_NORESIZE && new_frame && ~cnt->opt & GS_GUI_OPT_DOCKSPACE)
  6722. {
  6723. int32_t sz = ctx->style->title_height;
  6724. gs_gui_id id = gs_gui_get_id(ctx, "!resize", 7);
  6725. 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);
  6726. gs_gui_update_control(ctx, id, r, opt);
  6727. if (id == ctx->focus && ctx->mouse_down == GS_GUI_MOUSE_LEFT)
  6728. {
  6729. ctx->active_root = cnt;
  6730. if (root_split)
  6731. {
  6732. gs_gui_request_t req = gs_default_val();
  6733. req.type = GS_GUI_SPLIT_RESIZE_SE;
  6734. req.split = root_split;
  6735. gs_dyn_array_push(ctx->requests, req);
  6736. }
  6737. else
  6738. {
  6739. cnt->rect.w = gs_max(96, cnt->rect.w + ctx->mouse_delta.x);
  6740. cnt->rect.h = gs_max(64, cnt->rect.h + ctx->mouse_delta.y);
  6741. }
  6742. }
  6743. // Draw resize icon (this will also be a callback)
  6744. const uint32_t grid = 5;
  6745. const float w = r.w / (float)grid;
  6746. const float h = r.h / (float)grid;
  6747. const float m = 2.f;
  6748. const float o = 5.f;
  6749. gs_color_t col =
  6750. ctx->focus == id ? ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x02].colors[GS_GUI_COLOR_BACKGROUND] :
  6751. ctx->hover == id ? ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x01].colors[GS_GUI_COLOR_BACKGROUND] :
  6752. ctx->style_sheet->styles[GS_GUI_ELEMENT_BUTTON][0x00].colors[GS_GUI_COLOR_BACKGROUND];
  6753. gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * grid - o, r.y + h * (grid - 2) - o, w - m, h - m), col);
  6754. gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * grid - o, r.y + h * (grid - 1) - o, w - m, h - m), col);
  6755. 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);
  6756. gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * grid - o, r.y + h * grid - o, w - m, h - m), col);
  6757. gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * (grid - 1) - o, r.y + h * grid - o, w - m, h - m), col);
  6758. gs_gui_draw_rect(ctx, gs_gui_rect(r.x + w * (grid - 2) - o, r.y + h * grid - o, w - m, h - m), col);
  6759. }
  6760. if (~cnt->opt & GS_GUI_OPT_NOCLIP)
  6761. {
  6762. gs_gui_pop_clip_rect(ctx);
  6763. }
  6764. // draw shadow
  6765. if (~opt & GS_GUI_OPT_NOFRAME && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
  6766. {
  6767. gs_gui_rect_t* r = &cnt->rect;
  6768. uint32_t ssz = (u32)(split ? GS_GUI_SPLIT_SIZE : 5);
  6769. 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]);
  6770. 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]);
  6771. gs_gui_draw_rect(ctx, gs_gui_rect(r->x + r->w, r->y, 1, r->h), ctx->style->colors[GS_GUI_COLOR_SHADOW]);
  6772. 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]);
  6773. }
  6774. #define _gui_window_resize_ctrl(ID, RECT, MOUSE, SPLIT_TYPE, MOD_KEY, ...)\
  6775. do {\
  6776. if (ctx->key_down == (MOD_KEY))\
  6777. {\
  6778. gs_gui_id _ID = (ID);\
  6779. gs_gui_rect_t _R = (RECT);\
  6780. gs_gui_update_control(ctx, (ID), _R, opt);\
  6781. \
  6782. if (_ID == ctx->hover|| _ID == ctx->focus)\
  6783. {\
  6784. gs_gui_draw_rect(ctx, _R, GS_COLOR_WHITE);\
  6785. }\
  6786. \
  6787. if (_ID == ctx->focus && ctx->mouse_down == (MOUSE))\
  6788. {\
  6789. gs_gui_draw_rect(ctx, _R, GS_COLOR_WHITE);\
  6790. if (root_split)\
  6791. {\
  6792. gs_gui_request_t req = gs_default_val();\
  6793. req.type = (SPLIT_TYPE);\
  6794. req.split = root_split;\
  6795. gs_dyn_array_push(ctx->requests, req);\
  6796. }\
  6797. else if (new_frame)\
  6798. {\
  6799. __VA_ARGS__\
  6800. }\
  6801. }\
  6802. }\
  6803. } while (0)\
  6804. // Control frame for body resize
  6805. if (~opt & GS_GUI_OPT_NORESIZE && cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
  6806. {
  6807. // Cache main rect
  6808. gs_gui_rect_t* r = root_split ? &root_split->rect : &cnt->rect;
  6809. gs_gui_rect_t* cr = &cnt->rect;
  6810. const float border_ratio = 0.333f;
  6811. if (split)
  6812. {
  6813. _gui_window_resize_ctrl(
  6814. gs_gui_get_id(ctx, "!split_w", 8),
  6815. gs_gui_rect(cr->x, cr->y + cr->h * border_ratio, cr->w * border_ratio, cr->h * (1.f - 2.f * border_ratio)),
  6816. GS_GUI_MOUSE_RIGHT,
  6817. GS_GUI_SPLIT_RESIZE_INVALID,
  6818. GS_GUI_KEY_CTRL,
  6819. {
  6820. });
  6821. _gui_window_resize_ctrl(
  6822. gs_gui_get_id(ctx, "!split_e", 8),
  6823. 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)),
  6824. GS_GUI_MOUSE_LEFT,
  6825. GS_GUI_SPLIT_RESIZE_INVALID,
  6826. GS_GUI_KEY_CTRL,
  6827. {
  6828. });
  6829. _gui_window_resize_ctrl(
  6830. gs_gui_get_id(ctx, "!split_n", 8),
  6831. gs_gui_rect(cr->x + cr->w * border_ratio, cr->y, cr->w * (1.f - 2.f * border_ratio), cr->h * border_ratio),
  6832. GS_GUI_MOUSE_LEFT,
  6833. GS_GUI_SPLIT_RESIZE_INVALID,
  6834. GS_GUI_KEY_CTRL,
  6835. {
  6836. });
  6837. _gui_window_resize_ctrl(
  6838. gs_gui_get_id(ctx, "!split_s", 8),
  6839. 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),
  6840. GS_GUI_MOUSE_LEFT,
  6841. GS_GUI_SPLIT_RESIZE_INVALID,
  6842. GS_GUI_KEY_CTRL,
  6843. {
  6844. });
  6845. _gui_window_resize_ctrl(
  6846. gs_gui_get_id(ctx, "!split_se", 9),
  6847. 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),
  6848. GS_GUI_MOUSE_LEFT,
  6849. GS_GUI_SPLIT_RESIZE_INVALID,
  6850. GS_GUI_KEY_CTRL,
  6851. {
  6852. });
  6853. _gui_window_resize_ctrl(
  6854. gs_gui_get_id(ctx, "!split_ne", 9),
  6855. gs_gui_rect(cr->x + cr->w - cr->w * border_ratio, cr->y, cr->w * border_ratio, cr->h * border_ratio),
  6856. GS_GUI_MOUSE_LEFT,
  6857. GS_GUI_SPLIT_RESIZE_INVALID,
  6858. GS_GUI_KEY_CTRL,
  6859. {
  6860. });
  6861. _gui_window_resize_ctrl(
  6862. gs_gui_get_id(ctx, "!split_nw", 9),
  6863. gs_gui_rect(cr->x, cr->y, cr->w * border_ratio, cr->h * border_ratio),
  6864. GS_GUI_MOUSE_LEFT,
  6865. GS_GUI_SPLIT_RESIZE_INVALID,
  6866. GS_GUI_KEY_CTRL,
  6867. {
  6868. });
  6869. _gui_window_resize_ctrl(
  6870. gs_gui_get_id(ctx, "!split_sw", 9),
  6871. gs_gui_rect(cr->x, cr->y + cr->h - cr->h * border_ratio, cr->w * border_ratio, cr->h * border_ratio),
  6872. GS_GUI_MOUSE_LEFT,
  6873. GS_GUI_SPLIT_RESIZE_INVALID,
  6874. GS_GUI_KEY_CTRL,
  6875. {
  6876. });
  6877. }
  6878. _gui_window_resize_ctrl(
  6879. gs_gui_get_id(ctx, "!res_w", 6),
  6880. gs_gui_rect(r->x, r->y + r->h * border_ratio, r->w * border_ratio, r->h * (1.f - 2.f * border_ratio)),
  6881. GS_GUI_MOUSE_LEFT,
  6882. GS_GUI_SPLIT_RESIZE_W,
  6883. GS_GUI_KEY_ALT,
  6884. {
  6885. float w = r->w;
  6886. float max_x = r->x + r->w;
  6887. r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
  6888. if (fabsf(r->w - w) > 0.f)
  6889. {
  6890. r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
  6891. }
  6892. });
  6893. _gui_window_resize_ctrl(
  6894. gs_gui_get_id(ctx, "!res_e", 6),
  6895. 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)),
  6896. GS_GUI_MOUSE_LEFT,
  6897. GS_GUI_SPLIT_RESIZE_E,
  6898. GS_GUI_KEY_ALT,
  6899. {
  6900. r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
  6901. });
  6902. _gui_window_resize_ctrl(
  6903. gs_gui_get_id(ctx, "!res_n", 6),
  6904. gs_gui_rect(r->x + r->w * border_ratio, r->y, r->w * (1.f - 2.f * border_ratio), r->h * border_ratio),
  6905. GS_GUI_MOUSE_LEFT,
  6906. GS_GUI_SPLIT_RESIZE_N,
  6907. GS_GUI_KEY_ALT,
  6908. {
  6909. float h = r->h;
  6910. float max_y = h + r->y;
  6911. r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
  6912. if (fabsf(r->h - h) > 0.f)
  6913. {
  6914. r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
  6915. }
  6916. });
  6917. _gui_window_resize_ctrl(
  6918. gs_gui_get_id(ctx, "!res_s", 6),
  6919. 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),
  6920. GS_GUI_MOUSE_LEFT,
  6921. GS_GUI_SPLIT_RESIZE_S,
  6922. GS_GUI_KEY_ALT,
  6923. {
  6924. r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
  6925. });
  6926. _gui_window_resize_ctrl(
  6927. gs_gui_get_id(ctx, "!res_se", 7),
  6928. 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),
  6929. GS_GUI_MOUSE_LEFT,
  6930. GS_GUI_SPLIT_RESIZE_SE,
  6931. GS_GUI_KEY_ALT,
  6932. {
  6933. r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
  6934. r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
  6935. });
  6936. _gui_window_resize_ctrl(
  6937. gs_gui_get_id(ctx, "!res_ne", 7),
  6938. gs_gui_rect(r->x + r->w - r->w * border_ratio, r->y, r->w * border_ratio, r->h * border_ratio),
  6939. GS_GUI_MOUSE_LEFT,
  6940. GS_GUI_SPLIT_RESIZE_NE,
  6941. GS_GUI_KEY_ALT,
  6942. {
  6943. r->w = gs_max(r->w + ctx->mouse_delta.x, 40);
  6944. float h = r->h;
  6945. float max_y = h + r->y;
  6946. r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
  6947. if (fabsf(r->h - h) > 0.f)
  6948. {
  6949. r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
  6950. }
  6951. });
  6952. _gui_window_resize_ctrl(
  6953. gs_gui_get_id(ctx, "!res_nw", 7),
  6954. gs_gui_rect(r->x, r->y, r->w * border_ratio, r->h * border_ratio),
  6955. GS_GUI_MOUSE_LEFT,
  6956. GS_GUI_SPLIT_RESIZE_NW,
  6957. GS_GUI_KEY_ALT,
  6958. {
  6959. float h = r->h;
  6960. float max_y = h + r->y;
  6961. r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
  6962. if (fabsf(r->h - h) > 0.f)
  6963. {
  6964. r->y = gs_min(r->y + ctx->mouse_delta.y, max_y);
  6965. }
  6966. float w = r->w;
  6967. float max_x = r->x + r->w;
  6968. r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
  6969. if (fabsf(r->w - w) > 0.f)
  6970. {
  6971. r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
  6972. }
  6973. });
  6974. _gui_window_resize_ctrl(
  6975. gs_gui_get_id(ctx, "!res_sw", 7),
  6976. gs_gui_rect(r->x, r->y + r->h - r->h * border_ratio, r->w * border_ratio, r->h * border_ratio),
  6977. GS_GUI_MOUSE_LEFT,
  6978. GS_GUI_SPLIT_RESIZE_SW,
  6979. GS_GUI_KEY_ALT,
  6980. {
  6981. float h = r->h;
  6982. float max_y = h + r->y;
  6983. r->h = gs_max(r->h + ctx->mouse_delta.y, 40);
  6984. float w = r->w;
  6985. float max_x = r->x + r->w;
  6986. r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
  6987. if (fabsf(r->w - w) > 0.f)
  6988. {
  6989. r->x = gs_min(r->x + ctx->mouse_delta.x, max_x);
  6990. }
  6991. });
  6992. // move instead of resize?
  6993. _gui_window_resize_ctrl(
  6994. gs_gui_get_id(ctx, "!res_c", 6),
  6995. gs_gui_rect(r->x + r->w * border_ratio,
  6996. r->y + r->h * border_ratio,
  6997. r->w * border_ratio,
  6998. r->h * border_ratio),
  6999. GS_GUI_MOUSE_LEFT,
  7000. GS_GUI_SPLIT_MOVE,
  7001. GS_GUI_KEY_ALT,
  7002. {
  7003. ctx->next_focus_root = cnt;
  7004. r->x += ctx->mouse_delta.x;
  7005. r->y += ctx->mouse_delta.y;
  7006. });
  7007. static bool capture = false;
  7008. static gs_vec2 mp = {0};
  7009. static gs_gui_rect_t _rect = {0};
  7010. /*
  7011. _gui_window_resize_ctrl(
  7012. gs_gui_get_id(ctx, "!res_c", 5),
  7013. gs_gui_rect(r->x + r->w * border_ratio, r->y + r->h * border_ratio, r->w * border_ratio, r->h * border_ratio),
  7014. GS_GUI_SPLIT_RESIZE_CENTER,
  7015. {
  7016. if (!capture)
  7017. {
  7018. capture = true;
  7019. mp = ctx->mouse_pos;
  7020. _rect = *r;
  7021. }
  7022. // Grow based on dist from center
  7023. gs_vec2 c = gs_v2(r->x + r->w * 0.5f, r->y + r->h * 0.5f);
  7024. gs_vec2 a = gs_vec2_sub(c, mp);
  7025. gs_vec2 b = gs_vec2_sub(c, ctx->mouse_pos);
  7026. gs_vec2 na = gs_vec2_norm(a);
  7027. gs_vec2 nb = gs_vec2_norm(b);
  7028. float dist = gs_vec2_len(gs_vec2_sub(b, a));
  7029. float dot = gs_vec2_dot(na, nb);
  7030. gs_println("len: %.2f, dot: %.2f", dist, dot);
  7031. // Grow rect by dot product (scale dimensions)
  7032. float sign = dot >= 0.f ? 1.f : -1.f;
  7033. float factor = 1.f - dist / 1000.f;
  7034. r->w = _rect.w * factor * sign;
  7035. r->h = _rect.h * factor * sign;
  7036. // Equidistant resize from middle (grow rect based on delta)
  7037. float h = r->h;
  7038. float max_y = h + r->y;
  7039. r->h = gs_max(r->h - ctx->mouse_delta.y, 40);
  7040. if (fabsf(r->h - h) > 0.f)
  7041. {
  7042. r->y = gs_min(r->y - ctx->mouse_delta.y, max_y);
  7043. }
  7044. float w = r->w;
  7045. float max_x = r->x + r->w;
  7046. r->w = gs_max(r->w - ctx->mouse_delta.x, 40);
  7047. if (fabsf(r->w - w) > 0.f)
  7048. {
  7049. r->x = gs_min(r->x - ctx->mouse_delta.x, max_x);
  7050. }
  7051. });
  7052. */
  7053. if (ctx->mouse_down != GS_GUI_MOUSE_LEFT)
  7054. {
  7055. capture = false;
  7056. mp = gs_v2s(0.f);
  7057. }
  7058. }
  7059. // Determine if focus root in same tab group as current window for docking
  7060. bool can_dock = true;
  7061. if (cnt->tab_bar)
  7062. {
  7063. gs_gui_tab_bar_t* tab_bar = gs_slot_array_getp(ctx->tab_bars, cnt->tab_bar);
  7064. for (uint32_t t = 0; t < tab_bar->size; ++t)
  7065. {
  7066. if (tab_bar->items[t].data == ctx->focus_root)
  7067. {
  7068. can_dock = false;
  7069. }
  7070. }
  7071. }
  7072. // Do docking overlay (if enabled)
  7073. if (
  7074. can_dock &&
  7075. ~cnt->opt & GS_GUI_OPT_NODOCK &&
  7076. ctx->focus_root &&
  7077. ctx->focus_root != cnt &&
  7078. gs_gui_rect_overlaps_vec2(cnt->rect, ctx->mouse_pos) && // This is the incorrect part -
  7079. // need to check if this container isn't being overlapped by another
  7080. ctx->mouse_down == GS_GUI_MOUSE_LEFT &&
  7081. ~cnt->opt & GS_GUI_OPT_NOHOVER &&
  7082. cnt->flags & GS_GUI_WINDOW_FLAGS_VISIBLE)
  7083. {
  7084. gs_gui_split_t* focus_split = gs_gui_get_root_split(ctx, ctx->focus_root);
  7085. gs_gui_split_t* cnt_split = gs_gui_get_root_split(ctx, cnt);
  7086. // NOTE(john): this is incorrect...
  7087. if ((!focus_split && !cnt_split) || ((focus_split || cnt_split) && (focus_split != cnt_split)))
  7088. {
  7089. // Set dockable root container
  7090. ctx->dockable_root = ctx->dockable_root && cnt->zindex > ctx->dockable_root->zindex ?
  7091. cnt : ctx->dockable_root? ctx->dockable_root : cnt;
  7092. }
  7093. }
  7094. // Set current frame
  7095. cnt->frame = ctx->frame;
  7096. // Pop root container
  7097. gs_gui_root_container_end(ctx);
  7098. }
  7099. GS_API_DECL void
  7100. gs_gui_popup_open(gs_gui_context_t* ctx, const char* name)
  7101. {
  7102. char id_tag[256] = gs_default_val();
  7103. gs_gui_parse_id_tag(ctx, name, id_tag, sizeof(id_tag), 0);
  7104. gs_gui_container_t *cnt = gs_gui_get_container(ctx, *id_tag ? id_tag : name);
  7105. // Set as hover root so popup isn't closed in window_begin_ex()
  7106. ctx->hover_root = ctx->next_hover_root = cnt;
  7107. // position at mouse cursor, open and bring-to-front
  7108. cnt->rect = gs_gui_rect(ctx->mouse_pos.x, ctx->mouse_pos.y, 100, 100);
  7109. cnt->open = 1;
  7110. gs_gui_bring_to_front(ctx, cnt);
  7111. }
  7112. GS_API_DECL int32_t
  7113. gs_gui_popup_begin_ex(gs_gui_context_t* ctx, const char* name, gs_gui_rect_t r,
  7114. const gs_gui_selector_desc_t* desc, uint64_t opt)
  7115. {
  7116. opt |= (GS_GUI_OPT_POPUP | GS_GUI_OPT_NODOCK | GS_GUI_OPT_CLOSED);
  7117. return gs_gui_window_begin_ex(ctx, name, r, NULL, NULL, opt);
  7118. }
  7119. GS_API_DECL void
  7120. gs_gui_popup_end(gs_gui_context_t *ctx)
  7121. {
  7122. gs_gui_window_end(ctx);
  7123. }
  7124. GS_API_DECL void
  7125. gs_gui_panel_begin_ex(gs_gui_context_t* ctx, const char* name, const gs_gui_selector_desc_t* desc, uint64_t opt)
  7126. {
  7127. gs_gui_container_t *cnt;
  7128. const int32_t elementid = GS_GUI_ELEMENT_PANEL;
  7129. char id_tag[256] = gs_default_val();
  7130. gs_gui_parse_id_tag(ctx, name, id_tag, sizeof(id_tag), opt);
  7131. // if (id_tag) gs_gui_push_id(ctx, id_tag, strlen(id_tag));
  7132. // else gs_gui_push_id(ctx, name, strlen(name));
  7133. gs_gui_push_id(ctx, name, strlen(name));
  7134. cnt = gs_gui_get_container_ex(ctx, ctx->last_id, opt);
  7135. cnt->rect = gs_gui_layout_next(ctx);
  7136. const gs_gui_id id = gs_gui_get_id(ctx, name, strlen(name));
  7137. gs_gui_style_t style = gs_default_val();
  7138. gs_gui_animation_t* anim = gs_gui_get_animation(ctx, id, desc, elementid);
  7139. // Update anim (keep states locally within animation, only way to do this)
  7140. if (anim)
  7141. {
  7142. gs_gui_animation_update(ctx, anim);
  7143. // Get blended style based on animation
  7144. style = gs_gui_animation_get_blend_style(ctx, anim, desc, elementid);
  7145. }
  7146. else
  7147. {
  7148. style = ctx->focus == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x02) :
  7149. ctx->hover == id ? gs_gui_get_current_element_style(ctx, desc, elementid, 0x01) :
  7150. gs_gui_get_current_element_style(ctx, desc, elementid, 0x00);
  7151. }
  7152. if (~opt & GS_GUI_OPT_NOFRAME) {
  7153. gs_gui_draw_frame(ctx, cnt->rect, &style);
  7154. }
  7155. // Need a way to push/pop syles temp styles
  7156. gs_gui_stack_push(ctx->container_stack, cnt);
  7157. gs_gui_push_container_body(ctx, cnt, cnt->rect, desc, opt);
  7158. gs_gui_push_clip_rect(ctx, cnt->body);
  7159. }
  7160. GS_API_DECL void
  7161. gs_gui_panel_end(gs_gui_context_t *ctx)
  7162. {
  7163. gs_gui_pop_clip_rect(ctx);
  7164. gs_gui_pop_container(ctx);
  7165. }
  7166. static uint8_t
  7167. uint8_slider(gs_gui_context_t *ctx, unsigned char *value, int low, int high, const gs_gui_selector_desc_t* desc, uint64_t opt)
  7168. {
  7169. static float tmp;
  7170. gs_gui_push_id(ctx, &value, sizeof(value));
  7171. tmp = (float)*value;
  7172. int res = gs_gui_slider_ex(ctx, &tmp, (gs_gui_real)low, (gs_gui_real)high, 0, "%.0f", desc, opt);
  7173. *value = (u8)tmp;
  7174. gs_gui_pop_id(ctx);
  7175. return res;
  7176. }
  7177. static int32_t
  7178. 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)
  7179. {
  7180. static float tmp;
  7181. gs_gui_push_id(ctx, &value, sizeof(value));
  7182. tmp = (float)*value;
  7183. int res = gs_gui_slider_ex(ctx, &tmp, (gs_gui_real)low, (gs_gui_real)high, 0, "%.0f", desc, opt);
  7184. *value = (int32_t)tmp;
  7185. gs_gui_pop_id(ctx);
  7186. return res;
  7187. }
  7188. static int16_t
  7189. 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)
  7190. {
  7191. static float tmp;
  7192. gs_gui_push_id(ctx, &value, sizeof(value));
  7193. tmp = (float)*value;
  7194. int res = gs_gui_slider_ex(ctx, &tmp, (gs_gui_real)low, (gs_gui_real)high, 0, "%.0f", desc, opt);
  7195. *value = (int16_t)tmp;
  7196. gs_gui_pop_id(ctx);
  7197. return res;
  7198. }
  7199. //=== Gizmo ===//
  7200. typedef struct
  7201. {
  7202. b32 hit;
  7203. gs_vec3 point;
  7204. } gs_gui_gizmo_line_intersection_result_t;
  7205. enum {
  7206. GS_GUI_AXIS_RIGHT = 0x01,
  7207. GS_GUI_AXIS_UP,
  7208. GS_GUI_AXIS_FORWARD
  7209. };
  7210. typedef struct {
  7211. struct {
  7212. gs_vqs model;
  7213. union {
  7214. gs_cylinder_t cylinder;
  7215. gs_plane_t plane;
  7216. } shape;
  7217. } axis;
  7218. struct {
  7219. gs_vqs model;
  7220. union {
  7221. gs_cone_t cone;
  7222. gs_aabb_t aabb;
  7223. } shape;
  7224. } cap;
  7225. } gs_gui_axis_t;
  7226. typedef struct {
  7227. gs_gui_axis_t right;
  7228. gs_gui_axis_t up;
  7229. gs_gui_axis_t forward;
  7230. } gs_gizmo_translate_t;
  7231. typedef struct {
  7232. gs_gui_axis_t right;
  7233. gs_gui_axis_t up;
  7234. gs_gui_axis_t forward;
  7235. } gs_gizmo_scale_t;
  7236. typedef struct {
  7237. gs_gui_axis_t right;
  7238. gs_gui_axis_t up;
  7239. gs_gui_axis_t forward;
  7240. } gs_gizmo_rotate_t;
  7241. static gs_gizmo_scale_t
  7242. gs_gizmo_scale(const gs_vqs* parent)
  7243. {
  7244. gs_gizmo_scale_t gizmo = gs_default_val();
  7245. const gs_vec3 ax_scl = gs_v3(0.03f, 1.f, 0.03f);
  7246. const gs_vec3 cap_scl = gs_vec3_scale(gs_v3s(0.05f), 2.f * gs_vec3_len(parent->scale));
  7247. gs_vqs local = gs_vqs_default();
  7248. gs_vqs abs = gs_vqs_default();
  7249. #define GS_GUI_GIZMO_AXIS_DEFINE_SCALE(MEMBER, OFFSET, DEG, AXIS)\
  7250. do {\
  7251. /* Axis */\
  7252. {\
  7253. local = gs_vqs_ctor(\
  7254. OFFSET,\
  7255. gs_quat_angle_axis(gs_deg2rad(DEG), AXIS),\
  7256. ax_scl\
  7257. );\
  7258. gizmo.MEMBER.axis.model = gs_vqs_absolute_transform(&local, parent);\
  7259. gs_cylinder_t axis = gs_default_val();\
  7260. axis.r = 1.f;\
  7261. axis.base = gs_v3(0.f, 0.0f, 0.f);\
  7262. axis.height = 1.f;\
  7263. gizmo.MEMBER.axis.shape.cylinder = axis;\
  7264. }\
  7265. \
  7266. /* Cap */\
  7267. {\
  7268. local = gs_vqs_ctor(\
  7269. gs_v3(0.f, 0.5f, 0.f),\
  7270. gs_quat_default(),\
  7271. gs_v3s(1.f)\
  7272. );\
  7273. \
  7274. gizmo.MEMBER.cap.model = gs_vqs_absolute_transform(&local, &gizmo.MEMBER.axis.model);\
  7275. gizmo.MEMBER.cap.model.scale = cap_scl;\
  7276. gs_aabb_t aabb = gs_default_val();\
  7277. aabb.min = gs_v3s(-0.5f);\
  7278. aabb.max = gs_v3s(0.5f);\
  7279. gizmo.MEMBER.cap.shape.aabb = aabb;\
  7280. }\
  7281. } while (0)
  7282. const float off = 0.6f;
  7283. GS_GUI_GIZMO_AXIS_DEFINE_SCALE(right, gs_v3(-off, 0.f, 0.f), 90.f, GS_ZAXIS);
  7284. GS_GUI_GIZMO_AXIS_DEFINE_SCALE(up, gs_v3(0.f, off, 0.f), 0.f, GS_YAXIS);
  7285. GS_GUI_GIZMO_AXIS_DEFINE_SCALE(forward, gs_v3(0.f, 0.f, off), 90.f, GS_XAXIS);
  7286. return gizmo;
  7287. }
  7288. static gs_gizmo_translate_t
  7289. gs_gizmo_translate(const gs_vqs* parent)
  7290. {
  7291. gs_gizmo_translate_t trans = gs_default_val();
  7292. const gs_vec3 ax_scl = gs_v3(0.03f, 1.f, 0.03f);
  7293. const gs_vec3 cap_scl = gs_vec3_scale(gs_v3(0.02f, 0.05f, 0.02f), 2.f * gs_vec3_len(parent->scale));
  7294. gs_vqs local = gs_vqs_default();
  7295. gs_vqs abs = gs_vqs_default();
  7296. #define GS_GUI_GIZMO_AXIS_DEFINE_TRANSLATE(MEMBER, OFFSET, DEG, AXIS)\
  7297. do {\
  7298. /* Axis */\
  7299. {\
  7300. local = gs_vqs_ctor(\
  7301. OFFSET,\
  7302. gs_quat_angle_axis(gs_deg2rad(DEG), AXIS),\
  7303. ax_scl\
  7304. );\
  7305. trans.MEMBER.axis.model = gs_vqs_absolute_transform(&local, parent);\
  7306. gs_cylinder_t axis = gs_default_val();\
  7307. axis.r = 1.f;\
  7308. axis.base = gs_v3(0.f, 0.0f, 0.f);\
  7309. axis.height = 1.f;\
  7310. trans.MEMBER.axis.shape.cylinder = axis;\
  7311. }\
  7312. \
  7313. /* Cap */\
  7314. {\
  7315. local = gs_vqs_ctor(\
  7316. gs_v3(0.f, 0.5f, 0.f),\
  7317. gs_quat_default(),\
  7318. gs_v3s(1.f)\
  7319. );\
  7320. \
  7321. trans.MEMBER.cap.model = gs_vqs_absolute_transform(&local, &trans.MEMBER.axis.model);\
  7322. trans.MEMBER.cap.model.scale = cap_scl;\
  7323. gs_cone_t cap = gs_default_val();\
  7324. cap.r = 1.f;\
  7325. cap.base = gs_v3(0.f, 0.0f, 0.f);\
  7326. cap.height = 1.0f;\
  7327. trans.MEMBER.cap.shape.cone = cap;\
  7328. }\
  7329. } while (0)
  7330. const float off = 0.6f;
  7331. GS_GUI_GIZMO_AXIS_DEFINE_TRANSLATE(right, gs_v3(-off, 0.f, 0.f), 90.f, GS_ZAXIS);
  7332. GS_GUI_GIZMO_AXIS_DEFINE_TRANSLATE(up, gs_v3(0.f, off, 0.f), 0.f, GS_YAXIS);
  7333. GS_GUI_GIZMO_AXIS_DEFINE_TRANSLATE(forward, gs_v3(0.f, 0.f, off), 90.f, GS_XAXIS);
  7334. return trans;
  7335. }
  7336. static gs_gizmo_rotate_t
  7337. gs_gizmo_rotate(const gs_vqs* parent)
  7338. {
  7339. gs_gizmo_rotate_t gizmo = gs_default_val();
  7340. const gs_vec3 ax_scl = gs_v3(1.f, 1.f, 1.f);
  7341. gs_vqs local = gs_vqs_default();
  7342. gs_vqs abs = gs_vqs_default();
  7343. #define GS_GUI_GIZMO_AXIS_DEFINE_ROTATE(MEMBER, OFFSET, DEG, AXIS)\
  7344. do {\
  7345. /* Axis */\
  7346. {\
  7347. local = gs_vqs_ctor(\
  7348. OFFSET,\
  7349. gs_quat_angle_axis(gs_deg2rad(DEG), AXIS),\
  7350. ax_scl\
  7351. );\
  7352. gizmo.MEMBER.axis.model = gs_vqs_absolute_transform(&local, parent);\
  7353. gs_plane_t axis = gs_plane_from_pt_normal(gs_v3s(0.f), GS_ZAXIS);\
  7354. gizmo.MEMBER.axis.shape.plane = axis;\
  7355. }\
  7356. } while (0)
  7357. GS_GUI_GIZMO_AXIS_DEFINE_ROTATE(right, gs_v3(0.f, 0.f, 0.f), 90.f, GS_YAXIS);
  7358. GS_GUI_GIZMO_AXIS_DEFINE_ROTATE(up, gs_v3(0.f, 0.f, 0.f), -90.f, GS_XAXIS);
  7359. GS_GUI_GIZMO_AXIS_DEFINE_ROTATE(forward, gs_v3(0.f, 0.f, 0.f), 0.f, GS_ZAXIS);
  7360. return gizmo;
  7361. }
  7362. typedef struct {
  7363. int32_t op;
  7364. int32_t mode;
  7365. gs_vqs xform;
  7366. gs_contact_info_t info;
  7367. union {
  7368. gs_gizmo_translate_t translate;
  7369. gs_gizmo_scale_t scale;
  7370. gs_gizmo_rotate_t rotate;
  7371. } gizmo;
  7372. int16_t hover;
  7373. gs_camera_t camera;
  7374. gs_gui_rect_t viewport;
  7375. gs_gui_gizmo_line_intersection_result_t li;
  7376. } gs_gizmo_desc_t;
  7377. static void
  7378. gs_gui_gizmo_render(gs_gui_context_t* ctx, gs_gui_customcommand_t* cmd)
  7379. {
  7380. const gs_vec2 fbs = ctx->framebuffer_size;
  7381. const float t = gs_platform_elapsed_time();
  7382. gs_immediate_draw_t* gsi = &ctx->gsi;
  7383. gs_gizmo_desc_t* desc = (gs_gizmo_desc_t*)cmd->data;
  7384. gs_gui_rect_t clip = cmd->clip;
  7385. gs_gui_rect_t viewport = desc->viewport;
  7386. gs_camera_t cam = desc->camera;
  7387. const uint16_t segments = 4;
  7388. const gs_gui_gizmo_line_intersection_result_t* li = &desc->li;
  7389. const gs_contact_info_t* info = &desc->info;
  7390. const uint8_t alpha = 150;
  7391. gsi_defaults(gsi);
  7392. gsi_depth_enabled(gsi, false);
  7393. gsi_camera(gsi, &cam, (uint32_t)viewport.w, (uint32_t)viewport.h);
  7394. gs_graphics_set_viewport(&gsi->commands, (u32)viewport.x, (u32)(fbs.y - viewport.h - viewport.y),
  7395. (u32)viewport.w, (u32)viewport.h);
  7396. gs_graphics_primitive_type primitive = GS_GRAPHICS_PRIMITIVE_TRIANGLES;
  7397. #define GS_GUI_GIZMO_AXIS_TRANSLATE(ID, AXIS, COLOR)\
  7398. do {\
  7399. gs_gui_id id = gs_gui_get_id_hash(ctx, ID, strlen(ID), cmd->hash);\
  7400. bool hover = cmd->hover == id;\
  7401. bool focus = cmd->focus == id;\
  7402. gs_color_t color = hover || focus ? GS_COLOR_YELLOW : COLOR;\
  7403. /* Axis */\
  7404. {\
  7405. gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
  7406. gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.translate.AXIS.axis.model));\
  7407. {\
  7408. gs_cylinder_t* axis = &desc->gizmo.translate.AXIS.axis.shape.cylinder;\
  7409. gsi_cylinder(gsi, axis->base.x, axis->base.y, axis->base.z, axis->r,\
  7410. axis->r, axis->height, segments, color.r, color.g, color.b, alpha, primitive);\
  7411. }\
  7412. gsi_pop_matrix(gsi);\
  7413. }\
  7414. \
  7415. /* Cap */\
  7416. {\
  7417. gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
  7418. gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.translate.AXIS.cap.model));\
  7419. {\
  7420. gs_cone_t* cap = &desc->gizmo.translate.AXIS.cap.shape.cone;\
  7421. 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);\
  7422. }\
  7423. gsi_pop_matrix(gsi);\
  7424. }\
  7425. } while (0)
  7426. #define GS_GUI_GIZMO_AXIS_SCALE(ID, AXIS, COLOR)\
  7427. do {\
  7428. gs_gui_id id = gs_gui_get_id_hash(ctx, ID, strlen(ID), cmd->hash);\
  7429. bool hover = cmd->hover == id;\
  7430. bool focus = cmd->focus == id;\
  7431. gs_color_t color = hover || focus ? GS_COLOR_YELLOW : COLOR;\
  7432. /* Axis */\
  7433. {\
  7434. gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
  7435. gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.scale.AXIS.axis.model));\
  7436. {\
  7437. gs_cylinder_t* axis = &desc->gizmo.scale.AXIS.axis.shape.cylinder;\
  7438. gsi_cylinder(gsi, axis->base.x, axis->base.y, axis->base.z, axis->r,\
  7439. axis->r, axis->height, segments, color.r, color.g, color.b, alpha, primitive);\
  7440. }\
  7441. gsi_pop_matrix(gsi);\
  7442. }\
  7443. \
  7444. /* Cap */\
  7445. {\
  7446. gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
  7447. gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.scale.AXIS.cap.model));\
  7448. {\
  7449. gs_aabb_t* cap = &desc->gizmo.scale.AXIS.cap.shape.aabb;\
  7450. gs_vec3 hd = gs_vec3_scale(gs_vec3_sub(cap->max, cap->min), 0.5f);\
  7451. gs_vec3 c = gs_vec3_add(cap->min, hd);\
  7452. gsi_box(gsi, c.x, c.y, c.z, hd.x, hd.y, hd.z, color.r, color.g, color.b, alpha, primitive);\
  7453. }\
  7454. gsi_pop_matrix(gsi);\
  7455. }\
  7456. } while (0)
  7457. #define GS_GUI_GIZMO_AXIS_ROTATE(ID, AXIS, COLOR)\
  7458. do {\
  7459. gs_color_t def_color = (COLOR);\
  7460. gs_gui_id id = gs_gui_get_id_hash(ctx, ID, strlen(ID), cmd->hash);\
  7461. bool hover = cmd->hover == id;\
  7462. bool focus = cmd->focus == id;\
  7463. gs_color_t color = hover || focus ? GS_COLOR_YELLOW : def_color;\
  7464. /* Axis */\
  7465. {\
  7466. gsi_push_matrix(gsi, GSI_MATRIX_MODELVIEW);\
  7467. gsi_mul_matrix(gsi, gs_vqs_to_mat4(&desc->gizmo.rotate.AXIS.axis.model));\
  7468. {\
  7469. gs_plane_t* axis = &desc->gizmo.rotate.AXIS.axis.shape.plane;\
  7470. 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);\
  7471. }\
  7472. gsi_pop_matrix(gsi);\
  7473. if (focus) {\
  7474. gs_vec3 ls = desc->xform.translation;\
  7475. gs_vec3 le = gs_vec3_add(ls, gs_vec3_scale(info->normal, 0.5f));\
  7476. gsi_line3Dv(gsi, ls, le, GS_COLOR_BLUE);\
  7477. }\
  7478. }\
  7479. } while (0)
  7480. switch (desc->op)
  7481. {
  7482. case GS_GUI_GIZMO_TRANSLATE:
  7483. {
  7484. GS_GUI_GIZMO_AXIS_TRANSLATE("#gizmo_trans_right", right, GS_COLOR_RED);
  7485. GS_GUI_GIZMO_AXIS_TRANSLATE("#gizmo_trans_up", up, GS_COLOR_GREEN);
  7486. GS_GUI_GIZMO_AXIS_TRANSLATE("#gizmo_trans_forward", forward, GS_COLOR_BLUE);
  7487. } break;
  7488. case GS_GUI_GIZMO_SCALE:
  7489. {
  7490. GS_GUI_GIZMO_AXIS_SCALE("#gizmo_scale_right", right, GS_COLOR_RED);
  7491. GS_GUI_GIZMO_AXIS_SCALE("#gizmo_scale_up", up, GS_COLOR_GREEN);
  7492. GS_GUI_GIZMO_AXIS_SCALE("#gizmo_scale_forward", forward, GS_COLOR_BLUE);
  7493. } break;
  7494. case GS_GUI_GIZMO_ROTATE:
  7495. {
  7496. gs_gui_id id_r = gs_gui_get_id_hash(ctx, "#gizmo_rotate_right", strlen("#gizmo_rotate_right"), cmd->hash);
  7497. gs_gui_id id_u = gs_gui_get_id_hash(ctx, "#gizmo_rotate_up", strlen("#gizmo_rotate_up"), cmd->hash);
  7498. gs_gui_id id_f = gs_gui_get_id_hash(ctx, "#gizmo_rotate_forward", strlen("#gizmo_rotate_forward"), cmd->hash);
  7499. GS_GUI_GIZMO_AXIS_ROTATE("#gizmo_rotate_right", right,
  7500. (cmd->focus == id_u || cmd->focus == id_f) ? gs_color_alpha(GS_COLOR_RED, 25) :
  7501. gs_color_alpha(GS_COLOR_RED, alpha));
  7502. GS_GUI_GIZMO_AXIS_ROTATE("#gizmo_rotate_up", up,
  7503. (cmd->focus == id_r || cmd->focus == id_f) ? gs_color_alpha(GS_COLOR_GREEN, 25) :
  7504. gs_color_alpha(GS_COLOR_GREEN, alpha));
  7505. GS_GUI_GIZMO_AXIS_ROTATE("#gizmo_rotate_forward", forward,
  7506. (cmd->focus == id_r || cmd->focus == id_u) ? gs_color_alpha(GS_COLOR_BLUE, 25) :
  7507. gs_color_alpha(GS_COLOR_BLUE, alpha));
  7508. } break;
  7509. }
  7510. if (li->hit)
  7511. {
  7512. gsi_sphere(gsi, li->point.x, li->point.y, li->point.z, 0.005f, 255, 0, 0, 255, GS_GRAPHICS_PRIMITIVE_LINES);
  7513. gsi_line3Dv(gsi, li->point, desc->xform.translation, gs_color(255, 0, 0, 255));
  7514. }
  7515. }
  7516. static gs_gui_gizmo_line_intersection_result_t
  7517. gs_gui_gizmo_get_line_intersection(const gs_vqs* model, gs_vec3 axis_a, gs_vec3 axis_b, gs_vec3 axis_c,
  7518. const gs_camera_t* camera, const gs_ray_t* ray, gs_vec3 plane_normal_axis, bool compare_supporting_axes, bool override_axis)
  7519. {
  7520. gs_gui_gizmo_line_intersection_result_t res = gs_default_val();
  7521. // Find absolute dot between cam forward and right axis
  7522. gs_vec3 cf = gs_vec3_norm(gs_camera_forward(camera));
  7523. gs_vec3 ta = gs_vec3_norm(gs_quat_rotate(model->rotation, axis_a));
  7524. float cfdta = fabsf(gs_vec3_dot(cf, ta));
  7525. // This doesn't really make sense. I want to project along the x/y or x/z planes.
  7526. gs_plane_t intersection_plane = gs_default_val();
  7527. gs_vec3 op = model->translation;
  7528. if (compare_supporting_axes)
  7529. {
  7530. // Now determine appropriate axis to move along
  7531. gs_vec3 tb = gs_vec3_norm(gs_quat_rotate(model->rotation, axis_b));
  7532. gs_vec3 tc = gs_vec3_norm(gs_quat_rotate(model->rotation, axis_c));
  7533. float cfdtb = fabsf(gs_vec3_dot(cf, tb));
  7534. float cfdtc = fabsf(gs_vec3_dot(cf, tc));
  7535. intersection_plane = cfdtb < cfdtc ? gs_plane_from_pt_normal(op, tc) :
  7536. gs_plane_from_pt_normal(op, tb);
  7537. }
  7538. else
  7539. {
  7540. if (override_axis)
  7541. {
  7542. intersection_plane = gs_plane_from_pt_normal(op, plane_normal_axis);
  7543. }
  7544. else
  7545. {
  7546. intersection_plane = gs_plane_from_pt_normal(op, ta);
  7547. }
  7548. }
  7549. // Get line intersection from ray and chosen intersection plane
  7550. gs_plane_t* ip = &intersection_plane;
  7551. float denom = gs_vec3_dot(gs_v3(ip->a, ip->b, ip->c), ray->d);
  7552. if (fabsf(denom) >= GS_EPSILON)
  7553. {
  7554. float t = -(ip->a * ray->p.x + ip->b * ray->p.y + ip->c * ray->p.z + ip->d) / denom;
  7555. res.hit = t >= 0.f ? true : false;
  7556. res.point = gs_vec3_add(ray->p, gs_vec3_scale(ray->d, t));
  7557. }
  7558. return res;
  7559. }
  7560. static gs_vec3 s_intersection_start = gs_default_val();
  7561. static gs_vqs s_delta = gs_default_val();
  7562. static bool just_set_focus = false;
  7563. GS_API_DECL int32_t
  7564. gs_gui_gizmo(gs_gui_context_t* ctx, gs_camera_t* camera, gs_vqs* model, gs_gui_rect_t viewport,
  7565. bool invert_view_y, float snap, int32_t op, int32_t mode, uint64_t opt)
  7566. {
  7567. int32_t res = 0;
  7568. if (model->rotation.w == 0.f) model->rotation = gs_quat_default();
  7569. if (gs_vec3_len(model->scale) == 0.f) model->scale = gs_v3s(1.f);
  7570. const gs_vec2 fbs = ctx->framebuffer_size;
  7571. const float t = gs_platform_elapsed_time();
  7572. const bool in_hover_root = gs_gui_in_hover_root(ctx);
  7573. gs_immediate_draw_t* dl = &ctx->overlay_draw_list;
  7574. // This doesn't actually work for the clip...
  7575. gs_gui_rect_t clip = viewport;
  7576. if (invert_view_y) {
  7577. clip.y = fbs.y - clip.h - clip.y;
  7578. }
  7579. // Capture event information (might have to do something like this for smaller framebuffers scaled to higher viewports)
  7580. /*
  7581. gs_vec2 mc = gs_platform_mouse_positionv();
  7582. // Check for scale and bias
  7583. int32_t flags = 0x00;
  7584. if (flags & GS_GUI_HINT_FLAG_NO_SCALE_BIAS_MOUSE) {
  7585. float px = (mc.x - clip.x) / clip.w;
  7586. float py = (mc.y - clip.y) / clip.h;
  7587. float xv = clip.w * px;
  7588. float yv = clip.h * py;
  7589. mc = gs_v2(xv, yv);
  7590. }
  7591. else {
  7592. gs_vec2 fb_vp_ratio = gs_v2(fbs.x / clip.w, fbs.y / clip.h);
  7593. float px = mc.x - (clip.x * fb_vp_ratio.x);
  7594. float py = mc.y + (clip.y * fb_vp_ratio.y);
  7595. float xv = px / fb_vp_ratio.x;
  7596. float yv = py / fb_vp_ratio.y;
  7597. mc = gs_v2(xv, yv);
  7598. }
  7599. */
  7600. gs_vec2 mc = gs_platform_mouse_positionv();
  7601. mc = gs_vec2_sub(mc, gs_v2(clip.x, clip.y));
  7602. // Project ray to world
  7603. const float ray_len = 1000.f;
  7604. gs_vec3 ms = gs_v3(mc.x, mc.y, 0.f);
  7605. gs_vec3 me = gs_v3(mc.x, mc.y, -ray_len);
  7606. gs_vec3 ro = gs_camera_screen_to_world(camera, ms, 0, 0, (int32_t)clip.w, (int32_t)clip.h);
  7607. gs_vec3 rd = gs_camera_screen_to_world(camera, me, 0, 0, (int32_t)clip.w, (int32_t)clip.h);
  7608. rd = gs_vec3_norm(gs_vec3_sub(ro, rd));
  7609. gs_ray_t ray = gs_default_val();
  7610. ray.p = ro;
  7611. ray.d = rd;
  7612. ray.len = ray_len;
  7613. // Check for nan
  7614. if (gs_vec3_nan(ray.p)) ray.p = gs_v3s(0.f);
  7615. if (gs_vec3_nan(ray.d)) ray.d = gs_v3s(0.f);
  7616. gs_gizmo_desc_t desc = gs_default_val();
  7617. desc.op = op;
  7618. desc.mode = mode;
  7619. desc.camera = *camera;
  7620. desc.info.depth = FLT_MAX;
  7621. desc.viewport = clip;
  7622. desc.xform = gs_vqs_default();
  7623. desc.xform.translation = model->translation;
  7624. 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)
  7625. switch (camera->proj_type)
  7626. {
  7627. case GS_PROJECTION_TYPE_ORTHOGRAPHIC:
  7628. {
  7629. desc.xform.scale = gs_v3s(camera->ortho_scale * 0.2f);
  7630. } break;
  7631. case GS_PROJECTION_TYPE_PERSPECTIVE:
  7632. {
  7633. float dist_from_cam = gs_vec3_dist(desc.xform.translation, camera->transform.translation);
  7634. desc.xform.scale = gs_v3s(dist_from_cam * 0.3f);
  7635. } break;
  7636. }
  7637. #define UPDATE_GIZMO_CONTROL(ID, RAY, SHAPE, MODEL, FUNC, CAP_SHAPE, CAP_MODEL, CAP_FUNC, INFO)\
  7638. do {\
  7639. int32_t mouseover = 0;\
  7640. gs_gui_id id = (ID);\
  7641. gs_contact_info_t info0 = gs_default_val();\
  7642. gs_contact_info_t info1 = gs_default_val();\
  7643. if (in_hover_root) {\
  7644. FUNC(&(SHAPE), &(MODEL), &(RAY), NULL, &info0);\
  7645. info0.depth = gs_vec3_dist(info0.point, (RAY).p);\
  7646. CAP_FUNC(&(CAP_SHAPE), &(CAP_MODEL), &(RAY), NULL, &info1);\
  7647. info1.depth = gs_vec3_dist(info1.point, (RAY).p);\
  7648. }\
  7649. gs_contact_info_t* info = info0.depth < info1.depth ? &info0 : &info1;\
  7650. mouseover = info->hit && info->depth <= INFO.depth && in_hover_root && !ctx->hover_split && !ctx->lock_hover_id;\
  7651. if (ctx->focus == id) {ctx->updated_focus = 1;}\
  7652. if (~opt & GS_GUI_OPT_NOINTERACT) {\
  7653. /* Check for hold focus here */\
  7654. if (mouseover && !ctx->mouse_down) {\
  7655. gs_gui_set_hover(ctx, id);\
  7656. INFO = *info;\
  7657. }\
  7658. \
  7659. if (ctx->focus == id)\
  7660. {\
  7661. res |= GS_GUI_RES_ACTIVE;\
  7662. just_set_focus = false;\
  7663. gs_gui_set_focus(ctx, id);\
  7664. if (ctx->mouse_pressed && !mouseover) {gs_gui_set_focus(ctx, 0);}\
  7665. if (!ctx->mouse_down && ~opt & GS_GUI_OPT_HOLDFOCUS) {gs_gui_set_focus(ctx, 0);}\
  7666. }\
  7667. \
  7668. if (ctx->prev_hover == id && !mouseover) {ctx->prev_hover = ctx->hover;}\
  7669. \
  7670. if (ctx->hover == id)\
  7671. {\
  7672. if (ctx->mouse_pressed)\
  7673. {\
  7674. if ((opt & GS_GUI_OPT_LEFTCLICKONLY && ctx->mouse_pressed == GS_GUI_MOUSE_LEFT) || (~opt & GS_GUI_OPT_LEFTCLICKONLY))\
  7675. {\
  7676. gs_gui_set_focus(ctx, id);\
  7677. just_set_focus = true;\
  7678. }\
  7679. }\
  7680. else if (!mouseover)\
  7681. {\
  7682. gs_gui_set_hover(ctx, 0);\
  7683. }\
  7684. }\
  7685. }\
  7686. } while (0)
  7687. switch (op)
  7688. {
  7689. case GS_GUI_GIZMO_TRANSLATE:
  7690. {
  7691. // Construct translate gizmo for this frame based on given parent transform
  7692. desc.gizmo.translate = gs_gizmo_translate(&desc.xform);
  7693. gs_gui_id id_r = gs_gui_get_id(ctx, "#gizmo_trans_right", strlen("#gizmo_trans_right"));
  7694. gs_gui_id id_u = gs_gui_get_id(ctx, "#gizmo_trans_up", strlen("#gizmo_trans_up"));
  7695. gs_gui_id id_f = gs_gui_get_id(ctx, "#gizmo_trans_forward", strlen("#gizmo_trans_forward"));
  7696. // Right
  7697. UPDATE_GIZMO_CONTROL(id_r, ray,
  7698. desc.gizmo.translate.right.axis.shape.cylinder, desc.gizmo.translate.right.axis.model, gs_cylinder_vs_ray,
  7699. desc.gizmo.translate.right.cap.shape.cone, desc.gizmo.translate.right.cap.model, gs_cone_vs_ray,
  7700. desc.info);
  7701. // Up
  7702. UPDATE_GIZMO_CONTROL(id_u, ray,
  7703. desc.gizmo.translate.up.axis.shape.cylinder, desc.gizmo.translate.up.axis.model, gs_cylinder_vs_ray,
  7704. desc.gizmo.translate.up.cap.shape.cone, desc.gizmo.translate.up.cap.model, gs_cone_vs_ray,
  7705. desc.info);
  7706. // Forward
  7707. UPDATE_GIZMO_CONTROL(id_f, ray,
  7708. desc.gizmo.translate.forward.axis.shape.cylinder, desc.gizmo.translate.forward.axis.model, gs_cylinder_vs_ray,
  7709. desc.gizmo.translate.forward.cap.shape.cone, desc.gizmo.translate.forward.cap.model, gs_cone_vs_ray,
  7710. desc.info);
  7711. // Control
  7712. if (ctx->focus == id_r)
  7713. {
  7714. desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_XAXIS, GS_YAXIS, GS_ZAXIS,
  7715. camera, &ray, gs_v3s(0.f), true, false);
  7716. if (just_set_focus) {
  7717. s_intersection_start = desc.li.point;
  7718. memset(&s_delta, 0, sizeof(s_delta));
  7719. s_delta.rotation = gs_quat(
  7720. model->translation.x,
  7721. model->translation.y,
  7722. model->translation.z,
  7723. 0.f
  7724. );
  7725. }
  7726. if (desc.li.hit)
  7727. {
  7728. gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_XAXIS));
  7729. gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
  7730. float udotn = gs_vec3_dot(u, axis);
  7731. s_delta.translation = gs_vec3_scale(axis, udotn);
  7732. s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
  7733. if (gs_vec3_eq(axis, GS_XAXIS)) {
  7734. s_delta.translation.y = 0.f;
  7735. s_delta.translation.z = 0.f;
  7736. }
  7737. if (snap > 0.f)
  7738. {
  7739. s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
  7740. float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
  7741. gs_vec3 norm = gs_vec3_norm(s_delta.scale);
  7742. gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
  7743. gs_vec3 op = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
  7744. model->translation = gs_vec3_add(op, delta);
  7745. }
  7746. else
  7747. {
  7748. // Set final translation
  7749. model->translation = gs_vec3_add(desc.xform.translation, s_delta.translation);
  7750. }
  7751. }
  7752. }
  7753. else if (ctx->focus == id_u)
  7754. {
  7755. desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_YAXIS, GS_XAXIS, GS_ZAXIS,
  7756. camera, &ray, gs_v3s(0.f), true, false);
  7757. if (just_set_focus) {
  7758. s_intersection_start = desc.li.point;
  7759. memset(&s_delta, 0, sizeof(s_delta));
  7760. s_delta.rotation = gs_quat(
  7761. model->translation.x,
  7762. model->translation.y,
  7763. model->translation.z,
  7764. 0.f
  7765. );
  7766. }
  7767. if (desc.li.hit)
  7768. {
  7769. gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_YAXIS));
  7770. gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
  7771. float udotn = gs_vec3_dot(u, axis);
  7772. s_delta.translation = gs_vec3_scale(axis, udotn);
  7773. s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
  7774. if (gs_vec3_eq(axis, GS_YAXIS)) {
  7775. s_delta.translation.x = 0.f;
  7776. s_delta.translation.z = 0.f;
  7777. }
  7778. if (snap > 0.f)
  7779. {
  7780. s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
  7781. float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
  7782. gs_vec3 norm = gs_vec3_norm(s_delta.scale);
  7783. gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
  7784. gs_vec3 op = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
  7785. model->translation = gs_vec3_add(op, delta);
  7786. }
  7787. else
  7788. {
  7789. // Set final translation
  7790. model->translation = gs_vec3_add(desc.xform.translation, s_delta.translation);
  7791. }
  7792. }
  7793. }
  7794. else if (ctx->focus == id_f)
  7795. {
  7796. desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_ZAXIS, GS_XAXIS, GS_YAXIS,
  7797. camera, &ray, gs_v3s(0.f), true, false);
  7798. if (just_set_focus) {
  7799. s_intersection_start = desc.li.point;
  7800. memset(&s_delta, 0, sizeof(s_delta));
  7801. s_delta.rotation = gs_quat(
  7802. model->translation.x,
  7803. model->translation.y,
  7804. model->translation.z,
  7805. 0.f
  7806. );
  7807. }
  7808. if (desc.li.hit)
  7809. {
  7810. gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_ZAXIS));
  7811. gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
  7812. float udotn = gs_vec3_dot(u, axis);
  7813. s_delta.translation = gs_vec3_scale(axis, udotn);
  7814. s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
  7815. if (gs_vec3_eq(axis, GS_ZAXIS)) {
  7816. s_delta.translation.x = 0.f;
  7817. s_delta.translation.y = 0.f;
  7818. }
  7819. if (snap > 0.f)
  7820. {
  7821. s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
  7822. float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
  7823. gs_vec3 norm = gs_vec3_norm(s_delta.scale);
  7824. gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
  7825. gs_vec3 op = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
  7826. model->translation = gs_vec3_add(op, delta);
  7827. }
  7828. else
  7829. {
  7830. // Set final translation
  7831. model->translation = gs_vec3_add(desc.xform.translation, s_delta.translation);
  7832. }
  7833. }
  7834. }
  7835. } break;
  7836. case GS_GUI_GIZMO_SCALE:
  7837. {
  7838. // Construct translate gizmo for this frame based on given parent transform
  7839. desc.gizmo.scale = gs_gizmo_scale(&desc.xform);
  7840. gs_gui_id id_r = gs_gui_get_id(ctx, "#gizmo_scale_right", strlen("#gizmo_scale_right"));
  7841. gs_gui_id id_u = gs_gui_get_id(ctx, "#gizmo_scale_up", strlen("#gizmo_scale_up"));
  7842. gs_gui_id id_f = gs_gui_get_id(ctx, "#gizmo_scale_forward", strlen("#gizmo_scale_forward"));
  7843. // Right
  7844. UPDATE_GIZMO_CONTROL(id_r, ray,
  7845. desc.gizmo.scale.right.axis.shape.cylinder, desc.gizmo.scale.right.axis.model, gs_cylinder_vs_ray,
  7846. desc.gizmo.scale.right.cap.shape.aabb, desc.gizmo.scale.right.cap.model, gs_aabb_vs_ray,
  7847. desc.info);
  7848. // Up
  7849. UPDATE_GIZMO_CONTROL(id_u, ray,
  7850. desc.gizmo.scale.up.axis.shape.cylinder, desc.gizmo.scale.up.axis.model, gs_cylinder_vs_ray,
  7851. desc.gizmo.scale.up.cap.shape.aabb, desc.gizmo.scale.up.cap.model, gs_aabb_vs_ray,
  7852. desc.info);
  7853. // Forward
  7854. UPDATE_GIZMO_CONTROL(id_f, ray,
  7855. desc.gizmo.scale.forward.axis.shape.cylinder, desc.gizmo.scale.forward.axis.model, gs_cylinder_vs_ray,
  7856. desc.gizmo.scale.forward.cap.shape.aabb, desc.gizmo.scale.forward.cap.model, gs_aabb_vs_ray,
  7857. desc.info);
  7858. // Control
  7859. if (ctx->focus == id_r)
  7860. {
  7861. desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_XAXIS, GS_YAXIS, GS_ZAXIS,
  7862. camera, &ray, gs_v3s(0.f), true, false);
  7863. if (desc.li.hit)
  7864. {
  7865. if (just_set_focus) {
  7866. s_intersection_start = desc.li.point;
  7867. memset(&s_delta, 0, sizeof(s_delta));
  7868. s_delta.rotation = gs_quat(
  7869. model->scale.x,
  7870. model->scale.y,
  7871. model->scale.z,
  7872. 0.f
  7873. );
  7874. }
  7875. gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_XAXIS));
  7876. gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
  7877. float udotn = gs_vec3_dot(u, axis);
  7878. float neg = gs_vec3_dot(axis, GS_XAXIS) < 0.f ? 1.f : -1.f;
  7879. s_delta.translation = gs_vec3_scale(axis, udotn);
  7880. s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
  7881. s_delta.translation = gs_vec3_scale(s_delta.translation, neg);
  7882. s_delta.translation.z = 0.f;
  7883. s_delta.translation.y = 0.f;
  7884. if (snap > 0.f)
  7885. {
  7886. s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
  7887. float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
  7888. gs_vec3 norm = gs_vec3_norm(s_delta.scale);
  7889. gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
  7890. gs_vec3 os = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
  7891. model->scale = gs_vec3_add(os, delta);
  7892. }
  7893. else
  7894. {
  7895. model->scale = gs_vec3_add(model->scale, s_delta.translation);
  7896. }
  7897. }
  7898. }
  7899. else if (ctx->focus == id_u)
  7900. {
  7901. desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_YAXIS, GS_XAXIS, GS_ZAXIS,
  7902. camera, &ray, gs_v3s(0.f), true, false);
  7903. if (desc.li.hit)
  7904. {
  7905. if (just_set_focus) {
  7906. s_intersection_start = desc.li.point;
  7907. memset(&s_delta, 0, sizeof(s_delta));
  7908. s_delta.rotation = gs_quat(
  7909. model->scale.x,
  7910. model->scale.y,
  7911. model->scale.z,
  7912. 0.f
  7913. );
  7914. }
  7915. gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_YAXIS));
  7916. gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
  7917. float udotn = gs_vec3_dot(u, axis);
  7918. s_delta.translation = gs_vec3_scale(axis, udotn);
  7919. float neg = gs_vec3_dot(axis, GS_YAXIS) < 0.f ? -1.f : 1.f;
  7920. s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
  7921. s_delta.translation = gs_vec3_scale(s_delta.translation, neg);
  7922. s_delta.translation.z = 0.f;
  7923. s_delta.translation.x = 0.f;
  7924. if (snap > 0.f)
  7925. {
  7926. s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
  7927. float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
  7928. gs_vec3 norm = gs_vec3_norm(s_delta.scale);
  7929. gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
  7930. gs_vec3 os = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
  7931. model->scale = gs_vec3_add(os, delta);
  7932. }
  7933. else
  7934. {
  7935. model->scale = gs_vec3_add(model->scale, s_delta.translation);
  7936. }
  7937. }
  7938. }
  7939. else if (ctx->focus == id_f)
  7940. {
  7941. desc.li = gs_gui_gizmo_get_line_intersection(&desc.xform, GS_ZAXIS, GS_XAXIS, GS_YAXIS,
  7942. camera, &ray, gs_v3s(0.f), true, false);
  7943. if (desc.li.hit)
  7944. {
  7945. if (just_set_focus) {
  7946. s_intersection_start = desc.li.point;
  7947. memset(&s_delta, 0, sizeof(s_delta));
  7948. s_delta.rotation = gs_quat(
  7949. model->scale.x,
  7950. model->scale.y,
  7951. model->scale.z,
  7952. 0.f
  7953. );
  7954. }
  7955. gs_vec3 axis = gs_vec3_norm(gs_quat_rotate(desc.xform.rotation, GS_ZAXIS));
  7956. gs_vec3 u = gs_vec3_sub(desc.li.point, s_intersection_start);
  7957. float udotn = gs_vec3_dot(u, axis);
  7958. float neg = gs_vec3_dot(axis, GS_ZAXIS) < 0.f ? -1.f : 1.f;
  7959. s_delta.translation = gs_vec3_scale(axis, udotn);
  7960. s_intersection_start = gs_vec3_add(s_intersection_start, s_delta.translation);
  7961. s_delta.translation = gs_vec3_scale(s_delta.translation, neg);
  7962. s_delta.translation.y = 0.f;
  7963. s_delta.translation.x = 0.f;
  7964. if (snap > 0.f)
  7965. {
  7966. s_delta.scale = gs_vec3_add(s_delta.scale, s_delta.translation); // Store total delta since interaction began
  7967. float snap_len = round(gs_vec3_len(s_delta.scale) / snap) * snap;
  7968. gs_vec3 norm = gs_vec3_norm(s_delta.scale);
  7969. gs_vec3 delta = gs_vec3_scale(gs_vec3_norm(s_delta.scale), snap_len);
  7970. gs_vec3 os = gs_v3(s_delta.rotation.x, s_delta.rotation.y, s_delta.rotation.z);
  7971. model->scale = gs_vec3_add(os, delta);
  7972. }
  7973. else
  7974. {
  7975. model->scale = gs_vec3_add(model->scale, s_delta.translation);
  7976. }
  7977. }
  7978. }
  7979. } break;
  7980. #define UPDATE_GIZMO_CONTROL_ROTATE(ID, RAY, SHAPE, MODEL, AXIS, INFO)\
  7981. do {\
  7982. int32_t mouseover = 0;\
  7983. gs_gui_id id = (ID);\
  7984. gs_contact_info_t info = gs_default_val();\
  7985. gs_vec3 axis = gs_quat_rotate(desc.xform.rotation, AXIS);\
  7986. info.normal = axis;\
  7987. if (in_hover_root) {\
  7988. gs_plane_t ip = gs_plane_from_pt_normal(desc.xform.translation, axis);\
  7989. float denom = gs_vec3_dot(gs_v3(ip.a, ip.b, ip.c), ray.d);\
  7990. denom = fabsf(denom) >= GS_EPSILON ? denom : 0.00001f;\
  7991. info.depth = -(ip.a * ray.p.x + ip.b * ray.p.y + ip.c * ray.p.z + ip.d) / denom;\
  7992. gs_gui_gizmo_line_intersection_result_t res = gs_default_val();\
  7993. res.point = gs_vec3_add(ray.p, gs_vec3_scale(ray.d, info.depth));\
  7994. float dist = gs_vec3_dist(res.point, model->translation);\
  7995. float scl = gs_vec3_len(desc.xform.scale);\
  7996. if (dist <= 0.6f * scl && dist >= 0.45f * scl) {\
  7997. info.hit = true;\
  7998. }\
  7999. }\
  8000. mouseover = info.hit && info.depth <= INFO.depth && in_hover_root && !ctx->hover_split && !ctx->lock_hover_id;\
  8001. if (ctx->focus == id) {ctx->updated_focus = 1; INFO = info;}\
  8002. if (~opt & GS_GUI_OPT_NOINTERACT) {\
  8003. /* Check for hold focus here */\
  8004. if (mouseover && !ctx->mouse_down) {\
  8005. gs_gui_set_hover(ctx, id);\
  8006. INFO = info;\
  8007. }\
  8008. \
  8009. if (ctx->focus == id)\
  8010. {\
  8011. res |= GS_GUI_RES_ACTIVE;\
  8012. just_set_focus = false;\
  8013. gs_gui_set_focus(ctx, id);\
  8014. if (ctx->mouse_pressed && !mouseover) {gs_gui_set_focus(ctx, 0);}\
  8015. if (!ctx->mouse_down && ~opt & GS_GUI_OPT_HOLDFOCUS) {gs_gui_set_focus(ctx, 0);}\
  8016. }\
  8017. \
  8018. if (ctx->prev_hover == id && !mouseover) {ctx->prev_hover = ctx->hover;}\
  8019. \
  8020. if (ctx->hover == id)\
  8021. {\
  8022. if (ctx->mouse_pressed)\
  8023. {\
  8024. if ((opt & GS_GUI_OPT_LEFTCLICKONLY && ctx->mouse_pressed == GS_GUI_MOUSE_LEFT) || (~opt & GS_GUI_OPT_LEFTCLICKONLY))\
  8025. {\
  8026. gs_gui_set_focus(ctx, id);\
  8027. just_set_focus = true;\
  8028. }\
  8029. }\
  8030. else if (!mouseover)\
  8031. {\
  8032. gs_gui_set_hover(ctx, 0);\
  8033. }\
  8034. }\
  8035. }\
  8036. } while (0)
  8037. case GS_GUI_GIZMO_ROTATE:
  8038. {
  8039. // Construct translate gizmo for this frame based on given parent transform
  8040. desc.gizmo.rotate = gs_gizmo_rotate(&desc.xform);
  8041. gs_gui_id id_r = gs_gui_get_id(ctx, "#gizmo_rotate_right", strlen("#gizmo_rotate_right"));
  8042. gs_gui_id id_u = gs_gui_get_id(ctx, "#gizmo_rotate_up", strlen("#gizmo_rotate_up"));
  8043. gs_gui_id id_f = gs_gui_get_id(ctx, "#gizmo_rotate_forward", strlen("#gizmo_rotate_forward"));
  8044. // Right
  8045. UPDATE_GIZMO_CONTROL_ROTATE(id_r, ray, desc.gizmo.rotate.right.axis.shape.plane,
  8046. desc.gizmo.rotate.right.axis.model, GS_XAXIS, desc.info);
  8047. // Up
  8048. UPDATE_GIZMO_CONTROL_ROTATE(id_u, ray, desc.gizmo.rotate.up.axis.shape.plane,
  8049. desc.gizmo.rotate.up.axis.model, GS_YAXIS, desc.info);
  8050. // Forward
  8051. UPDATE_GIZMO_CONTROL_ROTATE(id_f, ray, desc.gizmo.rotate.forward.axis.shape.plane,
  8052. desc.gizmo.rotate.forward.axis.model, GS_ZAXIS, desc.info);
  8053. if (ctx->focus == id_r)
  8054. {
  8055. desc.li = op == GS_GUI_TRANSFORM_LOCAL ?
  8056. gs_gui_gizmo_get_line_intersection(&desc.xform, GS_XAXIS, GS_YAXIS, GS_ZAXIS, camera, &ray, gs_v3s(0.f), false, false) :
  8057. gs_gui_gizmo_get_line_intersection(&desc.xform, GS_XAXIS, GS_YAXIS, GS_ZAXIS, camera, &ray, gs_v3s(0.f), false, true);
  8058. if (desc.li.hit)
  8059. {
  8060. if (just_set_focus) {
  8061. s_intersection_start = desc.li.point;
  8062. memset(&s_delta, 0, sizeof(s_delta));
  8063. s_delta.translation = gs_v3(
  8064. model->rotation.x,
  8065. model->rotation.y,
  8066. model->rotation.z
  8067. );
  8068. s_delta.scale.y = model->rotation.w;
  8069. }
  8070. float dist_from_cam = gs_vec3_dist(desc.xform.translation, camera->transform.translation);
  8071. const float denom = dist_from_cam != 0.f ? dist_from_cam : 2.f;
  8072. const gs_vec3 end_vector = gs_vec3_sub(desc.li.point, desc.xform.translation);
  8073. const gs_vec3 start_norm = gs_vec3_norm(gs_vec3_sub(s_intersection_start, desc.xform.translation));
  8074. const gs_vec3 end_norm = gs_vec3_norm(end_vector);
  8075. const gs_vec3 rot_local = gs_quat_rotate(desc.xform.rotation, GS_XAXIS);
  8076. float len = gs_vec3_len(end_vector) / denom;
  8077. float angle = gs_vec3_angle_between_signed(start_norm, end_norm);
  8078. if (len > 1.f) {
  8079. angle *= len;
  8080. }
  8081. gs_vec3 cross = gs_vec3_cross(start_norm, end_norm);
  8082. if (gs_vec3_dot(rot_local, cross) < 0.f) {
  8083. angle *= -1.f;
  8084. }
  8085. s_intersection_start = desc.li.point;
  8086. float delta = gs_rad2deg(angle);
  8087. s_delta.scale.x += delta;
  8088. s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(delta), GS_XAXIS);
  8089. if (snap > 0.f)
  8090. {
  8091. float snap_delta = round(s_delta.scale.x / snap) * snap;
  8092. s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(snap_delta), GS_XAXIS);
  8093. gs_quat orot = gs_quat(s_delta.translation.x, s_delta.translation.y, s_delta.translation.z, s_delta.scale.y);
  8094. switch (mode) {
  8095. case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, orot); break;
  8096. case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(orot, s_delta.rotation); break;
  8097. }
  8098. }
  8099. else
  8100. {
  8101. switch (mode) {
  8102. case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, model->rotation); break;
  8103. case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(model->rotation, s_delta.rotation); break;
  8104. }
  8105. }
  8106. }
  8107. }
  8108. else if (ctx->focus == id_u)
  8109. {
  8110. desc.li = op == GS_GUI_TRANSFORM_LOCAL ?
  8111. gs_gui_gizmo_get_line_intersection(&desc.xform, GS_YAXIS, GS_XAXIS, GS_ZAXIS, camera, &ray, gs_v3s(0.f), false, false) :
  8112. gs_gui_gizmo_get_line_intersection(&desc.xform, GS_YAXIS, GS_XAXIS, GS_ZAXIS, camera, &ray, gs_v3s(0.f), false, true);
  8113. if (desc.li.hit)
  8114. {
  8115. if (just_set_focus) {
  8116. s_intersection_start = desc.li.point;
  8117. memset(&s_delta, 0, sizeof(s_delta));
  8118. s_delta.translation = gs_v3(
  8119. model->rotation.x,
  8120. model->rotation.y,
  8121. model->rotation.z
  8122. );
  8123. s_delta.scale.y = model->rotation.w;
  8124. }
  8125. float dist_from_cam = gs_vec3_dist(desc.xform.translation, camera->transform.translation);
  8126. const float denom = dist_from_cam != 0.f ? dist_from_cam : 2.f;
  8127. const gs_vec3 end_vector = gs_vec3_sub(desc.li.point, desc.xform.translation);
  8128. const gs_vec3 start_norm = gs_vec3_norm(gs_vec3_sub(s_intersection_start, desc.xform.translation));
  8129. const gs_vec3 end_norm = gs_vec3_norm(end_vector);
  8130. const gs_vec3 rot_local = gs_quat_rotate(desc.xform.rotation, GS_YAXIS);
  8131. float len = gs_vec3_len(end_vector) / denom;
  8132. float angle = gs_vec3_angle_between_signed(start_norm, end_norm);
  8133. if (len > 1.f) {
  8134. angle *= len;
  8135. }
  8136. gs_vec3 cross = gs_vec3_cross(start_norm, end_norm);
  8137. if (gs_vec3_dot(rot_local, cross) < 0.f) {
  8138. angle *= -1.f;
  8139. }
  8140. s_intersection_start = desc.li.point;
  8141. float delta = gs_rad2deg(angle);
  8142. s_delta.scale.x += delta;
  8143. s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(delta), GS_YAXIS);
  8144. if (snap > 0.f)
  8145. {
  8146. float snap_delta = round(s_delta.scale.x / snap) * snap;
  8147. s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(snap_delta), GS_YAXIS);
  8148. gs_quat orot = gs_quat(s_delta.translation.x, s_delta.translation.y, s_delta.translation.z, s_delta.scale.y);
  8149. switch (mode) {
  8150. case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, orot); break;
  8151. case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(orot, s_delta.rotation); break;
  8152. }
  8153. }
  8154. else
  8155. {
  8156. switch (mode) {
  8157. case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, model->rotation); break;
  8158. case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(model->rotation, s_delta.rotation); break;
  8159. }
  8160. }
  8161. }
  8162. }
  8163. else if (ctx->focus == id_f)
  8164. {
  8165. desc.li = op == GS_GUI_TRANSFORM_LOCAL ?
  8166. gs_gui_gizmo_get_line_intersection(&desc.xform, GS_ZAXIS, GS_XAXIS, GS_YAXIS, camera, &ray, gs_v3s(0.f), false, false) :
  8167. gs_gui_gizmo_get_line_intersection(&desc.xform, GS_ZAXIS, GS_XAXIS, GS_YAXIS, camera, &ray, gs_v3s(0.f), false, true);
  8168. if (desc.li.hit)
  8169. {
  8170. if (just_set_focus) {
  8171. s_intersection_start = desc.li.point;
  8172. memset(&s_delta, 0, sizeof(s_delta));
  8173. s_delta.translation = gs_v3(
  8174. model->rotation.x,
  8175. model->rotation.y,
  8176. model->rotation.z
  8177. );
  8178. s_delta.scale.y = model->rotation.w;
  8179. }
  8180. float dist_from_cam = gs_vec3_dist(desc.xform.translation, camera->transform.translation);
  8181. const float denom = dist_from_cam != 0.f ? dist_from_cam : 2.f;
  8182. const gs_vec3 end_vector = gs_vec3_sub(desc.li.point, desc.xform.translation);
  8183. const gs_vec3 start_norm = gs_vec3_norm(gs_vec3_sub(s_intersection_start, desc.xform.translation));
  8184. const gs_vec3 end_norm = gs_vec3_norm(end_vector);
  8185. const gs_vec3 rot_local = gs_quat_rotate(desc.xform.rotation, GS_ZAXIS);
  8186. float len = gs_vec3_len(end_vector) / denom;
  8187. float angle = gs_vec3_angle_between_signed(start_norm, end_norm);
  8188. if (len > 1.f) {
  8189. angle *= len;
  8190. }
  8191. gs_vec3 cross = gs_vec3_cross(start_norm, end_norm);
  8192. if (gs_vec3_dot(rot_local, cross) < 0.f) {
  8193. angle *= -1.f;
  8194. }
  8195. s_intersection_start = desc.li.point;
  8196. float delta = gs_rad2deg(angle);
  8197. s_delta.scale.x += delta;
  8198. s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(delta), GS_ZAXIS);
  8199. if (snap > 0.f)
  8200. {
  8201. float snap_delta = round(s_delta.scale.x / snap) * snap;
  8202. s_delta.rotation = gs_quat_angle_axis(gs_deg2rad(snap_delta), GS_ZAXIS);
  8203. gs_quat orot = gs_quat(s_delta.translation.x, s_delta.translation.y, s_delta.translation.z, s_delta.scale.y);
  8204. switch (mode) {
  8205. case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, orot); break;
  8206. case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(orot, s_delta.rotation); break;
  8207. }
  8208. }
  8209. else
  8210. {
  8211. switch (mode) {
  8212. case GS_GUI_TRANSFORM_WORLD: model->rotation = gs_quat_mul(s_delta.rotation, model->rotation); break;
  8213. case GS_GUI_TRANSFORM_LOCAL: model->rotation = gs_quat_mul(model->rotation, s_delta.rotation); break;
  8214. }
  8215. }
  8216. }
  8217. }
  8218. } break;
  8219. }
  8220. // Have to render the gizmo using view/projection (so a custom render command)
  8221. gs_gui_draw_custom(ctx, clip, gs_gui_gizmo_render, &desc, sizeof(desc));
  8222. return res;
  8223. }
  8224. //=== Demos ===//
  8225. GS_API_DECL int32_t
  8226. gs_gui_style_editor(gs_gui_context_t *ctx, gs_gui_style_sheet_t* style_sheet, gs_gui_rect_t rect, bool* open)
  8227. {
  8228. if (!style_sheet)
  8229. {
  8230. style_sheet = &gs_gui_default_style_sheet;
  8231. }
  8232. static struct {const char* label; int32_t idx;} elements[] = {
  8233. {"container", GS_GUI_ELEMENT_CONTAINER},
  8234. {"button", GS_GUI_ELEMENT_BUTTON} ,
  8235. {"panel", GS_GUI_ELEMENT_PANEL},
  8236. {"input", GS_GUI_ELEMENT_INPUT},
  8237. {"label", GS_GUI_ELEMENT_LABEL},
  8238. {"text", GS_GUI_ELEMENT_TEXT},
  8239. {"scroll", GS_GUI_ELEMENT_SCROLL},
  8240. {"image", GS_GUI_ELEMENT_IMAGE},
  8241. {NULL}
  8242. };
  8243. static char* states[] = {
  8244. "default",
  8245. "hover",
  8246. "focus"
  8247. };
  8248. static struct {const char* label; int32_t idx;} colors[] = {
  8249. {"background", GS_GUI_COLOR_BACKGROUND},
  8250. {"border", GS_GUI_COLOR_BORDER},
  8251. {"shadow", GS_GUI_COLOR_SHADOW},
  8252. {"content", GS_GUI_COLOR_CONTENT} ,
  8253. {"content_shadow", GS_GUI_COLOR_CONTENT_SHADOW},
  8254. {"content_background", GS_GUI_COLOR_CONTENT_BACKGROUND},
  8255. {"content_border", GS_GUI_COLOR_CONTENT_BORDER},
  8256. {NULL}
  8257. };
  8258. if (gs_gui_window_begin_ex(ctx, "Style_Editor", rect, open, NULL, 0x00))
  8259. {
  8260. for (uint32_t i = 0; elements[i].label; ++i)
  8261. {
  8262. int32_t idx = elements[i].idx;
  8263. if (gs_gui_treenode_begin_ex(ctx, elements[i].label, NULL, 0x00))
  8264. {
  8265. for (uint32_t j = 0; j < GS_GUI_ELEMENT_STATE_COUNT; ++j)
  8266. {
  8267. gs_gui_push_id(ctx, &j, sizeof(j));
  8268. gs_gui_style_t* s = &style_sheet->styles[idx][j];
  8269. if (gs_gui_treenode_begin_ex(ctx, states[j], NULL, 0x00))
  8270. {
  8271. gs_gui_style_t* save = gs_gui_push_style(ctx, &ctx->style_sheet->styles[GS_GUI_ELEMENT_PANEL][0x00]);
  8272. int32_t row[] = {-1};
  8273. gs_gui_layout_row(ctx, 1, row, 300);
  8274. gs_gui_panel_begin(ctx, states[j]);
  8275. {
  8276. gs_gui_layout_t* l = gs_gui_get_layout(ctx);
  8277. gs_gui_rect_t* r = &l->body;
  8278. const int32_t ls = 80;
  8279. // size
  8280. int32_t w = (int32_t)((l->body.w - ls) * 0.35f);
  8281. {
  8282. int32_t row[] = {ls, w, w};
  8283. gs_gui_layout_row(ctx, 3, row, 0);
  8284. }
  8285. gs_gui_label(ctx, "size:");
  8286. gs_gui_slider(ctx, &s->size[0], 0.f, 500.f);
  8287. gs_gui_slider(ctx, &s->size[1], 0.f, 500.f);
  8288. w = (int32_t)((l->body.w - ls) * 0.2f);
  8289. {
  8290. int32_t row[] = {ls, w, w, w, w};
  8291. gs_gui_layout_row(ctx, 5, row, 0);
  8292. }
  8293. gs_gui_label(ctx, "border_width:");
  8294. int16_slider(ctx, &s->border_width[0], 0, 100, NULL, 0x00);
  8295. int16_slider(ctx, &s->border_width[1], 0, 100, NULL, 0x00);
  8296. int16_slider(ctx, &s->border_width[2], 0, 100, NULL, 0x00);
  8297. int16_slider(ctx, &s->border_width[3], 0, 100, NULL, 0x00);
  8298. gs_gui_label(ctx, "border_radius:");
  8299. int16_slider(ctx, &s->border_radius[0], 0, 100, NULL, 0x00);
  8300. int16_slider(ctx, &s->border_radius[1], 0, 100, NULL, 0x00);
  8301. int16_slider(ctx, &s->border_radius[2], 0, 100, NULL, 0x00);
  8302. int16_slider(ctx, &s->border_radius[3], 0, 100, NULL, 0x00);
  8303. // padding/margin
  8304. gs_gui_label(ctx, "padding:");
  8305. int32_slider(ctx, &s->padding[0], 0, 100, NULL, 0x00);
  8306. int32_slider(ctx, &s->padding[1], 0, 100, NULL, 0x00);
  8307. int32_slider(ctx, &s->padding[2], 0, 100, NULL, 0x00);
  8308. int32_slider(ctx, &s->padding[3], 0, 100, NULL, 0x00);
  8309. gs_gui_label(ctx, "margin:");
  8310. int16_slider(ctx, &s->margin[0], 0, 100, NULL, 0x00);
  8311. int16_slider(ctx, &s->margin[1], 0, 100, NULL, 0x00);
  8312. int16_slider(ctx, &s->margin[2], 0, 100, NULL, 0x00);
  8313. int16_slider(ctx, &s->margin[3], 0, 100, NULL, 0x00);
  8314. // Colors
  8315. int sw = (int32_t)(l->body.w * 0.14);
  8316. {
  8317. int32_t row[] = {80, sw, sw, sw, sw, -1};
  8318. gs_gui_layout_row(ctx, 6, row, 0);
  8319. }
  8320. for (uint32_t c = 0; colors[c].label; ++c)
  8321. {
  8322. gs_gui_label(ctx, colors[c].label);
  8323. uint8_slider(ctx, &s->colors[c].r, 0, 255, NULL, 0x00);
  8324. uint8_slider(ctx, &s->colors[c].g, 0, 255, NULL, 0x00);
  8325. uint8_slider(ctx, &s->colors[c].b, 0, 255, NULL, 0x00);
  8326. uint8_slider(ctx, &s->colors[c].a, 0, 255, NULL, 0x00);
  8327. gs_gui_draw_rect(ctx, gs_gui_layout_next(ctx), s->colors[c]);
  8328. }
  8329. }
  8330. gs_gui_panel_end(ctx);
  8331. gs_gui_pop_style(ctx, save);
  8332. gs_gui_treenode_end(ctx);
  8333. }
  8334. gs_gui_pop_id(ctx);
  8335. }
  8336. gs_gui_treenode_end(ctx);
  8337. }
  8338. }
  8339. gs_gui_window_end(ctx);
  8340. }
  8341. return 0x01;
  8342. }
  8343. GS_API_DECL int32_t gs_gui_demo_window(gs_gui_context_t* ctx, gs_gui_rect_t rect, bool* open)
  8344. {
  8345. /*
  8346. Window for all of the available features for gs_gui:
  8347. - Opts:
  8348. - Widgets:
  8349. - Label
  8350. - Panel
  8351. - Button
  8352. - Textbox
  8353. - Text
  8354. Todo:
  8355. - Menu / MenuItem
  8356. - ColorPicker
  8357. - Lists (with images / various style enums for item types (circle, rect, box)
  8358. - Window Navigation
  8359. - VIM controls eventually?
  8360. - Docking
  8361. - Menus
  8362. - Tabs
  8363. - Styles:
  8364. - Inline styles
  8365. - Style sheets
  8366. */
  8367. if (gs_gui_window_begin_ex(ctx, "Demo_Window", rect, open, NULL, 0x00))
  8368. {
  8369. gs_gui_container_t* win = gs_gui_get_current_container(ctx);
  8370. if (gs_gui_treenode_begin(ctx, "Help"))
  8371. {
  8372. {
  8373. int32_t row[] = {-10};
  8374. gs_gui_layout_row(ctx, 1, row, 170);
  8375. }
  8376. gs_gui_panel_begin(ctx, "#!window_info");
  8377. {
  8378. {
  8379. int32_t row[] = {-1};
  8380. gs_gui_layout_row(ctx, 1, row, 0);
  8381. }
  8382. gs_gui_label(ctx, "ABOUT THIS DEMO:");
  8383. gs_gui_text(ctx, " - Sections below are demonstrating many aspects of the util.");
  8384. }
  8385. gs_gui_panel_end(ctx);
  8386. gs_gui_treenode_end(ctx);
  8387. }
  8388. if (gs_gui_treenode_begin(ctx, "Window Info"))
  8389. {
  8390. {
  8391. int32_t row[] = {-10};
  8392. gs_gui_layout_row(ctx, 1, row, 170);
  8393. }
  8394. gs_gui_panel_begin(ctx, "#!window_info");
  8395. {
  8396. char buf[64];
  8397. {
  8398. int32_t row[] = {65, -1};
  8399. gs_gui_layout_row(ctx, 2, row, 0);
  8400. }
  8401. gs_gui_label(ctx,"Position:");
  8402. gs_snprintf(buf, 64, "%.2f, %.2f", win->rect.x, win->rect.y); gs_gui_label(ctx, buf);
  8403. gs_gui_label(ctx, "Size:");
  8404. gs_snprintf(buf, 64, "%.2f, %.2f", win->rect.w, win->rect.h); gs_gui_label(ctx, buf);
  8405. gs_gui_label(ctx, "Title:");
  8406. gs_gui_label(ctx, win->name);
  8407. gs_gui_label(ctx, "ID:");
  8408. gs_snprintf(buf, 64, "%zu", win->id); gs_gui_label(ctx, buf);
  8409. gs_gui_label(ctx, "Open:");
  8410. gs_snprintf(buf, 64, "%s", win->open ? "true" : "close"); gs_gui_label(ctx, buf);
  8411. }
  8412. gs_gui_panel_end(ctx);
  8413. gs_gui_treenode_end(ctx);
  8414. }
  8415. if (gs_gui_treenode_begin(ctx, "Context State"))
  8416. {
  8417. {
  8418. int32_t row[] = {-10};
  8419. gs_gui_layout_row(ctx, 1, row, 170);
  8420. }
  8421. gs_gui_panel_begin(ctx, "#!context_state");
  8422. {
  8423. char buf[64];
  8424. {
  8425. int32_t row[] = {80, -1};
  8426. gs_gui_layout_row(ctx, 2, row, 0);
  8427. }
  8428. gs_gui_label(ctx,"Hovered:");
  8429. gs_snprintf(buf, 64, "%s", ctx->hover_root ? ctx->hover_root->name : "NULL"); gs_gui_label(ctx, buf);
  8430. gs_gui_label(ctx, "Focused:");
  8431. gs_snprintf(buf, 64, "%s", ctx->focus_root ? ctx->focus_root->name : "NULL"); gs_gui_label(ctx, buf);
  8432. gs_gui_label(ctx, "Active:");
  8433. gs_snprintf(buf, 64, "%s", ctx->active_root ? ctx->active_root->name : "NULL"); gs_gui_label(ctx, buf);
  8434. gs_gui_label(ctx, "Lock Focus:");
  8435. gs_snprintf(buf, 64, "%zu", ctx->lock_focus); gs_gui_label(ctx, buf);
  8436. }
  8437. gs_gui_panel_end(ctx);
  8438. gs_gui_treenode_end(ctx);
  8439. }
  8440. if (gs_gui_treenode_begin(ctx, "Widgets"))
  8441. {
  8442. {
  8443. int32_t row[] = {-10};
  8444. gs_gui_layout_row(ctx, 1, row, 170);
  8445. }
  8446. gs_gui_panel_begin(ctx, "#!widgets");
  8447. {
  8448. {
  8449. int32_t row[] = {150, 50};
  8450. gs_gui_layout_row(ctx, 2, row, 0);
  8451. }
  8452. gs_gui_layout_column_begin(ctx);
  8453. {
  8454. {
  8455. int32_t row[] = {0};
  8456. gs_gui_layout_row(ctx, 1, row, 0);
  8457. }
  8458. gs_gui_button(ctx, "Button");
  8459. // Label
  8460. gs_gui_label(ctx, "Label");
  8461. // Text
  8462. {
  8463. int32_t row[] = {150};
  8464. gs_gui_layout_row(ctx, 1, row, 0);
  8465. }
  8466. gs_gui_text(ctx, "This is some text");
  8467. static char buf[64] = {0};
  8468. gs_gui_textbox(ctx, buf, 64);
  8469. }
  8470. gs_gui_layout_column_end(ctx);
  8471. gs_gui_layout_column_begin(ctx);
  8472. {
  8473. gs_gui_label(ctx, "(?)");
  8474. if (ctx->hover == ctx->last_id) gs_println("HOVERED");
  8475. }
  8476. gs_gui_layout_column_end(ctx);
  8477. }
  8478. gs_gui_panel_end(ctx);
  8479. gs_gui_treenode_end(ctx);
  8480. }
  8481. gs_gui_window_end(ctx);
  8482. }
  8483. return 0x01;
  8484. }
  8485. //==== Resource Loading ===//
  8486. typedef enum
  8487. {
  8488. GS_GUI_SS_DEF_NUMBER = 0x00,
  8489. GS_GUI_SS_DEF_ENUM,
  8490. GS_GUI_SS_DEF_COLOR,
  8491. GS_GUI_SS_DEF_STR
  8492. } gs_gui_ss_var_def_type;
  8493. typedef struct
  8494. {
  8495. gs_gui_ss_var_def_type type;
  8496. union {
  8497. int32_t number;
  8498. gs_color_t color;
  8499. char str[64];
  8500. } val;
  8501. } gs_gui_ss_var_def_t;
  8502. typedef struct
  8503. {
  8504. gs_hash_table(uint64_t, gs_gui_ss_var_def_t) variables;
  8505. } gs_gui_ss_variables_t;
  8506. #define _GS_GUI_SS_GET_TO_VALUES(T0, T1)\
  8507. do {\
  8508. bool ret = gs_lexer_require_token_type(lex, T0);\
  8509. ret &= gs_lexer_require_token_type(lex, T1);\
  8510. if (!ret) {\
  8511. gs_log_warning("Unidentified token.");\
  8512. return false;\
  8513. }\
  8514. token = gs_lexer_current_token(lex);\
  8515. } while (0)
  8516. bool _gs_gui_style_sheet_parse_attribute_transition(gs_gui_context_t* ctx, gs_lexer_t* lex,
  8517. gs_gui_style_sheet_t* ss, const uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
  8518. {
  8519. // Name of enum attribute
  8520. gs_token_t token = gs_lexer_current_token(lex);
  8521. // gs_token_debug_print(&token);
  8522. if (id_tag)
  8523. {
  8524. if (!gs_hash_table_exists(ss->cid_animations, id_tag))
  8525. {
  8526. gs_gui_animation_property_list_t sl = gs_default_val();
  8527. gs_hash_table_insert(ss->cid_animations, id_tag, sl);
  8528. }
  8529. }
  8530. #define PARSE_TRANSITION(T)\
  8531. do {\
  8532. uint32_t time_v = 0;\
  8533. uint32_t delay_v = 0;\
  8534. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);\
  8535. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
  8536. if (!ret) {\
  8537. gs_log_warning("Transition: Unidentified token.");\
  8538. return false;\
  8539. }\
  8540. gs_token_t time = gs_lexer_current_token(lex);\
  8541. switch (time.type)\
  8542. { \
  8543. case GS_TOKEN_NUMBER:\
  8544. {\
  8545. gs_snprintfc(TIME, 32, "%.*s", time.len, time.text);\
  8546. time_v = (uint32_t)atoi(TIME);\
  8547. } break;\
  8548. \
  8549. case GS_TOKEN_DOLLAR:\
  8550. {\
  8551. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER));\
  8552. if (!ret) {\
  8553. gs_log_warning("Transition: Variable missing identifier token.");\
  8554. return false;\
  8555. }\
  8556. token = gs_lexer_current_token(lex);\
  8557. gs_snprintfc(VAR, 64, "%.*s", token.len, token.text);\
  8558. uint64_t hash = gs_hash_str64(VAR);\
  8559. if (gs_hash_table_exists(variables->variables, hash))\
  8560. {\
  8561. time_v = (uint32_t)(gs_hash_table_getp(variables->variables, hash))->val.number;\
  8562. }\
  8563. else\
  8564. {\
  8565. gs_log_warning("Transition: Variable not found: %s.", VAR);\
  8566. return false;\
  8567. }\
  8568. } break;\
  8569. }\
  8570. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
  8571. if (!ret) {\
  8572. gs_log_warning("Transition: Unidentified token.");\
  8573. return false;\
  8574. }\
  8575. gs_token_t delay = gs_lexer_current_token(lex);\
  8576. switch (delay.type)\
  8577. {\
  8578. case GS_TOKEN_NUMBER:\
  8579. {\
  8580. gs_snprintfc(DELAY, 32, "%.*s", delay.len, delay.text);\
  8581. uint32_t delay_v = (uint32_t)atoi(DELAY);\
  8582. } break;\
  8583. \
  8584. case GS_TOKEN_DOLLAR:\
  8585. {\
  8586. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER));\
  8587. if (!ret) {\
  8588. gs_log_warning("Transition: Variable missing identifier token.");\
  8589. return false;\
  8590. }\
  8591. token = gs_lexer_current_token(lex);\
  8592. gs_snprintfc(VAR, 64, "%.*s", token.len, token.text);\
  8593. uint64_t hash = gs_hash_str64(VAR);\
  8594. if (gs_hash_table_exists(variables->variables, hash))\
  8595. {\
  8596. delay_v = (uint32_t)(gs_hash_table_getp(variables->variables, hash))->val.number;\
  8597. }\
  8598. else\
  8599. {\
  8600. gs_log_warning("Transition: Variable not found: %s.", VAR);\
  8601. return false;\
  8602. }\
  8603. } break;\
  8604. }\
  8605. gs_gui_animation_property_t prop = gs_default_val();\
  8606. prop.type = T;\
  8607. prop.time = (int16_t)time_v;\
  8608. prop.delay = (int16_t)delay_v;\
  8609. switch (state)\
  8610. {\
  8611. case GS_GUI_ELEMENT_STATE_DEFAULT:\
  8612. {\
  8613. for (uint32_t s = 0; s < 3; ++s)\
  8614. {\
  8615. gs_dyn_array_push(list->properties[s], prop);\
  8616. }\
  8617. } break;\
  8618. default:\
  8619. {\
  8620. gs_dyn_array_push(list->properties[state], prop);\
  8621. } break;\
  8622. }\
  8623. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON));\
  8624. if (!ret) {\
  8625. gs_log_warning("Transition: Missing semicolon.");\
  8626. return false;\
  8627. }\
  8628. } while (0)
  8629. _GS_GUI_SS_GET_TO_VALUES(GS_TOKEN_COLON, GS_TOKEN_LBRACE);
  8630. if (!gs_hash_table_exists(ss->animations, (gs_gui_element_type)elementid))
  8631. {
  8632. gs_gui_animation_property_list_t list = gs_default_val();
  8633. gs_hash_table_insert(ss->animations, (gs_gui_element_type)elementid, list);
  8634. }
  8635. gs_gui_animation_property_list_t* list = id_tag ? gs_hash_table_getp(ss->cid_animations, id_tag) :
  8636. gs_hash_table_getp(ss->animations, (gs_gui_element_type)elementid);
  8637. int32_t bc = 1;
  8638. while (gs_lexer_can_lex(lex) && bc)
  8639. {
  8640. token = gs_lexer_next_token(lex);
  8641. switch (token.type)
  8642. {
  8643. case GS_TOKEN_LBRACE: bc++; break;
  8644. case GS_TOKEN_RBRACE: bc--; break;
  8645. case GS_TOKEN_IDENTIFIER:
  8646. {
  8647. if (gs_token_compare_text(&token, "color_background")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_BACKGROUND);
  8648. else if (gs_token_compare_text(&token, "color_border")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_BORDER);
  8649. else if (gs_token_compare_text(&token, "color_shadow")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_SHADOW);
  8650. else if (gs_token_compare_text(&token, "color_content")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_CONTENT);
  8651. else if (gs_token_compare_text(&token, "width")) PARSE_TRANSITION(GS_GUI_STYLE_WIDTH);
  8652. else if (gs_token_compare_text(&token, "height")) PARSE_TRANSITION(GS_GUI_STYLE_HEIGHT);
  8653. else if (gs_token_compare_text(&token, "padding")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING);
  8654. else if (gs_token_compare_text(&token, "padding_left")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING_LEFT);
  8655. else if (gs_token_compare_text(&token, "padding_right")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING_RIGHT);
  8656. else if (gs_token_compare_text(&token, "padding_top")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING_TOP);
  8657. else if (gs_token_compare_text(&token, "padding_bottom")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING_BOTTOM);
  8658. else if (gs_token_compare_text(&token, "margin")) PARSE_TRANSITION(GS_GUI_STYLE_PADDING);
  8659. else if (gs_token_compare_text(&token, "margin_left")) PARSE_TRANSITION(GS_GUI_STYLE_MARGIN_LEFT);
  8660. else if (gs_token_compare_text(&token, "margin_right")) PARSE_TRANSITION(GS_GUI_STYLE_MARGIN_RIGHT);
  8661. else if (gs_token_compare_text(&token, "margin_top")) PARSE_TRANSITION(GS_GUI_STYLE_MARGIN_TOP);
  8662. else if (gs_token_compare_text(&token, "margin_bottom")) PARSE_TRANSITION(GS_GUI_STYLE_MARGIN_BOTTOM);
  8663. else if (gs_token_compare_text(&token, "shadow_x")) PARSE_TRANSITION(GS_GUI_STYLE_SHADOW_X);
  8664. else if (gs_token_compare_text(&token, "shadow_y")) PARSE_TRANSITION(GS_GUI_STYLE_SHADOW_Y);
  8665. else if (gs_token_compare_text(&token, "color_content_background")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND);
  8666. else if (gs_token_compare_text(&token, "color_content_border")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_CONTENT_BORDER);
  8667. else if (gs_token_compare_text(&token, "color_content_shadow")) PARSE_TRANSITION(GS_GUI_STYLE_COLOR_CONTENT_SHADOW);
  8668. else
  8669. {
  8670. gs_log_warning("Unidentified attribute: %.*s.", token.len, token.text);
  8671. return false;
  8672. }
  8673. } break;
  8674. }
  8675. }
  8676. return true;
  8677. }
  8678. bool _gs_gui_style_sheet_parse_attribute_font(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
  8679. uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
  8680. {
  8681. // Name of enum attribute
  8682. gs_token_t token = gs_lexer_current_token(lex);
  8683. // gs_token_debug_print(&token);
  8684. //
  8685. gs_gui_style_list_t* idsl = NULL;
  8686. if (id_tag)
  8687. {
  8688. const uint64_t idhash = id_tag;
  8689. if (!gs_hash_table_exists(ss->cid_styles, idhash))
  8690. {
  8691. gs_gui_style_list_t sl = gs_default_val();
  8692. gs_hash_table_insert(ss->cid_styles, idhash, sl);
  8693. }
  8694. idsl = gs_hash_table_getp(ss->cid_styles, idhash);
  8695. }
  8696. #define SET_FONT(FONT)\
  8697. do {\
  8698. switch (state)\
  8699. {\
  8700. case GS_GUI_ELEMENT_STATE_DEFAULT:\
  8701. {\
  8702. se.font = FONT;\
  8703. for (uint32_t s = 0; s < 3; ++s)\
  8704. {\
  8705. if (idsl) gs_dyn_array_push(idsl->styles[s], se);\
  8706. else ss->styles[elementid][s].font = FONT;\
  8707. }\
  8708. } break;\
  8709. default:\
  8710. {\
  8711. se.font = FONT;\
  8712. if (idsl) gs_dyn_array_push(idsl->styles[state], se);\
  8713. ss->styles[elementid][state].font = FONT;\
  8714. } break;\
  8715. }\
  8716. } while (0)
  8717. if (gs_token_compare_text(&token, "font"))
  8718. {
  8719. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);
  8720. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_STRING) ||
  8721. gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR));
  8722. if (!ret)
  8723. {
  8724. gs_log_warning("Missing either string value or variable.");
  8725. return false;
  8726. }
  8727. gs_gui_style_element_t se = gs_default_val();
  8728. se.type = GS_GUI_STYLE_FONT;
  8729. token = gs_lexer_current_token(lex);
  8730. char FONT[64] = gs_default_val();
  8731. switch (token.type)
  8732. {
  8733. case GS_TOKEN_STRING:
  8734. {
  8735. gs_snprintf(FONT, sizeof(FONT), "%.*s", token.len - 2, token.text + 1);
  8736. } break;
  8737. case GS_TOKEN_DOLLAR:
  8738. {
  8739. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);
  8740. if (!ret) {
  8741. gs_log_warning("Unidentified token.");
  8742. return false;
  8743. }\
  8744. token = gs_lexer_current_token(lex);
  8745. gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);
  8746. uint64_t hash = gs_hash_str64(TMP);
  8747. gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?
  8748. gs_hash_table_getp(variables->variables, hash) : NULL;
  8749. if (!var) {
  8750. gs_log_warning("Variable not found: %s", TMP);
  8751. return false;
  8752. }
  8753. memcpy(FONT, var->val.str, sizeof(FONT));
  8754. ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);
  8755. if (!ret) {
  8756. gs_log_warning("Missing semicolon.");
  8757. return false;
  8758. }
  8759. token = gs_lexer_current_token(lex);
  8760. } break;
  8761. }
  8762. uint64_t hash = gs_hash_str64(FONT);
  8763. bool found = false;
  8764. for (
  8765. gs_hash_table_iter it = gs_hash_table_iter_new(ctx->font_stash);
  8766. gs_hash_table_iter_valid(ctx->font_stash, it);
  8767. gs_hash_table_iter_advance(ctx->font_stash, it)
  8768. )
  8769. {
  8770. uint64_t key = gs_hash_table_getk(ctx->font_stash, it);
  8771. if (hash == key)
  8772. {
  8773. gs_asset_font_t* font = gs_hash_table_iter_get(ctx->font_stash, it);
  8774. SET_FONT(font);
  8775. found = true;
  8776. break;
  8777. }
  8778. }
  8779. if (!found)
  8780. {
  8781. gs_log_warning("Font not found in gui font stash: %s", FONT);
  8782. }
  8783. }
  8784. else
  8785. {
  8786. gs_log_warning("Unidentified token.");
  8787. return false;
  8788. }
  8789. return true;
  8790. }
  8791. bool _gs_gui_style_sheet_parse_attribute_enum(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
  8792. uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
  8793. {
  8794. // Name of enum attribute
  8795. gs_token_t token = gs_lexer_current_token(lex);
  8796. // gs_token_debug_print(&token);
  8797. gs_gui_style_list_t* idsl = NULL;
  8798. if (id_tag)
  8799. {
  8800. const uint64_t idhash = id_tag;
  8801. if (!gs_hash_table_exists(ss->cid_styles, idhash))
  8802. {
  8803. gs_gui_style_list_t sl = gs_default_val();
  8804. gs_hash_table_insert(ss->cid_styles, idhash, sl);
  8805. }
  8806. idsl = gs_hash_table_getp(ss->cid_styles, idhash);
  8807. }
  8808. #define SET_ENUM(COMP, VAL)\
  8809. do {\
  8810. se.value = VAL;\
  8811. switch (state)\
  8812. {\
  8813. case GS_GUI_ELEMENT_STATE_DEFAULT:\
  8814. {\
  8815. if (idsl)\
  8816. {\
  8817. for (uint32_t s = 0; s < 3; ++s) {\
  8818. gs_dyn_array_push(idsl->styles[s], se);\
  8819. }\
  8820. }\
  8821. else\
  8822. {\
  8823. for (uint32_t s = 0; s < 3; ++s) {\
  8824. ss->styles[elementid][s].COMP = VAL;\
  8825. }\
  8826. }\
  8827. } break;\
  8828. default:\
  8829. {\
  8830. if (idsl) gs_dyn_array_push(idsl->styles[state], se);\
  8831. else ss->styles[elementid][state].COMP = VAL;\
  8832. } break;\
  8833. }\
  8834. } while (0)
  8835. if (gs_token_compare_text(&token, "justify_content"))
  8836. {
  8837. gs_gui_style_element_t se = gs_default_val();
  8838. se.type = GS_GUI_STYLE_JUSTIFY_CONTENT;
  8839. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);
  8840. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER) ||
  8841. gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR));
  8842. if (!ret)
  8843. {
  8844. gs_log_warning("Missing either identifier value or variable.");
  8845. return false;
  8846. }
  8847. token = gs_lexer_current_token(lex);
  8848. switch (token.type)
  8849. {
  8850. case GS_TOKEN_IDENTIFIER:
  8851. {
  8852. if (gs_token_compare_text(&token, "start")) SET_ENUM(justify_content, GS_GUI_JUSTIFY_START);
  8853. else if (gs_token_compare_text(&token, "end")) SET_ENUM(justify_content, GS_GUI_JUSTIFY_END);
  8854. else if (gs_token_compare_text(&token, "center")) SET_ENUM(justify_content, GS_GUI_JUSTIFY_CENTER);
  8855. } break;
  8856. case GS_TOKEN_DOLLAR:
  8857. {
  8858. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);
  8859. if (!ret) {
  8860. gs_log_warning("Unidentified token.");
  8861. return false;
  8862. }\
  8863. token = gs_lexer_current_token(lex);
  8864. gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);
  8865. uint64_t hash = gs_hash_str64(TMP);
  8866. gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?
  8867. gs_hash_table_getp(variables->variables, hash) : NULL;
  8868. if (!var) {
  8869. gs_log_warning("Variable not found: %s", TMP);
  8870. return false;
  8871. }
  8872. SET_ENUM(justify_content, var->val.number);
  8873. ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);
  8874. if (!ret) {
  8875. gs_log_warning("Missing semicolon.");
  8876. return false;
  8877. }
  8878. token = gs_lexer_current_token(lex);
  8879. } break;
  8880. }
  8881. }
  8882. else if (gs_token_compare_text(&token, "align_content"))
  8883. {
  8884. gs_gui_style_element_t se = gs_default_val();
  8885. se.type = GS_GUI_STYLE_ALIGN_CONTENT;
  8886. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);
  8887. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER) ||
  8888. gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR));
  8889. if (!ret)
  8890. {
  8891. gs_log_warning("Missing either identifier value or variable.");
  8892. return false;
  8893. }
  8894. token = gs_lexer_current_token(lex);
  8895. switch (token.type)
  8896. {
  8897. case GS_TOKEN_IDENTIFIER:
  8898. {
  8899. if (gs_token_compare_text(&token, "start")) SET_ENUM(align_content, GS_GUI_ALIGN_START);
  8900. else if (gs_token_compare_text(&token, "end")) SET_ENUM(align_content, GS_GUI_ALIGN_END);
  8901. else if (gs_token_compare_text(&token, "center")) SET_ENUM(align_content, GS_GUI_ALIGN_CENTER);
  8902. } break;
  8903. case GS_TOKEN_DOLLAR:
  8904. {
  8905. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);
  8906. if (!ret) {
  8907. gs_log_warning("Unidentified token.");
  8908. return false;
  8909. }\
  8910. token = gs_lexer_current_token(lex);
  8911. gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);
  8912. uint64_t hash = gs_hash_str64(TMP);
  8913. gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?
  8914. gs_hash_table_getp(variables->variables, hash) : NULL;
  8915. if (!var) {
  8916. gs_log_warning("Variable not found: %s", TMP);
  8917. return false;
  8918. }
  8919. SET_ENUM(align_content, var->val.number);
  8920. ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);
  8921. if (!ret) {
  8922. gs_log_warning("Missing semicolon.");
  8923. return false;
  8924. }
  8925. token = gs_lexer_current_token(lex);
  8926. } break;
  8927. }
  8928. }
  8929. return true;
  8930. }
  8931. bool _gs_gui_style_sheet_parse_attribute_val(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
  8932. uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
  8933. {
  8934. // Name of value attribute
  8935. gs_token_t token = gs_lexer_current_token(lex);
  8936. // gs_token_debug_print(&token);
  8937. gs_gui_style_list_t* idsl = NULL;
  8938. if (id_tag)
  8939. {
  8940. const uint64_t idhash = id_tag;
  8941. if (!gs_hash_table_exists(ss->cid_styles, idhash))
  8942. {
  8943. gs_gui_style_list_t sl = gs_default_val();
  8944. gs_hash_table_insert(ss->cid_styles, idhash, sl);
  8945. }
  8946. idsl = gs_hash_table_getp(ss->cid_styles, idhash);
  8947. }
  8948. #define SET_VAL4(COMP, T, SE)\
  8949. do {\
  8950. gs_gui_style_element_t se = gs_default_val();\
  8951. se.type = SE;\
  8952. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);\
  8953. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
  8954. token = gs_lexer_current_token(lex);\
  8955. gs_token_debug_print(&token);\
  8956. if (!ret) {\
  8957. gs_log_warning("Unidentified token.");\
  8958. return false;\
  8959. }\
  8960. switch (token.type)\
  8961. {\
  8962. case GS_TOKEN_NUMBER:\
  8963. {\
  8964. gs_snprintfc(TMP, 10, "%.*s", token.len, token.text);\
  8965. uint32_t val = (uint32_t)atoi(TMP);\
  8966. se.value = (T)val;\
  8967. switch (state) {\
  8968. case GS_GUI_ELEMENT_STATE_DEFAULT:\
  8969. {\
  8970. if (idsl) {\
  8971. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se);\
  8972. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se);\
  8973. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se);\
  8974. }\
  8975. else {\
  8976. for (uint32_t p = 0; p < 4; ++p) {\
  8977. ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP[p] = (T)val;\
  8978. ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP[p] = (T)val;\
  8979. ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP[p] = (T)val;\
  8980. }\
  8981. }\
  8982. } break;\
  8983. default:\
  8984. {\
  8985. if (idsl) gs_dyn_array_push(idsl->styles[state], se);\
  8986. else {\
  8987. for (uint32_t p = 0; p < 4; ++p) {\
  8988. ss->styles[elementid][state].COMP[p] = (T)val;\
  8989. }\
  8990. }\
  8991. } break;\
  8992. }\
  8993. } break;\
  8994. case GS_TOKEN_DOLLAR:\
  8995. {\
  8996. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);\
  8997. if (!ret) {\
  8998. gs_log_warning("Unidentified token.");\
  8999. return false;\
  9000. }\
  9001. token = gs_lexer_current_token(lex);\
  9002. gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);\
  9003. uint64_t hash = gs_hash_str64(TMP);\
  9004. gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?\
  9005. gs_hash_table_getp(variables->variables, hash) : NULL;\
  9006. if (!var) {\
  9007. gs_log_warning("Variable not found: %s", TMP);\
  9008. return false;\
  9009. }\
  9010. T val = var->val.number;\
  9011. se.value = val;\
  9012. switch (state) {\
  9013. case GS_GUI_ELEMENT_STATE_DEFAULT:\
  9014. {\
  9015. if (idsl) {\
  9016. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se);\
  9017. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se);\
  9018. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se);\
  9019. }\
  9020. else {\
  9021. for (uint32_t p = 0; p < 4; ++p) {\
  9022. ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP[p] = val;\
  9023. ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP[p] = val;\
  9024. ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP[p] = val;\
  9025. }\
  9026. }\
  9027. } break;\
  9028. default:\
  9029. {\
  9030. if (idsl) gs_dyn_array_push(idsl->styles[state], se);\
  9031. else {\
  9032. for (uint32_t p = 0; p < 4; ++p) {\
  9033. ss->styles[elementid][state].COMP[p] = val;\
  9034. }\
  9035. }\
  9036. } break;\
  9037. }\
  9038. ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);\
  9039. if (!ret) {\
  9040. gs_log_warning("Missing semicolon.");\
  9041. return false;\
  9042. }\
  9043. token = gs_lexer_current_token(lex);\
  9044. } break;\
  9045. }\
  9046. } while (0)
  9047. #define SET_VAL2(COMP0, COMP1, T, SE0, SE1)\
  9048. do {\
  9049. gs_gui_style_element_t se0 = gs_default_val();\
  9050. gs_gui_style_element_t se1 = gs_default_val();\
  9051. se0.type = SE0;\
  9052. se1.type = SE1;\
  9053. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);\
  9054. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
  9055. if (!ret) {\
  9056. gs_log_warning("Unidentified token.");\
  9057. return false;\
  9058. }\
  9059. token = gs_lexer_current_token(lex);\
  9060. T val = 0;\
  9061. switch (token.type)\
  9062. {\
  9063. case GS_TOKEN_NUMBER:\
  9064. {\
  9065. gs_snprintfc(TMP, 10, "%.*s", token.len, token.text);\
  9066. val = (T)atoi(TMP);\
  9067. } break;\
  9068. case GS_TOKEN_DOLLAR:\
  9069. {\
  9070. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);\
  9071. if (!ret) {\
  9072. gs_log_warning("Unidentified token.");\
  9073. return false;\
  9074. }\
  9075. token = gs_lexer_current_token(lex);\
  9076. gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);\
  9077. uint64_t hash = gs_hash_str64(TMP);\
  9078. gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?\
  9079. gs_hash_table_getp(variables->variables, hash) : NULL;\
  9080. if (!var) {\
  9081. gs_log_warning("Variable not found: %s", TMP);\
  9082. return false;\
  9083. }\
  9084. val = (T)var->val.number;\
  9085. ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);\
  9086. if (!ret) {\
  9087. gs_log_warning("Missing semicolon.");\
  9088. return false;\
  9089. }\
  9090. token = gs_lexer_current_token(lex);\
  9091. } break;\
  9092. }\
  9093. switch (state)\
  9094. {\
  9095. case GS_GUI_ELEMENT_STATE_DEFAULT:\
  9096. {\
  9097. if (idsl)\
  9098. {\
  9099. se0.value = val;\
  9100. se1.value = val;\
  9101. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se0);\
  9102. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se0);\
  9103. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se0);\
  9104. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se1);\
  9105. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se1);\
  9106. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se1);\
  9107. }\
  9108. else\
  9109. {\
  9110. ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP0 = val;\
  9111. ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP0 = val;\
  9112. ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP0 = val;\
  9113. ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP1 = val;\
  9114. ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP1 = val;\
  9115. ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP1 = val;\
  9116. }\
  9117. } break;\
  9118. default:\
  9119. {\
  9120. if (idsl)\
  9121. {\
  9122. se0.value = val;\
  9123. se1.value = val;\
  9124. gs_dyn_array_push(idsl->styles[state], se0);\
  9125. gs_dyn_array_push(idsl->styles[state], se1);\
  9126. }\
  9127. else\
  9128. {\
  9129. ss->styles[elementid][state].COMP0 = val;\
  9130. ss->styles[elementid][state].COMP1 = val;\
  9131. }\
  9132. } break;\
  9133. }\
  9134. } while (0)
  9135. #define SET_VAL(COMP, T, SE)\
  9136. do {\
  9137. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_COLON);\
  9138. token = gs_lexer_current_token(lex);\
  9139. ret &= (gs_lexer_require_token_type(lex, GS_TOKEN_DOLLAR) || gs_lexer_require_token_type(lex, GS_TOKEN_NUMBER));\
  9140. token = gs_lexer_current_token(lex);\
  9141. if (!ret) {\
  9142. gs_log_warning("Unidentified token: %.*s", token.len, token.text);\
  9143. return false;\
  9144. }\
  9145. gs_gui_style_element_t se = gs_default_val();\
  9146. se.type = SE;\
  9147. T val = 0;\
  9148. switch (token.type)\
  9149. {\
  9150. case GS_TOKEN_NUMBER:\
  9151. {\
  9152. gs_snprintfc(TMP, 10, "%.*s", token.len, token.text);\
  9153. val = (T)atoi(TMP);\
  9154. } break;\
  9155. \
  9156. case GS_TOKEN_DOLLAR:\
  9157. {\
  9158. bool ret = gs_lexer_require_token_type(lex, GS_TOKEN_IDENTIFIER);\
  9159. if (!ret) {\
  9160. gs_log_warning("Unidentified token.");\
  9161. return false;\
  9162. }\
  9163. token = gs_lexer_current_token(lex);\
  9164. gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);\
  9165. uint64_t hash = gs_hash_str64(TMP);\
  9166. gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ?\
  9167. gs_hash_table_getp(variables->variables, hash) : NULL;\
  9168. if (!var) {\
  9169. gs_log_warning("Variable not found: %s", TMP);\
  9170. return false;\
  9171. }\
  9172. val = (T)var->val.number;\
  9173. ret = gs_lexer_require_token_type(lex, GS_TOKEN_SEMICOLON);\
  9174. if (!ret) {\
  9175. gs_log_warning("Missing semicolon.");\
  9176. return false;\
  9177. }\
  9178. token = gs_lexer_current_token(lex);\
  9179. } break;\
  9180. }\
  9181. switch (state)\
  9182. {\
  9183. case GS_GUI_ELEMENT_STATE_DEFAULT:\
  9184. {\
  9185. if (idsl)\
  9186. {\
  9187. se.value = (int32_t)val;\
  9188. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_DEFAULT], se);\
  9189. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_HOVER], se);\
  9190. gs_dyn_array_push(idsl->styles[GS_GUI_ELEMENT_STATE_FOCUS], se);\
  9191. }\
  9192. else\
  9193. {\
  9194. ss->styles[elementid][GS_GUI_ELEMENT_STATE_DEFAULT].COMP = val;\
  9195. ss->styles[elementid][GS_GUI_ELEMENT_STATE_HOVER].COMP = val;\
  9196. ss->styles[elementid][GS_GUI_ELEMENT_STATE_FOCUS].COMP = val;\
  9197. }\
  9198. } break;\
  9199. default:\
  9200. {\
  9201. if (idsl)\
  9202. {\
  9203. se.value = (int32_t)val;\
  9204. gs_dyn_array_push(idsl->styles[state], se);\
  9205. }\
  9206. else\
  9207. {\
  9208. ss->styles[elementid][state].COMP = val;\
  9209. }\
  9210. } break;\
  9211. }\
  9212. } while (0)
  9213. if (gs_token_compare_text(&token, "width")) SET_VAL(size[0], float, GS_GUI_STYLE_WIDTH);
  9214. else if (gs_token_compare_text(&token, "height")) SET_VAL(size[1], float, GS_GUI_STYLE_HEIGHT);
  9215. else if (gs_token_compare_text(&token, "padding")) SET_VAL4(padding, int16_t, GS_GUI_STYLE_PADDING);
  9216. else if (gs_token_compare_text(&token, "padding_left")) SET_VAL(padding[GS_GUI_PADDING_LEFT], int16_t, GS_GUI_STYLE_PADDING_LEFT);
  9217. else if (gs_token_compare_text(&token, "padding_right")) SET_VAL(padding[GS_GUI_PADDING_RIGHT], int16_t, GS_GUI_STYLE_PADDING_RIGHT);
  9218. else if (gs_token_compare_text(&token, "padding_top")) SET_VAL(padding[GS_GUI_PADDING_TOP], int16_t, GS_GUI_STYLE_PADDING_TOP);
  9219. else if (gs_token_compare_text(&token, "padding_bottom")) SET_VAL(padding[GS_GUI_PADDING_BOTTOM], int16_t, GS_GUI_STYLE_PADDING_BOTTOM);
  9220. else if (gs_token_compare_text(&token, "margin")) SET_VAL4(margin, int16_t, GS_GUI_STYLE_MARGIN);
  9221. else if (gs_token_compare_text(&token, "margin_left")) SET_VAL(margin[GS_GUI_MARGIN_LEFT], int16_t, GS_GUI_STYLE_MARGIN_LEFT);
  9222. else if (gs_token_compare_text(&token, "margin_right")) SET_VAL(margin[GS_GUI_MARGIN_RIGHT], int16_t, GS_GUI_STYLE_MARGIN_RIGHT);
  9223. else if (gs_token_compare_text(&token, "margin_top")) SET_VAL(margin[GS_GUI_MARGIN_TOP], int16_t, GS_GUI_STYLE_MARGIN_TOP);
  9224. else if (gs_token_compare_text(&token, "margin_bottom")) SET_VAL(margin[GS_GUI_MARGIN_BOTTOM], int16_t, GS_GUI_STYLE_MARGIN_BOTTOM);
  9225. else if (gs_token_compare_text(&token, "border")) SET_VAL4(border_width, int16_t, GS_GUI_STYLE_BORDER_WIDTH);
  9226. else if (gs_token_compare_text(&token, "border_left")) SET_VAL(border_width[0], int16_t, GS_GUI_STYLE_BORDER_WIDTH_LEFT);
  9227. else if (gs_token_compare_text(&token, "border_right")) SET_VAL(border_width[1], int16_t, GS_GUI_STYLE_BORDER_WIDTH_RIGHT);
  9228. else if (gs_token_compare_text(&token, "border_top")) SET_VAL(border_width[2], int16_t, GS_GUI_STYLE_BORDER_WIDTH_TOP);
  9229. else if (gs_token_compare_text(&token, "border_bottom")) SET_VAL(border_width[3], int16_t, GS_GUI_STYLE_BORDER_WIDTH_BOTTOM);
  9230. 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);
  9231. return true;
  9232. }
  9233. bool _gs_gui_style_sheet_parse_attribute_color(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
  9234. uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
  9235. {
  9236. // Name of color attribute
  9237. gs_token_t token = gs_lexer_current_token(lex);
  9238. // gs_token_debug_print(&token);
  9239. gs_gui_style_list_t* idsl = NULL;
  9240. if (id_tag)
  9241. {
  9242. const uint64_t idhash = id_tag;
  9243. if (!gs_hash_table_exists(ss->cid_styles, idhash))
  9244. {
  9245. gs_gui_style_list_t sl = gs_default_val();
  9246. gs_hash_table_insert(ss->cid_styles, idhash, sl);
  9247. }
  9248. idsl = gs_hash_table_getp(ss->cid_styles, idhash);
  9249. }
  9250. gs_gui_style_element_type type = (gs_gui_style_element_type)0x00;
  9251. int32_t color = GS_GUI_COLOR_BACKGROUND;
  9252. if (gs_token_compare_text(&token, "color_background")) {color = GS_GUI_COLOR_BACKGROUND; type = GS_GUI_STYLE_COLOR_BACKGROUND;}
  9253. else if (gs_token_compare_text(&token, "color_border")) {color = GS_GUI_COLOR_BORDER; type = GS_GUI_STYLE_COLOR_BORDER;}
  9254. else if (gs_token_compare_text(&token, "color_shadow")) {color = GS_GUI_COLOR_SHADOW; type = GS_GUI_STYLE_COLOR_SHADOW;}
  9255. else if (gs_token_compare_text(&token, "color_content")) {color = GS_GUI_COLOR_CONTENT; type = GS_GUI_STYLE_COLOR_CONTENT;}
  9256. else if (gs_token_compare_text(&token, "color_content_background")) {color = GS_GUI_COLOR_CONTENT_BACKGROUND; type = GS_GUI_STYLE_COLOR_CONTENT_BACKGROUND;}
  9257. else if (gs_token_compare_text(&token, "color_content_border")) {color = GS_GUI_COLOR_CONTENT_BORDER; type = GS_GUI_STYLE_COLOR_CONTENT_BORDER;}
  9258. else if (gs_token_compare_text(&token, "color_content_shadow")) {color = GS_GUI_COLOR_CONTENT_SHADOW; type = GS_GUI_STYLE_COLOR_CONTENT_SHADOW;}
  9259. else
  9260. {
  9261. gs_log_warning("Unidentified attribute: %.*s.", token.len, token.text);
  9262. return false;
  9263. }
  9264. token = gs_lexer_next_token(lex);
  9265. if (token.type != GS_TOKEN_COLON)
  9266. {
  9267. gs_log_warning("Unidentified token. (Expected colon after attribute type).");
  9268. gs_token_debug_print(&token);
  9269. return false;
  9270. }
  9271. gs_gui_style_element_t se = gs_default_val();
  9272. se.type = type;
  9273. token = gs_lexer_next_token(lex);
  9274. // gs_println("Parsing color: %.*s", token.len, token.text);
  9275. if (gs_token_compare_text(&token, "rgba") ||
  9276. gs_token_compare_text(&token, "rgb")
  9277. )
  9278. {
  9279. int32_t i = 0;
  9280. while (gs_lexer_can_lex(lex) && token.type != GS_TOKEN_RPAREN)
  9281. {
  9282. token = gs_lexer_next_token(lex);
  9283. switch (token.type)
  9284. {
  9285. case GS_TOKEN_NUMBER:
  9286. {
  9287. if (i < 4)
  9288. {
  9289. gs_snprintfc(TMP, 10, "%.*s", token.len, token.text);
  9290. uint8_t val = (uint8_t)atoi(TMP);
  9291. if (idsl)
  9292. {
  9293. se.color.rgba[i] = val;
  9294. }
  9295. else
  9296. {
  9297. #define SET_COLOR(STATE)\
  9298. do {\
  9299. switch (i)\
  9300. {\
  9301. case 0: ss->styles[elementid][STATE].colors[color].r = val; break;\
  9302. case 1: ss->styles[elementid][STATE].colors[color].g = val; break;\
  9303. case 2: ss->styles[elementid][STATE].colors[color].b = val; break;\
  9304. case 3: ss->styles[elementid][STATE].colors[color].a = val; break;\
  9305. }\
  9306. } while (0)
  9307. switch (state)
  9308. {
  9309. case GS_GUI_ELEMENT_STATE_HOVER:
  9310. case GS_GUI_ELEMENT_STATE_FOCUS:
  9311. {
  9312. SET_COLOR(state);
  9313. } break;
  9314. case GS_GUI_ELEMENT_STATE_DEFAULT:
  9315. {
  9316. for (uint32_t s = 0; s < 3; ++s) {
  9317. SET_COLOR(s);
  9318. }
  9319. } break;
  9320. }
  9321. }
  9322. i++;
  9323. // gs_token_debug_print(&token);
  9324. }
  9325. } break;
  9326. }
  9327. }
  9328. // Set alpha, if not provided
  9329. if (i < 4)
  9330. {
  9331. ss->styles[elementid][state].colors[color].a = 255;
  9332. se.color.a = 255;
  9333. }
  9334. // Push style element
  9335. if (idsl)
  9336. {
  9337. switch (state)
  9338. {
  9339. case GS_GUI_ELEMENT_STATE_DEFAULT:
  9340. {
  9341. for (uint32_t s = 0; s < 3; ++s) {
  9342. gs_dyn_array_push(idsl->styles[s], se);
  9343. }
  9344. } break;
  9345. default:
  9346. {
  9347. gs_dyn_array_push(idsl->styles[state], se);
  9348. } break;
  9349. }
  9350. }
  9351. }
  9352. else if (gs_token_compare_text(&token, "$"))
  9353. {
  9354. token = gs_lexer_next_token(lex);
  9355. if (token.type != GS_TOKEN_IDENTIFIER)
  9356. {
  9357. gs_log_warning("Unidentified symbol found: %.*s. Expecting identifier for variable name.", token.len, token.text);
  9358. return false;
  9359. }
  9360. gs_snprintfc(TMP, 256, "%.*s", token.len, token.text);
  9361. uint64_t hash = gs_hash_str64(TMP);
  9362. gs_gui_ss_var_def_t* var = gs_hash_table_exists(variables->variables, hash) ? gs_hash_table_getp(variables->variables, hash) : NULL;
  9363. if (var) {
  9364. se.color = var->val.color;
  9365. switch (state)
  9366. {
  9367. default:
  9368. {
  9369. if (idsl) gs_dyn_array_push(idsl->styles[state], se);
  9370. else ss->styles[elementid][state].colors[color] = var->val.color;
  9371. } break;
  9372. case GS_GUI_ELEMENT_STATE_DEFAULT:
  9373. {
  9374. for (uint32_t s = 0; s < 3; ++s) {
  9375. if (idsl) gs_dyn_array_push(idsl->styles[s], se);
  9376. else ss->styles[elementid][s].colors[color] = var->val.color;
  9377. }
  9378. } break;
  9379. }
  9380. }
  9381. else
  9382. {
  9383. gs_log_warning("Variable not found: %.*s.", token.len, token.text);
  9384. }
  9385. token = gs_lexer_next_token(lex);
  9386. if (token.type != GS_TOKEN_SEMICOLON)
  9387. {
  9388. gs_log_warning("Syntax error. Expecting semicolon, found: %.*s.", token.len, token.text);
  9389. return false;
  9390. }
  9391. }
  9392. else
  9393. {
  9394. gs_log_warning("Unidentified color type found: %.*s. (Expect either 'rgba' or 'rgb').", token.len, token.text);
  9395. return false;
  9396. }
  9397. return true;
  9398. }
  9399. bool _gs_gui_style_sheet_parse_attribute(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
  9400. uint64_t id_tag, int32_t elementid, int32_t state, gs_gui_ss_variables_t* variables)
  9401. {
  9402. // Name of attribute
  9403. gs_token_t token = gs_lexer_current_token(lex);
  9404. if (gs_token_compare_text(&token, "color_background") ||
  9405. gs_token_compare_text(&token, "color_border") ||
  9406. gs_token_compare_text(&token, "color_shadow") ||
  9407. gs_token_compare_text(&token, "color_content") ||
  9408. gs_token_compare_text(&token, "color_content_background") ||
  9409. gs_token_compare_text(&token, "color_content_border") ||
  9410. gs_token_compare_text(&token, "color_content_shadow")
  9411. )
  9412. {
  9413. if (!_gs_gui_style_sheet_parse_attribute_color(ctx, lex, ss, id_tag, elementid, state, variables))
  9414. {
  9415. gs_log_warning("Failed to parse color attribute.");
  9416. return false;
  9417. }
  9418. }
  9419. else if (gs_token_compare_text(&token, "width") ||
  9420. gs_token_compare_text(&token, "height") ||
  9421. gs_token_compare_text(&token, "padding") ||
  9422. gs_token_compare_text(&token, "padding_left") ||
  9423. gs_token_compare_text(&token, "padding_right") ||
  9424. gs_token_compare_text(&token, "padding_top") ||
  9425. gs_token_compare_text(&token, "padding_bottom") ||
  9426. gs_token_compare_text(&token, "margin") ||
  9427. gs_token_compare_text(&token, "margin_left") ||
  9428. gs_token_compare_text(&token, "margin_right") ||
  9429. gs_token_compare_text(&token, "margin_top") ||
  9430. gs_token_compare_text(&token, "margin_bottom") ||
  9431. gs_token_compare_text(&token, "border") ||
  9432. gs_token_compare_text(&token, "border_left") ||
  9433. gs_token_compare_text(&token, "border_right") ||
  9434. gs_token_compare_text(&token, "border_top") ||
  9435. gs_token_compare_text(&token, "border_bottom") ||
  9436. gs_token_compare_text(&token, "shadow")
  9437. )
  9438. {
  9439. if (!_gs_gui_style_sheet_parse_attribute_val(ctx, lex, ss, id_tag, elementid, state, variables))
  9440. {
  9441. gs_log_warning("Failed to parse value attribute.");
  9442. return false;
  9443. }
  9444. }
  9445. else if (gs_token_compare_text(&token, "justify_content") ||
  9446. gs_token_compare_text(&token, "align_content")
  9447. )
  9448. {
  9449. if (!_gs_gui_style_sheet_parse_attribute_enum(ctx, lex, ss, id_tag, elementid, state, variables))
  9450. {
  9451. gs_log_warning("Failed to parse enum attribute.");
  9452. return false;
  9453. }
  9454. }
  9455. else if (gs_token_compare_text(&token, "font"))
  9456. {
  9457. if (!_gs_gui_style_sheet_parse_attribute_font(ctx, lex, ss, id_tag, elementid, state, variables))
  9458. {
  9459. gs_log_warning("Failed to parse font attribute.");
  9460. return false;
  9461. }
  9462. }
  9463. else if (gs_token_compare_text(&token, "transition"))
  9464. {
  9465. if (!_gs_gui_style_sheet_parse_attribute_transition(ctx, lex, ss, id_tag, elementid, state, variables))
  9466. {
  9467. gs_log_warning("Failed to parse transition attribute.");
  9468. return false;
  9469. }
  9470. }
  9471. else
  9472. {
  9473. gs_log_warning("Unidentified attribute: %.*s.", token.len, token.text);
  9474. return false;
  9475. }
  9476. return true;
  9477. }
  9478. bool _gs_gui_style_sheet_parse_element(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
  9479. int32_t elementid, gs_gui_ss_variables_t* variables)
  9480. {
  9481. int32_t state = 0x00;
  9482. int32_t bc = 0;
  9483. gs_token_t token = gs_lexer_next_token(lex);
  9484. if (token.type == GS_TOKEN_COLON)
  9485. {
  9486. token = gs_lexer_next_token(lex);
  9487. if (token.type != GS_TOKEN_IDENTIFIER)
  9488. {
  9489. gs_log_warning("Unidentified Token. (Expected identifier after colon).");
  9490. gs_token_debug_print(&token);
  9491. return false;
  9492. }
  9493. if (gs_token_compare_text(&token, "focus")) state = GS_GUI_ELEMENT_STATE_FOCUS;
  9494. else if (gs_token_compare_text(&token, "hover")) state = GS_GUI_ELEMENT_STATE_HOVER;
  9495. else
  9496. {
  9497. gs_log_warning("Unidentified element state provided: %.*s", token.len, token.text);
  9498. return false;
  9499. }
  9500. // Get rbrace
  9501. token = gs_lexer_next_token(lex);
  9502. }
  9503. if (token.type != GS_TOKEN_LBRACE)
  9504. {
  9505. gs_log_warning("Unidentified token. (Expected brace after element declaration).");
  9506. gs_token_debug_print(&token);
  9507. return false;
  9508. }
  9509. bc = 1;
  9510. while (gs_lexer_can_lex(lex) && bc)
  9511. {
  9512. token = gs_lexer_next_token(lex);
  9513. switch (token.type)
  9514. {
  9515. case GS_TOKEN_LBRACE: bc++; break;
  9516. case GS_TOKEN_RBRACE: bc--; break;
  9517. case GS_TOKEN_IDENTIFIER:
  9518. {
  9519. // gs_println("Parsing attribute: %.*s", token.len, token.text);
  9520. if (!_gs_gui_style_sheet_parse_attribute(ctx, lex, ss, 0, elementid, state, variables))
  9521. {
  9522. gs_log_warning("Unable to parse attribute");
  9523. return false;
  9524. }
  9525. } break;
  9526. }
  9527. }
  9528. return true;
  9529. }
  9530. bool _gs_gui_style_sheet_parse_cid_tag(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
  9531. const uint64_t cid_tag, gs_gui_ss_variables_t* variables)
  9532. {
  9533. int32_t state = 0x00;
  9534. int32_t bc = 0;
  9535. gs_token_t token = gs_lexer_next_token(lex);
  9536. if (token.type == GS_TOKEN_COLON)
  9537. {
  9538. token = gs_lexer_next_token(lex);
  9539. if (token.type != GS_TOKEN_IDENTIFIER)
  9540. {
  9541. gs_log_warning("Unidentified Token. (Expected identifier after colon).");
  9542. gs_token_debug_print(&token);
  9543. return false;
  9544. }
  9545. if (gs_token_compare_text(&token, "focus")) state = GS_GUI_ELEMENT_STATE_FOCUS;
  9546. else if (gs_token_compare_text(&token, "hover")) state = GS_GUI_ELEMENT_STATE_HOVER;
  9547. else
  9548. {
  9549. gs_log_warning("Unidentified element state provided: %.*s", token.len, token.text);
  9550. return false;
  9551. }
  9552. // Get rbrace
  9553. token = gs_lexer_next_token(lex);
  9554. }
  9555. if (token.type != GS_TOKEN_LBRACE)
  9556. {
  9557. gs_log_warning("Unidentified token. (Expected brace after element declaration).");
  9558. gs_token_debug_print(&token);
  9559. return false;
  9560. }
  9561. bc = 1;
  9562. while (gs_lexer_can_lex(lex) && bc)
  9563. {
  9564. token = gs_lexer_next_token(lex);
  9565. switch (token.type)
  9566. {
  9567. case GS_TOKEN_LBRACE: bc++; break;
  9568. case GS_TOKEN_RBRACE: bc--; break;
  9569. case GS_TOKEN_IDENTIFIER:
  9570. {
  9571. // gs_println("Parsing attribute: %.*s", token.len, token.text);
  9572. if (!_gs_gui_style_sheet_parse_attribute(ctx, lex, ss, cid_tag, GS_GUI_ELEMENT_COUNT, state, variables))
  9573. {
  9574. gs_log_warning("Unable to parse attribute");
  9575. return false;
  9576. }
  9577. } break;
  9578. }
  9579. }
  9580. return true;
  9581. }
  9582. GS_API_DECL gs_gui_style_sheet_t gs_gui_style_sheet_load_from_file(gs_gui_context_t* ctx, const char* file_path)
  9583. {
  9584. // Generate new style sheet based on default element styles
  9585. gs_gui_style_sheet_t ss = gs_default_val();
  9586. bool success = true;
  9587. size_t sz = 0;
  9588. char* fd = gs_platform_read_file_contents(file_path, "rb", &sz);
  9589. if (!fd) {
  9590. gs_log_warning("Cannot load file: %s", file_path);
  9591. return ss;
  9592. }
  9593. ss = gs_gui_style_sheet_load_from_memory(ctx, fd, sz, &success);
  9594. if (success) {
  9595. gs_log_success("Successfully loaded style sheet %s.", file_path);
  9596. }
  9597. else {
  9598. gs_log_warning("Failed to loaded style sheet %s.", file_path);
  9599. }
  9600. gs_free(fd);
  9601. return ss;
  9602. }
  9603. static bool _gs_gui_style_sheet_parse_variable(gs_gui_context_t* ctx, gs_lexer_t* lex, gs_gui_style_sheet_t* ss,
  9604. char* name_buf, size_t sz, gs_gui_ss_var_def_t* out)
  9605. {
  9606. // Get next token, needs to be identifier
  9607. gs_token_t token = gs_lexer_next_token(lex);
  9608. if (token.type != GS_TOKEN_IDENTIFIER)
  9609. {
  9610. gs_log_warning("Unidentified token. (Expected variable name after percent sign).");
  9611. gs_token_debug_print(&token);
  9612. return false;
  9613. }
  9614. // Copy name of variable
  9615. memcpy(name_buf, token.text, gs_min(token.len, sz));
  9616. // Expect colon
  9617. token = gs_lexer_next_token(lex);
  9618. if (token.type != GS_TOKEN_COLON)
  9619. {
  9620. gs_log_warning("Syntax error. (Expected colon name after variable name).");
  9621. gs_token_debug_print(&token);
  9622. return false;
  9623. }
  9624. // Now to get variable
  9625. token = gs_lexer_next_token(lex);
  9626. while (gs_lexer_can_lex(lex) && token.type != GS_TOKEN_SEMICOLON)
  9627. {
  9628. switch (token.type)
  9629. {
  9630. case GS_TOKEN_IDENTIFIER:
  9631. {
  9632. if (gs_token_compare_text(&token, "rgb"))
  9633. {
  9634. token = gs_lexer_next_token(lex);
  9635. if (token.type != GS_TOKEN_LPAREN)
  9636. {
  9637. gs_log_warning("rgb: missing paren (", token.len, token.text);
  9638. gs_token_debug_print(&token);
  9639. return false;
  9640. }
  9641. out->type = GS_GUI_SS_DEF_COLOR;
  9642. for (uint32_t i = 0; i < 3; ++i)
  9643. {
  9644. token = gs_lexer_next_token(lex);
  9645. if (token.type != GS_TOKEN_NUMBER)
  9646. {
  9647. gs_log_warning("rgb expects numbers", token.len, token.text);
  9648. gs_token_debug_print(&token);
  9649. return false;
  9650. }
  9651. gs_snprintfc(VAL, 32, "%.*s", token.len, token.text);\
  9652. uint8_t v = (uint8_t)atoi(VAL);\
  9653. out->val.color.rgba[i] = v;
  9654. }
  9655. out->val.color.rgba[3] = 255;
  9656. }
  9657. else if (gs_token_compare_text(&token, "rgba"))
  9658. {
  9659. token = gs_lexer_next_token(lex);
  9660. if (token.type != GS_TOKEN_LPAREN)
  9661. {
  9662. gs_log_warning("rgb: missing paren (", token.len, token.text);
  9663. gs_token_debug_print(&token);
  9664. return false;
  9665. }
  9666. out->type = GS_GUI_SS_DEF_COLOR;
  9667. for (uint32_t i = 0; i < 4; ++i)
  9668. {
  9669. token = gs_lexer_next_token(lex);
  9670. if (token.type != GS_TOKEN_NUMBER)
  9671. {
  9672. gs_log_warning("rgb expects numbers", token.len, token.text);
  9673. gs_token_debug_print(&token);
  9674. return false;
  9675. }
  9676. gs_snprintfc(VAL, 32, "%.*s", token.len, token.text);\
  9677. uint8_t v = (uint8_t)atoi(VAL);\
  9678. out->val.color.rgba[i] = v;
  9679. }
  9680. }
  9681. else if (gs_token_compare_text(&token, "center"))
  9682. {
  9683. out->type = GS_GUI_SS_DEF_ENUM;
  9684. out->val.number = (int32_t)GS_GUI_JUSTIFY_CENTER;
  9685. }
  9686. else if (gs_token_compare_text(&token, "start"))
  9687. {
  9688. out->type = GS_GUI_SS_DEF_ENUM;
  9689. out->val.number = (int32_t)GS_GUI_JUSTIFY_START;
  9690. }
  9691. else if (gs_token_compare_text(&token, "end"))
  9692. {
  9693. out->type = GS_GUI_SS_DEF_ENUM;
  9694. out->val.number = (int32_t)GS_GUI_JUSTIFY_END;
  9695. }
  9696. else
  9697. {
  9698. gs_log_warning("Variable value unknown: %.*s", token.len, token.text);
  9699. gs_token_debug_print(&token);
  9700. return false;
  9701. }
  9702. } break;
  9703. case GS_TOKEN_NUMBER:
  9704. {
  9705. gs_snprintfc(VAL, 32, "%.*s", token.len, token.text);\
  9706. int32_t v = (int32_t)atoi(VAL);\
  9707. out->type = GS_GUI_SS_DEF_NUMBER;
  9708. out->val.number = v;
  9709. } break;
  9710. case GS_TOKEN_STRING:
  9711. {
  9712. gs_snprintfc(VAL, 64, "%.*s", token.len - 2, token.text + 1);\
  9713. out->type = GS_GUI_SS_DEF_STR;
  9714. memcpy(out->val.str, VAL, 64);
  9715. }
  9716. }
  9717. token = gs_lexer_next_token(lex);
  9718. }
  9719. return true;
  9720. }
  9721. // Going to require a lot of parsing
  9722. 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)
  9723. {
  9724. // Generate new style sheet based on default element styles
  9725. gs_gui_style_sheet_t ss = gs_default_val();
  9726. bool success = true;
  9727. gs_gui_ss_variables_t variables = gs_default_val();
  9728. // Copy all default styles
  9729. GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_CONTAINER);
  9730. GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_LABEL);
  9731. GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_TEXT);
  9732. GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_PANEL);
  9733. GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_INPUT);
  9734. GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_BUTTON);
  9735. GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_SCROLL);
  9736. GS_GUI_COPY_STYLES(ss.styles, gs_gui_default_style_sheet.styles, GS_GUI_ELEMENT_IMAGE);
  9737. #define PARSE_ELEMENT(TYPE, TYPESTR)\
  9738. do {\
  9739. if (!_gs_gui_style_sheet_parse_element(ctx, &lex, &ss, TYPE, &variables))\
  9740. {\
  9741. gs_log_warning("Failed to parse element: %s", TYPESTR);\
  9742. success = false;\
  9743. break;\
  9744. }\
  9745. } while (0)
  9746. // Parse style sheet for styles
  9747. gs_lexer_t lex = gs_lexer_c_ctor(fd);
  9748. while (success && gs_lexer_c_can_lex(&lex))
  9749. {
  9750. gs_token_t token = lex.next_token(&lex);
  9751. switch (token.type)
  9752. {
  9753. case GS_TOKEN_IDENTIFIER:
  9754. {
  9755. if (gs_token_compare_text(&token, "button")) PARSE_ELEMENT(GS_GUI_ELEMENT_BUTTON, "button");
  9756. else if (gs_token_compare_text(&token, "text")) PARSE_ELEMENT(GS_GUI_ELEMENT_TEXT, "text");
  9757. else if (gs_token_compare_text(&token, "label")) PARSE_ELEMENT(GS_GUI_ELEMENT_LABEL, "label");
  9758. else if (gs_token_compare_text(&token, "image")) PARSE_ELEMENT(GS_GUI_ELEMENT_IMAGE, "image");
  9759. else if (gs_token_compare_text(&token, "scroll")) PARSE_ELEMENT(GS_GUI_ELEMENT_SCROLL, "scroll");
  9760. else if (gs_token_compare_text(&token, "panel")) PARSE_ELEMENT(GS_GUI_ELEMENT_PANEL, "panel");
  9761. else if (gs_token_compare_text(&token, "container")) PARSE_ELEMENT(GS_GUI_ELEMENT_CONTAINER, "container");
  9762. else if (gs_token_compare_text(&token, "input")) PARSE_ELEMENT(GS_GUI_ELEMENT_INPUT, "input");
  9763. else
  9764. {
  9765. gs_log_warning("Unidentified token. (Invalid element type found).");
  9766. gs_token_debug_print(&token);
  9767. break;
  9768. }
  9769. } break;
  9770. case GS_TOKEN_PERIOD:
  9771. {
  9772. // Do single class for now
  9773. gs_token_t cls_tag = gs_lexer_next_token(&lex);
  9774. char CLS_TAG[256] = gs_default_val();
  9775. CLS_TAG[0] = '.';
  9776. memcpy(CLS_TAG + 1, cls_tag.text, cls_tag.len);
  9777. uint64_t cls_hash = gs_hash_str64(CLS_TAG);
  9778. if (!_gs_gui_style_sheet_parse_cid_tag(ctx, &lex, &ss, cls_hash, &variables))
  9779. {
  9780. gs_log_warning("Failed to parse id tag: %s", CLS_TAG);
  9781. success = false;
  9782. break;
  9783. }
  9784. } break;
  9785. case GS_TOKEN_HASH:
  9786. {
  9787. gs_token_t id_tag = gs_lexer_next_token(&lex);
  9788. char ID_TAG[256] = gs_default_val();
  9789. ID_TAG[0] = '#';
  9790. memcpy(ID_TAG + 1, id_tag.text, id_tag.len);
  9791. uint64_t id_hash = gs_hash_str64(ID_TAG);
  9792. if (!_gs_gui_style_sheet_parse_cid_tag(ctx, &lex, &ss, id_hash, &variables))
  9793. {
  9794. gs_log_warning("Failed to parse id tag: %s", ID_TAG);
  9795. success = false;
  9796. break;
  9797. }
  9798. } break;
  9799. case GS_TOKEN_ASTERISK:
  9800. {
  9801. // Save token
  9802. gs_token_t token = gs_lexer_next_token(&lex); gs_lexer_set_token(&lex, token);
  9803. PARSE_ELEMENT(GS_GUI_ELEMENT_CONTAINER, "* (container)"); gs_lexer_set_token(&lex, token);
  9804. PARSE_ELEMENT(GS_GUI_ELEMENT_TEXT, "* (text)"); gs_lexer_set_token(&lex, token);
  9805. PARSE_ELEMENT(GS_GUI_ELEMENT_LABEL, "* (label)"); gs_lexer_set_token(&lex, token);
  9806. PARSE_ELEMENT(GS_GUI_ELEMENT_IMAGE, "* (image)"); gs_lexer_set_token(&lex, token);
  9807. PARSE_ELEMENT(GS_GUI_ELEMENT_BUTTON, "* (button)"); gs_lexer_set_token(&lex, token);
  9808. PARSE_ELEMENT(GS_GUI_ELEMENT_PANEL, "* (panel)"); gs_lexer_set_token(&lex, token);
  9809. PARSE_ELEMENT(GS_GUI_ELEMENT_INPUT, "* (input)"); gs_lexer_set_token(&lex, token);
  9810. PARSE_ELEMENT(GS_GUI_ELEMENT_SCROLL, "* (scroll)");
  9811. } break;
  9812. case GS_TOKEN_DOLLAR:
  9813. {
  9814. gs_gui_ss_var_def_t variable = gs_default_val();
  9815. char variable_name[256] = gs_default_val();
  9816. if (!_gs_gui_style_sheet_parse_variable(ctx, &lex, &ss, variable_name, sizeof(variable_name), &variable))
  9817. {
  9818. gs_log_warning("Failed to parse variable: %s", variable_name);
  9819. success = false;
  9820. break;
  9821. }
  9822. else
  9823. {
  9824. gs_hash_table_insert(variables.variables, gs_hash_str64(variable_name), variable);
  9825. }
  9826. /*
  9827. typedef enum
  9828. {
  9829. GS_GUI_SS_DEF_VALUE = 0x00,
  9830. GS_GUI_SS_DEF_COLOR,
  9831. GS_GUI_SS_DEF_STR
  9832. } gs_gui_ss_var_def_type;
  9833. typedef struct
  9834. {
  9835. gs_gui_ss_var_def_type type;
  9836. union {
  9837. int32_t value;
  9838. gs_color_t color;
  9839. char str[64];
  9840. } val;
  9841. } gs_gui_ss_var_def_t;
  9842. */
  9843. } break;
  9844. }
  9845. }
  9846. if (!sp)
  9847. {
  9848. if (success) {
  9849. gs_log_success("Successfully loaded style sheet from memory.");
  9850. }
  9851. else {
  9852. gs_log_warning("Failed to loaded style sheet from memory.");
  9853. }
  9854. }
  9855. else
  9856. {
  9857. *sp = success;
  9858. }
  9859. if (variables.variables) gs_hash_table_free(variables.variables);
  9860. return ss;
  9861. }
  9862. #endif // GS_GUI_IMPL
  9863. #endif // GS_GUI_H