Віртуальний GUI на Scala

Розглянемо створення застосунку з графічним інтерфейсом, який би працював на робочому столі , в браузері та під Android без зміни коду.
Звичайно нам знадобляться бібліотеки- фасади, які інкапсулюють різницю між бібліотеками контролів цих систем (JavaFX, Android, DOM)

Для створення таких фасадів прекрасно підходять implicit класи. Наприклад:

implicit class TextFieldExt(txt: Text) {
  def text=txt.getText
  def text_=(a: Any) = txt.setText(a.toString)
}

Так ми зможемо зробити GUI не тільки на методах, але і на властивостях: btHello.text=”Hello”

Але ні в DOM ні в Android немає готового контрола Table. Доопрацювання тих віджетів що там є на implicit класах уже не можливе, потрібно зберігати стан.

Тому Table – це звичайний клас. Крім того в Android для табличних даних потрібенще клас адаптера. Серед стандартних адаптерів немає такого,що вміє працювати з Array, ArrayList ,ArrayBuffer в яких зберігаються класи даних. Прийшлось написати свій ItemAdapter.
Для обробки структур даних в яких віджет це поле і в якому можуть бути різні класи віджетів:
val card=Array(new Card{id=txtName, prop=TEXT,….},new Card{id=cbCat, prop=INDEX…},….)
нас виручить потужний механізм зіставлення з взірцем :

for(c<-card)
  c.id match {
  case text :Text =>text.text=...
  case combo: Combo => combo.index=...
 ….....
  }
 }

Ще однією проблемою є те, що в кожній із трьох систем свій механізм ініціалізації графічного інтерфейсу. Можливо стабілізація API макросів і мій вільний час вирішать цю проблему в майбутньому. А поки що так))

Код для Android:
 class MorgFx extends AppCompatActivity {

  override def onCreate(savedInstanceState: Bundle)={
    super.onCreate(savedInstanceState)
    setContentView(R.layout.hello_app)
    val context:Context = this
    val root=this
    val btHello = root.elem(R.layout.btHello).asInstanceOf[Button]
    val cbHello = root.elem(R.layout.cbHello).asInstanceOf[Combo]
    val txtHello = root.elem(R.layout.txtHello).asInstanceOf[Text]
    cbHello.fill(Array("man","woman"), context)
    btHello.onSelect(btSelect)

    def btSelect(sender:Any){
      context.showMessage(txtHello.text)
      }
  }
 }

Код для браузера:

object HelloApp extends JSApp {
  def main(): Unit = {
    val context=startGUI("divHello")
    val root=context
    val btHello = root.elem("#btHello").asInstanceOf[Button]
    val cbHello = root.elem("#cbHello").asInstanceOf[Combo]
    val txtHello = root.elem("#txtHello").asInstanceOf[Text]
    cbHello.fill(Array("man","woman"), context)
    btHello.onSelect(btSelect);

     def btSelect(sender:Any){
      context.showMessage(txtHello.text)
    }
  }
 }

Код для робочого столу:

object HelloApp {
  def main(args: Array[String]): Unit ={
    Application.launch(classOf[HelloApp], args: _*)
  }
}

class HelloApp extends Application {
  type Combo = ComboBox[String]
  override def start(stage: Stage): Unit = {
    val context = stage
    val root = context.startGUI("divHello")
    val btHello =root.elem("#btHello").asInstanceOf[Button]
    val cbHello = root.elem("#cbHello").asInstanceOf[Combo]
    val txtHello = root.elem("#txtHello").asInstanceOf[Text]
    btHello.onSelect(btSelect);

    def btSelect(sender:Any){
      context.showMessage(txtHello.text)
    }
  }
 }

Посилання на  GitHub  https://github.com/anatoly62/scala-gui