ui.h 139 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223
  1. /*
  2. * ui.h
  3. * https://gitlab.com/bztsrc/smgui
  4. *
  5. * Copyright (C) 2024 bzt (bztsrc@gitlab), MIT license
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to
  9. * deal in the Software without restriction, including without limitation the
  10. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  11. * sell copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
  20. * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
  22. * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. *
  24. * @brief Single header, State-Mode Graphical User Interface ANSI C/C++ library
  25. */
  26. #ifndef UI_H
  27. #define UI_H
  28. #include <stdint.h>
  29. #include <string.h>
  30. #include <stdlib.h>
  31. #ifndef UI_OK
  32. /* return codes */
  33. #define UI_OK 0
  34. #define UI_ERR_BADINP -1
  35. #define UI_ERR_BACKEND -2
  36. #define UI_ERR_NOMEM -3
  37. #ifndef UI_MAXEVENTS
  38. #define UI_MAXEVENTS 16
  39. #endif
  40. #ifndef UI_MAXPOPUPS
  41. #define UI_MAXPOPUPS 16
  42. #endif
  43. #ifdef __cplusplus
  44. extern "C" {
  45. #endif
  46. /* theme colors */
  47. enum {
  48. UI_FG, /* foreground color */
  49. UI_TIT, /* popup and menu title color */
  50. UI_BG, /* popup and menu background color */
  51. UI_SH, /* popup and menu shadow color */
  52. UI_TFG, /* toggle active foreground color */
  53. UI_TBG, /* toggle active background color */
  54. UI_HLFG, /* menu highlight foreground color */
  55. UI_HLBG, /* menu highlight background color */
  56. UI_DFG, /* disabled foreground color */
  57. UI_DBG, /* disabled background color */
  58. UI_SBG, /* scrollbar background color */
  59. UI_IFG, /* input box foreground color */
  60. UI_IL, /* input box light border color */
  61. UI_ID, /* input box dark border color */
  62. UI_IBG, /* input box background color */
  63. UI_ICF, /* input box selected foreground color */
  64. UI_IB, /* input box selected border color */
  65. UI_IC, /* input box selected cursor color */
  66. UI_BFG, /* button foreground color */
  67. UI_BSG, /* button shadow color */
  68. UI_BN, /* button normal outter border color */
  69. UI_SFG, /* button selected foreground color */
  70. UI_SSG, /* button selected shadow color */
  71. UI_BS, /* button selected outter border color */
  72. UI_BL, /* button light inner border color */
  73. UI_BD, /* button dark inner border color */
  74. UI_BGL, /* button light background color */
  75. UI_BGD, /* button dark background color */
  76. UI_PL, /* progressbar light color */
  77. UI_PB, /* progressbar background color */
  78. UI_PD, /* progressbar dark color */
  79. UI_NUMTHEME
  80. };
  81. /* skin images */
  82. enum {
  83. UI_CURSOR, /* the mouse cursor */
  84. UI_P_TL, UI_P_TM, UI_P_TR, /* popup and menu top left corner, top middle, top right corner */
  85. UI_P_ML, UI_P_BG, UI_P_MR, /* popup and menu left, background, right */
  86. UI_P_BL, UI_P_BM, UI_P_BR, /* popup and menu bottom left corner, bottom middle, bottom right corner */
  87. UI_P_TIT, UI_P_CLOSE, /* popup and menu title background, close button */
  88. UI_A_TL, UI_A_TM, UI_A_TR, /* same, but with alternative images */
  89. UI_A_ML, UI_A_BG, UI_A_MR,
  90. UI_A_BL, UI_A_BM, UI_A_BR,
  91. UI_A_TIT, UI_A_CLOSE,
  92. UI_M_L, UI_M_BG, UI_M_R, /* menu alternative left side, background and right side */
  93. UI_HL, /* menu highlight background */
  94. UI_INP, /* input box background */
  95. UI_PBG, /* progress bar button background */
  96. UI_S_L, UI_S_M, UI_S_R, UI_S_B, /* slider left side, background, right side and button */
  97. UI_SV_T, UI_SV_M, UI_SV_B, /* vertical scrollbar background top, middle, bottom */
  98. UI_SV_BT, UI_SV_BM, UI_SV_BB, /* vertical scrollbar button top, middle, bottom */
  99. UI_SH_L, UI_SH_M, UI_SH_R, /* horizontal scrollbar background left, middle, right */
  100. UI_SH_BL, UI_SH_BM, UI_SH_BR, /* horizontal scrollbar button left, middle, right */
  101. UI_CHK0, UI_CHK1, /* checkbox unchecked, checked */
  102. UI_RADIO0, UI_RADIO1, /* radiobutton unchecked, checked */
  103. UI_BNL, UI_BNM, UI_BNR, /* normal button left, middle, right */
  104. UI_BPL, UI_BPM, UI_BPR, /* pressed button left, middle, right */
  105. UI_BSL, UI_BSM, UI_BSR, /* selected button left, middle, right */
  106. UI_LN, UI_DN, UI_RN, /* normal arrow button left, down, right */
  107. UI_LP, UI_DP, UI_RP, /* pressed arrow button left, down, right */
  108. UI_LS, UI_DS, UI_RS, /* selected arrow button left, down, right */
  109. UI_NUMSKIN };
  110. /* field types */
  111. enum { UI_END,
  112. /* containers */
  113. UI_POPUP, UI_MENU, UI_DIV,
  114. /* display labels */
  115. UI_LABEL, UI_STATUS, UI_DEC_FLOAT, UI_PBAR, UI_IMAGE, UI_DEC8, UI_DEC16, UI_DEC32, UI_DEC64, UI_HEX8, UI_HEX16, UI_HEX32, UI_HEX64,
  116. /* input fields */
  117. UI_TXTINP, UI_SELECT, UI_OPTION, UI_FLOAT, UI_INT8, UI_INT16, UI_INT32, UI_INT64, UI_SLIDER, UI_VSCRBAR, UI_HSCRBAR, UI_COLOR,
  118. /* buttons */
  119. UI_TOGGLE, UI_CHECK, UI_RADIO, UI_BUTTON, UI_BTNTGL, UI_BTNICN,
  120. /* lines */
  121. UI_LINES, UI_VCONNECT, UI_HCONNECT, UI_CURVE,
  122. /* custom implemented widget */
  123. UI_CUSTOM
  124. };
  125. #define UI_CONTAINER(x) ((x) > UI_END && (x) <= UI_DIV)
  126. /* ui_form_t positions */
  127. #define UI_REL(x) ((x) & 0xffff)
  128. #define UI_ABS(x) (((x) & 0xffff) | 0x800000)
  129. #define UI_PERCENT(x) (((x)<<16) | 0x800000)
  130. #define UI_PERPLUS(x,p) (((x)<<16) | ((p) & 0xffff))
  131. #define UI_ABS_RIGHT(x) (-((int16_t)(x)) | 0x800000)
  132. #define UI_ABS_BOTTOM(x) (-((int16_t)(x)) | 0x800000)
  133. /* ui_form_t alignments */
  134. #define UI_LEFT 0
  135. #define UI_RIGHT 1
  136. #define UI_CENTER 2
  137. #define UI_TOP (0<<2)
  138. #define UI_BOTTOM (1<<2)
  139. #define UI_MIDDLE (2<<2)
  140. /* ui_form_t flags */
  141. #define UI_HIDDEN 1
  142. #define UI_NOBULLET 2
  143. #define UI_NOHEADER 2
  144. #define UI_NOBR 4
  145. #define UI_FORCEBR 8
  146. #define UI_POINTER 8
  147. #define UI_NOBORDER 16
  148. #define UI_NOSHADOW 32
  149. #define UI_ALTSKIN 64
  150. #define UI_HSCROLL 128
  151. #define UI_VSCROLL 256
  152. #define UI_SCROLL 384
  153. #define UI_DRAGGABLE 512
  154. #define UI_RESIZABLE 1024
  155. #define UI_SELECTED 2048
  156. #define UI_DISABLED 4096
  157. #define UI_REFRESH 1
  158. #define UI_RECALC 2
  159. #define UI_CLOSE 4
  160. #define UI_DONE 8
  161. enum { UI_FILTER_NONE, UI_FILTER_ID, UI_FILTER_VAR, UI_FILTER_EXPR, UI_FILTER_HEX, UI_FILTER_PASS };
  162. enum { UI_EVT_NONE, UI_EVT_MOUSE, UI_EVT_GAMEPAD, UI_EVT_KEY, UI_EVT_DROP, UI_EVT_RESIZE };
  163. enum { UI_BTN_L = 1, UI_BTN_M = 2, UI_BTN_R = 4, UI_BTN_U = 8, UI_BTN_D = 16,
  164. UI_BTN_A = 32, UI_BTN_B = 64, UI_BTN_X = 128, UI_BTN_Y = 256, UI_BTN_BA = 512, UI_BTN_ST = 1024, UI_BTN_GU = 2048,
  165. UI_BTN_LT = 4096, UI_BTN_RT = 8192, UI_BTN_LS = 16384, UI_BTN_RS = 32768 };
  166. enum { UI_BTN_RELEASE = 0x80000, UI_BTN_SHIFT = 0x100000, UI_BTN_CONTROL = 0x200000, UI_BTN_ALT = 0x400000, UI_BTN_GUI = 0x800000 };
  167. typedef struct {
  168. int type, btn, x, y, rx, ry;
  169. char key[8];
  170. const char *fn;
  171. } ui_event_t;
  172. typedef struct ui_form_s ui_form_t;
  173. typedef struct ui_s ui_t;
  174. /**
  175. * The font and custom widget callbacks
  176. */
  177. typedef int (*ui_font_bbox)(void *fnt, char *str, char *end, int *w, int *h, int *l, int *t);
  178. typedef int (*ui_font_draw)(void *fnt, char *str, char *end, uint8_t *dst, uint32_t color, int x, int y, int l, int t, int p,
  179. int cx0, int cy0, int cx1, int cy1);
  180. typedef int (*ui_bbox)(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, int *dw, int *dh);
  181. typedef int (*ui_view)(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form);
  182. typedef int (*ui_ctrl)(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt);
  183. typedef int (*ui_fini)(ui_t *ctx, ui_form_t *form);
  184. typedef int (*ui_comp)(const void *a, const void *b);
  185. /**
  186. * Pixel buffer type
  187. */
  188. typedef struct {
  189. int w, h, p;
  190. uint8_t *buf;
  191. } ui_image_t;
  192. /**
  193. * Form element
  194. */
  195. typedef struct ui_form_s {
  196. char type, align;
  197. short flags;
  198. int x, y, w, h, m, p, l, t, desc;
  199. int ex, ey, ew, eh;
  200. void *ptr;
  201. int64_t min, max, inc;
  202. union {
  203. struct { float fmin, fmax, finc; };
  204. struct { ui_image_t *icon; int value, label, ox, oy, mw, mh, sw, sh; };
  205. struct { ui_bbox bbox; ui_view view; ui_ctrl ctrl; ui_fini fini; ui_comp *cmps; void *data; int str; };
  206. struct { char **optv; int optc, sel; };
  207. };
  208. } ui_form_t;
  209. /**
  210. * The main UI context
  211. */
  212. typedef struct ui_s {
  213. ui_image_t screen; /* important this must be at offset(0) */
  214. ui_image_t skin[UI_NUMSKIN]; /* skin images */
  215. void *bck; /* backend specific */
  216. uint8_t *skinbuf; /* skin buffer if loaded from packed PNG */
  217. uint32_t theme[UI_NUMTHEME]; /* color theme */
  218. int txtc; /* number of texts */
  219. char **txtv; /* texts, localized string array */
  220. void *fnt; /* selected font */
  221. ui_font_bbox bbox; /* font bounding box hook */
  222. ui_font_draw draw; /* font rendering hook */
  223. ui_form_t *form, *menu, *hover, *drag, *resize, *pressed, *vscr, *hscr, *text, *popup; /* for states */
  224. int dx, dy; /* drag delta coordinates */
  225. int ds, dt; /* default font size and default top baseline */
  226. int numpopups; /* number of visible popups */
  227. ui_form_t *popups[UI_MAXPOPUPS];/* visible popups in z order */
  228. volatile int head, tail; /* event queue */
  229. ui_event_t events[UI_MAXEVENTS];
  230. ui_ctrl pe; /* popup event handler */
  231. ui_view dr; /* popup draw handler */
  232. int px, py, pw, ph; /* popup position and size */
  233. volatile uint64_t flags; /* pupop flags */
  234. int hue, sat, val; /* color picker HSV */
  235. uint32_t color, hist[16]; /* color picker current color and history */
  236. char *scr, *cur, *end, *buf, *str; /* edited text buffer */
  237. int maxlen; /* edited text buffer max length */
  238. int mousex, mousey; /* current mouse coordinates */
  239. int lastx, lasty; /* last mouse coordinates */
  240. ui_image_t mouse; /* mouse backbuffer */
  241. int cx0, cy0, cx1, cy1; /* crop area */
  242. int bx, by; /* prev coorindates for Bezier */
  243. int sw, sh, sb, sm, s1, s2; /* scrollbar width, height and some temp variables */
  244. } ui_t;
  245. /* public API */
  246. int ui_fonthook(ui_t *ctx, ui_font_bbox bbox, ui_font_draw draw);
  247. int ui_font(ui_t *ctx, void *fnt);
  248. int ui_swcursor(ui_t *ctx, ui_image_t *cursor);
  249. int ui_hwcursor(ui_t *ctx);
  250. int ui_theme(ui_t *ctx, uint32_t *theme);
  251. int ui_skin(ui_t *ctx, ui_image_t *skin);
  252. int ui_pngskin(ui_t *ctx, uint8_t *png, int size);
  253. int ui_refresh(ui_t *ctx);
  254. int ui_settxt(ui_t *ctx, char **txtv);
  255. char *ui_getclipboard(ui_t *ctx);
  256. int ui_setclipboard(ui_t *ctx, char *str);
  257. int ui_getmouse(ui_t *ctx, int *x, int *y);
  258. int ui_init(ui_t *ctx, int txtc, char **txtv, int w, int h, ui_image_t *icon);
  259. int ui_fullscreen(ui_t *ctx);
  260. void *ui_getwindow(ui_t *ctx);
  261. ui_event_t *ui_event(ui_t *ctx, ui_form_t *form);
  262. int ui_free(ui_t *ctx);
  263. /* private API (for modules) */
  264. void _ui_fit(int mw, int mh, int sw, int sh, int *w, int *h);
  265. void _ui_rgb2hsv(uint32_t c, int *h, int *s, int *v);
  266. uint32_t _ui_hsv2rgb(int a, int h, int s, int v);
  267. void _ui_line(ui_t *ctx, int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint32_t color);
  268. void _ui_qbez(ui_t *ctx, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t cx, int16_t cy, uint32_t color);
  269. void _ui_cbez(ui_t *ctx, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t cx0, int16_t cy0, int16_t cx1, int16_t cy1, uint32_t color);
  270. void _ui_bmp16(ui_t *ctx, int x, int y, uint16_t *glyph, uint32_t color);
  271. void _ui_tri(ui_t *ctx, int x, int y, int type, uint32_t l, uint32_t b, uint32_t d);
  272. void _ui_rect(ui_t *ctx, int x, int y, int w, int h, uint32_t l, uint32_t c, uint32_t d);
  273. void _ui_frect(ui_t *ctx, int x, int y, int w, int h, uint32_t color);
  274. void _ui_checker(ui_t *ctx, int x, int y, int w, int h, uint32_t color);
  275. void _ui_blit(ui_t *ctx, int x, int y, int w, int h, ui_image_t *img, int sx, int sy, int grscale);
  276. void _ui_copy(ui_image_t *dst, int x, int y, int w, int h, ui_image_t *src, int sx, int sy);
  277. void _ui_boxbg(ui_t *ctx, int x, int y, int w, int h, int row, int col, int scr);
  278. void _ui_checkbox(ui_t *ctx, int x, int y, int checked, int flags);
  279. void _ui_radiobtn(ui_t *ctx, int x, int y, int checked, int flags);
  280. void _ui_button(ui_t *ctx, int x, int y, int w, int h, int pressed, int flags, ui_image_t *icon, int label, int top);
  281. void _ui_slider(ui_t *ctx, int x, int y, int w, int h, int cur, int max, int flags);
  282. void _ui_pbar(ui_t *ctx, int x, int y, int w, int h, int64_t cur, int64_t max, int flags);
  283. int _ui_scr(int s, int cur, int max, int bmin, int *b);
  284. void _ui_vscrbar(ui_t *ctx, int x, int y, int h, int cur, int max, int pressed);
  285. void _ui_hscrbar(ui_t *ctx, int x, int y, int w, int cur, int max, int pressed);
  286. void _ui_text(ui_t *ctx, int x, int y, int w, int h, int l, int t, int flags, int edit, char *str, char *end);
  287. void _ui_header(ui_t *ctx, int x, int y, int w, int h, int pressed, int tri, char *str);
  288. void _ui_option(ui_t *ctx, int x, int y, int w, int h, int l, int t, int flags, int pressed, int right, int m, char *str);
  289. void _ui_select(ui_t *ctx, int x, int y, int w, int h, int l, int t, int flags, int pressed, int m, char *str);
  290. void _ui_color(ui_t *ctx, int x, int y, int w, int h, int l, int t, int flags, uint32_t color);
  291. int _ui_popup(ui_t *ctx, int x, int y, ui_form_t *form);
  292. int _ui_recalc(ui_t *ctx, int x, int y, int w, int h, int p, ui_form_t *form, int *ow, int *oh);
  293. int _ui_draw(ui_t *ctx, int x, int y, int w, int h, int ox, int oy, ui_form_t *form, int parent);
  294. int _ui_redraw(ui_t *ctx, ui_form_t *form);
  295. int _ui_setmouse(ui_t *ctx, int x, int y);
  296. ui_event_t *_ui_evtslot(ui_t *ctx);
  297. int _ui_resize(ui_t *ctx, int w, int h);
  298. int _ui_evtproc(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, int parent, ui_event_t *evt);
  299. int _ui_select_ctrl(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt);
  300. void _ui_select_start(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form);
  301. void _ui_text_start(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, char *str, int maxlen);
  302. void _ui_text_add(ui_t *ctx, char *k, int i);
  303. void _ui_text_ctrl(ui_t *ctx, ui_event_t *evt);
  304. int _ui_color_ctrl(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt);
  305. void _ui_color_start(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form);
  306. #ifdef __cplusplus
  307. }
  308. #endif
  309. #endif /* UI_OK */
  310. #ifdef UI_IMPLEMENTATION
  311. #ifndef UI_NOAA
  312. #include <math.h>
  313. #endif
  314. #ifndef UI_BACKEND
  315. #include <ui_glfw.h>
  316. #endif
  317. #ifndef UI_FONT
  318. #define UI_DEFAULTFONT
  319. #include <ui_psf2.h>
  320. #endif
  321. #ifdef __WIN32__
  322. #define L "I64"
  323. #else
  324. #define L "l"
  325. #endif
  326. #ifndef UI_TEXT
  327. #define UI_TEXT UI_TXTINP
  328. #endif
  329. #ifdef __cplusplus
  330. extern "C" {
  331. #endif
  332. /**
  333. * The default theme
  334. */
  335. static const uint32_t ui_default[UI_NUMTHEME] = {
  336. 0xffa0a0a0 /*fg*/, 0xff707070 /*tit*/, 0xff3f3f3f /*bg*/, 0x3f000000 /*sh*/, 0xffffffff /*tfg*/, 0xff3f3f3f /*tbg*/,
  337. 0xffffffff /*hlfg*/, 0xff7f7f7f /*hlbg*/, 0xff2f2f2f /*dfg*/, 0xff373737 /*dbg*/, 0xff101010 /*sbg*/,
  338. 0xffc0c0c0 /*ifg*/, 0xff5f5f5f /*il*/, 0xff2f2f2f /*id*/, 0xff1f1f1f /*ibg*/, 0xffffffff /*icf*/, 0xffffffff /*ib*/, 0xffffffff /*ic*/,
  339. 0xff000000 /*bfg*/, 0xff5f5f5f /*bsg*/, 0xff101010 /*bn*/, 0xff000000 /*sfg*/, 0xff5f5f5f /*ssg*/, 0xff001010 /*bs*/,
  340. 0xff5f5f5f /*bl*/, 0xff1f1f1f /*bd*/, 0xff474747 /*bgl*/, 0xff373737 /*bgd*/,
  341. 0xffaf0000 /*pl*/, 0xff7f0000 /*pb*/, 0xff5f0000 /*pd*/ };
  342. /**
  343. * Recalculate size (sw, sh) to fit into (mw, mh) keeping aspect ratio
  344. */
  345. void _ui_fit(int mw, int mh, int sw, int sh, int *w, int *h)
  346. {
  347. if(!w || !h) return;
  348. if(mw < 1 || mh < 1 || sw < 1 || sh < 1) { *w = *h = 0; return; }
  349. *w = mw; *h = sh * mw / sw;
  350. if(*h > mh) { *h = mh; *w = sw * mh / sh; }
  351. if(*w < 1) *w = 1;
  352. if(*h < 1) *h = 1;
  353. }
  354. /**
  355. * Color conversion sRGB to HSV
  356. */
  357. void _ui_rgb2hsv(uint32_t c, int *h, int *s, int *v)
  358. {
  359. int r = (int)(((uint8_t*)&c)[2]), g = (int)(((uint8_t*)&c)[1]), b = (int)(((uint8_t*)&c)[0]), m, d;
  360. m = r < g? r : g; if(b < m) m = b;
  361. *v = r > g? r : g; if(b > *v) *v = b;
  362. d = *v - m; *h = 0;
  363. if(!*v) { *s = 0; return; }
  364. *s = d * 255 / *v;
  365. if(!*s) return;
  366. if(r == *v) *h = 43*(g - b) / d;
  367. else if(g == *v) *h = 85 + 43*(b - r) / d;
  368. else *h = 171 + 43*(r - g) / d;
  369. if(*h < 0) *h += 256;
  370. }
  371. /**
  372. * Color conversion HSV to sRGB
  373. */
  374. uint32_t _ui_hsv2rgb(int a, int h, int s, int v)
  375. {
  376. int i, f, p, q, t;
  377. uint32_t c = (a & 255) << 24;
  378. if(!s) { ((uint8_t*)&c)[2] = ((uint8_t*)&c)[1] = ((uint8_t*)&c)[0] = v; }
  379. else {
  380. if(h > 255) i = 0; else i = h / 43;
  381. f = (h - i * 43) * 6;
  382. p = (v * (255 - s) + 127) >> 8;
  383. q = (v * (255 - ((s * f + 127) >> 8)) + 127) >> 8;
  384. t = (v * (255 - ((s * (255 - f) + 127) >> 8)) + 127) >> 8;
  385. switch(i) {
  386. case 0: ((uint8_t*)&c)[2] = v; ((uint8_t*)&c)[1] = t; ((uint8_t*)&c)[0] = p; break;
  387. case 1: ((uint8_t*)&c)[2] = q; ((uint8_t*)&c)[1] = v; ((uint8_t*)&c)[0] = p; break;
  388. case 2: ((uint8_t*)&c)[2] = p; ((uint8_t*)&c)[1] = v; ((uint8_t*)&c)[0] = t; break;
  389. case 3: ((uint8_t*)&c)[2] = p; ((uint8_t*)&c)[1] = q; ((uint8_t*)&c)[0] = v; break;
  390. case 4: ((uint8_t*)&c)[2] = t; ((uint8_t*)&c)[1] = p; ((uint8_t*)&c)[0] = v; break;
  391. default: ((uint8_t*)&c)[2] = v; ((uint8_t*)&c)[1] = p; ((uint8_t*)&c)[0] = q; break;
  392. }
  393. }
  394. return c;
  395. }
  396. /**
  397. * Draw a line
  398. */
  399. void _ui_line(ui_t *ctx, int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint32_t color)
  400. {
  401. #ifdef UI_NOAA
  402. int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
  403. int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
  404. int err = dx+dy, e2, p;
  405. uint8_t *a, *b = (uint8_t*)&color;
  406. uint32_t *d, b0, b1, b2, b3, c;
  407. if(!ctx || !ctx->screen.buf || !b[3] || (x0 == x1 && y0 == y1)) return;
  408. d = (uint32_t*)ctx->screen.buf; p = ctx->screen.p / 4;
  409. b0 = b[0]*b[3]; b1 = b[1]*b[3]; b2 = b[2]*b[3]; b3 = b[3]*b[3]; c = 255 - b[3];
  410. for (;;){
  411. if(x0 >= ctx->cx0 && x0 < ctx->cx1 && y0 >= ctx->cy0 && y0 < ctx->cy1) {
  412. a = (uint8_t*)&d[y0 * p + x0];
  413. a[3] = (b3 + c*a[3]) >> 8;
  414. a[2] = (b2 + c*a[2]) >> 8;
  415. a[1] = (b1 + c*a[1]) >> 8;
  416. a[0] = (b0 + c*a[0]) >> 8;
  417. }
  418. e2 = 2*err;
  419. if (e2 >= dy) { if (x0 == x1) { break; } err += dy; x0 += sx; }
  420. if (e2 <= dx) { if (y0 == y1) { break; } err += dx; y0 += sy; }
  421. }
  422. #else
  423. uint8_t *b = (uint8_t*)&color, *e;
  424. uint32_t *d, c;
  425. int sx = x0 < x1 ? 1 : -1, sy = y0 < y1 ? 1 : -1, x2, a, i, p;
  426. int dx = abs(x1-x0), dy = abs(y1-y0), err = dx*dx+dy*dy;
  427. int e2 = err == 0 ? 1 : 0xffff7fl/sqrt(err);
  428. if(!ctx || !ctx->screen.buf || !b[3] || (x0 == x1 && y0 == y1)) return;
  429. d = (uint32_t*)ctx->screen.buf; p = ctx->screen.p / 4;
  430. dx *= e2; dy *= e2; err = dx-dy;
  431. while(1) {
  432. if(x0 >= ctx->cx0 && x0 < ctx->cx1 && y0 >= ctx->cy0 && y0 < ctx->cy1) {
  433. a = err-dx+dy; if(a < 0) a = -a;
  434. a = 255 - (a >> 16); a = a * b[3] / 255; c = 255 - a;
  435. e = (uint8_t*)&d[y0 * p + x0];
  436. e[3] = (b[3]*a + c*e[3]) >> 8;
  437. e[2] = (b[2]*a + c*e[2]) >> 8;
  438. e[1] = (b[1]*a + c*e[1]) >> 8;
  439. e[0] = (b[0]*a + c*e[0]) >> 8;
  440. }
  441. e2 = err; x2 = x0;
  442. if(2*e2 >= -dx) {
  443. if(x0 == x1) break;
  444. i = y0 + sy;
  445. if(e2+dy < 0xff0000l && x0 >= ctx->cx0 && x0 < ctx->cx1 && i >= ctx->cy0 && i < ctx->cy1) {
  446. a = 255 - ((e2+dy) >> 16); a = a * b[3] / 255; c = 255 - a;
  447. e = (uint8_t*)&d[i * p + x0];
  448. e[3] = (b[3]*a + c*e[3]) >> 8;
  449. e[2] = (b[2]*a + c*e[2]) >> 8;
  450. e[1] = (b[1]*a + c*e[1]) >> 8;
  451. e[0] = (b[0]*a + c*e[0]) >> 8;
  452. }
  453. err -= dy; x0 += sx;
  454. }
  455. if(2*e2 <= dy) {
  456. if(y0 == y1) break;
  457. i = x2 + sx;
  458. if(dx-e2 < 0xff0000l && i >= ctx->cx0 && i < ctx->cx1 && y0 >= ctx->cy0 && y0 < ctx->cy1) {
  459. a = 255 - ((dx-e2) >> 16); a = a * b[3] / 255; c = 255 - a;
  460. e = (uint8_t*)&d[y0 * p + i];
  461. e[3] = (b[3]*a + c*e[3]) >> 8;
  462. e[2] = (b[2]*a + c*e[2]) >> 8;
  463. e[1] = (b[1]*a + c*e[1]) >> 8;
  464. e[0] = (b[0]*a + c*e[0]) >> 8;
  465. }
  466. err += dx; y0 += sy;
  467. }
  468. }
  469. #endif
  470. }
  471. /**
  472. * Recursively calculate Bezier curve
  473. */
  474. static void __ui_b(ui_t *ctx, uint32_t color, int x0,int y0, int x1,int y1, int x2,int y2, int x3,int y3, int l)
  475. {
  476. int m0x, m0y, m1x, m1y, m2x, m2y, m3x, m3y, m4x, m4y,m5x, m5y;
  477. if(l < 8 && (x0 != x3 || y0 != y3)) {
  478. m0x = ((x1-x0)/2) + x0; m0y = ((y1-y0)/2) + y0;
  479. m1x = ((x2-x1)/2) + x1; m1y = ((y2-y1)/2) + y1;
  480. m2x = ((x3-x2)/2) + x2; m2y = ((y3-y2)/2) + y2;
  481. m3x = ((m1x-m0x)/2) + m0x; m3y = ((m1y-m0y)/2) + m0y;
  482. m4x = ((m2x-m1x)/2) + m1x; m4y = ((m2y-m1y)/2) + m1y;
  483. m5x = ((m4x-m3x)/2) + m3x; m5y = ((m4y-m3y)/2) + m3y;
  484. __ui_b(ctx, color, x0,y0, m0x,m0y, m3x,m3y, m5x,m5y, l + 1);
  485. __ui_b(ctx, color, m5x,m5y, m4x,m4y, m2x,m2y, x3,y3, l + 1);
  486. }
  487. if(l) {
  488. _ui_line(ctx, ctx->bx >> 8, ctx->by >> 8, x3 >> 8, y3 >> 8, color);
  489. ctx->bx = x3; ctx->by = y3;
  490. }
  491. }
  492. /**
  493. * Draws a quadratic Bezier curve
  494. */
  495. void _ui_qbez(ui_t *ctx, int16_t x0, int16_t y0, int16_t x1, int16_t y1,
  496. int16_t cx, int16_t cy, uint32_t color)
  497. {
  498. if(!ctx || !ctx->screen.buf || !(color & 0xff000000)) return;
  499. ctx->bx = x0 << 8; ctx->by = y0 << 8;
  500. __ui_b(ctx, color, x0 << 8, y0 << 8, (x0 << 8) + (((cx << 8) - (x0 << 8)) >> 1), (y0 << 8) + (((cy << 8) - (y0 << 8)) >> 1),
  501. (cx << 8) + (((x1 << 8) - (cx << 8)) >> 1), (cy << 8) + (((y1 << 8) - (cy << 8)) >> 1), (x1 << 8), (y1 << 8), 0);
  502. }
  503. /**
  504. * Draws a cubic Bezier curve
  505. */
  506. void _ui_cbez(ui_t *ctx, int16_t x0, int16_t y0, int16_t x1, int16_t y1,
  507. int16_t cx0, int16_t cy0, int16_t cx1, int16_t cy1, uint32_t color)
  508. {
  509. if(!ctx || !ctx->screen.buf || !(color & 0xff000000)) return;
  510. ctx->bx = x0 << 8; ctx->by = y0 << 8;
  511. __ui_b(ctx, color, x0 << 8, y0 << 8, cx0 << 8, cy0 << 8, cx1 << 8, cy1 << 8, x1 << 8, y1 << 8, 0);
  512. }
  513. /**
  514. * Draw a 16 x 16 pixel bitmap icon
  515. */
  516. void _ui_bmp16(ui_t *ctx, int x, int y, uint16_t *glyph, uint32_t color)
  517. {
  518. uint8_t *dst;
  519. int i = 0, j = 0, k = x + 16, l = 16, m, n;
  520. if(!ctx || !ctx->screen.buf || !glyph || !color || y >= ctx->cy1 || y + 16 < ctx->cy0 || x >= ctx->cx1 || k < ctx->cx0) return;
  521. if(y < ctx->cy0) { i = ctx->cy0 - y; glyph += i; l -= i; y = ctx->cy0; }
  522. if(x < ctx->cx0) { j = ctx->cx0 - x; k -= i; x = ctx->cx0; }
  523. for(i = 0; i < l && y + i < ctx->cy1; i++, glyph++) {
  524. dst = ctx->screen.buf + (y + i) * ctx->screen.p + x * 4;
  525. for(m = (1 << j), n = j; m < 0x10000 && x + n < k; m <<= 1, dst += 4, n++)
  526. if(*glyph & m) *((uint32_t*)dst) = color;
  527. }
  528. }
  529. /**
  530. * Draw a small, up or down or left or right triangle with shadowed border
  531. */
  532. void _ui_tri(ui_t *ctx, int x, int y, int type, uint32_t l, uint32_t b, uint32_t d)
  533. {
  534. int i, j, p;
  535. uint32_t *data;
  536. if(!ctx || x < ctx->cx0 || y < ctx->cy0 || x + 7 >= ctx->cx1 || y + 7 >= ctx->cy1) return;
  537. data = (uint32_t*)ctx->screen.buf; p = ctx->screen.p / 4;
  538. j = p * y + x;
  539. switch(type) {
  540. case 1:
  541. /* up */
  542. data[j+3] = b; j += p;
  543. data[j+2] = d; data[j+3] = b; data[j+4] = l; j += p;
  544. data[j+1] = d; data[j+2] = data[j+3] = data[j+4] = b; data[j+5] = l; j += p;
  545. data[j] = d; data[j+1] = data[j+2] = data[j+3] = data[j+4] = data[j+5] = b; data[j+6] = l; j += p;
  546. for(i = 0; i < 7; i++)
  547. data[j + i] = l;
  548. break;
  549. case 2:
  550. /* left */
  551. data[j+3] = d; data[j+4] = b; j += p;
  552. data[j+2] = d; data[j+3] = b; data[j+4] = l; j += p;
  553. data[j+1] = d; data[j+2] = data[j+3] = b; data[j+4] = l; j += p;
  554. data[j] = d; data[j+1] = data[j+2] = data[j+3] = b; data[j+4] = l; j += p;
  555. data[j+1] = b; data[j+2] = data[j+3] = b; data[j+4] = l; j += p;
  556. data[j+2] = b; data[j+3] = b; data[j+4] = l; j += p;
  557. data[j+3] = b; data[j+4] = l; j += p;
  558. break;
  559. case 3:
  560. /* right */
  561. data[j] = b; data[j+1] = d; j += p;
  562. data[j] = b; data[j+1] = b; data[j+2] = d; j += p;
  563. data[j] = b; data[j+1] = data[j+2] = b; data[j+3] = d; j += p;
  564. data[j] = b; data[j+1] = data[j+2] = data[j+3] = b; data[j+4] = l; j += p;
  565. data[j] = b; data[j+1] = data[j+2] = b; data[j+3] = l; j += p;
  566. data[j] = b; data[j+1] = b; data[j+2] = l; j += p;
  567. data[j] = b; data[j+1] = l; j += p;
  568. break;
  569. case 4:
  570. /* right - down */
  571. j += 2 * p;
  572. data[j+6] = l; j += p;
  573. data[j+5] = data[j+6] = l; j += p;
  574. data[j+4] = data[j+5] = data[j+6] = l; j += p;
  575. data[j+3] = data[j+4] = data[j+5] = data[j+6] = l; j += p;
  576. data[j+2] = data[j+3] = data[j+4] = data[j+5] = data[j+6] = l; j += p;
  577. break;
  578. default:
  579. /* down */
  580. for(i = 0; i < 7; i++)
  581. data[j + i] = d;
  582. j += p;
  583. data[j] = d; data[j+1] = data[j+2] = data[j+3] = data[j+4] = data[j+5] = b; data[j+6] = l; j += p;
  584. data[j+1] = d; data[j+2] = data[j+3] = data[j+4] = b; data[j+5] = l; j += p;
  585. data[j+2] = d; data[j+3] = b; data[j+4] = l; j += p;
  586. data[j+3] = l;
  587. break;
  588. }
  589. }
  590. /**
  591. * Draw a rectangle
  592. */
  593. void _ui_rect(ui_t *ctx, int x, int y, int w, int h, uint32_t l, uint32_t c, uint32_t d)
  594. {
  595. int i, j, p, p2, pitch;
  596. uint8_t *a, *b;
  597. uint32_t *data, l0, l1, l2, l3, lc, d0, d1, d2, d3, dc, cc;
  598. if(!ctx || !ctx->screen.buf || x >= ctx->cx1 || x + w < ctx->cx0 || y >= ctx->cy1 || y + h < ctx->cy0 || w < 2 || h < 2)
  599. return;
  600. b = (uint8_t*)&l; l0 = b[0]*b[3]; l1 = b[1]*b[3]; l2 = b[2]*b[3]; l3 = b[3]*b[3]; lc = 255 - b[3];
  601. b = (uint8_t*)&d; d0 = b[0]*b[3]; d1 = b[1]*b[3]; d2 = b[2]*b[3]; d3 = b[3]*b[3]; dc = 255 - b[3];
  602. data = (uint32_t*)ctx->screen.buf; pitch = ctx->screen.p / 4;
  603. p = y * pitch + x;
  604. p2 = (y + h - 1) * pitch + x;
  605. for(i=!c; i + 1 < w && x + i + 1 < ctx->cx1; i++) {
  606. if(y >= ctx->cy0 && x + i >= ctx->cx0) {
  607. a = (uint8_t*)&data[p + i];
  608. a[3] = (l3 + lc*a[3]) >> 8;
  609. a[2] = (l2 + lc*a[2]) >> 8;
  610. a[1] = (l1 + lc*a[1]) >> 8;
  611. a[0] = (l0 + lc*a[0]) >> 8;
  612. }
  613. if(y + h - 1 >= ctx->cy0 && y + h - 1 < ctx->cy1 && x + i >= ctx->cx0) {
  614. a = (uint8_t*)&data[p2 + i + 1 - !c];
  615. a[3] = (d3 + dc*a[3]) >> 8;
  616. a[2] = (d2 + dc*a[2]) >> 8;
  617. a[1] = (d1 + dc*a[1]) >> 8;
  618. a[0] = (d0 + dc*a[0]) >> 8;
  619. }
  620. }
  621. if(c) {
  622. b = (uint8_t*)&c; cc = 255 - b[3];
  623. if(y >= ctx->cy0 && x + i >= ctx->cx0) {
  624. a = (uint8_t*)&data[p + i];
  625. a[3] = (b[3]*b[3] + cc*a[3]) >> 8;
  626. a[2] = (b[2]*b[3] + cc*a[2]) >> 8;
  627. a[1] = (b[1]*b[3] + cc*a[1]) >> 8;
  628. a[0] = (b[0]*b[3] + cc*a[0]) >> 8;
  629. }
  630. if(y + h - 1 >= ctx->cy0 && y + h - 1 < ctx->cy1 && x >= ctx->cx0) {
  631. a = (uint8_t*)&data[p2];
  632. a[3] = (b[3]*b[3] + cc*a[3]) >> 8;
  633. a[2] = (b[2]*b[3] + cc*a[2]) >> 8;
  634. a[1] = (b[1]*b[3] + cc*a[1]) >> 8;
  635. a[0] = (b[0]*b[3] + cc*a[0]) >> 8;
  636. }
  637. }
  638. p += pitch;
  639. for(j=1; j + 1 < h && y + j < ctx->cy1; j++, p += pitch)
  640. if(y + j >= ctx->cy0) {
  641. if(x >= ctx->cx0) {
  642. a = (uint8_t*)&data[p];
  643. a[3] = (l3 + lc*a[3]) >> 8;
  644. a[2] = (l2 + lc*a[2]) >> 8;
  645. a[1] = (l1 + lc*a[1]) >> 8;
  646. a[0] = (l0 + lc*a[0]) >> 8;
  647. }
  648. if(x + w - 1 < ctx->cx1) {
  649. a = (uint8_t*)&data[p + w - 1];
  650. a[3] = (d3 + dc*a[3]) >> 8;
  651. a[2] = (d2 + dc*a[2]) >> 8;
  652. a[1] = (d1 + dc*a[1]) >> 8;
  653. a[0] = (d0 + dc*a[0]) >> 8;
  654. }
  655. }
  656. }
  657. /**
  658. * Draw a filled rectangle
  659. */
  660. void _ui_frect(ui_t *ctx, int x, int y, int w, int h, uint32_t color)
  661. {
  662. int i, j, p;
  663. uint8_t *d, *a, *b = (uint8_t*)&color;
  664. uint32_t b0, b1, b2, b3, c;
  665. if(!ctx || !ctx->screen.buf || !b[3]) return;
  666. d = ctx->screen.buf; p = ctx->screen.p;
  667. if(x < ctx->cx0) { w -= ctx->cx0 - x; x = ctx->cx0; }
  668. if(y < ctx->cy0) { h -= ctx->cy0 - y; y = ctx->cy0; }
  669. if(x + w > ctx->cx1) w = ctx->cx1 - x;
  670. if(y + h > ctx->cy1) h = ctx->cy1 - y;
  671. if(x >= ctx->cx1 || y >= ctx->cy1 || w < 1 || h < 1) return;
  672. b0 = b[0]*b[3]; b1 = b[1]*b[3]; b2 = b[2]*b[3]; b3 = b[3]*b[3]; c = 255 - b[3];
  673. d += p * y + x * 4;
  674. for(j = 0; j < h; j++, d += p) {
  675. for(i = 0, a = d; i < w; i++, a += 4) {
  676. a[3] = (b3 + c*a[3]) >> 8;
  677. a[2] = (b2 + c*a[2]) >> 8;
  678. a[1] = (b1 + c*a[1]) >> 8;
  679. a[0] = (b0 + c*a[0]) >> 8;
  680. }
  681. }
  682. }
  683. /**
  684. * Draw a filled rectangle with checker background (for opaque colors, same as ui_frect)
  685. */
  686. void _ui_checker(ui_t *ctx, int x, int y, int w, int h, uint32_t color)
  687. {
  688. int i, j, k, p;
  689. uint8_t *d, *a, *b = (uint8_t*)&color;
  690. uint32_t b0, b1, b2, c, e;
  691. if(!ctx || !ctx->screen.buf) return;
  692. d = ctx->screen.buf; p = ctx->screen.p;
  693. k = (w < h ? w : h) / 2; if(k < 1) k = 1;
  694. if(x < ctx->cx0) { w -= ctx->cx0 - x; x = ctx->cx0; }
  695. if(y < ctx->cy0) { h -= ctx->cy0 - y; y = ctx->cy0; }
  696. if(x + w > ctx->cx1) w = ctx->cx1 - x;
  697. if(y + h > ctx->cy1) h = ctx->cy1 - y;
  698. if(x >= ctx->cx1 || y >= ctx->cy1 || w < 1 || h < 1) return;
  699. b0 = b[0]*b[3]; b1 = b[1]*b[3]; b2 = b[2]*b[3]; c = 255 - b[3];
  700. d += p * y + x * 4;
  701. for(j = 0; j < h; j++, d += p) {
  702. for(i = 0, a = d; i < w; i++, a += 4) {
  703. e = c * (((j / k) & 1) ^ ((i / k) & 1) ? 0x9f : 0x5f);
  704. a[3] = 255;
  705. a[2] = (b2 + e) >> 8;
  706. a[1] = (b1 + e) >> 8;
  707. a[0] = (b0 + e) >> 8;
  708. }
  709. }
  710. }
  711. /**
  712. * Blit a pixel buffer to the screen
  713. */
  714. void _ui_blit(ui_t *ctx, int x, int y, int w, int h, ui_image_t *img, int sx, int sy, int grscale)
  715. {
  716. int i, j, p, sp, sw, sh;
  717. uint8_t *s, *d, *a, *b;
  718. register uint32_t c, P;
  719. if(!ctx || !ctx->screen.buf || !img || !img->buf) return;
  720. d = ctx->screen.buf; p = ctx->screen.p;
  721. s = img->buf; sw = img->w; sh = img->h; sp = img->p;
  722. if(x < ctx->cx0) { w -= ctx->cx0 - x; sx += ctx->cx0 - x; x = ctx->cx0; }
  723. if(y < ctx->cy0) { h -= ctx->cy0 - y; sy += ctx->cy0 - y; y = ctx->cy0; }
  724. if(x + w > ctx->cx1) w = ctx->cx1 - x;
  725. if(y + h > ctx->cy1) h = ctx->cy1 - y;
  726. if(x >= ctx->cx1 || y >= ctx->cy1 || w < 1 || h < 1) return;
  727. d += p * y + x * 4;
  728. for(j = 0; j < h; j++, d += p) {
  729. for(i = 0, a = d; i < w; i++, a += 4) {
  730. b = s + ((sy + j) % sh) * sp + ((sx + i) % sw) * 4;
  731. if(b[3]) {
  732. if(grscale > 0) {
  733. c = 255 - b[3];
  734. P = (b[2] + b[1] + b[0]) >> 3;
  735. a[3] = (b[3]*b[3] + c*a[3]) >> 8;
  736. a[2] = (P*b[3] + c*a[2]) >> 8;
  737. a[1] = (P*b[3] + c*a[1]) >> 8;
  738. a[0] = (P*b[3] + c*a[0]) >> 8;
  739. } else {
  740. c = 255 - b[3];
  741. a[3] = (b[3]*b[3] + c*a[3]) >> 8;
  742. a[2] = (b[2]*b[3] + c*a[2]) >> 8;
  743. a[1] = (b[1]*b[3] + c*a[1]) >> 8;
  744. a[0] = (b[0]*b[3] + c*a[0]) >> 8;
  745. }
  746. }
  747. }
  748. }
  749. }
  750. /**
  751. * Copy a pixel buffer to another
  752. */
  753. void _ui_copy(ui_image_t *dst, int x, int y, int w, int h, ui_image_t *src, int sx, int sy)
  754. {
  755. int n, j;
  756. uint8_t *s, *d;
  757. if(!src || !src->buf || !dst || !dst->buf) return;
  758. if(x < 0) { w += x; sx -= x; x = 0; }
  759. if(y < 0) { h += y; sy -= y; y = 0; }
  760. if(x + w > dst->w) w = dst->w - x;
  761. if(y + h > dst->h) h = dst->h - y;
  762. if(sx < 0) { w += sx; x -= sx; sx = 0; }
  763. if(sy < 0) { h += sy; y -= sy; sy = 0; }
  764. if(sx + w > src->w) w = src->w - sx;
  765. if(sy + h > src->h) h = src->h - sy;
  766. if(x >= dst->w || y >= dst->h || sx >= src->w || sy >= src->h || w < 1 || h < 1) return;
  767. s = src->buf + src->p * sy + sx * 4;
  768. d = dst->buf + dst->p * y + x * 4;
  769. n = w * 4;
  770. for(j = 0; j < h; j++, d += dst->p, s += src->p)
  771. memcpy(d, s, n);
  772. }
  773. /**
  774. * Draw an input background filled with altering boxes
  775. */
  776. void _ui_boxbg(ui_t *ctx, int x, int y, int w, int h, int row, int col, int scr)
  777. {
  778. int i, j, p, sp, sw, sh, sx = 0, sy = 0;
  779. uint8_t *s, *d, *a, *b;
  780. uint32_t b0[2], b1[2], b2[2], b3, c, g = 8;
  781. if(!ctx || !ctx->screen.buf) return;
  782. if(!row) row = h;
  783. if(!col) col = w;
  784. d = ctx->screen.buf; p = ctx->screen.p;
  785. if(x < ctx->cx0) { w -= ctx->cx0 - x; sx += ctx->cx0 - x; x = ctx->cx0; }
  786. if(y < ctx->cy0) { h -= ctx->cy0 - y; sy += ctx->cy0 - y; y = ctx->cy0; }
  787. if(x + w > ctx->cx1) w = ctx->cx1 - x;
  788. if(y + h > ctx->cy1) h = ctx->cy1 - y;
  789. if(x > ctx->cx1 || y > ctx->cy1 || w < 1 || h < 1) return;
  790. d += p * y + x * 4;
  791. if(ctx->skin[UI_INP].buf) {
  792. s = ctx->skin[UI_INP].buf; sw = ctx->skin[UI_INP].w; sh = ctx->skin[UI_INP].h; sp = ctx->skin[UI_INP].p;
  793. sy += scr;
  794. for(j = 0; j < h; j++, d += p)
  795. for(i = 0, a = d; i < w; i++, a += 4) {
  796. b = s + ((sy + j) % sh) * sp + ((sx + i) % sw) * 4;
  797. c = 255 - b[3];
  798. a[3] = (b[3]*b[3] + c*a[3]) >> 8;
  799. a[2] = (b[2]*b[3] + c*a[2]) >> 8;
  800. a[1] = (b[1]*b[3] + c*a[1]) >> 8;
  801. a[0] = (b[0]*b[3] + c*a[0]) >> 8;
  802. if((((j + scr) / row) & 1) ^ ((i / col) & 1)) {
  803. if(a[2] > g) a[2] -= g; else a[2] = 0;
  804. if(a[1] > g) a[1] -= g; else a[1] = 0;
  805. if(a[0] > g) a[0] -= g; else a[0] = 0;
  806. }
  807. }
  808. } else {
  809. b = (uint8_t*)&ctx->theme[UI_IBG];
  810. b0[0] = b[0]*b[3]; b1[0] = b[1]*b[3]; b2[0] = b[2]*b[3]; b3 = b[3]*b[3]; c = 255 - b[3];
  811. b0[1] = b[0]>g?(b[0]-g)*b[3] : 0; b1[1] = b[1]>g?(b[1]-g)*b[3] : 0; b2[1] = b[2]>g?(b[2]-g)*b[3] : 0;
  812. for(j = 0; j < h; j++, d += p)
  813. for(i = 0, a = d; i < w; i++, a += 4) {
  814. g = (((j + scr) / row) & 1) ^ ((i / col) & 1);
  815. a[3] = (b3 + c*a[3]) >> 8;
  816. a[2] = (b2[g] + c*a[2]) >> 8;
  817. a[1] = (b1[g] + c*a[1]) >> 8;
  818. a[0] = (b0[g] + c*a[0]) >> 8;
  819. }
  820. }
  821. }
  822. /**
  823. * Draw checkbox (reused as non-skinned popup close icon)
  824. */
  825. void _ui_checkbox(ui_t *ctx, int x, int y, int checked, int flags)
  826. {
  827. ui_image_t *s;
  828. int p, id = UI_ID, il = UI_IL, ifg = UI_IFG, ibg = UI_IBG;
  829. uint32_t *buf;
  830. if(!ctx || !ctx->screen.buf || x - 5 < ctx->cx0 || y - 5 < ctx->cy0 || x + 4 >= ctx->cx1 || y + 4 >= ctx->cy1) return;
  831. if(checked == -2) { id = il = ibg = UI_TIT; ifg = UI_BG; }
  832. if(checked == -1) { id = il = ifg = UI_DFG; ibg = UI_DBG; }
  833. if(ctx->skin[UI_CHK0].buf && checked >= -1) {
  834. s = &ctx->skin[checked > 0 ? UI_CHK1 : UI_CHK0];
  835. _ui_blit(ctx, x - s->w / 2, y - s->h / 2, s->w, s->h, s, 0, 0, flags & UI_DISABLED ? 1 : 0);
  836. } else {
  837. _ui_rect(ctx, x - 5, y - 5, 9, 9, ctx->theme[id], ctx->theme[ibg], ctx->theme[il]);
  838. _ui_frect(ctx, x - 4, y - 4, 7, 7, ctx->theme[ibg]);
  839. if(checked) {
  840. buf = (uint32_t*)(ctx->screen.buf + (y - 3) * ctx->screen.p + (x - 3) * 4);
  841. p = ctx->screen.p / 4;
  842. buf[0] = buf[4] = ctx->theme[ifg]; buf += p;
  843. buf[1] = buf[3] = ctx->theme[ifg]; buf += p;
  844. buf[2] = ctx->theme[ifg]; buf += p;
  845. buf[1] = buf[3] = ctx->theme[ifg]; buf += p;
  846. buf[0] = buf[4] = ctx->theme[ifg];
  847. }
  848. }
  849. }
  850. /**
  851. * Draw radio button (reused as non-skinned slider button)
  852. */
  853. void _ui_radiobtn(ui_t *ctx, int x, int y, int checked, int flags)
  854. {
  855. ui_image_t *s;
  856. int p, id = UI_ID, il = UI_IL, ibg = UI_IBG, ifg = UI_IFG;
  857. uint32_t *buf, b;
  858. if(!ctx || !ctx->screen.buf || x - 5 < ctx->cx0 || y - 5 < ctx->cy0 || x + 4 >= ctx->cx1 || y + 4 >= ctx->cy1) return;
  859. if(checked == -3) id = il = ibg = ifg = UI_DFG;
  860. if(checked == -2) id = il = ibg = ifg = UI_IFG;
  861. if(checked == -1) { id = il = ifg = UI_DFG; ibg = UI_DBG; }
  862. if(ctx->skin[UI_RADIO0].buf && checked >= -1) {
  863. s = &ctx->skin[checked > 0 ? UI_RADIO1 : UI_RADIO0];
  864. _ui_blit(ctx, x - s->w / 2, y - s->h / 2, s->w, s->h, s, 0, 0, flags & UI_DISABLED ? 1 : 0);
  865. } else {
  866. buf = (uint32_t*)(ctx->screen.buf + (y - 5) * ctx->screen.p + (x - 5) * 4);
  867. p = ctx->screen.p / 4;
  868. b = ctx->theme[checked ? ifg : ibg];
  869. buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = ctx->theme[id]; buf += p;
  870. buf[1] = buf[7] = ctx->theme[id];
  871. buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = ctx->theme[ibg]; buf += p;
  872. buf[0] = ctx->theme[id]; buf[8] = ctx->theme[il]; buf[1] = buf[2] = buf[6] = buf[7] = ctx->theme[ibg];
  873. buf[3] = buf[4] = buf[5] = b; buf += p;
  874. buf[0] = ctx->theme[id]; buf[8] = ctx->theme[il]; buf[1] = buf[7] = ctx->theme[ibg];
  875. buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = b; buf += p;
  876. buf[0] = ctx->theme[id]; buf[8] = ctx->theme[il]; buf[1] = buf[7] = ctx->theme[ibg];
  877. buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = b; buf += p;
  878. buf[0] = ctx->theme[id]; buf[8] = ctx->theme[il]; buf[1] = buf[7] = ctx->theme[ibg];
  879. buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = b; buf += p;
  880. buf[0] = ctx->theme[id]; buf[8] = ctx->theme[il]; buf[1] = buf[2] = buf[6] = buf[7] = ctx->theme[ibg];
  881. buf[3] = buf[4] = buf[5] = b; buf += p;
  882. buf[1] = buf[7] = ctx->theme[il];
  883. buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = ctx->theme[ibg]; buf += p;
  884. buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = ctx->theme[il]; buf += p;
  885. }
  886. }
  887. /**
  888. * Draw a button
  889. */
  890. void _ui_button(ui_t *ctx, int x, int y, int w, int h, int pressed, int flags, ui_image_t *icon, int label, int top)
  891. {
  892. ui_image_t *s;
  893. int H = h * 5 / 8, dw = 0, dh, tx, ty, l = 0, t = 0;
  894. uint32_t c;
  895. if(!ctx || !ctx->screen.buf || x + w + 2 < ctx->cx0 || y + h + 2 < ctx->cy0 || x - 1 >= ctx->cx1 || y - 1 >= ctx->cy1) return;
  896. if(ctx->fnt && ctx->bbox && ctx->txtv && label > 0 && label < ctx->txtc) {
  897. (*ctx->bbox)(ctx->fnt, ctx->txtv[label], NULL, &dw, &dh, &l, &t);
  898. if(icon && icon->buf) dw += 4;
  899. }
  900. if(icon && icon->buf) dw += icon->w;
  901. tx = x + (w - dw) / 2;
  902. ty = y + (h - dh) / 2 + top;
  903. if(!(flags & UI_NOBORDER) && !ctx->skin[UI_BNM].buf) {
  904. c = ctx->theme[pressed == -1 || flags & UI_DISABLED ? UI_DFG : (pressed < 0 ? UI_BS : UI_BN)];
  905. _ui_rect(ctx, x - 1, y - 1, w + 2, h + 2, c, 0, c);
  906. }
  907. if(pressed > 0) ty++;
  908. s = &ctx->skin[pressed > 0 ? UI_BPL : (pressed < 0 ? UI_BSL : UI_BNL)];
  909. if(s[1].buf) {
  910. c = pressed == -1 || flags & UI_DISABLED;
  911. _ui_blit(ctx, x, y + (h - s[0].h)/2, s[0].w, s[0].h, &s[0], 0, 0, c);
  912. _ui_blit(ctx, x + s[0].w, y + (h - s[1].h)/2, w - s[0].w - s[2].w, s[1].h, &s[1], 0, 0, c);
  913. _ui_blit(ctx, x + w - s[2].w, y + (h - s[2].h)/2, s[2].w, s[2].h, &s[2], 0, 0, c);
  914. } else
  915. if(pressed > 0 && !(flags & UI_DISABLED)) {
  916. _ui_rect(ctx, x, y, w, h, ctx->theme[UI_BD], ctx->theme[UI_BGD], ctx->theme[UI_BL]);
  917. _ui_frect(ctx, x + 1, y + 1, w - 2, h - H - 1, ctx->theme[UI_BGD]);
  918. _ui_frect(ctx, x + 1, y + h - H - 1, w - 2, H, ctx->theme[UI_BGL]);
  919. } else
  920. if(pressed == -1 || (flags & UI_DISABLED)) {
  921. _ui_frect(ctx, x, y, w, h, ctx->theme[UI_DBG]);
  922. } else {
  923. _ui_rect(ctx, x, y, w, h, ctx->theme[UI_BL], ctx->theme[UI_BGL], ctx->theme[UI_BD]);
  924. _ui_frect(ctx, x + 1, y + 1, w - 2, H, ctx->theme[UI_BGL]);
  925. _ui_frect(ctx, x + 1, y + H, w - 2, h - H - 1, ctx->theme[UI_BGD]);
  926. }
  927. if(icon && icon->buf) {
  928. _ui_blit(ctx, tx, y + top + (h - icon->h) / 2 + !!(pressed > 0), icon->w, icon->h, icon, 0, 0, flags & UI_DISABLED);
  929. tx += icon->w + 4;
  930. }
  931. if(ctx->fnt && ctx->draw && ctx->txtv && label > 0 && label < ctx->txtc) {
  932. if(pressed != -1 && ctx->theme[UI_BSG])
  933. (*ctx->draw)(ctx->fnt, ctx->txtv[label], NULL, ctx->screen.buf, ctx->theme[pressed == -2 ? UI_SSG : UI_BSG],
  934. tx + 1 - l , ty + 1, l, t, ctx->screen.p, ctx->cx0 + 1, ctx->cy0 + 1, ctx->cx1 - 1, ctx->cy1 - 1);
  935. (*ctx->draw)(ctx->fnt, ctx->txtv[label], NULL, ctx->screen.buf, ctx->theme[pressed == -1 ? UI_DFG :
  936. (pressed == -2 ? UI_SFG : UI_BFG)], tx - l, ty, l, t, ctx->screen.p,
  937. ctx->cx0 + 1, ctx->cy0 + 1, ctx->cx1 - 1, ctx->cy1 - 1);
  938. }
  939. }
  940. /**
  941. * Draw a slider
  942. */
  943. void _ui_slider(ui_t *ctx, int x, int y, int w, int h, int cur, int max, int flags)
  944. {
  945. ui_image_t *s;
  946. int n, id = UI_ID, il = UI_IL, ifg = UI_IFG, ibg = UI_IBG;
  947. if(!ctx || !ctx->screen.buf || x + w < ctx->cx0 || y + h < ctx->cy0 || x >= ctx->cx1 || y >= ctx->cy1 || w < 7) return;
  948. if(max < 1) max = 1;
  949. if(cur < 0) cur = 0;
  950. if(cur > max) cur = max;
  951. if(flags & UI_SELECTED) { id = il = UI_BS; }
  952. if(flags & UI_DISABLED) { id = il = ibg = UI_DBG; ifg = UI_DFG; }
  953. n = cur * (w - 9) / max;
  954. if(ctx->skin[UI_S_B].buf) {
  955. s = &ctx->skin[UI_S_L];
  956. _ui_blit(ctx, x, y + (h - s->h)/2, s->w, s->h, s, 0, 0, flags & UI_DISABLED);
  957. _ui_blit(ctx, x + s->w, y + (h - ctx->skin[UI_S_M].h)/2, w - s->w - ctx->skin[UI_S_R].w, ctx->skin[UI_S_M].h,
  958. &ctx->skin[UI_S_M], 0, 0, flags & UI_DISABLED);
  959. s = &ctx->skin[UI_S_R];
  960. _ui_blit(ctx, x + w - s->w, y + (h - s->h)/2, s->w, s->h, s, 0, 0, flags & UI_DISABLED);
  961. s = &ctx->skin[UI_S_B];
  962. _ui_blit(ctx, x + n + 3 - s->w/2, y + (h - s->h)/2, s->w, s->h, s, 0, 0, flags & UI_DISABLED);
  963. } else {
  964. h /= 2;
  965. if(flags & UI_NOBORDER) {
  966. _ui_frect(ctx, x + 1, y + h - 3, n + 3, 5, ctx->theme[ifg]);
  967. _ui_frect(ctx, x + n + 3, y + h - 3, w - 4 - n, 5, ctx->theme[ibg]);
  968. } else {
  969. _ui_rect(ctx, x, y + h - 3, w, 5, ctx->theme[id], ctx->theme[ibg], ctx->theme[il]);
  970. _ui_frect(ctx, x + 1, y + h - 2, n + 3, 3, ctx->theme[ifg]);
  971. _ui_frect(ctx, x + n + 3, y + h - 2, w - 4 - n, 3, ctx->theme[ibg]);
  972. }
  973. _ui_radiobtn(ctx, x + n + 5, y + h, -2 - !!(flags & UI_DISABLED), 0);
  974. }
  975. }
  976. /**
  977. * Draw a progressbar
  978. */
  979. void _ui_pbar(ui_t *ctx, int x, int y, int w, int h, int64_t cur, int64_t max, int flags)
  980. {
  981. char tmp[16];
  982. int n, dw, dh, l = 0, t = 0, X, Y, W, H;
  983. if(!ctx || !ctx->screen.buf || x + w < ctx->cx0 || y + h < ctx->cy0 || x >= ctx->cx1 || y >= ctx->cy1) return;
  984. if(max < 1) max = 1;
  985. if(cur < 0) cur = 0;
  986. if(cur > max) cur = max;
  987. if(!(flags & UI_NOBORDER)) {
  988. _ui_rect(ctx, x, y, w, h, ctx->theme[UI_ID], ctx->theme[UI_IBG], ctx->theme[UI_IL]);
  989. x++; y++; w -= 2; h -= 2;
  990. }
  991. n = (int)(cur * (int64_t)w / max);
  992. X = x; Y = y; W = n; H = h;
  993. if(n > 1 && ctx->theme[UI_PL] && ctx->theme[UI_PD]) {
  994. _ui_rect(ctx, x, y, n, h, ctx->theme[UI_PL], ctx->theme[UI_PB], ctx->theme[UI_PD]);
  995. X++; Y++; W -= 2; H -= 2;
  996. }
  997. if(ctx->skin[UI_PBG].buf)
  998. _ui_blit(ctx, X, Y, W, H, &ctx->skin[UI_PBG], 0, 0, 0);
  999. else
  1000. _ui_frect(ctx, X, Y, W, H, ctx->theme[UI_PB]);
  1001. if(ctx->skin[UI_INP].buf)
  1002. _ui_blit(ctx, x + n, y, w - n, h, &ctx->skin[UI_INP], n % ctx->skin[UI_INP].w, 0, 0);
  1003. else
  1004. _ui_frect(ctx, x + n, y, w - n, h, ctx->theme[UI_IBG]);
  1005. n = (int)(cur * 1000 / max);
  1006. sprintf(tmp, "%u.%u%%", n / 10, n % 10);
  1007. if(ctx->fnt && ctx->bbox && ctx->draw) {
  1008. (*ctx->bbox)(ctx->fnt, tmp, NULL, &dw, &dh, &l, &t);
  1009. (*ctx->draw)(ctx->fnt, tmp, NULL, ctx->screen.buf, ctx->theme[UI_IFG], x + (w - dw) / 2 - l, y + (h - dh) / 2, l, t,
  1010. ctx->screen.p, x + 1, y + 1, x + w - 2, y + h - 2);
  1011. }
  1012. }
  1013. /**
  1014. * Calculate scrollbar button size and position
  1015. */
  1016. int _ui_scr(int s, int cur, int max, int bmin, int *b)
  1017. {
  1018. if(cur < 0) cur = 0;
  1019. if(cur > max) cur = max;
  1020. if(s >= max || max < 1) { *b = s; return 0; }
  1021. *b = s * s / max;
  1022. if(*b < bmin) *b = bmin;
  1023. return (s - *b) * cur / (max - s);
  1024. }
  1025. /**
  1026. * Draw a vertical scrollbar
  1027. */
  1028. void _ui_vscrbar(ui_t *ctx, int x, int y, int h, int cur, int max, int pressed)
  1029. {
  1030. ui_image_t *st, *sm, *sb;
  1031. int il = UI_IL, id = UI_ID, sbg = UI_SBG, bgl = UI_BGL, p, b;
  1032. if(!ctx || !ctx->screen.buf || x + ctx->sw < ctx->cx0 || y + h < ctx->cy0 || x >= ctx->cx1 || y >= ctx->cy1) return;
  1033. p = _ui_scr(h, cur, max, ctx->sw, &b);
  1034. if(ctx->skin[UI_SV_BM].buf) {
  1035. st = &ctx->skin[UI_SV_T]; sm = &ctx->skin[UI_SV_M]; sb = &ctx->skin[UI_SV_B];
  1036. _ui_blit(ctx, x + (ctx->sw - st->w)/2, y, st->w, st->h, st, 0, 0, pressed < 0);
  1037. _ui_blit(ctx, x + (ctx->sw - sm->w)/2, y + st->h, sm->w, h - st->h - sb->h, sm, 0, 0, pressed < 0);
  1038. _ui_blit(ctx, x + (ctx->sw - sb->w)/2, y + h - sb->h, sb->w, sb->h, sb, 0, 0, pressed < 0);
  1039. st = &ctx->skin[UI_SV_BT]; sm = &ctx->skin[UI_SV_BM]; sb = &ctx->skin[UI_SV_BB];
  1040. _ui_blit(ctx, x + (ctx->sw - st->w)/2, y + p, st->w, st->h, st, 0, 0, pressed < 0);
  1041. _ui_blit(ctx, x + (ctx->sw - sm->w)/2, y + p + st->h, sm->w, b - st->h - sb->h, sm, 0, 0, pressed < 0);
  1042. _ui_blit(ctx, x + (ctx->sw - sb->w)/2, y + p + b - sb->h, sb->w, sb->h, sb, 0, 0, pressed < 0);
  1043. } else {
  1044. if(pressed > 0) { il = UI_ID; id = UI_IL; }
  1045. if(pressed < 0) { sbg = UI_DBG; il = id = bgl = UI_DFG; }
  1046. _ui_frect(ctx, x, y, ctx->sw, p, ctx->theme[sbg]);
  1047. _ui_rect(ctx, x, y + p, ctx->sw, b, ctx->theme[il], ctx->theme[bgl], ctx->theme[id]);
  1048. _ui_frect(ctx, x + 1, y + p + 1, ctx->sw - 2, b - 2, ctx->theme[bgl]);
  1049. _ui_frect(ctx, x, y + p + b, ctx->sw, h - p - b, ctx->theme[sbg]);
  1050. }
  1051. }
  1052. /**
  1053. * Draw a horizontal scrollbar
  1054. */
  1055. void _ui_hscrbar(ui_t *ctx, int x, int y, int w, int cur, int max, int pressed)
  1056. {
  1057. ui_image_t *sl, *sm, *sr;
  1058. int il = UI_IL, id = UI_ID, sbg = UI_SBG, bgl = UI_BGL, p, b;
  1059. if(!ctx || !ctx->screen.buf || x + w < ctx->cx0 || y + ctx->sh < ctx->cy0 || x >= ctx->cx1 || y >= ctx->cy1) return;
  1060. p = _ui_scr(w, cur, max, ctx->sh, &b);
  1061. if(ctx->skin[UI_SH_BM].buf) {
  1062. sl = &ctx->skin[UI_SH_L]; sm = &ctx->skin[UI_SH_M]; sr = &ctx->skin[UI_SH_R];
  1063. _ui_blit(ctx, x, y + (ctx->sh - sl->h)/2, sl->w, sl->h, sl, 0, 0, pressed < 0);
  1064. _ui_blit(ctx, x + sl->w, y + (ctx->sh - sm->h)/2, w - sl->w - sr->w, sm->h, sm, 0, 0, pressed < 0);
  1065. _ui_blit(ctx, x + w - sr->w, y + (ctx->sh - sm->h)/2, sr->w, sr->h, sr, 0, 0, pressed < 0);
  1066. sl = &ctx->skin[UI_SH_BL]; sm = &ctx->skin[UI_SH_BM]; sr = &ctx->skin[UI_SH_BR];
  1067. _ui_blit(ctx, x + p, y + (ctx->sh - sl->h)/2, sl->w, sl->h, sl, 0, 0, pressed < 0);
  1068. _ui_blit(ctx, x + p + sl->w, y + (ctx->sh - sm->h)/2, b - sl->w - sr->w, sm->h, sm, 0, 0, pressed < 0);
  1069. _ui_blit(ctx, x + p + b - sr->w, y + (ctx->sh - sm->h)/2, sr->w, sr->h, sr, 0, 0, pressed < 0);
  1070. } else {
  1071. if(pressed > 0) { il = UI_ID; id = UI_IL; }
  1072. if(pressed < 0) { sbg = UI_DBG; il = id = bgl = UI_DFG; }
  1073. _ui_frect(ctx, x, y, p, ctx->sh, ctx->theme[sbg]);
  1074. _ui_rect(ctx, x + p, y, b, ctx->sh, ctx->theme[il], ctx->theme[bgl], ctx->theme[id]);
  1075. _ui_frect(ctx, x + p + 1, y + 1, b - 2, ctx->sh - 2, ctx->theme[bgl]);
  1076. _ui_frect(ctx, x + p + b, y, w - p - b, ctx->sh, ctx->theme[sbg]);
  1077. }
  1078. }
  1079. /**
  1080. * Draw a text field
  1081. */
  1082. void _ui_text(ui_t *ctx, int x, int y, int w, int h, int l, int t, int flags, int edit, char *str, char *end)
  1083. {
  1084. char pass[128] = { 0 }, *ptr;
  1085. int il = UI_IL, id = UI_ID, ibg = UI_IBG, ifg = UI_IFG, is = UI_INP, dw, dh, i, p;
  1086. uint32_t *buf;
  1087. if(!ctx || !ctx->screen.buf || x + w < ctx->cx0 || y + h < ctx->cy0 || x >= ctx->cx1 || y >= ctx->cy1) return;
  1088. p = ctx->screen.p / 4;
  1089. if(flags & UI_SELECTED) { id = il = UI_BS; }
  1090. if(edit & 1) il = id = UI_IB;
  1091. if(edit & 16) { il = UI_ID; id = UI_IL; }
  1092. if(edit & 64 && !ctx->popup) { ibg = UI_BG; is = UI_P_BG; }
  1093. if(flags & UI_DISABLED) { il = id = ibg = UI_DBG; ifg = UI_DFG; edit = 0; }
  1094. if(!(edit & 8)) {
  1095. if(!(flags & UI_NOBORDER)) {
  1096. _ui_rect(ctx, x, y, w, h, ctx->theme[id], ctx->theme[ibg], ctx->theme[il]);
  1097. x++; y++; w -= 2; h -= 2;
  1098. }
  1099. if(ctx->skin[is].buf)
  1100. _ui_blit(ctx, x, y, w, h, &ctx->skin[is], 0, 0, flags & UI_DISABLED);
  1101. else
  1102. _ui_frect(ctx, x, y, w, h, ctx->theme[ibg]);
  1103. }
  1104. if(ctx->fnt && ctx->draw && ctx->bbox) {
  1105. if(edit & 1) {
  1106. if(ctx->cur < ctx->scr) ctx->scr = ctx->cur;
  1107. do {
  1108. (*ctx->bbox)(ctx->fnt, ctx->scr, ctx->cur, &dw, &dh, NULL, NULL);
  1109. if(dw < w - 4) break;
  1110. do { ctx->scr++; } while(ctx->scr < ctx->end && (*ctx->scr & 0xC0) == 0x80);
  1111. } while(1);
  1112. if(edit & 2) {
  1113. for(i = 0, ptr = ctx->scr; i < (int)sizeof(pass) - 1 && *ptr; i++) {
  1114. pass[i] = '*'; do { ptr++; } while(ptr < ctx->end && *ptr && (*ptr & 0xC0) == 0x80);
  1115. }
  1116. pass[i] = 0;
  1117. ptr = pass;
  1118. } else ptr = ctx->scr;
  1119. (*ctx->draw)(ctx->fnt, ptr, NULL, ctx->screen.buf, ctx->theme[UI_ICF], x + 2 - l, y + 1, l, t, ctx->screen.p,
  1120. x + 2, y + 1, x + w - 2, y + h - 2);
  1121. buf = (uint32_t*)(ctx->screen.buf + (y + 1) * ctx->screen.p + (x + dw + 2) * 4);
  1122. for(i = 0; i < h - 2; i++, buf += p) *buf = ctx->theme[UI_IC];
  1123. } else
  1124. if(str) {
  1125. if(edit & 2) {
  1126. for(i = 0, ptr = str; i < (int)sizeof(pass) - 1 && *ptr; i++) {
  1127. pass[i] = '*'; do { ptr++; } while(*ptr && (*ptr & 0xC0) == 0x80);
  1128. }
  1129. pass[i] = 0;
  1130. ptr = pass;
  1131. end = NULL;
  1132. } else ptr = str;
  1133. y++; if(edit & 32) y++;
  1134. (*ctx->draw)(ctx->fnt, ptr, end, ctx->screen.buf, ctx->theme[ifg], x + 2 - l, y, l, t, ctx->screen.p,
  1135. x + 2, y, x + w - 2, y + h - 1);
  1136. }
  1137. }
  1138. }
  1139. /**
  1140. * Draw a header
  1141. */
  1142. void _ui_header(ui_t *ctx, int x, int y, int w, int h, int pressed, int tri, char *str)
  1143. {
  1144. int j = 0, il = UI_IL, id = UI_ID, ibg = UI_IBG, is = UI_INP, mw, mh, dw, dh, t;
  1145. if(!ctx || !ctx->screen.buf || x + w < ctx->cx0 || y + h < ctx->cy0 || x >= ctx->cx1 || y >= ctx->cy1) return;
  1146. if(!ctx->popup) { ibg = UI_BG; is = UI_P_BG; }
  1147. if(ctx->skin[is].buf)
  1148. _ui_blit(ctx, x + 1, y + 1, w - 2, h - 2, &ctx->skin[is], 0, 0, 0);
  1149. else
  1150. _ui_frect(ctx, x + 1, y + 1, w - 2, h - 2, ctx->theme[ibg]);
  1151. if(pressed) { il = UI_ID; id = UI_IL; j = 1; }
  1152. if(str && *str && ctx->fnt && ctx->bbox && ctx->draw) {
  1153. mw = x + w - 2; if(mw > ctx->cx1) mw = ctx->cx1;
  1154. mh = y + h - 2; if(mh > ctx->cy1) mh = ctx->cy1;
  1155. (*ctx->bbox)(ctx->fnt, str, NULL, &dw, &dh, NULL, &t);
  1156. (*ctx->draw)(ctx->fnt, str, NULL, ctx->screen.buf, ctx->theme[UI_BSG], x + 4, y + (h - dh) / 2 + 1 + j, 0, t, ctx->screen.p, x + 2, y + 2, mw, mh);
  1157. (*ctx->draw)(ctx->fnt, str, NULL, ctx->screen.buf, ctx->theme[UI_BFG], x + 3, y + (h - dh) / 2 + j, 0, t, ctx->screen.p, x + 2, y + 2, mw, mh);
  1158. }
  1159. if(tri == 0) _ui_tri(ctx, x + w - 10, y + h / 2 - 2, 0, ctx->theme[UI_IL], ctx->theme[UI_BFG], ctx->theme[UI_ID]); else
  1160. if(tri == 1) _ui_tri(ctx, x + w - 10, y + h / 2 - 2, 1, ctx->theme[UI_IL], ctx->theme[UI_BFG], ctx->theme[UI_ID]);
  1161. _ui_rect(ctx, x, y, w, h, ctx->theme[il], ctx->theme[UI_IBG], ctx->theme[id]);
  1162. }
  1163. /**
  1164. * Draw an option list or number input field
  1165. */
  1166. void _ui_option(ui_t *ctx, int x, int y, int w, int h, int l, int t, int flags, int pressed, int right, int m, char *str)
  1167. {
  1168. ui_image_t *s;
  1169. int il = UI_IL, id = UI_ID, ibg = UI_IBG, ifg = UI_IFG, ibl = UI_BGL, bil = UI_BL, bid = UI_BD, bbg = UI_IBG, dw = 0, dh, bl, bd;
  1170. int p;
  1171. if(!ctx || !ctx->screen.buf || x + w < ctx->cx0 || y + h < ctx->cy0 || x >= ctx->cx1 || y >= ctx->cy1 || h < 2 || w < 2 * h)
  1172. return;
  1173. if(flags & UI_SELECTED) { bid = bil = UI_BS; }
  1174. if(flags & UI_DISABLED) { ibg = bil = bid = bbg = UI_DBG; id = il = ifg = ibl = UI_DFG; pressed = 0; }
  1175. if(!(flags & UI_NOBORDER)) {
  1176. _ui_rect(ctx, x + m, y, w - 2 * m, h, ctx->theme[id], ctx->theme[ibg], ctx->theme[il]);
  1177. x++; y++; w -= 2; h -= 2;
  1178. }
  1179. if(ctx->skin[UI_INP].buf)
  1180. _ui_blit(ctx, x + m, y, w - 2 * m, h, &ctx->skin[UI_INP], 0, 0, flags & UI_DISABLED);
  1181. else
  1182. _ui_frect(ctx, x + h + m, y, w - 2 * (h + m), h, ctx->theme[ibg]);
  1183. s = &ctx->skin[pressed & 1 ? UI_LP : (flags & UI_SELECTED && !(flags & UI_DISABLED) ? UI_LS : UI_LN)];
  1184. if(s->buf)
  1185. _ui_blit(ctx, x, y + 1 + (h - s->h)/2, s->w, s->h, s, 0, 0, flags & UI_DISABLED);
  1186. else {
  1187. if(pressed & 1) { bd = bil; bl = bid; p = 1; } else { bd = bid; bl = bil; p = 0; }
  1188. _ui_rect(ctx, x, y, h, h, ctx->theme[bl], ctx->theme[ibl], ctx->theme[bd]);
  1189. _ui_frect(ctx, x + 1, y + 1, h - 2, h - 2, ctx->theme[ibl]);
  1190. _ui_tri(ctx, x + h / 2 - 3, y + h / 2 - 4 + p, 2, ctx->theme[il], ctx->theme[bbg], ctx->theme[id]);
  1191. }
  1192. s = &ctx->skin[pressed & 2 ? UI_RP : (flags & UI_SELECTED && !(flags & UI_DISABLED) ? UI_RS : UI_RN)];
  1193. if(s->buf)
  1194. _ui_blit(ctx, x + w - s->w, y + 1 + (h - s->h)/2, s->w, s->h, s, 0, 0, flags & UI_DISABLED);
  1195. else {
  1196. if(pressed & 2) { bd = bil; bl = bid; p = 1; } else { bd = bid; bl = bil; p = 0; }
  1197. _ui_rect(ctx, x + w - h, y, h, h, ctx->theme[bl], ctx->theme[ibl], ctx->theme[bd]);
  1198. _ui_frect(ctx, x + w - h + 1, y + 1, h - 2, h - 2, ctx->theme[ibl]);
  1199. _ui_tri(ctx, x + w - h / 2 - 2, y + h / 2 - 4 + p, 3, ctx->theme[il], ctx->theme[bbg], ctx->theme[id]);
  1200. }
  1201. if(ctx->fnt && ctx->draw && str) {
  1202. if(right && ctx->bbox) {
  1203. (*ctx->bbox)(ctx->fnt, str, NULL, &dw, &dh, NULL, NULL);
  1204. dw = w - 2 * h - 2 - dw;
  1205. }
  1206. (*ctx->draw)(ctx->fnt, str, NULL, ctx->screen.buf, ctx->theme[ifg], x + h + 2 + dw - l, y + 1, l, t, ctx->screen.p,
  1207. x + h + 2 - l, y + 1, x + w - h - 2, y + h - 2);
  1208. }
  1209. }
  1210. /**
  1211. * Draw a selectbox
  1212. */
  1213. void _ui_select(ui_t *ctx, int x, int y, int w, int h, int l, int t, int flags, int pressed, int m, char *str)
  1214. {
  1215. ui_image_t *s;
  1216. int il = UI_IL, id = UI_ID, ibg = UI_IBG, ifg = UI_IFG, ibl = UI_BGL, bil = UI_BL, bid = UI_BD, bbg = UI_IBG, bl, bd, p;
  1217. if(!ctx || !ctx->screen.buf || x + w < ctx->cx0 || y + h < ctx->cy0 || x >= ctx->cx1 || y >= ctx->cy1 || h < 2 || w < h)
  1218. return;
  1219. if(flags & UI_SELECTED) { bid = bil = UI_BS; }
  1220. if(flags & UI_DISABLED) { ibg = bil = bid = bbg = UI_DBG; il = id = ifg = ibl = UI_DFG; pressed = 0; }
  1221. if(!(flags & UI_NOBORDER)) {
  1222. _ui_rect(ctx, x, y, w - m, h, ctx->theme[id], ctx->theme[ibg], ctx->theme[il]);
  1223. x++; y++; w -= 2; h -= 2;
  1224. }
  1225. if(ctx->skin[UI_INP].buf)
  1226. _ui_blit(ctx, x, y, w - m, h, &ctx->skin[UI_INP], 0, 0, flags & UI_DISABLED);
  1227. else
  1228. _ui_frect(ctx, x, y, w - h - m, h, ctx->theme[ibg]);
  1229. s = &ctx->skin[pressed & 1 ? UI_DP : (flags & UI_SELECTED && !(flags & UI_DISABLED) ? UI_DS : UI_DN)];
  1230. if(s->buf)
  1231. _ui_blit(ctx, x + w - s->w, y + 1 + (h - s->h)/2, s->w, s->h, s, 0, 0, flags & UI_DISABLED);
  1232. else {
  1233. if(pressed & 1) { bd = bil; bl = bid; p = 1; } else { bd = bid; bl = bil; p = 0; }
  1234. _ui_rect(ctx, x + w - h, y, h, h, ctx->theme[bl], ctx->theme[ibl], ctx->theme[bd]);
  1235. _ui_frect(ctx, x + w - h + 1, y + 1, h - 2, h - 2, ctx->theme[ibl]);
  1236. _ui_tri(ctx, x + w - h / 2 - 3, y + h / 2 - 2 + p, 0, ctx->theme[il], ctx->theme[bbg], ctx->theme[id]);
  1237. }
  1238. if(ctx->fnt && ctx->draw && str)
  1239. (*ctx->draw)(ctx->fnt, str, NULL, ctx->screen.buf, ctx->theme[ifg], x + 2 - l, y + 1, l, t, ctx->screen.p,
  1240. x + 2, y + 1, x + w - h - 2, y + h - 2);
  1241. }
  1242. /**
  1243. * Draw a selectbox popup
  1244. */
  1245. int _ui_select_popup(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form)
  1246. {
  1247. int i, n;
  1248. if(!ctx || !ctx->screen.buf || !form || !form->optv || form->optc < 1 || x >= ctx->screen.w || y >= ctx->screen.h || w < 1 || h < 1)
  1249. return UI_ERR_BADINP;
  1250. if(!(form->flags & UI_NOBORDER)) {
  1251. _ui_rect(ctx, x, y, w, h, ctx->theme[UI_IB], ctx->theme[UI_IBG], ctx->theme[UI_IB]);
  1252. x++; y++; w -= 2; h -= 2;
  1253. if(!(form->flags & UI_NOSHADOW)) {
  1254. _ui_frect(ctx, x + w + 1, y + 3, 4, h - 2, ctx->theme[UI_SH]);
  1255. _ui_frect(ctx, x + 3, y + h + 1, w + 2, 4, ctx->theme[UI_SH]);
  1256. }
  1257. }
  1258. if(ctx->skin[UI_INP].buf)
  1259. _ui_blit(ctx, x, y, w, h, &ctx->skin[UI_INP], 0, 0, 0);
  1260. else
  1261. _ui_frect(ctx, x, y, w, h, ctx->theme[UI_IBG]);
  1262. for(i = 0; i < form->optc; i++, y += form->eh - 4) {
  1263. if(ctx->mousey >= y + 1 && ctx->mousey < y + 1 + (form->eh - 4)) {
  1264. if(form->sel != i) { form->sel = i; ctx->flags |= UI_REFRESH; }
  1265. if(ctx->skin[UI_HL].buf)
  1266. _ui_blit(ctx, x + 1, y + 1, w - 2, form->eh - 4, &ctx->skin[UI_HL], 0, 0, 0);
  1267. else
  1268. _ui_frect(ctx, x + 1, y + 1, w - 2, form->eh - 4, ctx->theme[UI_HLBG]);
  1269. n = UI_HLFG;
  1270. } else n = UI_IFG;
  1271. if(ctx->fnt && ctx->draw)
  1272. (*ctx->draw)(ctx->fnt, form->optv[i], NULL, ctx->screen.buf, ctx->theme[n], x + 2 - form->l, y + 1, form->l, form->t,
  1273. ctx->screen.p, x + 2, y + 1, x + w, y + 1 + (form->eh - 4));
  1274. }
  1275. return UI_OK;
  1276. }
  1277. /**
  1278. * Draw a color field
  1279. */
  1280. void _ui_color(ui_t *ctx, int x, int y, int w, int h, int l, int t, int flags, uint32_t color)
  1281. {
  1282. char tmp[16];
  1283. int il = UI_IL, id = UI_ID, ibg = UI_IBG, ifg = UI_IFG;
  1284. if(!ctx || !ctx->screen.buf || x + w < ctx->cx0 || y + h < ctx->cy0 || x >= ctx->cx1 || y >= ctx->cy1) return;
  1285. if(flags & UI_SELECTED) { id = il = UI_BS; }
  1286. if(flags & UI_DISABLED) { il = id = ibg = UI_DBG; ifg = UI_DFG; }
  1287. if(!(flags & UI_NOBORDER)) {
  1288. _ui_rect(ctx, x, y, w, h, ctx->theme[id], ctx->theme[ibg], ctx->theme[il]);
  1289. x++; y++; w -= 2; h -= 2;
  1290. }
  1291. if(ctx->skin[UI_INP].buf)
  1292. _ui_blit(ctx, x, y, w, h, &ctx->skin[UI_INP], 0, 0, flags & UI_DISABLED);
  1293. else
  1294. _ui_frect(ctx, x, y, w, h, ctx->theme[ibg]);
  1295. if(flags & UI_DISABLED) _ui_checker(ctx, x + 2, y + 2, h - 4, h - 4, ctx->theme[ifg]);
  1296. else _ui_checker(ctx, x + 2, y + 2, h - 4, h - 4, color);
  1297. if(ctx->fnt && ctx->draw) {
  1298. sprintf(tmp, "%08x", color);
  1299. (*ctx->draw)(ctx->fnt, tmp, NULL, ctx->screen.buf, ctx->theme[ifg], x + h - l, y + 1, l, t, ctx->screen.p,
  1300. x + h, y + 1, x + w - 2, y + h - 2);
  1301. }
  1302. }
  1303. /**
  1304. * Draw a color popup
  1305. */
  1306. int _ui_color_popup(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form)
  1307. {
  1308. int cw, dw = 0, dh, i, j, p, pitch;
  1309. uint8_t *C;
  1310. uint32_t *buf, c, d, H;
  1311. if(!ctx || !ctx->screen.buf || !form || !form->ptr || x >= ctx->screen.w || y >= ctx->screen.h || w < 56+256 || h < 16+256)
  1312. return UI_ERR_BADINP;
  1313. C = (uint8_t*)&ctx->color; cw = form->eh - 4; pitch = ctx->screen.p / 4;
  1314. if(!(form->flags & UI_NOBORDER)) {
  1315. _ui_rect(ctx, x, y, w, h, ctx->theme[UI_IB], ctx->theme[UI_IBG], ctx->theme[UI_IB]);
  1316. x++; y++; w -= 2; h -= 2; cw -= 2;
  1317. if(!(form->flags & UI_NOSHADOW)) {
  1318. _ui_frect(ctx, x + w + 1, y + 3, 4, h - 2, ctx->theme[UI_SH]);
  1319. _ui_frect(ctx, x + 3, y + h + 1, w + 2, 4, ctx->theme[UI_SH]);
  1320. }
  1321. }
  1322. if(ctx->skin[UI_INP].buf)
  1323. _ui_blit(ctx, x, y, w, h, &ctx->skin[UI_INP], 0, 0, 0);
  1324. else
  1325. _ui_frect(ctx, x, y, w, h, ctx->theme[UI_IBG]);
  1326. _ui_checker(ctx, x + 2, y + 2, cw, cw, ctx->color);
  1327. if(ctx->fnt && ctx->draw && ctx->bbox && ctx->buf) {
  1328. (*ctx->bbox)(ctx->fnt, ctx->buf, ctx->cur, &dw, &dh, NULL, NULL);
  1329. (*ctx->draw)(ctx->fnt, ctx->buf, NULL, ctx->screen.buf, ctx->theme[UI_ICF], x + cw + 4 - form->l, y + 1, form->l, form->t,
  1330. ctx->screen.p, x + cw + 4, y + 1, x + w - 2, y + 3 + cw);
  1331. buf = (uint32_t*)(ctx->screen.buf + (y + 1) * ctx->screen.p + (x + cw + dw + 4) * 4);
  1332. for(i = 0; i < cw + 2; i++, buf += pitch) *buf = ctx->theme[UI_IC];
  1333. }
  1334. x += 2; y += form->eh + 6;
  1335. for(i = 0; i < 16; i++)
  1336. _ui_checker(ctx, x + 1, y + 2 + (i << 4), 12, 12, ctx->hist[i]);
  1337. buf = (uint32_t*)(ctx->screen.buf + y * ctx->screen.p + x * 4);
  1338. for(p = 20, j = 0; j < 256; j++, p += pitch) {
  1339. if(C[3] == 255 - j) {
  1340. buf[p - 4] = buf[p - 3] = buf[p - 2] = buf[p - 3 - pitch] = buf[p - 3 + pitch] = buf[p - 4 - 2*pitch] =
  1341. buf[p - 4 - pitch] = buf[p - 4 + pitch] = buf[p - 4 + 2*pitch] = ctx->theme[UI_IC];
  1342. for(i = 0; i < 16; i++)
  1343. buf[p + i] = ctx->theme[UI_IC];
  1344. } else {
  1345. c = 256 - j;
  1346. for(i = 0; i < 16; i++) {
  1347. d = j * ((j & 8) ^ (i & 8) ? 0x9f : 0x5f);
  1348. ((uint8_t*)&buf[p+i])[3] = 255;
  1349. ((uint8_t*)&buf[p+i])[2] = (d + c*C[2]) >> 8;
  1350. ((uint8_t*)&buf[p+i])[1] = (d + c*C[1]) >> 8;
  1351. ((uint8_t*)&buf[p+i])[0] = (d + c*C[0]) >> 8;
  1352. }
  1353. }
  1354. }
  1355. for(p = 38, j = 0; j < 256; j++, p += pitch) {
  1356. for(i = 0; i < 256; i++) {
  1357. H = 255 - j == ctx->val ? j + (((ctx->val * i) >> 8) & 0xFF) : j; H |= 0xFF000000 | (H << 16) | (H << 8);
  1358. buf[p + i] = ctx->sat == i || 255-j == ctx->val ? H : _ui_hsv2rgb(255, ctx->hue, i, 255 - j);
  1359. }
  1360. }
  1361. for(p = 40 + 256, j = 0; j < 256; j++, p += pitch) {
  1362. H = _ui_hsv2rgb(255, j, 255, 255);
  1363. if(ctx->hue == j) {
  1364. H = ctx->theme[UI_IC];
  1365. buf[p + 20] = buf[p + 19] = buf[p + 18] = buf[p + 19 - pitch] = buf[p + 19 + pitch] = buf[p + 20 - 2*pitch] =
  1366. buf[p + 20 - pitch] = buf[p + 20 + pitch] = buf[p + 20 + 2*pitch] = H;
  1367. }
  1368. for(i = 0; i < 16; i++)
  1369. buf[p + i] = H;
  1370. }
  1371. return UI_OK;
  1372. }
  1373. /**
  1374. * Display a popup
  1375. */
  1376. int _ui_popup(ui_t *ctx, int x, int y, ui_form_t *form)
  1377. {
  1378. ui_image_t *s;
  1379. int n = UI_TIT, w, h, m, m2, top;
  1380. if(!ctx || !ctx->screen.buf || !form || x >= ctx->screen.w || y >= ctx->screen.h || form->ew < 1 || form->eh < 1)
  1381. return UI_ERR_BADINP;
  1382. if(x < 0) x = 0;
  1383. if(y < 0) y = 0;
  1384. w = form->ew; h = form->eh; m = form->m - 2;
  1385. if(m < 0) m = 0;
  1386. m2 = 2 * m;
  1387. top = form->value < 0 ? 0 : form->value;
  1388. ctx->cx0 = ctx->cy0 = 0; ctx->cx1 = ctx->screen.w - 1; ctx->cy1 = ctx->screen.h - 1;
  1389. if((form->type == UI_POPUP || form->type == UI_MENU) && !(form->flags & UI_NOBORDER)) {
  1390. x++; y++; w -= 2; h -= 2;
  1391. if(ctx->skin[UI_P_BG].buf || ((form->flags & UI_ALTSKIN) && ctx->skin[UI_A_BG].buf)) {
  1392. s = &ctx->skin[form->flags & UI_ALTSKIN ? UI_A_TL : UI_P_TL];
  1393. _ui_blit(ctx, x - s[0].w, y - s[0].h, s[0].w, s[0].h, &s[0], 0, 0, 0);
  1394. _ui_blit(ctx, x, y - s[1].h, w, s[1].h, &s[1], 0, 0, 0);
  1395. _ui_blit(ctx, x + w, y - s[2].h, s[2].w, s[2].h, &s[2], 0, 0, 0);
  1396. _ui_blit(ctx, x - s[3].w, y, s[3].w, h, &s[3], 0, 0, 0);
  1397. _ui_blit(ctx, x + w, y, s[5].w, h, &s[5], 0, 0, 0);
  1398. _ui_blit(ctx, x - s[6].w, y + h, s[6].w, s[6].h, &s[6], 0, 0, 0);
  1399. _ui_blit(ctx, x, y + h, w, s[7].h, &s[7], 0, 0, 0);
  1400. _ui_blit(ctx, x + w, y + h, s[8].w, s[8].h, &s[8], 0, 0, 0);
  1401. } else {
  1402. _ui_rect(ctx, x - 1, y - 1, w + 2, h + 2, ctx->theme[UI_IL], ctx->theme[UI_BG], ctx->theme[UI_ID]);
  1403. if(!(form->flags & UI_NOSHADOW)) {
  1404. _ui_frect(ctx, x + w + 1, y + 3, 4, h - 2, ctx->theme[UI_SH]);
  1405. _ui_frect(ctx, x + 3, y + h + 1, w + 2, 4, ctx->theme[UI_SH]);
  1406. }
  1407. }
  1408. }
  1409. ctx->cx0 = x; ctx->cy0 = y; ctx->cx1 = x + w + 1; ctx->cy1 = y + h + 1;
  1410. if(ctx->cx1 >= ctx->screen.w) ctx->cx1 = ctx->screen.w - 1;
  1411. if(ctx->cy1 >= ctx->screen.h) ctx->cy1 = ctx->screen.h - 1;
  1412. if(form->type != UI_DIV) {
  1413. if((form->flags & UI_ALTSKIN) && ctx->skin[UI_A_BG].buf)
  1414. _ui_blit(ctx, x, y, w, h, &ctx->skin[UI_A_BG], 0, 0, 0);
  1415. else
  1416. if(ctx->skin[UI_P_BG].buf)
  1417. _ui_blit(ctx, x, y, w, h, &ctx->skin[UI_P_BG], 0, 0, 0);
  1418. else
  1419. _ui_frect(ctx, x, y, w, h, ctx->theme[UI_BG]);
  1420. }
  1421. form->sw = form->sh = 0;
  1422. if(form->type == UI_POPUP) {
  1423. if(form->flags & UI_RESIZABLE)
  1424. _ui_tri(ctx, x + w - 7, y + h - 7, 4, ctx->theme[UI_IL], ctx->theme[UI_IL], ctx->theme[UI_IL]);
  1425. if(form->flags & UI_DRAGGABLE) {
  1426. if((form->flags & UI_ALTSKIN) && ctx->skin[UI_A_TIT].buf) {
  1427. _ui_blit(ctx, x + 1, y + 1, w - ctx->skin[UI_A_CLOSE].w - 1, top - 2, &ctx->skin[UI_A_TIT], 0, 0, 0);
  1428. _ui_blit(ctx, x + w - ctx->skin[UI_A_CLOSE].w, y + (top - ctx->skin[UI_A_CLOSE].h) / 2,
  1429. ctx->skin[UI_A_CLOSE].w, ctx->skin[UI_A_CLOSE].h, &ctx->skin[UI_A_CLOSE], 0, 0, 0);
  1430. } else
  1431. if(ctx->skin[UI_P_TIT].buf) {
  1432. _ui_blit(ctx, x + 1, y + 1, w - ctx->skin[UI_P_CLOSE].w - 1, top - 2, &ctx->skin[UI_P_TIT], 0, 0, 0);
  1433. _ui_blit(ctx, x + w - ctx->skin[UI_P_CLOSE].w, y + (top - ctx->skin[UI_P_CLOSE].h) / 2,
  1434. ctx->skin[UI_P_CLOSE].w, ctx->skin[UI_P_CLOSE].h, &ctx->skin[UI_P_CLOSE], 0, 0, 0);
  1435. } else {
  1436. _ui_frect(ctx, x + 1, y + 1, w - 12, top - 2, ctx->theme[UI_TIT]);
  1437. _ui_checkbox(ctx, x + w - 5, y + (top + 1) / 2, -2, 0);
  1438. }
  1439. n = UI_BG;
  1440. }
  1441. if(ctx->fnt && ctx->draw && ctx->txtv && form->label > 0 && form->label < ctx->txtc)
  1442. (*ctx->draw)(ctx->fnt, ctx->txtv[form->label], NULL, ctx->screen.buf, ctx->theme[n],
  1443. x + 2 - form->l, y + 1, form->l, form->t, ctx->screen.p, ctx->cx0, ctx->cy0, ctx->cx1 - 16, ctx->cy1);
  1444. y += top; h -= top;
  1445. if((form->flags & UI_HSCROLL) && w - m2 < form->mw) {
  1446. h -= ctx->sh;
  1447. form->sw = w - m2 - ((form->flags & UI_VSCROLL) && h - m2 < form->mh ? ctx->sw : 0);
  1448. if(form->ox > form->mw - form->sw) form->ox = form->mw - form->sw;
  1449. if(form->ox < 0) form->ox = 0;
  1450. _ui_hscrbar(ctx, x + m, y + h - m, form->sw, form->ox, form->mw, ctx->hscr == form);
  1451. } else form->ox = 0;
  1452. if((form->flags & UI_VSCROLL) && h - m2 < form->mh) {
  1453. w -= ctx->sw;
  1454. form->sh = h - m2;
  1455. if(form->oy > form->mh - form->sh) form->oy = form->mh - form->sh;
  1456. if(form->oy < 0) form->oy = 0;
  1457. _ui_vscrbar(ctx, x + w - m, y + m, form->sh, form->oy, form->mh, ctx->vscr == form);
  1458. } else form->oy = 0;
  1459. }
  1460. if(form->ptr) _ui_draw(ctx, x + m, y + m, w - m2, h - m2, form->ox, form->oy, form->ptr, form->type);
  1461. return UI_OK;
  1462. }
  1463. /**
  1464. * Set font hooks. Only needed if you want custom driver
  1465. */
  1466. int ui_fonthook(ui_t *ctx, ui_font_bbox bbox, ui_font_draw draw)
  1467. {
  1468. if(!ctx || !bbox || !draw) return UI_ERR_BADINP;
  1469. ctx->bbox = bbox; ctx->draw = draw;
  1470. return UI_OK;
  1471. }
  1472. /**
  1473. * Set the UI font (driver specific, opaque to UI)
  1474. */
  1475. int ui_font(ui_t *ctx, void *fnt)
  1476. {
  1477. if(!ctx || !fnt) return UI_ERR_BADINP;
  1478. ctx->fnt = fnt;
  1479. return UI_OK;
  1480. }
  1481. /**
  1482. * Set the software mouse cursor
  1483. */
  1484. int ui_swcursor(ui_t *ctx, ui_image_t *cursor)
  1485. {
  1486. if(!ctx) return UI_ERR_BADINP;
  1487. ctx->mouse.w = ctx->mouse.h = 0;
  1488. if(!cursor || !cursor->buf || cursor->w < 1 || cursor->h < 1) return UI_ERR_BADINP;
  1489. if(!(ctx->mouse.buf = (uint8_t*)realloc(ctx->mouse.buf, cursor->w * cursor->h * 4))) return UI_ERR_NOMEM;
  1490. memset(ctx->mouse.buf, 0, cursor->w * cursor->h * 4);
  1491. ctx->mouse.p = cursor->w * 4;
  1492. ctx->mouse.w = cursor->w;
  1493. ctx->mouse.h = cursor->h;
  1494. ui_backend_hidecursor(ctx->bck);
  1495. return UI_OK;
  1496. }
  1497. /**
  1498. * Set the hardware mouse cursor
  1499. */
  1500. int ui_hwcursor(ui_t *ctx)
  1501. {
  1502. if(!ctx) return UI_ERR_BADINP;
  1503. if(ctx->mouse.buf) free(ctx->mouse.buf);
  1504. ctx->mouse.buf = NULL;
  1505. ctx->mouse.w = ctx->mouse.h = ctx->mouse.p = 0;
  1506. ui_backend_showcursor(ctx->bck);
  1507. return UI_OK;
  1508. }
  1509. /**
  1510. * Set the color theme
  1511. */
  1512. int ui_theme(ui_t *ctx, uint32_t *theme)
  1513. {
  1514. if(!ctx || !theme) return UI_ERR_BADINP;
  1515. memcpy(&ctx->theme, theme, UI_NUMTHEME * sizeof(uint32_t));
  1516. return UI_OK;
  1517. }
  1518. /**
  1519. * Set the skin from multiple images
  1520. */
  1521. int ui_skin(ui_t *ctx, ui_image_t *skin)
  1522. {
  1523. if(!ctx || !skin) return UI_ERR_BADINP;
  1524. memcpy(&ctx->skin, skin, UI_NUMSKIN * sizeof(ui_image_t));
  1525. ctx->sw = ctx->sh = 0;
  1526. if(ctx->sw < ctx->skin[UI_SV_BT].w) ctx->sw = ctx->skin[UI_SV_BT].w;
  1527. if(ctx->sw < ctx->skin[UI_SV_BM].w) ctx->sw = ctx->skin[UI_SV_BM].w;
  1528. if(ctx->sw < ctx->skin[UI_SV_BB].w) ctx->sw = ctx->skin[UI_SV_BB].w;
  1529. if(ctx->sh < ctx->skin[UI_SH_BL].h) ctx->sh = ctx->skin[UI_SH_BL].h;
  1530. if(ctx->sh < ctx->skin[UI_SH_BM].h) ctx->sh = ctx->skin[UI_SH_BM].h;
  1531. if(ctx->sh < ctx->skin[UI_SH_BR].h) ctx->sh = ctx->skin[UI_SH_BR].h;
  1532. ui_swcursor(ctx, &ctx->skin[UI_CURSOR]);
  1533. return UI_OK;
  1534. }
  1535. /**
  1536. * Set the skin from a single PNG file
  1537. */
  1538. int ui_pngskin(ui_t *ctx, uint8_t *png, int size)
  1539. {
  1540. #if defined(STBI_INCLUDE_STB_IMAGE_H) && !defined(STBI_NO_PNG)
  1541. ui_image_t skin[UI_NUMSKIN] = { 0 };
  1542. uint8_t *ret, *end, *ptr, *comment = NULL, *cend;
  1543. uint32_t cs;
  1544. int i, x, y, w, h, W, H, p;
  1545. stbi__context s = { 0 };
  1546. stbi__result_info ri = { 0 };
  1547. /* decode the png */
  1548. s.img_buffer = s.img_buffer_original = png;
  1549. s.img_buffer_end = s.img_buffer_original_end = end = png + size;
  1550. ri.bits_per_channel = 8;
  1551. if(!ctx || !png || size < 16 || !(ret = (uint8_t*)stbi__png_load(&s, &W, &H, &p, 4, &ri))) return UI_ERR_BADINP;
  1552. /* locate the comment */
  1553. for(ptr = png + 8; ptr < end; ptr += cs + 12) {
  1554. cs = ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]);
  1555. if(!memcmp(ptr + 4, "zTXtComment", 12)) {
  1556. comment = (uint8_t*)stbi_zlib_decode_malloc_guesssize_headerflag((const char *)ptr + 17, cs, 65536, &p, 1);
  1557. cend = comment + p;
  1558. break;
  1559. } else
  1560. if(!memcmp(ptr + 4, "tEXtComment", 12)) {
  1561. comment = ptr + 17;
  1562. cend = comment + cs - 9;
  1563. break;
  1564. }
  1565. }
  1566. if(comment) {
  1567. for(i = 0, ptr = comment; ptr < cend && i < UI_NUMSKIN;) {
  1568. /* only parse numbers in each line, so this can work with tabtext, json and xml as well */
  1569. while(ptr < cend && (*ptr < '0' || *ptr > '9')) { ptr++; } if(ptr >= cend) break;
  1570. x = atoi((char*)ptr); while(ptr < cend && *ptr >= '0' && *ptr <= '9') { ptr++; }
  1571. while(ptr < cend && (*ptr < '0' || *ptr > '9')) { ptr++; } if(ptr >= cend) break;
  1572. y = atoi((char*)ptr); while(ptr < cend && *ptr >= '0' && *ptr <= '9') { ptr++; }
  1573. while(ptr < cend && (*ptr < '0' || *ptr > '9')) { ptr++; } if(ptr >= cend) break;
  1574. w = atoi((char*)ptr); while(ptr < cend && *ptr >= '0' && *ptr <= '9') { ptr++; }
  1575. while(ptr < cend && (*ptr < '0' || *ptr > '9')) { ptr++; } if(ptr >= cend) break;
  1576. h = atoi((char*)ptr); while(ptr < cend && *ptr >= '0' && *ptr <= '9') { ptr++; }
  1577. while(ptr < cend && *ptr != '\n' && *ptr != ')' && *ptr != ']' && *ptr != '}' && *ptr != '>') ptr++;
  1578. if(w > 0 && h > 0 && x >= 0 && x + w <= W && y >= 0 && y + h <= H) {
  1579. skin[i].w = w; skin[i].h = h; skin[i].p = W * 4;
  1580. skin[i++].buf = ret + (y * W + x) * 4;
  1581. }
  1582. }
  1583. if(comment < png || comment >= end) free(comment);
  1584. ui_skin(ctx, skin);
  1585. if(ctx->skinbuf) free(ctx->skinbuf);
  1586. ctx->skinbuf = ret;
  1587. return UI_OK;
  1588. } else
  1589. free(ret);
  1590. #else
  1591. (void)ctx; (void)png; (void)size;
  1592. #endif
  1593. return UI_ERR_BADINP;
  1594. }
  1595. /**
  1596. * Recalculate form element positions
  1597. */
  1598. int _ui_recalc(ui_t *ctx, int x, int y, int w, int h, int p, ui_form_t *form, int *ow, int *oh)
  1599. {
  1600. ui_form_t *last = NULL;
  1601. char tmp[32];
  1602. int ret, i, rx = 0, dx = 0, dy = 0, dw, dh, lh = 0, mw = 0, mh = 0, tw, th, tl, tt, nw;
  1603. int16_t sx, sy, sw, sh, px, py, pw, ph;
  1604. if(!ctx) return UI_ERR_BADINP;
  1605. if(!form) return UI_OK;
  1606. if(w < 0) w = 0;
  1607. if(h < 0) h = 0;
  1608. rx = w - 2;
  1609. ctx->flags |= UI_REFRESH;
  1610. /* get the default size and default top */
  1611. if(!ctx->ds && ctx->fnt && ctx->bbox)
  1612. (*ctx->bbox)(ctx->fnt, "Ag", NULL, &dw, &ctx->ds, NULL, &ctx->dt);
  1613. for(; form->type != UI_END; last = form++) {
  1614. /* calculate effective values from user provided values */
  1615. sx = (int16_t)form->x; sy = (int16_t)form->y;
  1616. sw = (int16_t)form->w; sh = (int16_t)form->h;
  1617. px = (form->x & 0x7f0000) >> 16; py = (form->y & 0x7f0000) >> 16;
  1618. pw = (form->w & 0x7f0000) >> 16; ph = (form->h & 0x7f0000) >> 16;
  1619. form->ex = x + (px ? w * px / 100 + sx : (form->x & 0x800000 ? (sx < 0 ? w + sx : sx) : dx + sx));
  1620. form->ey = y + (py ? h * py / 100 + sy : (form->y & 0x800000 ? (sy < 0 ? h + sy : sy) : dy + sy));
  1621. form->ew = pw ? w * pw / 100 + sw : (form->w & 0x800000 ? w - (sw & 0x7fff) - form->ex : (sw & 0x7fff));
  1622. form->eh = ph ? h * ph / 100 + sh : (form->h & 0x800000 ? h - (sh & 0x7fff) - form->ey : (sh & 0x7fff));
  1623. /* measure the size */
  1624. dw = dh = 0;
  1625. switch(form->type) {
  1626. case UI_POPUP:
  1627. case UI_MENU:
  1628. case UI_DIV:
  1629. dw = form->ew - 2 * form->m; if(dw < 0) dw = 0;
  1630. dh = form->eh - 2 * form->m; if(dh < 0) dh = 0;
  1631. dw += 4; dh += 4;
  1632. if(w && dw > w) dw = w;
  1633. form->mw = dw; form->mh = dh;
  1634. /* calculate container's contents */
  1635. if((ret = _ui_recalc(ctx, 2, 2, dw, dh, form->p, form->ptr, &dw, &dh)) != UI_OK) continue;
  1636. /* add 2 + 2 pixel margin */
  1637. if(form->mw < dw + 4) form->mw = dw + 4;
  1638. if(form->mh < dh + 4) form->mh = dh + 4;
  1639. /* add popup title */
  1640. form->value = 0;
  1641. if(form->type == UI_POPUP) {
  1642. if(ctx->fnt && ctx->bbox && ctx->txtv && form->label > 0 && form->label < ctx->txtc) {
  1643. (*ctx->bbox)(ctx->fnt, ctx->txtv[form->label], NULL, &tw, &th, &form->l, &form->t);
  1644. if(form->flags & UI_DRAGGABLE && th < 9) th = 9;
  1645. if(dw < tw) dw = tw;
  1646. form->value = th + 2;
  1647. }
  1648. if(form->flags & UI_DRAGGABLE) {
  1649. form->value = 11;
  1650. if((form->flags & UI_ALTSKIN) && ctx->skin[UI_A_TIT].buf && form->value < ctx->skin[UI_A_CLOSE].h + 2)
  1651. form->value = ctx->skin[UI_A_CLOSE].h + 2;
  1652. else
  1653. if(ctx->skin[UI_P_TIT].buf && form->value < ctx->skin[UI_P_CLOSE].h + 2)
  1654. form->value = ctx->skin[UI_P_CLOSE].h + 2;
  1655. }
  1656. }
  1657. dw += 2 * form->m + 2;
  1658. dh += 2 * form->m + form->value + 2;
  1659. if(dw < 16) dw = 16;
  1660. if(dh < 16) dh = 16;
  1661. if(form->type == UI_DIV) { dw += 2; dh += 2; }
  1662. if(!(form->flags & UI_SCROLL) || form->ew < 1 || form->eh < 1) {
  1663. if(form->ew < dw) form->ew = dw;
  1664. if(form->eh < dh) form->eh = dh;
  1665. }
  1666. if(form->type == UI_MENU) form->flags |= UI_HIDDEN;
  1667. if(last && last->type == UI_TOGGLE && !form->x && !form->y) {
  1668. form->ex = last->ex + (last->flags & UI_NOBULLET ? 0 : last->m + 7);
  1669. form->ey = last->ey + last->eh;
  1670. if(form->ex + form->ew > x + w) form->ew = x + w - form->ex;
  1671. if(form->type == UI_DIV && !(form->flags & UI_HIDDEN)) {
  1672. dx = form->ex; dy = form->ey;
  1673. }
  1674. }
  1675. if(form->type == UI_POPUP || form->type == UI_MENU) {
  1676. if(form->ex + form->ew + 1 > ctx->screen.w) {
  1677. form->ex = ctx->screen.w - form->ew - 1;
  1678. if(form->ex < 1) { form->ex = 1; form->ew = ctx->screen.w - 2; }
  1679. }
  1680. if(form->ey + form->eh + 1 > ctx->screen.h) {
  1681. form->ey = ctx->screen.h - form->eh - 1;
  1682. if(form->ey < 1) { form->ey = 1; form->eh = ctx->screen.h - 2; }
  1683. }
  1684. }
  1685. dw = form->ew; dh = form->eh;
  1686. break;
  1687. case UI_TOGGLE:
  1688. case UI_STATUS:
  1689. case UI_LABEL:
  1690. case UI_CHECK:
  1691. case UI_RADIO:
  1692. if(ctx->fnt && ctx->bbox) {
  1693. if(ctx->txtv && form->type != UI_STATUS && form->label > 0 && form->label < ctx->txtc)
  1694. (*ctx->bbox)(ctx->fnt, ctx->txtv[form->label], NULL, &dw, &dh, &form->l, &form->t);
  1695. else if(form->ptr && (form->type == UI_LABEL || form->type == UI_TOGGLE))
  1696. (*ctx->bbox)(ctx->fnt, (char*)form->ptr, NULL, &dw, &dh, &form->l, &form->t);
  1697. else if(form->type == UI_STATUS) { dh = ctx->ds; form->t = ctx->dt; }
  1698. dh += 4;
  1699. }
  1700. if(dh < 9) dh = 9;
  1701. if(form->type == UI_TOGGLE)
  1702. dw += (form->flags & UI_NOBULLET ? 2 * form->m : 9);
  1703. else
  1704. if(!(form->flags & UI_NOBULLET) && form->type > UI_STATUS)
  1705. dw += dh;
  1706. if(form->icon && form->icon->buf) dw += dh;
  1707. break;
  1708. case UI_BUTTON:
  1709. case UI_BTNTGL:
  1710. if(ctx->fnt && ctx->bbox && ctx->txtv && form->label > 0 && form->label < ctx->txtc) {
  1711. (*ctx->bbox)(ctx->fnt, ctx->txtv[form->label], NULL, &dw, &dh, NULL, NULL);
  1712. if(form->icon && form->icon->buf) dw += 4;
  1713. }
  1714. if(form->icon && form->icon->buf) {
  1715. dw += form->icon->w;
  1716. if(dh < form->icon->h) dh = form->icon->h;
  1717. }
  1718. dw += 8 + form->min; dh += 4;
  1719. if(ctx->skin[UI_BNM].buf) {
  1720. dw += ctx->skin[UI_BNL].w + ctx->skin[UI_BNR].w;
  1721. if(dh < ctx->skin[UI_BNL].h) dh = ctx->skin[UI_BNL].h;
  1722. if(dh < ctx->skin[UI_BNM].h) dh = ctx->skin[UI_BNM].h;
  1723. if(dh < ctx->skin[UI_BNR].h) dh = ctx->skin[UI_BNR].h;
  1724. }
  1725. break;
  1726. case UI_DEC8: case UI_DEC16: case UI_DEC32: case UI_DEC64:
  1727. case UI_HEX8: case UI_HEX16: case UI_HEX32: case UI_HEX64:
  1728. case UI_DEC_FLOAT:
  1729. sprintf(tmp, "0.000");
  1730. if(ctx->fnt && ctx->bbox) {
  1731. (*ctx->bbox)(ctx->fnt, tmp, NULL, &dw, &dh, &form->l, &form->t);
  1732. dh += 4;
  1733. }
  1734. break;
  1735. case UI_SLIDER:
  1736. case UI_PBAR:
  1737. sprintf(tmp, "100.0%%");
  1738. if(ctx->fnt && ctx->bbox) {
  1739. (*ctx->bbox)(ctx->fnt, tmp, NULL, &dw, &dh, NULL, NULL);
  1740. dh += 4;
  1741. }
  1742. nw = x + mw - form->ex;
  1743. if(form->ew < 1) form->ew = nw > 100 ? nw : 100;
  1744. if(form->type == UI_SLIDER) {
  1745. if(dh < ctx->skin[UI_S_L].h) dh = ctx->skin[UI_S_L].h;
  1746. if(dh < ctx->skin[UI_S_M].h) dh = ctx->skin[UI_S_M].h;
  1747. if(dh < ctx->skin[UI_S_R].h) dh = ctx->skin[UI_S_R].h;
  1748. if(dh < ctx->skin[UI_S_B].h) dh = ctx->skin[UI_S_B].h;
  1749. }
  1750. break;
  1751. case UI_VSCRBAR:
  1752. form->ew = ctx->sw;
  1753. if(form->eh < ctx->sh) form->eh = ctx->sh;
  1754. break;
  1755. case UI_HSCRBAR:
  1756. if(form->ew < ctx->sw) form->ew = ctx->sw;
  1757. form->eh = ctx->sh;
  1758. break;
  1759. case UI_IMAGE:
  1760. case UI_BTNICN:
  1761. if(form->icon && form->icon->buf && form->icon->w > 0 && form->icon->h > 0) {
  1762. dw = form->icon->w; dh = form->icon->h;
  1763. }
  1764. break;
  1765. case UI_TXTINP:
  1766. if(ctx->fnt && ctx->bbox) {
  1767. /* if string is empty, use a default one to get height and baseline correctly */
  1768. (*ctx->bbox)(ctx->fnt, form->ptr, NULL, &dw, &dh, &form->l, &form->t);
  1769. if(!form->ptr || !*((char*)form->ptr)) { dw = 0; dh = ctx->ds; form->t = ctx->dt; }
  1770. dh += 4;
  1771. }
  1772. if(form->ew > 0) dw = form->ew;
  1773. break;
  1774. case UI_SELECT:
  1775. case UI_OPTION:
  1776. form->l = form->t = 0;
  1777. if(form->optv && form->optc && ctx->bbox) {
  1778. for(tl = tt = i = 0; i < form->optc; i++) {
  1779. (*ctx->bbox)(ctx->fnt, form->optv[i], NULL, &tw, &th, &tl, &tt);
  1780. th += 4;
  1781. if(dw < tw) dw = tw;
  1782. if(dh < th) dh = th;
  1783. if(form->l < tl) form->l = tl;
  1784. if(form->t < tt) form->t = tt;
  1785. }
  1786. dw += 4 + (form->type == UI_SELECT ? dh : 2 * dh);
  1787. }
  1788. break;
  1789. case UI_INT8: case UI_INT16: case UI_INT32: case UI_INT64:
  1790. case UI_FLOAT:
  1791. if(form->type == UI_FLOAT)
  1792. sprintf(tmp, "%g.000", -form->fmin > form->fmax ? form->fmin : form->fmax);
  1793. else
  1794. sprintf(tmp, "%" L "d", -form->min > form->max ? form->min : form->max);
  1795. if(ctx->fnt && ctx->bbox) {
  1796. (*ctx->bbox)(ctx->fnt, tmp, NULL, &dw, &dh, &form->l, &form->t);
  1797. dh += 4; dw += 4 + 2 * dh;
  1798. }
  1799. break;
  1800. case UI_COLOR:
  1801. sprintf(tmp, "FFFFFFFF");
  1802. if(ctx->fnt && ctx->bbox) {
  1803. (*ctx->bbox)(ctx->fnt, tmp, NULL, &dw, &dh, &form->l, &form->t);
  1804. dh += 4; dw += dh;
  1805. }
  1806. break;
  1807. case UI_CUSTOM:
  1808. if(form->bbox)
  1809. (*form->bbox)(ctx, form->ex, form->ey, form->ew, form->eh, form, &dw, &dh);
  1810. break;
  1811. }
  1812. if(form->ew < dw) form->ew = dw;
  1813. if(form->eh < dh) form->eh = dh;
  1814. dw = form->ew; dh = form->eh;
  1815. /* handle alignment */
  1816. switch(form->align & 3) {
  1817. case 1: form->ex -= dw; break;
  1818. case 2: form->ex -= dw / 2; break;
  1819. case 3: form->ex = x; break;
  1820. }
  1821. switch((form->align >> 2) & 3) {
  1822. case 1: form->ey -= dh; break;
  1823. case 2: form->ey -= dh / 2; break;
  1824. case 3: form->ey = y; break;
  1825. }
  1826. /* handle flow */
  1827. if(!(form->flags & UI_HIDDEN)) {
  1828. if(form->type != UI_POPUP && form->type != UI_MENU && !(form->x & 0xff0000) && !(form->y & 0xff0000)) {
  1829. i = (last && (last->flags & UI_NOBR));
  1830. if((form->align & 3) == 1 && !form->x) {
  1831. /* subtract mandatory 2 pixel margin */
  1832. if(rx < w - 2) rx -= p;
  1833. rx -= dw;
  1834. if(lh < form->eh) lh = form->eh;
  1835. if(rx < x) { rx = w - dw - 2; if(lh) { lh += p; } form->ey += lh; dy += lh; lh = 0; }
  1836. form->ex = rx;
  1837. } else {
  1838. if(form->ex > x) form->ex += p;
  1839. if((w > 0 && !(form->flags & UI_FORCEBR)) || i) {
  1840. if(!i && form->ex + form->ew + 2 >= w) {
  1841. if(lh) { lh += p; } form->ex = x; form->ey += lh; dy += lh; lh = 0;
  1842. }
  1843. if(!(form->y & 0x800000)) dy += sy;
  1844. if(lh < form->eh) lh = form->eh;
  1845. dx = form->ex + form->ew;
  1846. } else {
  1847. if(lh < form->eh) lh = form->eh;
  1848. dy += lh + p;
  1849. dx = lh = 0;
  1850. }
  1851. dw = form->ex + form->ew - x; if(mw < dw) mw = dw;
  1852. dh = form->ey + form->eh - y; if(mh < dh) mh = dh;
  1853. }
  1854. }
  1855. }
  1856. }
  1857. if(ow) *ow = mw;
  1858. if(oh) *oh = mh;
  1859. return UI_OK;
  1860. }
  1861. /**
  1862. * Display a form
  1863. */
  1864. int _ui_draw(ui_t *ctx, int x, int y, int w, int h, int ox, int oy, ui_form_t *form, int parent)
  1865. {
  1866. ui_form_t *popup;
  1867. char tmp[32];
  1868. int i, s, e, n, m, tx, ty, cx0, cx1, cy0, cy1, x0, x1, dw, dh;
  1869. int16_t *ints, bx0, bx1, by0, by1;
  1870. if(!ctx || !ctx->screen.buf || x >= ctx->screen.w || y >= ctx->screen.h)
  1871. return UI_ERR_BADINP;
  1872. if(!form || w < 1 || h < 1) return UI_OK;
  1873. cx0 = ctx->cx0; cx1 = ctx->cx1; cy0 = ctx->cy0; cy1 = ctx->cy1;
  1874. if(ctx->cx0 < x) ctx->cx0 = x;
  1875. if(ctx->cy0 < y) ctx->cy0 = y;
  1876. if(ctx->cx1 > x + w) ctx->cx1 = x + w;
  1877. if(ctx->cy1 > y + h) ctx->cy1 = y + h;
  1878. x0 = ctx->cx0; x1 = ctx->cx1;
  1879. for(; form->type != UI_END; form++) {
  1880. if((form->flags & UI_HIDDEN)) { if(parent < 0) break; else continue; }
  1881. tx = x + form->ex - ox; ty = y + form->ey - oy;
  1882. ctx->cx0 = x0; ctx->cx1 = x1;
  1883. switch(form->type) {
  1884. case UI_POPUP:
  1885. case UI_MENU:
  1886. if(ctx->numpopups < UI_MAXPOPUPS) {
  1887. for(n = 0; n < ctx->numpopups && ctx->popups[n]->min > form->min; n++);
  1888. if(ctx->numpopups > n)
  1889. memmove(&ctx->popups[n + 1], &ctx->popups[n], (ctx->numpopups - n) * sizeof(void*));
  1890. ctx->popups[n] = form;
  1891. ctx->numpopups++;
  1892. }
  1893. break;
  1894. case UI_DIV:
  1895. n = ctx->cy0; m = ctx->cy1;
  1896. _ui_popup(ctx, tx, ty, form);
  1897. ctx->cy0 = n; ctx->cy1 = m;
  1898. break;
  1899. case UI_TOGGLE:
  1900. popup = !form->value && UI_CONTAINER(form[1].type) ? &form[1] : &ctx->form[form->value];
  1901. s = UI_CONTAINER(popup->type) ? !(popup->flags & UI_HIDDEN) : 0;
  1902. n = parent == -1 ? UI_HLFG : UI_FG;
  1903. if(form->flags & UI_DISABLED) {
  1904. n = UI_DFG;
  1905. } else {
  1906. if(parent == UI_MENU) {
  1907. if(ctx->hover == form) {
  1908. if(ctx->skin[UI_HL].buf)
  1909. _ui_blit(ctx, x, ty, w, form->eh, &ctx->skin[UI_HL], 0, 0, 0);
  1910. else
  1911. _ui_frect(ctx, x, ty, w, form->eh, ctx->theme[UI_HLBG]);
  1912. n = UI_HLFG;
  1913. }
  1914. } else {
  1915. if(form->flags & UI_NOBULLET) {
  1916. if(s) n = UI_TFG;
  1917. if((form->flags & UI_ALTSKIN) && ctx->skin[UI_A_BG].buf) {
  1918. _ui_blit(ctx, tx, ty, ctx->skin[UI_M_L].w, form->eh, &ctx->skin[UI_M_L], 0, 0, !s);
  1919. _ui_blit(ctx, tx + ctx->skin[UI_M_L].w, ty, form->ew - ctx->skin[UI_M_L].w - ctx->skin[UI_M_R].w,
  1920. form->eh, &ctx->skin[UI_M_BG], 0, 0, !s);
  1921. _ui_blit(ctx, tx + form->ew - ctx->skin[UI_M_R].w, ty, ctx->skin[UI_M_R].w, form->eh,
  1922. &ctx->skin[UI_M_R], 0, 0, !s);
  1923. } else
  1924. if(ctx->skin[UI_P_BG].buf && s)
  1925. _ui_blit(ctx, tx, ty, form->ew, form->eh, &ctx->skin[UI_P_BG], 0, 0, 0);
  1926. else
  1927. if(s)
  1928. _ui_frect(ctx, tx, ty, form->ew, form->eh, ctx->theme[UI_TBG]);
  1929. tx += form->m;
  1930. } else {
  1931. m = ty + (form->eh - 5) / 2;
  1932. if(s) _ui_tri(ctx, tx + 1, m, 0, ctx->theme[UI_FG], ctx->theme[UI_FG], ctx->theme[UI_FG]);
  1933. else _ui_tri(ctx, tx + 2, m - 2, 3, ctx->theme[UI_FG], ctx->theme[UI_FG], ctx->theme[UI_FG]);
  1934. tx += 9;
  1935. }
  1936. }
  1937. if(form->icon && form->icon->buf) {
  1938. ctx->cx0 = tx;
  1939. if(ctx->cx1 > tx + form->eh) ctx->cx1 = tx + form->eh;
  1940. _ui_blit(ctx, tx + (form->ey - form->icon->w) / 2, ty + (form->ey - form->icon->h) / 2,
  1941. form->icon->w, form->icon->h, form->icon, 0, 0, form->flags & UI_DISABLED);
  1942. ctx->cx1 = x1;
  1943. tx += form->eh;
  1944. }
  1945. }
  1946. if(ctx->fnt && ctx->draw) {
  1947. if(ctx->txtv && form->label > 0 && form->label < ctx->txtc)
  1948. (*ctx->draw)(ctx->fnt, ctx->txtv[form->label], NULL, ctx->screen.buf, ctx->theme[n],
  1949. tx - form->l, ty + 2, form->l, form->t, ctx->screen.p, ctx->cx0, ctx->cy0, ctx->cx1, ctx->cy1);
  1950. else if(form->ptr)
  1951. (*ctx->draw)(ctx->fnt, (char*)form->ptr, NULL, ctx->screen.buf, ctx->theme[n],
  1952. tx - form->l, ty + 2, form->l, form->t, ctx->screen.p, ctx->cx0, ctx->cy0, ctx->cx1, ctx->cy1);
  1953. }
  1954. break;
  1955. case UI_STATUS:
  1956. case UI_LABEL:
  1957. n = form->flags & UI_DISABLED ? UI_DFG : (parent == -1 ? UI_HLFG : UI_FG);
  1958. if(form->type == UI_LABEL && !(form->flags & UI_DISABLED) && parent == UI_MENU && ctx->hover == form) {
  1959. if(ctx->skin[UI_HL].buf)
  1960. _ui_blit(ctx, x, ty, w, form->eh, &ctx->skin[UI_HL], 0, 0, 0);
  1961. else
  1962. _ui_frect(ctx, x, ty, w, form->eh, ctx->theme[UI_HLBG]);
  1963. n = UI_HLFG;
  1964. }
  1965. if(form->type == UI_STATUS) {
  1966. if(ctx->skin[UI_P_BG].buf)
  1967. _ui_blit(ctx, tx, ty, form->ew, form->eh, &ctx->skin[UI_INP], 0, 0, 0);
  1968. else
  1969. _ui_frect(ctx, tx, ty, form->ew, form->eh, ctx->theme[UI_IBG]);
  1970. s = ctx->hover ? ctx->hover->desc : 0;
  1971. } else
  1972. s = form->label;
  1973. if(form->icon && form->icon->buf) {
  1974. ctx->cx0 = tx;
  1975. if(ctx->cx1 > tx + form->eh) ctx->cx1 = tx + form->eh;
  1976. _ui_blit(ctx, tx + (form->ey - form->icon->w) / 2, ty + (form->ey - form->icon->h) / 2,
  1977. form->icon->w, form->icon->h, form->icon, 0, 0, form->flags & UI_DISABLED);
  1978. ctx->cx1 = x1;
  1979. tx += form->eh;
  1980. }
  1981. if(ctx->fnt && ctx->draw) {
  1982. if(ctx->txtv && s > 0 && s < ctx->txtc)
  1983. (*ctx->draw)(ctx->fnt, ctx->txtv[s], NULL, ctx->screen.buf, ctx->theme[n],
  1984. tx - form->l, ty + 2, form->l, form->t, ctx->screen.p, ctx->cx0, ctx->cy0, ctx->cx1, ctx->cy1);
  1985. else if(form->ptr)
  1986. (*ctx->draw)(ctx->fnt, (char*)form->ptr, NULL, ctx->screen.buf, ctx->theme[n],
  1987. tx - form->l, ty + 2, form->l, form->t, ctx->screen.p, ctx->cx0, ctx->cy0, ctx->cx1, ctx->cy1);
  1988. }
  1989. break;
  1990. case UI_CHECK:
  1991. case UI_RADIO:
  1992. n = form->flags & UI_DISABLED ? UI_DFG : (parent == -1 ? UI_HLFG : UI_FG);
  1993. if(!(form->flags & UI_DISABLED) && parent == UI_MENU && ctx->hover == form) {
  1994. if(ctx->skin[UI_HL].buf)
  1995. _ui_blit(ctx, x, ty, w, form->eh, &ctx->skin[UI_HL], 0, 0, 0);
  1996. else
  1997. _ui_frect(ctx, x, ty, w, form->eh, ctx->theme[UI_HLBG]);
  1998. n = UI_HLFG;
  1999. }
  2000. if(!(form->flags & UI_NOBULLET)) {
  2001. s = form->eh / 2;
  2002. ctx->cx0 = tx;
  2003. if(ctx->cx1 > tx + form->eh) ctx->cx1 = tx + form->eh;
  2004. if(form->type == UI_CHECK)
  2005. _ui_checkbox(ctx, tx + s, ty + s,
  2006. form->flags & UI_DISABLED ? -1 : (form->ptr ? *((int*)form->ptr) & (form->value ? form->value : 1) : 0),
  2007. form->flags);
  2008. else
  2009. _ui_radiobtn(ctx, tx + s, ty + s,
  2010. form->flags & UI_DISABLED ? -1 : (form->ptr ? *((int*)form->ptr) == form->value : 0),
  2011. form->flags);
  2012. ctx->cx1 = x1;
  2013. tx += form->eh;
  2014. }
  2015. if(form->icon && form->icon->buf) {
  2016. ctx->cx0 = tx;
  2017. if(ctx->cx1 > tx + form->eh) ctx->cx1 = tx + form->eh;
  2018. _ui_blit(ctx, tx + (form->eh - form->icon->w) / 2, ty + (form->eh - form->icon->h) / 2,
  2019. form->icon->w, form->icon->h, form->icon, 0, 0, form->flags & UI_DISABLED);
  2020. ctx->cx1 = x1;
  2021. tx += form->eh;
  2022. }
  2023. if(ctx->fnt && ctx->draw && ctx->txtv && form->label > 0 && form->label < ctx->txtc)
  2024. (*ctx->draw)(ctx->fnt, ctx->txtv[form->label], NULL, ctx->screen.buf, ctx->theme[n],
  2025. tx - form->l, ty + 2, form->l, form->t, ctx->screen.p, ctx->cx0, ctx->cy0, ctx->cx1, ctx->cy1);
  2026. break;
  2027. case UI_BUTTON:
  2028. _ui_button(ctx, tx, ty, form->ew, form->eh, form->flags & UI_DISABLED ? -1 :
  2029. (ctx->pressed == form || (form->ptr && *((int*)form->ptr) == form->value) ? 1 :
  2030. (ctx->hover == form ? -2 : 0)), form->flags, form->icon, form->label, ctx->skin[UI_BNM].buf ? form->m : 0);
  2031. break;
  2032. case UI_BTNTGL:
  2033. popup = form->ptr && UI_CONTAINER(((ui_form_t*)form->ptr)->type) ? (ui_form_t*)form->ptr : &ctx->form[form->value];
  2034. _ui_button(ctx, tx, ty, form->ew, form->eh, form->flags & UI_DISABLED ? -1 :
  2035. (ctx->pressed == form ? 1 : (ctx->hover == form ? -2 : 0)), form->flags, form->icon, form->label,
  2036. ctx->skin[UI_BNM].buf ? form->m : 0);
  2037. break;
  2038. case UI_BTNICN:
  2039. if(form->icon && form->icon->buf && form->icon->w > 0 && form->icon->h > 0 && form->ptr &&
  2040. *((int*)form->ptr) & (form->value ? form->value : 1))
  2041. _ui_blit(ctx, tx + (form->ew - form->icon->w) / 2, ty + (form->eh - form->icon->h) / 2,
  2042. form->icon->w, form->icon->h, form->icon, 0, 0, form->flags & UI_DISABLED);
  2043. break;
  2044. case UI_SLIDER:
  2045. _ui_slider(ctx, tx, ty, form->ew, form->eh, form->ptr ? *((int*)form->ptr) : 0, form->max - form->min, form->flags);
  2046. break;
  2047. case UI_VSCRBAR:
  2048. _ui_vscrbar(ctx, tx, ty, form->eh, form->ptr ? *((int*)form->ptr) : 0, form->max,
  2049. form->flags & UI_DISABLED ? -1 : ctx->vscr == form);
  2050. break;
  2051. case UI_HSCRBAR:
  2052. _ui_hscrbar(ctx, tx, ty, form->ew, form->ptr ? *((int*)form->ptr) : 0, form->max,
  2053. form->flags & UI_DISABLED ? -1 : ctx->hscr == form);
  2054. break;
  2055. case UI_PBAR: _ui_pbar(ctx, tx, ty, form->ew, form->eh, form->ptr ? *((int64_t*)form->ptr) : 0, form->max, form->flags); break;
  2056. case UI_DEC8: case UI_DEC16: case UI_DEC32: case UI_DEC64:
  2057. case UI_HEX8: case UI_HEX16: case UI_HEX32: case UI_HEX64:
  2058. case UI_DEC_FLOAT:
  2059. if(form->ptr) {
  2060. n = form->flags & UI_DISABLED ? UI_DFG : (parent == -1 ? UI_HLFG : UI_FG);
  2061. switch(form->type) {
  2062. case UI_DEC8: sprintf(tmp, "%d", *((int8_t*)form->ptr)); break;
  2063. case UI_DEC16: sprintf(tmp, "%d", *((int16_t*)form->ptr)); break;
  2064. case UI_DEC32: sprintf(tmp, "%d", *((int32_t*)form->ptr)); break;
  2065. case UI_DEC64: sprintf(tmp, "%" L "d", *((int64_t*)form->ptr)); break;
  2066. case UI_HEX8: sprintf(tmp, "%x", *((uint8_t*)form->ptr)); break;
  2067. case UI_HEX16: sprintf(tmp, "%x", *((uint16_t*)form->ptr)); break;
  2068. case UI_HEX32: sprintf(tmp, "%x", *((uint32_t*)form->ptr)); break;
  2069. case UI_HEX64: sprintf(tmp, "%" L "x", *((uint64_t*)form->ptr)); break;
  2070. case UI_DEC_FLOAT: sprintf(tmp, "%.4g", *((float*)form->ptr)); break;
  2071. }
  2072. if(ctx->fnt && ctx->bbox && ctx->draw) {
  2073. (*ctx->bbox)(ctx->fnt, tmp, NULL, &dw, &dh, NULL, NULL);
  2074. (*ctx->draw)(ctx->fnt, tmp, NULL, ctx->screen.buf, ctx->theme[n],
  2075. tx + form->ew - dw - form->l, ty + 2, form->l, form->t, ctx->screen.p,
  2076. ctx->cx0, ctx->cy0, ctx->cx1, ctx->cy1);
  2077. }
  2078. }
  2079. break;
  2080. case UI_IMAGE:
  2081. if(form->icon && form->icon->buf && form->icon->w > 0 && form->icon->h > 0)
  2082. _ui_blit(ctx, tx, ty, form->ew, form->eh, form->icon, 0, 0, form->flags & UI_DISABLED);
  2083. break;
  2084. case UI_LINES:
  2085. if((ints = (int16_t*)form->ptr)) {
  2086. for(s = 0; ints[s + 2] || ints[s + 3]; s += 2)
  2087. _ui_line(ctx, ints[s], ints[s + 1], ints[s + 2], ints[s + 3], form->value);
  2088. }
  2089. break;
  2090. case UI_VCONNECT:
  2091. case UI_HCONNECT:
  2092. if(form->ptr) {
  2093. bx0 = ((int16_t*)form->ptr)[0]; by0 = ((int16_t*)form->ptr)[1];
  2094. bx1 = ((int16_t*)form->ptr)[2]; by1 = ((int16_t*)form->ptr)[3];
  2095. if(bx0 > bx1) { i = bx0; bx0 = bx1; bx1 = i; }
  2096. if(by0 > by1) { i = by0; by0 = by1; by1 = i; }
  2097. if(form->type == UI_HCONNECT)
  2098. _ui_cbez(ctx, bx0,by0, bx1,by1, bx1,by0, bx0,by1, form->value);
  2099. else
  2100. _ui_cbez(ctx, bx0,by0, bx1,by1, bx0,by1, bx1,by0, form->value);
  2101. }
  2102. break;
  2103. case UI_CURVE:
  2104. if((ints = (int16_t*)form->ptr))
  2105. _ui_cbez(ctx, ints[0], ints[1], ints[2], ints[3], ints[4], ints[5], ints[6], ints[7], form->value);
  2106. break;
  2107. case UI_TXTINP:
  2108. _ui_text(ctx, tx, ty, form->ew, form->eh, form->l, form->t, form->flags,
  2109. (ctx->text == form) | (form->inc == UI_FILTER_PASS ? 2 : 0), form->ptr, NULL);
  2110. break;
  2111. case UI_SELECT:
  2112. _ui_select(ctx, tx, ty, form->ew, form->eh, form->l, form->t, form->flags, ctx->pressed == form,
  2113. ctx->skin[UI_INP].buf ? form->m : 0,
  2114. form->optv && form->ptr && *((int*)form->ptr) >= 0 && *((int*)form->ptr) < form->optc ? form->optv[*((int*)form->ptr)] : "");
  2115. break;
  2116. case UI_OPTION:
  2117. _ui_option(ctx, tx, ty, form->ew, form->eh, form->l, form->t, form->flags, ctx->pressed == form ? ctx->pw : 0, UI_LEFT,
  2118. ctx->skin[UI_INP].buf ? form->m : 0,
  2119. form->optv && form->ptr && *((int*)form->ptr) >= 0 && *((int*)form->ptr) < form->optc ? form->optv[*((int*)form->ptr)] : "");
  2120. break;
  2121. case UI_INT8: case UI_INT16: case UI_INT32: case UI_INT64:
  2122. case UI_FLOAT:
  2123. if(form->ptr) {
  2124. switch(form->type) {
  2125. case UI_INT8: sprintf(tmp, "%d", *((int8_t*)form->ptr)); break;
  2126. case UI_INT16: sprintf(tmp, "%d", *((int16_t*)form->ptr)); break;
  2127. case UI_INT32: sprintf(tmp, "%d", *((int32_t*)form->ptr)); break;
  2128. case UI_INT64: sprintf(tmp, "%" L "d", *((int64_t*)form->ptr)); break;
  2129. case UI_FLOAT: sprintf(tmp, "%.4g", *((float*)form->ptr)); break;
  2130. }
  2131. _ui_option(ctx, tx, ty, form->ew, form->eh, form->l, form->t, form->flags, ctx->pressed == form ? ctx->pw : 0,
  2132. UI_RIGHT, ctx->skin[UI_INP].buf ? form->m : 0, tmp);
  2133. }
  2134. break;
  2135. case UI_COLOR:
  2136. _ui_color(ctx, tx, ty, form->ew, form->eh, form->l, form->t, form->flags, form->ptr ? *((uint32_t*)form->ptr) : 0xff000000);
  2137. break;
  2138. case UI_CUSTOM:
  2139. if(form->view) {
  2140. s = ctx->cx0; e = ctx->cx1; n = ctx->cy0; m = ctx->cy1;
  2141. if(ctx->cx0 < tx) ctx->cx0 = tx;
  2142. if(ctx->cy0 < ty) ctx->cy0 = ty;
  2143. if(ctx->cx1 > tx + form->ew) ctx->cx1 = tx + form->ew;
  2144. if(ctx->cy1 > ty + form->eh) ctx->cy1 = ty + form->eh;
  2145. (*form->view)(ctx, tx, ty, form->ew, form->eh, form);
  2146. ctx->cx0 = s; ctx->cx1 = e; ctx->cy0 = n; ctx->cy1 = m;
  2147. }
  2148. break;
  2149. }
  2150. if(parent < 0) break;
  2151. }
  2152. ctx->cx0 = cx0; ctx->cx1 = cx1; ctx->cy0 = cy0; ctx->cy1 = cy1;
  2153. return UI_OK;
  2154. }
  2155. /**
  2156. * Redraw form elements
  2157. */
  2158. int _ui_redraw(ui_t *ctx, ui_form_t *form)
  2159. {
  2160. int64_t mz = 0;
  2161. int ret, i;
  2162. if(!ctx || !ctx->screen.buf) return UI_ERR_BADINP;
  2163. memset(ctx->screen.buf, 0, ctx->screen.p * ctx->screen.h);
  2164. ctx->menu = NULL;
  2165. ctx->numpopups = 0;
  2166. ctx->cx0 = ctx->cy0 = 0; ctx->cx1 = ctx->screen.w; ctx->cy1 = ctx->screen.h;
  2167. ret = _ui_draw(ctx, 0, 0, ctx->screen.w, ctx->screen.h, 0, 0, form, UI_END);
  2168. for(i = ctx->numpopups - 1; i >= 0; i--) {
  2169. if(mz < ctx->popups[i]->min) mz = ctx->popups[i]->min;
  2170. if(ctx->popups[i]->type == UI_MENU && !(ctx->popups[i]->flags & UI_HIDDEN)) ctx->menu = ctx->popups[i];
  2171. _ui_popup(ctx, ctx->popups[i]->ex, ctx->popups[i]->ey, ctx->popups[i]);
  2172. }
  2173. if(mz >= 0x7fffffffffff8000L)
  2174. for(i = 0; i < ctx->numpopups; i++)
  2175. ctx->popups[i]->min >>= 16;
  2176. if(ctx->popup && ctx->dr) {
  2177. ctx->cx0 = ctx->cy0 = 0; ctx->cx1 = ctx->screen.w; ctx->cy1 = ctx->screen.h;
  2178. if(ctx->px + ctx->pw + 2 >= ctx->screen.w) ctx->px = ctx->screen.w - ctx->pw - 2;
  2179. if(ctx->py + ctx->ph + 2 >= ctx->screen.h) ctx->py = ctx->screen.h - ctx->ph - 2;
  2180. if(ctx->px < 0) { ctx->pw += ctx->px; ctx->px = 0; }
  2181. if(ctx->py < 0) { ctx->ph += ctx->py; ctx->py = 0; }
  2182. (*ctx->dr)(ctx, ctx->px, ctx->py, ctx->pw, ctx->ph, ctx->popup);
  2183. }
  2184. return ret;
  2185. }
  2186. /**
  2187. * Force refresh
  2188. */
  2189. int ui_refresh(ui_t *ctx)
  2190. {
  2191. if(!ctx) return UI_ERR_BADINP;
  2192. ctx->flags |= UI_REFRESH | UI_RECALC;
  2193. return UI_OK;
  2194. }
  2195. /**
  2196. * Set the localized string array
  2197. */
  2198. int ui_settxt(ui_t *ctx, char **txtv)
  2199. {
  2200. if(!ctx || !ctx->txtc || !txtv || !*txtv) return UI_ERR_BADINP;
  2201. ctx->txtv = txtv;
  2202. ui_backend_settitle(ctx->bck, *txtv);
  2203. ctx->ds = ctx->dt = 0;
  2204. _ui_recalc(ctx, 0, 0, ctx->screen.w, ctx->screen.h, 0, ctx->form, NULL, NULL);
  2205. ctx->flags |= UI_REFRESH;
  2206. return UI_OK;
  2207. }
  2208. /**
  2209. * Get the clipboard's text
  2210. */
  2211. char *ui_getclipboard(ui_t *ctx)
  2212. {
  2213. return ctx ? ui_backend_getclipboard(ctx->bck) : NULL;
  2214. }
  2215. /**
  2216. * Set the clipboard's text
  2217. */
  2218. int ui_setclipboard(ui_t *ctx, char *str)
  2219. {
  2220. return ctx && str && *str ? ui_backend_setclipboard(ctx->bck, str) : UI_ERR_BADINP;
  2221. }
  2222. /**
  2223. * Set mouse position
  2224. */
  2225. int _ui_setmouse(ui_t *ctx, int x, int y)
  2226. {
  2227. if(!ctx) return UI_ERR_BADINP;
  2228. ctx->mousex = x; ctx->mousey = y;
  2229. return UI_OK;
  2230. }
  2231. /**
  2232. * Get mouse position
  2233. */
  2234. int ui_getmouse(ui_t *ctx, int *x, int *y)
  2235. {
  2236. if(!ctx || !x || !y) return UI_ERR_BADINP;
  2237. *x = ctx->mousex; *y = ctx->mousey;
  2238. return UI_OK;
  2239. }
  2240. /**
  2241. * Get the next free event slot
  2242. */
  2243. ui_event_t *_ui_evtslot(ui_t *ctx)
  2244. {
  2245. ui_event_t *evt = NULL;
  2246. if(ctx && (ctx->head + 1) % UI_MAXEVENTS != ctx->tail) {
  2247. evt = &ctx->events[ctx->head++];
  2248. ctx->head %= UI_MAXEVENTS;
  2249. memset(evt, 0, sizeof(ui_event_t));
  2250. }
  2251. return evt;
  2252. }
  2253. /**
  2254. * Resize the window backbuffer
  2255. */
  2256. int _ui_resize(ui_t *ctx, int w, int h)
  2257. {
  2258. ui_event_t *evt;
  2259. if(!ctx || w < 1 || h < 1) return UI_ERR_BADINP;
  2260. ctx->screen.w = w; ctx->screen.h = h; ctx->screen.p = w * 4;
  2261. if((ctx->screen.buf = (uint8_t*)realloc(ctx->screen.buf, w * h * 4))) {
  2262. memset(ctx->screen.buf, 0, w * h * 4);
  2263. if(ctx->menu) ctx->menu->flags |= UI_HIDDEN;
  2264. ctx->form = ctx->menu = ctx->text = NULL;
  2265. ctx->flags |= UI_CLOSE;
  2266. if((evt = _ui_evtslot(ctx))) { evt->x = w; evt->y = h; evt->type = UI_EVT_RESIZE; }
  2267. return UI_OK;
  2268. }
  2269. return UI_ERR_NOMEM;
  2270. }
  2271. /**
  2272. * Increment an option list or number field
  2273. */
  2274. void _ui_inc(ui_form_t *form)
  2275. {
  2276. if(form && form->ptr)
  2277. switch(form->type) {
  2278. case UI_SELECT:
  2279. case UI_OPTION:
  2280. *((int*)form->ptr) += 1;
  2281. if(*((int*)form->ptr) >= form->optc) *((int*)form->ptr) = 0;
  2282. break;
  2283. case UI_FLOAT:
  2284. *((float*)form->ptr) += form->finc != 0.0 ? form->finc : 1.0;
  2285. if(*((float*)form->ptr) > form->fmax) *((float*)form->ptr) = form->fmax;
  2286. break;
  2287. case UI_INT8:
  2288. *((int8_t*)form->ptr) += form->inc ? form->inc : 1;
  2289. if(*((int8_t*)form->ptr) > form->max) *((int8_t*)form->ptr) = form->max;
  2290. break;
  2291. case UI_INT16:
  2292. *((int16_t*)form->ptr) += form->inc ? form->inc : 1;
  2293. if(*((int16_t*)form->ptr) > form->max) *((int16_t*)form->ptr) = form->max;
  2294. break;
  2295. case UI_INT32:
  2296. *((int32_t*)form->ptr) += form->inc ? form->inc : 1;
  2297. if(*((int32_t*)form->ptr) > form->max) *((int32_t*)form->ptr) = form->max;
  2298. break;
  2299. case UI_INT64:
  2300. *((int64_t*)form->ptr) += form->inc ? form->inc : 1;
  2301. if(*((int64_t*)form->ptr) > form->max) *((int64_t*)form->ptr) = form->max;
  2302. break;
  2303. }
  2304. }
  2305. /**
  2306. * Decrement an option list or number field
  2307. */
  2308. void _ui_dec(ui_form_t *form)
  2309. {
  2310. if(form && form->ptr)
  2311. switch(form->type) {
  2312. case UI_SELECT:
  2313. case UI_OPTION:
  2314. *((int*)form->ptr) -= 1;
  2315. if(*((int*)form->ptr) < 0) *((int*)form->ptr) = form->optc - 1;
  2316. break;
  2317. case UI_FLOAT:
  2318. *((float*)form->ptr) -= form->finc != 0.0 ? form->finc : 1.0;
  2319. if(*((float*)form->ptr) < form->fmin) *((float*)form->ptr) = form->fmin;
  2320. break;
  2321. case UI_INT8:
  2322. *((int8_t*)form->ptr) -= form->inc ? form->inc : 1;
  2323. if(*((int8_t*)form->ptr) < form->min) *((int8_t*)form->ptr) = form->min;
  2324. break;
  2325. case UI_INT16:
  2326. *((int16_t*)form->ptr) -= form->inc ? form->inc : 1;
  2327. if(*((int16_t*)form->ptr) < form->min) *((int16_t*)form->ptr) = form->min;
  2328. break;
  2329. case UI_INT32:
  2330. *((int32_t*)form->ptr) -= form->inc ? form->inc : 1;
  2331. if(*((int32_t*)form->ptr) < form->min) *((int32_t*)form->ptr) = form->min;
  2332. break;
  2333. case UI_INT64:
  2334. *((int64_t*)form->ptr) -= form->inc ? form->inc : 1;
  2335. if(*((int64_t*)form->ptr) < form->min) *((int64_t*)form->ptr) = form->min;
  2336. break;
  2337. }
  2338. }
  2339. /**
  2340. * Selectbox controller
  2341. */
  2342. int _ui_select_ctrl(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt)
  2343. {
  2344. if(!ctx || !ctx->screen.buf || !form || !form->ptr || form->eh <= 4 || x >= ctx->screen.w || y >= ctx->screen.h ||
  2345. w < 1 || h < 1 || !evt || evt->type == UI_EVT_NONE)
  2346. return UI_ERR_BADINP;
  2347. if((evt->type == UI_EVT_MOUSE && (evt->btn & UI_BTN_L) && (ctx->mousey - y - 2) / (form->eh - 4) == form->sel) ||
  2348. (evt->type == UI_EVT_KEY && evt->key[0] == '\n')) {
  2349. *((int*)form->ptr) = form->sel;
  2350. ctx->flags |= UI_CLOSE;
  2351. }
  2352. return UI_OK;
  2353. }
  2354. /**
  2355. * Start select input
  2356. */
  2357. void _ui_select_start(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form)
  2358. {
  2359. int i;
  2360. if(!ctx || !form || form->type != UI_SELECT || !form->ptr || !form->optv || form->optc < 1) return;
  2361. form->sel = *((int*)form->ptr);
  2362. if(form->sel < 0) form->sel = 0;
  2363. if(form->sel >= form->optc) form->sel = form->optc - 1;
  2364. ctx->px = x; ctx->py = y - (h - 4) * form->sel; ctx->pw = w; ctx->ph = 4 + (h - 4) * form->optc;
  2365. ctx->popup = form;
  2366. ctx->pe = &_ui_select_ctrl;
  2367. ctx->dr = &_ui_select_popup;
  2368. if(ctx->fnt && ctx->bbox)
  2369. for(i = 0; i < form->optc; i++) {
  2370. (*ctx->bbox)(ctx->fnt, form->optv[i], NULL, &w, &h, NULL, NULL);
  2371. if(ctx->pw < w) ctx->pw = w;
  2372. }
  2373. }
  2374. /**
  2375. * Start text input
  2376. */
  2377. void _ui_text_start(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, char *str, int maxlen)
  2378. {
  2379. int l;
  2380. if(!ctx || !form || !str || maxlen < 1) return;
  2381. ctx->px = x; ctx->py = y; ctx->pw = w; ctx->ph = h;
  2382. l = strlen(str); if(l >= maxlen) l = maxlen - 1;
  2383. if((ctx->buf = (char*)realloc(ctx->buf, maxlen))) {
  2384. memcpy(ctx->buf, str, l + 1);
  2385. ctx->maxlen = maxlen;
  2386. ctx->scr = ctx->buf;
  2387. ctx->cur = ctx->end = ctx->buf + l;
  2388. ctx->text = form;
  2389. ctx->str = str;
  2390. ui_backend_showosk(ctx->bck);
  2391. }
  2392. }
  2393. /**
  2394. * Add key to input string
  2395. */
  2396. void _ui_text_add(ui_t *ctx, char *k, int i)
  2397. {
  2398. char *s;
  2399. int ok;
  2400. if(!ctx || !ctx->text || i < 1 || i > 4 || !k || !k[0] || k[0] == '\\' || (uint8_t)k[0] < ' ') return;
  2401. switch(ctx->text->type == UI_COLOR ? UI_FILTER_HEX : ctx->text->inc) {
  2402. /* 1, a unix id, only 0-9a-zA-Z allowed, and _ if not first */
  2403. case UI_FILTER_ID: ok = (k[0] >= '0' && k[0] <= '9') || (k[0] >= 'a' && k[0] <= 'z') || (k[0] >= 'A' && k[0] <= 'Z') ||
  2404. (ctx->cur > ctx->buf && k[0] == '_'); break;
  2405. /* 2, a variable id, same as 1 except 0-9 not allowed as first */
  2406. case UI_FILTER_VAR: ok = (k[0] >= 'a' && k[0] <= 'z') || (k[0] >= 'A' && k[0] <= 'Z') ||
  2407. (ctx->cur > ctx->buf && (k[0] == '_' || (k[0] >= '0' && k[0] <= '9'))); break;
  2408. /* 3, an expression */
  2409. case UI_FILTER_EXPR: ok = (k[0] >= 'a' && k[0] <= 'z') || (k[0] >= 'A' && k[0] <= 'Z') || (k[0] == '.' || k[0] == '_') ||
  2410. (k[0] >= '0' && k[0] <= '9') || k[0] == '(' || k[0] == '-' || k[0] == '@' || (ctx->cur > ctx->buf &&
  2411. ((k[0] == ')' || k[0] == '+' || k[0] == '*' || k[0] == '/' || k[0] == '%' || k[0] == '>' || k[0] == '<' ||
  2412. k[0] == '=' || k[0] == '!' || k[0] == '&' || k[0] == '|' || k[0] == ','))); break;
  2413. /* 4, a hex value */
  2414. case UI_FILTER_HEX:
  2415. if(k[0] >= 'A' && k[0] <= 'F') k[0] += 'a' - 'A';
  2416. ok = (k[0] >= '0' && k[0] <= '9') || (k[0] >= 'a' && k[0] <= 'f');
  2417. break;
  2418. /* 0, not an id, everything allowed except space as first */
  2419. default: ok = (ctx->cur > ctx->buf || k[0] != ' '); break;
  2420. }
  2421. if(ok && ctx->end - ctx->buf + i < ctx->maxlen) {
  2422. for(s = ctx->end; s >= ctx->cur; s--) s[i] = s[0];
  2423. memcpy(ctx->cur, k, i);
  2424. ctx->cur += i;
  2425. ctx->end += i;
  2426. *ctx->end = 0;
  2427. }
  2428. }
  2429. /**
  2430. * Text input box controller
  2431. */
  2432. void _ui_text_ctrl(ui_t *ctx, ui_event_t *evt)
  2433. {
  2434. int i, dw, dh;
  2435. char *s, *c, tmp[8];
  2436. if(!ctx || !ctx->text || !evt || evt->type == UI_EVT_NONE) return;
  2437. if((evt->type == UI_EVT_KEY && (evt->key[0] == '\x1b' || evt->key[0] == '\n')) ||
  2438. (evt->type == UI_EVT_MOUSE && !(evt->btn & UI_BTN_RELEASE) &&
  2439. (ctx->mousex < ctx->px || ctx->mousex >= ctx->px + ctx->pw ||
  2440. ctx->mousey < ctx->py || ctx->mousey >= ctx->py + ctx->ph)) || (ctx->flags & UI_CLOSE)) {
  2441. /* ESC or Enter pressed, or clicked outside: we must close */
  2442. if(ctx->buf) {
  2443. if(ctx->text->type == UI_COLOR && evt->type == UI_EVT_KEY && evt->key[0] == '\n') return;
  2444. if(ctx->text->type != UI_COLOR && ctx->str && (evt->type != UI_EVT_KEY || evt->key[0] != '\x1b'))
  2445. memcpy((char*)ctx->str, ctx->buf, (uintptr_t)ctx->end - (uintptr_t)ctx->buf + 1);
  2446. free(ctx->buf); ctx->buf = NULL;
  2447. }
  2448. ctx->buf = ctx->scr = ctx->cur = ctx->end = ctx->str = NULL;
  2449. ctx->text = NULL;
  2450. ui_backend_hideosk(ctx->bck);
  2451. ctx->flags |= UI_CLOSE;
  2452. } else
  2453. if(evt->type == UI_EVT_MOUSE && !(evt->btn & UI_BTN_RELEASE) && ctx->scr && ctx->fnt && ctx->bbox && ctx->px > 0 &&
  2454. ctx->mousex >= ctx->px && ctx->mousex < ctx->px + ctx->pw && ctx->mousey >= ctx->py && ctx->mousey < ctx->py + ctx->text->eh) {
  2455. /* the text input field clicked, move text cursor to mouse cursor */
  2456. i = ctx->mousex - ctx->px - (ctx->text->type == UI_COLOR ? ctx->text->eh : 2);
  2457. for(s = ctx->scr, dw = 0; s <= ctx->end;) {
  2458. (*ctx->bbox)(ctx->fnt, ctx->scr, s, &dw, &dh, NULL, NULL);
  2459. if(dw > i) break;
  2460. do { s++; } while(s < ctx->end && (*s & 0xC0) == 0x80);
  2461. }
  2462. ctx->cur = s > ctx->end ? ctx->end : s;
  2463. } else
  2464. if(evt->type == UI_EVT_KEY && !(evt->btn & (UI_BTN_ALT | UI_BTN_GUI))) {
  2465. /* key pressed */
  2466. if(!memcmp(evt->key, "Home", 5)) { ctx->cur = ctx->buf; } else
  2467. if(!memcmp(evt->key, "End", 4)) { ctx->cur = ctx->end; } else
  2468. if(!memcmp(evt->key, "Left", 5)) {
  2469. if(ctx->cur > ctx->buf)
  2470. do { ctx->cur--; } while(ctx->cur > ctx->buf && (*ctx->cur & 0xC0) == 0x80);
  2471. } else
  2472. if(!memcmp(evt->key, "Right", 6)) {
  2473. if(ctx->cur < ctx->end)
  2474. do { ctx->cur++; } while(ctx->cur < ctx->end && (*ctx->cur & 0xC0) == 0x80);
  2475. } else
  2476. if(!memcmp(evt->key, "Paste", 6)) {
  2477. if((s = c = ui_backend_getclipboard(ctx->bck))) {
  2478. while(*s) {
  2479. memset(&tmp, 0, sizeof(tmp)); i = 0;
  2480. if(!(*s & 128)) { i = 1; tmp[0] = *s++; } else
  2481. if(!(*s & 32)) { i = 2; memcpy(&tmp, s, 2); s += 2; } else
  2482. if(!(*s & 16)) { i = 3; memcpy(&tmp, s, 3); s += 3; } else
  2483. if(!(*s & 8)) { i = 4; memcpy(&tmp, s, 4); s += 4; }
  2484. if(tmp[0] < ' ') break;
  2485. _ui_text_add(ctx, (char*)&tmp, i);
  2486. }
  2487. free(c);
  2488. }
  2489. } else
  2490. if(!memcmp(evt->key, "Del", 4)) {
  2491. if(ctx->cur < ctx->end) {
  2492. s = ctx->cur;
  2493. do { s++; } while(s < ctx->end && (*s & 0xC0) == 0x80);
  2494. for(i = 0; s + i <= ctx->end; i++) ctx->cur[i] = s[i];
  2495. ctx->end -= (uintptr_t)s - (uintptr_t)ctx->cur;
  2496. }
  2497. } else
  2498. if(evt->key[0] == '\b') {
  2499. if(ctx->cur > ctx->buf) {
  2500. s = ctx->cur;
  2501. do { ctx->cur--; } while(ctx->cur > ctx->buf && (*ctx->cur & 0xC0) == 0x80);
  2502. for(i = 0; s + i <= ctx->end; i++) ctx->cur[i] = s[i];
  2503. ctx->end -= (uintptr_t)s - (uintptr_t)ctx->cur;
  2504. }
  2505. } else
  2506. if((uint8_t)evt->key[0] >= ' ' && (!evt->key[1] || (evt->key[0] & 0x80)))
  2507. _ui_text_add(ctx, evt->key, strlen(evt->key));
  2508. if(ctx->text->type == UI_CUSTOM && ctx->text->ctrl)
  2509. (*ctx->text->ctrl)(ctx, ctx->text->ex, ctx->text->ey, ctx->text->ew, ctx->text->eh, ctx->text, evt);
  2510. }
  2511. }
  2512. /**
  2513. * Color picker controller
  2514. */
  2515. int _ui_color_ctrl(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt)
  2516. {
  2517. int i, X, Y;
  2518. uint8_t *C;
  2519. if(!ctx || !ctx->screen.buf || !form || !form->ptr || x >= ctx->screen.w || y >= ctx->screen.h || w < 56+256 || h < 16+256 || !evt)
  2520. return UI_ERR_BADINP;
  2521. if(!ctx->buf) {
  2522. if(evt->type != UI_EVT_KEY || evt->key[0] == '\n') {
  2523. setcolor: for(i = 0; i < 16 && ctx->hist[i] != ctx->color; i++) {}
  2524. if(i) memmove(&ctx->hist[1], &ctx->hist[0], (i > 15 ? 15 : i) * sizeof(uint32_t));
  2525. ctx->hist[0] = ctx->color;
  2526. *((uint32_t*)form->ptr) = ctx->color;
  2527. }
  2528. if(ctx->text) ui_backend_hideosk(ctx->bck);
  2529. if(ctx->buf) free(ctx->buf);
  2530. ctx->buf = ctx->scr = ctx->cur = ctx->end = NULL;
  2531. ctx->text = NULL;
  2532. ctx->flags |= UI_CLOSE;
  2533. return UI_OK;
  2534. }
  2535. if(evt->type == UI_EVT_KEY) {
  2536. if(evt->key[0] == '\n') goto setcolor;
  2537. if(ctx->buf && (uintptr_t)ctx->end - (uintptr_t)ctx->buf == 8) {
  2538. for(ctx->color = i = 0; i < 8; i++) {
  2539. ctx->color <<= 4;
  2540. ctx->color |= ctx->buf[i] >= '0' && ctx->buf[i] <= '9' ? ctx->buf[i] - '0' :
  2541. (ctx->buf[i] >= 'a' && ctx->buf[i] <= 'f' ? ctx->buf[i] - 'a' + 10 : 0);
  2542. }
  2543. _ui_rgb2hsv(ctx->color, &ctx->hue, &ctx->sat, &ctx->val);
  2544. }
  2545. return UI_OK;
  2546. }
  2547. C = (uint8_t*)&ctx->color;
  2548. Y = ctx->mousey - y - form->eh - 8;
  2549. if(evt->type == UI_EVT_MOUSE) {
  2550. if(evt->btn & UI_BTN_RELEASE) { ctx->s1 = ctx->mousex; ctx->s2 = ctx->mousey; ctx->sm = 0; } else
  2551. if(evt->btn & UI_BTN_L) {
  2552. if(ctx->mousex == ctx->s1 && ctx->mousey == ctx->s2) goto setcolor;
  2553. X = ctx->mousex - x - 2;
  2554. if(Y >= 0 && Y < 256) {
  2555. if(X < 16) ctx->sm = 1; else
  2556. if(X >= 20 && X < 36) ctx->sm = 2; else
  2557. if(X >= 38 && X < 38 + 256) ctx->sm = 3; else
  2558. if(X >= 296 && X < 296 + 16) ctx->sm = 4;
  2559. }
  2560. }
  2561. }
  2562. if(ctx->sm) {
  2563. X = ctx->mousex - x - 42; if(X < 0) { X = 0; } if(X > 255) X = 255;
  2564. if(Y < 0) { Y = 0; } if(Y > 255) Y = 255;
  2565. switch(ctx->sm) {
  2566. case 1: ctx->sm = 0; ctx->color = ctx->hist[Y >> 4]; _ui_rgb2hsv(ctx->color, &ctx->hue, &ctx->sat, &ctx->val); break;
  2567. case 2: C[3] = 255 - Y; break;
  2568. case 3: ctx->sat = X; ctx->val = 255 - Y; ctx->color = _ui_hsv2rgb(C[3], ctx->hue, ctx->sat, ctx->val); break;
  2569. case 4: ctx->hue = Y; ctx->color = _ui_hsv2rgb(C[3], ctx->hue, ctx->sat, ctx->val); break;
  2570. }
  2571. sprintf(ctx->buf, "%08x", ctx->color);
  2572. }
  2573. return UI_OK;
  2574. }
  2575. /**
  2576. * Start color picker
  2577. */
  2578. void _ui_color_start(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form)
  2579. {
  2580. if(!ctx || !form || form->type != UI_COLOR || !form->ptr) return;
  2581. (void)w;
  2582. ctx->px = x; ctx->py = y; ctx->pw = 42 + 256 + 26; ctx->ph = h + 256 + 16;
  2583. ctx->s1 = ctx->s2 = ctx->sm = 0;
  2584. if((ctx->buf = (char*)realloc(ctx->buf, 16))) {
  2585. ctx->color = *((uint32_t*)form->ptr);
  2586. sprintf(ctx->buf, "%08x", ctx->color);
  2587. _ui_rgb2hsv(ctx->color, &ctx->hue, &ctx->sat, &ctx->val);
  2588. ctx->maxlen = 9;
  2589. ctx->scr = ctx->buf;
  2590. ctx->cur = ctx->end = ctx->buf + 8;
  2591. ctx->popup = ctx->text = form;
  2592. ctx->pe = &_ui_color_ctrl;
  2593. ctx->dr = &_ui_color_popup;
  2594. }
  2595. }
  2596. /**
  2597. * Get container scrollbars
  2598. */
  2599. int _ui_evtscr(ui_t *ctx, int x, int y, ui_form_t *form, ui_event_t *evt)
  2600. {
  2601. int b, t;
  2602. if(ctx && form && evt) {
  2603. b = (UI_CONTAINER(form->type) && !(form->flags & UI_NOBORDER)) ? 2 : 0;
  2604. t = form->type == UI_POPUP ? form->value : 0;
  2605. if(form->sh && ctx->mousex < x + form->ew + b - form->m && ctx->mousex >= x + form->ew + b - form->m - ctx->sw &&
  2606. ctx->mousey >= y + form->m + t && ctx->mousey < y + form->m + t + form->sh) {
  2607. if(evt->type == UI_EVT_MOUSE && !(evt->btn & UI_BTN_RELEASE)) {
  2608. ctx->vscr = form;
  2609. ctx->s1 = y + form->m + t;
  2610. ctx->sm = form->mh - form->sh;
  2611. t = _ui_scr(form->sh, form->oy, form->mh, ctx->sw, &b);
  2612. ctx->sb = ctx->mousey >= ctx->s1 + t && ctx->mousey < ctx->s1 + t + b ? ctx->mousey - ctx->s1 - t : b / 2;
  2613. ctx->s2 = ctx->s1 + form->sh - b + ctx->sb;
  2614. ctx->flags |= UI_REFRESH;
  2615. }
  2616. return 1;
  2617. }
  2618. if(form->sw && ctx->mousex >= x + form->m && ctx->mousex < x + form->m + form->sw &&
  2619. ctx->mousey >= y + form->eh + b - form->m - ctx->sh && ctx->mousey < y + form->eh + b - form->m) {
  2620. if(evt->type == UI_EVT_MOUSE && !(evt->btn & UI_BTN_RELEASE)) {
  2621. ctx->hscr = form;
  2622. ctx->s1 = x + form->m;
  2623. ctx->sm = form->mw - form->sw;
  2624. t = _ui_scr(form->sw, form->ox, form->mw, ctx->sh, &b);
  2625. ctx->sb = ctx->mousex >= ctx->s1 + t && ctx->mousex < ctx->s1 + t + b ? ctx->mousex - ctx->s1 - t : b / 2;
  2626. ctx->s2 = ctx->s1 + form->sw - b + ctx->sb;
  2627. ctx->flags |= UI_REFRESH;
  2628. }
  2629. return 1;
  2630. }
  2631. }
  2632. return 0;
  2633. }
  2634. /**
  2635. * Process events
  2636. */
  2637. int _ui_evtproc(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, int parent, ui_event_t *evt)
  2638. {
  2639. ui_form_t *popup;
  2640. int tx, ty, x2, y2, t, b;
  2641. (void)h;
  2642. if(!ctx) return UI_ERR_BADINP;
  2643. if(!form || !evt) return 0;
  2644. for(; form->type != UI_END; form++) {
  2645. if(form->flags & (UI_HIDDEN | UI_DISABLED)) continue;
  2646. tx = x + form->ex; ty = y + form->ey;
  2647. x2 = parent == UI_MENU ? x + w : tx + form->ew;
  2648. y2 = ty + form->eh;
  2649. if(x2 > x + w) x2 = x + w;
  2650. if(y2 > y + h) y2 = y + h;
  2651. if(ctx->mousex >= tx && ctx->mousex < x2 && ctx->mousey >= ty && ctx->mousey < y2) {
  2652. if(form->type == UI_DIV) {
  2653. tx -= form->ox; ty -= form->oy;
  2654. return _ui_evtscr(ctx, tx, ty, form, evt) || _ui_evtproc(ctx, tx, ty, form->ew, form->eh, form->ptr, parent, evt);
  2655. }
  2656. ctx->hover = form;
  2657. if(form->type == UI_CUSTOM) {
  2658. if(form->ctrl) {
  2659. ctx->px = tx; ctx->py = ty; ctx->pw = x2 - tx; ctx->ph = y2 - ty;
  2660. ctx->pe = form->ctrl;
  2661. (*form->ctrl)(ctx, tx, ty, ctx->pw, ctx->ph, form, evt);
  2662. ctx->flags |= UI_REFRESH;
  2663. }
  2664. break;
  2665. } else
  2666. if(form->type == UI_BUTTON || form->type == UI_BTNTGL) {
  2667. if(evt->type == UI_EVT_MOUSE && (evt->btn & (UI_BTN_L | UI_BTN_RELEASE))) {
  2668. if(evt->btn & UI_BTN_L) ctx->pressed = form; else
  2669. if((evt->btn & UI_BTN_RELEASE) && ctx->pressed == form) {
  2670. if(form->type == UI_BTNTGL) {
  2671. popup = form->ptr && UI_CONTAINER(((ui_form_t*)form->ptr)->type) ? (ui_form_t*)form->ptr : &ctx->form[form->value];
  2672. goto toggle;
  2673. } else
  2674. if(form->ptr) *((int*)form->ptr) = form->value;
  2675. }
  2676. ctx->flags |= UI_REFRESH;
  2677. return 1;
  2678. }
  2679. break;
  2680. } else
  2681. if(evt->type == UI_EVT_MOUSE && (evt->btn & (UI_BTN_U | UI_BTN_D) && (form->type >= UI_SELECT && form->type <= UI_INT64))) {
  2682. if((form->type <= UI_OPTION && evt->btn & UI_BTN_D) || (evt->btn & UI_BTN_U)) _ui_inc(form); else _ui_dec(form);
  2683. ctx->flags |= UI_REFRESH;
  2684. return 1;
  2685. } else
  2686. if(form->type == UI_SELECT) {
  2687. if(evt->type == UI_EVT_MOUSE && (evt->btn & (UI_BTN_L | UI_BTN_RELEASE))) {
  2688. if(evt->btn & UI_BTN_L) ctx->pressed = form; else
  2689. if((evt->btn & UI_BTN_RELEASE) && ctx->pressed == form && form->ptr)
  2690. _ui_select_start(ctx, tx, ty, form->ew, form->eh, form);
  2691. ctx->flags |= UI_REFRESH;
  2692. return 1;
  2693. }
  2694. break;
  2695. } else
  2696. if(evt->type == UI_EVT_MOUSE && (evt->btn & UI_BTN_L)) {
  2697. switch(form->type) {
  2698. case UI_TOGGLE:
  2699. popup = !form->value && UI_CONTAINER(form[1].type) ? &form[1] : &ctx->form[form->value];
  2700. toggle: switch(popup->type) {
  2701. case UI_MENU:
  2702. /* note: could be ctx->menu == popups! */
  2703. if(popup->flags & UI_HIDDEN) {
  2704. if(ctx->menu) ctx->menu->flags |= UI_HIDDEN;
  2705. ctx->menu = popup;
  2706. popup->flags &= ~UI_HIDDEN;
  2707. } else {
  2708. if(ctx->menu) ctx->menu->flags |= UI_HIDDEN;
  2709. ctx->menu = NULL;
  2710. popup->flags |= UI_HIDDEN;
  2711. }
  2712. break;
  2713. case UI_POPUP: popup->flags ^= UI_HIDDEN; break;
  2714. case UI_DIV: popup->flags ^= UI_HIDDEN; ctx->flags |= UI_RECALC; break;
  2715. }
  2716. if(!(popup->flags & UI_HIDDEN) && ctx->popups[0])
  2717. popup->min = ctx->popups[0]->min + 1;
  2718. break;
  2719. case UI_BTNICN:
  2720. case UI_CHECK:
  2721. if(form->ptr) *((int*)form->ptr) ^= (form->value ? form->value : 1);
  2722. break;
  2723. case UI_RADIO:
  2724. if(form->ptr) *((int*)form->ptr) = form->value;
  2725. if(ctx->menu) {
  2726. ctx->menu->flags |= UI_HIDDEN;
  2727. ctx->menu = NULL;
  2728. }
  2729. break;
  2730. case UI_SLIDER:
  2731. ctx->hscr = form;
  2732. ctx->s1 = tx + 4; ctx->s2 = tx + form->ew - 5;
  2733. ctx->sm = form->max - form->min; ctx->sb = 0;
  2734. break;
  2735. case UI_HSCRBAR:
  2736. ctx->hscr = form;
  2737. ctx->sm = form->max - form->ew;
  2738. t = _ui_scr(form->ew, form->ptr ? *((int*)form->ptr) : 0, form->max, ctx->sh, &b);
  2739. ctx->sb = ctx->mousex >= tx + t && ctx->mousex < tx + t + b ? ctx->mousex - tx - t : b / 2;
  2740. ctx->s1 = tx; ctx->s2 = tx + form->ew - b + ctx->sb;
  2741. break;
  2742. case UI_VSCRBAR:
  2743. ctx->vscr = form;
  2744. ctx->sm = form->max - form->eh;
  2745. t = _ui_scr(form->eh, form->ptr ? *((int*)form->ptr) : 0, form->max, ctx->sw, &b);
  2746. ctx->sb = ctx->mousey >= ty + t && ctx->mousey < ty + t + b ? ctx->mousey - ty - t : b / 2;
  2747. ctx->s1 = ty; ctx->s2 = ty + form->eh - b + ctx->sb;
  2748. break;
  2749. case UI_TXTINP:
  2750. if(form->ptr && form->max > 0) {
  2751. ((char*)form->ptr)[form->max - 1] = 0;
  2752. _ui_text_start(ctx, tx, ty, x2 - tx, y2 - ty, form, form->ptr, form->max);
  2753. }
  2754. break;
  2755. case UI_OPTION: case UI_INT8: case UI_INT16: case UI_INT32: case UI_INT64: case UI_FLOAT:
  2756. ctx->pw = 0;
  2757. if(form->ptr) {
  2758. if(ctx->mousex < tx + form->eh) { ctx->pressed = form; ctx->pw = 1; _ui_dec(form); }
  2759. if(ctx->mousex >= x2 - form->eh) { ctx->pressed = form; ctx->pw = 2; _ui_inc(form); }
  2760. }
  2761. break;
  2762. case UI_IMAGE: if(form->ptr) *((int*)form->ptr) = form->value; break;
  2763. case UI_COLOR: _ui_color_start(ctx, tx, ty, form->ew, form->eh, form); break;
  2764. }
  2765. ctx->flags |= UI_REFRESH;
  2766. return 1;
  2767. }
  2768. break;
  2769. }
  2770. }
  2771. return 0;
  2772. }
  2773. /**
  2774. * Initialize the main UI context
  2775. * Gets a string array with translatable UTF-8 strings, window size and a window icon
  2776. */
  2777. int ui_init(ui_t *ctx, int txtc, char **txtv, int w, int h, ui_image_t *icon)
  2778. {
  2779. if(!ctx || txtc < 1 || !txtv || !*txtv) return UI_ERR_BADINP;
  2780. memset(ctx, 0, sizeof(ui_t));
  2781. ctx->txtc = txtc;
  2782. ctx->txtv = txtv;
  2783. #ifdef UI_DEFAULTFONT
  2784. ctx->fnt = ui_psf2_default;
  2785. ctx->bbox = &ui_psf2_bbox; ctx->draw = &ui_psf2_draw;
  2786. #else
  2787. #ifdef UI_SETFONTHOOK
  2788. ui_fonthook(ctx, UI_SETFONTHOOK);
  2789. #endif
  2790. #endif
  2791. ctx->sw = ctx->sh = 10;
  2792. ui_theme(ctx, (uint32_t*)&ui_default);
  2793. _ui_resize(ctx, w, h);
  2794. return ui_backend_init(ctx, txtv[0], w, h, icon);
  2795. }
  2796. /**
  2797. * Toggle fullscreen mode
  2798. */
  2799. int ui_fullscreen(ui_t *ctx)
  2800. {
  2801. return ctx ? ui_backend_fullscreen(ctx->bck) : UI_ERR_BADINP;
  2802. }
  2803. /**
  2804. * Return a backend specific window handle
  2805. */
  2806. void *ui_getwindow(ui_t *ctx)
  2807. {
  2808. return ctx ? ui_backend_getwindow(ctx->bck) : NULL;
  2809. }
  2810. /**
  2811. * Main event loop
  2812. * Returns event on success, NULL if you should exit the loop
  2813. */
  2814. ui_event_t *ui_event(ui_t *ctx, ui_form_t *form)
  2815. {
  2816. static ui_event_t empty = { 0 };
  2817. ui_event_t *ret = &empty, *evt = &empty;
  2818. ui_form_t *lasthover;
  2819. int i, j, k, l, clk;
  2820. if(ctx) {
  2821. lasthover = ctx->hover;
  2822. ctx->hover = NULL;
  2823. ctx->flags &= ~UI_DONE;
  2824. /* pump events */
  2825. if(ui_backend_event(ctx->bck)) return NULL;
  2826. if(ctx->tail != ctx->head) {
  2827. ret = evt = &ctx->events[ctx->tail++];
  2828. ctx->tail %= UI_MAXEVENTS;
  2829. }
  2830. if(!ctx->drag && !ctx->resize && !ctx->vscr && !ctx->hscr && !ctx->text && !ctx->popup) {
  2831. /* recalculate positions */
  2832. if(form && ctx->form != form) {
  2833. ctx->form = form;
  2834. _ui_recalc(ctx, 0, 0, ctx->screen.w, ctx->screen.h, 0, ctx->form, NULL, NULL);
  2835. }
  2836. /* check events for popups, in reverse z order */
  2837. clk = ret->type == UI_EVT_MOUSE && !(ret->btn & UI_BTN_RELEASE);
  2838. for(i = 0; i < ctx->numpopups; i++) {
  2839. if(ctx->mousex >= ctx->popups[i]->ex && ctx->mousex < ctx->popups[i]->ex + ctx->popups[i]->ew &&
  2840. ctx->mousey >= ctx->popups[i]->ey && ctx->mousey < ctx->popups[i]->ey + ctx->popups[i]->eh) {
  2841. if(clk) {
  2842. ret = &empty;
  2843. /* a popup or menu clicked, handle z order, bring to foreground */
  2844. if(i) {
  2845. ctx->popups[i]->min = ctx->popups[0]->min + 1;
  2846. ctx->flags |= UI_REFRESH;
  2847. }
  2848. /* popup header clicked */
  2849. if(ctx->popups[i]->type == UI_POPUP && (ctx->popups[i]->flags & UI_DRAGGABLE) &&
  2850. ctx->mousey < ctx->popups[i]->ey + ctx->popups[i]->value) {
  2851. if(ctx->mousex > ctx->popups[i]->ex + ctx->popups[i]->ew - ctx->popups[i]->value) {
  2852. /* close button */
  2853. ctx->popups[i]->flags |= UI_HIDDEN;
  2854. ctx->flags |= UI_REFRESH;
  2855. } else {
  2856. /* drag popup */
  2857. ctx->drag = ctx->popups[i];
  2858. ctx->dx = ctx->mousex - ctx->popups[i]->ex;
  2859. ctx->dy = ctx->mousey - ctx->popups[i]->ey;
  2860. }
  2861. break;
  2862. } else
  2863. if(ctx->popups[i]->type == UI_POPUP && (ctx->popups[i]->flags & UI_RESIZABLE) &&
  2864. ctx->mousex > ctx->popups[i]->ex + ctx->popups[i]->ew - 7 &&
  2865. ctx->mousey > ctx->popups[i]->ey + ctx->popups[i]->eh - 7) {
  2866. /* resize corner clicked */
  2867. ctx->resize = ctx->popups[i];
  2868. ctx->dx = ctx->popups[i]->ex + ctx->popups[i]->ew - ctx->mousex;
  2869. ctx->dy = ctx->popups[i]->ey + ctx->popups[i]->eh - ctx->mousey;
  2870. break;
  2871. }
  2872. }
  2873. /* scrollbars clicked */
  2874. if(_ui_evtscr(ctx, ctx->popups[i]->ex, ctx->popups[i]->ey, ctx->popups[i], evt))
  2875. break;
  2876. /* bubble event down to its children */
  2877. if(!_ui_evtproc(ctx, ctx->popups[i]->ex + ctx->popups[i]->m - ctx->popups[i]->ox,
  2878. ctx->popups[i]->ey + ctx->popups[i]->m + ctx->popups[i]->value - ctx->popups[i]->oy,
  2879. ctx->popups[i]->ew - 2 * ctx->popups[i]->m - (ctx->popups[i]->sh ? ctx->sw : 0),
  2880. ctx->popups[i]->eh - 2 * ctx->popups[i]->m - (ctx->popups[i]->sw ? ctx->sh : 0),
  2881. ctx->popups[i]->ptr, ctx->popups[i]->type, evt) && evt->type == UI_EVT_MOUSE) {
  2882. /* if no children caught the wheel event, try to scroll the popup */
  2883. k = ctx->popups[i]->eh / 10; if(k < 4) k = 4;
  2884. l = ctx->popups[i]->ew / 10; if(l < 4) l = 4;
  2885. if(evt->btn & UI_BTN_U) ctx->popups[i]->oy -= k; else
  2886. if(evt->btn & UI_BTN_D) ctx->popups[i]->oy += k; else
  2887. if(evt->btn & UI_BTN_A) ctx->popups[i]->ox -= l; else
  2888. if(evt->btn & UI_BTN_B) ctx->popups[i]->ox += l;
  2889. if(ctx->popups[i]->oy > ctx->popups[i]->mh) ctx->popups[i]->oy = ctx->popups[i]->mh;
  2890. if(ctx->popups[i]->oy < 0) ctx->popups[i]->oy = 0;
  2891. if(ctx->popups[i]->ox > ctx->popups[i]->mw) ctx->popups[i]->ox = ctx->popups[i]->mw;
  2892. if(ctx->popups[i]->ox < 0) ctx->popups[i]->ox = 0;
  2893. ctx->flags |= UI_REFRESH;
  2894. ret = &empty;
  2895. }
  2896. break;
  2897. }
  2898. }
  2899. /* if menu active and clicked outside or Esc pressed */
  2900. if(ctx->menu && (clk || (evt->type == UI_EVT_KEY && evt->key[0] == '\x1b'))) {
  2901. ctx->menu->flags |= UI_HIDDEN;
  2902. ctx->menu = NULL;
  2903. ctx->flags |= UI_REFRESH;
  2904. ret = &empty;
  2905. i = ctx->numpopups;
  2906. }
  2907. /* check if we need to swallow this event */
  2908. if(i >= ctx->numpopups && _ui_evtproc(ctx, 0, 0, ctx->screen.w, ctx->screen.h, form, UI_END, ret) && ret->type == UI_EVT_MOUSE)
  2909. ret = &empty;
  2910. }
  2911. /* if a button was pressed, but released outside */
  2912. if((ctx->pressed || ctx->drag || ctx->resize || ctx->vscr || ctx->hscr) &&
  2913. evt->type == UI_EVT_MOUSE && (evt->btn & UI_BTN_RELEASE)) {
  2914. ctx->pressed = ctx->drag = ctx->resize = ctx->vscr = ctx->hscr = NULL;
  2915. ctx->flags |= UI_REFRESH;
  2916. ret = &empty;
  2917. }
  2918. /* handle "window" operations */
  2919. if(ctx->drag) {
  2920. i = ctx->mousex - ctx->dx; if(i < 1) { i = 1; } clk = ctx->screen.w - ctx->drag->ew - 1; if(i > clk) i = clk;
  2921. j = ctx->mousey - ctx->dy; if(j < 1) { j = 1; } clk = ctx->screen.h - ctx->drag->eh - 1; if(j > clk) j = clk;
  2922. ctx->drag->align = 0;
  2923. ctx->drag->x = UI_ABS(i);
  2924. ctx->drag->y = UI_ABS(j);
  2925. ctx->drag->ex = i;
  2926. ctx->drag->ey = j;
  2927. ctx->flags |= UI_REFRESH;
  2928. }
  2929. if(ctx->resize) {
  2930. k = ctx->resize->sh ? ctx->sw : 0;
  2931. l = ctx->resize->sw ? ctx->sh : 0;
  2932. i = ctx->mousex - ctx->resize->ex + ctx->dx;
  2933. j = ctx->mousey - ctx->resize->ey + ctx->dy;
  2934. clk = 8 + 2 * ctx->resize->m + k; if(i < clk) i = clk;
  2935. clk = 8 + ctx->resize->value + 2 * ctx->resize->m + l; if(j < clk) j = clk;
  2936. _ui_recalc(ctx, 2, 2, i - 2 * ctx->resize->m - k, j - 2 * ctx->resize->m - l, ctx->resize->p, ctx->resize->ptr,
  2937. NULL, NULL);
  2938. ctx->resize->align = 0;
  2939. ctx->resize->h = ctx->resize->ew = i;
  2940. ctx->resize->h = ctx->resize->eh = j;
  2941. ctx->flags |= UI_REFRESH;
  2942. }
  2943. if((ctx->vscr || ctx->hscr) && ctx->sm > 0) {
  2944. i = (ctx->vscr ? ctx->mousey : ctx->mousex) - ctx->sb + 1;
  2945. if(i > ctx->s2) i = ctx->s2;
  2946. i = i > ctx->s1 ? i - ctx->s1 : 0;
  2947. j = ctx->s2 - ctx->s1 - ctx->sb;
  2948. k = i * ctx->sm / (j < 1 ? 1 : j);
  2949. if(k > ctx->sm) k = ctx->sm;
  2950. if(ctx->hscr) {
  2951. if(ctx->hscr->type == UI_SLIDER) {
  2952. k += ctx->hscr->min;
  2953. if(ctx->hscr->inc > 0) k -= k % ctx->hscr->inc;
  2954. if(ctx->hscr->ptr) *((int*)ctx->hscr->ptr) = k;
  2955. } else
  2956. if(ctx->hscr->type == UI_HSCRBAR) {
  2957. if(ctx->hscr->ptr) *((int*)ctx->hscr->ptr) = k;
  2958. } else
  2959. ctx->hscr->ox = k;
  2960. } else {
  2961. if(ctx->vscr->type == UI_VSCRBAR) {
  2962. if(ctx->vscr->ptr) *((int*)ctx->vscr->ptr) = k;
  2963. } else
  2964. ctx->vscr->oy = k;
  2965. }
  2966. ctx->flags |= UI_REFRESH;
  2967. }
  2968. /* if text input active */
  2969. if(ctx->text) {
  2970. _ui_text_ctrl(ctx, evt);
  2971. ctx->flags |= UI_REFRESH;
  2972. ret = &empty;
  2973. }
  2974. /* if custom popup active */
  2975. if(ctx->popup) {
  2976. if(!ctx->pe || (ctx->flags & UI_CLOSE) || (evt->type == UI_EVT_MOUSE && !(evt->btn & UI_BTN_RELEASE) &&
  2977. (ctx->mousex < ctx->px || ctx->mousex >= ctx->px + ctx->pw ||
  2978. ctx->mousey < ctx->py || ctx->mousey >= ctx->py + ctx->ph))) {
  2979. close: if(ctx->popup->type == UI_CUSTOM && ctx->popup->fini)
  2980. (*ctx->popup->fini)(ctx, ctx->popup);
  2981. ctx->flags |= UI_REFRESH;
  2982. ctx->popup = NULL;
  2983. ctx->pe = NULL;
  2984. ctx->dr = NULL;
  2985. } else
  2986. if(ctx->pe) {
  2987. (*ctx->pe)(ctx, ctx->px, ctx->py, ctx->pw, ctx->ph, ctx->popup, evt);
  2988. if(ctx->flags & UI_CLOSE) goto close;
  2989. ctx->flags |= UI_REFRESH;
  2990. }
  2991. ret = &empty;
  2992. }
  2993. ctx->flags &= ~UI_CLOSE;
  2994. /* recalculate positions */
  2995. if(ctx->flags & UI_RECALC) {
  2996. _ui_recalc(ctx, 0, 0, ctx->screen.w, ctx->screen.h, 0, ctx->form, NULL, NULL);
  2997. ctx->flags &= ~UI_RECALC;
  2998. }
  2999. /* if we have to redraw the form */
  3000. if((ctx->flags & UI_REFRESH) || (ctx->hover != lasthover)) {
  3001. _ui_redraw(ctx, form);
  3002. ctx->lastx = -1;
  3003. ctx->flags &= ~UI_REFRESH;
  3004. }
  3005. /* display software cursor */
  3006. if(ctx->mouse.buf && ctx->mouse.w > 0 && (ctx->lastx != ctx->mousex || ctx->lasty != ctx->mousey)) {
  3007. ctx->cx0 = ctx->cy0 = 0; ctx->cx1 = ctx->screen.w; ctx->cy1 = ctx->screen.h;
  3008. if(ctx->lastx != -1)
  3009. _ui_copy(&ctx->screen, ctx->lastx - ctx->mouse.w / 2, ctx->lasty - ctx->mouse.h / 2,
  3010. ctx->mouse.w, ctx->mouse.h, &ctx->mouse, 0, 0);
  3011. ctx->lastx = ctx->mousex; ctx->lasty = ctx->mousey;
  3012. _ui_copy(&ctx->mouse, 0, 0, ctx->mouse.w, ctx->mouse.h, &ctx->screen,
  3013. ctx->mousex - ctx->mouse.w / 2, ctx->mousey - ctx->mouse.h / 2);
  3014. _ui_blit(ctx, ctx->mousex - ctx->mouse.w / 2, ctx->mousey - ctx->mouse.h / 2,
  3015. ctx->mouse.w, ctx->mouse.h, &ctx->skin[UI_CURSOR], 0, 0, 0);
  3016. }
  3017. /* put the (potentially updated) UI layer on screen */
  3018. ui_backend_redraw(ctx->bck);
  3019. }
  3020. return ret;
  3021. }
  3022. /**
  3023. * Free all UI resources
  3024. */
  3025. int _ui_free(ui_t *ctx, ui_form_t *form)
  3026. {
  3027. if(!ctx || !form) return UI_ERR_BADINP;
  3028. for(; form->type != UI_END; form++) {
  3029. if(form->type == UI_CUSTOM && form->fini)
  3030. (*form->fini)(ctx, form);
  3031. if(UI_CONTAINER(form->type))
  3032. _ui_free(ctx, form->ptr);
  3033. }
  3034. return UI_OK;
  3035. }
  3036. int ui_free(ui_t *ctx)
  3037. {
  3038. if(!ctx) return UI_ERR_BADINP;
  3039. _ui_free(ctx, ctx->form);
  3040. ui_backend_free(ctx->bck);
  3041. if(ctx->screen.buf) free(ctx->screen.buf);
  3042. if(ctx->mouse.buf) free(ctx->mouse.buf);
  3043. if(ctx->skinbuf) free(ctx->skinbuf);
  3044. memset(ctx, 0, sizeof(ui_t));
  3045. return UI_OK;
  3046. }
  3047. #ifdef __cplusplus
  3048. }
  3049. #endif
  3050. #endif /* UI_IMPLEMENTATION */
  3051. #endif /* UI_H */