diff --git a/core/lib/Drupal/Core/Layout/IconGenerator.php b/core/lib/Drupal/Core/Layout/IconGenerator.php index 1daa0b0..621672d 100644 --- a/core/lib/Drupal/Core/Layout/IconGenerator.php +++ b/core/lib/Drupal/Core/Layout/IconGenerator.php @@ -10,7 +10,7 @@ class IconGenerator implements IconGeneratorInterface { /** * {@inheritdoc} */ - public function generateSvgFromIconMap(array $icon_map, $width = 250, $height = 300, $stroke_width = 2, $padding = 5, $fill = 'lightgray', $stroke = 'black') { + public function generateSvg(array $regions, $width, $height, $stroke_width, $fill, $stroke) { $build = [ '#type' => 'html_tag', '#tag' => 'svg', @@ -20,40 +20,8 @@ public function generateSvgFromIconMap(array $icon_map, $width = 250, $height = ], ]; - $region_rects = []; - $num_rows = count($icon_map); - foreach ($icon_map as $row => $cols) { - $num_cols = count($cols); - foreach ($cols as $col => $region) { - if (!isset($region_rects[$region])) { - // The first instance of a region is always the starting point. - $x = $col * ($width / $num_cols); - $y = ($row / $num_rows) * $height; - $region_rects[$region] = [ - 'x' => $x, - 'y' => $y, - 'width' => ($width / $num_cols) - $padding, - 'height' => ($height / $num_rows) - $padding, - 'last_col' => $col, - 'last_row' => $row, - ]; - } - else { - // Only increase the width/height if we've moved in that direction. - if ($region_rects[$region]['last_col'] != $col) { - $region_rects[$region]['width'] += ($width / $num_cols); - $region_rects[$region]['last_col'] = $col; - } - if ($region_rects[$region]['last_row'] != $row) { - $region_rects[$region]['height'] += ($height / $num_rows); - $region_rects[$region]['last_row'] = $row; - } - } - } - } - // Append each polygon to the SVG. - foreach ($region_rects as $region => $attributes) { + foreach ($regions as $region => $attributes) { // Group our regions allows for metadata, nested elements, and tooltips. $build[$region] = [ '#type' => 'html_tag', @@ -85,4 +53,110 @@ public function generateSvgFromIconMap(array $icon_map, $width = 250, $height = return $build; } + /** + * {@inheritdoc} + */ + public function generateSvgFromIconMap(array $icon_map, $width, $height, $stroke_width, $padding, $fill, $stroke) { + $regions = $this->calculateSvgValues($icon_map, $width, $height, $stroke_width, $padding); + return $this->generateSvg($regions, $width, $height, $stroke_width, $fill, $stroke); + + } + + /** + * Calculates the dimensions and offsets of all regions. + * + * @param array $rows + * A two dimensional array representing the visual output of the layout. + * See \Drupal\Core\Layout\IconGeneratorInterface::generateSvgFromIconMap(). + * @param int $width + * The width of the generated SVG. + * @param int $height + * The height of the generated SVG. + * @param int $stroke_width + * The width of region borders. + * @param int $padding + * The padding between regions. Any value above 0 is valid. + * + * @return mixed[] + * An array keyed by region name, with each element containing the 'height', + * 'width', and 'x' and 'y' offsets of each region. + */ + protected function calculateSvgValues(array $rows, $width, $height, $stroke_width, $padding) { + $region_rects = []; + if (!$rows) { + return $region_rects; + } + + $num_rows = count($rows); + $row_height = $this->getLength($num_rows, $height, $stroke_width, $padding); + foreach ($rows as $row => $cols) { + $num_cols = count($cols); + $column_width = $this->getLength($num_cols, $width, $stroke_width, $padding); + $vertical_offset = $this->getOffset($row, $row_height, $stroke_width, $padding); + foreach ($cols as $col => $region) { + if (!isset($region_rects[$region])) { + $region_rects[$region] = [ + 'x' => $this->getOffset($col, $column_width, $stroke_width, $padding), + 'y' => $vertical_offset, + 'width' => $column_width, + 'height' => $row_height, + // Store the original values. + 'original_width' => $column_width, + 'original_height' => $row_height, + ]; + } + else { + $region_rects[$region]['width'] = $this->getOffset($col, $column_width, $stroke_width, $padding) - $region_rects[$region]['x'] + $region_rects[$region]['original_width']; + $region_rects[$region]['height'] = $this->getOffset($row, $row_height, $stroke_width, $padding) - $region_rects[$region]['y'] + $region_rects[$region]['original_height']; + } + } + } + return $region_rects; + } + + /** + * Gets the offset for this region. + * + * @param int $delta + * The zero-based delta of the region. + * @param int $length + * The height or width of the region. + * @param int $stroke_width + * The width of the region borders. + * @param int $padding + * The padding between regions. + * + * @return int + * The offset for this region. + */ + protected function getOffset($delta, $length, $stroke_width, $padding) { + // Half of the stroke width is drawn outside the dimensions. + $stroke_width /= 2; + // For every region in front of this add two strokes, as well as one + // directly in front. + $num_of_strokes = 2 * $delta + 1; + return ($num_of_strokes * $stroke_width) + ($delta * ($length + $padding)); + } + + /** + * Gets the height or width of a region. + * + * @param int $number_of_regions + * The total number of regions. + * @param int $length + * The total height or width of the icon. + * @param int $stroke_width + * The width of the region borders. + * @param int $padding + * The padding between regions. + * + * @return float|int + * The height or width of a region. + */ + protected function getLength($number_of_regions, $length, $stroke_width, $padding) { + $total_stroke = $number_of_regions * $stroke_width; + $total_padding = ($number_of_regions - 1) * $padding; + return ($length - $total_padding - $total_stroke) / $number_of_regions; + } + } diff --git a/core/lib/Drupal/Core/Layout/IconGeneratorInterface.php b/core/lib/Drupal/Core/Layout/IconGeneratorInterface.php index 711f8c9..56777f9 100644 --- a/core/lib/Drupal/Core/Layout/IconGeneratorInterface.php +++ b/core/lib/Drupal/Core/Layout/IconGeneratorInterface.php @@ -8,7 +8,7 @@ interface IconGeneratorInterface { /** - * Generates a SVG based on a Layout's icon map. + * Generates an SVG based on an icon map. * * @param array $icon_map * A two dimensional array representing the visual output of the layout. @@ -31,22 +31,43 @@ * - [first, second, second, third] * - [bottom]. * @param int $width - * (optional) The width of the generated SVG. Defaults to 250. + * The width of the generated SVG. * @param int $height - * (optional) The height of the generated SVG. Defaults to 300. + * The height of the generated SVG. * @param int $stroke_width - * (optional) The width of region borders. Defaults to 2. + * The width of region borders. * @param int $padding - * (optional) The padding between regions. Any value above 0 is valid. - * Defaults to 5. + * The padding between regions. * @param string $fill - * (optional) The fill color of regions. Defaults to 'lightgray'. + * The fill color of regions. * @param string $stroke - * (optional) The color of region borders. Defaults to 'black'. + * The color of region borders. * * @return array * A render array representing a SVG icon. */ - public function generateSvgFromIconMap(array $icon_map, $width = 250, $height = 300, $stroke_width = 2, $padding = 5, $fill = 'lightgray', $stroke = 'black'); + public function generateSvgFromIconMap(array $icon_map, $width, $height, $stroke_width, $padding, $fill, $stroke); + + /** + * Generates an SVG. + * + * @param mixed[] $regions + * An array keyed by region name, with each element containing the 'height', + * 'width', and 'x' and 'y' offsets of each region. + * @param int $width + * The width of the generated SVG. + * @param int $height + * The height of the generated SVG. + * @param int $stroke_width + * The width of region borders. + * @param string $fill + * The fill color of regions. + * @param string $stroke + * The color of region borders. + * + * @return array + * A render array representing a SVG icon. + */ + public function generateSvg(array $regions, $width, $height, $stroke_width, $fill, $stroke); } diff --git a/core/lib/Drupal/Core/Layout/LayoutDefinition.php b/core/lib/Drupal/Core/Layout/LayoutDefinition.php index cc452a7..a8e43dc 100644 --- a/core/lib/Drupal/Core/Layout/LayoutDefinition.php +++ b/core/lib/Drupal/Core/Layout/LayoutDefinition.php @@ -428,7 +428,7 @@ public function setIconMap($icon_map) { * @return array * A render array for the icon. */ - public function getIcon($width = 250, $height = 300, $stroke_width = 2, $padding = 5, $fill = 'lightgray', $stroke = 'black') { + public function getIcon($width = 125, $height = 150, $stroke_width = 1, $padding = 4, $fill = '#f5f5f2', $stroke = '#666') { $icon = []; if ($icon_path = $this->getIconPath()) { $icon = [ diff --git a/core/tests/Drupal/KernelTests/Core/Layout/IconGeneratorTest.php b/core/tests/Drupal/KernelTests/Core/Layout/IconGeneratorTest.php index bc69478..c08475a 100644 --- a/core/tests/Drupal/KernelTests/Core/Layout/IconGeneratorTest.php +++ b/core/tests/Drupal/KernelTests/Core/Layout/IconGeneratorTest.php @@ -13,14 +13,18 @@ class IconGeneratorTest extends KernelTestBase { /** + * @covers ::generateSvg * @covers ::generateSvgFromIconMap + * @covers ::calculateSvgValues + * @covers ::getLength + * @covers ::getOffset * * @dataProvider providerTestGenerateSvgFromIconMap */ - public function testGenerateSvgFromIconMap($icon_map, $expected) { + public function testGenerateSvgFromIconMap($icon_map, $expected, $stroke_width = 2) { $renderer = $this->container->get('renderer'); $icon_generator = new IconGenerator(); - $build = $icon_generator->generateSvgFromIconMap($icon_map); + $build = $icon_generator->generateSvgFromIconMap($icon_map, 250, 300, $stroke_width, 4, 'lightgray', 'black'); $output = (string) $renderer->executeInRenderContext(new RenderContext(), function () use ($build, $renderer) { return $renderer->render($build); }); @@ -38,15 +42,28 @@ public function providerTestGenerateSvgFromIconMap() { $data['two_column'][] = [['left', 'right']]; $data['two_column'][] = <<<'EOD' left - + right - + EOD; + $data['two_column_no_stroke'][] = [['left', 'right']]; + $data['two_column_no_stroke'][] = <<<'EOD' +left + + +right + + + + +EOD; + $data['two_column_no_stroke'][] = 0; + $data['stacked'][] = [ ['sidebar', 'top', 'top'], ['sidebar', 'left', 'right'], @@ -56,28 +73,28 @@ public function providerTestGenerateSvgFromIconMap() { ]; $data['stacked'][] = <<<'EOD' sidebar - + top - + left - + right - + middle - + footer_left - + footer_right - + footer_full - +