Last active
December 31, 2015 23:08
-
-
Save b-studios/8057677 to your computer and use it in GitHub Desktop.
Example code to illustrate a different approach for Vaadin's scala wrapper classes using the "pimp my library" pattern.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package scalavaadin | |
import com.vaadin.{ ui => orig } | |
import scala.language.implicitConversions | |
import scala.collection.mutable | |
import scala.collection.JavaConversions | |
package object ui { | |
// Adding new Methods | |
// ------------------ | |
// Let's start with how to add methods to existing Vaadin classes / interfaces. | |
// The good thing is, that this pattern ("Pimp My Library") also works for interfaces | |
// and thus removes the need to reconstruct the same inheritance structure on the | |
// scala-interface site. | |
// | |
// It's use is also fully transparent to the user, since all methods always return | |
// the original base classes instead of the wrapper. | |
implicit class ComponentWrapper(that: orig.Component) { | |
def enabled: Boolean = that.isEnabled | |
def enabled_=(enabled: Boolean) { that.setEnabled(enabled) } | |
def visible: Boolean = that.isVisible | |
def visible_=(visible: Boolean) { that.setVisible(visible) } | |
def caption: Option[String] = Option(that.getCaption) | |
def caption_=(caption: Option[String]) { that.setCaption(caption.orNull) } | |
def caption_=(caption: String) { that.setCaption(caption) } | |
// ... | |
} | |
// Creating Instances | |
// ------------------ | |
// Since we are not fully wrapping classes anymore there is no way to | |
// add new (more convenient) constructors. Since assembling the GUI | |
// most of the time consists of plugging (mutable) components together | |
// to form the GUI graph, adding a curried "initialization function" | |
// partially solves this problem: | |
object Button { | |
private type ClickEvent = orig.Button.ClickEvent | |
// constructor with default values set to null, since they can be omitted | |
def apply(caption: String = null, listener: ClickEvent => Unit = null) | |
(implicit init: orig.Button => Unit): orig.Button = const(new orig.Button) { b => | |
Option(caption).map(b.setCaption(_)) | |
Option(listener).map(b.addClickListener(_)) | |
} | |
} | |
// adding an noop implicit to be able to avoid the callback all together | |
implicit def noop[T](x: T): Unit = () | |
// companion class to the Button object above, necessary to allow adding lambdas as | |
// listener | |
implicit class ButtonWrapper(that: orig.Button) { | |
def addClickListener(listener: orig.Button.ClickEvent => Unit) { | |
that.addClickListener(new orig.Button.ClickListener { | |
def buttonClick(evt: orig.Button.ClickEvent) = listener(evt) | |
}) | |
} | |
def +=(listener: orig.Button.ClickEvent => Unit) { that.addClickListener(listener) } | |
} | |
// Collection Like Classes | |
// ----------------------- | |
// Container classes (like layouts) can be used as scala-collections by | |
// inheriting from the appropriate scala collection class: | |
private type AbsolutePosition = orig.AbsoluteLayout#ComponentPosition | |
implicit class AbsoluteLayoutWrapper(that: orig.AbsoluteLayout) | |
extends mutable.Map[orig.Component, AbsolutePosition] { | |
def -=(key: orig.Component): this.type = { that.removeComponent(key); this } | |
def +=(kv: (orig.Component, AbsolutePosition)): this.type = { | |
that.addComponent(kv._1, kv._2.getCSSString); | |
this | |
} | |
def +=(key: orig.Component): this.type = { | |
that.addComponent(key); | |
this | |
} | |
def get(key: orig.Component): Option[AbsolutePosition] = Option(that.getPosition(key)) | |
def iterator: Iterator[(orig.Component, AbsolutePosition)] = | |
JavaConversions.asScalaIterator(that.iterator).map { k => | |
(k, that.getPosition(k)) | |
} | |
override def size = that.getComponentCount | |
override def empty: AbsoluteLayout = AbsoluteLayout(new orig.AbsoluteLayout) | |
} | |
// just a helper combinator | |
private def const[T](x: T)(f: T => Unit): T = { f(x); x } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.vaadin.ui._ | |
import scalavaadin.ui._ | |
import Button.ClickEvent | |
object usageExample { | |
// example for not providing an init-callback | |
val b1 = Button() | |
// example using the callback | |
val b2 = Button("boo") { b => | |
b += { evt: ClickEvent => | |
println("clicked") | |
} | |
println("name is" + b.caption) | |
} | |
b2.caption = "bar" | |
println(b1) | |
println(b2) | |
val layout = AbsoluteLayout { l => | |
l += Button() | |
l += b1 | |
l += b2 | |
l(b1) setCSSString "top: 10px; left: 20%; z-index: 16;" | |
} | |
// layout now can be used like a collection | |
for ((copm, pos) <- layout) { | |
println(s"$comp at $pos") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment