List.cpp 71 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. LCM_MOUSE is disabled if last action was by keyboard,
  6. to allow changing cursor with the keyboard.
  7. /******************************************************************************/
  8. #define MIN_COLUMN_EDGE_WIDTH 0.030f
  9. #define COLUMN_RESIZE_WIDTH 0.015f
  10. /******************************************************************************/
  11. static _Memx& NodeChildren( Ptr data, Int children_offset) {return *(_Memx*)(((Byte*)data)+children_offset);}
  12. static Int NodeElms (_Memx &node, Int children_offset)
  13. {
  14. Int elms=0; FREPA(node)elms+=1+NodeElms(NodeChildren(node[i], children_offset), children_offset); // increase self + children of self
  15. return elms;
  16. }
  17. static void NodeVisible(_Memx &node, Int children_offset, Ptr* &abs_to_data, Ptr* &vis_to_data, C MemPtr<Bool> &visible, Int &index)
  18. {
  19. FREPA(node)
  20. {
  21. Ptr data=node[i];
  22. *abs_to_data++=data;
  23. if(!visible || ElmIs(visible, index))*vis_to_data++=data;
  24. NodeVisible(NodeChildren(data, children_offset), children_offset, abs_to_data, vis_to_data, visible, ++index);
  25. }
  26. }
  27. static Ptr NodeAbsToData(_Memx &node, Int children_offset, Int &abs)
  28. {
  29. FREPA(node)
  30. {
  31. Ptr e=node[i]; if(!abs--)return e;
  32. if( e=NodeAbsToData(NodeChildren(e, children_offset), children_offset, abs))return e;
  33. }
  34. return null;
  35. }
  36. static Int NodeDataToAbs(_Memx &node, Int children_offset, Ptr data, Int &abs)
  37. {
  38. FREPA(node)
  39. {
  40. Ptr e=node[i]; if(e==data)return abs; abs++;
  41. Int j=NodeDataToAbs(NodeChildren(e, children_offset), children_offset, data, abs); if(j>=0)return j;
  42. }
  43. return -1;
  44. }
  45. /******************************************************************************/
  46. GuiPC::GuiPC(C GuiPC &old, _List &list) // this is to be used for columns only
  47. {
  48. T=old;
  49. visible&=(list.visible() && list.columnsVisible());
  50. enabled&= list.enabled();
  51. #if 0
  52. if(list.parent() && list.parent()->type()==GO_REGION)offset.y-=list.parent()->asRegion().slidebar[1].offset(); // this can't be used due to pixel align (when scrolling list, the column buttons shake)
  53. #else
  54. offset.y=client_rect.max.y;
  55. #endif
  56. }
  57. static Bool SetGPC(_List &list, GuiPC &gpc, C Vec2 &offset, C VecI2 &abs_col, C VecI2 &visible_range) // this is to be used for children only
  58. {
  59. // column can be greater than the number of columns, in that case it will be drawn after the last visible column
  60. if(abs_col.y>=0)
  61. if(!(list.drawMode()==LDM_RECTS && abs_col.y!=list.draw_column)) // for 'LDM_RECTS' we draw only the 'draw_column'
  62. if(!(list.drawMode()==LDM_LIST && InRange(abs_col.y, list._columns) && list.column(abs_col.y).hidden())) // for 'LDM_LIST' don't draw hidden columns (do this check only for existing columns, but not when we want after all columns)
  63. {
  64. Int vis=list.absToVis(abs_col.x);
  65. if( vis>=0 && vis>=visible_range.x && vis<=visible_range.y)
  66. {
  67. gpc.offset=list.visToLocalPos(vis)+offset;
  68. if(list.drawMode()==LDM_LIST)gpc.offset.x+=list.columnOffset(abs_col.y);
  69. return true;
  70. }
  71. }
  72. return false;
  73. }
  74. /******************************************************************************/
  75. // COLUMN
  76. /******************************************************************************/
  77. ListColumn::ListColumn( ) {create(null, 0, S);}
  78. ListColumn::ListColumn(DATA_TYPE type, Int offset, Int size, Flt width, C Str &name) : md(type, offset, size) {create(null, width, name);}
  79. ListColumn::ListColumn(ListColumn &&lc ) : ListColumn() {Swap(T, lc);}
  80. /******************************************************************************/
  81. void ListColumn::create(Str (*data_to_text)(CPtr data), Flt width, C Str &name)
  82. {
  83. T.md._data_to_text=data_to_text;
  84. if(data_to_text && !T.md.type)T.md.type=DATA_CHAR_PTR;
  85. T._resize_edge=0;
  86. T.text_align =DataAlign(md.type);
  87. T.precision =3 ;
  88. T.width =width;
  89. T.sort =null ;
  90. super::create(name).focusable(false)._sub_type=BUTTON_TYPE_LIST_COLUMN;
  91. super::text_align=1;
  92. }
  93. void ListColumn::pushed()
  94. {
  95. if(_List *list=CAST(_List, parent()))
  96. {
  97. Int index =list->_columns.index(this);
  98. if( index>=0)
  99. {
  100. if(index==list->draw_column) // if clicked a draw column, then toggle list draw mode
  101. {
  102. list->drawMode((list->drawMode()==LDM_LIST) ? LDM_RECTS : LDM_LIST);
  103. }else // otherwise
  104. {
  105. if(list->flag&LIST_SORTABLE)list->sort(index); // check for sorting
  106. }
  107. }
  108. }
  109. }
  110. static void Pushed(ListColumn &lc) {lc.pushed();}
  111. void ListColumn::create(C ListColumn &src, _List &list)
  112. {
  113. super::create(src).func(Pushed, T)._parent=&list;
  114. skin=list.skin();
  115. precision =src.precision;
  116. width =src.width;
  117. text_align=src.text_align;
  118. md =src.md;
  119. sort =src.sort;
  120. if(md.type==DATA_IMAGE
  121. || md.type==DATA_IMAGE_PTR
  122. || md.type==DATA_IMAGEPTR)
  123. {
  124. Int index = list._columns.index(this);
  125. if( index>=0)list.draw_column=index;
  126. }
  127. }
  128. /******************************************************************************/
  129. static Int ResizeColumn(_List *list, ListColumn &column, Int &resize_edge)
  130. {
  131. if(resize_edge)
  132. {
  133. if(!list)resize_edge=0;else
  134. {
  135. Int i=list->_columns.index(&column); if(i>=0)
  136. {
  137. if(resize_edge==-1)for(i--; i>=0; i--)if(list->_columns[i].visible())break; // find first previous visible column
  138. if(!InRange(i, list->_columns))resize_edge=0;
  139. return i;
  140. }
  141. }
  142. }
  143. return -1;
  144. }
  145. void ListColumn::update(C GuiPC &gpc)
  146. {
  147. if(Gui.ms()==this)
  148. {
  149. _List *list=((parent() && parent()->type()==GO_LIST) ? &parent()->asList() : null);
  150. if(Ms.b(0) || Ms.br(0)) // resize
  151. {
  152. Int col=ResizeColumn(list, T, _resize_edge);
  153. if(list && InRange(col, list->_columns))
  154. {
  155. if(Ms.bd(0)) // auto-size
  156. {
  157. list->columnWidth(col, Max(MIN_COLUMN_EDGE_WIDTH, list->columnDataWidthEx(col)));
  158. }else
  159. if(Flt d=Ms.dc().x)
  160. list->columnWidth(col, Max(MIN_COLUMN_EDGE_WIDTH, list->columnWidth(col)+d));
  161. }
  162. }else
  163. {
  164. _resize_edge=0;
  165. if(list && (list->flag&LIST_RESIZABLE_COLUMNS))
  166. {
  167. if(Ms.pos().x<=rect().min.x+gpc.offset.x+COLUMN_RESIZE_WIDTH)_resize_edge=-1;else
  168. if(Ms.pos().x>=rect().max.x+gpc.offset.x-COLUMN_RESIZE_WIDTH)_resize_edge=+1;
  169. }
  170. ResizeColumn(list, T, _resize_edge);
  171. }
  172. }else _resize_edge=0;
  173. if(_resize_edge)super::update(GuiPC(gpc, true, false));else super::update(gpc);
  174. }
  175. /******************************************************************************/
  176. Flt _List::parentWidth()C
  177. {
  178. if(GuiObj *parent=T.parent())
  179. {
  180. if(parent->type()==GO_MENU)
  181. {
  182. if(GuiObj *owner=parent->asMenu().Owner())if(owner->type()==GO_COMBOBOX)return owner->clientSize().x;
  183. }
  184. if(parent->type()==GO_REGION)return parent->size().x-parent->asRegion().slidebarSize();
  185. else return parent->clientSize().x;
  186. }
  187. return 0;
  188. }
  189. Flt _List::columnDataWidth(Int i, Bool visible)C
  190. {
  191. Flt max_width=0;
  192. if(InRange(i, _columns))
  193. {
  194. C ListColumn &lc=_columns[i];
  195. if(GuiSkin *skin=getSkin())
  196. if(TextStyle *text_style=skin->list.text_style())
  197. {
  198. TextStyleParams ts=*text_style; ts.size=textSizeActual();
  199. #if DEFAULT_FONT_FROM_CUSTOM_SKIN
  200. if(!ts.font())ts.font(skin->font()); // adjust font in case it's empty and the custom skin has a different font than the 'Gui.skin'
  201. #endif
  202. REP(visible ? visibleElms() : totalElms())
  203. {
  204. Ptr data=(visible ? visToData(i) : absToData(i));
  205. Flt elm_width=ts.textWidth(lc.md.asText(data, lc.precision));
  206. if(_offset_offset>=0)elm_width+=*(Flt*)((Byte*)data+_offset_offset);
  207. MAX(max_width, elm_width);
  208. }
  209. }
  210. }
  211. return max_width;
  212. }
  213. Flt _List::columnDataWidthEx(Int i)C
  214. {
  215. Flt w=columnDataWidth(i);
  216. if(InRange(i, _columns))
  217. {
  218. C ListColumn &lc=_columns[i];
  219. if(lc.visible())MAX(w, lc.textWidth(&_column_height)); // specify '_column_height' because 'ListColumn' height may not be set yet
  220. w+=0.02f;
  221. }
  222. return w;
  223. }
  224. Flt _List::columnWidthActual(Int i)C
  225. {
  226. if(InRange(i, _columns))
  227. {
  228. C ListColumn &lc=_columns[i];
  229. if(lc.width<0) // special modes
  230. {
  231. if(Equal(lc.width, LCW_DATA , 0.5f))return columnDataWidthEx(i) ;
  232. if(Equal(lc.width, LCW_PARENT , 0.5f))return parentWidth() ;
  233. if(Equal(lc.width, LCW_MAX_DATA_PARENT, 0.5f))return Max(columnDataWidthEx(i), parentWidth());
  234. return 0;
  235. }
  236. return lc.width;
  237. }
  238. return 0;
  239. }
  240. _List& _List::columnWidth(Int i, Flt width)
  241. {
  242. if(InRange(i, _columns))
  243. {
  244. ListColumn &lc=_columns[i];
  245. if(lc.width!=width){lc.width=width; setRects();}
  246. }
  247. return T;
  248. }
  249. _List& _List::columnVisible(Int i, Bool visible)
  250. {
  251. if(InRange(i, _columns))
  252. {
  253. ListColumn &lc=_columns[i];
  254. if(lc.visible()!=visible){lc.visible(visible); setRects();}
  255. }
  256. return T;
  257. }
  258. Flt _List::columnWidth (Int i)C {return InRange(i, _columns) ? _columns[i].rect().w() : 0;}
  259. Bool _List::columnVisible(Int i)C {return InRange(i, _columns) ? _columns[i].visible () : false;}
  260. Flt _List::columnOffset (Int i)C
  261. {
  262. if(i>=1)
  263. {
  264. if(InRange(i, _columns ))return _columns [i].rect().min.x;
  265. if( _columns.elms())return _columns.last().rect().max.x;
  266. }
  267. return 0;
  268. }
  269. Int _List::localToColumnX(Flt local_x)C
  270. {
  271. REPA(_columns)
  272. {
  273. C ListColumn &lc=_columns[i]; if(lc.visible() && lc.rect().includesX(local_x))return i;
  274. }
  275. return -1;
  276. }
  277. /******************************************************************************/
  278. Int _List::firstColumn(DATA_TYPE type)
  279. {
  280. FREP(columns())if(column(i).md.type==type)return i;
  281. return -1;
  282. }
  283. Int _List::firstColumnText()
  284. {
  285. FREP(columns())if(DataIsText(column(i).md.type))return i;
  286. return -1;
  287. }
  288. _List& _List::offsetAllColumns(Bool on) {_offset_first_column=!on; return T;}
  289. /******************************************************************************/
  290. // CHILDREN
  291. /******************************************************************************/
  292. _List::Children::Children() {children.replaceClass<Child>();}
  293. _List::Children::Child* _List::Children::add (GuiObj &child, GuiObj &parent) {return static_cast<Child*>(GuiObjChildren::add(child, parent));}
  294. _List::Children::Child& _List::Children::operator[](Int i) {return SCAST( Child, children[i]);}
  295. C _List::Children::Child& _List::Children::operator[](Int i)C {return SCAST(C Child, children[i]);}
  296. Vec2 _List::childOffset(C GuiObj &child)C
  297. {
  298. Vec2 offset=0; Int index; if(_children.find(child, index))
  299. {
  300. C Children::Child &child=_children[index];
  301. Int vis=absToVis(child.abs_col.x);
  302. if( vis>=0)
  303. {
  304. offset=visToLocalPos(vis);
  305. if(drawMode()==LDM_LIST)offset.x+=columnOffset(child.abs_col.y);
  306. }
  307. }
  308. return offset;
  309. }
  310. /******************************************************************************/
  311. // LIST
  312. /******************************************************************************/
  313. void _List::zero()
  314. {
  315. sort_swap [0]=sort_swap [1]=sort_swap [2]=false;
  316. sort_column[0]=sort_column[1]=sort_column[2]=-1;
  317. draw_column =-1;
  318. cur=lit=-1;
  319. cur_mode=LCM_DEFAULT;
  320. _draw_mode=LDM_LIST;
  321. sel_mode=LSM_SET;
  322. flag=LIST_SORTABLE|LIST_SEARCHABLE;
  323. image_alpha=ALPHA_BLEND;
  324. zoom_min=Pow(1.2f, -3);
  325. zoom_max=Pow(1.2f, 2);
  326. padding.zero();
  327. _total_elms =0;
  328. _visible_elms=0;
  329. _elm_size=0;
  330. _abs_to_vis =null;
  331. _abs_to_data=null;
  332. _vis_to_abs =null;
  333. _vis_to_data=null;
  334. _rects=null;
  335. _horizontal=false;
  336. _columns_hidden=false;
  337. _offset_first_column=true;
  338. _kb_action=false;
  339. _data=null;
  340. _memb=null;
  341. _memx=null;
  342. _meml=null;
  343. _map =null;
  344. _node=null;
  345. _search[0]=0;
  346. _search_i=0;
  347. _search_t=0;
  348. _tap_vis=-1;
  349. _tap_touch_id=0;
  350. _type_offset=-1;
  351. _desc_offset=-1;
  352. _image_color_offset=-1;
  353. _text_color_offset=-1;
  354. _text_shadow_offset=-1;
  355. _alpha_mode_offset=-1;
  356. _offset_offset=-1;
  357. _group_offset=-1;
  358. _children_offset= 0;
  359. _column_height=0.055f;
  360. _elm_height=0.050f;
  361. _text_base =0.050f;
  362. _text_rel =0;
  363. _image_base =0;
  364. _image_rel =0.1f/64;
  365. _image_padd .zero();
  366. _zoom =1;
  367. _height_ez=_elm_height*_zoom;
  368. _cur_changed_user=_sel_changed_user=_sel_changing_user=null;
  369. _cur_changed =_sel_changed =_sel_changing =null;
  370. }
  371. _List::_List() {zero();}
  372. _List& _List::del()
  373. {
  374. _children.del ();
  375. _columns .del ();
  376. sel .del ();
  377. _skin .clear();
  378. Free(_abs_to_vis );
  379. Free(_abs_to_data);
  380. Free(_vis_to_abs );
  381. Free(_vis_to_data);
  382. Free(_rects );
  383. super::del(); zero(); return T;
  384. }
  385. _List& _List::clear(SET_MODE mode)
  386. {
  387. Bool cur_changed=(cur >=0 && mode!=QUIET),
  388. sel_changed=(sel.elms()> 0 && mode!=QUIET); // if had selection but now it's empty
  389. if( sel_changed)callSelChanging();
  390. _children.del();
  391. Free(_abs_to_vis );
  392. Free(_abs_to_data);
  393. Free(_vis_to_abs );
  394. Free(_vis_to_data);
  395. Free(_rects );
  396. _data=null;
  397. _memb=null;
  398. _memx=null;
  399. _meml=null;
  400. _map =null;
  401. _node=null;
  402. _total_elms=_visible_elms=_elm_size=0;
  403. cur=lit=-1; sel.clear();
  404. setRects();
  405. if(cur_changed)callCurChanged();
  406. if(sel_changed)callSelChanged();
  407. return T;
  408. }
  409. /******************************************************************************/
  410. _List& _List::create()
  411. {
  412. del();
  413. _type =GO_LIST;
  414. _visible=true;
  415. return T;
  416. }
  417. _List& _List::create(C _List &src)
  418. {
  419. if(this!=&src)
  420. {
  421. if(!src.is())del();else
  422. {
  423. _children.del();
  424. copyParams(src);
  425. _type=GO_LIST;
  426. CopyFast(sort_swap , src.sort_swap );
  427. CopyFast(sort_column, src.sort_column);
  428. cur=src.cur;
  429. lit=src.lit;
  430. sel=src.sel;
  431. cur_mode =src. cur_mode;
  432. _draw_mode =src._draw_mode;
  433. sel_mode =src. sel_mode;
  434. flag =src. flag;
  435. image_alpha=src. image_alpha;
  436. zoom_min =src. zoom_min;
  437. zoom_max =src. zoom_max;
  438. padding =src. padding;
  439. _skin =src._skin;
  440. _total_elms =src._total_elms;
  441. _visible_elms=src._visible_elms;
  442. _elm_size =src._elm_size;
  443. if(src._abs_to_vis )CopyFastN(Alloc(_abs_to_vis , totalElms()), src._abs_to_vis , totalElms());
  444. if(src._abs_to_data)CopyFastN(Alloc(_abs_to_data, totalElms()), src._abs_to_data, totalElms());
  445. if(src._vis_to_abs )CopyFastN(Alloc(_vis_to_abs , visibleElms()), src._vis_to_abs , visibleElms());
  446. if(src._vis_to_data)CopyFastN(Alloc(_vis_to_data, visibleElms()), src._vis_to_data, visibleElms());
  447. if(src._rects )CopyFastN(Alloc(_rects , visibleElms()), src._rects , visibleElms());
  448. _offset_first_column=src._offset_first_column;
  449. _horizontal =src._horizontal;
  450. _kb_action =false;
  451. _data=src._data;
  452. _memb=src._memb;
  453. _memx=src._memx;
  454. _meml=src._meml;
  455. _map =src._map ;
  456. _node=src._node;
  457. CopyFast(_search ,src._search);
  458. _search_i=src._search_i;
  459. _search_t=src._search_t;
  460. _type_offset=src. _type_offset;
  461. _desc_offset=src. _desc_offset;
  462. _image_color_offset=src._image_color_offset;
  463. _text_color_offset=src. _text_color_offset;
  464. _text_shadow_offset=src._text_shadow_offset;
  465. _alpha_mode_offset=src. _alpha_mode_offset;
  466. _offset_offset=src. _offset_offset;
  467. _group_offset=src. _group_offset;
  468. _children_offset=src. _children_offset;
  469. _column_height=src._column_height;
  470. _elm_height=src. _elm_height;
  471. _text_base =src. _text_base;
  472. _text_rel =src. _text_rel;
  473. _image_base =src. _image_base;
  474. _image_rel =src. _image_rel;
  475. _image_padd =src. _image_padd;
  476. _zoom =src._zoom;
  477. _height_ez =src._height_ez;
  478. _cur_changed_user =src._cur_changed_user;
  479. _cur_changed =src._cur_changed;
  480. _sel_changed_user =src._sel_changed_user;
  481. _sel_changed =src._sel_changed;
  482. _sel_changing_user=src._sel_changing_user;
  483. _sel_changing =src._sel_changing;
  484. setColumns(src._columns.data(), src.columns(), src.columnsHidden()); // do this as last because it relies on other parameters
  485. }
  486. }
  487. return T;
  488. }
  489. /******************************************************************************/
  490. _List& _List::setColumns(C ListColumn *column, Int columns, Bool columns_hidden)
  491. {
  492. T. draw_column =-1;
  493. T._columns_hidden=columns_hidden;
  494. T._columns.clear().setNum(columns); FREPAO(T._columns).create(column[i], T);
  495. parentClientRectChanged(null, null);
  496. return T;
  497. }
  498. /******************************************************************************/
  499. _List& _List::_setData(Ptr data, Int elms, Int elm_size, C MemPtr<Bool> &visible, Bool keep_cur)
  500. {
  501. T._data =data;
  502. T._memb =null;
  503. T._memx =null;
  504. T._meml =null;
  505. T._map =null;
  506. T._node =null;
  507. T._elm_size=elm_size;
  508. init(elms, visible, keep_cur);
  509. return T;
  510. }
  511. _List& _List::setData(_Memc &memc, C MemPtr<Bool> &visible, Bool keep_cur) {return _setData(memc.data(), memc.elms(), memc.elmSize(), visible, keep_cur);}
  512. _List& _List::setData(_Memb &memb, C MemPtr<Bool> &visible, Bool keep_cur)
  513. {
  514. T._data = null;
  515. T._memb =&memb;
  516. T._memx = null;
  517. T._meml = null;
  518. T._map = null;
  519. T._node = null;
  520. T._elm_size= memb.elmSize();
  521. init(memb.elms(), visible, keep_cur);
  522. return T;
  523. }
  524. _List& _List::setData(_Memx &memx, C MemPtr<Bool> &visible, Bool keep_cur)
  525. {
  526. T._data = null;
  527. T._memb = null;
  528. T._memx =&memx;
  529. T._meml = null;
  530. T._map = null;
  531. T._node = null;
  532. T._elm_size= memx.elmSize();
  533. init(memx.elms(), visible, keep_cur);
  534. return T;
  535. }
  536. _List& _List::setData(_Meml &meml, C MemPtr<Bool> &visible, Bool keep_cur)
  537. {
  538. T._data = null;
  539. T._memb = null;
  540. T._memx = null;
  541. T._meml =&meml;
  542. T._map = null;
  543. T._node = null;
  544. T._elm_size= meml.elmSize();
  545. init(meml.elms(), visible, keep_cur);
  546. return T;
  547. }
  548. _List& _List::setData(_Map &map, C MemPtr<Bool> &visible, Bool keep_cur)
  549. {
  550. T._data = null;
  551. T._memb = null;
  552. T._memx = null;
  553. T._meml = null;
  554. T._map =&map ;
  555. T._node = null;
  556. T._elm_size= map.dataSize();
  557. init(map.elms(), visible, keep_cur);
  558. return T;
  559. }
  560. _List& _List::_setData(_Memx &node, Int children_offset, C MemPtr<Bool> &visible, Bool keep_cur)
  561. {
  562. T._data = null;
  563. T._memb = null;
  564. T._memx = null;
  565. T._meml = null;
  566. T._map = null;
  567. T._node =&node;
  568. T._elm_size= node.elmSize();
  569. T._children_offset=children_offset;
  570. init(NodeElms(node, children_offset), visible, keep_cur);
  571. return T;
  572. }
  573. /******************************************************************************/
  574. void _List::init(Int elms, C MemPtr<Bool> &visible, Bool keep_cur)
  575. {
  576. //Int vis_cur=cur; // remember visible cursor, in case we want to keep cursor as visible (not absolute)
  577. T._total_elms =elms;
  578. T._visible_elms=(visible ? CountIs(visible) : elms);
  579. cur=visToAbs(cur); // convert to abs index because we're about to reset vis<->abs remap
  580. Free(_abs_to_data);
  581. Free(_vis_to_data);
  582. Free(_abs_to_vis ); Alloc(_abs_to_vis, totalElms()); REP(totalElms())_abs_to_vis[i]=-1;
  583. Free(_vis_to_abs ); Alloc(_vis_to_abs, visibleElms()); Int v=0; FREPD(e, elms)if(!visible || ElmIs(visible, e)){_abs_to_vis[e]=v; _vis_to_abs[v]=e; v++;}
  584. Free(_rects );
  585. if(_meml)
  586. {
  587. Alloc(_abs_to_data, totalElms());
  588. Alloc(_vis_to_data, visibleElms());
  589. Int e=0, v=0; MFREP(*_meml){_abs_to_data[e]=i->data(); if(!visible || ElmIs(visible, e))_vis_to_data[v++]=i->data(); e++;}
  590. }
  591. if(_node)
  592. {
  593. Alloc(_abs_to_data, totalElms());
  594. Alloc(_vis_to_data, visibleElms());
  595. Ptr *etd=_abs_to_data, *otd=_vis_to_data; // use copied pointers, because below variables are passed as references and increased inside
  596. Int index=0; NodeVisible(*_node, _children_offset, etd, otd, visible, index);
  597. }
  598. cur=absToVis(cur); // convert back to vis index
  599. sort ();
  600. parentClientRectChanged(null, null);
  601. if(cur_mode==LCM_MOUSE && !_kb_action && Gui.ms()==this)lit=cur=screenToVis(Ms.pos());else
  602. if(!keep_cur ) cur=-1;
  603. if(!keep_cur)sel.clear();else REPA(sel)if(sel[i]>=totalElms())sel.remove(i);
  604. }
  605. /******************************************************************************/
  606. void _List::setRects()
  607. {
  608. Vec2 size=0;
  609. // process columns
  610. FREPA(_columns) // set all columns so we can access their horizontal offsets properly
  611. {
  612. ListColumn &lc=_columns[i];
  613. Flt next=size.x; if(_columns[i].visible())next+=columnWidthActual(i);
  614. lc.rect(Rect(size.x, -columnHeight(), next, 0));
  615. size.x=next;
  616. }
  617. // process children objects
  618. Flt children_width=0; REPA(_children)
  619. {
  620. Children::Child &child=_children[i];
  621. if(child.abs_col.y>=_columns.elms() && absToVis(child.abs_col.x)>=0)
  622. if(GuiObj *go=child.go)if(go->visible())MAX(children_width, GuiMaxX(go->rect()));
  623. }
  624. size.x+=children_width;
  625. // process elements
  626. if(drawMode()==LDM_LIST)
  627. {
  628. size.y=visibleElms()*_height_ez;
  629. }else
  630. if(drawMode()==LDM_RECTS)
  631. {
  632. if(!_rects)Alloc(_rects, visibleElms());
  633. Bool type_new_line=(FlagTest(flag, LIST_TYPE_SORT) && FlagTest(flag, LIST_TYPE_LINE) && _type_offset>=0);
  634. UInt type_prev =0,
  635. type_cur =0;
  636. Flt x=0, y=0, W=0, H=0;
  637. Vec2 max_size=size;
  638. if(parent() && parent()->type()==GO_REGION)
  639. {
  640. Region &region=parent()->asRegion();
  641. MAX(max_size.x, region.rect().w()-region.slidebarSize());
  642. MAX(max_size.y, region.rect().h()-region.slidebarSize());
  643. }
  644. max_size+=EPS;
  645. FREPA(T)
  646. {
  647. Flt w=0, h=0;
  648. if(Ptr data=visToData(i))
  649. {
  650. w=imageSizeBase().x+_image_padd.w();
  651. h=imageSizeBase().y+_image_padd.h();
  652. if(InRange(draw_column, _columns))if(Image *image=_columns[draw_column].md.asImage(data))
  653. {
  654. w+=image->w()*imageSizeRel();
  655. h+=image->h()*imageSizeRel();
  656. }
  657. w*=zoom();
  658. h*=zoom();
  659. if(type_new_line)type_cur=*(UInt*)((Byte*)data+_type_offset);
  660. if(_horizontal)
  661. {
  662. if(y && ((type_new_line && type_cur!=type_prev) || -y+h>max_size.y)){y=0; x+=W; W=0;}
  663. MAX(W, w);
  664. }else
  665. {
  666. if(x && ((type_new_line && type_cur!=type_prev) || x+w>max_size.x)){x=0; y-=H; H=0;}
  667. MAX(H, h);
  668. }
  669. type_prev=type_cur;
  670. }
  671. Flt max_x=x+w,
  672. min_y=y-h;
  673. _rects[i].set(x, min_y, max_x, y);
  674. MAX(size.x, max_x);
  675. MAX(size.y, -min_y);
  676. if(_horizontal)y-=h;else x+=w;
  677. }
  678. }
  679. if(columnsVisible())size.y+=columnHeight();
  680. size+=padding;
  681. T.size(size);
  682. if(_parent)switch(_parent->type())
  683. {
  684. /*case GO_REGION:
  685. {
  686. Region &region=_parent->asRegion();
  687. Flt add=-_height_ez*0.5f;
  688. region.slidebar[0]._scroll_add=add;
  689. region.slidebar[1]._scroll_add=add;
  690. }break;*/
  691. case GO_MENU:
  692. {
  693. _parent->asMenu().clientSize(size);
  694. }break;
  695. }
  696. }
  697. void _List::parentClientRectChanged(C Rect *old_client, C Rect *new_client) {setRects();}
  698. /******************************************************************************/
  699. _List& _List::skin(C GuiSkinPtr &skin)
  700. {
  701. _skin=skin;
  702. REPAO(_columns).skin=skin;
  703. return T;
  704. }
  705. TextStyle* _List::getTextStyle()C
  706. {
  707. if(GuiSkin *skin=getSkin())return skin->list.text_style();
  708. return null;
  709. }
  710. Flt _List::textSizeActual()C {return _height_ez*textSizeRel()+textSizeBase();}
  711. _List& _List::columnsHidden( Bool hidden ) { if(T._columns_hidden!=hidden ){T._columns_hidden=hidden ; setRects();} return T;}
  712. _List& _List:: columnHeight( Flt height ) {MAX(height, 0); if(T. _column_height!=height ){T. _column_height=height ; setRects();} return T;}
  713. _List& _List:: elmHeight( Flt height ) {MAX(height, 0); if(T. _elm_height!=height ){T. _elm_height=height ; _height_ez=_elm_height*_zoom; setRects();} return T;}
  714. _List& _List:: textSize( Flt base, Flt relative ) {MAX(base, 0); MAX(relative, 0); if(T. _text_base!=base || T. _text_rel!=relative ){T. _text_base=base ; T. _text_rel=relative; setRects();} return T;}
  715. _List& _List:: imageSize(C Vec2 &base, Flt relative, C Rect &padding) {Vec2 b=Max(base, Vec2(0)); MAX(relative, 0); if(T._image_base!=base || T._image_rel!=relative || T._image_padd!=padding){T. _image_base=base ; T._image_rel=relative; T._image_padd=padding; setRects();} return T;}
  716. _List& _List:: zoom( Flt zoom ) {Clamp(zoom, zoom_min, zoom_max); if(T._zoom!=zoom ){T. _zoom=zoom ; _height_ez=_elm_height*_zoom; setRects();} return T;}
  717. _List& _List:: drawMode( LIST_DRAW_MODE mode ) { if(T._draw_mode!=mode ){T. _draw_mode=mode ; setRects();} return T;}
  718. _List& _List:: horizontal( Bool horizontal ) { if(T._horizontal!=horizontal ){T. _horizontal=horizontal; setRects();} return T;}
  719. _List& _List:: vertical( Bool vertical ) {return horizontal(!vertical);}
  720. /******************************************************************************/
  721. Ptr _List::visToData(Int visible)C
  722. {
  723. if(InRange(visible, visibleElms()))
  724. {
  725. if(_vis_to_data)return _vis_to_data[visible];
  726. return absToData(visToAbs(visible));
  727. }
  728. return null;
  729. }
  730. Int _List::visToAbs(Int visible)C
  731. {
  732. if(InRange(visible, visibleElms()) && _vis_to_abs)return _vis_to_abs[visible];
  733. return -1;
  734. }
  735. Int _List::absToVis(Int absolute)C
  736. {
  737. if(InRange(absolute, totalElms()) && _abs_to_vis)return _abs_to_vis[absolute];
  738. return -1;
  739. }
  740. Ptr _List::absToData(Int absolute)C
  741. {
  742. if(InRange(absolute, totalElms()))
  743. {
  744. if(_abs_to_data)return _abs_to_data[absolute];
  745. if(_data)return (Byte*)_data + absolute*_elm_size;
  746. if(_memb)return (*_memb)[absolute];
  747. if(_memx)return (*_memx)[absolute];
  748. if(_meml)return (*_meml)[absolute];
  749. if(_map )return (*_map )[absolute];
  750. if(_node)return NodeAbsToData(*_node, _children_offset, absolute);
  751. }
  752. return null;
  753. }
  754. Int _List::dataToVis(CPtr data)C
  755. {
  756. if(data)REP(visibleElms())if(visToData(i)==data)return i;
  757. return -1;
  758. }
  759. Int _List::dataToAbs(CPtr data)C
  760. {
  761. if(data)REP(totalElms())if(absToData(i)==data)return i;
  762. return -1;
  763. }
  764. /******************************************************************************/
  765. static Int CompareRectX(C Rect &rect, C Flt &x) {return Compare(rect.min.x, x);}
  766. static Int CompareRectY(C Rect &rect, C Flt &y) {return Compare(y, rect.max.y);}
  767. Int _List::localToVirtualX(Flt local_x)C
  768. {
  769. switch(drawMode())
  770. {
  771. case LDM_RECTS: if(_rects && local_x>=0) // if "local_x<0" then we want -1, so just skip to the end
  772. {
  773. if(local_x>rect().max.x)return elms(); // out of range
  774. Int i; if(!BinarySearch(_rects, elms(), local_x, i, CompareRectX))i--; // if didn't found an exact match, then we need to get the previous one, and not the next one, this will make 'i' always in range (-1..elms-1) inclusive
  775. //if(InRange(i, T)) no need to check this, because we're checking below "InRange(i-1, T)" and in this case if i-1 is in range, then 'i' is in range too
  776. for(; InRange(i-1, T) && Equal(_rects[i-1].min.x, _rects[i].min.x); i--); // if multiple elements have the same rect.min.x then get the first one
  777. return i;
  778. }break;
  779. }
  780. return -1;
  781. }
  782. Int _List::localToVirtualY(Flt local_y)C
  783. {
  784. if(columnsVisible())local_y+=columnHeight();
  785. switch(drawMode())
  786. {
  787. case LDM_LIST : return Floor(-local_y/_height_ez);
  788. case LDM_RECTS: if(_rects && local_y<=0) // if "local_y>0" then we want -1, so just skip to the end
  789. {
  790. if(local_y<rect().min.y)return elms(); // out of range
  791. Int i; if(!BinarySearch(_rects, elms(), local_y, i, CompareRectY))i--; // if didn't found an exact match, then we need to get the previous one, and not the next one, this will make 'i' always in range (-1..elms-1) inclusive
  792. //if(InRange(i, T)) no need to check this, because we're checking below "InRange(i-1, T)" and in this case if i-1 is in range, then 'i' is in range too
  793. for(; InRange(i-1, T) && Equal(_rects[i-1].max.y, _rects[i].max.y); i--); // if multiple elements have the same rect.max.y then get the first one
  794. return i;
  795. }break;
  796. }
  797. return -1;
  798. }
  799. Int _List::localToVis(C Vec2 &local_pos)C
  800. {
  801. if(drawMode()==LDM_RECTS)
  802. {
  803. if(_rects)
  804. {
  805. Vec2 pos=local_pos; if(columnsVisible())pos.y+=columnHeight();
  806. if(pos.y<=0 && pos.y>=rect().min.y
  807. && pos.x>=0 && pos.x<=rect().max.x)
  808. {
  809. if(_horizontal)
  810. {
  811. for(Int i=Max(0, localToVirtualX(local_pos.x)); i<elms(); i++){C Rect &r=_rects[i]; if(r.min.x>pos.x)break; if(Cuts(pos, r))return i;}
  812. }else
  813. {
  814. for(Int i=Max(0, localToVirtualY(local_pos.y)); i<elms(); i++){C Rect &r=_rects[i]; if(r.max.y<pos.y)break; if(Cuts(pos, r))return i;}
  815. }
  816. }
  817. }
  818. return -1;
  819. }
  820. return /*_horizontal ? localToVisX(local_pos.x) : */localToVisY(local_pos.y); // '_horizontal' affects only LDM_RECTS which we've already checked above, other modes always use Y
  821. }
  822. Int _List::localToVisX(Flt local_x)C {Int v=localToVirtualX(local_x); return InRange(v, visibleElms()) ? v : -1;}
  823. Int _List::localToVisY(Flt local_y)C {Int v=localToVirtualY(local_y); return InRange(v, visibleElms()) ? v : -1;}
  824. Int _List::screenToVisX ( Flt x , C GuiPC *gpc)C {return localToVisX (x -(gpc ? gpc->offset.x : screenPos().x));}
  825. Int _List::screenToVisY ( Flt y , C GuiPC *gpc)C {return localToVisY (y -(gpc ? gpc->offset.y : screenPos().y));}
  826. Int _List::screenToVis (C Vec2 &pos, C GuiPC *gpc)C {return localToVis (pos-(gpc ? gpc->offset : screenPos() ));}
  827. Int _List::screenToVirtualY( Flt y , C GuiPC *gpc)C {return localToVirtualY(y -(gpc ? gpc->offset.y : screenPos().y));}
  828. Int _List::screenToColumnX ( Flt x , C GuiPC *gpc)C {return localToColumnX (x -(gpc ? gpc->offset.x : screenPos().x));}
  829. Ptr _List::screenToData( Flt y , C GuiPC *gpc)C {return visToData(screenToVisY(y , gpc));}
  830. Ptr _List::screenToData(C Vec2 &pos, C GuiPC *gpc)C {return visToData(screenToVis (pos, gpc));}
  831. Flt _List::visToLocalY(Int visible)C
  832. {
  833. Flt y=(columnsVisible() ? -columnHeight() : 0);
  834. switch(drawMode())
  835. {
  836. case LDM_LIST : y-=visible*_height_ez; break;
  837. case LDM_RECTS: if(_rects && InRange(visible, visibleElms()))y+=_rects[visible].max.y; break;
  838. }
  839. return y;
  840. }
  841. Vec2 _List::visToLocalPos(Int visible)C
  842. {
  843. Vec2 pos(0, columnsVisible() ? -columnHeight() : 0);
  844. switch(drawMode())
  845. {
  846. case LDM_LIST : pos.y-=visible*_height_ez; break;
  847. case LDM_RECTS: if(_rects && InRange(visible, visibleElms()))pos+=_rects[visible].lu(); break;
  848. }
  849. return pos;
  850. }
  851. Rect _List::visToLocalRect(Int visible)C
  852. {
  853. Vec2 pos(0, columnsVisible() ? -columnHeight() : 0);
  854. switch(drawMode())
  855. {
  856. case LDM_LIST : pos.y-=visible*_height_ez; return Rect_LU(pos, rect().w(), _height_ez);
  857. case LDM_RECTS: if(_rects && InRange(visible, visibleElms()))return _rects[visible]+pos; break;
  858. }
  859. return pos;
  860. }
  861. Vec2 _List::visToScreenPos(Int visible, C GuiPC *gpc)C
  862. {
  863. Vec2 pos=(gpc ? gpc->offset : screenPos()); if(columnsVisible())pos.y-=columnHeight();
  864. switch(drawMode())
  865. {
  866. case LDM_LIST : pos.y-=visible*_height_ez; break;
  867. case LDM_RECTS: if(_rects && InRange(visible, visibleElms()))pos+=_rects[visible].lu(); break;
  868. }
  869. return pos;
  870. }
  871. Rect _List::visToScreenRect(Int visible, C GuiPC *gpc)C
  872. {
  873. Vec2 pos=(gpc ? gpc->offset : screenPos()); if(columnsVisible())pos.y-=columnHeight();
  874. switch(drawMode())
  875. {
  876. case LDM_LIST : pos.y-=visible*_height_ez; return Rect_LU(pos, rect().w(), _height_ez);
  877. case LDM_RECTS: if(_rects && InRange(visible, visibleElms()))return _rects[visible]+pos; break;
  878. }
  879. return pos;
  880. }
  881. /******************************************************************************/
  882. Rect _List::elmsScreenRect(C GuiPC *gpc)C
  883. {
  884. Rect clip =(gpc ? gpc->clip : D.rect());
  885. Rect elms_rect=(gpc ? gpc->client_rect : parent() ? parent()->screenClientRect() : clip); if(columnsVisible())elms_rect.max.y-=columnHeight(); elms_rect&=clip;
  886. return elms_rect;
  887. }
  888. VecI2 _List::visibleElmsOnScreen(C GuiPC *gpc)C
  889. {
  890. if(visibleElms())
  891. if(gpc ? visible() && gpc->visible : visibleFull())
  892. {
  893. Vec2 offset =(gpc ? gpc->offset : screenPos());
  894. Rect elms_rect=elmsScreenRect(gpc);
  895. VecI2 range;
  896. if(_horizontal && drawMode()==LDM_RECTS)
  897. {
  898. range.set(Max(0 , localToVirtualX(elms_rect.min.x-offset.x)), // clip start only using Max to allow start>end (invalid range)
  899. Min(visibleElms()-1, localToVirtualX(elms_rect.max.x-offset.x))); // clip end only using Min to allow start>end (invalid range)
  900. if(drawMode()==LDM_RECTS)for(; InRange(range.y+1, visibleElms()) && _rects[range.y+1].min.x+offset.x<elms_rect.max.x; range.y++);
  901. }else
  902. {
  903. range.set(Max(0 , localToVirtualY(elms_rect.max.y-offset.y)), // clip start only using Max to allow start>end (invalid range)
  904. Min(visibleElms()-1, localToVirtualY(elms_rect.min.y-offset.y))); // clip end only using Min to allow start>end (invalid range)
  905. if(drawMode()==LDM_RECTS)for(; InRange(range.y+1, visibleElms()) && _rects[range.y+1].max.y+offset.y>elms_rect.min.y; range.y++);
  906. }
  907. return range;
  908. }
  909. return VecI2(0, -1);
  910. }
  911. Int _List::pageElms(C GuiPC *gpc)C
  912. {
  913. Int page_elms=1;
  914. if(drawMode()==LDM_LIST)
  915. {
  916. Rect elms_rect=elmsScreenRect(gpc);
  917. MAX(page_elms, Trunc(elms_rect.h()/_height_ez-0.5f));
  918. }
  919. return page_elms;
  920. }
  921. /******************************************************************************/
  922. _List& _List::scrollTo(Int i, Bool immediate, Flt center)
  923. {
  924. Clamp(i, 0, elms());
  925. if(InRange(i, T) && _parent && _parent->type()==GO_REGION)
  926. {
  927. Region &region=_parent->asRegion();
  928. Flt add =(columnsVisible() ? columnHeight() : 0), h=region.clientHeight()-add, e;
  929. center=Sat(center)*0.5f;
  930. switch(drawMode())
  931. {
  932. case LDM_LIST:
  933. {
  934. Flt pos= i*_height_ez;
  935. e =(h-_height_ez)*center;
  936. region.scrollFitY(pos-e, pos+_height_ez+add+e, immediate);
  937. }break;
  938. case LDM_RECTS: if(_rects)
  939. {
  940. C Rect &rect=_rects[i];
  941. if(_horizontal){e=(region.clientWidth()-rect.w())*center; region.scrollFitX( rect.min.x-e, rect.max.x +e, immediate);}
  942. else {e=( h -rect.h())*center; region.scrollFitY(-rect.max.y-e, -rect.min.y+add+e, immediate);}
  943. }break;
  944. }
  945. }
  946. return T;
  947. }
  948. /******************************************************************************/
  949. static struct ListComparer
  950. {
  951. Int elm_size, type_offset;
  952. Ptr *abs_to_data;
  953. Byte *data;
  954. _Memb *memb;
  955. _Memx *memx;
  956. _Map *map;
  957. Bool swap[3], type_sort;
  958. MemberDesc *md[3];
  959. SyncLock lock;
  960. }LCmp;
  961. static inline Int ListCompareData(CPtr d0, CPtr d1, Int i0, Int i1)
  962. {
  963. if(LCmp.type_sort) // sort by type first
  964. {
  965. UInt t0=*(UInt*)((Byte*)d0+LCmp.type_offset),
  966. t1=*(UInt*)((Byte*)d1+LCmp.type_offset);
  967. if(t0<t1)return -1;
  968. if(t0>t1)return +1;
  969. }
  970. if(LCmp.md[0])if(Int c=LCmp.md[0]->compare(d0, d1))return LCmp.swap[0] ? -c : c;
  971. if(LCmp.md[1])if(Int c=LCmp.md[1]->compare(d0, d1))return LCmp.swap[1] ? -c : c;
  972. if(LCmp.md[2])if(Int c=LCmp.md[2]->compare(d0, d1))return LCmp.swap[2] ? -c : c;
  973. //return Compare(d0, d1); // compare by memory address to avoid randomizing element visible order (this can happen because sorting algorithm may not preserve order for equal elements)
  974. return Compare(i0, i1); // compare by index to avoid randomizing element visible order (this can happen because sorting algorithm may not preserve order for equal elements)
  975. }
  976. static Int ListComparePtr(C Int &i0, C Int &i1)
  977. {
  978. Ptr d0=LCmp.abs_to_data[i0],
  979. d1=LCmp.abs_to_data[i1];
  980. return ListCompareData(d0, d1, i0, i1);
  981. }
  982. static Int ListCompareContinuous(C Int &i0, C Int &i1)
  983. {
  984. Ptr d0=LCmp.data+i0*LCmp.elm_size,
  985. d1=LCmp.data+i1*LCmp.elm_size;
  986. return ListCompareData(d0, d1, i0, i1);
  987. }
  988. static Int ListCompareMemb(C Int &i0, C Int &i1)
  989. {
  990. Ptr d0=(*LCmp.memb)[i0],
  991. d1=(*LCmp.memb)[i1];
  992. return ListCompareData(d0, d1, i0, i1);
  993. }
  994. static Int ListCompareMemx(C Int &i0, C Int &i1)
  995. {
  996. Ptr d0=(*LCmp.memx)[i0],
  997. d1=(*LCmp.memx)[i1];
  998. return ListCompareData(d0, d1, i0, i1);
  999. }
  1000. static Int ListCompareMap(C Int &i0, C Int &i1)
  1001. {
  1002. Ptr d0=(*LCmp.map)[i0],
  1003. d1=(*LCmp.map)[i1];
  1004. return ListCompareData(d0, d1, i0, i1);
  1005. }
  1006. void _List::sort()
  1007. {
  1008. Bool type_sort=(FlagTest(flag, LIST_TYPE_SORT) && _type_offset>=0);
  1009. if( type_sort || sort_column[0]>=0 || sort_column[1]>=0 || sort_column[2]>=0)
  1010. {
  1011. cur=visToAbs(cur);
  1012. { // use synchronization lock because we're operating on one global 'LCmp' object, because 'Sort' does not accept user pointer parameters
  1013. SyncLocker locker(LCmp.lock);
  1014. REPA(sort_column)
  1015. {
  1016. Int c=sort_column[i];
  1017. ListColumn *lc=(InRange(c, _columns) ? &_columns[c] : null);
  1018. LCmp.md [i]=(lc ? (lc->sort ? lc->sort : &lc->md) : null);
  1019. LCmp.swap[i]=sort_swap[i];
  1020. }
  1021. LCmp.elm_size =_elm_size;
  1022. LCmp.type_sort = type_sort;
  1023. LCmp.type_offset=_type_offset;
  1024. LCmp.abs_to_data=_abs_to_data;
  1025. LCmp.data =(Byte*)_data;
  1026. LCmp.memb =_memb;
  1027. LCmp.memx =_memx;
  1028. LCmp.map =_map ;
  1029. Int (&compare)(C Int &i0, C Int &i1)=(_abs_to_data ? ListComparePtr : _data ? ListCompareContinuous : _memb ? ListCompareMemb : _memx ? ListCompareMemx : ListCompareMap);
  1030. Sort(_vis_to_abs, visibleElms(), compare);
  1031. }
  1032. REPD(v, visibleElms())
  1033. {
  1034. Int absolute=_vis_to_abs [v ];
  1035. _abs_to_vis [absolute]=v;
  1036. if(_vis_to_data)_vis_to_data[v ]=absToData(absolute);
  1037. }
  1038. cur=absToVis(cur);
  1039. }
  1040. }
  1041. _List& _List::sort(Int column, Int swap)
  1042. {
  1043. if(InRange(column, _columns))
  1044. {
  1045. // set sorting parameters
  1046. if(column==sort_column[0])
  1047. {
  1048. Bool new_swap=((swap<0) ? sort_swap[0]^1 : (swap!=0)); // if swap is not specified (<0) then toggle current mode, otherwise set as specified
  1049. if( sort_swap[0]==new_swap)return T; // if swap is the same, then do nothing
  1050. sort_swap[0] =new_swap;
  1051. }else
  1052. if(column==sort_column[1])
  1053. {
  1054. Swap(sort_column[0], sort_column[1]);
  1055. Swap(sort_swap [0], sort_swap [1]);
  1056. if(swap>=0)sort_swap[0]=(swap!=0);
  1057. }else
  1058. if(column==sort_column[2])
  1059. {
  1060. if(swap<0)swap=sort_swap[2];
  1061. sort_column[2]=sort_column[1]; sort_swap[2]=sort_swap[1];
  1062. sort_column[1]=sort_column[0]; sort_swap[1]=sort_swap[0];
  1063. sort_column[0]= column ; sort_swap[0]= (swap!=0);
  1064. }else
  1065. {
  1066. sort_column[2]=sort_column[1]; sort_swap[2]=sort_swap[1];
  1067. sort_column[1]=sort_column[0]; sort_swap[1]=sort_swap[0];
  1068. sort_column[0]= column ; sort_swap[0]=((swap<0) ? false : (swap!=0));
  1069. }
  1070. // sort
  1071. if(sorting()) // call after 'sort_column' and 'sort_swap' have been changed, so the callback can verify latest sorting parameters
  1072. {
  1073. sort();
  1074. if(drawMode()==LDM_RECTS)setRects();
  1075. }
  1076. }
  1077. return T;
  1078. }
  1079. LIST_SEL_MODE _List::selMode()C
  1080. {
  1081. if(Kb.alt ())return Kb.shift() ? LSM_EXCLUDE_MULTI : LSM_EXCLUDE;
  1082. if(Kb.shift ())return LSM_INCLUDE;
  1083. if(Kb.ctrlCmd())return LSM_TOGGLE ;
  1084. return sel_mode;
  1085. }
  1086. void _List::childRectChanged(C Rect *old_rect, C Rect *new_rect, GuiObj &child)
  1087. {
  1088. // null rectangle means object is hidden
  1089. if(old_rect || new_rect)
  1090. {
  1091. Flt x=(new_rect ? GuiMaxX(*new_rect) : 0),
  1092. old_x=(old_rect ? GuiMaxX(*old_rect) : 0),
  1093. ofs_x=0; if(_columns.elms())ofs_x+=_columns.last().rect().max.x; ofs_x+=padding.x;
  1094. x+=ofs_x;
  1095. old_x+=ofs_x;
  1096. Int index; if(_children.find(child, index)) // if child belongs to List
  1097. {
  1098. C Children::Child &c=_children[index]; if(c.abs_col.y>=_columns.elms() && absToVis(c.abs_col.x)>=0) // process only if it's attached to the last column
  1099. {
  1100. if(old_x>=rect().w()-EPS && x<rect().w()-EPS)setRects();else // if decreasing then we need to recalculate fully
  1101. if( x>rect().w()+EPS)size(Vec2(x, rect().h()));
  1102. }
  1103. }else // assume that it was removed and belonged to last column
  1104. {
  1105. if(old_x>=rect().w()-EPS && x<rect().w()-EPS)setRects(); // if decreasing then we need to recalculate fully
  1106. }
  1107. }//else having null rectangles means object is hidden and we don't need to do anything
  1108. }
  1109. _List& _List::addChild(GuiObj &child, Int abs, Int column)
  1110. {
  1111. Bool same_parent=(child.parent()==this);
  1112. if(Children::Child *c=_children.add(child, T)) // add to the end
  1113. {
  1114. if(same_parent && c->abs_col.x==abs && c->abs_col.y==column)return T; // if child had the same parent and we're not changing indexes, then we're done
  1115. c->abs_col.set(abs, column); // setup abs column
  1116. _children.validateLevel(child); // validate after setting abs and column
  1117. if(column>=_columns.elms() && absToVis(abs)>=0 && child.visible())
  1118. {
  1119. Flt width=GuiMaxX(child.rect()); MAX(width, 0); if(_columns.elms())width+=_columns.last().rect().max.x; width+=padding.x;
  1120. if(width>rect().w())size(Vec2(width, rect().h()));
  1121. }
  1122. }
  1123. return T;
  1124. }
  1125. void _List::removeChild(GuiObj &child)
  1126. {
  1127. if(_children.remove(child))childRectChanged(child.visible() ? &child.rect() : null, null, child);
  1128. }
  1129. /******************************************************************************/
  1130. _List& _List::curChanged(void (*func)(Ptr), Ptr user)
  1131. {
  1132. T._cur_changed =func;
  1133. T._cur_changed_user=user;
  1134. return T;
  1135. }
  1136. _List& _List::selChanged(void (*func)(Ptr), Ptr user)
  1137. {
  1138. T._sel_changed =func;
  1139. T._sel_changed_user=user;
  1140. return T;
  1141. }
  1142. _List& _List::selChanging(void (*func)(Ptr), Ptr user)
  1143. {
  1144. T._sel_changing =func;
  1145. T._sel_changing_user=user;
  1146. return T;
  1147. }
  1148. void _List::callCurChanged()
  1149. {
  1150. Gui.addFuncCall(_cur_changed, _cur_changed_user);
  1151. }
  1152. void _List::callSelChanged()
  1153. {
  1154. Gui.addFuncCall(_sel_changed, _sel_changed_user);
  1155. }
  1156. void _List::callSelChanging()
  1157. {
  1158. if(_sel_changing)_sel_changing(_sel_changing_user);
  1159. }
  1160. /******************************************************************************/
  1161. _List& _List::clearElmType ( ) { _type_offset=-1 ; return T;}
  1162. _List& _List::clearElmDesc ( ) { _desc_offset=-1 ; return T;}
  1163. _List& _List::clearElmImageColor( ) {_image_color_offset=-1 ; return T;}
  1164. _List& _List::clearElmTextColor ( ) { _text_color_offset=-1 ; return T;}
  1165. _List& _List::clearElmTextShadow( ) {_text_shadow_offset=-1 ; return T;}
  1166. _List& _List::clearElmAlphaMode ( ) { _alpha_mode_offset=-1 ; return T;}
  1167. _List& _List::clearElmOffset ( ) { _offset_offset=-1 ; return T;}
  1168. _List& _List::clearElmGroup ( ) { _group_offset=-1 ; return T;}
  1169. _List& _List:: setElmType (UInt &member) { _type_offset=UInt(UIntPtr(&member)) ; return T;}
  1170. _List& _List:: setElmDesc (CChar* &member) { _desc_offset=UInt(UIntPtr(&member)) ; return T;}
  1171. _List& _List:: setElmDesc (Str &member) { _desc_offset=UInt(UIntPtr(&member))+OFFSET(Str, _d); return T;}
  1172. _List& _List:: setElmImageColor(Color &member) {_image_color_offset=UInt(UIntPtr(&member)) ; return T;}
  1173. _List& _List:: setElmTextColor (Color &member) { _text_color_offset=UInt(UIntPtr(&member)) ; return T;}
  1174. _List& _List:: setElmTextShadow(Byte &member) {_text_shadow_offset=UInt(UIntPtr(&member)) ; return T;}
  1175. _List& _List:: setElmAlphaMode (ALPHA_MODE &member) { _alpha_mode_offset=UInt(UIntPtr(&member)) ; return T;}
  1176. _List& _List:: setElmOffset (Flt &member) { _offset_offset=UInt(UIntPtr(&member)) ; return T;}
  1177. _List& _List:: setElmGroup (Str &member) { _group_offset=UInt(UIntPtr(&member)) ; return T;}
  1178. /******************************************************************************/
  1179. GuiObj* _List::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel)
  1180. {
  1181. if(visible() && gpc.visible && Cuts(pos, gpc.clip))
  1182. {
  1183. if(Kb.ctrlCmd() && (flag&LIST_SCALABLE))mouse_wheel=this;
  1184. GuiPC gpc_col(gpc, T); REP(columns())if(GuiObj *go=column(i).test(gpc_col, pos, mouse_wheel))return go;
  1185. if(_children.children.elms())
  1186. {
  1187. VecI2 visible_range=visibleElmsOnScreen(&gpc); if(visible_range.y>=visible_range.x)
  1188. {
  1189. GuiPC gpc2(gpc, visible(), enabled()); Vec2 offset=gpc2.offset;
  1190. if(columnsVisible())
  1191. {
  1192. Flt max_y=gpc.client_rect.max.y-columnHeight();
  1193. MIN(gpc2.clip.max.y, max_y);
  1194. }
  1195. REPA(_children) // 'test' must be done from the end
  1196. {
  1197. Children::Child &child=_children[i];
  1198. if(SetGPC(T, gpc2, offset, child.abs_col, visible_range))if(GuiObj *go=child.go->test(gpc2, pos, mouse_wheel))return go;
  1199. }
  1200. }
  1201. }
  1202. return this;
  1203. }
  1204. return null;
  1205. }
  1206. /******************************************************************************/
  1207. Bool _List::setSel(Int visible) // returns if selection has changed, this may call ONLY 'selChanging', but NOT 'selChanged', 'curChanged'
  1208. {
  1209. if((flag&LIST_MULTI_SEL) && InRange(visible, visibleElms())) // set only 'visible'
  1210. {
  1211. Int abs=visToAbs(visible);
  1212. if(sel.elms()!=1 || sel[0]!=abs)
  1213. {
  1214. callSelChanging();
  1215. sel.setNum(1)[0]=abs;
  1216. return true;
  1217. }
  1218. }else
  1219. if(sel.elms()) // clear
  1220. {
  1221. callSelChanging();
  1222. sel.clear();
  1223. return true;
  1224. }
  1225. return false;
  1226. }
  1227. _List& _List::setCur(Int i)
  1228. {
  1229. // first set what's changing
  1230. Bool cur_changed=(T.cur!=i),
  1231. sel_changed=setSel(i);
  1232. // make the change
  1233. T.cur=i;
  1234. // call after making changes
  1235. if(cur_changed)callCurChanged();
  1236. if(sel_changed)callSelChanged();
  1237. return T;
  1238. }
  1239. _List& _List::processSel(Int absolute, Int sel_mode) // this may call ONLY 'selChanging', 'selChanged', but NOT 'curChanged'
  1240. {
  1241. if(flag&LIST_MULTI_SEL)
  1242. {
  1243. Bool sel_changed=false;
  1244. if(sel_mode<0)sel_mode=selMode();
  1245. if(!InRange(absolute, totalElms()))absolute=-1;
  1246. if(sel_mode==LSM_SET)
  1247. {
  1248. if(absolute>=0) // set only 'absolute'
  1249. {
  1250. if(sel.elms()!=1 || sel[0]!=absolute){callSelChanging(); sel.setNum(1)[0]=absolute; sel_changed=true;}
  1251. }else
  1252. {
  1253. if(sel.elms()){callSelChanging(); sel.clear(); sel_changed=true;}
  1254. }
  1255. }else
  1256. if(absolute>=0)switch(sel_mode)
  1257. {
  1258. case LSM_TOGGLE : callSelChanging(); sel. toggle(absolute, true); sel_changed=true; break;
  1259. case LSM_INCLUDE : if(_sel_changing && !sel.has(absolute))callSelChanging(); if(sel.include(absolute ) )sel_changed=true; break;
  1260. case LSM_EXCLUDE :
  1261. case LSM_EXCLUDE_MULTI: if(_sel_changing && sel.has(absolute))callSelChanging(); if(sel.exclude(absolute, true) )sel_changed=true; break;
  1262. }
  1263. if(sel_changed)callSelChanged();
  1264. }
  1265. return T;
  1266. }
  1267. Bool _List::setCurEx(Int cur, Int dir, Bool pushed, UInt touch_id) // returns if selection has changed, this may call ONLY 'selChanging', but NOT 'selChanged', 'curChanged'
  1268. {
  1269. Bool sel_changed=false;
  1270. if(flag&LIST_MULTI_SEL)
  1271. {
  1272. if(_group_offset>=0)
  1273. if(Ptr data=visToData(cur))
  1274. if(((Str*)((Byte*)data+_group_offset))->is())goto skip_sel_change; // do not modify 'sel' because we want to click on a group, to open it, to show its elements
  1275. switch(selMode())
  1276. {
  1277. case LSM_SET:
  1278. {
  1279. if(touch_id && !pushed){lit=cur; return false;} // if it's a touch and it's not pushed then set 'lit' only and don't do anything on the cursor and selection
  1280. if(pushed && sel.has(visToAbs(cur))){_tap_vis=cur; _tap_touch_id=touch_id;} // if we're activating using button push, and the element is already selected, then there's possibility we want to drag and drop the elements, so instead of activating that element, we will store its index to temporary member, and activate it only quick release of the button (tap)
  1281. else sel_changed|=setSel(cur);
  1282. }break;
  1283. case LSM_TOGGLE:
  1284. {
  1285. if(!pushed)return false; // don't do anything if it's TOGGLE mode and button was not pushed
  1286. if(InRange(cur, visibleElms())){Int abs=visToAbs(cur); callSelChanging(); sel_changed=true; sel.toggle(abs, true);}
  1287. }break;
  1288. case LSM_INCLUDE:
  1289. {
  1290. if(InRange(cur, visibleElms()))
  1291. {
  1292. if(InRange(T.cur, visibleElms()))
  1293. {
  1294. if(!dir)dir=((cur>T.cur) ? +1 : -1);else Clamp(dir, -1, +1);
  1295. for(Bool called=false; ; T.cur=Mod(T.cur+dir, visibleElms()))
  1296. {
  1297. Int abs=visToAbs(T.cur);
  1298. if(_sel_changing && !called && !sel.has(abs)){called=true; callSelChanging();}
  1299. sel_changed|=sel.include(abs);
  1300. if(T.cur==cur)break;
  1301. }
  1302. }else
  1303. {
  1304. Int abs=visToAbs(cur);
  1305. if(_sel_changing && !sel.has(abs))callSelChanging();
  1306. sel_changed|=sel.include(abs);
  1307. }
  1308. }
  1309. }break;
  1310. case LSM_EXCLUDE:
  1311. {
  1312. if(InRange(cur, visibleElms())){exclude_single: Int abs=visToAbs(cur); if(_sel_changing && sel.has(abs))callSelChanging(); sel_changed|=sel.exclude(abs, true);}
  1313. }break;
  1314. case LSM_EXCLUDE_MULTI:
  1315. {
  1316. if(InRange(cur, visibleElms()))
  1317. {
  1318. if(InRange(T.cur, visibleElms()))
  1319. {
  1320. if(!dir)dir=((cur>T.cur) ? +1 : -1);else Clamp(dir, -1, +1);
  1321. for(Bool called=false; ; T.cur=Mod(T.cur+dir, visibleElms()))
  1322. {
  1323. Int abs=visToAbs(T.cur);
  1324. if(_sel_changing && !called && sel.has(abs)){called=true; callSelChanging();}
  1325. sel_changed|=sel.exclude(abs, true);
  1326. if(T.cur==cur)break;
  1327. }
  1328. }else goto exclude_single;
  1329. }
  1330. }break;
  1331. }
  1332. }else sel_changed|=setSel(-1);
  1333. skip_sel_change:
  1334. T.cur=cur;
  1335. return sel_changed;
  1336. }
  1337. void _List::update(C GuiPC &gpc)
  1338. {
  1339. lit=-1;
  1340. if(disabled() || gpc.disabled())
  1341. {
  1342. if(cur_mode==LCM_MOUSE)setCur(-1);
  1343. }else
  1344. if(visible() && gpc.visible && visibleElms())
  1345. {
  1346. if(Gui.wheel()==this)
  1347. {
  1348. if(Kb.ctrlCmd() && Ms.wheel() && (flag&LIST_SCALABLE))
  1349. {
  1350. zoom(zoom()*ScaleFactor(0.2f*Ms.wheel()));
  1351. Ms.eatWheel();
  1352. }
  1353. }
  1354. Bool sel_changed=false;
  1355. Int cur_prev=cur;
  1356. _kb_action&=!Ms._action; // disable keyboard action if there's a mouse action
  1357. // mouse highlight
  1358. if(Gui.msLit()==this &&
  1359. (Gui.ms()==this || Gui.dragging()) // allow highlight when mouse original focus is at the list or when dragging
  1360. )lit=screenToVis(Ms.pos(), &gpc);
  1361. // touch
  1362. REPA(Touches)
  1363. {
  1364. Touch &touch=Touches[i]; if(touch.guiObj()==this && (touch.state()&(BS_PUSHED|BS_ON|BS_RELEASED))) // process cursor for (pushed to set) and (on+released to skip from mouse), release needed so 'Menu' detection can work
  1365. {
  1366. GuiObj *container=this; if(parent()->is(GO_MENU))container=parent(); if(container->contains(Gui.objAtPos(touch.pos())))
  1367. {
  1368. if( touch.pd() // set cursor only when pushed
  1369. || !touch.scrolling() // or not scrolling, to avoid situations when scrolling could possibly change the cursor, but allow for 'Menu'
  1370. )sel_changed|=setCurEx(screenToVis(touch.pos(), &gpc), 0, touch.pd(), touch.id());
  1371. goto skip_mt; // skip processing cursor by mouse touches
  1372. }
  1373. }
  1374. }
  1375. // mouse cursor
  1376. if(Gui.ms()==this)
  1377. {
  1378. if(Ms.bp(0) || (cur_mode==LCM_MOUSE && !_kb_action))sel_changed|=setCurEx(lit, 0, Ms.bp(0));
  1379. }else
  1380. {
  1381. if(cur_mode==LCM_MOUSE && !_kb_action)sel_changed|=setSel(cur=-1);
  1382. }
  1383. skip_mt:
  1384. Int cur_prev2=cur;
  1385. // select on tap
  1386. if(_tap_vis>=0)
  1387. {
  1388. Touch *touch=FindTouch(_tap_touch_id);
  1389. if(_tap_touch_id ? (!touch || touch->rs()) : !Ms.b(0))
  1390. {
  1391. if(InRange(_tap_vis, T))if(_tap_touch_id ? (touch && touch->tapped()) : Ms.tapped(0))sel_changed|=setCurEx(_tap_vis);
  1392. _tap_vis=-1;
  1393. }
  1394. }
  1395. // keyboard
  1396. if(Gui.kb()==this)
  1397. {
  1398. if(Kb.k.ctrlCmd())
  1399. {
  1400. // select all (Ctrl+A)
  1401. if(Kb.k(KB_A) && !Kb.k.shift() && !Kb.k.alt() && (flag&LIST_MULTI_SEL))
  1402. {
  1403. if(Kb.k.first())
  1404. {
  1405. REPA(sel )if( absToVis(sel[i])<0 )goto different; // if element is selected but not visible
  1406. sel.sort(Compare); REP (visibleElms())if(!sel.binaryHas(visToAbs( i ), Compare))goto different; // if element is visible but not selected (sort first)
  1407. if(0)
  1408. {
  1409. different:
  1410. callSelChanging();
  1411. sel.clear(); FREP(visibleElms())sel.add(visToAbs(i));
  1412. sel_changed=true;
  1413. }
  1414. }
  1415. Kb.eatKey();
  1416. }
  1417. }else
  1418. {
  1419. if(Kb.k(KB_HOME)){_kb_action=true; sel_changed|=setCurEx( 0); Kb.eatKey();}
  1420. if(Kb.k(KB_END )){_kb_action=true; sel_changed|=setCurEx(visibleElms()-1); Kb.eatKey();}
  1421. if(Kb.k(KB_PGUP) || Kb.k(KB_PGDN))
  1422. {
  1423. _kb_action=true;
  1424. if(drawMode()==LDM_LIST)
  1425. {
  1426. if(cur<0)cur=0;
  1427. Int page_elms=pageElms(&gpc), dir, new_cur;
  1428. if(Kb.k(KB_PGUP)){new_cur=cur-page_elms; dir=-1;}
  1429. else {new_cur=cur+page_elms; dir=+1;}
  1430. Kb.eatKey();
  1431. sel_changed|=setCurEx((flag&LIST_ROLLABLE) ? Mod(new_cur, visibleElms()) : Mid(new_cur, 0, visibleElms()-1), dir);
  1432. }
  1433. }
  1434. if(drawMode()==LDM_LIST)
  1435. {
  1436. if(Kb.k(KB_UP )){_kb_action=true; sel_changed|=setCurEx((flag&LIST_ROLLABLE) ? ((cur<0) ? visibleElms()-1 : Mod(cur-1, visibleElms())) : Max(0 , cur-1), -1);}
  1437. if(Kb.k(KB_DOWN)){_kb_action=true; sel_changed|=setCurEx((flag&LIST_ROLLABLE) ? Mod(cur+1, visibleElms()) : Min(visibleElms()-1, cur+1), +1);}
  1438. }else
  1439. if(drawMode()==LDM_RECTS)
  1440. {
  1441. if(Kb.k(KB_LEFT )){_kb_action=true; sel_changed|=setCurEx(Max(0 , cur-1));}
  1442. if(Kb.k(KB_RIGHT)){_kb_action=true; sel_changed|=setCurEx(Min(visibleElms()-1, cur+1));}
  1443. }
  1444. }
  1445. // smooth scroll
  1446. if(Kb.ctrlCmd() && _parent && _parent->type()==GO_REGION)
  1447. {
  1448. Region &region=_parent->asRegion();
  1449. if(Kb.b(KB_UP )){Kb.eat(KB_UP ); region.slidebar[1].button[1].push();}
  1450. if(Kb.b(KB_DOWN )){Kb.eat(KB_DOWN ); region.slidebar[1].button[2].push();}
  1451. if(Kb.b(KB_LEFT )){Kb.eat(KB_LEFT ); region.slidebar[0].button[1].push();}
  1452. if(Kb.b(KB_RIGHT)){Kb.eat(KB_RIGHT); region.slidebar[0].button[2].push();}
  1453. }
  1454. // quick search
  1455. if(flag&LIST_SEARCHABLE)
  1456. {
  1457. if(Kb.k.c && !Kb.k.ctrl() && !Kb.k.lalt() && !Kb.k.win())
  1458. {
  1459. _kb_action=true;
  1460. for(; Kb.k.c; Kb.nextKey())if(_search_i<Elms(_search)-1)
  1461. {
  1462. _search[ _search_i]=Kb.k.c;
  1463. _search[++_search_i]=0;
  1464. }
  1465. FREPA(_columns)
  1466. {
  1467. ListColumn &lc=_columns[i];
  1468. if(DataIsText(lc.md.type))
  1469. {
  1470. FREPA(T)if(Ptr data=visToData(i))if(Starts(lc.md.asText(data, lc.precision), _search)){sel_changed|=setSel(cur=i); break;}
  1471. break;
  1472. }
  1473. }
  1474. _search_t=0.9f;
  1475. }else
  1476. if((_search_t-=Time.ad())<=0
  1477. || (Kb.k.k && Kb.k.k!=KB_SHIFT && Kb.k.k!=KB_LSHIFT && Kb.k.k!=KB_RSHIFT))_search[_search_i=0]=0; // if time has passed, or function key was pressed (like arrow up/down or enter, but not shift)
  1478. }
  1479. }
  1480. if(cur!=cur_prev)
  1481. {
  1482. if((cur_prev2!=cur || cur_mode!=LCM_MOUSE) && cur>=0)scrollTo(cur, _search_i!=0, _search_i!=0); // scroll immediately if cursor was changed by "search"
  1483. callCurChanged();
  1484. }
  1485. if(sel_changed)callSelChanged();
  1486. // update children
  1487. GuiPC gpc_col(gpc, T); REPAO(_columns).update(gpc_col);
  1488. GuiPC gpc2(gpc, visible(), enabled()); _children.update(gpc2);
  1489. }
  1490. }
  1491. /******************************************************************************/
  1492. void _List::draw(C GuiPC &gpc)
  1493. {
  1494. if(visible() && gpc.visible)
  1495. {
  1496. GuiSkin * skin=getSkin();
  1497. GuiSkin::List *list_skin=(skin ? &skin->list : null);
  1498. // background
  1499. Vec2 _offset=gpc.offset , elms_offset=_offset;
  1500. Rect _rect =gpc.client_rect, elms_rect =_rect ;
  1501. if(columnsVisible())
  1502. {
  1503. elms_offset. y-=columnHeight();
  1504. elms_rect .max.y-=columnHeight();
  1505. }
  1506. // elements
  1507. if(visibleElms())
  1508. {
  1509. Bool show_sel=(list_skin && list_skin->selection_color.a),
  1510. show_cur=(((cur_mode==LCM_DEFAULT) ? Gui.kb()==this : true) && InRange(cur, T) && list_skin && list_skin->cursor_color.a),
  1511. show_lit=(InRange(lit, T) && list_skin && list_skin->highlight_color.a);
  1512. if(show_lit && show_cur && lit==cur) // if 'lit' is the same as 'cur'
  1513. show_lit=(Gui.dragging() || !Ms.b(0)); // disable highlight when clicking but always allow when dragging
  1514. elms_rect&=gpc.clip;
  1515. D.clip(elms_rect);
  1516. if(drawMode()==LDM_RECTS)
  1517. {
  1518. if(_rects)
  1519. {
  1520. Int pos=Max(0, _horizontal ? screenToVisX(elms_rect.min.x, &gpc) : screenToVisY(elms_rect.max.y, &gpc));
  1521. // selection
  1522. if(show_sel && sel.elms())
  1523. {
  1524. REPA(sel)
  1525. {
  1526. Int vis=absToVis(sel[i]); if(/*vis>=0 && */vis>=pos/* && vis<end*/) // no need to test for >=0 because 'pos' is always >=0, we don't test for 'end' because we don't know it yet
  1527. {
  1528. Rect r=_rects[vis]+elms_offset; if(_horizontal ? r.min.x<=elms_rect.max.x : r.max.y>=elms_rect.min.y)
  1529. {
  1530. if(list_skin->selection)list_skin->selection->draw(list_skin->selection_color, TRANSPARENT, r);
  1531. else r.draw(list_skin->selection_color);
  1532. }
  1533. }
  1534. }
  1535. }
  1536. // draw elements
  1537. ALPHA_MODE alpha=D.alpha();
  1538. Color color=WHITE, color_add=TRANSPARENT;
  1539. for(; pos<elms(); pos++)
  1540. if(Ptr data=visToData(pos))
  1541. {
  1542. Rect r=_rects[pos]+elms_offset; if(_horizontal ? r.min.x>elms_rect.max.x : r.max.y<elms_rect.min.y)break;
  1543. if(show_lit && lit==pos)
  1544. {
  1545. D.alpha(alpha);
  1546. if(list_skin->highlight)list_skin->highlight->draw(list_skin->highlight_color, TRANSPARENT, r);
  1547. else r.draw(list_skin->highlight_color);
  1548. }
  1549. if(show_cur && cur==pos)
  1550. {
  1551. D.alpha(alpha);
  1552. if(flag&LIST_MULTI_SEL)
  1553. {
  1554. Color c=list_skin->cursor_color; c.a=Min(255, c.a*2); // since we're drawing only borders then increase the alpha
  1555. if(list_skin->cursor)list_skin->cursor->drawBorders(c, TRANSPARENT, r);
  1556. else r.draw (c, false);
  1557. }else
  1558. {
  1559. if(list_skin->cursor)list_skin->cursor->draw(list_skin->cursor_color, TRANSPARENT, r);
  1560. else r.draw(list_skin->cursor_color);
  1561. }
  1562. }
  1563. if(InRange(draw_column, _columns))if(Image *image=_columns[draw_column].md.asImage(data))
  1564. {
  1565. ALPHA_MODE alpha=image_alpha;
  1566. if( _alpha_mode_offset>=0)alpha=*(ALPHA_MODE*)((Byte*)data+ _alpha_mode_offset); D.alpha(alpha);
  1567. if(_image_color_offset>=0)color=*(Color *)((Byte*)data+_image_color_offset);
  1568. r.min-=_image_padd.min*zoom();
  1569. r.max-=_image_padd.max*zoom();
  1570. image->drawFit(color, color_add, r);
  1571. }
  1572. }
  1573. D.alpha(alpha); // restore alpha
  1574. }
  1575. }else
  1576. {
  1577. Int pos=Max( 0, screenToVirtualY(elms_rect.max.y, &gpc)), // clip start only using Max to allow start>end (invalid range)
  1578. end=Min(elms()-1, screenToVirtualY(elms_rect.min.y, &gpc)); // clip end only using Min to allow start>end (invalid range)
  1579. // highlight
  1580. if(columnsVisible() && list_skin && list_skin->highlight_color.a)REPA(_columns)
  1581. {
  1582. ListColumn &lc=column(i); if(lc.lit())
  1583. {
  1584. Rect r(lc.rect().min.x+_offset.x, _rect.min.y, lc.rect().max.x+_offset.x, _rect.max.y);
  1585. Color color=ColorAlpha(list_skin->highlight_color, lc.lit());
  1586. if(list_skin->highlight)list_skin->highlight->draw(color, TRANSPARENT, r);else r.draw(color);
  1587. }
  1588. }
  1589. if(show_sel && sel.elms())
  1590. {
  1591. Rect r=_rect;
  1592. REPA(sel)
  1593. {
  1594. Int vis=absToVis(sel[i]);
  1595. if(/*vis>=0 && */vis>=pos && vis<=end) // no need to test for >=0 because 'pos' is always >=0
  1596. {
  1597. r.max.y=elms_offset.y-vis*_height_ez;
  1598. r.min.y=r.max.y - _height_ez;
  1599. if(list_skin->selection)list_skin->selection->draw(list_skin->selection_color, TRANSPARENT, r);
  1600. else r.draw(list_skin->selection_color);
  1601. }
  1602. }
  1603. }
  1604. if(show_lit)
  1605. {
  1606. Rect r(_rect.min.x, elms_offset.y-(lit+1)*_height_ez, _rect.max.x, elms_offset.y-lit*_height_ez);
  1607. if(list_skin->highlight)list_skin->highlight->draw(list_skin->highlight_color, TRANSPARENT, r);
  1608. else r.draw(list_skin->highlight_color);
  1609. }
  1610. if(show_cur)
  1611. {
  1612. Rect r(_rect.min.x, elms_offset.y-(cur+1)*_height_ez, _rect.max.x, elms_offset.y-cur*_height_ez);
  1613. if(flag&LIST_MULTI_SEL)
  1614. {
  1615. Color c=list_skin->cursor_color; c.a=Min(255, c.a*2); // since we're drawing only borders then increase the alpha
  1616. if(list_skin->cursor)list_skin->cursor->drawBorders(c, TRANSPARENT, r);
  1617. else r.draw (c, false);
  1618. }else
  1619. {
  1620. if(list_skin->cursor)list_skin->cursor->draw(list_skin->cursor_color, TRANSPARENT, r);
  1621. else r.draw(list_skin->cursor_color);
  1622. }
  1623. }
  1624. // draw elements
  1625. if(skin)
  1626. if(TextStyle *text_style=skin->list.text_style())
  1627. {
  1628. Flt wae =0.01f,
  1629. wae_2=wae*0.5f;
  1630. TextStyleParams ts=*text_style; ts.align.set(1, 0); ts.size=textSizeActual();
  1631. #if DEFAULT_FONT_FROM_CUSTOM_SKIN
  1632. if(!ts.font())ts.font(skin->font()); // adjust font in case it's empty and the custom skin has a different font than the 'Gui.skin'
  1633. #endif
  1634. // if we're drawing text list, and spacing between elements is a fixed number of pixels then flickering can occur if positions will be at 0.5 pixels (0.5, 1.5, 2.5, ..), to prevent that from happening align the vertical position
  1635. elms_offset+=D.alignScreenToPixelOffset(Vec2(elms_offset.x, ts.posY(elms_offset.y-0.5f*_height_ez)));
  1636. for(; pos<=end; pos++)if(Ptr data=visToData(pos))
  1637. {
  1638. ALPHA_MODE image_alpha=T.image_alpha;
  1639. Color color=WHITE, color_add=TRANSPARENT;
  1640. Flt ofs =0,
  1641. x =elms_offset.x,
  1642. y =elms_offset.y-pos *_height_ez, // element position
  1643. yp = pos *_height_ez*D._pixel_size_inv.y; if(Equal(Frac(yp), 0.5f, 0.001f))y+=D._pixel_size_2.y; // vertical offset in pixel coordinates (this can happen very often, text being aligned exactly between 2 pixels)
  1644. Flt yt = y-0.5f*_height_ez; // element text position
  1645. if( _text_color_offset>=0)ts.color =*(Color *)((Byte*)data+ _text_color_offset);
  1646. if(_text_shadow_offset>=0)ts.shadow =*(Byte *)((Byte*)data+_text_shadow_offset);
  1647. if( _alpha_mode_offset>=0)image_alpha=*(ALPHA_MODE*)((Byte*)data+ _alpha_mode_offset);
  1648. if(_image_color_offset>=0)color =*(Color *)((Byte*)data+_image_color_offset);
  1649. if( _offset_offset>=0)ofs =*(Flt *)((Byte*)data+ _offset_offset);
  1650. Str *group=((_group_offset>=0) ? (Str *)((Byte*)data+ _group_offset) : null);
  1651. if(group && group->is())
  1652. {
  1653. ts.align.x=1;
  1654. D.clip(elms_rect);
  1655. D.text(ts, wae+x+ofs, yt, *group);
  1656. }else
  1657. FREPAD(c, _columns)
  1658. {
  1659. ListColumn &lc=_columns[c];
  1660. if(lc.visible() && lc.md.type)
  1661. {
  1662. if(Image *image=lc.md.asImage(data))
  1663. {
  1664. ALPHA_MODE alpha=D.alpha(image_alpha);
  1665. D.clip (elms_rect);
  1666. image->drawFit(color, color_add, Rect_LU(lc.rect().min.x+x+ofs, y, lc.rect().w(), _height_ez));
  1667. D.alpha(alpha);
  1668. }else
  1669. {
  1670. C Str &text=lc.md.asText(data, lc.precision); if(text.is())
  1671. {
  1672. ts.align.x=lc.text_align;
  1673. D.clip(gpc.clip & Rect(lc.rect().min.x+wae_2+x, elms_rect.min.y, lc.rect().max.x-wae_2+x, elms_rect.max.y));
  1674. D.text(ts, Lerp(lc.rect().max.x-wae, lc.rect().min.x+wae, lc.text_align*0.5f+0.5f)+x+ofs, yt, text);
  1675. }
  1676. }
  1677. Color color;
  1678. if(C ImagePtr &image=DataGuiImage(data, lc, color))
  1679. {
  1680. D.clip(gpc.clip & Rect(lc.rect().min.x+wae_2+x, elms_rect.min.y, lc.rect().max.x-wae_2+x, elms_rect.max.y));
  1681. image->drawFit(color, TRANSPARENT, Rect_C(Vec2(lc.rect().centerX()+x+ofs, yt), Vec2(ts.size.avg())));
  1682. }
  1683. if(!DataIsImage(lc.md.type) && _offset_first_column)ofs=0; // reset offset for >=1 columns
  1684. }
  1685. }
  1686. }
  1687. }
  1688. }
  1689. // draw children
  1690. if(_children.children.elms())
  1691. {
  1692. VecI2 visible_range=visibleElmsOnScreen(&gpc); if(visible_range.y>=visible_range.x)
  1693. {
  1694. GuiPC gpc2(gpc, visible(), enabled()); gpc2.clip=elms_rect; Vec2 offset=gpc2.offset;
  1695. FREPA(_children) // 'draw' must be done from the start
  1696. {
  1697. Children::Child &child=_children[i];
  1698. if(SetGPC(T, gpc2, offset, child.abs_col, visible_range))child.go->draw(gpc2);
  1699. }
  1700. }
  1701. }
  1702. }
  1703. // draw columns
  1704. if(columnsVisible())
  1705. {
  1706. ListColumn *resize_col=null;
  1707. GuiPC gpc_col(gpc, T); FREPA(_columns){ListColumn &lc=_columns[i]; lc.draw(gpc_col); if(lc.resizeEdge())resize_col=&lc;}
  1708. if(resize_col && list_skin)if(Image *image=list_skin->resize_column())
  1709. {
  1710. D.clip(gpc.clip);
  1711. Vec2 pos=Ms.pos(), size(MOUSE_IMAGE_SIZE*image->aspect(), MOUSE_IMAGE_SIZE); pos+=Vec2(-size.x, size.y)*0.5f;
  1712. image->draw(Rect_LU(D.screenAlignedToPixel(pos), size));
  1713. }
  1714. }
  1715. }
  1716. }
  1717. /******************************************************************************/
  1718. }
  1719. /******************************************************************************/