104 lines
3.6 KiB
C++
104 lines
3.6 KiB
C++
// tab_strip — pure implementation. See tab_strip.h.
|
|
|
|
#include "core/ui/tab_strip.h"
|
|
|
|
#include <cstddef>
|
|
|
|
namespace reasampler::ui {
|
|
|
|
TabStripLayout computeTabStripLayout(const TabStripRect& strip, int tabCount,
|
|
const TabStripSpec& spec, int scrollOffset) {
|
|
(void)scrollOffset; // layout depends on geometry only
|
|
TabStripLayout out;
|
|
if (tabCount <= 0 || strip.width <= 0) {
|
|
out.trackX = strip.x;
|
|
out.trackWidth = strip.width > 0 ? strip.width : 0;
|
|
return out;
|
|
}
|
|
|
|
const int totalTabsWidth = tabCount * spec.tabWidth;
|
|
if (totalTabsWidth <= strip.width) {
|
|
out.overflow = false;
|
|
out.trackX = strip.x;
|
|
out.trackWidth = strip.width;
|
|
out.maxScroll = 0;
|
|
return out;
|
|
}
|
|
|
|
out.overflow = true;
|
|
out.leftChevron = true;
|
|
out.rightChevron = true;
|
|
out.trackX = strip.x + spec.chevronWidth;
|
|
out.trackWidth = strip.width - 2 * spec.chevronWidth;
|
|
if (out.trackWidth < 0) out.trackWidth = 0;
|
|
out.maxScroll = totalTabsWidth - out.trackWidth;
|
|
if (out.maxScroll < 0) out.maxScroll = 0;
|
|
return out;
|
|
}
|
|
|
|
int clampTabScroll(int desiredOffset, const TabStripLayout& layout) {
|
|
if (desiredOffset < 0) return 0;
|
|
if (desiredOffset > layout.maxScroll) return layout.maxScroll;
|
|
return desiredOffset;
|
|
}
|
|
|
|
std::vector<TabRect> computeTabRects(const TabStripRect& strip, int tabCount,
|
|
const TabStripSpec& spec, int scrollOffset) {
|
|
std::vector<TabRect> rects;
|
|
if (tabCount <= 0 || strip.width <= 0) return rects;
|
|
|
|
const TabStripLayout layout =
|
|
computeTabStripLayout(strip, tabCount, spec, scrollOffset);
|
|
const int offset = layout.overflow ? clampTabScroll(scrollOffset, layout) : 0;
|
|
const int trackLeft = layout.trackX;
|
|
const int trackRight = layout.trackX + layout.trackWidth;
|
|
|
|
rects.reserve(static_cast<std::size_t>(tabCount));
|
|
for (int i = 0; i < tabCount; ++i) {
|
|
const int rawLeft = trackLeft + i * spec.tabWidth - offset;
|
|
const int rawRight = rawLeft + spec.tabWidth;
|
|
int left = rawLeft < trackLeft ? trackLeft : rawLeft;
|
|
int right = rawRight > trackRight ? trackRight : rawRight;
|
|
if (right <= left) continue; // fully scrolled out of view
|
|
TabRect r;
|
|
r.index = i;
|
|
r.x = left;
|
|
r.y = strip.y;
|
|
r.width = right - left;
|
|
r.height = strip.height;
|
|
rects.push_back(r);
|
|
}
|
|
return rects;
|
|
}
|
|
|
|
TabHit hitTestTabStrip(int px, int py, const TabStripRect& strip, int tabCount,
|
|
const TabStripSpec& spec, int scrollOffset) {
|
|
TabHit miss;
|
|
if (tabCount <= 0 || strip.width <= 0 || strip.height <= 0) return miss;
|
|
|
|
if (px < strip.x || px >= strip.x + strip.width ||
|
|
py < strip.y || py >= strip.y + strip.height)
|
|
return miss;
|
|
|
|
const TabStripLayout layout =
|
|
computeTabStripLayout(strip, tabCount, spec, scrollOffset);
|
|
|
|
// Chevron bands take precedence at the strip ends over any tab.
|
|
if (layout.overflow) {
|
|
if (px < strip.x + spec.chevronWidth)
|
|
return TabHit{TabHitKind::ScrollLeft, -1};
|
|
if (px >= strip.x + strip.width - spec.chevronWidth)
|
|
return TabHit{TabHitKind::ScrollRight, -1};
|
|
}
|
|
|
|
// Reuse computeTabRects so the hit matches exactly what was drawn (clipping included).
|
|
const std::vector<TabRect> rects =
|
|
computeTabRects(strip, tabCount, spec, scrollOffset);
|
|
for (const TabRect& r : rects) {
|
|
if (px >= r.x && px < r.x + r.width) return TabHit{TabHitKind::Tab, r.index};
|
|
}
|
|
return miss;
|
|
}
|
|
|
|
} // namespace reasampler::ui
|