diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 7be37ffcae..06aba33cb3 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -556,6 +556,7 @@ set( COMMON_SRCS
     hotkeys_basic.cpp
     kiface_base.cpp
     kiway_player.cpp
+    layer_presentation.cpp
     lib_table_grid_tricks.cpp
     lib_tree_model.cpp
     lib_tree_model_adapter.cpp
diff --git a/common/layer_presentation.cpp b/common/layer_presentation.cpp
new file mode 100644
index 0000000000..0f2a9bc2aa
--- /dev/null
+++ b/common/layer_presentation.cpp
@@ -0,0 +1,171 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include "layer_presentation.h"
+
+#include <wx/bitmap.h>
+#include <wx/dcmemory.h>
+
+#include <gal/color4d.h>
+
+
+using namespace KIGFX;
+
+
+void LAYER_PRESENTATION::DrawColorSwatch( wxBitmap& aLayerbmp, const COLOR4D& aBackground,
+                                      const COLOR4D& aColor )
+{
+    wxMemoryDC bmpDC;
+    wxBrush    brush;
+
+    // Prepare Bitmap
+    bmpDC.SelectObject( aLayerbmp );
+
+    brush.SetStyle( wxBRUSHSTYLE_SOLID );
+
+    if( aBackground != COLOR4D::UNSPECIFIED )
+    {
+        brush.SetColour( aBackground.WithAlpha( 1.0 ).ToColour() );
+        bmpDC.SetBrush( brush );
+        bmpDC.DrawRectangle( 0, 0, aLayerbmp.GetWidth(), aLayerbmp.GetHeight() );
+    }
+
+    brush.SetColour( aColor.ToColour() );
+    bmpDC.SetBrush( brush );
+    bmpDC.DrawRectangle( 0, 0, aLayerbmp.GetWidth(), aLayerbmp.GetHeight() );
+
+    bmpDC.SetBrush( *wxTRANSPARENT_BRUSH );
+    bmpDC.SetPen( *wxBLACK_PEN );
+    bmpDC.DrawRectangle( 0, 0, aLayerbmp.GetWidth(), aLayerbmp.GetHeight() );
+}
+
+
+void LAYER_PRESENTATION::DrawColorSwatch( wxBitmap& aLayerbmp, int aLayer ) const
+{
+    const COLOR4D bgColor = getLayerColor( LAYER_PCB_BACKGROUND );
+    const COLOR4D color = getLayerColor( aLayer );
+
+    DrawColorSwatch( aLayerbmp, bgColor, color );
+}
+
+
+static constexpr unsigned BM_LAYERICON_SIZE = 24;
+static const char         s_BitmapLayerIcon[BM_LAYERICON_SIZE][BM_LAYERICON_SIZE] = {
+    // 0 = draw pixel with white
+    // 1 = draw pixel with black
+    // 2 = draw pixel with top layer from router pair
+    // 3 = draw pixel with bottom layer from router pair
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+    { 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
+};
+
+static COLOR4D ICON_WHITE{ 0.86, 0.86, 0.86, 1.0 };
+static COLOR4D ICON_BLACK{ 0.28, 0.28, 0.28, 1.0 };
+
+
+std::unique_ptr<wxBitmap> LAYER_PRESENTATION::CreateLayerPairIcon( const COLOR4D& aBgColor, const COLOR4D& aTopColor,
+                                               const COLOR4D& aBottomColor, int aScale )
+{
+    auto layerPairBitmap = std::make_unique<wxBitmap>( BM_LAYERICON_SIZE, BM_LAYERICON_SIZE );
+
+    // Draw the icon, with colors according to the router's layer pair
+    wxMemoryDC iconDC;
+    iconDC.SelectObject( *layerPairBitmap );
+    wxBrush brush;
+    wxPen   pen;
+    int     buttonColor = -1;
+
+    brush.SetStyle( wxBRUSHSTYLE_SOLID );
+    brush.SetColour( aBgColor.WithAlpha( 1.0 ).ToColour() );
+    iconDC.SetBrush( brush );
+    iconDC.DrawRectangle( 0, 0, BM_LAYERICON_SIZE, BM_LAYERICON_SIZE );
+
+    for( unsigned ii = 0; ii < BM_LAYERICON_SIZE; ii++ )
+    {
+        for( unsigned jj = 0; jj < BM_LAYERICON_SIZE; jj++ )
+        {
+            if( s_BitmapLayerIcon[ii][jj] != buttonColor )
+            {
+                switch( s_BitmapLayerIcon[ii][jj] )
+                {
+                default:
+                case 0: pen.SetColour( ICON_WHITE.ToColour() ); break;
+                case 1: pen.SetColour( ICON_BLACK.ToColour() ); break;
+                case 2: pen.SetColour( aTopColor.ToColour() ); break;
+                case 3: pen.SetColour( aBottomColor.ToColour() ); break;
+                }
+
+                buttonColor = s_BitmapLayerIcon[ii][jj];
+                iconDC.SetPen( pen );
+            }
+
+            iconDC.DrawPoint( jj, ii );
+        }
+    }
+
+    // Deselect the bitmap from the DC in order to delete the MemoryDC safely without
+    // deleting the bitmap
+    iconDC.SelectObject( wxNullBitmap );
+
+    // Scale the bitmap
+    wxImage image = layerPairBitmap->ConvertToImage();
+
+    // "NEAREST" causes less mixing of colors
+    image.Rescale( aScale * image.GetWidth() / 4, aScale * image.GetHeight() / 4,
+                   wxIMAGE_QUALITY_NEAREST );
+
+    return std::make_unique<wxBitmap>( image );
+}
+
+
+std::unique_ptr<wxBitmap> LAYER_PRESENTATION::CreateLayerPairIcon( const LAYER_PAIR& aPair,
+                                                                   int               aScale ) const
+{
+    const COLOR4D bgColor = getLayerColor( LAYER_PCB_BACKGROUND );
+    const COLOR4D topColor = getLayerColor( aPair.GetLayerA() );
+    const COLOR4D bottomColor = getLayerColor( aPair.GetLayerB() );
+
+    return CreateLayerPairIcon( bgColor, topColor, bottomColor, aScale );
+}
\ No newline at end of file
diff --git a/common/widgets/layer_box_selector.cpp b/common/widgets/layer_box_selector.cpp
index d99a07c618..b7450522e9 100644
--- a/common/widgets/layer_box_selector.cpp
+++ b/common/widgets/layer_box_selector.cpp
@@ -47,34 +47,6 @@ bool LAYER_SELECTOR::SetLayersHotkeys( bool value )
 }
 
 
-void LAYER_SELECTOR::DrawColorSwatch( wxBitmap& aLayerbmp, const COLOR4D& aBackground,
-                                      const COLOR4D& aColor )
-{
-    wxMemoryDC bmpDC;
-    wxBrush    brush;
-
-    // Prepare Bitmap
-    bmpDC.SelectObject( aLayerbmp );
-
-    brush.SetStyle( wxBRUSHSTYLE_SOLID );
-
-    if( aBackground != COLOR4D::UNSPECIFIED )
-    {
-        brush.SetColour( aBackground.WithAlpha( 1.0 ).ToColour() );
-        bmpDC.SetBrush( brush );
-        bmpDC.DrawRectangle( 0, 0, aLayerbmp.GetWidth(), aLayerbmp.GetHeight() );
-    }
-
-    brush.SetColour( aColor.ToColour() );
-    bmpDC.SetBrush( brush );
-    bmpDC.DrawRectangle( 0, 0, aLayerbmp.GetWidth(), aLayerbmp.GetHeight() );
-
-    bmpDC.SetBrush( *wxTRANSPARENT_BRUSH );
-    bmpDC.SetPen( *wxBLACK_PEN );
-    bmpDC.DrawRectangle( 0, 0, aLayerbmp.GetWidth(), aLayerbmp.GetHeight() );
-}
-
-
 LAYER_BOX_SELECTOR::LAYER_BOX_SELECTOR( wxWindow* parent, wxWindowID id, const wxPoint& pos,
                                         const wxSize& size, int n, const wxString choices[] ) :
         wxBitmapComboBox( parent, id, wxEmptyString, pos, size, n, choices, wxCB_READONLY ),
diff --git a/gerbview/widgets/gbr_layer_box_selector.cpp b/gerbview/widgets/gbr_layer_box_selector.cpp
index 89471c7231..f4ea864b7c 100644
--- a/gerbview/widgets/gbr_layer_box_selector.cpp
+++ b/gerbview/widgets/gbr_layer_box_selector.cpp
@@ -23,6 +23,10 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
+#include "gbr_layer_box_selector.h"
+
+#include <layer_presentation.h>
+
 #include <gerbview_frame.h>
 #include <gerber_file_image_list.h>
 
@@ -30,7 +34,44 @@
 #include <dpi_scaling_common.h>
 #endif
 
-#include "gbr_layer_box_selector.h"
+
+/**
+ * Gerbview-specific implementation of the LAYER_PRESENTATION interface.
+ */
+class GBR_LAYER_PRESENTATION : public LAYER_PRESENTATION
+{
+public:
+    GBR_LAYER_PRESENTATION( GERBVIEW_FRAME& aFrame ) : m_frame( aFrame ) {}
+
+    // Returns a color index from the layer id
+    COLOR4D getLayerColor( int aLayer ) const override
+    {
+        return m_frame.GetLayerColor( GERBER_DRAW_LAYER( aLayer ) );
+    }
+
+    // Returns the name of the layer id
+    wxString getLayerName( int aLayer ) const override
+    {
+        GERBER_FILE_IMAGE_LIST& images = GERBER_FILE_IMAGE_LIST::GetImagesList();
+        wxString                name = images.GetDisplayName( aLayer );
+
+        return name;
+    }
+
+private:
+    GERBVIEW_FRAME& m_frame;
+};
+
+
+GBR_LAYER_BOX_SELECTOR::GBR_LAYER_BOX_SELECTOR( wxWindow* parent, wxWindowID id, const wxPoint& pos,
+                                                const wxSize& size, int n,
+                                                const wxString choices[] ) :
+        LAYER_BOX_SELECTOR( parent, id, pos, size, n, choices ),
+        m_layerPresentation( std::make_unique<GBR_LAYER_PRESENTATION>(
+                static_cast<GERBVIEW_FRAME&>( *parent->GetParent() ) ) )
+{
+    m_layerhotkeys = false;
+}
 
 void GBR_LAYER_BOX_SELECTOR::Resync()
 {
@@ -57,14 +98,14 @@ void GBR_LAYER_BOX_SELECTOR::Resync()
         {
             wxBitmap bmp( size * scale, size * scale );
 
-            DrawColorSwatch( bmp, getLayerColor( LAYER_PCB_BACKGROUND ), getLayerColor( layerid ) );
+            m_layerPresentation->DrawColorSwatch( bmp, layerid );
 
             bmp.SetScaleFactor( scale );
             bitmaps.push_back( bmp );
         }
 
-        Append( getLayerName( layerid ), wxBitmapBundle::FromBitmaps( bitmaps ),
-                (void*) (intptr_t) layerid );
+        Append( m_layerPresentation->getLayerName( layerid ),
+                wxBitmapBundle::FromBitmaps( bitmaps ), (void*) (intptr_t) layerid );
     }
 
     // Ensure the size of the widget is enough to show the text and the icon
@@ -88,22 +129,3 @@ void GBR_LAYER_BOX_SELECTOR::Resync()
 
     Thaw();
 }
