diff --git a/src/desktoplib/terminal/ui/InnerScroll.cpp b/src/desktoplib/terminal/ui/InnerScroll.cpp index 8c15b8b..b48d4ca 100644 --- a/src/desktoplib/terminal/ui/InnerScroll.cpp +++ b/src/desktoplib/terminal/ui/InnerScroll.cpp @@ -5,53 +5,64 @@ namespace ckitty::terminal { InnerScroll::InnerScroll(pos p, int w, int h, Content& c) - : position(p), width(w), height(h), _content(c) { + : position(p), width(w), height(h), rowY(0), scrollY(0), _content(c) { } void InnerScroll::scroll(int delta) { - int maxScroll = std::max(0, _content.getTotalHeight() - height); - scrollY = std::clamp(scrollY + delta, 0, maxScroll); + int totalH = _content.getTotalHeight(); + int viewH = height; + if (totalH == 0) return; + + // 1. Update and clamp the cursor position within absolute content bounds + rowY = std::clamp(rowY + delta, 0, std::max(0, totalH - 1)); + + // 2. Adjust scrollY to keep rowY visible + // Case A: Cursor moved above the top of the viewport + if (rowY < scrollY) { + scrollY = rowY; + } + // Case B: Cursor moved below the bottom of the viewport + else if (rowY >= scrollY + viewH) { + scrollY = rowY - viewH + 1; + } + + // 3. Final safety clamp for scrollY + int maxScroll = std::max(0, totalH - viewH); + scrollY = std::clamp(scrollY, 0, maxScroll); } void InnerScroll::render(Terminal& t) const { int totalH = _content.getTotalHeight(); int viewH = height; int contentWidth = width - 1; // Last column reserved for scrollbar + int scrollX = position.x + width - 1; - // 1. Render the Content Viewport - // We only ask content to draw what fits in our height + // draw content int start = scrollY; int end = std::min(totalH, scrollY + viewH); - _content.render(t, position, pos(contentWidth, height), { start, end }); - // 2. Clear empty space if content is shorter than viewport - if (end - start < viewH) { - for (int y = (end - start); y < viewH; ++y) { - t << pos{ position.x, position.y + y } - << std::string(std::size_t(contentWidth), ' '); - } - } - - // 3. Render Scrollbar Track - int scrollX = position.x + width - 1; - t << trackColor; + // 3. Render Scrollbar Track First + t << style::RESET << trackColor; for (int y = 0; y < viewH; ++y) { - t << pos{ scrollX, position.y + y } << "░"; + t << pos(scrollX, position.y + y) << "░"; } - // 4. Render Scrollbar Thumb + // 4. Render Scrollbar Thumb Over the Track if (totalH > viewH) { // Calculate thumb height (proportional to visibility) int thumbH = std::max(1, (viewH * viewH) / totalH); - // Calculate thumb position + // Calculate thumb position safely float scrollPercent = float(scrollY) / float(totalH - viewH); int thumbPos = int(scrollPercent * float(viewH - thumbH)); + // Clamp thumbPos to ensure it stays within the track bounds + thumbPos = std::clamp(thumbPos, 0, viewH - thumbH); + t << thumbColor; for (int y = 0; y < thumbH; ++y) { - t << pos{ scrollX, position.y + thumbPos + y } << "█"; + t << pos(scrollX, position.y + thumbPos + y) << "█"; } } diff --git a/src/desktoplib/terminal/ui/InnerScroll.hpp b/src/desktoplib/terminal/ui/InnerScroll.hpp index 935dc7b..905c873 100644 --- a/src/desktoplib/terminal/ui/InnerScroll.hpp +++ b/src/desktoplib/terminal/ui/InnerScroll.hpp @@ -12,7 +12,7 @@ namespace ckitty::terminal { pos position; int width, height; - int scrollY = 0; // The top-most visible row of the content + int rowY, scrollY; // Styling color trackColor = color::B_BLACK;