はじめに
第1回の連載では見積デモで使用されていたRecordGridというAPIについて説明しました。今回は、見積入力画面で利用されているドラッグ&ドロップの機能について見ていきたいと思います。見積入力画面では、商品名から見積の明細を入力するためにドラッグ&ドロップが利用されています。
ドラッグ&ドロップを使用することによってユーザーは商品一覧から見積を選択するだけで自動的に売上が計算されます。新規に商品を選択した場合は新しい項目が追加され、既に選択済みの商品については個数が増加します。今回はこのドラッグ&ドロップの機能について見ていきます。
前回の記事
ドラッグ&ドロップとは
具体的な見積デモの内容を見る前に、まずはドラッグ&ドロップの基本的な機能について見てみましょう。ドラッグ&ドロップのサンプルソースは次のとおりです。
{curl 6.0 applet}
{curl-file-attributes character-encoding = "shift-jis"}
{HBox spacing = 5cm, valign = "center",
|| Step 1: ドラッグしたいグラフィックのdrageeプロパティに
|| ImageDrageeを指定します。
{EllipseGraphic width = 1cm, height = 1cm, dragee = {ImageDragee}},
|| Step 2: これはドロップする対象のグラフィックです。
{Frame
width = 2cm, height = 2cm,
border-width = 2pt,
background = "#ccccff",
|| 以下は DragOver イベントです。
|| DragOver.will-accept-drop?メソッドの呼び出しにより、
|| グラフィックをドラッグしようとするときの処理を制御します。
{on e:DragOver do
{e.will-accept-drop?
|| ここでは何もせずドロップグラフィックを許容します。
{proc {type:Type, x:Distance, y:Distance,
effect:#DragEffect}:DragEffect
{return drag-effect-copy}
} } }, || stacked for compactness
|| Step 3: 以下は Drop イベントです。
|| Drop.accept-dropイベントの呼び出しにより、
|| グラフィックをドロップしたときの処理を制御します。
{on e:Drop do
{e.accept-drop
{proc {a:any, x:Distance, y:Distance,
effect:#DragEffect}:DropResult
{return {DropResultCopy
|| 'action' プロパティは
|| ボールがドロップされたときに発生するイベントです。
action = {proc {}:void
{popup-message
"ボールをドロップしました!",
modal? = true}
} } } } } } } }
このサンプルを実行すると次のような画面が表示されます。画面左のボールを右側の四角にドラッグすると、メッセージが表示されます。


サンプルを見るとわかりますが、ドラッグ&ドロップの処理はドラッグされる側のグラフィックとドロップされる側のグラフィックが一対になって実装されます。そのため実装が難しく感じられるかもしれませんが、実装箇所と内容を整理すれば他の機能の実装とあまり変化はありません。
サンプルの実装解説
さて、見積デモのドラッグ&ドロップ機能を見ていきましょう。
見積デモではリストの一覧から項目をドラッグすると、グリッド上にデータが表示されるようになっています。このうち、まずは画面左側のListBoxに注目します。ListBoxは複数の項目から任意の項目を選択するというものですが、今回はこの選択した項目がドラッグできるようにします。項目はListValueItemというクラスによって実装されているので、このListValueItemに対してドラッグ機能を追加します。

{let lb:ListBox = {ListBox width = 100pt,height = 300pt}}
{do
||ListBoxにアイテムを追加します。
{for record:Record in goods-list do
let new-item:ListValueItem =
{ListValueItem
record,
label = record["name"]
}
||ListValueItemをドラッグするために
||drageeを設定します。
set new-item.dragee = {ImageDragee}
{lb.append new-item}
}
}
これで選択した商品がドラッグできるようになりました。
次に、RecordGridの方にも処理を実装します。RecordGridはグラフィックをドロップされる側なので、ドロップ時に発生するイベントを実装します。
{let rg:RecordGrid =
{RecordGrid
width = 500pt,
height = 300pt,
record-source = list,
||RecordGridにドラッグされたときのイベント
||(DragOver)を追加します。
{on e:DragOver do
{e.will-accept-drop?
{proc {t:Type,x:Distance,y:Distance,effect:#DragEffect}:DragEffect
{return
||ここでは、ListValueItemが来たときのみドラッグを許容します。
||それ以外のオブジェクトについてはドラッグすることが出来ないようにします。
{if {t.subtype-of? ListValueItem}
then
drag-effect-copy
else
drag-effect-none
}
}
}
}
},
||RecordGridにドロップしたときのイベント
||(Drop)を追加します。
{on e:Drop do
{e.accept-drop
{proc {obj:any,x:Distance,y:Distance,effect:#DragEffect}:DropResult
{if not obj isa ListValueItem
then
{return {DropResultNone}}
}
let item:ListValueItem = obj asa ListValueItem
let goods-record:Record = item.value asa Record
||現在表示されているレコードと突合せを行います。
||Codeがドロップした商品のIDと等しい項目があるかどうかで
||処理が分岐します。
{if-non-null list-record:Record =
{list.select-one
filter = {RecordData Code = goods-record["id"]}
}
then
||レコードが存在する場合は、個数と小計の更新を行います。
let count:int = list-record["Count"] asa int
let price:int = list-record["Price"] asa int
set list-record["Count"] = count + 1
set list-record["Subtotal"] = (count + 1) * price
else
||レコードが存在しない場合は、新規にレコードを追加します。
{list.append
{RecordData
Code = goods-record["id"],
Name = goods-record["name"],
Price = goods-record["price"],
Count = 1,
Subtotal = goods-record["price"]
}
}
}
{return {DropResultNone}}
}
}
}
}
}
グリッドにDragOverとDropイベントを記述することによって、RecordGridは初めてドラッグされてきたグラフィックを受けつけることができます。これを実際に起動すると、次の図のようなアプリケーションが実行されます。実際のサンプルについては添付のソースを参照してください。なお、Developer Centerではこれ以外にもさまざまなサンプルが紹介されています。
ドラッグ&ドロップの利点
ドラッグ&ドロップの実装方法について理解いただけましたでしょうか? ドラッグ&ドロップを実装することのメリットは、直感的な操作が行えるようになることで、コンピュータの利用に慣れていない初心者でも分かりやすいUIを提供できることにあります。新規システムを構築する際にはぜひ検討してみてください。
次回は、印刷の機能について詳しく見ていきます。