-
-
-// Returns a color index from the layer id
-COLOR4D GBR_LAYER_BOX_SELECTOR::getLayerColor( int aLayer ) const
-{
-    GERBVIEW_FRAME* frame = (GERBVIEW_FRAME*) GetParent()->GetParent();
-
-    return frame->GetLayerColor( GERBER_DRAW_LAYER( aLayer ) );
-}
-
-
-// Returns the name of the layer id
-wxString GBR_LAYER_BOX_SELECTOR::getLayerName( int aLayer ) const
-{
-    GERBER_FILE_IMAGE_LIST& images = GERBER_FILE_IMAGE_LIST::GetImagesList();
-    wxString name = images.GetDisplayName( aLayer );
-
-    return name;
-}
diff --git a/gerbview/widgets/gbr_layer_box_selector.h b/gerbview/widgets/gbr_layer_box_selector.h
index 44dbba6a39..b1d2474a11 100644
--- a/gerbview/widgets/gbr_layer_box_selector.h
+++ b/gerbview/widgets/gbr_layer_box_selector.h
@@ -23,35 +23,30 @@
  */
 
 #ifndef GBR_LAYER_BOX_SELECTOR_H
-#define GBR_LAYER_BOX_SELECTOR_H 1
+#define GBR_LAYER_BOX_SELECTOR_H
+
+#include <memory>
 
 #include <widgets/layer_box_selector.h>
 
