]> git.sesse.net Git - kdenlive/commitdiff
Colorplane export changes:
authorSimon A. Eugster <simon.eu@gmail.com>
Sat, 17 Jul 2010 14:01:51 +0000 (14:01 +0000)
committerSimon A. Eugster <simon.eu@gmail.com>
Sat, 17 Jul 2010 14:01:51 +0000 (14:01 +0000)
* YUV plane with Y varying can be exported (ratio U:V fixed)
* RGB plane with two components on one and one component on other axis can be exported
  Usage: e.g. for Curves widget, to visualize color changes
Vectorscope changes:
* Realtime factor shown
* Pixel brightness relative to image size for Green (still needs to be tweaked)

svn path=/trunk/kdenlive/; revision=4587

src/colorplaneexport.cpp
src/colorplaneexport.h
src/colortools.cpp
src/colortools.h
src/vectorscope.cpp
src/widgets/colorplaneexport_ui.ui

index 95a2ab68fa47eed40d5744a92ea16ef207f9af77..d773486d7ee470787c658fc0f24f4af5dc5479df 100644 (file)
@@ -25,8 +25,10 @@ ColorPlaneExport::ColorPlaneExport(QWidget *parent) :
     tResX->setText("800");
     tResY->setText("800");
 
-    cbColorspace->addItem(i18n("YUV"), QVariant(CPE_YUV));
+    cbColorspace->addItem(i18n("YUV UV plane"), QVariant(CPE_YUV));
+    cbColorspace->addItem(i18n("YUV Y plane"), QVariant(CPE_YUV_Y));
     cbColorspace->addItem(i18n("Modified YUV (Chroma)"), QVariant(CPE_YUV_MOD));
+    cbColorspace->addItem(i18n("RGB plane, one component varying"), QVariant(CPE_RGB_CURVE));
 
     sliderColor->setSliderPosition(128);
 
@@ -43,6 +45,7 @@ ColorPlaneExport::ColorPlaneExport(QWidget *parent) :
     connect(kurlrequester, SIGNAL(textChanged(QString)), this, SLOT(slotValidate()));
     connect(sliderColor, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateDisplays()));
     connect(sliderScaling, SIGNAL(valueChanged(int)), this, SLOT(slotUpdateDisplays()));
+    connect(cbColorspace, SIGNAL(currentIndexChanged(int)), this, SLOT(slotColormodeChanged()));
 
     kurlrequester->setText("/tmp/yuv-plane.png");
 
@@ -55,12 +58,53 @@ ColorPlaneExport::~ColorPlaneExport()
     delete m_colorTools;
 }
 
+
+
+///// Helper functions /////
+
+void ColorPlaneExport::enableSliderScaling(const bool &enable)
+{
+    sliderScaling->setEnabled(enable);
+    lblScaling->setEnabled(enable);
+    lblScaleNr->setEnabled(enable);
+}
+
+void ColorPlaneExport::enableSliderColor(const bool &enable)
+{
+    sliderColor->setEnabled(enable);
+    lblSliderName->setEnabled(enable);
+    lblColNr->setEnabled(enable);
+}
+
+void ColorPlaneExport::enableCbVariant(const bool &enable)
+{
+   cbVariant->setEnabled(enable);
+   lblVariant->setEnabled(enable);
+   if (!enable) {
+       while (cbVariant->count() > 0) {
+           cbVariant->removeItem(0);
+       }
+   }
+}
+
+
+
+///// Slots /////
+
 void ColorPlaneExport::slotUpdateDisplays()
 {
     m_scaling = 1 - (float)sliderScaling->value()/100;
 
     lblScaleNr->setText("0..." + QString::number(m_scaling, 'f', 2));
-    lblColNr->setText(QString::number(sliderColor->value()));
+
+    switch (cbColorspace->itemData(cbColorspace->currentIndex()).toInt()) {
+    case CPE_YUV_Y:
+        lblColNr->setText(i18n("%1 °", QString::number(sliderColor->value())));
+        break;
+    default:
+        lblColNr->setText(QString::number(sliderColor->value()));
+        break;
+    }
 
     lblSize->setText(i18n("%1 px", QVariant(tResX->text()).toInt()*QVariant(tResY->text()).toInt()));
 }
@@ -106,23 +150,55 @@ void ColorPlaneExport::slotExportPlane()
     case CPE_YUV:
         img = m_colorTools->yuvColorWheel(size, sliderColor->value(), m_scaling, false, false);
         break;
