Gadgets > ガジェットのカスタマイズ > 手順 3:複合ガジェットの作成
 
手順 3:複合ガジェットの作成
この手順では、新しいガジェットの作成方法、つまり IlvGadget クラスの直接サブクラスの作成方法を説明します。作成するガジェットは、3 つのガジェット、つまり、1 個のテキスト・フィールドおよび 2 つのボタンから構成される複合ガジェットです。UpDownField ガジェットは、次のように表示されます。
この手順では、次のタスクを実行する方法を紹介します。
*他のガジェットから構成されるガジェットの作成
*ガジェットでキーボード・フォーカスを処理する
*ガジェットでのイベント処理
*ガジェットへコールバックを追加する
*複合ガジェットのテスト
他のガジェットから構成されるガジェットの作成
複合ガジェットの作成とは、1 つ以上の既存のガジェットから構成される新しいガジェットを作成するということです。スクロール操作を可能にする内部 IlvScrollBar オブジェクトを有する IlvTreeGadget がこれに相当します。これはまた、この手順で作成される UpDownField クラスにもあてはまります。
複合ガジェットの宣言
UpDownField が構成されているオブジェクトへのポインターを維持するために、保護されたメンバー変数を使用します。
class UpDownField
: public IlvGadget
{
....
protected:
IlvTextField* _textField;
IlvButton* _rightButton;
IlvButton* _leftButton;
};
コンポーネントの作成
最初に、複合ガジェットを構成するオブジェクトを作成しなくてはなりません。UpDownField クラスは、クラスの各コンストラクターに呼び出される保護されたメンバー関数 init を持っています。
void
UpDownField::init(const char* label)
{
// Compute the bounding boxes of each element.
IlvRect r1, r2, r3;
computeRects(r1, r2, r3);
// Text field.
_textField =
new IlvTextField(getDisplay(),
label,
r2,
getThickness(),
getPalette());
_focusGadget = _textField;
// Left Button.
_leftButton = new IlvButton(getDisplay(), "-", r1, getThickness(),
getPalette());
_leftButton->setCallback(_internal_Down, this);
// Right Button.
_rightButton = new IlvButton(getDisplay(), "+", r3, getThickness(),
getPalette());
_rightButton->setCallback(_internal_Up, this);
}
init メソッドはまず、全オブジェクトのバウンディング・ボックスを computeRects メンバー関数を呼び出して計算します。それから、オブジェクトを作成して初期化します。
メモ: 2 つのボタンにはコールバックが与えられます。左ボタンのコールバックは、UpDownField::decrement メソッドを呼び出し、右ボタンコールバックは UpDownField::increment メソッドを呼び出します。
レイアウト
以下に、UpDownField クラスのレイアウトを担当する computeRects メソッドの記述です。
void
UpDownField::computeRects(IlvRect& r1,
IlvRect& r2,
IlvRect& r3,
const IlvTransformer* t) const
{
IlvRect rect = _drawrect;
if (t)
t->apply(rect);
r1.moveResize(rect.x(), rect.y(), (IlvDim)ButtonWidth, rect.h());
IlvDim width = rect.w() - (2*(ButtonWidth + Margin));
r2.moveResize(rect.x() + (IlvPos)(ButtonWidth + Margin),
rect.y(),
(IlvDim)IlvMax(width, (IlvDim)0),
rect.h());
IlvPos deltaX = (IlvPos)(rect.w() - ButtonWidth);
r3.moveResize(rect.x() + (IlvPos)IlvMax(deltaX, (IlvPos)0),
rect.y(),
ButtonWidth,
rect.h());
r1.intersection(rect);
r2.intersection(rect);
r3.intersection(rect);
}
*r1 は、左ボタンのバウンディング・ボックスです。
*r2 は、テキスト・フィールドのバウンディング・ボックスです。
*r3 は、右ボタンのバウンディング・ボックスです。
このメソッドは、毎回複合ガジェットを移動、リサイズするたびに呼び出されます。すべてのグラフィック・オブジェクトと同様に、applyTransform メソッドはどちらのケースでも呼び出されます。
void
UpDownField::applyTransform(const IlvTransformer* t)
{
IlvGadget::applyTransform(t);
IlvRect r1, r2, r3;
computeRects(r1, r2, r3);
_rightButton->moveResize(r3);
_textField->moveResize(r2);
_leftButton->moveResize(r1);
}
これはまた、コンポーネント・バウンディング・ボックスについて複合ガジェットに問い合わせを行うのにも使用されます。
コンポーネントの描画
以上で、複合ガジェットのコンポーネントが配置されました。複合ガジェットは、これらを各コンポーネントの draw メンバー関数を呼び出して描画します。
void
UpDownField::draw(IlvPort* dst,
const IlvTransformer* t,
const IlvRegion* clip) const
{
_textField->draw(dst, t, clip);
_rightButton->draw(dst, t, clip);
_leftButton->draw(dst, t, clip);
}
IlvGraphic メンバー関数の再定義
IlvGraphic クラスのいくつかのメンバー関数は、複合ガジェットのコンポーネントへの委譲を可能にするために再定義されています。setPalette メソッドを例として挙げます。
void
UpDownField::setPalette(IlvPalette* palette)
{
IlvGadget::setPalette(palette);
_textField->setPalette(palette);
_rightButton->setPalette(palette);
_leftButton->setPalette(palette);
}
UpDownField のパレットが変更されると、UpDownField の各コンポーネントに同じパレットが設定されます。
最後に、setHolder メソッドを次のようにオーバーライドしなくてはなりません。
void
UpDownField::setHolder(IlvGraphicHolder* holder)
{
IlvGadget::setHolder(holder);
_textField->setHolder(holder);
_rightButton->setHolder(holder);
_leftButton->setHolder(holder);
}
このメソッドは、UpDownField がホルダーに追加、あるいは削除されるたびに呼び出されます。これによって UpDownField のコンポーネントすべてが同じホルダーを持つようになります。ガジェットが適切に振る舞うためにはホルダーを必要とするため、これは必須です。
ガジェットでキーボード・フォーカスを処理する
グラフィックおよびガジェット・クラスでのイベント処理の主な違いは、ガジェットはキーボード・フォーカスを持てるという点です。ガジェットがキーボード・フォーカスを持てば、キーボード・イベントはこのガジェットに送られるようになります。詳細は、ガジェットの理解にある「フォーカス管理」を参照してください。
キーボード・フォーカス処理には、次のような意味があります。
*フォーカス・イベントに反応すること。ガジェットにフォーカスが与えられようとするとき、IlvKeyboardFocusIn イベントを受け取ります。同様に、ガジェットがフォーカスを失いかけるとき、IlvKeyboardFocusOut イベントを受け取ります。
*フォーカス描画の処理。フォーカスの描画機構には 2 つのメソッドが含まれています。IlvGraphic::computeFocusRegion および IlvGraphic::drawFocus です。最初のメソッドは、フォーカス描画の位置に関する情報を与えます。
2 番目のメソッドはフォーカスを描画します。
UpDownField の各コンポーネントにフォーカスを与えることができなくてはなりません。フォーカスが UpDownField に与えられると、それを送信するために選んだコンポーネントに転送されます。保護されたメンバー変数が、UpDownField の現在のフォーカス・コンポーネントにポインターを維持するために追加されます。
protected:
IlvGadget* _focusGadget;
setFocus という名前のメソッドも、UpDownField 内の現在フォーカスされているオブジェクトを変更するために追加されます。
void
UpDownField::setFocus(IlvGadget* gadget)
{
IlvRegion region;
// Send a focus_out event to the gadget that loses the focus.
if (_focusGadget) {
IlvEvent fo;
fo._type = IlvKeyboardFocusOut;
_focusGadget->computeFocusRegion(region, getTransformer());
_focusGadget->handleEvent(fo);
_focusGadget = 0;
}
_focusGadget = gadget;
// Send a focus_in event to the gadget that receives the focus.
if (_focusGadget) {
IlvEvent fi;
fi._type = IlvKeyboardFocusIn;
_focusGadget->handleEvent(fi);
_focusGadget->computeFocusRegion(region, getTransformer());
}
if (getHolder())
getHolder()->reDraw(&region);
}
メソッドは最初に IlvKeyboardFocusOut イベントをキーボード・フォーカスを失うコンポーネントに送ります。それから、IlvKeyboardFocusIn イベントを新しくフォーカスされたコンポーネントに送ります。最後に、変更された領域が IlvGraphicHolder API を使用して再描画されます。
以下はフォーカスの描画方法を表しています。computeFocusRegion および drawFocusメソッドがこのプロセスに含まれています。
void
UpDownField::drawFocus(IlvPort* dst,
const IlvPalette* palette,
const IlvTransformer* t,
const IlvRegion* clip) const
{
_focusGadget->drawFocus(dst, palette, t, clip);
}
 
