RealBasic University

このコラムはStone Table Softwareのオーナーであり、またREALbasic Developerの編集者でもあるMarc Zeedar氏により書かれたものを、著者の許可を得て翻訳したものです。この翻訳はHREM Researchにより提供されています。この日本語版へのご意見はRBU-Jまでご連絡下さい。

URL: http://www.applelinks.com/rbu/026/
INDEXに戻る

CSSの変更

今週のレッスンを始める前に、先週新登場したRBUのレイアウト変更について返事をくださった皆さんに感謝したいと思います。

今までのところ全て問題無いようです:私は今週に向けてユーザの提案をもとにCSSのスタイル定義ファイルに少しの変更を加えました(先週のコラムをもう一度見れば、この変更が適用されているはずです)。この新しいフォーマットが皆さんの環境でうまく動くことを期待しています。私はまた、元のテキストをHTMLに変換するREALbasicのプログラムのバグを修正しました、また私はプログラム・リストの中のキーワードを正しくマークしていませんでした。

以前のように、あなたのブラウザとの非整合性や問題があることに気づいたら私に連絡してください

カスタムクラスの作成 I

先週我々が学んだように、カスタムクラスは他のプログラマの作成したオブジェクトをあなた自身のプロジェクトに組み込む簡単な方法です。しかし自分の使うカスタムクラスをどのように作るのでしょうか? そう、今日は我々はそれを学ぼうとしているのです!

このレッスンでは、我々は2つの異なったカスタムクラスを作るつもりです。一つ目は簡単なon/offスイッチです。2つ目は箱を格子状に描いたキャンバスで、箱を選択してそれをあちこち動かすことが出来るものです。

例によって、あなたは完全なサンプルプロジェクトファイルをここからダウンロードできます、あるいはコラムに従って一歩一歩作りあげることもできます。

もしあなたがゼロから始めるのならば、新しいプロジェクトを作り、2つのキャンバスと1つの文字入力欄Window1の上へドラッグしてください。

OnOffClass

我々の最初のデモンストレーションは、オンかオフかを表示する次のような単純なキャンバスクラスです:

キャンバスをクリックすればオン/オフが切り替わります。

最初に、新しいクラスを作成しましょう:ファイルメニューに行って、"New Class"を選んでください。これはあなたのプロジェクト・ウィンドウにclass1を加えるでしょう。それを選択して、属性ウィンドウ上でその名前を変えます。それをonOffClassと呼びましょう。次にその"super" をCanvasに設定します。

追記

我々はここで何を行っているのでしょうか? オブジェクトの重要な点の1つは継承inheritanceの概念であることを覚えておいてください。基になるオブジェクトがあれば、オリジナルのプロパティのすべてを継承する変種を作成することができます(あなたが追加あるいはオーバーライドしたものを除いて)。

REALbasicの用語での "super" は基になるオブジェクトです。この場合、我々はsuperをキャンバスcanvasに設定しました。したがって、我々の新しいクラスは正確にキャンバスのように働くでしょう。それは従来のキャンバスの属性およびメソッドのすべてを持つでしょう。我々がそれらを変更するか、新しいものを加えれば、我々のキャンバスはカスタムされるでしょう。しかし、我々の修正は、従来のキャンバスにはまったく影響しません。

あなたがツールパレットからウィンドウ上にキャンバスオブジェクトをドラッグする場合、REALbasicはデフォルトでは従来のキャンバスを追加します。しかし、あなたが望めば、そのオブジェクトがあなたのキャンバスクラスであるとRBに指示することができます。それは我々が次に行こなおうとしている事です。

Window1上のcanvas1を選択してください(もしキャンバスがなければ、そこへ1つドラッグして来てください)。そのsuperを"onOffClass"にセットし、幅と高さをこのようにセットしてください。

では、onOffClassクラスをダブルクリックします。コード編集ウィンドウが開くでしょう。ここでプロパティを追加しましょう(Editメニューへ行き、"New Property"を選択します)。その名前をvalue as booleanにしてください。

Valueはスイッチのオンかオフかといった状態を表わすことになります。

ユーザがそれをクリックしたときに状態が変更されるので、MouseUpイベントに次のコードを入力しましょう:

   value = not value 
me.refresh

これは状態を交互に切換え、コントロールを再描画します(状態が変化したので)。

