// component_geometry — pure implementation. See component_geometry.h. #include "core/ui/component_geometry.h" namespace reasampler::ui { bool hitTestBox(int px, int py, const KitBox& box) { if (box.empty()) return false; return px >= box.x && px < box.x + box.width && py >= box.y && py < box.y + box.height; } KitButtonBox computeButtonBox(const KitBox& cell, int padding) { if (cell.empty()) return {}; if (padding < 0) padding = 0; KitBox b; b.x = cell.x + padding; b.y = cell.y + padding; b.width = cell.width - 2 * padding; b.height = cell.height - 2 * padding; if (b.empty()) return {}; return KitButtonBox{b}; } SliderGeometry computeSlider(const KitBox& control, double value, int handleSize, int trackThickness) { if (control.empty() || handleSize <= 0 || trackThickness <= 0) return {}; if (control.width < handleSize || control.height < handleSize) return {}; if (value < 0.0) value = 0.0; if (value > 1.0) value = 1.0; const int half = handleSize / 2; KitBox track; track.x = control.x + half; track.width = control.width - handleSize; // travel span for the handle centre if (track.width < 0) track.width = 0; track.height = trackThickness; track.y = control.y + (control.height - trackThickness) / 2; const int centre = track.x + static_cast(value * track.width + 0.5); KitBox handle; handle.x = centre - half; handle.y = control.y + (control.height - handleSize) / 2; handle.width = handleSize; handle.height = handleSize; KitBox filled; filled.x = track.x; filled.y = track.y; filled.width = centre - track.x; if (filled.width < 0) filled.width = 0; filled.height = track.height; return SliderGeometry{track, filled, handle}; } double sliderValueAt(int px, const KitBox& control, int handleSize) { if (control.empty() || handleSize <= 0) return 0.0; if (control.width < handleSize) return 0.0; const int half = handleSize / 2; const int trackStart = control.x + half; const int trackSpan = control.width - handleSize; // matches computeSlider's travel if (trackSpan <= 0) return 0.0; if (px <= trackStart) return 0.0; if (px >= trackStart + trackSpan) return 1.0; return static_cast(px - trackStart) / static_cast(trackSpan); } ListRowBox computeListRow(const KitBox& list, int index, int rowHeight) { if (list.empty() || rowHeight <= 0 || index < 0) return {}; const int top = list.y + index * rowHeight; if (top >= list.y + list.height) return {}; KitBox b; b.x = list.x; b.y = top; b.width = list.width; b.height = rowHeight; return ListRowBox{index, b}; } int hitTestListRow(int px, int py, const KitBox& list, int rowHeight, int rowCount) { if (list.empty() || rowHeight <= 0 || rowCount <= 0) return -1; if (px < list.x || px >= list.x + list.width || py < list.y || py >= list.y + list.height) return -1; const int row = (py - list.y) / rowHeight; if (row < 0 || row >= rowCount) return -1; return row; } int waveformColumnCount(const KitBox& box) { const int w = box.width - 4; // fixed 2px inset each side (matches drawWaveform) return w > 0 ? w : 0; } WaveformBand waveformBand(int bandTop, int bandHeight) { WaveformBand b; b.top = bandTop; b.height = bandHeight; b.midY = bandTop + bandHeight / 2; // matches drawWaveform's 2px vertical breathing room; clamped at 0 so a degenerate band // (height <= 3, where this would otherwise go negative) can't flip a positive peak below // the zero line. const int rawHalfSpan = (bandHeight / 2) - 2; b.halfSpan = rawHalfSpan > 0 ? rawHalfSpan : 0; // lo/hi (waveformColumnSpan) are midY-height/2 .. midY+height/2-1 — asymmetric by one px for // even heights. Unreachable while |compressAmplitudeForDisplay| <= 1, so left as-is. return b; } WaveformColumnSpan waveformColumnSpan(const WaveformBand& band, double compressedMax, double compressedMin) { const int lo = band.top; const int hi = band.top + band.height - 1; WaveformColumnSpan s; s.top = band.midY - static_cast(compressedMax * band.halfSpan); s.bottom = band.midY - static_cast(compressedMin * band.halfSpan); s.topF = band.midY - compressedMax * band.halfSpan; s.bottomF = band.midY - compressedMin * band.halfSpan; if (s.top < lo) s.top = lo; if (s.bottom > hi) s.bottom = hi; if (s.topF < lo) s.topF = lo; if (s.bottomF > hi) s.bottomF = hi; return s; } } // namespace reasampler::ui