*/ class Layout { /** * List of screens this layout uses * @var array[] */ protected $_screens; /** * Relations between the screens * @var array[] */ protected $_links; /** * Initialize a new layout * * @param array[] $screens * @param array[] $links */ public function __construct(array $screens, array $links) { $this->_screens = $screens; $this->_links = $links; } /** * Fetch the names of the screens this layout depends on * * @return string[] */ public function getScreenNames(): array { return array_keys($this->_screens); } /** * Execute this layout * * @param Configuration $config * @param ILayoutDriver $driver * @return void */ public function execute(Configuration $config, ILayoutDriver $driver): void { $primary_name = $config->getPrimaryScreenName(); $primary = $config->getPrimaryScreen(); $layout_stack = [$primary_name]; $did_layout = false; $layout = [$primary_name=>['x'=>0, 'y'=>0]]; foreach ($this->_screens as $screen_name=>$screen_data) { if (isset($screen_data['width']) && isset($screen_data['height'])) { $config->getScreen($screen_name)->setDimensions($screen_data['width'], $screen_data['height']); } } while (sizeof($layout_stack)) { $did_layout = false; $match_screen = array_pop($layout_stack); foreach ($this->_links as $link) { $target_name = $link['display']; $source_name = $link['above'] ?? $link['below'] ?? $link['right_of'] ?? $link['left_of']; if ($source_name == $match_screen) { $did_layout = true; $calculate_layout = null; if (isset($link['above'])) { $calculate_layout = function($s_x, $s_y, $s_w, $s_h, $d_x, $d_y, $d_w, $d_h) { $center = $s_x + ($s_w / 2); $x = $center - ($d_w / 2); $y = $s_y - $d_h; return [$x, $y]; }; } else if (isset($link['below'])) { $calculate_layout = function($s_x, $s_y, $s_w, $s_h, $d_x, $d_y, $d_w, $d_h) { $center = $s_x + ($s_w / 2); $x = $center - ($d_w / 2); $y = $s_y + $s_h; return [$x, $y]; }; } else if (isset($link['left_of'])) { $calculate_layout = function($s_x, $s_y, $s_w, $s_h, $d_x, $d_y, $d_w, $d_h) { return [$s_x - $d_w, $s_y]; }; } else if (isset($link['right_of'])) { $calculate_layout = function($s_x, $s_y, $s_w, $s_h, $d_x, $d_y, $d_w, $d_h) { return [$s_x + $s_w, $s_y]; }; } $source = $config->getScreen($source_name); $target = $config->getScreen($target_name); if (isset($layout[$source_name])) { $s_x = $layout[$source_name]['x']; $s_y = $layout[$source_name]['y']; } else { list($s_x, $s_y) = $source->getOffset(); } list($s_w, $s_h) = $source->getDimensions(); if (isset($layout[$target_name])) { $d_x = $layout[$target_name]['x']; $d_y = $layout[$target_name]['y']; } else { list($d_x, $d_y) = $target->getOffset(); } list($d_w, $d_h) = $target->getDimensions(); list($x, $y) = $calculate_layout($s_x, $s_y, $s_w, $s_h, $d_x, $d_y, $d_w, $d_h); $layout[$target_name] = ['x'=>$x, 'y'=>$y]; $layout_stack[] = $target_name; } } } $min_x = PHP_INT_MAX; $min_y = PHP_INT_MAX; foreach ($layout as $screen_name=>$screen_offset) { $min_x = min($screen_offset['x'], $min_x); $min_y = min($screen_offset['y'], $min_y); } foreach ($layout as $screen_name=>$screen_offset) { list($current_x, $current_y) = $config->getScreen($screen_name)->getOffset(); if ($current_x != $screen_offset['x']-$min_x || $current_y != $screen_offset['y']-$min_y) { $config->getScreen($screen_name)->setOffset($screen_offset['x']-$min_x, $screen_offset['y']-$min_y); } } } }