Fantom

 

//
// Copyright (c) 2011, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   28 Oct 09  Andy Frank  Creation
//

using fwt
using gfx

**
** FlowPane lays out widgets in rows. Widgets are layed out consecutively
** based on their preferred size. Widgets are wrapped to the next row
** when they exceed the Pane's width. The row height will be the max
** pref size of the widgets in that row.
**
@NoDoc
@Js
class FlowPane : Pane
{

//////////////////////////////////////////////////////////////////////////
// Children
//////////////////////////////////////////////////////////////////////////

  ** Gap to leave between widgets horizontally.
  Int hgap := 4

  ** Gap to leave between widgets vertically.
  Int vgap := 4

//////////////////////////////////////////////////////////////////////////
// Layout
//////////////////////////////////////////////////////////////////////////

  override Size prefSize(Hints hints := Hints.defVal)
  {
    //w  := hints.w==0 ? Int.maxVal : hints.w
    w := Int.maxVal
    pw := 0
    ph := 0
    rh := 0 // cur row pref width
    rw := 0 // cur row pref height

    children.each |kid,i|
    {
      // widget pref size
      tw := 0
      if (i > 0) tw += hgap
      pref := kid.prefSize
      tw += pref.w

      if (rw+tw > w)
      {
        // wrap row
        pw = pw.max(rw)
        ph += rh + vgap
        rw = pref.w
        rh = pref.h
      }
      else
      {
        // accumulate
        rw += tw
        rh = rh.max(pref.h)
      }
    }

    pw = pw.max(rw)
    ph = ph + rh
    return Size(pw, ph)
  }

  override Void onLayout()
  {
    row := Widget[,]
    w   := size.w
    dx  := 0
    dy  := 0
    rh  := 0 // cur row max height

    // valign method
    valign := |Widget x, Int h|
    {
      x.pos = Point(x.pos.x, (h - x.bounds.h) / 2)
    }

    children.each |kid|
    {
      pref := kid.prefSize

      if (dx+pref.w > w)
      {
        // center vertically for this row
        row.each |r| { valign(r, rh) }

        // wrap row
        dx = 0
        dy += rh + vgap
        rh = 0
      }
      else row.add(kid)

      // set bounds
      kid.bounds = Rect(dx, dy, pref.w, pref.h)
      dx += pref.w + hgap
      rh = rh.max(pref.h)
    }

    // center vertically any leftovers
    row.each |r| { valign(r, rh) }
  }
}