+    case CPE_YUV_Y:
+        img = m_colorTools->yuvVerticalPlane(size, sliderColor->value(), m_scaling);
+        break;
     case CPE_YUV_MOD:
         img = m_colorTools->yuvColorWheel(size, sliderColor->value(), m_scaling, true, false);
         break;
+    case CPE_RGB_CURVE:
+        img = m_colorTools->rgbCurvePlane(size, (ColorTools::ColorsRGB) (cbVariant->itemData(cbVariant->currentIndex()).toInt()));
+        break;
     }
     img.save(kurlrequester->text());
 }
 
 void ColorPlaneExport::slotColormodeChanged()
 {
+    qDebug() << "Color mode changed to " << cbColorspace->itemData(cbColorspace->currentIndex()).toInt();
     switch (cbColorspace->itemData(cbColorspace->currentIndex()).toInt()) {
     case CPE_YUV:
     case CPE_YUV_MOD:
-        sliderColor->setVisible(true);
+        enableSliderScaling(true);
+        enableSliderColor(true);
+        enableCbVariant(false);
         sliderColor->setRange(0,255);
+        sliderColor->setPageStep(128);
+        lblSliderName->setText(i18n("Y value"));
+        lblSliderName->setToolTip(i18n("The Y value describes the brightness of the colors."));
+        break;
+    case CPE_YUV_Y:
+        qDebug() << "Changing slider range.";
+        enableSliderScaling(true);
+        enableSliderColor(true);
+        enableCbVariant(false);
+        sliderColor->setMaximum(321);
+        sliderColor->setRange(0,179);
+        sliderColor->setPageStep(90);
+        lblSliderName->setText(i18n("UV angle"));
+        lblSliderName->setToolTip(i18n("Angle through the UV plane, with all possible Y values."));
         break;
+    case CPE_RGB_CURVE:
+        // deliberately fall through
     default:
-        sliderColor->setVisible(false);
+        enableSliderScaling(false);
+        enableSliderColor(false);
+        enableCbVariant(true);
+        cbVariant->addItem(i18n("Red"), QVariant(ColorTools::COL_R));
+        cbVariant->addItem(i18n("Green"), QVariant(ColorTools::COL_G));
+        cbVariant->addItem(i18n("Blue"), QVariant(ColorTools::COL_B));
         break;
     }
+    this->update();
+    slotUpdateDisplays();
 }
index 32ec2bbea7f4c14f41991151a9df298e504b5b0d..48225586a2cca8b39e43e0013ab8ba582b3a7c13 100644 (file)
@@ -22,7 +22,7 @@
 
 class ColorPlaneExport_UI;
 