しかし、我々はこれをMouseDownではなくMouseUpに入れたことに注意してください:なぜ我々はこうしたのでしょうか? それはMacがそのように動くようにデザインされているからです:ボタンをクリックすることではアクティブになりません。それはボタンを押すかもしれませんが、ボタンの状態はユーザがボタンを放すまで変更されません。このようにする1つの利点は、その少しの遅れは我々に状態の変更を示す時間を与えるということです。我々の場合は、何かが起こっていることを示すために黒でボタン全体を塗ることにします。

MouseDownイベントに次のものを入れてください:

   me.graphics.fillRect(0, 0, me.width, me.height) 
return true

これは、単にデフォルト色(黒)でキャンバス全体を満たし、そして -- とても重要なのは -- 我々はMouseUpイベントを有効にするためにtrueを返すということです。もし、この行が無かったならば、MouseDownはデフォルトではfalseを返しますので、MouseUpイベントは起こらなくなります。(ここでtrueを返すことにより、あなたがそのイベントを処理したいと伝えている訳です。)

あなたがそれを行ったならば、次に大部分のコードをPaintイベントに書き込みます。それはコントロールを描画します。それは状態をチェックし、それに従って描くもの変えます(オンならば緑の箱、オフならば赤い箱)。

 
   dim s as string 
dim x as integer

if value then
s = "ON"
g.foreColor = rgb(0, 155, 0) // Green
else
s = "OFF"
g.foreColor = rgb(230, 0, 0) // Red
end if

// Fills it with color
g.fillRect(0, 0, me.width - 1, me.height - 1)

// Draw the label
g.foreColor = rgb(255, 255, 255) // White
g.textFont = "Geneva"
g.textSize = 12
g.bold = true
x = (me.width - g.stringWidth(s)) \ 2
g.drawString s, x, g.textHeight

g.foreColor = rgb(0, 0, 0) // Black
g.drawRect(0, 0, me.width - 1, me.height - 1)

これだけです! もうプログラムを実行することができ、コントロールをクリックすることにより状態が変化する(交互に切り替る)のを確かめることができます。

これは明らかに単純な例ですが、あなたはそれをより複雑にすることができるでしょう。例えば、3状態のスイッチ、あるいはレバーのようなグラフィックも考えられます。クールなことは、クラスに行なうどんな修正もそのクラスに基づくすべてのインスタンスを即座に変更するということです。従って、あなたは多くのスイッチを備えたプログラムを書いて、その後で、3Dスイッチがよりよく見えるだろうと思った時には、1つのPaintイベントのコードを変更すれば、全てのスイッチは新しい外見を持つことになるでしょう!

課外テーマ:ボタンをより三次元的にするために下記の行をPaintイベントへ追加してみてください。

   g.foreColor = lightBevelColor 
g.drawLine(0, 0, 0, g.height - 1)
g.drawLine(0, 0, g.width - 1, 0)

g.foreColor = darkBevelColor
g.drawLine(g.width - 1, 0, g.width - 1, g.height - 1)
g.drawLine(g.width - 1, g.height - 1, 0, g.height - 1)

BoxClass

それではより複雑なクラスに挑戦してみましょう。BoxClassはキャンバスの一種です。ユーザは箱を選択してその中で移動させることができます。明らかに、それ自身に特に有用ではありません。しかし、ここでのその概念はお絵かきプログラムやその内部でユーザが位置を決めたりサイズ変更することができる要素を備えたコントロールを作るときの良い出発点となるでしょう。

まず、再び新しいクラスを作り、その名前を"boxClass"へ変更し、superをキャンバスにします。

では、Window1canvas2へ行きましょう(必要ならば新しいキャンバスを追加してください)、そしてそのsuperをboxClassにします。

ダブルクリックしboxClassを開きます(注意canvas2ではありません)。そして次ぎの属性を追加します:

既にお判りのように、これはさらに複雑なクラスです:我々はそれらの全ての属性使って、箱の位置やクラスの様々な状態(箱が選択されているかなどのような)を追跡することができます。

我々のキャンバスがスタートする前に、幾つかの要素を初期化する必要があります。箱の位置と色をランダムに設定します。さらに、我々はgrid属性をtrueに設定すます--しかし、もちろんあなたはプログラムを走らせる時にこれを変更してその効果を調べることも出来ます。

   grid = true 
boxLeft = (rnd * (me.width - 20)) + 1
boxTop = (rnd * (me.height - 20)) + 1
boxColor = rgb(rnd * 200 + 50, rnd * 200 + 50, rnd * 200 + 50)

我々のキャンバスが今初期化されたので、すべてが描かれるPaintイベントを書きましょう。これは幾らか複雑なルーチンであることが判るでしょう。

   dim x, y, x1, y1 as integer 
const gridSize = 15

// Draw grid
// (This is mostly here just to give us a background.)
if grid then
g.foreColor = rgb(200, 200, 200) // Light gray

x1 = me.width \ gridSize '- 1
y1 = me.height \ gridSize '- 1
for x = 0 to x1
g.drawLine x * gridSize, 0, x * gridSize, me.height
next // x

for y = 0 to y1
g.drawLine 0, y * gridSize, me.width, y * gridSize
next // y

end if

// Draws border
g.foreColor = rgb(0, 0, 0) // Black
g.drawRect(0, 0, me.width - 1, me.height - 1)

// Draw Box
g.foreColor = boxColor
g.fillRect(boxLeft, boxTop, 20, 20)

// Draw Selection handles
if selected then
g.foreColor = rgb(0, 0, 0) // Black

// Upper left corner
g.fillRect(boxLeft - 2, boxTop - 2, 4, 4)
// Bottom left corner
g.fillRect(boxLeft - 2, boxTop + 18, 4, 4)
// Upper right corner
g.fillRect(boxLeft + 18, boxTop - 2, 4, 4)
// Bottom Right corner
g.fillRect(boxLeft + 18, boxTop + 18, 4, 4)
end if

もしgridtrueならば、我々は薄い灰色のグリッド・パターンを背景に描画します。どうしてそれは背景になるのでしょうか? その理由はそれが最初に描かれたからです:その後に描かれた他の要素はその上に現われます。描画の順序はこれらのようなコントロールにおいては重要なことですので、それを覚えておいて下さい。

次に、我々は境界、そしから箱を描きます。最後に、我々は選択用ハンドルを描きます。このステップはselectedtrueの時のみ実行されます、そしてそれは最後のステップなので、ハンドルは何によっても覆い隠されないということが判るでしょう。

どこに物体を描くかを正確に求めるためのちょっとした計算が含まれていますが、全体としてこれはかなり単純な通常の描画コマンドです。例えば、選択ハンドルは、箱の4つの角の各々に描かれますが、我々はそれらが箱の外側に少し突き出るように移動しました(それによりハンドルをより見えやすくします)。

ユーザがキャンバスの内部でクリックした場合には、はるかに複雑なことが起こります。我々は複数の状況をチェックしなければなりません:箱の選択と解除、ユーザがドラッグしている場合の箱の移動。

これはMouseDownイベントのコードです:

   if x > boxLeft and x < boxLeft + 20 and y > boxTop and y < boxTop + 20 then 
// User clicked inside of box
xDif = x - boxLeft
yDif = y - boxTop

if not selected then
selected = true
me.refresh
end if

// Pass MouseDown to MouseDrag
return true
else
// User clicked outside of box
if selected then
selected = false
me.refresh
end if
end if

外側のif-thenループは、ユーザがその箱の外部あるいは内部をクリックしたかどうかを単純にチェックしています。それが外部の場合には、我々は箱が既に選択されているかどうかチェックします。もしそうならば、その選択を解除します(そしてキャンバスに強制的に再描画させます)。

もし、ユーザが箱の上でクリックした場合には、我々は2つのことをします。最初に、xDifyDifの値を保存します。これらはなんでしょうか? そうですね、箱は左上の端から描画されますので、これらはその位置とユーザがクリックした場所と差を示しています。ユーザが箱の中央でクリックするのと箱の底の近くでクリックするのでは違いがあります。この違いを保存することで、我々はユーザに箱のドラッグを許し、かつ箱の位置はユーザのマウスに追随させることができます。(そうでなければ、箱はジャンプして、その左上の端はユーザのマウス位置になってしまうでしょう。)

次に、もし箱が選択されていなければ、箱を選択します。ユーザは箱の選択を解除するためではなく、それを移動させるためにクリックすることもありえますので、この場合は箱の状態の切換えはしません。選択を解除するためには、ユーザは箱以外のどこかをクリックします。

