メイン・コンテンツまでスキップ

カスタム・ウィジェット

アプリケーションの作成時には、TouchGFX配布には含まれていないウィジェットが必要になる場合があります。 TouchGFXでは、ユーザが独自のグラフィック要素を作成できる方法がいくつか用意されています。 最も単純なのはカスタム・コンテナ・アプローチを使用する方法で、既存のウィジェットを独自のウィジェットに結合するものです。 しかしこの記事では、本質的に、フレームバッファを完全制御して望むものを正確に描画することができるウィジェットを作成できるようになる、もっと高度なアプローチを詳しく取り上げます。

カスタム・ウィジェット・アプローチを使用すべき場合

カスタム・ウィジェットの作成は、独自のウィジェットを作成するための最も一般的な方法ではありません。 ユーザのニーズに合っていれば、カスタム・コンテナ・アプローチの方が優先されますが、このアプローチでは十分でない場合もあります。 フレームバッファを完全制御する必要がある場合は、カスタム・ウィジェット・アプローチを使用する必要があります。

カスタム・ウィジェット・アプローチを使用して作成できる(あるいは作成すべき)ウィジェットの例を次に示します。

  • セピア・フィルタ
  • マンデルブロ・フラクタル・ウィジェット
  • ファイルからのデータを表示するウィジェット(gifアニメーションなど)

TouchGFX Designerでの操作

TouchGFX Designerは、現時点ではカスタム・ウィジェットの作成をサポートしていません。 このため、カスタム・ウィジェット用のコードを手動で作成し(作成方法についてはこの記事の残りの部分を参照)、ウィジェットをビューのユーザ・コード部分に挿入する必要があります。

コードでの操作

独自のカスタム・ウィジェットを作成するには、Widgetクラスを拡張します。 このためにはユーザ側の作業がやや多く必要になりますが、ウィジェットによって描画されるすべてのピクセルを完全に制御できるようになります。 カスタム・ウィジェットは必ずしも既存のウィジェットを活用するわけではなく、ピクセルの色を指定することで描画方法を定義します。 一般的にカスタム・ウィジェット・アプローチはメモリ・フットプリントも小さく、場合によっては、このことが極めて重要なポイントになります。

カスタム・ウィジェットの一例として、QRコード・ウィジェットが挙げられます。 この特定のウィジェットはわかりやすい例になるので、以下のセクションではこの作成方法について説明します。 この例で、QRコード・ウィジェットは白黒のピクセルのN x Nのマトリックスになっています。 ウィジェットのサイズと各ピクセルの色は実行時に決定され、QRコード・データ・オブジェクト内の情報によって異なります。

次に、QRコード・ウィジェットの見え方の2つの例を示します。

QRコード・ウィジェットの例

Caution
n x nの黒または白のボックスをコンテナの子として持つことで、カスタム・コンテナ・アプローチでQRコード・ウィジェットを作成することも可能です。 ただし、この方法は大量のメモリを使用し、おそらく効率性や柔軟性が低くなります。
Tip
標準の touchgfx::Button (フレームワークで提供される)は、カスタム・コンテナではなく、カスタム・ウィジェットとして作成されます。 これによりスクリーン上のボタンごとのメモリが節約されます。

独自のカスタム・ウィジェット

Widgetクラスを拡張する、ユーザ独自のウィジェットを作成するには、以下の2つを記述する必要があります。

  • ウィジェットの描画方法
  • ウィジェットの塗りつぶし部分

部分描画

どのウィジェットも(したがってカスタム・ウィジェットも)部分描画をサポートする必要があります。 これは、ウィジェットはその一部のみを描画できるようにする必要があります。

その理由は2点あります。 多くの場合、スクリーン全体を再描画する必要はなく、一部のみを再描画します。 TouchGFXのアルゴリズムでは、スクリーンの描画を複数の部分描画に分割して、描画されるピクセルの全体数を最小化することがあります。 こうしたスクリーンの部分描画により、ウィジェットがその一部のみを描画するように求められることになります。

