Window.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. #define WINDOW_PADD 0.030f
  6. /******************************************************************************/
  7. GuiPC::GuiPC(C GuiPC &old, Window &window)
  8. {
  9. T=old;
  10. visible &=window.visible();
  11. enabled &=window.enabled();
  12. client_rect=window._crect+offset;
  13. offset =client_rect.lu();
  14. GuiSkin *skin=window.getSkin(); if(skin && skin->region.normal && skin->region.normal->pixelBorder())clip&=Rect(client_rect).extend(-D._pixel_size);else clip&=client_rect; // if the panel draws 1-pixel-border then leave it, this is so that Region children will not draw on top of it, because border should look like it's on top
  15. if(1)D.alignScreenToPixel(offset);
  16. }
  17. /******************************************************************************/
  18. static void Hide (Window &window) {window.hide();}
  19. static void FadeOut (Window &window) {window.fadeOut();}
  20. static void Maximize(Window &window) {window.maximize();}
  21. /******************************************************************************/
  22. void Window::zero()
  23. {
  24. flag =WIN_MOVABLE;
  25. _base_level=GBL_WINDOW;
  26. _level =0;
  27. _resize=0;
  28. _crect.zero();
  29. ripple=null;
  30. resize_mask=DIRF_RIGHT|DIRF_LEFT|DIRF_DOWN|DIRF_UP;
  31. _bar_visible=true;
  32. _fade_type =FADE_NONE;
  33. _alpha =_fade_alpha=_final_alpha=0; // keep as zero when deleted
  34. _lit_hover =0;
  35. _lit_focus =0;
  36. }
  37. Window::Window() {zero();}
  38. Window& Window::del()
  39. {
  40. _children.del ();
  41. title .clear();
  42. _skin .clear();
  43. REPA(button)button[i].del();
  44. super::del(); zero(); return T;
  45. }
  46. void Window::setParent()
  47. {
  48. REPAO(button)._parent=this;
  49. }
  50. void Window::setParams()
  51. {
  52. _type=GO_WINDOW;
  53. setParent();
  54. button[0]._sub_type=BUTTON_TYPE_WINDOW_MINIMIZE;
  55. button[1]._sub_type=BUTTON_TYPE_WINDOW_MAXIMIZE;
  56. button[2]._sub_type=BUTTON_TYPE_WINDOW_CLOSE;
  57. button[1].func(Maximize, T);
  58. button[2].func(Gui.window_fade ? FadeOut : Hide, T);
  59. }
  60. Window& Window::create(C Str &title)
  61. {
  62. del();
  63. _visible=true;
  64. _alpha =_fade_alpha=_final_alpha=1;
  65. T.title=title;
  66. // create buttons and setup their initial sizes
  67. Flt h=defaultBarHeight(), w;
  68. button[0].create().hide(); w=h; if(GuiSkin *s=button[0].getSkin())if(PanelImage *pi=s->window.minimize.normal())if(pi->image.is())w*=pi->image.aspect(); button[0].rect(Rect_LU(0, 0, w, h));
  69. button[1].create().hide(); w=h; if(GuiSkin *s=button[1].getSkin())if(PanelImage *pi=s->window.maximize.normal())if(pi->image.is())w*=pi->image.aspect(); button[1].rect(Rect_LU(0, 0, w, h));
  70. button[2].create().hide(); w=h; if(GuiSkin *s=button[2].getSkin())if(PanelImage *pi=s->window.close .normal())if(pi->image.is())w*=pi->image.aspect(); button[2].rect(Rect_LU(0, 0, w, h));
  71. button[0]._focusable=button[1]._focusable=button[2]._focusable=false;
  72. setParams();
  73. return T;
  74. }
  75. Window& Window::create(C Window &src)
  76. {
  77. if(this!=&src)
  78. {
  79. if(!src.is())del();else
  80. {
  81. _children.del();
  82. copyParams(src);
  83. _type =GO_WINDOW;
  84. flag =src. flag;
  85. resize_mask=src. resize_mask;
  86. title =src. title;
  87. ripple =src. ripple;
  88. _bar_visible=src._bar_visible;
  89. _fade_type =src._fade_type;
  90. _resize =src._resize;
  91. _level =src._level;
  92. _alpha =src._alpha;
  93. _fade_alpha =src._fade_alpha;
  94. _final_alpha=src._final_alpha;
  95. _lit_hover =src._lit_hover;
  96. _lit_focus =src._lit_focus;
  97. _crect =src._crect;
  98. _skin =src._skin;
  99. REPAO(T.button).create(src.button[i]);
  100. setParent();
  101. }
  102. }
  103. return T;
  104. }
  105. /******************************************************************************/
  106. void Window:: addChild(GuiObj &child) {_children.add (child, T);}
  107. void Window::removeChild(GuiObj &child) {_children.remove(child );}
  108. /******************************************************************************/
  109. Flt Window:: barHeight ()C {return rect().max.y-clientRect().max.y;}
  110. Flt Window::defaultBarHeight ()C {if(Panel *panel=getNormalPanel()){Rect padd; panel->innerPadding(T.rect(), padd); return padd.max.y;} return 0;}
  111. Flt Window::defaultBarTextWidth()C
  112. {
  113. if(GuiSkin *skin=getSkin())
  114. if(TextStyle *text_style=skin->window.normal_text_style())
  115. {
  116. TextStyleParams ts=*text_style; ts.size=defaultBarHeight()*skin->window.text_size; return ts.textWidth(title);
  117. }
  118. return 0;
  119. }
  120. Flt Window::defaultBarFullWidth()C
  121. {
  122. Flt width=0, bar_height=defaultBarHeight();
  123. if(GuiSkin *skin=getSkin())
  124. {
  125. width+=skin->window.text_padd*2*bar_height;
  126. if(TextStyle *text_style=skin->window.normal_text_style())
  127. {
  128. TextStyleParams ts=*text_style; ts.size=skin->window.text_size*bar_height; width+=ts.textWidth(title);
  129. }
  130. }
  131. REPA(button)if(button[i].visible())width+=button[i].rect().aspect()*bar_height;
  132. return width;
  133. }
  134. Panel* Window::getNormalPanel()C
  135. {
  136. if(GuiSkin *skin=getSkin())return barVisible() ? skin->window.normal() : skin->window.normal_no_bar();
  137. return null;
  138. }
  139. void Window::extendedRect (Rect &rect )C {if(Panel *panel=getNormalPanel())panel->extendedRect (T.rect(), rect);else rect=T.rect();}
  140. void Window::defaultInnerPadding (Rect &padding )C {if(Panel *panel=getNormalPanel())panel->defaultInnerPadding (padding );else padding .zero();}
  141. void Window::defaultInnerPaddingSize(Vec2 &padd_size)C {if(Panel *panel=getNormalPanel())panel->defaultInnerPaddingSize(padd_size );else padd_size.zero();}
  142. Vec2 Window::defaultInnerPaddingSize( )C {Vec2 size; defaultInnerPaddingSize(size); return size;}
  143. /******************************************************************************/
  144. static Rect ResizedRect(C Rect &src, C Rect &dest, UInt mask)
  145. {
  146. C Rect *r[2]={&src, &dest};
  147. Rect resized;
  148. resized.min.x=r[FlagTest(mask, DIRF_LEFT )]->min.x;
  149. resized.min.y=r[FlagTest(mask, DIRF_DOWN )]->min.y;
  150. resized.max.x=r[FlagTest(mask, DIRF_RIGHT)]->max.x;
  151. resized.max.y=r[FlagTest(mask, DIRF_UP )]->max.y;
  152. return resized;
  153. }
  154. static Rect MaximizedRect(C Window &window)
  155. {
  156. if(GuiObj *parent=window.parent())return parent->localClientRect();
  157. return D.rect();
  158. }
  159. Bool Window::maximized()C {return InsideEps(ResizedRect(rect(), MaximizedRect(T), resize_mask), rect());}
  160. Window& Window::maximize () {return rect(ResizedRect(rect(), maximized() ? MaximizedRect(T)*0.5f : MaximizedRect(T), resize_mask));}
  161. /******************************************************************************/
  162. Bool Window::showing()C {return visible() && _fade_type!=FADE_OUT;}
  163. Bool Window::hiding ()C {return hidden () || _fade_type==FADE_OUT;}
  164. Window& Window::fadeIn ()
  165. {
  166. if(hiding())
  167. {
  168. Flt fade_alpha=fadeAlpha(); // remember current alpha because 'show' may change it
  169. show();
  170. if(visible() && Gui.allow_window_fade) // don't modify fade unless we're actually visible ('show' succeeded)
  171. {
  172. _fade_type =FADE_IN;
  173. _fade_alpha=fade_alpha; setFinalAlpha();
  174. }
  175. }
  176. return T;
  177. }
  178. Window& Window::fadeOut()
  179. {
  180. if(visible())
  181. {
  182. if(!Gui.allow_window_fade)hide();else
  183. {
  184. _fade_type=FADE_OUT;
  185. if(contains(Gui.menu()))Gui.menu()->hide();
  186. }
  187. }
  188. return T;
  189. }
  190. Window& Window::fadeToggle()
  191. {
  192. if(hiding())fadeIn ();
  193. else fadeOut();
  194. return T;
  195. }
  196. Window& Window::fade(Bool in)
  197. {
  198. return in ? fadeIn() : fadeOut();
  199. }
  200. void Window::focusToggle()
  201. {
  202. if(hidden() || partiallyOccludedInSameLevel())activate();else hide();
  203. }
  204. void Window::setFinalAlpha( ) {_final_alpha=_alpha*_fade_alpha;}
  205. Window& Window::alpha (Flt alpha ) {_alpha=Sat(alpha); setFinalAlpha(); return T;}
  206. Window& Window::level (Int level ) {if(_level !=level ){_level =level ; validateLevel();} return T;}
  207. Window& Window::barVisible (Bool visible) {if(_bar_visible!=visible){_bar_visible^=1 ; setRect ();} return T;}
  208. //Window& Window::barHeight (Flt height ) {if(_bar_height !=height ){_bar_height =height; setRect ();} return T;}
  209. /******************************************************************************/
  210. Window& Window::skin(C GuiSkinPtr &skin)
  211. {
  212. if(_skin!=skin)
  213. {
  214. _skin=skin;
  215. REPAO(button).skin=skin;
  216. setRect();
  217. }
  218. return T;
  219. }
  220. Window& Window::setTitle(C Str &title) {T.title=title; return T;}
  221. void Window::setButtons()
  222. {
  223. Flt size=barHeight();
  224. Vec2 pos(_crect.max.x, _rect.max.y);
  225. if(GuiSkin *skin=getSkin())
  226. {
  227. Flt scale;
  228. if(Panel *panel=(barVisible() ? skin->window.normal() : skin->window.normal_no_bar()))
  229. if(panel->getSideScale(T.rect(), scale))pos+=skin->window.button_offset*scale;else pos+=skin->window.button_offset;
  230. if(!barVisible())if(Panel *panel=skin->window.normal()) // if the window has no bar, then we need to detect the size from regular window panel with bar
  231. {
  232. Rect padd; panel->innerPadding(T.rect(), padd); MAX(size, padd.max.y);
  233. }
  234. if(size<=0)size=skin->menubar.bar_height; // if the size is unavailable then default to size taken from menu bar
  235. }
  236. if(!Gui.windowButtonsRight())pos.x=_crect.min.x+(_crect.max.x-pos.x); // mirror to the left side
  237. // setup buttons
  238. if(button[2].visible()){Flt w=size; if(GuiSkin *s=button[2].getSkin())if(PanelImage *pi=s->window.close .normal())if(pi->image.is())w*=pi->image.aspect(); Rect rect; if(Gui.windowButtonsRight()){rect.setRU(pos, w, size); pos.x-=w;}else{rect.setLU(pos, w, size); pos.x+=w;} button[2].rect(rect);}
  239. if(button[1].visible()){Flt w=size; if(GuiSkin *s=button[1].getSkin())if(PanelImage *pi=s->window.maximize.normal())if(pi->image.is())w*=pi->image.aspect(); Rect rect; if(Gui.windowButtonsRight()){rect.setRU(pos, w, size); pos.x-=w;}else{rect.setLU(pos, w, size); pos.x+=w;} button[1].rect(rect);}
  240. if(button[0].visible()){Flt w=size; if(GuiSkin *s=button[0].getSkin())if(PanelImage *pi=s->window.minimize.normal())if(pi->image.is())w*=pi->image.aspect(); Rect rect; if(Gui.windowButtonsRight()){rect.setRU(pos, w, size); pos.x-=w;}else{rect.setLU(pos, w, size); pos.x+=w;} button[0].rect(rect);}
  241. }
  242. void Window::setRect()
  243. {
  244. Rect old_client=localClientRect();
  245. // first calculate client rectangle
  246. T._crect=rect();
  247. if(GuiSkin *skin=getSkin())
  248. if(Panel *panel=panel=(barVisible() ? skin->window.normal() : skin->window.normal_no_bar()))
  249. {
  250. Rect padd; panel->innerPadding(T.rect(), padd);
  251. _crect.min+=padd.min;
  252. _crect.max-=padd.max;
  253. }
  254. // now that client rectangle is known, we can calculate bar height and button rectangles
  255. setButtons();
  256. Rect new_client=localClientRect();
  257. notifyChildrenOfClientRectChange(&old_client, &new_client);
  258. }
  259. Window& Window::rect(C Rect &rect)
  260. {
  261. if(T.rect()!=rect)
  262. {
  263. Rect old_rect=T.rect(), r=rect, size_limit=sizeLimit();
  264. r.max.x=r.min.x+Mid(r.w(), size_limit.min.x, size_limit.max.x);
  265. r.min.y=r.max.y-Mid(r.h(), size_limit.min.y, size_limit.max.y);
  266. T._rect=r; // set manually instead of calling 'super::rect' because that will call 'notifyChildrenOfClientRectChange' before we finish setting client rectangle
  267. setRect();
  268. notifyParentOfRectChange(old_rect, visible());
  269. }
  270. return T;
  271. }
  272. Window& Window::clientRect(C Rect &rect)
  273. {
  274. Rect r=rect, padd;
  275. defaultInnerPadding(padd);
  276. r.min-=padd.min;
  277. r.max+=padd.max;
  278. return T.rect(r);
  279. }
  280. Window& Window::move(C Vec2 &delta)
  281. {
  282. if(delta.any())
  283. {
  284. super::move(delta);
  285. _crect+=delta;
  286. button[0].move(delta);
  287. button[1].move(delta);
  288. button[2].move(delta);
  289. }
  290. return T;
  291. }
  292. /******************************************************************************/
  293. Window& Window::show()
  294. {
  295. super::show();
  296. _fade_type =FADE_NONE;
  297. _fade_alpha=1; setFinalAlpha();
  298. return T;
  299. }
  300. Window& Window::hide()
  301. {
  302. if(visible())
  303. {
  304. Bool activate_next=(Gui.window()==this);
  305. super::hide();
  306. // activate next window from mutual parent
  307. if(activate_next && parent())if(GuiObjChildren *children=parent()->children())
  308. REPA(*children)
  309. if(GuiObj *go=(*children)[i])if(go->type()==GO_WINDOW && go!=this && go->asWindow().showing()/* && !FlagTest(go->asWindow().flag, WIN_IMMEDIATE_DEACT)*/)
  310. {go->activate(); break;}
  311. }
  312. _fade_type =FADE_NONE;
  313. _fade_alpha=0; setFinalAlpha();
  314. return T;
  315. }
  316. /******************************************************************************/
  317. void Window::parentClientRectChanged(C Rect *old_client, C Rect *new_client)
  318. {
  319. if(old_client && new_client)
  320. {
  321. Flt eps =0.03f,
  322. new_top=new_client->max.y,
  323. old_top=old_client->max.y;
  324. if(parent())REP(parent()->childNum())if(GuiObj *menu=parent()->child(i))if(menu->type()==GO_MENU_BAR && menu->visible())
  325. {
  326. Flt h=menu->rect().h();
  327. new_top-=h;
  328. old_top-=h;
  329. break;
  330. }
  331. if(new_client->w()>old_client->w())
  332. {
  333. Bool stick_r=(rect().max.x>=old_client->max.x-eps),
  334. stick_l=(rect().min.x<=old_client->min.x+eps);
  335. if(stick_r && !stick_l)move(Vec2(new_client->max.x-old_client->max.x, 0));else
  336. if(stick_l && !stick_r)move(Vec2(new_client->min.x-old_client->min.x, 0));
  337. }else
  338. if(new_client->w()<old_client->w())
  339. {
  340. Bool stick_r=(new_client->max.x<rect().max.x), // new smaller size doesn't contain the full window
  341. stick_l=(new_client->min.x>rect().min.x); // new smaller size doesn't contain the full window
  342. if(stick_r && !stick_l)move(Vec2(Max(new_client->max.x-rect().max.x, new_client->max.x-old_client->max.x), 0));else
  343. if(stick_l && !stick_r)move(Vec2(Min(new_client->min.x-rect().min.x, new_client->min.x-old_client->min.x), 0));
  344. }
  345. if(new_client->h()>old_client->h())
  346. {
  347. Bool stick_u=(rect().max.y>=old_top -eps),
  348. stick_d=(rect().min.y<=old_client->min.y+eps);
  349. if(stick_u && !stick_d)move(Vec2(0, new_client->max.y-old_client->max.y));else
  350. if(stick_d && !stick_u)move(Vec2(0, new_client->min.y-old_client->min.y));
  351. }else
  352. if(new_client->h()<old_client->h())
  353. {
  354. Bool stick_u=(new_top <rect().max.y), // new smaller size doesn't contain the full window
  355. stick_d=(new_client->min.y>rect().min.y); // new smaller size doesn't contain the full window
  356. if(stick_u && !stick_d)move(Vec2(0, Max(new_top -rect().max.y, new_client->max.y-old_client->max.y)));else
  357. if(stick_d && !stick_u)move(Vec2(0, Min(new_client->min.y-rect().min.y, new_client->min.y-old_client->min.y)));
  358. }
  359. // if shrinking screen and window is resizable, then try to adjust its size for new screen size
  360. if(flag&WIN_RESIZABLE)
  361. {
  362. Flt w=rect().w(),
  363. h=rect().h();
  364. if(w>new_client->w() || h>new_client->h())
  365. {
  366. size(Vec2(Min(w, new_client->w()), Min(h, new_client->h())));
  367. if(rect().min.x<new_client->min.x
  368. || rect().max.y>new_client->max.y)
  369. move(Vec2(Mid(new_client->min.x-rect().min.x, 0.0f, w-rect().w()),
  370. Mid(new_client->max.y-rect().max.y, rect().h()-h, 0.0f)));
  371. }
  372. }
  373. }
  374. }
  375. /******************************************************************************/
  376. GuiObj* Window::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel)
  377. {
  378. if(visible() && gpc.visible && _fade_type!=FADE_OUT)
  379. {
  380. Rect r=rect()+gpc.offset;
  381. if(flag&WIN_RESIZABLE)
  382. {
  383. if(resize_mask&DIRF_LEFT )r.min.x-=Gui.resize_radius;
  384. if(resize_mask&DIRF_RIGHT)r.max.x+=Gui.resize_radius;
  385. if(resize_mask&DIRF_DOWN )r.min.y-=Gui.resize_radius;
  386. if(resize_mask&DIRF_UP )r.max.y+=Gui.resize_radius;
  387. }
  388. if(Cuts(pos, r&gpc.clip))
  389. {
  390. mouse_wheel=this;
  391. GuiPC gpc2(gpc, true, enabled());
  392. if(GuiObj *go=button[2].test(gpc2, pos, mouse_wheel))return go;
  393. if(GuiObj *go=button[1].test(gpc2, pos, mouse_wheel))return go;
  394. if(GuiObj *go=button[0].test(gpc2, pos, mouse_wheel))return go;
  395. GuiPC gpc_children(gpc, T); if(GuiObj *go=_children.test(gpc_children, pos, mouse_wheel))return go;
  396. return this;
  397. }
  398. }
  399. return null;
  400. }
  401. /******************************************************************************/
  402. static inline void UpdateStretch(Flt side, Flt &l, Flt &r, Int &li, Int &ri, Flt d)
  403. {
  404. if(side<0)
  405. {
  406. li++;
  407. l+=d;
  408. }else
  409. if(side>0)
  410. {
  411. ri++;
  412. r+=d;
  413. }
  414. }
  415. static void UpdateResize(Flt &min, Flt &max, Flt l, Flt r, Flt size_min, Flt size_max, UInt move, UInt resize_l, UInt resize_r)
  416. {
  417. // move, and reduce 'l', 'r' by what was moved
  418. if(move)
  419. {
  420. if(l>0 && r>0){Flt d=Min(l, r); l-=d; r-=d; min+=d; max+=d;}else
  421. if(l<0 && r<0){Flt d=Max(l, r); l-=d; r-=d; min+=d; max+=d;}
  422. }
  423. // resize both by the same amount, and reduce 'l', 'r' by what was resized
  424. if(resize_l && resize_r)
  425. {
  426. if(l>0 && r<0) // shrink
  427. {
  428. Flt d=Min(l, -r); Flt w=max-min; MIN(d, (w-size_min)*0.5f); l-=d; r+=d; min+=d; max-=d; // new_width=w-d*2, size_min=new_width -> size_min=w-d*2 -> d*2=w-size_min -> d=(w-size_min)/2
  429. }else
  430. if(l<0 && r>0) // extend
  431. {
  432. Flt d=Min(-l, r); Flt w=max-min; MIN(d, (size_max-w)*0.5f); l+=d; r-=d; min-=d; max+=d; // new_width=w+d*2, size_max=new_width -> size_max=w+d*2 -> d*2=size_max-w -> d=(size_max-w)/2
  433. }
  434. }
  435. // resize separately by what's left
  436. if(resize_l)min+=l;
  437. if(resize_r)max+=r;
  438. // clamp
  439. Clamp(min, max-size_max, max-size_min);
  440. Clamp(max, min+size_min, min+size_max);
  441. }
  442. /******************************************************************************/
  443. void Window::update(C GuiPC &gpc)
  444. {
  445. GuiPC gpc_this(gpc, visible(), enabled()),
  446. gpc_children(gpc, T);
  447. if(gpc_this.visible && Gui.ms()==this && (flag&WIN_RESIZABLE) && _fade_type!=FADE_OUT && !Ms.b(1))
  448. {
  449. if(!Ms.b(0)) // if not pressed then detect new resize, if pressed then keep previous
  450. {
  451. _resize=0;
  452. Vec2 aligned_offset=gpc_children.offset-_crect.lu(), pos=Ms.pos()-aligned_offset;
  453. if(pos.x<=rect().min.x+D.pixelToScreenSize().x)_resize|=DIRF_LEFT ;else
  454. if(pos.x>=rect().max.x-D.pixelToScreenSize().x)_resize|=DIRF_RIGHT;
  455. if(pos.y<=rect().min.y+D.pixelToScreenSize().y)_resize|=DIRF_DOWN ;else
  456. if(pos.y>=rect().max.y-D.pixelToScreenSize().y)_resize|=DIRF_UP ;
  457. _resize&=resize_mask;
  458. }
  459. }else _resize=0;
  460. if(gpc_this.visible)
  461. {
  462. if(gpc.enabled)
  463. {
  464. Bool have_client_rect=false; Rect client_rect; if(parent()){have_client_rect=true; client_rect=parent()->localClientRect();}
  465. if(Gui.ms()==this)
  466. {
  467. if(Ms.b(0) && _resize)
  468. {
  469. Vec2 pos=Ms.pos()-gpc.offset;
  470. Rect r =rect(), size=sizeLimit();
  471. MIN(size.max.x, Max(size.min.x, D.w2(), rect().w()));
  472. MIN(size.max.y, Max(size.min.y, D.h2(), rect().h()));
  473. if(_resize&DIRF_LEFT )r.min.x=Mid(have_client_rect ? Min(pos.x, client_rect.max.x-WINDOW_PADD) : pos.x, r.max.x-size.max.x, r.max.x-size.min.x);
  474. if(_resize&DIRF_UP )r.max.y=Mid(have_client_rect ? Max(pos.y, client_rect.min.y+WINDOW_PADD) : pos.y, r.min.y+size.min.y, r.min.y+size.max.y);
  475. if(_resize&DIRF_RIGHT)r.max.x=Mid(have_client_rect ? Max(pos.x, client_rect.min.x+WINDOW_PADD) : pos.x, r.min.x+size.min.x, r.min.x+size.max.x);
  476. if(_resize&DIRF_DOWN )r.min.y=Mid(have_client_rect ? Min(pos.y, client_rect.max.y-WINDOW_PADD) : pos.y, r.max.y-size.max.y, r.max.y-size.min.y);
  477. rect(r);
  478. }
  479. if(flag&WIN_RESIZABLE)
  480. {
  481. if(Ms.bd(0) && Ms.pos().y>=_crect.max.y+gpc.offset.y){Maximize(T); _resize=0;} // maximize when double clicking on the title bar
  482. if(Ms.b (1))
  483. {
  484. Ms.freeze();
  485. if(Ms.d().any() && resize_mask)
  486. {
  487. Rect r=rect(), size=sizeLimit();
  488. MIN(size.max.x, Max(size.min.x, D.w2(), rect().w()));
  489. MIN(size.max.y, Max(size.min.y, D.h2(), rect().h()));
  490. if(resize_mask&DIRF_RIGHT)Clamp(r.max.x+=Ms.d().x, r.min.x+size.min.x, r.min.x+size.max.x);else if(resize_mask&DIRF_LEFT)Clamp(r.min.x+=Ms.d().x, r.max.x-size.max.x, r.max.x-size.min.x);
  491. if(resize_mask&DIRF_DOWN )Clamp(r.min.y+=Ms.d().y, r.max.y-size.max.y, r.max.y-size.min.y);else if(resize_mask&DIRF_UP )Clamp(r.max.y+=Ms.d().y, r.min.y+size.min.y, r.min.y+size.max.y);
  492. rect(r);
  493. }
  494. }
  495. }
  496. }
  497. if(flag&(WIN_MOVABLE|WIN_RESIZABLE))
  498. {
  499. Vec2 pos=0, delta=0; Int n=0;
  500. if( Gui.ms()==this && Ms.b(0) && !_resize){pos+=Ms.pos()-Ms.dc(); delta+=Ms.dc(); n++;}
  501. REPA(Touches){Touch &t=Touches[i]; if(t.guiObj()==this && t.on( ) ){pos+= t.pos()- t.d (); delta+= t.d (); n++;}}
  502. if(n)
  503. {
  504. if(n>1 && (flag&WIN_RESIZABLE))
  505. {
  506. pos/=n; // touch center before movement
  507. Flt l =0, r =0, u =0, d =0; // left right up down movement
  508. Int li=0, ri=0, ui=0, di=0; // number of touches on each side
  509. if( Gui.ms()==this && Ms.b(0) && !_resize){delta=(Ms.pos()-Ms.dc())-pos; UpdateStretch(delta.x, l, r, li, ri, Ms.dc().x); UpdateStretch(delta.y, d, u, di, ui, Ms.dc().y);}
  510. REPA(Touches){Touch &t=Touches[i]; if(t.guiObj()==this && t.on( ) ){delta=( t.pos()- t.d ())-pos; UpdateStretch(delta.x, l, r, li, ri, t.d ().x); UpdateStretch(delta.y, d, u, di, ui, t.d ().y);}}
  511. Rect rect=T.rect(), size=sizeLimit();
  512. MIN(size.max.x, Max(size.min.x, D.w2(), rect.w()));
  513. MIN(size.max.y, Max(size.min.y, D.h2(), rect.h()));
  514. UpdateResize(rect.min.x, rect.max.x, li ? l/li : 0, ri ? r/ri : 0, size.min.x, size.max.x, flag&WIN_MOVABLE, resize_mask&DIRF_LEFT, resize_mask&DIRF_RIGHT);
  515. UpdateResize(rect.min.y, rect.max.y, di ? d/di : 0, ui ? u/ui : 0, size.min.y, size.max.y, flag&WIN_MOVABLE, resize_mask&DIRF_DOWN, resize_mask&DIRF_UP );
  516. T.rect(rect);
  517. }else
  518. if((flag&WIN_MOVABLE) && delta.any())
  519. {
  520. delta/=n;
  521. if(have_client_rect)
  522. {
  523. Flt paddx=Min(WINDOW_PADD, rect().w()),
  524. paddy=Min(WINDOW_PADD, rect().h());
  525. Clamp(delta.x, client_rect.min.x+paddx-rect().max.x, client_rect.max.x-paddx-rect().min.x);
  526. Clamp(delta.y, client_rect.min.y+paddy-rect().max.y, client_rect.max.y-paddy-rect().min.y);
  527. }
  528. move(delta);
  529. }
  530. }
  531. }
  532. }
  533. /*if((flag&WIN_IMMEDIATE) && !Gui.menu() && !Touches.elms() && !Ms.b(0) && !Ms.br(0))
  534. {
  535. if((flag&WIN_IMMEDIATE_ACT ) && Gui.window()!=this && contains(Gui.ms()) && !_was_lit) activate();else
  536. if((flag&WIN_IMMEDIATE_DEACT) && Gui.window()==this && !contains(Gui.ms()) && _was_lit)deactivate();
  537. }*/
  538. //AdjustValBool(_lit_hover, lit(), Gui._time_d_fade_in, Gui._time_d_fade_out);
  539. //AdjustValBool(_lit_focus, active(), Gui._time_d_fade_in, Gui._time_d_fade_out);
  540. if( lit())_lit_hover=1;else MAX(_lit_hover-=Gui._time_d_fade_out, 0);
  541. if(active())_lit_focus=1;else MAX(_lit_focus-=Gui._time_d_fade_out, 0);
  542. switch(_fade_type)
  543. {
  544. case FADE_IN : _fade_alpha+=Time.ad()*Gui.window_fade_in_speed ; if(_fade_alpha>1){_fade_alpha=1; _fade_type=FADE_NONE; } setFinalAlpha(); break;
  545. case FADE_OUT: _fade_alpha-=Time.ad()*Gui.window_fade_out_speed; if(_fade_alpha<0){_fade_alpha=0; _fade_type=FADE_NONE; hide();} setFinalAlpha(); break;
  546. }
  547. }
  548. if(gpc_this.enabled)
  549. {
  550. _children.update(gpc_children);
  551. button[0].update(gpc_this);
  552. button[1].update(gpc_this);
  553. button[2].update(gpc_this);
  554. }
  555. }
  556. /******************************************************************************/
  557. void Window::draw(C GuiPC &gpc)
  558. {
  559. if(visible() && gpc.visible)
  560. {
  561. Bool active=T.active(), transparent=(finalAlpha()<1-EPS_COL);
  562. GuiPC gpc_children(gpc, T), // first calculate the GuiPC for children which sets the offset per pixel aligned exactly at client rect top left corner
  563. gpc_aligned(gpc, true, active); // use 'active' for 'gpc_aligned' to draw buttons as half transparent when inactive
  564. gpc_aligned.offset=gpc_children.offset-_crect.lu(); // now based on that offset calculate the GuiPC for the Window, which is just as 'gpc' however with offset compatible with 'gpc_children'
  565. Rect r=rect()+gpc_aligned.offset, ext_rect;
  566. if(ripple || transparent)
  567. {
  568. D.clip (null);
  569. D.fxBegin();
  570. if(ripple)D.clearCol();else // clear entire screen for ripple as it may use various tex coordinates basing on the ripple intensity
  571. { // clear only the screen part that we're going to use
  572. ALPHA_MODE alpha=D.alpha(ALPHA_NONE);
  573. extendedRect(ext_rect); ext_rect+=gpc_aligned.offset;
  574. Sh.clear(Vec4Zero, &Rect(ext_rect).extend(D.pixelToScreenSize())); // extend by 1 pixel to avoid tex filtering issues
  575. D .alpha(alpha);
  576. }
  577. }else
  578. {
  579. D.clip(gpc.clip);
  580. }
  581. GuiSkin *skin=getSkin();
  582. if(skin)
  583. {
  584. if(Panel *panel=(barVisible() ? (active ? skin->window.active() : skin->window.normal()) : (active ? skin->window.active_no_bar() : skin->window.normal_no_bar())))
  585. panel->draw(active ? skin->window.active_color : skin->window.normal_color, r);
  586. if(barVisible() && title.is())
  587. if(TextStyle *text_style=(active ? skin->window.active_text_style() : skin->window.normal_text_style()))
  588. {
  589. Flt bar_height=barHeight(), text_padd=bar_height*skin->window.text_padd;
  590. Rect text_rect(_crect.min.x+gpc_aligned.offset.x+text_padd, r.max.y-bar_height, _crect.max.x+gpc_aligned.offset.x-text_padd, r.max.y);
  591. TextStyleParams ts=*text_style; ts.size=bar_height*skin->window.text_size; ts.color=ColorAdd(ts.color, ColorBA(highlightHover()*0.11f, 0));
  592. // check if title overlaps the buttons
  593. if(Gui.windowButtonsRight())
  594. {
  595. if(ts.align.x<1-EPS) // if alignment is not to the right
  596. {
  597. Flt text_width=ts.textWidth(title),
  598. x_align_mul=ts.align.x*0.5f-0.5f,
  599. right=text_rect.lerpX(-x_align_mul) + text_width*x_align_mul + text_width,
  600. max_x;
  601. if(button[0].visible())max_x=button[0].rect().min.x+gpc_aligned.offset.x-text_padd;else
  602. if(button[1].visible())max_x=button[1].rect().min.x+gpc_aligned.offset.x-text_padd;else
  603. if(button[2].visible())max_x=button[2].rect().min.x+gpc_aligned.offset.x-text_padd;else
  604. max_x=text_rect.max.x;
  605. if(right>max_x)
  606. {
  607. text_rect.max.x=max_x;
  608. if(text_width>text_rect.w())ts.align.x=1; // if text width is bigger than available space, then align to the right
  609. }
  610. }
  611. }else
  612. {
  613. Flt min_x;
  614. if(button[0].visible())min_x=button[0].rect().max.x+gpc_aligned.offset.x+text_padd;else
  615. if(button[1].visible())min_x=button[1].rect().max.x+gpc_aligned.offset.x+text_padd;else
  616. if(button[2].visible())min_x=button[2].rect().max.x+gpc_aligned.offset.x+text_padd;else
  617. min_x=text_rect.min.x;
  618. if(ts.align.x<1-EPS) // if alignment is not to the right
  619. {
  620. Flt text_width=ts.textWidth(title),
  621. x_align_mul=ts.align.x*0.5f-0.5f,
  622. left=text_rect.lerpX(-x_align_mul) + text_width*x_align_mul;
  623. if(left<min_x)
  624. {
  625. text_rect.min.x=min_x;
  626. if(text_width>text_rect.w())ts.align.x=1; // if text width is bigger than available space, then align to the right
  627. }
  628. }else // if alignment is to the right
  629. {
  630. text_rect.min.x=min_x;
  631. }
  632. }
  633. D.clip(text_rect&gpc.clip);
  634. D.text(ts, text_rect, title);
  635. }
  636. }
  637. _children.draw(gpc_children);
  638. button[0].draw(gpc_aligned );
  639. button[1].draw(gpc_aligned );
  640. button[2].draw(gpc_aligned );
  641. if(ripple || transparent)
  642. if(C ImageRTPtr &rt=D.fxEnd())
  643. {
  644. D.clip(gpc.clip);
  645. if(ripple )ripple->draw(*rt, D.rect());else
  646. if(transparent)
  647. {
  648. Color color=ColorMul(finalAlpha());
  649. ALPHA_MODE alpha=D.alpha(ALPHA_MERGE);
  650. if(Equal(Gui.window_fade_scale, 1))Sh .draw (*rt, color, Vec4Zero, &ext_rect);
  651. else rt->drawPart(color, TRANSPARENT, Rect_C(ext_rect.center(), ext_rect.size()*Lerp(Mid(Gui.window_fade_scale, 0.0f, 2.0f), 1.0f, fadeAlpha())), D.screenToUV(ext_rect));
  652. D.alpha(alpha);
  653. }
  654. }
  655. if(_resize && Gui.ms()==this) // check for Gui.ms as well in case we've lost focus
  656. {
  657. Image *image=null;
  658. switch(_resize)
  659. {
  660. case DIRF_LEFT : image=Gui.image_resize_x (); break;
  661. case DIRF_RIGHT : image=Gui.image_resize_x (); break;
  662. case DIRF_UP : image=Gui.image_resize_y (); break;
  663. case DIRF_DOWN : image=Gui.image_resize_y (); break;
  664. case DIRF_LEFT |DIRF_UP : image=Gui.image_resize_lu(); break;
  665. case DIRF_RIGHT|DIRF_UP : image=Gui.image_resize_ru(); break;
  666. case DIRF_LEFT |DIRF_DOWN: image=Gui.image_resize_ld(); break;
  667. case DIRF_RIGHT|DIRF_DOWN: image=Gui.image_resize_rd(); break;
  668. }
  669. if(image)
  670. {
  671. D.clip(gpc.clip);
  672. Vec2 pos=Ms.pos(), size(MOUSE_IMAGE_SIZE*image->aspect(), MOUSE_IMAGE_SIZE); pos+=Vec2(-size.x, size.y)*0.5f;
  673. image->draw(Rect_LU(D.screenAlignedToPixel(pos), size));
  674. }
  675. }
  676. }
  677. }
  678. /******************************************************************************/
  679. Bool Window::save(File &f, CChar *path)C
  680. {
  681. if(super::save(f, path))
  682. {
  683. f.putMulti(Byte(5), flag, resize_mask, _level, _crect, _bar_visible, _fade_type, _fade_alpha, _alpha)<<title; // version
  684. f.putAsset(_skin.id());
  685. if(button[0].save(f, path))
  686. if(button[1].save(f, path))
  687. if(button[2].save(f, path))
  688. return f.ok();
  689. }
  690. return false;
  691. }
  692. Bool Window::load(File &f, CChar *path)
  693. {
  694. del(); if(super::load(f, path))switch(f.decUIntV()) // version
  695. {
  696. case 5:
  697. {
  698. f.getMulti(flag, resize_mask, _level, _crect, _bar_visible, _fade_type, _fade_alpha, _alpha)>>title;
  699. _skin.require(f.getAssetID(), path);
  700. if(button[0].load(f, path))
  701. if(button[1].load(f, path))
  702. if(button[2].load(f, path))
  703. if(f.ok()){setFinalAlpha(); setParams(); return true;}
  704. }break;
  705. case 4:
  706. {
  707. f.getMulti(flag, resize_mask, _level, _crect, _bar_visible, _fade_type, _fade_alpha, _alpha)._getStr2(title);
  708. _skin.require(f.getAssetID(), path);
  709. if(button[0].load(f, path))
  710. if(button[1].load(f, path))
  711. if(button[2].load(f, path))
  712. if(f.ok()){setFinalAlpha(); setParams(); return true;}
  713. }break;
  714. case 3:
  715. {
  716. f>>flag>>resize_mask>>_level>>_crect>>_bar_visible>>_fade_type>>_fade_alpha; f._getStr2(title); _alpha=1;
  717. _skin.require(f._getAsset(), path);
  718. if(button[0].load(f, path))
  719. if(button[1].load(f, path))
  720. if(button[2].load(f, path))
  721. if(f.ok()){setFinalAlpha(); setParams(); return true;}
  722. }break;
  723. case 2:
  724. {
  725. f>>flag>>_level>>_crect; _bar_visible=!f.getBool(); f>>_fade_type; f.skip(4); f>>_fade_alpha; f.skip(4); _alpha=1;
  726. f._getStr(title); f._getStr();
  727. if(button[0].load(f, path))
  728. if(button[1].load(f, path))
  729. if(button[2].load(f, path))
  730. if(f.ok()){setFinalAlpha(); setParams(); return true;}
  731. }break;
  732. case 1:
  733. {
  734. f>>flag>>_level>>_crect; _bar_visible=!f.getBool(); f>>_fade_type; f.skip(4); f>>_fade_alpha; f.skip(4); _alpha=1;
  735. title=f._getStr16(); f._getStr16();
  736. if(button[0].load(f, path))
  737. if(button[1].load(f, path))
  738. if(button[2].load(f, path))
  739. if(f.ok()){setFinalAlpha(); setParams(); return true;}
  740. }break;
  741. case 0:
  742. {
  743. f>>flag>>_level>>_crect; _bar_visible=!f.getBool(); f>>_fade_type; f.skip(4); f>>_fade_alpha; f.skip(4); _alpha=1;
  744. title=f._getStr8(); f._getStr8();
  745. if(button[0].load(f, path))
  746. if(button[1].load(f, path))
  747. if(button[2].load(f, path))
  748. if(f.ok()){setFinalAlpha(); setParams(); return true;}
  749. }break;
  750. }
  751. del(); return false;
  752. }
  753. /******************************************************************************/
  754. // CLOSABLE WINDOW
  755. /******************************************************************************/
  756. void ClosableWindow::update(C GuiPC &gpc)
  757. {
  758. super::update(gpc);
  759. if(Gui.window()==this && button[2].func()) // this is the active window and close button has a function assigned
  760. {
  761. if((Kb.kf(KB_ESC) && !Kb.k.ctrlCmd() && !Kb.k.shift()) || Kb.kf(KB_NAV_BACK) || (Ms.bp(2) && contains(Gui.ms()) && !Gui.menu()->contains(Gui.ms())))
  762. {
  763. Kb.eatKey(); Ms.eat(2); button[2].push(); // use "button.push" to trigger calling custom function assigned to that button
  764. }
  765. }
  766. }
  767. /******************************************************************************/
  768. // MODAL WINDOW
  769. /******************************************************************************/
  770. GuiObj* ModalWindow::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel)
  771. {
  772. if(visible())
  773. {
  774. if(GuiObj *go=super::test(gpc, pos, mouse_wheel))return go;
  775. return &_background;
  776. }
  777. return null;
  778. }
  779. void ModalWindow::update(C GuiPC &gpc)
  780. {
  781. super::update(gpc);
  782. if(visible())REPA(MT)if(MT.bp(i) && MT.guiObj(i)==&_background)fadeOut();
  783. }
  784. void ModalWindow::draw(C GuiPC &gpc)
  785. {
  786. if(visible())
  787. {
  788. D.clip();
  789. D.rect().draw(ColorBA(0, finalAlpha()*0.5f));
  790. super::draw(gpc);
  791. }
  792. }
  793. /******************************************************************************/
  794. // DIALOG
  795. /******************************************************************************/
  796. GuiObj* Dialog::Text2::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel) {return null;}
  797. Dialog& Dialog::autoSize()
  798. {
  799. Flt button_w=Gui.dialog_button_padd*buttons.elms(); if(buttons.elms())button_w+=Gui.dialog_button_margin*(buttons.elms()-1);
  800. FREPA(buttons)button_w+=buttons[i].textWidth(&Gui.dialog_button_height);
  801. Flt text_w=0, text_h=0;
  802. if(text().is())
  803. if(C TextStyle *ts=text.getTextStyle())
  804. if(Flt line_h=ts->lineHeight())
  805. {
  806. const Flt desired_aspect=2.5f, min_w=line_h*17, max_w=(D.w()-Gui.dialog_padd)*2; // min_w gives some tolerable minimum width based on a single line height
  807. Int lines=ts->textLines(text(), max_w, text.auto_line, &text_w);
  808. text_h=line_h*lines;
  809. if(text_w>min_w)
  810. {
  811. AUTO_LINE_MODE auto_line=text.auto_line; if(auto_line==AUTO_LINE_SPACE_SPLIT || auto_line==AUTO_LINE_SPLIT)auto_line=AUTO_LINE_SPACE; // don't split words in next attempts, attempt above can split because we've set max_w as entire screen, however next attempts will use less
  812. Flt aspect=text_w/text_h;
  813. REP(3) // 3 attempts
  814. {
  815. Flt multiplier=Sqrt(desired_aspect/aspect), // need to apply 'Sqrt' because this affects both width and height
  816. test_w=Mid(text_w*multiplier, min_w, max_w);
  817. Int test_lines=ts->textLines(text(), test_w, auto_line, &test_w);
  818. Flt test_h=line_h*test_lines,
  819. test_aspect=test_w/test_h;
  820. if(AbsScale(test_aspect, desired_aspect)<AbsScale(aspect, desired_aspect)) // if 'test_aspect' is closer to 'desired_aspect'
  821. {
  822. text_w=test_w;
  823. text_h=test_h;
  824. aspect=test_aspect;
  825. }else break;
  826. }
  827. }
  828. }
  829. Flt max_w=Max(button_w, text_w);
  830. Vec2 size(Gui.dialog_padd+max_w+Gui.dialog_padd, Gui.dialog_padd+text_h+Gui.dialog_padd+Gui.dialog_button_height+Gui.dialog_padd);
  831. rect(Rect_C(Vec2(0), size+defaultInnerPaddingSize()));
  832. text.rect(Rect_U(size.x/2, -Gui.dialog_padd, text_w, text_h));
  833. Flt x=(size.x-button_w)/2, y=text.rect().min.y-Gui.dialog_padd;
  834. FREPA(buttons)
  835. {
  836. Button &button=buttons[i];
  837. Flt w=button.textWidth(&Gui.dialog_button_height)+Gui.dialog_button_padd;
  838. button.rect(Rect_LU(x, y, w, Gui.dialog_button_height));
  839. x+=w+Gui.dialog_button_margin;
  840. }
  841. return T;
  842. }
  843. Dialog& Dialog::set(C Str &title, C Str &text, C MemPtr<Str> &buttons, C TextStylePtr &text_style)
  844. {
  845. super::setTitle(title).barVisible(title.is());
  846. T+=T.text.create(text, text_style); T.text.auto_line=AUTO_LINE_SPACE_SPLIT;
  847. T.buttons.setNum(buttons.elms());
  848. FREPA(T.buttons)T+=T.buttons[i].create(buttons[i]);
  849. return autoSize();
  850. }
  851. Dialog& Dialog::create(C Str &title, C Str &text, C MemPtr<Str> &buttons, C TextStylePtr &text_style)
  852. {
  853. super::create();
  854. return set(title, text, buttons, text_style);
  855. }
  856. /******************************************************************************/
  857. }
  858. /******************************************************************************/