+class LAYER_PRESENTATION;
 
 // class to display a layer list in GerbView.
 class GBR_LAYER_BOX_SELECTOR : public LAYER_BOX_SELECTOR
 {
 public:
-    GBR_LAYER_BOX_SELECTOR( wxWindow* parent, wxWindowID id,
-                            const wxPoint& pos = wxDefaultPosition,
-                            const wxSize& size = wxDefaultSize,
-                            int n = 0, const wxString choices[] = nullptr ) :
-        LAYER_BOX_SELECTOR( parent, id, pos, size, n, choices )
-    {
-        m_layerhotkeys = false;
-    }
+    GBR_LAYER_BOX_SELECTOR( wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition,
+                            const wxSize& size = wxDefaultSize, int n = 0,
+                            const wxString choices[] = nullptr );
 
     // Reload the Layers names and bitmaps
     void Resync() override;
 
-    // Return a color index from the layer id
-    COLOR4D getLayerColor( int aLayer ) const override;
-
     // Return true if the layer id is enabled (i.e. is it should be displayed)
     bool isLayerEnabled( int aLayer ) const override { return true; }
 
-    // Return the name of the layer id
-    wxString getLayerName( int aLayer ) const override;
+private:
+    std::unique_ptr<LAYER_PRESENTATION> m_layerPresentation;
 };
 
 #endif //GBR_LAYER_BOX_SELECTOR_H
diff --git a/include/layer_presentation.h b/include/layer_presentation.h
new file mode 100644
index 0000000000..9dc208152c
--- /dev/null
+++ b/include/layer_presentation.h
@@ -0,0 +1,73 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef LAYER_PRESENTATION_H
+#define LAYER_PRESENTATION_H
+
+#include <gal/color4d.h>
+#include <layer_ids.h>
+#include <layer_pairs.h>
+
+class wxBitmap;
+
+using KIGFX::COLOR4D;
+
+/**
+ * Base class for an object that can provide information about
+ * presenting layers (colours, etc).
+ */
+class LAYER_PRESENTATION
+{
+public:
+    // Return a color index from the layer id
+    virtual COLOR4D getLayerColor( int aLayer ) const = 0;
+
+    // Return the name of the layer id
+    virtual wxString getLayerName( int aLayer ) const = 0;
+
+    // Fill the layer bitmap aLayerbmp with the layer color
+    static void DrawColorSwatch( wxBitmap& aLayerbmp, const COLOR4D& aBackground,
+                                 const COLOR4D& aColor );
+
+    /**
+     * Fill the layer bitmap aLayerbmp with the layer color
+     * for the layer ID.
+     */
+    void DrawColorSwatch( wxBitmap& aLayerbmp, int aLayer ) const;
+
+    /**
+     * Create a layer pair "side-by-side swatch" icon
+     */
+    static std::unique_ptr<wxBitmap> CreateLayerPairIcon( const KIGFX::COLOR4D& aBgColor,
+                                                          const KIGFX::COLOR4D& aTopColor,
+                                                          const KIGFX::COLOR4D& aBottomColor,
+                                                          int                   aScale );
+
+    /**
+     * Create a layer pair "side-by-side swatch" icon for the given
+     * layer pair with the style of this presentation.
+     */
+    std::unique_ptr<wxBitmap> CreateLayerPairIcon( const LAYER_PAIR& aPair, int aScale ) const;
+};
+
+#endif // LAYER_PRESENTATION_H
\ No newline at end of file
diff --git a/include/widgets/layer_box_selector.h b/include/widgets/layer_box_selector.h
index e275bd1bcc..2f1849ff01 100644
--- a/include/widgets/layer_box_selector.h
+++ b/include/widgets/layer_box_selector.h
@@ -26,10 +26,7 @@
 #define LAYER_BOX_SELECTOR_H
 
 #include <wx/bmpcbox.h>
-#include <gal/color4d.h>
-#include <layer_ids.h>
 