void
UpDownField::computeFocusRegion(IlvRegion& region,
const IlvTransformer* t) const
{
_focusGadget->computeFocusRegion(region, t);
}
UpDownField は、現在フォーカスされているオブジェクトに委譲します。次のセクションでは、UpDownField のキーボード・フォーカスをどのように変更するかを説明します。
ガジェットでのイベント処理
ガジェット・クラスのイベント用エントリー・ポイントは、IlvGadget::handleEvent メソッドです。このメソッドは、ガジェット・ホルダーがガジェットによって処理するイベントを受け取ったときにガジェット・ホルダーによって呼び出されます。フォーカスされたオブジェクトへイベントのほとんどを委譲するため、UpDownField::handleEvent は非常に単純です (_focusGadget メンバー変数によってポイントされています)。
IlvBoolean
UpDownField::handleEvent(IlvEvent& event)
{
IlvBoolean result = IlvFalse;
switch (event.type()) {
case IlvButtonDown:
{
// Changing focus on click
IlvRect r1, r2, r3;
IlvPoint evp(event.x(), event.y());
computeRects(r1, r2, r3, getTransformer());
if (r2.contains(evp) && _focusGadget != _textField)
setFocus(_textField);
else
if (r3.contains(evp) && _focusGadget != _rightButton)
setFocus(_rightButton);
else
if (r1.contains(evp) && _focusGadget != _leftButton)
setFocus(_leftButton);
result = _focusGadget->handleEvent(event);
break;
}
case IlvKeyDown:
{
// Moving focus with the Tab key.
if (event.data() == IlvTab &&
(!(event.modifiers() & IlvShiftModifier)) &&
(!(event.modifiers() & IlvCtrlModifier))) {
if (_focusGadget == _textField)
setFocus(_rightButton);
else
if (_focusGadget == _rightButton)
setFocus(_leftButton);
else
if (_focusGadget == _leftButton)
setFocus(_textField);
return IlvTrue;
}
// Moving focus with the Shift Tab key.
if (event.data() == IlvTab &&
((event.modifiers() & IlvShiftModifier)) &&
(!(event.modifiers() & IlvCtrlModifier))) {
if (_focusGadget == _leftButton)
setFocus(_rightButton);
else
if (_focusGadget == _rightButton)
setFocus(_textField);
else
if (_focusGadget == _textField)
setFocus(_leftButton);
return IlvTrue;
}
}
default:
result = _focusGadget->handleEvent(event);
}
return result;
}
handleEvent メソッドの主要部分は、キーボード・フォーカス管理を処理します。IlvButtonDown のケースは、複合ガジェットがボタン・ダウン・イベントを受け取ったときに、現在フォーカスされているオブジェクトの変更を行います。IlvKeyDown ケースは、複合ガジェットが TAB キーあるいは Shift-TAB キー・イベントを受け取ったときに、現在フォーカスされているオブジェクトの変更を行います。デフォルト・ケースは、単に現在フォーカスされているオブジェクトに委譲します。
ガジェットへコールバックを追加する
この手順の最初に示したように、UpDownField の左および右ボタンには、押されたときに通知を可能にするコールバックが割り当てられています。2 つのコールバックが定義されています。左ボタンが押されたときに呼び出される Down コールバック、および右ボタンが押されたときに呼び出される Up コールバックです。
これらのコールバックは、getCallbackType メソッドで宣言されています。
IlvUInt
UpDownField::getCallbackTypes(const char* const** names,
const IlvSymbol* const** types) const
{
IlvUInt count = IlvGadget::getCallbackTypes(names, types);
AddToCallbackTypeList(count, names, types,
"Down", downCallbackType());
AddToCallbackTypeList(count, names, types,
"Up", upCallbackType());
return count;
}
メモ: このメソッドを再定義することは必須ではありませんが、UpDownField によって処理されているコールバックのリストをエディター (Rogue Wave Views Studio など) が問い合わせることができるようになるため、エディターでの UpDownField の統合が容易になります。
次に、UpDownField::increment メソッドが Up コールバックを呼び出すために実装され、UpDownField::decrement メソッドが Down コールバックを呼び出すために実装されます。
void
UpDownField::increment()
{
callCallbacks(upCallbackType());
}
 
