Line data Source code
1 : #include "OptionsPanelLayout.h"
2 :
3 : #include <algorithm>
4 :
5 : // ── Fold state ───────────────────────────────────────────────────────────────
6 :
7 12 : void OptionsPanelLayout::setFolded(Section s, bool folded) {
8 12 : if (s >= 0 && s < SectionCount) {
9 12 : folded_[s] = folded;
10 12 : clampScroll();
11 12 : }
12 12 : }
13 :
14 3 : bool OptionsPanelLayout::isFolded(Section s) const {
15 3 : return s >= 0 && s < SectionCount && folded_[s];
16 : }
17 :
18 2 : void OptionsPanelLayout::toggleFolded(Section s) {
19 2 : if (s >= 0 && s < SectionCount) {
20 2 : folded_[s] = !folded_[s];
21 2 : clampScroll();
22 2 : }
23 2 : }
24 :
25 : // ── Scroll state ─────────────────────────────────────────────────────────────
26 :
27 3 : void OptionsPanelLayout::setScrollOffset(int offset) {
28 3 : scrollOffset_ = offset;
29 3 : clampScroll();
30 3 : }
31 :
32 7 : int OptionsPanelLayout::scrollOffset() const {
33 7 : return scrollOffset_;
34 : }
35 :
36 2 : void OptionsPanelLayout::scrollBy(int delta) {
37 2 : scrollOffset_ += delta;
38 2 : clampScroll();
39 2 : }
40 :
41 : // ── Viewport ─────────────────────────────────────────────────────────────────
42 :
43 5 : void OptionsPanelLayout::setViewportHeight(int h) {
44 5 : viewportHeight_ = h;
45 5 : clampScroll();
46 5 : }
47 :
48 0 : int OptionsPanelLayout::viewportHeight() const {
49 0 : return viewportHeight_;
50 : }
51 :
52 : // ── Computed values ──────────────────────────────────────────────────────────
53 :
54 33 : int OptionsPanelLayout::contentHeight() const {
55 : // Walk sections to compute total height
56 33 : int y = kFirstSectionY;
57 165 : for (int s = 0; s < SectionCount; ++s) {
58 132 : y += kHeaderH + kSectionGap; // header + gap before next section
59 132 : if (!folded_[s]) {
60 99 : switch (static_cast<Section>(s)) {
61 20 : case MidiRouting: y += kMidiRoutingContentH; break;
62 24 : case Video: y += kVideoContentH; break;
63 27 : case Circles: y += kCirclesContentH; break;
64 28 : case Animation: y += kAnimationContentH; break;
65 0 : default: break;
66 : }
67 99 : }
68 132 : }
69 33 : y += kButtonsH; // save/load buttons always visible
70 33 : return y;
71 : }
72 :
73 30 : int OptionsPanelLayout::maxScrollOffset() const {
74 30 : return std::max(0, contentHeight() - viewportHeight_);
75 : }
76 :
77 : // ── Y positions ──────────────────────────────────────────────────────────────
78 :
79 28 : int OptionsPanelLayout::sectionHeaderY(Section s) const {
80 28 : int y = kFirstSectionY;
81 58 : for (int i = 0; i < s; ++i) {
82 30 : y += kHeaderH + kSectionGap;
83 30 : if (!folded_[i]) {
84 26 : switch (static_cast<Section>(i)) {
85 15 : case MidiRouting: y += kMidiRoutingContentH; break;
86 8 : case Video: y += kVideoContentH; break;
87 3 : case Circles: y += kCirclesContentH; break;
88 0 : case Animation: y += kAnimationContentH; break;
89 0 : default: break;
90 : }
91 26 : }
92 30 : }
93 28 : return y;
94 : }
95 :
96 2 : int OptionsPanelLayout::sectionContentY(Section s) const {
97 2 : return sectionHeaderY(s) + kHeaderH;
98 : }
99 :
100 : // MIDI ROUTING content: voice rows start 2px below header bottom (88 = 66 + 22)
101 2 : int OptionsPanelLayout::midiRoutingFirstRowY() const {
102 2 : return sectionContentY(MidiRouting) + 2;
103 : }
104 :
105 : // VIDEO content offsets (relative to VIDEO header Y)
106 : // Transport buttons (play/pause, stop, loop, eye, blur) on first line.
107 0 : int OptionsPanelLayout::videoTransportY() const {
108 0 : return sectionHeaderY(Video) + 18;
109 : }
110 :
111 : // Seekbar on its own line below transport buttons.
112 0 : int OptionsPanelLayout::videoSeekBarY() const {
113 0 : return sectionHeaderY(Video) + 46;
114 : }
115 :
116 0 : int OptionsPanelLayout::videoTimeLabelY() const {
117 0 : return sectionHeaderY(Video) + 96;
118 : }
119 :
120 0 : int OptionsPanelLayout::videoCtrlY() const {
121 0 : return sectionHeaderY(Video) + 114;
122 : }
123 :
124 0 : int OptionsPanelLayout::videoFilesY() const {
125 0 : return videoCtrlY() + 134;
126 : }
127 :
128 0 : int OptionsPanelLayout::videoFileListTopY() const {
129 0 : return videoFilesY() + 16;
130 : }
131 :
132 : // CIRCLES content offsets (relative to CIRCLES header Y)
133 0 : int OptionsPanelLayout::circlesSizeRangeY() const {
134 0 : return sectionHeaderY(Circles) + 18;
135 : }
136 :
137 0 : int OptionsPanelLayout::circlesSliderY() const {
138 0 : return sectionHeaderY(Circles) + 34;
139 : }
140 :
141 0 : int OptionsPanelLayout::circlesHandleLabelsY() const {
142 0 : return sectionHeaderY(Circles) + 66;
143 : }
144 :
145 : // ANIMATION content offsets (relative to ANIMATION header Y)
146 0 : int OptionsPanelLayout::animFloatToggleY() const {
147 0 : return sectionHeaderY(Animation) + 20;
148 : }
149 :
150 0 : int OptionsPanelLayout::animCollisionToggleY() const {
151 0 : return sectionHeaderY(Animation) + 46;
152 : }
153 :
154 0 : int OptionsPanelLayout::animClockToggleY() const {
155 0 : return sectionHeaderY(Animation) + 72;
156 : }
157 :
158 0 : int OptionsPanelLayout::animClockDivY() const {
159 0 : return sectionHeaderY(Animation) + 118;
160 : }
161 :
162 0 : int OptionsPanelLayout::animClockKickSliderY() const {
163 0 : return sectionHeaderY(Animation) + 168;
164 : }
165 :
166 0 : int OptionsPanelLayout::animFloatIntensitySliderY() const {
167 0 : return sectionHeaderY(Animation) + 218;
168 : }
169 :
170 0 : int OptionsPanelLayout::animFloatSpeedSliderY() const {
171 0 : return sectionHeaderY(Animation) + 268;
172 : }
173 :
174 2 : int OptionsPanelLayout::buttonsY() const {
175 2 : int y = kFirstSectionY;
176 10 : for (int i = 0; i < SectionCount; ++i) {
177 8 : y += kHeaderH + kSectionGap;
178 8 : if (!folded_[i]) {
179 7 : switch (static_cast<Section>(i)) {
180 2 : case MidiRouting: y += kMidiRoutingContentH; break;
181 2 : case Video: y += kVideoContentH; break;
182 2 : case Circles: y += kCirclesContentH; break;
183 1 : case Animation: y += kAnimationContentH; break;
184 0 : default: break;
185 : }
186 7 : }
187 8 : }
188 2 : return y;
189 : }
190 :
191 : // ── Hit-test ─────────────────────────────────────────────────────────────────
192 :
193 6 : OptionsPanelLayout::Section OptionsPanelLayout::hitTestHeader(int contentY) const {
194 16 : for (int s = 0; s < SectionCount; ++s) {
195 14 : const int hy = sectionHeaderY(static_cast<Section>(s));
196 14 : if (contentY >= hy && contentY < hy + kHeaderH)
197 4 : return static_cast<Section>(s);
198 10 : }
199 2 : return SectionCount;
200 6 : }
201 :
202 : // ── Internal ─────────────────────────────────────────────────────────────────
203 :
204 24 : void OptionsPanelLayout::clampScroll() {
205 24 : const int max = maxScrollOffset();
206 24 : if (scrollOffset_ < 0) scrollOffset_ = 0;
207 24 : if (scrollOffset_ > max) scrollOffset_ = max;
208 24 : }
|