最後に、我々はMouseDragイベントを許可するためにtrueを返します。次はそのMouseDragイベントです:

   if selected then 
if (boxLeft <> x - xDif) or (boxTop <> y - yDif) then
boxLeft = x - xDif
boxTop = y - yDif
me.refresh
end if
end if

ドラッグは箱が選択されているときにかぎり許されていますので、我々はそれを最初にチェックします。それから、ユーザのマウス・カーソルが動いたかをチェックします:もしカーソルが動いていないなら、箱を新しい場所に描画する必要はありません。

もしユーザがマウスを動かしていたならば、我々は単に新しい場所に箱を描画し、キャンバスを再描画します。

簡単でしょ、ね? 試してみてください。プログラムを起動し、箱をあちこち移動してみてください。選択したり、選択を解除したり。クールでしょ?

新規のイベントの追加

では、次は既存のオブジェクトからあなた自身のクラスを作る際に重要なことです。あなたはCanvas2boxClassではなく)を開いた時に、幾つかのイベントがEventsリストから消えていることに気付いたでしょう。キャンバスが常に持っているPaintMouseDownイベントはどうなったのでしょうか? それらは無くなっています!

これは継承の重要な側面です:クラスはそれらのイベントを継承しています、そして何かを行っています(あなたはそこにコードを入力しています)。ですから、それらは事実上既に使われています。したがって、それらにコードを追加することはできないのです。

ほとんどの場合にはカスタムクラスのイベントを拡張する必要はありませんので、これは問題ではありません。例えば、boxClassはそれ自体の描画を自動的に処理しますので、その描画を修正するためにPaintイベントにアクセスする必要はありません。

しかし、Openイベントはどうでしょうか? それはオブジェクトを初期化するところです。もし、Canvas2が開かれた時に、いくつかの設定を初期化する必要がある場合にはどうなるでしょうか。あなたはクラス定義の中で既にOpenイベントを使っていますので、それを再び使用することはできません。したがってあなたはCanvas2が開かれれた時を知ることはできません!

しかし、絶望しないでください:REALbasicは我々にエレガントな解決方法を与えてくれています。カスタムクラス内に新しいイベントを作成することが可能です! それらのイベントには好きな名前をつけることができます、そしてコードにその名前を書くだけでそのイベントを「アクティブ」にするためことができます。

例えば、MouseUpのイベント内に、ダブルクリックをチェックするコードを書くことができます。(これは2つのMouseUpが一定の期間内に生じなければならないので、少し複雑です。) あなたのコードがユーザのダブルクリックをしたと決定した時は、あなたはDoubleClickedイベントを呼ぶことができます。

課外テーマとして、次のことを試してみてください:boxClassfirstClick as integer属性を加えます。それから、MouseUpイベントに、以下のコードをを入力してください:

 
   if firstClick = 0 then 
firstClick = ticks
else
if ticks - firstClick < 30 then
DoubleClick x, y
end if
firstClick = 0
end if

あー、そーですね、DoubleClickイベントも同様に加える必要があります:Editメニューに行き、"New Event"を選択して、それにDoubleClickと名前を付けます。それにx as integer, y as integerというパラメーターを与えてください。

これで全てですが、我々はまだダブルクリックに関しては何もしていません。ですからCanvas2を開き(boxClassではありません!)、DoubleClickイベントを探し、そこへmsgBox "Double-Clicked!"を入力してください。これであなたが箱をダブルクリックした時に、ダブルクリックをしたことを示すダイアログボックスが現れるはずです。

ところで、我々の本来の問題に戻りましょう:我々はクラス定義の中でOpenイベントを使用しましたので、canvas2Openを持ちません。それで、我々はそれを取り戻しましょう!

boxClassへ行き、Editメニューから"New Event"を選びます。それの名前を"Open"にします。これであなたのcanvas2Openイベントを持っています。その中にmsgBox "test"と入力し、プログラムを走らせ何が起こるか見てみましょう。何も起きません:それはあなたが新しいOpenイベントを定義したのですが、それを呼んでいないからです(そのイベントは決して自動的には起こりません)。その解決法は簡単です:boxClassのOpenイベントの最後に次ぎのコードを加えます:

   // Pass this event on... 
open

