1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace ManiaLive\Gui;
13:
14: use ManiaLib\Gui\Manialink;
15: use ManiaLive\Event\Dispatcher;
16: use ManiaLive\Features\Tick\Event as TickEvent;
17: use ManiaLive\Features\Tick\Listener as TickListener;
18:
19: 20: 21:
22: abstract class Window extends Container implements TickListener
23: {
24: const Z_MIN = -60;
25: const Z_MAXIMIZED = 0;
26: const Z_MODAL = 25;
27: const Z_MAX = 50;
28: const Z_OFFSET = .1;
29:
30: const RECIPIENT_ALL = null;
31: const RECIPIENT_PLAYERS = true;
32: const RECIPIENT_SPECTATORS = false;
33:
34: static private $singletons = array();
35: static private $instancesByClass = array();
36: static private $instancesByLoginAndClass = array();
37:
38: private $recipient;
39: private $visibilities = array();
40: private $timeout = false;
41: private $linksDisabled = false;
42: private $closeCallbacks = array();
43: private $above = array();
44: private $below = array();
45:
46: 47: 48: 49: 50: 51: 52: 53:
54: static function Create($recipient = self::RECIPIENT_ALL, $singleton = true)
55: {
56: if($recipient === self::RECIPIENT_ALL)
57: $recipient = Group::Get('all');
58: else if($recipient === self::RECIPIENT_PLAYERS)
59: $recipient = Group::Get('players');
60: else if($recipient === self::RECIPIENT_SPECTATORS)
61: $recipient = Group::Get('spectators');
62:
63: $className = get_called_class();
64: $args = array_slice(func_get_args(), 2);
65: $login = strval($recipient);
66:
67: if(!isset(self::$instancesByClass[$className]))
68: self::$instancesByClass[$className] = array();
69: if(!isset(self::$instancesByLoginAndClass[$login]))
70: self::$instancesByLoginAndClass[$login] = array($className => array());
71: else if(!isset(self::$instancesByLoginAndClass[$login][$className]))
72: self::$instancesByLoginAndClass[$login][$className] = array();
73:
74: if(!$singleton)
75: $instance = new static($recipient, $args);
76: else if(isset(self::$singletons[$login]))
77: {
78: if(isset(self::$singletons[$login][$className]))
79: return self::$singletons[$login][$className];
80: else
81: {
82: $instance = new static($recipient, $args);
83: self::$singletons[$login][$className] = $instance;
84: }
85: }
86: else
87: {
88: $instance = new static($recipient, $args);
89: self::$singletons[$login] = array($className => $instance);
90: }
91:
92: self::$instancesByClass[$className][$instance->id] = $instance;
93: self::$instancesByLoginAndClass[$login][$className][$instance->id] = $instance;
94: return $instance;
95: }
96:
97: 98: 99: 100: 101:
102: static function GetAll()
103: {
104: $className = get_called_class();
105: $instances = array();
106:
107: foreach(self::$instancesByClass as $class => $windows)
108: if($class == $className || is_subclass_of($class, $className))
109: array_splice($instances, count($instances), 0, self::$instancesByClass[$class]);
110: return $instances;
111: }
112:
113: 114: 115: 116: 117: 118:
119: static function Get($recipient)
120: {
121: $className = get_called_class();
122: $instances = array();
123: $login = strval($recipient);
124:
125: if(isset(self::$instancesByLoginAndClass[$login]))
126: foreach(self::$instancesByLoginAndClass[$login] as $class => $windows)
127: if($class == $className || is_subclass_of($class, $className))
128: array_splice($instances, count($instances), 0, self::$instancesByLoginAndClass[$login][$class]);
129: return $instances;
130: }
131:
132: 133: 134: 135:
136: static function RedrawAll()
137: {
138: $className = get_called_class();
139:
140: foreach(self::$instancesByClass as $class => $windows)
141: if($class == $className || is_subclass_of($class, $className))
142: foreach($windows as $window)
143: if($window->isVisible())
144: $window->show();
145: }
146:
147: 148: 149: 150: 151:
152: static function Erase($recipient)
153: {
154: $className = get_called_class();
155: $login = strval($recipient);
156:
157: if(isset(self::$instancesByLoginAndClass[$login]))
158: foreach(self::$instancesByLoginAndClass[$login] as $class => $windows)
159: if($class == $className || is_subclass_of($class, $className))
160: foreach($windows as $window)
161: $window->destroy();
162: }
163:
164: 165: 166: 167: 168:
169: static function EraseAll()
170: {
171: $className = get_called_class();
172:
173: foreach(self::$instancesByClass as $class => $windows)
174: if($class == $className || is_subclass_of($class, $className))
175: foreach($windows as $window)
176: $window->destroy();
177: }
178:
179: 180: 181: 182: 183:
184: final private function __construct($recipient, $args = array())
185: {
186: $this->recipient = $recipient;
187: $this->id = spl_object_hash($this);
188: if($this->recipient instanceof Group)
189: foreach($this->recipient as $login)
190: $this->visibilities[$login] = false;
191: else
192: $this->visibilities[$this->recipient] = false;
193:
194: if(empty($args))
195: $this->onConstruct();
196: else
197: call_user_func_array(array($this, 'onConstruct'), $args);
198:
199: $this->posZ = null;
200: }
201:
202: 203: 204: 205:
206: protected function onConstruct() {}
207:
208: 209: 210:
211: final public function getRecipient()
212: {
213: return $this->recipient;
214: }
215:
216: 217: 218: 219:
220: public function setTimeout($seconds)
221: {
222: $this->timeout = time() + $seconds;
223: Dispatcher::register(TickEvent::getClass(), $this);
224: }
225:
226: 227: 228:
229: public function getTimeout()
230: {
231: return !$this->timeout ? false : $this->timeout - time();
232: }
233:
234: 235: 236: 237:
238: public function onTick()
239: {
240: if(time() >= $this->timeout)
241: {
242: $this->timeout = false;
243: $this->hide();
244: Dispatcher::unregister(TickEvent::getClass(), $this);
245: }
246: }
247:
248: final function disableLinks()
249: {
250: $this->linksDisabled = true;
251: }
252:
253: final function enableLinks()
254: {
255: $this->linksDisabled = false;
256: }
257:
258: final function areLinksDisabled()
259: {
260: return $this->linksDisabled;
261: }
262:
263: 264: 265: 266:
267: final public function addCloseCallback($callback)
268: {
269: if(is_callable($callback))
270: $this->closeCallbacks[] = $callback;
271: }
272:
273: 274: 275: 276:
277: final public function moveAbove(Window $window)
278: {
279: if(isset($window->below[$this->id]))
280: {
281: unset($window->below[$this->id]);
282: unset($this->above[$window->id]);
283: }
284:
285: $this->below[$window->id] = $window;
286: $window->above[$this->id] = $this;
287: }
288:
289: 290: 291:
292: final public function centerOnScreen()
293: {
294: $this->setPosition(0, 0);
295: $this->setAlign('center', 'center');
296: }
297:
298: 299: 300: 301: 302:
303: final public function centerOn($windows)
304: {
305: $minX = 0;
306: $minY = 0;
307: $maxX = 0;
308: $maxY = 0;
309:
310: foreach($windows as $window)
311: {
312: if(!($window instanceof Window))
313: continue;
314:
315: $minX = min($minX, $window->getBorderLeft());
316: $minY = min($minY, $window->getBorderTop());
317: $maxX = max($maxX, $window->getBorderRight());
318: $maxY = max($maxY, $window->getBorderBottom());
319: };
320:
321:
322: $this->setPosition(($minX + $maxX) / 2, ($minY + $maxY) / 2);
323: $this->setAlign('center', 'center');
324: }
325:
326: 327: 328: 329:
330: final public function show($recipient = null)
331: {
332: if(!($this->recipient instanceof Group && $this->recipient->contains($recipient)))
333: $recipient = $this->recipient;
334:
335: $wasVisible = true;
336: if(!($recipient instanceof Group || is_array($recipient)))
337: $recipient = array($recipient);
338: foreach($recipient as $login)
339: {
340: $wasVisible = $wasVisible && isset($this->visibilities[$login]) && $this->visibilities[$login];
341: $this->visibilities[$login] = true;
342: }
343:
344: GuiHandler::getInstance()->addToShow($this, $recipient);
345:
346: if(!$wasVisible)
347: $this->onShow();
348: }
349:
350: final public function showModal($recipient = null)
351: {
352: $this->centerOnScreen();
353:
354: if(!($this->recipient instanceof Group && $this->recipient->contains($recipient)))
355: $recipient = $this->recipient;
356:
357: $wasVisible = true;
358: if(!($recipient instanceof Group || is_array($recipient)))
359: $recipient = array($recipient);
360: foreach($recipient as $login)
361: {
362: $wasVisible = $wasVisible && isset($this->visibilities[$login]) && $this->visibilities[$login];
363: $this->visibilities[$login] = true;
364: }
365:
366: GuiHandler::getInstance()->addModal($this, $recipient);
367:
368: if(!$wasVisible)
369: $this->onShow();
370: }
371:
372: 373: 374: 375:
376: final public function hide($recipient = null)
377: {
378: if(!($this->recipient instanceof Group && $this->recipient->contains($recipient)))
379: $recipient = $this->recipient;
380:
381: $oldVisibilities = array();
382: if(!($recipient instanceof Group || is_array($recipient)))
383: $recipient = array($recipient);
384: foreach($recipient as $login)
385: {
386: $oldVisibilities[$login] = !isset($this->visibilities[$login]) || $this->visibilities[$login];
387: $this->visibilities[$login] = false;
388: }
389:
390: GuiHandler::getInstance()->addToHide($this, $recipient);
391:
392: $onHideCalled = false;
393: foreach($oldVisibilities as $login => $wasVisible)
394: {
395: if(!$onHideCalled && $wasVisible)
396: {
397: $this->onHide();
398: $onHideCalled = true;
399: }
400: if($wasVisible)
401: foreach($this->closeCallbacks as $callback)
402: call_user_func($callback, $login, $this);
403: }
404: }
405:
406: final public function redraw($recipient = null)
407: {
408: if(!($this->recipient instanceof Group && $this->recipient->contains($recipient)))
409: $recipient = $this->recipient;
410: if(!($recipient instanceof Group || is_array($recipient)))
411: $recipient = array($recipient);
412: GuiHandler::getInstance()->addToRedraw($this, $recipient);
413: }
414:
415: final public function save()
416: {
417: $this->onDraw();
418:
419: $posX = $this->posX;
420: $posY = $this->posY;
421:
422:
423: if($this->halign == 'right')
424: $posX -= $this->getRealSizeX();
425: else if($this->halign == 'center')
426: $posX -= $this->getRealSizeX() / 2;
427:
428:
429: if($this->valign == 'bottom')
430: $posY += $this->getRealSizeY();
431: else if($this->valign == 'center')
432: $posY += $this->getRealSizeY() / 2;
433:
434: Manialink::beginFrame($posX, $posY, $this->posZ, $this->scale);
435: if($this->linksDisabled)
436: Manialink::disableLinks();
437:
438:
439: $zCur = 0;
440: foreach($this->getComponents() as $component)
441: {
442: $component->setPosZ($zCur);
443: if($component instanceof Control)
444: $zCur += $component->save();
445: else
446: {
447: $component->save();
448: $zCur += self::Z_OFFSET;
449: }
450: }
451:
452: if($this->linksDisabled)
453: Manialink::enableLinks();
454: Manialink::endFrame();
455: }
456:
457: final function getMinZ()
458: {
459: $minZ = self::Z_MIN;
460: foreach($this->below as $window)
461: $minZ = max($minZ, $window->getMaxZ() + self::Z_OFFSET);
462: return $minZ;
463: }
464:
465: final function getMaxZ()
466: {
467: return $this->getMinZ() + self::Z_OFFSET * $this->countElements();
468: }
469:
470: 471: 472: 473:
474: protected function onDraw() {}
475:
476: 477: 478: 479:
480: protected function onHide() {}
481:
482: 483: 484: 485:
486: protected function onShow() {}
487:
488: 489: 490: 491:
492: public function destroy()
493: {
494: $this->hide();
495: $this->clearComponents();
496:
497: foreach($this->below as $below)
498: unset($below->above[$this->id]);
499: foreach($this->above as $above)
500: unset($above->below[$this->id]);
501:
502: $this->closeCallbacks = array();
503: $this->below = array();
504: $this->above = array();
505:
506:
507: $className = get_called_class();
508: unset(self::$singletons[strval($this->recipient)][$className]);
509: unset(self::$instancesByClass[$className][$this->id]);
510: unset(self::$instancesByLoginAndClass[strval($this->recipient)][$className][$this->id]);
511:
512: foreach($this->actions as $action)
513: ActionHandler::getInstance()->deleteAction($action);
514: }
515:
516:
517: private $actions = array();
518:
519: function createAction($callback)
520: {
521: $action = call_user_func_array(array(ActionHandler::getInstance(), 'createAction'), func_get_args());
522: $this->actions[] = $action;
523: return $action;
524: }
525: }
526:
527: ?>