こんにちは、高木です。
前回予告したように、今回はwidgetクラスにconfigureとcgetの機能を追加していきます。
念のためconfigureとcgetを簡単に説明しておくと、configureはウィジェットにオプションを設定するコマンドであり、cgetは設定されたオプションの値を取得するためのコマンドです。
簡単に使い方をおさらいしておきます。
0 1 2 3 4 |
label .l .l configure -text hello puts [.l cget -text] |
上記の例では、オプションを何も指定せずに、つまりデフォルトの設定でラベルウィジェット「.l」を作っています。そのあとconfigureコマンドでラベルのテキストを「hello」に変更しています。そしてcgetコマンドでテキストの値を取得してputsコマンドで標準出力に書き出しています。
今回は上記のようなconfigureとcgetをwidgetクラスに追加していきます。
widgetクラスに手を付ける前に、下準備としてinterpreterクラスにevaluateメンバー関数を多重定義します。
0 1 2 3 4 5 6 |
int evaluate(const std::vector<Tcl_Obj*>& objv) { auto command = Tcl_ConcatObj(objv.size(), objv.data()); return evaluate(command); } |
多重定義したevaluateメンバー関数は、Tcl_Obj*の列を受け取って連結したあとコマンドとして評価します。連結にはTcl_ConcatObj関数を使っています。
実は、TclのC言語APIにはTcl_EvalObjv関数というのがあってまさにTcl_Obj*の列を受け取るのですが、この関数はちゃんと字句をパースした結果の列を渡さないとうまくいかない気がしています。若干効率は落ちますが、受け取ったTcl_Obj*の列をいったんTcl_ConcatObj関数で連結してから、単一の文字列として評価させるようにしています。
次に、widgetクラスにevaluateメンバー関数を追加します。
0 1 2 3 4 5 6 7 8 9 |
protected: int evaluate(std::vector<Tcl_Obj*>& objv, std::initializer_list<tcl::obj> args) { std::for_each(args.begin(), args.end(), [&objv](auto arg) { objv.push_back(arg.get()); }); return this->interp_.evaluate(objv); } |
このevaluateメンバー関数はinterpreterクラスの同名の関数とはちがって、ウィジェット関連のコマンドを評価するのに特化しています。すなわち、オプション列に先行していくつかの字句を並べるようなコマンドを想定しています。ウィジェットの生成コマンドやconfigureコマンドがまさにそうですね。
やっと下準備が終わりましたので、いよいよconfigureとcgetを追加します。次のコードがそれにあたります。
0 1 2 3 4 5 6 7 8 9 10 11 |
int configure(std::initializer_list<tcl::obj> options) { std::vector<Tcl_Obj*> args{ this->path_.get(), tcl::obj(u8"configure").get() }; return evaluate(args, options); } tcl::obj cget(const tcl::obj& option) { return this->interp_.evaluate({ this->path_.get(), tcl::obj(u8"configure").get(), option.get() }); } |
先ほどinterpreterクラスとwidgetクラスに追加したevaluateメンバー関数を早速使っていますね。
これでconfigureとcgetの機能をwidgetクラスに追加することができました。次回からはジオメトリーマネージャーについて考えていくことにします。