-using KIGFX::COLOR4D;
 
 /**
  * Base class to build a layer list.
@@ -43,17 +40,7 @@ public:
 
     bool SetLayersHotkeys( bool value );
 
-    // Fill the layer bitmap aLayerbmp with the layer color
-    static void DrawColorSwatch( wxBitmap& aLayerbmp, const COLOR4D& aBackground,
-                                 const COLOR4D& aColor );
-
 protected:
-    // Return a color index from the layer id
-    virtual COLOR4D getLayerColor( int aLayer ) const = 0;
-
-    // Return the name of the layer id
-    virtual wxString getLayerName( int aLayer ) const = 0;
-
     // Return true if the layer id is enabled (i.e. is it should be displayed)
     virtual bool isLayerEnabled( int aLayer ) const = 0;
 
diff --git a/pcbnew/board_stackup_manager/panel_board_stackup.cpp b/pcbnew/board_stackup_manager/panel_board_stackup.cpp
index 9e96dc1b58..ef7d0022ad 100644
--- a/pcbnew/board_stackup_manager/panel_board_stackup.cpp
+++ b/pcbnew/board_stackup_manager/panel_board_stackup.cpp
@@ -26,6 +26,7 @@
 #include <pcb_edit_frame.h>
 #include <board.h>
 #include <board_design_settings.h>
+#include <layer_presentation.h>
 #include <dialogs/dialog_color_picker.h>
 #include <widgets/paged_dialog.h>
 #include <widgets/layer_box_selector.h>
@@ -684,7 +685,7 @@ void PANEL_SETUP_BOARD_STACKUP::synchronizeWithBoard( bool aFullSync )
                 {
                     bm_combo->SetString( selected, item->GetColor( sub_item ) );
                     wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
-                    LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D(), custom_color );
+                    LAYER_PRESENTATION::DrawColorSwatch( layerbmp, COLOR4D(), custom_color );
                     bm_combo->SetItemBitmap( selected, layerbmp );
                 }
             }
@@ -1404,7 +1405,7 @@ void PANEL_SETUP_BOARD_STACKUP::onColorSelected( wxCommandEvent& event )
             combo->SetString( idx, color.ToHexString() );
 
             wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
-            LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ), color );
+            LAYER_PRESENTATION::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ), color );
             combo->SetItemBitmap( combo->GetCount() - 1, layerbmp );
 
             combo->SetSelection( idx );
@@ -1654,7 +1655,7 @@ wxBitmapComboBox* PANEL_SETUP_BOARD_STACKUP::createColorBox( BOARD_STACKUP_ITEM*
         }
 
         wxBitmap layerbmp( m_colorSwatchesSize.x, m_colorSwatchesSize.y );
-        LAYER_SELECTOR::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ), curr_color );
+        LAYER_PRESENTATION::DrawColorSwatch( layerbmp, COLOR4D( 0, 0, 0, 0 ), curr_color );
 
         combo->Append( label, layerbmp );
     }
diff --git a/pcbnew/grid_layer_box_helpers.cpp b/pcbnew/grid_layer_box_helpers.cpp
index 57cfd5c278..30ee18ad34 100644
--- a/pcbnew/grid_layer_box_helpers.cpp
+++ b/pcbnew/grid_layer_box_helpers.cpp
@@ -22,17 +22,20 @@
  */
 
 #include <grid_layer_box_helpers.h>
+
+#include <wx/textctrl.h>
+
 #include <pgm_base.h>
 #include <settings/settings_manager.h>
 #include <settings/color_settings.h>
 #include <footprint_editor_settings.h>
 #include <board.h>
+#include <layer_presentation.h>
 #include <lset.h>
 #include <pcb_edit_frame.h>
 #include <pcb_layer_box_selector.h>
 #include <settings/color_settings.h>
 #include <widgets/layer_box_selector.h>
-#include <wx/textctrl.h>
 
 
 //-------- Custom wxGridCellRenderers --------------------------------------------------
@@ -76,9 +79,9 @@ void GRID_CELL_LAYER_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC&
     int      size = KiROUND( 14 * aDC.GetContentScaleFactor() );
     wxBitmap bitmap( size, size );
 
-    LAYER_SELECTOR::DrawColorSwatch( bitmap,
-                                     cs->GetColor( ToLAYER_ID( LAYER_PCB_BACKGROUND ) ),
-                                     cs->GetColor( ToLAYER_ID( value ) ) );
+    LAYER_PRESENTATION::DrawColorSwatch( bitmap,
+                                         cs->GetColor( ToLAYER_ID( LAYER_PCB_BACKGROUND ) ),
+                                         cs->GetColor( ToLAYER_ID( value ) ) );
 
     aDC.DrawBitmap( bitmap, rect.GetLeft() + 4,
                     rect.GetTop() + ( rect.GetHeight() - bitmap.GetHeight() ) / 2, true );
diff --git a/pcbnew/pcb_layer_box_selector.cpp b/pcbnew/pcb_layer_box_selector.cpp
index fd17b07292..ac831a0598 100644
--- a/pcbnew/pcb_layer_box_selector.cpp
+++ b/pcbnew/pcb_layer_box_selector.cpp
@@ -23,19 +23,35 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#include <pgm_base.h>
-#include <settings/settings_manager.h>
-#include <footprint_editor_settings.h>
-#include <pcb_edit_frame.h>
-#include <layer_ids.h>
-#include <settings/color_settings.h>
+#include "pcb_layer_box_selector.h"
+
 #include <board.h>
-#include <pcb_layer_box_selector.h>
+#include <layer_ids.h>
+#include <pcb_edit_frame.h>
+#include <pcb_layer_presentation.h>
+#include <settings/color_settings.h>
 #include <tools/pcb_actions.h>
 #include <dpi_scaling_common.h>
 
 
