9fans.net/go@v0.0.7/cmd/samterm/flayer.go (about)

     1  package main
     2  
     3  import (
     4  	"image"
     5  
     6  	"9fans.net/go/draw"
     7  	"9fans.net/go/draw/frame"
     8  )
     9  
    10  var llist []*Flayer /* front to back */
    11  var nlalloc int
    12  var lDrect image.Rectangle
    13  
    14  var maincols [frame.NCOL]*draw.Image
    15  var cmdcols [frame.NCOL]*draw.Image
    16  
    17  func flstart(r image.Rectangle) {
    18  	lDrect = r
    19  
    20  	/* Main text is yellowish */
    21  	maincols[frame.BACK] = display.AllocImageMix(draw.PaleYellow, draw.White)
    22  	maincols[frame.HIGH], _ = display.AllocImage(image.Rect(0, 0, 1, 1), screen.Pix, true, draw.DarkYellow)
    23  	maincols[frame.BORD], _ = display.AllocImage(image.Rect(0, 0, 2, 2), screen.Pix, true, draw.YellowGreen)
    24  	maincols[frame.TEXT] = display.Black
    25  	maincols[frame.HTEXT] = display.Black
    26  
    27  	/* Command text is blueish */
    28  	cmdcols[frame.BACK] = display.AllocImageMix(draw.PaleBlueGreen, draw.White)
    29  	cmdcols[frame.HIGH], _ = display.AllocImage(image.Rect(0, 0, 1, 1), screen.Pix, true, draw.PaleGreyGreen)
    30  	cmdcols[frame.BORD], _ = display.AllocImage(image.Rect(0, 0, 2, 2), screen.Pix, true, draw.PurpleBlue)
    31  	cmdcols[frame.TEXT] = display.Black
    32  	cmdcols[frame.HTEXT] = display.Black
    33  }
    34  
    35  func flnew(l *Flayer, fn func(*Flayer, int) []rune, text *Text) {
    36  	l.textfn = fn
    37  	l.text = text
    38  	l.lastsr = draw.ZR
    39  	llinsert(l)
    40  }
    41  
    42  func flrect(l *Flayer, r image.Rectangle) image.Rectangle {
    43  	draw.RectClip(&r, lDrect)
    44  	l.entire = r
    45  	l.scroll = r.Inset(FLMARGIN(l))
    46  	l.scroll.Max.X = r.Min.X + FLMARGIN(l) + FLSCROLLWID(l) + (FLGAP(l) - FLMARGIN(l))
    47  	r.Min.X = l.scroll.Max.X
    48  	return r
    49  }
    50  
    51  func flinit(l *Flayer, r image.Rectangle, ft *draw.Font, cols []*draw.Image) {
    52  	lldelete(l)
    53  	llinsert(l)
    54  	l.visible = All
    55  	l.p1 = 0
    56  	l.p0 = l.p1
    57  	l.origin = l.p0
    58  	l.f.Display = display // for FLMARGIN
    59  	l.f.Init(flrect(l, r).Inset(FLMARGIN(l)), ft, screen, cols)
    60  	l.f.MaxTab = maxtab * ft.StringWidth("0")
    61  	newvisibilities(true)
    62  	screen.Draw(l.entire, l.f.Cols[frame.BACK], nil, draw.ZP)
    63  	scrdraw(l, 0)
    64  	flborder(l, false)
    65  }
    66  
    67  func flclose(l *Flayer) {
    68  	if l.visible == All {
    69  		screen.Draw(l.entire, display.White, nil, draw.ZP)
    70  	} else if l.visible == Some {
    71  		if l.f.B == nil {
    72  			l.f.B, _ = display.AllocImage(l.entire, screen.Pix, false, draw.NoFill)
    73  		}
    74  		if l.f.B != nil {
    75  			l.f.B.Draw(l.entire, display.White, nil, draw.ZP)
    76  			flrefresh(l, l.entire, 0)
    77  		}
    78  	}
    79  	l.f.Clear(true)
    80  	lldelete(l)
    81  	if l.f.B != nil && l.visible != All {
    82  		l.f.B.Free()
    83  	}
    84  	l.textfn = nil
    85  	newvisibilities(true)
    86  }
    87  
    88  func flborder(l *Flayer, wide bool) {
    89  	if flprepare(l) {
    90  		l.f.B.Border(l.entire, FLMARGIN(l), l.f.Cols[frame.BACK], draw.ZP)
    91  		w := 1
    92  		if wide {
    93  			w = FLMARGIN(l)
    94  		}
    95  		l.f.B.Border(l.entire, w, l.f.Cols[frame.BORD], draw.ZP)
    96  		if l.visible == Some {
    97  			flrefresh(l, l.entire, 0)
    98  		}
    99  	}
   100  }
   101  
   102  func flwhich(p image.Point) *Flayer {
   103  	if p.X == 0 && p.Y == 0 {
   104  		if len(llist) > 0 {
   105  			return llist[0]
   106  		}
   107  		return nil
   108  	}
   109  	for _, l := range llist {
   110  		if p.In(l.entire) {
   111  			return l
   112  		}
   113  	}
   114  	return nil
   115  }
   116  
   117  func flupfront(l *Flayer) {
   118  	v := l.visible
   119  	lldelete(l)
   120  	llinsert(l)
   121  	if v != All {
   122  		newvisibilities(false)
   123  	}
   124  }
   125  
   126  func newvisibilities(redraw bool) {
   127  	/* if redraw false, we know it's a flupfront, and needn't
   128  	 * redraw anyone becoming partially covered */
   129  	for _, l := range llist {
   130  		l.lastsr = draw.ZR /* make sure scroll bar gets redrawn */
   131  		ov := l.visible
   132  		l.visible = visibility(l)
   133  		V := func(a, b Vis) int { return int(a)<<2 | int(b) }
   134  		switch V(ov, l.visible) {
   135  		case V(Some, None):
   136  			if l.f.B != nil {
   137  				l.f.B.Free()
   138  			}
   139  			fallthrough
   140  		case V(All, None),
   141  			V(All, Some):
   142  			l.f.B = nil
   143  			l.f.Clear(false)
   144  
   145  		case V(Some, Some),
   146  			V(None, Some):
   147  			if ov == None || (l.f.B == nil && redraw) {
   148  				flprepare(l)
   149  			}
   150  			if l.f.B != nil && redraw {
   151  				flrefresh(l, l.entire, 0)
   152  				l.f.B.Free()
   153  				l.f.B = nil
   154  				l.f.Clear(false)
   155  			}
   156  			fallthrough
   157  		case V(None, None),
   158  			V(All, All):
   159  			break
   160  
   161  		case V(Some, All):
   162  			if l.f.B != nil {
   163  				screen.Draw(l.entire, l.f.B, nil, l.entire.Min)
   164  				l.f.B.Free()
   165  				l.f.B = screen
   166  				break
   167  			}
   168  			fallthrough
   169  		case V(None, All):
   170  			flprepare(l)
   171  		}
   172  		if ov == None && l.visible != None {
   173  			flnewlyvisible(l)
   174  		}
   175  	}
   176  }
   177  
   178  func llinsert(l *Flayer) {
   179  	llist = append(llist, nil)
   180  	copy(llist[1:], llist)
   181  	llist[0] = l
   182  }
   183  
   184  func lldelete(l *Flayer) {
   185  	for i := range llist {
   186  		if llist[i] == l {
   187  			copy(llist[i:], llist[i+1:])
   188  			llist = llist[:len(llist)-1]
   189  			return
   190  		}
   191  	}
   192  	panic("lldelete")
   193  }
   194  
   195  func flinsert(l *Flayer, rp []rune, p0 int) {
   196  	if flprepare(l) {
   197  		l.f.Insert(rp, p0-l.origin)
   198  		scrdraw(l, scrtotal(l))
   199  		if l.visible == Some {
   200  			flrefresh(l, l.entire, 0)
   201  		}
   202  	}
   203  }
   204  
   205  func fldelete(l *Flayer, p0 int, p1 int) {
   206  	if flprepare(l) {
   207  		p0 -= l.origin
   208  		if p0 < 0 {
   209  			p0 = 0
   210  		}
   211  		p1 -= l.origin
   212  		if p1 < 0 {
   213  			p1 = 0
   214  		}
   215  		l.f.Delete(p0, p1)
   216  		scrdraw(l, scrtotal(l))
   217  		if l.visible == Some {
   218  			flrefresh(l, l.entire, 0)
   219  		}
   220  	}
   221  }
   222  
   223  func flselect(l *Flayer) bool {
   224  	if l.visible != All {
   225  		flupfront(l)
   226  	}
   227  	l.f.Select(mousectl)
   228  	ret := false
   229  	if l.f.P0 == l.f.P1 {
   230  		if mousep.Msec-l.click < Clicktime && l.f.P0+l.origin == l.p0 {
   231  			ret = true
   232  			l.click = 0
   233  		} else {
   234  			l.click = mousep.Msec
   235  		}
   236  	} else {
   237  		l.click = 0
   238  	}
   239  	l.p0 = l.f.P0 + l.origin
   240  	l.p1 = l.f.P1 + l.origin
   241  	return ret
   242  }
   243  
   244  func flsetselect(l *Flayer, p0 int, p1 int) {
   245  	l.click = 0
   246  	if l.visible == None || !flprepare(l) {
   247  		l.p0 = p0
   248  		l.p1 = p1
   249  		return
   250  	}
   251  	l.p0 = p0
   252  	l.p1 = p1
   253  	var fp0 int
   254  	var fp1 int
   255  	var ticked bool
   256  	flfp0p1(l, &fp0, &fp1, &ticked)
   257  	if fp0 == l.f.P0 && fp1 == l.f.P1 {
   258  		if l.f.Ticked != ticked {
   259  			l.f.Tick(l.f.PointOf(fp0), ticked)
   260  		}
   261  		return
   262  	}
   263  
   264  	if fp1 <= l.f.P0 || fp0 >= l.f.P1 || l.f.P0 == l.f.P1 || fp0 == fp1 {
   265  		/* no overlap or trivial repainting */
   266  		l.f.Drawsel(l.f.PointOf(l.f.P0), l.f.P0, l.f.P1, false)
   267  		if fp0 != fp1 || ticked {
   268  			l.f.Drawsel(l.f.PointOf(fp0), fp0, fp1, true)
   269  		}
   270  		goto Refresh
   271  	}
   272  	/* the current selection and the desired selection overlap and are both non-empty */
   273  	if fp0 < l.f.P0 {
   274  		/* extend selection backwards */
   275  		l.f.Drawsel(l.f.PointOf(fp0), fp0, l.f.P0, true)
   276  	} else if fp0 > l.f.P0 {
   277  		/* trim first part of selection */
   278  		l.f.Drawsel(l.f.PointOf(l.f.P0), l.f.P0, fp0, false)
   279  	}
   280  	if fp1 > l.f.P1 {
   281  		/* extend selection forwards */
   282  		l.f.Drawsel(l.f.PointOf(l.f.P1), l.f.P1, fp1, true)
   283  	} else if fp1 < l.f.P1 {
   284  		/* trim last part of selection */
   285  		l.f.Drawsel(l.f.PointOf(fp1), fp1, l.f.P1, false)
   286  	}
   287  
   288  Refresh:
   289  	l.f.P0 = fp0
   290  	l.f.P1 = fp1
   291  	if l.visible == Some {
   292  		flrefresh(l, l.entire, 0)
   293  	}
   294  }
   295  
   296  func flfp0p1(l *Flayer, pp0 *int, pp1 *int, ticked *bool) {
   297  	p0 := l.p0 - l.origin
   298  	p1 := l.p1 - l.origin
   299  
   300  	*ticked = p0 == p1
   301  	if p0 < 0 {
   302  		*ticked = false
   303  		p0 = 0
   304  	}
   305  	if p1 < 0 {
   306  		p1 = 0
   307  	}
   308  	if p0 > l.f.NumChars {
   309  		p0 = l.f.NumChars
   310  	}
   311  	if p1 > l.f.NumChars {
   312  		*ticked = false
   313  		p1 = l.f.NumChars
   314  	}
   315  	*pp0 = p0
   316  	*pp1 = p1
   317  }
   318  
   319  func rscale(r image.Rectangle, old image.Point, new image.Point) image.Rectangle {
   320  	r.Min.X = r.Min.X * new.X / old.X
   321  	r.Min.Y = r.Min.Y * new.Y / old.Y
   322  	r.Max.X = r.Max.X * new.X / old.X
   323  	r.Max.Y = r.Max.Y * new.Y / old.Y
   324  	return r
   325  }
   326  
   327  func flresize(dr image.Rectangle) {
   328  	olDrect := lDrect
   329  	lDrect = dr
   330  	move := false
   331  	/* no moving on rio; must repaint */
   332  	if false && dr.Dx() == olDrect.Dx() && dr.Dy() == olDrect.Dy() {
   333  		move = true
   334  	} else {
   335  		screen.Draw(lDrect, display.White, nil, draw.ZP)
   336  	}
   337  	for _, l := range llist {
   338  		l.lastsr = draw.ZR
   339  		f := &l.f
   340  		var r image.Rectangle
   341  		if move {
   342  			r = l.entire.Sub(olDrect.Min).Add(dr.Min)
   343  		} else {
   344  			r = rscale(l.entire.Sub(olDrect.Min), olDrect.Max.Sub(olDrect.Min), dr.Max.Sub(dr.Min)).Add(dr.Min)
   345  			if l.visible == Some && f.B != nil {
   346  				f.B.Free()
   347  				f.Clear(false)
   348  			}
   349  			f.B = nil
   350  			if l.visible != None {
   351  				f.Clear(false)
   352  			}
   353  		}
   354  		if !draw.RectClip(&r, dr) {
   355  			panic("flresize")
   356  		}
   357  		if r.Max.X-r.Min.X < 100 {
   358  			r.Min.X = dr.Min.X
   359  		}
   360  		if r.Max.X-r.Min.X < 100 {
   361  			r.Max.X = dr.Max.X
   362  		}
   363  		if r.Max.Y-r.Min.Y < 2*FLMARGIN(l)+f.Font.Height {
   364  			r.Min.Y = dr.Min.Y
   365  		}
   366  		if r.Max.Y-r.Min.Y < 2*FLMARGIN(l)+f.Font.Height {
   367  			r.Max.Y = dr.Max.Y
   368  		}
   369  		if !move {
   370  			l.visible = None
   371  		}
   372  		f.SetRects(flrect(l, r).Inset(FLMARGIN(l)), f.B)
   373  		if !move && f.B != nil {
   374  			scrdraw(l, scrtotal(l))
   375  		}
   376  	}
   377  	newvisibilities(true)
   378  }
   379  
   380  func flprepare(l *Flayer) bool {
   381  	if l.visible == None {
   382  		return false
   383  	}
   384  	f := &l.f
   385  	if f.B == nil {
   386  		if l.visible == All {
   387  			f.B = screen
   388  		} else {
   389  			f.B, _ = display.AllocImage(l.entire, screen.Pix, false, 0)
   390  			if f.B == nil {
   391  				return false
   392  			}
   393  		}
   394  		f.B.Draw(l.entire, f.Cols[frame.BACK], nil, draw.ZP)
   395  		w := 1
   396  		if l == llist[0] {
   397  			w = FLMARGIN(l)
   398  		}
   399  		f.B.Border(l.entire, w, f.Cols[frame.BORD], draw.ZP)
   400  		n := f.NumChars
   401  		f.Init(f.Entire, f.Font, f.B, nil)
   402  		f.MaxTab = maxtab * f.Font.StringWidth("0")
   403  		rp := l.textfn(l, n)
   404  		f.Insert(rp, 0)
   405  		f.Drawsel(f.PointOf(f.P0), f.P0, f.P1, false)
   406  		var ticked bool
   407  		flfp0p1(l, &f.P0, &f.P1, &ticked)
   408  		if f.P0 != f.P1 || ticked {
   409  			f.Drawsel(f.PointOf(f.P0), f.P0, f.P1, true)
   410  		}
   411  		l.lastsr = draw.ZR
   412  		scrdraw(l, scrtotal(l))
   413  	}
   414  	return true
   415  }
   416  
   417  var somevis, someinvis, justvis bool
   418  
   419  func visibility(l *Flayer) Vis {
   420  	someinvis = false
   421  	somevis = someinvis
   422  	justvis = true
   423  	flrefresh(l, l.entire, 0)
   424  	justvis = false
   425  	if !somevis {
   426  		return None
   427  	}
   428  	if !someinvis {
   429  		return All
   430  	}
   431  	return Some
   432  }
   433  
   434  func flrefresh(l *Flayer, r image.Rectangle, i int) {
   435  Top:
   436  	t := llist[i]
   437  	i++
   438  	if t == l {
   439  		if !justvis {
   440  			screen.Draw(r, l.f.B, nil, r.Min)
   441  		}
   442  		somevis = true
   443  	} else {
   444  		if !draw.RectXRect(t.entire, r) {
   445  			goto Top /* avoid stacking unnecessarily */
   446  		}
   447  		var s image.Rectangle
   448  		if t.entire.Min.X > r.Min.X {
   449  			s = r
   450  			s.Max.X = t.entire.Min.X
   451  			flrefresh(l, s, i)
   452  			r.Min.X = t.entire.Min.X
   453  		}
   454  		if t.entire.Min.Y > r.Min.Y {
   455  			s = r
   456  			s.Max.Y = t.entire.Min.Y
   457  			flrefresh(l, s, i)
   458  			r.Min.Y = t.entire.Min.Y
   459  		}
   460  		if t.entire.Max.X < r.Max.X {
   461  			s = r
   462  			s.Min.X = t.entire.Max.X
   463  			flrefresh(l, s, i)
   464  			r.Max.X = t.entire.Max.X
   465  		}
   466  		if t.entire.Max.Y < r.Max.Y {
   467  			s = r
   468  			s.Min.Y = t.entire.Max.Y
   469  			flrefresh(l, s, i)
   470  			r.Max.Y = t.entire.Max.Y
   471  		}
   472  		/* remaining piece of r is blocked by t; forget about it */
   473  		someinvis = true
   474  	}
   475  }
   476  
   477  func flscale(l *Flayer, n int) int {
   478  	if l == nil {
   479  		return n
   480  	}
   481  	return l.f.Display.ScaleSize(n)
   482  }