9fans.net/go@v0.0.7/cmd/acme/internal/wind/cols.go (about) 1 package wind 2 3 import ( 4 "sort" 5 6 "9fans.net/go/cmd/acme/internal/adraw" 7 "9fans.net/go/cmd/acme/internal/runes" 8 "9fans.net/go/cmd/acme/internal/util" 9 "9fans.net/go/draw" 10 "9fans.net/go/draw/frame" 11 ) 12 13 type Column struct { 14 R draw.Rectangle 15 Tag Text 16 Row *Row 17 W []*Window 18 Safe bool 19 } 20 21 var Activecol *Column 22 23 func colinit(c *Column, r draw.Rectangle) { 24 adraw.Display.ScreenImage.Draw(r, adraw.Display.White, nil, draw.ZP) 25 c.R = r 26 c.W = nil 27 t := &c.Tag 28 t.W = nil 29 t.Col = c 30 r1 := r 31 r1.Max.Y = r1.Min.Y + adraw.Font.Height 32 textinit(t, fileaddtext(nil, t), r1, &adraw.RefFont1, adraw.TagCols[:]) 33 t.What = Columntag 34 r1.Min.Y = r1.Max.Y 35 r1.Max.Y += adraw.Border() 36 adraw.Display.ScreenImage.Draw(r1, adraw.Display.Black, nil, draw.ZP) 37 Textinsert(t, 0, []rune("New Cut Paste Snarf Sort Zerox Delcol "), true) 38 Textsetselect(t, t.Len(), t.Len()) 39 adraw.Display.ScreenImage.Draw(t.ScrollR, adraw.ColButton, nil, adraw.ColButton.R.Min) 40 c.Safe = true 41 } 42 43 func Coladd(c *Column, w *Window, clone *Window, y int) *Window { 44 var v *Window 45 r := c.R 46 r.Min.Y = c.Tag.Fr.R.Max.Y + adraw.Border() 47 if y < r.Min.Y && len(c.W) > 0 { // steal half of last window by default 48 v = c.W[len(c.W)-1] 49 y = v.Body.Fr.R.Min.Y + v.Body.Fr.R.Dy()/2 50 } 51 var i int 52 // look for window we'll land on 53 for i = 0; i < len(c.W); i++ { 54 v = c.W[i] 55 if y < v.R.Max.Y { 56 break 57 } 58 } 59 buggered := 0 60 if len(c.W) > 0 { 61 if i < len(c.W) { 62 i++ // new window will go after v 63 } 64 /* 65 * if landing window (v) is too small, grow it first. 66 */ 67 minht := v.Tag.Fr.Font.Height + adraw.Border() + 1 68 j := 0 69 for !c.Safe || v.Body.Fr.MaxLines <= 3 || v.Body.All.Dy() <= minht { 70 j++ 71 if j > 10 { 72 buggered = 1 // too many windows in column 73 break 74 } 75 Colgrow(c, v, 1) 76 } 77 var ymax int 78 79 /* 80 * figure out where to split v to make room for w 81 */ 82 83 // new window stops where next window begins 84 if i < len(c.W) { 85 ymax = c.W[i].R.Min.Y - adraw.Border() 86 } else { 87 ymax = c.R.Max.Y 88 } 89 90 // new window must start after v's tag ends 91 y = util.Max(y, v.tagtop.Max.Y+adraw.Border()) 92 93 // new window must start early enough to end before ymax 94 y = util.Min(y, ymax-minht) 95 96 // if y is too small, too many windows in column 97 if y < v.tagtop.Max.Y+adraw.Border() { 98 buggered = 1 99 } 100 101 /* 102 * resize & redraw v 103 */ 104 r = v.R 105 r.Max.Y = ymax 106 adraw.Display.ScreenImage.Draw(r, adraw.TextCols[frame.BACK], nil, draw.ZP) 107 r1 := r 108 y = util.Min(y, ymax-(v.Tag.Fr.Font.Height*v.Taglines+v.Body.Fr.Font.Height+adraw.Border()+1)) 109 r1.Max.Y = util.Min(y, v.Body.Fr.R.Min.Y+v.Body.Fr.NumLines*v.Body.Fr.Font.Height) 110 r1.Min.Y = Winresize(v, r1, false, false) 111 r1.Max.Y = r1.Min.Y + adraw.Border() 112 adraw.Display.ScreenImage.Draw(r1, adraw.Display.Black, nil, draw.ZP) 113 114 /* 115 * leave r with w's coordinates 116 */ 117 r.Min.Y = r1.Max.Y 118 } 119 if w == nil { 120 w = new(Window) 121 w.Col = c 122 adraw.Display.ScreenImage.Draw(r, adraw.TextCols[frame.BACK], nil, draw.ZP) 123 Init(w, clone, r) 124 } else { 125 w.Col = c 126 Winresize(w, r, false, true) 127 } 128 w.Tag.Col = c 129 w.Tag.Row = c.Row 130 w.Body.Col = c 131 w.Body.Row = c.Row 132 c.W = append(c.W, nil) 133 copy(c.W[i+1:], c.W[i:]) 134 c.W[i] = w 135 c.Safe = true 136 137 // if there were too many windows, redraw the whole column 138 if buggered != 0 { 139 Colresize(c, c.R) 140 } 141 142 return w 143 } 144 145 func Colclose(c *Column, w *Window, dofree bool) *Window { 146 // w is locked 147 if !c.Safe { 148 Colgrow(c, w, 1) 149 } 150 var i int 151 for i = 0; i < len(c.W); i++ { 152 if c.W[i] == w { 153 goto Found 154 } 155 } 156 util.Fatal("can't find window") 157 Found: 158 r := w.R 159 w.Tag.Col = nil 160 w.Body.Col = nil 161 w.Col = nil 162 if dofree { 163 windelete(w) 164 Winclose(w) 165 } 166 copy(c.W[i:], c.W[i+1:]) 167 c.W = c.W[:len(c.W)-1] 168 if len(c.W) == 0 { 169 adraw.Display.ScreenImage.Draw(r, adraw.Display.White, nil, draw.ZP) 170 return nil 171 } 172 if i == len(c.W) { // extend last window down 173 w = c.W[i-1] 174 r.Min.Y = w.R.Min.Y 175 r.Max.Y = c.R.Max.Y 176 } else { // extend next window up 177 w = c.W[i] 178 r.Max.Y = w.R.Max.Y 179 } 180 adraw.Display.ScreenImage.Draw(r, adraw.TextCols[frame.BACK], nil, draw.ZP) 181 if c.Safe { 182 Winresize(w, r, false, true) 183 } 184 return w 185 } 186 187 func colcloseall(c *Column) { 188 if c == Activecol { 189 Activecol = nil 190 } 191 textclose(&c.Tag) 192 for i := 0; i < len(c.W); i++ { 193 w := c.W[i] 194 Winclose(w) 195 } 196 } 197 198 func Colresize(c *Column, r draw.Rectangle) { 199 r1 := r 200 r1.Max.Y = r1.Min.Y + c.Tag.Fr.Font.Height 201 Textresize(&c.Tag, r1, true) 202 adraw.Display.ScreenImage.Draw(c.Tag.ScrollR, adraw.ColButton, nil, adraw.ColButton.R.Min) 203 r1.Min.Y = r1.Max.Y 204 r1.Max.Y += adraw.Border() 205 adraw.Display.ScreenImage.Draw(r1, adraw.Display.Black, nil, draw.ZP) 206 r1.Max.Y = r.Max.Y 207 new_ := r.Dy() - len(c.W)*(adraw.Border()+adraw.Font.Height) 208 old := c.R.Dy() - len(c.W)*(adraw.Border()+adraw.Font.Height) 209 for i := 0; i < len(c.W); i++ { 210 w := c.W[i] 211 w.Maxlines = 0 212 if i == len(c.W)-1 { 213 r1.Max.Y = r.Max.Y 214 } else { 215 r1.Max.Y = r1.Min.Y 216 if new_ > 0 && old > 0 && w.R.Dy() > adraw.Border()+adraw.Font.Height { 217 r1.Max.Y += (w.R.Dy()-adraw.Border()-adraw.Font.Height)*new_/old + adraw.Border() + adraw.Font.Height 218 } 219 } 220 r1.Max.Y = util.Max(r1.Max.Y, r1.Min.Y+adraw.Border()+adraw.Font.Height) 221 r2 := r1 222 r2.Max.Y = r2.Min.Y + adraw.Border() 223 adraw.Display.ScreenImage.Draw(r2, adraw.Display.Black, nil, draw.ZP) 224 r1.Min.Y = r2.Max.Y 225 r1.Min.Y = Winresize(w, r1, false, i == len(c.W)-1) 226 } 227 c.R = r 228 } 229 230 func Colclean(c *Column) bool { 231 clean := true 232 for i := 0; i < len(c.W); i++ { 233 clean = Winclean(c.W[i], true) && clean 234 } 235 return clean 236 } 237 238 func Colsort(c *Column) { 239 if len(c.W) == 0 { 240 return 241 } 242 rp := make([]draw.Rectangle, len(c.W)) 243 wp := make([]*Window, len(c.W)) 244 copy(wp, c.W) 245 sort.Slice(wp, func(i, j int) bool { 246 return runes.Compare(wp[i].Body.File.Name(), wp[j].Body.File.Name()) < 0 247 }) 248 249 for i := 0; i < len(c.W); i++ { 250 rp[i] = wp[i].R 251 } 252 r := c.R 253 r.Min.Y = c.Tag.Fr.R.Max.Y 254 adraw.Display.ScreenImage.Draw(r, adraw.TextCols[frame.BACK], nil, draw.ZP) 255 y := r.Min.Y 256 for i := 0; i < len(c.W); i++ { 257 w := wp[i] 258 r.Min.Y = y 259 if i == len(c.W)-1 { 260 r.Max.Y = c.R.Max.Y 261 } else { 262 r.Max.Y = r.Min.Y + w.R.Dy() + adraw.Border() 263 } 264 r1 := r 265 r1.Max.Y = r1.Min.Y + adraw.Border() 266 adraw.Display.ScreenImage.Draw(r1, adraw.Display.Black, nil, draw.ZP) 267 r.Min.Y = r1.Max.Y 268 y = Winresize(w, r, false, i == len(c.W)-1) 269 } 270 c.W = wp 271 } 272 273 func Colgrow(c *Column, w *Window, but int) { 274 var i int 275 for i = 0; i < len(c.W); i++ { 276 if c.W[i] == w { 277 goto Found 278 } 279 } 280 util.Fatal("can't find window") 281 282 Found: 283 cr := c.R 284 var r draw.Rectangle 285 if but < 0 { // make sure window fills its own space properly 286 r = w.R 287 if i == len(c.W)-1 || !c.Safe { 288 r.Max.Y = cr.Max.Y 289 } else { 290 r.Max.Y = c.W[i+1].R.Min.Y - adraw.Border() 291 } 292 Winresize(w, r, false, true) 293 return 294 } 295 cr.Min.Y = c.W[0].R.Min.Y 296 var v *Window 297 if but == 3 { // full size 298 if i != 0 { 299 v = c.W[0] 300 c.W[0] = w 301 c.W[i] = v 302 } 303 adraw.Display.ScreenImage.Draw(cr, adraw.TextCols[frame.BACK], nil, draw.ZP) 304 Winresize(w, cr, false, true) 305 for i = 1; i < len(c.W); i++ { 306 c.W[i].Body.Fr.MaxLines = 0 307 } 308 c.Safe = false 309 return 310 } 311 // store old #lines for each window 312 onl := w.Body.Fr.MaxLines 313 nl := make([]int, len(c.W)) 314 ny := make([]int, len(c.W)) 315 tot := 0 316 var j int 317 var l int 318 for j = 0; j < len(c.W); j++ { 319 l = c.W[j].Taglines - 1 + c.W[j].Body.Fr.MaxLines 320 nl[j] = l 321 tot += l 322 } 323 // approximate new #lines for this window 324 if but == 2 { // as big as can be 325 for i := range nl { 326 nl[i] = 0 327 } 328 } else { 329 nnl := util.Min(onl+util.Max(util.Min(5, w.Taglines-1+w.Maxlines), onl/2), tot) 330 if nnl < w.Taglines-1+w.Maxlines { 331 nnl = (w.Taglines - 1 + w.Maxlines + nnl) / 2 332 } 333 if nnl == 0 { 334 nnl = 2 335 } 336 dnl := nnl - onl 337 // compute new #lines for each window 338 for k := 1; k < len(c.W); k++ { 339 // prune from later window 340 j = i + k 341 if j < len(c.W) && nl[j] != 0 { 342 l = util.Min(dnl, util.Max(1, nl[j]/2)) 343 nl[j] -= l 344 nl[i] += l 345 dnl -= l 346 } 347 // prune from earlier window 348 j = i - k 349 if j >= 0 && nl[j] != 0 { 350 l = util.Min(dnl, util.Max(1, nl[j]/2)) 351 nl[j] -= l 352 nl[i] += l 353 dnl -= l 354 } 355 } 356 } 357 // pack everyone above 358 y1 := cr.Min.Y 359 for j = 0; j < i; j++ { 360 v = c.W[j] 361 r = v.R 362 r.Min.Y = y1 363 r.Max.Y = y1 + v.tagtop.Dy() 364 if nl[j] != 0 { 365 r.Max.Y += 1 + nl[j]*v.Body.Fr.Font.Height 366 } 367 r.Min.Y = Winresize(v, r, c.Safe, false) 368 r.Max.Y += adraw.Border() 369 adraw.Display.ScreenImage.Draw(r, adraw.Display.Black, nil, draw.ZP) 370 y1 = r.Max.Y 371 } 372 // scan to see new size of everyone below 373 y2 := c.R.Max.Y 374 for j = len(c.W) - 1; j > i; j-- { 375 v = c.W[j] 376 r = v.R 377 r.Min.Y = y2 - v.tagtop.Dy() 378 if nl[j] != 0 { 379 r.Min.Y -= 1 + nl[j]*v.Body.Fr.Font.Height 380 } 381 r.Min.Y -= adraw.Border() 382 ny[j] = r.Min.Y 383 y2 = r.Min.Y 384 } 385 // compute new size of window 386 r = w.R 387 r.Min.Y = y1 388 r.Max.Y = y2 389 h := w.Body.Fr.Font.Height 390 if r.Dy() < w.tagtop.Dy()+1+h+adraw.Border() { 391 r.Max.Y = r.Min.Y + w.tagtop.Dy() + 1 + h + adraw.Border() 392 } 393 // draw window 394 r.Max.Y = Winresize(w, r, c.Safe, true) 395 if i < len(c.W)-1 { 396 r.Min.Y = r.Max.Y 397 r.Max.Y += adraw.Border() 398 adraw.Display.ScreenImage.Draw(r, adraw.Display.Black, nil, draw.ZP) 399 for j = i + 1; j < len(c.W); j++ { 400 ny[j] -= (y2 - r.Max.Y) 401 } 402 } 403 // pack everyone below 404 y1 = r.Max.Y 405 for j = i + 1; j < len(c.W); j++ { 406 v = c.W[j] 407 r = v.R 408 r.Min.Y = y1 409 r.Max.Y = y1 + v.tagtop.Dy() 410 if nl[j] != 0 { 411 r.Max.Y += 1 + nl[j]*v.Body.Fr.Font.Height 412 } 413 y1 = Winresize(v, r, c.Safe, j == len(c.W)-1) 414 if j < len(c.W)-1 { // no border on last window 415 r.Min.Y = y1 416 r.Max.Y += adraw.Border() 417 adraw.Display.ScreenImage.Draw(r, adraw.Display.Black, nil, draw.ZP) 418 y1 = r.Max.Y 419 } 420 } 421 c.Safe = true 422 } 423 424 func Coldragwin1(c *Column, w *Window, but int, op, p draw.Point) { 425 var i int 426 427 for i = 0; i < len(c.W); i++ { 428 if c.W[i] == w { 429 goto Found 430 } 431 } 432 util.Fatal("can't find window") 433 434 Found: 435 if w.Tagexpand { // force recomputation of window tag size 436 w.Taglines = 1 437 } 438 if util.Abs(p.X-op.X) < 5 && util.Abs(p.Y-op.Y) < 5 { 439 Colgrow(c, w, but) 440 return 441 } 442 // is it a flick to the right? 443 if util.Abs(p.Y-op.Y) < 10 && p.X > op.X+30 && rowwhichcol(c.Row, p) == c { 444 p.X = op.X + w.R.Dx() // yes: toss to next column 445 } 446 nc := rowwhichcol(c.Row, p) 447 if nc != nil && nc != c { 448 Colclose(c, w, false) 449 Coladd(nc, w, nil, p.Y) 450 return 451 } 452 if i == 0 && len(c.W) == 1 { 453 return // can't do it 454 } 455 if (i > 0 && p.Y < c.W[i-1].R.Min.Y) || (i < len(c.W)-1 && p.Y > w.R.Max.Y) || (i == 0 && p.Y > w.R.Max.Y) { 456 // shuffle 457 Colclose(c, w, false) 458 Coladd(c, w, nil, p.Y) 459 return 460 } 461 if i == 0 { 462 return 463 } 464 v := c.W[i-1] 465 if p.Y < v.tagtop.Max.Y { 466 p.Y = v.tagtop.Max.Y 467 } 468 if p.Y > w.R.Max.Y-w.tagtop.Dy()-adraw.Border() { 469 p.Y = w.R.Max.Y - w.tagtop.Dy() - adraw.Border() 470 } 471 r := v.R 472 r.Max.Y = p.Y 473 if r.Max.Y > v.Body.Fr.R.Min.Y { 474 r.Max.Y -= (r.Max.Y - v.Body.Fr.R.Min.Y) % v.Body.Fr.Font.Height 475 if v.Body.Fr.R.Min.Y == v.Body.Fr.R.Max.Y { 476 r.Max.Y++ 477 } 478 } 479 r.Min.Y = Winresize(v, r, c.Safe, false) 480 r.Max.Y = r.Min.Y + adraw.Border() 481 adraw.Display.ScreenImage.Draw(r, adraw.Display.Black, nil, draw.ZP) 482 r.Min.Y = r.Max.Y 483 if i == len(c.W)-1 { 484 r.Max.Y = c.R.Max.Y 485 } else { 486 r.Max.Y = c.W[i+1].R.Min.Y - adraw.Border() 487 } 488 Winresize(w, r, c.Safe, true) 489 c.Safe = true 490 } 491 492 func colwhich(c *Column, p draw.Point) *Text { 493 if !p.In(c.R) { 494 return nil 495 } 496 if p.In(c.Tag.All) { 497 return &c.Tag 498 } 499 for i := 0; i < len(c.W); i++ { 500 w := c.W[i] 501 if p.In(w.R) { 502 if p.In(w.tagtop) || p.In(w.Tag.All) { 503 return &w.Tag 504 } 505 // exclude partial line at bottom 506 if p.X >= w.Body.ScrollR.Max.X && p.Y >= w.Body.Fr.R.Max.Y { 507 return nil 508 } 509 return &w.Body 510 } 511 } 512 return nil 513 }