drawHelpPanel renders the bordered slide-up panel. anim ∈ [0,1] controls its height. Columns are rendered at fixed x-offsets so all keys/descriptions align visually regardless of their text length.
(c *Canvas, anim float32)
| 1476 | // controls its height. Columns are rendered at fixed x-offsets so all |
| 1477 | // keys/descriptions align visually regardless of their text length. |
| 1478 | func drawHelpPanel(c *Canvas, anim float32) { |
| 1479 | if anim <= 0 || c.H < 4 { |
| 1480 | return |
| 1481 | } |
| 1482 | |
| 1483 | // Per-column widths. |
| 1484 | keyW := make([]int, len(helpData)) |
| 1485 | descW := make([]int, len(helpData)) |
| 1486 | for i, col := range helpData { |
| 1487 | kw := runeLen(col.title) |
| 1488 | dw := 0 |
| 1489 | for _, e := range col.entries { |
| 1490 | if runeLen(e.key) > kw { |
| 1491 | kw = runeLen(e.key) |
| 1492 | } |
| 1493 | if runeLen(e.desc) > dw { |
| 1494 | dw = runeLen(e.desc) |
| 1495 | } |
| 1496 | } |
| 1497 | keyW[i] = kw |
| 1498 | descW[i] = dw |
| 1499 | } |
| 1500 | const colGap = 4 // horizontal space between columns |
| 1501 | const keyDescGap = 1 |
| 1502 | colTotal := make([]int, len(helpData)) |
| 1503 | contentW := 0 |
| 1504 | for i := range helpData { |
| 1505 | colTotal[i] = keyW[i] + keyDescGap + descW[i] |
| 1506 | contentW += colTotal[i] |
| 1507 | if i < len(helpData)-1 { |
| 1508 | contentW += colGap |
| 1509 | } |
| 1510 | } |
| 1511 | |
| 1512 | maxRows := 0 |
| 1513 | for _, col := range helpData { |
| 1514 | if len(col.entries) > maxRows { |
| 1515 | maxRows = len(col.entries) |
| 1516 | } |
| 1517 | } |
| 1518 | fullContentRows := 1 + maxRows // header + entries |
| 1519 | fullPanelH := fullContentRows + 2 |
| 1520 | |
| 1521 | panelH := int(anim*float32(fullPanelH) + 0.5) |
| 1522 | if panelH > fullPanelH { |
| 1523 | panelH = fullPanelH |
| 1524 | } |
| 1525 | if panelH > c.H-2 { |
| 1526 | panelH = c.H - 2 |
| 1527 | } |
| 1528 | if panelH < 1 { |
| 1529 | // Panel has fully retracted — don't draw anything (avoids the |
| 1530 | // "stuck thin sliver" frame at the tail of the close animation). |
| 1531 | return |
| 1532 | } |
| 1533 | |
| 1534 | panelW := contentW + 6 // 1 border + 2 padding each side |
| 1535 | if panelW > c.W { |