-// class to display a layer list in a wxBitmapComboBox.
+PCB_LAYER_BOX_SELECTOR::PCB_LAYER_BOX_SELECTOR( wxWindow* parent, wxWindowID id,
+                                                const wxString& value, const wxPoint& pos,
+                                                const wxSize& size, int n, const wxString choices[],
+                                                int style ) :
+        LAYER_BOX_SELECTOR( parent, id, pos, size, n, choices ), m_boardFrame( nullptr ),
+        m_showNotEnabledBrdlayers( false ),
+        m_layerPresentation( std::make_unique<PCB_LAYER_PRESENTATION>(
+                nullptr ) ) // The parent isn't awlays the frame
+{
+}
+
+
+void PCB_LAYER_BOX_SELECTOR::SetBoardFrame( PCB_BASE_FRAME* aFrame )
+{
+    m_boardFrame = aFrame;
+    m_layerPresentation->SetBoardFrame( m_boardFrame );
+}
+
 
 // Reload the Layers
 void PCB_LAYER_BOX_SELECTOR::Resync()
@@ -64,13 +80,13 @@ void PCB_LAYER_BOX_SELECTOR::Resync()
         {
             wxBitmap bmp( size * scale, size * scale );
 
-            DrawColorSwatch( bmp, getLayerColor( LAYER_PCB_BACKGROUND ), getLayerColor( layerid ) );
+            m_layerPresentation->DrawColorSwatch( bmp, layerid );
 
             bmp.SetScaleFactor( scale );
             bitmaps.push_back( bmp );
         }
 
-        wxString layername = getLayerName( layerid ) + layerstatus;
+        wxString layername = m_layerPresentation->getLayerName( layerid ) + layerstatus;
 
         if( m_layerhotkeys )
         {
@@ -122,31 +138,3 @@ LSET PCB_LAYER_BOX_SELECTOR::getEnabledLayers() const
     else
         return footprintEditorLayers;
 }
-
-
-// Returns a color index from the layer id
-COLOR4D PCB_LAYER_BOX_SELECTOR::getLayerColor( int aLayer ) const
-{
-    if( m_boardFrame )
-    {
-        return m_boardFrame->GetColorSettings()->GetColor( aLayer );
-    }
-    else
-    {
-        SETTINGS_MANAGER&          mgr = Pgm().GetSettingsManager();
-        FOOTPRINT_EDITOR_SETTINGS* settings = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>();
-        COLOR_SETTINGS*            current  = mgr.GetColorSettings( settings->m_ColorTheme );
-
-        return current->GetColor( aLayer );
-    }
-}
-
-
-// Returns the name of the layer id
-wxString PCB_LAYER_BOX_SELECTOR::getLayerName( int aLayer ) const
-{
-    if( m_boardFrame )
-        return m_boardFrame->GetBoard()->GetLayerName( ToLAYER_ID( aLayer ) );
-    else
-        return BOARD::GetStandardLayerName( ToLAYER_ID( aLayer ) );
-}
diff --git a/pcbnew/pcb_layer_box_selector.h b/pcbnew/pcb_layer_box_selector.h
index e9ce2c9b7c..0c58828cb7 100644
--- a/pcbnew/pcb_layer_box_selector.h
+++ b/pcbnew/pcb_layer_box_selector.h
@@ -25,10 +25,14 @@
 #ifndef PCB_LAYER_BOX_SELECTOR_H
 #define PCB_LAYER_BOX_SELECTOR_H
 
+#include <memory>
+
 #include <lset.h>
 #include <widgets/layer_box_selector.h>
 
 class PCB_BASE_FRAME;
