Patch title: Release 96 bulk changes
Abstract:
File: /pliant/graphic/layout/form.pli
Key:
    Removed line
    Added line
   
module "/pliant/language/compiler.pli"
module "/pliant/language/compiler.pli"
module "/pliant/language/compiler/type/inherit.pli"
module "/pliant/util/pml/io.pli"
module "prototype.pli"
module "/pliant/graphic/draw/prototype.pli"
module "/pliant/graphic/console/prototype.pli"
module "/pliant/util/pml/io.pli"
module "prototype.pli"
module "/pliant/graphic/draw/prototype.pli"
module "/pliant/graphic/console/prototype.pli"
module "helper/position.pli"
module "helper/draw.pli"
module "helper/event.pli"
module "sequence.pli"
module "helper/draw.pli"
module "helper/event.pli"
module "sequence.pli"
module "text.pli"
module "/pliant/util/encoding/utf8.pli"
module "/pliant/util/encoding/utf8.pli"
module "/pliant/graphic/browser/client/context.pli"
module "/pliant/graphic/browser/client/window.pli"
module "/pliant/graphic/ui/client/context.pli"
module "/pliant/graphic/ui/client/window.pli"


public
  type LayoutInput


public
  type LayoutInput
    field Link:LayoutPrototype next
    field Int parent_and_flags <- 0
    inherit LayoutPrototype
    field LayoutArea bbox ; field Float tx ty
    field Str value
    field Str id
    field Int flags <- 0
    field LayoutArea bbox ; field Float tx ty
    field Str value
    field Str id
    field Int flags <- 0
    field Str help


  type LayoutSelect


  type LayoutSelect
    field Link:LayoutPrototype next
    field Int parent_and_flags <- 0
    inherit LayoutPrototype
    field LayoutArea bbox ; field Float tx ty
    field Str value
    field Str id
    field Int flags <- 0
    field LayoutArea bbox ; field Float tx ty
    field Str value
    field Str id
    field Int flags <- 0
    field Str help
    field List:LayoutSelectOption options


method i bbox c -> b
  oarg_rw LayoutInput i ; arg_rw LayoutPC c ; arg LayoutArea
    field List:LayoutSelectOption options


