diff --git a/3d-viewer/3d_canvas/board_adapter.h b/3d-viewer/3d_canvas/board_adapter.h
index 8a0d7ab3b9..b00b7d892b 100644
--- a/3d-viewer/3d_canvas/board_adapter.h
+++ b/3d-viewer/3d_canvas/board_adapter.h
@@ -479,18 +479,21 @@ private:
     void addFootprintShapes( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aDstContainer,
                              PCB_LAYER_ID aLayerId );
 
-    void addShape( const PCB_TEXT* aText, CONTAINER_2D_BASE* aDstContainer );
+    void addText( const EDA_TEXT* aText, CONTAINER_2D_BASE* aDstContainer,
+                  const BOARD_ITEM* aOwner );
 
-    void addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aDstContainer );
+    void addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aContainer,
+                   const BOARD_ITEM* aOwner );
 
-    void addShape( const PCB_DIMENSION_BASE* aDimension, CONTAINER_2D_BASE* aDstContainer );
+    void addShape( const PCB_DIMENSION_BASE* aDimension, CONTAINER_2D_BASE* aDstContainer,
+                   const BOARD_ITEM* aOwner );
 
-    void addSolidAreasShapes( const ZONE* aZoneContainer, CONTAINER_2D_BASE* aDstContainer,
+    void addSolidAreasShapes( const ZONE* aZone, CONTAINER_2D_BASE* aDstContainer,
                               PCB_LAYER_ID aLayerId );
 
     void transformArcToSegments( const VECTOR2I& aCentre, const VECTOR2I& aStart,
                                  const EDA_ANGLE& aArcAngle, int aCircleToSegmentsCount, int aWidth,
-                                 CONTAINER_2D_BASE* aDstContainer, const BOARD_ITEM& aBoardItem );
+                                 CONTAINER_2D_BASE* aDstContainer, const BOARD_ITEM& aOwner );
 
     void buildPadOutlineAsSegments( const PAD* aPad, CONTAINER_2D_BASE* aDstContainer, int aWidth );
 
diff --git a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp
index c344dab757..a87648e3b4 100644
--- a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp
+++ b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp
@@ -34,6 +34,7 @@
 #include "../3d_rendering/raytracing/shapes2D/filled_circle_2d.h"
 #include "../3d_rendering/raytracing/shapes2D/round_segment_2d.h"
 #include "../3d_rendering/raytracing/shapes2D/triangle_2d.h"
+#include "fp_textbox.h"
 #include <board_adapter.h>
 #include <footprint.h>
 #include <pad.h>
@@ -55,42 +56,49 @@
 #include <macros.h>
 #include <callback_gal.h>
 
-void BOARD_ADAPTER::addShape( const PCB_TEXT* aText, CONTAINER_2D_BASE* aDstContainer )
+
+#define TO_3DU( x ) ( x * m_biuTo3Dunits )
+
+#define TO_SFVEC2F( vec ) SFVEC2F( TO_3DU( vec.x ), TO_3DU( -vec.y ) )
+
+
+void BOARD_ADAPTER::addText( const EDA_TEXT* aText, CONTAINER_2D_BASE* aContainer,
+                             const BOARD_ITEM* aOwner )
 {
     KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
     KIFONT::FONT*              font = aText->GetDrawFont();
-    float                      penWidth = aText->GetEffectiveTextPenWidth() * m_biuTo3Dunits;
+    float                      penWidth = TO_3DU( aText->GetEffectiveTextPenWidth() );
 
     CALLBACK_GAL callback_gal( empty_opts,
             // Stroke callback
             [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
             {
-                const SFVEC2F a3DU( aPt1.x * m_biuTo3Dunits, -aPt1.y * m_biuTo3Dunits );
-                const SFVEC2F b3DU( aPt2.x * m_biuTo3Dunits, -aPt2.y * m_biuTo3Dunits );
+                const SFVEC2F pt1_3DU = TO_SFVEC2F( aPt1 );
+                const SFVEC2F pt2_3DU = TO_SFVEC2F( aPt2 );
 
-                if( Is_segment_a_circle( a3DU, b3DU ) )
-                    aDstContainer->Add( new FILLED_CIRCLE_2D( a3DU, penWidth / 2, *aText ) );
+                if( Is_segment_a_circle( pt1_3DU, pt2_3DU ) )
+                    aContainer->Add( new FILLED_CIRCLE_2D( pt1_3DU, penWidth / 2, *aOwner ) );
                 else
-                    aDstContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, penWidth, *aText ) );
+                    aContainer->Add( new ROUND_SEGMENT_2D( pt1_3DU, pt2_3DU, penWidth, *aOwner ) );
             },
             // Triangulation callback
             [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2, const VECTOR2I& aPt3 )
             {
-                const SFVEC2F a3DU( aPt1.x * m_biuTo3Dunits, -aPt1.y * m_biuTo3Dunits );
-                const SFVEC2F b3DU( aPt2.x * m_biuTo3Dunits, -aPt2.y * m_biuTo3Dunits );
-                const SFVEC2F c3DU( aPt3.x * m_biuTo3Dunits, -aPt3.y * m_biuTo3Dunits );
-
-                aDstContainer->Add( new TRIANGLE_2D( a3DU, b3DU, c3DU, *aText ) );
+                aContainer->Add( new TRIANGLE_2D( TO_SFVEC2F( aPt1 ), TO_SFVEC2F( aPt2 ),
+                                                  TO_SFVEC2F( aPt3 ), *aOwner ) );
             } );
 
-    font->Draw( &callback_gal, aText->GetShownText(), aText->GetTextPos(), aText->GetAttributes() );
+    TEXT_ATTRIBUTES attrs = aText->GetAttributes();
+    attrs.m_Angle = aText->GetDrawRotation();
+
+    font->Draw( &callback_gal, aText->GetShownText(), aText->GetDrawPos(), attrs );
 }
 
 
-void BOARD_ADAPTER::addShape( const PCB_DIMENSION_BASE* aDimension,
-                              CONTAINER_2D_BASE* aDstContainer )
+void BOARD_ADAPTER::addShape( const PCB_DIMENSION_BASE* aDimension, CONTAINER_2D_BASE* aContainer,
+                              const BOARD_ITEM* aOwner )
 {
-    addShape( &aDimension->Text(), aDstContainer );
+    addText( &aDimension->Text(), aContainer, aDimension );
 
     const int linewidth = aDimension->GetLineThickness();
 
@@ -100,12 +108,10 @@ void BOARD_ADAPTER::addShape( const PCB_DIMENSION_BASE* aDimension,
         {
         case SH_SEGMENT:
         {
-            const SEG&    seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
-            const SFVEC2F start3DU( seg.A.x * m_biuTo3Dunits, -seg.A.y * m_biuTo3Dunits );
-            const SFVEC2F end3DU  ( seg.B.x * m_biuTo3Dunits, -seg.B.y * m_biuTo3Dunits );
+            const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
 
-            aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, linewidth * m_biuTo3Dunits,
-                                                      *aDimension ) );
+            aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( seg.A ), TO_SFVEC2F( seg.B ),
+                                                   TO_3DU( linewidth ), *aOwner ) );
             break;
         }
 
@@ -114,12 +120,8 @@ void BOARD_ADAPTER::addShape( const PCB_DIMENSION_BASE* aDimension,
             int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
             int delta = aDimension->GetLineThickness() / 2;
 
-            SFVEC2F center( shape->Centre().x * m_biuTo3Dunits,
-                            shape->Centre().y * m_biuTo3Dunits );
-
-            aDstContainer->Add( new RING_2D( center, ( radius - delta ) * m_biuTo3Dunits,
-                                             ( radius + delta ) * m_biuTo3Dunits, *aDimension ) );
-
+            aContainer->Add( new RING_2D( TO_SFVEC2F( shape->Centre() ), TO_3DU( radius - delta ),
+                                          TO_3DU( radius + delta ), *aOwner ) );
             break;
         }
 
@@ -130,41 +132,16 @@ void BOARD_ADAPTER::addShape( const PCB_DIMENSION_BASE* aDimension,
 }
 
 
-void BOARD_ADAPTER::addFootprintShapes( const FOOTPRINT* aFootprint,
-                                        CONTAINER_2D_BASE* aDstContainer, PCB_LAYER_ID aLayerId )
+void BOARD_ADAPTER::addFootprintShapes( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aContainer,
+                                        PCB_LAYER_ID aLayerId )
 {
     KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
-    std::vector<FP_TEXT*>      textItems;
-    FP_TEXT*                   textItem = nullptr;
-    float                      penWidth = 0;
-
-    CALLBACK_GAL callback_gal( empty_opts,
-            // Stroke callback
-            [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
-            {
-                const SFVEC2F a3DU( aPt1.x * m_biuTo3Dunits, -aPt1.y * m_biuTo3Dunits );
-                const SFVEC2F b3DU( aPt2.x * m_biuTo3Dunits, -aPt2.y * m_biuTo3Dunits );
-
-                if( Is_segment_a_circle( a3DU, b3DU ) )
-                    aDstContainer->Add( new FILLED_CIRCLE_2D( a3DU, penWidth / 2, *textItem ) );
-                else
-                    aDstContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, penWidth, *textItem ) );
-            },
-            // Triangulation callback
-            [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2, const VECTOR2I& aPt3 )
-            {
-                const SFVEC2F a3DU( aPt1.x * m_biuTo3Dunits, -aPt1.y * m_biuTo3Dunits );
-                const SFVEC2F b3DU( aPt2.x * m_biuTo3Dunits, -aPt2.y * m_biuTo3Dunits );
-                const SFVEC2F c3DU( aPt3.x * m_biuTo3Dunits, -aPt3.y * m_biuTo3Dunits );
-
-                aDstContainer->Add( new TRIANGLE_2D( a3DU, b3DU, c3DU, *textItem ) );
-            } );
 
     if( aFootprint->Reference().GetLayer() == aLayerId && aFootprint->Reference().IsVisible() )
-        textItems.push_back( &aFootprint->Reference() );
+        addText( &aFootprint->Reference(), aContainer, &aFootprint->Reference() );
 
     if( aFootprint->Value().GetLayer() == aLayerId && aFootprint->Value().IsVisible() )
-        textItems.push_back( &aFootprint->Value() );
+        addText( &aFootprint->Value(), aContainer, &aFootprint->Value() );
 
     for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
     {
@@ -175,7 +152,20 @@ void BOARD_ADAPTER::addFootprintShapes( const FOOTPRINT* aFootprint,
             FP_TEXT* text = static_cast<FP_TEXT*>( item );
 
             if( text->GetLayer() == aLayerId && text->IsVisible() )
-                textItems.push_back( text );
+                addText( text, aContainer, text );
+
+            break;
+        }
+
+        case PCB_FP_TEXTBOX_T:
+        {
+            FP_TEXTBOX* textbox = static_cast<FP_TEXTBOX*>( item );
+
+            if( textbox->GetLayer() == aLayerId )
+            {
+                addShape( textbox, aContainer, aFootprint );
+                addText( textbox, aContainer, aFootprint );
+            }
 
             break;
         }
@@ -189,7 +179,7 @@ void BOARD_ADAPTER::addFootprintShapes( const FOOTPRINT* aFootprint,
             PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( item );
 
             if( dimension->GetLayer() == aLayerId )
-                addShape( dimension, aDstContainer );
+                addShape( dimension, aContainer, aFootprint );
 
             break;
         }
@@ -199,7 +189,7 @@ void BOARD_ADAPTER::addFootprintShapes( const FOOTPRINT* aFootprint,
             FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
 
             if( shape->GetLayer() == aLayerId )
-                addShape( static_cast<const PCB_SHAPE*>( shape ), aDstContainer );
+                addShape( shape, aContainer, aFootprint );
 
             break;
         }
@@ -208,32 +198,19 @@ void BOARD_ADAPTER::addFootprintShapes( const FOOTPRINT* aFootprint,
             break;
         }
     }
-
-    for( FP_TEXT* text : textItems )
-    {
-        textItem = text;
-        penWidth = textItem->GetEffectiveTextPenWidth() * m_biuTo3Dunits;
-
-        KIFONT::FONT*   font = textItem->GetDrawFont();
-        TEXT_ATTRIBUTES attrs = textItem->GetAttributes();
-
-        attrs.m_Angle = textItem->GetDrawRotation();
-
-        font->Draw( &callback_gal, textItem->GetShownText(), textItem->GetTextPos(), attrs );
-    }
 }
 
 
 void BOARD_ADAPTER::createTrack( const PCB_TRACK* aTrack, CONTAINER_2D_BASE* aDstContainer )
 {
-    SFVEC2F start3DU( aTrack->GetStart().x * m_biuTo3Dunits,
-                      -aTrack->GetStart().y * m_biuTo3Dunits ); // y coord is inverted
+    SFVEC2F start3DU = TO_SFVEC2F( aTrack->GetStart() );
+    SFVEC2F end3DU = TO_SFVEC2F( aTrack->GetEnd() );
 
     switch( aTrack->Type() )
     {
     case PCB_VIA_T:
     {
-        const float radius3DU = ( aTrack->GetWidth() / 2 ) * m_biuTo3Dunits;
+        const float radius3DU = TO_3DU(  aTrack->GetWidth() / 2 );
         aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius3DU, *aTrack ) );
         break;
     }
@@ -267,20 +244,16 @@ void BOARD_ADAPTER::createTrack( const PCB_TRACK* aTrack, CONTAINER_2D_BASE* aDs
 
     case PCB_TRACE_T:    // Track is a usual straight segment
     {
-        SFVEC2F end3DU( aTrack->GetEnd().x * m_biuTo3Dunits, -aTrack->GetEnd().y * m_biuTo3Dunits );
-
         // Cannot add segments that have the same start and end point
         if( Is_segment_a_circle( start3DU, end3DU ) )
         {
-            const float radius3DU = ( aTrack->GetWidth() / 2 ) * m_biuTo3Dunits;
-
-            aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius3DU, *aTrack ) );
+            aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aTrack->GetWidth() / 2 ),
+                                                      *aTrack ) );
         }
         else
         {
-            const float width3DU = aTrack->GetWidth() * m_biuTo3Dunits;
-
-            aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, width3DU, *aTrack ) );
+            aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aTrack->GetWidth() ),
+                                                      *aTrack ) );
         }
 
         break;
@@ -292,7 +265,7 @@ void BOARD_ADAPTER::createTrack( const PCB_TRACK* aTrack, CONTAINER_2D_BASE* aDs
 }
 
 
-void BOARD_ADAPTER::createPadWithMargin( const PAD* aPad, CONTAINER_2D_BASE* aDstContainer,
+void BOARD_ADAPTER::createPadWithMargin( const PAD* aPad, CONTAINER_2D_BASE* aContainer,
                                          PCB_LAYER_ID aLayer, const VECTOR2I& aMargin ) const
 {
     SHAPE_POLY_SET poly;
@@ -329,37 +302,34 @@ void BOARD_ADAPTER::createPadWithMargin( const PAD* aPad, CONTAINER_2D_BASE* aDs
             {
             case SH_SEGMENT:
             {
-                const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape;
+                const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*>( shape );
 
-                const SFVEC2F a3DU(  seg->GetSeg().A.x * m_biuTo3Dunits,
-                                    -seg->GetSeg().A.y * m_biuTo3Dunits );
-                const SFVEC2F b3DU(  seg->GetSeg().B.x * m_biuTo3Dunits,
-                                    -seg->GetSeg().B.y * m_biuTo3Dunits );
-                const double  width3DU = ( seg->GetWidth() + clearance.x * 2 ) * m_biuTo3Dunits;
+                const SFVEC2F a3DU = TO_SFVEC2F( seg->GetSeg().A );
+                const SFVEC2F b3DU = TO_SFVEC2F( seg->GetSeg().B );
+                const double  width3DU = TO_3DU(  seg->GetWidth() + clearance.x * 2 );
 
                  // Cannot add segments that have the same start and end point
                 if( Is_segment_a_circle( a3DU, b3DU ) )
-                    aDstContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aPad ) );
+                    aContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aPad ) );
                 else
-                    aDstContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aPad ) );
+                    aContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aPad ) );
             }
                 break;
 
             case SH_CIRCLE:
             {
-                const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape;
+                const SHAPE_CIRCLE* circle = static_cast<const SHAPE_CIRCLE*>( shape );
 
-                const double  radius3DU = ( circle->GetRadius() + clearance.x ) * m_biuTo3Dunits;
-                const SFVEC2F center3DU(  circle->GetCenter().x * m_biuTo3Dunits,
-                                         -circle->GetCenter().y * m_biuTo3Dunits );
+                const double  radius3DU = TO_3DU( circle->GetRadius() + clearance.x );
+                const SFVEC2F center3DU = TO_SFVEC2F( circle->GetCenter() );
 
-                aDstContainer->Add( new FILLED_CIRCLE_2D( center3DU, radius3DU, *aPad ) );
+                aContainer->Add( new FILLED_CIRCLE_2D( center3DU, radius3DU, *aPad ) );
             }
                 break;
 
             case SH_RECT:
             {
-                SHAPE_RECT* rect = (SHAPE_RECT*) shape;
+                const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>( shape );
 
                 poly.NewOutline();
                 poly.Append( rect->GetPosition() );
@@ -379,23 +349,21 @@ void BOARD_ADAPTER::createPadWithMargin( const PAD* aPad, CONTAINER_2D_BASE* aDs
 
             case SH_ARC:
             {
-                SHAPE_ARC*       arc = (SHAPE_ARC*) shape;
+                const SHAPE_ARC* arc = static_cast<const SHAPE_ARC*>( shape );
                 SHAPE_LINE_CHAIN l = arc->ConvertToPolyline( maxError );
 
                 for( int i = 0; i < l.SegmentCount(); i++ )
                 {
                     SHAPE_SEGMENT seg( l.Segment( i ).A, l.Segment( i ).B, arc->GetWidth() );
-                    const SFVEC2F a3DU(  seg.GetSeg().A.x * m_biuTo3Dunits,
-                                        -seg.GetSeg().A.y * m_biuTo3Dunits );
-                    const SFVEC2F b3DU(  seg.GetSeg().B.x * m_biuTo3Dunits,
-                                        -seg.GetSeg().B.y * m_biuTo3Dunits );
-                    const double  width3DU = ( arc->GetWidth() + clearance.x * 2 ) * m_biuTo3Dunits;
+                    const SFVEC2F a3DU = TO_SFVEC2F( seg.GetSeg().A );
+                    const SFVEC2F b3DU = TO_SFVEC2F( seg.GetSeg().B );
+                    const double  width3DU = TO_3DU( arc->GetWidth() + clearance.x * 2 );
 
                      // Cannot add segments that have the same start and end point
                     if( Is_segment_a_circle( a3DU, b3DU ) )
-                        aDstContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aPad ) );
+                        aContainer->Add( new FILLED_CIRCLE_2D( a3DU, width3DU / 2, *aPad ) );
                     else
-                        aDstContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aPad ) );
+                        aContainer->Add( new ROUND_SEGMENT_2D( a3DU, b3DU, width3DU, *aPad ) );
                 }
             }
                 break;
@@ -413,7 +381,7 @@ void BOARD_ADAPTER::createPadWithMargin( const PAD* aPad, CONTAINER_2D_BASE* aDs
             poly.Inflate( clearance.x, 32 );
 
         // Add the PAD polygon
-        ConvertPolygonToTriangles( poly, *aDstContainer, m_biuTo3Dunits, *aPad );
+        ConvertPolygonToTriangles( poly, *aContainer, m_biuTo3Dunits, *aPad );
     }
 }
 
@@ -432,10 +400,7 @@ OBJECT_2D* BOARD_ADAPTER::createPadWithDrill( const PAD* aPad, int aInflateValue
     {
         const int radius = ( drillSize.x / 2 ) + aInflateValue;
 
-        const SFVEC2F center(  aPad->GetPosition().x * m_biuTo3Dunits,
-                              -aPad->GetPosition().y * m_biuTo3Dunits );
-
-        return new FILLED_CIRCLE_2D( center, radius * m_biuTo3Dunits, *aPad );
+        return new FILLED_CIRCLE_2D( TO_SFVEC2F( aPad->GetPosition() ), TO_3DU( radius ), *aPad );
 
     }
     else                                // Oblong hole
@@ -443,20 +408,13 @@ OBJECT_2D* BOARD_ADAPTER::createPadWithDrill( const PAD* aPad, int aInflateValue
         const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
         float width = seg->GetWidth() + aInflateValue * 2;
 
-        SFVEC2F start3DU(  seg->GetSeg().A.x * m_biuTo3Dunits,
-                          -seg->GetSeg().A.y * m_biuTo3Dunits );
-
-        SFVEC2F end3DU (  seg->GetSeg().B.x * m_biuTo3Dunits,
-                         -seg->GetSeg().B.y * m_biuTo3Dunits );
-
-        return new ROUND_SEGMENT_2D( start3DU, end3DU, width * m_biuTo3Dunits, *aPad );
+        return new ROUND_SEGMENT_2D( TO_SFVEC2F( seg->GetSeg().A ), TO_SFVEC2F( seg->GetSeg().B ),
+                                     TO_3DU( width ), *aPad );
     }
-
-    return nullptr;
 }
 
 
-void BOARD_ADAPTER::addPads( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aDstContainer,
+void BOARD_ADAPTER::addPads( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aContainer,
                              PCB_LAYER_ID aLayerId, bool aSkipNPTHPadsWihNoCopper,
                              bool aSkipPlatedPads, bool aSkipNonPlatedPads )
 {
@@ -523,7 +481,7 @@ void BOARD_ADAPTER::addPads( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aDs
             break;
         }
 
-        createPadWithMargin( pad, aDstContainer, aLayerId, margin );
+        createPadWithMargin( pad, aContainer, aLayerId, margin );
     }
 }
 
@@ -532,8 +490,8 @@ void BOARD_ADAPTER::addPads( const FOOTPRINT* aFootprint, CONTAINER_2D_BASE* aDs
 // common/convert_basic_shapes_to_polygon.cpp
 void BOARD_ADAPTER::transformArcToSegments( const VECTOR2I& aCentre, const VECTOR2I& aStart,
                                             const EDA_ANGLE& aArcAngle, int aCircleToSegmentsCount,
-                                            int aWidth, CONTAINER_2D_BASE* aDstContainer,
-                                            const BOARD_ITEM& aBoardItem )
+                                            int aWidth, CONTAINER_2D_BASE* aContainer,
+                                            const BOARD_ITEM& aOwner )
 {
     VECTOR2I  arc_start, arc_end;
     EDA_ANGLE arcAngle( aArcAngle );
@@ -559,43 +517,32 @@ void BOARD_ADAPTER::transformArcToSegments( const VECTOR2I& aCentre, const VECTO
         curr_end = arc_start;
         RotatePoint( curr_end, aCentre, -ii );
 
-        const SFVEC2F start3DU( curr_start.x * m_biuTo3Dunits, -curr_start.y * m_biuTo3Dunits );
-        const SFVEC2F end3DU  ( curr_end.x   * m_biuTo3Dunits, -curr_end.y   * m_biuTo3Dunits );
+        const SFVEC2F start3DU = TO_SFVEC2F( curr_start );
+        const SFVEC2F end3DU = TO_SFVEC2F( curr_end );
 
         if( Is_segment_a_circle( start3DU, end3DU ) )
-        {
-            aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
-                                                      aBoardItem ) );
-        }
+            aContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aWidth / 2 ), aOwner ) );
         else
-        {
-            aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
-                                                      aBoardItem ) );
-        }
+            aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aWidth ), aOwner ) );
 
         curr_start = curr_end;
     }
 
     if( curr_end != arc_end )
     {
-        const SFVEC2F start3DU( curr_end.x * m_biuTo3Dunits, -curr_end.y * m_biuTo3Dunits );
-        const SFVEC2F end3DU  ( arc_end.x  * m_biuTo3Dunits, -arc_end.y  * m_biuTo3Dunits );
+        const SFVEC2F start3DU = TO_SFVEC2F( curr_end );
+        const SFVEC2F end3DU = TO_SFVEC2F( arc_end );
 
         if( Is_segment_a_circle( start3DU, end3DU ) )
-        {
-            aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
-                                                      aBoardItem ) );
-        }
+            aContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aWidth / 2 ), aOwner ) );
         else
-        {
-            aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
-                                                      aBoardItem ) );
-        }
+            aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aWidth ), aOwner ) );
     }
 }
 
 
-void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aDstContainer )
+void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aContainer,
+                              const BOARD_ITEM* aOwner )
 {
     // The full width of the lines to create
     // The extra 1 protects the inner/outer radius values from degeneracy
@@ -605,19 +552,17 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aDstCo
     {
     case SHAPE_T::CIRCLE:
     {
-        const SFVEC2F center3DU( aShape->GetCenter().x * m_biuTo3Dunits,
-                                 -aShape->GetCenter().y * m_biuTo3Dunits );
+        const SFVEC2F center3DU = TO_SFVEC2F( aShape->GetCenter() );
+        float         inner_radius3DU = TO_3DU( aShape->GetRadius() - linewidth / 2 );
+        float         outer_radius3DU = TO_3DU( aShape->GetRadius() + linewidth / 2 );
 
-        float inner_radius = ( aShape->GetRadius() - linewidth / 2 ) * m_biuTo3Dunits;
-        float outer_radius = ( aShape->GetRadius() + linewidth / 2 ) * m_biuTo3Dunits;
-
-        if( inner_radius < 0 )
-            inner_radius = 0;
+        if( inner_radius3DU < 0 )
+            inner_radius3DU = 0;
 
         if( aShape->IsFilled() )
-            aDstContainer->Add( new FILLED_CIRCLE_2D( center3DU, outer_radius, *aShape ) );
+            aContainer->Add( new FILLED_CIRCLE_2D( center3DU, outer_radius3DU, *aOwner ) );
         else
-            aDstContainer->Add( new RING_2D( center3DU, inner_radius, outer_radius, *aShape ) );
+            aContainer->Add( new RING_2D( center3DU, inner_radius3DU, outer_radius3DU, *aOwner ) );
     }
         break;
 
@@ -631,25 +576,20 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aDstCo
 
             polyList.Simplify( SHAPE_POLY_SET::PM_FAST );
 
-            ConvertPolygonToTriangles( polyList, *aDstContainer, m_biuTo3Dunits, *aShape );
+            ConvertPolygonToTriangles( polyList, *aContainer, m_biuTo3Dunits, *aOwner );
         }
         else
         {
             std::vector<VECTOR2I> pts = aShape->GetRectCorners();
 
-            const SFVEC2F topLeft3DU(  pts[0].x * m_biuTo3Dunits, -pts[0].y * m_biuTo3Dunits );
-            const SFVEC2F topRight3DU( pts[1].x * m_biuTo3Dunits, -pts[1].y * m_biuTo3Dunits );
-            const SFVEC2F botRight3DU( pts[2].x * m_biuTo3Dunits, -pts[2].y * m_biuTo3Dunits );
-            const SFVEC2F botLeft3DU(  pts[3].x * m_biuTo3Dunits, -pts[3].y * m_biuTo3Dunits );
-
-            aDstContainer->Add( new ROUND_SEGMENT_2D( topLeft3DU, topRight3DU,
-                                                      linewidth * m_biuTo3Dunits, *aShape ) );
-            aDstContainer->Add( new ROUND_SEGMENT_2D( topRight3DU, botRight3DU,
-                                                      linewidth * m_biuTo3Dunits, *aShape ) );
-            aDstContainer->Add( new ROUND_SEGMENT_2D( botRight3DU, botLeft3DU,
-                                                      linewidth * m_biuTo3Dunits, *aShape ) );
-            aDstContainer->Add( new ROUND_SEGMENT_2D( botLeft3DU, topLeft3DU,
-                                                      linewidth * m_biuTo3Dunits, *aShape ) );
+            aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[0] ), TO_SFVEC2F( pts[1] ),
+                                                   TO_3DU( linewidth ), *aOwner ) );
+            aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[1] ), TO_SFVEC2F( pts[2] ),
+                                                   TO_3DU( linewidth ), *aOwner ) );
+            aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[2] ), TO_SFVEC2F( pts[3] ),
+                                                   TO_3DU( linewidth ), *aOwner ) );
+            aContainer->Add( new ROUND_SEGMENT_2D( TO_SFVEC2F( pts[3] ), TO_SFVEC2F( pts[0] ),
+                                                   TO_3DU( linewidth ), *aOwner ) );
         }
         break;
 
@@ -658,28 +598,20 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aDstCo
         unsigned int segCount = GetCircleSegmentCount( aShape->GetBoundingBox().GetSizeMax() );
 
         transformArcToSegments( aShape->GetCenter(), aShape->GetStart(), aShape->GetArcAngle(),
-                                segCount, linewidth, aDstContainer, *aShape );
+                                segCount, linewidth, aContainer, *aOwner );
     }
     break;
 
     case SHAPE_T::SEGMENT:
     {
-        const SFVEC2F start3DU( aShape->GetStart().x * m_biuTo3Dunits,
-                                -aShape->GetStart().y * m_biuTo3Dunits );
-
-        const SFVEC2F end3DU  ( aShape->GetEnd().x * m_biuTo3Dunits,
-                                -aShape->GetEnd().y * m_biuTo3Dunits );
+        const SFVEC2F start3DU = TO_SFVEC2F( aShape->GetStart() );
+        const SFVEC2F end3DU = TO_SFVEC2F( aShape->GetEnd() );
+        const double  linewidth3DU = TO_3DU( linewidth );
 
         if( Is_segment_a_circle( start3DU, end3DU ) )
-        {
-            aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( linewidth / 2 ) * m_biuTo3Dunits,
-                                                     *aShape ) );
-        }
+            aContainer->Add( new FILLED_CIRCLE_2D( start3DU, linewidth3DU / 2, *aOwner ) );
         else
-        {
-            aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, linewidth * m_biuTo3Dunits,
-                                                      *aShape ) );
-        }
+            aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, linewidth3DU, *aOwner ) );
     }
     break;
 
@@ -696,7 +628,7 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aDstCo
         if( polyList.IsEmpty() ) // Just for caution
             break;
 
-        ConvertPolygonToTriangles( polyList, *aDstContainer, m_biuTo3Dunits, *aShape );
+        ConvertPolygonToTriangles( polyList, *aContainer, m_biuTo3Dunits, *aOwner );
     }
     break;
 
@@ -708,24 +640,21 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aDstCo
 }
 
 
-// Based on
-// TransformSolidAreasShapesToPolygonSet
-// board_items_to_polygon_shape_transform.cpp
-void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZoneContainer,
-                                         CONTAINER_2D_BASE* aDstContainer, PCB_LAYER_ID aLayerId )
+void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZone, CONTAINER_2D_BASE* aContainer,
+                                         PCB_LAYER_ID aLayerId )
 {
     // Copy the polys list because we have to simplify it
-    SHAPE_POLY_SET polyList = SHAPE_POLY_SET( aZoneContainer->GetFilledPolysList( aLayerId ) );
+    SHAPE_POLY_SET polyList = SHAPE_POLY_SET( aZone->GetFilledPolysList( aLayerId ) );
 
     // This convert the poly in outline and holes
-    ConvertPolygonToTriangles( polyList, *aDstContainer, m_biuTo3Dunits, *aZoneContainer );
+    ConvertPolygonToTriangles( polyList, *aContainer, m_biuTo3Dunits, *aZone );
 
     // add filled areas outlines, which are drawn with thick lines segments
     // but only if filled polygons outlines have thickness
-    if( !aZoneContainer->GetFilledPolysUseThickness() )
+    if( !aZone->GetFilledPolysUseThickness() )
         return;
 
-    float line_thickness = aZoneContainer->GetMinThickness() * m_biuTo3Dunits;
+    float width3DU = TO_3DU( aZone->GetMinThickness() );
 
     for( int i = 0; i < polyList.OutlineCount(); ++i )
     {
@@ -734,23 +663,19 @@ void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZoneContainer,
 
         for( int j = 0; j < pathOutline.PointCount(); ++j )
         {
-            const VECTOR2I& a = pathOutline.CPoint( j );
-            const VECTOR2I& b = pathOutline.CPoint( j + 1 );
-
-            SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits );
-            SFVEC2F end3DU  ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits );
+            SFVEC2F start3DU = TO_SFVEC2F( pathOutline.CPoint( j ) );
+            SFVEC2F end3DU = TO_SFVEC2F( pathOutline.CPoint( j + 1 ) );
 
             if( Is_segment_a_circle( start3DU, end3DU ) )
             {
-                float radius = line_thickness/2;
+                float radius3DU = width3DU / 2;
 
-                if( radius > 0.0 )  // degenerated circles crash 3D viewer
-                    aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius, *aZoneContainer ) );
+                if( radius3DU > 0.0 )  // degenerated circles crash 3D viewer
+                    aContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius3DU, *aZone ) );
             }
             else
             {
-                aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, line_thickness,
-                                                          *aZoneContainer ) );
+                aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, width3DU, *aZone ) );
             }
         }
 
@@ -761,24 +686,19 @@ void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZoneContainer,
 
             for( int j = 0; j < pathHole.PointCount(); j++ )
             {
-                const VECTOR2I& a = pathHole.CPoint( j );
-                const VECTOR2I& b = pathHole.CPoint( j + 1 );
-
-                SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits );
-                SFVEC2F end3DU  ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits );
+                SFVEC2F start3DU = TO_SFVEC2F( pathHole.CPoint( j ) );
+                SFVEC2F end3DU = TO_SFVEC2F( pathHole.CPoint( j + 1 ) );
 
                 if( Is_segment_a_circle( start3DU, end3DU ) )
                 {
-                    float radius = line_thickness/2;
+                    float radius3DU = width3DU / 2;
 
-                    if( radius > 0.0 )  // degenerated circles crash 3D viewer
-                        aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius,
-                                                                  *aZoneContainer ) );
+                    if( radius3DU > 0.0 )  // degenerated circles crash 3D viewer
+                        aContainer->Add( new FILLED_CIRCLE_2D( start3DU, radius3DU, *aZone ) );
                 }
                 else
                 {
-                    aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, line_thickness,
-                                                              *aZoneContainer ) );
+                    aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, width3DU, *aZone ) );
                 }
             }
         }
@@ -786,19 +706,17 @@ void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZoneContainer,
 }
 
 
-void BOARD_ADAPTER::buildPadOutlineAsSegments( const PAD* aPad, CONTAINER_2D_BASE* aDstContainer,
+void BOARD_ADAPTER::buildPadOutlineAsSegments( const PAD* aPad, CONTAINER_2D_BASE* aContainer,
                                                int aWidth )
 {
     if( aPad->GetShape() == PAD_SHAPE::CIRCLE )    // Draw a ring
     {
-        const SFVEC2F center3DU( aPad->ShapePos().x * m_biuTo3Dunits,
-                                 -aPad->ShapePos().y * m_biuTo3Dunits );
+        const SFVEC2F center3DU = TO_SFVEC2F( aPad->ShapePos() );
+        const int     radius = aPad->GetSize().x / 2;
+        const float   inner_radius3DU = TO_3DU( radius - aWidth / 2 );
+        const float   outer_radius3DU = TO_3DU( radius + aWidth / 2 );
 
-        const int   radius = aPad->GetSize().x / 2;
-        const float inner_radius = ( radius - aWidth / 2 ) * m_biuTo3Dunits;
-        const float outer_radius = ( radius + aWidth / 2 ) * m_biuTo3Dunits;
-
-        aDstContainer->Add( new RING_2D( center3DU, inner_radius, outer_radius, *aPad ) );
+        aContainer->Add( new RING_2D( center3DU, inner_radius3DU, outer_radius3DU, *aPad ) );
 
         return;
     }
@@ -809,21 +727,12 @@ void BOARD_ADAPTER::buildPadOutlineAsSegments( const PAD* aPad, CONTAINER_2D_BAS
 
     for( int j = 0; j < path.PointCount(); j++ )
     {
-        const VECTOR2I& a = path.CPoint( j );
-        const VECTOR2I& b = path.CPoint( j + 1 );
-
-        SFVEC2F start3DU( a.x * m_biuTo3Dunits, -a.y * m_biuTo3Dunits );
-        SFVEC2F end3DU  ( b.x * m_biuTo3Dunits, -b.y * m_biuTo3Dunits );
+        SFVEC2F start3DU = TO_SFVEC2F( path.CPoint( j ) );
+        SFVEC2F end3DU = TO_SFVEC2F( path.CPoint( j + 1 ) );
 
         if( Is_segment_a_circle( start3DU, end3DU ) )
-        {
-            aDstContainer->Add( new FILLED_CIRCLE_2D( start3DU, ( aWidth / 2 ) * m_biuTo3Dunits,
-                                                      *aPad ) );
-        }
+            aContainer->Add( new FILLED_CIRCLE_2D( start3DU, TO_3DU( aWidth / 2 ), *aPad ) );
         else
-        {
-            aDstContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, aWidth * m_biuTo3Dunits,
-                                                      *aPad ) );
-        }
+            aContainer->Add( new ROUND_SEGMENT_2D( start3DU, end3DU, TO_3DU( aWidth ), *aPad ) );
     }
 }
diff --git a/3d-viewer/3d_canvas/create_layer_items.cpp b/3d-viewer/3d_canvas/create_layer_items.cpp
index 3838c66d80..9cdc1efb8a 100644
--- a/3d-viewer/3d_canvas/create_layer_items.cpp
+++ b/3d-viewer/3d_canvas/create_layer_items.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2015-2016 Mario Luzeiro <mrluzeiro@ua.pt>
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -37,6 +37,7 @@
 #include <footprint.h>
 #include <pad.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
 #include <fp_shape.h>
 #include <zone.h>
 #include <convert_basic_shapes_to_polygon.h>
@@ -606,11 +607,16 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
             switch( item->Type() )
             {
             case PCB_SHAPE_T:
-                addShape( static_cast<PCB_SHAPE*>( item ), layerContainer );
+                addShape( static_cast<PCB_SHAPE*>( item ), layerContainer, item );
                 break;
 
             case PCB_TEXT_T:
-                addShape( static_cast<PCB_TEXT*>( item ), layerContainer );
+                addText( static_cast<PCB_TEXT*>( item ), layerContainer, item );
+                break;
+
+            case PCB_TEXTBOX_T:
+                addText( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
+                addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
                 break;
 
             case PCB_DIM_ALIGNED_T:
@@ -618,7 +624,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
             case PCB_DIM_RADIAL_T:
             case PCB_DIM_ORTHOGONAL_T:
             case PCB_DIM_LEADER_T:
-                addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer );
+                addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer, item );
                 break;
 
             default:
@@ -657,8 +663,17 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
 
                     text->TransformTextShapeWithClearanceToPolygon( *layerPoly, cur_layer_id, 0,
                                                                     ARC_HIGH_DEF, ERROR_INSIDE );
-                }
                     break;
+                }
+
+                case PCB_TEXTBOX_T:
+                {
+                    PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
+
+                    textbox->TransformTextShapeWithClearanceToPolygon( *layerPoly, cur_layer_id, 0,
+                                                                       ARC_HIGH_DEF, ERROR_INSIDE );
+                    break;
+                }
 
                 default:
                     wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
@@ -911,11 +926,16 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
             switch( item->Type() )
             {
             case PCB_SHAPE_T:
-                addShape( static_cast<PCB_SHAPE*>( item ), layerContainer );
+                addShape( static_cast<PCB_SHAPE*>( item ), layerContainer, item );
                 break;
 
             case PCB_TEXT_T:
-                addShape( static_cast<PCB_TEXT*>( item ), layerContainer );
+                addText( static_cast<PCB_TEXT*>( item ), layerContainer, item );
+                break;
+
+            case PCB_TEXTBOX_T:
+                addText( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
+                addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
                 break;
 
             case PCB_DIM_ALIGNED_T:
@@ -923,7 +943,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
             case PCB_DIM_RADIAL_T:
             case PCB_DIM_ORTHOGONAL_T:
             case PCB_DIM_LEADER_T:
-                addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer );
+                addShape( static_cast<PCB_DIMENSION_BASE*>( item ), layerContainer, item );
                 break;
 
             default:
@@ -950,8 +970,17 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
 
                 text->TransformTextShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
                                                                 ARC_HIGH_DEF, ERROR_INSIDE );
-            }
                 break;
+            }
+
+            case PCB_TEXTBOX_T:
+            {
+                PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
+
+                textbox->TransformTextShapeWithClearanceToPolygon( *layerPoly, curr_layer_id, 0,
+                                                                   ARC_HIGH_DEF, ERROR_INSIDE );
+                break;
+            }
 
             default:
                 break;
diff --git a/3d-viewer/3d_rendering/raytracing/shapes2D/object_2d.cpp b/3d-viewer/3d_rendering/raytracing/shapes2D/object_2d.cpp
index cfeaadac96..a538c8da9c 100644
--- a/3d-viewer/3d_rendering/raytracing/shapes2D/object_2d.cpp
+++ b/3d-viewer/3d_rendering/raytracing/shapes2D/object_2d.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2015-2020 Mario Luzeiro <mrluzeiro@ua.pt>
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -22,19 +22,14 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-/**
- * @file object_2d.cpp
- */
-
 #include "object_2d.h"
-#include <wx/log.h>
 #include <map>
 
 OBJECT_2D_STATS *OBJECT_2D_STATS::s_instance = nullptr;
 
 
-OBJECT_2D::OBJECT_2D( OBJECT_2D_TYPE aObjType, const BOARD_ITEM& aBoardItem )
-    : m_boardItem(aBoardItem)
+OBJECT_2D::OBJECT_2D( OBJECT_2D_TYPE aObjType, const BOARD_ITEM& aBoardItem ) :
+        m_boardItem( aBoardItem )
 {
     m_obj_type = aObjType;
     OBJECT_2D_STATS::Instance().AddOne( aObjType );
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 3e0cc66d58..026c388e06 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -545,8 +545,10 @@ set( PCB_COMMON_SRCS
     ${CMAKE_SOURCE_DIR}/pcbnew/pad.cpp
     ${CMAKE_SOURCE_DIR}/pcbnew/pcb_target.cpp
     ${CMAKE_SOURCE_DIR}/pcbnew/pcb_text.cpp
+    ${CMAKE_SOURCE_DIR}/pcbnew/pcb_textbox.cpp
     ${CMAKE_SOURCE_DIR}/pcbnew/board_stackup_manager/board_stackup.cpp
     ${CMAKE_SOURCE_DIR}/pcbnew/fp_text.cpp
+    ${CMAKE_SOURCE_DIR}/pcbnew/fp_textbox.cpp
     ${CMAKE_SOURCE_DIR}/pcbnew/pcb_track.cpp
     ${CMAKE_SOURCE_DIR}/pcbnew/zone.cpp
     ${CMAKE_SOURCE_DIR}/pcbnew/collectors.cpp
diff --git a/common/eda_item.cpp b/common/eda_item.cpp
index 373cdb63a1..1231bad124 100644
--- a/common/eda_item.cpp
+++ b/common/eda_item.cpp
@@ -316,7 +316,9 @@ static struct EDA_ITEM_DESC
             .Map( PCB_PAD_T,               _HKI( "Pad" ) )
             .Map( PCB_SHAPE_T,             _HKI( "Graphic" ) )
             .Map( PCB_TEXT_T,              _HKI( "Text" ) )
+            .Map( PCB_TEXTBOX_T,           _HKI( "Text Box" ) )
             .Map( PCB_FP_TEXT_T,           _HKI( "Text" ) )
+            .Map( PCB_FP_TEXTBOX_T,        _HKI( "Text Box" ) )
             .Map( PCB_FP_SHAPE_T,          _HKI( "Graphic" ) )
             .Map( PCB_FP_DIM_ALIGNED_T,    _HKI( "Dimension" ) )
             .Map( PCB_FP_DIM_ORTHOGONAL_T, _HKI( "Dimension" ) )
diff --git a/common/eda_shape.cpp b/common/eda_shape.cpp
index 3883d9fd4f..3fda107585 100644
--- a/common/eda_shape.cpp
+++ b/common/eda_shape.cpp
@@ -246,7 +246,7 @@ void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
             break;
         }
 
-        // Convert non-cartesian-rotated rect to a diamond
+        // Convert non-cardinally-rotated rect to a diamond
         m_shape = SHAPE_T::POLY;
         m_poly.RemoveAllContours();
         m_poly.NewOutline();
diff --git a/common/eda_text.cpp b/common/eda_text.cpp
index 3271dd273e..7ebd53b8b1 100644
--- a/common/eda_text.cpp
+++ b/common/eda_text.cpp
@@ -457,8 +457,11 @@ EDA_TEXT::GetRenderCache( const wxString& forResolvedText ) const
             m_render_cache.clear();
 
             KIFONT::OUTLINE_FONT* font = static_cast<KIFONT::OUTLINE_FONT*>( GetFont() );
-            font->GetLinesAsGlyphs( &m_render_cache, this );
+            TEXT_ATTRIBUTES       attrs = GetAttributes();
 
+            attrs.m_Angle = resolvedAngle;
+
+            font->GetLinesAsGlyphs( &m_render_cache, GetShownText(), GetDrawPos(), attrs );
             m_render_cache_angle = resolvedAngle;
             m_render_cache_text = forResolvedText;
         }
@@ -507,8 +510,14 @@ int EDA_TEXT::GetInterline() const
 
 EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
 {
+    VECTOR2I drawPos = GetDrawPos();
+
     if( m_bounding_box_cache_valid && aLine < 0 && !aInvertY )
+    {
+        m_bounding_box_cache.Offset( drawPos - m_bounding_box_cache_pos );
+        m_bounding_box_cache_pos = drawPos;
         return m_bounding_box_cache;
+    }
 
     EDA_RECT       rect;
     wxArrayString  strings;
@@ -540,7 +549,7 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
     // Creates bounding box (rectangle) for horizontal, left and top justified text. The
     // bounding box will be moved later according to the actual text options
     wxSize   textsize = wxSize( dx, fontSize.y );
-    VECTOR2I pos = GetTextPos();
+    VECTOR2I pos = drawPos;
 
     if( IsMultilineAllowed() && aLine > 0 && ( aLine < static_cast<int>( strings.GetCount() ) ) )
         pos.y -= aLine * GetInterline();
@@ -599,6 +608,7 @@ EDA_RECT EDA_TEXT::GetTextBox( int aLine, bool aInvertY ) const
     if( aLine < 0 && !aInvertY )
     {
         m_bounding_box_cache_valid = true;
+        m_bounding_box_cache_pos = drawPos;
         m_bounding_box_cache = rect;
     }
 
@@ -612,7 +622,7 @@ bool EDA_TEXT::TextHitTest( const VECTOR2I& aPoint, int aAccuracy ) const
     VECTOR2I location = aPoint;
 
     rect.Inflate( aAccuracy );
-    RotatePoint( location, GetTextPos(), -GetTextAngle() );
+    RotatePoint( location, GetDrawPos(), -GetDrawRotation() );
 
     return rect.Contains( location );
 }
@@ -627,7 +637,7 @@ bool EDA_TEXT::TextHitTest( const EDA_RECT& aRect, bool aContains, int aAccuracy
     if( aContains )
         return rect.Contains( GetTextBox() );
 
-    return rect.Intersects( GetTextBox(), GetTextAngle() );
+    return rect.Intersects( GetTextBox(), GetDrawRotation() );
 }
 
 
