benhutchison / ScalaSwingContrib

Collection of community contributions to Scala Swing
21 stars 9 forks source link

GroupPanel type woes #2

Open Sciss opened 11 years ago

Sciss commented 11 years ago

I am regularly running into trouble trying to figure out how to massage my components to fit into the GroupPanel's groups. I have come up with all sorts of crazy type conversions, and once again I forgot how I managed. It would be great to have an example that shows how to accomplish the IMO standard case of creating a group from a collection of widgets:

val labels  = (1 to 10).map(i => new Label(i.toString))
val fields  = (1 to 10).map(i => new TextField(10))

new GroupPanel {
  theHorizontalLayout is Sequential(
    Parallel(labels: _*), Parallel(fields: _*)  // type mismatch: required: Seq[this.InParallel]
  )
  theVerticalLayout is Sequential(              // type mismatch: required: Seq[this.InSequential]
    (labels zip fields).map { case (lb, f) =>
      Parallel(Baseline)(lb, f)
    }: _*
  )
}

This drives me nuts. InParallel and InSequential are private types, so the straight forward : InParallel doesn't work.

Am I blind not to see the solution?

asflierl commented 11 years ago

Hi, I am a bit swamped in work right now so can't fully check this out before next week...

Off the top of my head, here's an idea for the first case:

theHorizontalLayout is Sequential(
  Parallel((labels map add):_*), Parallel((fields map add):_*)
)

It might be possible to add your own implicit like

implicit def addAll[A <: G](comps: Seq[Component]): Seq[InGroup[A]] = comps map add

to enable your original code to work as is.

The (badly named, sorry) add method is what (implicitly) converts a component to an element inside a specific group. This usually happens by the time you specify a component "by hand" inside a Parallel(...) or Sequential(...), so triggering that conversion explicitly might help. You might have to give a specific subtype of GroupLayout#Group as type parameter to add.

By the way, note that InParallel and InSequential are nothing more than type aliases for InGroup[GroupLayout#ParallelGroup] and InGroup[GroupLayout#SequentialGroup] resp. and the InGroup type should be accessible. Softening the access restriction on those two type aliases from private[grouppanel] to protected shouldn't hurt, although it might not help at all in your case (since the conversion must be triggered somehow).

Also note that the underlying group layout is fully exposed through the layout field - for better or worse, so you can always fall back to Java-style as a last resort.

Sciss commented 11 years ago

Parallel((labels map add):_*) is not enough for the type checker, because it fails to infer the type parameter of add. So in the end I had something like

Parallel(labels.map(lb => add[GroupLayout#ParallelGroup](lb)): _*)

and

(labels zip fields).map { case (lb, f) =>    
  Parallel(Baseline)(lb, f): InGroup[GroupLayout#SequentialGroup]
}): _*

So I was wondering if not the best solution is to define the apply method on Parallel and Sequential along the lines of

def apply[A <% InParallel](elems: A*): Group

I haven't played this through, but perhaps it's an easy solution.