例として、このQRコード・ウィジェットでは、それ自身のハイライトされた部分のみを描画できるようにする必要があります。

QRコード・ウィジェットの部分描画

コード内でこれを行うには、drawメソッドをオーバーライドします。

virtual void draw(const touchgfx::Rect& invalidatedArea) const
{
//run through the pixels of the invalidated area
//draw a black pixel if the qr data object has a value at this position
//draw a white pixel otherwise
}

invalidatedAreaが、更新の必要があるウィジェットの部分です。 このQRコードの例では、無効化領域はハイライト表示された領域です。 領域は、ウィジェットの左上隅を基準とする座標で表されます。

Caution
このウィジェットの役目は、無効化領域の内部を描画することです。 無効化領域以外を描画した場合、スクリーン全体で正しい動作が得られません。
Tip
**分節不備** draw メソッドは const です。最適化された描画アルゴリズムは、ウィジェットの描画を複数の描画呼び出しに分割する可能性があるからです。 **分節不備** const によって、これらの複数の draw 実行の間にウィジェットが移動したり、更新されたりしなくなります。

塗りつぶし領域

さらに、ウィジェットが、それ自身の塗りつぶし部分の大きさをレポートできるようにする必要があります。 塗りつぶしとは、スクリーン上でその背後にあるものが見えない状態です。 たとえば、標準の赤色のボックスは完全な塗りつぶしですが、アルファ値が50%の画像は完全な非塗りつぶしです。その背後のものがすべて見えるからです。

塗りつぶしウィジェットによってTouchGFXは描画処理の速度を上げることができます。 塗りつぶしウィジェットの下にあるウィジェットを描画する必要がないので、描画の必要があるピクセル数が少なくなるからです。

コード内で塗りつぶし領域をレポートするには、getSolidRectメソッドをオーバーライドします。

virtual Rect getSolidRect() const;

この例のQRコード・ウィジェットは完全な塗りつぶしになります。 黒色と白色のピクセルの背後は何も見えなくなります。 このため、メソッドがウィジェットのフル・サイズの長方形を返すようにします。

virtual Rect getSolidRect() const
{
return touchgfx::Rect(0, 0, getWidth(), getHeight());
}

ソース・コードの例

アプリケーションを終了すると、ウィジェットは次のようになります(提供されるデータによって異なります)。

QRコード・ウィジェットのスクリーンショット

ウィジェットの完全なコードを以下に示します。

gui/include/gui/common/QRCodeWidget.hpp

#ifndef QR_CODE_WIDGET_HPP
#define QR_CODE_WIDGET_HPP

#include <touchgfx/widgets/Widget.hpp>

class QRCodeWidget : public touchgfx::Widget
{
public:
QRCodeWidget();

virtual void draw(const touchgfx::Rect& invalidatedArea) const;
virtual touchgfx::Rect getSolidRect() const;

void setQRCodeData(QRCodeData* data);
void setScale(uint8_t s);

private:
void updateSize();

QRCodeData* data;
uint8_t scale;
};
#endif

ヘッダ・ファイルで、touchgfx::Widgetクラスの拡張としてウィジェットを定義します。 drawメソッドとgetSolidRectメソッドをオーバーライドして、ウィジェットの描画方法を定義します。 QRコードのデータを設定するメソッドと、QRコードのスケーリング・ファクタを設定するメソッドを宣言します。

gui/src/common/QRCodeWidget.cpp
#include <gui/common/QRCodeWidget.hpp>

QRCodeWidget::QRCodeWidget() :
data(0),
scale(1)
{
}