では、プログラムを走らせ、それが動いていることを確認しましょう。boxClassの中のOpenイベントが発生し、それ自身の「新しいOpen」イベント(それはcanvas2の中のイベントです)が生じたというメッセージを送ります。ところで、新しいイベントは"Open"と名前をつける必要はありません -- あなたはそれを"Initialize"あるいは"Snogglegrass"と呼んでも、それは同じように機能します。

次週

我々は取り消し(Undo)を内蔵した文字入力欄EditFieldの変形版を作るでしょう。今度は有用なクラスです!

Letters

我々の今週の手紙はRBUのカスケーディング・スタイル・シート(CSS)への移行に関するものです。最初は、Craig Brownさんからです:

ハイ、Marcさん...

私はCSSを使用した新しいフォーマットを素晴らしいと思います。よい出来ばえです。否定的な幾つかのコメントが欲しい場合には、iCabブラウザーでそれを見てみてください。あなたがiCabを使用したかどうか知りませんが、それは、HTML「エラー」に遭遇すると笑顔アイコンは悲しい顔に変わります。あなたのコラムでは682の「エラー」が現れます。しかし、気を悪くしないでください。私は無数のエラーを表示しないサイトをまだ見たことがありません(Cabのサイト、http://www.icab.de/index.htmlを除いて)。

有難う、Craigさん。私はiCabを絶対的に信頼している多くの人を知っています。しかし、わたしは何度か試しましたが、それは本当に私のコンピュータとの相性が良く無いようです:それは数分間作動して、その後完全に私のシステムをフリーズさせます。私は、一年の間いくつかのバージョンを試みましたが、それらはすべて同じ問題を示しました。私は、それが私のシステム上の何かとコンフリクトしているのだと思いますが、私はそれを追求する興味も時間もありません。(マイクロソフトがIEを有料にするまでは、私はエクスプローラーで満足しています。)

次は、Johnさんからです:

今日は、Marcさん

新しいフォーマットに関するちょっとしたフィードバックです - 私はさらに読みやすくなったと思います。しまし、私にほんの小さな変更を提案させて下さいませんか? 見出しをサンセリフ書体にして、本文をセリフ書体のままにするのはどうでしょうか。例えば、見出しにHelvetica、本文にTimesというのは伝統的な組み合わせです。

あなたのコラムはまとめられて本として出版されるべきです - 私はこれが非常に有用な一連のレッスンだと思います。私はいつかデフォルトの72dpiより高い解像度のグラフィックスの印刷に関して解説されることを希望します。

Johnさん、有難うございます。グラフィックデザイナーとして、TimesとHelveticaはあまりにも使われ過ぎるので、私は大嫌いなものなのです。しかし、あなたの指摘は熟慮に値するものです:サンセリフの見出しとセリフの本文は多分良い組み合せでしょう(しかし、私はスクリーン上で読むにはサンセリフの方が好きです)。今日の変更をあなたはどう思いますか。(私は本文のために非常に判読しやすいマイクロソフトのフリーのフォントであるGeorgiaを使いました。)

最後は、Kevinさんの手紙です:

うーん。RBUの変更に対するの私の初期の印象は良くなったということです。私はこの変更が好きです。しかしながら、私のブラウザー(MacOS XのOmniWeb)はCSSを完全にはサポートしません、そしてクラスを定義している背景色もサポートしないようです。用語集の言葉は[ウェブの設定で]異なる背景色の白抜き文字として表わされるので、これは用語集の言葉を見る場合に問題となります。OmniWebが背景色をサポートしないので、私は白い背景の上に白いテキストを見ることになります。それでハイライトしないかぎり用語集の言葉を読むことができません。もし用語集の言葉が白以外の色であるならば、非常に助かります。

それ以外は素晴らしく見えますよ!

助言に感謝します、Kevinさん! 私はOmniWebでは試していせんでした(やればできたのですが)。私は、用語集の言葉を黒に変更しました--それは問題のあるブラウザーではよりうまく働き、しかし、他のすべての人にとっては読みやすいままであると思います。

このような手紙をお寄せ下さい。私はあなたの質問に直ぐには答えないかも知れませんが、たぶん今後のコラムで取り扱うでしょう。(もし、あなたの手紙が公表されるのを望まない場合には、その旨を明記して下さい。そうでなければ、公表してもいいものとして取り扱います。)


RBU-Jの通知サービス!コラムが発表されるたびに日本語版REALbasic Universityのお知らせの emailがあなたに届きます。登録・削除は ここ から。

INDEXに戻る