gitstack

grindlemire/go-tui code browser

4.1 KB Go 157 lines 2026-02-06 · 3ad544c raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package tui

import (
	"testing"
)

// --- Hit Testing Tests ---

func TestElement_ElementAt_ReturnsNilForPointOutsideBounds(t *testing.T) {
	e := New(WithWidth(10), WithHeight(10))
	// Calculate layout so the element has a position
	buf := NewBuffer(80, 25)
	e.Render(buf, 80, 25)

	result := e.ElementAt(50, 50)
	if result != nil {
		t.Error("ElementAt should return nil for points outside the element's bounds")
	}
}

func TestElement_ElementAt_ReturnsSelfForPointInsideBounds(t *testing.T) {
	e := New(WithWidth(10), WithHeight(10))
	// Calculate layout so the element has a position
	buf := NewBuffer(80, 25)
	e.Render(buf, 80, 25)

	result := e.ElementAt(5, 5)
	if result != e {
		t.Error("ElementAt should return the element itself when point is inside bounds and no children")
	}
}

func TestElement_ElementAt_ReturnsChildForPointInsideChild(t *testing.T) {
	parent := New(
		WithWidth(100),
		WithHeight(100),
		WithDirection(Column),
	)
	child := New(WithWidth(50), WithHeight(50))
	parent.AddChild(child)

	// Calculate layout
	buf := NewBuffer(100, 100)
	parent.Render(buf, 100, 100)

	// Point inside child bounds (child starts at 0,0 and is 50x50)
	result := parent.ElementAt(10, 10)
	if result != child {
		t.Error("ElementAt should return the child when point is inside child bounds")
	}
}

func TestElement_ElementAt_ReturnsDeepestChild(t *testing.T) {
	root := New(
		WithWidth(100),
		WithHeight(100),
	)
	child1 := New(WithWidth(80), WithHeight(80))
	child2 := New(WithWidth(60), WithHeight(60))
	grandchild := New(WithWidth(40), WithHeight(40))

	child2.AddChild(grandchild)
	child1.AddChild(child2)
	root.AddChild(child1)

	// Calculate layout
	buf := NewBuffer(100, 100)
	root.Render(buf, 100, 100)

	// Point inside grandchild bounds (should be at 0,0)
	result := root.ElementAt(10, 10)
	if result != grandchild {
		t.Error("ElementAt should return the deepest child containing the point")
	}
}

func TestElement_ElementAt_LastChildTakesPrecedence(t *testing.T) {
	// When children overlap, last child should take precedence (renders on top)
	parent := New(
		WithWidth(100),
		WithHeight(100),
	)
	// Both children start at 0,0 with same size
	child1 := New(WithWidth(50), WithHeight(50))
	child2 := New(WithWidth(50), WithHeight(50))

	parent.AddChild(child1)
	parent.AddChild(child2)

	// Calculate layout - both children will be at position (0,0)
	buf := NewBuffer(100, 100)
	parent.Render(buf, 100, 100)

	result := parent.ElementAt(10, 10)
	// child2 was added last, so it should take precedence
	// However, with default layout (row), they won't overlap.
	// Let's just verify we get one of the children
	if result != child1 && result != child2 {
		t.Error("ElementAt should return one of the children for overlapping bounds")
	}
}

func TestElement_ElementAtPoint_ReturnsFocusable(t *testing.T) {
	// Test that ElementAtPoint returns a Focusable interface
	e := New(WithWidth(10), WithHeight(10))
	buf := NewBuffer(80, 25)
	e.Render(buf, 80, 25)

	result := e.ElementAtPoint(5, 5)
	if result == nil {
		t.Error("ElementAtPoint should return a non-nil Focusable")
	}
	// Verify it's the same underlying element
	if result.(*Element) != e {
		t.Error("ElementAtPoint should return the element wrapped as Focusable")
	}
}

func TestElement_ElementAtPoint_ReturnsNilForPointOutsideBounds(t *testing.T) {
	e := New(WithWidth(10), WithHeight(10))
	buf := NewBuffer(80, 25)
	e.Render(buf, 80, 25)

	result := e.ElementAtPoint(50, 50)
	if result != nil {
		t.Error("ElementAtPoint should return nil for points outside bounds")
	}
}

// --- HandleEvent Tests ---

func TestElement_HandleEvent_MouseNotHandledByDefault(t *testing.T) {
	e := New()

	event := MouseEvent{
		Button: MouseLeft,
		Action: MousePress,
		X:      5,
		Y:      5,
	}
	consumed := e.HandleEvent(event)

	if consumed {
		t.Error("HandleEvent should return false for mouse events (mouse handling is via component HandleMouse)")
	}
}

func TestElement_HandleEvent_KeyNotHandledByDefault(t *testing.T) {
	e := New()

	event := KeyEvent{Key: KeyRune, Rune: 'x'}
	consumed := e.HandleEvent(event)

	if consumed {
		t.Error("HandleEvent should return false for key events (key handling is via component KeyMap)")
	}
}