-enum COLOR_EXPORT_MODE { CPE_YUV, CPE_YUV_MOD };
+enum COLOR_EXPORT_MODE { CPE_YUV, CPE_YUV_Y, CPE_YUV_MOD, CPE_RGB_CURVE };
 
 class ColorPlaneExport : public QDialog, public Ui::ColorPlaneExport_UI {
     Q_OBJECT
@@ -36,6 +36,9 @@ private:
     ColorTools *m_colorTools;
     float m_scaling;
     float m_Y;
+    void enableSliderScaling(const bool &enable);
+    void enableSliderColor(const bool &enable);
+    void enableCbVariant(const bool &enable);
 
 
 private slots:
index cc49e9ef451bbd6dbcb27e63d2a1f73a0a8b784b..70772cb5578df173c04d59a5df129ed584444bea 100644 (file)
@@ -17,7 +17,7 @@ ColorTools::ColorTools()
 
 
 
-QImage ColorTools::yuvColorWheel(const QSize &size, unsigned char Y, float scaling, bool modifiedVersion, bool circleOnly)
+QImage ColorTools::yuvColorWheel(const QSize &size, const unsigned char &Y, const float &scaling, const bool &modifiedVersion, const bool &circleOnly)
 {
     QImage wheel(size, QImage::Format_ARGB32);
     if (size.width() == 0 || size.height() == 0) {
@@ -89,3 +89,85 @@ QImage ColorTools::yuvColorWheel(const QSize &size, unsigned char Y, float scali
     emit signalWheelCalculationFinished();
     return wheel;
 }
+
+QImage ColorTools::yuvVerticalPlane(const QSize &size, const float &angle, const float &scaling)
+{
+    QImage plane(size, QImage::Format_ARGB32);
+    if (size.width() == 0 || size.height() == 0) {
+        qCritical("ERROR: Size of the color plane must not be 0!");
+        return plane;
+    }
+
+    double dr, dg, db, du, dv, Y;
+    const int w = size.width();
+    const int h = size.height();
+    const double uscaling = scaling*cos(M_PI*angle/180);
+    const double vscaling = scaling*sin(M_PI*angle/180);
+
+    for (int uv = 0; uv < w; uv++) {
+        du = uscaling*((double)2*uv/w - 1);//(double)?
+        dv = vscaling*((double)2*uv/w - 1);
+
+        for (int y = 0; y < h; y++) {
+            Y = (double)255*y/h;
+
+            // See yuv2rgb, yuvColorWheel
+            dr = Y + 290.8*dv;
+            dg = Y - 100.6*du - 148*dv;
+            db = Y + 517.2*du;
+            if (dr < 0) dr = 0;
+            if (dg < 0) dg = 0;
+            if (db < 0) db = 0;
+            if (dr > 255) dr = 255;
+            if (dg > 255) dg = 255;
+            if (db > 255) db = 255;
+
+            plane.setPixel(uv, (h-y-1), qRgba(dr, dg, db, 255));
+
+        }
+    }
+
+
+    return plane;
+
+}
+
+QImage ColorTools::rgbCurvePlane(const QSize &size, const ColorsRGB &color)
+{
+    QImage plane(size, QImage::Format_ARGB32);
+    if (size.width() == 0 || size.height() == 0) {
+        qCritical("ERROR: Size of the color plane must not be 0!");
+        return plane;
+    }
+
+    const int w = size.width();
+    const int h = size.height();
+
+    double dcol, dval;
+
+    for (int x = 0; x < w; x++) {
+        dval = (double)255*x/w;
+
+        for (int y = 0; y < h; y++) {
+            dcol = (double)255*y/h;
+
+            if (color == ColorTools::COL_R) {
+                plane.setPixel(x, (h-y-1), qRgb(dcol, dval, dval));
+            } else if (color == ColorTools::COL_G) {
+                plane.setPixel(x, (h-y-1), qRgb(dval, dcol, dval));
+            } else {
+                plane.setPixel(x, (h-y-1), qRgb(dval, dval, dcol));
+            }
+
+        }
+    }
+    return plane;
+}
+
+
+
+
+
+
+
+
index 6a4ec19d3e12c658d39c0162fdd3932c8b36cc18..7ba179167b69927a2ac798c22a45bfb539e73f31 100644 (file)
@@ -17,6 +17,8 @@
 
 #include <QImage>
 
+
+
 class ColorTools : public QObject
 {
     Q_OBJECT
@@ -24,6 +26,8 @@ class ColorTools : public QObject
 public:
     ColorTools();
 
+    enum ColorsRGB { COL_R, COL_G, COL_B };
+
     /**
       @brief Draws a UV plane with given Y value.
       scaling defines how far to zoom in (or out). Lower value = zoom in.
@@ -31,7 +35,23 @@ public:
       If not the full rect should be filled, set circleOnly to true.
       See also: http://en.wikipedia.org/wiki/YUV and http://de.wikipedia.org/wiki/Vektorskop
      */
-    QImage yuvColorWheel(const QSize& size, const unsigned char Y, const float scaling, const bool modifiedVersion, const bool circleOnly);
+    QImage yuvColorWheel(const QSize& size, const unsigned char &Y, const float &scaling, const bool &modifiedVersion, const bool &circleOnly);
+    /**
+      @brief Draws a UV plane with given UV angle (ratio u:v stays constant)
+      scaling defines how far to zoom in (or out). Lower value = zoom in.
+      angle defines the angle in a default U/V plane. A vertical plane, on which Y goes from 0 to 1,
+      is then laid through the UV plane, with the defined angle.
+      @see yuvColorWheel()
+     */
+    QImage yuvVerticalPlane(const QSize &size, const float &angle, const float &scaling);
+    /**
+      @brief Draws a RGB plane with two values on one axis and one on the other.
+      This is e.g. useful as background for a curves dialog. On the line from bottom left to top right
+      are neutral colors. The colors on the y axis show what the neutral color will look like when modifying the curve.
+      color defines the color to modify on the y axis. The other two components will be increased
+      in equal terms (linear as well) on the x axis.
+     */
+    QImage rgbCurvePlane(const QSize &size, const ColorTools::ColorsRGB &color);
 
 signals:
     void signalWheelCalculationFinished();
index 20f739795b73569851e12927ad3802fe27673041..f30fbfa9fda068316d533f59a8b2d23e825038e6 100644 (file)
@@ -251,6 +251,10 @@ void Vectorscope::calculateScope()
     QPoint pt;
     QRgb px;
 
+    // Just an average for the number of image pixels per scope pixel.
+    double avgPxPerPx = (double) 4*img.byteCount()/scope.size().width()/scope.size().height()/skipPixels;
+    qDebug() << "Expecting " << avgPxPerPx << " pixels per pixel.";
+
     const QRect scopeRect(QPoint(0,0), scope.size());
 
     for (int i = 0; i < img.byteCount(); i+= stepsize) {
@@ -318,12 +322,13 @@ void Vectorscope::calculateScope()
                 break;
             case PAINT_GREEN:
                 px = scope.pixel(pt);
-                scope.setPixel(pt, qRgba(qRed(px)+(255-qRed(px))/30, 255, qBlue(px)+(255-qBlue(px))/25, qAlpha(px)+(255-qAlpha(px))/20));
+                scope.setPixel(pt, qRgba(qRed(px)+(255-qRed(px))/(3*avgPxPerPx), qGreen(px)+20*(255-qGreen(px))/(avgPxPerPx),
+                                         qBlue(px)+(255-qBlue(px))/(avgPxPerPx), qAlpha(px)+(255-qAlpha(px))/(avgPxPerPx)));
                 break;
             case PAINT_GREEN2:
                 px = scope.pixel(pt);
-                scope.setPixel(pt, qRgba(qRed(px)+ceil((255-(float)qRed(px))/30), 255,
-                                         qBlue(px)+ceil((255-(float)qBlue(px))/25), qAlpha(px)+ceil((255-(float)qAlpha(px))/20)));
+                scope.setPixel(pt, qRgba(qRed(px)+ceil((255-(float)qRed(px))/(4*avgPxPerPx)), 255,
+                                         qBlue(px)+ceil((255-(float)qBlue(px))/(avgPxPerPx)), qAlpha(px)+ceil((255-(float)qAlpha(px))/(avgPxPerPx))));
                 break;
             case PAINT_BLACK:
                 px = scope.pixel(pt);
@@ -498,6 +503,13 @@ void Vectorscope::paintEvent(QPaintEvent *)
 
         circleEnabled = false;
     }
+
+    // Draw realtime factor (number of skipped pixels)
+    if (m_aRealtime->isChecked()) {
+        davinci.setPen(penThin);
+        davinci.drawText(m_scopeRect.bottomRight()-QPoint(40,15), QVariant(m_skipPixels).toString().append("x"));
+    }
+
 }
 
 
index 10a01212aa4b8be3215f311abb0a9496af54368f..624a771141e2e6a323c7e9c2e7d5e6093be72b92 100644 (file)
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>488</width>
-    <height>213</height>
+    <width>553</width>
+    <height>241</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Export color plane to PNG</string>
   </property>
-  <widget class="QWidget" name="gridLayoutWidget">
-   <property name="geometry">
-    <rect>
-     <x>10</x>
-     <y>10</y>
-     <width>471</width>
-     <height>193</height>
-    </rect>
-   </property>
-   <layout class="QGridLayout" name="gridLayout">
-    <property name="horizontalSpacing">
-     <number>10</number>
-    </property>
-    <item row="0" column="0">
-     <widget class="QLabel" name="lblSpace">
-      <property name="text">
-       <string>Color space</string>
-      </property>
-     </widget>
-    </item>
-    <item row="0" column="1" colspan="3">
-     <widget class="QComboBox" name="cbColorspace">
-      <property name="sizePolicy">
-       <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
-        <horstretch>0</horstretch>
-        <verstretch>0</verstretch>
-       </sizepolicy>
-      </property>
-     </widget>
-    </item>
-    <item row="3" column="0">
-     <widget class="QLabel" name="lblRes">
-      <property name="text">
-       <string>Resolution</string>
-      </property>
-     </widget>
-    </item>
-    <item row="3" column="1">
-     <widget class="QLineEdit" name="tResX">
-      <property name="maxLength">
-       <number>5</number>
-      </property>
-     </widget>
-    </item>
-    <item row="3" column="3">
-     <widget class="QLineEdit" name="tResY">
-      <property name="maxLength">
-       <number>5</number>
-      </property>
-     </widget>
-    </item>
-    <item row="3" column="2">
-     <widget class="QLabel" name="lblTimes">
-      <property name="text">
-       <string notr="true">×</string>
-      </property>
-     </widget>
-    </item>
-    <item row="5" column="0">
-     <widget class="QLabel" name="lblFilename">
-      <property name="text">
-       <string>Filename</string>
-      </property>
-     </widget>
-    </item>
-    <item row="5" column="1" colspan="3">
-     <widget class="KUrlRequester" name="kurlrequester">
-      <property name="mode">
-       <set>KFile::File|KFile::LocalOnly</set>
-      </property>
-     </widget>
-    </item>
-    <item row="4" column="1" colspan="3">
-     <widget class="QLabel" name="lblSize">
-      <property name="font">
-       <font>
-        <pointsize>8</pointsize>
-       </font>
-      </property>
-      <property name="text">
-       <string notr="true">(notranslate) Total pixels go here</string>
-      </property>
-      <property name="alignment">
-       <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-      </property>
-     </widget>
-    </item>
-    <item row="6" column="1" colspan="3">
-     <widget class="QDialogButtonBox" name="buttonBox">
-      <property name="orientation">
-       <enum>Qt::Horizontal</enum>
-      </property>
-      <property name="standardButtons">
-       <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
-      </property>
-     </widget>
-    </item>
-    <item row="1" column="1" colspan="2">
-     <widget class="QSlider" name="sliderColor">
-      <property name="toolTip">
-       <string>The Y value describes the brightness of the colors.</string>
-      </property>
-      <property name="whatsThis">
-       <string/>
-      </property>
-      <property name="orientation">
-       <enum>Qt::Horizontal</enum>
-      </property>
-     </widget>
-    </item>
-    <item row="1" column="0">
-     <widget class="QLabel" name="lblSliderName">
-      <property name="text">
-       <string>Y value</string>
-      </property>
-     </widget>
-    </item>
-    <item row="1" column="3">
-     <widget class="QLabel" name="lblColNr">
-      <property name="text">
-       <string notr="true">(notranslate) Y display</string>
-      </property>
-     </widget>
-    </item>
-    <item row="2" column="1" colspan="2">
-     <widget class="QSlider" name="sliderScaling">
-      <property name="toolTip">
-       <string>How much to zoom in</string>
-      </property>
-      <property name="whatsThis">
-       <string/>
-      </property>
-      <property name="orientation">
-       <enum>Qt::Horizontal</enum>
-      </property>
-     </widget>
-    </item>
-    <item row="2" column="3">
-     <widget class="QLabel" name="lblScaleNr">
-      <property name="text">
-       <string>(notranslate) % display</string>
-      </property>
-     </widget>
-    </item>
-    <item row="2" column="0">
-     <widget class="QLabel" name="lblScaling">
-      <property name="text">
-       <string>Scaling</string>
-      </property>
-     </widget>
-    </item>
-   </layout>
-  </widget>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="lblSpace">
+     <property name="text">
+      <string>Color space</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1" colspan="3">
+    <widget class="QComboBox" name="cbColorspace">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1" colspan="2">
+    <widget class="QLabel" name="lblVariant">
+     <property name="text">
+      <string>Variant</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="3">
+    <widget class="QComboBox" name="cbVariant"/>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="lblSliderName">
+     <property name="text">
+      <string notr="true">(notr.ansl.)</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QSlider" name="sliderColor">
+     <property name="whatsThis">
+      <string/>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="3">
+    <widget class="QLabel" name="lblColNr">
+     <property name="text">
+      <string notr="true">(notranslate) Y display</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="lblScaling">
+     <property name="text">
+      <string>Scaling</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QSlider" name="sliderScaling">
+     <property name="toolTip">
+      <string>How much to zoom in</string>
+     </property>
+     <property name="whatsThis">
+      <string/>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="3">
+    <widget class="QLabel" name="lblScaleNr">
+     <property name="text">
+      <string>(notranslate) % display</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QLabel" name="lblRes">
+     <property name="text">
+      <string>Resolution</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QLineEdit" name="tResX">
+     <property name="maxLength">
+      <number>5</number>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="2">
+    <widget class="QLabel" name="lblTimes">
+     <property name="text">
+      <string notr="true">×</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="3">
+    <widget class="QLineEdit" name="tResY">
+     <property name="maxLength">
+      <number>5</number>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="1" colspan="3">
+    <widget class="QLabel" name="lblSize">
+     <property name="font">
+      <font>
+       <pointsize>8</pointsize>
+      </font>
+     </property>
+     <property name="text">
+      <string notr="true">(notranslate) Total pixels go here</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="0">
+    <widget class="QLabel" name="lblFilename">
+     <property name="text">
+      <string>Filename</string>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="1" colspan="3">
+    <widget class="KUrlRequester" name="kurlrequester">
+     <property name="mode">
+      <set>KFile::File|KFile::LocalOnly</set>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="1" colspan="3">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
  </widget>
  <customwidgets>
   <customwidget>