method i bbox c -> b
  oarg_rw LayoutInput i ; arg_rw LayoutPC c ; arg LayoutArea
  var Pointer:LayoutStyleInput s :> c:style input
  implicit s
    var Str32 v := utf8_decode i:value
    if (i:flags .and. 2)<>0
      v := repeat v:len "*"
    var Int start := v search "[lf]" v:len
    value_font bbox (v 0 start) null (var Float bx0) (var Fl
    while start<v:len
      start += 1
      var Int stop := ((v start v:len) search "[lf]" v:len-s
      value_font bbox (v start stop-start) null (var Float c
      bx1 := max bx1 bx0+(cx1-cx0)
      by1 += cy1-cy0
      start := stop
    i:bbox x0 := bx0*value_scale-padding_x
    i:bbox y0 := by0*value_scale-padding_y
    i:bbox x1 := bx1*value_scale+padding_x
    i:bbox y1 := by1*value_scale+padding_y
  var Pointer:LayoutStyleInput s :> c:style:input standard
  var Str32 v := utf8_decode i:value
  if (i:flags .and. 2)<>0
    v := repeat v:len "*"
  var Int start := v search "[lf]" v:len
  s:text:font bbox (v 0 start) null 0 s:text:size (var Float bx0) (var Float by0) (var Float bx1) (var Float by1)
  while start<v:len
    start += 1
    var Int stop := ((v start v:len) search "[lf]" v:len-start)+start
    s:text:font bbox (v start stop-start) null 0 s:text:size (var Float cx0) (var Float cy0) (var Float cx1) (var Float cy1)
    bx1 := max bx1 cx1
    by1 += cy1-cy0
    start := stop
  i bbox := box_position (area bx0 by0 bx1 by1) s:box
  b := i bbox


method i draw d c
  arg_rw LayoutInput i ; oarg_rw DrawPrototype d ; arg_rw La
  b := i bbox


method i draw d c
  arg_rw LayoutInput i ; oarg_rw DrawPrototype d ; arg_rw La
  var Pointer:LayoutStyleInput s :> c:style input
  var Pointer:LayoutStyleInput st :> c:style:input standard
  if (addressof c:window:session:focus_target)=addressof:i
    st :> c:style:input focus
  var Pointer:LayoutStyleFocus fs :> c:style focus
  var Pointer:LayoutStyleFocus fs :> c:style focus
  implicit s
    d rectangle i:bbox:x0+i:tx i:bbox:y0+i:ty i:bbox:x1+i:tx
    if border_size>0
      d rectangle i:bbox:x0+i:tx i:bbox:y0+i:ty i:bbox:x1+i:
    var Str32 v := utf8_decode i:value
    if (i:flags .and. 2)<>0
      v := repeat v:len "*"
    var Int index := shunt (addressof c:window:session:focus
    var Int start := v search "[lf]" v:len
    value_font bbox (v 0 start) null (var Float bx0) (var Fl
    var Transform2 t := transform i:bbox:x0+padding_x-value_
    d text (v 0 start) value_font null t (c color value_colo
    if index>=0 and index<=start
      var Float x := i:bbox:x0+padding_x-value_scale*bx0+val
      d rectangle x t:yt+value_scale*value_font:bbox_y0 x+fs
      c:window focus_area x t:yt+value_scale*value_font:bbox
    while start<v:len
      start += 1
      var Int stop := ((v start v:len) search "[lf]" v:len-s
      value_font bbox (v start stop-start) null (var Float c
      t yt += value_scale*(cy1-cy0)
      d text (v start stop-start) value_font null t (c color
      if index>=start and index<=stop
        var Float x := i:bbox:x0+padding_x-value_scale*bx0+v
        d rectangle x t:yt+value_scale*value_font:bbox_y0 x+
        c:window focus_area x t:yt+value_scale*value_font:bb
      start := stop
  box_draw i:bbox:x0+i:tx i:bbox:y0+i:ty i:bbox:x1+i:tx i:bbox:y1+i:ty st:box d c
  var Str32 v := utf8_decode i:value
  if (i:flags .and. 2)<>0
    v := repeat v:len "*"
  var Int start := v search "[lf]" v:len
  var Pointer:Font font :> st:text font
  var Float size := st:text size
  var Transform2 t := transform i:tx i:ty size size 0 0
  d text (v 0 start) font null t (c color st:text:color)
  var Int index := shunt (addressof c:window:session:focus_target)=addressof:i c:window:session:focus_index undefined
  if index>=0 and index<=start
    var Float x := i:tx+size*(font vector (v 0 index) null):x
    d rectangle x t:yt+size*font:bbox_y0 x+fs:thickness t:yt+size*font:bbox_y1 (c color fs:color)
    c:window focus_area x t:yt+size*font:bbox_y0 x+fs:thickness t:yt+size*font:bbox_y1
  while start<v:len
    start += 1
    var Int stop := ((v start v:len) search "[lf]" v:len-start)+start
    t yt += size*(font:bbox_y1-font:bbox_y0)
    d text (v start stop-start) font null t (c color st:text:color)
    if index>=start and index<=stop
      var Float x := i:tx+size*(font vector (v start index-start) null):x
      d rectangle x t:yt+size*font:bbox_y0 x+fs:thickness t:yt+size*font:bbox_y1 (c color fs:color)
      c:window focus_area x t:yt+size*font:bbox_y0 x+fs:thickness t:yt+size*font:bbox_y1
    start := stop




method session help text
  arg_rw UISession session ; arg Str text
  var Link:LayoutSection s :> (session:namespace first "help") map LayoutSection
  if not exists:s or (entry_type addressof:s)<>LayoutSection
    return
  var Link:LayoutPrototype p :> s first
  while exists:p
    session discard p
    var Link:LayoutPrototype n :> p next
    p next :> null map LayoutPrototype
    p :> n
  if text<>""
    var Link:LayoutPara para :> new LayoutPara
    para next :> null map LayoutPrototype  
    para set_parent s
    s first_son :> para
    var Link:LayoutText txt :> new LayoutText
    txt text := text
    txt set_parent para
    para first_son :> txt
  else
    s first_son :> null map LayoutPrototype
  session reposition s

method p next_input target found -> next
  oarg_rw LayoutPrototype p ; arg_rw LayoutPrototype target 
  var Link:LayoutPrototype p2 :> p first
  while exists:p2
    if p2:focusable and found
      next :> p2
      return
    if addressof:p2=addressof:target
      found := true
    next :> p2 next_input target found
    if exists:next
      return
    p2 :> p2 next
  next :> null map LayoutPrototype

method p next_input target found -> next
  oarg_rw LayoutPrototype p ; arg_rw LayoutPrototype target 
  var Link:LayoutPrototype p2 :> p first
  while exists:p2
    if p2:focusable and found
      next :> p2
      return
    if addressof:p2=addressof:target
      found := true
    next :> p2 next_input target found
    if exists:next
      return
    p2 :> p2 next
  next :> null map LayoutPrototype

method ec send_change l id value
  arg_rw LayoutEC ec ; oarg LayoutPrototype l ; arg Str id value
  var Pointer:UISession s :> ec:window session
  s:connection otag "set" id value
  s:connection flush anytime
  var LayoutEC ec2
  ec2 style :> s:console default_style
  ec2 event := "change"
  ec2 window :> ec window
  ec2 event_options := "old_value "+(string s:focus_value)+" new_value "+string:value
  var Link:LayoutPrototype p :> l parent
  while exists:p
    p event ec2
    p :> p parent
  s focus_value := value

method i event c
  oarg_rw LayoutInput i ; arg_rw LayoutEC c
method i event c
  oarg_rw LayoutInput i ; arg_rw LayoutEC c
  var Pointer:BrowserSession s :> c:window session
  var Pointer:LayoutStyleInput st :> c:style input
  implicit st
    var CBool pos := i:bbox:x0<>undefined
    var CBool resize := false
    var CBool redraw := false
    if c:mode=layout_event_pointer and c:x>=i:bbox:x0+i:tx a
      var Float x := i:bbox:x0+padding_x+i:tx ; var Float y 
      var Str32 v := i value
      part scan
        for (var Int index) 0 v:len-1
          var Float dx := value_scale*(value_font vector v:i
          if c:y>=y+value_scale*value_font:bbox_y0 and c:y<=
            if c:x>=x and c:x<=x+dx
              leave scan
          if v:index="[lf]"
            x := i:bbox:x0+padding_x ; y += value_scale*(val
          else
            x += dx
      c pointer_target :> i
      c pointer_index := index
      if c:event="press" and c:key="button1"
        if (addressof s:focus_target)<>addressof:i
          c set_focus i index
  var Pointer:UISession s :> c:window session
  var Pointer:LayoutStyleInput st :> c:style:input standard
  var Pointer:Font font :> st:text font
  var Float size := st:text size
  var CBool pos := i:bbox:x0<>undefined
  var CBool resize := false
  var CBool redraw := false
  if c:mode=layout_event_pointer and c:x>=i:bbox:x0+i:tx and c:x<=i:bbox:x1+i:tx and c:y>=i:bbox:y0+i:ty and c:y<=i:bbox:y1+i:ty
    var Float x := i tx ; var Float y := i ty
    var Str32 v := i value
    part scan
      for (var Int index) 0 v:len-1
        var Float dx := size*(font vector v:index:number):x
        if c:y>=y+size*font:bbox_y0 and c:y<=y+size*font:bbox_y1
          if c:x>=x and c:x<=x+dx
            leave scan
        if v:index="[lf]"
          x := i tx ; y += size*(font:bbox_y1-font:bbox_y0)
        else
        else
          s set_focus_index index
          redraw := true
        s:console clipboard_target :> i
        s:console clipboard_index := index
        c cancel := true
      eif c:event="release" and c:key="button1" and (address
        var Int i0 := min s:console:clipboard_index index
        var Int i1 := max s:console:clipboard_index index
        if i0>=0
          var Str32 v := utf8_decode i:value
          s:console clipboard_set_text utf8_encode:(v i0 i1-
        c cancel := true
      eif c:event="press" and c:key="button2" and index>=0
          x += dx
    c pointer_target :> i
    c pointer_index := index
    if c:event="press" and c:key="button1"
      if (addressof s:focus_target)<>addressof:i
        c set_focus i index
      else
        s set_focus_index index
        redraw := true
      s:console clipboard_target :> i
      s:console clipboard_index := index
      c cancel := true
    eif c:event="release" and c:key="button1" and (addressof s:console:clipboard_target)=addressof:i
      var Int i0 := min s:console:clipboard_index index
      var Int i1 := max s:console:clipboard_index index
      if i0>=0
        var Str32 v := utf8_decode i:value
        var Str32 v := utf8_decode i:value
        var Str32 cp := utf8_decode s:console:clipboard_get_
        i value := utf8_encode (v 0 index)+cp+(v index v:len
        s set_focus_index s:focus_index+cp:len
        resize := true ; redraw := true
        c cancel := true
      eif c:event="move" and (i:flags .and. 1)<>0
        c set_over i
        c cancel := true
    if c:mode=layout_event_focus and (addressof s:focus_targ
      var Int index := s focus_index
      if c:event="character"
        var Str32 v := utf8_decode i:value
        i value := utf8_encode (v 0 index)+(utf8_decode c:ke
        s set_focus_index s:focus_index+1
        resize := true ; redraw := true
      eif c:event="press" and c:key="enter" and (i:flags .an
        var Str32 v := i value
        i value := utf8_encode (v 0 index)+"[lf]"+(v index v
        s set_focus_index s:focus_index+1
        resize := true ; redraw := true
      eif c:event="press" and c:key="backspace" and index>0
        var Str32 v := utf8_decode i:value
        i value := utf8_encode (v 0 index-1)+(v index v:len)
        s set_focus_index s:focus_index-1
        resize := true ; redraw := true
      eif c:event="press" and c:key="tab"
        var Link:LayoutPrototype next :> c:window:root next_
        if exists:next
          c set_focus next 0
    if c:event="focus"
      if c:key="on"
        s focus_value := i value
      if (c:key="off" or c:key="sync") and i:value<>s:focus_
        c:window:session:connection otag "set" i:id i:value
        c:window:session:connection flush anytime
        s focus_value := i value
      redraw := true
    if c:event="over"
      c:window:session:connection otag "over" i:id c:key="on
      c:window:session:connection flush anytime
    if pos and resize
        s:console clipboard_set_text utf8_encode:(v i0 i1-i0+1)
      c cancel := true
    eif c:event="press" and c:key="button3" and index>=0
      var Str32 v := utf8_decode i:value
      var Str32 v := utf8_decode i:value
      if (i:flags .and. 2)<>0
        v := repeat v:len "*"
      var Int start := v search "[lf]" v:len
      value_font bbox (v 0 start) null (var Float bx0) (var 
      while start<v:len
        start += 1
        var Int stop := ((v start v:len) search "[lf]" v:len
        value_font bbox (v start stop-start) null (var Float
        bx1 := max bx1 bx0+(cx1-cx0)
        by1 += cy1-cy0
        start := stop
      var Float delta_x := (value_scale*(bx1-bx0)+2*padding_
      var Float delta_y := (value_scale*(by1-by0)+2*padding_
      if abs:delta_x>1e-6 or abs:delta_y>=1e-6
        c reposition i
        if delta_x>0
          i:bbox:x1 += delta_x
        if delta_y>0
          i:bbox:y1 += delta_y
    if pos and redraw
      c:window redraw_area i:bbox:x0+i:tx i:bbox:y0+i:ty i:b
      var Str32 cp := utf8_decode s:console:clipboard_get_text
      i value := utf8_encode (v 0 index)+cp+(v index v:len)
      s set_focus_index s:focus_index+cp:len
      resize := true ; redraw := true
      c cancel := true
    eif c:event="move" and i:help<>""
      c set_over i
      c cancel := true
  if c:mode=layout_event_focus and (addressof s:focus_target)=addressof:i
    var Int index := s focus_index
    if c:event="character"
      var Str32 v := utf8_decode i:value
      i value := utf8_encode (v 0 index)+(utf8_decode c:key)+(v index v:len)
      s set_focus_index s:focus_index+1
      resize := true ; redraw := true
    eif c:event="press" and c:key="enter" and (i:flags .and. 4)<>0
      var Str32 v := utf8_decode i:value
      i value := utf8_encode (v 0 index)+"[lf]"+(v index v:len)
      s set_focus_index s:focus_index+1
      resize := true ; redraw := true
    eif c:event="press" and c:key="backspace" and index>0
      var Str32 v := utf8_decode i:value
      i value := utf8_encode (v 0 index-1)+(v index v:len)
      s set_focus_index s:focus_index-1
      resize := true ; redraw := true
    eif c:event="press" and c:key="left" and index>0
      s set_focus_index s:focus_index-1
      redraw := true
    eif c:event="press" and c:key="right" and index<(utf8_decode i:value):len
      s set_focus_index s:focus_index+1
      redraw := true
    eif c:event="press" and c:key="home"
      s set_focus_index 0
      redraw := true
    eif c:event="press" and c:key="end"
      s set_focus_index (utf8_decode i:value):len
      redraw := true
    eif c:event="press" and c:key="tab"
      var Link:LayoutPrototype next :> c:window:root next_input i { var CBool found := false ; found }
      if exists:next
        c set_focus next 0
  if c:event="focus"
    if c:key="on"
      s focus_value := i value
    if (c:key="off" or c:key="sync") and i:value<>s:focus_value
      c send_change i i:id i:value
    redraw := true
  if c:event="over"
    s help (shunt c:key="on" i:help "")
  if pos and resize
    var Str32 v := utf8_decode i:value
    if (i:flags .and. 2)<>0
      v := repeat v:len "*"
    var Int start := v search "[lf]" v:len
    font bbox (v 0 start) null 0 size (var Float bx0) (var Float by0) (var Float bx1) (var Float by1)
    while start<v:len
      start += 1
      var Int stop := ((v start v:len) search "[lf]" v:len-start)+start
      font bbox (v start stop-start) null 0 size (var Float cx0) (var Float cy0) (var Float cx1) (var Float cy1)
      bx1 := max bx1 cx1
      by1 += cy1-cy0
      start := stop
    var LayoutArea a := box_position (area bx0 by0 bx1 by1) st:box
    var Float delta_x := (a:x1-a:x0) - (i:bbox:x1-i:bbox:x0)
    var Float delta_y := (a:y1-a:y0) - (i:bbox:y1-i:bbox:y0)
    if abs:delta_x>1e-6 or abs:delta_y>=1e-6
      c reposition i
      if delta_x>0
        i:bbox:x1 += delta_x
      if delta_y>0
        i:bbox:y1 += delta_y
  if pos and redraw
    c:window redraw_area i:bbox:x0+i:tx i:bbox:y0+i:ty i:bbox:x1+i:tx i:bbox:y1+i:ty


method s bbox c -> b
  arg_rw LayoutSelect s ; arg_rw LayoutPC c ; arg LayoutArea


method s bbox c -> b
  arg_rw LayoutSelect s ; arg_rw LayoutPC c ; arg LayoutArea
  var Pointer:LayoutStyleInput st :> c:style input
  implicit st
    var Str label := ""
    each o s:options
      if o:value=s:value
        label := o label
    value_font bbox label null (var Float bx0) (var Float by
    s:bbox x0 := bx0*value_scale-padding_x
    s:bbox y0 := by0*value_scale-padding_y
    s:bbox x1 := bx1*value_scale+padding_x
    s:bbox y1 := by1*value_scale+padding_y
  var Pointer:LayoutStyleInput st :> c:style:select standard
  var Str label := ""
  each o s:options
    if o:value=s:value
      label := o label
  s bbox := box_position (content_position label st:text) st:box
  b := s bbox


method s draw d c
  arg_rw LayoutSelect s ; oarg_rw DrawPrototype d ; arg_rw L
  b := s bbox


method s draw d c
  arg_rw LayoutSelect s ; oarg_rw DrawPrototype d ; arg_rw L
  var Pointer:LayoutStyleInput st :> c:style input
  implicit st
    d rectangle s:bbox:x0+s:tx s:bbox:y0+s:ty s:bbox:x1+s:tx
    if border_size>0
      d rectangle s:bbox:x0+s:tx s:bbox:y0+s:ty s:bbox:x1+s:
      if round>0
        var Address color := (c color border_color)
        if (addressof c:window:session:focus_target)=address
          color := c color c:style:focus:color    
        d rectangle s:bbox:x1-2.001*round+s:tx s:bbox:y0+s:t
    var Str label := ""
    each o s:options
      if o:value=s:value
        label := o label
    value_font bbox label null (var Float bx0) (var Float by
    var Transform2 t := transform s:bbox:x0+padding_x-value_
    d text label value_font null t (c color value_color)
  # if (addressof c:window:session:focus_target)=addressof:s
  #   color := c color c:style:focus:color
  var Pointer:LayoutStyleInput st :> c:style:select standard
  if (addressof c:window:session:focus_target)=addressof:s
    st :> c:style:select focus
  box_draw s:bbox:x0+s:tx s:bbox:y0+s:ty s:bbox:x1+s:tx s:bbox:y1+s:ty st:box d c
  if (st:box:mode .and. 100h)<>0
    var LayoutColor memo := st:box:r1 color
    if (st:box:mode .and. 200h)<>0
      st:box:r1 color := c:style:focus color
    box_draw s:bbox:x1-2*st:box:r1:round+s:tx s:bbox:y0+s:ty s:bbox:x1+s:tx s:bbox:y1+s:ty st:box d c
    st:box:r1 color := memo
  if (addressof c:window:session:focus_target)=addressof:s
    c:window focus_area s:bbox:x0+s:tx s:bbox:y0+s:ty s:bbox:x1+s:tx s:bbox:y1+s:ty
  var Str label := ""
  each o s:options
    if o:value=s:value
      label := o label
  d text label st:text:font null (transform s:tx s:ty st:text:size st:text:size 0 0) (c color st:text:color)

 
public
  type LayoutSelectOptions

 
public
  type LayoutSelectOptions
    field Link:LayoutPrototype next
    field Int parent_and_flags <- 0
    inherit LayoutPrototype
    field Link:LayoutSelect select
    field LayoutArea bbox


method so position c -> b
  arg_rw LayoutSelectOptions so ; arg_rw LayoutPC c ; arg La
  var Link:LayoutSelect s :> so select
    field Link:LayoutSelect select
    field LayoutArea bbox


method so position c -> b
  arg_rw LayoutSelectOptions so ; arg_rw LayoutPC c ; arg La
  var Link:LayoutSelect s :> so select
  var Pointer:LayoutStyleInput st :> c:style input
  implicit st
    so:bbox x0 := s:bbox:x0+s:tx
    so:bbox y0 := s:bbox:y0+s:ty
    so:bbox x1 := s:bbox:x1+s:tx
    so:bbox y1 := so:bbox y0
    each o s:options
      if o:value=s:value
        so:bbox y0 += s:bbox:y0+s:ty-so:bbox:y1
        so:bbox y1 := s:bbox:y0+s:ty
      value_font bbox o:label null (var Float bx0) (var Floa
      so:bbox x1 := max so:bbox:x1 so:bbox:x0+bx1*value_scal
      so:bbox y1 += value_scale*(by1-by0)
    so:bbox x1 := max so:bbox:x1 so:bbox:x0+2.001*round
    so:bbox y1 := max (max s:bbox:y1 so:bbox:y1) so:bbox:y0+
  var Pointer:LayoutStyleInput st :> c:style:input standard
  so:bbox x0 := s:bbox:x0+s:tx
  so:bbox y0 := s:bbox:y0+s:ty
  so:bbox x1 := s:bbox:x1+s:tx
  so:bbox y1 := so:bbox:y0+st:box:padding:y0
  each o s:options
    if o:value=s:value
      so:bbox y0 += s:bbox:y0+s:ty+st:box:padding:y0-so:bbox:y1
      so:bbox y1 := s:bbox:y0+s:ty+st:box:padding:y0
    st:text:font bbox o:label null 0 st:text:size (var Float bx0) (var Float by0) (var Float bx1) (var Float by1)
    so:bbox x1 := max so:bbox:x1 so:bbox:x0+bx1+st:box:padding:x0+st:box:padding:x1
    so:bbox y1 += by1-by0
  so:bbox y1 := max so:bbox:y1+st:box:padding:y1 s:bbox:y1+s:ty
  b := so bbox

    
method so draw d c
  arg_rw LayoutSelectOptions so ; oarg_rw DrawPrototype d ; 
  if so:bbox:x0=undefined
    return
  var Link:LayoutSelect s :> so select
  b := so bbox

    
method so draw d c
  arg_rw LayoutSelectOptions so ; oarg_rw DrawPrototype d ; 
  if so:bbox:x0=undefined
    return
  var Link:LayoutSelect s :> so select
  var Pointer:LayoutStyleInput st :> c:style input
  implicit st
    d rectangle so:bbox:x0 so:bbox:y0 so:bbox:x1 so:bbox:y1 
    if border_size>0
      d rectangle so:bbox:x0 so:bbox:y0 so:bbox:x1 so:bbox:y
    var Float y := so:bbox:y0+padding_y-value_scale*value_fo
    each o s:options
      var Transform2 t := transform so:bbox:x0+padding_x y v
      d text o:label value_font null t (c color value_color)
      y += value_scale*(value_font:bbox_y1-value_font:bbox_y
  var Pointer:LayoutStyleInput st :> c:style:input standard
  box_draw so:bbox:x0 so:bbox:y0 so:bbox:x1 so:bbox:y1 st:box d c
  var Float y := so:bbox:y0+st:box:padding:y0-st:text:size*st:text:font:bbox_y0
  each o s:options
    var Transform2 t := transform so:bbox:x0+st:box:padding:x0 y st:text:size st:text:size 0 0
    d text o:label st:text:font null t (c color st:text:color)
    y += st:text:size*(st:text:font:bbox_y1-st:text:font:bbox_y0)
  if (addressof c:window:session:focus_target)=addressof:so
    c:window focus_area so:bbox:x0 so:bbox:y0 so:bbox:x1 so:bbox:y1
 

method s event c
  oarg_rw LayoutSelect s ; arg_rw LayoutEC c
 

method s event c
  oarg_rw LayoutSelect s ; arg_rw LayoutEC c
  if c:mode=layout_event_pointer and c:x>=s:bbox:x0+s:tx and
  var Pointer:LayoutStyleInput st :> c:style:input standard
  var CBool redraw := false
  if c:mode=layout_event_pointer and c:x>=s:bbox:x0+s:tx+st:box:padding:x0 and c:x<=s:bbox:x1+s:tx-st:box:padding:x1 and c:y>=s:bbox:y0+s:ty+st:box:padding:y0 and c:y<=s:bbox:y1+s:ty-st:box:padding:y1
    if c:event="press" and c:key="button1"
      var Link:LayoutSelectOptions o :> new LayoutSelectOpti
      o set_parent (null map LayoutPrototype)
      o select :> s
      c:window overlay :> o
      c:window orefresh := true
      c set_focus o 0
      c:window redraw_hurry
    if c:event="press" and c:key="button1"
      var Link:LayoutSelectOptions o :> new LayoutSelectOpti
      o set_parent (null map LayoutPrototype)
      o select :> s
      c:window overlay :> o
      c:window orefresh := true
      c set_focus o 0
      c:window redraw_hurry
    eif c:event="move" and (s:flags .and. 1)<>0
    eif c:event="move"
      c set_over s
      c cancel := true
  if c:mode=layout_event_focus and (addressof c:window:sessi
      c set_over s
      c cancel := true
  if c:mode=layout_event_focus and (addressof c:window:sessi
    if c:event="press" and c:key="tab"
    if c:event="press" and (c:key="up" or c:key="down")
      var Pointer:LayoutSelectOption o2 :> s:options first
      while exists:o2 and o2:value<>s:value
        o2 :> s:options next o2
      if c:key="up" and exists:o2 and exists:(s:options previous o2)
        s value := (s:options previous o2) value
        redraw := true
      eif c:key="down" and exists:o2 and exists:(s:options next o2)
        s value := (s:options next o2) value
        redraw := true
    eif c:event="press" and c:key="tab"
      var Link:LayoutPrototype next :> c:window:root next_in
      if exists:next
        c set_focus next 0
      var Link:LayoutPrototype next :> c:window:root next_in
      if exists:next
        c set_focus next 0
  if c:event="over"
    c:window:session help (shunt c:key="on" s:help "")
  if c:event="focus"
  if c:event="focus"
    if c:key="on"
      c:window:session focus_value := s value
    if (c:key="off" or c:key="sync") and s:value<>c:window:session:focus_value
      c send_change s s:id s:value
    redraw := true
  if c:event="focus"
    redraw := true
  if redraw
    c:window redraw_area s:bbox:x0+s:tx s:bbox:y0+s:ty s:bbo
    c:window redraw_area s:bbox:x0+s:tx s:bbox:y0+s:ty s:bbo
  if c:event="over"
    c:window:session:connection otag "over" s:id c:key="on"
    c:window:session:connection flush anytime


method so event c
  oarg_rw LayoutSelectOptions so ; arg_rw LayoutEC c
  var Link:LayoutSelect s :> so select


method so event c
  oarg_rw LayoutSelectOptions so ; arg_rw LayoutEC c
  var Link:LayoutSelect s :> so select
  var Pointer:LayoutStyleInput st :> c:style input
  var Pointer:LayoutStyleInput st :> c:style:input standard
  implicit st
    if c:mode=layout_event_pointer and c:x>=so:bbox:x0 and c
      if c:event="release" and c:key="button1"
  implicit st
    if c:mode=layout_event_pointer and c:x>=so:bbox:x0 and c
      if c:event="release" and c:key="button1"
        var Int index := cast (c:y-so:bbox:y0)/(st:value_fon
        var Int index := cast (c:y-so:bbox:y0-st:box:padding:y0)/(st:text:font:bbox_y1-st:text:font:bbox_y0)/st:text:size-0.5 Int
        var Int i := 0
        each o s:options
          if i=index
            s value := o value
          i += 1
        c set_focus s 0
        c:window redraw_hurry
    if c:event="focus"
        var Int i := 0
        each o s:options
          if i=index
            s value := o value
          i += 1
        c set_focus s 0
        c:window redraw_hurry
    if c:event="focus"
      var Pointer:BrowserSession session :> c:window session
      var Pointer:UISession session :> c:window session
      if c:key="on"
        session focus_value := s value
      if (c:key="off" or c:key="sync") and s:value<>session:
      if c:key="on"
        session focus_value := s value
      if (c:key="off" or c:key="sync") and s:value<>session:
        session:connection otag "set" s:id s:value
        session:connection flush anytime
        session focus_value := s value
        c send_change s s:id s:value
      if c:key="off"
        if (addressof c:window:overlay)=addressof:so
          session discard so
          c:window overlay :> null map LayoutPrototype
          c:window redraw_area so:bbox:x0 so:bbox:y0 so:bbox


public
  type LayoutButton
      if c:key="off"
        if (addressof c:window:overlay)=addressof:so
          session discard so
          c:window overlay :> null map LayoutPrototype
          c:window redraw_area so:bbox:x0 so:bbox:y0 so:bbox


public
  type LayoutButton
    field Link:LayoutPrototype next
    field Int parent_and_flags <- 0
    inherit LayoutPrototype
    field LayoutArea bbox ; field Float tx ty
    field Str label
    field Str id
    field Str key
    field Str label
    field Str id
    field Str key
    field Int flags <- 0 # 1 selected, 2 mouse over hook
    field LayoutArea bbox ; field Float tx ty
    field Int flags <- 0
    field Str help


method b bbox c -> bbox
  arg_rw LayoutButton b ; arg_rw LayoutPC c ; arg LayoutArea


method b bbox c -> bbox
  arg_rw LayoutButton b ; arg_rw LayoutPC c ; arg LayoutArea
  var Pointer:LayoutStyleButton s :> c:style button
  implicit s
    label_font bbox b:label null (var Float bx0) (var Float 
    b:bbox x0 := bx0*label_scale-s:padding_x
    b:bbox y0 := by0*label_scale-s:padding_y-border_size
    b:bbox x1 := bx1*label_scale+padding_x+border_size
    b:bbox y1 := by1*label_scale+padding_y
    if b:key<>"" and not (b:key parse word:"alt" any:(var St
      b:bbox x1 += key_padding+key_scale*(key_font length b:
    if (b:flags .and. 2)<>0
      void # FIXME: stretch b:bbox x1 := c:area x1
  var Pointer:LayoutStyleButton st
  if b:id=""
    st :> c:style:button inactive
  eif (b:flags .and. 4)<>0
    st :> c:style:button selected
  eif (b:flags .and. 8)<>0
    st :> c:style:link standard
  else
    st :> c:style:button standard
  st:text:font bbox b:label null 0 st:text:size (var Float bx0) (var Float by0) (var Float bx1) (var Float by1)
  b:bbox x0 := bx0-st:box:padding:x0
  b:bbox y0 := by0-st:box:padding:y0
  b:bbox x1 := bx1+st:box:padding:x1
  b:bbox y1 := by1+st:box:padding:y1
  if b:key=""
    void
  eif (b:key parse word:"alt" any:(var Str key2)) and key2<>"" and { var Int i := (cast b:label Str32) search key2 -1 ; i<>(-1) }
    void
  else
    b:bbox x1 += st:spacing+st:outside_key:size*(st:outside_key:font length b:key null)
  if (b:flags .and. 2)<>0
    void # FIXME: stretch b:bbox x1 := c:area x1
  bbox := b bbox


method b draw d c
  arg_rw LayoutButton b ; oarg_rw DrawPrototype d ; arg_rw L
  bbox := b bbox


method b draw d c
  arg_rw LayoutButton b ; oarg_rw DrawPrototype d ; arg_rw L
  var Pointer:LayoutStyleButton s :> c:style button
  implicit s
    var CBool selected := (b:flags .and. 4)<>0
    d rectangle b:bbox:x0+b:tx b:bbox:y0+b:ty b:bbox:x1-bord
    d rectangle b:bbox:x0+border_size+b:tx b:bbox:y0+border_
    label_font bbox b:label null (var Float bx0) (var Float 
    var Transform2 t := transform b:bbox:x0+border_size+padd
    d text b:label label_font null t (shunt b:id="" (c color
    if b:key=""
      void
    eif (b:key parse word:"alt" any:(var Str key2)) and key2
      t xt += label_scale*(label_font length ((cast b:label 
      d text key2 label_font null t (shunt b:id="" (c color 
    else
      t xt += label_scale*(label_font length b:label null)+k
      t xx := key_scale ; t yy := key_scale
      d text b:key key_font null t (shunt b:id="" (c color i
  var Pointer:LayoutStyleButton st
  if b:id=""
    st :> c:style:button inactive
  eif (b:flags .and. 4)<>0
    st :> c:style:button selected
  eif (b:flags .and. 8)<>0
    st :> c:style:link standard
  else
    st :> c:style:button standard
  box_draw b:bbox:x0+b:tx b:bbox:y0+b:ty b:bbox:x1+b:tx b:bbox:y1+b:ty st:box d c
  st:text:font bbox b:label null (var Float bx0) (var Float by0) (var Float bx1) (var Float by1)
  d text b:label st:text:font null (transform b:tx b:ty st:text:size st:text:size 0 0) (c color st:text:color)
  if b:key=""
    void
  eif (b:key parse word:"alt" any:(var Str key2)) and key2<>"" and { var Int i := (cast b:label Str32) search key2 -1 ; i<>(-1) }
    var Float x := st:text:size*(st:text:font length ((cast b:label Str32) 0 i) null)
    d text key2 st:inside_key:font null (transform b:tx+x b:ty st:inside_key:size st:inside_key:size 0 0) (c color st:inside_key:color)
  else
    var Float x := st:text:size*(st:text:font length b:label null)+st:spacing
    d text b:key st:outside_key:font null (transform b:tx+x b:ty st:outside_key:size st:outside_key:size 0 0) (c color st:outside_key:color)


method b event c
  oarg_rw LayoutButton b ; arg_rw LayoutEC c
  if c:mode=layout_event_pointer and c:x>=b:bbox:x0+b:tx and
    if c:event="press" and c:key="button1"
      if b:id<>""
        c focus_sync
        c:window:session:connection otag "run" b:id
        c:window:session:connection flush anytime
        c cancel := true


method b event c
  oarg_rw LayoutButton b ; arg_rw LayoutEC c
  if c:mode=layout_event_pointer and c:x>=b:bbox:x0+b:tx and
    if c:event="press" and c:key="button1"
      if b:id<>""
        c focus_sync
        c:window:session:connection otag "run" b:id
        c:window:session:connection flush anytime
        c cancel := true
    eif c:event="move" and (b:flags .and. 1)<>0
    eif c:event="move"
      c set_over b
      c cancel := true
  eif c:mode=layout_event_shortcut
    if c:event="press" and c:key=b:key
      if b:id<>""
        c focus_sync
        c:window:session:connection otag "run" b:id
        c:window:session:connection flush anytime
        c cancel := true
      c set_over b
      c cancel := true
  eif c:mode=layout_event_shortcut
    if c:event="press" and c:key=b:key
      if b:id<>""
        c focus_sync
        c:window:session:connection otag "run" b:id
        c:window:session:connection flush anytime
        c cancel := true
  eif c:event="over"
    c:window:session:connection otag "over" b:id c:key="on"
    c:window:session:connection flush anytime


public
  type LayoutLink
    field Link:LayoutPrototype next
    field Int parent_and_flags <- 0
    field Str label
    field Str id
    field LayoutArea bbox ; field Float tx ty

LayoutPrototype maybe LayoutLink

function build l
  arg_w LayoutLink l
  l:bbox x0 := undefined


method l bbox c -> b
  arg_rw LayoutLink l ; arg_rw LayoutPC c ; arg LayoutArea b
  var Pointer:LayoutStyleText s :> c:style text
  implicit s
    font:0 bbox l:label null (var Float bx0) (var Float by0)
    l:bbox x0 := bx0*scale
    l:bbox y0 := by0*scale
    l:bbox x1 := bx1*scale
    l:bbox y1 := by1*scale
  b := l bbox

method l translate tx ty
  oarg_rw LayoutLink l ; arg Float tx ty
  l tx := tx ; l ty := ty

method l offset x y
  oarg_rw LayoutLink l ; arg Float x y
  l tx += x ; l ty += y


method l draw d c
  arg_rw LayoutLink l ; oarg_rw DrawPrototype d ; arg_rw Lay
  var Pointer:LayoutStyleText s :> c:style text
  implicit s
    # font:0 bbox l:label null (var Float bx0) (var Float by
    d text l:label font:0 null (transform l:tx l:ty scale sc


method l event c
  arg_rw LayoutLink l ; arg_rw LayoutEC c
  if c:mode=layout_event_pointer and c:x>=l:bbox:x0+l:tx and
    if c:event="press" and c:key="button1"
      c focus_sync
      c:window:session:connection otag "run" l:id
      c:window:session:connection flush anytime
  if c:event="over"
    c:window:session help (shunt c:key="on" b:help "")