void
UpDownField::decrement()
{
callCallbacks(downCallbackType());
}
複合ガジェットのテスト
ファイル main.cpp は、UpDownField クラスをテストするコードを含んでいます。テキスト・フィールドの値を変更するために UpDownField クラスの Up および Down コールバックを実装する方法を示します。Down コールバックは、テキスト・フィールドの値を減少させ、Up コールバックはこれを増加させます。
まず、コンテナーが作成され、UpDownField クラスのインスタンスがこれに追加されます。
IlvDialog* dialog = new IlvDialog(display,
title,
title,
IlvRect(0, 0, 100, 100));
UpDownField* but = new UpDownField(display, IlvRect(5, 5, 100, 23) , "0");
dialog->addObject(but);
次に、UpDownField のコールバックが設定されます。
but->setUpCallback(Increment);
but->setDownCallback(Decrement);
以下にコールバックの実装を示します。
static void Increment(IlvGraphic* g, IlvAny)
{
char buffer[1000];
UpDownField * obj = (UpDownField*)g;
const char* label = obj->getLabel();
if (label && *label) {
IlvInt value = ((IlvInt)atof(label))+1;
sprintf(buffer, "%ld", value);
obj->setLabel(buffer, IlvTrue);
} else
obj->setLabel("0", IlvTrue);
}
 
static void Decrement(IlvGraphic* g, IlvAny)
{
char buffer[1000];
UpDownField * obj = (UpDownField*)g;
const char* label = obj->getLabel();
if (label && *label) {
IlvInt value = ((IlvInt)atof(label))-1;
sprintf(buffer, "%ld", value);
obj->setLabel(buffer, IlvTrue);
} else
obj->setLabel("0", IlvTrue);
}
これらのコールバックは、UpDownField インスタンスのテキスト・フィールドの値を UpDownField::getLabel を呼び出して取得し、値 1 を追加/削除して変更し、UpDownField::setLabel を呼び出してテキスト・フィールドのラベルとしてこれを設定します。
これで、手順 3 が完了しました。

Version 6.0
Copyright © 2015, Rogue Wave Software, Inc. All Rights Reserved.