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 }