Break a huge amount of the console
Writing CHAR_INFO doesn't work RowImage is half-baked Row.hpp contains eight reimplementations of the same slgotihm move shit around RowImage.split is fucking madness look for TODO(DH) everywhere however, termbench performance doubles for non-colored text.
This commit is contained in:
parent
d2f0f50651
commit
5bdb5e6caa
|
@ -31,6 +31,8 @@ public:
|
|||
using const_iterator = rle_vector::const_iterator;
|
||||
|
||||
ATTR_ROW(uint16_t width, TextAttribute attr);
|
||||
ATTR_ROW(rle_vector&& v) :
|
||||
_data(std::move(v)) {}
|
||||
|
||||
~ATTR_ROW() = default;
|
||||
|
||||
|
@ -57,11 +59,11 @@ public:
|
|||
friend bool operator==(const ATTR_ROW& a, const ATTR_ROW& b) noexcept;
|
||||
friend class ROW;
|
||||
|
||||
rle_vector _data;
|
||||
|
||||
private:
|
||||
void Reset(const TextAttribute attr);
|
||||
|
||||
rle_vector _data;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class CommonState;
|
||||
#endif
|
||||
|
|
|
@ -42,19 +42,6 @@ gsl::span<OutputCell> OutputCellRect::GetRow(const size_t row)
|
|||
return gsl::span<OutputCell>(_FindRowOffset(row), _cols);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Gets a read-only iterator view over a single row of the rectangle.
|
||||
// Arguments:
|
||||
// - row - The Y position or row index in the buffer.
|
||||
// Return Value:
|
||||
// - Read-only iterator of OutputCells
|
||||
OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const
|
||||
{
|
||||
const gsl::span<const OutputCell> view(_FindRowOffset(row), _cols);
|
||||
|
||||
return OutputCellIterator(view);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Internal helper to find the pointer to the specific row offset in the giant
|
||||
// contiguous block of memory allocated for this rectangle.
|
||||
|
|
|
@ -21,10 +21,8 @@ Revision History:
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "DbcsAttribute.hpp"
|
||||
#include "TextAttribute.hpp"
|
||||
#include "OutputCell.hpp"
|
||||
#include "OutputCellIterator.hpp"
|
||||
|
||||
class OutputCellRect final
|
||||
{
|
||||
|
@ -33,7 +31,6 @@ public:
|
|||
OutputCellRect(const size_t rows, const size_t cols);
|
||||
|
||||
gsl::span<OutputCell> GetRow(const size_t row);
|
||||
OutputCellIterator GetRowIter(const size_t row) const;
|
||||
|
||||
size_t Height() const noexcept;
|
||||
size_t Width() const noexcept;
|
||||
|
|
|
@ -91,6 +91,8 @@ void ROW::ClearColumn(const size_t column)
|
|||
WriteGlyphAtMeasured(column, 1, L" ");
|
||||
}
|
||||
|
||||
#if 0
|
||||
TODO (DH)
|
||||
// Routine Description:
|
||||
// - writes cell data to the row
|
||||
// Arguments:
|
||||
|
@ -203,3 +205,4 @@ OutputCellIterator ROW::WriteCells(OutputCellIterator it, const size_t index, co
|
|||
|
||||
return it;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -23,13 +23,144 @@ Revision History:
|
|||
#include "AttrRow.hpp"
|
||||
#include "LineRendition.hpp"
|
||||
#include "OutputCell.hpp"
|
||||
#include "OutputCellIterator.hpp"
|
||||
#include "unicode.hpp"
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4267)
|
||||
class TextBuffer;
|
||||
|
||||
using RowMeasurementBuffer = til::small_rle<uint8_t, uint16_t, 3>;
|
||||
|
||||
struct DamageRanges
|
||||
{
|
||||
size_t dataOffset;
|
||||
size_t dataLength;
|
||||
uint16_t firstColumn;
|
||||
uint16_t lastColumnExclusive;
|
||||
};
|
||||
|
||||
template<typename TRuns>
|
||||
static DamageRanges DamageRangesForColumnInMeasurementBuffer(const TRuns& cwid, size_t col)
|
||||
{
|
||||
size_t currentCol{ 0 };
|
||||
size_t currentWchar{ 0 };
|
||||
auto it{ cwid.runs().cbegin() };
|
||||
while (it != cwid.runs().cend())
|
||||
{
|
||||
// Each compressed pair tells us how many columns x N wchar_t
|
||||
const auto colsCoveredByRun{ it->value * it->length };
|
||||
if (currentCol + colsCoveredByRun > col)
|
||||
{
|
||||
// We want to break out of the loop to manually handle this run, because
|
||||
// we've determined that it is the run that covers the column of interest.
|
||||
break;
|
||||
}
|
||||
currentCol += colsCoveredByRun;
|
||||
currentWchar += it->length;
|
||||
it++;
|
||||
}
|
||||
|
||||
if (it == cwid.runs().cend())
|
||||
{
|
||||
// this is an interesting case- somebody requested a column we cannot answer for.
|
||||
// The string might actually have data, and the caller might be interested in where that data is.
|
||||
// Ideally, we would return the index of the first char out-of-bounds, and the length of the remaining data as a single unit.
|
||||
// We can't answer for how much space it takes up, though.
|
||||
__debugbreak();
|
||||
return { 0, 0, 0u, 0u };
|
||||
//return { currentWchar, _data.size() - currentWchar, 0u, 0u };
|
||||
}
|
||||
// currentWchar is how many wchar_t we are into the string before processing this run
|
||||
// currentCol is how many columns we've covered before processing this run
|
||||
|
||||
// We are *guaranteed* that the hit is in this run -- no need to check it->length
|
||||
// col-currentCol is how many columns are left unaccounted for (how far into this run we need to go)
|
||||
const auto colsLeftToCountInCurrentRun{ col - currentCol };
|
||||
currentWchar += colsLeftToCountInCurrentRun / it->value; // one wch per column unit -- rounds down (correct behavior)
|
||||
|
||||
size_t lenInWchars{ 1 }; // the first hit takes up one wchar
|
||||
|
||||
// We use this to determine if we have exhausted every column this run can cough up.
|
||||
// colsLeftToCountInCurrentRun is 0-indexed, but colsConsumedByRun is 1-indexed (index 0 consumes N columns, etc.)
|
||||
// therefore, we reindex colsLeftToCountInCurrentRun and compare it to colsConsumedByRun
|
||||
const auto colsConsumedFromRun{ colsLeftToCountInCurrentRun + it->value };
|
||||
const auto colsCoveredByRun{ it->value * it->length };
|
||||
// If we *have* consumed every column this run can provide, we must check the run after it:
|
||||
// if it contributes "0" columns, it is actually a set of trailing code units.
|
||||
if (colsConsumedFromRun >= colsCoveredByRun && it != cwid.runs().cend())
|
||||
{
|
||||
const auto nextRunIt{ it + 1 };
|
||||
if (nextRunIt != cwid.runs().cend() && nextRunIt->value == 0)
|
||||
{
|
||||
// we were at the boundary of a column run, so if the next one is 0 it tells us that each
|
||||
// wchar after it is a trailer
|
||||
lenInWchars += nextRunIt->length;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentWchar, // wchar start
|
||||
lenInWchars, // wchar size
|
||||
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value)), // Column damage to the left (where we overlapped the right of a wide glyph)
|
||||
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value) + it->value), // Column damage to the right (where we overlapped the left of a wide glyph)
|
||||
};
|
||||
}
|
||||
|
||||
class ROW;
|
||||
struct RowImage
|
||||
{
|
||||
std::wstring _data;
|
||||
RowMeasurementBuffer _cwid;
|
||||
ATTR_ROW _attrRow;
|
||||
uint16_t _width;
|
||||
friend class ROW;
|
||||
friend class TextBuffer;
|
||||
RowImage() :
|
||||
_data{}, _cwid{}, _attrRow{ {} }, _width{ 0 } {}
|
||||
RowImage(const std::wstring& data, const RowMeasurementBuffer& cwid, ATTR_ROW attrRow, uint16_t width):
|
||||
_data{data}, _cwid{cwid}, _attrRow{std::move(attrRow)}, _width{width} {}
|
||||
|
||||
// exclusive
|
||||
std::tuple<RowImage, RowImage> split(uint16_t col) const
|
||||
{
|
||||
if (col >= _width)
|
||||
{
|
||||
return { *this, RowImage{} };
|
||||
}
|
||||
else if (col == 0)
|
||||
{
|
||||
return { RowImage{}, *this };
|
||||
}
|
||||
auto yes_more_fucking_damage_ranges = DamageRangesForColumnInMeasurementBuffer(_cwid, col);
|
||||
// here's a dumb decision: when you split along a wide char, you get spaces over its damage on the left side.
|
||||
// X X XX X X X
|
||||
// ^ split
|
||||
// X X S <- left
|
||||
// XX X X X <- right
|
||||
auto chopped_off = col == yes_more_fucking_damage_ranges.lastColumnExclusive ? 0 : /*didn't get whole thing*/ yes_more_fucking_damage_ranges.lastColumnExclusive - col;
|
||||
auto chopped_on = col == yes_more_fucking_damage_ranges.lastColumnExclusive ? yes_more_fucking_damage_ranges.dataLength : 0;
|
||||
auto lwid = _cwid.slice(0, yes_more_fucking_damage_ranges.dataOffset + chopped_on);
|
||||
for (auto z{ chopped_off }; z > 0; --z)
|
||||
{
|
||||
lwid.append(1);
|
||||
}
|
||||
RowImage left{
|
||||
_data.substr(0, yes_more_fucking_damage_ranges.dataOffset + chopped_on) + std::wstring(chopped_off, UNICODE_SPACE),
|
||||
lwid,
|
||||
_attrRow._data.slice(0, yes_more_fucking_damage_ranges.lastColumnExclusive),
|
||||
col
|
||||
};
|
||||
RowImage right{
|
||||
_data.substr(yes_more_fucking_damage_ranges.dataOffset + chopped_on),
|
||||
_cwid.slice(yes_more_fucking_damage_ranges.dataOffset + chopped_on, _data.length()),
|
||||
// we use first col here to catch the overlap
|
||||
_attrRow._data.slice(yes_more_fucking_damage_ranges.firstColumn, _width),
|
||||
::base::ClampSub(_width, col),
|
||||
};
|
||||
return { left, right };
|
||||
}
|
||||
};
|
||||
|
||||
enum class DelimiterClass
|
||||
{
|
||||
ControlChar,
|
||||
|
@ -62,92 +193,15 @@ public:
|
|||
void ClearColumn(const size_t column);
|
||||
std::wstring GetText() const { return _data._data; }
|
||||
|
||||
OutputCellIterator WriteCells(OutputCellIterator it, const size_t index, const std::optional<bool> wrap = std::nullopt, std::optional<size_t> limitRight = std::nullopt);
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend constexpr bool operator==(const ROW& a, const ROW& b) noexcept;
|
||||
friend class RowTests;
|
||||
#endif
|
||||
|
||||
struct DamageRanges
|
||||
{
|
||||
size_t dataOffset;
|
||||
size_t dataLength;
|
||||
uint16_t firstColumn;
|
||||
uint16_t lastColumnExclusive;
|
||||
};
|
||||
|
||||
template<typename TRuns>
|
||||
static DamageRanges DamageRangesForColumnInMeasurementBuffer(const TRuns& cwid, size_t col)
|
||||
{
|
||||
size_t currentCol{ 0 };
|
||||
size_t currentWchar{ 0 };
|
||||
auto it{ cwid.runs().cbegin() };
|
||||
while (it != cwid.runs().cend())
|
||||
{
|
||||
// Each compressed pair tells us how many columns x N wchar_t
|
||||
const auto colsCoveredByRun{ it->value * it->length };
|
||||
if (currentCol + colsCoveredByRun > col)
|
||||
{
|
||||
// We want to break out of the loop to manually handle this run, because
|
||||
// we've determined that it is the run that covers the column of interest.
|
||||
break;
|
||||
}
|
||||
currentCol += colsCoveredByRun;
|
||||
currentWchar += it->length;
|
||||
it++;
|
||||
}
|
||||
|
||||
if (it == cwid.runs().cend())
|
||||
{
|
||||
// this is an interesting case- somebody requested a column we cannot answer for.
|
||||
// The string might actually have data, and the caller might be interested in where that data is.
|
||||
// Ideally, we would return the index of the first char out-of-bounds, and the length of the remaining data as a single unit.
|
||||
// We can't answer for how much space it takes up, though.
|
||||
__debugbreak();
|
||||
return { 0, 0, 0u, 0u };
|
||||
//return { currentWchar, _data.size() - currentWchar, 0u, 0u };
|
||||
}
|
||||
// currentWchar is how many wchar_t we are into the string before processing this run
|
||||
// currentCol is how many columns we've covered before processing this run
|
||||
|
||||
// We are *guaranteed* that the hit is in this run -- no need to check it->length
|
||||
// col-currentCol is how many columns are left unaccounted for (how far into this run we need to go)
|
||||
const auto colsLeftToCountInCurrentRun{ col - currentCol };
|
||||
currentWchar += colsLeftToCountInCurrentRun / it->value; // one wch per column unit -- rounds down (correct behavior)
|
||||
|
||||
size_t lenInWchars{ 1 }; // the first hit takes up one wchar
|
||||
|
||||
// We use this to determine if we have exhausted every column this run can cough up.
|
||||
// colsLeftToCountInCurrentRun is 0-indexed, but colsConsumedByRun is 1-indexed (index 0 consumes N columns, etc.)
|
||||
// therefore, we reindex colsLeftToCountInCurrentRun and compare it to colsConsumedByRun
|
||||
const auto colsConsumedFromRun{ colsLeftToCountInCurrentRun + it->value };
|
||||
const auto colsCoveredByRun{ it->value * it->length };
|
||||
// If we *have* consumed every column this run can provide, we must check the run after it:
|
||||
// if it contributes "0" columns, it is actually a set of trailing code units.
|
||||
if (colsConsumedFromRun >= colsCoveredByRun && it != cwid.runs().cend())
|
||||
{
|
||||
const auto nextRunIt{ it + 1 };
|
||||
if (nextRunIt != cwid.runs().cend() && nextRunIt->value == 0)
|
||||
{
|
||||
// we were at the boundary of a column run, so if the next one is 0 it tells us that each
|
||||
// wchar after it is a trailer
|
||||
lenInWchars += nextRunIt->length;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentWchar, // wchar start
|
||||
lenInWchars, // wchar size
|
||||
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value)), // Column damage to the left (where we overlapped the right of a wide glyph)
|
||||
gsl::narrow_cast<uint16_t>(col - (colsLeftToCountInCurrentRun % it->value) + it->value), // Column damage to the right (where we overlapped the left of a wide glyph)
|
||||
};
|
||||
}
|
||||
|
||||
struct RowData
|
||||
{
|
||||
std::wstring _data;
|
||||
til::small_rle<uint8_t, uint16_t, 3> _cwid;
|
||||
RowMeasurementBuffer _cwid;
|
||||
ATTR_ROW _attrRow;
|
||||
unsigned short _width;
|
||||
|
||||
|
@ -184,8 +238,9 @@ public:
|
|||
return damage;
|
||||
}
|
||||
|
||||
void _strikeDamageAndAdjust(size_t col, size_t ncols, DamageRanges& range)
|
||||
void _strikeDamageAndAdjust(size_t col, size_t ncols, size_t incomingCodeUnitCount, DamageRanges& range)
|
||||
{
|
||||
(void)incomingCodeUnitCount;
|
||||
const bool damaged{ range.firstColumn < col || col + ncols < range.lastColumnExclusive };
|
||||
if (damaged)
|
||||
{
|
||||
|
@ -295,7 +350,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
std::tuple<size_t, uint16_t, uint16_t> WriteStringAtMeasured(uint16_t col, uint16_t colCount, const std::wstring_view& string, const til::small_rle<uint8_t, uint16_t, 3>& measurements)
|
||||
std::tuple<size_t, uint16_t, uint16_t> WriteStringAtMeasured(uint16_t col, uint16_t colCount, const std::wstring_view& string, const RowMeasurementBuffer& measurements)
|
||||
{
|
||||
size_t incomingLastColumn{ std::min<size_t>(_data._width - col, colCount) };
|
||||
auto incomingLastColumnOffsets{ DamageRangesForColumnInMeasurementBuffer(measurements, incomingLastColumn - 1 /*inclusive*/) };
|
||||
|
@ -386,7 +441,7 @@ public:
|
|||
// If we are filling over the left/right halves of a character
|
||||
// This is a bit wasteful since it can grow/shrink the buffers and we're about
|
||||
// to do it again, but I was trying to be expedient.
|
||||
_data._strikeDamageAndAdjust(col, columnsRequired, damage);
|
||||
_data._strikeDamageAndAdjust(col, columnsRequired, charsFitOrRemain, damage);
|
||||
|
||||
const auto [begin, len, min, max] = damage;
|
||||
_data._data.replace(begin, len, charsFitOrRemain, ch);
|
||||
|
@ -440,6 +495,26 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
RowImage Dump(uint16_t left, uint16_t size)
|
||||
{
|
||||
auto [begin, len, min, max] = _data._damageForColumns(left, size);
|
||||
return RowImage{
|
||||
_data._data.substr(begin, len),
|
||||
_data._cwid.slice(begin, begin + len),
|
||||
ATTR_ROW{ _data._attrRow._data.slice(min, max) },
|
||||
::base::MakeClampedNum(max) - min
|
||||
};
|
||||
}
|
||||
|
||||
void Reinsert(uint16_t left, const RowImage& ri)
|
||||
{
|
||||
auto damage{ _data._damageForColumns(left, ri._width) };
|
||||
_data._strikeDamageAndAdjust(left, ri._width, ri._data.size(), damage);
|
||||
_data._data.replace(damage.dataOffset, damage.dataLength, ri._data);
|
||||
_data._cwid.replace(damage.dataOffset, damage.dataOffset + damage.dataLength, ri._cwid.runs());
|
||||
_data._attrRow._data.replace(damage.firstColumn, damage.lastColumnExclusive, ri._attrRow._data.runs());
|
||||
}
|
||||
|
||||
uint16_t _maxc{};
|
||||
size_t MeasureRight() const
|
||||
{
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
<ClCompile Include="..\AttrRow.cpp" />
|
||||
<ClCompile Include="..\cursor.cpp" />
|
||||
<ClCompile Include="..\OutputCell.cpp" />
|
||||
<ClCompile Include="..\OutputCellIterator.cpp" />
|
||||
<ClCompile Include="..\OutputCellRect.cpp" />
|
||||
<ClCompile Include="..\OutputCellView.cpp" />
|
||||
<ClCompile Include="..\Row.cpp" />
|
||||
|
@ -33,7 +32,6 @@
|
|||
<ClInclude Include="..\DbcsAttribute.hpp" />
|
||||
<ClInclude Include="..\LineRendition.hpp" />
|
||||
<ClInclude Include="..\OutputCell.hpp" />
|
||||
<ClInclude Include="..\OutputCellIterator.hpp" />
|
||||
<ClInclude Include="..\OutputCellRect.hpp" />
|
||||
<ClInclude Include="..\OutputCellView.hpp" />
|
||||
<ClInclude Include="..\Row.hpp" />
|
||||
|
|
|
@ -183,90 +183,6 @@ TextBufferCellIterator TextBuffer::GetCellDataAt(const COORD at, const Viewport
|
|||
return TextBufferCellIterator(*this, at, limit);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Writes cells to the output buffer. Writes at the cursor.
|
||||
// Arguments:
|
||||
// - givenIt - Iterator representing output cell data to write
|
||||
// Return Value:
|
||||
// - The final position of the iterator
|
||||
OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt)
|
||||
{
|
||||
const auto& cursor = GetCursor();
|
||||
const auto target = cursor.GetPosition();
|
||||
|
||||
const auto finalIt = Write(givenIt, target);
|
||||
|
||||
return finalIt;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Writes cells to the output buffer.
|
||||
// Arguments:
|
||||
// - givenIt - Iterator representing output cell data to write
|
||||
// - target - the row/column to start writing the text to
|
||||
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data
|
||||
// Return Value:
|
||||
// - The final position of the iterator
|
||||
OutputCellIterator TextBuffer::Write(const OutputCellIterator givenIt,
|
||||
const COORD target,
|
||||
const std::optional<bool> wrap)
|
||||
{
|
||||
// Make mutable copy so we can walk.
|
||||
auto it = givenIt;
|
||||
|
||||
// Make mutable target so we can walk down lines.
|
||||
auto lineTarget = target;
|
||||
|
||||
// Get size of the text buffer so we can stay in bounds.
|
||||
const auto size = GetSize();
|
||||
|
||||
// While there's still data in the iterator and we're still targeting in bounds...
|
||||
while (it && size.IsInBounds(lineTarget))
|
||||
{
|
||||
// Attempt to write as much data as possible onto this line.
|
||||
// NOTE: if wrap = true/false, we want to set the line's wrap to true/false (respectively) if we reach the end of the line
|
||||
it = WriteLine(it, lineTarget, wrap);
|
||||
|
||||
// Move to the next line down.
|
||||
lineTarget.X = 0;
|
||||
++lineTarget.Y;
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Writes one line of text to the output buffer.
|
||||
// Arguments:
|
||||
// - givenIt - The iterator that will dereference into cell data to insert
|
||||
// - target - Coordinate targeted within output buffer
|
||||
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data in the iterator.
|
||||
// - limitRight - Optionally restrict the right boundary for writing (e.g. stop writing earlier than the end of line)
|
||||
// Return Value:
|
||||
// - The iterator, but advanced to where we stopped writing. Use to find input consumed length or cells written length.
|
||||
OutputCellIterator TextBuffer::WriteLine(const OutputCellIterator givenIt,
|
||||
const COORD target,
|
||||
const std::optional<bool> wrap,
|
||||
std::optional<size_t> limitRight)
|
||||
{
|
||||
// If we're not in bounds, exit early.
|
||||
if (!GetSize().IsInBounds(target))
|
||||
{
|
||||
return givenIt;
|
||||
}
|
||||
|
||||
// Get the row and write the cells
|
||||
ROW& row = GetRowByOffset(target.Y);
|
||||
const auto newIt = row.WriteCells(givenIt, target.X, wrap, limitRight);
|
||||
|
||||
// Take the cell distance written and notify that it needs to be repainted.
|
||||
const auto written = newIt.GetCellDistance(givenIt);
|
||||
const Viewport paint = Viewport::FromDimensions(target, { gsl::narrow<SHORT>(written), 1 });
|
||||
_NotifyPaint(paint);
|
||||
|
||||
return newIt;
|
||||
}
|
||||
|
||||
//Routine Description:
|
||||
// - Finds the current row in the buffer (as indicated by the cursor position)
|
||||
// and specifies that we have forced a line wrap on that row
|
||||
|
@ -629,7 +545,7 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition)
|
|||
fillAttrs.SetStandardErase();
|
||||
const size_t fillOffset = GetLineWidth(rowIndex);
|
||||
const size_t fillLength = GetSize().Width() - fillOffset;
|
||||
FillWithCharacterAndAttributeLinear(til::point{fillOffset, ::base::saturated_cast<size_t>(cursorPosition.Y)}, fillLength, UNICODE_SPACE, fillAttrs);
|
||||
FillWithCharacterAndAttributeLinear(til::point{ fillOffset, ::base::saturated_cast<size_t>(cursorPosition.Y) }, fillLength, UNICODE_SPACE, fillAttrs);
|
||||
// We also need to make sure the cursor is clamped within the new width.
|
||||
GetCursor().SetPosition(ClampPositionWithinLine(cursorPosition));
|
||||
}
|
||||
|
@ -1939,10 +1855,14 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer,
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
OutputCellIterator it{ oldBuffer.GetCellDataAt({ 0, iOldRow }, Viewport::FromDimensions({ 0, iOldRow }, iRight, 1)) };
|
||||
auto preCurPos{ newBuffer.GetCursor().GetPosition() };
|
||||
const auto last = newBuffer.Write(it, preCurPos, true); // true - wrap if we don't fit!
|
||||
const auto distance = last.GetCellDistance(it);
|
||||
#endif
|
||||
auto preCurPos{ newBuffer.GetCursor().GetPosition() };
|
||||
const auto distance = 5;
|
||||
|
||||
if (iOldRow == cOldCursorPos.Y)
|
||||
{
|
||||
|
@ -2318,17 +2238,18 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c
|
|||
return result;
|
||||
}
|
||||
|
||||
void TextBuffer::FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute)
|
||||
TextBuffer::WriteResult TextBuffer::FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute)
|
||||
{
|
||||
_WriteRectangular(
|
||||
return _WriteRectangular(
|
||||
region,
|
||||
[&](WriteResult&, auto&& at, auto&& size) {
|
||||
[&](WriteResult& result, auto&& at, auto&& size) {
|
||||
auto& row = GetRowByOffset(at.y());
|
||||
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attribute);
|
||||
result.columnsWritten += size;
|
||||
});
|
||||
}
|
||||
|
||||
size_t TextBuffer::FillWithAttributeLinear(til::point at, size_t count, const TextAttribute& attribute)
|
||||
TextBuffer::WriteResult TextBuffer::FillWithAttributeLinear(til::point at, size_t count, const TextAttribute& attribute)
|
||||
{
|
||||
auto out = _WriteLinear(
|
||||
at,
|
||||
|
@ -2338,20 +2259,20 @@ size_t TextBuffer::FillWithAttributeLinear(til::point at, size_t count, const Te
|
|||
auto& row = GetRowByOffset(at.y());
|
||||
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), size), attribute);
|
||||
|
||||
result.colsConsumed += w.RawValue();
|
||||
result.columnsWritten += w.RawValue();
|
||||
|
||||
const Viewport paint = Viewport::FromDimensions(at, { w.Cast<SHORT>(), 1 });
|
||||
_NotifyPaint(paint);
|
||||
|
||||
return (count -= w.RawValue()) > 0; // keep going if data remains
|
||||
});
|
||||
return out.colsConsumed;
|
||||
return out;
|
||||
}
|
||||
|
||||
// TODO(DH): make this a member so that IsGlyphFullWidth can be a member ref
|
||||
static std::tuple<til::small_rle<uint8_t, uint16_t, 3>, uint16_t> _MeasureStringAndCountColumns(std::wstring_view string) //const noexcept
|
||||
static std::tuple<RowMeasurementBuffer, uint16_t> _MeasureStringAndCountColumns(std::wstring_view string) //const noexcept
|
||||
{
|
||||
til::small_rle<uint8_t, uint16_t, 3> measurements;
|
||||
RowMeasurementBuffer measurements;
|
||||
uint16_t colCount{};
|
||||
while (!string.empty())
|
||||
{
|
||||
|
@ -2374,7 +2295,7 @@ struct no_attributes
|
|||
};
|
||||
|
||||
template<typename T>
|
||||
size_t TextBuffer::_WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, til::small_rle<uint8_t, uint16_t, 3> measurements, [[maybe_unused]] const T& attributes)
|
||||
TextBuffer::WriteResult TextBuffer::_WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, RowMeasurementBuffer measurements, [[maybe_unused]] const T& attributes)
|
||||
{
|
||||
uint16_t colCount = std::accumulate(
|
||||
measurements.runs().cbegin(),
|
||||
|
@ -2391,34 +2312,52 @@ size_t TextBuffer::_WriteMeasuredStringLinearWithAttributes(const til::point& at
|
|||
{
|
||||
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), consumedDestCols), attributes);
|
||||
}
|
||||
#if 0
|
||||
else if constexpr (std::is_same<T, ATTR_ROW::rle_vector>::value)
|
||||
{
|
||||
// user has passed us a packed representation of attributes
|
||||
// It is a programming error to pass one whose total length does
|
||||
// not match the measurement buffer
|
||||
const auto attrRunSlice{ attributes.slice(0, consumedSourceCols) };
|
||||
attrRunSlice.resize_trailing_extent(consumedDestCols); // this may be very wrong. TODO(DH)
|
||||
// intent: I believe that this will take the attribute from the cell before the one we had to cut off
|
||||
// and then extend it to cover our empty column?
|
||||
row.GetAttrRow()._data.replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), consumedSourceCols), attrRunSlice.runs());
|
||||
}
|
||||
#endif
|
||||
|
||||
const Viewport paint = Viewport::FromDimensions(at, { ::base::saturated_cast<SHORT>(consumedDestCols), 1 });
|
||||
_NotifyPaint(paint);
|
||||
colCount -= consumedSourceCols;
|
||||
|
||||
result.charsConsumed += consumedWchar;
|
||||
result.colsConsumed += consumedDestCols;
|
||||
result.charactersRead += consumedWchar;
|
||||
result.charactersWritten += consumedWchar;
|
||||
result.columnsWritten += consumedDestCols;
|
||||
result.columnsRead += consumedSourceCols;
|
||||
|
||||
// unchecked substr
|
||||
//string = std::wstring_view{ &*string.begin() + consumedWchar, string.size() - consumedWchar };
|
||||
//string = til::unchecked_substr(string, consumedWchar);
|
||||
string = string.substr(consumedWchar);
|
||||
measurements = measurements.slice(gsl::narrow_cast<uint16_t>(consumedWchar), measurements.size());
|
||||
|
||||
return !string.empty();
|
||||
});
|
||||
return out.colsConsumed;
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t TextBuffer::WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, til::small_rle<uint8_t, uint16_t, 3> measurements)
|
||||
TextBuffer::WriteResult TextBuffer::WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, RowMeasurementBuffer measurements)
|
||||
{
|
||||
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), _currentAttributes);
|
||||
}
|
||||
|
||||
size_t TextBuffer::WriteStringLinearWithAttributes(const til::point &at, const std::wstring_view& string, const TextAttribute& attr)
|
||||
TextBuffer::WriteResult TextBuffer::WriteStringLinearWithAttributes(const til::point& at, const std::wstring_view& string, const TextAttribute& attr)
|
||||
{
|
||||
auto [measurements, colCount] = _MeasureStringAndCountColumns(string);
|
||||
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), attr);
|
||||
}
|
||||
|
||||
size_t TextBuffer::WriteStringLinearKeepAttributes(const til::point &at, const std::wstring_view& string)
|
||||
TextBuffer::WriteResult TextBuffer::WriteStringLinearKeepAttributes(const til::point& at, const std::wstring_view& string)
|
||||
{
|
||||
auto [measurements, colCount] = _MeasureStringAndCountColumns(string);
|
||||
return _WriteMeasuredStringLinearWithAttributes(at, string, std::move(measurements), no_attributes{});
|
||||
|
@ -2443,7 +2382,7 @@ TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeRectangular(co
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const til::point &at, size_t count, const wchar_t character, [[maybe_unused]] const T& attr)
|
||||
TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, [[maybe_unused]] const T& attr)
|
||||
{
|
||||
const uint8_t width{ IsGlyphFullWidth(character) ? uint8_t{ 2u } : uint8_t{ 1u } };
|
||||
return _WriteLinear(
|
||||
|
@ -2459,8 +2398,8 @@ TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const t
|
|||
row.GetAttrRow().Replace(::base::saturated_cast<uint16_t>(at.x()), ::base::ClampAdd<uint16_t>(::base::saturated_cast<uint16_t>(at.x()), w), attr);
|
||||
}
|
||||
|
||||
result.charsConsumed += consumedWchars;
|
||||
result.colsConsumed += w.RawValue();
|
||||
result.charactersWritten += consumedWchars;
|
||||
result.columnsWritten += w.RawValue();
|
||||
|
||||
const Viewport paint = Viewport::FromDimensions(at, { w.Cast<SHORT>(), 1 });
|
||||
_NotifyPaint(paint);
|
||||
|
@ -2469,26 +2408,31 @@ TextBuffer::WriteResult TextBuffer::_FillWithCharacterAndAttributeLinear(const t
|
|||
});
|
||||
}
|
||||
|
||||
size_t TextBuffer::FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character)
|
||||
TextBuffer::WriteResult TextBuffer::FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character)
|
||||
{
|
||||
return _FillWithCharacterAndAttributeRectangular(region, character, no_attributes{}).colsConsumed;
|
||||
return _FillWithCharacterAndAttributeRectangular(region, character, no_attributes{});
|
||||
}
|
||||
|
||||
void TextBuffer::FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr)
|
||||
TextBuffer::WriteResult TextBuffer::FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr)
|
||||
{
|
||||
_FillWithCharacterAndAttributeRectangular(region, character, attr);
|
||||
return _FillWithCharacterAndAttributeRectangular(region, character, attr);
|
||||
}
|
||||
|
||||
size_t TextBuffer::FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character)
|
||||
TextBuffer::WriteResult TextBuffer::FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character)
|
||||
{
|
||||
const auto out = _FillWithCharacterAndAttributeLinear(at, count, character, no_attributes{});
|
||||
return out.charsConsumed;
|
||||
return _FillWithCharacterAndAttributeLinear(at, count, character, no_attributes{});
|
||||
}
|
||||
|
||||
size_t TextBuffer::FillWithCharacterAndAttributeLinear(const til::point &at, size_t count, const wchar_t character, const TextAttribute& attr)
|
||||
TextBuffer::WriteResult TextBuffer::FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const TextAttribute& attr)
|
||||
{
|
||||
const auto out = _FillWithCharacterAndAttributeLinear(at, count, character, attr);
|
||||
return out.charsConsumed;
|
||||
return _FillWithCharacterAndAttributeLinear(at, count, character, attr);
|
||||
}
|
||||
|
||||
TextBuffer::WriteResult TextBuffer::WriteRowImage(const til::point at, const RowImage& ri)
|
||||
{
|
||||
auto& row{ GetRowByOffset(at.y()) };
|
||||
row.Reinsert(at.x<uint16_t>(), ri);
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename TLambda>
|
||||
|
|
|
@ -85,17 +85,6 @@ public:
|
|||
TextBufferTextIterator GetTextDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
|
||||
|
||||
// Text insertion functions
|
||||
OutputCellIterator Write(const OutputCellIterator givenIt);
|
||||
|
||||
OutputCellIterator Write(const OutputCellIterator givenIt,
|
||||
const COORD target,
|
||||
const std::optional<bool> wrap = true);
|
||||
|
||||
OutputCellIterator WriteLine(const OutputCellIterator givenIt,
|
||||
const COORD target,
|
||||
const std::optional<bool> setWrap = std::nullopt,
|
||||
const std::optional<size_t> limitRight = std::nullopt);
|
||||
|
||||
bool IncrementCursor();
|
||||
bool NewlineCursor();
|
||||
|
||||
|
@ -196,23 +185,27 @@ public:
|
|||
|
||||
struct WriteResult
|
||||
{
|
||||
size_t charsConsumed;
|
||||
size_t colsConsumed;
|
||||
size_t charactersWritten;
|
||||
size_t charactersRead;
|
||||
size_t columnsWritten;
|
||||
size_t columnsRead;
|
||||
};
|
||||
|
||||
size_t WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, til::small_rle<uint8_t, uint16_t, 3> measurements);
|
||||
WriteResult WriteMeasuredStringLinear(const til::point& at, const std::wstring_view& string, RowMeasurementBuffer measurements);
|
||||
|
||||
size_t WriteStringLinearWithAttributes(const til::point& at, const std::wstring_view& string, const TextAttribute& attr);
|
||||
size_t WriteStringLinearKeepAttributes(const til::point& at, const std::wstring_view& string);
|
||||
WriteResult WriteStringLinearWithAttributes(const til::point& at, const std::wstring_view& string, const TextAttribute& attr);
|
||||
WriteResult WriteStringLinearKeepAttributes(const til::point& at, const std::wstring_view& string);
|
||||
|
||||
size_t FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character);
|
||||
size_t FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character);
|
||||
WriteResult FillWithCharacterRectangular(const til::rectangle& region, const wchar_t character);
|
||||
WriteResult FillWithCharacterLinear(const til::point& at, size_t count, const wchar_t character);
|
||||
|
||||
void FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr);
|
||||
size_t FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const TextAttribute& attr);
|
||||
WriteResult FillWithCharacterAndAttributeRectangular(const til::rectangle& region, const wchar_t character, const TextAttribute& attr);
|
||||
WriteResult FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const TextAttribute& attr);
|
||||
|
||||
void FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute);
|
||||
size_t FillWithAttributeLinear(const til::point at, size_t count, const TextAttribute& attribute);
|
||||
WriteResult FillWithAttributeRectangular(const til::rectangle& region, const TextAttribute& attribute);
|
||||
WriteResult FillWithAttributeLinear(const til::point at, size_t count, const TextAttribute& attribute);
|
||||
|
||||
WriteResult WriteRowImage(const til::point at, const RowImage& ri);
|
||||
|
||||
private:
|
||||
void _UpdateSize();
|
||||
|
@ -266,7 +259,7 @@ private:
|
|||
WriteResult _FillWithCharacterAndAttributeLinear(const til::point& at, size_t count, const wchar_t character, const T& attr);
|
||||
|
||||
template<typename T>
|
||||
size_t _WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, til::small_rle<uint8_t, uint16_t, 3> measurements, const T& attributes);
|
||||
WriteResult _WriteMeasuredStringLinearWithAttributes(const til::point& at, std::wstring_view string, RowMeasurementBuffer measurements, const T& attributes);
|
||||
|
||||
std::unordered_map<size_t, std::wstring> _idsAndPatterns;
|
||||
size_t _currentPatternId;
|
||||
|
|
|
@ -429,7 +429,7 @@ void CommandListPopup::_drawList()
|
|||
TextAttribute inverted = _attributes;
|
||||
inverted.Invert();
|
||||
|
||||
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{WriteCoord, til::size{ Width(), 1 }}, inverted);
|
||||
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{ WriteCoord, til::size{ Width(), 1 } }, inverted);
|
||||
}
|
||||
|
||||
WriteCoord.Y += 1;
|
||||
|
@ -526,11 +526,11 @@ void CommandListPopup::_updateHighlight(const SHORT OldCurrentCommand, const SHO
|
|||
WriteCoord.X = _region.Left + 1i16;
|
||||
WriteCoord.Y = _region.Top + 1i16 + OldCurrentCommand - TopIndex;
|
||||
|
||||
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{WriteCoord, til::size{ Width(), 1 }}, _attributes);
|
||||
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{ WriteCoord, til::size{ Width(), 1 } }, _attributes);
|
||||
|
||||
// highlight new command
|
||||
WriteCoord.Y = _region.Top + 1i16 + NewCurrentCommand - TopIndex;
|
||||
TextAttribute inverted = _attributes;
|
||||
inverted.Invert();
|
||||
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{WriteCoord, til::size{ Width(), 1 }}, inverted);
|
||||
_screenInfo.GetTextBuffer().FillWithAttributeRectangular(til::rectangle{ WriteCoord, til::size{ Width(), 1 } }, inverted);
|
||||
}
|
||||
|
|
|
@ -89,10 +89,48 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
|
|||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
const OutputCellIterator it(attrs);
|
||||
const auto done = screenInfo.Write(it, target);
|
||||
//const OutputCellIterator it(attrs);
|
||||
//const auto done = screenInfo.Write(it, target);
|
||||
__debugbreak();
|
||||
// TODO(DH) restore writing runs of attributes
|
||||
////////////////
|
||||
#if 0
|
||||
static rle_container rle_encode(const basic_container_view& from)
|
||||
{
|
||||
if (from.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
used = done.GetCellDistance(it);
|
||||
rle_container to;
|
||||
value_type value = from.front();
|
||||
size_type length = 0;
|
||||
|
||||
for (auto v : from)
|
||||
{
|
||||
if (v != value)
|
||||
{
|
||||
to.emplace_back(value, length);
|
||||
value = v;
|
||||
length = 0;
|
||||
}
|
||||
|
||||
length++;
|
||||
}
|
||||
|
||||
if (length)
|
||||
{
|
||||
to.emplace_back(value, length);
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////
|
||||
|
||||
used = 0;
|
||||
//used = done.GetCellDistance(it);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -131,7 +169,8 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
|
|||
|
||||
try
|
||||
{
|
||||
used = screenInfo.GetTextBuffer().WriteStringLinearKeepAttributes(target, chars);
|
||||
const auto result = screenInfo.GetTextBuffer().WriteStringLinearKeepAttributes(target, chars);
|
||||
used = result.charactersWritten;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
|
@ -214,7 +253,8 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
|
|||
|
||||
try
|
||||
{
|
||||
cellsModified = screenBuffer.GetTextBuffer().FillWithAttributeLinear(startingCoordinate, lengthToWrite, TextAttribute{ attribute });
|
||||
const auto result = screenBuffer.GetTextBuffer().FillWithAttributeLinear(startingCoordinate, lengthToWrite, TextAttribute{ attribute });
|
||||
cellsModified = result.columnsWritten;
|
||||
|
||||
if (screenBuffer.HasAccessibilityEventing())
|
||||
{
|
||||
|
@ -271,12 +311,11 @@ void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
|
|||
HRESULT hr = S_OK;
|
||||
try
|
||||
{
|
||||
const OutputCellIterator it(character, lengthToWrite);
|
||||
|
||||
// when writing to the buffer, specifically unset wrap if we get to the last column.
|
||||
// a fill operation should UNSET wrap in that scenario. See GH #1126 for more details.
|
||||
const auto done = screenInfo.Write(it, startingCoordinate, false);
|
||||
cellsModified = done.GetInputDistance(it);
|
||||
// TODO(DH) maintain above fix to clear wrap ^^^
|
||||
const auto result = screenInfo.GetTextBuffer().FillWithCharacterLinear(startingCoordinate, lengthToWrite, character);
|
||||
cellsModified = result.columnsWritten;
|
||||
|
||||
// Notify accessibility
|
||||
if (screenInfo.HasAccessibilityEventing())
|
||||
|
|
|
@ -342,7 +342,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
SHORT XPosition;
|
||||
std::wstring local;
|
||||
local.reserve(LOCAL_BUFFER_SIZE);
|
||||
til::small_rle<uint8_t, uint16_t, 3> measurements{ gsl::narrow_cast<uint16_t>(LOCAL_BUFFER_SIZE), 1u };
|
||||
RowMeasurementBuffer measurements;
|
||||
size_t TempNumSpaces = 0;
|
||||
const bool fUnprocessed = WI_IsFlagClear(screenInfo.OutputMode, ENABLE_PROCESSED_OUTPUT);
|
||||
const bool fWrapAtEOL = WI_IsFlagSet(screenInfo.OutputMode, ENABLE_WRAP_AT_EOL_OUTPUT);
|
||||
|
@ -400,7 +400,6 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
// WCL-NOTE: is initialized from pwchRealUnicode.
|
||||
const wchar_t RealUnicodeChar = *pwchRealUnicode;
|
||||
const auto cbuf{ Utf16Parser::ParseNext(std::wstring_view{ pwchRealUnicode, (BufferSize / sizeof(WCHAR)) - static_cast<size_t>(pwchRealUnicode - base) }) };
|
||||
uint16_t lo{ gsl::narrow_cast<uint16_t>(local.size()) };
|
||||
if (IS_GLYPH_CHAR(RealUnicodeChar) || fUnprocessed)
|
||||
{
|
||||
// WCL-NOTE: This operates on a single code unit instead of a whole codepoint. It will mis-measure surrogate pairs.
|
||||
|
@ -409,7 +408,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
if (XPosition < (coordScreenBufferSize.X - 1))
|
||||
{
|
||||
local.append(cbuf);
|
||||
measurements.replace(lo, lo + 1, { uint8_t{ 2u }, 1u });
|
||||
measurements.append(uint8_t{ 2u });
|
||||
|
||||
// cursor adjusted by 2 because the char is double width
|
||||
XPosition += 2;
|
||||
|
@ -424,14 +423,14 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
else
|
||||
{
|
||||
local.append(cbuf);
|
||||
measurements.replace(lo, lo + 1, { uint8_t{ 1u }, 1u });
|
||||
measurements.append(uint8_t{ 1u });
|
||||
XPosition++;
|
||||
i += cbuf.size();
|
||||
pwchBuffer += cbuf.size();
|
||||
}
|
||||
if (cbuf.size() > 1)
|
||||
for (auto z = cbuf.size(); z > 1; --z)
|
||||
{
|
||||
measurements.replace(lo + 1, gsl::narrow_cast<uint16_t>(lo + 1 + cbuf.size() - 1), typename decltype(measurements)::rle_type{ uint8_t{ 0u }, gsl::narrow_cast<uint16_t>(cbuf.size() - 1) });
|
||||
measurements.append(uint8_t{ 0 });
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -561,7 +560,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
//OutputCellIterator it(std::wstring_view{ local }, Attributes);
|
||||
//const auto itEnd = screenInfo.Write(it);
|
||||
measurements.resize_trailing_extent(gsl::narrow_cast<uint16_t>(local.size()));
|
||||
auto vcolsTaken = screenInfo.WriteMeasuredStringLinear(local, measurements);
|
||||
auto result = screenInfo.GetTextBuffer().WriteMeasuredStringLinear(CursorPosition, local, measurements);
|
||||
local.clear();
|
||||
measurements.resize_trailing_extent(0);
|
||||
|
||||
|
@ -573,7 +572,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
|
||||
// The number of "spaces" or "cells" we have consumed needs to be reported and stored for later
|
||||
// when/if we need to erase the command line.
|
||||
TempNumSpaces += vcolsTaken; //itEnd.GetCellDistance(it);
|
||||
TempNumSpaces += result.columnsWritten; //itEnd.GetCellDistance(it);
|
||||
// WCL-NOTE: We are using the "estimated" X position delta instead of the actual delta from
|
||||
// WCL-NOTE: the iterator. It is not clear why. If they differ, the cursor ends up in the
|
||||
// WCL-NOTE: wrong place (typically inside another character).
|
||||
|
@ -711,7 +710,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
{
|
||||
try
|
||||
{
|
||||
screenInfo.Write(OutputCellIterator(UNICODE_SPACE, Attributes, 1), CursorPosition);
|
||||
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(CursorPosition, 1, UNICODE_SPACE, Attributes);
|
||||
Status = STATUS_SUCCESS;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
@ -729,7 +728,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
{
|
||||
try
|
||||
{
|
||||
screenInfo.Write(OutputCellIterator(UNICODE_SPACE, Attributes, 1), CursorPosition);
|
||||
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(CursorPosition, 1, UNICODE_SPACE, Attributes);
|
||||
Status = STATUS_SUCCESS;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
@ -751,7 +750,7 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
{
|
||||
try
|
||||
{
|
||||
screenInfo.Write(OutputCellIterator(UNICODE_SPACE, Attributes, 1), cursor.GetPosition());
|
||||
screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(cursor.GetPosition(), 1, UNICODE_SPACE, Attributes);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
@ -806,9 +805,8 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
|
||||
try
|
||||
{
|
||||
const OutputCellIterator it(UNICODE_SPACE, Attributes, NumChars);
|
||||
const auto done = screenInfo.Write(it, cursor.GetPosition());
|
||||
NumChars = done.GetCellDistance(it);
|
||||
const auto result = screenInfo.GetTextBuffer().FillWithCharacterAndAttributeLinear(cursor.GetPosition(), NumChars, UNICODE_SPACE, Attributes);
|
||||
NumChars = result.columnsWritten;
|
||||
}
|
||||
CATCH_LOG();
|
||||
|
||||
|
@ -865,15 +863,12 @@ constexpr unsigned int LOCAL_BUFFER_SIZE = 100;
|
|||
// If we're on top of a trailing cell, clear it and the previous cell.
|
||||
if (Row.DbcsAttrAt(TargetPoint.X).IsTrailing())
|
||||
{
|
||||
// Space to clear for 2 cells.
|
||||
OutputCellIterator it(UNICODE_SPACE, 2);
|
||||
|
||||
// Back target point up one.
|
||||
auto writeTarget = TargetPoint;
|
||||
writeTarget.X--;
|
||||
|
||||
// Write 2 clear cells.
|
||||
screenInfo.Write(it, writeTarget);
|
||||
screenInfo.GetTextBuffer().FillWithCharacterLinear(writeTarget, 2, UNICODE_SPACE);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
|
|
|
@ -109,11 +109,10 @@ void ConversionAreaInfo::SetAttributes(const TextAttribute& attr)
|
|||
// Arguments:
|
||||
// - text - Text to insert into the conversion area buffer
|
||||
// - column - Column to start at (X position)
|
||||
void ConversionAreaInfo::WriteText(const std::vector<OutputCell>& text,
|
||||
void ConversionAreaInfo::WriteText(const RowImage& text,
|
||||
const SHORT column)
|
||||
{
|
||||
gsl::span<const OutputCell> view(text.data(), text.size());
|
||||
_screenBuffer->Write(view, { column, 0 });
|
||||
_screenBuffer->GetTextBuffer().WriteRowImage(til::point{ column, 0 }, text);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
|
|
@ -61,7 +61,7 @@ public:
|
|||
void SetWindowInfo(const SMALL_RECT view) noexcept;
|
||||
void Paint() const noexcept;
|
||||
|
||||
void WriteText(const std::vector<OutputCell>& text, const SHORT column);
|
||||
void WriteText(const RowImage& text, const SHORT column);
|
||||
void SetAttributes(const TextAttribute& attr);
|
||||
|
||||
const TextBuffer& GetTextBuffer() const noexcept;
|
||||
|
|
|
@ -217,76 +217,60 @@ TextAttribute ConsoleImeInfo::s_RetrieveAttributeAt(const size_t pos,
|
|||
// - colorArray - Array of color values provided by the IME.
|
||||
// Return Value:
|
||||
// - Vector of OutputCells where each one represents one cell of the output buffer.
|
||||
std::vector<OutputCell> ConsoleImeInfo::s_ConvertToCells(const std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
RowImage ConsoleImeInfo::s_ConvertToCells(std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray)
|
||||
{
|
||||
RowImage e;
|
||||
std::vector<OutputCell> cells;
|
||||
|
||||
// - Convert incoming wchar_t stream into UTF-16 units.
|
||||
const auto glyphs = Utf16Parser::Parse(text);
|
||||
|
||||
// - Walk through all of the grouped up text, match up the correct attribute to it, and make a new cell.
|
||||
size_t attributesUsed = 0;
|
||||
for (const auto& parsedGlyph : glyphs)
|
||||
size_t attributesUsed{};
|
||||
while (!text.empty())
|
||||
{
|
||||
const std::wstring_view glyph{ parsedGlyph.data(), parsedGlyph.size() };
|
||||
// Collect up attributes that apply to this glyph range.
|
||||
auto drawingAttr = s_RetrieveAttributeAt(attributesUsed, attributes, colorArray);
|
||||
attributesUsed++;
|
||||
const auto glyph{ Utf16Parser::ParseNext(text) };
|
||||
text = text.substr(glyph.size());
|
||||
const uint8_t width{ gsl::narrow_cast<uint8_t>(IsGlyphFullWidth(glyph) ? 2u : 1u) };
|
||||
|
||||
// The IME gave us an attribute for every glyph position in a surrogate pair.
|
||||
// But the only important information will be the cursor position.
|
||||
// Check all additional attributes to see if the cursor resides on top of them.
|
||||
for (size_t i = 1; i < glyph.size(); i++)
|
||||
{
|
||||
TextAttribute additionalAttr = s_RetrieveAttributeAt(attributesUsed, attributes, colorArray);
|
||||
attributesUsed++;
|
||||
if (additionalAttr.IsLeftVerticalDisplayed())
|
||||
{
|
||||
drawingAttr.SetLeftVerticalDisplayed(true);
|
||||
}
|
||||
if (additionalAttr.IsRightVerticalDisplayed())
|
||||
{
|
||||
drawingAttr.SetRightVerticalDisplayed(true);
|
||||
}
|
||||
// add glyph
|
||||
e._data.append(glyph);
|
||||
|
||||
// register its width
|
||||
e._cwid.append(width);
|
||||
for (auto z{ glyph.size() }; z > 1; --z)
|
||||
{ // add a trailing 0-width for every surrogate pair trail
|
||||
e._cwid.append(0);
|
||||
}
|
||||
|
||||
// We have to determine if the glyph range is 1 column or two.
|
||||
// If it's full width, it's two, and we need to make sure we don't draw the cursor
|
||||
// right down the middle of the character.
|
||||
// Otherwise it's one column and we'll push it in with the default empty DbcsAttribute.
|
||||
DbcsAttribute dbcsAttr;
|
||||
if (IsGlyphFullWidth(glyph))
|
||||
auto drawingAttr = s_RetrieveAttributeAt(attributesUsed++, attributes, colorArray);
|
||||
if (width == 1 || !(drawingAttr.IsLeftVerticalDisplayed() || drawingAttr.IsRightVerticalDisplayed()))
|
||||
{
|
||||
auto leftHalfAttr = drawingAttr;
|
||||
auto rightHalfAttr = drawingAttr;
|
||||
|
||||
// Don't draw lines in the middle of full width glyphs.
|
||||
// If we need a right vertical, don't apply it to the left side of the character
|
||||
if (leftHalfAttr.IsRightVerticalDisplayed())
|
||||
for (auto z{ width }; z > 0; --z) // add an attribute for each cell covered
|
||||
{
|
||||
leftHalfAttr.SetRightVerticalDisplayed(false);
|
||||
e._attrRow._data.append(drawingAttr);
|
||||
}
|
||||
|
||||
dbcsAttr.SetLeading();
|
||||
cells.emplace_back(glyph, dbcsAttr, leftHalfAttr);
|
||||
dbcsAttr.SetTrailing();
|
||||
|
||||
// If we need a left vertical, don't apply it to the right side of the character
|
||||
if (rightHalfAttr.IsLeftVerticalDisplayed())
|
||||
{
|
||||
rightHalfAttr.SetLeftVerticalDisplayed(false);
|
||||
}
|
||||
cells.emplace_back(glyph, dbcsAttr, rightHalfAttr);
|
||||
}
|
||||
else
|
||||
{
|
||||
cells.emplace_back(glyph, dbcsAttr, drawingAttr);
|
||||
}
|
||||
}
|
||||
// the width was >1 and the attribute contained the left or the right cursor indicator; split them into [ ___ ] (left, middle, right)
|
||||
auto first = drawingAttr;
|
||||
first.SetRightVerticalDisplayed(false);
|
||||
auto middle = drawingAttr;
|
||||
middle.SetLeftVerticalDisplayed(false);
|
||||
middle.SetRightVerticalDisplayed(false);
|
||||
auto last = drawingAttr;
|
||||
last.SetLeftVerticalDisplayed(false);
|
||||
|
||||
return cells;
|
||||
e._attrRow._data.append(first);
|
||||
for (auto z{ width }; z > 2; --z)
|
||||
{
|
||||
e._attrRow._data.append(middle);
|
||||
}
|
||||
e._attrRow._data.append(last);
|
||||
}
|
||||
|
||||
e._width += width;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -308,81 +292,55 @@ std::vector<OutputCell> ConsoleImeInfo::s_ConvertToCells(const std::wstring_view
|
|||
// However, if text couldn't fit in our line (full-width character starting at the very last cell)
|
||||
// then we will give back the same begin and update the position for the next call to try again.
|
||||
// If the viewport is deemed too small, we'll skip past it and advance begin past the entire full-width character.
|
||||
std::vector<OutputCell>::const_iterator ConsoleImeInfo::_WriteConversionArea(const std::vector<OutputCell>::const_iterator begin,
|
||||
const std::vector<OutputCell>::const_iterator end,
|
||||
COORD& pos,
|
||||
const Microsoft::Console::Types::Viewport view,
|
||||
SCREEN_INFORMATION& screenInfo)
|
||||
void ConsoleImeInfo::_WriteConversionArea(const RowImage& cells,
|
||||
COORD pos,
|
||||
const Microsoft::Console::Types::Viewport view,
|
||||
SCREEN_INFORMATION& screenInfo)
|
||||
{
|
||||
// The position in the viewport where we will start inserting cells for this conversion area
|
||||
// NOTE: We might exit early if there's not enough space to fit here, so we take a copy of
|
||||
// the original and increment it up front.
|
||||
const auto insertionPos = pos;
|
||||
|
||||
// Advance the cursor position to set up the next call for success (insert the next conversion area
|
||||
// at the beginning of the following line)
|
||||
pos.X = view.Left();
|
||||
pos.Y++;
|
||||
|
||||
// The index of the last column in the viewport. (view is inclusive)
|
||||
const auto finalViewColumn = view.RightInclusive();
|
||||
|
||||
// The maximum number of cells we can insert into a line.
|
||||
const auto lineWidth = finalViewColumn - insertionPos.X + 1; // +1 because view was inclusive
|
||||
|
||||
// The iterator to the beginning position to form our line
|
||||
const auto lineBegin = begin;
|
||||
|
||||
// The total number of cells we could insert.
|
||||
const auto size = end - begin;
|
||||
FAIL_FAST_IF(size <= 0); // It's a programming error to have <= 0 cells to insert.
|
||||
|
||||
// The end is the smaller of the remaining number of cells or the amount of line cells we can write before
|
||||
// hitting the right edge of the viewport
|
||||
auto lineEnd = lineBegin + std::min(size, (ptrdiff_t)lineWidth);
|
||||
|
||||
// We must attempt to compensate for ending on a leading byte. We can't split a full-width character across lines.
|
||||
// As such, if the last item is a leading byte, back the end up by one.
|
||||
FAIL_FAST_IF(lineEnd <= lineBegin); // We should have at least 1 space we can back up.
|
||||
|
||||
// Get the last cell in the run and if it's a leading byte, move the end position back one so we don't
|
||||
// try to insert it.
|
||||
const auto lastCell = lineEnd - 1;
|
||||
if (lastCell->DbcsAttr().IsLeading())
|
||||
auto remain{ cells };
|
||||
do
|
||||
{
|
||||
lineEnd--;
|
||||
}
|
||||
// The maximum number of cells we can insert into a line.
|
||||
const auto lineWidth = finalViewColumn - pos.X + 1; // +1 because view was inclusive
|
||||
|
||||
// Copy out the substring into a vector.
|
||||
const std::vector<OutputCell> lineVec(lineBegin, lineEnd);
|
||||
auto [draw, left] = cells.split(::base::saturated_cast<uint16_t>(lineWidth));
|
||||
|
||||
// Add a conversion area to the internal state to hold this line.
|
||||
THROW_IF_FAILED(_AddConversionArea());
|
||||
// Add a conversion area to the internal state to hold this line.
|
||||
THROW_IF_FAILED(_AddConversionArea());
|
||||
|
||||
// Get the added conversion area.
|
||||
auto& area = ConvAreaCompStr.back();
|
||||
// Get the added conversion area.
|
||||
auto& area = ConvAreaCompStr.back();
|
||||
|
||||
// Write our text into the conversion area.
|
||||
area.WriteText(lineVec, insertionPos.X);
|
||||
area.WriteText(draw, pos.X);
|
||||
|
||||
// Set the viewport and positioning parameters for the conversion area to describe to the renderer
|
||||
// the appropriate location to overlay this conversion area on top of the main screen buffer inside the viewport.
|
||||
const SMALL_RECT region{ insertionPos.X, 0, gsl::narrow<SHORT>(insertionPos.X + lineVec.size() - 1), 0 };
|
||||
area.SetWindowInfo(region);
|
||||
area.SetViewPos({ 0 - view.Left(), insertionPos.Y - view.Top() });
|
||||
// Set the viewport and positioning parameters for the conversion area to describe to the renderer
|
||||
// the appropriate location to overlay this conversion area on top of the main screen buffer inside the viewport.
|
||||
const SMALL_RECT region{ pos.X, 0, gsl::narrow<SHORT>(pos.X + draw._width - 1), 0 };
|
||||
area.SetWindowInfo(region);
|
||||
area.SetViewPos({ 0 - view.Left(), pos.Y - view.Top() });
|
||||
|
||||
// Make it visible and paint it.
|
||||
area.SetHidden(false);
|
||||
area.Paint();
|
||||
// Make it visible and paint it.
|
||||
area.SetHidden(false);
|
||||
area.Paint();
|
||||
|
||||
// Notify accessibility that we have updated the text in this display region within the viewport.
|
||||
if (screenInfo.HasAccessibilityEventing())
|
||||
{
|
||||
screenInfo.NotifyAccessibilityEventing(insertionPos.X, insertionPos.Y, gsl::narrow<SHORT>(insertionPos.X + lineVec.size() - 1), insertionPos.Y);
|
||||
}
|
||||
// Notify accessibility that we have updated the text in this display region within the viewport.
|
||||
if (screenInfo.HasAccessibilityEventing())
|
||||
{
|
||||
screenInfo.NotifyAccessibilityEventing(pos.X, pos.Y, gsl::narrow<SHORT>(pos.X + draw._width - 1), pos.Y);
|
||||
}
|
||||
|
||||
// Hand back the iterator representing the end of what we used to be fed into the beginning of the next call.
|
||||
return lineEnd;
|
||||
if (remain._data.empty())
|
||||
break;
|
||||
|
||||
// Advance the cursor position to set up the next call for success (insert the next conversion area
|
||||
// at the beginning of the following line)
|
||||
pos.X = view.Left();
|
||||
pos.Y++;
|
||||
remain = std::move(left);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -432,17 +390,7 @@ void ConsoleImeInfo::_WriteUndeterminedChars(const std::wstring_view text,
|
|||
const auto view = screenInfo.GetViewport();
|
||||
// Set cursor position relative to viewport
|
||||
|
||||
// Set up our iterators. We will walk through the entire set of cells from beginning to end.
|
||||
// The first time, we will give the iterators as the whole span and the begin
|
||||
// will be moved forward by the conversion area write to set up the next call.
|
||||
auto begin = cells.cbegin();
|
||||
const auto end = cells.cend();
|
||||
|
||||
// Write over and over updating the beginning iterator until we reach the end.
|
||||
do
|
||||
{
|
||||
begin = _WriteConversionArea(begin, end, pos, view, screenInfo);
|
||||
} while (begin < end);
|
||||
_WriteConversionArea(cells, pos, view, screenInfo);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
|
|
@ -22,6 +22,7 @@ Revision History:
|
|||
#include "../buffer/out/OutputCell.hpp"
|
||||
#include "../buffer/out/TextAttribute.hpp"
|
||||
#include "../renderer/inc/FontInfo.hpp"
|
||||
#include "../buffer/out/Row.hpp"
|
||||
#include "../types/inc/viewport.hpp"
|
||||
|
||||
#include "conareainfo.h"
|
||||
|
@ -73,15 +74,14 @@ private:
|
|||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
|
||||
static std::vector<OutputCell> s_ConvertToCells(const std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
static RowImage s_ConvertToCells(std::wstring_view text,
|
||||
const gsl::span<const BYTE> attributes,
|
||||
const gsl::span<const WORD> colorArray);
|
||||
|
||||
std::vector<OutputCell>::const_iterator _WriteConversionArea(const std::vector<OutputCell>::const_iterator begin,
|
||||
const std::vector<OutputCell>::const_iterator end,
|
||||
COORD& pos,
|
||||
const Microsoft::Console::Types::Viewport view,
|
||||
SCREEN_INFORMATION& screenInfo);
|
||||
void _WriteConversionArea(const RowImage& cells,
|
||||
COORD pos,
|
||||
const Microsoft::Console::Types::Viewport view,
|
||||
SCREEN_INFORMATION& screenInfo);
|
||||
|
||||
bool _isSavedCursorVisible;
|
||||
|
||||
|
|
|
@ -1010,8 +1010,9 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
|
|||
const auto charInfos = gsl::span<const CHAR_INFO>(subspan.data(), subspan.size());
|
||||
|
||||
// Make the iterator and write to the target position.
|
||||
OutputCellIterator it(charInfos);
|
||||
storageBuffer.Write(it, target);
|
||||
__debugbreak();
|
||||
//OutputCellIterator it(charInfos);
|
||||
//storageBuffer.Write(it, target);
|
||||
}
|
||||
|
||||
// Since we've managed to write part of the request, return the clamped part that we actually used.
|
||||
|
|
|
@ -104,8 +104,9 @@ static void _CopyRectangle(SCREEN_INFORMATION& screenInfo,
|
|||
|
||||
do
|
||||
{
|
||||
const auto data = OutputCell(*screenInfo.GetCellDataAt(sourcePos));
|
||||
screenInfo.Write(OutputCellIterator({ &data, 1 }), targetPos);
|
||||
__debugbreak();
|
||||
//const auto data = OutputCell(*screenInfo.GetCellDataAt(sourcePos));
|
||||
//screenInfo.Write(OutputCellIterator({ &data, 1 }), targetPos);
|
||||
|
||||
source.WalkInBounds(sourcePos, walkDirection);
|
||||
} while (target.WalkInBounds(targetPos, walkDirection));
|
||||
|
|
|
@ -2334,42 +2334,8 @@ OutputCellRect SCREEN_INFORMATION::ReadRect(const Viewport viewport) const
|
|||
return result;
|
||||
}
|
||||
|
||||
size_t SCREEN_INFORMATION::WriteMeasuredStringLinear(std::wstring_view string, const til::small_rle<uint8_t, uint16_t, 3>& measurements)
|
||||
{
|
||||
return _textBuffer->WriteMeasuredStringLinear(_textBuffer->GetCursor().GetPosition(), string, measurements);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Writes cells to the output buffer at the cursor position.
|
||||
// Arguments:
|
||||
// - it - Iterator representing output cell data to write.
|
||||
// Return Value:
|
||||
// - the iterator at its final position
|
||||
// Note:
|
||||
// - will throw exception on error.
|
||||
OutputCellIterator SCREEN_INFORMATION::Write(const OutputCellIterator it)
|
||||
{
|
||||
return _textBuffer->Write(it);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Writes cells to the output buffer.
|
||||
// Arguments:
|
||||
// - it - Iterator representing output cell data to write.
|
||||
// - target - The position to start writing at
|
||||
// - wrap - change the wrap flag if we hit the end of the row while writing and there's still more data
|
||||
// Return Value:
|
||||
// - the iterator at its final position
|
||||
// Note:
|
||||
// - will throw exception on error.
|
||||
OutputCellIterator SCREEN_INFORMATION::Write(const OutputCellIterator it,
|
||||
const COORD target,
|
||||
const std::optional<bool> wrap)
|
||||
{
|
||||
// NOTE: if wrap = true/false, we want to set the line's wrap to true/false (respectively) if we reach the end of the line
|
||||
return _textBuffer->Write(it, target, wrap);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TODO(DH)
|
||||
// Routine Description:
|
||||
// - This routine writes a rectangular region into the screen buffer.
|
||||
// Arguments:
|
||||
|
@ -2393,6 +2359,7 @@ OutputCellIterator SCREEN_INFORMATION::WriteRect(const OutputCellIterator it,
|
|||
|
||||
return iter;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Routine Description:
|
||||
// - This routine writes a rectangular region into the screen buffer.
|
||||
|
@ -2406,6 +2373,10 @@ OutputCellIterator SCREEN_INFORMATION::WriteRect(const OutputCellIterator it,
|
|||
void SCREEN_INFORMATION::WriteRect(const OutputCellRect& data,
|
||||
const COORD location)
|
||||
{
|
||||
(void)data;
|
||||
(void)location;
|
||||
__debugbreak();
|
||||
#if 0
|
||||
for (size_t i = 0; i < data.Height(); i++)
|
||||
{
|
||||
const auto iter = data.GetRowIter(i);
|
||||
|
@ -2414,8 +2385,10 @@ void SCREEN_INFORMATION::WriteRect(const OutputCellRect& data,
|
|||
point.X = location.X;
|
||||
point.Y = location.Y + static_cast<short>(i);
|
||||
|
||||
_textBuffer->WriteLine(iter, point);
|
||||
__debugbreak();
|
||||
//_textBuffer->WriteLine(iter, point);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
|
|
@ -135,15 +135,7 @@ public:
|
|||
TextBufferTextIterator GetTextLineDataAt(const COORD at) const;
|
||||
TextBufferTextIterator GetTextDataAt(const COORD at, const Microsoft::Console::Types::Viewport limit) const;
|
||||
|
||||
size_t WriteMeasuredStringLinear(std::wstring_view string, const til::small_rle<uint8_t, uint16_t, 3>& measurements);
|
||||
OutputCellIterator Write(const OutputCellIterator it);
|
||||
|
||||
OutputCellIterator Write(const OutputCellIterator it,
|
||||
const COORD target,
|
||||
const std::optional<bool> wrap = true);
|
||||
|
||||
OutputCellIterator WriteRect(const OutputCellIterator it,
|
||||
const Microsoft::Console::Types::Viewport viewport);
|
||||
size_t WriteMeasuredStringLinear(std::wstring_view string, const RowMeasurementBuffer& measurements);
|
||||
|
||||
void WriteRect(const OutputCellRect& data,
|
||||
const COORD location);
|
||||
|
|
|
@ -520,6 +520,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
|||
_total_length = new_size;
|
||||
}
|
||||
|
||||
void append(const value_type& value)
|
||||
{
|
||||
++_total_length;
|
||||
if (!_runs.empty())
|
||||
{
|
||||
auto& back{ _runs.back() };
|
||||
if (back.value == value)
|
||||
{
|
||||
++back.length;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Either runs was empty, or we have to append a new run.
|
||||
_runs.emplace_back(value, S{ 1 });
|
||||
}
|
||||
|
||||
constexpr bool operator==(const basic_rle& other) const noexcept
|
||||
{
|
||||
return _total_length == other._total_length && _runs == other._runs;
|
||||
|
|
Loading…
Reference in a new issue