// component_geometry — pure implementation. See component_geometry.h. NO REAPER / SWELL / // LICE / vendor. Standard library only. #include "component_geometry.h" namespace reasampler { 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 {}; // padding collapsed the cell -> suppress return KitButtonBox{b}; } SliderGeometry computeSlider(const KitBox& control, double value, int handleSize, int trackThickness) { if (control.empty() || handleSize <= 0 || trackThickness <= 0) return {}; // The handle must fit in both axes; too small -> nothing sensible to draw. 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; // Track: horizontally inset by half the handle at each end so the handle's centre // travels only within the control; vertically centred at trackThickness. 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; // Handle centre travels [track.x, track.x + track.width]; its box is centred on that. 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; // Filled portion: from the track's left up to the handle centre. 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; // Fully below the list bottom -> clipped away entirely -> no box. if (top >= list.y + list.height) return {}; KitBox b; b.x = list.x; b.y = top; b.width = list.width; b.height = rowHeight; // a partially-visible last row keeps full height; caller clips 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; // Outside the list band entirely. 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; // in the empty tail past the last row return row; } } // namespace reasampler