@@ -649,14 +659,14 @@ void EDA_TEXT::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset,
     }
     else
     {
-        printOneLineOfText( aSettings, aOffset, aColor, aFillMode, GetShownText(), GetTextPos() );
+        printOneLineOfText( aSettings, aOffset, aColor, aFillMode, GetShownText(), GetDrawPos() );
     }
 }
 
 
 void EDA_TEXT::GetLinePositions( std::vector<VECTOR2I>& aPositions, int aLineCount ) const
 {
-    VECTOR2I pos = GetTextPos();    // Position of first line of the multiline text according
+    VECTOR2I pos = GetDrawPos();    // Position of first line of the multiline text according
                                     // to the center of the multiline text block
 
     VECTOR2I offset;                // Offset to next line.
@@ -681,10 +691,10 @@ void EDA_TEXT::GetLinePositions( std::vector<VECTOR2I>& aPositions, int aLineCou
     }
 
     // Rotate the position of the first line around the center of the multiline text block
-    RotatePoint( pos, GetTextPos(), GetTextAngle() );
+    RotatePoint( pos, GetDrawPos(), GetDrawRotation() );
 
     // Rotate the offset lines to increase happened in the right direction
-    RotatePoint( offset, GetTextAngle() );
+    RotatePoint( offset, GetDrawRotation() );
 
     for( int ii = 0; ii < aLineCount; ii++ )
     {
@@ -709,7 +719,7 @@ void EDA_TEXT::printOneLineOfText( const RENDER_SETTINGS* aSettings, const VECTO
     if( IsMirrored() )
         size.x = -size.x;
 
-    GRPrintText( DC, aOffset + aPos, aColor, aText, GetTextAngle(), size, GetHorizJustify(),
+    GRPrintText( DC, aOffset + aPos, aColor, aText, GetDrawRotation(), size, GetHorizJustify(),
                  GetVertJustify(), penWidth, IsItalic(), IsBold(), GetDrawFont() );
 }
 
@@ -850,7 +860,7 @@ std::shared_ptr<SHAPE_COMPOUND> EDA_TEXT::GetEffectiveTextShape( ) const
     TEXT_ATTRIBUTES attrs = GetAttributes();
     attrs.m_Angle = GetDrawRotation();
 
-    font->Draw( &callback_gal, GetShownText(), GetTextPos(), attrs );
+    font->Draw( &callback_gal, GetShownText(), GetDrawPos(), attrs );
 
     return shape;
 }
@@ -930,7 +940,7 @@ void EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( SHAPE_POLY_SET* aCorn
     for( VECTOR2I& corner : corners )
     {
         // Rotate polygon
-        RotatePoint( corner, GetTextPos(), GetTextAngle() );
+        RotatePoint( corner, GetDrawPos(), GetDrawRotation() );
         aCornerBuffer->Append( corner.x, corner.y );
     }
 }
diff --git a/common/font/font.cpp b/common/font/font.cpp
index 869659dd4e..8b0d9d1944 100644
--- a/common/font/font.cpp
+++ b/common/font/font.cpp
@@ -447,7 +447,8 @@ void FONT::LinebreakText( wxString& aText, int aColumnWidth, const VECTOR2I& aSi
 
         for( size_t jj = 0; jj < words.size(); /* advance in loop */ )
         {
-            if( lineWidth + spaceWidth + words[jj].second < aColumnWidth - aThickness )
+            if( lineWidth == 0
+                    || lineWidth + spaceWidth + words[jj].second < aColumnWidth - aThickness )
             {
                 if( lineWidth > 0 )
                 {
diff --git a/common/font/outline_font.cpp b/common/font/outline_font.cpp
index cd4ebe609d..3731a47e81 100644
--- a/common/font/outline_font.cpp
+++ b/common/font/outline_font.cpp
@@ -191,20 +191,6 @@ BOX2I OUTLINE_FONT::getBoundingBox( const std::vector<std::unique_ptr<GLYPH>>& a
 }
 
 
-void OUTLINE_FONT::GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
-                                     const EDA_TEXT* aText ) const
-{
-    wxArrayString         strings;
-    std::vector<VECTOR2I> positions;
-    std::vector<VECTOR2I> extents;
-    TEXT_ATTRIBUTES       attrs = aText->GetAttributes();
-
-    attrs.m_Angle = aText->GetDrawRotation();
-
-    return GetLinesAsGlyphs( aGlyphs, aText->GetShownText(), aText->GetTextPos(), attrs );
-}
-
-
 void OUTLINE_FONT::GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
                                      const wxString& aText, const VECTOR2I& aPosition,
                                      const TEXT_ATTRIBUTES& aAttrs ) const
diff --git a/common/hash_eda.cpp b/common/hash_eda.cpp
index 7d0d19e241..0939d2bc68 100644
--- a/common/hash_eda.cpp
+++ b/common/hash_eda.cpp
@@ -27,6 +27,7 @@
 
 #include <footprint.h>
 #include <fp_text.h>
+#include <fp_textbox.h>
 #include <fp_shape.h>
 #include <pad.h>
 
@@ -115,7 +116,7 @@ size_t hash_fp_item( const EDA_ITEM* aItem, int aFlags )
 
         ret = hash_board_item( text, aFlags );
         hash_combine( ret, text->GetText().ToStdString() );
-        hash_combine( ret,  text->IsItalic() );
+        hash_combine( ret, text->IsItalic() );
         hash_combine( ret, text->IsBold() );
         hash_combine( ret, text->IsMirrored() );
         hash_combine( ret, text->GetTextWidth() );
@@ -179,6 +180,46 @@ size_t hash_fp_item( const EDA_ITEM* aItem, int aFlags )
     }
         break;
 
+    case PCB_FP_TEXTBOX_T:
+    {
+        const FP_TEXTBOX* textbox = static_cast<const FP_TEXTBOX*>( aItem );
+
+        ret = hash_board_item( textbox, aFlags );
+        hash_combine( ret, textbox->GetText().ToStdString() );
+        hash_combine( ret, textbox->IsItalic() );
+        hash_combine( ret, textbox->IsBold() );
+        hash_combine( ret, textbox->IsMirrored() );
+        hash_combine( ret, textbox->GetTextWidth() );
+        hash_combine( ret, textbox->GetTextHeight() );
+        hash_combine( ret, textbox->GetHorizJustify() );
+        hash_combine( ret, textbox->GetVertJustify() );
+
+        if( aFlags & HASH_ROT )
+            hash_combine( ret, textbox->GetTextAngle().AsDegrees() );
+
+        hash_combine( ret, textbox->GetShape() );
+        hash_combine( ret, textbox->GetWidth() );
+
+        if( aFlags & HASH_POS )
+        {
+            if( aFlags & REL_COORD )
+            {
+                hash_combine( ret, textbox->GetStart0().x );
+                hash_combine( ret, textbox->GetStart0().y );
+                hash_combine( ret, textbox->GetEnd0().x );
+                hash_combine( ret, textbox->GetEnd0().y );
+            }
+            else
+            {
+                hash_combine( ret, textbox->GetStart().x );
+                hash_combine( ret, textbox->GetStart().y );
+                hash_combine( ret, textbox->GetEnd().x );
+                hash_combine( ret, textbox->GetEnd().y );
+            }
+        }
+    }
+        break;
+
     default:
         wxASSERT_MSG( false, "Unhandled type in function hash_fp_item() (exporter_gencad.cpp)" );
     }
diff --git a/common/pcb.keywords b/common/pcb.keywords
index 8ab096de41..c9bef4d109 100644
--- a/common/pcb.keywords
+++ b/common/pcb.keywords
@@ -2,7 +2,7 @@
 # This program source code file is part of KiCad, a free EDA CAD application.
 #
 # Copyright (C) 2012 CERN.
-# Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
+# Copyright (C) 2019-2022 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
@@ -120,6 +120,7 @@ fp_line
 fp_poly
 fp_rect
 fp_text
+fp_text_box
 free
 full
 general
@@ -133,6 +134,7 @@ gr_line
 gr_poly
 gr_rect
 gr_text
+gr_text_box
 hatch
 hatch_thickness
 hatch_gap
diff --git a/eeschema/dialogs/dialog_field_properties_base.cpp b/eeschema/dialogs/dialog_field_properties_base.cpp
index df0437c741..f61fdd6aae 100644
--- a/eeschema/dialogs/dialog_field_properties_base.cpp
+++ b/eeschema/dialogs/dialog_field_properties_base.cpp
@@ -96,7 +96,7 @@ DIALOG_FIELD_PROPERTIES_BASE::DIALOG_FIELD_PROPERTIES_BASE( wxWindow* parent, wx
 
 	m_fontLabel = new wxStaticText( this, wxID_ANY, _("Font:"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_fontLabel->Wrap( -1 );
-	gbSizer1->Add( m_fontLabel, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxALL, 5 );
+	gbSizer1->Add( m_fontLabel, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxRIGHT|wxLEFT, 5 );
 
 	wxString m_fontCtrlChoices[] = { _("Default Font"), _("KiCad Font") };
 	int m_fontCtrlNChoices = sizeof( m_fontCtrlChoices ) / sizeof( wxString );
diff --git a/eeschema/dialogs/dialog_field_properties_base.fbp b/eeschema/dialogs/dialog_field_properties_base.fbp
index d2f1eec390..d2cc76fc22 100644
--- a/eeschema/dialogs/dialog_field_properties_base.fbp
+++ b/eeschema/dialogs/dialog_field_properties_base.fbp
@@ -512,7 +512,7 @@
                                     <property name="border">5</property>
                                     <property name="colspan">1</property>
                                     <property name="column">0</property>
-                                    <property name="flag">wxALL</property>
+                                    <property name="flag">wxRIGHT|wxLEFT</property>
                                     <property name="row">0</property>
                                     <property name="rowspan">1</property>
                                     <object class="wxStaticText" expanded="1">
diff --git a/eeschema/dialogs/dialog_lib_text_properties_base.cpp b/eeschema/dialogs/dialog_lib_text_properties_base.cpp
index 1f234d0ce2..80fa9738ed 100644
--- a/eeschema/dialogs/dialog_lib_text_properties_base.cpp
+++ b/eeschema/dialogs/dialog_lib_text_properties_base.cpp
@@ -65,7 +65,7 @@ DIALOG_LIB_TEXT_PROPERTIES_BASE::DIALOG_LIB_TEXT_PROPERTIES_BASE( wxWindow* pare
 
 	m_fontLabel = new wxStaticText( this, wxID_ANY, _("Font:"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_fontLabel->Wrap( -1 );
-	gbSizer1->Add( m_fontLabel, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALL, 5 );
+	gbSizer1->Add( m_fontLabel, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxRIGHT|wxLEFT, 5 );
 
 	wxString m_fontCtrlChoices[] = { _("Default Font"), _("KiCad Font") };
 	int m_fontCtrlNChoices = sizeof( m_fontCtrlChoices ) / sizeof( wxString );
diff --git a/eeschema/dialogs/dialog_lib_text_properties_base.fbp b/eeschema/dialogs/dialog_lib_text_properties_base.fbp
index 31238aa229..2e55169235 100644
--- a/eeschema/dialogs/dialog_lib_text_properties_base.fbp
+++ b/eeschema/dialogs/dialog_lib_text_properties_base.fbp
@@ -222,7 +222,7 @@
                                     <property name="border">5</property>
                                     <property name="colspan">1</property>
                                     <property name="column">0</property>
-                                    <property name="flag">wxALL</property>
+                                    <property name="flag">wxRIGHT|wxLEFT</property>
                                     <property name="row">2</property>
                                     <property name="rowspan">1</property>
                                     <object class="wxStaticText" expanded="1">
@@ -356,7 +356,7 @@
                                     <property name="flag">wxEXPAND</property>
                                     <property name="row">2</property>
                                     <property name="rowspan">1</property>
-                                    <object class="wxBoxSizer" expanded="0">
+                                    <object class="wxBoxSizer" expanded="1">
                                         <property name="minimum_size"></property>
                                         <property name="name">formattingSizer</property>
                                         <property name="orient">wxHORIZONTAL</property>
diff --git a/eeschema/dialogs/dialog_lib_textbox_properties_base.cpp b/eeschema/dialogs/dialog_lib_textbox_properties_base.cpp
index b1dbf84f91..7d4b5ff6bc 100644
--- a/eeschema/dialogs/dialog_lib_textbox_properties_base.cpp
+++ b/eeschema/dialogs/dialog_lib_textbox_properties_base.cpp
@@ -63,13 +63,13 @@ DIALOG_LIB_TEXTBOX_PROPERTIES_BASE::DIALOG_LIB_TEXTBOX_PROPERTIES_BASE( wxWindow
 
 	m_fontLabel = new wxStaticText( this, wxID_ANY, _("Font:"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_fontLabel->Wrap( -1 );
-	m_textEntrySizer->Add( m_fontLabel, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxRIGHT|wxALIGN_CENTER_VERTICAL, 5 );
+	m_textEntrySizer->Add( m_fontLabel, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT, 5 );
 
 	wxString m_fontCtrlChoices[] = { _("Default Font"), _("KiCad Font") };
 	int m_fontCtrlNChoices = sizeof( m_fontCtrlChoices ) / sizeof( wxString );
 	m_fontCtrl = new FONT_CHOICE( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_fontCtrlNChoices, m_fontCtrlChoices, 0 );
 	m_fontCtrl->SetSelection( 0 );
-	m_textEntrySizer->Add( m_fontCtrl, wxGBPosition( 1, 1 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
+	m_textEntrySizer->Add( m_fontCtrl, wxGBPosition( 1, 1 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP, 5 );
 
 	wxBoxSizer* bSizeCtrlSizer;
 	bSizeCtrlSizer = new wxBoxSizer( wxHORIZONTAL );
@@ -77,50 +77,50 @@ DIALOG_LIB_TEXTBOX_PROPERTIES_BASE::DIALOG_LIB_TEXTBOX_PROPERTIES_BASE( wxWindow
 	m_separator1 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
 	m_separator1->Enable( false );
 
-	bSizeCtrlSizer->Add( m_separator1, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+	bSizeCtrlSizer->Add( m_separator1, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
 
 	m_bold = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
 	m_bold->SetToolTip( _("Bold") );
 
-	bSizeCtrlSizer->Add( m_bold, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 5 );
+	bSizeCtrlSizer->Add( m_bold, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 	m_italic = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
 	m_italic->SetToolTip( _("Italic") );
 
-	bSizeCtrlSizer->Add( m_italic, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 5 );
+	bSizeCtrlSizer->Add( m_italic, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 	m_separator2 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
 	m_separator2->Enable( false );
 
-	bSizeCtrlSizer->Add( m_separator2, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 5 );
+	bSizeCtrlSizer->Add( m_separator2, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 	m_spin0 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
 	m_spin0->SetToolTip( _("Align right") );
 
-	bSizeCtrlSizer->Add( m_spin0, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 5 );
+	bSizeCtrlSizer->Add( m_spin0, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 	m_spin1 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
 	m_spin1->SetToolTip( _("Align bottom") );
 
-	bSizeCtrlSizer->Add( m_spin1, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 5 );
+	bSizeCtrlSizer->Add( m_spin1, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 	m_spin2 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
 	m_spin2->SetToolTip( _("Align left") );
 
-	bSizeCtrlSizer->Add( m_spin2, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 5 );
+	bSizeCtrlSizer->Add( m_spin2, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 	m_spin3 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
 	m_spin3->SetToolTip( _("Align top") );
 
-	bSizeCtrlSizer->Add( m_spin3, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 5 );
+	bSizeCtrlSizer->Add( m_spin3, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 	m_separator3 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
 	m_separator3->Enable( false );
 
-	bSizeCtrlSizer->Add( m_separator3, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 );
+	bSizeCtrlSizer->Add( m_separator3, 0, wxALIGN_CENTER_VERTICAL, 5 );
 
 
-	m_textEntrySizer->Add( bSizeCtrlSizer, wxGBPosition( 1, 3 ), wxGBSpan( 1, 1 ), wxEXPAND, 5 );
+	m_textEntrySizer->Add( bSizeCtrlSizer, wxGBPosition( 1, 3 ), wxGBSpan( 1, 1 ), wxEXPAND|wxTOP, 5 );
 
 	wxBoxSizer* bSizer41;
 	bSizer41 = new wxBoxSizer( wxVERTICAL );
diff --git a/eeschema/dialogs/dialog_lib_textbox_properties_base.fbp b/eeschema/dialogs/dialog_lib_textbox_properties_base.fbp
index 102846ba22..8054e0e7f2 100644
--- a/eeschema/dialogs/dialog_lib_textbox_properties_base.fbp
+++ b/eeschema/dialogs/dialog_lib_textbox_properties_base.fbp
@@ -211,7 +211,7 @@
                             <property name="border">5</property>
                             <property name="colspan">1</property>
                             <property name="column">0</property>
-                            <property name="flag">wxRIGHT|wxALIGN_CENTER_VERTICAL</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT</property>
                             <property name="row">1</property>
                             <property name="rowspan">1</property>
                             <object class="wxStaticText" expanded="1">
@@ -275,7 +275,7 @@
                             <property name="border">5</property>
                             <property name="colspan">2</property>
                             <property name="column">1</property>
-                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND|wxTOP</property>
                             <property name="row">1</property>
                             <property name="rowspan">1</property>
                             <object class="wxChoice" expanded="1">
@@ -342,17 +342,17 @@
                             <property name="border">5</property>
                             <property name="colspan">1</property>
                             <property name="column">3</property>
-                            <property name="flag">wxEXPAND</property>
+                            <property name="flag">wxEXPAND|wxTOP</property>
                             <property name="row">1</property>
                             <property name="rowspan">1</property>
-                            <object class="wxBoxSizer" expanded="0">
+                            <object class="wxBoxSizer" expanded="1">
                                 <property name="minimum_size"></property>
                                 <property name="name">bSizeCtrlSizer</property>
                                 <property name="orient">wxHORIZONTAL</property>
                                 <property name="permission">none</property>
                                 <object class="sizeritem" expanded="0">
                                     <property name="border">5</property>
-                                    <property name="flag">wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
                                     <property name="proportion">0</property>
                                     <object class="wxBitmapButton" expanded="0">
                                         <property name="BottomDockable">1</property>
@@ -424,7 +424,7 @@
                                 </object>
                                 <object class="sizeritem" expanded="0">
                                     <property name="border">5</property>
-                                    <property name="flag">wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                                     <property name="proportion">0</property>
                                     <object class="wxBitmapButton" expanded="0">
                                         <property name="BottomDockable">1</property>
@@ -496,7 +496,7 @@
                                 </object>
                                 <object class="sizeritem" expanded="0">
                                     <property name="border">5</property>
-                                    <property name="flag">wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                                     <property name="proportion">0</property>
                                     <object class="wxBitmapButton" expanded="0">
                                         <property name="BottomDockable">1</property>
@@ -568,7 +568,7 @@
                                 </object>
                                 <object class="sizeritem" expanded="0">
                                     <property name="border">5</property>
-                                    <property name="flag">wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                                     <property name="proportion">0</property>
                                     <object class="wxBitmapButton" expanded="0">
                                         <property name="BottomDockable">1</property>
@@ -640,7 +640,7 @@
                                 </object>
                                 <object class="sizeritem" expanded="0">
                                     <property name="border">5</property>
-                                    <property name="flag">wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                                     <property name="proportion">0</property>
                                     <object class="wxBitmapButton" expanded="0">
                                         <property name="BottomDockable">1</property>
@@ -712,7 +712,7 @@
                                 </object>
                                 <object class="sizeritem" expanded="0">
                                     <property name="border">5</property>
-                                    <property name="flag">wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                                     <property name="proportion">0</property>
                                     <object class="wxBitmapButton" expanded="0">
                                         <property name="BottomDockable">1</property>
@@ -784,7 +784,7 @@
                                 </object>
                                 <object class="sizeritem" expanded="0">
                                     <property name="border">5</property>
-                                    <property name="flag">wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                                     <property name="proportion">0</property>
                                     <object class="wxBitmapButton" expanded="0">
                                         <property name="BottomDockable">1</property>
@@ -856,7 +856,7 @@
                                 </object>
                                 <object class="sizeritem" expanded="0">
                                     <property name="border">5</property>
-                                    <property name="flag">wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                                     <property name="proportion">0</property>
                                     <object class="wxBitmapButton" expanded="0">
                                         <property name="BottomDockable">1</property>
@@ -928,7 +928,7 @@
                                 </object>
                                 <object class="sizeritem" expanded="0">
                                     <property name="border">5</property>
-                                    <property name="flag">wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
                                     <property name="proportion">0</property>
                                     <object class="wxBitmapButton" expanded="0">
                                         <property name="BottomDockable">1</property>
diff --git a/eeschema/lib_textbox.cpp b/eeschema/lib_textbox.cpp
index 0c2497fb63..9adf0b60d3 100644
--- a/eeschema/lib_textbox.cpp
+++ b/eeschema/lib_textbox.cpp
@@ -120,20 +120,20 @@ void LIB_TEXTBOX::UpdateTextPosition()
     BOX2I bbox( VECTOR2I( std::min( m_start.x, m_end.x ), std::min( -m_start.y, -m_end.y ) ),
                 VECTOR2I( abs( m_end.x - m_start.x ), abs( m_end.y - m_start.y ) ) );
 
-    if( GetTextAngle() == ANGLE_HORIZONTAL )
-    {
-        if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
-            SetTextPos( VECTOR2I( bbox.GetRight() - margin, bbox.GetTop() + margin )  );
-        else
-            SetTextPos( VECTOR2I( bbox.GetLeft() + margin, bbox.GetTop() + margin )  );
-    }
-    else
+    if( GetTextAngle() == ANGLE_VERTICAL )
     {
         if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
             SetTextPos( VECTOR2I( bbox.GetLeft() + margin, bbox.GetTop() + margin )  );
         else
             SetTextPos( VECTOR2I( bbox.GetLeft() + margin, bbox.GetBottom() - margin )  );
     }
+    else
+    {
+        if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
+            SetTextPos( VECTOR2I( bbox.GetRight() - margin, bbox.GetTop() + margin )  );
+        else
+            SetTextPos( VECTOR2I( bbox.GetLeft() + margin, bbox.GetTop() + margin )  );
+    }
 }
 
 
@@ -374,8 +374,6 @@ void LIB_TEXTBOX::Plot( PLOTTER* aPlotter, const VECTOR2I& aOffset, bool aFill,
 
 void LIB_TEXTBOX::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
 {
-    wxString msg;
-
     // Don't use GetShownText() here; we want to show the user the variable references
     aList.emplace_back( _( "Text Box" ), UnescapeString( GetText() ) );
 
diff --git a/eeschema/sch_field.cpp b/eeschema/sch_field.cpp
index e1ec4a87d0..35409d092b 100644
--- a/eeschema/sch_field.cpp
+++ b/eeschema/sch_field.cpp
@@ -404,24 +404,6 @@ EDA_ANGLE SCH_FIELD::GetDrawRotation() const
 }
 
 
-VECTOR2I SCH_FIELD::GetDrawPos() const
-{
-    return GetBoundingBox().Centre();
-}
-
-
-GR_TEXT_H_ALIGN_T SCH_FIELD::GetDrawHorizJustify() const
-{
-    return GR_TEXT_H_ALIGN_CENTER;
-}
-
-
-GR_TEXT_V_ALIGN_T SCH_FIELD::GetDrawVertJustify() const
-{
-    return GR_TEXT_V_ALIGN_CENTER;
-}
-
-
 const EDA_RECT SCH_FIELD::GetBoundingBox() const
 {
     // Calculate the text bounding box:
diff --git a/eeschema/sch_field.h b/eeschema/sch_field.h
index 02dac77dfd..a2f2c783b8 100644
--- a/eeschema/sch_field.h
+++ b/eeschema/sch_field.h
@@ -122,9 +122,6 @@ public:
      * Adjusters to allow EDA_TEXT to draw/print/etc. text in absolute coords.
      */
     EDA_ANGLE         GetDrawRotation() const override;
-    VECTOR2I          GetDrawPos() const override;
-    GR_TEXT_H_ALIGN_T GetDrawHorizJustify() const override;
-    GR_TEXT_V_ALIGN_T GetDrawVertJustify() const override;
 
     const EDA_RECT GetBoundingBox() const override;
 
diff --git a/eeschema/sch_textbox.cpp b/eeschema/sch_textbox.cpp
index 629033c499..67de701993 100644
--- a/eeschema/sch_textbox.cpp
+++ b/eeschema/sch_textbox.cpp
@@ -114,20 +114,20 @@ void SCH_TEXTBOX::UpdateTextPosition()
 
     bbox.Normalize();
 
-    if( GetTextAngle() == ANGLE_HORIZONTAL )
-    {
-        if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
-            SetTextPos( VECTOR2I( bbox.GetRight() - margin, bbox.GetTop() + margin )  );
-        else
-            SetTextPos( VECTOR2I( bbox.GetLeft() + margin, bbox.GetTop() + margin )  );
-    }
-    else
+    if( GetTextAngle() == ANGLE_VERTICAL )
     {
         if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
             SetTextPos( VECTOR2I( bbox.GetLeft() + margin, bbox.GetTop() + margin )  );
         else
             SetTextPos( VECTOR2I( bbox.GetLeft() + margin, bbox.GetBottom() - margin )  );
     }
+    else
+    {
+        if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
+            SetTextPos( VECTOR2I( bbox.GetRight() - margin, bbox.GetTop() + margin )  );
+        else
+            SetTextPos( VECTOR2I( bbox.GetLeft() + margin, bbox.GetTop() + margin )  );
+    }
 }
 
 
@@ -360,8 +360,6 @@ void SCH_TEXTBOX::Plot( PLOTTER* aPlotter ) const
 
 void SCH_TEXTBOX::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
 {
-    wxString msg;
-
     // Don't use GetShownText() here; we want to show the user the variable references
     aList.emplace_back( _( "Text Box" ), UnescapeString( GetText() ) );
 
diff --git a/include/core/typeinfo.h b/include/core/typeinfo.h
index 028447edf2..757d1c0c9b 100644
--- a/include/core/typeinfo.h
+++ b/include/core/typeinfo.h
@@ -89,7 +89,9 @@ enum KICAD_T
     PCB_PAD_T,               ///< class PAD, a pad in a footprint
     PCB_SHAPE_T,             ///< class PCB_SHAPE, a segment not on copper layers
     PCB_TEXT_T,              ///< class PCB_TEXT, text on a layer
+    PCB_TEXTBOX_T,           ///< class PCB_TEXTBOX, wrapped text on a layer
     PCB_FP_TEXT_T,           ///< class FP_TEXT, text in a footprint
+    PCB_FP_TEXTBOX_T,        ///< class FP_TEXTBOX, wrapped text in a footprint
     PCB_FP_SHAPE_T,          ///< class FP_SHAPE, a footprint edge
     PCB_FP_DIM_ALIGNED_T,    ///< class PCB_DIM_ALIGNED, a linear dimension (graphic item)
     PCB_FP_DIM_LEADER_T,     ///< class PCB_DIM_LEADER, a leader dimension (graphic item)
@@ -405,7 +407,9 @@ constexpr bool IsPcbnewType( const KICAD_T aType )
     case PCB_PAD_T:
     case PCB_SHAPE_T:
     case PCB_TEXT_T:
+    case PCB_TEXTBOX_T:
     case PCB_FP_TEXT_T:
+    case PCB_FP_TEXTBOX_T:
     case PCB_FP_SHAPE_T:
     case PCB_FP_DIM_ALIGNED_T:
     case PCB_FP_DIM_LEADER_T:
diff --git a/include/eda_text.h b/include/eda_text.h
index 25ea5bacae..b3d173b4c9 100644
--- a/include/eda_text.h
+++ b/include/eda_text.h
@@ -304,8 +304,6 @@ public:
     virtual KIFONT::FONT* GetDrawFont() const;
     virtual EDA_ANGLE GetDrawRotation() const               { return GetTextAngle(); }
     virtual VECTOR2I GetDrawPos() const                     { return GetTextPos(); }
-    virtual GR_TEXT_H_ALIGN_T GetDrawHorizJustify() const   { return GetHorizJustify(); };
-    virtual GR_TEXT_V_ALIGN_T GetDrawVertJustify() const    { return GetVertJustify(); };
 
     virtual void ClearRenderCache();
     virtual void ClearBoundingBoxCache();
@@ -344,6 +342,7 @@ private:
     mutable std::vector<std::unique_ptr<KIFONT::GLYPH>> m_render_cache;
 
     mutable bool     m_bounding_box_cache_valid;
+    mutable VECTOR2I m_bounding_box_cache_pos;
     mutable EDA_RECT m_bounding_box_cache;
 
     TEXT_ATTRIBUTES  m_attributes;
diff --git a/include/font/outline_font.h b/include/font/outline_font.h
index cf0bb7bbfb..f5ee84284f 100644
--- a/include/font/outline_font.h
+++ b/include/font/outline_font.h
@@ -112,9 +112,6 @@ public:
      * @param aGlyphs returns text glyphs
      * @param aText the text item
      */
-    void GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>* aGlyphs,
-                           const EDA_TEXT* aText ) const;
-
     void GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>* aGlyphs, const wxString& aText,
                            const VECTOR2I& aPosition, const TEXT_ATTRIBUTES& aAttrs ) const;
 
diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index 24c55e0b71..2e17db2ac2 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -141,6 +141,8 @@ set( PCBNEW_DIALOGS
     dialogs/dialog_target_properties_base.cpp
     dialogs/dialog_text_properties.cpp
     dialogs/dialog_text_properties_base.cpp
+    dialogs/dialog_textbox_properties.cpp
+    dialogs/dialog_textbox_properties_base.cpp
     dialogs/dialog_track_via_properties.cpp
     dialogs/dialog_track_via_properties_base.cpp
     dialogs/dialog_track_via_size.cpp
diff --git a/pcbnew/array_creator.cpp b/pcbnew/array_creator.cpp
index ffdaab199d..412faeb892 100644
--- a/pcbnew/array_creator.cpp
+++ b/pcbnew/array_creator.cpp
@@ -113,6 +113,7 @@ void ARRAY_CREATOR::Invoke()
                     case PCB_FOOTPRINT_T:
                     case PCB_SHAPE_T:
                     case PCB_TEXT_T:
+                    case PCB_TEXTBOX_T:
                     case PCB_TRACE_T:
                     case PCB_ARC_T:
                     case PCB_VIA_T:
diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp
index b596698520..daf9cc8ff7 100644
--- a/pcbnew/board.cpp
+++ b/pcbnew/board.cpp
@@ -5,7 +5,7 @@
  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
  *
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -40,6 +40,7 @@
 #include <pcb_target.h>
 #include <pcb_shape.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
 #include <core/arraydim.h>
 #include <core/kicad_algo.h>
 #include <connectivity/connectivity_data.h>
@@ -299,6 +300,7 @@ void BOARD::Move( const VECTOR2I& aMoveVector ) // overload
     static const KICAD_T top_level_board_stuff[] = {
         PCB_MARKER_T,
         PCB_TEXT_T,
+        PCB_TEXTBOX_T,
         PCB_SHAPE_T,
         PCB_DIM_ALIGNED_T,
         PCB_DIM_ORTHOGONAL_T,
@@ -680,6 +682,7 @@ void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
     case PCB_DIM_LEADER_T:
     case PCB_SHAPE_T:
     case PCB_TEXT_T:
+    case PCB_TEXTBOX_T:
     case PCB_TARGET_T:
         if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
             m_drawings.push_back( aBoardItem );
@@ -787,6 +790,7 @@ void BOARD::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aRemoveMode )
     case PCB_DIM_LEADER_T:
     case PCB_SHAPE_T:
     case PCB_TEXT_T:
+    case PCB_TEXTBOX_T:
     case PCB_TARGET_T:
         alg::delete_matching( m_drawings, aBoardItem );
         break;
@@ -1225,6 +1229,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T s
         case PCB_FOOTPRINT_T:
         case PCB_PAD_T:
         case PCB_FP_TEXT_T:
+        case PCB_FP_TEXTBOX_T:
         case PCB_FP_SHAPE_T:
         case PCB_FP_DIM_ALIGNED_T:
         case PCB_FP_DIM_LEADER_T:
@@ -1244,6 +1249,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T s
                 case PCB_FOOTPRINT_T:
                 case PCB_PAD_T:
                 case PCB_FP_TEXT_T:
+                case PCB_FP_TEXTBOX_T:
                 case PCB_FP_SHAPE_T:
                 case PCB_FP_DIM_ALIGNED_T:
                 case PCB_FP_DIM_LEADER_T:
@@ -1264,6 +1270,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T s
 
         case PCB_SHAPE_T:
         case PCB_TEXT_T:
+        case PCB_TEXTBOX_T:
         case PCB_DIM_ALIGNED_T:
         case PCB_DIM_CENTER_T:
         case PCB_DIM_RADIAL_T:
@@ -1279,6 +1286,7 @@ SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T s
                 {
                 case PCB_SHAPE_T:
                 case PCB_TEXT_T:
+                case PCB_TEXTBOX_T:
                 case PCB_DIM_ALIGNED_T:
                 case PCB_DIM_CENTER_T:
                 case PCB_DIM_RADIAL_T:
@@ -2193,6 +2201,13 @@ bool BOARD::cmp_drawings::operator()( const BOARD_ITEM* aFirst,
         const PCB_TEXT* other = static_cast<const PCB_TEXT*>( aSecond );
         return text->Compare( other );
     }
+    else if( aFirst->Type() == PCB_TEXTBOX_T )
+    {
+        const PCB_TEXTBOX* textbox = static_cast<const PCB_TEXTBOX*>( aFirst );
+        const PCB_TEXTBOX* other = static_cast<const PCB_TEXTBOX*>( aSecond );
+
+        return textbox->PCB_SHAPE::Compare( other ) && textbox->EDA_TEXT::Compare( other );
+    }
 
     return aFirst->m_Uuid < aSecond->m_Uuid;
 }
@@ -2263,6 +2278,14 @@ void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer,
             break;
         }
 
+        case PCB_TEXTBOX_T:
+        {
+            const PCB_TEXTBOX* textbox = static_cast<const PCB_TEXTBOX*>( item );
+            textbox->TransformTextShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
+                                                               ERROR_INSIDE );
+            break;
+        }
+
         default:
             break;
         }
diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp
index a75dd1766e..ecca0bb117 100644
--- a/pcbnew/board_commit.cpp
+++ b/pcbnew/board_commit.cpp
@@ -179,15 +179,16 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a
                     if( !( changeFlags & CHT_DONE ) )
                         board->Footprints().front()->Add( boardItem );
                 }
-                else if( boardItem->Type() == PCB_PAD_T ||
-                         boardItem->Type() == PCB_FP_TEXT_T ||
-                         boardItem->Type() == PCB_FP_SHAPE_T ||
-                         boardItem->Type() == PCB_FP_DIM_ALIGNED_T ||
-                         boardItem->Type() == PCB_FP_DIM_LEADER_T ||
-                         boardItem->Type() == PCB_FP_DIM_CENTER_T ||
-                         boardItem->Type() == PCB_FP_DIM_RADIAL_T ||
-                         boardItem->Type() == PCB_FP_DIM_ORTHOGONAL_T ||
-                         boardItem->Type() == PCB_FP_ZONE_T )
+                else if( boardItem->Type() == PCB_PAD_T
+                        || boardItem->Type() == PCB_FP_TEXT_T
+                        || boardItem->Type() == PCB_FP_TEXTBOX_T
+                        || boardItem->Type() == PCB_FP_SHAPE_T
+                        || boardItem->Type() == PCB_FP_DIM_ALIGNED_T
+                        || boardItem->Type() == PCB_FP_DIM_LEADER_T
+                        || boardItem->Type() == PCB_FP_DIM_CENTER_T
+                        || boardItem->Type() == PCB_FP_DIM_RADIAL_T
+                        || boardItem->Type() == PCB_FP_DIM_ORTHOGONAL_T
+                        || boardItem->Type() == PCB_FP_ZONE_T )
                 {
                     wxASSERT( boardItem->GetParent() &&
                               boardItem->GetParent()->Type() == PCB_FOOTPRINT_T );
@@ -229,6 +230,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a
                 case PCB_PAD_T:
                 case PCB_FP_SHAPE_T:
                 case PCB_FP_TEXT_T:
+                case PCB_FP_TEXTBOX_T:
                 case PCB_FP_DIM_ALIGNED_T:
                 case PCB_FP_DIM_LEADER_T:
                 case PCB_FP_DIM_CENTER_T:
@@ -267,6 +269,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a
                 // Board items
                 case PCB_SHAPE_T:            // a shape (normally not on copper layers)
                 case PCB_TEXT_T:             // a text on a layer
+                case PCB_TEXTBOX_T:          // a wrapped text on a layer
                 case PCB_TRACE_T:            // a track segment (segment on a copper layer)
                 case PCB_ARC_T:              // an arced track segment (segment on a copper layer)
                 case PCB_VIA_T:              // a via (like track segment on a copper layer)
@@ -461,23 +464,24 @@ EDA_ITEM* BOARD_COMMIT::parentObject( EDA_ITEM* aItem ) const
 {
     switch( aItem->Type() )
     {
-        case PCB_PAD_T:
-        case PCB_FP_SHAPE_T:
-        case PCB_FP_TEXT_T:
-        case PCB_FP_DIM_ALIGNED_T:
-        case PCB_FP_DIM_LEADER_T:
-        case PCB_FP_DIM_CENTER_T:
-        case PCB_FP_DIM_RADIAL_T:
-        case PCB_FP_DIM_ORTHOGONAL_T:
-        case PCB_FP_ZONE_T:
-            return aItem->GetParent();
+    case PCB_PAD_T:
+    case PCB_FP_SHAPE_T:
+    case PCB_FP_TEXT_T:
+    case PCB_FP_TEXTBOX_T:
+    case PCB_FP_DIM_ALIGNED_T:
+    case PCB_FP_DIM_LEADER_T:
+    case PCB_FP_DIM_CENTER_T:
+    case PCB_FP_DIM_RADIAL_T:
+    case PCB_FP_DIM_ORTHOGONAL_T:
+    case PCB_FP_ZONE_T:
+        return aItem->GetParent();
 
-        case PCB_ZONE_T:
-            wxASSERT( !dynamic_cast<FOOTPRINT*>( aItem->GetParent() ) );
-            return aItem;
+    case PCB_ZONE_T:
+        wxASSERT( !dynamic_cast<FOOTPRINT*>( aItem->GetParent() ) );
+        return aItem;
 
-        default:
-            break;
+    default:
+        break;
     }
 
     return aItem;
diff --git a/pcbnew/collectors.cpp b/pcbnew/collectors.cpp
index f3f8f36754..bfca7df52b 100644
--- a/pcbnew/collectors.cpp
+++ b/pcbnew/collectors.cpp
@@ -49,6 +49,7 @@ const KICAD_T GENERAL_COLLECTOR::AllBoardItems[] = {
     //  *** all items in a same list (shown here) must be contiguous ****
     PCB_MARKER_T,           // in m_markers
     PCB_TEXT_T,             // in m_drawings
+    PCB_TEXTBOX_T,          // in m_drawings
     PCB_SHAPE_T,            // in m_drawings
     PCB_DIM_ALIGNED_T,      // in m_drawings
     PCB_DIM_CENTER_T,       // in m_drawings
@@ -61,6 +62,7 @@ const KICAD_T GENERAL_COLLECTOR::AllBoardItems[] = {
     PCB_ARC_T,              // in m_tracks
     PCB_PAD_T,              // in footprints
     PCB_FP_TEXT_T,          // in footprints
+    PCB_FP_TEXTBOX_T,       // in footprints
     PCB_FOOTPRINT_T,        // in m_footprints
     PCB_GROUP_T,            // in m_groups
     PCB_ZONE_T,             // in m_zones
@@ -71,6 +73,7 @@ const KICAD_T GENERAL_COLLECTOR::AllBoardItems[] = {
 const KICAD_T GENERAL_COLLECTOR::BoardLevelItems[] = {
     PCB_MARKER_T,
     PCB_TEXT_T,
+    PCB_TEXTBOX_T,
     PCB_SHAPE_T,
     PCB_DIM_ALIGNED_T,
     PCB_DIM_ORTHOGONAL_T,
@@ -105,6 +108,7 @@ const KICAD_T GENERAL_COLLECTOR::PadsOrTracks[] = {
 
 const KICAD_T GENERAL_COLLECTOR::FootprintItems[] = {
     PCB_FP_TEXT_T,
+    PCB_FP_TEXTBOX_T,
     PCB_FP_SHAPE_T,
     PCB_FP_DIM_ALIGNED_T,
     PCB_FP_DIM_ORTHOGONAL_T,
@@ -207,6 +211,10 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
         breakhere++;
         break;
 
+    case PCB_TEXTBOX_T:
+        breakhere++;
+        break;
+
     case PCB_SHAPE_T:
         breakhere++;
         break;
@@ -224,6 +232,10 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
         }
         break;
 
+    case PCB_FP_TEXTBOX_T:
+        breakhere++;
+        break;
+
     case PCB_FOOTPRINT_T:
         {
             FOOTPRINT* footprint = (FOOTPRINT*) item;
@@ -291,6 +303,7 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
         break;
 
     case PCB_TEXT_T:
+    case PCB_TEXTBOX_T:
         break;
 
     case PCB_SHAPE_T:
@@ -319,25 +332,36 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
         break;
 
     case PCB_FP_TEXT_T:
+    case PCB_FP_TEXTBOX_T:
     {
-        FP_TEXT *text = static_cast<FP_TEXT*>( item );
+        PCB_LAYER_ID layer = item->GetLayer();
 
-        if( m_Guide->IgnoreHiddenFPText() && !text->IsVisible() )
+        if( m_Guide->IgnoreHiddenFPText() && item->Type() == PCB_FP_TEXT_T )
+        {
+            FP_TEXT *text = static_cast<FP_TEXT*>( item );
+
+            if( !text->IsVisible() )
+                goto exit;
+        }
+
+        if( m_Guide->IgnoreFPTextOnBack() && IsBackLayer( layer ) )
             goto exit;
 
-        if( m_Guide->IgnoreFPTextOnBack() && IsBackLayer( text->GetLayer() ) )
+        if( m_Guide->IgnoreFPTextOnFront() && IsFrontLayer( layer ) )
             goto exit;
 
-        if( m_Guide->IgnoreFPTextOnFront() && IsFrontLayer( text->GetLayer() ) )
-            goto exit;
+        /*
+         * The three text types have different criteria: reference and value have their own
+         * ignore flags; user text instead follows their layer visibility. Checking this here
+         * is simpler than later (when layer visibility is checked for other entities)
+         */
 
-        /* The three text types have different criteria: reference
-         * and value have their own ignore flags; user text instead
-         * follows their layer visibility. Checking this here is
-         * simpler than later (when layer visibility is checked for
-         * other entities) */
+        FP_TEXT::TEXT_TYPE textType = FP_TEXT::TEXT_is_DIVERS;
 
-        switch( text->GetType() )
+        if( item->Type() == PCB_FP_TEXT_T )
+            textType = static_cast<FP_TEXT*>( item )->GetType();
+
+        switch( textType )
         {
         case FP_TEXT::TEXT_is_REFERENCE:
             if( m_Guide->IgnoreFPReferences() )
@@ -352,8 +376,7 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
             break;
 
         case FP_TEXT::TEXT_is_DIVERS:
-            if( !m_Guide->IsLayerVisible( text->GetLayer() )
-              && m_Guide->IgnoreNonVisibleLayers() )
+            if( !m_Guide->IsLayerVisible( layer ) && m_Guide->IgnoreNonVisibleLayers() )
                 goto exit;
 
             break;
diff --git a/pcbnew/cross-probing.cpp b/pcbnew/cross-probing.cpp
index ed717c63e4..4bf2139dc7 100644
--- a/pcbnew/cross-probing.cpp
+++ b/pcbnew/cross-probing.cpp
@@ -311,7 +311,8 @@ std::string FormatProbeItem( BOARD_ITEM* aItem )
         footprint = static_cast<FOOTPRINT*>( aItem->GetParent() );
         wxString pad = static_cast<PAD*>( aItem )->GetNumber();
 
-        return StrPrintf( "$PART: \"%s\" $PAD: \"%s\"", TO_UTF8( footprint->GetReference() ),
+        return StrPrintf( "$PART: \"%s\" $PAD: \"%s\"",
+                          TO_UTF8( footprint->GetReference() ),
                           TO_UTF8( pad ) );
     }
 
@@ -331,7 +332,9 @@ std::string FormatProbeItem( BOARD_ITEM* aItem )
         else
             break;
 
-        return StrPrintf( "$PART: \"%s\" %s \"%s\"", TO_UTF8( footprint->GetReference() ), text_key,
+        return StrPrintf( "$PART: \"%s\" %s \"%s\"",
+                          TO_UTF8( footprint->GetReference() ),
+                          text_key,
                           TO_UTF8( text->GetText() ) );
     }
 
diff --git a/pcbnew/dialogs/dialog_global_deletion.cpp b/pcbnew/dialogs/dialog_global_deletion.cpp
index 76aa2f846d..3b0f7be7e2 100644
--- a/pcbnew/dialogs/dialog_global_deletion.cpp
+++ b/pcbnew/dialogs/dialog_global_deletion.cpp
@@ -195,7 +195,7 @@ void DIALOG_GLOBAL_DELETION::DoGlobalDeletions()
                     if( !item->IsLocked() && !m_drawingFilterUnlocked->GetValue() )
                         continue;
                 }
-                else if( type == PCB_TEXT_T )
+                else if( type == PCB_TEXT_T || type == PCB_TEXTBOX_T )
                 {
                     if( !delete_texts || !del_text_layers[layer] )
                         continue;
diff --git a/pcbnew/dialogs/dialog_global_edit_text_and_graphics.cpp b/pcbnew/dialogs/dialog_global_edit_text_and_graphics.cpp
index fba915166a..d6289ffe0d 100644
--- a/pcbnew/dialogs/dialog_global_edit_text_and_graphics.cpp
+++ b/pcbnew/dialogs/dialog_global_edit_text_and_graphics.cpp
@@ -504,6 +504,11 @@ bool DIALOG_GLOBAL_EDIT_TEXT_AND_GRAPHICS::TransferDataFromWindow()
                 else if( m_otherFields->GetValue() )
                     visitItem( commit, boardItem );
             }
+            else if( itemType == PCB_FP_TEXTBOX_T )
+            {
+                if( m_otherFields->GetValue() )
+                    visitItem( commit, boardItem );
+            }
             else if( itemType == PCB_FP_SHAPE_T || BaseType( itemType ) == PCB_DIMENSION_T )
             {
                 if( m_footprintGraphics->GetValue() )
@@ -519,7 +524,7 @@ bool DIALOG_GLOBAL_EDIT_TEXT_AND_GRAPHICS::TransferDataFromWindow()
         {
             KICAD_T itemType = boardItem->Type();
 
-            if( itemType == PCB_TEXT_T )
+            if( itemType == PCB_TEXT_T || itemType == PCB_TEXTBOX_T )
             {
                 if( m_boardText->GetValue() )
                     visitItem( commit, boardItem );
diff --git a/pcbnew/dialogs/dialog_textbox_properties.cpp b/pcbnew/dialogs/dialog_textbox_properties.cpp
new file mode 100644
index 0000000000..ac3ce0e399
--- /dev/null
+++ b/pcbnew/dialogs/dialog_textbox_properties.cpp
@@ -0,0 +1,340 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2022 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 <widgets/bitmap_button.h>
+#include <widgets/font_choice.h>
+#include <dialog_textbox_properties.h>
+#include <confirm.h>
+#include <widgets/unit_binder.h>
+#include <board_commit.h>
+#include <board.h>
+#include <footprint.h>
+#include <string_utils.h>
+#include <pcb_textbox.h>
+#include <fp_textbox.h>
+#include <pcbnew.h>
+#include <pcb_edit_frame.h>
+#include <pcb_layer_box_selector.h>
+#include <wx/valnum.h>
+#include <math/util.h>      // for KiROUND
+#include <scintilla_tricks.h>
+#include "macros.h"
+
+DIALOG_TEXTBOX_PROPERTIES::DIALOG_TEXTBOX_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent,
+                                                      BOARD_ITEM* aItem ) :
+        DIALOG_TEXTBOX_PROPERTIES_BASE( aParent ),
+        m_frame( aParent ),
+        m_item( aItem ),
+        m_edaText( nullptr ),
+        m_fpTextBox( nullptr ),
+        m_pcbTextBox( nullptr ),
+        m_textWidth( aParent, m_SizeXLabel, m_SizeXCtrl, m_SizeXUnits ),
+        m_textHeight( aParent, m_SizeYLabel, m_SizeYCtrl, m_SizeYUnits ),
+        m_thickness( aParent, m_ThicknessLabel, m_ThicknessCtrl, m_ThicknessUnits ),
+        m_orientation( aParent, m_OrientLabel, m_OrientCtrl, nullptr )
+{
+    m_MultiLineText->SetEOLMode( wxSTC_EOL_LF );
+
+    m_scintillaTricks = new SCINTILLA_TRICKS( m_MultiLineText, wxT( "{}" ), false,
+            [this]()
+            {
+                wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
+            } );
+
+    // A hack which causes Scintilla to auto-size the text editor canvas
+    // See: https://github.com/jacobslusser/ScintillaNET/issues/216
+    m_MultiLineText->SetScrollWidth( 1 );
+    m_MultiLineText->SetScrollWidthTracking( true );
+
+    if( m_item->Type() == PCB_FP_TEXTBOX_T )
+    {
+        m_fpTextBox = static_cast<FP_TEXTBOX*>( m_item );
+        m_edaText = m_fpTextBox;
+
+        // Do not allow locking items in the footprint editor
+        m_cbLocked->Show( false );
+    }
+    else if( m_item->Type() == PCB_TEXTBOX_T )
+    {
+        m_pcbTextBox = static_cast<PCB_TEXTBOX*>( m_item );
+        m_edaText = m_pcbTextBox;
+    }
+    else
+    {
+        UNIMPLEMENTED_FOR( m_item->GetClass() );
+    }
+
+    SetInitialFocus( m_MultiLineText );
+
+    m_separator0->SetIsSeparator();
+
+    m_bold->SetIsCheckButton();
+    m_bold->SetBitmap( KiBitmap( BITMAPS::text_bold ) );
+    m_italic->SetIsCheckButton();
+    m_italic->SetBitmap( KiBitmap( BITMAPS::text_italic ) );
+
+    m_separator1->SetIsSeparator();
+
+    m_alignLeft->SetIsCheckButton();
+    m_alignLeft->SetBitmap( KiBitmap( BITMAPS::text_align_left ) );
+    m_alignCenter->SetIsCheckButton();
+    m_alignCenter->SetBitmap( KiBitmap( BITMAPS::text_align_center ) );
+    m_alignRight->SetIsCheckButton();
+    m_alignRight->SetBitmap( KiBitmap( BITMAPS::text_align_right ) );
+
+    m_separator2->SetIsSeparator();
+
+    m_mirrored->SetIsCheckButton();
+    m_mirrored->SetBitmap( KiBitmap( BITMAPS::text_mirrored ) );
+
+    m_separator3->SetIsSeparator();
+
+    // Configure the layers list selector.  Note that footprints are built outside the current
+    // board and so we may need to show all layers if the text is on an unactivated layer.
+    if( !m_frame->GetBoard()->IsLayerEnabled( m_item->GetLayer() ) )
+        m_LayerSelectionCtrl->ShowNonActivatedLayers( true );
+
+    m_LayerSelectionCtrl->SetLayersHotkeys( false );
+    m_LayerSelectionCtrl->SetBoardFrame( m_frame );
+    m_LayerSelectionCtrl->Resync();
+
+    m_orientation.SetUnits( EDA_UNITS::DEGREES );
+    m_orientation.SetPrecision( 3 );
+
+    // Set predefined rotations in combo dropdown, according to the locale floating point
+    // separator notation
+    double rot_list[] = { 0.0, 90.0, -90.0, 180.0 };
+
+    for( size_t ii = 0; ii < m_OrientCtrl->GetCount() && ii < 4; ++ii )
+        m_OrientCtrl->SetString( ii, wxString::Format( "%.1f", rot_list[ii] ) );
+
+    SetupStandardButtons();
+
+    // wxTextCtrls fail to generate wxEVT_CHAR events when the wxTE_MULTILINE flag is set,
+    // so we have to listen to wxEVT_CHAR_HOOK events instead.
+    Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( DIALOG_TEXTBOX_PROPERTIES::OnCharHook ),
+             nullptr, this );
+
+    finishDialogSettings();
+}
+
+
+DIALOG_TEXTBOX_PROPERTIES::~DIALOG_TEXTBOX_PROPERTIES()
+{
+    Disconnect( wxEVT_CHAR_HOOK, wxKeyEventHandler( DIALOG_TEXTBOX_PROPERTIES::OnCharHook ),
+                nullptr, this );
+
+    delete m_scintillaTricks;
+}
+
+
+int PCB_BASE_EDIT_FRAME::ShowTextBoxPropertiesDialog( BOARD_ITEM* aText )
+{
+    DIALOG_TEXTBOX_PROPERTIES dlg( this, aText );
+    return dlg.ShowQuasiModal();
+}
+
+
+bool DIALOG_TEXTBOX_PROPERTIES::TransferDataToWindow()
+{
+    BOARD*   board = m_frame->GetBoard();
+    wxString converted = board->ConvertKIIDsToCrossReferences(
+            UnescapeString( m_edaText->GetText() ) );
+
+    m_MultiLineText->SetValue( converted );
+    m_MultiLineText->SetSelection( -1, -1 );
+
+    m_cbLocked->SetValue( m_item->IsLocked() );
+
+    m_LayerSelectionCtrl->SetLayerSelection( m_item->GetLayer() );
+
+    m_fontCtrl->SetFontSelection( m_edaText->GetFont() );
+
+    m_textWidth.SetValue( m_edaText->GetTextSize().x );
+    m_textHeight.SetValue( m_edaText->GetTextSize().y );
+    m_thickness.SetValue( m_edaText->GetTextThickness() );
+
+    m_bold->Check( m_edaText->IsBold() );
+    m_italic->Check( m_edaText->IsItalic() );
+
+    switch ( m_edaText->GetHorizJustify() )
+    {
+    case GR_TEXT_H_ALIGN_LEFT:   m_alignLeft->Check( true );   break;
+    case GR_TEXT_H_ALIGN_CENTER: m_alignCenter->Check( true ); break;
+    case GR_TEXT_H_ALIGN_RIGHT:  m_alignRight->Check( true );  break;
+    }
+
+    m_mirrored->Check( m_edaText->IsMirrored() );
+
+    m_orientation.SetAngleValue( m_edaText->GetTextAngle() );
+
+    return DIALOG_TEXTBOX_PROPERTIES_BASE::TransferDataToWindow();
+}
+
+
+void DIALOG_TEXTBOX_PROPERTIES::onFontSelected( wxCommandEvent & aEvent )
+{
+    if( KIFONT::FONT::IsStroke( aEvent.GetString() ) )
+    {
+        m_thickness.Show( true );
+
+        int textSize = std::min( m_textWidth.GetValue(), m_textHeight.GetValue() );
+        int thickness = m_thickness.GetValue();
+
+        m_bold->Check( abs( thickness - GetPenSizeForBold( textSize ) )
+                        < abs( thickness - GetPenSizeForNormal( textSize ) ) );
+    }
+    else
+    {
+        m_thickness.Show( false );
+    }
+}
+
+
+void DIALOG_TEXTBOX_PROPERTIES::onBoldToggle( wxCommandEvent & aEvent )
+{
+    int textSize = std::min( m_textWidth.GetValue(), m_textHeight.GetValue() );
+
+    if( aEvent.IsChecked() )
+        m_thickness.ChangeValue( GetPenSizeForBold( textSize ) );
+    else
+        m_thickness.ChangeValue( GetPenSizeForNormal( textSize ) );
+
+    aEvent.Skip();
+}
+
+
+void DIALOG_TEXTBOX_PROPERTIES::onAlignButton( wxCommandEvent& aEvent )
+{
+    for( BITMAP_BUTTON* btn : { m_alignLeft, m_alignCenter, m_alignRight } )
+    {
+        if( btn->IsChecked() && btn != aEvent.GetEventObject() )
+            btn->Check( false );
+    }
+}
+
+
+void DIALOG_TEXTBOX_PROPERTIES::onThickness( wxCommandEvent& event )
+{
+    int textSize = std::min( m_textWidth.GetValue(), m_textHeight.GetValue() );
+    int thickness = m_thickness.GetValue();
+
+    m_bold->Check( abs( thickness - GetPenSizeForBold( textSize ) )
+                    < abs( thickness - GetPenSizeForNormal( textSize ) ) );
+}
+
+
+bool DIALOG_TEXTBOX_PROPERTIES::TransferDataFromWindow()
+{
+    if( !DIALOG_TEXTBOX_PROPERTIES_BASE::TransferDataFromWindow() )
+        return false;
+
+    if( !m_textWidth.Validate( TEXTS_MIN_SIZE, TEXTS_MAX_SIZE )
+        || !m_textHeight.Validate( TEXTS_MIN_SIZE, TEXTS_MAX_SIZE ) )
+    {
+        return false;
+    }
+
+    BOARD_COMMIT commit( m_frame );
+    commit.Modify( m_item );
+
+    // If no other command in progress, prepare undo command
+    // (for a command in progress, will be made later, at the completion of command)
+    bool pushCommit = ( m_item->GetEditFlags() == 0 );
+
+    // Set IN_EDIT flag to force undo/redo/abort proper operation and avoid new calls to
+    // SaveCopyInUndoList for the same text if is moved, and then rotated, edited, etc....
+    if( !pushCommit )
+        m_item->SetFlags( IN_EDIT );
+
+    // Set the new text content
+    if( !m_MultiLineText->GetValue().IsEmpty() )
+    {
+        BOARD*   board = m_frame->GetBoard();
+        wxString txt = board->ConvertCrossReferencesToKIIDs( m_MultiLineText->GetValue() );
+
+#ifdef __WXMAC__
+        // On macOS CTRL+Enter produces '\r' instead of '\n' regardless of EOL setting.
+        // Replace it now.
+        txt.Replace( "\r", "\n" );
+#elif defined( __WINDOWS__ )
+        // On Windows, a new line is coded as \r\n.  We use only \n in kicad files and in
+        // drawing routines so strip the \r char.
+        txt.Replace( "\r", "" );
+#endif
+        m_edaText->SetText( EscapeString( txt, CTX_QUOTED_STR ) );
+    }
+
+    m_item->SetLocked( m_cbLocked->GetValue() );
+
+    m_item->SetLayer( ToLAYER_ID( m_LayerSelectionCtrl->GetLayerSelection() ) );
+
+    if( m_fontCtrl->HaveFontSelection() )
+    {
+        m_edaText->SetFont( m_fontCtrl->GetFontSelection( m_bold->IsChecked(),
+                                                          m_italic->IsChecked() ) );
+    }
+
+    m_edaText->SetTextSize( wxSize( m_textWidth.GetValue(), m_textHeight.GetValue() ) );
+    m_edaText->SetTextThickness( m_thickness.GetValue() );
+
+    if( m_fpTextBox )
+        m_fpTextBox->SetLocalCoord();
+
+    // Test for acceptable values for thickness and size and clamp if fails
+    int maxPenWidth = Clamp_Text_PenSize( m_edaText->GetTextThickness(), m_edaText->GetTextSize() );
+
+    if( m_edaText->GetTextThickness() > maxPenWidth )
+    {
+        DisplayError( this, _( "The text thickness is too large for the text size.\n"
+                               "It will be clamped." ) );
+        m_edaText->SetTextThickness( maxPenWidth );
+    }
+
+    m_edaText->SetTextAngle( m_orientation.GetAngleValue() );
+    m_edaText->SetBold( m_bold->IsChecked() );
+    m_edaText->SetItalic( m_italic->IsChecked() );
+
+    if( m_alignLeft->IsChecked() )
+        m_edaText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
+    else if( m_alignCenter->IsChecked() )
+        m_edaText->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
+    else
+        m_edaText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
+
+    m_edaText->SetMirrored( m_mirrored->IsChecked() );
+
+    if( pushCommit )
+        commit.Push( _( "Change text box properties" ) );
+
+    return true;
+}
+
+
+void DIALOG_TEXTBOX_PROPERTIES::onMultiLineTCLostFocus( wxFocusEvent& event )
+{
+    if( m_scintillaTricks )
+        m_scintillaTricks->CancelAutocomplete();
+
+    event.Skip();
+}
diff --git a/pcbnew/dialogs/dialog_textbox_properties.h b/pcbnew/dialogs/dialog_textbox_properties.h
new file mode 100644
index 0000000000..9f2c354a90
--- /dev/null
+++ b/pcbnew/dialogs/dialog_textbox_properties.h
@@ -0,0 +1,73 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2022 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 DIALOG_TEXTBOX_PROPERTIES_H
+#define DIALOG_TEXTBOX_PROPERTIES_H
+
+#include <widgets/unit_binder.h>
+#include <wx/valnum.h>
+
+#include <dialog_textbox_properties_base.h>
+
+
+class PCB_BASE_EDIT_FRAME;
+class BOARD_ITEM;
+class EDA_TEXT;
+class FP_TEXTBOX;
+class PCB_TEXTBOX;
+class SCINTILLA_TRICKS;
+
+
+class DIALOG_TEXTBOX_PROPERTIES : public DIALOG_TEXTBOX_PROPERTIES_BASE
+{
+public:
+    DIALOG_TEXTBOX_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent, BOARD_ITEM* aItem );
+    ~DIALOG_TEXTBOX_PROPERTIES();
+
+private:
+    void onFontSelected( wxCommandEvent &aEvent ) override;
+    void onBoldToggle( wxCommandEvent &aEvent ) override;
+    void onAlignButton( wxCommandEvent &aEvent ) override;
+    void onThickness( wxCommandEvent &aEvent ) override;
+
+    bool TransferDataToWindow() override;
+    bool TransferDataFromWindow() override;
+    void onMultiLineTCLostFocus( wxFocusEvent& event ) override;
+
+private:
+    PCB_BASE_EDIT_FRAME* m_frame;
+    BOARD_ITEM*          m_item;           // FP_TEXTBOX or PCB_TEXTBOX
+    EDA_TEXT*            m_edaText;        // always non-null
+    FP_TEXTBOX*          m_fpTextBox;      // only non-null for FP_TEXTBOXes
+    PCB_TEXTBOX*         m_pcbTextBox;     // only non-null for PCB_TEXTBOXes
+
+    UNIT_BINDER          m_textWidth;
+    UNIT_BINDER          m_textHeight;
+    UNIT_BINDER          m_thickness;
+    UNIT_BINDER          m_orientation;     // rotation in degrees
+
+    SCINTILLA_TRICKS*    m_scintillaTricks;
+};
+
+
+#endif //DIALOG_TEXTBOX_PROPERTIES_H
diff --git a/pcbnew/dialogs/dialog_textbox_properties_base.cpp b/pcbnew/dialogs/dialog_textbox_properties_base.cpp
new file mode 100644
index 0000000000..bba3f9e926
--- /dev/null
+++ b/pcbnew/dialogs/dialog_textbox_properties_base.cpp
@@ -0,0 +1,274 @@
+///////////////////////////////////////////////////////////////////////////
+// C++ code generated with wxFormBuilder (version Oct 26 2018)
+// http://www.wxformbuilder.org/
+//
+// PLEASE DO *NOT* EDIT THIS FILE!
+///////////////////////////////////////////////////////////////////////////
+
+#include "pcb_layer_box_selector.h"
+#include "widgets/bitmap_button.h"
+#include "widgets/font_choice.h"
+
+#include "dialog_textbox_properties_base.h"
+
+///////////////////////////////////////////////////////////////////////////
+
+DIALOG_TEXTBOX_PROPERTIES_BASE::DIALOG_TEXTBOX_PROPERTIES_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
+{
+	this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize );
+
+	wxBoxSizer* bMainSizer;
+	bMainSizer = new wxBoxSizer( wxVERTICAL );
+
+	m_MultiLineSizer = new wxBoxSizer( wxVERTICAL );
+
+	wxStaticText* textLabel;
+	textLabel = new wxStaticText( this, wxID_ANY, _("Text:"), wxDefaultPosition, wxDefaultSize, 0 );
+	textLabel->Wrap( -1 );
+	m_MultiLineSizer->Add( textLabel, 0, wxRIGHT|wxLEFT, 5 );
+
+	m_MultiLineText = new wxStyledTextCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, wxEmptyString );
+	m_MultiLineText->SetUseTabs( true );
+	m_MultiLineText->SetTabWidth( 4 );
+	m_MultiLineText->SetIndent( 4 );
+	m_MultiLineText->SetTabIndents( false );
+	m_MultiLineText->SetBackSpaceUnIndents( false );
+	m_MultiLineText->SetViewEOL( false );
+	m_MultiLineText->SetViewWhiteSpace( false );
+	m_MultiLineText->SetMarginWidth( 2, 0 );
+	m_MultiLineText->SetIndentationGuides( true );
+	m_MultiLineText->SetMarginWidth( 1, 0 );
+	m_MultiLineText->SetMarginWidth( 0, 0 );
+	m_MultiLineText->MarkerDefine( wxSTC_MARKNUM_FOLDER, wxSTC_MARK_BOXPLUS );
+	m_MultiLineText->MarkerSetBackground( wxSTC_MARKNUM_FOLDER, wxColour( wxT("BLACK") ) );
+	m_MultiLineText->MarkerSetForeground( wxSTC_MARKNUM_FOLDER, wxColour( wxT("WHITE") ) );
+	m_MultiLineText->MarkerDefine( wxSTC_MARKNUM_FOLDEROPEN, wxSTC_MARK_BOXMINUS );
+	m_MultiLineText->MarkerSetBackground( wxSTC_MARKNUM_FOLDEROPEN, wxColour( wxT("BLACK") ) );
+	m_MultiLineText->MarkerSetForeground( wxSTC_MARKNUM_FOLDEROPEN, wxColour( wxT("WHITE") ) );
+	m_MultiLineText->MarkerDefine( wxSTC_MARKNUM_FOLDERSUB, wxSTC_MARK_EMPTY );
+	m_MultiLineText->MarkerDefine( wxSTC_MARKNUM_FOLDEREND, wxSTC_MARK_BOXPLUS );
+	m_MultiLineText->MarkerSetBackground( wxSTC_MARKNUM_FOLDEREND, wxColour( wxT("BLACK") ) );
+	m_MultiLineText->MarkerSetForeground( wxSTC_MARKNUM_FOLDEREND, wxColour( wxT("WHITE") ) );
+	m_MultiLineText->MarkerDefine( wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUS );
+	m_MultiLineText->MarkerSetBackground( wxSTC_MARKNUM_FOLDEROPENMID, wxColour( wxT("BLACK") ) );
+	m_MultiLineText->MarkerSetForeground( wxSTC_MARKNUM_FOLDEROPENMID, wxColour( wxT("WHITE") ) );
+	m_MultiLineText->MarkerDefine( wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_EMPTY );
+	m_MultiLineText->MarkerDefine( wxSTC_MARKNUM_FOLDERTAIL, wxSTC_MARK_EMPTY );
+	m_MultiLineText->SetSelBackground( true, wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
+	m_MultiLineText->SetSelForeground( true, wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
+	m_MultiLineText->SetToolTip( _("Enter the text placed on selected layer.") );
+	m_MultiLineText->SetMinSize( wxSize( -1,150 ) );
+
+	m_MultiLineSizer->Add( m_MultiLineText, 1, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+
+	bMainSizer->Add( m_MultiLineSizer, 20, wxEXPAND|wxALL, 10 );
+
+	wxGridBagSizer* gbSizer1;
+	gbSizer1 = new wxGridBagSizer( 3, 5 );
+	gbSizer1->SetFlexibleDirection( wxBOTH );
+	gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
+	gbSizer1->SetEmptyCellSize( wxSize( 20,8 ) );
+
+	m_cbLocked = new wxCheckBox( this, wxID_ANY, _("Locked"), wxDefaultPosition, wxDefaultSize, 0 );
+	gbSizer1->Add( m_cbLocked, wxGBPosition( 0, 0 ), wxGBSpan( 1, 3 ), wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+	m_LayerLabel = new wxStaticText( this, wxID_ANY, _("Layer:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_LayerLabel->Wrap( -1 );
+	gbSizer1->Add( m_LayerLabel, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
+
+	m_LayerSelectionCtrl = new PCB_LAYER_BOX_SELECTOR( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
+	gbSizer1->Add( m_LayerSelectionCtrl, wxGBPosition( 1, 1 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
+
+	m_fontLabel = new wxStaticText( this, wxID_ANY, _("Font:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_fontLabel->Wrap( -1 );
+	gbSizer1->Add( m_fontLabel, wxGBPosition( 3, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
+
+	wxString m_fontCtrlChoices[] = { _("KiCad Font") };
+	int m_fontCtrlNChoices = sizeof( m_fontCtrlChoices ) / sizeof( wxString );
+	m_fontCtrl = new FONT_CHOICE( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_fontCtrlNChoices, m_fontCtrlChoices, 0 );
+	m_fontCtrl->SetSelection( 0 );
+	gbSizer1->Add( m_fontCtrl, wxGBPosition( 3, 1 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL, 5 );
+
+	wxBoxSizer* bSizerButtonBar;
+	bSizerButtonBar = new wxBoxSizer( wxHORIZONTAL );
+
+	m_separator0 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	m_separator0->Enable( false );
+
+	bSizerButtonBar->Add( m_separator0, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_bold = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	bSizerButtonBar->Add( m_bold, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_italic = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	bSizerButtonBar->Add( m_italic, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_separator1 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	m_separator1->Enable( false );
+
+	bSizerButtonBar->Add( m_separator1, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_alignLeft = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	bSizerButtonBar->Add( m_alignLeft, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_alignCenter = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	bSizerButtonBar->Add( m_alignCenter, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_alignRight = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	bSizerButtonBar->Add( m_alignRight, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_separator2 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	m_separator2->Enable( false );
+
+	bSizerButtonBar->Add( m_separator2, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_mirrored = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	bSizerButtonBar->Add( m_mirrored, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_separator3 = new BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
+	m_separator3->Enable( false );
+
+	bSizerButtonBar->Add( m_separator3, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+
+	gbSizer1->Add( bSizerButtonBar, wxGBPosition( 3, 4 ), wxGBSpan( 1, 3 ), wxEXPAND|wxALIGN_CENTER_VERTICAL|wxRIGHT, 8 );
+
+	m_SizeXLabel = new wxStaticText( this, wxID_ANY, _("Text Width:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_SizeXLabel->Wrap( -1 );
+	m_SizeXLabel->SetToolTip( _("Text width") );
+
+	gbSizer1->Add( m_SizeXLabel, wxGBPosition( 4, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
+
+	m_SizeXCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
+	gbSizer1->Add( m_SizeXCtrl, wxGBPosition( 4, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
+
+	m_SizeXUnits = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_SizeXUnits->Wrap( -1 );
+	gbSizer1->Add( m_SizeXUnits, wxGBPosition( 4, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_SizeYLabel = new wxStaticText( this, wxID_ANY, _("Text Height:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_SizeYLabel->Wrap( -1 );
+	m_SizeYLabel->SetToolTip( _("Text height") );
+
+	gbSizer1->Add( m_SizeYLabel, wxGBPosition( 5, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
+
+	m_SizeYCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
+	gbSizer1->Add( m_SizeYCtrl, wxGBPosition( 5, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
+
+	m_SizeYUnits = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_SizeYUnits->Wrap( -1 );
+	gbSizer1->Add( m_SizeYUnits, wxGBPosition( 5, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_ThicknessLabel = new wxStaticText( this, wxID_ANY, _("Thickness:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_ThicknessLabel->Wrap( -1 );
+	m_ThicknessLabel->SetToolTip( _("Text thickness") );
+
+	gbSizer1->Add( m_ThicknessLabel, wxGBPosition( 6, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
+
+	m_ThicknessCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
+	gbSizer1->Add( m_ThicknessCtrl, wxGBPosition( 6, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
+
+	m_ThicknessUnits = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_ThicknessUnits->Wrap( -1 );
+	gbSizer1->Add( m_ThicknessUnits, wxGBPosition( 6, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_OrientLabel = new wxStaticText( this, wxID_ANY, _("Orientation:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_OrientLabel->Wrap( -1 );
+	m_OrientLabel->SetToolTip( _("Text orientation") );
+
+	gbSizer1->Add( m_OrientLabel, wxGBPosition( 1, 4 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
+
+	m_OrientCtrl = new wxComboBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
+	m_OrientCtrl->Append( _("0.0") );
+	m_OrientCtrl->Append( _("90.0") );
+	m_OrientCtrl->Append( _("-90.0") );
+	m_OrientCtrl->Append( _("180.0") );
+	gbSizer1->Add( m_OrientCtrl, wxGBPosition( 1, 5 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL|wxEXPAND|wxRIGHT, 5 );
+
+	m_borderWidthLabel = new wxStaticText( this, wxID_ANY, _("Border Width:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_borderWidthLabel->Wrap( -1 );
+	gbSizer1->Add( m_borderWidthLabel, wxGBPosition( 5, 4 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
+
+	m_borderWidthCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+	gbSizer1->Add( m_borderWidthCtrl, wxGBPosition( 5, 5 ), wxGBSpan( 1, 1 ), wxEXPAND|wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_borderWidthUnits = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_borderWidthUnits->Wrap( -1 );
+	gbSizer1->Add( m_borderWidthUnits, wxGBPosition( 5, 6 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
+
+	m_borderStyleLabel = new wxStaticText( this, wxID_ANY, _("Border Style:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_borderStyleLabel->Wrap( -1 );
+	gbSizer1->Add( m_borderStyleLabel, wxGBPosition( 6, 4 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
+
+	m_borderStyleCombo = new wxBitmapComboBox( this, wxID_ANY, _("Combo!"), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY );
+	m_borderStyleCombo->SetMinSize( wxSize( 240,-1 ) );
+
+	gbSizer1->Add( m_borderStyleCombo, wxGBPosition( 6, 5 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
+
+
+	gbSizer1->AddGrowableCol( 1 );
+	gbSizer1->AddGrowableCol( 5 );
+
+	bMainSizer->Add( gbSizer1, 0, wxRIGHT|wxLEFT|wxEXPAND, 10 );
+
+	m_staticline = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+	bMainSizer->Add( m_staticline, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 10 );
+
+	wxBoxSizer* lowerSizer;
+	lowerSizer = new wxBoxSizer( wxHORIZONTAL );
+
+
+	lowerSizer->Add( 0, 0, 1, wxEXPAND, 5 );
+
+	m_sdbSizer = new wxStdDialogButtonSizer();
+	m_sdbSizerOK = new wxButton( this, wxID_OK );
+	m_sdbSizer->AddButton( m_sdbSizerOK );
+	m_sdbSizerCancel = new wxButton( this, wxID_CANCEL );
+	m_sdbSizer->AddButton( m_sdbSizerCancel );
+	m_sdbSizer->Realize();
+
+	lowerSizer->Add( m_sdbSizer, 0, wxALL, 5 );
+
+
+	bMainSizer->Add( lowerSizer, 0, wxEXPAND, 5 );
+
+
+	this->SetSizer( bMainSizer );
+	this->Layout();
+	bMainSizer->Fit( this );
+
+	this->Centre( wxBOTH );
+
+	// Connect Events
+	this->Connect( wxEVT_INIT_DIALOG, wxInitDialogEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnInitDlg ) );
+	m_MultiLineText->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onMultiLineTCLostFocus ), NULL, this );
+	m_fontCtrl->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onFontSelected ), NULL, this );
+	m_bold->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onBoldToggle ), NULL, this );
+	m_alignLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onAlignButton ), NULL, this );
+	m_alignCenter->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onAlignButton ), NULL, this );
+	m_alignRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onAlignButton ), NULL, this );
+	m_SizeXCtrl->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnOkClick ), NULL, this );
+	m_SizeYCtrl->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnOkClick ), NULL, this );
+	m_ThicknessCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onThickness ), NULL, this );
+	m_OrientCtrl->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnOkClick ), NULL, this );
+	m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnOkClick ), NULL, this );
+}
+
+DIALOG_TEXTBOX_PROPERTIES_BASE::~DIALOG_TEXTBOX_PROPERTIES_BASE()
+{
+	// Disconnect Events
+	this->Disconnect( wxEVT_INIT_DIALOG, wxInitDialogEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnInitDlg ) );
+	m_MultiLineText->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onMultiLineTCLostFocus ), NULL, this );
+	m_fontCtrl->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onFontSelected ), NULL, this );
+	m_bold->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onBoldToggle ), NULL, this );
+	m_alignLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onAlignButton ), NULL, this );
+	m_alignCenter->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onAlignButton ), NULL, this );
+	m_alignRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onAlignButton ), NULL, this );
+	m_SizeXCtrl->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnOkClick ), NULL, this );
+	m_SizeYCtrl->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnOkClick ), NULL, this );
+	m_ThicknessCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::onThickness ), NULL, this );
+	m_OrientCtrl->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnOkClick ), NULL, this );
+	m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TEXTBOX_PROPERTIES_BASE::OnOkClick ), NULL, this );
+
+}
diff --git a/pcbnew/dialogs/dialog_textbox_properties_base.fbp b/pcbnew/dialogs/dialog_textbox_properties_base.fbp
new file mode 100644
index 0000000000..85802a3078
--- /dev/null
+++ b/pcbnew/dialogs/dialog_textbox_properties_base.fbp
@@ -0,0 +1,2436 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<wxFormBuilder_Project>
+    <FileVersion major="1" minor="15" />
+    <object class="Project" expanded="1">
+        <property name="class_decoration"></property>
+        <property name="code_generation">C++</property>
+        <property name="disconnect_events">1</property>
+        <property name="disconnect_mode">source_name</property>
+        <property name="disconnect_php_events">0</property>
+        <property name="disconnect_python_events">0</property>
+        <property name="embedded_files_path">res</property>
+        <property name="encoding">UTF-8</property>
+        <property name="event_generation">connect</property>
+        <property name="file">dialog_textbox_properties_base</property>
+        <property name="first_id">1000</property>
+        <property name="help_provider">none</property>
+        <property name="indent_with_spaces"></property>
+        <property name="internationalize">1</property>
+        <property name="name">DIALOG_TEXTBOX_PROPERTIES_BASE</property>
+        <property name="namespace"></property>
+        <property name="path">.</property>
+        <property name="precompiled_header"></property>
+        <property name="relative_path">1</property>
+        <property name="skip_lua_events">1</property>
+        <property name="skip_php_events">1</property>
+        <property name="skip_python_events">1</property>
+        <property name="ui_table">UI</property>
+        <property name="use_enum">0</property>
+        <property name="use_microsoft_bom">0</property>
+        <object class="Dialog" expanded="1">
+            <property name="aui_managed">0</property>
+            <property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
+            <property name="bg"></property>
+            <property name="center">wxBOTH</property>
+            <property name="context_help"></property>
+            <property name="context_menu">1</property>
+            <property name="enabled">1</property>
+            <property name="event_handler">impl_virtual</property>
+            <property name="extra_style"></property>
+            <property name="fg"></property>
+            <property name="font"></property>
+            <property name="hidden">0</property>
+            <property name="id">wxID_ANY</property>
+            <property name="maximum_size"></property>
+            <property name="minimum_size">-1,-1</property>
+            <property name="name">DIALOG_TEXTBOX_PROPERTIES_BASE</property>
+            <property name="pos"></property>
+            <property name="size">-1,-1</property>
+            <property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxSYSTEM_MENU</property>
+            <property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
+            <property name="title">Text Box Properties</property>
+            <property name="tooltip"></property>
+            <property name="window_extra_style"></property>
+            <property name="window_name"></property>
+            <property name="window_style"></property>
+            <event name="OnInitDialog">OnInitDlg</event>
+            <object class="wxBoxSizer" expanded="1">
+                <property name="minimum_size"></property>
+                <property name="name">bMainSizer</property>
+                <property name="orient">wxVERTICAL</property>
+                <property name="permission">none</property>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">10</property>
+                    <property name="flag">wxEXPAND|wxALL</property>
+                    <property name="proportion">20</property>
+                    <object class="wxBoxSizer" expanded="1">
+                        <property name="minimum_size">-1,-1</property>
+                        <property name="name">m_MultiLineSizer</property>
+                        <property name="orient">wxVERTICAL</property>
+                        <property name="permission">protected</property>
+                        <object class="sizeritem" expanded="0">
+                            <property name="border">5</property>
+                            <property name="flag">wxRIGHT|wxLEFT</property>
+                            <property name="proportion">0</property>
+                            <object class="wxStaticText" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Text:</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">textLabel</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">none</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass"></property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="sizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property>
+                            <property name="proportion">1</property>
+                            <object class="wxStyledTextCtrl" expanded="1">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="backspace_unindents">0</property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="folding">0</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="indentation_guides">1</property>
+                                <property name="line_numbers">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size">-1,150</property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_MultiLineText</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="tab_indents">0</property>
+                                <property name="tab_width">4</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip">Enter the text placed on selected layer.</property>
+                                <property name="use_tabs">1</property>
+                                <property name="view_eol">0</property>
+                                <property name="view_whitespace">0</property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <event name="OnKillFocus">onMultiLineTCLostFocus</event>
+                            </object>
+                        </object>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">10</property>
+                    <property name="flag">wxRIGHT|wxLEFT|wxEXPAND</property>
+                    <property name="proportion">0</property>
+                    <object class="wxGridBagSizer" expanded="1">
+                        <property name="empty_cell_size">20,8</property>
+                        <property name="flexible_direction">wxBOTH</property>
+                        <property name="growablecols">1,5</property>
+                        <property name="growablerows"></property>
+                        <property name="hgap">5</property>
+                        <property name="minimum_size"></property>
+                        <property name="name">gbSizer1</property>
+                        <property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
+                        <property name="permission">none</property>
+                        <property name="vgap">3</property>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">3</property>
+                            <property name="column">0</property>
+                            <property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>
+                            <property name="row">0</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxCheckBox" expanded="1">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="checked">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Locked</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_cbLocked</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">0</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT</property>
+                            <property name="row">1</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Layer:</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_LayerLabel</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass"></property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">2</property>
+                            <property name="column">1</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
+                            <property name="row">1</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxBitmapComboBox" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="choices"></property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_LayerSelectionCtrl</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="selection">-1</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">PCB_LAYER_BOX_SELECTOR; pcb_layer_box_selector.h</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="value"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">0</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT</property>
+                            <property name="row">3</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="1">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Font:</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_fontLabel</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">2</property>
+                            <property name="column">1</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                            <property name="row">3</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxChoice" expanded="1">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="choices">&quot;KiCad Font&quot;</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_fontCtrl</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="selection">0</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">FONT_CHOICE; widgets/font_choice.h; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <event name="OnChoice">onFontSelected</event>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">8</property>
+                            <property name="colspan">3</property>
+                            <property name="column">4</property>
+                            <property name="flag">wxEXPAND|wxALIGN_CENTER_VERTICAL|wxRIGHT</property>
+                            <property name="row">3</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxBoxSizer" expanded="1">
+                                <property name="minimum_size"></property>
+                                <property name="name">bSizerButtonBar</property>
+                                <property name="orient">wxHORIZONTAL</property>
+                                <property name="permission">none</property>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">0</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label"></property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_separator0</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                    </object>
+                                </object>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">1</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label">bold</property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_bold</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                        <event name="OnButtonClick">onBoldToggle</event>
+                                    </object>
+                                </object>
+                                <object class="sizeritem" expanded="0">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="0">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">1</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label">Italic</property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_italic</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                    </object>
+                                </object>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">0</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label"></property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_separator1</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                    </object>
+                                </object>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">1</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label">Left</property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_alignLeft</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                        <event name="OnButtonClick">onAlignButton</event>
+                                    </object>
+                                </object>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">1</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label">Center</property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_alignCenter</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                        <event name="OnButtonClick">onAlignButton</event>
+                                    </object>
+                                </object>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">1</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label">Right</property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_alignRight</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                        <event name="OnButtonClick">onAlignButton</event>
+                                    </object>
+                                </object>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">0</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label">MyButton</property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_separator2</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                    </object>
+                                </object>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">1</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label">Mirrored</property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_mirrored</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                    </object>
+                                </object>
+                                <object class="sizeritem" expanded="1">
+                                    <property name="border">5</property>
+                                    <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                                    <property name="proportion">0</property>
+                                    <object class="wxBitmapButton" expanded="1">
+                                        <property name="BottomDockable">1</property>
+                                        <property name="LeftDockable">1</property>
+                                        <property name="RightDockable">1</property>
+                                        <property name="TopDockable">1</property>
+                                        <property name="aui_layer"></property>
+                                        <property name="aui_name"></property>
+                                        <property name="aui_position"></property>
+                                        <property name="aui_row"></property>
+                                        <property name="best_size"></property>
+                                        <property name="bg"></property>
+                                        <property name="bitmap"></property>
+                                        <property name="caption"></property>
+                                        <property name="caption_visible">1</property>
+                                        <property name="center_pane">0</property>
+                                        <property name="close_button">1</property>
+                                        <property name="context_help"></property>
+                                        <property name="context_menu">1</property>
+                                        <property name="current"></property>
+                                        <property name="default">0</property>
+                                        <property name="default_pane">0</property>
+                                        <property name="disabled"></property>
+                                        <property name="dock">Dock</property>
+                                        <property name="dock_fixed">0</property>
+                                        <property name="docking">Left</property>
+                                        <property name="enabled">0</property>
+                                        <property name="fg"></property>
+                                        <property name="floatable">1</property>
+                                        <property name="focus"></property>
+                                        <property name="font"></property>
+                                        <property name="gripper">0</property>
+                                        <property name="hidden">0</property>
+                                        <property name="id">wxID_ANY</property>
+                                        <property name="label"></property>
+                                        <property name="margins"></property>
+                                        <property name="markup">0</property>
+                                        <property name="max_size"></property>
+                                        <property name="maximize_button">0</property>
+                                        <property name="maximum_size"></property>
+                                        <property name="min_size"></property>
+                                        <property name="minimize_button">0</property>
+                                        <property name="minimum_size"></property>
+                                        <property name="moveable">1</property>
+                                        <property name="name">m_separator3</property>
+                                        <property name="pane_border">1</property>
+                                        <property name="pane_position"></property>
+                                        <property name="pane_size"></property>
+                                        <property name="permission">protected</property>
+                                        <property name="pin_button">1</property>
+                                        <property name="pos"></property>
+                                        <property name="position"></property>
+                                        <property name="pressed"></property>
+                                        <property name="resize">Resizable</property>
+                                        <property name="show">1</property>
+                                        <property name="size">21,21</property>
+                                        <property name="style">wxBORDER_NONE</property>
+                                        <property name="subclass">BITMAP_BUTTON; widgets/bitmap_button.h; forward_declare</property>
+                                        <property name="toolbar_pane">0</property>
+                                        <property name="tooltip"></property>
+                                        <property name="validator_data_type"></property>
+                                        <property name="validator_style">wxFILTER_NONE</property>
+                                        <property name="validator_type">wxDefaultValidator</property>
+                                        <property name="validator_variable"></property>
+                                        <property name="window_extra_style"></property>
+                                        <property name="window_name"></property>
+                                        <property name="window_style"></property>
+                                    </object>
+                                </object>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">0</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
+                            <property name="row">4</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Text Width:</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_SizeXLabel</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass"></property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip">Text width</property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">1</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
+                            <property name="row">4</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxTextCtrl" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="maxlength">0</property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_SizeXCtrl</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style">wxTE_PROCESS_ENTER</property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="value"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <event name="OnTextEnter">OnOkClick</event>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">2</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                            <property name="row">4</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">unit</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_SizeXUnits</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">0</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
+                            <property name="row">5</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Text Height:</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_SizeYLabel</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass"></property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip">Text height</property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">1</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
+                            <property name="row">5</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxTextCtrl" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="maxlength">0</property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_SizeYCtrl</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style">wxTE_PROCESS_ENTER</property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="value"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <event name="OnTextEnter">OnOkClick</event>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">2</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                            <property name="row">5</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">unit</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_SizeYUnits</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">0</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
+                            <property name="row">6</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">0</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Thickness:</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_ThicknessLabel</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass"></property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip">Text thickness</property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">1</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
+                            <property name="row">6</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxTextCtrl" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="maxlength">0</property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_ThicknessCtrl</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style">wxTE_PROCESS_ENTER</property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="value"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <event name="OnText">onThickness</event>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">2</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                            <property name="row">6</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">unit</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_ThicknessUnits</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">4</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
+                            <property name="row">1</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Orientation:</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_OrientLabel</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass"></property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip">Text orientation</property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">2</property>
+                            <property name="column">5</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxEXPAND|wxRIGHT</property>
+                            <property name="row">1</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxComboBox" expanded="0">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="choices">&quot;0.0&quot; &quot;90.0&quot; &quot;-90.0&quot; &quot;180.0&quot;</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_OrientCtrl</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="selection">-1</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="value"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <event name="OnTextEnter">OnOkClick</event>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">4</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
+                            <property name="row">5</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="1">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Border Width:</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_borderWidthLabel</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">5</property>
+                            <property name="flag">wxEXPAND|wxALIGN_CENTER_VERTICAL</property>
+                            <property name="row">5</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxTextCtrl" expanded="1">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="maxlength"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_borderWidthCtrl</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="value"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">6</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL</property>
+                            <property name="row">5</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="1">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">unit</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_borderWidthUnits</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">1</property>
+                            <property name="column">4</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
+                            <property name="row">6</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxStaticText" expanded="1">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="label">Border Style:</property>
+                                <property name="markup">0</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_borderStyleLabel</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style"></property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                                <property name="wrap">-1</property>
+                            </object>
+                        </object>
+                        <object class="gbsizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="colspan">2</property>
+                            <property name="column">5</property>
+                            <property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT</property>
+                            <property name="row">6</property>
+                            <property name="rowspan">1</property>
+                            <object class="wxBitmapComboBox" expanded="1">
+                                <property name="BottomDockable">1</property>
+                                <property name="LeftDockable">1</property>
+                                <property name="RightDockable">1</property>
+                                <property name="TopDockable">1</property>
+                                <property name="aui_layer"></property>
+                                <property name="aui_name"></property>
+                                <property name="aui_position"></property>
+                                <property name="aui_row"></property>
+                                <property name="best_size"></property>
+                                <property name="bg"></property>
+                                <property name="caption"></property>
+                                <property name="caption_visible">1</property>
+                                <property name="center_pane">0</property>
+                                <property name="choices"></property>
+                                <property name="close_button">1</property>
+                                <property name="context_help"></property>
+                                <property name="context_menu">1</property>
+                                <property name="default_pane">0</property>
+                                <property name="dock">Dock</property>
+                                <property name="dock_fixed">0</property>
+                                <property name="docking">Left</property>
+                                <property name="enabled">1</property>
+                                <property name="fg"></property>
+                                <property name="floatable">1</property>
+                                <property name="font"></property>
+                                <property name="gripper">0</property>
+                                <property name="hidden">0</property>
+                                <property name="id">wxID_ANY</property>
+                                <property name="max_size"></property>
+                                <property name="maximize_button">0</property>
+                                <property name="maximum_size"></property>
+                                <property name="min_size"></property>
+                                <property name="minimize_button">0</property>
+                                <property name="minimum_size">240,-1</property>
+                                <property name="moveable">1</property>
+                                <property name="name">m_borderStyleCombo</property>
+                                <property name="pane_border">1</property>
+                                <property name="pane_position"></property>
+                                <property name="pane_size"></property>
+                                <property name="permission">protected</property>
+                                <property name="pin_button">1</property>
+                                <property name="pos"></property>
+                                <property name="resize">Resizable</property>
+                                <property name="selection">-1</property>
+                                <property name="show">1</property>
+                                <property name="size"></property>
+                                <property name="style">wxCB_READONLY</property>
+                                <property name="subclass">; ; forward_declare</property>
+                                <property name="toolbar_pane">0</property>
+                                <property name="tooltip"></property>
+                                <property name="validator_data_type"></property>
+                                <property name="validator_style">wxFILTER_NONE</property>
+                                <property name="validator_type">wxDefaultValidator</property>
+                                <property name="validator_variable"></property>
+                                <property name="value">Combo!</property>
+                                <property name="window_extra_style"></property>
+                                <property name="window_name"></property>
+                                <property name="window_style"></property>
+                            </object>
+                        </object>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">10</property>
+                    <property name="flag">wxEXPAND|wxTOP|wxRIGHT|wxLEFT</property>
+                    <property name="proportion">0</property>
+                    <object class="wxStaticLine" expanded="1">
+                        <property name="BottomDockable">1</property>
+                        <property name="LeftDockable">1</property>
+                        <property name="RightDockable">1</property>
+                        <property name="TopDockable">1</property>
+                        <property name="aui_layer"></property>
+                        <property name="aui_name"></property>
+                        <property name="aui_position"></property>
+                        <property name="aui_row"></property>
+                        <property name="best_size"></property>
+                        <property name="bg"></property>
+                        <property name="caption"></property>
+                        <property name="caption_visible">1</property>
+                        <property name="center_pane">0</property>
+                        <property name="close_button">1</property>
+                        <property name="context_help"></property>
+                        <property name="context_menu">1</property>
+                        <property name="default_pane">0</property>
+                        <property name="dock">Dock</property>
+                        <property name="dock_fixed">0</property>
+                        <property name="docking">Left</property>
+                        <property name="enabled">1</property>
+                        <property name="fg"></property>
+                        <property name="floatable">1</property>
+                        <property name="font"></property>
+                        <property name="gripper">0</property>
+                        <property name="hidden">0</property>
+                        <property name="id">wxID_ANY</property>
+                        <property name="max_size"></property>
+                        <property name="maximize_button">0</property>
+                        <property name="maximum_size"></property>
+                        <property name="min_size"></property>
+                        <property name="minimize_button">0</property>
+                        <property name="minimum_size"></property>
+                        <property name="moveable">1</property>
+                        <property name="name">m_staticline</property>
+                        <property name="pane_border">1</property>
+                        <property name="pane_position"></property>
+                        <property name="pane_size"></property>
+                        <property name="permission">protected</property>
+                        <property name="pin_button">1</property>
+                        <property name="pos"></property>
+                        <property name="resize">Resizable</property>
+                        <property name="show">1</property>
+                        <property name="size"></property>
+                        <property name="style">wxLI_HORIZONTAL</property>
+                        <property name="subclass"></property>
+                        <property name="toolbar_pane">0</property>
+                        <property name="tooltip"></property>
+                        <property name="window_extra_style"></property>
+                        <property name="window_name"></property>
+                        <property name="window_style"></property>
+                    </object>
+                </object>
+                <object class="sizeritem" expanded="1">
+                    <property name="border">5</property>
+                    <property name="flag">wxEXPAND</property>
+                    <property name="proportion">0</property>
+                    <object class="wxBoxSizer" expanded="1">
+                        <property name="minimum_size"></property>
+                        <property name="name">lowerSizer</property>
+                        <property name="orient">wxHORIZONTAL</property>
+                        <property name="permission">none</property>
+                        <object class="sizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="flag">wxEXPAND</property>
+                            <property name="proportion">1</property>
+                            <object class="spacer" expanded="1">
+                                <property name="height">0</property>
+                                <property name="permission">protected</property>
+                                <property name="width">0</property>
+                            </object>
+                        </object>
+                        <object class="sizeritem" expanded="1">
+                            <property name="border">5</property>
+                            <property name="flag">wxALL</property>
+                            <property name="proportion">0</property>
+                            <object class="wxStdDialogButtonSizer" expanded="1">
+                                <property name="Apply">0</property>
+                                <property name="Cancel">1</property>
+                                <property name="ContextHelp">0</property>
+                                <property name="Help">0</property>
+                                <property name="No">0</property>
+                                <property name="OK">1</property>
+                                <property name="Save">0</property>
+                                <property name="Yes">0</property>
+                                <property name="minimum_size"></property>
+                                <property name="name">m_sdbSizer</property>
+                                <property name="permission">protected</property>
+                                <event name="OnOKButtonClick">OnOkClick</event>
+                            </object>
+                        </object>
+                    </object>
+                </object>
+            </object>
+        </object>
+    </object>
+</wxFormBuilder_Project>
diff --git a/pcbnew/dialogs/dialog_textbox_properties_base.h b/pcbnew/dialogs/dialog_textbox_properties_base.h
new file mode 100644
index 0000000000..15cafec75d
--- /dev/null
+++ b/pcbnew/dialogs/dialog_textbox_properties_base.h
@@ -0,0 +1,105 @@
+///////////////////////////////////////////////////////////////////////////
+// C++ code generated with wxFormBuilder (version Oct 26 2018)
+// http://www.wxformbuilder.org/
+//
+// PLEASE DO *NOT* EDIT THIS FILE!
+///////////////////////////////////////////////////////////////////////////
+
+#pragma once
+
+#include <wx/artprov.h>
+#include <wx/xrc/xmlres.h>
+#include <wx/intl.h>
+class BITMAP_BUTTON;
+class FONT_CHOICE;
+class PCB_LAYER_BOX_SELECTOR;
+
+#include "dialog_shim.h"
+#include <wx/string.h>
+#include <wx/stattext.h>
+#include <wx/gdicmn.h>
+#include <wx/font.h>
+#include <wx/colour.h>
+#include <wx/settings.h>
+#include <wx/stc/stc.h>
+#include <wx/sizer.h>
+#include <wx/checkbox.h>
+#include <wx/bmpcbox.h>
+#include <wx/choice.h>
+#include <wx/bmpbuttn.h>
+#include <wx/bitmap.h>
+#include <wx/image.h>
+#include <wx/icon.h>
+#include <wx/button.h>
+#include <wx/textctrl.h>
+#include <wx/combobox.h>
+#include <wx/gbsizer.h>
+#include <wx/statline.h>
+#include <wx/dialog.h>
+
+///////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+/// Class DIALOG_TEXTBOX_PROPERTIES_BASE
+///////////////////////////////////////////////////////////////////////////////
+class DIALOG_TEXTBOX_PROPERTIES_BASE : public DIALOG_SHIM
+{
+	private:
+
+	protected:
+		wxBoxSizer* m_MultiLineSizer;
+		wxStyledTextCtrl* m_MultiLineText;
+		wxCheckBox* m_cbLocked;
+		wxStaticText* m_LayerLabel;
+		PCB_LAYER_BOX_SELECTOR* m_LayerSelectionCtrl;
+		wxStaticText* m_fontLabel;
+		FONT_CHOICE* m_fontCtrl;
+		BITMAP_BUTTON* m_separator0;
+		BITMAP_BUTTON* m_bold;
+		BITMAP_BUTTON* m_italic;
+		BITMAP_BUTTON* m_separator1;
+		BITMAP_BUTTON* m_alignLeft;
+		BITMAP_BUTTON* m_alignCenter;
+		BITMAP_BUTTON* m_alignRight;
+		BITMAP_BUTTON* m_separator2;
+		BITMAP_BUTTON* m_mirrored;
+		BITMAP_BUTTON* m_separator3;
+		wxStaticText* m_SizeXLabel;
+		wxTextCtrl* m_SizeXCtrl;
+		wxStaticText* m_SizeXUnits;
+		wxStaticText* m_SizeYLabel;
+		wxTextCtrl* m_SizeYCtrl;
+		wxStaticText* m_SizeYUnits;
+		wxStaticText* m_ThicknessLabel;
+		wxTextCtrl* m_ThicknessCtrl;
+		wxStaticText* m_ThicknessUnits;
+		wxStaticText* m_OrientLabel;
+		wxComboBox* m_OrientCtrl;
+		wxStaticText* m_borderWidthLabel;
+		wxTextCtrl* m_borderWidthCtrl;
+		wxStaticText* m_borderWidthUnits;
+		wxStaticText* m_borderStyleLabel;
+		wxBitmapComboBox* m_borderStyleCombo;
+		wxStaticLine* m_staticline;
+		wxStdDialogButtonSizer* m_sdbSizer;
+		wxButton* m_sdbSizerOK;
+		wxButton* m_sdbSizerCancel;
+
+		// Virtual event handlers, overide them in your derived class
+		virtual void OnInitDlg( wxInitDialogEvent& event ) { event.Skip(); }
+		virtual void onMultiLineTCLostFocus( wxFocusEvent& event ) { event.Skip(); }
+		virtual void onFontSelected( wxCommandEvent& event ) { event.Skip(); }
+		virtual void onBoldToggle( wxCommandEvent& event ) { event.Skip(); }
+		virtual void onAlignButton( wxCommandEvent& event ) { event.Skip(); }
+		virtual void OnOkClick( wxCommandEvent& event ) { event.Skip(); }
+		virtual void onThickness( wxCommandEvent& event ) { event.Skip(); }
+
+
+	public:
+
+		DIALOG_TEXTBOX_PROPERTIES_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Text Box Properties"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxSYSTEM_MENU );
+		~DIALOG_TEXTBOX_PROPERTIES_BASE();
+
+};
+
diff --git a/pcbnew/drc/drc_engine.cpp b/pcbnew/drc/drc_engine.cpp
index ad11d1a303..e4ba965d5a 100644
--- a/pcbnew/drc/drc_engine.cpp
+++ b/pcbnew/drc/drc_engine.cpp
@@ -995,7 +995,9 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
                         case PCB_SHAPE_T:        mask = DRC_DISALLOW_GRAPHICS;   break;
                         case PCB_FP_SHAPE_T:     mask = DRC_DISALLOW_GRAPHICS;   break;
                         case PCB_TEXT_T:         mask = DRC_DISALLOW_TEXTS;      break;
+                        case PCB_TEXTBOX_T:      mask = DRC_DISALLOW_TEXTS;      break;
                         case PCB_FP_TEXT_T:      mask = DRC_DISALLOW_TEXTS;      break;
+                        case PCB_FP_TEXTBOX_T:   mask = DRC_DISALLOW_TEXTS;      break;
                         case PCB_ZONE_T:         mask = DRC_DISALLOW_ZONES;      break;
                         case PCB_FP_ZONE_T:      mask = DRC_DISALLOW_ZONES;      break;
                         case PCB_LOCATE_HOLE_T:  mask = DRC_DISALLOW_HOLES;      break;
diff --git a/pcbnew/drc/drc_test_provider.cpp b/pcbnew/drc/drc_test_provider.cpp
index 0ec5827bb3..d1e612893c 100644
--- a/pcbnew/drc/drc_test_provider.cpp
+++ b/pcbnew/drc/drc_test_provider.cpp
@@ -208,7 +208,8 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
 
                 n++;
             }
-            else if( typeMask[ PCB_TEXT_T ] && item->Type() == PCB_TEXT_T )
+            else if( typeMask[ PCB_TEXT_T ]
+                    && ( item->Type() == PCB_TEXT_T || item->Type() == PCB_TEXTBOX_T ) )
             {
                 if( !aFunc( item ) )
                     return n;
@@ -287,7 +288,8 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
 
                     n++;
                 }
-                else if( typeMask[ PCB_FP_TEXT_T ] && dwg->Type() == PCB_FP_TEXT_T )
+                else if( typeMask[ PCB_FP_TEXT_T ]
+                        && ( dwg->Type() == PCB_FP_TEXT_T || dwg->Type() == PCB_FP_TEXTBOX_T ) )
                 {
                     if( !aFunc( dwg ) )
                         return n;
diff --git a/pcbnew/drc/drc_test_provider_disallow.cpp b/pcbnew/drc/drc_test_provider_disallow.cpp
index b47606a7f7..63d727f37d 100644
--- a/pcbnew/drc/drc_test_provider_disallow.cpp
+++ b/pcbnew/drc/drc_test_provider_disallow.cpp
@@ -99,7 +99,9 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run()
                 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_TEXT_ON_EDGECUTS )
                         && item->GetLayer() == Edge_Cuts )
                 {
-                    if( item->Type() == PCB_TEXT_T || BaseType( item->Type() ) == PCB_DIMENSION_T )
+                    if( item->Type() == PCB_TEXT_T
+                            || item->Type() == PCB_TEXTBOX_T
+                            || BaseType( item->Type() ) == PCB_DIMENSION_T )
                     {
                         std::shared_ptr<DRC_ITEM> drc = DRC_ITEM::Create( DRCE_TEXT_ON_EDGECUTS );
                         drc->SetItems( item );
diff --git a/pcbnew/drc/drc_test_provider_text_dims.cpp b/pcbnew/drc/drc_test_provider_text_dims.cpp
index f959385306..eca9be4ef2 100644
--- a/pcbnew/drc/drc_test_provider_text_dims.cpp
+++ b/pcbnew/drc/drc_test_provider_text_dims.cpp
@@ -1,7 +1,7 @@
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
- * Copyright (C) 2021 KiCad Developers.
+ * Copyright (C) 2021-2022 KiCad Developers.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -23,7 +23,9 @@
 
 #include <macros.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
 #include <fp_text.h>
+#include <fp_textbox.h>
 #include <drc/drc_engine.h>
 #include <drc/drc_item.h>
 #include <drc/drc_rule.h>
@@ -104,32 +106,31 @@ bool DRC_TEST_PROVIDER_TEXT_DIMS::Run()
                 if( !reportProgress( ii++, count, delta ) )
                     return false;
 
-                wxASSERT( item->Type() == PCB_TEXT_T || item->Type() == PCB_FP_TEXT_T );
+                wxASSERT( item->Type() == PCB_TEXT_T
+                       || item->Type() == PCB_TEXTBOX_T
+                       || item->Type() == PCB_FP_TEXT_T
+                       || item->Type() == PCB_FP_TEXTBOX_T );
 
                 DRC_CONSTRAINT constraint;
                 int            actualH = 0;
                 int            actualT = 0;
                 bool           visible = false;
 
-                if( item->Type() == PCB_TEXT_T )
+                EDA_TEXT* text = nullptr;
+
+                switch( item->Type() )
                 {
-                    PCB_TEXT*      textItem = static_cast<PCB_TEXT*>( item );
-                    visible = textItem->IsVisible();
-                    actualH = textItem->GetTextHeight();
-                    actualT = textItem->GetTextThickness();
-                }
-                else if( item->Type() == PCB_FP_TEXT_T )
-                {
-                    FP_TEXT*      fpTextItem = static_cast<FP_TEXT*>( item );
-                    visible = fpTextItem->IsVisible();
-                    actualH = fpTextItem->GetTextHeight();
-                    actualT = fpTextItem->GetTextThickness();
-                }
-                else
-                {
-                    UNIMPLEMENTED_FOR( item->GetClass() );
+                case PCB_TEXT_T:       text = static_cast<PCB_TEXT*>( item );    break;
+                case PCB_TEXTBOX_T:    text = static_cast<PCB_TEXTBOX*>( item ); break;
+                case PCB_FP_TEXT_T:    text = static_cast<FP_TEXT*>( item );     break;
+                case PCB_FP_TEXTBOX_T: text = static_cast<FP_TEXTBOX*>( item );  break;
+                default:               UNIMPLEMENTED_FOR( item->GetClass() );    break;
                 }
 
+                visible = text->IsVisible();
+                actualH = text->GetTextHeight();
+                actualT = text->GetTextThickness();
+
                 if( !visible )
                     return true;
 
diff --git a/pcbnew/edit.cpp b/pcbnew/edit.cpp
index 0b491278fb..de107c7e0f 100644
--- a/pcbnew/edit.cpp
+++ b/pcbnew/edit.cpp
@@ -120,9 +120,15 @@ void PCB_EDIT_FRAME::OnEditItemRequest( BOARD_ITEM* aItem )
     switch( aItem->Type() )
     {
     case PCB_TEXT_T:
+    case PCB_FP_TEXT_T:
         ShowTextPropertiesDialog( aItem );
         break;
 
+    case PCB_TEXTBOX_T:
+    case PCB_FP_TEXTBOX_T:
+        ShowTextBoxPropertiesDialog( aItem );
+        break;
+
     case PCB_PAD_T:
         ShowPadPropertiesDialog( static_cast<PAD*>( aItem ) );
         break;
@@ -146,10 +152,6 @@ void PCB_EDIT_FRAME::OnEditItemRequest( BOARD_ITEM* aItem )
         break;
     }
 
-    case PCB_FP_TEXT_T:
-        ShowTextPropertiesDialog( aItem );
-        break;
-
     case PCB_SHAPE_T:
         ShowGraphicItemPropertiesDialog( aItem );
         break;
diff --git a/pcbnew/exporters/export_gencad.cpp b/pcbnew/exporters/export_gencad.cpp
index 7fd6164141..7eb5e67a99 100644
--- a/pcbnew/exporters/export_gencad.cpp
+++ b/pcbnew/exporters/export_gencad.cpp
@@ -1213,6 +1213,7 @@ static void FootprintWriteShape( FILE* aFile, FOOTPRINT* aFootprint, const wxStr
         switch( PtStruct->Type() )
         {
         case PCB_FP_TEXT_T:
+        case PCB_FP_TEXTBOX_T:
 
             // If we wanted to export text, this is not the correct section
             break;
diff --git a/pcbnew/exporters/gen_drill_report_files.cpp b/pcbnew/exporters/gen_drill_report_files.cpp
index 7194dc9c64..12d47eaa9b 100644
--- a/pcbnew/exporters/gen_drill_report_files.cpp
+++ b/pcbnew/exporters/gen_drill_report_files.cpp
@@ -39,6 +39,9 @@
 #include <pcbplot.h>
 #include <gendrill_file_writer_base.h>
 #include <pcb_painter.h>
+#include <pcb_shape.h>
+#include <pcb_text.h>
+#include <pcb_textbox.h>
 
 
 /* Conversion utilities - these will be used often in there... */
@@ -197,11 +200,16 @@ bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName, PLOT_
         switch( item->Type() )
         {
         case PCB_SHAPE_T:
-            itemplotter.PlotPcbShape( (PCB_SHAPE*) item );
+            itemplotter.PlotPcbShape( static_cast<PCB_SHAPE*>( item ) );
             break;
 
         case PCB_TEXT_T:
-            itemplotter.PlotPcbText( (PCB_TEXT*) item );
+            itemplotter.PlotPcbText( static_cast<PCB_TEXT*>( item ), item->GetLayer() );
+            break;
+
+        case PCB_TEXTBOX_T:
+            itemplotter.PlotPcbText( static_cast<PCB_TEXTBOX*>( item ), item->GetLayer() );
+            itemplotter.PlotPcbShape( static_cast<PCB_TEXTBOX*>( item ) );
             break;
 
         case PCB_DIM_ALIGNED_T:
diff --git a/pcbnew/exporters/gerber_placefile_writer.cpp b/pcbnew/exporters/gerber_placefile_writer.cpp
index 321381902b..af9c2a04de 100644
--- a/pcbnew/exporters/gerber_placefile_writer.cpp
+++ b/pcbnew/exporters/gerber_placefile_writer.cpp
@@ -34,12 +34,14 @@
 
 #include <board.h>
 #include <board_design_settings.h>
+#include <pcb_shape.h>
 
 #include <pcbplot.h>
 #include <wildcards_and_files_ext.h>
 #include <gbr_metadata.h>
 #include <footprint.h>
 #include <pad.h>
+#include <fp_shape.h>
 
 
 PLACEFILE_GERBER_WRITER::PLACEFILE_GERBER_WRITER( BOARD* aPcb )
@@ -302,7 +304,7 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER
             for( BOARD_ITEM* item : footprint->GraphicalItems() )
             {
                 if( item->Type() == PCB_FP_SHAPE_T && item->GetLayer() == Edge_Cuts )
-                    brd_plotter.PlotFootprintGraphicItem( (FP_SHAPE*) item );
+                    brd_plotter.PlotFootprintShape( static_cast<FP_SHAPE*>( item ) );
             }
         }
     }
diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp
index 7d9043991f..65ac380b6c 100644
--- a/pcbnew/footprint.cpp
+++ b/pcbnew/footprint.cpp
@@ -47,6 +47,7 @@
 #include <i18n_utility.h>
 #include <convert_shape_list_to_polygon.h>
 #include <geometry/convex_hull.h>
+#include "fp_textbox.h"
 
 FOOTPRINT::FOOTPRINT( BOARD* parent ) :
         BOARD_ITEM_CONTAINER((BOARD_ITEM*) parent, PCB_FOOTPRINT_T ),
@@ -510,6 +511,7 @@ void FOOTPRINT::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
     case PCB_FP_DIM_RADIAL_T:
     case PCB_FP_DIM_ORTHOGONAL_T:
     case PCB_FP_SHAPE_T:
+    case PCB_FP_TEXTBOX_T:
         if( aMode == ADD_MODE::APPEND )
             m_drawings.push_back( aBoardItem );
         else
@@ -559,9 +561,8 @@ void FOOTPRINT::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aMode )
     {
     case PCB_FP_TEXT_T:
         // Only user text can be removed this way.
-        wxCHECK_RET(
-                static_cast<FP_TEXT*>( aBoardItem )->GetType() == FP_TEXT::TEXT_is_DIVERS,
-                "Please report this bug: Invalid remove operation on required text" );
+        wxCHECK_RET( static_cast<FP_TEXT*>( aBoardItem )->GetType() == FP_TEXT::TEXT_is_DIVERS,
+                     "Please report this bug: Invalid remove operation on required text" );
         KI_FALLTHROUGH;
 
     case PCB_FP_DIM_ALIGNED_T:
@@ -570,6 +571,7 @@ void FOOTPRINT::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aMode )
     case PCB_FP_DIM_RADIAL_T:
     case PCB_FP_DIM_LEADER_T:
     case PCB_FP_SHAPE_T:
+    case PCB_FP_TEXTBOX_T:
         for( auto it = m_drawings.begin(); it != m_drawings.end(); ++it )
         {
             if( *it == aBoardItem )
@@ -767,7 +769,7 @@ const EDA_RECT FOOTPRINT::GetBoundingBox( bool aIncludeText, bool aIncludeInvisi
         if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
             continue;
 
-        if( item->Type() == PCB_FP_SHAPE_T || BaseType( item->Type() ) == PCB_DIMENSION_T )
+        if( item->Type() != PCB_FP_TEXT_T )
             area.Merge( item->GetBoundingBox() );
     }
 
@@ -787,6 +789,8 @@ const EDA_RECT FOOTPRINT::GetBoundingBox( bool aIncludeText, bool aIncludeInvisi
             if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
                 continue;
 
+            // Only FP_TEXT items are independently selectable; FP_TEXTBOX items go in with
+            // other graphic items above.
             if( item->Type() == PCB_FP_TEXT_T )
                 area.Merge( item->GetBoundingBox() );
         }
@@ -869,7 +873,7 @@ SHAPE_POLY_SET FOOTPRINT::GetBoundingHull() const
         if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
             continue;
 
-        if( item->Type() == PCB_FP_SHAPE_T || BaseType( item->Type() ) == PCB_DIMENSION_T )
+        if( item->Type() != PCB_FP_TEXT_T )
         {
             item->TransformShapeWithClearanceToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
                                                         ERROR_OUTSIDE );
@@ -1069,6 +1073,9 @@ bool FOOTPRINT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy )
 
         for( BOARD_ITEM* item : m_drawings )
         {
+            // Text items are selectable on their own, and are therefore excluded from this
+            // test.  TextBox items are NOT selectable on their own, and so MUST be included
+            // here.
             if( item->Type() != PCB_FP_TEXT_T && item->HitTest( arect, false, 0 ) )
                 return true;
         }
@@ -1249,6 +1256,7 @@ SEARCH_RESULT FOOTPRINT::Visit( INSPECTOR inspector, void* testData, const KICAD
         case PCB_FP_DIM_RADIAL_T:
         case PCB_FP_DIM_ORTHOGONAL_T:
         case PCB_FP_SHAPE_T:
+        case PCB_FP_TEXTBOX_T:
             result = IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, p );
 
             // skip over any types handled in the above call.
@@ -1257,6 +1265,7 @@ SEARCH_RESULT FOOTPRINT::Visit( INSPECTOR inspector, void* testData, const KICAD
                 switch( stype = *++p )
                 {
                 case PCB_FP_TEXT_T:
+                case PCB_FP_TEXTBOX_T:
                 case PCB_FP_SHAPE_T:
                 case PCB_FP_DIM_ALIGNED_T:
                 case PCB_FP_DIM_LEADER_T:
@@ -1557,6 +1566,10 @@ void FOOTPRINT::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
             static_cast<FP_TEXT*>( item )->Flip( m_pos, false );
             break;
 
+        case PCB_FP_TEXTBOX_T:
+            static_cast<FP_TEXTBOX*>( item )->Flip( m_pos, false );
+            break;
+
         default:
             wxMessageBox( wxT( "FOOTPRINT::Flip() error: Unknown Draw Type" ) );
             break;
@@ -1597,6 +1610,7 @@ void FOOTPRINT::SetPosition( const VECTOR2I& aPos )
         switch( item->Type() )
         {
         case PCB_FP_SHAPE_T:
+        case PCB_FP_TEXTBOX_T:
         {
             FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
             shape->SetDrawCoord();
@@ -1633,7 +1647,8 @@ void FOOTPRINT::SetPosition( const VECTOR2I& aPos )
 
 void FOOTPRINT::MoveAnchorPosition( const VECTOR2I& aMoveVector )
 {
-    /* Move the reference point of the footprint
+    /*
+     * Move the reference point of the footprint
      * the footprints elements (pads, outlines, edges .. ) are moved
      * but:
      * - the footprint position is not modified.
@@ -1641,7 +1656,6 @@ void FOOTPRINT::MoveAnchorPosition( const VECTOR2I& aMoveVector )
      * - Draw coordinates are updated
      */
 
-
     // Update (move) the relative coordinates relative to the new anchor point.
     VECTOR2I moveVector = aMoveVector;
     RotatePoint( moveVector, -GetOrientation() );
@@ -1665,19 +1679,20 @@ void FOOTPRINT::MoveAnchorPosition( const VECTOR2I& aMoveVector )
         switch( item->Type() )
         {
         case PCB_FP_SHAPE_T:
+        case PCB_FP_TEXTBOX_T:
         {
             FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
             shape->Move( moveVector );
-        }
             break;
+        }
 
         case PCB_FP_TEXT_T:
         {
             FP_TEXT* text = static_cast<FP_TEXT*>( item );
             text->SetPos0( text->GetPos0() + moveVector );
             text->SetDrawCoord();
-        }
             break;
+        }
 
         default:
             break;
@@ -1727,10 +1742,20 @@ void FOOTPRINT::SetOrientation( const EDA_ANGLE& aNewAngle )
     // Displace contours and text of the footprint.
     for( BOARD_ITEM* item : m_drawings )
     {
-        if( item->Type() == PCB_FP_SHAPE_T )
+        switch( item->Type() )
+        {
+        case PCB_FP_SHAPE_T:
+        case PCB_FP_TEXTBOX_T:
             static_cast<FP_SHAPE*>( item )->SetDrawCoord();
-        else if( item->Type() == PCB_FP_TEXT_T )
+            break;
+
+        case PCB_FP_TEXT_T:
             static_cast<FP_TEXT*>( item )->SetDrawCoord();
+            break;
+
+        default:
+            break;
+        }
     }
 
     m_boundingBoxCacheTimeStamp = 0;
@@ -1822,6 +1847,18 @@ BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootpr
         break;
     }
 
+    case PCB_FP_TEXTBOX_T:
+    {
+        FP_TEXTBOX* new_textbox = new FP_TEXTBOX( *static_cast<const FP_TEXTBOX*>( aItem ) );
+        const_cast<KIID&>( new_textbox->m_Uuid ) = KIID();
+
+        if( aAddToFootprint )
+            Add( new_textbox );
+
+        new_item = new_textbox;
+        break;
+    }
+
     case PCB_FP_DIM_ALIGNED_T:
     case PCB_FP_DIM_LEADER_T:
     case PCB_FP_DIM_CENTER_T:
@@ -1937,6 +1974,13 @@ double FOOTPRINT::GetCoverageArea( const BOARD_ITEM* aItem, const GENERAL_COLLEC
         text->TransformTextShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, textMargin,
                                                         ARC_LOW_DEF, ERROR_OUTSIDE );
     }
+    else if( aItem->Type() == PCB_FP_TEXTBOX_T )
+    {
+        const FP_TEXTBOX* textbox = static_cast<const FP_TEXTBOX*>( aItem );
+
+        textbox->TransformTextShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, textMargin,
+                                                           ARC_LOW_DEF, ERROR_OUTSIDE );
+    }
     else if( aItem->Type() == PCB_SHAPE_T )
     {
         // Approximate "linear" shapes with just their width squared, as we don't want to consider
@@ -2004,6 +2048,7 @@ double FOOTPRINT::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
         switch( item->Type() )
         {
         case PCB_FP_TEXT_T:
+        case PCB_FP_TEXTBOX_T:
         case PCB_FP_SHAPE_T:
             if( item->GetParent() != this )
             {
@@ -2013,6 +2058,7 @@ double FOOTPRINT::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
             break;
 
         case PCB_TEXT_T:
+        case PCB_TEXTBOX_T:
         case PCB_SHAPE_T:
         case PCB_TRACE_T:
         case PCB_ARC_T:
@@ -2397,6 +2443,17 @@ void FOOTPRINT::TransformFPShapesWithClearanceToPolygon( SHAPE_POLY_SET& aCorner
                 texts.push_back( text );
         }
 
+        if( item->Type() == PCB_FP_TEXTBOX_T && aIncludeText )
+        {
+            FP_TEXTBOX* textbox = static_cast<FP_TEXTBOX*>( item );
+
+            if( aLayer != UNDEFINED_LAYER && textbox->GetLayer() == aLayer && textbox->IsVisible() )
+            {
+                textbox->TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, 0,
+                                                               aError, aErrorLoc, false );
+            }
+        }
+
         if( item->Type() == PCB_FP_SHAPE_T && aIncludeShapes )
         {
             const FP_SHAPE* outline = static_cast<FP_SHAPE*>( item );
diff --git a/pcbnew/footprint_edit_frame.cpp b/pcbnew/footprint_edit_frame.cpp
index 21be761f2f..b33195cea3 100644
--- a/pcbnew/footprint_edit_frame.cpp
+++ b/pcbnew/footprint_edit_frame.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2015-2016 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -1162,6 +1162,7 @@ void FOOTPRINT_EDIT_FRAME::setupUIConditions()
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawPolygon );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawRuleArea );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::placeText );
+    CURRENT_EDIT_TOOL( PCB_ACTIONS::drawTextBox );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawAlignedDimension );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawOrthogonalDimension );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawCenterDimension );
diff --git a/pcbnew/footprint_editor_utils.cpp b/pcbnew/footprint_editor_utils.cpp
index 6b1154fb20..b92dac4ee7 100644
--- a/pcbnew/footprint_editor_utils.cpp
+++ b/pcbnew/footprint_editor_utils.cpp
@@ -190,6 +190,10 @@ void FOOTPRINT_EDIT_FRAME::OnEditItemRequest( BOARD_ITEM* aItem )
         ShowTextPropertiesDialog( aItem );
         break;
 
+    case PCB_FP_TEXTBOX_T:
+        ShowTextBoxPropertiesDialog( aItem );
+        break;
+
     case PCB_FP_SHAPE_T :
         ShowGraphicItemPropertiesDialog( aItem );
         break;
diff --git a/pcbnew/footprint_libraries_utils.cpp b/pcbnew/footprint_libraries_utils.cpp
index 3f8da55722..3325b383da 100644
--- a/pcbnew/footprint_libraries_utils.cpp
+++ b/pcbnew/footprint_libraries_utils.cpp
@@ -1150,14 +1150,9 @@ FOOTPRINT* PCB_BASE_FRAME::CreateNewFootprint( const wxString& aFootprintName, b
 
         switch( footprintType )
         {
-        case 0:
-            footprintTranslated = FP_THROUGH_HOLE;
-            break;
-        case 1:
-            footprintTranslated = FP_SMD;
-            break;
-        default:
-            footprintTranslated = 0;
+        case 0:  footprintTranslated = FP_THROUGH_HOLE; break;
+        case 1:  footprintTranslated = FP_SMD;          break;
+        default: footprintTranslated = 0;               break;
         }
     }
 
diff --git a/pcbnew/fp_shape.cpp b/pcbnew/fp_shape.cpp
index 1b4200830d..b1caed6588 100644
--- a/pcbnew/fp_shape.cpp
+++ b/pcbnew/fp_shape.cpp
@@ -37,8 +37,8 @@
 #include <view/view.h>
 
 
-FP_SHAPE::FP_SHAPE( FOOTPRINT* parent, SHAPE_T aShape ) :
-        PCB_SHAPE( parent, PCB_FP_SHAPE_T, aShape )
+FP_SHAPE::FP_SHAPE( FOOTPRINT* parent, SHAPE_T aShape, KICAD_T aItemType ) :
+        PCB_SHAPE( parent, aItemType, aShape )
 {
     m_layer = F_SilkS;
 }
diff --git a/pcbnew/fp_shape.h b/pcbnew/fp_shape.h
index 3423828036..a645da47f7 100644
--- a/pcbnew/fp_shape.h
+++ b/pcbnew/fp_shape.h
@@ -37,7 +37,8 @@ class MSG_PANEL_ITEM;
 class FP_SHAPE : public PCB_SHAPE
 {
 public:
-    FP_SHAPE( FOOTPRINT* parent, SHAPE_T aShape = SHAPE_T::SEGMENT );
+    FP_SHAPE( FOOTPRINT* aParent, SHAPE_T aShape = SHAPE_T::SEGMENT,
+              KICAD_T aItemType = PCB_FP_SHAPE_T );
 
     // Do not create a copy constructor & operator=.
     // The ones generated by the compiler are adequate.
@@ -128,13 +129,13 @@ public:
      * Call in only when the geometry or the footprint is modified and therefore the relative
      * coordinates have to be updated from the draw coordinates.
      */
-    void SetLocalCoord();
+    virtual void SetLocalCoord();
 
     /**
      * Set draw coordinates (absolute values ) from relative coordinates.
      * Must be called when a relative coordinate has changed in order to see the changes on screen
      */
-    void SetDrawCoord();
+    virtual void SetDrawCoord();
 
     void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
 
diff --git a/pcbnew/fp_textbox.cpp b/pcbnew/fp_textbox.cpp
new file mode 100644
index 0000000000..d65959354d
--- /dev/null
+++ b/pcbnew/fp_textbox.cpp
@@ -0,0 +1,406 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2022 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 <pcb_edit_frame.h>
+#include <base_units.h>
+#include <bitmaps.h>
+#include <board.h>
+#include <board_design_settings.h>
+#include <core/mirror.h>
+#include <footprint.h>
+#include <fp_textbox.h>
+#include <settings/settings_manager.h>
+#include <trigo.h>
+#include <string_utils.h>
+#include <painter.h>
+#include <geometry/shape_compound.h>
+#include <callback_gal.h>
+#include <convert_basic_shapes_to_polygon.h>
+
+FP_TEXTBOX::FP_TEXTBOX( FOOTPRINT* aParentFootprint ) :
+        FP_SHAPE( aParentFootprint, SHAPE_T::RECT, PCB_FP_TEXTBOX_T ),
+        EDA_TEXT()
+{
+    SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
+    SetVertJustify( GR_TEXT_V_ALIGN_TOP );
+    SetMultilineAllowed( true );
+
+    SetDrawCoord();
+}
+
+
+FP_TEXTBOX::~FP_TEXTBOX()
+{
+}
+
+
+int FP_TEXTBOX::GetTextMargin() const
+{
+    return KiROUND( GetTextSize().y * 0.8 );
+}
+
+
+EDA_ANGLE FP_TEXTBOX::GetDrawRotation() const
+{
+    FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( m_parent );
+    EDA_ANGLE  rotation = GetTextAngle();
+
+    if( parentFootprint )
+        rotation += parentFootprint->GetOrientation();
+
+    rotation.Normalize();
+
+    return rotation;
+}
+
+
+std::vector<VECTOR2I> FP_TEXTBOX::GetAnchorAndOppositeCorner() const
+{
+    std::vector<VECTOR2I> pts;
+    std::vector<VECTOR2I> corners = GetCorners();
+
+    EDA_ANGLE toCorner1( corners[0] - corners[1] );
+    EDA_ANGLE toCorner3( corners[0] - corners[3] );
+    EDA_ANGLE textAngle( GetDrawRotation() );
+    toCorner1.Normalize();
+    toCorner3.Normalize();
+    textAngle.Normalize();
+
+    pts.emplace_back( corners[0] );
+
+    if( std::abs( toCorner1 - textAngle ) < std::abs( toCorner3 - textAngle ) )
+        pts.emplace_back( corners[1] );
+    else
+        pts.emplace_back( corners[3] );
+
+    return pts;
+}
+
+
+VECTOR2I FP_TEXTBOX::GetDrawPos() const
+{
+    std::vector<VECTOR2I> corners = GetAnchorAndOppositeCorner();
+    GR_TEXT_H_ALIGN_T     effectiveAlignment = GetHorizJustify();
+    VECTOR2I              textAnchor;
+    VECTOR2I              offset;
+
+    if( IsMirrored() )
+    {
+        switch( GetHorizJustify() )
+        {
+        case GR_TEXT_H_ALIGN_LEFT:   effectiveAlignment = GR_TEXT_H_ALIGN_RIGHT;  break;
+        case GR_TEXT_H_ALIGN_CENTER: effectiveAlignment = GR_TEXT_H_ALIGN_CENTER; break;
+        case GR_TEXT_H_ALIGN_RIGHT:  effectiveAlignment = GR_TEXT_H_ALIGN_LEFT;   break;
+        }
+    }
+
+    switch( effectiveAlignment )
+    {
+    case GR_TEXT_H_ALIGN_LEFT:
+        textAnchor = corners[0];
+        offset = VECTOR2I( GetTextMargin(), GetTextMargin() );
+        break;
+    case GR_TEXT_H_ALIGN_CENTER:
+        textAnchor = ( corners[0] + corners[1] ) / 2;
+        offset = VECTOR2I( 0, GetTextMargin() );
+        break;
+    case GR_TEXT_H_ALIGN_RIGHT:
+        textAnchor = corners[1];
+        offset = VECTOR2I( -GetTextMargin(), GetTextMargin() );
+        break;
+    }
+
+    RotatePoint( offset, GetDrawRotation() );
+    return textAnchor + offset;
+}
+
+
+bool FP_TEXTBOX::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
+{
+    EDA_RECT rect = GetBoundingBox();
+
+    rect.Inflate( aAccuracy );
+
+    return rect.Contains( aPosition );
+}
+
+
+bool FP_TEXTBOX::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
+{
+    EDA_RECT rect = aRect;
+
+    rect.Inflate( aAccuracy );
+
+    if( aContained )
+        return rect.Contains( GetBoundingBox() );
+
+    return rect.Intersects( GetBoundingBox() );
+}
+
+
+void FP_TEXTBOX::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
+{
+    FP_SHAPE::Rotate( aRotCentre, aAngle );
+    SetTextAngle( GetTextAngle() + aAngle );
+}
+
+
+void FP_TEXTBOX::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+{
+    // flipping the footprint is relative to the X axis
+    if( aFlipLeftRight )
+    {
+        SetTextX( MIRRORVAL( GetTextPos().x, aCentre.x ) );
+        SetTextAngle( -GetTextAngle() );
+    }
+    else
+    {
+        SetTextY( MIRRORVAL( GetTextPos().y, aCentre.y ) );
+        SetTextAngle( ANGLE_180 - GetTextAngle() );
+    }
+
+    SetLayer( FlipLayer( GetLayer(), GetBoard()->GetCopperLayerCount() ) );
+    SetMirrored( IsBackLayer( GetLayer() ) );
+    SetLocalCoord();
+}
+
+
+void FP_TEXTBOX::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
+{
+    // the position is mirrored, but the text itself is not mirrored
+
+    if( aMirrorAroundXAxis )
+        SetTextY( ::MIRRORVAL( GetTextPos().y, aCentre.y ) );
+    else
+        SetTextX( ::MIRRORVAL( GetTextPos().x, aCentre.x ) );
+
+    SetLocalCoord();
+}
+
+
+void FP_TEXTBOX::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
+{
+    EDA_UNITS units = aFrame->GetUserUnits();
+
+    // Don't use GetShownText() here; we want to show the user the variable references
+    aList.emplace_back( _( "Text Box" ), UnescapeString( GetText() ) );
+
+    if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
+        aList.emplace_back( _( "Status" ), _( "Locked" ) );
+
+    aList.emplace_back( _( "Layer" ), GetLayerName() );
+    aList.emplace_back( _( "Mirror" ), IsMirrored() ? _( "Yes" ) : _( "No" ) );
+    aList.emplace_back( _( "Angle" ), wxString::Format( "%g", GetTextAngle().AsDegrees() ) );
+
+    aList.emplace_back( _( "Thickness" ), MessageTextFromValue( units, GetTextThickness() ) );
+    aList.emplace_back( _( "Width" ), MessageTextFromValue( units, GetTextWidth() ) );
+    aList.emplace_back( _( "Height" ), MessageTextFromValue( units, GetTextHeight() ) );
+}
+
+
+wxString FP_TEXTBOX::GetSelectMenuText( EDA_UNITS aUnits ) const
+{
+    return wxString::Format( _( "Footprint Text Box of %s" ),
+                             static_cast<FOOTPRINT*>( GetParent() )->GetReference() );
+}
+
+
+BITMAPS FP_TEXTBOX::GetMenuImage() const
+{
+    return BITMAPS::add_textbox;
+}
+
+
+EDA_ITEM* FP_TEXTBOX::Clone() const
+{
+    return new FP_TEXTBOX( *this );
+}
+
+
+const BOX2I FP_TEXTBOX::ViewBBox() const
+{
+    EDA_ANGLE angle = GetDrawRotation();
+    EDA_RECT  text_area = GetTextBox();
+
+    if( !angle.IsZero() )
+        text_area = text_area.GetBoundingBoxRotated( GetDrawPos(), angle );
+
+    return BOX2I( text_area.GetPosition(), text_area.GetSize() );
+}
+
+
+void FP_TEXTBOX::ViewGetLayers( int aLayers[], int& aCount ) const
+{
+    if( IsVisible() )
+        aLayers[0] = GetLayer();
+    else
+        aLayers[0] = LAYER_MOD_TEXT_INVISIBLE;
+
+    aCount = 1;
+}
+
+
+double FP_TEXTBOX::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
+{
+    constexpr double HIDE = (double)std::numeric_limits<double>::max();
+
+    if( !aView )
+        return 0.0;
+
+    // Hidden text gets put on the LAYER_MOD_TEXT_INVISIBLE for rendering, but
+    // should only render if its native layer is visible.
+    if( !aView->IsLayerVisible( GetLayer() ) )
+        return HIDE;
+
+    RENDER_SETTINGS* renderSettings = aView->GetPainter()->GetSettings();
+    COLOR4D          backgroundColor = renderSettings->GetLayerColor( LAYER_PCB_BACKGROUND );
+
+    // Handle Render tab switches
+    if( renderSettings->GetLayerColor( LAYER_MOD_TEXT ) == backgroundColor )
+        return HIDE;
+
+    if( !IsParentFlipped() && !aView->IsLayerVisible( LAYER_MOD_FR ) )
+        return HIDE;
+
+    if( IsParentFlipped() && !aView->IsLayerVisible( LAYER_MOD_BK ) )
+        return HIDE;
+
+    if( !aView->IsLayerVisible( LAYER_MOD_TEXT ) )
+        return HIDE;
+
+    // Other layers are shown without any conditions
+    return 0.0;
+}
+
+
+wxString FP_TEXTBOX::GetShownText( int aDepth ) const
+{
+    const FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( GetParent() );
+    wxASSERT( parentFootprint );
+    const BOARD*  board = parentFootprint->GetBoard();
+
+    std::function<bool( wxString* )> footprintResolver =
+            [&]( wxString* token ) -> bool
+            {
+                return parentFootprint && parentFootprint->ResolveTextVar( token, aDepth );
+            };
+
+    std::function<bool( wxString* )> boardTextResolver =
+            [&]( wxString* token ) -> bool
+            {
+                return board->ResolveTextVar( token, aDepth + 1 );
+            };
+
+    wxString text = EDA_TEXT::GetShownText();
+
+    if( HasTextVars() )
+    {
+        PROJECT* project = nullptr;
+
+        if( parentFootprint && parentFootprint->GetParent() )
+            project = static_cast<BOARD*>( parentFootprint->GetParent() )->GetProject();
+
+        if( aDepth < 10 )
+            text = ExpandTextVars( text, &footprintResolver, &boardTextResolver, project );
+    }
+
+    KIFONT::FONT*         font = GetDrawFont();
+    std::vector<VECTOR2I> corners = GetAnchorAndOppositeCorner();
+    int                   colWidth = ( corners[1] - corners[0] ).EuclideanNorm();
+
+    colWidth -= GetTextMargin() * 2;
+    font->LinebreakText( text, colWidth, GetTextSize(), GetTextThickness(), IsBold(), IsItalic() );
+
+    return text;
+}
+
+
+std::shared_ptr<SHAPE> FP_TEXTBOX::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
+{
+    return GetEffectiveTextShape();
+}
+
+
+void FP_TEXTBOX::TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
+                                                        PCB_LAYER_ID aLayer, int aClearance,
+                                                        int aError, ERROR_LOC aErrorLoc ) const
+{
+    KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
+    KIFONT::FONT*              font = GetDrawFont();
+    int                        penWidth = GetEffectiveTextPenWidth();
+
+    CALLBACK_GAL callback_gal( empty_opts,
+            // Stroke callback
+            [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
+            {
+                TransformOvalToPolygon( aCornerBuffer, aPt1, aPt2, penWidth + ( 2 * aClearance ),
+                                        aError, ERROR_INSIDE );
+            },
+            // Triangulation callback
+            [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2, const VECTOR2I& aPt3 )
+            {
+                aCornerBuffer.NewOutline();
+
+                for( const VECTOR2I& point : { aPt1, aPt2, aPt3 } )
+                    aCornerBuffer.Append( point.x, point.y );
+            } );
+
+    TEXT_ATTRIBUTES attrs = GetAttributes();
+    attrs.m_Angle = GetDrawRotation();
+
+    font->Draw( &callback_gal, GetShownText(), GetDrawPos(), attrs );
+}
+
+
+void FP_TEXTBOX::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
+                                                    PCB_LAYER_ID aLayer, int aClearance,
+                                                    int aError, ERROR_LOC aErrorLoc,
+                                                    bool aIgnoreLineWidth ) const
+{
+    if( PCB_SHAPE::GetStroke().GetWidth() >= 0 )
+    {
+        FP_SHAPE::TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, aClearance,
+                                                        aError, aErrorLoc );
+    }
+    else
+    {
+        EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( &aCornerBuffer, aClearance );
+    }
+}
+
+
+static struct FP_TEXTBOX_DESC
+{
+    FP_TEXTBOX_DESC()
+    {
+        PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
+        REGISTER_TYPE( FP_TEXTBOX );
+        propMgr.AddTypeCast( new TYPE_CAST<FP_TEXTBOX, FP_SHAPE> );
+        propMgr.AddTypeCast( new TYPE_CAST<FP_TEXTBOX, EDA_TEXT> );
+        propMgr.InheritsAfter( TYPE_HASH( FP_TEXTBOX ), TYPE_HASH( FP_SHAPE ) );
+        propMgr.InheritsAfter( TYPE_HASH( FP_TEXTBOX ), TYPE_HASH( EDA_TEXT ) );
+
+        propMgr.AddProperty( new PROPERTY<FP_TEXTBOX, wxString>( _HKI( "Parent" ),
+                    NO_SETTER( FP_TEXTBOX, wxString ), &FP_TEXTBOX::GetParentAsString ) );
+    }
+} _FP_TEXTBOX_DESC;
diff --git a/pcbnew/fp_textbox.h b/pcbnew/fp_textbox.h
new file mode 100644
index 0000000000..7e6edf4bc9
--- /dev/null
+++ b/pcbnew/fp_textbox.h
@@ -0,0 +1,144 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2022 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 FP_TEXTBOX_H
+#define FP_TEXTBOX_H
+
+#include <eda_text.h>
+#include <fp_shape.h>
+
+class LINE_READER;
+class EDA_RECT;
+class FOOTPRINT;
+class MSG_PANEL_ITEM;
+class PCB_BASE_FRAME;
+class SHAPE;
+
+
+class FP_TEXTBOX : public FP_SHAPE, public EDA_TEXT
+{
+public:
+    FP_TEXTBOX( FOOTPRINT* aParentFootprint );
+
+    // Do not create a copy constructor & operator=.
+    // The ones generated by the compiler are adequate.
+
+    ~FP_TEXTBOX();
+
+    static inline bool ClassOf( const EDA_ITEM* aItem )
+    {
+        return aItem && aItem->Type() == PCB_FP_TEXT_T;
+    }
+
+    bool IsType( const KICAD_T aScanTypes[] ) const override
+    {
+        if( BOARD_ITEM::IsType( aScanTypes ) )
+            return true;
+
+        for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
+        {
+            if( *p == PCB_LOCATE_TEXT_T )
+                return true;
+        }
+
+        return false;
+    }
+
+    wxString GetParentAsString() const { return m_parent->m_Uuid.AsString(); }
+
+    bool Matches( const wxFindReplaceData& aSearchData, void* aAuxData ) const override
+    {
+        return BOARD_ITEM::Matches( GetShownText(), aSearchData );
+    }
+
+    int GetTextMargin() const;
+
+    virtual EDA_ANGLE GetDrawRotation() const override;
+
+    VECTOR2I GetDrawPos() const override;
+
+    std::vector<VECTOR2I> GetAnchorAndOppositeCorner() const;
+
+    /// Rotate text, in footprint editor
+    /// (for instance in footprint rotation transform)
+    void Rotate( const VECTOR2I& aOffset, const EDA_ANGLE& aAngle ) override;
+
+    /// Flip entity during footprint flip
+    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+
+    /// Mirror text position in footprint editing
+    /// the text itself is not mirrored, and the layer not modified,
+    /// only position is mirrored.
+    /// (use Flip to change layer to its paired and mirror the text in fp editor).
+    void Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis );
+
+    // The Pos0 accessors are for footprint-relative coordinates.
+    void SetPos0( const VECTOR2I& aPos ) { m_Pos0 = aPos; SetDrawCoord(); }
+    const VECTOR2I& GetPos0() const      { return m_Pos0; }
+
+    void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
+
+    bool HitTest( const VECTOR2I& aPosition, int aAccuracy ) const override;
+
+    bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override;
+
+    void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, PCB_LAYER_ID aLayer,
+                                               int aClearance, int aError, ERROR_LOC aErrorLoc,
+                                               bool aIgnoreLineWidth ) const override;
+
+    void TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
+                                                   PCB_LAYER_ID aLayer, int aClearanceValue,
+                                                   int aError, ERROR_LOC aErrorLoc ) const;
+
+    // @copydoc BOARD_ITEM::GetEffectiveShape
+    std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER ) const override;
+
+    wxString GetClass() const override
+    {
+        return wxT( "MTEXT" );
+    }
+
+    wxString GetSelectMenuText( EDA_UNITS aUnits ) const override;
+
+    BITMAPS GetMenuImage() const override;
+
+    EDA_ITEM* Clone() const override;
+
+    virtual wxString GetShownText( int aDepth = 0 ) const override;
+
+    virtual const BOX2I ViewBBox() const override;
+
+    virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;
+
+    double ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const override;
+
+#if defined(DEBUG)
+    virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
+#endif
+
+private:
+    VECTOR2I  m_Pos0;           ///< text coordinates relative to the footprint anchor, orient 0.
+                                ///< text coordinate ref point is the text center
+};
+
+#endif // FP_TEXTBOX_H
diff --git a/pcbnew/kicad_clipboard.cpp b/pcbnew/kicad_clipboard.cpp
index e44d271968..c13a25ecbb 100644
--- a/pcbnew/kicad_clipboard.cpp
+++ b/pcbnew/kicad_clipboard.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2017 KiCad Developers, see AUTHORS.TXT for contributors.
- * Copyright (C) 2017-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2017-2022 KiCad Developers, see AUTHORS.txt for contributors.
  * @author Kristoffer Ödmark
  *
  * This program is free software; you can redistribute it and/or
@@ -32,7 +32,9 @@
 #include <pcb_group.h>
 #include <pcb_shape.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
 #include <fp_text.h>
+#include <fp_textbox.h>
 #include <zone.h>
 #include <locale_io.h>
 #include <netinfo.h>
@@ -214,6 +216,18 @@ void CLIPBOARD_IO::SaveSelection( const PCB_SELECTION& aSelected, bool isFootpri
                 pcb_text->SetLayer( fp_text->GetLayer() );
                 copy = pcb_text;
             }
+            else if( item->Type() == PCB_FP_TEXTBOX_T )
+            {
+                // Convert to PCB_TEXTBOX_T
+                FP_TEXTBOX*  fp_textbox = static_cast<FP_TEXTBOX*>( item );
+                PCB_TEXTBOX* pcb_textbox = new PCB_TEXTBOX( m_board );
+
+                pcb_textbox->CopyText( *fp_textbox );
+
+                pcb_textbox->SetAttributes( *fp_textbox );
+                pcb_textbox->SetLayer( fp_textbox->GetLayer() );
+                copy = pcb_textbox;
+            }
             else if( item->Type() == PCB_PAD_T )
             {
                 // Create a parent to own the copied pad
diff --git a/pcbnew/menubar_footprint_editor.cpp b/pcbnew/menubar_footprint_editor.cpp
index 426a6e35e4..fbba74543f 100644
--- a/pcbnew/menubar_footprint_editor.cpp
+++ b/pcbnew/menubar_footprint_editor.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2015 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -186,6 +186,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateMenuBar()
     placeMenu->Add( PCB_ACTIONS::drawCircle );
     placeMenu->Add( PCB_ACTIONS::drawPolygon );
     placeMenu->Add( PCB_ACTIONS::placeText );
+    placeMenu->Add( PCB_ACTIONS::drawTextBox );
 
     placeMenu->AppendSeparator();
     placeMenu->Add( PCB_ACTIONS::drawAlignedDimension );
diff --git a/pcbnew/menubar_pcb_editor.cpp b/pcbnew/menubar_pcb_editor.cpp
index d472ac056c..cb8794c840 100644
--- a/pcbnew/menubar_pcb_editor.cpp
+++ b/pcbnew/menubar_pcb_editor.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -322,6 +322,7 @@ void PCB_EDIT_FRAME::ReCreateMenuBar()
     placeMenu->Add( PCB_ACTIONS::drawCircle );
     placeMenu->Add( PCB_ACTIONS::drawPolygon );
     placeMenu->Add( PCB_ACTIONS::placeText );
+    placeMenu->Add( PCB_ACTIONS::drawTextBox );
 
     placeMenu->AppendSeparator();
     placeMenu->Add( PCB_ACTIONS::drawAlignedDimension );
diff --git a/pcbnew/pcb_base_edit_frame.h b/pcbnew/pcb_base_edit_frame.h
index fa701383f6..8b8b3d028e 100644
--- a/pcbnew/pcb_base_edit_frame.h
+++ b/pcbnew/pcb_base_edit_frame.h
@@ -167,6 +167,7 @@ public:
     //void SetRotationAngle( EDA_ANGLE aRotationAngle );
 
     void ShowTextPropertiesDialog( BOARD_ITEM* aText );
+    int ShowTextBoxPropertiesDialog( BOARD_ITEM* aText );
     void ShowGraphicItemPropertiesDialog( BOARD_ITEM* aItem );
 
     ///< @copydoc EDA_DRAW_FRAME::UseGalCanvas()
diff --git a/pcbnew/pcb_base_frame.cpp b/pcbnew/pcb_base_frame.cpp
index 20ee74bacb..3451d2bfb8 100644
--- a/pcbnew/pcb_base_frame.cpp
+++ b/pcbnew/pcb_base_frame.cpp
@@ -360,7 +360,9 @@ void PCB_BASE_FRAME::FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer )
 
         case PCB_SHAPE_T:
         case PCB_TEXT_T:
+        case PCB_TEXTBOX_T:
         case PCB_FP_TEXT_T:
+        case PCB_FP_TEXTBOX_T:
         case PCB_FP_SHAPE_T:
         case PCB_FP_ZONE_T:
         case PCB_TRACE_T:
diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp
index 90e960b6cb..f6b4442ff5 100644
--- a/pcbnew/pcb_edit_frame.cpp
+++ b/pcbnew/pcb_edit_frame.cpp
@@ -815,6 +815,7 @@ void PCB_EDIT_FRAME::setupUIConditions()
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawArc );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawPolygon );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::placeText );
+    CURRENT_EDIT_TOOL( PCB_ACTIONS::drawTextBox );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawAlignedDimension );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawOrthogonalDimension );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::drawCenterDimension );
diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp
index b146668324..10288eaa79 100644
--- a/pcbnew/pcb_painter.cpp
+++ b/pcbnew/pcb_painter.cpp
@@ -30,15 +30,16 @@
 #include <pcb_track.h>
 #include <pcb_group.h>
 #include <footprint.h>
+#include <fp_textbox.h>
 #include <pad.h>
 #include <pcb_shape.h>
 #include <string_utils.h>
 #include <zone.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
 #include <pcb_marker.h>
 #include <pcb_dimension.h>
 #include <pcb_target.h>
-#include <advanced_config.h>
 
 #include <layer_ids.h>
 #include <pcb_painter.h>
@@ -460,10 +461,18 @@ bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
         draw( static_cast<const PCB_TEXT*>( item ), aLayer );
         break;
 
+    case PCB_TEXTBOX_T:
+        draw( static_cast<const PCB_TEXTBOX*>( item ), aLayer );
+        break;
+
     case PCB_FP_TEXT_T:
         draw( static_cast<const FP_TEXT*>( item ), aLayer );
         break;
 
+    case PCB_FP_TEXTBOX_T:
+        draw( static_cast<const FP_TEXTBOX*>( item ), aLayer );
+        break;
+
     case PCB_FOOTPRINT_T:
         draw( static_cast<const FOOTPRINT*>( item ), aLayer );
         break;
@@ -1603,6 +1612,66 @@ void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer )
 }
 
 
+void PCB_PAINTER::draw( const PCB_TEXTBOX* aTextBox, int aLayer )
+{
+    const COLOR4D& color = m_pcbSettings.GetColor( aTextBox, aTextBox->GetLayer() );
+    int            thickness = getLineThickness( aTextBox->GetWidth() );
+    PLOT_DASH_TYPE lineStyle = aTextBox->GetStroke().GetPlotStyle();
+
+    m_gal->SetFillColor( color );
+    m_gal->SetStrokeColor( color );
+    m_gal->SetIsFill( true );
+    m_gal->SetIsStroke( false );
+
+    if( lineStyle <= PLOT_DASH_TYPE::FIRST_TYPE )
+    {
+        if( thickness > 0 )
+        {
+            std::vector<VECTOR2I> pts = aTextBox->GetCorners();
+
+            for( size_t ii = 0; ii < pts.size(); ++ii )
+                m_gal->DrawSegment( pts[ ii ], pts[ (ii + 1) % pts.size() ], thickness );
+        }
+    }
+    else
+    {
+        std::vector<SHAPE*> shapes = aTextBox->MakeEffectiveShapes( true );
+
+        for( SHAPE* shape : shapes )
+        {
+            STROKE_PARAMS::Stroke( shape, lineStyle, thickness, &m_pcbSettings,
+                                   [&]( const VECTOR2I& a, const VECTOR2I& b )
+                                   {
+                                       m_gal->DrawSegment( a, b, thickness );
+                                   } );
+        }
+
+        for( SHAPE* shape : shapes )
+            delete shape;
+    }
+
+    wxString resolvedText( aTextBox->GetShownText() );
+
+    if( resolvedText.Length() == 0 )
+        return;
+
+    TEXT_ATTRIBUTES attrs = aTextBox->GetAttributes();
+    attrs.m_StrokeWidth = getLineThickness( aTextBox->GetEffectiveTextPenWidth() );
+
+    std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aTextBox->GetRenderCache( resolvedText );
+
+    if( cache )
+    {
+        for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *cache )
+            m_gal->DrawGlyph( *glyph.get() );
+    }
+    else
+    {
+        strokeText( resolvedText, aTextBox->GetDrawPos(), attrs );
+    }
+}
+
+
 void PCB_PAINTER::draw( const FP_TEXT* aText, int aLayer )
 {
     wxString resolvedText( aText->GetShownText() );
@@ -1647,6 +1716,67 @@ void PCB_PAINTER::draw( const FP_TEXT* aText, int aLayer )
 }
 
 
+void PCB_PAINTER::draw( const FP_TEXTBOX* aTextBox, int aLayer )
+{
+    const COLOR4D& color = m_pcbSettings.GetColor( aTextBox, aTextBox->GetLayer() );
+    int            thickness = getLineThickness( aTextBox->GetWidth() );
+    PLOT_DASH_TYPE lineStyle = aTextBox->GetStroke().GetPlotStyle();
+
+    m_gal->SetFillColor( color );
+    m_gal->SetStrokeColor( color );
+    m_gal->SetIsFill( true );
+    m_gal->SetIsStroke( false );
+
+    if( lineStyle <= PLOT_DASH_TYPE::FIRST_TYPE )
+    {
+        if( thickness > 0 )
+        {
+            std::vector<VECTOR2I> pts = aTextBox->GetCorners();
+
+            for( size_t ii = 0; ii < pts.size(); ++ii )
+                m_gal->DrawSegment( pts[ ii ], pts[ (ii + 1) % pts.size() ], thickness );
+        }
+    }
+    else
+    {
+        std::vector<SHAPE*> shapes = aTextBox->MakeEffectiveShapes( true );
+
+        for( SHAPE* shape : shapes )
+        {
+            STROKE_PARAMS::Stroke( shape, lineStyle, thickness, &m_pcbSettings,
+                                   [&]( const VECTOR2I& a, const VECTOR2I& b )
+                                   {
+                                       m_gal->DrawSegment( a, b, thickness );
+                                   } );
+        }
+
+        for( SHAPE* shape : shapes )
+            delete shape;
+    }
+
+    wxString resolvedText( aTextBox->GetShownText() );
+
+    if( resolvedText.Length() == 0 )
+        return;
+
+    TEXT_ATTRIBUTES attrs = aTextBox->GetAttributes();
+    attrs.m_Angle = aTextBox->GetDrawRotation();
+    attrs.m_StrokeWidth = getLineThickness( aTextBox->GetEffectiveTextPenWidth() );
+
+    std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = aTextBox->GetRenderCache( resolvedText );
+
+    if( cache )
+    {
+        for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *cache )
+            m_gal->DrawGlyph( *glyph.get() );
+    }
+    else
+    {
+        strokeText( resolvedText, aTextBox->GetDrawPos(), attrs );
+    }
+}
+
+
 void PCB_PAINTER::draw( const FOOTPRINT* aFootprint, int aLayer )
 {
     if( aLayer == LAYER_ANCHOR )
@@ -1866,7 +1996,6 @@ void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
     // Draw text
     const PCB_TEXT& text = aDimension->Text();
     wxString        resolvedText = text.GetShownText();
-    VECTOR2D        position( text.GetTextPos().x, text.GetTextPos().y );
     TEXT_ATTRIBUTES attrs = text.GetAttributes();
 
     if( outline_mode )
@@ -1883,7 +2012,7 @@ void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
     }
     else
     {
-        strokeText( resolvedText, position, attrs );
+        strokeText( resolvedText, text.GetTextPos(), attrs );
     }
 }
 
diff --git a/pcbnew/pcb_painter.h b/pcbnew/pcb_painter.h
index 0c1e9933de..837b68ce0c 100644
--- a/pcbnew/pcb_painter.h
+++ b/pcbnew/pcb_painter.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2013 CERN
- * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors.
  *
  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  * @author Maciej Suminski <maciej.suminski@cern.ch>
@@ -47,7 +47,9 @@ class PCB_GROUP;
 class FOOTPRINT;
 class ZONE;
 class PCB_TEXT;
+class PCB_TEXTBOX;
 class FP_TEXT;
+class FP_TEXTBOX;
 class PCB_DIMENSION_BASE;
 class PCB_TARGET;
 class PCB_MARKER;
@@ -170,7 +172,9 @@ protected:
     void draw( const PAD* aPad, int aLayer );
     void draw( const PCB_SHAPE* aSegment, int aLayer );
     void draw( const PCB_TEXT* aText, int aLayer );
+    void draw( const PCB_TEXTBOX* aText, int aLayer );
     void draw( const FP_TEXT* aText, int aLayer );
+    void draw( const FP_TEXTBOX* aText, int aLayer );
     void draw( const FOOTPRINT* aFootprint, int aLayer );
     void draw( const PCB_GROUP* aGroup, int aLayer );
     void draw( const ZONE* aZone, int aLayer );
diff --git a/pcbnew/pcb_shape.cpp b/pcbnew/pcb_shape.cpp
index 6dfeaddeaa..1e467e5c15 100644
--- a/pcbnew/pcb_shape.cpp
+++ b/pcbnew/pcb_shape.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -31,11 +31,11 @@
 #include <base_units.h>
 #include <geometry/shape_compound.h>
 #include <pcb_shape.h>
+#include "macros.h"
 
-
-PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T idtype, SHAPE_T shapetype ) :
-    BOARD_ITEM( aParent, idtype ),
-    EDA_SHAPE( shapetype, Millimeter2iu( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL, false )
+PCB_SHAPE::PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T aItemType, SHAPE_T aShapeType ) :
+    BOARD_ITEM( aParent, aItemType ),
+    EDA_SHAPE( aShapeType, Millimeter2iu( DEFAULT_LINE_WIDTH ), FILL_T::NO_FILL, false )
 {
 }
 
@@ -94,6 +94,31 @@ const VECTOR2I PCB_SHAPE::GetFocusPosition() const
 }
 
 
+std::vector<VECTOR2I> PCB_SHAPE::GetCorners() const
+{
+    std::vector<VECTOR2I> pts;
+
+    if( GetShape() == SHAPE_T::RECT )
+    {
+        pts = GetRectCorners();
+    }
+    else if( GetShape() == SHAPE_T::POLY )
+    {
+        for( const VECTOR2I& pt : GetPolyShape().Outline( 0 ).CPoints() )
+            pts.emplace_back( pt );
+    }
+    else
+    {
+        UNIMPLEMENTED_FOR( SHAPE_T_asString() );
+    }
+
+    while( pts.size() < 4 )
+        pts.emplace_back( pts.back() + VECTOR2I( 10, 10 ) );
+
+    return pts;
+}
+
+
 void PCB_SHAPE::Move( const VECTOR2I& aMoveVector )
 {
     move( aMoveVector );
diff --git a/pcbnew/pcb_shape.h b/pcbnew/pcb_shape.h
index 1519733983..786af5da8b 100644
--- a/pcbnew/pcb_shape.h
+++ b/pcbnew/pcb_shape.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2018 Jean-Pierre Charras jp.charras at wanadoo.fr
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -38,9 +38,9 @@ class MSG_PANEL_ITEM;
 class PCB_SHAPE : public BOARD_ITEM, public EDA_SHAPE
 {
 public:
-    PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T idtype, SHAPE_T shapetype );
+    PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T aItemType, SHAPE_T aShapeType );
 
-    PCB_SHAPE( BOARD_ITEM* aParent = NULL, SHAPE_T shapetype = SHAPE_T::SEGMENT );
+    PCB_SHAPE( BOARD_ITEM* aParent = NULL, SHAPE_T aShapeType = SHAPE_T::SEGMENT );
 
     // Do not create a copy constructor & operator=.
     // The ones generated by the compiler are adequate.
@@ -83,6 +83,12 @@ public:
     STROKE_PARAMS GetStroke() const override { return m_stroke; }
     void SetStroke( const STROKE_PARAMS& aStroke ) override { m_stroke = aStroke; }
 
+    /**
+     * Return 4 corners for a rectangle or rotated rectangle (stored as a poly).  Unimplemented
+     * for other shapes.
+     */
+    std::vector<VECTOR2I> GetCorners() const;
+
     /**
      * Allows items to return their visual center rather than their anchor. For some shapes this
      * is similar to GetCenter(), but for unfilled shapes a point on the outline is better.
diff --git a/pcbnew/pcb_text.cpp b/pcbnew/pcb_text.cpp
index 80a560d1b9..7c890210dd 100644
--- a/pcbnew/pcb_text.cpp
+++ b/pcbnew/pcb_text.cpp
@@ -23,7 +23,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#include <eda_item.h>
 #include <pcb_edit_frame.h>
 #include <base_units.h>
 #include <bitmaps.h>
diff --git a/pcbnew/pcb_text.h b/pcbnew/pcb_text.h
index 7670eafc94..54a446e9da 100644
--- a/pcbnew/pcb_text.h
+++ b/pcbnew/pcb_text.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
- * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -112,7 +112,7 @@ public:
 
     wxString GetClass() const override
     {
-        return wxT( "PTEXT" );
+        return wxT( "PCB_TEXT" );
     }
 
     /**
diff --git a/pcbnew/pcb_textbox.cpp b/pcbnew/pcb_textbox.cpp
new file mode 100644
index 0000000000..45a4b03da2
--- /dev/null
+++ b/pcbnew/pcb_textbox.cpp
@@ -0,0 +1,340 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2022 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 <pcb_edit_frame.h>
+#include <base_units.h>
+#include <bitmaps.h>
+#include <board.h>
+#include <board_design_settings.h>
+#include <footprint.h>
+#include <pcb_textbox.h>
+#include <pcb_painter.h>
+#include <trigo.h>
+#include <string_utils.h>
+#include <geometry/shape_compound.h>
+#include <callback_gal.h>
+#include <convert_basic_shapes_to_polygon.h>
+#include "macros.h"
+
+using KIGFX::PCB_RENDER_SETTINGS;
+
+
+PCB_TEXTBOX::PCB_TEXTBOX( BOARD_ITEM* parent ) :
+    PCB_SHAPE( parent, PCB_TEXTBOX_T, SHAPE_T::RECT ),
+    EDA_TEXT()
+{
+    SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
+    SetVertJustify( GR_TEXT_V_ALIGN_TOP );
+    SetMultilineAllowed( true );
+}
+
+
+PCB_TEXTBOX::~PCB_TEXTBOX()
+{
+}
+
+
+int PCB_TEXTBOX::GetTextMargin() const
+{
+    return KiROUND( GetTextSize().y * 0.8 );
+}
+
+
+std::vector<VECTOR2I> PCB_TEXTBOX::GetAnchorAndOppositeCorner() const
+{
+    EDA_ANGLE epsilon( 1.0, DEGREES_T );    // Yeah, it's a pretty coarse epsilon, but anything
+                                            // under 45° will work for our purposes.
+
+    std::vector<VECTOR2I> pts;
+    std::vector<VECTOR2I> corners = GetCorners();
+
+    EDA_ANGLE textAngle = GetDrawRotation().Normalize();
+    EDA_ANGLE toCorner1 = EDA_ANGLE( corners[1] - corners[0] ).Normalize();
+    EDA_ANGLE fromCorner1 = ( toCorner1 + ANGLE_180 ).Normalize();
+
+    pts.emplace_back( corners[0] );
+
+    if( std::abs( toCorner1 - textAngle ) < epsilon || std::abs( fromCorner1 - textAngle ) < epsilon )
+        pts.emplace_back( corners[1] );
+    else
+        pts.emplace_back( corners[3] );
+
+    return pts;
+}
+
+
+VECTOR2I PCB_TEXTBOX::GetDrawPos() const
+{
+    std::vector<VECTOR2I> corners = GetAnchorAndOppositeCorner();
+    GR_TEXT_H_ALIGN_T     effectiveAlignment = GetHorizJustify();
+    VECTOR2I              textAnchor;
+    VECTOR2I              offset;
+
+    if( IsMirrored() )
+    {
+        switch( GetHorizJustify() )
+        {
+        case GR_TEXT_H_ALIGN_LEFT:   effectiveAlignment = GR_TEXT_H_ALIGN_RIGHT;  break;
+        case GR_TEXT_H_ALIGN_CENTER: effectiveAlignment = GR_TEXT_H_ALIGN_CENTER; break;
+        case GR_TEXT_H_ALIGN_RIGHT:  effectiveAlignment = GR_TEXT_H_ALIGN_LEFT;   break;
+        }
+    }
+
+    switch( effectiveAlignment )
+    {
+    case GR_TEXT_H_ALIGN_LEFT:
+        textAnchor = corners[0];
+        offset = VECTOR2I( GetTextMargin(), GetTextMargin() );
+        break;
+    case GR_TEXT_H_ALIGN_CENTER:
+        textAnchor = ( corners[0] + corners[1] ) / 2;
+        offset = VECTOR2I( 0, GetTextMargin() );
+        break;
+    case GR_TEXT_H_ALIGN_RIGHT:
+        textAnchor = corners[1];
+        offset = VECTOR2I( -GetTextMargin(), GetTextMargin() );
+        break;
+    }
+
+    RotatePoint( offset, GetDrawRotation() );
+    return textAnchor + offset;
+}
+
+
+wxString PCB_TEXTBOX::GetShownText( int aDepth ) const
+{
+    BOARD* board = dynamic_cast<BOARD*>( GetParent() );
+
+    std::function<bool( wxString* )> pcbTextResolver =
+            [&]( wxString* token ) -> bool
+            {
+                if( token->IsSameAs( wxT( "LAYER" ) ) )
+                {
+                    *token = GetLayerName();
+                    return true;
+                }
+
+                if( token->Contains( ':' ) )
+                {
+                    wxString      remainder;
+                    wxString      ref = token->BeforeFirst( ':', &remainder );
+                    BOARD_ITEM*   refItem = board->GetItem( KIID( ref ) );
+
+                    if( refItem && refItem->Type() == PCB_FOOTPRINT_T )
+                    {
+                        FOOTPRINT* refFP = static_cast<FOOTPRINT*>( refItem );
+
+                        if( refFP->ResolveTextVar( &remainder, aDepth + 1 ) )
+                        {
+                            *token = remainder;
+                            return true;
+                        }
+                    }
+                }
+                return false;
+            };
+
+    std::function<bool( wxString* )> boardTextResolver =
+            [&]( wxString* token ) -> bool
+            {
+                return board->ResolveTextVar( token, aDepth + 1 );
+            };
+
+    wxString text = EDA_TEXT::GetShownText();
+
+    if( board && HasTextVars() && aDepth < 10 )
+        text = ExpandTextVars( text, &pcbTextResolver, &boardTextResolver, board->GetProject() );
+
+    KIFONT::FONT*         font = GetDrawFont();
+    std::vector<VECTOR2I> corners = GetAnchorAndOppositeCorner();
+    int                   colWidth = ( corners[1] - corners[0] ).EuclideanNorm();
+
+    colWidth -= GetTextMargin() * 2;
+    font->LinebreakText( text, colWidth, GetTextSize(), GetTextThickness(), IsBold(), IsItalic() );
+
+    return text;
+}
+
+
+void PCB_TEXTBOX::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
+{
+    EDA_UNITS units = aFrame->GetUserUnits();
+
+    // Don't use GetShownText() here; we want to show the user the variable references
+    aList.emplace_back( _( "Text Box" ), UnescapeString( GetText() ) );
+
+    if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
+        aList.emplace_back( _( "Status" ), _( "Locked" ) );
+
+    aList.emplace_back( _( "Layer" ), GetLayerName() );
+    aList.emplace_back( _( "Mirror" ), IsMirrored() ? _( "Yes" ) : _( "No" ) );
+    aList.emplace_back( _( "Angle" ), wxString::Format( "%g", GetTextAngle().AsDegrees() ) );
+
+    aList.emplace_back( _( "Thickness" ), MessageTextFromValue( units, GetTextThickness() ) );
+    aList.emplace_back( _( "Width" ), MessageTextFromValue( units, GetTextWidth() ) );
+    aList.emplace_back( _( "Height" ), MessageTextFromValue( units, GetTextHeight() ) );
+}
+
+
+void PCB_TEXTBOX::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
+{
+    PCB_SHAPE::Rotate( aRotCentre, aAngle );
+    SetTextAngle( GetTextAngle() + aAngle );
+}
+
+
+void PCB_TEXTBOX::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+{
+    if( aFlipLeftRight )
+    {
+        m_start.x = aCentre.x - ( m_start.x - aCentre.x );
+        m_end.x   = aCentre.x - ( m_end.x - aCentre.x );
+        SetTextAngle( -GetTextAngle() );
+    }
+    else
+    {
+        m_start.y = aCentre.y - ( m_start.y - aCentre.y );
+        m_end.y   = aCentre.y - ( m_end.y - aCentre.y );
+        SetTextAngle( ANGLE_180 - GetTextAngle() );
+    }
+
+    SetLayer( FlipLayer( GetLayer(), GetBoard()->GetCopperLayerCount() ) );
+    SetMirrored( !IsMirrored() );
+}
+
+
+bool PCB_TEXTBOX::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
+{
+    EDA_RECT rect = GetBoundingBox();
+
+    rect.Inflate( aAccuracy );
+
+    return rect.Contains( aPosition );
+}
+
+
+bool PCB_TEXTBOX::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
+{
+    EDA_RECT rect = aRect;
+
+    rect.Inflate( aAccuracy );
+
+    if( aContained )
+        return rect.Contains( GetBoundingBox() );
+
+    return rect.Intersects( GetBoundingBox() );
+}
+
+
+wxString PCB_TEXTBOX::GetSelectMenuText( EDA_UNITS aUnits ) const
+{
+    return wxString::Format( _( "PCB Text Box on %s"), GetLayerName() );
+}
+
+
+BITMAPS PCB_TEXTBOX::GetMenuImage() const
+{
+    return BITMAPS::add_textbox;
+}
+
+
+EDA_ITEM* PCB_TEXTBOX::Clone() const
+{
+    return new PCB_TEXTBOX( *this );
+}
+
+
+void PCB_TEXTBOX::SwapData( BOARD_ITEM* aImage )
+{
+    assert( aImage->Type() == PCB_TEXTBOX_T );
+
+    std::swap( *((PCB_TEXTBOX*) this), *((PCB_TEXTBOX*) aImage) );
+}
+
+
+std::shared_ptr<SHAPE> PCB_TEXTBOX::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
+{
+    if( PCB_SHAPE::GetStroke().GetWidth() >= 0 )
+        return PCB_SHAPE::GetEffectiveShape( aLayer );
+    else
+        return GetEffectiveTextShape();
+}
+
+
+void PCB_TEXTBOX::TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
+                                                         PCB_LAYER_ID aLayer, int aClearance,
+                                                         int aError, ERROR_LOC aErrorLoc ) const
+{
+    KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
+    KIFONT::FONT*              font = GetDrawFont();
+    int                        penWidth = GetEffectiveTextPenWidth();
+
+    CALLBACK_GAL callback_gal( empty_opts,
+            // Stroke callback
+            [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
+            {
+                TransformOvalToPolygon( aCornerBuffer, aPt1, aPt2, penWidth+ ( 2 * aClearance ),
+                                        aError, ERROR_INSIDE );
+            },
+            // Triangulation callback
+            [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2, const VECTOR2I& aPt3 )
+            {
+                aCornerBuffer.NewOutline();
+
+                for( const VECTOR2I& point : { aPt1, aPt2, aPt3 } )
+                    aCornerBuffer.Append( point.x, point.y );
+            } );
+
+    font->Draw( &callback_gal, GetShownText(), GetTextPos(), GetAttributes() );
+}
+
+
+void PCB_TEXTBOX::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
+                                                     PCB_LAYER_ID aLayer, int aClearance,
+                                                     int aError, ERROR_LOC aErrorLoc,
+                                                     bool aIgnoreLineWidth ) const
+{
+    if( PCB_SHAPE::GetStroke().GetWidth() >= 0 )
+    {
+        PCB_SHAPE::TransformShapeWithClearanceToPolygon( aCornerBuffer, aLayer, aClearance,
+                                                         aError, aErrorLoc );
+    }
+    else
+    {
+        EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon( &aCornerBuffer, aClearance );
+    }
+}
+
+
+static struct PCB_TEXTBOX_DESC
+{
+    PCB_TEXTBOX_DESC()
+    {
+        PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
+        REGISTER_TYPE( PCB_TEXTBOX );
+        propMgr.AddTypeCast( new TYPE_CAST<PCB_TEXTBOX, PCB_SHAPE> );
+        propMgr.AddTypeCast( new TYPE_CAST<PCB_TEXTBOX, EDA_TEXT> );
+        propMgr.InheritsAfter( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( PCB_SHAPE ) );
+        propMgr.InheritsAfter( TYPE_HASH( PCB_TEXTBOX ), TYPE_HASH( EDA_TEXT ) );
+    }
+} _PCB_TEXTBOX_DESC;
diff --git a/pcbnew/pcb_textbox.h b/pcbnew/pcb_textbox.h
new file mode 100644
index 0000000000..daa947967b
--- /dev/null
+++ b/pcbnew/pcb_textbox.h
@@ -0,0 +1,127 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
+ * Copyright (C) 1992-2022 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_TEXTBOX_H
+#define PCB_TEXTBOX_H
+
+#include <eda_text.h>
+#include <pcb_shape.h>
+
+
+class LINE_READER;
+class MSG_PANEL_ITEM;
+
+
+class PCB_TEXTBOX : public PCB_SHAPE, public EDA_TEXT
+{
+public:
+    PCB_TEXTBOX( BOARD_ITEM* parent );
+
+    // Do not create a copy constructor & operator=.
+    // The ones generated by the compiler are adequate.
+
+    ~PCB_TEXTBOX();
+
+    static inline bool ClassOf( const EDA_ITEM* aItem )
+    {
+        return aItem && PCB_TEXTBOX_T == aItem->Type();
+    }
+
+    bool IsType( const KICAD_T aScanTypes[] ) const override
+    {
+        if( BOARD_ITEM::IsType( aScanTypes ) )
+            return true;
+
+        for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
+        {
+            if( *p == PCB_LOCATE_TEXT_T )
+                return true;
+        }
+
+        return false;
+    }
+
+    int GetTextMargin() const;
+
+    VECTOR2I GetDrawPos() const override;
+
+    wxString GetShownText( int aDepth = 0 ) const override;
+
+    /// PCB_TEXTBOXes are always visible:
+    void SetVisible( bool aVisible ) override { /* do nothing */}
+    bool IsVisible() const override { return true; }
+
+    bool Matches( const wxFindReplaceData& aSearchData, void* aAuxData ) const override
+    {
+        return BOARD_ITEM::Matches( GetShownText(), aSearchData );
+    }
+
+    std::vector<VECTOR2I> GetAnchorAndOppositeCorner() const;
+
+    void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
+
+    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+
+    void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
+
+    bool HitTest( const VECTOR2I& aPosition, int aAccuracy ) const override;
+
+    bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override;
+
+    wxString GetClass() const override
+    {
+        return wxT( "PCB_TEXTBOX" );
+    }
+
+    /**
+     * Function TransformTextShapeWithClearanceToPolygon
+     * Convert the text to a polygonSet describing the actual character strokes (one per segment).
+     * Used in 3D viewer
+     * Circles and arcs are approximated by segments
+     * @aCornerBuffer = SHAPE_POLY_SET to store the polygon corners
+     * @aClearanceValue = the clearance around the text
+     * @aError = the maximum error to allow when approximating curves
+     */
+    void TransformTextShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
+                                                   PCB_LAYER_ID aLayer, int aClearanceValue,
+                                                   int aError, ERROR_LOC aErrorLoc ) const;
+
+    void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, PCB_LAYER_ID aLayer,
+                                               int aClearanceValue, int aError, ERROR_LOC aErrorLoc,
+                                               bool aIgnoreLineWidth = false ) const override;
+
+    // @copydoc BOARD_ITEM::GetEffectiveShape
+    virtual std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER ) const override;
+
+    wxString GetSelectMenuText( EDA_UNITS aUnits ) const override;
+
+    BITMAPS GetMenuImage() const override;
+
+    // Virtual function
+    EDA_ITEM* Clone() const override;
+
+    virtual void SwapData( BOARD_ITEM* aImage ) override;
+};
+
+#endif  // #define PCB_TEXTBOX_H
diff --git a/pcbnew/pcbplot.h b/pcbnew/pcbplot.h
index 070632f9a2..b3729afc30 100644
--- a/pcbnew/pcbplot.h
+++ b/pcbnew/pcbplot.h
@@ -79,7 +79,7 @@ public:
     // Basic functions to plot a board item
     void SetLayerSet( LSET aLayerMask ) { m_layerMask = aLayerMask; }
     void PlotFootprintGraphicItems( const FOOTPRINT* aFootprint );
-    void PlotFootprintGraphicItem( const FP_SHAPE* aShape );
+    void PlotFootprintShape( const FP_SHAPE* aShape );
     void PlotFootprintTextItem( const FP_TEXT* aText, const COLOR4D& aColor );
 
     /*
@@ -91,7 +91,7 @@ public:
     void PlotDimension( const PCB_DIMENSION_BASE* aDim );
     void PlotPcbTarget( const PCB_TARGET* aMire );
     void PlotFilledAreas( const ZONE* aZone, const SHAPE_POLY_SET& aPolysList );
-    void PlotPcbText( const PCB_TEXT* aText );
+    void PlotPcbText( const EDA_TEXT* aText, PCB_LAYER_ID aLayer );
     void PlotPcbShape( const PCB_SHAPE* aShape );
 
     /**
diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp
index 8a0d2fdc42..b3f0bc4a80 100644
--- a/pcbnew/plot_board_layers.cpp
+++ b/pcbnew/plot_board_layers.cpp
@@ -873,7 +873,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
                 }
                 else if( item->Type() == PCB_FP_SHAPE_T && item->IsOnLayer( Edge_Cuts ) )
                 {
-                   itemplotter.PlotFootprintGraphicItem( static_cast<const FP_SHAPE*>( item ) );
+                   itemplotter.PlotFootprintShape( static_cast<const FP_SHAPE*>( item ) );
                 }
             }
         }
diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp
index de964d4477..5e326e01dd 100644
--- a/pcbnew/plot_brditems_plotter.cpp
+++ b/pcbnew/plot_brditems_plotter.cpp
@@ -55,10 +55,12 @@
 #include <fp_shape.h>
 #include <footprint.h>
 #include <fp_text.h>
+#include <fp_textbox.h>
 #include <pcb_track.h>
 #include <pad.h>
 #include <pcb_target.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
 #include <zone.h>
 
 #include <wx/debug.h>                         // for wxASSERT_MSG
@@ -350,7 +352,12 @@ void BRDITEMS_PLOTTER::PlotPcbGraphicItem( const BOARD_ITEM* item )
         break;
 
     case PCB_TEXT_T:
-        PlotPcbText( static_cast<const PCB_TEXT*>( item ) );
+        PlotPcbText( static_cast<const PCB_TEXT*>( item ), item->GetLayer() );
+        break;
+
+    case PCB_TEXTBOX_T:
+        PlotPcbText( static_cast<const PCB_TEXTBOX*>( item ), item->GetLayer() );
+        PlotPcbShape( static_cast<const PCB_TEXTBOX*>( item ) );
         break;
 
     case PCB_DIM_ALIGNED_T:
@@ -434,7 +441,7 @@ void BRDITEMS_PLOTTER::PlotDimension( const PCB_DIMENSION_BASE* aDim )
     // the white items are not seen on a white paper or screen
     m_plotter->SetColor( color != WHITE ? color : LIGHTGRAY);
 
-    PlotPcbText( &aDim->Text() );
+    PlotPcbText( &aDim->Text(), aDim->GetLayer() );
 
     for( const std::shared_ptr<SHAPE>& shape : aDim->GetShapes() )
     {
@@ -534,25 +541,57 @@ void BRDITEMS_PLOTTER::PlotFootprintGraphicItems( const FOOTPRINT* aFootprint )
         if( aFootprint->GetPrivateLayers().test( item->GetLayer() ) )
             continue;
 
-        if( item->Type() == PCB_FP_SHAPE_T )
+        switch( item->Type() )
+        {
+        case PCB_FP_SHAPE_T:
         {
             const FP_SHAPE* shape = static_cast<const FP_SHAPE*>( item );
 
             if( m_layerMask[ shape->GetLayer() ] )
-                PlotFootprintGraphicItem( shape );
+                PlotFootprintShape( shape );
+
+            break;
         }
-        else if( BaseType( item->Type() ) == PCB_DIMENSION_T )
+
+        case PCB_FP_TEXTBOX_T:
+        {
+            const FP_TEXTBOX* textbox = static_cast<const FP_TEXTBOX*>( item );
+
+            if( m_layerMask[ textbox->GetLayer() ] )
+            {
+                PlotPcbText( textbox, textbox->GetLayer() );
+                PlotFootprintShape( textbox );
+            }
+
+            break;
+        }
+
+        case PCB_DIM_ALIGNED_T:
+        case PCB_DIM_CENTER_T:
+        case PCB_DIM_RADIAL_T:
+        case PCB_DIM_ORTHOGONAL_T:
+        case PCB_DIM_LEADER_T:
         {
             const PCB_DIMENSION_BASE* dimension = static_cast<const PCB_DIMENSION_BASE*>( item );
 
             if( m_layerMask[ dimension->GetLayer() ] )
                 PlotDimension( dimension );
+
+            break;
+        }
+
+        case PCB_FP_TEXT_T:
+            // Plotted in PlotFootprintTextItem()
+            break;
+
+        default:
+            UNIMPLEMENTED_FOR( item->GetClass() );
         }
     }
 }
 
 
-void BRDITEMS_PLOTTER::PlotFootprintGraphicItem( const FP_SHAPE* aShape )
+void BRDITEMS_PLOTTER::PlotFootprintShape( const FP_SHAPE* aShape )
 {
     if( aShape->Type() != PCB_FP_SHAPE_T )
         return;
@@ -735,7 +774,7 @@ void BRDITEMS_PLOTTER::PlotFootprintGraphicItem( const FP_SHAPE* aShape )
 }
 
 
-void BRDITEMS_PLOTTER::PlotPcbText( const PCB_TEXT* aText )
+void BRDITEMS_PLOTTER::PlotPcbText( const EDA_TEXT* aText, PCB_LAYER_ID aLayer )
 {
     wxString      shownText( aText->GetShownText() );
     KIFONT::FONT* font = aText->GetDrawFont();
@@ -743,15 +782,15 @@ void BRDITEMS_PLOTTER::PlotPcbText( const PCB_TEXT* aText )
     if( shownText.IsEmpty() )
         return;
 
-    if( !m_layerMask[aText->GetLayer()] )
+    if( !m_layerMask[aLayer] )
         return;
 
     GBR_METADATA gbr_metadata;
 
-    if( IsCopperLayer( aText->GetLayer() ) )
+    if( IsCopperLayer( aLayer ) )
         gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_NONCONDUCTOR );
 
-    COLOR4D color = getColor( aText->GetLayer() );
+    COLOR4D color = getColor( aLayer );
     m_plotter->SetColor( color );
 
     VECTOR2I size = aText->GetTextSize();
@@ -781,14 +820,14 @@ void BRDITEMS_PLOTTER::PlotPcbText( const PCB_TEXT* aText )
         for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
         {
             wxString& txt =  strings_list.Item( ii );
-            m_plotter->Text( positions[ii], color, txt, aText->GetTextAngle(), size,
+            m_plotter->Text( positions[ii], color, txt, aText->GetDrawRotation(), size,
                              aText->GetHorizJustify(), aText->GetVertJustify(), thickness,
                              aText->IsItalic(), allow_bold, false, font, &gbr_metadata );
         }
     }
     else
     {
-        m_plotter->Text( pos, color, shownText, aText->GetTextAngle(), size,
+        m_plotter->Text( pos, color, shownText, aText->GetDrawRotation(), size,
                          aText->GetHorizJustify(), aText->GetVertJustify(), thickness,
                          aText->IsItalic(), allow_bold, false, font, &gbr_metadata );
     }
diff --git a/pcbnew/plugins/kicad/pcb_parser.cpp b/pcbnew/plugins/kicad/pcb_parser.cpp
index 2622157b62..d010bfb5a7 100644
--- a/pcbnew/plugins/kicad/pcb_parser.cpp
+++ b/pcbnew/plugins/kicad/pcb_parser.cpp
@@ -35,19 +35,21 @@
 
 #include <board.h>
 #include <board_design_settings.h>
+#include <fp_shape.h>
+#include <fp_textbox.h>
 #include <pcb_dimension.h>
 #include <pcb_shape.h>
-#include <fp_shape.h>
 #include <pcb_group.h>
 #include <pcb_target.h>
+#include <pcb_track.h>
+#include <pcb_textbox.h>
+#include <pad.h>
+#include <zone.h>
 #include <footprint.h>
 #include <geometry/shape_line_chain.h>
 #include <font/font.h>
 #include <ignore.h>
 #include <netclass.h>
-#include <pad.h>
-#include <pcb_track.h>
-#include <zone.h>
 #include <plugins/kicad/pcb_plugin.h>
 #include <pcb_plot_params_parser.h>
 #include <pcb_plot_params.h>
@@ -849,6 +851,12 @@ BOARD* PCB_PARSER::parseBOARD_unchecked()
             bulkAddedItems.push_back( item );
             break;
 
+        case T_gr_text_box:
+            item = parsePCB_TEXTBOX();
+            m_board->Add( item, ADD_MODE::BULK_APPEND );
+            bulkAddedItems.push_back( item );
+            break;
+
         case T_dimension:
             item = parseDIMENSION( m_board, false );
             m_board->Add( item, ADD_MODE::BULK_APPEND );
@@ -1074,6 +1082,7 @@ void PCB_PARSER::resolveGroups( BOARD_ITEM* aParent )
                 {
                 // We used to allow fp items in non-footprint groups.  It was a mistake.
                 case PCB_FP_TEXT_T:
+                case PCB_FP_TEXTBOX_T:
                 case PCB_FP_SHAPE_T:
                 case PCB_FP_ZONE_T:
                 case PCB_PAD_T:
@@ -2810,9 +2819,10 @@ PCB_TEXT* PCB_PARSER::parsePCB_TEXT()
     T token;
 
     std::unique_ptr<PCB_TEXT> text = std::make_unique<PCB_TEXT>( m_board );
-    NeedSYMBOLorNUMBER();
 
+    NeedSYMBOLorNUMBER();
     text->SetText( FromUTF8() );
+
     NeedLEFT();
     token = NextTok();
 
@@ -2867,7 +2877,7 @@ PCB_TEXT* PCB_PARSER::parsePCB_TEXT()
             break;
 
         default:
-            Expecting( "layer, tstamp or effects" );
+            Expecting( "layer, effects, render_cache or tstamp" );
         }
     }
 
@@ -2875,6 +2885,108 @@ PCB_TEXT* PCB_PARSER::parsePCB_TEXT()
 }
 
 
+PCB_TEXTBOX* PCB_PARSER::parsePCB_TEXTBOX()
+{
+    wxCHECK_MSG( CurTok() == T_gr_text_box, nullptr,
+                 wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as PCB_TEXTBOX." ) );
+
+    std::unique_ptr<PCB_TEXTBOX> textbox = std::make_unique<PCB_TEXTBOX>( m_board );
+
+    T token = NextTok();
+
+    if( token == T_locked )
+    {
+        textbox->SetLocked( true );
+        token = NextTok();
+    }
+
+    if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
+        Expecting( "text value" );
+
+    textbox->SetText( FromUTF8() );
+
+    NeedLEFT();
+    token = NextTok();
+
+    if( token == T_start )
+    {
+        int x = parseBoardUnits( "X coordinate" );
+        int y = parseBoardUnits( "Y coordinate" );
+        textbox->SetStart( VECTOR2I( x, y ) );
+        NeedRIGHT();
+
+        NeedLEFT();
+        token = NextTok();
+
+        if( token != T_end )
+            Expecting( T_end );
+
+        x = parseBoardUnits( "X coordinate" );
+        y = parseBoardUnits( "Y coordinate" );
+        textbox->SetEnd( VECTOR2I( x, y ) );
+        NeedRIGHT();
+    }
+    else if( token == T_pts )
+    {
+        textbox->SetShape( SHAPE_T::POLY );
+        textbox->GetPolyShape().RemoveAllContours();
+        textbox->GetPolyShape().NewOutline();
+
+        while( (token = NextTok() ) != T_RIGHT )
+            parseOutlinePoints( textbox->GetPolyShape().Outline( 0 ) );
+    }
+    else
+    {
+        Expecting( "start or pts" );
+    }
+
+    for( token = NextTok(); token != T_RIGHT; token = NextTok() )
+    {
+        if( token != T_LEFT )
+            Expecting( T_LEFT );
+
+        token = NextTok();
+
+        switch( token )
+        {
+        case T_angle:
+            textbox->SetTextAngle( EDA_ANGLE( parseDouble( "text box angle" ), DEGREES_T ) );
+            NeedRIGHT();
+            break;
+
+        case T_width:
+            textbox->SetWidth( parseBoardUnits( "text box border width" ) );
+            NeedRIGHT();
+            break;
+
+        case T_layer:
+            textbox->SetLayer( parseBoardItemLayer() );
+            NeedRIGHT();
+            break;
+
+        case T_tstamp:
+            NextTok();
+            const_cast<KIID&>( textbox->m_Uuid ) = CurStrToKIID();
+            NeedRIGHT();
+            break;
+
+        case T_effects:
+            parseEDA_TEXT( static_cast<EDA_TEXT*>( textbox.get() ) );
+            break;
+
+        case T_render_cache:
+            parseRenderCache( static_cast<EDA_TEXT*>( textbox.get() ) );
+            break;
+
+        default:
+            Expecting( "angle, width, layer, effects, render_cache or tstamp" );
+        }
+    }
+
+    return textbox.release();
+}
+
+
 PCB_DIMENSION_BASE* PCB_PARSER::parseDIMENSION( BOARD_ITEM* aParent, bool aInFP )
 {
     wxCHECK_MSG( CurTok() == T_dimension, nullptr,
@@ -3590,6 +3702,15 @@ FOOTPRINT* PCB_PARSER::parseFOOTPRINT_unchecked( wxArrayString* aInitialComments
             break;
         }
 
+        case T_fp_text_box:
+        {
+            FP_TEXTBOX* textbox = parseFP_TEXTBOX();
+            textbox->SetParent( footprint.get() );
+            textbox->SetDrawCoord();
+            footprint->Add( textbox, ADD_MODE::APPEND );
+            break;
+        }
+
         case T_fp_arc:
         case T_fp_circle:
         case T_fp_curve:
@@ -3780,7 +3901,7 @@ FP_TEXT* PCB_PARSER::parseFP_TEXT()
             break;
 
         default:
-            Expecting( "layer, hide, effects or tstamp" );
+            Expecting( "layer, hide, effects, render_cache or tstamp" );
         }
     }
 
@@ -3788,6 +3909,107 @@ FP_TEXT* PCB_PARSER::parseFP_TEXT()
 }
 
 
+FP_TEXTBOX* PCB_PARSER::parseFP_TEXTBOX()
+{
+    wxCHECK_MSG( CurTok() == T_fp_text_box, nullptr,
+                 wxString::Format( wxT( "Cannot parse %s as FP_TEXTBOX at line %d, offset %d." ),
+                                   GetTokenString( CurTok() ), CurLineNumber(), CurOffset() ) );
+
+    std::unique_ptr<FP_TEXTBOX> textbox = std::make_unique<FP_TEXTBOX>( nullptr );
+
+    T token = NextTok();
+
+    if( token == T_locked )
+    {
+        textbox->SetLocked( true );
+        token = NextTok();
+    }
+
+    if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
+        Expecting( "text value" );
+
+    textbox->SetText( FromUTF8() );
+
+    NeedLEFT();
+    token = NextTok();
+
+    if( token == T_start )
+    {
+        int x = parseBoardUnits( "X coordinate" );
+        int y = parseBoardUnits( "Y coordinate" );
+        textbox->SetStart0( VECTOR2I( x, y ) );
+        NeedRIGHT();
+
+        NeedLEFT();
+        token = NextTok();
+
+        if( token != T_end )
+            Expecting( T_end );
+
+        x = parseBoardUnits( "X coordinate" );
+        y = parseBoardUnits( "Y coordinate" );
+        textbox->SetEnd0( VECTOR2I( x, y ) );
+        NeedRIGHT();
+    }
+    else if( token == T_pts )
+    {
+        textbox->SetShape( SHAPE_T::POLY );
+        textbox->GetPolyShape().RemoveAllContours();
+        textbox->GetPolyShape().NewOutline();
+
+        while( (token = NextTok() ) != T_RIGHT )
+            parseOutlinePoints( textbox->GetPolyShape().Outline( 0 ) );
+    }
+    else
+    {
+        Expecting( "start or pts" );
+    }
+
+    for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
+    {
+        if( token == T_LEFT )
+            token = NextTok();
+
+        switch( token )
+        {
+        case T_angle:
+            textbox->SetTextAngle( EDA_ANGLE( parseDouble( "text box angle" ), DEGREES_T ) );
+            NeedRIGHT();
+            break;
+
+        case T_width:
+            textbox->SetWidth( parseBoardUnits( "text box border width" ) );
+            NeedRIGHT();
+            break;
+
+        case T_layer:
+            textbox->SetLayer( parseBoardItemLayer() );
+            NeedRIGHT();
+            break;
+
+        case T_effects:
+            parseEDA_TEXT( static_cast<EDA_TEXT*>( textbox.get() ) );
+            break;
+
+        case T_render_cache:
+            parseRenderCache( static_cast<EDA_TEXT*>( textbox.get() ) );
+            break;
+
+        case T_tstamp:
+            NextTok();
+            const_cast<KIID&>( textbox->m_Uuid ) = CurStrToKIID();
+            NeedRIGHT();
+            break;
+
+        default:
+            Expecting( "angle, width, layer, effects, render_cache or tstamp" );
+        }
+    }
+
+    return textbox.release();
+}
+
+
 FP_SHAPE* PCB_PARSER::parseFP_SHAPE()
 {
     wxCHECK_MSG( CurTok() == T_fp_arc || CurTok() == T_fp_circle || CurTok() == T_fp_curve ||
diff --git a/pcbnew/plugins/kicad/pcb_parser.h b/pcbnew/plugins/kicad/pcb_parser.h
index 67782699e0..6cbab5f5f4 100644
--- a/pcbnew/plugins/kicad/pcb_parser.h
+++ b/pcbnew/plugins/kicad/pcb_parser.h
@@ -174,12 +174,14 @@ private:
 
     PCB_SHAPE*          parsePCB_SHAPE();
     PCB_TEXT*           parsePCB_TEXT();
+    PCB_TEXTBOX*        parsePCB_TEXTBOX();
     PCB_DIMENSION_BASE* parseDIMENSION( BOARD_ITEM* aParent, bool aInFP );
 
     // Parse a footprint, but do not replace PARSE_ERROR with FUTURE_FORMAT_ERROR automatically.
     FOOTPRINT*          parseFOOTPRINT_unchecked( wxArrayString* aInitialComments = nullptr );
 
     FP_TEXT*            parseFP_TEXT();
+    FP_TEXTBOX*         parseFP_TEXTBOX();
     FP_SHAPE*           parseFP_SHAPE();
     PAD*                parsePAD( FOOTPRINT* aParent = nullptr );
 
diff --git a/pcbnew/plugins/kicad/pcb_plugin.cpp b/pcbnew/plugins/kicad/pcb_plugin.cpp
index 79ec1d6487..9735853ac2 100644
--- a/pcbnew/plugins/kicad/pcb_plugin.cpp
+++ b/pcbnew/plugins/kicad/pcb_plugin.cpp
@@ -33,6 +33,7 @@
 #include <pcb_dimension.h>
 #include <footprint.h>
 #include <fp_shape.h>
+#include <fp_textbox.h>
 #include <string_utils.h>
 #include <kiface_base.h>
 #include <locale_io.h>
@@ -42,6 +43,7 @@
 #include <pcb_shape.h>
 #include <pcb_target.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
 #include <pcbnew_settings.h>
 #include <plugins/kicad/pcb_plugin.h>
 #include <plugins/kicad/pcb_parser.h>
@@ -437,10 +439,18 @@ void PCB_PLUGIN::Format( const BOARD_ITEM* aItem, int aNestLevel ) const
         format( static_cast<const PCB_TEXT*>( aItem ), aNestLevel );
         break;
 
+    case PCB_TEXTBOX_T:
+        format( static_cast<const PCB_TEXTBOX*>( aItem ), aNestLevel );
+        break;
+
     case PCB_FP_TEXT_T:
         format( static_cast<const FP_TEXT*>( aItem ), aNestLevel );
         break;
 
+    case PCB_FP_TEXTBOX_T:
+        format( static_cast<const FP_TEXTBOX*>( aItem ), aNestLevel );
+        break;
+
     case PCB_GROUP_T:
         format( static_cast<const PCB_GROUP*>( aItem ), aNestLevel );
         break;
@@ -906,7 +916,7 @@ void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const
     {
     case SHAPE_T::SEGMENT:
         m_out->Print( aNestLevel, "(gr_line%s (start %s) (end %s)",
-                     locked.c_str(),
+                      locked.c_str(),
                       FormatInternalUnits( aShape->GetStart() ).c_str(),
                       FormatInternalUnits( aShape->GetEnd() ).c_str() );
         break;
@@ -1739,6 +1749,53 @@ void PCB_PLUGIN::format( const PCB_TEXT* aText, int aNestLevel ) const
 }
 
 
+void PCB_PLUGIN::format( const PCB_TEXTBOX* aTextBox, int aNestLevel ) const
+{
+    std::string locked = aTextBox->IsLocked() ? " locked" : "";
+
+    m_out->Print( aNestLevel, "(gr_text_box%s %s\n",
+                  locked.c_str(),
+                  m_out->Quotew( aTextBox->GetText() ).c_str() );
+
+    if( aTextBox->GetShape() == SHAPE_T::RECT )
+    {
+        m_out->Print( aNestLevel, "(start %s) (end %s)",
+                      FormatInternalUnits( aTextBox->GetStart() ).c_str(),
+                      FormatInternalUnits( aTextBox->GetEnd() ).c_str() );
+    }
+    else if( aTextBox->GetShape() == SHAPE_T::POLY )
+    {
+        const SHAPE_POLY_SET& poly = aTextBox->GetPolyShape();
+        const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
+
+        formatPolyPts( outline, aNestLevel, true );
+    }
+    else
+    {
+        UNIMPLEMENTED_FOR( aTextBox->SHAPE_T_asString() );
+    }
+
+    if( !aTextBox->GetTextAngle().IsZero() )
+        m_out->Print( 0, " (angle %s)", FormatAngle( aTextBox->GetTextAngle() ).c_str() );
+
+    m_out->Print( 0, " (width %s)", FormatInternalUnits( aTextBox->GetWidth() ).c_str() );
+
+    formatLayer( aTextBox );
+
+    m_out->Print( 0, " (tstamp %s)", TO_UTF8( aTextBox->m_Uuid.AsString() ) );
+
+    m_out->Print( 0, "\n" );
+
+    // PCB_TEXTBOXes are never hidden, so always omit "hide" attribute
+    aTextBox->EDA_TEXT::Format( m_out, aNestLevel, m_ctl | CTL_OMIT_HIDE );
+
+    if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )
+        formatRenderCache( aTextBox, aNestLevel + 1 );
+
+    m_out->Print( aNestLevel, ")\n" );
+}
+
+
 void PCB_PLUGIN::format( const PCB_GROUP* aGroup, int aNestLevel ) const
 {
     // Don't write empty groups
@@ -1835,6 +1892,54 @@ void PCB_PLUGIN::format( const FP_TEXT* aText, int aNestLevel ) const
 }
 
 
+void PCB_PLUGIN::format( const FP_TEXTBOX* aTextBox, int aNestLevel ) const
+{
+    std::string locked = aTextBox->IsLocked() ? " locked" : "";
+
+    m_out->Print( aNestLevel, "(fp_text_box%s %s\n",
+                  locked.c_str(),
+                  m_out->Quotew( aTextBox->GetText() ).c_str() );
+
+    if( aTextBox->GetShape() == SHAPE_T::RECT )
+    {
+        m_out->Print( aNestLevel, "(start %s) (end %s) (angle %s) (width %s)",
+                      FormatInternalUnits( aTextBox->GetStart0() ).c_str(),
+                      FormatInternalUnits( aTextBox->GetEnd0() ).c_str(),
+                      FormatAngle( aTextBox->GetTextAngle() ).c_str(),
+                      FormatInternalUnits( aTextBox->GetWidth() ).c_str() );
+    }
+    else if( aTextBox->GetShape() == SHAPE_T::POLY )
+    {
+        const SHAPE_POLY_SET& poly = aTextBox->GetPolyShape();
+        const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
+
+        formatPolyPts( outline, aNestLevel, true );
+
+        m_out->Print( aNestLevel, " (angle %s) (width %s)",
+                      FormatAngle( aTextBox->GetTextAngle() ).c_str(),
+                      FormatInternalUnits( aTextBox->GetWidth() ).c_str() );
+    }
+    else
+    {
+        UNIMPLEMENTED_FOR( aTextBox->SHAPE_T_asString() );
+    }
+
+    formatLayer( aTextBox );
+
+    m_out->Print( 0, " (tstamp %s)", TO_UTF8( aTextBox->m_Uuid.AsString() ) );
+
+    m_out->Print( 0, "\n" );
+
+    // FP_TEXTBOXes are never hidden, so always omit "hide" attribute
+    aTextBox->EDA_TEXT::Format( m_out, aNestLevel, m_ctl | CTL_OMIT_HIDE );
+
+    if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )
+        formatRenderCache( aTextBox, aNestLevel + 1 );
+
+    m_out->Print( aNestLevel, ")\n" );
+}
+
+
 void PCB_PLUGIN::format( const PCB_TRACK* aTrack, int aNestLevel ) const
 {
     if( aTrack->Type() == PCB_VIA_T )
diff --git a/pcbnew/plugins/kicad/pcb_plugin.h b/pcbnew/plugins/kicad/pcb_plugin.h
index f60ce336ef..b480cb1cb6 100644
--- a/pcbnew/plugins/kicad/pcb_plugin.h
+++ b/pcbnew/plugins/kicad/pcb_plugin.h
@@ -41,10 +41,12 @@ class PCB_SHAPE;
 class PCB_TARGET;
 class PAD;
 class FP_TEXT;
+class FP_TEXTBOX;
 class PCB_GROUP;
 class PCB_TRACK;
 class ZONE;
 class PCB_TEXT;
+class PCB_TEXTBOX;
 class EDA_TEXT;
 class SHAPE_LINE_CHAIN;
 
@@ -275,8 +277,10 @@ private:
     void format( const PAD* aPad, int aNestLevel = 0 ) const;
 
     void format( const PCB_TEXT* aText, int aNestLevel = 0 ) const;
+    void format( const PCB_TEXTBOX* aTextBox, int aNestLevel = 0 ) const;
 
     void format( const FP_TEXT* aText, int aNestLevel = 0 ) const;
+    void format( const FP_TEXTBOX* aTextBox, int aNestLevel = 0 ) const;
 
     void format( const PCB_TRACK* aTrack, int aNestLevel = 0 ) const;
 
diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp
index 604c6801e8..14fab0aaab 100644
--- a/pcbnew/router/pns_kicad_iface.cpp
+++ b/pcbnew/router/pns_kicad_iface.cpp
@@ -1286,7 +1286,7 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld )
 
     for( BOARD_ITEM* gitem : m_board->Drawings() )
     {
-        if ( gitem->Type() == PCB_SHAPE_T )
+        if ( gitem->Type() == PCB_SHAPE_T || gitem->Type() == PCB_TEXTBOX_T )
         {
             syncGraphicalItem( aWorld, static_cast<PCB_SHAPE*>( gitem ) );
         }
@@ -1328,7 +1328,7 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld )
 
         for( BOARD_ITEM* mgitem : footprint->GraphicalItems() )
         {
-            if( mgitem->Type() == PCB_FP_SHAPE_T )
+            if( mgitem->Type() == PCB_FP_SHAPE_T || mgitem->Type() == PCB_FP_TEXTBOX_T )
             {
                 syncGraphicalItem( aWorld, static_cast<PCB_SHAPE*>( mgitem ) );
             }
diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp
index 16a0151578..88a96276cf 100644
--- a/pcbnew/router/pns_router.cpp
+++ b/pcbnew/router/pns_router.cpp
@@ -252,6 +252,8 @@ bool ROUTER::isStartingPointRoutable( const VECTOR2I& aWhere, ITEM* aStartItem,
 
             case PCB_TEXT_T:
             case PCB_FP_TEXT_T:
+            case PCB_TEXTBOX_T:
+            case PCB_FP_TEXTBOX_T:
                 SetFailureReason( _( "Cannot start routing from a text item." ) );
                 break;
 
diff --git a/pcbnew/toolbars_footprint_editor.cpp b/pcbnew/toolbars_footprint_editor.cpp
index b9a619790d..6d0ef7d4d4 100644
--- a/pcbnew/toolbars_footprint_editor.cpp
+++ b/pcbnew/toolbars_footprint_editor.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -186,6 +186,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateVToolbar()
     m_drawToolBar->Add( PCB_ACTIONS::drawCircle,      ACTION_TOOLBAR::TOGGLE );
     m_drawToolBar->Add( PCB_ACTIONS::drawPolygon,     ACTION_TOOLBAR::TOGGLE );
     m_drawToolBar->Add( PCB_ACTIONS::placeText,       ACTION_TOOLBAR::TOGGLE );
+    m_drawToolBar->Add( PCB_ACTIONS::drawTextBox,     ACTION_TOOLBAR::TOGGLE );
     m_drawToolBar->AddGroup( dimensionGroup,          ACTION_TOOLBAR::TOGGLE );
     m_drawToolBar->Add( ACTIONS::deleteTool,          ACTION_TOOLBAR::TOGGLE );
 
diff --git a/pcbnew/toolbars_pcb_editor.cpp b/pcbnew/toolbars_pcb_editor.cpp
index d7ed5ac486..64c4fa87ce 100644
--- a/pcbnew/toolbars_pcb_editor.cpp
+++ b/pcbnew/toolbars_pcb_editor.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2022 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
@@ -456,6 +456,7 @@ void PCB_EDIT_FRAME::ReCreateVToolbar()
     m_drawToolBar->Add( PCB_ACTIONS::drawCircle,           ACTION_TOOLBAR::TOGGLE );
     m_drawToolBar->Add( PCB_ACTIONS::drawPolygon,          ACTION_TOOLBAR::TOGGLE );
     m_drawToolBar->Add( PCB_ACTIONS::placeText,            ACTION_TOOLBAR::TOGGLE );
+    m_drawToolBar->Add( PCB_ACTIONS::drawTextBox,          ACTION_TOOLBAR::TOGGLE );
     m_drawToolBar->AddGroup( dimensionGroup,               ACTION_TOOLBAR::TOGGLE );
     m_drawToolBar->Add( PCB_ACTIONS::placeTarget,          ACTION_TOOLBAR::TOGGLE );
     m_drawToolBar->Add( ACTIONS::deleteTool,               ACTION_TOOLBAR::TOGGLE );
diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp
index b5020ae8ff..711f1c446c 100644
--- a/pcbnew/tools/drawing_tool.cpp
+++ b/pcbnew/tools/drawing_tool.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2014-2017 CERN
- * Copyright (C) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2018-2022 KiCad Developers, see AUTHORS.txt for contributors.
  * @author Maciej Suminski <maciej.suminski@cern.ch>
  *
  * This program is free software; you can redistribute it and/or
@@ -40,7 +40,6 @@
 #include <router/router_tool.h>
 #include <tool/tool_manager.h>
 #include <tools/pcb_actions.h>
-#include <tools/pcb_editor_conditions.h>
 #include <tools/pcb_grid_helper.h>
 #include <tools/pcb_selection_tool.h>
 #include <tools/tool_event_utils.h>
@@ -56,11 +55,13 @@
 #include <confirm.h>
 #include <footprint.h>
 #include <fp_shape.h>
+#include <fp_textbox.h>
 #include <macros.h>
 #include <painter.h>
 #include <pcb_edit_frame.h>
 #include <pcb_group.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
 #include <pcb_dimension.h>
 #include <pcbnew_id.h>
 #include <preview_items/arc_assistant.h>
@@ -333,12 +334,34 @@ int DRAWING_TOOL::DrawRectangle( const TOOL_EVENT& aEvent )
 
     REENTRANCY_GUARD guard( &m_inDrawingTool );
 
-    FOOTPRINT*       parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
-    PCB_SHAPE*       rect = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
+    bool             isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
+    PCB_SHAPE*       rect = nullptr;
     BOARD_COMMIT     commit( m_frame );
     SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
     OPT<VECTOR2D>    startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
 
+    auto makeNew =
+            [&]() -> PCB_SHAPE*
+            {
+                if( m_isFootprintEditor )
+                {
+                    FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
+
+                    if( isTextBox )
+                        return new FP_TEXTBOX( parentFootprint );
+                    else
+                        return new FP_SHAPE( parentFootprint );
+                }
+                else
+                {
+                    if( isTextBox )
+                        return new PCB_TEXTBOX( m_frame->GetModel() );
+                    else
+                        return new PCB_SHAPE();
+                }
+            };
+
+    rect = makeNew();
     rect->SetShape( SHAPE_T::RECT );
     rect->SetFilled( false );
     rect->SetFlags(IS_NEW );
@@ -357,13 +380,21 @@ int DRAWING_TOOL::DrawRectangle( const TOOL_EVENT& aEvent )
             if( m_isFootprintEditor )
                 static_cast<FP_SHAPE*>( rect )->SetLocalCoord();
 
-            commit.Add( rect );
-            commit.Push( _( "Draw a rectangle" ) );
+            if( isTextBox && m_frame->ShowTextBoxPropertiesDialog( rect ) != wxID_OK )
+            {
+                delete rect;
+                rect = nullptr;
+            }
+            else
+            {
+                commit.Add( rect );
+                commit.Push( isTextBox ? _( "Draw a text box" ) : _( "Draw a rectangle" ) );
 
-            m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, rect );
+                m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, rect );
+            }
         }
 
-        rect = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
+        rect = makeNew();
         rect->SetShape( SHAPE_T::RECT );
         rect->SetFilled( false );
         rect->SetFlags(IS_NEW );
@@ -589,7 +620,7 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
                                      abs( thickness - GetPenSizeForNormal( textSize ) ) );
                     fpText->SetItalic( dsnSettings.GetTextItalic( layer ) );
                     fpText->SetKeepUpright( dsnSettings.GetTextUpright( layer ) );
-                    fpText->SetTextPos( (wxPoint) cursorPos );
+                    fpText->SetTextPos( cursorPos );
 
                     text = fpText;
 
@@ -606,7 +637,7 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
                         delete text;
                         text = nullptr;
                     }
-                    else if( fpText->GetTextPos() != (wxPoint) cursorPos )
+                    else if( fpText->GetTextPos() != cursorPos )
                     {
                         // If the user modified the location then go ahead and place it there.
                         // Otherwise we'll drag.
@@ -629,7 +660,7 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
                     pcbText->SetBold( abs( thickness - GetPenSizeForBold( textSize ) ) <
                                       abs( thickness - GetPenSizeForNormal( textSize ) ) );
                     pcbText->SetItalic( dsnSettings.GetTextItalic( layer ) );
-                    pcbText->SetTextPos( (wxPoint) cursorPos );
+                    pcbText->SetTextPos( cursorPos );
 
                     RunMainStack( [&]()
                                   {
@@ -686,7 +717,7 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
         }
         else if( text && evt->IsMotion() )
         {
-            text->SetPosition( (wxPoint) cursorPos );
+            text->SetPosition( cursorPos );
             selection().SetReferencePoint( cursorPos );
             m_view->Update( &selection() );
         }
@@ -722,7 +753,7 @@ void DRAWING_TOOL::constrainDimension( PCB_DIMENSION_BASE* aDim )
 {
     const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
 
-    aDim->SetEnd( wxPoint( VECTOR2I( aDim->GetStart() ) + GetVectorSnapped45( lineVector ) ) );
+    aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
     aDim->Update();
 }
 
@@ -914,7 +945,7 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
                 else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
                 {
                     dimension = new PCB_DIM_LEADER( m_frame->GetModel(), m_isFootprintEditor );
-                    dimension->Text().SetPosition( wxPoint( cursorPos ) );
+                    dimension->Text().SetPosition( cursorPos );
                 }
                 else
                 {
@@ -930,8 +961,8 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
                 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
                 dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
                 dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
-                dimension->SetStart( (wxPoint) cursorPos );
-                dimension->SetEnd( (wxPoint) cursorPos );
+                dimension->SetStart( cursorPos );
+                dimension->SetEnd( cursorPos );
                 dimension->Update();
 
                 if( !m_view->IsLayerVisible( layer ) )
@@ -1000,7 +1031,7 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
             switch( step )
             {
             case SET_END:
-                dimension->SetEnd( (wxPoint) cursorPos );
+                dimension->SetEnd( cursorPos );
 
                 if( Is45Limited() || t == PCB_DIM_CENTER_T || t == PCB_FP_DIM_CENTER_T )
                     constrainDimension( dimension );
@@ -1021,7 +1052,7 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
                 else if( t == PCB_DIM_RADIAL_T || t == PCB_FP_DIM_RADIAL_T )
                 {
                     PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
-                    wxPoint         textOffset( radialDim->GetArrowLength() * 10, 0 );
+                    VECTOR2I        textOffset( radialDim->GetArrowLength() * 10, 0 );
 
                     if( radialDim->GetEnd().x < radialDim->GetStart().x )
                         textOffset = -textOffset;
@@ -1030,7 +1061,7 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
                 }
                 else if( t == PCB_DIM_LEADER_T || t == PCB_FP_DIM_LEADER_T )
                 {
-                    wxPoint textOffset( dimension->GetArrowLength() * 10, 0 );
+                    VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
 
                     if( dimension->GetEnd().x < dimension->GetStart().x )
                         textOffset = -textOffset;
@@ -1266,7 +1297,7 @@ int DRAWING_TOOL::PlaceImportedGraphics( const TOOL_EVENT& aEvent )
     VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.Front() )->GetPosition();
 
     for( BOARD_ITEM* item : selectedItems )
-        item->Move( (wxPoint) delta );
+        item->Move( delta );
 
     m_view->Update( &preview );
 
@@ -1290,7 +1321,7 @@ int DRAWING_TOOL::PlaceImportedGraphics( const TOOL_EVENT& aEvent )
             delta = cursorPos - static_cast<BOARD_ITEM*>( preview.Front() )->GetPosition();
 
             for( BOARD_ITEM* item : selectedItems )
-                item->Move( (wxPoint) delta );
+                item->Move( delta );
 
             m_view->Update( &preview );
         }
@@ -1375,7 +1406,7 @@ int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
             commit.Modify( footprint );
 
             // set the new relative internal local coordinates of footprint items
-            VECTOR2I moveVector = footprint->GetPosition() - (wxPoint) cursorPos;
+            VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
             footprint->MoveAnchorPosition( moveVector );
 
             commit.Push( _( "Move the footprint reference anchor" ) );
@@ -1433,8 +1464,8 @@ static void updateSegmentFromGeometryMgr( const KIGFX::PREVIEW::TWO_POINT_GEOMET
 {
     if( !aMgr.IsReset() )
     {
-        aGraphic->SetStart( (wxPoint) aMgr.GetOrigin() );
-        aGraphic->SetEnd( (wxPoint) aMgr.GetEnd() );
+        aGraphic->SetStart( aMgr.GetOrigin() );
+        aGraphic->SetEnd( aMgr.GetEnd() );
     }
 }
 
@@ -1618,8 +1649,8 @@ bool DRAWING_TOOL::drawSegment( const std::string& aTool, PCB_SHAPE** aGraphic,
                 graphic->SetLayer( m_layer );
                 grid.SetSkipPoint( cursorPos );
 
-                twoPointManager.SetOrigin( (wxPoint) cursorPos );
-                twoPointManager.SetEnd( (wxPoint) cursorPos );
+                twoPointManager.SetOrigin( cursorPos );
+                twoPointManager.SetEnd( cursorPos );
 
                 if( !isLocalOriginSet )
                     m_frame->GetScreen()->m_LocalOrigin = cursorPos;
@@ -1691,12 +1722,12 @@ bool DRAWING_TOOL::drawSegment( const std::string& aTool, PCB_SHAPE** aGraphic,
                 // get a restricted 45/H/V line from the last fixed point to the cursor
                 auto newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECT ) );
                 m_controls->ForceCursorPosition( true, VECTOR2I( twoPointManager.GetEnd() ) );
-                twoPointManager.SetEnd( twoPointManager.GetOrigin() + (wxPoint) newEnd );
+                twoPointManager.SetEnd( twoPointManager.GetOrigin() + newEnd );
                 twoPointManager.SetAngleSnap( true );
             }
             else
             {
-                twoPointManager.SetEnd( (wxPoint) cursorPos );
+                twoPointManager.SetEnd( cursorPos );
                 twoPointManager.SetAngleSnap( false );
             }
 
@@ -1766,12 +1797,12 @@ static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER
 {
     VECTOR2I vec = aMgr.GetOrigin();
 
-    aArc.SetCenter( (wxPoint) vec );
+    aArc.SetCenter( vec );
 
     vec = aMgr.GetStartRadiusEnd();
-    aArc.SetStart( (wxPoint) vec );
+    aArc.SetStart( vec );
     vec = aMgr.GetEndRadiusEnd();
-    aArc.SetEnd( (wxPoint) vec );
+    aArc.SetEnd( vec );
 }
 
 
@@ -2275,7 +2306,7 @@ int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
 
             if( polyGeomMgr.IsSelfIntersecting( true ) )
             {
-                wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
+                VECTOR2I p = wxGetMousePosition() + wxPoint( 20, 20 );
                 status.Move( p );
                 status.PopupFor( 1500 );
             }
@@ -2376,15 +2407,15 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
         {
             const LSET lset = aVia->GetLayerSet();
             VECTOR2I   position = aVia->GetPosition();
-            BOX2I bbox = aVia->GetBoundingBox();
+            BOX2I      bbox = aVia->GetBoundingBox();
 
             std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
-            auto view = m_frame->GetCanvas()->GetView();
+            KIGFX::PCB_VIEW*        view = m_frame->GetCanvas()->GetView();
             std::vector<PCB_TRACK*> possible_tracks;
 
             view->Query( bbox, items );
 
-            for( auto it : items )
+            for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
             {
                 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
 
@@ -2395,7 +2426,9 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
                 {
                     if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
                                         ( track->GetWidth() + aVia->GetWidth() ) / 2 ) )
+                    {
                         possible_tracks.push_back( track );
+                    }
                 }
             }
 
@@ -2525,15 +2558,17 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
         PAD* findPad( PCB_VIA* aVia )
         {
             const VECTOR2I position = aVia->GetPosition();
-            const LSET    lset = aVia->GetLayerSet();
+            const LSET     lset = aVia->GetLayerSet();
 
             for( FOOTPRINT* fp : m_board->Footprints() )
             {
-                for(PAD* pad : fp->Pads() )
+                for( PAD* pad : fp->Pads() )
                 {
                     if( pad->HitTest( position ) && ( pad->GetLayerSet() & lset ).any() )
+                    {
                         if( pad->GetNetCode() > 0 )
                             return pad;
+                    }
                 }
             }
 
@@ -2581,11 +2616,10 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
 
         void SnapItem( BOARD_ITEM *aItem ) override
         {
-            // If you place a Via on a track but not on its centerline, the current
-            // connectivity algorithm will require us to put a kink in the track when
-            // we break it (so that each of the two segments ends on the via center).
-            // That's not ideal, and is in fact probably worse than forcing snap in
-            // this situation.
+            // If you place a Via on a track but not on its centerline, the current connectivity
+            // algorithm will require us to put a kink in the track when we break it (so that each
+            // of the two segments ends on the via center).
+            // That's not ideal, and is probably worse than forcing snap in this situation.
 
             m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
             PCB_VIA*   via = static_cast<PCB_VIA*>( aItem );
@@ -2598,11 +2632,10 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
                 SEG      trackSeg( track->GetStart(), track->GetEnd() );
                 VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
 
-                aItem->SetPosition( (wxPoint) snap );
+                aItem->SetPosition( snap );
             }
             else if( pad && m_gridHelper.GetSnap()
-                     && m_frame->GetMagneticItemsSettings()->pads
-                                == MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
+                 && m_frame->GetMagneticItemsSettings()->pads == MAGNETIC_OPTIONS::CAPTURE_ALWAYS )
             {
                 aItem->SetPosition( pad->GetPosition() );
             }
@@ -2767,6 +2800,7 @@ void DRAWING_TOOL::setTransitions()
     Go( &DRAWING_TOOL::DrawZone,              PCB_ACTIONS::drawSimilarZone.MakeEvent() );
     Go( &DRAWING_TOOL::DrawVia,               PCB_ACTIONS::drawVia.MakeEvent() );
     Go( &DRAWING_TOOL::PlaceText,             PCB_ACTIONS::placeText.MakeEvent() );
+    Go( &DRAWING_TOOL::DrawRectangle,         PCB_ACTIONS::drawTextBox.MakeEvent() );
     Go( &DRAWING_TOOL::PlaceImportedGraphics, PCB_ACTIONS::placeImportedGraphics.MakeEvent() );
     Go( &DRAWING_TOOL::SetAnchor,             PCB_ACTIONS::setAnchor.MakeEvent() );
     Go( &DRAWING_TOOL::ToggleLine45degMode,   PCB_ACTIONS::toggle45.MakeEvent() );
diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp
index d081420427..6b6cb2bd26 100644
--- a/pcbnew/tools/edit_tool.cpp
+++ b/pcbnew/tools/edit_tool.cpp
@@ -30,6 +30,7 @@
 #include <board_design_settings.h>
 #include <footprint.h>
 #include <fp_shape.h>
+#include <fp_textbox.h>
 #include <collectors.h>
 #include <pcb_edit_frame.h>
 #include <drawing_sheet/ds_proxy_view_item.h>
@@ -1584,6 +1585,7 @@ int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
         {
         case PCB_FP_SHAPE_T:
         case PCB_FP_TEXT_T:
+        case PCB_FP_TEXTBOX_T:
         case PCB_FP_ZONE_T:
         case PCB_PAD_T:
             // Only create undo entry for items on the board
@@ -1619,6 +1621,13 @@ int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
             break;
         }
 
+        case PCB_FP_TEXTBOX_T:
+        {
+            FP_TEXTBOX* textbox = static_cast<FP_TEXTBOX*>( item );
+            textbox->Mirror( mirrorPoint, false );
+            break;
+        }
+
         case PCB_PAD_T:
         {
             PAD* pad = static_cast<PAD*>( item );
@@ -1849,6 +1858,17 @@ int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
             break;
         }
 
+        case PCB_FP_TEXTBOX_T:
+        {
+            FP_TEXTBOX* textbox = static_cast<FP_TEXTBOX*>( item );
+            FOOTPRINT*  parent = static_cast<FOOTPRINT*>( item->GetParent() );
+
+            m_commit->Modify( parent );
+            getView()->Remove( textbox );
+            parent->Remove( textbox );
+            break;
+        }
+
         case PCB_PAD_T:
             if( IsFootprintEditor() || frame()->Settings().m_AllowFreePads )
             {
@@ -2142,6 +2162,7 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
             {
             case PCB_FOOTPRINT_T:
             case PCB_TEXT_T:
+            case PCB_TEXTBOX_T:
             case PCB_SHAPE_T:
             case PCB_TRACE_T:
             case PCB_ARC_T:
diff --git a/pcbnew/tools/group_tool.cpp b/pcbnew/tools/group_tool.cpp
index a3e086c18d..5d01156037 100644
--- a/pcbnew/tools/group_tool.cpp
+++ b/pcbnew/tools/group_tool.cpp
@@ -236,6 +236,7 @@ int GROUP_TOOL::Group( const TOOL_EVENT& aEvent )
                         switch( item->Type() )
                         {
                         case PCB_FP_TEXT_T:
+                        case PCB_FP_TEXTBOX_T:
                         case PCB_FP_SHAPE_T:
                         case PCB_FP_ZONE_T:
                         case PCB_PAD_T:
diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp
index 25251bd9fd..f482501703 100644
--- a/pcbnew/tools/pcb_actions.cpp
+++ b/pcbnew/tools/pcb_actions.cpp
@@ -123,6 +123,11 @@ TOOL_ACTION PCB_ACTIONS::placeText( "pcbnew.InteractiveDrawing.text",
         _( "Add Text" ), _( "Add a text item" ),
         BITMAPS::text, AF_ACTIVATE );
 
+TOOL_ACTION PCB_ACTIONS::drawTextBox( "pcbnew.InteractiveDrawing.textbox",
+        AS_GLOBAL, 0, "",
+        _( "Add Text Box" ), _( "Add a wrapped text item" ),
+        BITMAPS::add_textbox, AF_ACTIVATE );
+
 TOOL_ACTION PCB_ACTIONS::drawAlignedDimension( "pcbnew.InteractiveDrawing.alignedDimension",
         AS_GLOBAL,
         MD_SHIFT + MD_CTRL + 'H', LEGACY_HK_NAME(  "Add Dimension" ),
diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h
index 77bd02d082..7d168b4072 100644
--- a/pcbnew/tools/pcb_actions.h
+++ b/pcbnew/tools/pcb_actions.h
@@ -148,6 +148,7 @@ public:
     static TOOL_ACTION drawCircle;
     static TOOL_ACTION drawArc;
     static TOOL_ACTION placeText;
+    static TOOL_ACTION drawTextBox;
     static TOOL_ACTION drawAlignedDimension;
     static TOOL_ACTION drawCenterDimension;
     static TOOL_ACTION drawRadialDimension;
diff --git a/pcbnew/tools/pcb_control.cpp b/pcbnew/tools/pcb_control.cpp
index 5b048f771c..7f8e86a530 100644
--- a/pcbnew/tools/pcb_control.cpp
+++ b/pcbnew/tools/pcb_control.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2014-2016 CERN
- * Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
  * @author Maciej Suminski <maciej.suminski@cern.ch>
  *
  * This program is free software; you can redistribute it and/or
@@ -40,9 +40,11 @@
 #include <pcb_dimension.h>
 #include <footprint.h>
 #include <pcb_group.h>
+#include <pcb_textbox.h>
 #include <pcb_track.h>
 #include <zone.h>
 #include <fp_shape.h>
+#include <fp_textbox.h>
 #include <confirm.h>
 #include <connectivity/connectivity_data.h>
 #include <core/kicad_algo.h>
@@ -661,7 +663,7 @@ static void pasteFootprintItemsToFootprintEditor( FOOTPRINT* aClipFootprint, BOA
     // So they will be skipped
     for( BOARD_ITEM* item : aClipFootprint->GraphicalItems() )
     {
-        if( item->Type() == PCB_FP_SHAPE_T )
+        if( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_FP_TEXTBOX_T )
         {
             FP_SHAPE* shape = static_cast<FP_SHAPE*>( item );
 
@@ -790,6 +792,18 @@ int PCB_CONTROL::Paste( const TOOL_EVENT& aEvent )
                         pastedTextItem->SetParent( editorFootprint );
                         pastedItems.push_back( pastedTextItem );
                     }
+                    else if( clipDrawItem->Type() == PCB_TEXTBOX_T )
+                    {
+                        PCB_TEXTBOX* clipTextBox = static_cast<PCB_TEXTBOX*>( clipDrawItem );
+
+                        // Convert to PCB_FP_TEXTBOX_T
+                        FP_TEXTBOX* pastedTextBox = new FP_TEXTBOX( editorFootprint );
+                        static_cast<EDA_TEXT*>( pastedTextBox )->SwapText( *clipTextBox );
+                        static_cast<EDA_TEXT*>( pastedTextBox )->SwapAttributes( *clipTextBox );
+
+                        pastedTextBox->SetParent( editorFootprint );
+                        pastedItems.push_back( pastedTextBox );
+                    }
                 }
 
                 delete clipBoard;
diff --git a/pcbnew/tools/pcb_grid_helper.cpp b/pcbnew/tools/pcb_grid_helper.cpp
index d247a42abc..c83994a8a9 100644
--- a/pcbnew/tools/pcb_grid_helper.cpp
+++ b/pcbnew/tools/pcb_grid_helper.cpp
@@ -24,7 +24,6 @@
  */
 
 #include <functional>
-#include <board.h>
 #include <pcb_dimension.h>
 #include <fp_shape.h>
 #include <footprint.h>
@@ -555,6 +554,8 @@ void PCB_GRID_HELPER::computeAnchors( BOARD_ITEM* aItem, const VECTOR2I& aRefPos
 
         case PCB_FP_SHAPE_T:
         case PCB_SHAPE_T:
+        case PCB_FP_TEXTBOX_T:
+        case PCB_TEXTBOX_T:
         {
             if( !m_magneticSettings->graphics )
                 break;
diff --git a/pcbnew/tools/pcb_point_editor.cpp b/pcbnew/tools/pcb_point_editor.cpp
index 3e843de780..0431cb1137 100644
--- a/pcbnew/tools/pcb_point_editor.cpp
+++ b/pcbnew/tools/pcb_point_editor.cpp
@@ -32,15 +32,16 @@ using namespace std::placeholders;
 #include <view/view_controls.h>
 #include <geometry/seg.h>
 #include <confirm.h>
-#include "pcb_actions.h"
-#include "pcb_selection_tool.h"
-#include "pcb_point_editor.h"
-#include "pcb_grid_helper.h"
+#include <tools/pcb_actions.h>
+#include <tools/pcb_selection_tool.h>
+#include <tools/pcb_point_editor.h>
+#include <tools/pcb_grid_helper.h>
 #include <board_commit.h>
-#include <bitmaps.h>
 #include <status_popup.h>
 #include <pcb_edit_frame.h>
+#include <pcb_textbox.h>
 #include <fp_shape.h>
+#include <fp_textbox.h>
 #include <pcb_dimension.h>
 #include <pad.h>
 #include <zone.h>
@@ -154,8 +155,7 @@ void PCB_POINT_EDITOR::buildForPolyOutline( std::shared_ptr<EDIT_POINTS> points,
             points->AddBreak();
     }
 
-    // Lines have to be added after creating edit points,
-    // as they use EDIT_POINT references
+    // Lines have to be added after creating edit points, as they use EDIT_POINT references
     for( int i = 0; i < cornersCount - 1; ++i )
     {
         if( points->IsContourEnd( i ) )
@@ -182,9 +182,21 @@ std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
     if( !aItem )
         return points;
 
+    if( aItem->Type() == PCB_TEXTBOX_T || aItem->Type() == PCB_FP_TEXTBOX_T )
+    {
+        const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
+
+        // We can't currently handle TEXTBOXes that have been turned into SHAPE_T::POLYs due
+        // to non-cardinal rotations
+        if( shape->GetShape() != SHAPE_T::RECT )
+            return points;
+    }
+
     // Generate list of edit points basing on the item type
     switch( aItem->Type() )
     {
+    case PCB_TEXTBOX_T:
+    case PCB_FP_TEXTBOX_T:
     case PCB_SHAPE_T:
     case PCB_FP_SHAPE_T:
     {
@@ -1053,6 +1065,8 @@ void PCB_POINT_EDITOR::updateItem() const
 
     switch( item->Type() )
     {
+    case PCB_TEXTBOX_T:
+    case PCB_FP_TEXTBOX_T:
     case PCB_SHAPE_T:
     case PCB_FP_SHAPE_T:
     {
@@ -1108,11 +1122,14 @@ void PCB_POINT_EDITOR::updateItem() const
             for( unsigned i = 0; i < m_editPoints->LinesSize(); ++i )
             {
                 if( !isModified( m_editPoints->Line( i ) ) )
+                {
                     m_editPoints->Line( i ).SetConstraint(
                             new EC_PERPLINE( m_editPoints->Line( i ) ) );
+                }
             }
-        }
+
             break;
+        }
 
         case SHAPE_T::ARC:
         {
@@ -1145,8 +1162,9 @@ void PCB_POINT_EDITOR::updateItem() const
                 else
                     editArcEndpointKeepTangent( shape, center, start, mid, end, cursorPos );
             }
-        }
+
             break;
+        }
 
         case SHAPE_T::CIRCLE:
         {
@@ -1162,8 +1180,9 @@ void PCB_POINT_EDITOR::updateItem() const
             {
                 shape->SetEnd( VECTOR2I( end.x, end.y ) );
             }
-        }
+
             break;
+        }
 
         case SHAPE_T::POLY:
         {
@@ -1180,8 +1199,8 @@ void PCB_POINT_EDITOR::updateItem() const
             }
 
             validatePolygon( outline );
-        }
             break;
+        }
 
         case SHAPE_T::BEZIER:
             if( isModified( m_editPoints->Point( BEZIER_CURVE_START ) ) )
@@ -1222,8 +1241,8 @@ void PCB_POINT_EDITOR::updateItem() const
             int     diameter = (int) EuclideanNorm( end - pad->GetPosition() ) * 2;
 
             pad->SetSize( wxSize( diameter, diameter ) );
-        }
             break;
+        }
 
         case PAD_SHAPE::OVAL:
         case PAD_SHAPE::TRAPEZOID:
@@ -1307,8 +1326,8 @@ void PCB_POINT_EDITOR::updateItem() const
                 pad->SetPosition( VECTOR2I( ( left + right ) / 2, ( top + bottom ) / 2 ) );
                 pad->SetLocalCoord();
             }
-        }
             break;
+        }
 
         default:        // suppress warnings
             break;
@@ -1618,10 +1637,25 @@ void PCB_POINT_EDITOR::updatePoints()
     if( !item )
         return;
 
+    if( item->Type() == PCB_TEXTBOX_T || item->Type() == PCB_FP_TEXTBOX_T )
+    {
+        const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
+
+        // We can't currently handle TEXTBOXes that have been turned into SHAPE_T::POLYs due
+        // to non-cardinal rotations
+        if( shape->GetShape() != SHAPE_T::RECT )
+        {
+            m_editPoints.reset();
+            return;
+        }
+    }
+
     switch( item->Type() )
     {
     case PCB_SHAPE_T:
     case PCB_FP_SHAPE_T:
+    case PCB_TEXTBOX_T:
+    case PCB_FP_TEXTBOX_T:
     {
         const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
 
diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp
index 2313aacdf5..65e86d7609 100644
--- a/pcbnew/tools/pcb_selection_tool.cpp
+++ b/pcbnew/tools/pcb_selection_tool.cpp
@@ -39,6 +39,8 @@ using namespace std::placeholders;
 #include <pcb_group.h>
 #include <pcb_shape.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
+#include <fp_textbox.h>
 #include <pcb_marker.h>
 #include <zone.h>
 #include <collectors.h>
@@ -1889,7 +1891,9 @@ static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard
             break;
 
         case PCB_FP_TEXT_T:
+        case PCB_FP_TEXTBOX_T:
         case PCB_TEXT_T:
+        case PCB_TEXTBOX_T:
             include = aFilterOptions.includePcbTexts;
             break;
 
@@ -2023,7 +2027,9 @@ bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect
         break;
 
     case PCB_FP_TEXT_T:
+    case PCB_FP_TEXTBOX_T:
     case PCB_TEXT_T:
+    case PCB_TEXTBOX_T:
         if( !m_filter.text )
             return false;
 
@@ -2430,6 +2436,7 @@ bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibili
         break;
 
     case PCB_FP_SHAPE_T:
+    case PCB_FP_TEXTBOX_T:
         if( m_isFootprintEditor )
         {
             if( !view()->IsLayerVisible( aItem->GetLayer() ) )
@@ -2652,6 +2659,13 @@ int PCB_SELECTION_TOOL::hitTestDistance( const wxPoint& aWhere, BOARD_ITEM* aIte
         break;
     }
 
+    case PCB_TEXTBOX_T:
+    {
+        PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
+        textbox->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
+        break;
+    }
+
     case PCB_FP_TEXT_T:
     {
         FP_TEXT* text = static_cast<FP_TEXT*>( aItem );
@@ -2659,6 +2673,13 @@ int PCB_SELECTION_TOOL::hitTestDistance( const wxPoint& aWhere, BOARD_ITEM* aIte
         break;
     }
 
+    case PCB_FP_TEXTBOX_T:
+    {
+        FP_TEXTBOX* textbox = static_cast<FP_TEXTBOX*>( aItem );
+        textbox->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance );
+        break;
+    }
+
     case PCB_ZONE_T:
     {
         ZONE* zone = static_cast<ZONE*>( aItem );
@@ -2757,7 +2778,7 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector
             BOARD_ITEM* item = aCollector[i];
             KICAD_T type = item->Type();
 
-            if( ( type == PCB_FP_TEXT_T || type == PCB_TEXT_T || type == PCB_SHAPE_T )
+            if( ( type == PCB_TEXT_T || type == PCB_TEXTBOX_T || type == PCB_SHAPE_T )
                     && silkLayers[item->GetLayer()] )
             {
                 preferred.insert( item );
diff --git a/pcbnew/tools/pcb_viewer_tools.cpp b/pcbnew/tools/pcb_viewer_tools.cpp
index 140f0456b5..b928f4dafe 100644
--- a/pcbnew/tools/pcb_viewer_tools.cpp
+++ b/pcbnew/tools/pcb_viewer_tools.cpp
@@ -169,7 +169,7 @@ int PCB_VIEWER_TOOLS::TextOutlines( const TOOL_EVENT& aEvent )
     {
         KICAD_T t = item->Type();
 
-        if( t == PCB_TEXT_T || BaseType( t ) == PCB_DIMENSION_T )
+        if( t == PCB_TEXT_T || t == PCB_TEXTBOX_T || BaseType( t ) == PCB_DIMENSION_T )
             view()->Update( item, KIGFX::REPAINT );
     }
 
diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp
index 8cc8a4dee9..ea7e049a07 100644
--- a/pcbnew/zone_filler.cpp
+++ b/pcbnew/zone_filler.cpp
@@ -560,6 +560,8 @@ void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap,
     {
     case PCB_SHAPE_T:
     case PCB_TEXT_T:
+    case PCB_TEXTBOX_T:
+    case PCB_FP_TEXTBOX_T:
     case PCB_FP_SHAPE_T:
     case PCB_TARGET_T:
         aItem->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError,
@@ -575,8 +577,9 @@ void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap,
             text->TransformShapeWithClearanceToPolygon( aHoles, aLayer, aGap, m_maxError,
                                                         ERROR_OUTSIDE, aIgnoreLineWidth );
         }
-    }
+
         break;
+    }
 
     default:
         break;
diff --git a/qa/pcbnew/test_board_item.cpp b/qa/pcbnew/test_board_item.cpp
index eaca9f96e2..c4e2aeed09 100644
--- a/qa/pcbnew/test_board_item.cpp
+++ b/qa/pcbnew/test_board_item.cpp
@@ -1,7 +1,7 @@
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
- * Copyright (C) 2021 KiCad Developers, see AUTHORS.TXT for contributors.
+ * Copyright (C) 2022 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
@@ -33,7 +33,9 @@
 #include <pad.h>
 #include <pcb_shape.h>
 #include <pcb_text.h>
+#include <pcb_textbox.h>
 #include <fp_text.h>
+#include <fp_textbox.h>
 #include <fp_shape.h>
 #include <zone.h>
 #include <pcb_track.h>
@@ -73,7 +75,9 @@ public:
         case PCB_PAD_T:               return new PAD( &m_footprint );
         case PCB_SHAPE_T:             return new PCB_SHAPE( &m_board );
         case PCB_TEXT_T:              return new PCB_TEXT( &m_board );
+        case PCB_TEXTBOX_T:           return new PCB_TEXTBOX( &m_board );
         case PCB_FP_TEXT_T:           return new FP_TEXT( &m_footprint );
+        case PCB_FP_TEXTBOX_T:        return new FP_TEXTBOX( &m_footprint );
         case PCB_FP_SHAPE_T:          return new FP_SHAPE( &m_footprint );
         case PCB_FP_DIM_ALIGNED_T:    return new PCB_DIM_ALIGNED( &m_footprint, PCB_FP_DIM_ALIGNED_T );
         case PCB_FP_DIM_LEADER_T:     return new PCB_DIM_LEADER( &m_footprint, true );