*/ 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'])) { /** * @return int[] */ $calculate_layout = static function(int $s_x, int $s_y, int $s_w, int $s_h, int $d_x, int $d_y, int $d_w, int $d_h): array { $center = $s_x + ($s_w / 2); $x = $center - ($d_w / 2); $y = $s_y - $d_h; return [$x, $y]; }; } elseif (isset($link['below'])) { /** * @return int[] */ $calculate_layout = static function(int $s_x, int $s_y, int $s_w, int $s_h, int $d_x, int $d_y, int $d_w, int $d_h): array { $center = $s_x + ($s_w / 2); $x = $center - ($d_w / 2); $y = $s_y + $s_h; return [$x, $y]; }; } elseif (isset($link['left_of'])) { /** * @return int[] */ $calculate_layout = static function(int $s_x, int $s_y, int $s_w, int $s_h, int $d_x, int $d_y, int $d_w, int $d_h): array { return [$s_x - $d_w, $s_y]; }; } elseif (isset($link['right_of'])) { /** * @return int[] */ $calculate_layout = static function(int $s_x, int $s_y, int $s_w, int $s_h, int $d_x, int $d_y, int $d_w, int $d_h): array { 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 { [$s_x, $s_y] = $source->getOffset(); } [$s_w, $s_h] = $source->getDimensions(); if (isset($layout[$target_name])) { $d_x = $layout[$target_name]['x']; $d_y = $layout[$target_name]['y']; } else { [$d_x, $d_y] = $target->getOffset(); } [$d_w, $d_h] = $target->getDimensions(); [$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) { [$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); } } } }