編集不可のTextFieldを作る

TextFieldを編集不可(あるいは読み取り専用)にして使いたいんですがどうすればいいですか?enabled?=falseだと文字列のコピーができなくなるので好ましくないです。

ありきたりな要求だと思うのですが、残念ながら現行のバージョン(5.0)では簡単に(例えばプロパティ設定するだけとかで)は実現できません。TextFieldを拡張する必要があります。


以下は、TextFieldを拡張して、editable?というプロパティを追加する例です。
まずはTextFieldのサブクラスを宣言して、editable?というbool型のfieldを用意します。

{define-class public EditableTextField {inherits TextField}
  
  field public editable?:bool

}


次にコンストラクタの宣言ですが、ここでは面倒臭がらずにTextFieldに用意されているコンストラクタと同じ引数を受け取るように定義してやります。これを残余引数で省略してしまうと、引数のコーディングミスがコンパイルエラーにならなかったり、IDEで出てくる引数のツールヒントで情報が表示されなかったりといったデメリットにつながります。なお、通常のTextFieldのコンストラクタ引数に加えて、editable?初期化用のキーワード引数も追加しておきます。

  {constructor public {default
                          data-model:#StringDataModel = null,
                          value:#String = null,
                          prompt:#String = null,
                          max-chars:int = -1,
                          ui-object:#TextFieldUI = null,
                          editable?:bool = true,
                          ...
                      }
    {construct-super data-model=data-model, value=value,
        prompt=prompt, max-chars=max-chars, ui-object=ui-object, ...}
    set self.editable? = editable?
  }


次が肝心の編集不可を実現するためのコードです。TextFieldが持つふたつのメソッドをオーバーライドして、editable?の値による制御を加えます。

  {method public {delete-selection}:void
    {if self.editable? then
        {super.delete-selection}
    }
  }
  
  {method public {replace-selection-with-string
                     text-to-insert:StringInterface
                 }:void
    {if self.editable? then
        {super.replace-selection-with-string text-to-insert}
    }
  }


最後に仕上げで、writable?というgetterを以下のようにオーバーライドしてやります。

  {getter public {writable?}:bool
    {return super.writable? and self.editable?}
  }

TextFieldを右クリックすると「切り取り」「コピー」「貼り付け」「削除」というメニューが出てきますが、上記のオーバーライドによってこれらの右クリックメニュー項目のenabled?が適切に制御されるようになります。(editable?=falseのときに「コピー」以外の右クリックメニュー項目がグレーアウトされるようになります)


あとは、標準のTextFieldの代わりに上記の拡張TextFieldを使うようにするだけです。

{EditableTextField
    editable? = false,
    value = "あいうえお"
}


最後に今回のEditableTextFieldのコード全体を示しておきます。

{define-class public EditableTextField {inherits TextField}
  
  field public editable?:bool
  
  {constructor public {default
                          data-model:#StringDataModel = null,
                          value:#String = null,
                          prompt:#String = null,
                          max-chars:int = -1,
                          ui-object:#TextFieldUI = null,
                          editable?:bool = true,
                          ...
                      }
    {construct-super data-model=data-model, value=value,
        prompt=prompt, max-chars=max-chars, ui-object=ui-object, ...}
    set self.editable? = editable?
  }
  
  {getter public {writable?}:bool
    {return super.writable? and self.editable?}
  }
  
  {method public {delete-selection}:void
    {if self.editable? then
        {super.delete-selection}
    }
  }
  
  {method public {replace-selection-with-string
                     text-to-insert:StringInterface
                 }:void
    {if self.editable? then
        {super.replace-selection-with-string text-to-insert}
    }
  }
}