void QRCodeWidget::draw(const touchgfx::Rect& invalidatedArea) const
{
if (!data)
{
return;
}

touchgfx::Rect absolute = getAbsoluteRect();

uint16_t* framebuffer = touchgfx::HAL::getInstance()->lockFrameBuffer();

for (int y = invalidatedArea.y; y < invalidatedArea.bottom(); y++)
{
for (int x = invalidatedArea.x; x < invalidatedArea.right(); x++)
{
framebuffer[absolute.x + x + (absolute.y + y) * touchgfx::HAL::FRAME_BUFFER_WIDTH] =
data->at(x / scale, y / scale) ? 0x0000 : 0xffff;
}
}

touchgfx::HAL::getInstance()->unlockFrameBuffer();
}

touchgfx::Rect QRCodeWidget::getSolidRect() const
{
return touchgfx::Rect(0, 0, getWidth(), getHeight());
}

void QRCodeWidget::setQRCodeData(QRCodeData* qrCode)
{
data = qrCode;
updateSize();
}

void QRCodeWidget::setScale(uint8_t s)
{
scale = s;
updateSize();
}

void QRCodeWidget::updateSize()
{
if (data)
{
setWidth(data->getSize() * scale);
setHeight(data->getSize() * scale);
}
}

ソース・ファイルではdrawメソッドを定義します。 このメソッドは、無効化領域内のピクセルごとに処理を進め、データ・オブジェクトとスケーリング・ファクタのコンテンツに基づいてフレームバッファの色を決定します。

getSolidRectメソッドは完全な塗りつぶしとしてウィジェットをレポートします。

Caution
ここでは draw メソッドの一部としてフレームバッファをロックおよびロック解除しました。 これは、描画の開始時に確実にDMAが描画を終えているようにするためです。

このコードでは小さいクラス / インタフェースを使用して、QRコードのデータにもアクセスします。

class QRCodeData
{
public:
uint8_t getSize();
bool at(uint8_t x, uint8_t y);
};
Further reading
QRCodeウィジェットとLensウィジェットをダウンロードしてチェックしてください。
Things to try
  • 白色のピクセルが完全に透明になるようにQRコード・ウィジェットを変更します。
  • セピア・フィルタ、マンデルブロ・フラクタル、gif画像、その他を表示するカスタム・ウィジェットを作成します。
  • カスタム・コンテナを使用して最も簡単に実行できるウィジェットや、カスタム・ウィジェット・アプローチを使用して最も簡単に実現できるウィジェットについて検討します。
  • 標準ウィジェット / コンテナの変更

    標準ウィジェットのソース・コードは、TouchGFXのインストール時に含まれます。 これらはアプリケーションのニーズに合わせて変更することもできます。 標準ウィジェットおよびコンテナのソース・コードは次のフォルダ内に配置されています。

    Middlewares\ST\touchgfx\framework\source\touchgfx\widgets

    後方互換性を維持するために、コア・ライブラリには標準ウィジェットおよびコンテナのコンパイル・バージョンが含まれています。 したがって、これらのファイルをプロジェクト内でコンパイルする必要はありません。

    Caution
    標準ウィジェット / コンテナを直接変更することは推奨されません。

    ソース・コードは、TouchGFXウィジェットの内部動作を直感的に理解し習得することを主な目的としています。 何か標準実装とは異なる動作が必要な場合には、カスタム・コンテナをサブクラス化または作成することで達成できます。これは推奨されるアプローチでもあります。

    この推奨の理由は次の2つの要素によるものです。

    • 1つ目は、これまでに行った変更を手動でマージする必要があるために、TouchGFXの新しいバージョンへのアップグレードが困難になることです。
    • 2つ目は、特定の動作に依存する標準ウィジェットおよびコンテナを壊す危険が生じることです。

    したがって、標準ウィジェット / コンテナを変更する必要がある場合は、ソース・コードを直接変更するのではなく、コピーを作成し、名前を変更して使用することをお勧めします。

    とは言うものの、開発者が適切だと判断したことを実行するのは自由です。 標準ウィジェットのソース・コードをプロジェクトに追加すると、リンカは(コア・ライブラリにあるものではなく)そちらのバージョンを選択します。 この結果、ソース・コードを自分のコンパイルに追加することで、標準ウィジェットの変更にアクセスできるようになります。