L1: shared LICE drawing kit — theme/palette + component geometry + draw kit; retire GDI text in bank_panel

This commit is contained in:
2026-07-26 20:04:04 -04:00
parent ec05af35ef
commit beca6487c7
10 changed files with 1394 additions and 42 deletions
+103
View File
@@ -0,0 +1,103 @@
// 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<int>(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<double>(px - trackStart) / static_cast<double>(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