Repository: microsoft/terminal
File: src/renderer/atlas/AtlasEngine.cpp (C++)

Code before:
    }
    {
        _api.invalidatedRows.start = std::min(_api.invalidatedRows.start, _p.s->cellCount.y);
        _api.invalidatedRows.end = clamp(_api.invalidatedRows.end, _api.invalidatedRows.start, _p.s->cellCount.y);
    }
    {
        const auto limit = gsl::narrow_cast<i16>(_p.s->cellCount.y & 0x7fff);
        const auto offset = gsl::narrow_cast<i16>(clamp<int>(_api.scrollOffset, -limit, limit));
        const auto nothingInvalid = _api.invalidatedRows.start == _api.invalidatedRows.end;

        // Mark the newly scrolled in rows as invalidated
        if (offset < 0)
        {
            const u16 begRow = _p.s->cellCount.y + offset;
            _api.invalidatedRows.start = nothingInvalid ? begRow : std::min(_api.invalidatedRows.start, begRow);
            _api.invalidatedRows.end = _p.s->cellCount.y;
        }
        else
        {
            const u16 endRow = offset;
            _api.invalidatedRows.start = 0;
            _api.invalidatedRows.end = nothingInvalid ? endRow : std::max(_api.invalidatedRows.end, endRow);
        }
    }

    _api.dirtyRect = {
        0,
        _api.invalidatedRows.start,
        _p.s->cellCount.x,
        _api.invalidatedRows.end,
    };

    _p.dirtyRectInPx = {
        til::CoordTypeMax,
        til::CoordTypeMax,
        til::CoordTypeMin,
        til::CoordTypeMin,
    };
    _p.invalidatedRows = _api.invalidatedRows;
    _p.cursorRect = {};
    _p.scrollOffset = _api.scrollOffset;

    // This if condition serves 2 purposes:
    // * By setting top/bottom to the full height we ensure that we call Present() without
    //   any dirty rects and not Present1() on the first frame after the settings change.
    // * If the scrollOffset is so large that it scrolls the entire viewport, invalidatedRows will span
    //   the entire viewport as well. We need to set scrollOffset to 0 then, not just because scrolling
    //   the contents of the entire swap chain is redundant, but more importantly because the scroll rect
    //   is the subset of the contents that are being scrolled into. If you scroll the entire viewport
    //   then the scroll rect is empty, which Present1() will loudly complain about.
    if (_api.invalidatedRows == range<u16>{ 0, _p.s->cellCount.y })

Review comment (suggestion):
This moved up from down below (search for `_api.invalidatedRows.end`), because when we do this here, before doing any actual scrolling, we can more easily check for the `MarkAllAsDirty()` case below. That's because this moved code block calculates the range of `invalidatedRows` that would exist after scrolling. The `MarkAllAsDirty()` can then detect if everything's been scrolled off by just checking if the `invalidatedRows` range is the entire viewport.

Code after:
    }
    {
        _api.invalidatedRows.start = std::min(_api.invalidatedRows.start, _p.s->cellCount.y);
        _api.invalidatedRows.end = clamp(_api.invalidatedRows.end, _api.invalidatedRows.start, _p.s->cellCount.y);
    }
    {
        const auto limit = gsl::narrow_cast<i16>(_p.s->cellCount.y & 0x7fff);
        const auto offset = gsl::narrow_cast<i16>(clamp<int>(_api.scrollOffset, -limit, limit));
        const auto nothingInvalid = _api.invalidatedRows.start == _api.invalidatedRows.end;

        _api.scrollOffset = offset;

        // Mark the newly scrolled in rows as invalidated
        if (offset < 0)
        {
            const u16 begRow = _p.s->cellCount.y + offset;
            _api.invalidatedRows.start = nothingInvalid ? begRow : std::min(_api.invalidatedRows.start, begRow);
            _api.invalidatedRows.end = _p.s->cellCount.y;
        }
        else
        {
            const u16 endRow = offset;
            _api.invalidatedRows.start = 0;
            _api.invalidatedRows.end = nothingInvalid ? endRow : std::max(_api.invalidatedRows.end, endRow);
        }
    }

    _api.dirtyRect = {
        0,
        _api.invalidatedRows.start,
        _p.s->cellCount.x,
        _api.invalidatedRows.end,
    };

    _p.dirtyRectInPx = {
        til::CoordTypeMax,
        til::CoordTypeMax,
        til::CoordTypeMin,
        til::CoordTypeMin,
    };
    _p.invalidatedRows = _api.invalidatedRows;
    _p.cursorRect = {};
    _p.scrollOffset = _api.scrollOffset;

    // This if condition serves 2 purposes:
    // * By setting top/bottom to the full height we ensure that we call Present() without
    //   any dirty rects and not Present1() on the first frame after the settings change.
    // * If the scrollOffset is so large that it scrolls the entire viewport, invalidatedRows will span
    //   the entire viewport as well. We need to set scrollOffset to 0 then, not just because scrolling
    //   the contents of the entire swap chain is redundant, but more importantly because the scroll rect
    //   is the subset of the contents that are being scrolled into. If you scroll the entire viewport