fix(render): follow the tmux rule for lit separators
- commit
- 0e47d2a2379afec660062d05a21038733d4a6d3c
- author
- brice · · 2026-09-26 15:07 UTC
- committer
- brice · · 2026-09-26 15:07 UTC
- parent
- 181a3c8
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +51 -77
M internal/adapters/vulkan/renderer.go +43 -67
| @@ -458,11 +458,17 @@ func (r *Renderer) draws(s ports.Scene, contents map[ports.WindowID]ports.Surfac | |||
| 458 | 458 | } | |
| 459 | 459 | // The focused window lights the lines along its own sides only: its | |
| 460 | 460 | // inset border, and its neighbors' border line where they own it. | |
| 461 | - | // tmux style: each stretch of line shared by two tiles is split in | |
| 462 | - | // half, the left or top window owning the first half. The focused | |
| 463 | - | // window lights its half of every stretch along its sides. | |
| 461 | + | // tmux rule (screen-redraw.c): the focused window lights the lines | |
| 462 | + | // along its sides, corners included. With exactly two tiles a single | |
| 463 | + | // line splits them: each lights only its half of it (left/top window: | |
| 464 | + | // first half). | |
| 464 | 465 | if focused != nil && s.Border.Active != "" && s.Border.Width > 0 { | |
| 465 | - | f := focused | |
| 466 | + | f, tiles := focused, 0 | |
| 467 | + | for _, w := range s.Windows { | |
| 468 | + | if !w.Hidden && !w.Popup && w.Neighbors != 0 { | |
| 469 | + | tiles++ | |
| 470 | + | } | |
| 471 | + | } | |
| 466 | 472 | // Inset sides with no neighbor (floats) light fully. | |
| 467 | 473 | for _, strip := range borderStrips(f.Rect, f.Inset&^f.Neighbors, 0, s.Border.Width) { | |
| 468 | 474 | add(physRect(strip.X, strip.Y, strip.W, strip.H), parseColor(s.Border.Active)) | |
| @@ -475,15 +481,11 @@ func (r *Renderer) draws(s ports.Scene, contents map[ports.WindowID]ports.Surfac | |||
| 475 | 481 | if f.Inset&side != 0 { | |
| 476 | 482 | out = 0 | |
| 477 | 483 | } | |
| 478 | - | line := borderStrips(f.Rect, side, out, s.Border.Width)[0] | |
| 479 | - | for _, n := range s.Windows { | |
| 480 | - | if n.ID == f.ID || n.Hidden || n.Popup || n.Neighbors&opposite(side) == 0 { | |
| 481 | - | continue | |
| 482 | - | } | |
| 483 | - | if part, ok := focusedHalf(line, *f, n.Rect, side, s.Border.Width); ok { | |
| 484 | - | add(physRect(part.X, part.Y, part.W, part.H), parseColor(s.Border.Active)) | |
| 485 | - | } | |
| 484 | + | line := closeCorners(borderStrips(f.Rect, side, out, s.Border.Width)[0], *f, side, s.Border.Width) | |
| 485 | + | if tiles == 2 { | |
| 486 | + | line = halfStrip(line, side) | |
| 486 | 487 | } | |
| 488 | + | add(physRect(line.X, line.Y, line.W, line.H), parseColor(s.Border.Active)) | |
| 487 | 489 | } | |
| 488 | 490 | } | |
| 489 | 491 | // Popups draw only what the client drew, shadows clipped. Window popups | |
| @@ -641,66 +643,40 @@ func borderStrips(r ports.Rect, sides ports.Sides, out, b int) []ports.Rect { | |||
| 641 | 643 | return strips | |
| 642 | 644 | } | |
| 643 | 645 | ||
| 644 | - | // opposite is the side facing side. | |
| 645 | - | func opposite(side ports.Sides) ports.Sides { | |
| 646 | - | switch side { | |
| 647 | - | case ports.SideLeft: | |
| 648 | - | return ports.SideRight | |
| 649 | - | case ports.SideRight: | |
| 650 | - | return ports.SideLeft | |
| 651 | - | case ports.SideTop: | |
| 652 | - | return ports.SideBottom | |
| 653 | - | } | |
| 654 | - | return ports.SideTop | |
| 655 | - | } | |
| 656 | - | ||
| 657 | - | // focusedHalf is the part of line, along side of window w, that w owns | |
| 658 | - | // in the stretch it shares with neighbor n: the first half when w is the | |
| 659 | - | // left or top window, else the second. ok is false when n is not across | |
| 660 | - | // side or they share no stretch. A part reaching a corner of w whose | |
| 661 | - | // crossing line lies outside w (a neighbor's) runs b further to close | |
| 662 | - | // the corner. | |
| 663 | - | func focusedHalf(line ports.Rect, w ports.SceneWindow, n ports.Rect, side ports.Sides, b int) (ports.Rect, bool) { | |
| 664 | - | f := w.Rect | |
| 646 | + | // closeCorners runs a line along side of w b further at each end where | |
| 647 | + | // the crossing line lies outside w (a neighbor's), so lit lines meet. | |
| 648 | + | func closeCorners(line ports.Rect, w ports.SceneWindow, side ports.Sides, b int) ports.Rect { | |
| 665 | 649 | outside := func(s ports.Sides) bool { return w.Neighbors&s != 0 && w.Inset&s == 0 } | |
| 666 | - | var lo, hi int | |
| 667 | - | switch side { | |
| 668 | - | case ports.SideRight, ports.SideLeft: | |
| 669 | - | if side == ports.SideRight && n.X < f.X+f.W || side == ports.SideLeft && n.X+n.W > f.X { | |
| 670 | - | return ports.Rect{}, false | |
| 650 | + | if side == ports.SideLeft || side == ports.SideRight { | |
| 651 | + | if outside(ports.SideTop) { | |
| 652 | + | line.Y, line.H = line.Y-b, line.H+b | |
| 671 | 653 | } | |
| 672 | - | lo, hi = max(f.Y, n.Y), min(f.Y+f.H, n.Y+n.H) | |
| 673 | - | default: | |
| 674 | - | if side == ports.SideBottom && n.Y < f.Y+f.H || side == ports.SideTop && n.Y+n.H > f.Y { | |
| 675 | - | return ports.Rect{}, false | |
| 654 | + | if outside(ports.SideBottom) { | |
| 655 | + | line.H += b | |
| 676 | 656 | } | |
| 677 | - | lo, hi = max(f.X, n.X), min(f.X+f.W, n.X+n.W) | |
| 657 | + | return line | |
| 678 | 658 | } | |
| 679 | - | if hi <= lo { | |
| 680 | - | return ports.Rect{}, false | |
| 659 | + | if outside(ports.SideLeft) { | |
| 660 | + | line.X, line.W = line.X-b, line.W+b | |
| 681 | 661 | } | |
| 682 | - | mid := lo + (hi-lo)/2 | |
| 683 | - | if side == ports.SideLeft || side == ports.SideTop { | |
| 684 | - | lo = mid | |
| 685 | - | } else { | |
| 686 | - | hi = mid | |
| 662 | + | if outside(ports.SideRight) { | |
| 663 | + | line.W += b | |
| 687 | 664 | } | |
| 688 | - | if side == ports.SideRight || side == ports.SideLeft { | |
| 689 | - | if lo == f.Y && outside(ports.SideTop) { | |
| 690 | - | lo -= b | |
| 691 | - | } | |
| 692 | - | if hi == f.Y+f.H && outside(ports.SideBottom) { | |
| 693 | - | hi += b | |
| 694 | - | } | |
| 695 | - | line.Y, line.H = lo, hi-lo | |
| 696 | - | } else { | |
| 697 | - | if lo == f.X && outside(ports.SideLeft) { | |
| 698 | - | lo -= b | |
| 699 | - | } | |
| 700 | - | if hi == f.X+f.W && outside(ports.SideRight) { | |
| 701 | - | hi += b | |
| 702 | - | } | |
| 703 | - | line.X, line.W = lo, hi-lo | |
| 665 | + | return line | |
| 666 | + | } | |
| 667 | + | ||
| 668 | + | // halfStrip keeps the half of a separator strip owned by the window on | |
| 669 | + | // side of it: the first half for the left or top window, else the second. | |
| 670 | + | func halfStrip(r ports.Rect, side ports.Sides) ports.Rect { | |
| 671 | + | switch side { | |
| 672 | + | case ports.SideRight: | |
| 673 | + | r.H /= 2 | |
| 674 | + | case ports.SideLeft: | |
| 675 | + | r.Y, r.H = r.Y+r.H/2, r.H-r.H/2 | |
| 676 | + | case ports.SideBottom: | |
| 677 | + | r.W /= 2 | |
| 678 | + | case ports.SideTop: | |
| 679 | + | r.X, r.W = r.X+r.W/2, r.W-r.W/2 | |
| 704 | 680 | } | |
| 705 | - | return line, true | |
| 681 | + | return r | |
| 706 | 682 | } | |
| @@ -458,11 +458,17 @@ func (r *Renderer) draws(s ports.Scene, contents map[ports.WindowID]ports.Surfac | |||||
| 458 | } | 458 | } | ||
| 459 | // The focused window lights the lines along its own sides only: its | 459 | // The focused window lights the lines along its own sides only: its | ||
| 460 | // inset border, and its neighbors' border line where they own it. | 460 | // inset border, and its neighbors' border line where they own it. | ||
| 461 | - | // tmux style: each stretch of line shared by two tiles is split in | 461 | + | // tmux rule (screen-redraw.c): the focused window lights the lines |
| 462 | - | // half, the left or top window owning the first half. The focused | 462 | + | // along its sides, corners included. With exactly two tiles a single |
| 463 | - | // window lights its half of every stretch along its sides. | 463 | + | // line splits them: each lights only its half of it (left/top window: |
| 464 | + | // first half). | |||
| 464 | if focused != nil && s.Border.Active != "" && s.Border.Width > 0 { | 465 | if focused != nil && s.Border.Active != "" && s.Border.Width > 0 { | ||
| 465 | - | f := focused | 466 | + | f, tiles := focused, 0 |
| 467 | + | for _, w := range s.Windows { | |||
| 468 | + | if !w.Hidden && !w.Popup && w.Neighbors != 0 { | |||
| 469 | + | tiles++ | |||
| 470 | + | } | |||
| 471 | + | } | |||
| 466 | // Inset sides with no neighbor (floats) light fully. | 472 | // Inset sides with no neighbor (floats) light fully. | ||
| 467 | for _, strip := range borderStrips(f.Rect, f.Inset&^f.Neighbors, 0, s.Border.Width) { | 473 | for _, strip := range borderStrips(f.Rect, f.Inset&^f.Neighbors, 0, s.Border.Width) { | ||
| 468 | add(physRect(strip.X, strip.Y, strip.W, strip.H), parseColor(s.Border.Active)) | 474 | add(physRect(strip.X, strip.Y, strip.W, strip.H), parseColor(s.Border.Active)) | ||
| @@ -475,15 +481,11 @@ func (r *Renderer) draws(s ports.Scene, contents map[ports.WindowID]ports.Surfac | |||||
| 475 | if f.Inset&side != 0 { | 481 | if f.Inset&side != 0 { | ||
| 476 | out = 0 | 482 | out = 0 | ||
| 477 | } | 483 | } | ||
| 478 | - | line := borderStrips(f.Rect, side, out, s.Border.Width)[0] | 484 | + | line := closeCorners(borderStrips(f.Rect, side, out, s.Border.Width)[0], *f, side, s.Border.Width) |
| 479 | - | for _, n := range s.Windows { | 485 | + | if tiles == 2 { |
| 480 | - | if n.ID == f.ID || n.Hidden || n.Popup || n.Neighbors&opposite(side) == 0 { | 486 | + | line = halfStrip(line, side) |
| 481 | - | continue | |||
| 482 | - | } | |||
| 483 | - | if part, ok := focusedHalf(line, *f, n.Rect, side, s.Border.Width); ok { | |||
| 484 | - | add(physRect(part.X, part.Y, part.W, part.H), parseColor(s.Border.Active)) | |||
| 485 | - | } | |||
| 486 | } | 487 | } | ||
| 488 | + | add(physRect(line.X, line.Y, line.W, line.H), parseColor(s.Border.Active)) | |||
| 487 | } | 489 | } | ||
| 488 | } | 490 | } | ||
| 489 | // Popups draw only what the client drew, shadows clipped. Window popups | 491 | // Popups draw only what the client drew, shadows clipped. Window popups | ||
| @@ -641,66 +643,40 @@ func borderStrips(r ports.Rect, sides ports.Sides, out, b int) []ports.Rect { | |||||
| 641 | return strips | 643 | return strips | ||
| 642 | } | 644 | } | ||
| 643 | 645 | ||||
| 644 | - | // opposite is the side facing side. | 646 | + | // closeCorners runs a line along side of w b further at each end where |
| 645 | - | func opposite(side ports.Sides) ports.Sides { | 647 | + | // the crossing line lies outside w (a neighbor's), so lit lines meet. |
| 646 | - | switch side { | 648 | + | func closeCorners(line ports.Rect, w ports.SceneWindow, side ports.Sides, b int) ports.Rect { |
| 647 | - | case ports.SideLeft: | |||
| 648 | - | return ports.SideRight | |||
| 649 | - | case ports.SideRight: | |||
| 650 | - | return ports.SideLeft | |||
| 651 | - | case ports.SideTop: | |||
| 652 | - | return ports.SideBottom | |||
| 653 | - | } | |||
| 654 | - | return ports.SideTop | |||
| 655 | - | } | |||
| 656 | - | ||||
| 657 | - | // focusedHalf is the part of line, along side of window w, that w owns | |||
| 658 | - | // in the stretch it shares with neighbor n: the first half when w is the | |||
| 659 | - | // left or top window, else the second. ok is false when n is not across | |||
| 660 | - | // side or they share no stretch. A part reaching a corner of w whose | |||
| 661 | - | // crossing line lies outside w (a neighbor's) runs b further to close | |||
| 662 | - | // the corner. | |||
| 663 | - | func focusedHalf(line ports.Rect, w ports.SceneWindow, n ports.Rect, side ports.Sides, b int) (ports.Rect, bool) { | |||
| 664 | - | f := w.Rect | |||
| 665 | outside := func(s ports.Sides) bool { return w.Neighbors&s != 0 && w.Inset&s == 0 } | 649 | outside := func(s ports.Sides) bool { return w.Neighbors&s != 0 && w.Inset&s == 0 } | ||
| 666 | - | var lo, hi int | 650 | + | if side == ports.SideLeft || side == ports.SideRight { |
| 667 | - | switch side { | 651 | + | if outside(ports.SideTop) { |
| 668 | - | case ports.SideRight, ports.SideLeft: | 652 | + | line.Y, line.H = line.Y-b, line.H+b |
| 669 | - | if side == ports.SideRight && n.X < f.X+f.W || side == ports.SideLeft && n.X+n.W > f.X { | |||
| 670 | - | return ports.Rect{}, false | |||
| 671 | } | 653 | } | ||
| 672 | - | lo, hi = max(f.Y, n.Y), min(f.Y+f.H, n.Y+n.H) | 654 | + | if outside(ports.SideBottom) { |
| 673 | - | default: | 655 | + | line.H += b |
| 674 | - | if side == ports.SideBottom && n.Y < f.Y+f.H || side == ports.SideTop && n.Y+n.H > f.Y { | |||
| 675 | - | return ports.Rect{}, false | |||
| 676 | } | 656 | } | ||
| 677 | - | lo, hi = max(f.X, n.X), min(f.X+f.W, n.X+n.W) | 657 | + | return line |
| 678 | } | 658 | } | ||
| 679 | - | if hi <= lo { | 659 | + | if outside(ports.SideLeft) { |
| 680 | - | return ports.Rect{}, false | 660 | + | line.X, line.W = line.X-b, line.W+b |
| 681 | } | 661 | } | ||
| 682 | - | mid := lo + (hi-lo)/2 | 662 | + | if outside(ports.SideRight) { |
| 683 | - | if side == ports.SideLeft || side == ports.SideTop { | 663 | + | line.W += b |
| 684 | - | lo = mid | |||
| 685 | - | } else { | |||
| 686 | - | hi = mid | |||
| 687 | } | 664 | } | ||
| 688 | - | if side == ports.SideRight || side == ports.SideLeft { | 665 | + | return line |
| 689 | - | if lo == f.Y && outside(ports.SideTop) { | 666 | + | } |
| 690 | - | lo -= b | 667 | + | |
| 691 | - | } | 668 | + | // halfStrip keeps the half of a separator strip owned by the window on |
| 692 | - | if hi == f.Y+f.H && outside(ports.SideBottom) { | 669 | + | // side of it: the first half for the left or top window, else the second. |
| 693 | - | hi += b | 670 | + | func halfStrip(r ports.Rect, side ports.Sides) ports.Rect { |
| 694 | - | } | 671 | + | switch side { |
| 695 | - | line.Y, line.H = lo, hi-lo | 672 | + | case ports.SideRight: |
| 696 | - | } else { | 673 | + | r.H /= 2 |
| 697 | - | if lo == f.X && outside(ports.SideLeft) { | 674 | + | case ports.SideLeft: |
| 698 | - | lo -= b | 675 | + | r.Y, r.H = r.Y+r.H/2, r.H-r.H/2 |
| 699 | - | } | 676 | + | case ports.SideBottom: |
| 700 | - | if hi == f.X+f.W && outside(ports.SideRight) { | 677 | + | r.W /= 2 |
| 701 | - | hi += b | 678 | + | case ports.SideTop: |
| 702 | - | } | 679 | + | r.X, r.W = r.X+r.W/2, r.W-r.W/2 |
| 703 | - | line.X, line.W = lo, hi-lo | |||
| 704 | } | 680 | } | ||
| 705 | - | return line, true | 681 | + | return r |
| 706 | } | 682 | } | ||
M internal/adapters/vulkan/renderer_test.go +8 -10
| @@ -77,29 +77,27 @@ func TestRendererRender(t *testing.T) { | |||
| 77 | 77 | check(pair, map[image.Point]color.RGBA{{14, 3}: lit, {14, 12}: gray}) | |
| 78 | 78 | pair.Windows[0].Focused = false | |
| 79 | 79 | check(pair, map[image.Point]color.RGBA{{14, 5}: gray, {15, 5}: gray}) | |
| 80 | - | // Three columns: each line is split on its own. Focus in the middle | |
| 81 | - | // lights the bottom half of the left line and the top half of the right one. | |
| 80 | + | // Three columns (tmux rule): the focused window lights its full lines. | |
| 82 | 81 | three := s | |
| 83 | 82 | three.Windows = []ports.SceneWindow{ | |
| 84 | 83 | {ID: 1, Rect: ports.Rect{X: 0, Y: 0, W: 16, H: 16}, Inset: ports.SideRight, Neighbors: ports.SideRight}, | |
| 85 | 84 | {ID: 2, Rect: ports.Rect{X: 16, Y: 0, W: 16, H: 16}, Inset: ports.SideRight, Neighbors: ports.SideLeft | ports.SideRight, Focused: true}, | |
| 86 | 85 | {ID: 3, Rect: ports.Rect{X: 32, Y: 0, W: 16, H: 16}, Neighbors: ports.SideLeft}, | |
| 87 | 86 | } | |
| 88 | - | check(three, map[image.Point]color.RGBA{{14, 3}: gray, {14, 12}: lit, {30, 3}: lit, {30, 12}: gray}) | |
| 89 | - | // Bottom-right tile of a split column: its top line (owned by the tile | |
| 90 | - | // above) meets the left line (owned by the left column); lit halves | |
| 91 | - | // ending at that corner close it. | |
| 87 | + | check(three, map[image.Point]color.RGBA{{14, 3}: lit, {14, 12}: lit, {30, 3}: lit, {30, 12}: lit}) | |
| 88 | + | three.Windows[1].Focused, three.Windows[0].Focused = false, true | |
| 89 | + | check(three, map[image.Point]color.RGBA{{14, 3}: lit, {14, 12}: lit, {30, 3}: gray, {30, 12}: gray}) | |
| 90 | + | // Bottom-right tile of a split column: its left line and top line are | |
| 91 | + | // lit, and they meet in the corner. | |
| 92 | 92 | grid := s | |
| 93 | 93 | grid.Windows = []ports.SceneWindow{ | |
| 94 | 94 | {ID: 1, Rect: ports.Rect{X: 0, Y: 0, W: 16, H: 32}, Inset: ports.SideRight, Neighbors: ports.SideRight}, | |
| 95 | 95 | {ID: 2, Rect: ports.Rect{X: 16, Y: 0, W: 16, H: 16}, Inset: ports.SideBottom, Neighbors: ports.SideLeft | ports.SideBottom}, | |
| 96 | 96 | {ID: 3, Rect: ports.Rect{X: 16, Y: 16, W: 16, H: 16}, Neighbors: ports.SideLeft | ports.SideTop, Focused: true}, | |
| 97 | 97 | } | |
| 98 | - | check(grid, map[image.Point]color.RGBA{{14, 30}: lit, {14, 20}: gray, {24, 14}: lit, {17, 14}: gray}) | |
| 98 | + | check(grid, map[image.Point]color.RGBA{{14, 30}: lit, {14, 20}: lit, {14, 14}: lit, {14, 5}: gray, {24, 14}: lit, {17, 14}: lit}) | |
| 99 | 99 | grid.Windows[2].Focused, grid.Windows[1].Focused = false, true | |
| 100 | - | // Top-right tile: left line bottom half and bottom line left half meet | |
| 101 | - | // at the corner inside the left column's line. | |
| 102 | - | check(grid, map[image.Point]color.RGBA{{14, 14}: lit, {14, 15}: lit, {16, 14}: lit, {14, 3}: gray, {30, 14}: gray}) | |
| 100 | + | check(grid, map[image.Point]color.RGBA{{14, 3}: lit, {14, 14}: lit, {14, 15}: lit, {14, 20}: gray, {30, 14}: lit}) | |
| 103 | 101 | s.Windows[0].Fullscreen = true | |
| 104 | 102 | check(s, map[image.Point]color.RGBA{{9, 9}: wc}) | |
| 105 | 103 | s.Windows[0].Fullscreen = false | |
| @@ -77,29 +77,27 @@ func TestRendererRender(t *testing.T) { | |||||
| 77 | check(pair, map[image.Point]color.RGBA{{14, 3}: lit, {14, 12}: gray}) | 77 | check(pair, map[image.Point]color.RGBA{{14, 3}: lit, {14, 12}: gray}) | ||
| 78 | pair.Windows[0].Focused = false | 78 | pair.Windows[0].Focused = false | ||
| 79 | check(pair, map[image.Point]color.RGBA{{14, 5}: gray, {15, 5}: gray}) | 79 | check(pair, map[image.Point]color.RGBA{{14, 5}: gray, {15, 5}: gray}) | ||
| 80 | - | // Three columns: each line is split on its own. Focus in the middle | 80 | + | // Three columns (tmux rule): the focused window lights its full lines. |
| 81 | - | // lights the bottom half of the left line and the top half of the right one. | |||
| 82 | three := s | 81 | three := s | ||
| 83 | three.Windows = []ports.SceneWindow{ | 82 | three.Windows = []ports.SceneWindow{ | ||
| 84 | {ID: 1, Rect: ports.Rect{X: 0, Y: 0, W: 16, H: 16}, Inset: ports.SideRight, Neighbors: ports.SideRight}, | 83 | {ID: 1, Rect: ports.Rect{X: 0, Y: 0, W: 16, H: 16}, Inset: ports.SideRight, Neighbors: ports.SideRight}, | ||
| 85 | {ID: 2, Rect: ports.Rect{X: 16, Y: 0, W: 16, H: 16}, Inset: ports.SideRight, Neighbors: ports.SideLeft | ports.SideRight, Focused: true}, | 84 | {ID: 2, Rect: ports.Rect{X: 16, Y: 0, W: 16, H: 16}, Inset: ports.SideRight, Neighbors: ports.SideLeft | ports.SideRight, Focused: true}, | ||
| 86 | {ID: 3, Rect: ports.Rect{X: 32, Y: 0, W: 16, H: 16}, Neighbors: ports.SideLeft}, | 85 | {ID: 3, Rect: ports.Rect{X: 32, Y: 0, W: 16, H: 16}, Neighbors: ports.SideLeft}, | ||
| 87 | } | 86 | } | ||
| 88 | - | check(three, map[image.Point]color.RGBA{{14, 3}: gray, {14, 12}: lit, {30, 3}: lit, {30, 12}: gray}) | 87 | + | check(three, map[image.Point]color.RGBA{{14, 3}: lit, {14, 12}: lit, {30, 3}: lit, {30, 12}: lit}) |
| 89 | - | // Bottom-right tile of a split column: its top line (owned by the tile | 88 | + | three.Windows[1].Focused, three.Windows[0].Focused = false, true |
| 90 | - | // above) meets the left line (owned by the left column); lit halves | 89 | + | check(three, map[image.Point]color.RGBA{{14, 3}: lit, {14, 12}: lit, {30, 3}: gray, {30, 12}: gray}) |
| 91 | - | // ending at that corner close it. | 90 | + | // Bottom-right tile of a split column: its left line and top line are |
| 91 | + | // lit, and they meet in the corner. | |||
| 92 | grid := s | 92 | grid := s | ||
| 93 | grid.Windows = []ports.SceneWindow{ | 93 | grid.Windows = []ports.SceneWindow{ | ||
| 94 | {ID: 1, Rect: ports.Rect{X: 0, Y: 0, W: 16, H: 32}, Inset: ports.SideRight, Neighbors: ports.SideRight}, | 94 | {ID: 1, Rect: ports.Rect{X: 0, Y: 0, W: 16, H: 32}, Inset: ports.SideRight, Neighbors: ports.SideRight}, | ||
| 95 | {ID: 2, Rect: ports.Rect{X: 16, Y: 0, W: 16, H: 16}, Inset: ports.SideBottom, Neighbors: ports.SideLeft | ports.SideBottom}, | 95 | {ID: 2, Rect: ports.Rect{X: 16, Y: 0, W: 16, H: 16}, Inset: ports.SideBottom, Neighbors: ports.SideLeft | ports.SideBottom}, | ||
| 96 | {ID: 3, Rect: ports.Rect{X: 16, Y: 16, W: 16, H: 16}, Neighbors: ports.SideLeft | ports.SideTop, Focused: true}, | 96 | {ID: 3, Rect: ports.Rect{X: 16, Y: 16, W: 16, H: 16}, Neighbors: ports.SideLeft | ports.SideTop, Focused: true}, | ||
| 97 | } | 97 | } | ||
| 98 | - | check(grid, map[image.Point]color.RGBA{{14, 30}: lit, {14, 20}: gray, {24, 14}: lit, {17, 14}: gray}) | 98 | + | check(grid, map[image.Point]color.RGBA{{14, 30}: lit, {14, 20}: lit, {14, 14}: lit, {14, 5}: gray, {24, 14}: lit, {17, 14}: lit}) |
| 99 | grid.Windows[2].Focused, grid.Windows[1].Focused = false, true | 99 | grid.Windows[2].Focused, grid.Windows[1].Focused = false, true | ||
| 100 | - | // Top-right tile: left line bottom half and bottom line left half meet | 100 | + | check(grid, map[image.Point]color.RGBA{{14, 3}: lit, {14, 14}: lit, {14, 15}: lit, {14, 20}: gray, {30, 14}: lit}) |
| 101 | - | // at the corner inside the left column's line. | |||
| 102 | - | check(grid, map[image.Point]color.RGBA{{14, 14}: lit, {14, 15}: lit, {16, 14}: lit, {14, 3}: gray, {30, 14}: gray}) | |||
| 103 | s.Windows[0].Fullscreen = true | 101 | s.Windows[0].Fullscreen = true | ||
| 104 | check(s, map[image.Point]color.RGBA{{9, 9}: wc}) | 102 | check(s, map[image.Point]color.RGBA{{9, 9}: wc}) | ||
| 105 | s.Windows[0].Fullscreen = false | 103 | s.Windows[0].Fullscreen = false | ||