+class PCB_LAYER_PRESENTATION;
+
 
 /**
  * Class to display a pcb layer list in a wxBitmapComboBox.
@@ -39,21 +43,15 @@ public:
     // If you are thinking the constructor is a bit curious, just remember it is automatically
     // generated when used in wxFormBuilder files, and so must have the same signature as the
     // wxBitmapComboBox constructor.  In particular, value and style are not used by this class.
-    PCB_LAYER_BOX_SELECTOR( wxWindow* parent, wxWindowID id,
-                            const wxString& value = wxEmptyString,
+    PCB_LAYER_BOX_SELECTOR( wxWindow* parent, wxWindowID id, const wxString& value = wxEmptyString,
                             const wxPoint& pos = wxDefaultPosition,
-                            const wxSize& size = wxDefaultSize,
-                            int n = 0, const wxString choices[] = nullptr, int style = 0 ) :
-        LAYER_BOX_SELECTOR( parent, id, pos, size, n, choices )
-    {
-        m_boardFrame = nullptr;
-        m_showNotEnabledBrdlayers = false;
-    }
+                            const wxSize& size = wxDefaultSize, int n = 0,
+                            const wxString choices[] = nullptr, int style = 0 );
 
     // SetBoardFrame should be called after creating a PCB_LAYER_BOX_SELECTOR.  It is not passed
     // through the constructor because it must have the same signature as wxBitmapComboBox for
     // use with wxFormBuilder.
-    void SetBoardFrame( PCB_BASE_FRAME* aFrame ) { m_boardFrame = aFrame; };
+    void SetBoardFrame( PCB_BASE_FRAME* aFrame );
 
     // SetLayerSet allows disabling some layers, which are not shown in list
     void SetNotAllowedLayerSet( LSET aMask ) { m_layerMaskDisable = aMask; }
@@ -70,15 +68,9 @@ public:
     void ShowNonActivatedLayers( bool aShow ) { m_showNotEnabledBrdlayers = aShow; }
 
 private:
-    // Returns a color index from the layer id
-    COLOR4D getLayerColor( int aLayer ) const override;
-
     // Returns true if the layer id is enabled (i.e. if it should be displayed)
     bool isLayerEnabled( int aLayer ) const override;
 
-    // Returns the name of the layer id
-    wxString getLayerName( int aLayer ) const override;
-
     LSET getEnabledLayers() const;
 
     PCB_BASE_FRAME* m_boardFrame;
@@ -89,6 +81,8 @@ private:
                                         // (with not activated layers flagged)
     wxString m_undefinedLayerName;      // if not empty add an item with this name which sets
                                         // the layer to UNDEFINED_LAYER
+
+    std::unique_ptr<PCB_LAYER_PRESENTATION> m_layerPresentation;
 };
 
 #endif // PCB_LAYER_BOX_SELECTOR_H
diff --git a/pcbnew/pcb_layer_presentation.h b/pcbnew/pcb_layer_presentation.h
new file mode 100644
index 0000000000..4b39de422d
--- /dev/null
+++ b/pcbnew/pcb_layer_presentation.h
@@ -0,0 +1,53 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef PCB_LAYER_PRESENTATION_H
+#define PCB_LAYER_PRESENTATION_H
+
+#include <layer_presentation.h>
+
+class PCB_BASE_FRAME;
+
+/**
+ * Class that manages the presentation of PCB layers in a PCB frame.
+ */
+class PCB_LAYER_PRESENTATION : public LAYER_PRESENTATION
+{
+public:
+    PCB_LAYER_PRESENTATION( PCB_BASE_FRAME* aFrame );
+
+    COLOR4D getLayerColor( int aLayer ) const override;
+
+    wxString getLayerName( int aLayer ) const override;
+
+    /**
+     * Annoying post-ctor initialization (for when PCB_LAYER_BOX_SELECTOR doesn't
+     * have access to the PCB_BASE_FRAME at construction time).
+     */
+    void SetBoardFrame( PCB_BASE_FRAME* aFrame ) { m_boardFrame = aFrame; }
+
+private:
+    PCB_BASE_FRAME* m_boardFrame;
+};
+
+#endif // PCB_LAYER_PRESENTATION_H
\ No newline at end of file
diff --git a/pcbnew/sel_layer.cpp b/pcbnew/sel_layer.cpp
index 48ca2e1774..ff9946af35 100644
--- a/pcbnew/sel_layer.cpp
+++ b/pcbnew/sel_layer.cpp
@@ -26,11 +26,14 @@
 #include <kiplatform/ui.h>
 #include <confirm.h>
 #include <lset.h>
-#include <pcb_base_frame.h>
-#include <widgets/layer_box_selector.h>
 #include <board.h>
+#include <pgm_base.h>
+#include <pcb_base_frame.h>
+#include <pcb_layer_presentation.h>
+#include <footprint_editor_settings.h>
 #include <dialogs/dialog_layer_selection_base.h>
 #include <router/router_tool.h>
+#include <settings/settings_manager.h>
 #include <settings/color_settings.h>
 #include <tools/pcb_actions.h>
 
@@ -40,45 +43,40 @@
 #define LAYERNAME_COLNUM    2
 #define LAYER_HK_COLUMN     3
 
-/*
- * Display a layer list using a wxGrid.
- */
-class PCB_LAYER_SELECTOR: public LAYER_SELECTOR
+
+PCB_LAYER_PRESENTATION::PCB_LAYER_PRESENTATION( PCB_BASE_FRAME* aFrame ) : m_boardFrame( aFrame )
 {
-public:
-    PCB_LAYER_SELECTOR( PCB_BASE_FRAME* aFrame ) :
-        LAYER_SELECTOR()
-    {
-        m_frame = aFrame;
-    }
+}
 
-protected:
-    PCB_BASE_FRAME*  m_frame;
-
-    ///< @return true if the layer id is enabled (i.e. is it should be displayed).
-    bool isLayerEnabled( int aLayer ) const override
+COLOR4D PCB_LAYER_PRESENTATION::getLayerColor( int aLayer ) const
+{
+    if( m_boardFrame )
     {
-        return m_frame->GetBoard()->IsLayerEnabled( PCB_LAYER_ID( aLayer ) );
+        return m_boardFrame->GetColorSettings()->GetColor( aLayer );
     }
-
-    // Return the color index from the layer ID.
-    COLOR4D getLayerColor( int aLayer ) const override
+    else
     {
-        return m_frame->GetColorSettings()->GetColor( aLayer );
-    }
+        SETTINGS_MANAGER&          mgr = Pgm().GetSettingsManager();
+        FOOTPRINT_EDITOR_SETTINGS* settings = mgr.GetAppSettings<FOOTPRINT_EDITOR_SETTINGS>();
+        COLOR_SETTINGS*            current = mgr.GetColorSettings( settings->m_ColorTheme );
 
-    // Return the name of the layer ID.
-    wxString getLayerName( int aLayer ) const override
-    {
-        return m_frame->GetBoard()->GetLayerName( ToLAYER_ID( aLayer ) );
+        return current->GetColor( aLayer );
     }
-};
+}
+
+wxString PCB_LAYER_PRESENTATION::getLayerName( int aLayer ) const
+{
+    if( m_boardFrame )
+        return m_boardFrame->GetBoard()->GetLayerName( ToLAYER_ID( aLayer ) );
+    else
+        return BOARD::GetStandardLayerName( ToLAYER_ID( aLayer ) );
+}
 
 
 /**
  * Display a PCB layers list in a dialog to select one layer from this list.
  */
