Fortran · 8773 bytes Raw Blame History
1 module layout_mod
2 use pane_mod
3 implicit none
4 private
5
6 public :: layout_split_vertical, layout_split_horizontal
7 public :: layout_remove_pane
8 public :: layout_recalculate
9 public :: layout_find_neighbor
10
11 ! Direction constants for navigation
12 integer, parameter, public :: DIR_LEFT = 1
13 integer, parameter, public :: DIR_RIGHT = 2
14 integer, parameter, public :: DIR_UP = 3
15 integer, parameter, public :: DIR_DOWN = 4
16
17 ! Split type constants
18 integer, parameter, public :: SPLIT_NONE = 0
19 integer, parameter, public :: SPLIT_VERTICAL = 1 ! Side-by-side (left|right)
20 integer, parameter, public :: SPLIT_HORIZONTAL = 2 ! Stacked (top/bottom)
21
22 contains
23
24 ! Split the active pane vertically (side-by-side)
25 ! Returns the new pane index, or 0 on failure
26 function layout_split_vertical(panes, pane_count, active_idx, rows, cols) result(new_idx)
27 type(pane_t), intent(inout) :: panes(:)
28 integer, intent(inout) :: pane_count
29 integer, intent(in) :: active_idx
30 integer, intent(in) :: rows, cols
31 integer :: new_idx
32
33 new_idx = 0
34 if (pane_count >= MAX_PANES) return
35 if (active_idx < 1 .or. active_idx > pane_count) return
36
37 ! Create new pane
38 pane_count = pane_count + 1
39 new_idx = pane_count
40
41 call pane_init(panes(new_idx), rows, cols)
42
43 ! Mark the split relationship
44 panes(active_idx)%split_type = SPLIT_VERTICAL
45 panes(new_idx)%parent_idx = active_idx
46 panes(new_idx)%split_ratio = 0.5 ! Right half
47 end function layout_split_vertical
48
49 ! Split the active pane horizontally (stacked top/bottom)
50 ! Returns the new pane index, or 0 on failure
51 function layout_split_horizontal(panes, pane_count, active_idx, rows, cols) result(new_idx)
52 type(pane_t), intent(inout) :: panes(:)
53 integer, intent(inout) :: pane_count
54 integer, intent(in) :: active_idx
55 integer, intent(in) :: rows, cols
56 integer :: new_idx
57
58 new_idx = 0
59 if (pane_count >= MAX_PANES) return
60 if (active_idx < 1 .or. active_idx > pane_count) return
61
62 ! Create new pane
63 pane_count = pane_count + 1
64 new_idx = pane_count
65
66 call pane_init(panes(new_idx), rows, cols)
67
68 ! Mark the split relationship
69 panes(active_idx)%split_type = SPLIT_HORIZONTAL
70 panes(new_idx)%parent_idx = active_idx
71 panes(new_idx)%split_ratio = 0.5 ! Bottom half
72 end function layout_split_horizontal
73
74 ! Remove a pane and update layout
75 subroutine layout_remove_pane(panes, pane_count, idx)
76 type(pane_t), intent(inout) :: panes(:)
77 integer, intent(inout) :: pane_count
78 integer, intent(in) :: idx
79 integer :: i, parent_idx
80
81 if (idx < 1 .or. idx > pane_count) return
82
83 parent_idx = panes(idx)%parent_idx
84
85 ! Destroy the pane
86 call pane_destroy(panes(idx))
87
88 ! Shift remaining panes down
89 do i = idx, pane_count - 1
90 panes(i) = panes(i + 1)
91 end do
92
93 pane_count = pane_count - 1
94
95 ! Update parent references for shifted panes
96 do i = 1, pane_count
97 if (panes(i)%parent_idx > idx) then
98 panes(i)%parent_idx = panes(i)%parent_idx - 1
99 else if (panes(i)%parent_idx == idx) then
100 ! Orphaned - reparent to the removed pane's parent
101 panes(i)%parent_idx = parent_idx
102 end if
103 end do
104
105 ! If we removed a pane that had children, clear the split type of sibling
106 if (parent_idx > 0 .and. parent_idx <= pane_count) then
107 panes(parent_idx)%split_type = SPLIT_NONE
108 end if
109 end subroutine layout_remove_pane
110
111 ! Recalculate all pane viewports based on available space
112 subroutine layout_recalculate(panes, pane_count, x, y, w, h, cell_w, cell_h)
113 type(pane_t), intent(inout) :: panes(:)
114 integer, intent(in) :: pane_count
115 integer, intent(in) :: x, y, w, h
116 integer, intent(in) :: cell_w, cell_h
117 integer :: i
118 integer, allocatable :: viewport(:,:) ! (4, MAX_PANES) - x, y, w, h per pane
119 logical, allocatable :: processed(:)
120 integer :: px, py, pw, ph, split_pos
121
122 if (pane_count < 1) return
123
124 allocate(viewport(4, pane_count))
125 allocate(processed(pane_count))
126 processed = .false.
127
128 ! Start by giving the first pane (root) the entire space
129 viewport(1, 1) = x
130 viewport(2, 1) = y
131 viewport(3, 1) = w
132 viewport(4, 1) = h
133 processed(1) = .true.
134
135 ! Process panes in order, calculating child viewports from parents
136 do i = 2, pane_count
137 if (panes(i)%parent_idx > 0 .and. panes(i)%parent_idx <= pane_count) then
138 ! Get parent's viewport
139 px = viewport(1, panes(i)%parent_idx)
140 py = viewport(2, panes(i)%parent_idx)
141 pw = viewport(3, panes(i)%parent_idx)
142 ph = viewport(4, panes(i)%parent_idx)
143
144 if (panes(panes(i)%parent_idx)%split_type == SPLIT_VERTICAL) then
145 ! Parent was split vertically - child gets right half
146 split_pos = pw / 2
147 ! Shrink parent to left half
148 viewport(3, panes(i)%parent_idx) = split_pos - 1
149 ! Child gets right half
150 viewport(1, i) = px + split_pos + 1
151 viewport(2, i) = py
152 viewport(3, i) = pw - split_pos - 2
153 viewport(4, i) = ph
154 else if (panes(panes(i)%parent_idx)%split_type == SPLIT_HORIZONTAL) then
155 ! Parent was split horizontally - child gets bottom half
156 split_pos = ph / 2
157 ! Shrink parent to top half
158 viewport(4, panes(i)%parent_idx) = split_pos - 1
159 ! Child gets bottom half
160 viewport(1, i) = px
161 viewport(2, i) = py + split_pos + 1
162 viewport(3, i) = pw
163 viewport(4, i) = ph - split_pos - 2
164 else
165 ! No split type - shouldn't happen, give equal share
166 viewport(1, i) = x
167 viewport(2, i) = y
168 viewport(3, i) = w
169 viewport(4, i) = h
170 end if
171 processed(i) = .true.
172 else
173 ! Orphan pane - give it the full space (shouldn't happen normally)
174 viewport(1, i) = x
175 viewport(2, i) = y
176 viewport(3, i) = w
177 viewport(4, i) = h
178 processed(i) = .true.
179 end if
180 end do
181
182 ! Apply viewports to all panes
183 do i = 1, pane_count
184 call pane_set_viewport(panes(i), viewport(1, i), viewport(2, i), &
185 viewport(3, i), viewport(4, i), cell_w, cell_h)
186 end do
187
188 deallocate(viewport)
189 deallocate(processed)
190 end subroutine layout_recalculate
191
192 ! Find the nearest pane in the given direction
193 ! Returns 0 if no neighbor found
194 function layout_find_neighbor(panes, pane_count, active_idx, direction) result(neighbor_idx)
195 type(pane_t), intent(in) :: panes(:)
196 integer, intent(in) :: pane_count
197 integer, intent(in) :: active_idx
198 integer, intent(in) :: direction
199 integer :: neighbor_idx
200
201 integer :: i
202 integer :: active_cx, active_cy
203 integer :: cx, cy
204 integer :: best_idx, best_dist, dist, perp_dist
205
206 neighbor_idx = 0
207 if (pane_count < 2) return
208 if (active_idx < 1 .or. active_idx > pane_count) return
209
210 ! Get active pane's center point
211 active_cx = panes(active_idx)%x + panes(active_idx)%width / 2
212 active_cy = panes(active_idx)%y + panes(active_idx)%height / 2
213
214 best_idx = 0
215 best_dist = huge(best_dist)
216
217 do i = 1, pane_count
218 if (i == active_idx) cycle
219
220 cx = panes(i)%x + panes(i)%width / 2
221 cy = panes(i)%y + panes(i)%height / 2
222
223 select case (direction)
224 case (DIR_LEFT)
225 if (cx < active_cx) then
226 dist = active_cx - cx
227 perp_dist = abs(cy - active_cy)
228 ! Prefer panes more directly to the left
229 dist = dist + perp_dist / 2
230 if (dist < best_dist) then
231 best_dist = dist
232 best_idx = i
233 end if
234 end if
235
236 case (DIR_RIGHT)
237 if (cx > active_cx) then
238 dist = cx - active_cx
239 perp_dist = abs(cy - active_cy)
240 dist = dist + perp_dist / 2
241 if (dist < best_dist) then
242 best_dist = dist
243 best_idx = i
244 end if
245 end if
246
247 case (DIR_UP)
248 if (cy < active_cy) then
249 dist = active_cy - cy
250 perp_dist = abs(cx - active_cx)
251 dist = dist + perp_dist / 2
252 if (dist < best_dist) then
253 best_dist = dist
254 best_idx = i
255 end if
256 end if
257
258 case (DIR_DOWN)
259 if (cy > active_cy) then
260 dist = cy - active_cy
261 perp_dist = abs(cx - active_cx)
262 dist = dist + perp_dist / 2
263 if (dist < best_dist) then
264 best_dist = dist
265 best_idx = i
266 end if
267 end if
268 end select
269 end do
270
271 neighbor_idx = best_idx
272 end function layout_find_neighbor
273
274 end module layout_mod
275