-class PCB_ONE_LAYER_SELECTOR : public PCB_LAYER_SELECTOR, public DIALOG_LAYER_SELECTION_BASE
+class PCB_ONE_LAYER_SELECTOR : public DIALOG_LAYER_SELECTION_BASE
 {
 public:
     PCB_ONE_LAYER_SELECTOR( PCB_BASE_FRAME* aParent, BOARD * aBrd, PCB_LAYER_ID aDefaultLayer,
@@ -104,6 +102,7 @@ private:
 
     void buildList();
 
+    PCB_LAYER_PRESENTATION    m_layerPresentation;
     PCB_LAYER_ID              m_layerSelected;
     LSET                      m_notAllowedLayersMask;
     BOARD*                    m_brd;
@@ -114,10 +113,8 @@ private:
 
 PCB_ONE_LAYER_SELECTOR::PCB_ONE_LAYER_SELECTOR( PCB_BASE_FRAME* aParent, BOARD* aBrd,
                                                 PCB_LAYER_ID aDefaultLayer,
-                                                LSET aNotAllowedLayersMask,
-                                                bool aHideCheckBoxes ) :
-        PCB_LAYER_SELECTOR( aParent ),
-        DIALOG_LAYER_SELECTION_BASE( aParent )
+                                                LSET aNotAllowedLayersMask, bool aHideCheckBoxes ) :
+        DIALOG_LAYER_SELECTION_BASE( aParent ), m_layerPresentation( aParent )
 {
     m_useCalculatedSize = true;
 
@@ -198,7 +195,7 @@ void PCB_ONE_LAYER_SELECTOR::onCharHook( wxKeyEvent& event )
 
 void PCB_ONE_LAYER_SELECTOR::buildList()
 {
-    wxColour bg = getLayerColor( LAYER_PCB_BACKGROUND ).ToColour();
+    wxColour bg = m_layerPresentation.getLayerColor( LAYER_PCB_BACKGROUND ).ToColour();
     int      left_row = 0;
     int      right_row = 0;
     wxString layername;
@@ -208,12 +205,12 @@ void PCB_ONE_LAYER_SELECTOR::buildList()
         if( m_notAllowedLayersMask[layerid] )
             continue;
 
-        wxColour fg = getLayerColor( layerid ).ToColour();
+        wxColour fg = m_layerPresentation.getLayerColor( layerid ).ToColour();
         wxColour color( wxColour::AlphaBlend( fg.Red(), bg.Red(), fg.Alpha() / 255.0 ),
                         wxColour::AlphaBlend( fg.Green(), bg.Green(), fg.Alpha() / 255.0 ),
                         wxColour::AlphaBlend( fg.Blue(), bg.Blue(), fg.Alpha() / 255.0 ) );
 
-        layername = wxT( " " ) + getLayerName( layerid );
+        layername = wxT( " " ) + m_layerPresentation.getLayerName( layerid );
 
         if( IsCopperLayer( layerid ) )
         {
@@ -304,8 +301,7 @@ PCB_LAYER_ID PCB_BASE_FRAME::SelectOneLayer( PCB_LAYER_ID aDefaultLayer, LSET aN
 /**
  * Display a pair PCB copper layers list in a dialog to select a layer pair from these lists.
  */
-class SELECT_COPPER_LAYERS_PAIR_DIALOG: public PCB_LAYER_SELECTOR,
-                                        public DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE
+class SELECT_COPPER_LAYERS_PAIR_DIALOG : public DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE
 {
 public:
     SELECT_COPPER_LAYERS_PAIR_DIALOG( PCB_BASE_FRAME* aParent, BOARD* aPcb,
@@ -323,11 +319,12 @@ private:
 
     void buildList();
 
-    BOARD*       m_brd;
-    PCB_LAYER_ID m_frontLayer;
-    PCB_LAYER_ID m_backLayer;
-    int          m_leftRowSelected;
-    int          m_rightRowSelected;
+    PCB_LAYER_PRESENTATION m_layerPresentation;
+    BOARD*                 m_brd;
+    PCB_LAYER_ID           m_frontLayer;
+    PCB_LAYER_ID           m_backLayer;
+    int                    m_leftRowSelected;
+    int                    m_rightRowSelected;
 
     std::vector<PCB_LAYER_ID> m_layersId;
 };
@@ -354,10 +351,11 @@ int ROUTER_TOOL::SelectCopperLayerPair( const TOOL_EVENT& aEvent )
 }
 
 
-SELECT_COPPER_LAYERS_PAIR_DIALOG::SELECT_COPPER_LAYERS_PAIR_DIALOG(
-        PCB_BASE_FRAME* aParent, BOARD * aPcb, PCB_LAYER_ID aFrontLayer, PCB_LAYER_ID aBackLayer) :
-    PCB_LAYER_SELECTOR( aParent ),
-    DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE( aParent )
+SELECT_COPPER_LAYERS_PAIR_DIALOG::SELECT_COPPER_LAYERS_PAIR_DIALOG( PCB_BASE_FRAME* aParent,
+                                                                    BOARD*          aPcb,
+                                                                    PCB_LAYER_ID    aFrontLayer,
+                                                                    PCB_LAYER_ID    aBackLayer ) :
+        DIALOG_COPPER_LAYER_PAIR_SELECTION_BASE( aParent ), m_layerPresentation( aParent )
 {
     m_frontLayer = aFrontLayer;
     m_backLayer = aBackLayer;
@@ -380,7 +378,7 @@ SELECT_COPPER_LAYERS_PAIR_DIALOG::SELECT_COPPER_LAYERS_PAIR_DIALOG(
 
 void SELECT_COPPER_LAYERS_PAIR_DIALOG::buildList()
 {
-    wxColour bg = getLayerColor( LAYER_PCB_BACKGROUND ).ToColour();
+    wxColour bg = m_layerPresentation.getLayerColor( LAYER_PCB_BACKGROUND ).ToColour();
     int      row = 0;
     wxString layername;
 
@@ -389,12 +387,12 @@ void SELECT_COPPER_LAYERS_PAIR_DIALOG::buildList()
         if( !IsCopperLayer( layerid ) )
             continue;
 
-        wxColour fg = getLayerColor( layerid ).ToColour();
+        wxColour fg = m_layerPresentation.getLayerColor( layerid ).ToColour();
         wxColour color( wxColour::AlphaBlend( fg.Red(), bg.Red(), fg.Alpha() / 255.0 ),
                         wxColour::AlphaBlend( fg.Green(), bg.Green(), fg.Alpha() / 255.0 ),
                         wxColour::AlphaBlend( fg.Blue(), bg.Blue(), fg.Alpha() / 255.0 ) );
 
-        layername = wxT( " " ) + getLayerName( layerid );
+        layername = wxT( " " ) + m_layerPresentation.getLayerName( layerid );
 
         if( row )
             m_leftGridLayers->AppendRows( 1 );
diff --git a/pcbnew/toolbars_pcb_editor.cpp b/pcbnew/toolbars_pcb_editor.cpp
index db50672647..ee816bd853 100644
--- a/pcbnew/toolbars_pcb_editor.cpp
+++ b/pcbnew/toolbars_pcb_editor.cpp
@@ -33,6 +33,7 @@
 #include <board_design_settings.h>
 #include <kiface_base.h>
 #include <kiplatform/ui.h>
+#include <layer_presentation.h>
 #include <macros.h>
 #include <pcb_edit_frame.h>
 #include <pcb_layer_box_selector.h>
@@ -49,12 +50,12 @@
 #include <tools/pcb_actions.h>
 #include <tools/pcb_selection_tool.h>
 #include <widgets/appearance_controls.h>
+#include <widgets/layer_box_selector.h>
 #include <widgets/pcb_properties_panel.h>
 #include <widgets/net_inspector_panel.h>
 #include <widgets/pcb_search_pane.h>
 #include <widgets/wx_aui_utils.h>
 #include <wx/wupdlock.h>
-#include <wx/dcmemory.h>
 #include <wx/combobox.h>
 
 #include "../scripting/python_scripting.h"
@@ -63,46 +64,9 @@
 /* Data to build the layer pair indicator button */
 static std::unique_ptr<wxBitmap> LayerPairBitmap;
 
-#define BM_LAYERICON_SIZE 24
-static const char s_BitmapLayerIcon[BM_LAYERICON_SIZE][BM_LAYERICON_SIZE] =
-{
-    // 0 = draw pixel with white
-    // 1 = draw pixel with black
-    // 2 = draw pixel with top layer from router pair
-    // 3 = draw pixel with bottom layer from router pair
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-    { 2, 2, 2, 2, 2, 0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
-};
-
-static COLOR4D ICON_WHITE { 0.86, 0.86, 0.86, 1.0 };
-static COLOR4D ICON_BLACK { 0.28, 0.28, 0.28, 1.0 };
-
 
 void PCB_EDIT_FRAME::PrepareLayerIndicator( bool aForceRebuild )
 {
-    int        ii, jj;
     COLOR4D    top_color, bottom_color, background_color;
     bool       change = aForceRebuild;
 
@@ -140,56 +104,9 @@ void PCB_EDIT_FRAME::PrepareLayerIndicator( bool aForceRebuild )
 
     if( change || !LayerPairBitmap )
     {
-        LayerPairBitmap = std::make_unique<wxBitmap>( 24, 24 );
-
-        // Draw the icon, with colors according to the router's layer pair
-        wxMemoryDC iconDC;
-        iconDC.SelectObject( *LayerPairBitmap );
-        wxBrush    brush;
-        wxPen      pen;
-        int buttonColor = -1;
-
-        brush.SetStyle( wxBRUSHSTYLE_SOLID );
-        brush.SetColour( background_color.WithAlpha(1.0).ToColour() );
-        iconDC.SetBrush( brush );
-        iconDC.DrawRectangle( 0, 0, BM_LAYERICON_SIZE, BM_LAYERICON_SIZE );
-
-        for( ii = 0; ii < BM_LAYERICON_SIZE; ii++ )
-        {
-            for( jj = 0; jj < BM_LAYERICON_SIZE; jj++ )
-            {
-                if( s_BitmapLayerIcon[ii][jj] != buttonColor )
-                {
-                    switch( s_BitmapLayerIcon[ii][jj] )
-                    {
-                    default:
-                    case 0: pen.SetColour( ICON_WHITE.ToColour() );   break;
-                    case 1: pen.SetColour( ICON_BLACK.ToColour() );   break;
-                    case 2: pen.SetColour( top_color.ToColour() );    break;
-                    case 3: pen.SetColour( bottom_color.ToColour() ); break;
-                    }
-
-                    buttonColor = s_BitmapLayerIcon[ii][jj];
-                    iconDC.SetPen( pen );
-                }
-
-                iconDC.DrawPoint( jj, ii );
-            }
-        }
-
-        // Deselect the bitmap from the DC in order to delete the MemoryDC safely without
-        // deleting the bitmap
-        iconDC.SelectObject( wxNullBitmap );
-
-        // Scale the bitmap
         const int scale = ( requested_scale <= 0 ) ? KiIconScale( this ) : requested_scale;
-        wxImage image = LayerPairBitmap->ConvertToImage();
-
-        // "NEAREST" causes less mixing of colors
-        image.Rescale( scale * image.GetWidth() / 4, scale * image.GetHeight() / 4,
-                       wxIMAGE_QUALITY_NEAREST );
-
-        LayerPairBitmap = std::make_unique<wxBitmap>( image );
+        LayerPairBitmap = LAYER_PRESENTATION::CreateLayerPairIcon( background_color, top_color,
+                                                                   bottom_color, scale );
 
         if( m_auxiliaryToolBar )
         {