Prepare til wrappers for migrating off of SMALL_RECT (#11902)

This commit makes the following changes to `til::point/size/rectangle`
for the following reasons:
* Rename `rectangle` into `rect`
  This will make the naming consistent with a later `small_rect` struct
  as well as the existing Win32 POINT/SIZE/RECT structs.
* Standardizes til wrappers on `int32_t` instead of `ptrdiff_t`
  Provides a consistent behavior between x86 and x64, preventing accidental
  errors on x86, as it's less rigorously tested than x64. Additionally it
  improves interop with MIDL3 which only supports fixed width integer types.
* Standardizes til wrappers on throwing `gsl::narrow_error`
  Makes the behavior of our code more consistent.
* Makes all eligible functions `constexpr`
  Because why not.
* Removes implicit constructors and conversion operators
  This is a complex and controversial topic. My reasons are: You can't Ctrl+F
  for an implicit conversion. This breaks most non-IDE engines, like the one on
  GitHub or those we have internally at MS. This is important for me as these
  implicit conversion operators aren't cost free. Narrowing integers itself,
  as well as the boundary checks that need to be done have a certain,
  fixed overhead each time. Additionally the lack of noexcept prevents
  many advanced compiler optimizations. Removing their use entirely
  drops conhost's code segment size by around ~6.5%.

## References

Preliminary work for #4015.

## PR Checklist
* [x] I work here
* [x] Tests added/passed

## Validation Steps Performed
I'm mostly relying on our unit tests here. Both OpenConsole and WT appear to work fine.
This commit is contained in:
Leonard Hecker 2022-01-13 22:09:29 +01:00 committed by GitHub
parent 1a62f473ad
commit b3fab518f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 2704 additions and 3651 deletions

View File

@ -1076,7 +1076,7 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
#pragma warning(suppress : 26496)
auto copy{ target };
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
if (target == bufferSize.Origin())
{
// can't expand left
@ -1088,10 +1088,10 @@ const COORD TextBuffer::GetWordStart(const COORD target, const std::wstring_view
// that it actually points to a space in the buffer
copy = { bufferSize.RightInclusive(), bufferSize.BottomInclusive() };
}
else if (bufferSize.CompareInBounds(target, limit, true) >= 0)
else if (bufferSize.CompareInBounds(target, limit.to_win32_coord(), true) >= 0)
{
// if at/past the limit --> clamp to limit
copy = *limitOptional;
copy = limitOptional->to_win32_coord();
}
if (accessibilityMode)
@ -1203,15 +1203,15 @@ const COORD TextBuffer::GetWordEnd(const COORD target, const std::wstring_view w
// Already at/past the limit. Can't move forward.
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
if (bufferSize.CompareInBounds(target, limit, true) >= 0)
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
if (bufferSize.CompareInBounds(target, limit.to_win32_coord(), true) >= 0)
{
return target;
}
if (accessibilityMode)
{
return _GetWordEndForAccessibility(target, wordDelimiters, limit);
return _GetWordEndForAccessibility(target, wordDelimiters, limit.to_win32_coord());
}
else
{
@ -1366,10 +1366,10 @@ bool TextBuffer::MoveToNextWord(COORD& pos, const std::wstring_view wordDelimite
// NOTE: _GetWordEnd...() returns the exclusive position of the "end of the word"
// This is also the inclusive start of the next word.
const auto bufferSize{ GetSize() };
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit) };
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
const auto copy{ _GetWordEndForAccessibility(pos, wordDelimiters, limit.to_win32_coord()) };
if (bufferSize.CompareInBounds(copy, limit, true) >= 0)
if (bufferSize.CompareInBounds(copy, limit.to_win32_coord(), true) >= 0)
{
return false;
}
@ -1411,23 +1411,23 @@ bool TextBuffer::MoveToPreviousWord(COORD& pos, std::wstring_view wordDelimiters
// - pos - The COORD for the first cell of the current glyph (inclusive)
const til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<til::point> limitOptional) const
{
COORD resultPos = pos;
COORD resultPos = pos.to_win32_coord();
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
// Clamp pos to limit
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
if (bufferSize.CompareInBounds(resultPos, limit.to_win32_coord(), true) > 0)
{
resultPos = limit;
resultPos = limit.to_win32_coord();
}
// limit is exclusive, so we need to move back to be within valid bounds
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
if (resultPos != limit.to_win32_coord() && GetCellDataAt(resultPos)->DbcsAttr().IsTrailing())
{
bufferSize.DecrementInBounds(resultPos, true);
}
return resultPos;
return til::point{ resultPos };
}
// Method Description:
@ -1439,17 +1439,17 @@ const til::point TextBuffer::GetGlyphStart(const til::point pos, std::optional<t
// - pos - The COORD for the last cell of the current glyph (exclusive)
const til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilityMode, std::optional<til::point> limitOptional) const
{
COORD resultPos = pos;
COORD resultPos = pos.to_win32_coord();
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
// Clamp pos to limit
if (bufferSize.CompareInBounds(resultPos, limit, true) > 0)
if (bufferSize.CompareInBounds(resultPos, limit.to_win32_coord(), true) > 0)
{
resultPos = limit;
resultPos = limit.to_win32_coord();
}
if (resultPos != limit && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
if (resultPos != limit.to_win32_coord() && GetCellDataAt(resultPos)->DbcsAttr().IsLeading())
{
bufferSize.IncrementInBounds(resultPos, true);
}
@ -1459,7 +1459,7 @@ const til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilit
{
bufferSize.IncrementInBounds(resultPos, true);
}
return resultPos;
return til::point{ resultPos };
}
// Method Description:
@ -1474,9 +1474,9 @@ const til::point TextBuffer::GetGlyphEnd(const til::point pos, bool accessibilit
bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::optional<til::point> limitOptional) const
{
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
const auto distanceToLimit{ bufferSize.CompareInBounds(pos, limit, true) };
const auto distanceToLimit{ bufferSize.CompareInBounds(pos.to_win32_coord(), limit.to_win32_coord(), true) };
if (distanceToLimit >= 0)
{
// Corner Case: we're on/past the limit
@ -1493,7 +1493,7 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::o
}
// Try to move forward, but if we hit the buffer boundary, we fail to move.
auto iter{ GetCellDataAt(pos, bufferSize) };
auto iter{ GetCellDataAt(pos.to_win32_coord(), bufferSize) };
const bool success{ ++iter };
// Move again if we're on a wide glyph
@ -1502,7 +1502,7 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::o
++iter;
}
pos = iter.Pos();
pos = til::point{ iter.Pos() };
return success;
}
@ -1515,11 +1515,11 @@ bool TextBuffer::MoveToNextGlyph(til::point& pos, bool allowExclusiveEnd, std::o
// - pos - The COORD for the first cell of the previous glyph (inclusive)
bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point> limitOptional) const
{
COORD resultPos = pos;
COORD resultPos = pos.to_win32_coord();
const auto bufferSize = GetSize();
const auto limit{ limitOptional.value_or(bufferSize.EndExclusive()) };
const auto limit{ limitOptional.value_or(til::point{ bufferSize.EndExclusive() }) };
if (bufferSize.CompareInBounds(pos, limit, true) > 0)
if (bufferSize.CompareInBounds(pos.to_win32_coord(), limit.to_win32_coord(), true) > 0)
{
// we're past the end
// clamp us to the limit
@ -1534,7 +1534,7 @@ bool TextBuffer::MoveToPreviousGlyph(til::point& pos, std::optional<til::point>
bufferSize.DecrementInBounds(resultPos, true);
}
pos = resultPos;
pos = til::point{ resultPos };
return success;
}

View File

@ -549,11 +549,11 @@ try
if (multiClickMapper == 3)
{
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansion::Line);
_terminal->MultiClickSelection((cursorPosition / fontSize).to_win32_coord(), ::Terminal::SelectionExpansion::Line);
}
else if (multiClickMapper == 2)
{
_terminal->MultiClickSelection(cursorPosition / fontSize, ::Terminal::SelectionExpansion::Word);
_terminal->MultiClickSelection((cursorPosition / fontSize).to_win32_coord(), ::Terminal::SelectionExpansion::Word);
}
else
{
@ -582,19 +582,25 @@ try
RETURN_HR_IF(E_NOT_VALID_STATE, fontSize.area() == 0); // either dimension = 0, area == 0
if (this->_singleClickTouchdownPos)
// This is a copy of ControlInteractivity::PointerMoved
if (_singleClickTouchdownPos)
{
const auto& touchdownPoint{ *this->_singleClickTouchdownPos };
const auto distance{ std::sqrtf(std::powf(cursorPosition.x<float>() - touchdownPoint.x<float>(), 2) + std::powf(cursorPosition.y<float>() - touchdownPoint.y<float>(), 2)) };
if (distance >= (std::min(fontSize.width(), fontSize.height()) / 4.f))
const auto touchdownPoint = *_singleClickTouchdownPos;
const auto dx = cursorPosition.x - touchdownPoint.x;
const auto dy = cursorPosition.y - touchdownPoint.y;
const auto w = fontSize.width;
const auto distanceSquared = dx * dx + dy * dy;
const auto maxDistanceSquared = w * w / 16; // (w / 4)^2
if (distanceSquared >= maxDistanceSquared)
{
_terminal->SetSelectionAnchor(touchdownPoint / fontSize);
_terminal->SetSelectionAnchor((touchdownPoint / fontSize).to_win32_coord());
// stop tracking the touchdown point
_singleClickTouchdownPos = std::nullopt;
}
}
this->_terminal->SetSelectionEnd(cursorPosition / fontSize);
this->_terminal->SetSelectionEnd((cursorPosition / fontSize).to_win32_coord());
this->_renderer->TriggerSelection();
return S_OK;
@ -696,9 +702,9 @@ try
wheelDelta = HIWORD(wParam);
// If it's a *WHEEL event, it's in screen coordinates, not window (?!)
POINT coordsToTransform = cursorPosition;
POINT coordsToTransform = cursorPosition.to_win32_point();
ScreenToClient(_hwnd.get(), &coordsToTransform);
cursorPosition = coordsToTransform;
cursorPosition = til::point{ coordsToTransform };
}
const TerminalInput::MouseButtonState state{
@ -707,7 +713,7 @@ try
WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed)
};
return _terminal->SendMouseEvent(cursorPosition / fontSize, uMsg, getControlKeyState(), wheelDelta, state);
return _terminal->SendMouseEvent((cursorPosition / fontSize).to_win32_coord(), uMsg, getControlKeyState(), wheelDelta, state);
}
catch (...)
{

View File

@ -423,7 +423,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const short wheelDelta,
const TerminalInput::MouseButtonState state)
{
return _terminal->SendMouseEvent(viewportPos, uiButton, states, wheelDelta, state);
return _terminal->SendMouseEvent(viewportPos.to_win32_coord(), uiButton, states, wheelDelta, state);
}
void ControlCore::UserScrollViewport(const int viewTop)
@ -546,12 +546,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_lastHoveredCell = terminalPosition;
uint16_t newId{ 0u };
// we can't use auto here because we're pre-declaring newInterval.
decltype(_terminal->GetHyperlinkIntervalFromPosition(til::point{})) newInterval{ std::nullopt };
decltype(_terminal->GetHyperlinkIntervalFromPosition(COORD{})) newInterval{ std::nullopt };
if (terminalPosition.has_value())
{
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
newId = _terminal->GetHyperlinkIdAtPosition(*terminalPosition);
newInterval = _terminal->GetHyperlinkIntervalFromPosition(*terminalPosition);
newId = _terminal->GetHyperlinkIdAtPosition(terminalPosition->to_win32_coord());
newInterval = _terminal->GetHyperlinkIntervalFromPosition(terminalPosition->to_win32_coord());
}
// If the hyperlink ID changed or the interval changed, trigger a redraw all
@ -577,11 +577,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
winrt::hstring ControlCore::GetHyperlink(const til::point pos) const
winrt::hstring ControlCore::GetHyperlink(const Core::Point pos) const
{
// Lock for the duration of our reads.
auto lock = _terminal->LockForReading();
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(pos) };
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(til::point{ pos }.to_win32_coord()) };
}
winrt::hstring ControlCore::HoveredUriText() const
@ -589,14 +589,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto lock = _terminal->LockForReading(); // Lock for the duration of our reads.
if (_lastHoveredCell.has_value())
{
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(*_lastHoveredCell) };
return winrt::hstring{ _terminal->GetHyperlinkAtPosition(_lastHoveredCell->to_win32_coord()) };
}
return {};
}
Windows::Foundation::IReference<Core::Point> ControlCore::HoveredCell() const
{
return _lastHoveredCell.has_value() ? Windows::Foundation::IReference<Core::Point>{ _lastHoveredCell.value() } : nullptr;
return _lastHoveredCell.has_value() ? Windows::Foundation::IReference<Core::Point>{ _lastHoveredCell.value().to_core_point() } : nullptr;
}
// Method Description:
@ -938,7 +938,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::SetSelectionAnchor(til::point const& position)
{
auto lock = _terminal->LockForWriting();
_terminal->SetSelectionAnchor(position);
_terminal->SetSelectionAnchor(position.to_win32_coord());
}
// Method Description:
@ -956,16 +956,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// you move its endpoints while it is generating a frame.
auto lock = _terminal->LockForWriting();
const short lastVisibleRow = std::max<short>(_terminal->GetViewport().Height() - 1, 0);
const short lastVisibleCol = std::max<short>(_terminal->GetViewport().Width() - 1, 0);
til::point terminalPosition{
std::clamp<short>(position.x<short>(), 0, lastVisibleCol),
std::clamp<short>(position.y<short>(), 0, lastVisibleRow)
std::clamp(position.x, 0, _terminal->GetViewport().Width() - 1),
std::clamp(position.y, 0, _terminal->GetViewport().Height() - 1)
};
// save location (for rendering) + render
_terminal->SetSelectionEnd(terminalPosition);
_terminal->SetSelectionEnd(terminalPosition.to_win32_coord());
_renderer->TriggerSelection();
}
@ -1433,7 +1430,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _terminal != nullptr && _terminal->IsTrackingMouseInput();
}
til::point ControlCore::CursorPosition() const
Core::Point ControlCore::CursorPosition() const
{
// If we haven't been initialized yet, then fake it.
if (!_initializedTerminal)
@ -1442,7 +1439,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
auto lock = _terminal->LockForReading();
return _terminal->GetCursorPosition();
return til::point{ _terminal->GetCursorPosition() }.to_core_point();
}
// This one's really pushing the boundary of what counts as "encapsulation".
@ -1494,7 +1491,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
// If shift is pressed and there is a selection we extend it using
// the selection mode (expand the "end" selection point)
_terminal->SetSelectionEnd(terminalPosition, mode);
_terminal->SetSelectionEnd(terminalPosition.to_win32_coord(), mode);
selectionNeedsToBeCopied = true;
}
else if (mode != ::Terminal::SelectionExpansion::Char || shiftEnabled)
@ -1502,7 +1499,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// If we are handling a double / triple-click or shift+single click
// we establish selection using the selected mode
// (expand both "start" and "end" selection points)
_terminal->MultiClickSelection(terminalPosition, mode);
_terminal->MultiClickSelection(terminalPosition.to_win32_coord(), mode);
selectionNeedsToBeCopied = true;
}

View File

@ -88,7 +88,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void UpdatePatternLocations();
void SetHoveredCell(Core::Point terminalPosition);
void ClearHoveredCell();
winrt::hstring GetHyperlink(const til::point position) const;
winrt::hstring GetHyperlink(const Core::Point position) const;
winrt::hstring HoveredUriText() const;
Windows::Foundation::IReference<Core::Point> HoveredCell() const;
@ -138,7 +138,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void CursorOn(const bool isCursorOn);
bool IsVtMouseModeEnabled() const;
til::point CursorPosition() const;
Core::Point CursorPosition() const;
bool HasSelection() const;
bool CopyOnSelect() const;

View File

@ -84,7 +84,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Return Value:
// - if the click is in the same position as the last click and within the timeout, the number of clicks within that time window
// - otherwise, 1
unsigned int ControlInteractivity::_numberOfClicks(til::point clickPos,
unsigned int ControlInteractivity::_numberOfClicks(Core::Point clickPos,
Timestamp clickTime)
{
// if click occurred at a different location or past the multiClickTimer...
@ -192,16 +192,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const unsigned int pointerUpdateKind,
const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const til::point pixelPosition)
const Core::Point pixelPosition)
{
const til::point terminalPosition = _getTerminalPosition(pixelPosition);
const til::point terminalPosition = _getTerminalPosition(til::point{ pixelPosition });
const auto altEnabled = modifiers.IsAltPressed();
const auto shiftEnabled = modifiers.IsShiftPressed();
const auto ctrlEnabled = modifiers.IsCtrlPressed();
// GH#9396: we prioritize hyper-link over VT mouse events
auto hyperlink = _core->GetHyperlink(terminalPosition);
auto hyperlink = _core->GetHyperlink(terminalPosition.to_core_point());
if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) &&
ctrlEnabled && !hyperlink.empty())
{
@ -265,7 +265,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void ControlInteractivity::TouchPressed(const til::point contactPoint)
void ControlInteractivity::TouchPressed(const Core::Point contactPoint)
{
_touchAnchor = contactPoint;
}
@ -274,10 +274,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused,
const til::point pixelPosition,
const Core::Point pixelPosition,
const bool pointerPressedInBounds)
{
const til::point terminalPosition = _getTerminalPosition(pixelPosition);
const til::point terminalPosition = _getTerminalPosition(til::point{ pixelPosition });
// Short-circuit isReadOnly check to avoid warning dialog
if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
@ -292,21 +292,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
if (_singleClickTouchdownPos)
{
// Figure out if the user's moved a quarter of a cell's smaller axis away from the clickdown point
auto& touchdownPoint{ *_singleClickTouchdownPos };
float dx = ::base::saturated_cast<float>(pixelPosition.x() - touchdownPoint.x());
float dy = ::base::saturated_cast<float>(pixelPosition.y() - touchdownPoint.y());
auto distance{ std::sqrtf(std::powf(dx, 2) +
std::powf(dy, 2)) };
// Figure out if the user's moved a 1/4th of a cell's smaller axis
// (practically always the width) away from the clickdown point.
const auto fontSizeInDips = _core->FontSizeInDips();
const auto touchdownPoint = *_singleClickTouchdownPos;
const auto dx = pixelPosition.X - touchdownPoint.X;
const auto dy = pixelPosition.Y - touchdownPoint.Y;
const auto w = fontSizeInDips.width;
const auto distanceSquared = dx * dx + dy * dy;
const auto maxDistanceSquared = w * w / 16; // (w / 4)^2
const auto fontSizeInDips{ _core->FontSizeInDips() };
if (distance >= (std::min(fontSizeInDips.width(), fontSizeInDips.height()) / 4.f))
if (distanceSquared >= maxDistanceSquared)
{
// GH#9955.c: Make sure to use the terminal location of the
// _touchdown_ point here. We want to start the selection
// from where the user initially clicked, not where they are
// now.
_core->SetSelectionAnchor(_getTerminalPosition(touchdownPoint));
_core->SetSelectionAnchor(_getTerminalPosition(til::point{ touchdownPoint }));
// stop tracking the touchdown point
_singleClickTouchdownPos = std::nullopt;
@ -316,10 +318,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
SetEndSelectionPoint(pixelPosition);
}
_core->SetHoveredCell(terminalPosition);
_core->SetHoveredCell(terminalPosition.to_core_point());
}
void ControlInteractivity::TouchMoved(const til::point newTouchPoint,
void ControlInteractivity::TouchMoved(const Core::Point newTouchPoint,
const bool focused)
{
if (focused &&
@ -332,19 +334,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto fontSizeInDips{ _core->FontSizeInDips() };
// Get the difference between the point we've dragged to and the start of the touch.
const float dy = ::base::saturated_cast<float>(newTouchPoint.y() - anchor.y());
const auto dy = static_cast<double>(newTouchPoint.Y - anchor.Y);
// Start viewport scroll after we've moved more than a half row of text
if (std::abs(dy) > (fontSizeInDips.height<float>() / 2.0f))
if (std::abs(dy) > (fontSizeInDips.height / 2.0))
{
// Multiply by -1, because moving the touch point down will
// create a positive delta, but we want the viewport to move up,
// so we'll need a negative scroll amount (and the inverse for
// panning down)
const float numRows = -1.0f * (dy / fontSizeInDips.height<float>());
const auto numRows = dy / -fontSizeInDips.height;
const double currentOffset = ::base::ClampedNumeric<double>(_core->ScrollOffset());
const double newValue = numRows + currentOffset;
const auto currentOffset = _core->ScrollOffset();
const auto newValue = numRows + currentOffset;
// Update the Core's viewport position, and raise a
// ScrollPositionChanged event to update the scrollbar
@ -359,9 +361,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::PointerReleased(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const til::point pixelPosition)
const Core::Point pixelPosition)
{
const til::point terminalPosition = _getTerminalPosition(pixelPosition);
const til::point terminalPosition = _getTerminalPosition(til::point{ pixelPosition });
// Short-circuit isReadOnly check to avoid warning dialog
if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
{
@ -402,10 +404,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - delta: the mouse wheel delta that triggered this event.
bool ControlInteractivity::MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const int32_t delta,
const til::point pixelPosition,
const Core::Point pixelPosition,
const Control::MouseButtonState buttonState)
{
const til::point terminalPosition = _getTerminalPosition(pixelPosition);
const til::point terminalPosition = _getTerminalPosition(til::point{ pixelPosition });
// Short-circuit isReadOnly check to avoid warning dialog
if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
@ -470,7 +472,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - pixelPosition: the location of the mouse during this event
// - isLeftButtonPressed: true iff the left mouse button was pressed during this event.
void ControlInteractivity::_mouseScrollHandler(const double mouseDelta,
const til::point pixelPosition,
const Core::Point pixelPosition,
const bool isLeftButtonPressed)
{
// GH#9955.b: Start scrolling from our internal scrollbar position. This
@ -573,9 +575,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
// Arguments:
// - cursorPosition: in pixels, relative to the origin of the control
void ControlInteractivity::SetEndSelectionPoint(const til::point pixelPosition)
void ControlInteractivity::SetEndSelectionPoint(const Core::Point pixelPosition)
{
_core->SetEndSelectionPoint(_getTerminalPosition(pixelPosition));
_core->SetEndSelectionPoint(_getTerminalPosition(til::point{ pixelPosition }));
_selectionNeedsToBeCopied = true;
}
@ -587,7 +589,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// NOTE: origin (0,0) is top-left.
// Return Value:
// - the corresponding viewport terminal position for the given Point parameter
til::point ControlInteractivity::_getTerminalPosition(const til::point& pixelPosition)
til::point ControlInteractivity::_getTerminalPosition(const til::point pixelPosition)
{
// Get the size of the font, which is in pixels
const til::size fontSize{ _core->GetFont().GetSize() };
@ -603,9 +605,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
const auto adjustment = _core->ScrollOffset() > 0 ? _core->BufferHeight() - _core->ScrollOffset() - _core->ViewHeight() : 0;
// If the click happened outside the active region, just don't send any mouse event
if (const auto adjustedY = terminalPosition.y() - adjustment; adjustedY >= 0)
if (const auto adjustedY = terminalPosition.y - adjustment; adjustedY >= 0)
{
return _core->SendMouseEvent({ terminalPosition.x(), adjustedY }, pointerUpdateKind, modifiers, wheelDelta, toInternalMouseState(buttonState));
return _core->SendMouseEvent({ terminalPosition.x, adjustedY }, pointerUpdateKind, modifiers, wheelDelta, toInternalMouseState(buttonState));
}
return false;
}

View File

@ -52,27 +52,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const unsigned int pointerUpdateKind,
const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const til::point pixelPosition);
void TouchPressed(const til::point contactPoint);
const Core::Point pixelPosition);
void TouchPressed(const Core::Point contactPoint);
void PointerMoved(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused,
const til::point pixelPosition,
const Core::Point pixelPosition,
const bool pointerPressedInBounds);
void TouchMoved(const til::point newTouchPoint,
void TouchMoved(const Core::Point newTouchPoint,
const bool focused);
void PointerReleased(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const til::point pixelPosition);
const Core::Point pixelPosition);
void TouchReleased();
bool MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const int32_t delta,
const til::point pixelPosition,
const Core::Point pixelPosition,
const Control::MouseButtonState state);
void UpdateScrollbar(const double newValue);
@ -82,7 +82,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool CopySelectionToClipboard(bool singleLine,
const Windows::Foundation::IReference<CopyFormat>& formats);
void RequestPasteTextFromClipboard();
void SetEndSelectionPoint(const til::point pixelPosition);
void SetEndSelectionPoint(const Core::Point pixelPosition);
bool ManglePathsForWsl();
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
@ -105,7 +105,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// If this is set, then we assume we are in the middle of panning the
// viewport via touch input.
std::optional<til::point> _touchAnchor;
std::optional<Core::Point> _touchAnchor;
using Timestamp = uint64_t;
@ -114,9 +114,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Timestamp _multiClickTimer;
unsigned int _multiClickCounter;
Timestamp _lastMouseClickTimestamp;
std::optional<til::point> _lastMouseClickPos;
std::optional<til::point> _singleClickTouchdownPos;
std::optional<til::point> _lastMouseClickPosNoSelection;
std::optional<Core::Point> _lastMouseClickPos;
std::optional<Core::Point> _singleClickTouchdownPos;
std::optional<Core::Point> _lastMouseClickPosNoSelection;
// This field tracks whether the selection has changed meaningfully
// since it was last copied. It's generally used to prevent copyOnSelect
// from firing when the pointer _just happens_ to be released over the
@ -129,20 +129,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> _lastHoveredInterval{ std::nullopt };
unsigned int _numberOfClicks(til::point clickPos, Timestamp clickTime);
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
void _updateSystemParameterSettings() noexcept;
void _mouseTransparencyHandler(const double mouseDelta);
void _mouseZoomHandler(const double mouseDelta);
void _mouseScrollHandler(const double mouseDelta,
const til::point terminalPosition,
const Core::Point terminalPosition,
const bool isLeftButtonPressed);
void _hyperlinkHandler(const std::wstring_view uri);
bool _canSendVTMouseInput(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers);
void _sendPastedTextToConnection(std::wstring_view wstr);
til::point _getTerminalPosition(const til::point& pixelPosition);
til::point _getTerminalPosition(const til::point pixelPosition);
bool _sendMouseEventHelper(const til::point terminalPosition,
const unsigned int pointerUpdateKind,

View File

@ -37,11 +37,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void InteractivityAutomationPeer::SetControlBounds(const Windows::Foundation::Rect bounds)
{
_controlBounds = til::rectangle{ til::math::rounding, bounds };
_controlBounds = til::rect{ til::math::rounding, bounds };
}
void InteractivityAutomationPeer::SetControlPadding(const Core::Padding padding)
{
_controlPadding = padding;
_controlPadding = til::rect{ til::math::rounding, padding };
}
void InteractivityAutomationPeer::ParentProvider(AutomationPeer parentProvider)
{
@ -143,12 +143,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma region IControlAccessibilityInfo
COORD InteractivityAutomationPeer::GetFontSize() const noexcept
{
return til::size{ til::math::rounding, _interactivity->Core().FontSize() };
return til::size{ til::math::rounding, _interactivity->Core().FontSize() }.to_win32_coord();
}
RECT InteractivityAutomationPeer::GetBounds() const noexcept
{
return _controlBounds;
return _controlBounds.to_win32_rect();
}
HRESULT InteractivityAutomationPeer::GetHostUiaProvider(IRawElementProviderSimple** provider)
@ -161,7 +161,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
RECT InteractivityAutomationPeer::GetPadding() const noexcept
{
return _controlPadding;
return _controlPadding.to_win32_rect();
}
double InteractivityAutomationPeer::GetScaleFactor() const noexcept

View File

@ -81,8 +81,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Microsoft::Terminal::Control::implementation::ControlInteractivity* _interactivity;
weak_ref<Windows::UI::Xaml::Automation::Peers::AutomationPeer> _parentProvider;
til::rectangle _controlBounds{};
til::rectangle _controlPadding{};
til::rect _controlBounds{};
til::rect _controlPadding{};
winrt::com_array<Windows::UI::Xaml::Automation::Provider::ITextRangeProvider> WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges);
};

View File

@ -144,7 +144,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Get the cursor position in text buffer position
auto cursorArgs = winrt::make_self<CursorPositionEventArgs>();
_CurrentCursorPositionHandlers(*this, *cursorArgs);
const til::point cursorPos{ gsl::narrow_cast<ptrdiff_t>(cursorArgs->CurrentPosition().X), gsl::narrow_cast<ptrdiff_t>(cursorArgs->CurrentPosition().Y) };
const til::point cursorPos{ til::math::flooring, cursorArgs->CurrentPosition() };
const double actualCanvasWidth{ Canvas().ActualWidth() };
const double actualTextBlockHeight{ TextBlock().ActualHeight() };
@ -189,14 +189,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Get scale factor for view
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
const til::point clientCursorInDips{ clientCursorPos / scaleFactor };
const til::point clientCursorInDips{ til::math::flooring, clientCursorPos.x / scaleFactor, clientCursorPos.y / scaleFactor };
// Position our TextBlock at the cursor position
Canvas().SetLeft(TextBlock(), clientCursorInDips.x<double>());
Canvas().SetTop(TextBlock(), clientCursorInDips.y<double>());
Canvas().SetLeft(TextBlock(), clientCursorInDips.x);
Canvas().SetTop(TextBlock(), clientCursorInDips.y);
// calculate FontSize in pixels from Points
const double fontSizePx = (fontSize.height<double>() * 72) / USER_DEFAULT_SCREEN_DPI;
const double fontSizePx = (fontSize.height * 72) / USER_DEFAULT_SCREEN_DPI;
const double unscaledFontSizePx = fontSizePx / scaleFactor;
// Make sure to unscale the font size to correct for DPI! XAML needs
@ -212,7 +212,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TextBlock().MinHeight(unscaledFontSizePx);
_currentTextBlockHeight = std::max(unscaledFontSizePx, _currentTextBlockHeight);
const auto widthToTerminalEnd = _currentCanvasWidth - clientCursorInDips.x<double>();
const auto widthToTerminalEnd = _currentCanvasWidth - clientCursorInDips.x;
// Make sure that we're setting the MaxWidth to a positive number - a
// negative number here will crash us in mysterious ways with a useless
// stack trace
@ -221,7 +221,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Get window in screen coordinates, this is the entire window including
// tabs. THIS IS IN DIPs
const til::point windowOrigin{ til::math::flooring, _currentWindowBounds };
const til::point windowOrigin{ til::math::flooring, _currentWindowBounds.X, _currentWindowBounds.Y };
// Get the offset (margin + tabs, etc..) of the control within the window
const til::point controlOrigin{ til::math::flooring,
@ -232,25 +232,25 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const til::point controlAbsoluteOrigin{ windowOrigin + controlOrigin };
// Convert the control origin to pixels
const til::point scaledFrameOrigin = controlAbsoluteOrigin * scaleFactor;
const til::point scaledFrameOrigin = til::point{ til::math::flooring, controlAbsoluteOrigin.x * scaleFactor, controlAbsoluteOrigin.y * scaleFactor };
// Get the location of the cursor in the display, in pixels.
til::point screenCursorPos{ scaledFrameOrigin + clientCursorPos };
// GH #5007 - make sure to account for wrapping the IME composition at
// the right side of the viewport.
const ptrdiff_t textBlockHeight = ::base::ClampMul(_currentTextBlockHeight, scaleFactor);
const auto textBlockHeight = ::base::ClampMul(_currentTextBlockHeight, scaleFactor);
// Get the bounds of the composition text, in pixels.
const til::rectangle textBounds{ til::point{ screenCursorPos.x(), screenCursorPos.y() },
til::size{ 0, textBlockHeight } };
const til::rect textBounds{ til::point{ screenCursorPos.x, screenCursorPos.y },
til::size{ 0, textBlockHeight } };
_currentTextBounds = textBounds;
_currentTextBounds = textBounds.to_winrt_rect();
_currentControlBounds = Rect(screenCursorPos.x<float>(),
screenCursorPos.y<float>(),
0,
fontSize.height<float>());
_currentControlBounds = Rect(static_cast<float>(screenCursorPos.x),
static_cast<float>(screenCursorPos.y),
0.0f,
static_cast<float>(fontSize.height));
}
// Method Description:

View File

@ -1137,7 +1137,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
const auto contactRect = point.Properties().ContactRect();
auto anchor = til::point{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity.TouchPressed(anchor);
_interactivity.TouchPressed(anchor.to_core_point());
}
else
{
@ -1146,7 +1146,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TermControl::GetPointerUpdateKind(point),
point.Timestamp(),
ControlKeyStates{ args.KeyModifiers() },
_toTerminalOrigin(cursorPosition));
_toTerminalOrigin(cursorPosition).to_core_point());
}
args.Handled(true);
@ -1185,7 +1185,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()),
_focused,
pixelPosition,
pixelPosition.to_core_point(),
_pointerPressedInBounds);
// GH#9109 - Only start an auto-scroll when the drag actually
@ -1227,7 +1227,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto contactRect = point.Properties().ContactRect();
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity.TouchMoved(newTouchPoint, _focused);
_interactivity.TouchMoved(newTouchPoint.to_core_point(), _focused);
}
args.Handled(true);
@ -1263,7 +1263,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_interactivity.PointerReleased(TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()),
pixelPosition);
pixelPosition.to_core_point());
}
else if (type == Windows::Devices::Input::PointerDeviceType::Touch)
{
@ -1303,7 +1303,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto result = _interactivity.MouseWheel(ControlKeyStates{ args.KeyModifiers() },
point.Properties().MouseWheelDelta(),
_toTerminalOrigin(point.Position()),
_toTerminalOrigin(point.Position()).to_core_point(),
TermControl::GetPressedMouseButtons(point));
if (result)
{
@ -1334,7 +1334,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WI_SetFlagIf(state, Control::MouseButtonState::IsMiddleButtonDown, midButtonDown);
WI_SetFlagIf(state, Control::MouseButtonState::IsRightButtonDown, rightButtonDown);
return _interactivity.MouseWheel(modifiers, delta, _toTerminalOrigin(location), state);
return _interactivity.MouseWheel(modifiers, delta, _toTerminalOrigin(location).to_core_point(), state);
}
// Method Description:
@ -1704,7 +1704,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - cursorPosition: in pixels, relative to the origin of the control
void TermControl::_SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition)
{
_interactivity.SetEndSelectionPoint(_toTerminalOrigin(cursorPosition));
_interactivity.SetEndSelectionPoint(_toTerminalOrigin(cursorPosition).to_core_point());
}
// Method Description:
@ -2157,7 +2157,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const til::point relativeToMarginInDIPs = cursorPosInDIPs - marginsInDips;
// Convert it to pixels
const til::point relativeToMarginInPixels{ relativeToMarginInDIPs * SwapChainPanel().CompositionScaleX() };
const auto scale = SwapChainPanel().CompositionScaleX();
const til::point relativeToMarginInPixels{
til::math::flooring,
relativeToMarginInDIPs.x * scale,
relativeToMarginInDIPs.y * scale,
};
return relativeToMarginInPixels;
}
@ -2196,10 +2201,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
const til::point cursorPos = _core.CursorPosition();
Windows::Foundation::Point p = { ::base::ClampedNumeric<float>(cursorPos.x()),
::base::ClampedNumeric<float>(cursorPos.y()) };
eventArgs.CurrentPosition(p);
const auto cursorPos = _core.CursorPosition();
eventArgs.CurrentPosition({ static_cast<float>(cursorPos.X), static_cast<float>(cursorPos.Y) });
}
// Method Description:
@ -2597,13 +2600,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto uriText = _core.HoveredUriText();
if (!uriText.empty())
{
const auto panel = SwapChainPanel();
const auto scale = panel.CompositionScaleX();
const auto offset = panel.ActualOffset();
// Update the tooltip with the URI
HoveredUri().Text(uriText);
// Set the border thickness so it covers the entire cell
const auto charSizeInPixels = CharacterDimensions();
const auto htInDips = charSizeInPixels.Height / SwapChainPanel().CompositionScaleY();
const auto wtInDips = charSizeInPixels.Width / SwapChainPanel().CompositionScaleX();
const auto htInDips = charSizeInPixels.Height / scale;
const auto wtInDips = charSizeInPixels.Width / scale;
const Thickness newThickness{ wtInDips, htInDips, 0, 0 };
HyperlinkTooltipBorder().BorderThickness(newThickness);
@ -2612,14 +2619,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const til::point startPos{ lastHoveredCell.Value() };
const til::size fontSize{ til::math::rounding, _core.FontSize() };
const til::point posInPixels{ startPos * fontSize };
const til::point posInDIPs{ posInPixels / SwapChainPanel().CompositionScaleX() };
const til::point posInDIPs{ til::math::flooring, posInPixels.x / scale, posInPixels.y / scale };
const til::point locationInDIPs{ posInDIPs + marginsInDips };
// Move the border to the top left corner of the cell
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(),
(locationInDIPs.x() - SwapChainPanel().ActualOffset().x));
OverlayCanvas().SetTop(HyperlinkTooltipBorder(),
(locationInDIPs.y() - SwapChainPanel().ActualOffset().y));
OverlayCanvas().SetLeft(HyperlinkTooltipBorder(), locationInDIPs.x - offset.x);
OverlayCanvas().SetTop(HyperlinkTooltipBorder(), locationInDIPs.y - offset.y);
}
}
}

View File

@ -515,8 +515,8 @@ std::wstring Terminal::GetHyperlinkAtPosition(const COORD position)
const auto end = result->stop;
std::wstring uri;
const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start));
const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end));
const auto startIter = _buffer->GetCellDataAt(_ConvertToBufferCell(start.to_win32_coord()));
const auto endIter = _buffer->GetCellDataAt(_ConvertToBufferCell(end.to_win32_coord()));
for (auto iter = startIter; iter != endIter; ++iter)
{
uri += iter->Chars();
@ -545,7 +545,7 @@ uint16_t Terminal::GetHyperlinkIdAtPosition(const COORD position)
// - The interval representing the start and end coordinates
std::optional<PointTree::interval> Terminal::GetHyperlinkIntervalFromPosition(const COORD position)
{
const auto results = _patternIntervalTree.findOverlapping(COORD{ position.X + 1, position.Y }, position);
const auto results = _patternIntervalTree.findOverlapping(til::point{ position.X + 1, position.Y }, til::point{ position });
if (results.size() > 0)
{
for (const auto& result : results)
@ -715,8 +715,8 @@ void Terminal::_InvalidatePatternTree(interval_tree::IntervalTree<til::point, si
{
const auto vis = _VisibleStartIndex();
auto invalidate = [=](const PointTree::interval& interval) {
COORD startCoord{ gsl::narrow<SHORT>(interval.start.x()), gsl::narrow<SHORT>(interval.start.y() + vis) };
COORD endCoord{ gsl::narrow<SHORT>(interval.stop.x()), gsl::narrow<SHORT>(interval.stop.y() + vis) };
COORD startCoord{ gsl::narrow<SHORT>(interval.start.x), gsl::narrow<SHORT>(interval.start.y + vis) };
COORD endCoord{ gsl::narrow<SHORT>(interval.stop.x), gsl::narrow<SHORT>(interval.stop.y + vis) };
_InvalidateFromCoords(startCoord, endCoord);
};
tree.visit_all(invalidate);
@ -738,18 +738,18 @@ void Terminal::_InvalidateFromCoords(const COORD start, const COORD end)
const auto rowSize = gsl::narrow<SHORT>(_buffer->GetRowByOffset(0).size());
// invalidate the first line
SMALL_RECT region{ start.X, start.Y, rowSize - 1, start.Y };
SMALL_RECT region{ start.X, start.Y, gsl::narrow<short>(rowSize - 1), gsl::narrow<short>(start.Y) };
_buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region));
if ((end.Y - start.Y) > 1)
{
// invalidate the lines in between the first and last line
region = til::rectangle(0, start.Y + 1, rowSize - 1, end.Y - 1);
region = SMALL_RECT{ 0, start.Y + 1, gsl::narrow<short>(rowSize - 1), gsl::narrow<short>(end.Y - 1) };
_buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region));
}
// invalidate the last line
region = til::rectangle(0, end.Y, end.X, end.Y);
region = SMALL_RECT{ 0, end.Y, end.X, end.Y };
_buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region));
}
}
@ -1104,7 +1104,7 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition)
// We have to report the delta here because we might have circled the text buffer.
// That didn't change the viewport and therefore the TriggerScroll(void)
// method can't detect the delta on its own.
COORD delta{ 0, -rowsPushedOffTopOfBuffer };
COORD delta{ 0, gsl::narrow_cast<short>(-rowsPushedOffTopOfBuffer) };
_buffer->GetRenderTarget().TriggerScroll(&delta);
}

View File

@ -335,11 +335,11 @@ void Terminal::_MoveByChar(SelectionDirection direction, COORD& pos)
{
case SelectionDirection::Left:
_buffer->GetSize().DecrementInBounds(pos);
pos = _buffer->GetGlyphStart(pos);
pos = _buffer->GetGlyphStart(til::point{ pos }).to_win32_coord();
break;
case SelectionDirection::Right:
_buffer->GetSize().IncrementInBounds(pos);
pos = _buffer->GetGlyphEnd(pos);
pos = _buffer->GetGlyphEnd(til::point{ pos }).to_win32_coord();
break;
case SelectionDirection::Up:
{

View File

@ -169,7 +169,7 @@ const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uin
const std::vector<size_t> Terminal::GetPatternId(const COORD location) const noexcept
{
// Look through our interval tree for this location
const auto intervals = _patternIntervalTree.findOverlapping(COORD{ location.X + 1, location.Y }, location);
const auto intervals = _patternIntervalTree.findOverlapping(til::point{ location.X + 1, location.Y }, til::point{ location });
if (intervals.size() == 0)
{
return {};

View File

@ -144,7 +144,7 @@ namespace ControlUnitTests
// The mouse location and buttons don't matter here.
interactivity->MouseWheel(modifiers,
30,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
buttonState);
}
@ -162,7 +162,7 @@ namespace ControlUnitTests
// The mouse location and buttons don't matter here.
interactivity->MouseWheel(modifiers,
-30,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
buttonState);
}
}
@ -219,7 +219,7 @@ namespace ControlUnitTests
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
buttonState);
Log::Comment(L"Scroll up 19 more times, to the top");
@ -228,18 +228,18 @@ namespace ControlUnitTests
expectedTop--;
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
buttonState);
}
Log::Comment(L"Scrolling up more should do nothing");
expectedTop = 0;
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
buttonState);
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
buttonState);
Log::Comment(L"Scroll down 21 more times, to the bottom");
@ -249,7 +249,7 @@ namespace ControlUnitTests
expectedTop++;
interactivity->MouseWheel(modifiers,
-WHEEL_DELTA,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
buttonState);
Log::Comment(NoThrowString().Format(L"internal scrollbar pos:%f", interactivity->_internalScrollbarPosition));
}
@ -257,11 +257,11 @@ namespace ControlUnitTests
expectedTop = 21;
interactivity->MouseWheel(modifiers,
-WHEEL_DELTA,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
buttonState);
interactivity->MouseWheel(modifiers,
-WHEEL_DELTA,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
buttonState);
}
@ -292,7 +292,7 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition0);
cursorPosition0.to_core_point());
Log::Comment(L"Verify that there's not yet a selection");
VERIFY_IS_FALSE(core->HasSelection());
@ -305,7 +305,7 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@ -318,7 +318,7 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition2,
cursorPosition2.to_core_point(),
true);
Log::Comment(L"Verify that there's now two selections (one on each row)");
VERIFY_IS_TRUE(core->HasSelection());
@ -328,7 +328,7 @@ namespace ControlUnitTests
interactivity->PointerReleased(noMouseDown,
WM_LBUTTONUP, //pointerUpdateKind
modifiers,
cursorPosition2);
cursorPosition2.to_core_point());
Log::Comment(L"Verify that there's still two selections");
VERIFY_IS_TRUE(core->HasSelection());
VERIFY_ARE_EQUAL(2u, core->_terminal->GetSelectionRects().size());
@ -340,7 +340,7 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition3);
cursorPosition3.to_core_point());
Log::Comment(L"Verify that there's now no selection");
VERIFY_IS_FALSE(core->HasSelection());
VERIFY_ARE_EQUAL(0u, core->_terminal->GetSelectionRects().size());
@ -352,7 +352,7 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition4,
cursorPosition4.to_core_point(),
true);
Log::Comment(L"Verify that there's now one selection");
VERIFY_IS_TRUE(core->HasSelection());
@ -392,13 +392,13 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition0);
cursorPosition0.to_core_point());
Log::Comment(L"Verify that there's not yet a selection");
VERIFY_IS_FALSE(core->HasSelection());
VERIFY_IS_TRUE(interactivity->_singleClickTouchdownPos.has_value());
VERIFY_ARE_EQUAL(cursorPosition0, interactivity->_singleClickTouchdownPos.value());
VERIFY_ARE_EQUAL(cursorPosition0.to_core_point(), interactivity->_singleClickTouchdownPos.value());
Log::Comment(L"Drag the mouse just a little");
// move not quite a whole cell, but enough to start a selection
@ -407,7 +407,7 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@ -423,7 +423,7 @@ namespace ControlUnitTests
Log::Comment(L"Scroll up a line, with the left mouse button selected");
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
cursorPosition1,
cursorPosition1.to_core_point(),
leftMouseDown);
Log::Comment(L"Verify the location of the selection");
@ -466,7 +466,7 @@ namespace ControlUnitTests
// WHEEL_DELTA is 120, so we'll use 24 for now as the delta, just so the tests don't take forever.
const int delta = WHEEL_DELTA / 5;
const til::point mousePos{ 0, 0 };
const Core::Point mousePos{ 0, 0 };
Control::MouseButtonState state{};
interactivity->MouseWheel(modifiers, delta, mousePos, state); // 1/5
@ -541,21 +541,21 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition0);
cursorPosition0.to_core_point());
Log::Comment(L"Verify that there's not yet a selection");
VERIFY_IS_FALSE(core->HasSelection());
VERIFY_IS_TRUE(interactivity->_singleClickTouchdownPos.has_value());
VERIFY_ARE_EQUAL(cursorPosition0, interactivity->_singleClickTouchdownPos.value());
VERIFY_ARE_EQUAL(cursorPosition0.to_core_point(), interactivity->_singleClickTouchdownPos.value());
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
const til::point cursorPosition1{ 6 + fontSize.width<int>() * 2, 0 };
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@ -586,21 +586,21 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition0);
cursorPosition0.to_core_point());
Log::Comment(L"Verify that there's not yet a selection");
VERIFY_IS_FALSE(core->HasSelection());
VERIFY_IS_TRUE(interactivity->_singleClickTouchdownPos.has_value());
VERIFY_ARE_EQUAL(cursorPosition0, interactivity->_singleClickTouchdownPos.value());
VERIFY_ARE_EQUAL(cursorPosition0.to_core_point(), interactivity->_singleClickTouchdownPos.value());
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
const til::point cursorPosition1{ 6 + fontSize.width<int>() * 2, 0 };
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@ -615,18 +615,18 @@ namespace ControlUnitTests
interactivity->PointerReleased(noMouseDown,
WM_LBUTTONUP,
modifiers,
cursorPosition1);
cursorPosition1.to_core_point());
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
Log::Comment(L"Simulate dragging the mouse into the control, without first clicking into the control");
const til::point cursorPosition2{ fontSize.width<int>() * 10, 0 };
const til::point cursorPosition2{ fontSize.width * 10, 0 };
interactivity->PointerMoved(leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition2,
cursorPosition2.to_core_point(),
false);
Log::Comment(L"The selection should be unchanged.");
@ -689,7 +689,7 @@ namespace ControlUnitTests
expectedTop--;
interactivity->MouseWheel(modifiers,
WHEEL_DELTA,
til::point{ 0, 0 },
Core::Point{ 0, 0 },
noMouseDown);
}
@ -704,7 +704,7 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition0);
cursorPosition0.to_core_point());
Log::Comment(L"Verify that there's not yet a selection");
VERIFY_IS_FALSE(core->HasSelection());
@ -717,7 +717,7 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's still no selection");
VERIFY_IS_FALSE(core->HasSelection());
@ -753,13 +753,13 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
cursorPosition0);
cursorPosition0.to_core_point());
Log::Comment(L"Verify that there's not yet a selection");
VERIFY_IS_FALSE(core->HasSelection());
VERIFY_IS_TRUE(interactivity->_singleClickTouchdownPos.has_value());
VERIFY_ARE_EQUAL(cursorPosition0, interactivity->_singleClickTouchdownPos.value());
VERIFY_ARE_EQUAL(cursorPosition0.to_core_point(), interactivity->_singleClickTouchdownPos.value());
Log::Comment(L"Drag the mouse just a little");
// move not quite a whole cell, but enough to start a selection
@ -768,7 +768,7 @@ namespace ControlUnitTests
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1,
cursorPosition1.to_core_point(),
true);
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());

View File

@ -17,7 +17,6 @@
#include "../../renderer/vt/Xterm256Engine.hpp"
#include "../../renderer/vt/XtermEngine.hpp"
class InputBuffer; // This for some reason needs to be fwd-decl'd
#include "../host/inputBuffer.hpp"
#include "../host/readDataCooked.hpp"
#include "../host/output.h"
@ -227,7 +226,7 @@ private:
void _resizeConpty(const unsigned short sx, const unsigned short sy);
void _clearConpty();
[[nodiscard]] std::tuple<TextBuffer*, TextBuffer*> _performResize(const til::size& newSize);
[[nodiscard]] std::tuple<TextBuffer*, TextBuffer*> _performResize(const til::size newSize);
std::deque<std::string> expectedOutput;
@ -307,7 +306,7 @@ void ConptyRoundtripTests::_clearConpty()
_pConApi->PrivateClearBuffer();
}
[[nodiscard]] std::tuple<TextBuffer*, TextBuffer*> ConptyRoundtripTests::_performResize(const til::size& newSize)
[[nodiscard]] std::tuple<TextBuffer*, TextBuffer*> ConptyRoundtripTests::_performResize(const til::size newSize)
{
// IMPORTANT! Anyone calling this should make sure that the test is running
// in IsolationLevel: Method. If you don't add that, then it might secretly
@ -315,9 +314,9 @@ void ConptyRoundtripTests::_clearConpty()
Log::Comment(L"========== Resize the Terminal and conpty ==========");
auto resizeResult = term->UserResize(newSize);
auto resizeResult = term->UserResize(newSize.to_win32_coord());
VERIFY_SUCCEEDED(resizeResult);
_resizeConpty(newSize.width<unsigned short>(), newSize.height<unsigned short>());
_resizeConpty(newSize.narrow_width<unsigned short>(), newSize.narrow_height<unsigned short>());
// After we resize, make sure to get the new textBuffers
return { &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetTextBuffer(),
@ -900,7 +899,7 @@ void ConptyRoundtripTests::TestResizeHeight()
// Conpty's doesn't have a scrollback, it's view's origin is always 0,0
const auto thirdHostView = si.GetViewport();
VERIFY_ARE_EQUAL(0, thirdHostView.Top());
VERIFY_ARE_EQUAL(newViewportSize.height(), thirdHostView.BottomExclusive());
VERIFY_ARE_EQUAL(newViewportSize.height, thirdHostView.BottomExclusive());
// The Terminal should be stuck to the top of the viewport, unless dy<0,
// rows=50. In that set of cases, we _didn't_ pin the top of the Terminal to
@ -928,7 +927,7 @@ void ConptyRoundtripTests::TestResizeHeight()
// Conpty's doesn't have a scrollback, it's view's origin is always 0,0
const auto fourthHostView = si.GetViewport();
VERIFY_ARE_EQUAL(0, fourthHostView.Top());
VERIFY_ARE_EQUAL(newViewportSize.height(), fourthHostView.BottomExclusive());
VERIFY_ARE_EQUAL(newViewportSize.height, fourthHostView.BottomExclusive());
// The Terminal should be stuck to the top of the viewport, unless dy<0,
// rows=50. In that set of cases, we _didn't_ pin the top of the Terminal to
@ -1086,16 +1085,16 @@ void ConptyRoundtripTests::PassthroughClearAll()
sm.ProcessString(L"~");
}
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport, const bool afterClear = false) {
const auto width = viewport.width<short>();
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport, const bool afterClear = false) {
const auto width = viewport.narrow_width<short>();
// "~" rows
for (short row = 0; row < viewport.bottom<short>(); row++)
for (short row = 0; row < viewport.narrow_bottom<short>(); row++)
{
Log::Comment(NoThrowString().Format(L"Checking row %d", row));
VERIFY_IS_FALSE(tb.GetRowByOffset(row).WasWrapForced());
auto iter = tb.GetCellDataAt({ 0, row });
if (afterClear && row >= viewport.top<short>())
if (afterClear && row >= viewport.narrow_top<short>())
{
TestUtils::VerifySpanOfText(L" ", iter, 0, width);
}
@ -1108,15 +1107,15 @@ void ConptyRoundtripTests::PassthroughClearAll()
};
Log::Comment(L"========== Checking the host buffer state (before) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive());
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (before) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() });
const til::rectangle originalTerminalView{ term->_mutableViewport.ToInclusive() };
const til::rect originalTerminalView{ term->_mutableViewport.ToInclusive() };
// Here, we'll emit the 2J to EraseAll, and move the viewport contents into
// the scrollback.
@ -1127,12 +1126,12 @@ void ConptyRoundtripTests::PassthroughClearAll()
// Make sure that the terminal's new viewport is actually just lower than it
// used to be.
const til::rectangle newTerminalView{ term->_mutableViewport.ToInclusive() };
VERIFY_ARE_EQUAL(end, newTerminalView.top<short>());
VERIFY_IS_GREATER_THAN(newTerminalView.top(), originalTerminalView.top());
const til::rect newTerminalView{ term->_mutableViewport.ToInclusive() };
VERIFY_ARE_EQUAL(end, newTerminalView.narrow_top<short>());
VERIFY_IS_GREATER_THAN(newTerminalView.top, originalTerminalView.top);
Log::Comment(L"========== Checking the host buffer state (after) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), true);
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() }, true);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
@ -1370,9 +1369,9 @@ void ConptyRoundtripTests::OutputWrappedLinesAtBottomOfBuffer()
auto iter0 = tb.GetCellDataAt({ 0, wrappedRow });
TestUtils::VerifySpanOfText(L"A", iter0, 0, TerminalViewWidth);
auto iter1 = tb.GetCellDataAt({ 0, wrappedRow + 1 });
auto iter1 = tb.GetCellDataAt({ 0, gsl::narrow<short>(wrappedRow + 1) });
TestUtils::VerifySpanOfText(L"A", iter1, 0, 20);
auto iter2 = tb.GetCellDataAt({ 20, wrappedRow + 1 });
auto iter2 = tb.GetCellDataAt({ 20, gsl::narrow<short>(wrappedRow + 1) });
TestUtils::VerifySpanOfText(L" ", iter2, 0, TerminalViewWidth - 20);
};
@ -1457,9 +1456,9 @@ void ConptyRoundtripTests::ScrollWithChangesInMiddle()
L"8"); // Restore
hostSm.ProcessString(std::wstring(wrappedLineLength, L'A')); // Print 100 'A's
auto verifyBuffer = [](const TextBuffer& tb, const til::rectangle viewport) {
const short wrappedRow = viewport.bottom<short>() - 2;
const short start = viewport.top<short>();
auto verifyBuffer = [](const TextBuffer& tb, const til::rect& viewport) {
const auto wrappedRow = gsl::narrow<short>(viewport.bottom - 2);
const short start = viewport.narrow_top<short>();
for (short i = start; i < wrappedRow; i++)
{
Log::Comment(NoThrowString().Format(L"Checking row %d", i));
@ -1472,20 +1471,20 @@ void ConptyRoundtripTests::ScrollWithChangesInMiddle()
auto iter0 = tb.GetCellDataAt({ 0, wrappedRow });
TestUtils::VerifySpanOfText(L"A", iter0, 0, TerminalViewWidth);
auto iter1 = tb.GetCellDataAt({ 0, wrappedRow + 1 });
auto iter1 = tb.GetCellDataAt({ 0, gsl::narrow<short>(wrappedRow + 1) });
TestUtils::VerifySpanOfText(L"A", iter1, 0, 20);
auto iter2 = tb.GetCellDataAt({ 20, wrappedRow + 1 });
auto iter2 = tb.GetCellDataAt({ 20, gsl::narrow<short>(wrappedRow + 1) });
TestUtils::VerifySpanOfText(L" ", iter2, 0, TerminalViewWidth - 20);
};
Log::Comment(NoThrowString().Format(L"Checking the host buffer..."));
verifyBuffer(hostTb, hostView.ToInclusive());
verifyBuffer(hostTb, til::rect{ hostView.ToInclusive() });
Log::Comment(NoThrowString().Format(L"... Done"));
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(NoThrowString().Format(L"Checking the terminal buffer..."));
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(termTb, til::rect{ term->_mutableViewport.ToInclusive() });
Log::Comment(NoThrowString().Format(L"... Done"));
}
@ -1840,11 +1839,11 @@ void ConptyRoundtripTests::ClearHostTrickeryTest()
_flushFirstFrame();
auto verifyBuffer = [&cursorOnNextLine, &useLongSpaces, &printTextAfterSpaces](const TextBuffer& tb,
const til::rectangle viewport) {
const til::rect viewport) {
// We _would_ expect the Terminal's cursor to be on { 8, 0 }, but this
// is currently broken due to #381/#4676. So we'll use the viewport
// provided to find the actual Y position of the cursor.
const short viewTop = viewport.origin().y<short>();
const short viewTop = viewport.origin().narrow_y<short>();
const short cursorRow = viewTop + (cursorOnNextLine ? 1 : 0);
const short cursorCol = (cursorOnNextLine ? 5 :
(10 + (useLongSpaces ? 5 : 0) + (printTextAfterSpaces ? 5 : 0)));
@ -1921,13 +1920,13 @@ void ConptyRoundtripTests::ClearHostTrickeryTest()
hostSm.ProcessString(L"\x1b[?1049l");
Log::Comment(L"Checking the host buffer state");
verifyBuffer(hostTb, si.GetViewport().ToInclusive());
verifyBuffer(hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"Checking the terminal buffer state");
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(termTb, til::rect{ term->_mutableViewport.ToInclusive() });
}
void ConptyRoundtripTests::OverstrikeAtBottomOfBuffer()
@ -1948,15 +1947,15 @@ void ConptyRoundtripTests::OverstrikeAtBottomOfBuffer()
_flushFirstFrame();
auto verifyBuffer = [](const TextBuffer& tb,
const til::rectangle viewport) {
const auto lastRow = viewport.bottom<short>() - 1;
const til::rect viewport) {
const auto lastRow = viewport.bottom - 1;
const til::point expectedCursor{ 0, lastRow - 1 };
VERIFY_ARE_EQUAL(expectedCursor, til::point{ tb.GetCursor().GetPosition() });
VERIFY_IS_TRUE(tb.GetCursor().IsVisible());
TestUtils::VerifyExpectedString(tb, L"AAAAAAAAAA DDDDDDDDDD", til::point{ 0, lastRow - 2 });
TestUtils::VerifyExpectedString(tb, L"BBBBBBBBBB", til::point{ 0, lastRow - 1 });
TestUtils::VerifyExpectedString(tb, L"FFFFFFFFFE", til::point{ 0, lastRow });
TestUtils::VerifyExpectedString(tb, L"AAAAAAAAAA DDDDDDDDDD", COORD{ 0, gsl::narrow<short>(lastRow - 2) });
TestUtils::VerifyExpectedString(tb, L"BBBBBBBBBB", COORD{ 0, gsl::narrow<short>(lastRow - 1) });
TestUtils::VerifyExpectedString(tb, L"FFFFFFFFFE", COORD{ 0, gsl::narrow<short>(lastRow) });
};
_logConpty = true;
@ -1993,14 +1992,14 @@ void ConptyRoundtripTests::OverstrikeAtBottomOfBuffer()
hostSm.ProcessString(L"\n");
Log::Comment(L"========== Checking the host buffer state ==========");
verifyBuffer(hostTb, si.GetViewport().ToInclusive());
verifyBuffer(hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state ==========");
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(termTb, til::rect{ term->_mutableViewport.ToInclusive() });
}
void ConptyRoundtripTests::MarginsWithStatusLine()
@ -2026,17 +2025,17 @@ void ConptyRoundtripTests::MarginsWithStatusLine()
_flushFirstFrame();
auto verifyBuffer = [](const TextBuffer& tb,
const til::rectangle viewport) {
const auto lastRow = viewport.bottom<short>() - 1;
const til::rect viewport) {
const auto lastRow = gsl::narrow<short>(viewport.bottom - 1);
const til::point expectedCursor{ 1, lastRow };
VERIFY_ARE_EQUAL(expectedCursor, til::point{ tb.GetCursor().GetPosition() });
VERIFY_IS_TRUE(tb.GetCursor().IsVisible());
TestUtils::VerifyExpectedString(tb, L"EEEEEEEEEE", til::point{ 0, lastRow - 4 });
TestUtils::VerifyExpectedString(tb, L"AAAAAAAAAA", til::point{ 0, lastRow - 3 });
TestUtils::VerifyExpectedString(tb, L" ", til::point{ 0, lastRow - 2 });
TestUtils::VerifyExpectedString(tb, L"XBBBBBBBBB", til::point{ 0, lastRow - 1 });
TestUtils::VerifyExpectedString(tb, L"YCCCCCCCCC", til::point{ 0, lastRow });
TestUtils::VerifyExpectedString(tb, L"EEEEEEEEEE", COORD{ 0, gsl::narrow<short>(lastRow - 4) });
TestUtils::VerifyExpectedString(tb, L"AAAAAAAAAA", COORD{ 0, gsl::narrow<short>(lastRow - 3) });
TestUtils::VerifyExpectedString(tb, L" ", COORD{ 0, gsl::narrow<short>(lastRow - 2) });
TestUtils::VerifyExpectedString(tb, L"XBBBBBBBBB", COORD{ 0, gsl::narrow<short>(lastRow - 1) });
TestUtils::VerifyExpectedString(tb, L"YCCCCCCCCC", COORD{ 0, lastRow });
};
// We're _not_ checking the conpty output during this test, only the side effects.
@ -2076,7 +2075,7 @@ void ConptyRoundtripTests::MarginsWithStatusLine()
src.Left = 0;
src.Right = si.GetViewport().Width();
src.Bottom = originalBottom;
COORD tgt = { 0, newBottom - 1 };
COORD tgt{ 0, gsl::narrow<short>(newBottom - 1) };
TextAttribute useThisAttr(0x07); // We don't terribly care about the attributes so this is arbitrary
ScrollRegion(si, src, std::nullopt, tgt, L' ', useThisAttr);
}
@ -2090,14 +2089,14 @@ void ConptyRoundtripTests::MarginsWithStatusLine()
hostSm.ProcessString(L"Y");
Log::Comment(L"========== Checking the host buffer state ==========");
verifyBuffer(hostTb, si.GetViewport().ToInclusive());
verifyBuffer(hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state ==========");
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(termTb, til::rect{ term->_mutableViewport.ToInclusive() });
}
void ConptyRoundtripTests::OutputWrappedLineWithSpace()
@ -2273,7 +2272,7 @@ void ConptyRoundtripTests::OutputWrappedLineWithSpaceAtBottomOfBuffer()
sm.ProcessString(std::wstring(spacesLength, L' '));
sm.ProcessString(std::wstring(secondTextLength, L'B'));
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport) {
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport) {
// Buffer contents should look like the following: (80 wide)
// (w) means we hard wrapped the line
// (b) means the line is _not_ wrapped (it's broken, the default state.)
@ -2282,7 +2281,7 @@ void ConptyRoundtripTests::OutputWrappedLineWithSpaceAtBottomOfBuffer()
// | B_ ... | (b) (cursor is on the '_')
// | ... | (b)
const short wrappedRow = viewport.bottom<short>() - 2;
const auto wrappedRow = gsl::narrow<short>(viewport.bottom - 2);
VERIFY_IS_TRUE(tb.GetRowByOffset(wrappedRow).WasWrapForced());
VERIFY_IS_FALSE(tb.GetRowByOffset(wrappedRow + 1).WasWrapForced());
@ -2292,20 +2291,20 @@ void ConptyRoundtripTests::OutputWrappedLineWithSpaceAtBottomOfBuffer()
TestUtils::VerifySpanOfText(L" ", iter0, 0, 2);
// Second row
auto iter1 = tb.GetCellDataAt({ 0, wrappedRow + 1 });
auto iter1 = tb.GetCellDataAt({ 0, gsl::narrow<short>(wrappedRow + 1) });
TestUtils::VerifySpanOfText(L" ", iter1, 0, 1);
auto iter2 = tb.GetCellDataAt({ 1, wrappedRow + 1 });
auto iter2 = tb.GetCellDataAt({ 1, gsl::narrow<short>(wrappedRow + 1) });
TestUtils::VerifySpanOfText(L"B", iter2, 0, secondTextLength);
};
Log::Comment(L"========== Checking the host buffer state ==========");
verifyBuffer(hostTb, hostView.ToInclusive());
verifyBuffer(hostTb, til::rect{ hostView.ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state ==========");
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(termTb, til::rect{ term->_mutableViewport.ToInclusive() });
}
void ConptyRoundtripTests::BreakLinesOnCursorMovement()
@ -2344,22 +2343,22 @@ void ConptyRoundtripTests::BreakLinesOnCursorMovement()
(cursorMovementMode == MoveCursorWithCUB_LF);
auto verifyBuffer = [&](const TextBuffer& tb,
const til::rectangle viewport) {
const auto lastRow = viewport.bottom<short>() - 1;
const til::rect viewport) {
const auto lastRow = gsl::narrow<short>(viewport.bottom - 1);
const til::point expectedCursor{ 5, lastRow };
VERIFY_ARE_EQUAL(expectedCursor, til::point{ tb.GetCursor().GetPosition() });
VERIFY_IS_TRUE(tb.GetCursor().IsVisible());
for (auto y = viewport.top<short>(); y < lastRow; y++)
for (auto y = viewport.narrow_top<short>(); y < lastRow; y++)
{
// We're using CUP to move onto the status line _always_, so the
// second-last row will always be marked as wrapped.
const auto rowWrapped = (!expectHardBreak) || (y == lastRow - 1);
VERIFY_ARE_EQUAL(rowWrapped, tb.GetRowByOffset(y).WasWrapForced());
TestUtils::VerifyExpectedString(tb, L"~ ", til::point{ 0, y });
TestUtils::VerifyExpectedString(tb, L"~ ", COORD{ 0, y });
}
TestUtils::VerifyExpectedString(tb, L"AAAAA", til::point{ 0, lastRow });
TestUtils::VerifyExpectedString(tb, L"AAAAA", COORD{ 0, lastRow });
};
// We're _not_ checking the conpty output during this test, only the side effects.
@ -2468,14 +2467,14 @@ void ConptyRoundtripTests::BreakLinesOnCursorMovement()
hostSm.ProcessString(L"AAAAA");
Log::Comment(L"========== Checking the host buffer state ==========");
verifyBuffer(altTextBuffer, altBuffer.GetViewport().ToInclusive());
verifyBuffer(altTextBuffer, til::rect{ altBuffer.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state ==========");
verifyBuffer(termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(termTb, til::rect{ term->_mutableViewport.ToInclusive() });
}
void ConptyRoundtripTests::TestCursorInDeferredEOLPositionOnNewLineWithSpaces()
@ -2519,7 +2518,7 @@ void ConptyRoundtripTests::TestCursorInDeferredEOLPositionOnNewLineWithSpaces()
VERIFY_IS_FALSE(lastRow.WasWrapForced());
auto expectedStringSecondToLastRow{ std::wstring(gsl::narrow_cast<size_t>(tb.GetSize().Width()) - 1, L'A') + L" " };
TestUtils::VerifyExpectedString(tb, expectedStringSecondToLastRow, { 0, bottomRow - 1 });
TestUtils::VerifyExpectedString(tb, expectedStringSecondToLastRow, { 0, gsl::narrow<short>(bottomRow - 1) });
TestUtils::VerifyExpectedString(tb, L"B", { 0, bottomRow });
};
@ -2588,9 +2587,9 @@ void ConptyRoundtripTests::ResizeRepaintVimExeBuffer()
drawVim();
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport) {
const auto firstRow = viewport.top<short>();
const auto width = viewport.width<short>();
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport) {
const auto firstRow = viewport.narrow_top<short>();
const auto width = viewport.narrow_width<short>();
// First row
VERIFY_IS_FALSE(tb.GetRowByOffset(firstRow).WasWrapForced());
@ -2600,12 +2599,12 @@ void ConptyRoundtripTests::ResizeRepaintVimExeBuffer()
// Second row
VERIFY_IS_FALSE(tb.GetRowByOffset(firstRow + 1).WasWrapForced());
auto iter1 = tb.GetCellDataAt({ 0, firstRow + 1 });
auto iter1 = tb.GetCellDataAt({ 0, gsl::narrow<short>(firstRow + 1) });
TestUtils::VerifySpanOfText(L"B", iter1, 0, 3);
TestUtils::VerifySpanOfText(L" ", iter1, 0, width - 3);
// "~" rows
for (short row = firstRow + 2; row < viewport.bottom<short>() - 1; row++)
for (short row = firstRow + 2; row < viewport.bottom - 1; row++)
{
Log::Comment(NoThrowString().Format(L"Checking row %d", row));
VERIFY_IS_TRUE(tb.GetRowByOffset(row).WasWrapForced());
@ -2616,7 +2615,7 @@ void ConptyRoundtripTests::ResizeRepaintVimExeBuffer()
// Last row
{
short row = viewport.bottom<short>() - 1;
const auto row = gsl::narrow<short>(viewport.bottom - 1);
Log::Comment(NoThrowString().Format(L"Checking row %d", row));
VERIFY_IS_TRUE(tb.GetRowByOffset(row).WasWrapForced());
auto iter = tb.GetCellDataAt({ 0, row });
@ -2626,13 +2625,13 @@ void ConptyRoundtripTests::ResizeRepaintVimExeBuffer()
};
Log::Comment(L"========== Checking the host buffer state (before) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive());
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (before) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() });
// After we resize, make sure to get the new textBuffers
std::tie(hostTb, termTb) = _performResize({ TerminalViewWidth - 1,
@ -2646,13 +2645,13 @@ void ConptyRoundtripTests::ResizeRepaintVimExeBuffer()
drawVim();
Log::Comment(L"========== Checking the host buffer state (after) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive());
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (after) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() });
}
void ConptyRoundtripTests::ClsAndClearHostClearsScrollbackTest()
@ -2698,11 +2697,11 @@ void ConptyRoundtripTests::ClsAndClearHostClearsScrollbackTest()
sm.ProcessString(L"~");
}
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport, const bool afterClear = false) {
const auto width = viewport.width<short>();
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport, const bool afterClear = false) {
const auto width = viewport.narrow_width<short>();
// "~" rows
for (short row = 0; row < viewport.bottom<short>(); row++)
for (short row = 0; row < viewport.narrow_bottom<short>(); row++)
{
Log::Comment(NoThrowString().Format(L"Checking row %d", row));
VERIFY_IS_FALSE(tb.GetRowByOffset(row).WasWrapForced());
@ -2720,13 +2719,13 @@ void ConptyRoundtripTests::ClsAndClearHostClearsScrollbackTest()
};
Log::Comment(L"========== Checking the host buffer state (before) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive());
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (before) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() });
VERIFY_ARE_EQUAL(si.GetViewport().Dimensions(), si.GetBufferSize().Dimensions());
VERIFY_ARE_EQUAL(si.GetViewport(), si.GetBufferSize());
@ -2790,12 +2789,12 @@ void ConptyRoundtripTests::ClsAndClearHostClearsScrollbackTest()
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the host buffer state (after) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), true);
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() }, true);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (after) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive(), true);
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() }, true);
}
void ConptyRoundtripTests::TestResizeWithCookedRead()
@ -2934,8 +2933,8 @@ void ConptyRoundtripTests::ResizeInitializeBufferWithDefaultAttrs()
sm.ProcessString(L"#");
}
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport, const bool isTerminal, const bool afterResize) {
const auto width = viewport.width<short>();
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport, const bool isTerminal, const bool afterResize) {
const auto width = viewport.narrow_width<short>();
// Conhost and Terminal attributes are potentially different.
const auto greenAttrs = isTerminal ? terminalGreenAttrs : conhostGreenAttrs;
@ -2967,19 +2966,19 @@ void ConptyRoundtripTests::ResizeInitializeBufferWithDefaultAttrs()
}
else
{
TestUtils::VerifyLineContains(tb, { 0, row }, L' ', actualDefaultAttrs, viewport.width<size_t>());
TestUtils::VerifyLineContains(tb, { 0, row }, L' ', actualDefaultAttrs, viewport.narrow_width<size_t>());
}
}
};
Log::Comment(L"========== Checking the host buffer state (before) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), false, false);
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() }, false, false);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (before) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive(), true, false);
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() }, true, false);
// After we resize, make sure to get the new textBuffers
std::tie(hostTb, termTb) = _performResize({ TerminalViewWidth + dx,
@ -2989,13 +2988,13 @@ void ConptyRoundtripTests::ResizeInitializeBufferWithDefaultAttrs()
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the host buffer state (after) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), false, true);
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() }, false, true);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (after) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive(), true, true);
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() }, true, true);
}
void ConptyRoundtripTests::NewLinesAtBottomWithBackground()
@ -3072,20 +3071,20 @@ void ConptyRoundtripTests::NewLinesAtBottomWithBackground()
}
}
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport) {
const auto width = viewport.width<short>();
const auto isTerminal = viewport.top() != 0;
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport) {
const auto width = viewport.narrow_width<short>();
const auto isTerminal = viewport.top != 0;
// Conhost and Terminal attributes are potentially different.
const auto blueAttrs = isTerminal ? terminalBlueAttrs : conhostBlueAttrs;
for (short row = 0; row < viewport.bottom<short>() - 2; row++)
for (short row = 0; row < viewport.bottom - 2; row++)
{
Log::Comment(NoThrowString().Format(L"Checking row %d", row));
VERIFY_IS_FALSE(tb.GetRowByOffset(row).WasWrapForced());
const auto isBlank = (row % 2) == 0;
const auto rowCircled = row > (viewport.bottom<short>() - 1 - circledRows);
const auto rowCircled = row > (viewport.bottom - 1 - circledRows);
// When the buffer circles, new lines will be initialized using the
// current text attributes. Those will be the default-on-default
// attributes. All of the Terminal's buffer will use
@ -3095,7 +3094,7 @@ void ConptyRoundtripTests::NewLinesAtBottomWithBackground()
if (isBlank)
{
TestUtils::VerifyLineContains(tb, { 0, row }, L' ', actualDefaultAttrs, viewport.width<size_t>());
TestUtils::VerifyLineContains(tb, { 0, row }, L' ', actualDefaultAttrs, viewport.narrow_width<size_t>());
}
else
{
@ -3109,13 +3108,13 @@ void ConptyRoundtripTests::NewLinesAtBottomWithBackground()
};
Log::Comment(L"========== Checking the host buffer state ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive());
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() });
}
void doWriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view string, DWORD flags = 0)
@ -3272,17 +3271,17 @@ void ConptyRoundtripTests::WrapNewLineAtBottom()
// row[4]: |~~~~~~~~~~~~~~~~~~~| <wrap>
// row[5]: |~~~~~~ | <break>
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport) {
const auto width = viewport.width<short>();
const auto isTerminal = viewport.top() != 0;
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport) {
const auto width = viewport.narrow_width<short>();
const auto isTerminal = viewport.top != 0;
for (short row = 0; row < viewport.bottom<short>(); row++)
for (short row = 0; row < viewport.narrow_bottom<short>(); row++)
{
Log::Comment(NoThrowString().Format(L"Checking row %d", row));
// The first line wrapped, the second didn't, so on and so forth
const auto isWrapped = (row % 2) == 0;
const auto rowCircled = row >= (viewport.bottom<short>() - circledRows);
const auto rowCircled = row >= (viewport.bottom - circledRows);
const auto actualNonSpacesAttrs = defaultAttrs;
const auto actualSpacesAttrs = rowCircled || isTerminal ? defaultAttrs : conhostDefaultAttrs;
@ -3290,25 +3289,25 @@ void ConptyRoundtripTests::WrapNewLineAtBottom()
VERIFY_ARE_EQUAL(isWrapped, tb.GetRowByOffset(row).WasWrapForced());
if (isWrapped)
{
TestUtils::VerifyExpectedString(tb, std::wstring(charsInFirstLine, L'~'), til::point{ 0, row });
TestUtils::VerifyExpectedString(tb, std::wstring(charsInFirstLine, L'~'), COORD{ 0, row });
}
else
{
auto iter = TestUtils::VerifyExpectedString(tb, std::wstring(charsInSecondLine, L'~'), til::point{ 0, row });
auto iter = TestUtils::VerifyExpectedString(tb, std::wstring(charsInSecondLine, L'~'), COORD{ 0, row });
TestUtils::VerifyExpectedString(std::wstring(width - charsInSecondLine, L' '), iter);
}
}
};
Log::Comment(L"========== Checking the host buffer state ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive());
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state ==========");
VERIFY_ARE_EQUAL(circledRows, term->_mutableViewport.Top());
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() });
}
void ConptyRoundtripTests::WrapNewLineAtBottomLikeMSYS()
@ -3474,10 +3473,10 @@ void ConptyRoundtripTests::WrapNewLineAtBottomLikeMSYS()
}
}
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport) {
const auto width = viewport.width<short>();
const auto isTerminal = viewport.top() != 0;
auto lastRow = viewport.bottom<short>() - 1;
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport) {
const auto width = viewport.narrow_width<short>();
const auto isTerminal = viewport.top != 0;
auto lastRow = gsl::narrow<short>(viewport.bottom - 1);
for (short row = 0; row < lastRow; row++)
{
Log::Comment(NoThrowString().Format(L"Checking row %d", row));
@ -3489,7 +3488,7 @@ void ConptyRoundtripTests::WrapNewLineAtBottomLikeMSYS()
// buffer, then the top line of the conpty will _not_ be wrapped,
// when the 0th line of the terminal buffer _is_.
const auto isWrapped = (row % 2) == (isTerminal ? 0 : 1);
const auto rowCircled = row >= (viewport.bottom<short>() - circledRows);
const auto rowCircled = row >= (viewport.bottom - circledRows);
const auto actualNonSpacesAttrs = defaultAttrs;
const auto actualSpacesAttrs = rowCircled || isTerminal ? defaultAttrs : conhostDefaultAttrs;
@ -3497,28 +3496,28 @@ void ConptyRoundtripTests::WrapNewLineAtBottomLikeMSYS()
VERIFY_ARE_EQUAL(isWrapped, tb.GetRowByOffset(row).WasWrapForced());
if (isWrapped)
{
TestUtils::VerifyExpectedString(tb, std::wstring(charsInFirstLine, L'~'), til::point{ 0, row });
TestUtils::VerifyExpectedString(tb, std::wstring(charsInFirstLine, L'~'), COORD{ 0, row });
}
else
{
auto iter = TestUtils::VerifyExpectedString(tb, std::wstring(charsInSecondLine, L'~'), til::point{ 0, row });
auto iter = TestUtils::VerifyExpectedString(tb, std::wstring(charsInSecondLine, L'~'), COORD{ 0, row });
TestUtils::VerifyExpectedString(std::wstring(width - charsInSecondLine, L' '), iter);
}
}
VERIFY_IS_FALSE(tb.GetRowByOffset(lastRow).WasWrapForced());
auto iter = TestUtils::VerifyExpectedString(tb, std::wstring(1, L':'), til::point{ 0, lastRow });
auto iter = TestUtils::VerifyExpectedString(tb, std::wstring(1, L':'), COORD{ 0, lastRow });
TestUtils::VerifyExpectedString(std::wstring(width - 1, L' '), iter);
};
Log::Comment(L"========== Checking the host buffer state ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive());
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() });
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state ==========");
VERIFY_ARE_EQUAL(circledRows + 1, term->_mutableViewport.Top());
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive());
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() });
}
void ConptyRoundtripTests::DeleteWrappedWord()
@ -3550,8 +3549,8 @@ void ConptyRoundtripTests::DeleteWrappedWord()
sm.ProcessString(std::wstring(50, L'B'));
sm.ProcessString(L"\x1b[?25h");
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport, const bool after) {
const auto width = viewport.width<short>();
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport, const bool after) {
const auto width = viewport.narrow_width<short>();
auto iter1 = tb.GetCellDataAt({ 0, 0 });
TestUtils::VerifySpanOfText(L"A", iter1, 0, 50);
@ -3574,12 +3573,12 @@ void ConptyRoundtripTests::DeleteWrappedWord()
};
Log::Comment(L"========== Checking the host buffer state (before) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), false);
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() }, false);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (before) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive(), false);
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() }, false);
// Now, go back and erase all the 'B's, as if the user executed a
// backward-kill-word in PowerShell. Afterwards, the buffer will look like:
@ -3603,12 +3602,12 @@ void ConptyRoundtripTests::DeleteWrappedWord()
sm.ProcessString(L"\x1b[?25h");
Log::Comment(L"========== Checking the host buffer state (after) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), true);
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() }, true);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (after) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive(), true);
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() }, true);
}
// This test checks that upon conpty rendering again, terminal still maintains
@ -3712,8 +3711,8 @@ void ConptyRoundtripTests::ClearBufferSignal()
sm.ProcessString(L"\x1b[?m");
sm.ProcessString(L"\x1b[?25h");
auto verifyBuffer = [&](const TextBuffer& tb, const til::rectangle viewport, const bool before) {
const short width = viewport.width<short>();
auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& viewport, const bool before) {
const short width = viewport.narrow_width<short>();
const short numCharsOnSecondLine = 50 - (width - 51);
auto iter1 = tb.GetCellDataAt({ 0, 0 });
if (before)
@ -3733,21 +3732,21 @@ void ConptyRoundtripTests::ClearBufferSignal()
};
Log::Comment(L"========== Checking the host buffer state (before) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), true);
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() }, true);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (before) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive(), true);
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() }, true);
Log::Comment(L"========== Clear the ConPTY buffer with the signal ==========");
_clearConpty();
Log::Comment(L"========== Checking the host buffer state (after) ==========");
verifyBuffer(*hostTb, si.GetViewport().ToInclusive(), false);
verifyBuffer(*hostTb, til::rect{ si.GetViewport().ToInclusive() }, false);
Log::Comment(L"Painting the frame");
VERIFY_SUCCEEDED(renderer.PaintFrame());
Log::Comment(L"========== Checking the terminal buffer state (after) ==========");
verifyBuffer(*termTb, term->_mutableViewport.ToInclusive(), false);
verifyBuffer(*termTb, til::rect{ term->_mutableViewport.ToInclusive() }, false);
}

View File

@ -3,23 +3,6 @@
#pragma once
// Method Description:
// - Scales a Rect based on a scale factor
// Arguments:
// - rect: Rect to scale by scale
// - scale: amount to scale rect by
// Return Value:
// - Rect scaled by scale
inline winrt::Windows::Foundation::Rect ScaleRect(winrt::Windows::Foundation::Rect rect, double scale)
{
const auto scaleLocal = base::ClampedNumeric<float>(scale);
rect.X = base::ClampMul(rect.X, scaleLocal);
rect.Y = base::ClampMul(rect.Y, scaleLocal);
rect.Width = base::ClampMul(rect.Width, scaleLocal);
rect.Height = base::ClampMul(rect.Height, scaleLocal);
return rect;
}
// Function Description:
// - This function presents a File Open "common dialog" and returns its selected file asynchronously.
// Parameters:

View File

@ -532,10 +532,10 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, LaunchMode
// Get the size of a window we'd need to host that client rect. This will
// add the titlebar space.
const til::size nonClientSize = _window->GetTotalNonClientExclusiveSize(dpix);
const til::rectangle nonClientFrame = _window->GetNonClientFrame(dpix);
adjustedWidth = islandWidth + nonClientSize.width<long>();
adjustedHeight = islandHeight + nonClientSize.height<long>();
const til::size nonClientSize{ _window->GetTotalNonClientExclusiveSize(dpix) };
const til::rect nonClientFrame{ _window->GetNonClientFrame(dpix) };
adjustedWidth = islandWidth + nonClientSize.width;
adjustedHeight = islandHeight + nonClientSize.height;
til::size dimensions{ Utils::ClampToShortMax(adjustedWidth, 1),
Utils::ClampToShortMax(adjustedHeight, 1) };
@ -554,7 +554,7 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, LaunchMode
// for the top here - the IslandWindow includes the titlebar in
// nonClientFrame.top, so adjusting for that would actually place the
// titlebar _off_ the monitor.
til::point origin{ (proposedRect.left + nonClientFrame.left<LONG>()),
til::point origin{ (proposedRect.left + nonClientFrame.left),
(proposedRect.top) };
if (_logic.IsQuakeWindow())
@ -564,12 +564,12 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, LaunchMode
const til::size availableSpace = desktopDimensions + nonClientSize;
origin = til::point{
::base::ClampSub<long>(nearestMonitorInfo.rcWork.left, (nonClientSize.width() / 2)),
::base::ClampSub(nearestMonitorInfo.rcWork.left, (nonClientSize.width / 2)),
(nearestMonitorInfo.rcWork.top)
};
dimensions = til::size{
availableSpace.width(),
availableSpace.height() / 2
availableSpace.width,
availableSpace.height / 2
};
launchMode = LaunchMode::FocusMode;
}
@ -577,18 +577,18 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, RECT proposedRect, LaunchMode
{
// Move our proposed location into the center of that specific monitor.
origin = til::point{
(nearestMonitorInfo.rcWork.left + ((desktopDimensions.width() / 2) - (dimensions.width() / 2))),
(nearestMonitorInfo.rcWork.top + ((desktopDimensions.height() / 2) - (dimensions.height() / 2)))
(nearestMonitorInfo.rcWork.left + ((desktopDimensions.width / 2) - (dimensions.width / 2))),
(nearestMonitorInfo.rcWork.top + ((desktopDimensions.height / 2) - (dimensions.height / 2)))
};
}
const til::rectangle newRect{ origin, dimensions };
const til::rect newRect{ origin, dimensions };
bool succeeded = SetWindowPos(hwnd,
nullptr,
newRect.left<int>(),
newRect.top<int>(),
newRect.width<int>(),
newRect.height<int>(),
newRect.left,
newRect.top,
newRect.width(),
newRect.height(),
SWP_NOACTIVATE | SWP_NOZORDER);
// Refresh the dpi of HWND because the dpi where the window will launch may be different
@ -705,7 +705,7 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta)
if (_logic)
{
// Find all the elements that are underneath the mouse
auto elems = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::FindElementsInHostCoordinates(coord, _logic.GetRoot());
auto elems = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::FindElementsInHostCoordinates(coord.to_winrt_point(), _logic.GetRoot());
for (const auto& e : elems)
{
// If that element has implemented IMouseWheelListener, call OnMouseWheel on that element.
@ -716,7 +716,7 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta)
// Translate the event to the coordinate space of the control
// we're attempting to dispatch it to
const auto transform = e.TransformToVisual(nullptr);
const til::point controlOrigin{ til::math::flooring, transform.TransformPoint(til::point{ 0, 0 }) };
const til::point controlOrigin{ til::math::flooring, transform.TransformPoint({}) };
const til::point offsetPoint = coord - controlOrigin;
@ -724,7 +724,7 @@ void AppHost::_WindowMouseWheeled(const til::point coord, const int32_t delta)
const auto mButtonDown = WI_IsFlagSet(GetKeyState(VK_MBUTTON), KeyPressed);
const auto rButtonDown = WI_IsFlagSet(GetKeyState(VK_RBUTTON), KeyPressed);
if (control.OnMouseWheel(offsetPoint, delta, lButtonDown, mButtonDown, rButtonDown))
if (control.OnMouseWheel(offsetPoint.to_winrt_point(), delta, lButtonDown, mButtonDown, rButtonDown))
{
// If the element handled the mouse wheel event, don't
// continue to iterate over the remaining controls.

View File

@ -202,8 +202,7 @@ LRESULT IslandWindow::_OnSizing(const WPARAM wParam, const LPARAM lParam)
// bad parameters, which we won't have, so no big deal.
LOG_IF_FAILED(GetDpiForMonitor(hmon, MDT_EFFECTIVE_DPI, &dpix, &dpiy));
const auto widthScale = base::ClampedNumeric<float>(dpix) / USER_DEFAULT_SCREEN_DPI;
const long minWidthScaled = minimumWidth * widthScale;
const long minWidthScaled = minimumWidth * dpix / USER_DEFAULT_SCREEN_DPI;
const auto nonClientSize = GetTotalNonClientExclusiveSize(dpix);
@ -383,11 +382,10 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam
// From now we use dpix for all computations (same as in _OnSizing).
const auto nonClientSizeScaled = GetTotalNonClientExclusiveSize(dpix);
const auto scale = base::ClampedNumeric<float>(dpix) / USER_DEFAULT_SCREEN_DPI;
auto lpMinMaxInfo = reinterpret_cast<LPMINMAXINFO>(lParam);
lpMinMaxInfo->ptMinTrackSize.x = _calculateTotalSize(true, minimumWidth * scale, nonClientSizeScaled.cx);
lpMinMaxInfo->ptMinTrackSize.y = _calculateTotalSize(false, minimumHeight * scale, nonClientSizeScaled.cy);
lpMinMaxInfo->ptMinTrackSize.x = _calculateTotalSize(true, minimumWidth * dpix / USER_DEFAULT_SCREEN_DPI, nonClientSizeScaled.cx);
lpMinMaxInfo->ptMinTrackSize.y = _calculateTotalSize(false, minimumHeight * dpiy / USER_DEFAULT_SCREEN_DPI, nonClientSizeScaled.cy);
}
// Method Description:
@ -521,11 +519,12 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
// and HIWORD treat the coordinates as unsigned quantities.
const til::point eventPoint{ GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam) };
// This mouse event is relative to the display origin, not the window. Convert here.
const til::rectangle windowRect{ GetWindowRect() };
const til::rect windowRect{ GetWindowRect() };
const auto origin = windowRect.origin();
const auto relative = eventPoint - origin;
// Convert to logical scaling before raising the event.
const auto real = relative / GetCurrentDpiScale();
const auto scale = GetCurrentDpiScale();
const til::point real{ til::math::flooring, relative.x / scale, relative.y / scale };
const short wheelDelta = static_cast<short>(HIWORD(wparam));
@ -574,8 +573,8 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
GetMonitorInfo(proposed, &proposedInfo);
// If the monitor changed...
if (til::rectangle{ proposedInfo.rcMonitor } !=
til::rectangle{ currentInfo.rcMonitor })
if (til::rect{ proposedInfo.rcMonitor } !=
til::rect{ currentInfo.rcMonitor })
{
const auto newWindowRect{ _getQuakeModeSize(proposed) };
@ -583,10 +582,10 @@ long IslandWindow::_calculateTotalSize(const bool isWidth, const long clientSize
// and dimensions that _getQuakeModeSize returned. When we
// snap across monitor boundaries, this will re-evaluate our
// size for the new monitor.
lpwpos->x = newWindowRect.left<int>();
lpwpos->y = newWindowRect.top<int>();
lpwpos->cx = newWindowRect.width<int>();
lpwpos->cy = newWindowRect.height<int>();
lpwpos->x = newWindowRect.left;
lpwpos->y = newWindowRect.top;
lpwpos->cx = newWindowRect.width();
lpwpos->cy = newWindowRect.height();
return 0;
}
@ -973,14 +972,14 @@ void IslandWindow::_SetIsBorderless(const bool borderlessEnabled)
// Resize the window, with SWP_FRAMECHANGED, to trigger user32 to
// recalculate the non/client areas
const til::rectangle windowPos{ GetWindowRect() };
const til::rect windowPos{ GetWindowRect() };
SetWindowPos(GetHandle(),
HWND_TOP,
windowPos.left<int>(),
windowPos.top<int>(),
windowPos.width<int>(),
windowPos.height<int>(),
windowPos.left,
windowPos.top,
windowPos.width(),
windowPos.height(),
SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE);
}
@ -1048,8 +1047,8 @@ void IslandWindow::_RestoreFullscreenPosition(const RECT rcWork)
// We want to make sure the window is restored within the bounds of the
// monitor we're on, but it's totally fine if the invisible borders are
// outside the monitor.
const auto halfWidth{ ncSize.width<long>() / 2 };
const auto halfHeight{ ncSize.height<long>() / 2 };
const auto halfWidth{ ncSize.width / 2 };
const auto halfHeight{ ncSize.height / 2 };
rcWorkAdjusted.left -= halfWidth;
rcWorkAdjusted.right += halfWidth;
@ -1254,8 +1253,8 @@ void IslandWindow::_summonWindowRoutineBody(Remoting::SummonWindowBehavior args)
// mouse, then we should move to that monitor instead of dismissing.
if (args.ToMonitor() == Remoting::MonitorBehavior::ToMouse)
{
const til::rectangle cursorMonitorRect{ _getMonitorForCursor().rcMonitor };
const til::rectangle currentMonitorRect{ _getMonitorForWindow(GetHandle()).rcMonitor };
const til::rect cursorMonitorRect{ _getMonitorForCursor().rcMonitor };
const til::rect currentMonitorRect{ _getMonitorForWindow(GetHandle()).rcMonitor };
if (cursorMonitorRect != currentMonitorRect)
{
// We're not on the same monitor as the mouse. Go to that monitor.
@ -1292,8 +1291,8 @@ void IslandWindow::_summonWindowRoutineBody(Remoting::SummonWindowBehavior args)
// - <none>
void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool down)
{
til::rectangle fullWindowSize{ GetWindowRect() };
const double fullHeight = fullWindowSize.height<double>();
til::rect fullWindowSize{ GetWindowRect() };
const auto fullHeight = fullWindowSize.height();
const double animationDuration = dropdownDuration; // use floating-point math throughout
const auto start = std::chrono::system_clock::now();
@ -1312,15 +1311,11 @@ void IslandWindow::_doSlideAnimation(const uint32_t dropdownDuration, const bool
}
// If going down, increase the height over time. If going up, decrease the height.
const double currentHeight = ::base::saturated_cast<double>(
const auto currentHeight = ::base::saturated_cast<int>(
down ? ((dt / animationDuration) * fullHeight) :
((1.0 - (dt / animationDuration)) * fullHeight));
wil::unique_hrgn rgn{ CreateRectRgn(0,
0,
fullWindowSize.width<int>(),
::base::saturated_cast<int>(currentHeight)) };
wil::unique_hrgn rgn{ CreateRectRgn(0, 0, fullWindowSize.width(), currentHeight) };
SetWindowRgn(_interopWindowHandle, rgn.get(), true);
// Go immediately into another frame. This prevents the window from
@ -1562,20 +1557,20 @@ void IslandWindow::_moveToMonitor(const MONITORINFO activeMonitor)
// Get the monitor info for the window's current monitor.
const auto currentMonitor = _getMonitorForWindow(GetHandle());
const til::rectangle currentRect{ currentMonitor.rcMonitor };
const til::rectangle activeRect{ activeMonitor.rcMonitor };
const til::rect currentRect{ currentMonitor.rcMonitor };
const til::rect activeRect{ activeMonitor.rcMonitor };
if (currentRect != activeRect)
{
const til::rectangle currentWindowRect{ GetWindowRect() };
const til::rect currentWindowRect{ GetWindowRect() };
const til::point offset{ currentWindowRect.origin() - currentRect.origin() };
const til::point newOrigin{ activeRect.origin() + offset };
SetWindowPos(GetHandle(),
0,
newOrigin.x<int>(),
newOrigin.y<int>(),
currentWindowRect.width<int>(),
currentWindowRect.height<int>(),
newOrigin.x,
newOrigin.y,
currentWindowRect.width(),
currentWindowRect.height(),
SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
// GH#10274, GH#10182: Re-evaluate the size of the quake window when we
@ -1627,10 +1622,10 @@ void IslandWindow::_enterQuakeMode()
SetWindowPos(GetHandle(),
HWND_TOP,
newRect.left<int>(),
newRect.top<int>(),
newRect.width<int>(),
newRect.height<int>(),
newRect.left,
newRect.top,
newRect.width(),
newRect.height(),
SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE);
}
@ -1642,7 +1637,7 @@ void IslandWindow::_enterQuakeMode()
// - <none>
// Return Value:
// - <none>
til::rectangle IslandWindow::_getQuakeModeSize(HMONITOR hmon)
til::rect IslandWindow::_getQuakeModeSize(HMONITOR hmon)
{
MONITORINFO nearestMonitorInfo;
@ -1667,15 +1662,15 @@ til::rectangle IslandWindow::_getQuakeModeSize(HMONITOR hmon)
// smaller on either side to account for that, so they don't hang onto
// adjacent monitors.
const til::point origin{
::base::ClampSub<long>(nearestMonitorInfo.rcWork.left, (ncSize.width() / 2)) + 1,
::base::ClampSub(nearestMonitorInfo.rcWork.left, (ncSize.width / 2)) + 1,
(nearestMonitorInfo.rcWork.top)
};
const til::size dimensions{
availableSpace.width() - 2,
availableSpace.height() / 2
availableSpace.width - 2,
availableSpace.height / 2
};
return til::rectangle{ origin, dimensions };
return til::rect{ origin, dimensions };
}
void IslandWindow::HideWindow()

View File

@ -134,7 +134,7 @@ protected:
bool _isQuakeWindow{ false };
void _enterQuakeMode();
til::rectangle _getQuakeModeSize(HMONITOR hmon);
til::rect _getQuakeModeSize(HMONITOR hmon);
void _summonWindowRoutineBody(winrt::Microsoft::Terminal::Remoting::SummonWindowBehavior args);

View File

@ -87,7 +87,7 @@ void NonClientIslandWindow::MakeWindow() noexcept
THROW_HR_IF_NULL(E_UNEXPECTED, _dragBarWindow);
}
LRESULT NonClientIslandWindow::_dragBarNcHitTest(const til::point& pointer)
LRESULT NonClientIslandWindow::_dragBarNcHitTest(const til::point pointer)
{
RECT rcParent = GetWindowRect();
// The size of the buttons doesn't change over the life of the application.
@ -97,23 +97,23 @@ LRESULT NonClientIslandWindow::_dragBarNcHitTest(const til::point& pointer)
const auto buttonWidthInPixels{ buttonWidthInDips * GetCurrentDpiScale() };
// make sure to account for the width of the window frame!
const til::rectangle nonClientFrame{ GetNonClientFrame(_currentDpi) };
const auto rightBorder{ rcParent.right - nonClientFrame.right<int>() };
const til::rect nonClientFrame{ GetNonClientFrame(_currentDpi) };
const auto rightBorder{ rcParent.right - nonClientFrame.right };
// From the right to the left,
// * are we in the close button?
// * the maximize button?
// * the minimize button?
// If we're not, then we're in either the top resize border, or just
// generally in the titlebar.
if ((rightBorder - pointer.x()) < (buttonWidthInPixels))
if ((rightBorder - pointer.x) < (buttonWidthInPixels))
{
return HTCLOSE;
}
else if ((rightBorder - pointer.x()) < (buttonWidthInPixels * 2))
else if ((rightBorder - pointer.x) < (buttonWidthInPixels * 2))
{
return HTMAXBUTTON;
}
else if ((rightBorder - pointer.x()) < (buttonWidthInPixels * 3))
else if ((rightBorder - pointer.x) < (buttonWidthInPixels * 3))
{
return HTMINBUTTON;
}
@ -123,7 +123,7 @@ LRESULT NonClientIslandWindow::_dragBarNcHitTest(const til::point& pointer)
// border. If we're not on the top border, then we're just generally in
// the caption area.
const auto resizeBorderHeight = _GetResizeHandleHeight();
const auto isOnResizeBorder = pointer.y() < rcParent.top + resizeBorderHeight;
const auto isOnResizeBorder = pointer.y < rcParent.top + resizeBorderHeight;
return isOnResizeBorder ? HTTOP : HTCAPTION;
}
@ -294,15 +294,15 @@ LRESULT NonClientIslandWindow::_InputSinkMessageHandler(UINT const message,
// This window is used to capture clicks on the non-client area.
void NonClientIslandWindow::_ResizeDragBarWindow() noexcept
{
const til::rectangle rect{ _GetDragAreaRect() };
const til::rect rect{ _GetDragAreaRect() };
if (_IsTitlebarVisible() && rect.size().area() > 0)
{
SetWindowPos(_dragBarWindow.get(),
HWND_TOP,
rect.left<int>(),
rect.top<int>() + _GetTopBorderHeight(),
rect.width<int>(),
rect.height<int>(),
rect.left,
rect.top + _GetTopBorderHeight(),
rect.width(),
rect.height(),
SWP_NOACTIVATE | SWP_SHOWWINDOW);
SetLayeredWindowAttributes(_dragBarWindow.get(), 0, 255, LWA_ALPHA);
}
@ -1087,13 +1087,13 @@ void NonClientIslandWindow::_SetIsBorderless(const bool borderlessEnabled)
// Resize the window, with SWP_FRAMECHANGED, to trigger user32 to
// recalculate the non/client areas
const til::rectangle windowPos{ GetWindowRect() };
const til::rect windowPos{ GetWindowRect() };
SetWindowPos(GetHandle(),
HWND_TOP,
windowPos.left<int>(),
windowPos.top<int>(),
windowPos.width<int>(),
windowPos.height<int>(),
windowPos.left,
windowPos.top,
windowPos.width(),
windowPos.height(),
SWP_SHOWWINDOW | SWP_FRAMECHANGED | SWP_NOACTIVATE);
}

View File

@ -72,7 +72,7 @@ private:
int _GetResizeHandleHeight() const noexcept;
RECT _GetDragAreaRect() const noexcept;
int _GetTopBorderHeight() const noexcept;
LRESULT _dragBarNcHitTest(const til::point& pointer);
LRESULT _dragBarNcHitTest(const til::point pointer);
[[nodiscard]] LRESULT _OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept override;
[[nodiscard]] LRESULT _OnNcCalcSize(const WPARAM wParam, const LPARAM lParam) noexcept;

View File

@ -109,7 +109,7 @@ void NotificationIcon::CreateNotificationIcon()
// - peasants: The map of all peasants that should be available in the context menu.
// Return Value:
// - <none>
void NotificationIcon::ShowContextMenu(const til::point& coord,
void NotificationIcon::ShowContextMenu(const til::point coord,
const IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants)
{
if (const auto hMenu = _CreateContextMenu(peasants))
@ -132,7 +132,7 @@ void NotificationIcon::ShowContextMenu(const til::point& coord,
uFlags |= TPM_LEFTALIGN;
}
TrackPopupMenuEx(hMenu, uFlags, gsl::narrow_cast<int>(coord.x()), gsl::narrow_cast<int>(coord.y()), _owningHwnd, NULL);
TrackPopupMenuEx(hMenu, uFlags, coord.x, coord.y, _owningHwnd, NULL);
}
}

View File

@ -23,7 +23,7 @@ public:
void ReAddNotificationIcon();
void NotificationIconPressed();
void ShowContextMenu(const til::point& coord, const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
void ShowContextMenu(const til::point coord, const winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Terminal::Remoting::PeasantInfo>& peasants);
void MenuItemSelected(const HMENU menu, const UINT menuItemIndex);
WINRT_CALLBACK(SummonWindowRequested, winrt::delegate<void(winrt::Microsoft::Terminal::Remoting::SummonWindowSelectionArgs)>);

View File

@ -24,6 +24,8 @@ Revision History:
#include "../server/IWaitRoutine.h"
#include "../server/WaitTerminationReason.h"
class InputBuffer;
class ReadData : public IWaitRoutine
{
public:

View File

@ -2196,7 +2196,7 @@ void TextBufferTests::MoveByWord()
Log::Comment(NoThrowString().Format(L"COORD (%hd, %hd)", test.startPos.X, test.startPos.Y));
auto pos{ test.startPos };
const auto result = movingForwards ?
_buffer->MoveToNextWord(pos, delimiters, lastCharPos) :
_buffer->MoveToNextWord(pos, delimiters, til::point{ lastCharPos }) :
_buffer->MoveToPreviousWord(pos, delimiters);
const auto expected = movingForwards ? test.expected.moveForwards : test.expected.moveBackwards;
VERIFY_ARE_EQUAL(expected, pos);
@ -2250,7 +2250,7 @@ void TextBufferTests::GetGlyphBoundaries()
{
Log::Comment(test.name.c_str());
auto target = test.start;
_buffer->Write(iter, target);
_buffer->Write(iter, target.to_win32_coord());
auto start = _buffer->GetGlyphStart(target);
auto end = _buffer->GetGlyphEnd(target, true);

View File

@ -234,7 +234,7 @@ void VtRendererTest::Xterm256TestInvalidate()
VERIFY_SUCCEEDED(engine->Invalidate(&invalid));
TestPaint(*engine, [&]() {
VERIFY_IS_TRUE(engine->_invalidMap.one());
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
});
Log::Comment(NoThrowString().Format(
@ -249,7 +249,7 @@ void VtRendererTest::Xterm256TestInvalidate()
const auto runs = engine->_invalidMap.runs();
VERIFY_ARE_EQUAL(1u, runs.size());
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
qExpectedInput.push_back("\x1b[H"); // Go Home
qExpectedInput.push_back("\x1b[L"); // insert a line
@ -275,7 +275,7 @@ void VtRendererTest::Xterm256TestInvalidate()
}
// verify the rect matches the invalid one.
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
// We would expect a CUP here, but the cursor is already at the home position
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
VERIFY_SUCCEEDED(engine->ScrollFrame());
@ -291,7 +291,7 @@ void VtRendererTest::Xterm256TestInvalidate()
const auto runs = engine->_invalidMap.runs();
VERIFY_ARE_EQUAL(1u, runs.size());
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
qExpectedInput.push_back("\x1b[32;1H"); // Bottom of buffer
qExpectedInput.push_back("\n"); // Scroll down once
@ -316,7 +316,7 @@ void VtRendererTest::Xterm256TestInvalidate()
}
// verify the rect matches the invalid one.
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
// We would expect a CUP here, but we're already at the bottom from the last call.
qExpectedInput.push_back("\n\n\n"); // Scroll down three times
@ -346,7 +346,7 @@ void VtRendererTest::Xterm256TestInvalidate()
}
// verify the rect matches the invalid one.
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
qExpectedInput.push_back("\x1b[H"); // Go to home
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
@ -386,7 +386,7 @@ void VtRendererTest::Xterm256TestInvalidate()
// 0000
// 0000
// 1111
const til::rectangle expected{ til::point{ view.Left(), view.BottomInclusive() }, til::size{ view.Width(), 1 } };
const til::rect expected{ til::point{ view.Left(), view.BottomInclusive() }, til::size{ view.Width(), 1 } };
VERIFY_ARE_EQUAL(expected, invalidRect);
VERIFY_SUCCEEDED(engine->ScrollFrame());
@ -912,7 +912,7 @@ void VtRendererTest::XtermTestInvalidate()
VERIFY_SUCCEEDED(engine->Invalidate(&invalid));
TestPaint(*engine, [&]() {
VERIFY_IS_TRUE(engine->_invalidMap.one());
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, *(engine->_invalidMap.begin()));
});
Log::Comment(NoThrowString().Format(
@ -927,7 +927,7 @@ void VtRendererTest::XtermTestInvalidate()
const auto runs = engine->_invalidMap.runs();
VERIFY_ARE_EQUAL(1u, runs.size());
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
qExpectedInput.push_back("\x1b[H"); // Go Home
qExpectedInput.push_back("\x1b[L"); // insert a line
@ -952,7 +952,7 @@ void VtRendererTest::XtermTestInvalidate()
}
// verify the rect matches the invalid one.
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
// We would expect a CUP here, but the cursor is already at the home position
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
VERIFY_SUCCEEDED(engine->ScrollFrame());
@ -968,7 +968,7 @@ void VtRendererTest::XtermTestInvalidate()
const auto runs = engine->_invalidMap.runs();
VERIFY_ARE_EQUAL(1u, runs.size());
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, runs.front());
qExpectedInput.push_back("\x1b[32;1H"); // Bottom of buffer
qExpectedInput.push_back("\n"); // Scroll down once
@ -993,7 +993,7 @@ void VtRendererTest::XtermTestInvalidate()
}
// verify the rect matches the invalid one.
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
// We would expect a CUP here, but we're already at the bottom from the last call.
qExpectedInput.push_back("\n\n\n"); // Scroll down three times
@ -1023,7 +1023,7 @@ void VtRendererTest::XtermTestInvalidate()
}
// verify the rect matches the invalid one.
VERIFY_ARE_EQUAL(til::rectangle{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
VERIFY_ARE_EQUAL(til::rect{ Viewport::FromExclusive(invalid).ToInclusive() }, invalidRect);
qExpectedInput.push_back("\x1b[H"); // Go to home
qExpectedInput.push_back("\x1b[3L"); // insert 3 lines
@ -1063,7 +1063,7 @@ void VtRendererTest::XtermTestInvalidate()
// 0000
// 0000
// 1111
const til::rectangle expected{ til::point{ view.Left(), view.BottomInclusive() }, til::size{ view.Width(), 1 } };
const til::rect expected{ til::point{ view.Left(), view.BottomInclusive() }, til::size{ view.Width(), 1 } };
VERIFY_ARE_EQUAL(expected, invalidRect);
VERIFY_SUCCEEDED(engine->ScrollFrame());

View File

@ -242,66 +242,14 @@ namespace WEX::TestExecution
public:
static bool AreEqual(const CONSOLE_SCREEN_BUFFER_INFOEX& expected, const CONSOLE_SCREEN_BUFFER_INFOEX& actual)
{
return expected.bFullscreenSupported == actual.bFullscreenSupported &&
expected.wAttributes == actual.wAttributes &&
expected.wPopupAttributes == actual.wPopupAttributes &&
VerifyCompareTraits<COORD>::AreEqual(expected.dwCursorPosition, actual.dwCursorPosition) &&
VerifyCompareTraits<COORD>::AreEqual(expected.dwSize, actual.dwSize) &&
VerifyCompareTraits<COORD>::AreEqual(expected.dwMaximumWindowSize, actual.dwMaximumWindowSize) &&
VerifyCompareTraits<SMALL_RECT>::AreEqual(expected.srWindow, actual.srWindow) &&
expected.ColorTable[0] == actual.ColorTable[0] &&
expected.ColorTable[1] == actual.ColorTable[1] &&
expected.ColorTable[2] == actual.ColorTable[2] &&
expected.ColorTable[3] == actual.ColorTable[3] &&
expected.ColorTable[4] == actual.ColorTable[4] &&
expected.ColorTable[5] == actual.ColorTable[5] &&
expected.ColorTable[6] == actual.ColorTable[6] &&
expected.ColorTable[7] == actual.ColorTable[7] &&
expected.ColorTable[8] == actual.ColorTable[8] &&
expected.ColorTable[9] == actual.ColorTable[9] &&
expected.ColorTable[10] == actual.ColorTable[10] &&
expected.ColorTable[11] == actual.ColorTable[11] &&
expected.ColorTable[12] == actual.ColorTable[12] &&
expected.ColorTable[13] == actual.ColorTable[13] &&
expected.ColorTable[14] == actual.ColorTable[14] &&
expected.ColorTable[15] == actual.ColorTable[15];
static_assert(std::has_unique_object_representations_v<CONSOLE_SCREEN_BUFFER_INFOEX>);
return memcmp(&expected, &actual, sizeof(CONSOLE_SCREEN_BUFFER_INFOEX)) == 0;
}
static bool AreSame(const CONSOLE_SCREEN_BUFFER_INFOEX& expected, const CONSOLE_SCREEN_BUFFER_INFOEX& actual)
{
return &expected == &actual;
}
static bool IsLessThan(const CONSOLE_SCREEN_BUFFER_INFOEX& expectedLess, const CONSOLE_SCREEN_BUFFER_INFOEX& expectedGreater) = delete;
static bool IsGreaterThan(const CONSOLE_SCREEN_BUFFER_INFOEX& expectedGreater, const CONSOLE_SCREEN_BUFFER_INFOEX& expectedLess) = delete;
static bool IsNull(const CONSOLE_SCREEN_BUFFER_INFOEX& object)
{
return object.bFullscreenSupported == 0 &&
object.wAttributes == 0 &&
object.wPopupAttributes == 0 &&
VerifyCompareTraits<COORD>::IsNull(object.dwCursorPosition) &&
VerifyCompareTraits<COORD>::IsNull(object.dwSize) &&
VerifyCompareTraits<COORD>::IsNull(object.dwMaximumWindowSize) &&
VerifyCompareTraits<SMALL_RECT>::IsNull(object.srWindow) &&
object.ColorTable[0] == 0x0 &&
object.ColorTable[1] == 0x0 &&
object.ColorTable[2] == 0x0 &&
object.ColorTable[3] == 0x0 &&
object.ColorTable[4] == 0x0 &&
object.ColorTable[5] == 0x0 &&
object.ColorTable[6] == 0x0 &&
object.ColorTable[7] == 0x0 &&
object.ColorTable[8] == 0x0 &&
object.ColorTable[9] == 0x0 &&
object.ColorTable[10] == 0x0 &&
object.ColorTable[11] == 0x0 &&
object.ColorTable[12] == 0x0 &&
object.ColorTable[13] == 0x0 &&
object.ColorTable[14] == 0x0 &&
object.ColorTable[15] == 0x0;
}
};
template<>

View File

@ -6,22 +6,15 @@
#define _TIL_INLINEPREFIX __declspec(noinline) inline
#include "til/at.h"
#include "til/color.h"
#include "til/math.h"
#include "til/some.h"
#include "til/size.h"
#include "til/point.h"
#include "til/operators.h"
#include "til/rectangle.h"
#include "til/rle.h"
#include "til/bitmap.h"
#include "til/u8u16convert.h"
#include "til/spsc.h"
#include "til/coalesce.h"
#include "til/replace.h"
#include "til/string.h"
#include "til/pmr.h"
#include "til/color.h"
#include "til/enumset.h"
#include "til/pmr.h"
#include "til/replace.h"
#include "til/rle.h"
#include "til/string.h"
#include "til/u8u16convert.h"
// Use keywords on TraceLogging providers to specify the category
// of event that we are emitting for filtering purposes.

View File

@ -3,6 +3,8 @@
#pragma once
#include "rect.h"
#ifdef UNIT_TESTING
class BitmapTests;
#endif
@ -15,13 +17,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
class _bitmap_const_iterator
{
public:
using iterator_category = typename std::input_iterator_tag;
using value_type = typename const til::rectangle;
using difference_type = typename ptrdiff_t;
using pointer = typename const til::rectangle*;
using reference = typename const til::rectangle&;
using iterator_category = std::input_iterator_tag;
using value_type = const til::rect;
using difference_type = ptrdiff_t;
using pointer = const til::rect*;
using reference = const til::rect&;
_bitmap_const_iterator(const dynamic_bitset<unsigned long long, Allocator>& values, til::rectangle rc, ptrdiff_t pos) :
_bitmap_const_iterator(const dynamic_bitset<unsigned long long, Allocator>& values, til::rect rc, ptrdiff_t pos) :
_values(values),
_rc(rc),
_pos(pos),
@ -76,11 +78,11 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
private:
const dynamic_bitset<unsigned long long, Allocator>& _values;
const til::rectangle _rc;
ptrdiff_t _pos;
ptrdiff_t _nextPos;
const ptrdiff_t _end;
til::rectangle _run;
const til::rect _rc;
size_t _pos;
size_t _nextPos;
const size_t _end;
til::rect _run;
// Update _run to contain the next rectangle of consecutively set bits within this bitmap.
// _calculateArea may be called repeatedly to yield all those rectangles.
@ -92,23 +94,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
// dynamic_bitset allows you to quickly find the next set bit using find_next(prev),
// where "prev" is the position _past_ which should be searched (i.e. excluding position "prev").
// If _pos is still 0, we thus need to use the counterpart find_first().
const auto nextPos = _pos == 0 ? _values.find_first() : _values.find_next(_pos - 1);
// If no next set bit can be found, npos is returned, which is SIZE_T_MAX.
// saturated_cast can ensure that this will be converted to PTRDIFF_T_MAX (which is greater than _end).
_nextPos = base::saturated_cast<ptrdiff_t>(nextPos);
_nextPos = _pos == 0 ? _values.find_first() : _values.find_next(_pos - 1);
// If we haven't reached the end yet...
if (_nextPos < _end)
{
// pos is now at the first on bit.
const auto runStart = _rc.point_at(_nextPos);
// If no next set bit can be found, npos is returned, which is SIZE_T_MAX.
// saturated_cast can ensure that this will be converted to CoordType's max (which is greater than _end).
const auto runStart = _rc.point_at(base::saturated_cast<CoordType>(_nextPos));
// We'll only count up until the end of this row.
// a run can be a max of one row tall.
const ptrdiff_t rowEndIndex = _rc.index_of(til::point(_rc.right() - 1, runStart.y())) + 1;
const size_t rowEndIndex = _rc.index_of<size_t>(til::point(_rc.right - 1, runStart.y)) + 1;
// Find the length for the rectangle.
ptrdiff_t runLength = 0;
size_t runLength = 0;
// We have at least 1 so start with a do/while.
do
@ -119,7 +120,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
// Keep going until we reach end of row, end of the buffer, or the next bit is off.
// Assemble and store that run.
_run = til::rectangle{ runStart, til::size{ runLength, static_cast<ptrdiff_t>(1) } };
_run = til::rect{ runStart, til::size{ base::saturated_cast<CoordType>(runLength), 1 } };
}
else
{
@ -127,7 +128,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
// ---> Mark the end of the iterator by updating the state with _end.
_pos = _end;
_nextPos = _end;
_run = til::rectangle{};
_run = til::rect{};
}
}
};
@ -140,7 +141,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
using const_iterator = details::_bitmap_const_iterator<allocator_type>;
private:
using run_allocator_type = typename std::allocator_traits<allocator_type>::template rebind_alloc<til::rectangle>;
using run_allocator_type = typename std::allocator_traits<allocator_type>::template rebind_alloc<til::rect>;
public:
explicit bitmap(const allocator_type& allocator) noexcept :
@ -255,15 +256,15 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
const_iterator begin() const
{
return const_iterator(_bits, _sz, 0);
return const_iterator(_bits, til::rect{ _sz }, 0);
}
const_iterator end() const
{
return const_iterator(_bits, _sz, _sz.area());
return const_iterator(_bits, til::rect{ _sz }, _sz.area());
}
const gsl::span<const til::rectangle> runs() const
const gsl::span<const til::rect> runs() const
{
// If we don't have cached runs, rebuild.
if (!_runs.has_value())
@ -278,10 +279,10 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
// optional fill the uncovered area with bits.
void translate(const til::point delta, bool fill = false)
{
if (delta.x() == 0)
if (delta.x == 0)
{
// fast path by using bit shifting
translate_y(delta.y(), fill);
translate_y(delta.y, fill);
return;
}
@ -356,14 +357,14 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
_bits.set(_rc.index_of(pt));
}
void set(const til::rectangle rc)
void set(const til::rect& rc)
{
THROW_HR_IF(E_INVALIDARG, !_rc.contains(rc));
_runs.reset(); // reset cached runs on any non-const method
for (auto row = rc.top(); row < rc.bottom(); ++row)
for (auto row = rc.top; row < rc.bottom; ++row)
{
_bits.set(_rc.index_of(til::point{ rc.left(), row }), rc.width(), true);
_bits.set(_rc.index_of(til::point{ rc.left, row }), rc.width(), true);
}
}
@ -480,7 +481,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return;
}
const auto bitShift = delta_y * _sz.width();
const auto bitShift = delta_y * _sz.width;
#pragma warning(push)
// we can't depend on GSL here, so we use static_cast for explicit narrowing
@ -530,10 +531,10 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
allocator_type _alloc;
til::size _sz;
til::rectangle _rc;
til::rect _rc;
dynamic_bitset<unsigned long long, allocator_type> _bits;
mutable std::optional<std::vector<til::rectangle, run_allocator_type>> _runs;
mutable std::optional<std::vector<til::rect, run_allocator_type>> _runs;
#ifdef UNIT_TESTING
friend class ::BitmapTests;

View File

@ -12,19 +12,30 @@ namespace til
{
namespace details
{
// Just like gsl::narrow, but also checks for NAN.
template<typename O, typename T>
constexpr O narrow_float(T val)
{
const auto o = gsl::narrow_cast<O>(val);
if (std::isnan(val) || static_cast<T>(o) != val)
{
throw gsl::narrowing_error{};
}
return o;
}
struct ceiling_t
{
template<typename O, typename T>
static O cast(T val)
static constexpr O cast(T val)
{
if constexpr (std::is_floating_point_v<T>)
{
THROW_HR_IF(E_ABORT, ::std::isnan(val));
return ::base::saturated_cast<O>(::std::ceil(val));
return narrow_float<O, T>(std::ceil(val));
}
else
{
return ::base::saturated_cast<O>(val);
return gsl::narrow<O>(val);
}
}
};
@ -32,16 +43,15 @@ namespace til
struct flooring_t
{
template<typename O, typename T>
static O cast(T val)
static constexpr O cast(T val)
{
if constexpr (std::is_floating_point_v<T>)
{
THROW_HR_IF(E_ABORT, ::std::isnan(val));
return ::base::saturated_cast<O>(::std::floor(val));
return narrow_float<O, T>(std::floor(val));
}
else
{
return ::base::saturated_cast<O>(val);
return gsl::narrow<O>(val);
}
}
};
@ -49,37 +59,22 @@ namespace til
struct rounding_t
{
template<typename O, typename T>
static O cast(T val)
static constexpr O cast(T val)
{
if constexpr (std::is_floating_point_v<T>)
{
THROW_HR_IF(E_ABORT, ::std::isnan(val));
return ::base::saturated_cast<O>(::std::round(val));
return narrow_float<O, T>(std::round(val));
}
else
{
return ::base::saturated_cast<O>(val);
return gsl::narrow<O>(val);
}
}
};
struct truncating_t
{
template<typename O, typename T>
static O cast(T val)
{
if constexpr (std::is_floating_point_v<T>)
{
THROW_HR_IF(E_ABORT, ::std::isnan(val));
}
return ::base::saturated_cast<O>(val);
}
};
}
static constexpr details::ceiling_t ceiling; // positives become more positive, negatives become less negative
static constexpr details::flooring_t flooring; // positives become less positive, negatives become more negative
static constexpr details::rounding_t rounding; // it's rounding, from math class
static constexpr details::truncating_t truncating; // drop the decimal point, regardless of how close it is to the next value
}
}

View File

@ -9,47 +9,47 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
#pragma region POINT VS SIZE
// This is a convenience and will take X vs WIDTH and Y vs HEIGHT.
_TIL_INLINEPREFIX point operator+(const point& lhs, const size& rhs)
constexpr point operator+(const point lhs, const size rhs)
{
return lhs + til::point{ rhs.width(), rhs.height() };
return lhs + til::point{ rhs.width, rhs.height };
}
_TIL_INLINEPREFIX point operator-(const point& lhs, const size& rhs)
constexpr point operator-(const point lhs, const size rhs)
{
return lhs - til::point{ rhs.width(), rhs.height() };
return lhs - til::point{ rhs.width, rhs.height };
}
_TIL_INLINEPREFIX point operator*(const point& lhs, const size& rhs)
constexpr point operator*(const point lhs, const size rhs)
{
return lhs * til::point{ rhs.width(), rhs.height() };
return lhs * til::point{ rhs.width, rhs.height };
}
_TIL_INLINEPREFIX point operator/(const point& lhs, const size& rhs)
constexpr point operator/(const point lhs, const size rhs)
{
return lhs / til::point{ rhs.width(), rhs.height() };
return lhs / til::point{ rhs.width, rhs.height };
}
#pragma endregion
#pragma region SIZE VS POINT
// This is a convenience and will take WIDTH vs X and HEIGHT vs Y.
_TIL_INLINEPREFIX size operator+(const size& lhs, const point& rhs)
constexpr size operator+(const size lhs, const point rhs)
{
return lhs + til::size(rhs.x(), rhs.y());
return lhs + til::size(rhs.x, rhs.y);
}
_TIL_INLINEPREFIX size operator-(const size& lhs, const point& rhs)
constexpr size operator-(const size lhs, const point rhs)
{
return lhs - til::size(rhs.x(), rhs.y());
return lhs - til::size(rhs.x, rhs.y);
}
_TIL_INLINEPREFIX size operator*(const size& lhs, const point& rhs)
constexpr size operator*(const size lhs, const point rhs)
{
return lhs * til::size(rhs.x(), rhs.y());
return lhs * til::size(rhs.x, rhs.y);
}
_TIL_INLINEPREFIX size operator/(const size& lhs, const point& rhs)
constexpr size operator/(const size lhs, const point rhs)
{
return lhs / til::size(rhs.x(), rhs.y());
return lhs / til::size(rhs.x, rhs.y);
}
#pragma endregion
}

View File

@ -3,368 +3,231 @@
#pragma once
#ifdef UNIT_TESTING
class PointTests;
#endif
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
class point
using CoordType = int32_t;
namespace details
{
public:
constexpr point() noexcept :
point(0, 0)
template<typename T, typename U = T>
constexpr U extract(const ::base::CheckedNumeric<T>& num)
{
U val;
if (!num.AssignIfValid(&val))
{
throw gsl::narrowing_error{};
}
return val;
}
}
// On 64-bit processors, int and ptrdiff_t are different fundamental types.
// On 32-bit processors, they're the same which makes this a double-definition
// with the `ptrdiff_t` one below.
#if defined(_M_AMD64) || defined(_M_ARM64)
constexpr point(int x, int y) noexcept :
point(static_cast<ptrdiff_t>(x), static_cast<ptrdiff_t>(y))
{
}
constexpr point(ptrdiff_t width, int height) noexcept :
point(width, static_cast<ptrdiff_t>(height))
{
}
constexpr point(int width, ptrdiff_t height) noexcept :
point(static_cast<ptrdiff_t>(width), height)
{
}
#endif
struct point
{
CoordType x = 0;
CoordType y = 0;
point(size_t x, size_t y)
{
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(x).AssignIfValid(&_x));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(y).AssignIfValid(&_y));
}
point(long x, long y)
{
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(x).AssignIfValid(&_x));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(y).AssignIfValid(&_y));
}
constexpr point() noexcept = default;
constexpr point(ptrdiff_t x, ptrdiff_t y) noexcept :
_x(x),
_y(y)
{
}
// This template will convert to size from anything that has an X and a Y field that appear convertible to an integer value
template<typename TOther>
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().X)> && std::is_integral_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
point(static_cast<ptrdiff_t>(other.X), static_cast<ptrdiff_t>(other.Y))
{
}
// This template will convert to size from anything that has a x and a y field that appear convertible to an integer value
template<typename TOther>
constexpr point(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().x)> && std::is_integral_v<decltype(std::declval<TOther>().y)>, int> /*sentinel*/ = 0) :
point(static_cast<ptrdiff_t>(other.x), static_cast<ptrdiff_t>(other.y))
constexpr point(CoordType x, CoordType y) noexcept :
x{ x }, y{ y }
{
}
// This template will convert to point from floating-point args;
// a math type is required. If you _don't_ provide one, you're going to
// get a compile-time error about "cannot convert from initializer-list to til::point"
template<typename TilMath, typename TOther>
constexpr point(TilMath, const TOther& x, const TOther& y, std::enable_if_t<std::is_floating_point_v<TOther>, int> /*sentinel*/ = 0) :
point(TilMath::template cast<ptrdiff_t>(x), TilMath::template cast<ptrdiff_t>(y))
template<typename TilMath, typename T>
constexpr point(TilMath, const T x, const T y) :
x{ TilMath::template cast<CoordType>(x) }, y{ TilMath::template cast<CoordType>(y) }
{
}
// This template will convert to size from anything that has a X and a Y field that are floating-point;
// a math type is required. If you _don't_ provide one, you're going to
// get a compile-time error about "cannot convert from initializer-list to til::point"
template<typename TilMath, typename TOther>
constexpr point(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().X)> && std::is_floating_point_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
point(TilMath::template cast<ptrdiff_t>(other.X), TilMath::template cast<ptrdiff_t>(other.Y))
constexpr bool operator==(const point rhs) const noexcept
{
// `__builtin_memcmp` isn't an official standard, but it's the
// only way at the time of writing to get a constexpr `memcmp`.
return __builtin_memcmp(this, &rhs, sizeof(rhs)) == 0;
}
// This template will convert to size from anything that has a x and a y field that are floating-point;
// a math type is required. If you _don't_ provide one, you're going to
// get a compile-time error about "cannot convert from initializer-list to til::point"
template<typename TilMath, typename TOther>
constexpr point(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().x)> && std::is_floating_point_v<decltype(std::declval<TOther>().y)>, int> /*sentinel*/ = 0) :
point(TilMath::template cast<ptrdiff_t>(other.x), TilMath::template cast<ptrdiff_t>(other.y))
constexpr bool operator!=(const point rhs) const noexcept
{
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0;
}
constexpr bool operator==(const point& other) const noexcept
constexpr bool operator<(const point other) const noexcept
{
return _x == other._x &&
_y == other._y;
return y < other.y || (y == other.y && x < other.x);
}
constexpr bool operator!=(const point& other) const noexcept
constexpr bool operator<=(const point other) const noexcept
{
return !(*this == other);
return y < other.y || (y == other.y && x <= other.x);
}
constexpr bool operator<(const point& other) const noexcept
constexpr bool operator>(const point other) const noexcept
{
if (_y < other._y)
{
return true;
}
else if (_y > other._y)
{
return false;
}
else
{
return _x < other._x;
}
return y > other.y || (y == other.y && x > other.x);
}
constexpr bool operator>(const point& other) const noexcept
constexpr bool operator>=(const point other) const noexcept
{
if (_y > other._y)
{
return true;
}
else if (_y < other._y)
{
return false;
}
else
{
return _x > other._x;
}
return y > other.y || (y == other.y && x >= other.x);
}
constexpr bool operator<=(const point& other) const noexcept
constexpr point operator+(const point other) const
{
if (_y < other._y)
{
return true;
}
else if (_y > other._y)
{
return false;
}
else
{
return _x <= other._x;
}
return point{
details::extract(::base::CheckAdd(x, other.x)),
details::extract(::base::CheckAdd(y, other.y)),
};
}
constexpr bool operator>=(const point& other) const noexcept
{
if (_y > other._y)
{
return true;
}
else if (_y < other._y)
{
return false;
}
else
{
return _x >= other._x;
}
}
point operator+(const point& other) const
{
ptrdiff_t x;
THROW_HR_IF(E_ABORT, !base::CheckAdd(_x, other._x).AssignIfValid(&x));
ptrdiff_t y;
THROW_HR_IF(E_ABORT, !base::CheckAdd(_y, other._y).AssignIfValid(&y));
return point{ x, y };
}
point& operator+=(const point& other)
constexpr point& operator+=(const point other)
{
*this = *this + other;
return *this;
}
point operator-(const point& other) const
constexpr point operator-(const point other) const
{
ptrdiff_t x;
THROW_HR_IF(E_ABORT, !base::CheckSub(_x, other._x).AssignIfValid(&x));
ptrdiff_t y;
THROW_HR_IF(E_ABORT, !base::CheckSub(_y, other._y).AssignIfValid(&y));
return point{ x, y };
return point{
details::extract(::base::CheckSub(x, other.x)),
details::extract(::base::CheckSub(y, other.y)),
};
}
point& operator-=(const point& other)
constexpr point& operator-=(const point other)
{
*this = *this - other;
return *this;
}
point operator*(const point& other) const
constexpr point operator*(const point other) const
{
ptrdiff_t x;
THROW_HR_IF(E_ABORT, !base::CheckMul(_x, other._x).AssignIfValid(&x));
ptrdiff_t y;
THROW_HR_IF(E_ABORT, !base::CheckMul(_y, other._y).AssignIfValid(&y));
return point{ x, y };
return point{
details::extract(::base::CheckMul(x, other.x)),
details::extract(::base::CheckMul(y, other.y)),
};
}
point& operator*=(const point& other)
constexpr point& operator*=(const point other)
{
*this = *this * other;
return *this;
}
template<typename TilMath>
point scale(TilMath, const float scale) const
constexpr point operator/(const point other) const
{
struct
{
float x, y;
} pt;
THROW_HR_IF(E_ABORT, !base::CheckMul(scale, _x).AssignIfValid(&pt.x));
THROW_HR_IF(E_ABORT, !base::CheckMul(scale, _y).AssignIfValid(&pt.y));
return til::point(TilMath(), pt);
return point{
details::extract(::base::CheckDiv(x, other.x)),
details::extract(::base::CheckDiv(y, other.y)),
};
}
point operator/(const point& other) const
{
ptrdiff_t x;
THROW_HR_IF(E_ABORT, !base::CheckDiv(_x, other._x).AssignIfValid(&x));
ptrdiff_t y;
THROW_HR_IF(E_ABORT, !base::CheckDiv(_y, other._y).AssignIfValid(&y));
return point{ x, y };
}
point& operator/=(const point& other)
constexpr point& operator/=(const point other)
{
*this = *this / other;
return *this;
}
template<typename T>
point operator*(const T& scale) const
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
constexpr point operator*(const T scale) const
{
static_assert(std::is_arithmetic<T>::value, "Type must be arithmetic");
ptrdiff_t x;
THROW_HR_IF(E_ABORT, !base::CheckMul(_x, scale).AssignIfValid(&x));
return point{
details::extract(::base::CheckMul(x, scale)),
details::extract(::base::CheckMul(y, scale)),
};
}
ptrdiff_t y;
THROW_HR_IF(E_ABORT, !base::CheckMul(_y, scale).AssignIfValid(&y));
return point{ x, y };
template<typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
constexpr point operator/(const T scale) const
{
return point{
details::extract(::base::CheckDiv(x, scale)),
details::extract(::base::CheckDiv(y, scale)),
};
}
template<typename T>
point operator/(const T& scale) const
constexpr T narrow_x() const
{
static_assert(std::is_arithmetic<T>::value, "Type must be arithmetic");
ptrdiff_t x;
THROW_HR_IF(E_ABORT, !base::CheckDiv(_x, scale).AssignIfValid(&x));
ptrdiff_t y;
THROW_HR_IF(E_ABORT, !base::CheckDiv(_y, scale).AssignIfValid(&y));
return point{ x, y };
}
constexpr ptrdiff_t x() const noexcept
{
return _x;
return gsl::narrow<T>(x);
}
template<typename T>
T x() const
constexpr T narrow_y() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(x()).AssignIfValid(&ret));
return ret;
}
constexpr ptrdiff_t y() const noexcept
{
return _y;
}
template<typename T>
T y() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(y()).AssignIfValid(&ret));
return ret;
return gsl::narrow<T>(y);
}
#ifdef _WINCONTYPES_
operator COORD() const
explicit constexpr point(const COORD other) noexcept :
x{ other.X }, y{ other.Y }
{
COORD ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_x).AssignIfValid(&ret.X));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_y).AssignIfValid(&ret.Y));
return ret;
}
constexpr COORD to_win32_coord() const
{
return { narrow_x<short>(), narrow_y<short>() };
}
#endif
#ifdef _WINDEF_
operator POINT() const
explicit constexpr point(const POINT other) noexcept :
x{ other.x }, y{ other.y }
{
POINT ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_x).AssignIfValid(&ret.x));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_y).AssignIfValid(&ret.y));
return ret;
}
constexpr POINT to_win32_point() const noexcept
{
return { x, y };
}
#endif
#ifdef DCOMMON_H_INCLUDED
constexpr operator D2D1_POINT_2F() const noexcept
template<typename TilMath>
constexpr point(TilMath, const D2D1_POINT_2F other) :
x{ TilMath::template cast<CoordType>(other.x) },
y{ TilMath::template cast<CoordType>(other.y) }
{
return D2D1_POINT_2F{ gsl::narrow_cast<float>(_x), gsl::narrow_cast<float>(_y) };
}
constexpr D2D1_POINT_2F to_d2d_point() const noexcept
{
return { static_cast<float>(x), static_cast<float>(y) };
}
#endif
#ifdef WINRT_Windows_Foundation_H
operator winrt::Windows::Foundation::Point() const
template<typename TilMath>
constexpr point(TilMath, const winrt::Windows::Foundation::Point other) :
x{ TilMath::template cast<CoordType>(other.X) },
y{ TilMath::template cast<CoordType>(other.Y) }
{
winrt::Windows::Foundation::Point ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_x).AssignIfValid(&ret.X));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_y).AssignIfValid(&ret.Y));
return ret;
}
winrt::Windows::Foundation::Point to_winrt_point() const noexcept
{
return { static_cast<float>(x), static_cast<float>(y) };
}
#endif
#ifdef WINRT_Microsoft_Terminal_Core_H
constexpr point(const winrt::Microsoft::Terminal::Core::Point& corePoint) :
point(corePoint.X, corePoint.Y)
explicit constexpr point(const winrt::Microsoft::Terminal::Core::Point other) :
x{ other.X }, y{ other.Y }
{
}
operator winrt::Microsoft::Terminal::Core::Point() const
winrt::Microsoft::Terminal::Core::Point to_core_point() const noexcept
{
winrt::Microsoft::Terminal::Core::Point ret;
ret.X = x<int>();
ret.Y = y<int>();
return ret;
return { x, y };
}
#endif
std::wstring to_string() const
{
return wil::str_printf<std::wstring>(L"(X:%td, Y:%td)", x(), y());
return wil::str_printf<std::wstring>(L"(X:%td, Y:%td)", x, y);
}
protected:
ptrdiff_t _x;
ptrdiff_t _y;
#ifdef UNIT_TESTING
friend class ::PointTests;
#endif
};
}
@ -372,34 +235,34 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
namespace WEX::TestExecution
{
template<>
class VerifyOutputTraits<::til::point>
class VerifyOutputTraits<til::point>
{
public:
static WEX::Common::NoThrowString ToString(const ::til::point& point)
static WEX::Common::NoThrowString ToString(const til::point point)
{
return WEX::Common::NoThrowString(point.to_string().c_str());
}
};
template<>
class VerifyCompareTraits<::til::point, ::til::point>
class VerifyCompareTraits<til::point, til::point>
{
public:
static bool AreEqual(const ::til::point& expected, const ::til::point& actual) noexcept
static constexpr bool AreEqual(const til::point expected, const til::point actual) noexcept
{
return expected == actual;
}
static bool AreSame(const ::til::point& expected, const ::til::point& actual) noexcept
static constexpr bool AreSame(const til::point expected, const til::point actual) noexcept
{
return &expected == &actual;
}
static bool IsLessThan(const ::til::point& expectedLess, const ::til::point& expectedGreater) = delete;
static constexpr bool IsLessThan(const til::point expectedLess, const til::point expectedGreater) = delete;
static bool IsGreaterThan(const ::til::point& expectedGreater, const ::til::point& expectedLess) = delete;
static constexpr bool IsGreaterThan(const til::point expectedGreater, const til::point expectedLess) = delete;
static bool IsNull(const ::til::point& object) noexcept
static constexpr bool IsNull(const til::point object) noexcept
{
return object == til::point{};
}

860
src/inc/til/rect.h Normal file
View File

@ -0,0 +1,860 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "bit.h"
#include "some.h"
#include "math.h"
#include "size.h"
#include "point.h"
#include "operators.h"
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
namespace details
{
class _rectangle_const_iterator
{
public:
constexpr _rectangle_const_iterator(point topLeft, point bottomRight) :
_topLeft{ topLeft },
_bottomRight{ bottomRight },
_current{ topLeft }
{
}
constexpr _rectangle_const_iterator(point topLeft, point bottomRight, point start) :
_topLeft{ topLeft },
_bottomRight{ bottomRight },
_current{ start }
{
}
_rectangle_const_iterator& operator++()
{
const auto nextX = details::extract(::base::CheckAdd(_current.x, 1));
if (nextX >= _bottomRight.x)
{
const auto nextY = details::extract(::base::CheckAdd(_current.y, 1));
// Note for the standard Left-to-Right, Top-to-Bottom walk,
// the end position is one cell below the bottom left.
// (or more accurately, on the exclusive bottom line in the inclusive left column.)
_current = { _topLeft.x, nextY };
}
else
{
_current = { nextX, _current.y };
}
return (*this);
}
constexpr bool operator==(const _rectangle_const_iterator& rhs) const noexcept
{
// `__builtin_memcmp` isn't an official standard, but it's the
// only way at the time of writing to get a constexpr `memcmp`.
return __builtin_memcmp(this, &rhs, sizeof(rhs)) == 0;
}
constexpr bool operator!=(const _rectangle_const_iterator& rhs) const noexcept
{
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0;
}
constexpr bool operator<(const _rectangle_const_iterator& other) const
{
return _current < other._current;
}
constexpr bool operator>(const _rectangle_const_iterator& other) const
{
return _current > other._current;
}
constexpr point operator*() const
{
return _current;
}
protected:
point _current;
const point _topLeft;
const point _bottomRight;
};
}
struct rect
{
using const_iterator = details::_rectangle_const_iterator;
CoordType left = 0;
CoordType top = 0;
CoordType right = 0;
CoordType bottom = 0;
constexpr rect() noexcept = default;
constexpr rect(CoordType left, CoordType top, CoordType right, CoordType bottom) noexcept :
left{ left }, top{ top }, right{ right }, bottom{ bottom }
{
}
// This template will convert to point from floating-point args;
// a math type is required. If you _don't_ provide one, you're going to
// get a compile-time error about "cannot convert from initializer-list to til::point"
template<typename TilMath, typename T>
constexpr rect(TilMath, T left, T top, T right, T bottom) :
left{ TilMath::template cast<CoordType>(left) },
top{ TilMath::template cast<CoordType>(top) },
right{ TilMath::template cast<CoordType>(right) },
bottom{ TilMath::template cast<CoordType>(bottom) }
{
}
// Creates a rect where you specify the top-left corner (included)
// and the bottom-right corner (excluded)
constexpr rect(point topLeft, point bottomRight) noexcept :
left{ topLeft.x }, top{ topLeft.y }, right{ bottomRight.x }, bottom{ bottomRight.y }
{
}
// Creates a rect with the given size where the top-left corner
// is set to 0,0.
explicit constexpr rect(size size) noexcept :
right{ size.width }, bottom{ size.height }
{
}
// Creates a rect at the given top-left corner point X,Y that extends
// down (+Y direction) and right (+X direction) for the given size.
constexpr rect(point topLeft, size size) :
rect{ topLeft, topLeft + size }
{
}
constexpr bool operator==(const rect& rhs) const noexcept
{
// `__builtin_memcmp` isn't an official standard, but it's the
// only way at the time of writing to get a constexpr `memcmp`.
return __builtin_memcmp(this, &rhs, sizeof(rhs)) == 0;
}
constexpr bool operator!=(const rect& rhs) const noexcept
{
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0;
}
explicit constexpr operator bool() const noexcept
{
return (left >= 0) & (top >= 0) &
(right > left) & (bottom > top);
}
constexpr const_iterator begin() const
{
return const_iterator({ left, top }, { right, bottom });
}
constexpr const_iterator end() const
{
// For the standard walk: Left-To-Right then Top-To-Bottom
// the end box is one cell below the left most column.
// |----| 5x2 square. Remember bottom & right are exclusive
// | | while top & left are inclusive.
// X----- X is the end position.
return const_iterator({ left, top }, { right, bottom }, { left, bottom });
}
#pragma region RECTANGLE OPERATORS
// OR = union
constexpr rect operator|(const rect& other) const noexcept
{
const auto thisEmpty = empty();
const auto otherEmpty = other.empty();
// If both are empty, return empty rect.
if (thisEmpty && otherEmpty)
{
return rect{};
}
// If this is empty but not the other one, then give the other.
if (thisEmpty)
{
return other;
}
// If the other is empty but not this, give this.
if (otherEmpty)
{
return *this;
}
// If we get here, they're both not empty. Do math.
const auto l = std::min(left, other.left);
const auto t = std::min(top, other.top);
const auto r = std::max(right, other.right);
const auto b = std::max(bottom, other.bottom);
return rect{ l, t, r, b };
}
constexpr rect& operator|=(const rect& other) noexcept
{
*this = *this | other;
return *this;
}
// AND = intersect
constexpr rect operator&(const rect& other) const noexcept
{
const auto l = std::max(left, other.left);
const auto r = std::min(right, other.right);
// If the width dimension would be empty, give back empty rect.
if (l >= r)
{
return rect{};
}
const auto t = std::max(top, other.top);
const auto b = std::min(bottom, other.bottom);
// If the height dimension would be empty, give back empty rect.
if (t >= b)
{
return rect{};
}
return rect{ l, t, r, b };
}
constexpr rect& operator&=(const rect& other) noexcept
{
*this = *this & other;
return *this;
}
// - = subtract
constexpr some<rect, 4> operator-(const rect& other) const
{
some<rect, 4> result;
// We could have up to four rectangles describing the area resulting when you take removeMe out of main.
// Find the intersection of the two so we know which bits of removeMe are actually applicable
// to the original rect for subtraction purposes.
const auto intersect = *this & other;
// If there's no intersect, there's nothing to remove.
if (intersect.empty())
{
// Just put the original rect into the results and return early.
result.push_back(*this);
}
// If the original rect matches the intersect, there is nothing to return.
else if (*this != intersect)
{
// Generate our potential four viewports that represent the region of the original that falls outside of the remove area.
// We will bias toward generating wide rectangles over tall rectangles (if possible) so that optimizations that apply
// to manipulating an entire row at once can be realized by other parts of the console code. (i.e. Run Length Encoding)
// In the following examples, the found remaining regions are represented by:
// T = Top B = Bottom L = Left R = Right
//
// 4 Sides but Identical:
// |-----------this-----------| |-----------this-----------|
// | | | |
// | | | |
// | | | |
// | | ======> | intersect | ======> early return of nothing
// | | | |
// | | | |
// | | | |
// |-----------other----------| |--------------------------|
//
// 4 Sides:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | |---------| | | |---------| | |LLLLLLLL|---------|RRRRRRR|
// | |other | | ======> | |intersect| | ======> |LLLLLLLL| |RRRRRRR|
// | |---------| | | |---------| | |LLLLLLLL|---------|RRRRRRR|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// |--------------------------| |--------------------------| |--------------------------|
//
// 3 Sides:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
// | |other | ======> | |intersect | ======> |LLLLLLLL| |
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// |--------------------------| |--------------------------| |--------------------------|
//
// 2 Sides:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
// | |other | ======> | |intersect | ======> |LLLLLLLL| |
// | | | | | | |LLLLLLLL| |
// | | | | | | |LLLLLLLL| |
// | | | | | | |LLLLLLLL| |
// |--------| | |--------------------------| |--------------------------|
// | |
// |--------------------|
//
// 1 Side:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// |-----------------------------| |--------------------------| |--------------------------|
// | other | ======> | intersect | ======> | |
// | | | | | |
// | | | | | |
// | | | | | |
// | | |--------------------------| |--------------------------|
// | |
// |-----------------------------|
//
// 0 Sides:
// |-----------this-----------| |-----------this-----------|
// | | | |
// | | | |
// | | | |
// | | ======> | | ======> early return of this
// | | | |
// | | | |
// | | | |
// |--------------------------| |--------------------------|
//
//
// |---------------|
// | other |
// |---------------|
// We generate these rectangles by the original and intersect points, but some of them might be empty when the intersect
// lines up with the edge of the original. That's OK. That just means that the subtraction didn't leave anything behind.
// We will filter those out below when adding them to the result.
const rect t{ left, top, right, intersect.top };
const rect b{ left, intersect.bottom, right, bottom };
const rect l{ left, intersect.top, intersect.left, intersect.bottom };
const rect r{ intersect.right, intersect.top, right, intersect.bottom };
if (!t.empty())
{
result.push_back(t);
}
if (!b.empty())
{
result.push_back(b);
}
if (!l.empty())
{
result.push_back(l);
}
if (!r.empty())
{
result.push_back(r);
}
}
return result;
}
#pragma endregion
#pragma region RECTANGLE VS POINT
// ADD will translate (offset) the rect by the point.
constexpr rect operator+(const point point) const
{
const auto l = details::extract(::base::CheckAdd(left, point.x));
const auto t = details::extract(::base::CheckAdd(top, point.y));
const auto r = details::extract(::base::CheckAdd(right, point.x));
const auto b = details::extract(::base::CheckAdd(bottom, point.y));
return { l, t, r, b };
}
constexpr rect& operator+=(const point point)
{
*this = *this + point;
return *this;
}
// SUB will translate (offset) the rect by the point.
constexpr rect operator-(const point point) const
{
const auto l = details::extract(::base::CheckSub(left, point.x));
const auto t = details::extract(::base::CheckSub(top, point.y));
const auto r = details::extract(::base::CheckSub(right, point.x));
const auto b = details::extract(::base::CheckSub(bottom, point.y));
return { l, t, r, b };
}
constexpr rect& operator-=(const point point)
{
*this = *this - point;
return *this;
}
#pragma endregion
#pragma region RECTANGLE VS SIZE
// ADD will grow the total area of the rect. The sign is the direction to grow.
constexpr rect operator+(const size size) const
{
// Fetch the pieces of the rect.
auto l = left;
auto r = right;
auto t = top;
auto b = bottom;
// Fetch the scale factors we're using.
const auto width = size.width;
const auto height = size.height;
// Since this is the add operation versus a size, the result
// should grow the total rect area.
// The sign determines which edge of the rect moves.
// We use the magnitude as how far to move.
if (width > 0)
{
// Adding the positive makes the rect "grow"
// because right stretches outward (to the right).
//
// Example with adding width 3...
// |-- x = origin
// V
// x---------| x------------|
// | | | |
// | | | |
// |---------| |------------|
// BEFORE AFTER
r = details::extract(::base::CheckAdd(r, width));
}
else
{
// Adding the negative makes the rect "grow"
// because left stretches outward (to the left).
//
// Example with adding width -3...
// |-- x = origin
// V
// x---------| |--x---------|
// | | | |
// | | | |
// |---------| |------------|
// BEFORE AFTER
l = details::extract(::base::CheckAdd(l, width));
}
if (height > 0)
{
// Adding the positive makes the rect "grow"
// because bottom stretches outward (to the down).
//
// Example with adding height 2...
// |-- x = origin
// V
// x---------| x---------|
// | | | |
// | | | |
// |---------| | |
// | |
// |---------|
// BEFORE AFTER
b = details::extract(::base::CheckAdd(b, height));
}
else
{
// Adding the negative makes the rect "grow"
// because top stretches outward (to the up).
//
// Example with adding height -2...
// |-- x = origin
// |
// | |---------|
// V | |
// x---------| x |
// | | | |
// | | | |
// |---------| |---------|
// BEFORE AFTER
t = details::extract(::base::CheckAdd(t, height));
}
return rect{ point{ l, t }, point{ r, b } };
}
constexpr rect& operator+=(const size size)
{
*this = *this + size;
return *this;
}
// SUB will shrink the total area of the rect. The sign is the direction to shrink.
constexpr rect operator-(const size size) const
{
// Fetch the pieces of the rect.
auto l = left;
auto r = right;
auto t = top;
auto b = bottom;
// Fetch the scale factors we're using.
const auto width = size.width;
const auto height = size.height;
// Since this is the subtract operation versus a size, the result
// should shrink the total rect area.
// The sign determines which edge of the rect moves.
// We use the magnitude as how far to move.
if (width > 0)
{
// Subtracting the positive makes the rect "shrink"
// because right pulls inward (to the left).
//
// Example with subtracting width 3...
// |-- x = origin
// V
// x---------| x------|
// | | | |
// | | | |
// |---------| |------|
// BEFORE AFTER
r = details::extract(::base::CheckSub(r, width));
}
else
{
// Subtracting the negative makes the rect "shrink"
// because left pulls inward (to the right).
//
// Example with subtracting width -3...
// |-- x = origin
// V
// x---------| x |------|
// | | | |
// | | | |
// |---------| |------|
// BEFORE AFTER
l = details::extract(::base::CheckSub(l, width));
}
if (height > 0)
{
// Subtracting the positive makes the rect "shrink"
// because bottom pulls inward (to the up).
//
// Example with subtracting height 2...
// |-- x = origin
// V
// x---------| x---------|
// | | |---------|
// | |
// |---------|
// BEFORE AFTER
b = details::extract(::base::CheckSub(b, height));
}
else
{
// Subtracting the positive makes the rect "shrink"
// because top pulls inward (to the down).
//
// Example with subtracting height -2...
// |-- x = origin
// V
// x---------| x
// | |
// | | |---------|
// |---------| |---------|
// BEFORE AFTER
t = details::extract(::base::CheckSub(t, height));
}
return rect{ point{ l, t }, point{ r, b } };
}
constexpr rect& operator-=(const size size)
{
*this = *this - size;
return *this;
}
// scale_up will scale the entire rect up by the size factor
// This includes moving the origin.
constexpr rect scale_up(const size size) const
{
const auto topLeft = point{ left, top } * size;
const auto bottomRight = point{ right, bottom } * size;
return rect{ topLeft, bottomRight };
}
// scale_down will scale the entire rect down by the size factor,
// but rounds the bottom-right corner out.
// This includes moving the origin.
constexpr rect scale_down(const size size) const
{
auto topLeft = point{ left, top };
auto bottomRight = point{ right, bottom };
topLeft = topLeft / size;
// Move bottom right point into a size
// Use size specialization of divide_ceil to round up against the size given.
// Add leading addition to point to convert it back into a point.
bottomRight = point{} + til::size{ right, bottom }.divide_ceil(size);
return rect{ topLeft, bottomRight };
}
#pragma endregion
template<typename T>
constexpr T narrow_left() const
{
return gsl::narrow<T>(left);
}
template<typename T>
constexpr T narrow_top() const
{
return gsl::narrow<T>(top);
}
template<typename T>
constexpr T narrow_right() const
{
return gsl::narrow<T>(right);
}
template<typename T>
constexpr T narrow_bottom() const
{
return gsl::narrow<T>(bottom);
}
constexpr CoordType width() const
{
return details::extract(::base::CheckSub(right, left));
}
template<typename T>
constexpr T narrow_width() const
{
return details::extract<CoordType, T>(::base::CheckSub(right, left));
}
constexpr CoordType height() const
{
return details::extract(::base::CheckSub(bottom, top));
}
template<typename T>
constexpr T narrow_height() const
{
return details::extract<CoordType, T>(::base::CheckSub(bottom, top));
}
constexpr point origin() const noexcept
{
return { left, top };
}
constexpr size size() const noexcept
{
return til::size{ width(), height() };
}
constexpr bool empty() const noexcept
{
return !operator bool();
}
constexpr bool contains(point pt) const noexcept
{
return (pt.x >= left) & (pt.x < right) &
(pt.y >= top) & (pt.y < bottom);
}
constexpr bool contains(const rect& rc) const noexcept
{
return (rc.left >= left) & (rc.top >= top) &
(rc.right <= right) & (rc.bottom <= bottom);
}
template<typename T = CoordType>
constexpr T index_of(point pt) const
{
THROW_HR_IF(E_INVALIDARG, !contains(pt));
// Take Y away from the top to find how many rows down
auto check = ::base::CheckSub(pt.y, top);
// Multiply by the width because we've passed that many
// widths-worth of indices.
check *= width();
// Then add in the last few indices in the x position this row
// and subtract left to find the offset from left edge.
check = check + pt.x - left;
return details::extract<CoordType, T>(check);
}
point point_at(size_t index) const
{
const auto width = details::extract<CoordType, size_t>(::base::CheckSub(right, left));
const auto area = details::extract<CoordType, size_t>(::base::CheckSub(bottom, top) * width);
THROW_HR_IF(E_INVALIDARG, index >= area);
// Not checking math on these because we're presuming
// that the point can't be in bounds of a rect where
// this would overflow on addition after the division.
const auto quot = gsl::narrow_cast<CoordType>(index / width);
const auto rem = gsl::narrow_cast<CoordType>(index % width);
return point{ left + rem, top + quot };
}
#ifdef _WINCONTYPES_
// NOTE: This will convert from INCLUSIVE on the way in because
// that is generally how SMALL_RECTs are handled in console code and via the APIs.
explicit constexpr rect(const SMALL_RECT other) noexcept :
rect{ other.Left, other.Top, other.Right + 1, other.Bottom + 1 }
{
}
// NOTE: This will convert back to INCLUSIVE on the way out because
// that is generally how SMALL_RECTs are handled in console code and via the APIs.
constexpr SMALL_RECT to_small_rect() const
{
// The two -1 operations below are technically UB if they underflow.
// But practically speaking no hardware without two's complement for
// signed integers is supported by Windows. If they do underflow, they'll
// result in INT_MAX which will throw in gsl::narrow just like INT_MAX does.
return {
gsl::narrow<short>(left),
gsl::narrow<short>(top),
gsl::narrow<short>(right - 1),
gsl::narrow<short>(bottom - 1),
};
}
#endif
#ifdef _WINDEF_
explicit constexpr rect(const RECT& other) noexcept :
rect{ other.left, other.top, other.right, other.bottom }
{
}
constexpr RECT to_win32_rect() const noexcept
{
return { left, top, right, bottom };
}
#endif
#ifdef DCOMMON_H_INCLUDED
template<typename TilMath>
constexpr rect(TilMath&& math, const D2D1_RECT_F& other) :
rect{ std::forward<TilMath>(math), other.left, other.top, other.right, other.bottom }
{
}
constexpr D2D1_RECT_F to_d2d_rect() const noexcept
{
return {
static_cast<float>(left),
static_cast<float>(top),
static_cast<float>(right),
static_cast<float>(bottom),
};
}
#endif
#ifdef WINRT_Windows_Foundation_H
template<typename TilMath>
constexpr rect(TilMath&& math, const winrt::Windows::Foundation::Rect& other) :
rect{ std::forward<TilMath>(math), other.X, other.Y, other.X + other.Width, other.Y + other.Height }
{
}
winrt::Windows::Foundation::Rect to_winrt_rect() const noexcept
{
return {
static_cast<float>(left),
static_cast<float>(top),
static_cast<float>(width()),
static_cast<float>(height()),
};
}
#endif
#ifdef WINRT_Microsoft_Terminal_Core_H
template<typename TilMath>
constexpr rect(TilMath&& math, const winrt::Microsoft::Terminal::Core::Padding& other) :
rect{ std::forward<TilMath>(math), other.Left, other.Top, other.Right, other.Bottom }
{
}
winrt::Microsoft::Terminal::Core::Padding to_core_padding() const noexcept
{
return {
static_cast<float>(left),
static_cast<float>(top),
static_cast<float>(right),
static_cast<float>(bottom),
};
}
#endif
std::wstring to_string() const
{
return wil::str_printf<std::wstring>(L"(L:%d, T:%d, R:%d, B:%d) [W:%d, H:%d]", left, top, right, bottom, width(), height());
}
};
}
#ifdef __WEX_COMMON_H__
namespace WEX::TestExecution
{
template<>
class VerifyOutputTraits<til::rect>
{
public:
static WEX::Common::NoThrowString ToString(const til::rect& rect)
{
return WEX::Common::NoThrowString(rect.to_string().c_str());
}
};
template<>
class VerifyCompareTraits<til::rect, til::rect>
{
public:
static bool AreEqual(const til::rect& expected, const til::rect& actual) noexcept
{
return expected == actual;
}
static bool AreSame(const til::rect& expected, const til::rect& actual) noexcept
{
return &expected == &actual;
}
static bool IsLessThan(const til::rect& expectedLess, const til::rect& expectedGreater) = delete;
static bool IsGreaterThan(const til::rect& expectedGreater, const til::rect& expectedLess) = delete;
static bool IsNull(const til::rect& object) noexcept
{
return object == til::rect{};
}
};
};
#endif

View File

@ -1,955 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#ifdef UNIT_TESTING
class RectangleTests;
#endif
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
namespace details
{
class _rectangle_const_iterator
{
public:
constexpr _rectangle_const_iterator(point topLeft, point bottomRight) :
_topLeft(topLeft),
_bottomRight(bottomRight),
_current(topLeft)
{
}
constexpr _rectangle_const_iterator(point topLeft, point bottomRight, point start) :
_topLeft(topLeft),
_bottomRight(bottomRight),
_current(start)
{
}
_rectangle_const_iterator& operator++()
{
ptrdiff_t nextX;
THROW_HR_IF(E_ABORT, !::base::CheckAdd(_current.x(), 1).AssignIfValid(&nextX));
if (nextX >= _bottomRight.x())
{
ptrdiff_t nextY;
THROW_HR_IF(E_ABORT, !::base::CheckAdd(_current.y(), 1).AssignIfValid(&nextY));
// Note for the standard Left-to-Right, Top-to-Bottom walk,
// the end position is one cell below the bottom left.
// (or more accurately, on the exclusive bottom line in the inclusive left column.)
_current = { _topLeft.x(), nextY };
}
else
{
_current = { nextX, _current.y() };
}
return (*this);
}
constexpr bool operator==(const _rectangle_const_iterator& other) const
{
return _current == other._current &&
_topLeft == other._topLeft &&
_bottomRight == other._bottomRight;
}
constexpr bool operator!=(const _rectangle_const_iterator& other) const
{
return !(*this == other);
}
constexpr bool operator<(const _rectangle_const_iterator& other) const
{
return _current < other._current;
}
constexpr bool operator>(const _rectangle_const_iterator& other) const
{
return _current > other._current;
}
constexpr point operator*() const
{
return _current;
}
protected:
point _current;
const point _topLeft;
const point _bottomRight;
#ifdef UNIT_TESTING
friend class ::RectangleTests;
#endif
};
}
class rectangle
{
public:
using const_iterator = details::_rectangle_const_iterator;
constexpr rectangle() noexcept :
rectangle(til::point{ 0, 0 }, til::point{ 0, 0 })
{
}
// On 64-bit processors, int and ptrdiff_t are different fundamental types.
// On 32-bit processors, they're the same which makes this a double-definition
// with the `ptrdiff_t` one below.
#if defined(_M_AMD64) || defined(_M_ARM64)
constexpr rectangle(int left, int top, int right, int bottom) noexcept :
rectangle(til::point{ left, top }, til::point{ right, bottom })
{
}
#endif
rectangle(size_t left, size_t top, size_t right, size_t bottom) :
rectangle(til::point{ left, top }, til::point{ right, bottom })
{
}
constexpr rectangle(ptrdiff_t left, ptrdiff_t top, ptrdiff_t right, ptrdiff_t bottom) noexcept :
rectangle(til::point{ left, top }, til::point{ right, bottom })
{
}
// Creates a 1x1 rectangle with the given top-left corner.
rectangle(til::point topLeft) :
_topLeft(topLeft)
{
_bottomRight = _topLeft + til::point{ 1, 1 };
}
// Creates a rectangle where you specify the top-left corner (included)
// and the bottom-right corner (excluded)
constexpr rectangle(til::point topLeft, til::point bottomRight) noexcept :
_topLeft(topLeft),
_bottomRight(bottomRight)
{
}
// Creates a rectangle with the given size where the top-left corner
// is set to 0,0.
constexpr rectangle(til::size size) noexcept :
_topLeft(til::point{ 0, 0 }),
_bottomRight(til::point{ size.width(), size.height() })
{
}
// Creates a rectangle at the given top-left corner point X,Y that extends
// down (+Y direction) and right (+X direction) for the given size.
rectangle(til::point topLeft, til::size size) :
_topLeft(topLeft),
_bottomRight(topLeft + til::point{ size.width(), size.height() })
{
}
#ifdef _WINCONTYPES_
// This extra specialization exists for SMALL_RECT because it's the only rectangle in the world that we know of
// with the bottom and right fields INCLUSIVE to the rectangle itself.
// It will perform math on the way in to ensure that it is represented as EXCLUSIVE.
rectangle(SMALL_RECT sr)
{
_topLeft = til::point{ static_cast<ptrdiff_t>(sr.Left), static_cast<ptrdiff_t>(sr.Top) };
_bottomRight = til::point{ static_cast<ptrdiff_t>(sr.Right), static_cast<ptrdiff_t>(sr.Bottom) } + til::point{ 1, 1 };
}
#endif
// This template will convert to rectangle from anything that has a Left, Top, Right, and Bottom field that appear convertible to an integer value
template<typename TOther>
constexpr rectangle(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().Top)> && std::is_integral_v<decltype(std::declval<TOther>().Left)> && std::is_integral_v<decltype(std::declval<TOther>().Bottom)> && std::is_integral_v<decltype(std::declval<TOther>().Right)>, int> /*sentinel*/ = 0) :
rectangle(til::point{ static_cast<ptrdiff_t>(other.Left), static_cast<ptrdiff_t>(other.Top) }, til::point{ static_cast<ptrdiff_t>(other.Right), static_cast<ptrdiff_t>(other.Bottom) })
{
}
// This template will convert to rectangle from anything that has a left, top, right, and bottom field that appear convertible to an integer value
template<typename TOther>
constexpr rectangle(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().top)> && std::is_integral_v<decltype(std::declval<TOther>().left)> && std::is_integral_v<decltype(std::declval<TOther>().bottom)> && std::is_integral_v<decltype(std::declval<TOther>().right)>, int> /*sentinel*/ = 0) :
rectangle(til::point{ static_cast<ptrdiff_t>(other.left), static_cast<ptrdiff_t>(other.top) }, til::point{ static_cast<ptrdiff_t>(other.right), static_cast<ptrdiff_t>(other.bottom) })
{
}
// This template will convert to rectangle from anything that has a Left, Top, Right, and Bottom field that are floating-point;
// a math type is required.
template<typename TilMath, typename TOther>
constexpr rectangle(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().Left)> && std::is_floating_point_v<decltype(std::declval<TOther>().Top)> && std::is_floating_point_v<decltype(std::declval<TOther>().Right)> && std::is_floating_point_v<decltype(std::declval<TOther>().Bottom)>, int> /*sentinel*/ = 0) :
rectangle(til::point{ TilMath::template cast<ptrdiff_t>(other.Left), TilMath::template cast<ptrdiff_t>(other.Top) }, til::point{ TilMath::template cast<ptrdiff_t>(other.Right), TilMath::template cast<ptrdiff_t>(other.Bottom) })
{
}
// This template will convert to rectangle from anything that has a X, Y, Width, and Height field that are floating-point;
// a math type is required.
template<typename TilMath, typename TOther>
constexpr rectangle(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().X)> && std::is_floating_point_v<decltype(std::declval<TOther>().Y)> && std::is_floating_point_v<decltype(std::declval<TOther>().Width)> && std::is_floating_point_v<decltype(std::declval<TOther>().Height)>, int> /*sentinel*/ = 0) :
rectangle(til::point{ TilMath::template cast<ptrdiff_t>(other.X), TilMath::template cast<ptrdiff_t>(other.Y) }, til::size{ TilMath::template cast<ptrdiff_t>(other.Width), TilMath::template cast<ptrdiff_t>(other.Height) })
{
}
// This template will convert to rectangle from anything that has a left, top, right, and bottom field that are floating-point;
// a math type is required.
template<typename TilMath, typename TOther>
constexpr rectangle(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().left)> && std::is_floating_point_v<decltype(std::declval<TOther>().top)> && std::is_floating_point_v<decltype(std::declval<TOther>().right)> && std::is_floating_point_v<decltype(std::declval<TOther>().bottom)>, int> /*sentinel*/ = 0) :
rectangle(til::point{ TilMath::template cast<ptrdiff_t>(other.left), TilMath::template cast<ptrdiff_t>(other.top) }, til::point{ TilMath::template cast<ptrdiff_t>(other.right), TilMath::template cast<ptrdiff_t>(other.bottom) })
{
}
constexpr bool operator==(const rectangle& other) const noexcept
{
return _topLeft == other._topLeft &&
_bottomRight == other._bottomRight;
}
constexpr bool operator!=(const rectangle& other) const noexcept
{
return !(*this == other);
}
explicit constexpr operator bool() const noexcept
{
return _topLeft.x() < _bottomRight.x() &&
_topLeft.y() < _bottomRight.y();
}
constexpr const_iterator begin() const
{
return const_iterator(_topLeft, _bottomRight);
}
constexpr const_iterator end() const
{
// For the standard walk: Left-To-Right then Top-To-Bottom
// the end box is one cell below the left most column.
// |----| 5x2 square. Remember bottom & right are exclusive
// | | while top & left are inclusive.
// X----- X is the end position.
return const_iterator(_topLeft, _bottomRight, { _topLeft.x(), _bottomRight.y() });
}
#pragma region RECTANGLE OPERATORS
// OR = union
constexpr rectangle operator|(const rectangle& other) const noexcept
{
const auto thisEmpty = empty();
const auto otherEmpty = other.empty();
// If both are empty, return empty rect.
if (thisEmpty && otherEmpty)
{
return rectangle{};
}
// If this is empty but not the other one, then give the other.
if (thisEmpty)
{
return other;
}
// If the other is empty but not this, give this.
if (otherEmpty)
{
return *this;
}
// If we get here, they're both not empty. Do math.
const auto l = std::min(left(), other.left());
const auto t = std::min(top(), other.top());
const auto r = std::max(right(), other.right());
const auto b = std::max(bottom(), other.bottom());
return rectangle{ l, t, r, b };
}
constexpr rectangle& operator|=(const rectangle& other) noexcept
{
*this = *this | other;
return *this;
}
// AND = intersect
constexpr rectangle operator&(const rectangle& other) const noexcept
{
const auto l = std::max(left(), other.left());
const auto r = std::min(right(), other.right());
// If the width dimension would be empty, give back empty rectangle.
if (l >= r)
{
return rectangle{};
}
const auto t = std::max(top(), other.top());
const auto b = std::min(bottom(), other.bottom());
// If the height dimension would be empty, give back empty rectangle.
if (t >= b)
{
return rectangle{};
}
return rectangle{ l, t, r, b };
}
constexpr rectangle& operator&=(const rectangle& other) noexcept
{
*this = *this & other;
return *this;
}
// - = subtract
some<rectangle, 4> operator-(const rectangle& other) const
{
some<rectangle, 4> result;
// We could have up to four rectangles describing the area resulting when you take removeMe out of main.
// Find the intersection of the two so we know which bits of removeMe are actually applicable
// to the original rectangle for subtraction purposes.
const auto intersect = *this & other;
// If there's no intersect, there's nothing to remove.
if (intersect.empty())
{
// Just put the original rectangle into the results and return early.
result.push_back(*this);
}
// If the original rectangle matches the intersect, there is nothing to return.
else if (*this != intersect)
{
// Generate our potential four viewports that represent the region of the original that falls outside of the remove area.
// We will bias toward generating wide rectangles over tall rectangles (if possible) so that optimizations that apply
// to manipulating an entire row at once can be realized by other parts of the console code. (i.e. Run Length Encoding)
// In the following examples, the found remaining regions are represented by:
// T = Top B = Bottom L = Left R = Right
//
// 4 Sides but Identical:
// |-----------this-----------| |-----------this-----------|
// | | | |
// | | | |
// | | | |
// | | ======> | intersect | ======> early return of nothing
// | | | |
// | | | |
// | | | |
// |-----------other----------| |--------------------------|
//
// 4 Sides:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | |---------| | | |---------| | |LLLLLLLL|---------|RRRRRRR|
// | |other | | ======> | |intersect| | ======> |LLLLLLLL| |RRRRRRR|
// | |---------| | | |---------| | |LLLLLLLL|---------|RRRRRRR|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// |--------------------------| |--------------------------| |--------------------------|
//
// 3 Sides:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
// | |other | ======> | |intersect | ======> |LLLLLLLL| |
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// | | | | |BBBBBBBBBBBBBBBBBBBBBBBBBB|
// |--------------------------| |--------------------------| |--------------------------|
//
// 2 Sides:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | |--------------------| | |-----------------| |LLLLLLLL|-----------------|
// | |other | ======> | |intersect | ======> |LLLLLLLL| |
// | | | | | | |LLLLLLLL| |
// | | | | | | |LLLLLLLL| |
// | | | | | | |LLLLLLLL| |
// |--------| | |--------------------------| |--------------------------|
// | |
// |--------------------|
//
// 1 Side:
// |-----------this-----------| |-----------this-----------| |--------------------------|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// | | | | |TTTTTTTTTTTTTTTTTTTTTTTTTT|
// |-----------------------------| |--------------------------| |--------------------------|
// | other | ======> | intersect | ======> | |
// | | | | | |
// | | | | | |
// | | | | | |
// | | |--------------------------| |--------------------------|
// | |
// |-----------------------------|
//
// 0 Sides:
// |-----------this-----------| |-----------this-----------|
// | | | |
// | | | |
// | | | |
// | | ======> | | ======> early return of this
// | | | |
// | | | |
// | | | |
// |--------------------------| |--------------------------|
//
//
// |---------------|
// | other |
// |---------------|
// We generate these rectangles by the original and intersect points, but some of them might be empty when the intersect
// lines up with the edge of the original. That's OK. That just means that the subtraction didn't leave anything behind.
// We will filter those out below when adding them to the result.
const til::rectangle t{ left(), top(), right(), intersect.top() };
const til::rectangle b{ left(), intersect.bottom(), right(), bottom() };
const til::rectangle l{ left(), intersect.top(), intersect.left(), intersect.bottom() };
const til::rectangle r{ intersect.right(), intersect.top(), right(), intersect.bottom() };
if (!t.empty())
{
result.push_back(t);
}
if (!b.empty())
{
result.push_back(b);
}
if (!l.empty())
{
result.push_back(l);
}
if (!r.empty())
{
result.push_back(r);
}
}
return result;
}
#pragma endregion
#pragma region RECTANGLE VS POINT
// ADD will translate (offset) the rectangle by the point.
rectangle operator+(const point& point) const
{
ptrdiff_t l, t, r, b;
THROW_HR_IF(E_ABORT, !::base::CheckAdd(left(), point.x()).AssignIfValid(&l));
THROW_HR_IF(E_ABORT, !::base::CheckAdd(top(), point.y()).AssignIfValid(&t));
THROW_HR_IF(E_ABORT, !::base::CheckAdd(right(), point.x()).AssignIfValid(&r));
THROW_HR_IF(E_ABORT, !::base::CheckAdd(bottom(), point.y()).AssignIfValid(&b));
return til::rectangle{ til::point{ l, t }, til::point{ r, b } };
}
rectangle& operator+=(const point& point)
{
*this = *this + point;
return *this;
}
// SUB will translate (offset) the rectangle by the point.
rectangle operator-(const point& point) const
{
ptrdiff_t l, t, r, b;
THROW_HR_IF(E_ABORT, !::base::CheckSub(left(), point.x()).AssignIfValid(&l));
THROW_HR_IF(E_ABORT, !::base::CheckSub(top(), point.y()).AssignIfValid(&t));
THROW_HR_IF(E_ABORT, !::base::CheckSub(right(), point.x()).AssignIfValid(&r));
THROW_HR_IF(E_ABORT, !::base::CheckSub(bottom(), point.y()).AssignIfValid(&b));
return til::rectangle{ til::point{ l, t }, til::point{ r, b } };
}
rectangle& operator-=(const point& point)
{
*this = *this - point;
return *this;
}
#pragma endregion
#pragma region RECTANGLE VS SIZE
// ADD will grow the total area of the rectangle. The sign is the direction to grow.
rectangle operator+(const size& size) const
{
// Fetch the pieces of the rectangle.
auto l = left();
auto r = right();
auto t = top();
auto b = bottom();
// Fetch the scale factors we're using.
const auto width = size.width();
const auto height = size.height();
// Since this is the add operation versus a size, the result
// should grow the total rectangle area.
// The sign determines which edge of the rectangle moves.
// We use the magnitude as how far to move.
if (width > 0)
{
// Adding the positive makes the rectangle "grow"
// because right stretches outward (to the right).
//
// Example with adding width 3...
// |-- x = origin
// V
// x---------| x------------|
// | | | |
// | | | |
// |---------| |------------|
// BEFORE AFTER
THROW_HR_IF(E_ABORT, !base::CheckAdd(r, width).AssignIfValid(&r));
}
else
{
// Adding the negative makes the rectangle "grow"
// because left stretches outward (to the left).
//
// Example with adding width -3...
// |-- x = origin
// V
// x---------| |--x---------|
// | | | |
// | | | |
// |---------| |------------|
// BEFORE AFTER
THROW_HR_IF(E_ABORT, !base::CheckAdd(l, width).AssignIfValid(&l));
}
if (height > 0)
{
// Adding the positive makes the rectangle "grow"
// because bottom stretches outward (to the down).
//
// Example with adding height 2...
// |-- x = origin
// V
// x---------| x---------|
// | | | |
// | | | |
// |---------| | |
// | |
// |---------|
// BEFORE AFTER
THROW_HR_IF(E_ABORT, !base::CheckAdd(b, height).AssignIfValid(&b));
}
else
{
// Adding the negative makes the rectangle "grow"
// because top stretches outward (to the up).
//
// Example with adding height -2...
// |-- x = origin
// |
// | |---------|
// V | |
// x---------| x |
// | | | |
// | | | |
// |---------| |---------|
// BEFORE AFTER
THROW_HR_IF(E_ABORT, !base::CheckAdd(t, height).AssignIfValid(&t));
}
return rectangle{ til::point{ l, t }, til::point{ r, b } };
}
rectangle& operator+=(const size& size)
{
*this = *this + size;
return *this;
}
// SUB will shrink the total area of the rectangle. The sign is the direction to shrink.
rectangle operator-(const size& size) const
{
// Fetch the pieces of the rectangle.
auto l = left();
auto r = right();
auto t = top();
auto b = bottom();
// Fetch the scale factors we're using.
const auto width = size.width();
const auto height = size.height();
// Since this is the subtract operation versus a size, the result
// should shrink the total rectangle area.
// The sign determines which edge of the rectangle moves.
// We use the magnitude as how far to move.
if (width > 0)
{
// Subtracting the positive makes the rectangle "shrink"
// because right pulls inward (to the left).
//
// Example with subtracting width 3...
// |-- x = origin
// V
// x---------| x------|
// | | | |
// | | | |
// |---------| |------|
// BEFORE AFTER
THROW_HR_IF(E_ABORT, !base::CheckSub(r, width).AssignIfValid(&r));
}
else
{
// Subtracting the negative makes the rectangle "shrink"
// because left pulls inward (to the right).
//
// Example with subtracting width -3...
// |-- x = origin
// V
// x---------| x |------|
// | | | |
// | | | |
// |---------| |------|
// BEFORE AFTER
THROW_HR_IF(E_ABORT, !base::CheckSub(l, width).AssignIfValid(&l));
}
if (height > 0)
{
// Subtracting the positive makes the rectangle "shrink"
// because bottom pulls inward (to the up).
//
// Example with subtracting height 2...
// |-- x = origin
// V
// x---------| x---------|
// | | |---------|
// | |
// |---------|
// BEFORE AFTER
THROW_HR_IF(E_ABORT, !base::CheckSub(b, height).AssignIfValid(&b));
}
else
{
// Subtracting the positive makes the rectangle "shrink"
// because top pulls inward (to the down).
//
// Example with subtracting height -2...
// |-- x = origin
// V
// x---------| x
// | |
// | | |---------|
// |---------| |---------|
// BEFORE AFTER
THROW_HR_IF(E_ABORT, !base::CheckSub(t, height).AssignIfValid(&t));
}
return rectangle{ til::point{ l, t }, til::point{ r, b } };
}
rectangle& operator-=(const size& size)
{
*this = *this - size;
return *this;
}
// scale_up will scale the entire rectangle up by the size factor
// This includes moving the origin.
rectangle scale_up(const size& size) const
{
const auto topLeft = _topLeft * size;
const auto bottomRight = _bottomRight * size;
return til::rectangle{ topLeft, bottomRight };
}
// scale_down will scale the entire rectangle down by the size factor,
// but rounds the bottom-right corner out.
// This includes moving the origin.
rectangle scale_down(const size& size) const
{
auto topLeft = _topLeft;
auto bottomRight = _bottomRight;
topLeft = topLeft / size;
// Move bottom right point into a size
// Use size specialization of divide_ceil to round up against the size given.
// Add leading addition to point to convert it back into a point.
bottomRight = til::point{} + til::size{ right(), bottom() }.divide_ceil(size);
return til::rectangle{ topLeft, bottomRight };
}
template<typename TilMath>
rectangle scale(TilMath, const float scale) const
{
return til::rectangle{ _topLeft.scale(TilMath{}, scale), _bottomRight.scale(TilMath{}, scale) };
}
#pragma endregion
constexpr ptrdiff_t top() const noexcept
{
return _topLeft.y();
}
template<typename T>
T top() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret));
return ret;
}
constexpr ptrdiff_t bottom() const noexcept
{
return _bottomRight.y();
}
template<typename T>
T bottom() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(bottom()).AssignIfValid(&ret));
return ret;
}
constexpr ptrdiff_t left() const noexcept
{
return _topLeft.x();
}
template<typename T>
T left() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret));
return ret;
}
constexpr ptrdiff_t right() const noexcept
{
return _bottomRight.x();
}
template<typename T>
T right() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(right()).AssignIfValid(&ret));
return ret;
}
ptrdiff_t width() const
{
ptrdiff_t ret;
THROW_HR_IF(E_ABORT, !::base::CheckSub(right(), left()).AssignIfValid(&ret));
return ret;
}
template<typename T>
T width() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width()).AssignIfValid(&ret));
return ret;
}
ptrdiff_t height() const
{
ptrdiff_t ret;
THROW_HR_IF(E_ABORT, !::base::CheckSub(bottom(), top()).AssignIfValid(&ret));
return ret;
}
template<typename T>
T height() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height()).AssignIfValid(&ret));
return ret;
}
constexpr point origin() const noexcept
{
return _topLeft;
}
size size() const
{
return til::size{ width(), height() };
}
constexpr bool empty() const noexcept
{
return !operator bool();
}
constexpr bool contains(til::point pt) const
{
return pt.x() >= _topLeft.x() && pt.x() < _bottomRight.x() &&
pt.y() >= _topLeft.y() && pt.y() < _bottomRight.y();
}
bool contains(ptrdiff_t index) const
{
return index >= 0 && index < size().area();
}
constexpr bool contains(til::rectangle rc) const
{
// Union the other rectangle and ourselves.
// If the result of that didn't grow at all, then we already
// fully contained the rectangle we were given.
return (*this | rc) == *this;
}
ptrdiff_t index_of(til::point pt) const
{
THROW_HR_IF(E_INVALIDARG, !contains(pt));
// Take Y away from the top to find how many rows down
auto check = base::CheckSub(pt.y(), top());
// Multiply by the width because we've passed that many
// widths-worth of indices.
check *= width();
// Then add in the last few indices in the x position this row
// and subtract left to find the offset from left edge.
check = check + pt.x() - left();
ptrdiff_t result;
THROW_HR_IF(E_ABORT, !check.AssignIfValid(&result));
return result;
}
til::point point_at(ptrdiff_t index) const
{
THROW_HR_IF(E_INVALIDARG, !contains(index));
const auto div = std::div(index, width());
// Not checking math on these because we're presuming
// that the point can't be in bounds of a rectangle where
// this would overflow on addition after the division.
return til::point{ div.rem + left(), div.quot + top() };
}
#ifdef _WINCONTYPES_
// NOTE: This will convert back to INCLUSIVE on the way out because
// that is generally how SMALL_RECTs are handled in console code and via the APIs.
operator SMALL_RECT() const
{
SMALL_RECT ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret.Left));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret.Top));
THROW_HR_IF(E_ABORT, !base::CheckSub(right(), 1).AssignIfValid(&ret.Right));
THROW_HR_IF(E_ABORT, !base::CheckSub(bottom(), 1).AssignIfValid(&ret.Bottom));
return ret;
}
#endif
#ifdef _WINDEF_
operator RECT() const
{
RECT ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret.left));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret.top));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(right()).AssignIfValid(&ret.right));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(bottom()).AssignIfValid(&ret.bottom));
return ret;
}
#endif
#ifdef DCOMMON_H_INCLUDED
constexpr operator D2D1_RECT_F() const noexcept
{
return D2D1_RECT_F{ gsl::narrow_cast<FLOAT>(left()), gsl::narrow_cast<FLOAT>(top()), gsl::narrow_cast<FLOAT>(right()), gsl::narrow_cast<FLOAT>(bottom()) };
}
#endif
#ifdef WINRT_Windows_Foundation_H
operator winrt::Windows::Foundation::Rect() const
{
winrt::Windows::Foundation::Rect ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret.X));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret.Y));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width()).AssignIfValid(&ret.Width));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height()).AssignIfValid(&ret.Height));
return ret;
}
#endif
#ifdef WINRT_Microsoft_Terminal_Core_H
operator winrt::Microsoft::Terminal::Core::Padding() const
{
winrt::Microsoft::Terminal::Core::Padding ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(left()).AssignIfValid(&ret.Left));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(top()).AssignIfValid(&ret.Top));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(right()).AssignIfValid(&ret.Right));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(bottom()).AssignIfValid(&ret.Bottom));
return ret;
}
constexpr rectangle(const winrt::Microsoft::Terminal::Core::Padding& padding) :
rectangle(til::math::rounding, padding)
{
}
#endif
std::wstring to_string() const
{
return wil::str_printf<std::wstring>(L"(L:%td, T:%td, R:%td, B:%td) [W:%td, H:%td]", left(), top(), right(), bottom(), width(), height());
}
protected:
til::point _topLeft;
til::point _bottomRight;
#ifdef UNIT_TESTING
friend class ::RectangleTests;
#endif
};
}
#ifdef __WEX_COMMON_H__
namespace WEX::TestExecution
{
template<>
class VerifyOutputTraits<::til::rectangle>
{
public:
static WEX::Common::NoThrowString ToString(const ::til::rectangle& rect)
{
return WEX::Common::NoThrowString(rect.to_string().c_str());
}
};
template<>
class VerifyCompareTraits<::til::rectangle, ::til::rectangle>
{
public:
static bool AreEqual(const ::til::rectangle& expected, const ::til::rectangle& actual) noexcept
{
return expected == actual;
}
static bool AreSame(const ::til::rectangle& expected, const ::til::rectangle& actual) noexcept
{
return &expected == &actual;
}
static bool IsLessThan(const ::til::rectangle& expectedLess, const ::til::rectangle& expectedGreater) = delete;
static bool IsGreaterThan(const ::til::rectangle& expectedGreater, const ::til::rectangle& expectedLess) = delete;
static bool IsNull(const ::til::rectangle& object) noexcept
{
return object == til::rectangle{};
}
};
};
#endif

View File

@ -3,195 +3,107 @@
#pragma once
#ifdef UNIT_TESTING
class SizeTests;
#endif
#include "point.h"
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
class size
struct size
{
public:
constexpr size() noexcept :
size(0, 0)
{
}
CoordType width = 0;
CoordType height = 0;
// On 64-bit processors, int and ptrdiff_t are different fundamental types.
// On 32-bit processors, they're the same which makes this a double-definition
// with the `ptrdiff_t` one below.
#if defined(_M_AMD64) || defined(_M_ARM64)
constexpr size(int width, int height) noexcept :
size(static_cast<ptrdiff_t>(width), static_cast<ptrdiff_t>(height))
{
}
constexpr size(ptrdiff_t width, int height) noexcept :
size(width, static_cast<ptrdiff_t>(height))
{
}
constexpr size(int width, ptrdiff_t height) noexcept :
size(static_cast<ptrdiff_t>(width), height)
{
}
#endif
constexpr size() noexcept = default;
size(size_t width, size_t height)
{
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width).AssignIfValid(&_width));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height).AssignIfValid(&_height));
}
size(long width, long height)
{
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width).AssignIfValid(&_width));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height).AssignIfValid(&_height));
}
constexpr size(ptrdiff_t width, ptrdiff_t height) noexcept :
_width(width),
_height(height)
{
}
// This template will convert to size from anything that has an X and a Y field that appear convertible to an integer value
template<typename TOther>
constexpr size(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().X)> && std::is_integral_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
size(static_cast<ptrdiff_t>(other.X), static_cast<ptrdiff_t>(other.Y))
{
}
// This template will convert to size from anything that has a cx and a cy field that appear convertible to an integer value
template<typename TOther>
constexpr size(const TOther& other, std::enable_if_t<std::is_integral_v<decltype(std::declval<TOther>().cx)> && std::is_integral_v<decltype(std::declval<TOther>().cy)>, int> /*sentinel*/ = 0) :
size(static_cast<ptrdiff_t>(other.cx), static_cast<ptrdiff_t>(other.cy))
{
}
// This template will convert to size from anything that has a X and a Y field that are floating-point;
// a math type is required. If you _don't_ provide one, you're going to
// get a compile-time error about "cannot convert from initializer-list to til::size"
template<typename TilMath, typename TOther>
constexpr size(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().X)> && std::is_floating_point_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
size(TilMath::template cast<ptrdiff_t>(other.X), TilMath::template cast<ptrdiff_t>(other.Y))
{
}
// This template will convert to size from anything that has a cx and a cy field that are floating-point;
// a math type is required. If you _don't_ provide one, you're going to
// get a compile-time error about "cannot convert from initializer-list to til::size"
template<typename TilMath, typename TOther>
constexpr size(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().cx)> && std::is_floating_point_v<decltype(std::declval<TOther>().cy)>, int> /*sentinel*/ = 0) :
size(TilMath::template cast<ptrdiff_t>(other.cx), TilMath::template cast<ptrdiff_t>(other.cy))
{
}
// This template will convert to size from anything that has a Width and a Height field that are floating-point;
// a math type is required. If you _don't_ provide one, you're going to
// get a compile-time error about "cannot convert from initializer-list to til::size"
template<typename TilMath, typename TOther>
constexpr size(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().Width)> && std::is_floating_point_v<decltype(std::declval<TOther>().Height)>, int> /*sentinel*/ = 0) :
size(TilMath::template cast<ptrdiff_t>(other.Width), TilMath::template cast<ptrdiff_t>(other.Height))
constexpr size(CoordType width, CoordType height) noexcept :
width{ width }, height{ height }
{
}
// This template will convert to size from floating-point args;
// a math type is required. If you _don't_ provide one, you're going to
// get a compile-time error about "cannot convert from initializer-list to til::size"
template<typename TilMath, typename TOther>
constexpr size(TilMath, const TOther& width, const TOther& height, std::enable_if_t<std::is_floating_point_v<TOther>, int> /*sentinel*/ = 0) :
size(TilMath::template cast<ptrdiff_t>(width), TilMath::template cast<ptrdiff_t>(height))
template<typename TilMath, typename T>
constexpr size(TilMath, const T width, const T height) :
width{ TilMath::template cast<CoordType>(width) }, height{ TilMath::template cast<CoordType>(height) }
{
}
constexpr bool operator==(const size& other) const noexcept
constexpr bool operator==(const size rhs) const noexcept
{
return _width == other._width &&
_height == other._height;
// `__builtin_memcmp` isn't an official standard, but it's the
// only way at the time of writing to get a constexpr `memcmp`.
return __builtin_memcmp(this, &rhs, sizeof(rhs)) == 0;
}
constexpr bool operator!=(const size& other) const noexcept
constexpr bool operator!=(const size rhs) const noexcept
{
return !(*this == other);
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0;
}
constexpr explicit operator bool() const noexcept
{
return _width > 0 && _height > 0;
return width > 0 && height > 0;
}
size operator+(const size& other) const
constexpr size operator+(const size other) const
{
ptrdiff_t width;
THROW_HR_IF(E_ABORT, !base::CheckAdd(_width, other._width).AssignIfValid(&width));
ptrdiff_t height;
THROW_HR_IF(E_ABORT, !base::CheckAdd(_height, other._height).AssignIfValid(&height));
return size{ width, height };
return size{
details::extract(::base::CheckAdd(width, other.width)),
details::extract(::base::CheckAdd(height, other.height)),
};
}
size operator-(const size& other) const
constexpr size operator-(const size other) const
{
ptrdiff_t width;
THROW_HR_IF(E_ABORT, !base::CheckSub(_width, other._width).AssignIfValid(&width));
ptrdiff_t height;
THROW_HR_IF(E_ABORT, !base::CheckSub(_height, other._height).AssignIfValid(&height));
return size{ width, height };
return size{
details::extract(::base::CheckSub(width, other.width)),
details::extract(::base::CheckSub(height, other.height)),
};
}
size operator*(const size& other) const
constexpr size operator*(const size other) const
{
ptrdiff_t width;
THROW_HR_IF(E_ABORT, !base::CheckMul(_width, other._width).AssignIfValid(&width));
ptrdiff_t height;
THROW_HR_IF(E_ABORT, !base::CheckMul(_height, other._height).AssignIfValid(&height));
return size{ width, height };
return size{
details::extract(::base::CheckMul(width, other.width)),
details::extract(::base::CheckMul(height, other.height)),
};
}
template<typename TilMath>
size scale(TilMath, const float scale) const
constexpr size operator/(const size other) const
{
struct
{
float Width, Height;
} sz;
THROW_HR_IF(E_ABORT, !base::CheckMul(scale, _width).AssignIfValid(&sz.Width));
THROW_HR_IF(E_ABORT, !base::CheckMul(scale, _height).AssignIfValid(&sz.Height));
return til::size(TilMath(), sz);
return size{
details::extract(::base::CheckDiv(width, other.width)),
details::extract(::base::CheckDiv(height, other.height)),
};
}
size operator/(const size& other) const
template<typename TilMath, typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
constexpr size scale(TilMath math, const T scale) const
{
ptrdiff_t width;
THROW_HR_IF(E_ABORT, !base::CheckDiv(_width, other._width).AssignIfValid(&width));
ptrdiff_t height;
THROW_HR_IF(E_ABORT, !base::CheckDiv(_height, other._height).AssignIfValid(&height));
return size{ width, height };
return til::size{
math,
width * scale,
height * scale,
};
}
size divide_ceil(const size& other) const
constexpr size divide_ceil(const size other) const
{
// Divide normally to get the floor.
const size floor = *this / other;
ptrdiff_t adjWidth = 0;
ptrdiff_t adjHeight = 0;
CoordType adjWidth = 0;
CoordType adjHeight = 0;
// Check for width remainder, anything not 0.
// If we multiply the floored number with the other, it will equal
// the old width if there was no remainder.
if (other._width * floor._width != _width)
if (other.width * floor.width != width)
{
// If there was any remainder,
// Grow the magnitude by 1 in the
// direction of the sign.
if (floor.width() >= 0)
if (floor.width >= 0)
{
++adjWidth;
}
@ -204,12 +116,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
// Check for height remainder, anything not 0.
// If we multiply the floored number with the other, it will equal
// the old width if there was no remainder.
if (other._height * floor._height != _height)
if (other.height * floor.height != height)
{
// If there was any remainder,
// Grow the magnitude by 1 in the
// direction of the sign.
if (_height >= 0)
if (height >= 0)
{
++adjHeight;
}
@ -222,86 +134,80 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return floor + size{ adjWidth, adjHeight };
}
constexpr ptrdiff_t width() const noexcept
template<typename T = CoordType>
constexpr T narrow_width() const
{
return _width;
return gsl::narrow<T>(width);
}
template<typename T>
T width() const
template<typename T = CoordType>
constexpr T narrow_height() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(width()).AssignIfValid(&ret));
return ret;
return gsl::narrow<T>(height);
}
constexpr ptrdiff_t height() const noexcept
template<typename T = CoordType>
constexpr T area() const
{
return _height;
}
template<typename T>
T height() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(height()).AssignIfValid(&ret));
return ret;
}
ptrdiff_t area() const
{
ptrdiff_t result;
THROW_HR_IF(E_ABORT, !base::CheckMul(_width, _height).AssignIfValid(&result));
return result;
}
template<typename T>
T area() const
{
T ret;
THROW_HR_IF(E_ABORT, !base::CheckMul(_width, _height).AssignIfValid(&ret));
return ret;
return gsl::narrow<T>(static_cast<int64_t>(width) * static_cast<int64_t>(height));
}
#ifdef _WINCONTYPES_
operator COORD() const
explicit constexpr size(const COORD other) noexcept :
width{ other.X }, height{ other.Y }
{
COORD ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_width).AssignIfValid(&ret.X));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_height).AssignIfValid(&ret.Y));
return ret;
}
constexpr COORD to_win32_coord() const
{
return { gsl::narrow<short>(width), gsl::narrow<short>(height) };
}
#endif
#ifdef _WINDEF_
operator SIZE() const
explicit constexpr size(const SIZE other) noexcept :
width{ other.cx }, height{ other.cy }
{
SIZE ret;
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_width).AssignIfValid(&ret.cx));
THROW_HR_IF(E_ABORT, !base::MakeCheckedNum(_height).AssignIfValid(&ret.cy));
return ret;
}
constexpr SIZE to_win32_size() const noexcept
{
return { width, height };
}
#endif
#ifdef DCOMMON_H_INCLUDED
constexpr operator D2D1_SIZE_F() const noexcept
template<typename TilMath>
constexpr size(TilMath, const D2D1_SIZE_F other) :
width{ TilMath::template cast<CoordType>(other.width) },
height{ TilMath::template cast<CoordType>(other.height) }
{
return D2D1_SIZE_F{ gsl::narrow_cast<float>(_width), gsl::narrow_cast<float>(_height) };
}
constexpr D2D1_SIZE_F to_d2d_size() const noexcept
{
return { static_cast<float>(width), static_cast<float>(height) };
}
#endif
#ifdef WINRT_Windows_Foundation_H
template<typename TilMath>
constexpr size(TilMath, const winrt::Windows::Foundation::Size other) :
width{ TilMath::template cast<CoordType>(other.Width) },
height{ TilMath::template cast<CoordType>(other.Height) }
{
}
winrt::Windows::Foundation::Size to_winrt_size() const noexcept
{
return { static_cast<float>(width), static_cast<float>(height) };
}
#endif
std::wstring to_string() const
{
return wil::str_printf<std::wstring>(L"[W:%td, H:%td]", width(), height());
return wil::str_printf<std::wstring>(L"[W:%td, H:%td]", width, height);
}
protected:
ptrdiff_t _width;
ptrdiff_t _height;
#ifdef UNIT_TESTING
friend class ::SizeTests;
#endif
};
};
@ -309,34 +215,34 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
namespace WEX::TestExecution
{
template<>
class VerifyOutputTraits<::til::size>
class VerifyOutputTraits<til::size>
{
public:
static WEX::Common::NoThrowString ToString(const ::til::size& size)
static WEX::Common::NoThrowString ToString(const til::size size)
{
return WEX::Common::NoThrowString(size.to_string().c_str());
}
};
template<>
class VerifyCompareTraits<::til::size, ::til::size>
class VerifyCompareTraits<til::size, til::size>
{
public:
static bool AreEqual(const ::til::size& expected, const ::til::size& actual) noexcept
static bool AreEqual(const til::size expected, const til::size actual) noexcept
{
return expected == actual;
}
static bool AreSame(const ::til::size& expected, const ::til::size& actual) noexcept
static bool AreSame(const til::size expected, const til::size actual) noexcept
{
return &expected == &actual;
}
static bool IsLessThan(const ::til::size& expectedLess, const ::til::size& expectedGreater) = delete;
static bool IsLessThan(const til::size expectedLess, const til::size expectedGreater) = delete;
static bool IsGreaterThan(const ::til::size& expectedGreater, const ::til::size& expectedLess) = delete;
static bool IsGreaterThan(const til::size expectedGreater, const til::size expectedLess) = delete;
static bool IsNull(const ::til::size& object) noexcept
static bool IsNull(const til::size object) noexcept
{
return object == til::size{};
}

View File

@ -33,13 +33,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
using reverse_iterator = typename decltype(_array)::reverse_iterator;
using const_reverse_iterator = typename decltype(_array)::const_reverse_iterator;
some() noexcept :
constexpr some() noexcept :
_array{},
_used{ 0 }
{
}
some(std::initializer_list<T> init)
constexpr some(std::initializer_list<T> init)
{
if (init.size() > N)
{
@ -60,13 +60,13 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return !(*this == other);
}
void fill(const T& _Value)
constexpr void fill(const T& _Value)
{
_array.fill(_Value);
_used = N;
}
void swap(some& _Other) noexcept(std::is_nothrow_swappable<T>::value)
constexpr void swap(some& _Other) noexcept(std::is_nothrow_swappable<T>::value)
{
_array.swap(_Other._array);
std::swap(_used, _Other._used);
@ -163,7 +163,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return _array.data();
}
void push_back(const T& val)
constexpr void push_back(const T& val)
{
if (_used >= N)
{
@ -175,7 +175,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
++_used;
}
void push_back(T&& val)
constexpr void push_back(T&& val)
{
if (_used >= N)
{
@ -187,7 +187,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
++_used;
}
void pop_back()
constexpr void pop_back()
{
if (_used <= 0)
{
@ -199,12 +199,12 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
til::at(_array, _used) = 0;
}
[[noreturn]] void _invalidArg() const
[[noreturn]] constexpr void _invalidArg() const
{
throw std::invalid_argument("invalid argument");
}
[[noreturn]] void _outOfRange() const
[[noreturn]] constexpr void _outOfRange() const
{
throw std::out_of_range("invalid some<T, N> subscript");
}

View File

@ -231,15 +231,10 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid
return S_OK;
}
[[nodiscard]] HRESULT BgfxEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
[[nodiscard]] HRESULT BgfxEngine::GetDirtyArea(gsl::span<const til::rect>& area) noexcept
{
SMALL_RECT r;
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
r.Top = 0;
r.Left = 0;
r.Right = _displayWidth > 0 ? (SHORT)(_displayWidth - 1) : 0;
_dirtyArea = r;
_dirtyArea.bottom = std::max(0, _displayHeight);
_dirtyArea.right = std::max(0, _displayWidth);
area = { &_dirtyArea,
1 };

View File

@ -68,7 +68,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rect>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
@ -81,7 +81,7 @@ namespace Microsoft::Console::Render
LONG _displayHeight;
LONG _displayWidth;
til::rectangle _dirtyArea;
til::rect _dirtyArea;
COORD _fontSize;

View File

@ -5,7 +5,7 @@
// These were generated by tools\TestTableWriter\GenerateTests.ps1
// Read tools\TestTableWriter\README.md for more details
// Define a few helpful variables
constexpr til::rectangle bufferSize{ 0, 0, 80, 300 };
constexpr til::rect bufferSize{ 0, 0, 80, 300 };
constexpr short midX{ 40 };
constexpr short midY{ 150 };
constexpr short midPopulatedY{ 75 };
@ -28,7 +28,7 @@ constexpr auto docEndM1L{ point_offset_by_line(docEnd, bufferSize, -1) };
constexpr auto docEndM5C{ point_offset_by_char(docEnd, bufferSize, -5) };
constexpr auto docEndM5L{ point_offset_by_line(docEnd, bufferSize, -5) };
constexpr auto docEndP1C{ point_offset_by_char(docEnd, bufferSize, 1) };
constexpr til::point lastCharPosLeft{ bufferSize.left(), lastCharPos.y() };
constexpr til::point lastCharPosLeft{ bufferSize.left, lastCharPos.y };
constexpr auto lastCharPosM1C{ point_offset_by_char(lastCharPos, bufferSize, -1) };
constexpr auto lastCharPosM1L{ point_offset_by_line(lastCharPos, bufferSize, -1) };
constexpr auto lastCharPosM4C{ point_offset_by_char(lastCharPos, bufferSize, -4) };
@ -39,7 +39,7 @@ constexpr auto lastCharPosP1C{ point_offset_by_char(lastCharPos, bufferSize, 1)
constexpr auto lastCharPosP2C{ point_offset_by_char(lastCharPos, bufferSize, 2) };
constexpr auto lastCharPosP5C{ point_offset_by_char(lastCharPos, bufferSize, 5) };
constexpr auto lastCharPosP6C{ point_offset_by_char(lastCharPos, bufferSize, 6) };
constexpr til::point midDocEndLeft{ bufferSize.left(), midDocEnd.y() };
constexpr til::point midDocEndLeft{ bufferSize.left, midDocEnd.y };
constexpr auto midDocEndM1C{ point_offset_by_char(midDocEnd, bufferSize, -1) };
constexpr auto midDocEndM1L{ point_offset_by_line(midDocEnd, bufferSize, -1) };
constexpr auto midDocEndM4C{ point_offset_by_char(midDocEnd, bufferSize, -4) };
@ -51,7 +51,7 @@ constexpr auto midDocEndP2C{ point_offset_by_char(midDocEnd, bufferSize, 2) };
constexpr auto midDocEndP5C{ point_offset_by_char(midDocEnd, bufferSize, 5) };
constexpr auto midDocEndP6C{ point_offset_by_char(midDocEnd, bufferSize, 6) };
constexpr auto midEmptySpaceP1C{ point_offset_by_char(midEmptySpace, bufferSize, 1) };
constexpr til::point midHistoryLeft{ bufferSize.left(), midHistory.y() };
constexpr til::point midHistoryLeft{ bufferSize.left, midHistory.y };
constexpr auto midHistoryM1C{ point_offset_by_char(midHistory, bufferSize, -1) };
constexpr auto midHistoryM1L{ point_offset_by_line(midHistory, bufferSize, -1) };
constexpr auto midHistoryM4C{ point_offset_by_char(midHistory, bufferSize, -4) };
@ -83,29 +83,29 @@ constexpr auto originP5C{ point_offset_by_char(origin, bufferSize, 5) };
constexpr auto originP5L{ point_offset_by_line(origin, bufferSize, 5) };
constexpr auto originP6C{ point_offset_by_char(origin, bufferSize, 6) };
constexpr auto originP6L{ point_offset_by_line(origin, bufferSize, 6) };
constexpr til::point segment0LmidTopP1L{ segment0, midTopP1L.y() };
constexpr til::point segment1LmidDocEnd{ segment1, midDocEnd.y() };
constexpr til::point segment1LmidHistory{ segment1, midHistory.y() };
constexpr til::point segment1LmidTop{ segment1, midTop.y() };
constexpr til::point segment1LmidTopP1L{ segment1, midTopP1L.y() };
constexpr til::point segment2LmidDocEnd{ segment2, midDocEnd.y() };
constexpr til::point segment2LmidDocEndM1L{ segment2, midDocEndM1L.y() };
constexpr til::point segment2LmidHistory{ segment2, midHistory.y() };
constexpr til::point segment2LmidHistoryM1L{ segment2, midHistoryM1L.y() };
constexpr til::point segment2LmidHistoryP1L{ segment2, midHistoryP1L.y() };
constexpr til::point segment2LmidTop{ segment2, midTop.y() };
constexpr til::point segment2LmidTopP1L{ segment2, midTopP1L.y() };
constexpr til::point segment3LmidDocEnd{ segment3, midDocEnd.y() };
constexpr til::point segment3LmidDocEndM1L{ segment3, midDocEndM1L.y() };
constexpr til::point segment3LmidHistory{ segment3, midHistory.y() };
constexpr til::point segment3LmidHistoryM1L{ segment3, midHistoryM1L.y() };
constexpr til::point segment3LmidHistoryP1L{ segment3, midHistoryP1L.y() };
constexpr til::point segment3LmidTop{ segment3, midTop.y() };
constexpr til::point segment3LmidTopP1L{ segment3, midTopP1L.y() };
constexpr til::point segment4LlastCharPosM1L{ segment4, lastCharPosM1L.y() };
constexpr til::point segment4LmidDocEnd{ segment4, midDocEnd.y() };
constexpr til::point segment4LmidHistory{ segment4, midHistory.y() };
constexpr til::point segment4LmidTop{ segment4, midTop.y() };
constexpr til::point segment0LmidTopP1L{ segment0, midTopP1L.y };
constexpr til::point segment1LmidDocEnd{ segment1, midDocEnd.y };
constexpr til::point segment1LmidHistory{ segment1, midHistory.y };
constexpr til::point segment1LmidTop{ segment1, midTop.y };
constexpr til::point segment1LmidTopP1L{ segment1, midTopP1L.y };
constexpr til::point segment2LmidDocEnd{ segment2, midDocEnd.y };
constexpr til::point segment2LmidDocEndM1L{ segment2, midDocEndM1L.y };
constexpr til::point segment2LmidHistory{ segment2, midHistory.y };
constexpr til::point segment2LmidHistoryM1L{ segment2, midHistoryM1L.y };
constexpr til::point segment2LmidHistoryP1L{ segment2, midHistoryP1L.y };
constexpr til::point segment2LmidTop{ segment2, midTop.y };
constexpr til::point segment2LmidTopP1L{ segment2, midTopP1L.y };
constexpr til::point segment3LmidDocEnd{ segment3, midDocEnd.y };
constexpr til::point segment3LmidDocEndM1L{ segment3, midDocEndM1L.y };
constexpr til::point segment3LmidHistory{ segment3, midHistory.y };
constexpr til::point segment3LmidHistoryM1L{ segment3, midHistoryM1L.y };
constexpr til::point segment3LmidHistoryP1L{ segment3, midHistoryP1L.y };
constexpr til::point segment3LmidTop{ segment3, midTop.y };
constexpr til::point segment3LmidTopP1L{ segment3, midTopP1L.y };
constexpr til::point segment4LlastCharPosM1L{ segment4, lastCharPosM1L.y };
constexpr til::point segment4LmidDocEnd{ segment4, midDocEnd.y };
constexpr til::point segment4LmidHistory{ segment4, midHistory.y };
constexpr til::point segment4LmidTop{ segment4, midTop.y };
struct GeneratedMovementTestInput
{
TextUnit unit;

View File

@ -21,23 +21,23 @@ using namespace Microsoft::WRL;
using namespace Microsoft::Console::Interactivity::Win32;
static constexpr til::point point_offset_by_char(const til::point start, const til::rectangle bounds, ptrdiff_t amt)
static constexpr til::point point_offset_by_char(const til::point start, const til::rect& bounds, til::CoordType amt)
{
ptrdiff_t pos_x = start.x();
ptrdiff_t pos_y = start.y();
auto pos_x = start.x;
auto pos_y = start.y;
while (amt != 0)
{
if (amt > 0)
{
if (pos_x == bounds.left() && pos_y == bounds.bottom())
if (pos_x == bounds.left && pos_y == bounds.bottom)
{
// end exclusive --> can't move any more
break;
}
else if (pos_x == bounds.right() - 1)
else if (pos_x == bounds.right - 1)
{
// right boundary --> wrap
pos_x = bounds.left();
pos_x = bounds.left;
++pos_y;
}
else
@ -49,15 +49,15 @@ static constexpr til::point point_offset_by_char(const til::point start, const t
}
else
{
if (pos_x == bounds.left() && pos_y == bounds.top())
if (pos_x == bounds.left && pos_y == bounds.top)
{
// origin --> can't move any more
break;
}
else if (pos_x == bounds.left())
else if (pos_x == bounds.left)
{
// left boundary --> wrap
pos_x = bounds.right() - 1;
pos_x = bounds.right - 1;
--pos_y;
}
else
@ -71,16 +71,16 @@ static constexpr til::point point_offset_by_char(const til::point start, const t
return { pos_x, pos_y };
}
static constexpr til::point point_offset_by_line(const til::point start, const til::rectangle bounds, ptrdiff_t amt)
static constexpr til::point point_offset_by_line(const til::point start, const til::rect& bounds, til::CoordType amt)
{
// X = left boundary for UIA
ptrdiff_t pos_x = bounds.left();
ptrdiff_t pos_y = start.y();
auto pos_x = bounds.left;
auto pos_y = start.y;
while (amt != 0)
{
if (amt > 0)
{
if (pos_y == bounds.bottom() + 1)
if (pos_y == bounds.bottom + 1)
{
break;
}
@ -92,7 +92,7 @@ static constexpr til::point point_offset_by_line(const til::point start, const t
}
else
{
if (pos_y == bounds.top())
if (pos_y == bounds.top)
{
break;
}
@ -399,8 +399,8 @@ class UiaTextRangeTests
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&degenerate,
_pUiaData,
&_dummyProvider,
origin,
origin));
origin.to_win32_coord(),
origin.to_win32_coord()));
VERIFY_IS_TRUE(degenerate->IsDegenerate());
VERIFY_ARE_EQUAL(degenerate->_start, degenerate->_end);
@ -410,8 +410,8 @@ class UiaTextRangeTests
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&notDegenerate,
_pUiaData,
&_dummyProvider,
origin,
end));
origin.to_win32_coord(),
end.to_win32_coord()));
VERIFY_IS_FALSE(notDegenerate->IsDegenerate());
VERIFY_ARE_NOT_EQUAL(notDegenerate->_start, notDegenerate->_end);
}
@ -422,8 +422,8 @@ class UiaTextRangeTests
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr1,
_pUiaData,
&_dummyProvider,
origin,
origin));
origin.to_win32_coord(),
origin.to_win32_coord()));
// utr2 initialized to have the same start/end as utr1
Microsoft::WRL::ComPtr<ITextRangeProvider> utr2;
@ -439,8 +439,8 @@ class UiaTextRangeTests
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr2,
_pUiaData,
&_dummyProvider,
origin,
end));
origin.to_win32_coord(),
end.to_win32_coord()));
Log::Comment(L"_end is different");
THROW_IF_FAILED(utr1->Compare(utr2.Get(), &comparison));
@ -453,8 +453,8 @@ class UiaTextRangeTests
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr1,
_pUiaData,
&_dummyProvider,
origin,
origin));
origin.to_win32_coord(),
origin.to_win32_coord()));
Microsoft::WRL::ComPtr<ITextRangeProvider> utr2;
THROW_IF_FAILED(utr1->Clone(&utr2));
@ -475,8 +475,8 @@ class UiaTextRangeTests
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr2,
_pUiaData,
&_dummyProvider,
origin,
end));
origin.to_win32_coord(),
end.to_win32_coord()));
Log::Comment(L"_start should match");
THROW_IF_FAILED(utr1->CompareEndpoints(TextPatternRangeEndpoint_Start, utr2.Get(), TextPatternRangeEndpoint_Start, &comparison));
@ -659,8 +659,8 @@ class UiaTextRangeTests
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&target,
_pUiaData,
&_dummyProvider,
origin,
origin));
origin.to_win32_coord(),
origin.to_win32_coord()));
};
Log::Comment(L"Move target's end to utr1's start");
@ -669,7 +669,7 @@ class UiaTextRangeTests
THROW_IF_FAILED(target->MoveEndpointByRange(TextPatternRangeEndpoint_End,
utr.Get(),
TextPatternRangeEndpoint_Start));
VERIFY_ARE_EQUAL(target->GetEndpoint(TextPatternRangeEndpoint_Start), origin);
VERIFY_ARE_EQUAL(target->GetEndpoint(TextPatternRangeEndpoint_Start), origin.to_win32_coord());
VERIFY_ARE_EQUAL(target->GetEndpoint(TextPatternRangeEndpoint_End), utr->GetEndpoint(TextPatternRangeEndpoint_Start));
}
@ -679,7 +679,7 @@ class UiaTextRangeTests
THROW_IF_FAILED(target->MoveEndpointByRange(TextPatternRangeEndpoint_End,
utr.Get(),
TextPatternRangeEndpoint_End));
VERIFY_ARE_EQUAL(target->GetEndpoint(TextPatternRangeEndpoint_Start), origin);
VERIFY_ARE_EQUAL(target->GetEndpoint(TextPatternRangeEndpoint_Start), origin.to_win32_coord());
VERIFY_ARE_EQUAL(target->GetEndpoint(TextPatternRangeEndpoint_End), utr->GetEndpoint(TextPatternRangeEndpoint_End));
THROW_IF_FAILED(target->MoveEndpointByRange(TextPatternRangeEndpoint_Start,
@ -738,7 +738,7 @@ class UiaTextRangeTests
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), gsl::narrow<short>(_pTextBuffer->GetLastNonSpaceCharacter().Y + 1) };
// clang-format off
const std::vector<MoveTest> testData
@ -798,8 +798,8 @@ class UiaTextRangeTests
-5,
{
-5,
{lastColumnIndex - 4, 0},
{lastColumnIndex - 3, 0}
{gsl::narrow<short>(lastColumnIndex - 4), 0},
{gsl::narrow<short>(lastColumnIndex - 3), 0}
}
}
};
@ -826,7 +826,7 @@ class UiaTextRangeTests
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), gsl::narrow<short>(_pTextBuffer->GetLastNonSpaceCharacter().Y + 1) };
// clang-format off
const std::vector<MoveTest> testData
@ -1060,7 +1060,7 @@ class UiaTextRangeTests
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), gsl::narrow<short>(_pTextBuffer->GetLastNonSpaceCharacter().Y + 1) };
// clang-format off
const std::vector<MoveEndpointTest> testData
@ -1134,7 +1134,7 @@ class UiaTextRangeTests
MoveEndpointTest{
L"can't move _end forwards when it's on the bottom row (past doc end)",
{0, 0},
{lastColumnIndex - 3, bottomRow},
{gsl::narrow<short>(lastColumnIndex - 3), bottomRow},
1,
TextPatternRangeEndpoint_End,
0,
@ -1145,7 +1145,7 @@ class UiaTextRangeTests
MoveEndpointTest{
L"can't move _end forwards when it's at the end of the buffer already (past doc end)",
{0, 0},
{0, bottomRow+1},
{0, gsl::narrow<short>(bottomRow + 1)},
1,
TextPatternRangeEndpoint_End,
0,
@ -1167,7 +1167,7 @@ class UiaTextRangeTests
MoveEndpointTest{
L"moving _end backward when it's already on the top row creates a degenerate range at the document start",
{4, 0},
{lastColumnIndex - 5, 0},
{gsl::narrow<short>(lastColumnIndex - 5), 0},
-1,
TextPatternRangeEndpoint_End,
-1,
@ -1198,7 +1198,7 @@ class UiaTextRangeTests
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), _pTextBuffer->GetLastNonSpaceCharacter().Y + 1 };
const COORD documentEnd{ _pTextBuffer->GetSize().Left(), gsl::narrow<short>(_pTextBuffer->GetLastNonSpaceCharacter().Y + 1) };
// clang-format off
const std::vector<MoveEndpointTest> testData =
@ -1232,7 +1232,7 @@ class UiaTextRangeTests
MoveEndpointTest{
L"can't move _end forward when it's already at the end of the buffer (past doc end)",
{3, 2},
{0, bottomRow+1},
{0, gsl::narrow<short>(bottomRow + 1)},
1,
TextPatternRangeEndpoint_End,
{
@ -1320,10 +1320,10 @@ class UiaTextRangeTests
Log::Comment(NoThrowString().Format(L"%s", toString(static_cast<TextUnit>(textUnit))));
// Create a degenerate UTR at EndExclusive
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, bufferEnd, endExclusive));
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, bufferEnd.to_win32_coord(), endExclusive.to_win32_coord()));
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(static_cast<TextUnit>(textUnit)));
VERIFY_ARE_EQUAL(documentEnd, til::point{ utr->_end });
VERIFY_ARE_EQUAL(documentEnd, utr->_end);
}
TEST_METHOD(MovementAtExclusiveEnd)
@ -1335,16 +1335,16 @@ class UiaTextRangeTests
// write "temp" at (2,2)
_pTextBuffer->Reset();
const til::point writeTarget{ 2, 2 };
_pTextBuffer->Write({ L"temp" }, writeTarget);
_pTextBuffer->Write({ L"temp" }, writeTarget.to_win32_coord());
// GH#6986: This is used as the "end of the buffer" to help screen readers run faster
// instead of parsing through thousands of empty lines of text.
const COORD documentEndInclusive{ base::ClampSub<short, short>(static_cast<short>(bufferSize.right()), 1), _pTextBuffer->GetLastNonSpaceCharacter().Y };
const COORD documentEndExclusive{ static_cast<short>(bufferSize.left()), base::ClampAdd(documentEndInclusive.Y, 1) };
const til::point documentEndInclusive{ base::ClampSub<short, short>(static_cast<short>(bufferSize.right), 1), _pTextBuffer->GetLastNonSpaceCharacter().Y };
const til::point documentEndExclusive{ static_cast<short>(bufferSize.left), base::ClampAdd(documentEndInclusive.y, 1) };
const COORD lastLineStart{ static_cast<short>(bufferSize.left()), documentEndInclusive.Y };
const auto secondToLastLinePos{ point_offset_by_line(lastLineStart, bufferSize, -1) };
const COORD secondToLastCharacterPos{ documentEndInclusive.X - 1, documentEndInclusive.Y };
const til::point lastLineStart{ static_cast<short>(bufferSize.left), documentEndInclusive.y };
const til::point secondToLastLinePos{ point_offset_by_line(til::point{ lastLineStart }, bufferSize, -1) };
const til::point secondToLastCharacterPos{ documentEndInclusive.x - 1, documentEndInclusive.y };
// Iterate over each TextUnit. If we don't support
// the given TextUnit, we're supposed to fallback
@ -1368,22 +1368,22 @@ class UiaTextRangeTests
Log::Comment(NoThrowString().Format(L"Forward by %s", toString(textUnit)));
// Create an UTR at EndExclusive
const auto utrEnd{ atDocumentEnd ? documentEndExclusive : static_cast<COORD>(endExclusive) };
const auto utrEnd{ atDocumentEnd ? documentEndExclusive : til::point{ endExclusive } };
if (degenerate)
{
// UTR: (exclusive, exclusive) range
const auto utrStart{ atDocumentEnd ? documentEndExclusive : static_cast<COORD>(endExclusive) };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
const auto utrStart{ atDocumentEnd ? documentEndExclusive : endExclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart.to_win32_coord(), utrEnd.to_win32_coord()));
}
else
{
// UTR: (inclusive, exclusive) range
const auto utrStart{ atDocumentEnd ? documentEndInclusive : static_cast<COORD>(endInclusive) };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
const auto utrStart{ atDocumentEnd ? documentEndInclusive : endInclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart.to_win32_coord(), utrEnd.to_win32_coord()));
}
THROW_IF_FAILED(utr->Move(textUnit, 1, &moveAmt));
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
VERIFY_ARE_EQUAL(documentEndExclusive.to_win32_coord(), utr->_end);
VERIFY_ARE_EQUAL(0, moveAmt);
// Verify expansion works properly
@ -1391,37 +1391,37 @@ class UiaTextRangeTests
THROW_IF_FAILED(utr->ExpandToEnclosingUnit(textUnit));
if (textUnit <= TextUnit::TextUnit_Character)
{
VERIFY_ARE_EQUAL(documentEndInclusive, utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
VERIFY_ARE_EQUAL(documentEndInclusive.to_win32_coord(), utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive.to_win32_coord(), utr->_end);
}
else if (textUnit <= TextUnit::TextUnit_Word)
{
VERIFY_ARE_EQUAL(writeTarget, til::point{ utr->_start });
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
VERIFY_ARE_EQUAL(writeTarget.to_win32_coord(), utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive.to_win32_coord(), utr->_end);
}
else if (textUnit <= TextUnit::TextUnit_Line)
{
VERIFY_ARE_EQUAL(lastLineStart, utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
VERIFY_ARE_EQUAL(lastLineStart.to_win32_coord(), utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive.to_win32_coord(), utr->_end);
}
else // textUnit <= TextUnit::TextUnit_Document:
{
VERIFY_ARE_EQUAL(origin, til::point{ utr->_start });
VERIFY_ARE_EQUAL(documentEndExclusive, utr->_end);
VERIFY_ARE_EQUAL(origin.to_win32_coord(), utr->_start);
VERIFY_ARE_EQUAL(documentEndExclusive.to_win32_coord(), utr->_end);
}
// reset the UTR
if (degenerate)
{
// UTR: (exclusive, exclusive) range
const auto utrStart{ atDocumentEnd ? documentEndExclusive : static_cast<COORD>(endExclusive) };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
const auto utrStart{ atDocumentEnd ? documentEndExclusive : endExclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart.to_win32_coord(), utrEnd.to_win32_coord()));
}
else
{
// UTR: (inclusive, exclusive) range
const auto utrStart{ atDocumentEnd ? documentEndInclusive : static_cast<COORD>(endInclusive) };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart, utrEnd));
const auto utrStart{ atDocumentEnd ? documentEndInclusive : endInclusive };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, utrStart.to_win32_coord(), utrEnd.to_win32_coord()));
}
// Verify that moving backwards still works properly
@ -1435,8 +1435,8 @@ class UiaTextRangeTests
// - degenerate --> it moves with _start to stay degenerate
// - !degenerate --> it excludes the last char, to select the second to last char
VERIFY_ARE_EQUAL(-1, moveAmt);
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? documentEndInclusive : secondToLastCharacterPos, utr->_start);
VERIFY_ARE_EQUAL(documentEndInclusive, utr->_end);
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? documentEndInclusive : secondToLastCharacterPos, til::point{ utr->_start });
VERIFY_ARE_EQUAL(documentEndInclusive, til::point{ utr->_end });
}
else if (textUnit <= TextUnit::TextUnit_Word)
{
@ -1448,7 +1448,7 @@ class UiaTextRangeTests
{
VERIFY_ARE_EQUAL(-1, moveAmt);
VERIFY_ARE_EQUAL(degenerate || !atDocumentEnd ? til::point{ lastLineStart } : secondToLastLinePos, til::point{ utr->_start });
VERIFY_ARE_EQUAL(lastLineStart, utr->_end);
VERIFY_ARE_EQUAL(lastLineStart, til::point{ utr->_end });
}
else // textUnit <= TextUnit::TextUnit_Document:
{
@ -1464,11 +1464,11 @@ class UiaTextRangeTests
const auto originExclusive{ point_offset_by_char(origin, bufferSize, 1) };
_pTextBuffer->Write({ L"My name is Carlos" }, origin);
_pTextBuffer->Write({ L"My name is Carlos" }, origin.to_win32_coord());
// Create degenerate UTR at origin
Microsoft::WRL::ComPtr<UiaTextRange> utr;
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, origin, origin));
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, origin.to_win32_coord(), origin.to_win32_coord()));
// move forward by a word
int moveAmt;
@ -1503,17 +1503,17 @@ class UiaTextRangeTests
const auto viewportSize{ _pUiaData->GetViewport() };
const std::vector<ScrollTest> testData{
{ L"Origin", gsl::narrow<short>(bufferSize.top()) },
{ L"ViewportHeight From Top - 1", base::ClampedNumeric<short>(bufferSize.top()) + viewportSize.Height() - 1 },
{ L"ViewportHeight From Top", base::ClampedNumeric<short>(bufferSize.top()) + viewportSize.Height() },
{ L"ViewportHeight From Top + 1", base::ClampedNumeric<short>(bufferSize.top()) + viewportSize.Height() + 1 },
{ L"ViewportHeight From Bottom - 1", base::ClampedNumeric<short>(bufferSize.bottom()) - viewportSize.Height() - 2 },
{ L"ViewportHeight From Bottom", base::ClampedNumeric<short>(bufferSize.bottom()) - viewportSize.Height() - 1 },
{ L"ViewportHeight From Bottom + 1", base::ClampedNumeric<short>(bufferSize.bottom()) - viewportSize.Height() + 1 },
{ L"Origin", gsl::narrow<short>(bufferSize.top) },
{ L"ViewportHeight From Top - 1", base::ClampedNumeric<short>(bufferSize.top) + viewportSize.Height() - 1 },
{ L"ViewportHeight From Top", base::ClampedNumeric<short>(bufferSize.top) + viewportSize.Height() },
{ L"ViewportHeight From Top + 1", base::ClampedNumeric<short>(bufferSize.top) + viewportSize.Height() + 1 },
{ L"ViewportHeight From Bottom - 1", base::ClampedNumeric<short>(bufferSize.bottom) - viewportSize.Height() - 2 },
{ L"ViewportHeight From Bottom", base::ClampedNumeric<short>(bufferSize.bottom) - viewportSize.Height() - 1 },
{ L"ViewportHeight From Bottom + 1", base::ClampedNumeric<short>(bufferSize.bottom) - viewportSize.Height() + 1 },
// GH#7839: ExclusiveEnd is a non-existent space,
// so scrolling to it when !alignToTop used to crash
{ L"Exclusive End", gsl::narrow<short>(bufferSize.bottom()) }
{ L"Exclusive End", gsl::narrow<short>(bufferSize.bottom) }
};
BEGIN_TEST_METHOD_PROPERTIES()
@ -1527,8 +1527,8 @@ class UiaTextRangeTests
for (const auto test : testData)
{
Log::Comment(test.comment.c_str());
const til::point pos{ bufferSize.left(), test.yPos };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, pos, pos));
const til::point pos{ bufferSize.left, test.yPos };
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, pos.to_win32_coord(), pos.to_win32_coord()));
VERIFY_SUCCEEDED(utr->ScrollIntoView(alignToTop));
}
}
@ -1856,7 +1856,7 @@ class UiaTextRangeTests
const auto secondChar{ point_offset_by_char(origin, bufferSize, 2) };
const auto fifthChar{ point_offset_by_char(origin, bufferSize, 5) };
const auto sixthChar{ point_offset_by_char(origin, bufferSize, 6) };
const til::point documentEnd{ bufferSize.left(), (bufferSize.height() / 2) + 1 };
const til::point documentEnd{ bufferSize.left, (bufferSize.height() / 2) + 1 };
// Populate buffer
// Split the line into 5 segments alternating between "X" and whitespace
@ -1868,9 +1868,9 @@ class UiaTextRangeTests
// |_______________|
{
short i = 0;
auto iter{ _pTextBuffer->GetCellDataAt(origin) };
auto iter{ _pTextBuffer->GetCellDataAt(origin.to_win32_coord()) };
const auto segment{ bufferSize.width() / 5 };
while (iter.Pos() != documentEnd)
while (iter.Pos() != documentEnd.to_win32_coord())
{
bool fill{ true };
if (i % segment == 0)
@ -1936,12 +1936,12 @@ class UiaTextRangeTests
// +------------------------------+
{
short i = 0;
auto iter{ _pTextBuffer->GetCellDataAt(bufferSize.origin()) };
auto iter{ _pTextBuffer->GetCellDataAt(bufferSize.origin().to_win32_coord()) };
const auto segment{ bufferSize.width() / 10 };
bool fill{ true };
while (iter.Pos() != docEnd)
while (iter.Pos() != docEnd.to_win32_coord())
{
if (iter.Pos().X == bufferSize.left())
if (iter.Pos().X == bufferSize.left)
{
fill = true;
}
@ -1977,7 +1977,7 @@ class UiaTextRangeTests
{
Microsoft::WRL::ComPtr<UiaTextRange> utr;
int amountMoved;
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, testCase.input.start, testCase.input.end));
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<UiaTextRange>(&utr, _pUiaData, &_dummyProvider, testCase.input.start.to_win32_coord(), testCase.input.end.to_win32_coord()));
THROW_IF_FAILED(utr->Move(testCase.input.unit, testCase.input.moveAmount, &amountMoved));
VERIFY_ARE_EQUAL(testCase.expected.moveAmount, amountMoved);

View File

@ -232,7 +232,7 @@ try
}
CATCH_RETURN()
[[nodiscard]] HRESULT AtlasEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
[[nodiscard]] HRESULT AtlasEngine::GetDirtyArea(gsl::span<const til::rect>& area) noexcept
{
area = gsl::span{ &_api.dirtyRect, 1 };
return S_OK;

View File

@ -377,11 +377,11 @@ try
}
}
_api.dirtyRect = til::rectangle{
static_cast<ptrdiff_t>(0),
static_cast<ptrdiff_t>(_api.invalidatedRows.x),
static_cast<ptrdiff_t>(_api.cellCount.x),
static_cast<ptrdiff_t>(_api.invalidatedRows.y),
_api.dirtyRect = til::rect{
0,
_api.invalidatedRows.x,
_api.cellCount.x,
_api.invalidatedRows.y,
};
return S_OK;

View File

@ -49,7 +49,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT UpdateDpi(int iDpi) noexcept override;
[[nodiscard]] HRESULT UpdateViewport(SMALL_RECT srNewViewport) noexcept override;
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, int iDpi) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rect>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(std::wstring_view glyph, _Out_ bool* pResult) noexcept override;
[[nodiscard]] HRESULT UpdateTitle(std::wstring_view newTitle) noexcept override;
@ -741,7 +741,7 @@ namespace Microsoft::Console::Render
u32 selectionColor = 0x7fffffff;
// dirtyRect is a computed value based on invalidatedRows.
til::rectangle dirtyRect;
til::rect dirtyRect;
// These "invalidation" fields are reset in EndPaint()
u16r invalidatedCursorArea = invalidatedAreaNone;
u16x2 invalidatedRows = invalidatedRowsNone; // x is treated as "top" and y as "bottom"

View File

@ -107,8 +107,8 @@ FontResource::operator HFONT()
void FontResource::_regenerateFont()
{
const auto targetWidth = _targetSize.width<WORD>();
const auto targetHeight = _targetSize.height<WORD>();
const auto targetWidth = _targetSize.narrow_width<WORD>();
const auto targetHeight = _targetSize.narrow_height<WORD>();
const auto charSizeInBytes = (targetWidth + 7) / 8 * targetHeight;
const DWORD fontBitmapSize = charSizeInBytes * CHAR_COUNT;
@ -171,10 +171,10 @@ void FontResource::_regenerateFont()
void FontResource::_resizeBitPattern(gsl::span<byte> targetBuffer)
{
auto sourceWidth = _sourceSize.width<int>();
auto targetWidth = _targetSize.width<int>();
const auto sourceHeight = _sourceSize.height<int>();
const auto targetHeight = _targetSize.height<int>();
auto sourceWidth = _sourceSize.width;
auto targetWidth = _targetSize.width;
const auto sourceHeight = _sourceSize.height;
const auto targetHeight = _targetSize.height;
// If the text in the font is not perfectly centered, the _centeringHint
// gives us the offset needed to correct that misalignment. So to ensure
@ -214,7 +214,7 @@ void FontResource::_resizeBitPattern(gsl::span<byte> targetBuffer)
// Once we've calculated the scaling increments, taking the centering hint
// into account, we reset the target width back to its original value.
targetWidth = _targetSize.width<int>();
targetWidth = _targetSize.width;
auto targetBufferPointer = targetBuffer.begin();
for (auto ch = 0; ch < CHAR_COUNT; ch++)

View File

@ -354,17 +354,17 @@ void Renderer::TriggerSelection()
// Restrict all previous selection rectangles to inside the current viewport bounds
for (auto& sr : _previousSelection)
{
// Make the exclusive SMALL_RECT into a til::rectangle.
til::rectangle rc{ Viewport::FromExclusive(sr).ToInclusive() };
// Make the exclusive SMALL_RECT into a til::rect.
til::rect rc{ Viewport::FromExclusive(sr).ToInclusive() };
// Make a viewport representing the coordinates that are currently presentable.
const til::rectangle viewport{ til::size{ _pData->GetViewport().Dimensions() } };
const til::rect viewport{ til::size{ _pData->GetViewport().Dimensions() } };
// Intersect them so we only invalidate things that are still visible.
rc &= viewport;
// Convert back into the exclusive SMALL_RECT and store in the vector.
sr = Viewport::FromInclusive(rc).ToExclusive();
sr = Viewport::FromInclusive(rc.to_small_rect()).ToExclusive();
}
FOREACH_ENGINE(pEngine)
@ -408,7 +408,7 @@ bool Renderer::_CheckViewportAndScroll()
LOG_IF_FAILED(engine->InvalidateScroll(&coordDelta));
}
_ScrollPreviousSelection(coordDelta);
_ScrollPreviousSelection(til::point{ coordDelta });
return true;
}
@ -443,7 +443,7 @@ void Renderer::TriggerScroll(const COORD* const pcoordDelta)
LOG_IF_FAILED(pEngine->InvalidateScroll(pcoordDelta));
}
_ScrollPreviousSelection(*pcoordDelta);
_ScrollPreviousSelection(til::point{ *pcoordDelta });
_NotifyPaintFrame();
}
@ -665,7 +665,7 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
// This is effectively the number of cells on the visible screen that need to be redrawn.
// The origin is always 0, 0 because it represents the screen itself, not the underlying buffer.
gsl::span<const til::rectangle> dirtyAreas;
gsl::span<const til::rect> dirtyAreas;
LOG_IF_FAILED(pEngine->GetDirtyArea(dirtyAreas));
// This is to make sure any transforms are reset when this paint is finished.
@ -676,12 +676,12 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
for (const auto& dirtyRect : dirtyAreas)
{
// Shortcut: don't bother redrawing if the width is 0.
if (dirtyRect.left() == dirtyRect.right())
if (dirtyRect.left == dirtyRect.right)
{
continue;
}
auto dirty = Viewport::FromInclusive(dirtyRect);
auto dirty = Viewport::FromInclusive(dirtyRect.to_small_rect());
// Shift the origin of the dirty region to match the underlying buffer so we can
// compare the two regions directly for intersection.
@ -805,7 +805,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine,
// We also accumulate clusters according to regex patterns
do
{
COORD thisPoint{ screenPoint.X + gsl::narrow<SHORT>(cols), screenPoint.Y };
COORD thisPoint{ gsl::narrow<SHORT>(screenPoint.X + cols), screenPoint.Y };
const auto thisPointPatterns = _pData->GetPatternId(thisPoint);
const auto thisUsingSoftFont = s_IsSoftFontChar(it->Chars(), _firstSoftFontChar, _lastSoftFontChar);
const auto changedPatternOrFont = patternIds != thisPointPatterns || usingSoftFont != thisUsingSoftFont;
@ -1113,12 +1113,13 @@ void Renderer::_PaintOverlay(IRenderEngine& engine,
// Set it up in a Viewport helper structure and trim it the IME viewport to be within the full console viewport.
Viewport viewConv = Viewport::FromInclusive(srCaView);
gsl::span<const til::rectangle> dirtyAreas;
gsl::span<const til::rect> dirtyAreas;
LOG_IF_FAILED(engine.GetDirtyArea(dirtyAreas));
for (SMALL_RECT srDirty : dirtyAreas)
for (const auto& rect : dirtyAreas)
{
// Dirty is an inclusive rectangle, but oddly enough the IME was an exclusive one, so correct it.
auto srDirty = rect.to_small_rect();
srDirty.Bottom++;
srDirty.Right++;
@ -1173,7 +1174,7 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
{
try
{
gsl::span<const til::rectangle> dirtyAreas;
gsl::span<const til::rect> dirtyAreas;
LOG_IF_FAILED(pEngine->GetDirtyArea(dirtyAreas));
// Get selection rectangles
@ -1185,7 +1186,7 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
// Make a copy as `TrimToViewport` will manipulate it and
// can destroy it for the next dirtyRect to test against.
auto rectCopy = rect;
Viewport dirtyView = Viewport::FromInclusive(dirtyRect);
Viewport dirtyView = Viewport::FromInclusive(dirtyRect.to_small_rect());
if (dirtyView.TrimToViewport(&rectCopy))
{
LOG_IF_FAILED(pEngine->PaintSelection(rectCopy));
@ -1280,13 +1281,13 @@ void Renderer::_ScrollPreviousSelection(const til::point delta)
for (auto& sr : _previousSelection)
{
// Get a rectangle representing this piece of the selection.
til::rectangle rc = Viewport::FromExclusive(sr).ToInclusive();
til::rect rc{ Viewport::FromExclusive(sr).ToInclusive() };
// Offset the entire existing rectangle by the delta.
rc += delta;
// Store it back into the vector.
sr = Viewport::FromInclusive(rc).ToExclusive();
sr = Viewport::FromInclusive(rc.to_small_rect()).ToExclusive();
}
}
}

View File

@ -27,7 +27,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<DxFontRenderData*> const fontRe
_runs{},
_breakpoints{},
_runIndex{ 0 },
_width{ gsl::narrow_cast<size_t>(fontRenderData->GlyphCell().width()) },
_width{ gsl::narrow_cast<size_t>(fontRenderData->GlyphCell().width) },
_isEntireTextSimple{ false }
{
_localeName.resize(gsl::narrow_cast<size_t>(fontRenderData->DefaultTextFormat()->GetLocaleNameLength()) + 1); // +1 for null

View File

@ -273,18 +273,17 @@ try
// TODO GH#6338: Add support for `"cursorTextColor": null` for letting the
// cursor draw on top again.
// **MATH** PHASE
const til::size glyphSize{ til::math::flooring,
drawingContext.cellSize.width,
drawingContext.cellSize.height };
// Create rectangular block representing where the cursor can fill.
D2D1_RECT_F rect = til::rectangle{ til::point{ options.coordCursor } }.scale_up(glyphSize);
D2D1_RECT_F rect;
rect.left = options.coordCursor.X * drawingContext.cellSize.width;
rect.top = options.coordCursor.Y * drawingContext.cellSize.height;
rect.right = rect.left + drawingContext.cellSize.width;
rect.bottom = rect.top + drawingContext.cellSize.height;
// If we're double-width, make it one extra glyph wider
if (options.fIsDoubleWidth)
{
rect.right += glyphSize.width();
rect.right += drawingContext.cellSize.width;
}
// If the cursor isn't within the bounds of this current run of text, do nothing.
@ -303,7 +302,7 @@ try
{
// Enforce min/max cursor height
ULONG ulHeight = std::clamp(options.ulCursorHeightPercent, MinCursorHeightPercent, MaxCursorHeightPercent);
ulHeight = (glyphSize.height<ULONG>() * ulHeight) / 100;
ulHeight = gsl::narrow_cast<ULONG>(drawingContext.cellSize.height * ulHeight) / 100;
ulHeight = std::max(ulHeight, MinCursorHeightPixels); // No smaller than 1px
rect.top = rect.bottom - ulHeight;

View File

@ -114,7 +114,7 @@ DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr<IDWriteFactory1> dwr
if (!_boxDrawingEffect)
{
// Calculate and cache the box effect for the base font. Scale is 1.0f because the base font is exactly the scale we want already.
THROW_IF_FAILED(s_CalculateBoxEffect(DefaultTextFormat().Get(), _glyphCell.width(), DefaultFontFace().Get(), 1.0f, &_boxDrawingEffect));
THROW_IF_FAILED(s_CalculateBoxEffect(DefaultTextFormat().Get(), _glyphCell.width, DefaultFontFace().Get(), 1.0f, &_boxDrawingEffect));
}
return _boxDrawingEffect;
@ -738,7 +738,7 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font
// - 12 ppi font * (96 dpi / 96 dpi) * (96 dpi / 72 points per inch) = 16 pixels tall font for 100% display (96 dpi is 100%)
// - 12 ppi font * (144 dpi / 96 dpi) * (96 dpi / 72 points per inch) = 24 pixels tall font for 150% display (144 dpi is 150%)
// - 12 ppi font * (192 dpi / 96 dpi) * (96 dpi / 72 points per inch) = 32 pixels tall font for 200% display (192 dpi is 200%)
float heightDesired = static_cast<float>(desired.GetEngineSize().Y) * static_cast<float>(USER_DEFAULT_SCREEN_DPI) / POINTS_PER_INCH;
float heightDesired = desired.GetEngineSize().Y * USER_DEFAULT_SCREEN_DPI / POINTS_PER_INCH;
// The advance is the number of pixels left-to-right (X dimension) for the given font.
// We're finding a proportional factor here with the design units in "ems", not an actual pixel measurement.
@ -891,7 +891,7 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font
_lineMetrics = lineMetrics;
_glyphCell = actual.GetSize();
_glyphCell = til::size{ actual.GetSize() };
}
Microsoft::WRL::ComPtr<IDWriteTextFormat> DxFontRenderData::_BuildTextFormat(const DxFontInfo& fontInfo, const std::wstring_view localeName)

View File

@ -344,8 +344,8 @@ HRESULT DxEngine::_SetupTerminalEffects()
// Setup the viewport.
D3D11_VIEWPORT vp;
vp.Width = _displaySizePixels.width<float>();
vp.Height = _displaySizePixels.height<float>();
vp.Width = static_cast<float>(_displaySizePixels.width);
vp.Height = static_cast<float>(_displaySizePixels.height);
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
@ -463,8 +463,8 @@ void DxEngine::_ComputePixelShaderSettings() noexcept
_pixelShaderSettings.Scale = _scale;
// Set the display resolution
const float w = 1.0f * _displaySizePixels.width<UINT>();
const float h = 1.0f * _displaySizePixels.height<UINT>();
const float w = static_cast<float>(_displaySizePixels.width);
const float h = static_cast<float>(_displaySizePixels.height);
_pixelShaderSettings.Resolution = XMFLOAT2{ w, h };
// Set the background
@ -653,8 +653,8 @@ try
RETURN_IF_FAILED(_dxgiFactory2.As(&_dxgiFactoryMedia));
// Use the given target size for compositions.
_swapChainDesc.Width = _displaySizePixels.width<UINT>();
_swapChainDesc.Height = _displaySizePixels.height<UINT>();
_swapChainDesc.Width = _displaySizePixels.narrow_width<UINT>();
_swapChainDesc.Height = _displaySizePixels.narrow_height<UINT>();
// We're doing advanced composition pretty much for the purpose of pretty alpha, so turn it on.
_swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
@ -942,8 +942,8 @@ try
return _dwriteFactory->CreateTextLayout(string,
gsl::narrow<UINT32>(stringLength),
_fontRenderData->DefaultTextFormat().Get(),
_displaySizePixels.width<float>(),
_fontRenderData->GlyphCell().height() != 0 ? _fontRenderData->GlyphCell().height<float>() : _displaySizePixels.height<float>(),
static_cast<float>(_displaySizePixels.width),
_fontRenderData->GlyphCell().height != 0 ? _fontRenderData->GlyphCell().narrow_height<float>() : _displaySizePixels.narrow_height<float>(),
ppTextLayout);
}
CATCH_RETURN()
@ -965,7 +965,7 @@ CATCH_RETURN()
[[nodiscard]] HRESULT DxEngine::SetWindowSize(const SIZE Pixels) noexcept
try
{
_sizeTarget = Pixels;
_sizeTarget = til::size{ Pixels };
return S_OK;
}
CATCH_RETURN();
@ -1006,7 +1006,7 @@ try
{
// Enable shader effects if the path isn't empty. Otherwise leave it untouched.
_terminalEffectsEnabled = value.empty() ? _terminalEffectsEnabled : true;
_pixelShaderPath = { value };
_pixelShaderPath = std::wstring{ value };
_recreateDeviceRequested = true;
LOG_IF_FAILED(InvalidateAll());
}
@ -1058,17 +1058,17 @@ HANDLE DxEngine::GetSwapChainHandle() noexcept
return _swapChainHandle.get();
}
void DxEngine::_InvalidateRectangle(const til::rectangle& rc)
void DxEngine::_InvalidateRectangle(const til::rect& rc)
{
const auto size = _invalidMap.size();
const auto topLeft = til::point{ 0, std::min(size.height(), rc.top()) };
const auto bottomRight = til::point{ size.width(), std::min(size.height(), rc.bottom()) };
_invalidMap.set({ topLeft, bottomRight });
const auto topLeft = til::point{ 0, std::min(size.height, rc.top) };
const auto bottomRight = til::point{ size.width, std::min(size.height, rc.bottom) };
_invalidMap.set(til::rect{ topLeft, bottomRight });
}
bool DxEngine::_IsAllInvalid() const noexcept
{
return std::llabs(_invalidScroll.y()) >= _invalidMap.size().height();
return std::abs(_invalidScroll.y) >= _invalidMap.size().height;
}
// Routine Description:
@ -1084,7 +1084,7 @@ try
if (!_allInvalid)
{
_InvalidateRectangle(Viewport::FromExclusive(*psrRegion).ToInclusive());
_InvalidateRectangle(til::rect{ Viewport::FromExclusive(*psrRegion).ToInclusive() });
}
return S_OK;
@ -1117,7 +1117,7 @@ try
{
// Dirty client is in pixels. Use divide specialization against glyph factor to make conversion
// to cells.
_InvalidateRectangle(til::rectangle{ *prcDirtyClient }.scale_down(_fontRenderData->GlyphCell()));
_InvalidateRectangle(til::rect{ *prcDirtyClient }.scale_down(_fontRenderData->GlyphCell()));
}
return S_OK;
@ -1159,7 +1159,7 @@ try
if (!_allInvalid)
{
if (deltaCells != til::point{ 0, 0 })
if (deltaCells != til::point{})
{
// Shift the contents of the map and fill in revealed area.
_invalidMap.translate(deltaCells, true);
@ -1226,7 +1226,7 @@ CATCH_RETURN();
RECT clientRect = { 0 };
LOG_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &clientRect));
return til::rectangle{ clientRect }.size();
return til::rect{ clientRect }.size();
}
case SwapChainMode::ForComposition:
{
@ -1323,7 +1323,7 @@ try
_d2dBitmap.Reset();
// Change the buffer size and recreate the render target (and surface)
RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.width<UINT>(), clientSize.height<UINT>(), _swapChainDesc.Format, _swapChainDesc.Flags));
RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.narrow_width<UINT>(), clientSize.narrow_height<UINT>(), _swapChainDesc.Format, _swapChainDesc.Flags));
RETURN_IF_FAILED(_PrepareRenderTarget());
// OK we made it past the parts that can cause errors. We can release our failure handler.
@ -1354,7 +1354,7 @@ try
_ShouldForceGrayscaleAA(),
_dwriteFactory.Get(),
spacing,
glyphCellSize,
glyphCellSize.to_d2d_size(),
_d2dDeviceContext->GetSize(),
std::nullopt,
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
@ -1391,11 +1391,11 @@ try
{
if (_invalidScroll != til::point{ 0, 0 })
{
// Copy `til::rectangles` into RECT map.
// Copy `til::rects` into RECT map.
_presentDirty.assign(_invalidMap.begin(), _invalidMap.end());
// Scale all dirty rectangles into pixels
std::transform(_presentDirty.begin(), _presentDirty.end(), _presentDirty.begin(), [&](til::rectangle rc) {
std::transform(_presentDirty.begin(), _presentDirty.end(), _presentDirty.begin(), [&](const til::rect& rc) {
return rc.scale_up(_fontRenderData->GlyphCell());
});
@ -1403,20 +1403,25 @@ try
const auto scrollPixels = (_invalidScroll * _fontRenderData->GlyphCell());
// The scroll rect is the entire field of cells, but in pixels.
til::rectangle scrollArea{ _invalidMap.size() * _fontRenderData->GlyphCell() };
til::rect scrollArea{ _invalidMap.size() * _fontRenderData->GlyphCell() };
// Reduce the size of the rectangle by the scroll.
scrollArea -= til::size{} - scrollPixels;
// Assign the area to the present storage
_presentScroll = scrollArea;
_presentScroll = scrollArea.to_win32_rect();
// Pass the offset.
_presentOffset = scrollPixels;
_presentOffset = scrollPixels.to_win32_point();
// Now fill up the parameters structure from the member variables.
_presentParams.DirtyRectsCount = gsl::narrow<UINT>(_presentDirty.size());
_presentParams.pDirtyRects = _presentDirty.data();
// It's not nice to use reinterpret_cast between til::rect and RECT,
// but to be honest... it does save a ton of type juggling.
static_assert(sizeof(decltype(_presentDirty)::value_type) == sizeof(RECT));
#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1).
_presentParams.pDirtyRects = reinterpret_cast<RECT*>(_presentDirty.data());
_presentParams.pScrollOffset = &_presentOffset;
_presentParams.pScrollRect = &_presentScroll;
@ -1646,7 +1651,7 @@ try
// Runs are counts of cells.
// Use a transform by the size of one cell to convert cells-to-pixels
// as we clear.
_d2dDeviceContext->SetTransform(D2D1::Matrix3x2F::Scale(_fontRenderData->GlyphCell()));
_d2dDeviceContext->SetTransform(D2D1::Matrix3x2F::Scale(_fontRenderData->GlyphCell().to_d2d_size()));
for (const auto& rect : _invalidMap.runs())
{
// Use aliased.
@ -1654,7 +1659,7 @@ try
// the edges are cut nice and sharp (not blended by anti-aliasing).
// For performance reasons, it takes a lot less work to not
// do anti-alias blending.
_d2dDeviceContext->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_ALIASED);
_d2dDeviceContext->PushAxisAlignedClip(rect.to_d2d_rect(), D2D1_ANTIALIAS_MODE_ALIASED);
_d2dDeviceContext->Clear(nothing);
_d2dDeviceContext->PopAxisAlignedClip();
}
@ -1680,7 +1685,7 @@ CATCH_RETURN()
try
{
// Calculate positioning of our origin.
const D2D1_POINT_2F origin = til::point{ coord } * _fontRenderData->GlyphCell();
const D2D1_POINT_2F origin = (til::point{ coord } * _fontRenderData->GlyphCell()).to_d2d_point();
// Create the text layout
RETURN_IF_FAILED(_customLayout->Reset());
@ -1714,7 +1719,7 @@ try
_d2dBrushForeground->SetColor(_ColorFFromColorRef(color));
const D2D1_SIZE_F font = _fontRenderData->GlyphCell();
const D2D1_SIZE_F font = _fontRenderData->GlyphCell().to_d2d_size();
const D2D_POINT_2F target = { coordTarget.X * font.width, coordTarget.Y * font.height };
const auto fullRunWidth = font.width * gsl::narrow_cast<unsigned>(cchLine);
@ -1833,7 +1838,7 @@ try
_d2dBrushForeground->SetColor(_selectionBackground);
const auto resetColorOnExit = wil::scope_exit([&]() noexcept { _d2dBrushForeground->SetColor(existingColor); });
const D2D1_RECT_F draw = til::rectangle{ Viewport::FromExclusive(rect).ToInclusive() }.scale_up(_fontRenderData->GlyphCell());
const D2D1_RECT_F draw = til::rect{ Viewport::FromExclusive(rect).ToInclusive() }.scale_up(_fontRenderData->GlyphCell()).to_d2d_rect();
_d2dDeviceContext->FillRectangle(draw, _d2dBrushForeground.Get());
@ -2018,16 +2023,16 @@ CATCH_RETURN();
[[nodiscard]] Viewport DxEngine::GetViewportInCharacters(const Viewport& viewInPixels) const noexcept
{
const short widthInChars = base::saturated_cast<short>(viewInPixels.Width() / _fontRenderData->GlyphCell().width());
const short heightInChars = base::saturated_cast<short>(viewInPixels.Height() / _fontRenderData->GlyphCell().height());
const short widthInChars = base::saturated_cast<short>(viewInPixels.Width() / _fontRenderData->GlyphCell().width);
const short heightInChars = base::saturated_cast<short>(viewInPixels.Height() / _fontRenderData->GlyphCell().height);
return Viewport::FromDimensions(viewInPixels.Origin(), { widthInChars, heightInChars });
}
[[nodiscard]] Viewport DxEngine::GetViewportInPixels(const Viewport& viewInCharacters) const noexcept
{
const short widthInPixels = base::saturated_cast<short>(viewInCharacters.Width() * _fontRenderData->GlyphCell().width());
const short heightInPixels = base::saturated_cast<short>(viewInCharacters.Height() * _fontRenderData->GlyphCell().height());
const short widthInPixels = base::saturated_cast<short>(viewInCharacters.Width() * _fontRenderData->GlyphCell().width);
const short heightInPixels = base::saturated_cast<short>(viewInCharacters.Height() * _fontRenderData->GlyphCell().height);
return Viewport::FromDimensions(viewInCharacters.Origin(), { widthInPixels, heightInPixels });
}
@ -2099,7 +2104,7 @@ float DxEngine::GetScaling() const noexcept
// - area - Rectangle describing dirty area in characters.
// Return Value:
// - S_OK
[[nodiscard]] HRESULT DxEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
[[nodiscard]] HRESULT DxEngine::GetDirtyArea(gsl::span<const til::rect>& area) noexcept
try
{
area = _invalidMap.runs();
@ -2116,7 +2121,9 @@ CATCH_RETURN();
[[nodiscard]] HRESULT DxEngine::GetFontSize(_Out_ COORD* const pFontSize) noexcept
try
{
*pFontSize = _fontRenderData->GlyphCell();
const auto size = _fontRenderData->GlyphCell();
pFontSize->X = size.narrow_width<short>();
pFontSize->Y = size.narrow_height<short>();
return S_OK;
}
CATCH_RETURN();

View File

@ -116,7 +116,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rect>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
@ -177,7 +177,7 @@ namespace Microsoft::Console::Render
bool _allInvalid;
bool _presentReady;
std::vector<RECT> _presentDirty;
std::vector<til::rect> _presentDirty;
RECT _presentScroll;
POINT _presentOffset;
DXGI_PRESENT_PARAMETERS _presentParams;
@ -298,7 +298,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] til::size _GetClientSize() const;
void _InvalidateRectangle(const til::rectangle& rc);
void _InvalidateRectangle(const til::rect& rc);
bool _IsAllInvalid() const noexcept;
[[nodiscard]] D2D1_COLOR_F _ColorFFromColorRef(const COLORREF color) noexcept;

View File

@ -76,7 +76,7 @@ namespace Microsoft::Console::Render
_Out_ FontInfo& Font,
const int iDpi) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rect>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
@ -92,7 +92,7 @@ namespace Microsoft::Console::Render
bool _fPaintStarted;
til::rectangle _invalidCharacters;
til::rect _invalidCharacters;
PAINTSTRUCT _psInvalidData;
HDC _hdcMemoryContext;
bool _isTrueTypeFont;

View File

@ -16,14 +16,14 @@ using namespace Microsoft::Console::Render;
// This is an Inclusive rect.
// Return Value:
// - S_OK or math failure
[[nodiscard]] HRESULT GdiEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
[[nodiscard]] HRESULT GdiEngine::GetDirtyArea(gsl::span<const til::rect>& area) noexcept
{
RECT rc = _psInvalidData.rcPaint;
SMALL_RECT sr = { 0 };
RETURN_IF_FAILED(_ScaleByFont(&rc, &sr));
_invalidCharacters = sr;
_invalidCharacters = til::rect{ sr };
area = { &_invalidCharacters, 1 };

View File

@ -435,7 +435,7 @@ GdiEngine::~GdiEngine()
_fontCodepage = Font.GetCodePage();
// Inform the soft font of the change in size.
_softFont.SetTargetSize(_GetFontSize());
_softFont.SetTargetSize(til::size{ _GetFontSize() });
LOG_IF_FAILED(InvalidateAll());
@ -462,7 +462,7 @@ GdiEngine::~GdiEngine()
}
// Create a new font resource with the updated pattern, or delete if empty.
_softFont = { bitPattern, cellSize, _GetFontSize(), centeringHint };
_softFont = FontResource{ bitPattern, til::size{ cellSize }, til::size{ _GetFontSize() }, centeringHint };
return S_OK;
}

View File

@ -82,7 +82,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] virtual HRESULT UpdateDpi(int iDpi) noexcept = 0;
[[nodiscard]] virtual HRESULT UpdateViewport(SMALL_RECT srNewViewport) noexcept = 0;
[[nodiscard]] virtual HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, int iDpi) noexcept = 0;
[[nodiscard]] virtual HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept = 0;
[[nodiscard]] virtual HRESULT GetDirtyArea(gsl::span<const til::rect>& area) noexcept = 0;
[[nodiscard]] virtual HRESULT GetFontSize(_Out_ COORD* pFontSize) noexcept = 0;
[[nodiscard]] virtual HRESULT IsGlyphWideByFont(std::wstring_view glyph, _Out_ bool* pResult) noexcept = 0;
[[nodiscard]] virtual HRESULT UpdateTitle(std::wstring_view newTitle) noexcept = 0;

View File

@ -440,11 +440,11 @@ void UiaEngine::WaitUntilCanRender() noexcept
// - area - Rectangle describing dirty area in characters.
// Return Value:
// - S_OK.
[[nodiscard]] HRESULT UiaEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
[[nodiscard]] HRESULT UiaEngine::GetDirtyArea(gsl::span<const til::rect>& area) noexcept
{
// Magic static is only valid because any instance of this object has the same behavior.
// Use member variable instead if this ever changes.
const static til::rectangle empty;
static constexpr til::rect empty;
area = { &empty, 1 };
return S_OK;
}

View File

@ -57,7 +57,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT UpdateDpi(const int iDpi) noexcept override;
[[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override;
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, const int iDpi) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rect>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;

View File

@ -37,7 +37,7 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
{
RETURN_IF_FAILED(VtEngine::StartPaint());
_trace.TraceLastText(_lastText);
_trace.TraceLastText(til::point{ _lastText });
// Prep us to think that the cursor is not visible this frame. If it _is_
// visible, then PaintCursor will be called, and we'll set this to true
@ -57,16 +57,16 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
}
else
{
gsl::span<const til::rectangle> dirty;
gsl::span<const til::rect> dirty;
RETURN_IF_FAILED(GetDirtyArea(dirty));
// If we have 0 or 1 dirty pieces in the area, set as appropriate.
Viewport dirtyView = dirty.empty() ? Viewport::Empty() : Viewport::FromInclusive(til::at(dirty, 0));
Viewport dirtyView = dirty.empty() ? Viewport::Empty() : Viewport::FromInclusive(til::at(dirty, 0).to_small_rect());
// If there's more than 1, union them all up with the 1 we already have.
for (size_t i = 1; i < dirty.size(); ++i)
{
dirtyView = Viewport::Union(dirtyView, Viewport::FromInclusive(til::at(dirty, i)));
dirtyView = Viewport::Union(dirtyView, Viewport::FromInclusive(til::at(dirty, i).to_small_rect()));
}
}
@ -237,7 +237,7 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
{
HRESULT hr = S_OK;
const auto originalPos = _lastText;
_trace.TraceMoveCursor(_lastText, coord);
_trace.TraceMoveCursor(til::point{ _lastText }, til::point{ coord });
bool performedSoftWrap = false;
if (coord.X != _lastText.X || coord.Y != _lastText.Y)
{
@ -348,18 +348,18 @@ try
{
_trace.TraceScrollFrame(_scrollDelta);
if (_scrollDelta.x() != 0)
if (_scrollDelta.x != 0)
{
// No easy way to shift left-right. Everything needs repainting.
return InvalidateAll();
}
if (_scrollDelta.y() == 0)
if (_scrollDelta.y == 0)
{
// There's nothing to do here. Do nothing.
return S_OK;
}
const short dy = _scrollDelta.y<short>();
const short dy = _scrollDelta.narrow_y<short>();
const short absDy = static_cast<short>(abs(dy));
// Save the old wrap state here. We're going to clear it so that
@ -411,7 +411,7 @@ try
// position we think we left the cursor.
//
// See GH#5113
_trace.TraceLastText(_lastText);
_trace.TraceLastText(til::point{ _lastText });
if (_wrappedRow.has_value())
{
_wrappedRow.value() += dy;
@ -430,7 +430,7 @@ try
// one frame, and the second line in another frame that included other
// changes _above_ the wrapped line, that we maintain the wrap state in
// the Terminal.
const til::rectangle lastCellOfWrappedRow{
const til::rect lastCellOfWrappedRow{
til::point{ _lastViewport.RightInclusive(), _wrappedRow.value() },
til::size{ 1, 1 }
};

View File

@ -49,7 +49,7 @@ using namespace Microsoft::Console::Render;
[[nodiscard]] HRESULT VtEngine::Invalidate(const SMALL_RECT* const psrRegion) noexcept
try
{
const til::rectangle rect{ Viewport::FromExclusive(*psrRegion).ToInclusive() };
const til::rect rect{ Viewport::FromExclusive(*psrRegion).ToInclusive() };
_trace.TraceInvalidate(rect);
_invalidMap.set(rect);
return S_OK;
@ -91,7 +91,7 @@ CATCH_RETURN();
[[nodiscard]] HRESULT VtEngine::InvalidateAll() noexcept
try
{
_trace.TraceInvalidateAll(_lastViewport.ToOrigin().ToInclusive());
_trace.TraceInvalidateAll(til::rect{ _lastViewport.ToOrigin().ToInclusive() });
_invalidMap.set_all();
return S_OK;
}

View File

@ -17,7 +17,7 @@ using namespace Microsoft::Console::Types;
// This is an Inclusive rect.
// Return Value:
// - S_OK.
[[nodiscard]] HRESULT VtEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
[[nodiscard]] HRESULT VtEngine::GetDirtyArea(gsl::span<const til::rect>& area) noexcept
{
area = _invalidMap.runs();
return S_OK;

View File

@ -34,7 +34,7 @@ using namespace Microsoft::Console::Types;
_quickReturn = !somethingToDo;
_trace.TraceStartPaint(_quickReturn,
_invalidMap,
_lastViewport.ToInclusive(),
til::rect{ _lastViewport.ToInclusive() },
_scrollDelta,
_cursorMoved,
_wrappedRow);
@ -158,7 +158,7 @@ using namespace Microsoft::Console::Types;
// - S_OK or suitable HRESULT error from writing pipe.
[[nodiscard]] HRESULT VtEngine::PaintCursor(const CursorOptions& options) noexcept
{
_trace.TracePaintCursor(options.coordCursor);
_trace.TracePaintCursor(til::point{ options.coordCursor });
// MSFT:15933349 - Send the terminal the updated cursor information, if it's changed.
LOG_IF_FAILED(_MoveCursor(options.coordCursor));

View File

@ -32,9 +32,9 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
_lastTextAttributes(INVALID_COLOR, INVALID_COLOR),
_lastViewport(initialViewport),
_pool(til::pmr::get_default_resource()),
_invalidMap(initialViewport.Dimensions(), false, &_pool),
_invalidMap(til::size{ initialViewport.Dimensions() }, false, &_pool),
_lastText({ 0 }),
_scrollDelta({ 0, 0 }),
_scrollDelta(0, 0),
_quickReturn(false),
_clearedAllThisFrame(false),
_cursorMoved(false),
@ -245,13 +245,13 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
// buffer will have triggered it's own invalidations for what it knows is
// invalid. Previously, we'd invalidate everything if the width changed,
// because we couldn't be sure if lines were reflowed.
_invalidMap.resize(newView.Dimensions());
_invalidMap.resize(til::size{ newView.Dimensions() });
}
else
{
if (SUCCEEDED(hr))
{
_invalidMap.resize(newView.Dimensions(), true); // resize while filling in new space with repaint requests.
_invalidMap.resize(til::size{ newView.Dimensions() }, true); // resize while filling in new space with repaint requests.
// Viewport is smaller now - just update it all.
if (oldView.Height() > newView.Height() || oldView.Width() > newView.Width())

View File

@ -82,7 +82,7 @@ void RenderTracing::TraceString(const std::string_view& instr) const
#endif UNIT_TESTING
}
void RenderTracing::TraceInvalidate(const til::rectangle invalidRect) const
void RenderTracing::TraceInvalidate(const til::rect& invalidRect) const
{
#ifndef UNIT_TESTING
if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, TIL_KEYWORD_TRACE))
@ -100,7 +100,7 @@ void RenderTracing::TraceInvalidate(const til::rectangle invalidRect) const
#endif UNIT_TESTING
}
void RenderTracing::TraceInvalidateAll(const til::rectangle viewport) const
void RenderTracing::TraceInvalidateAll(const til::rect& viewport) const
{
#ifndef UNIT_TESTING
if (TraceLoggingProviderEnabled(g_hConsoleVtRendererTraceProvider, WINEVENT_LEVEL_VERBOSE, TIL_KEYWORD_TRACE))
@ -151,7 +151,7 @@ void RenderTracing::TraceInvalidateScroll(const til::point scroll) const
void RenderTracing::TraceStartPaint(const bool quickReturn,
const til::pmr::bitmap& invalidMap,
const til::rectangle lastViewport,
const til::rect& lastViewport,
const til::point scrollDelt,
const bool cursorMoved,
const std::optional<short>& wrappedRow) const

View File

@ -27,7 +27,7 @@ namespace Microsoft::Console::VirtualTerminal
RenderTracing();
~RenderTracing();
void TraceString(const std::string_view& str) const;
void TraceInvalidate(const til::rectangle view) const;
void TraceInvalidate(const til::rect& view) const;
void TraceLastText(const til::point lastText) const;
void TraceScrollFrame(const til::point scrollDelta) const;
void TraceMoveCursor(const til::point lastText, const til::point cursor) const;
@ -35,12 +35,12 @@ namespace Microsoft::Console::VirtualTerminal
void TraceClearWrapped() const;
void TraceWrapped() const;
void TracePaintCursor(const til::point coordCursor) const;
void TraceInvalidateAll(const til::rectangle view) const;
void TraceInvalidateAll(const til::rect& view) const;
void TraceTriggerCircling(const bool newFrame) const;
void TraceInvalidateScroll(const til::point scroll) const;
void TraceStartPaint(const bool quickReturn,
const til::pmr::bitmap& invalidMap,
const til::rectangle lastViewport,
const til::rect& lastViewport,
const til::point scrollDelta,
const bool cursorMoved,
const std::optional<short>& wrappedRow) const;

View File

@ -66,7 +66,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT UpdateDpi(int iDpi) noexcept override;
[[nodiscard]] HRESULT UpdateViewport(SMALL_RECT srNewViewport) noexcept override;
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, int iDpi) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rect>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(std::wstring_view glyph, _Out_ bool* pResult) noexcept override;

View File

@ -354,15 +354,10 @@ bool WddmConEngine::IsInitialized()
return S_OK;
}
[[nodiscard]] HRESULT WddmConEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
[[nodiscard]] HRESULT WddmConEngine::GetDirtyArea(gsl::span<const til::rect>& area) noexcept
{
SMALL_RECT r;
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
r.Top = 0;
r.Left = 0;
r.Right = _displayWidth > 0 ? (SHORT)(_displayWidth - 1) : 0;
_dirtyArea = r;
_dirtyArea.bottom = std::max(0, _displayHeight);
_dirtyArea.right = std::max(0, _displayWidth);
area = { &_dirtyArea,
1 };

View File

@ -60,7 +60,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rect>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
@ -76,7 +76,7 @@ namespace Microsoft::Console::Render
// Variables
LONG _displayHeight;
LONG _displayWidth;
til::rectangle _dirtyArea;
til::rect _dirtyArea;
PCD_IO_ROW_INFORMATION* _displayState;

View File

@ -222,9 +222,9 @@ gsl::span<const uint16_t> FontBuffer::GetBitPattern() const noexcept
return { _buffer.data(), MAX_CHARS * _fullHeight };
}
til::size FontBuffer::GetCellSize() const
til::size FontBuffer::GetCellSize() const noexcept
{
return { _fullWidth, _fullHeight };
return { gsl::narrow_cast<til::CoordType>(_fullWidth), gsl::narrow_cast<til::CoordType>(_fullHeight) };
}
size_t FontBuffer::GetTextCenteringHint() const noexcept

View File

@ -31,7 +31,7 @@ namespace Microsoft::Console::VirtualTerminal
bool FinalizeSixelData();
gsl::span<const uint16_t> GetBitPattern() const noexcept;
til::size GetCellSize() const;
til::size GetCellSize() const noexcept;
size_t GetTextCenteringHint() const noexcept;
VTID GetDesignation() const noexcept;

View File

@ -2474,7 +2474,7 @@ ITermDispatch::StringHandler AdaptDispatch::DownloadDRCS(const size_t fontNumber
const auto bitPattern = _fontBuffer->GetBitPattern();
const auto cellSize = _fontBuffer->GetCellSize();
const auto centeringHint = _fontBuffer->GetTextCenteringHint();
_pConApi->PrivateUpdateSoftFont(bitPattern, cellSize, centeringHint);
_pConApi->PrivateUpdateSoftFont(bitPattern, cellSize.to_win32_size(), centeringHint);
}
return true;
};

View File

@ -1794,7 +1794,7 @@ public:
wchar_t pwszBuffer[50];
swprintf_s(pwszBuffer, ARRAYSIZE(pwszBuffer), L"\x1b[%d;%dR\x1b[%d;%dR", coordCursorExpectedFirst.y<int>(), coordCursorExpectedFirst.x<int>(), coordCursorExpectedSecond.y<int>(), coordCursorExpectedSecond.x<int>());
swprintf_s(pwszBuffer, ARRAYSIZE(pwszBuffer), L"\x1b[%d;%dR\x1b[%d;%dR", coordCursorExpectedFirst.y, coordCursorExpectedFirst.x, coordCursorExpectedSecond.y, coordCursorExpectedSecond.x);
_testGetSet->ValidateInputEvent(pwszBuffer);
}
}

View File

@ -387,7 +387,7 @@ bool InputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParameter
DWORD buttonState = 0;
DWORD eventFlags = 0;
const size_t firstParameter = parameters.at(0).value_or(0);
const til::point uiPos{ parameters.at(1) - 1, parameters.at(2) - 1 };
const til::point uiPos{ gsl::narrow_cast<til::CoordType>(parameters.at(1) - 1), gsl::narrow_cast<til::CoordType>(parameters.at(2) - 1) };
modifierState = _GetSGRMouseModifierState(firstParameter);
success = _UpdateSGRMouseButtonState(id, firstParameter, buttonState, eventFlags, uiPos);
@ -730,7 +730,7 @@ bool InputStateMachineEngine::_WriteMouseEvent(const til::point uiPos, const DWO
{
INPUT_RECORD rgInput;
rgInput.EventType = MOUSE_EVENT;
rgInput.Event.MouseEvent.dwMousePosition = uiPos;
rgInput.Event.MouseEvent.dwMousePosition = uiPos.to_win32_coord();
rgInput.Event.MouseEvent.dwButtonState = buttonState;
rgInput.Event.MouseEvent.dwControlKeyState = controlKeyState;
rgInput.Event.MouseEvent.dwEventFlags = eventFlags;

View File

@ -14,14 +14,14 @@ class BitmapTests
TEST_CLASS(BitmapTests);
template<typename T>
void _checkBits(const til::rectangle& bitsOn,
void _checkBits(const til::rect& bitsOn,
const til::details::bitmap<T>& map)
{
_checkBits(std::vector<til::rectangle>{ bitsOn }, map);
_checkBits(std::vector<til::rect>{ bitsOn }, map);
}
template<typename T>
void _checkBits(const std::vector<til::rectangle>& bitsOn,
void _checkBits(const std::vector<til::rect>& bitsOn,
const til::details::bitmap<T>& map)
{
Log::Comment(L"Check all bits in map.");
@ -50,7 +50,7 @@ class BitmapTests
{
const til::bitmap bitmap;
const til::size expectedSize{ 0, 0 };
const til::rectangle expectedRect{ 0, 0, 0, 0 };
const til::rect expectedRect{ 0, 0, 0, 0 };
VERIFY_ARE_EQUAL(expectedSize, bitmap._sz);
VERIFY_ARE_EQUAL(expectedRect, bitmap._rc);
VERIFY_ARE_EQUAL(0u, bitmap._bits.size());
@ -63,7 +63,7 @@ class BitmapTests
TEST_METHOD(SizeConstruct)
{
const til::size expectedSize{ 5, 10 };
const til::rectangle expectedRect{ 0, 0, 5, 10 };
const til::rect expectedRect{ 0, 0, 5, 10 };
const til::bitmap bitmap{ expectedSize };
VERIFY_ARE_EQUAL(expectedSize, bitmap._sz);
VERIFY_ARE_EQUAL(expectedRect, bitmap._rc);
@ -84,7 +84,7 @@ class BitmapTests
VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"fill", fill));
const til::size expectedSize{ 5, 10 };
const til::rectangle expectedRect{ 0, 0, 5, 10 };
const til::rect expectedRect{ 0, 0, 5, 10 };
const til::bitmap bitmap{ expectedSize, fill };
VERIFY_ARE_EQUAL(expectedSize, bitmap._sz);
VERIFY_ARE_EQUAL(expectedRect, bitmap._rc);
@ -184,7 +184,7 @@ class BitmapTests
// 0 1 1 0
// 0 1 1 0
// 0 0 0 0
map.set(til::rectangle{ til::point{ 1, 1 }, til::size{ 2, 2 } });
map.set(til::rect{ til::point{ 1, 1 }, til::size{ 2, 2 } });
Log::Comment(L"1.) Move down and right");
{
@ -226,7 +226,7 @@ class BitmapTests
// 0 1 1 0 0 0 0 0
// 0 1 1 0 v --> 0 0 0 0
// 0 0 0 0 v 0 1 1 0
expected.set(til::rectangle{ til::point{ 1, 3 }, til::size{ 2, 1 } });
expected.set(til::rect{ til::point{ 1, 3 }, til::size{ 2, 1 } });
actual.translate(delta);
@ -271,7 +271,7 @@ class BitmapTests
// 0 1 1 0 --> 1 0 0 0
// 0 0 0 0 0 0 0 0
// <--<--
expected.set(til::rectangle{ til::point{ 0, 1 }, til::size{ 1, 2 } });
expected.set(til::rect{ til::point{ 0, 1 }, til::size{ 1, 2 } });
actual.translate(delta);
@ -318,7 +318,7 @@ class BitmapTests
// 0 1 1 0 ^ 0 0 0 0
// 0 1 1 0 --> 0 0 0 0
// 0 0 0 0 0 0 0 0
expected.set(til::rectangle{ til::point{ 1, 0 }, til::size{ 2, 1 } });
expected.set(til::rect{ til::point{ 1, 0 }, til::size{ 2, 1 } });
actual.translate(delta);
@ -363,7 +363,7 @@ class BitmapTests
// 0 1 1 0 --> 0 0 0 1
// 0 0 0 0 0 0 0 0
// ->->
expected.set(til::rectangle{ til::point{ 3, 1 }, til::size{ 1, 2 } });
expected.set(til::rect{ til::point{ 3, 1 }, til::size{ 1, 2 } });
actual.translate(delta);
@ -381,7 +381,7 @@ class BitmapTests
// 0 1 1 0
// 0 1 1 0
// 0 0 0 0
map.set(til::rectangle{ til::point{ 1, 1 }, til::size{ 2, 2 } });
map.set(til::rect{ til::point{ 1, 1 }, til::size{ 2, 2 } });
Log::Comment(L"1.) Move down and right");
{
@ -400,8 +400,8 @@ class BitmapTests
// 0 1 1 0 v --> 0 0 0 0 --> F F 0 0
// 0 0 0 0 v 0 1 1 0 F F 0 1
// ->->
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 4, 2 } });
expected.set(til::rectangle{ til::point{ 0, 2 }, til::size{ 2, 2 } });
expected.set(til::rect{ til::point{ 0, 0 }, til::size{ 4, 2 } });
expected.set(til::rect{ til::point{ 0, 2 }, til::size{ 2, 2 } });
expected.set(til::point{ 3, 3 });
actual.translate(delta, true);
@ -425,8 +425,8 @@ class BitmapTests
// 0 1 1 0 F F F F
// 0 1 1 0 v --> 0 0 0 0
// 0 0 0 0 v 0 1 1 0
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 4, 2 } });
expected.set(til::rectangle{ til::point{ 1, 3 }, til::size{ 2, 1 } });
expected.set(til::rect{ til::point{ 0, 0 }, til::size{ 4, 2 } });
expected.set(til::rect{ til::point{ 1, 3 }, til::size{ 2, 1 } });
actual.translate(delta, true);
@ -450,8 +450,8 @@ class BitmapTests
// 0 1 1 0 v --> 0 0 0 0 --> 0 0 F F
// 0 0 0 0 v 0 1 1 0 1 0 F F
// <-<-
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 4, 2 } });
expected.set(til::rectangle{ til::point{ 2, 2 }, til::size{ 2, 2 } });
expected.set(til::rect{ til::point{ 0, 0 }, til::size{ 4, 2 } });
expected.set(til::rect{ til::point{ 2, 2 }, til::size{ 2, 2 } });
expected.set(til::point{ 0, 3 });
actual.translate(delta, true);
@ -473,8 +473,8 @@ class BitmapTests
// 0 1 1 0 --> 1 0 F F
// 0 0 0 0 0 0 F F
// <--<--
expected.set(til::rectangle{ til::point{ 2, 0 }, til::size{ 2, 4 } });
expected.set(til::rectangle{ til::point{ 0, 1 }, til::size{ 1, 2 } });
expected.set(til::rect{ til::point{ 2, 0 }, til::size{ 2, 4 } });
expected.set(til::rect{ til::point{ 0, 1 }, til::size{ 1, 2 } });
actual.translate(delta, true);
@ -498,8 +498,8 @@ class BitmapTests
// 0 1 1 0 --> F F F F --> F F F F
// 0 0 0 0 F F F F F F F F
// <-<-
expected.set(til::rectangle{ til::point{ 2, 0 }, til::size{ 2, 2 } });
expected.set(til::rectangle{ til::point{ 0, 2 }, til::size{ 4, 2 } });
expected.set(til::rect{ til::point{ 2, 0 }, til::size{ 2, 2 } });
expected.set(til::rect{ til::point{ 0, 2 }, til::size{ 4, 2 } });
expected.set(til::point{ 0, 0 });
actual.translate(delta, true);
@ -523,8 +523,8 @@ class BitmapTests
// 0 1 1 0 ^ 0 0 0 0
// 0 1 1 0 --> F F F F
// 0 0 0 0 F F F F
expected.set(til::rectangle{ til::point{ 1, 0 }, til::size{ 2, 1 } });
expected.set(til::rectangle{ til::point{ 0, 2 }, til::size{ 4, 2 } });
expected.set(til::rect{ til::point{ 1, 0 }, til::size{ 2, 1 } });
expected.set(til::rect{ til::point{ 0, 2 }, til::size{ 4, 2 } });
actual.translate(delta, true);
@ -549,8 +549,8 @@ class BitmapTests
// 0 0 0 0 F F F F F F F F
// ->->
expected.set(til::point{ 3, 0 });
expected.set(til::rectangle{ til::point{ 0, 2 }, til::size{ 4, 2 } });
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 2, 2 } });
expected.set(til::rect{ til::point{ 0, 2 }, til::size{ 4, 2 } });
expected.set(til::rect{ til::point{ 0, 0 }, til::size{ 2, 2 } });
actual.translate(delta, true);
@ -571,8 +571,8 @@ class BitmapTests
// 0 1 1 0 --> F F 0 1
// 0 0 0 0 F F 0 0
// ->->
expected.set(til::rectangle{ til::point{ 3, 1 }, til::size{ 1, 2 } });
expected.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 2, 4 } });
expected.set(til::rect{ til::point{ 3, 1 }, til::size{ 1, 2 } });
expected.set(til::rect{ til::point{ 0, 0 }, til::size{ 2, 4 } });
actual.translate(delta, true);
@ -592,8 +592,8 @@ class BitmapTests
const til::point point{ 2, 2 };
bitmap.set(point);
std::vector<til::rectangle> expectedSet;
expectedSet.emplace_back(til::rectangle{ point });
std::vector<til::rect> expectedSet;
expectedSet.emplace_back(til::rect{ 2, 2, 3, 3 });
// Run through every bit. Only the one we set should be true.
Log::Comment(L"Only the bit we set should be true.");
@ -603,7 +603,7 @@ class BitmapTests
bitmap.set_all();
expectedSet.clear();
expectedSet.emplace_back(til::rectangle{ bitmap._rc });
expectedSet.emplace_back(til::rect{ bitmap._rc });
_checkBits(expectedSet, bitmap);
Log::Comment(L"Now reset them all.");
@ -612,13 +612,13 @@ class BitmapTests
expectedSet.clear();
_checkBits(expectedSet, bitmap);
til::rectangle totalZone{ sz };
til::rect totalZone{ sz };
Log::Comment(L"Set a rectangle of bits and test they went on.");
// 0 0 0 0 |1 1|0 0
// 0 0 0 0 --\ |1 1|0 0
// 0 0 0 0 --/ |1 1|0 0
// 0 0 0 0 0 0 0 0
til::rectangle setZone{ til::point{ 0, 0 }, til::size{ 2, 3 } };
til::rect setZone{ til::point{ 0, 0 }, til::size{ 2, 3 } };
bitmap.set(setZone);
expectedSet.clear();
@ -647,11 +647,7 @@ class BitmapTests
Log::Comment(L"2.) SetRectangle out of bounds.");
{
auto fn = [&]() {
map.set(til::rectangle{ til::point{
2,
2,
},
til::size{ 10, 10 } });
map.set(til::rect{ til::point{ 2, 2 }, til::size{ 10, 10 } });
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_INVALIDARG; });
@ -664,11 +660,11 @@ class BitmapTests
const til::size originalSize{ 2, 2 };
til::bitmap bitmap{ originalSize, true };
std::vector<til::rectangle> expectedFillRects;
std::vector<til::rect> expectedFillRects;
// 1 1
// 1 1
expectedFillRects.emplace_back(til::rectangle{ originalSize });
expectedFillRects.emplace_back(til::rect{ originalSize });
_checkBits(expectedFillRects, bitmap);
Log::Comment(L"Attempt resize to the same size.");
@ -688,7 +684,7 @@ class BitmapTests
Log::Comment(L"Set a bit out in the new space and check it.");
const til::point spaceBit{ 1, 2 };
expectedFillRects.emplace_back(til::rectangle{ spaceBit });
expectedFillRects.emplace_back(til::rect{ 1, 2, 2, 3 });
bitmap.set(spaceBit);
// 1 1 0
@ -697,7 +693,7 @@ class BitmapTests
_checkBits(expectedFillRects, bitmap);
Log::Comment(L"Grow vertically and shrink horizontally at the same time. Fill any new space.");
expectedFillRects.emplace_back(til::rectangle{ til::point{ 0, 3 }, til::size{ 2, 1 } });
expectedFillRects.emplace_back(til::rect{ til::point{ 0, 3 }, til::size{ 2, 1 } });
bitmap.resize(til::size{ 2, 4 }, true);
// 1 1
@ -846,19 +842,19 @@ class BitmapTests
// 0 0 0 0 0 0 0 0
// 0 0 0 0 --> 0 0 0 0
// 0 0 0 0 0 0 0 0
map.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 2, 1 } });
map.set(til::rect{ til::point{ 0, 0 }, til::size{ 2, 1 } });
// 1 1 0 0 1 1 0 0
// 0 0 0 0 0 0|1|0
// 0 0 0 0 --> 0 0|1|0
// 0 0 0 0 0 0|1|0
map.set(til::rectangle{ til::point{ 2, 1 }, til::size{ 1, 3 } });
map.set(til::rect{ til::point{ 2, 1 }, til::size{ 1, 3 } });
// 1 1 0 0 1 1 0|1|
// 0 0 1 0 0 0 1|1|
// 0 0 1 0 --> 0 0 1 0
// 0 0 1 0 0 0 1 0
map.set(til::rectangle{ til::point{ 3, 0 }, til::size{ 1, 2 } });
map.set(til::rect{ til::point{ 3, 0 }, til::size{ 1, 2 } });
// 1 1 0 1 1 1 0 1
// 0 0 1 1 |1|0 1 1
@ -879,16 +875,16 @@ class BitmapTests
// C _ D D
// _ _ E _
// _ F F _
til::some<til::rectangle, 6> expected;
expected.push_back(til::rectangle{ til::point{ 0, 0 }, til::size{ 2, 1 } });
expected.push_back(til::rectangle{ til::point{ 3, 0 }, til::size{ 1, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 1 }, til::size{ 1, 1 } });
expected.push_back(til::rectangle{ til::point{ 2, 1 }, til::size{ 2, 1 } });
expected.push_back(til::rectangle{ til::point{ 2, 2 }, til::size{ 1, 1 } });
expected.push_back(til::rectangle{ til::point{ 1, 3 }, til::size{ 2, 1 } });
til::some<til::rect, 6> expected;
expected.push_back(til::rect{ til::point{ 0, 0 }, til::size{ 2, 1 } });
expected.push_back(til::rect{ til::point{ 3, 0 }, til::size{ 1, 1 } });
expected.push_back(til::rect{ til::point{ 0, 1 }, til::size{ 1, 1 } });
expected.push_back(til::rect{ til::point{ 2, 1 }, til::size{ 2, 1 } });
expected.push_back(til::rect{ til::point{ 2, 2 }, til::size{ 1, 1 } });
expected.push_back(til::rect{ til::point{ 1, 3 }, til::size{ 2, 1 } });
Log::Comment(L"Run the iterator and collect the runs.");
til::some<til::rectangle, 6> actual;
til::some<til::rect, 6> actual;
for (auto run : map.runs())
{
actual.push_back(run);
@ -912,7 +908,7 @@ class BitmapTests
Log::Comment(L"Set point and validate runs updated.");
const til::point setPoint{ 2, 2 };
expected.push_back(til::rectangle{ setPoint });
expected.push_back(til::rect{ 2, 2, 3, 3 });
map.set(setPoint);
for (auto run : map.runs())
@ -922,10 +918,10 @@ class BitmapTests
VERIFY_ARE_EQUAL(expected, actual);
Log::Comment(L"Set rectangle and validate runs updated.");
const til::rectangle setRect{ setPoint, til::size{ 2, 2 } };
const til::rect setRect{ setPoint, til::size{ 2, 2 } };
expected.clear();
expected.push_back(til::rectangle{ til::point{ 2, 2 }, til::size{ 2, 1 } });
expected.push_back(til::rectangle{ til::point{ 2, 3 }, til::size{ 2, 1 } });
expected.push_back(til::rect{ til::point{ 2, 2 }, til::size{ 2, 1 } });
expected.push_back(til::rect{ til::point{ 2, 3 }, til::size{ 2, 1 } });
map.set(setRect);
actual.clear();
@ -937,10 +933,10 @@ class BitmapTests
Log::Comment(L"Set all and validate runs updated.");
expected.clear();
expected.push_back(til::rectangle{ til::point{ 0, 0 }, til::size{ 4, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 1 }, til::size{ 4, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 2 }, til::size{ 4, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 3 }, til::size{ 4, 1 } });
expected.push_back(til::rect{ til::point{ 0, 0 }, til::size{ 4, 1 } });
expected.push_back(til::rect{ til::point{ 0, 1 }, til::size{ 4, 1 } });
expected.push_back(til::rect{ til::point{ 0, 2 }, til::size{ 4, 1 } });
expected.push_back(til::rect{ til::point{ 0, 3 }, til::size{ 4, 1 } });
map.set_all();
actual.clear();
@ -953,9 +949,9 @@ class BitmapTests
Log::Comment(L"Resize and validate runs updated.");
const til::size newSize{ 3, 3 };
expected.clear();
expected.push_back(til::rectangle{ til::point{ 0, 0 }, til::size{ 3, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 1 }, til::size{ 3, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 2 }, til::size{ 3, 1 } });
expected.push_back(til::rect{ til::point{ 0, 0 }, til::size{ 3, 1 } });
expected.push_back(til::rect{ til::point{ 0, 1 }, til::size{ 3, 1 } });
expected.push_back(til::rect{ til::point{ 0, 2 }, til::size{ 3, 1 } });
map.resize(newSize);
actual.clear();
@ -984,19 +980,19 @@ class BitmapTests
// 0 0 0 0 0 0 0 0
// 0 0 0 0 --> 0 0 0 0
// 0 0 0 0 0 0 0 0
map.set(til::rectangle{ til::point{ 0, 0 }, til::size{ 2, 1 } });
map.set(til::rect{ til::point{ 0, 0 }, til::size{ 2, 1 } });
// 1 1 0 0 1 1 0 0
// 0 0 0 0 0 0|1|0
// 0 0 0 0 --> 0 0|1|0
// 0 0 0 0 0 0|1|0
map.set(til::rectangle{ til::point{ 2, 1 }, til::size{ 1, 3 } });
map.set(til::rect{ til::point{ 2, 1 }, til::size{ 1, 3 } });
// 1 1 0 0 1 1 0|1|
// 0 0 1 0 0 0 1|1|
// 0 0 1 0 --> 0 0 1 0
// 0 0 1 0 0 0 1 0
map.set(til::rectangle{ til::point{ 3, 0 }, til::size{ 1, 2 } });
map.set(til::rect{ til::point{ 3, 0 }, til::size{ 1, 2 } });
// 1 1 0 1 1 1 0 1
// 0 0 1 1 |1|0 1 1
@ -1017,16 +1013,16 @@ class BitmapTests
// C _ D D
// _ _ E _
// _ F F _
til::some<til::rectangle, 6> expected;
expected.push_back(til::rectangle{ til::point{ 0, 0 }, til::size{ 2, 1 } });
expected.push_back(til::rectangle{ til::point{ 3, 0 }, til::size{ 1, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 1 }, til::size{ 1, 1 } });
expected.push_back(til::rectangle{ til::point{ 2, 1 }, til::size{ 2, 1 } });
expected.push_back(til::rectangle{ til::point{ 2, 2 }, til::size{ 1, 1 } });
expected.push_back(til::rectangle{ til::point{ 1, 3 }, til::size{ 2, 1 } });
til::some<til::rect, 6> expected;
expected.push_back(til::rect{ til::point{ 0, 0 }, til::size{ 2, 1 } });
expected.push_back(til::rect{ til::point{ 3, 0 }, til::size{ 1, 1 } });
expected.push_back(til::rect{ til::point{ 0, 1 }, til::size{ 1, 1 } });
expected.push_back(til::rect{ til::point{ 2, 1 }, til::size{ 2, 1 } });
expected.push_back(til::rect{ til::point{ 2, 2 }, til::size{ 1, 1 } });
expected.push_back(til::rect{ til::point{ 1, 3 }, til::size{ 2, 1 } });
Log::Comment(L"Run the iterator and collect the runs.");
til::some<til::rectangle, 6> actual;
til::some<til::rect, 6> actual;
for (auto run : map.runs())
{
actual.push_back(run);
@ -1050,7 +1046,7 @@ class BitmapTests
Log::Comment(L"Set point and validate runs updated.");
const til::point setPoint{ 2, 2 };
expected.push_back(til::rectangle{ setPoint });
expected.push_back(til::rect{ 2, 2, 3, 3 });
map.set(setPoint);
for (auto run : map.runs())
@ -1060,10 +1056,10 @@ class BitmapTests
VERIFY_ARE_EQUAL(expected, actual);
Log::Comment(L"Set rectangle and validate runs updated.");
const til::rectangle setRect{ setPoint, til::size{ 2, 2 } };
const til::rect setRect{ setPoint, til::size{ 2, 2 } };
expected.clear();
expected.push_back(til::rectangle{ til::point{ 2, 2 }, til::size{ 2, 1 } });
expected.push_back(til::rectangle{ til::point{ 2, 3 }, til::size{ 2, 1 } });
expected.push_back(til::rect{ til::point{ 2, 2 }, til::size{ 2, 1 } });
expected.push_back(til::rect{ til::point{ 2, 3 }, til::size{ 2, 1 } });
map.set(setRect);
actual.clear();
@ -1075,10 +1071,10 @@ class BitmapTests
Log::Comment(L"Set all and validate runs updated.");
expected.clear();
expected.push_back(til::rectangle{ til::point{ 0, 0 }, til::size{ 4, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 1 }, til::size{ 4, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 2 }, til::size{ 4, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 3 }, til::size{ 4, 1 } });
expected.push_back(til::rect{ til::point{ 0, 0 }, til::size{ 4, 1 } });
expected.push_back(til::rect{ til::point{ 0, 1 }, til::size{ 4, 1 } });
expected.push_back(til::rect{ til::point{ 0, 2 }, til::size{ 4, 1 } });
expected.push_back(til::rect{ til::point{ 0, 3 }, til::size{ 4, 1 } });
map.set_all();
actual.clear();
@ -1091,9 +1087,9 @@ class BitmapTests
Log::Comment(L"Resize and validate runs updated.");
const til::size newSize{ 3, 3 };
expected.clear();
expected.push_back(til::rectangle{ til::point{ 0, 0 }, til::size{ 3, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 1 }, til::size{ 3, 1 } });
expected.push_back(til::rectangle{ til::point{ 0, 2 }, til::size{ 3, 1 } });
expected.push_back(til::rect{ til::point{ 0, 0 }, til::size{ 3, 1 } });
expected.push_back(til::rect{ til::point{ 0, 1 }, til::size{ 3, 1 } });
expected.push_back(til::rect{ til::point{ 0, 2 }, til::size{ 3, 1 } });
map.resize(newSize);
actual.clear();

View File

@ -13,103 +13,103 @@ class MathTests
{
TEST_CLASS(MathTests);
template<typename TG, typename TX = ptrdiff_t>
using FloatType = double;
using IntegralType = int;
using TargetType = int;
static constexpr auto nan = std::numeric_limits<FloatType>::quiet_NaN();
static constexpr auto infinity = std::numeric_limits<FloatType>::infinity();
template<typename TG>
struct TestCase
{
TG given;
TX expected;
TargetType expected;
bool throws = false;
};
template<class TilMath, typename TG, typename TX, int N>
static void _RunCases(TilMath, const std::array<TestCase<TG, TX>, N>& cases)
template<typename TG, typename TilMath>
static void _RunCases(TilMath, const std::initializer_list<TestCase<TG>>& cases)
{
for (const auto& tc : cases)
{
VERIFY_ARE_EQUAL(tc.expected, TilMath::template cast<decltype(tc.expected)>(tc.given));
if (tc.throws)
{
VERIFY_THROWS(TilMath::template cast<decltype(tc.expected)>(tc.given), gsl::narrowing_error);
}
else
{
VERIFY_ARE_EQUAL(tc.expected, TilMath::template cast<decltype(tc.expected)>(tc.given));
}
}
}
TEST_METHOD(Truncating)
{
std::array<TestCase<long double, ptrdiff_t>, 8> cases{
TestCase<long double, ptrdiff_t>{ 1., 1 },
{ 1.9, 1 },
{ -7.1, -7 },
{ -8.5, -8 },
{ PTRDIFF_MAX + 0.5, PTRDIFF_MAX },
{ PTRDIFF_MIN - 0.5, PTRDIFF_MIN },
{ INFINITY, PTRDIFF_MAX },
{ -INFINITY, PTRDIFF_MIN },
};
_RunCases(til::math::truncating, cases);
VERIFY_THROWS_SPECIFIC(til::math::details::truncating_t::cast<ptrdiff_t>(NAN), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
TEST_METHOD(Ceiling)
{
std::array<TestCase<long double, ptrdiff_t>, 8> cases{
TestCase<long double, ptrdiff_t>{ 1., 1 },
{ 1.9, 2 },
{ -7.1, -7 },
{ -8.5, -8 },
{ PTRDIFF_MAX + 0.5, PTRDIFF_MAX },
{ PTRDIFF_MIN - 0.5, PTRDIFF_MIN },
{ INFINITY, PTRDIFF_MAX },
{ -INFINITY, PTRDIFF_MIN },
};
_RunCases(til::math::ceiling, cases);
VERIFY_THROWS_SPECIFIC(til::math::details::ceiling_t::cast<ptrdiff_t>(NAN), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
_RunCases<FloatType>(
til::math::ceiling,
{
{ 1., 1 },
{ 1.9, 2 },
{ -7.1, -7 },
{ -8.5, -8 },
{ INT_MAX - 0.1, INT_MAX },
{ INT_MIN - 0.1, INT_MIN },
{ INT_MAX + 1.1, 0, true },
{ INT_MIN - 1.1, 0, true },
{ infinity, 0, true },
{ -infinity, 0, true },
{ nan, 0, true },
});
}
TEST_METHOD(Flooring)
{
std::array<TestCase<long double, ptrdiff_t>, 8> cases{
TestCase<long double, ptrdiff_t>{ 1., 1 },
{ 1.9, 1 },
{ -7.1, -8 },
{ -8.5, -9 },
{ PTRDIFF_MAX + 0.5, PTRDIFF_MAX },
{ PTRDIFF_MIN - 0.5, PTRDIFF_MIN },
{ INFINITY, PTRDIFF_MAX },
{ -INFINITY, PTRDIFF_MIN },
};
_RunCases(til::math::flooring, cases);
VERIFY_THROWS_SPECIFIC(til::math::details::flooring_t::cast<ptrdiff_t>(NAN), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
_RunCases<FloatType>(
til::math::flooring,
{
{ 1., 1 },
{ 1.9, 1 },
{ -7.1, -8 },
{ -8.5, -9 },
{ INT_MAX + 0.1, INT_MAX },
{ INT_MIN + 0.1, INT_MIN },
{ INT_MAX + 1.1, 0, true },
{ INT_MIN - 1.1, 0, true },
{ infinity, 0, true },
{ -infinity, 0, true },
{ nan, 0, true },
});
}
TEST_METHOD(Rounding)
{
std::array<TestCase<long double, ptrdiff_t>, 8> cases{
TestCase<long double, ptrdiff_t>{ 1., 1 },
{ 1.9, 2 },
{ -7.1, -7 },
{ -8.5, -9 },
{ PTRDIFF_MAX + 0.5, PTRDIFF_MAX },
{ PTRDIFF_MIN - 0.5, PTRDIFF_MIN },
{ INFINITY, PTRDIFF_MAX },
{ -INFINITY, PTRDIFF_MIN },
};
_RunCases(til::math::rounding, cases);
VERIFY_THROWS_SPECIFIC(til::math::details::rounding_t::cast<ptrdiff_t>(NAN), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
_RunCases<FloatType>(
til::math::rounding,
{
{ 1., 1 },
{ 1.9, 2 },
{ -7.1, -7 },
{ -8.5, -9 },
{ INT_MAX + 0.1, INT_MAX },
{ INT_MIN - 0.1, INT_MIN },
{ INT_MAX + 1.1, 0, true },
{ INT_MIN - 1.1, 0, true },
{ infinity, 0, true },
{ -infinity, 0, true },
{ nan, 0, true },
});
}
TEST_METHOD(NormalIntegers)
{
std::array<TestCase<ptrdiff_t, int>, 4> cases{
TestCase<ptrdiff_t, int>{ 1, 1 },
{ -1, -1 },
{ PTRDIFF_MAX, INT_MAX },
{ PTRDIFF_MIN, INT_MIN },
};
_RunCases(til::math::rounding, cases);
_RunCases<IntegralType>(
til::math::rounding,
{
{ 1, 1 },
{ -1, -1 },
{ INT_MAX, INT_MAX },
{ INT_MIN, INT_MIN },
});
}
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,8 @@
#include "precomp.h"
#include "WexTestClass.h"
#include <til/spsc.h>
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;

View File

@ -16,88 +16,51 @@ class SizeTests
TEST_METHOD(DefaultConstruct)
{
const til::size sz;
VERIFY_ARE_EQUAL(0, sz._width);
VERIFY_ARE_EQUAL(0, sz._height);
VERIFY_ARE_EQUAL(0, sz.width);
VERIFY_ARE_EQUAL(0, sz.height);
}
TEST_METHOD(RawConstruct)
{
const til::size sz{ 5, 10 };
VERIFY_ARE_EQUAL(5, sz._width);
VERIFY_ARE_EQUAL(10, sz._height);
VERIFY_ARE_EQUAL(5, sz.width);
VERIFY_ARE_EQUAL(10, sz.height);
}
TEST_METHOD(RawFloatingConstruct)
{
const til::size sz{ til::math::rounding, 3.2f, 7.8f };
VERIFY_ARE_EQUAL(3, sz._width);
VERIFY_ARE_EQUAL(8, sz._height);
}
TEST_METHOD(UnsignedConstruct)
{
Log::Comment(L"0.) Normal unsigned construct.");
{
const size_t width = 5;
const size_t height = 10;
const til::size sz{ width, height };
VERIFY_ARE_EQUAL(5, sz._width);
VERIFY_ARE_EQUAL(10, sz._height);
}
Log::Comment(L"1.) Unsigned construct overflow on width.");
{
constexpr size_t width = std::numeric_limits<size_t>().max();
const size_t height = 10;
auto fn = [&]() {
til::size sz{ width, height };
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
Log::Comment(L"2.) Unsigned construct overflow on height.");
{
constexpr size_t height = std::numeric_limits<size_t>().max();
const size_t width = 10;
auto fn = [&]() {
til::size sz{ width, height };
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
VERIFY_ARE_EQUAL(3, sz.width);
VERIFY_ARE_EQUAL(8, sz.height);
}
TEST_METHOD(SignedConstruct)
{
const ptrdiff_t width = -5;
const ptrdiff_t height = -10;
const auto width = -5;
const auto height = -10;
const til::size sz{ width, height };
VERIFY_ARE_EQUAL(width, sz._width);
VERIFY_ARE_EQUAL(height, sz._height);
VERIFY_ARE_EQUAL(width, sz.width);
VERIFY_ARE_EQUAL(height, sz.height);
}
TEST_METHOD(MixedRawTypeConstruct)
{
const ptrdiff_t a = -5;
const auto a = -5;
const int b = -10;
Log::Comment(L"Case 1: ptrdiff_t/int");
Log::Comment(L"Case 1: til::CoordType/int");
{
const til::size sz{ a, b };
VERIFY_ARE_EQUAL(a, sz._width);
VERIFY_ARE_EQUAL(b, sz._height);
VERIFY_ARE_EQUAL(a, sz.width);
VERIFY_ARE_EQUAL(b, sz.height);
}
Log::Comment(L"Case 2: int/ptrdiff_t");
Log::Comment(L"Case 2: int/til::CoordType");
{
const til::size sz{ b, a };
VERIFY_ARE_EQUAL(b, sz._width);
VERIFY_ARE_EQUAL(a, sz._height);
VERIFY_ARE_EQUAL(b, sz.width);
VERIFY_ARE_EQUAL(a, sz.height);
}
}
@ -106,8 +69,8 @@ class SizeTests
COORD coord{ -5, 10 };
const til::size sz{ coord };
VERIFY_ARE_EQUAL(coord.X, sz._width);
VERIFY_ARE_EQUAL(coord.Y, sz._height);
VERIFY_ARE_EQUAL(coord.X, sz.width);
VERIFY_ARE_EQUAL(coord.Y, sz.height);
}
TEST_METHOD(SizeConstruct)
@ -115,8 +78,8 @@ class SizeTests
SIZE size{ 5, -10 };
const til::size sz{ size };
VERIFY_ARE_EQUAL(size.cx, sz._width);
VERIFY_ARE_EQUAL(size.cy, sz._height);
VERIFY_ARE_EQUAL(size.cx, sz.width);
VERIFY_ARE_EQUAL(size.cy, sz.height);
}
TEST_METHOD(Equality)
@ -226,35 +189,35 @@ class SizeTests
const til::size sz{ 5, 10 };
const til::size sz2{ 23, 47 };
const til::size expected{ sz.width() + sz2.width(), sz.height() + sz2.height() };
const til::size expected{ sz.width + sz2.width, sz.height + sz2.height };
VERIFY_ARE_EQUAL(expected, sz + sz2);
}
Log::Comment(L"1.) Addition results in value that is too large (width).");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
const til::size sz{ bigSize, static_cast<ptrdiff_t>(0) };
constexpr til::CoordType bigSize = std::numeric_limits<til::CoordType>().max();
const til::size sz{ bigSize, static_cast<til::CoordType>(0) };
const til::size sz2{ 1, 1 };
auto fn = [&]() {
sz + sz2;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
Log::Comment(L"2.) Addition results in value that is too large (height).");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
const til::size sz{ static_cast<ptrdiff_t>(0), bigSize };
constexpr til::CoordType bigSize = std::numeric_limits<til::CoordType>().max();
const til::size sz{ static_cast<til::CoordType>(0), bigSize };
const til::size sz2{ 1, 1 };
auto fn = [&]() {
sz + sz2;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
}
@ -265,35 +228,35 @@ class SizeTests
const til::size sz{ 5, 10 };
const til::size sz2{ 23, 47 };
const til::size expected{ sz.width() - sz2.width(), sz.height() - sz2.height() };
const til::size expected{ sz.width - sz2.width, sz.height - sz2.height };
VERIFY_ARE_EQUAL(expected, sz - sz2);
}
Log::Comment(L"1.) Subtraction results in value that is too small (width).");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
const til::size sz{ bigSize, static_cast<ptrdiff_t>(0) };
constexpr til::CoordType bigSize = std::numeric_limits<til::CoordType>().max();
const til::size sz{ bigSize, static_cast<til::CoordType>(0) };
const til::size sz2{ -2, -2 };
auto fn = [&]() {
sz2 - sz;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
Log::Comment(L"2.) Subtraction results in value that is too small (height).");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
const til::size sz{ static_cast<ptrdiff_t>(0), bigSize };
constexpr til::CoordType bigSize = std::numeric_limits<til::CoordType>().max();
const til::size sz{ static_cast<til::CoordType>(0), bigSize };
const til::size sz2{ -2, -2 };
auto fn = [&]() {
sz2 - sz;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
}
@ -304,35 +267,35 @@ class SizeTests
const til::size sz{ 5, 10 };
const til::size sz2{ 23, 47 };
const til::size expected{ sz.width() * sz2.width(), sz.height() * sz2.height() };
const til::size expected{ sz.width * sz2.width, sz.height * sz2.height };
VERIFY_ARE_EQUAL(expected, sz * sz2);
}
Log::Comment(L"1.) Multiplication results in value that is too large (width).");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
const til::size sz{ bigSize, static_cast<ptrdiff_t>(0) };
constexpr til::CoordType bigSize = std::numeric_limits<til::CoordType>().max();
const til::size sz{ bigSize, static_cast<til::CoordType>(0) };
const til::size sz2{ 10, 10 };
auto fn = [&]() {
sz* sz2;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
Log::Comment(L"2.) Multiplication results in value that is too large (height).");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
const til::size sz{ static_cast<ptrdiff_t>(0), bigSize };
constexpr til::CoordType bigSize = std::numeric_limits<til::CoordType>().max();
const til::size sz{ static_cast<til::CoordType>(0), bigSize };
const til::size sz2{ 10, 10 };
auto fn = [&]() {
sz* sz2;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
}
@ -343,7 +306,7 @@ class SizeTests
const til::size sz{ 5, 10 };
const float scale = 1.783f;
const til::size expected{ static_cast<ptrdiff_t>(ceil(5 * scale)), static_cast<ptrdiff_t>(ceil(10 * scale)) };
const til::size expected{ static_cast<til::CoordType>(ceil(5 * scale)), static_cast<til::CoordType>(ceil(10 * scale)) };
const auto actual = sz.scale(til::math::ceiling, scale);
@ -359,7 +322,7 @@ class SizeTests
sz.scale(til::math::ceiling, scale);
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
}
@ -370,22 +333,22 @@ class SizeTests
const til::size sz{ 555, 510 };
const til::size sz2{ 23, 47 };
const til::size expected{ sz.width() / sz2.width(), sz.height() / sz2.height() };
const til::size expected{ sz.width / sz2.width, sz.height / sz2.height };
VERIFY_ARE_EQUAL(expected, sz / sz2);
}
Log::Comment(L"1.) Division by zero");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
const til::size sz{ bigSize, static_cast<ptrdiff_t>(0) };
constexpr til::CoordType bigSize = std::numeric_limits<til::CoordType>().max();
const til::size sz{ bigSize, static_cast<til::CoordType>(0) };
const til::size sz2{ 1, 1 };
auto fn = [&]() {
sz2 / sz;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
}
@ -416,28 +379,16 @@ class SizeTests
}
}
TEST_METHOD(Width)
{
const til::size sz{ 5, 10 };
VERIFY_ARE_EQUAL(sz._width, sz.width());
}
TEST_METHOD(WidthCast)
{
const til::size sz{ 5, 10 };
VERIFY_ARE_EQUAL(static_cast<SHORT>(sz._width), sz.width<SHORT>());
}
TEST_METHOD(Height)
{
const til::size sz{ 5, 10 };
VERIFY_ARE_EQUAL(sz._height, sz.height());
VERIFY_ARE_EQUAL(static_cast<SHORT>(sz.width), sz.narrow_width<SHORT>());
}
TEST_METHOD(HeightCast)
{
const til::size sz{ 5, 10 };
VERIFY_ARE_EQUAL(static_cast<SHORT>(sz._height), sz.height<SHORT>());
VERIFY_ARE_EQUAL(static_cast<SHORT>(sz.height), sz.narrow_height<SHORT>());
}
TEST_METHOD(Area)
@ -445,19 +396,19 @@ class SizeTests
Log::Comment(L"0.) Area of two things that should be in bounds.");
{
const til::size sz{ 5, 10 };
VERIFY_ARE_EQUAL(sz._width * sz._height, sz.area());
VERIFY_ARE_EQUAL(sz.width * sz.height, sz.area());
}
Log::Comment(L"1.) Area is out of bounds on multiplication.");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<ptrdiff_t>().max();
constexpr til::CoordType bigSize = std::numeric_limits<til::CoordType>().max();
const til::size sz{ bigSize, bigSize };
auto fn = [&]() {
sz.area();
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
}
TEST_METHOD(AreaCast)
@ -470,51 +421,14 @@ class SizeTests
Log::Comment(L"1.) Area is out of bounds on multiplication.");
{
constexpr ptrdiff_t bigSize = std::numeric_limits<SHORT>().max();
constexpr til::CoordType bigSize = std::numeric_limits<SHORT>().max();
const til::size sz{ bigSize, bigSize };
auto fn = [&]() {
sz.area<SHORT>();
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
}
TEST_METHOD(CastToCoord)
{
Log::Comment(L"0.) Typical situation.");
{
const til::size sz{ 5, 10 };
COORD val = sz;
VERIFY_ARE_EQUAL(5, val.X);
VERIFY_ARE_EQUAL(10, val.Y);
}
Log::Comment(L"1.) Overflow on width.");
{
constexpr ptrdiff_t width = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t height = 10;
const til::size sz{ width, height };
auto fn = [&]() {
COORD val = sz;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}
Log::Comment(L"2.) Overflow on height.");
{
constexpr ptrdiff_t height = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t width = 10;
const til::size sz{ width, height };
auto fn = [&]() {
COORD val = sz;
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
}
@ -523,55 +437,55 @@ class SizeTests
Log::Comment(L"0.) Typical situation.");
{
const til::size sz{ 5, 10 };
SIZE val = sz;
SIZE val = sz.to_win32_size();
VERIFY_ARE_EQUAL(5, val.cx);
VERIFY_ARE_EQUAL(10, val.cy);
}
Log::Comment(L"1.) Fit max width into SIZE (may overflow).");
{
constexpr ptrdiff_t width = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t height = 10;
constexpr til::CoordType width = std::numeric_limits<til::CoordType>().max();
const auto height = 10;
const til::size sz{ width, height };
// On some platforms, ptrdiff_t will fit inside cx/cy
// On some platforms, til::CoordType will fit inside cx/cy
const bool overflowExpected = width > std::numeric_limits<decltype(SIZE::cx)>().max();
if (overflowExpected)
{
auto fn = [&]() {
SIZE val = sz;
SIZE val = sz.to_win32_size();
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
else
{
SIZE val = sz;
SIZE val = sz.to_win32_size();
VERIFY_ARE_EQUAL(width, val.cx);
}
}
Log::Comment(L"2.) Fit max height into SIZE (may overflow).");
{
constexpr ptrdiff_t height = std::numeric_limits<ptrdiff_t>().max();
const ptrdiff_t width = 10;
constexpr til::CoordType height = std::numeric_limits<til::CoordType>().max();
const auto width = 10;
const til::size sz{ width, height };
// On some platforms, ptrdiff_t will fit inside cx/cy
// On some platforms, til::CoordType will fit inside cx/cy
const bool overflowExpected = height > std::numeric_limits<decltype(SIZE::cy)>().max();
if (overflowExpected)
{
auto fn = [&]() {
SIZE val = sz;
SIZE val = sz.to_win32_size();
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
VERIFY_THROWS(fn(), gsl::narrowing_error);
}
else
{
SIZE val = sz;
SIZE val = sz.to_win32_size();
VERIFY_ARE_EQUAL(height, val.cy);
}
}
@ -582,61 +496,40 @@ class SizeTests
Log::Comment(L"0.) Typical situation.");
{
const til::size sz{ 5, 10 };
D2D1_SIZE_F val = sz;
D2D1_SIZE_F val = sz.to_d2d_size();
VERIFY_ARE_EQUAL(5, val.width);
VERIFY_ARE_EQUAL(10, val.height);
}
// All ptrdiff_ts fit into a float, so there's no exception tests.
// All til::CoordTypes fit into a float, so there's no exception tests.
}
template<typename T>
struct SizeTypeWith_XY
{
T X, Y;
};
template<typename T>
struct SizeTypeWith_cxcy
{
T cx, cy;
};
template<typename T>
struct SizeTypeWith_WidthHeight
{
T Width, Height;
};
TEST_METHOD(CastFromFloatWithMathTypes)
{
SizeTypeWith_XY<float> XYFloatIntegral{ 1.f, 2.f };
SizeTypeWith_XY<float> XYFloat{ 1.6f, 2.4f };
SizeTypeWith_cxcy<double> cxcyDoubleIntegral{ 3., 4. };
SizeTypeWith_cxcy<double> cxcyDouble{ 3.6, 4.4 };
SizeTypeWith_WidthHeight<double> WHDoubleIntegral{ 5., 6. };
SizeTypeWith_WidthHeight<double> WHDouble{ 5.6, 6.4 };
Log::Comment(L"0.) Ceiling");
{
{
til::size converted{ til::math::ceiling, XYFloatIntegral };
til::size converted{ til::math::ceiling, 1.f, 2.f };
VERIFY_ARE_EQUAL((til::size{ 1, 2 }), converted);
}
{
til::size converted{ til::math::ceiling, XYFloat };
til::size converted{ til::math::ceiling, 1.6f, 2.4f };
VERIFY_ARE_EQUAL((til::size{ 2, 3 }), converted);
}
{
til::size converted{ til::math::ceiling, cxcyDoubleIntegral };
til::size converted{ til::math::ceiling, 3., 4. };
VERIFY_ARE_EQUAL((til::size{ 3, 4 }), converted);
}
{
til::size converted{ til::math::ceiling, cxcyDouble };
til::size converted{ til::math::ceiling, 3.6, 4.4 };
VERIFY_ARE_EQUAL((til::size{ 4, 5 }), converted);
}
{
til::size converted{ til::math::ceiling, WHDoubleIntegral };
til::size converted{ til::math::ceiling, 5., 6. };
VERIFY_ARE_EQUAL((til::size{ 5, 6 }), converted);
}
{
til::size converted{ til::math::ceiling, WHDouble };
til::size converted{ til::math::ceiling, 5.6, 6.4 };
VERIFY_ARE_EQUAL((til::size{ 6, 7 }), converted);
}
}
@ -644,27 +537,27 @@ class SizeTests
Log::Comment(L"1.) Flooring");
{
{
til::size converted{ til::math::flooring, XYFloatIntegral };
til::size converted{ til::math::flooring, 1.f, 2.f };
VERIFY_ARE_EQUAL((til::size{ 1, 2 }), converted);
}
{
til::size converted{ til::math::flooring, XYFloat };
til::size converted{ til::math::flooring, 1.6f, 2.4f };
VERIFY_ARE_EQUAL((til::size{ 1, 2 }), converted);
}
{
til::size converted{ til::math::flooring, cxcyDoubleIntegral };
til::size converted{ til::math::flooring, 3., 4. };
VERIFY_ARE_EQUAL((til::size{ 3, 4 }), converted);
}
{
til::size converted{ til::math::flooring, cxcyDouble };
til::size converted{ til::math::flooring, 3.6, 4.4 };
VERIFY_ARE_EQUAL((til::size{ 3, 4 }), converted);
}
{
til::size converted{ til::math::flooring, WHDoubleIntegral };
til::size converted{ til::math::flooring, 5., 6. };
VERIFY_ARE_EQUAL((til::size{ 5, 6 }), converted);
}
{
til::size converted{ til::math::flooring, WHDouble };
til::size converted{ til::math::flooring, 5.6, 6.4 };
VERIFY_ARE_EQUAL((til::size{ 5, 6 }), converted);
}
}
@ -672,57 +565,29 @@ class SizeTests
Log::Comment(L"2.) Rounding");
{
{
til::size converted{ til::math::rounding, XYFloatIntegral };
til::size converted{ til::math::rounding, 1.f, 2.f };
VERIFY_ARE_EQUAL((til::size{ 1, 2 }), converted);
}
{
til::size converted{ til::math::rounding, XYFloat };
til::size converted{ til::math::rounding, 1.6f, 2.4f };
VERIFY_ARE_EQUAL((til::size{ 2, 2 }), converted);
}
{
til::size converted{ til::math::rounding, cxcyDoubleIntegral };
til::size converted{ til::math::rounding, 3., 4. };
VERIFY_ARE_EQUAL((til::size{ 3, 4 }), converted);
}
{
til::size converted{ til::math::rounding, cxcyDouble };
til::size converted{ til::math::rounding, 3.6, 4.4 };
VERIFY_ARE_EQUAL((til::size{ 4, 4 }), converted);
}
{
til::size converted{ til::math::rounding, WHDoubleIntegral };
til::size converted{ til::math::rounding, 5., 6. };
VERIFY_ARE_EQUAL((til::size{ 5, 6 }), converted);
}
{
til::size converted{ til::math::rounding, WHDouble };
til::size converted{ til::math::rounding, 5.6, 6.4 };
VERIFY_ARE_EQUAL((til::size{ 6, 6 }), converted);
}
}
Log::Comment(L"3.) Truncating");
{
{
til::size converted{ til::math::truncating, XYFloatIntegral };
VERIFY_ARE_EQUAL((til::size{ 1, 2 }), converted);
}
{
til::size converted{ til::math::truncating, XYFloat };
VERIFY_ARE_EQUAL((til::size{ 1, 2 }), converted);
}
{
til::size converted{ til::math::truncating, cxcyDoubleIntegral };
VERIFY_ARE_EQUAL((til::size{ 3, 4 }), converted);
}
{
til::size converted{ til::math::truncating, cxcyDouble };
VERIFY_ARE_EQUAL((til::size{ 3, 4 }), converted);
}
{
til::size converted{ til::math::truncating, WHDoubleIntegral };
VERIFY_ARE_EQUAL((til::size{ 5, 6 }), converted);
}
{
til::size converted{ til::math::truncating, WHDouble };
VERIFY_ARE_EQUAL((til::size{ 5, 6 }), converted);
}
}
}
};

View File

@ -287,16 +287,16 @@ void UiaTextRangeBase::_expandToEnclosingUnit(TextUnit unit)
// If we're past document end,
// set us to ONE BEFORE the document end.
// This allows us to expand properly.
if (bufferSize.CompareInBounds(_start, documentEnd, true) >= 0)
if (bufferSize.CompareInBounds(_start, documentEnd.to_win32_coord(), true) >= 0)
{
_start = documentEnd;
_start = documentEnd.to_win32_coord();
bufferSize.DecrementInBounds(_start, true);
}
if (unit == TextUnit_Character)
{
_start = buffer.GetGlyphStart(_start, documentEnd);
_end = buffer.GetGlyphEnd(_start, true, documentEnd);
_start = buffer.GetGlyphStart(til::point{ _start }, documentEnd).to_win32_coord();
_end = buffer.GetGlyphEnd(til::point{ _start }, true, documentEnd).to_win32_coord();
}
else if (unit <= TextUnit_Word)
{
@ -308,10 +308,10 @@ void UiaTextRangeBase::_expandToEnclosingUnit(TextUnit unit)
{
// expand to line
_start.X = 0;
if (_start.Y == documentEnd.y())
if (_start.Y == documentEnd.y)
{
// we're on the last line
_end = documentEnd;
_end = documentEnd.to_win32_coord();
bufferSize.IncrementInBounds(_end, true);
}
else
@ -324,7 +324,7 @@ void UiaTextRangeBase::_expandToEnclosingUnit(TextUnit unit)
{
// expand to document
_start = bufferSize.Origin();
_end = documentEnd;
_end = documentEnd.to_win32_coord();
}
}
@ -859,7 +859,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_
// these viewport vars are converted to the buffer coordinate space
const auto viewport = bufferSize.ConvertToOrigin(_pData->GetViewport());
const til::point viewportOrigin = viewport.Origin();
const auto viewportOrigin = viewport.Origin();
const auto viewportEnd = viewport.EndExclusive();
// startAnchor: the earliest COORD we will get a bounding rect for
@ -900,8 +900,8 @@ IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_
// Convert the buffer coordinates to an equivalent range of
// screen cells, taking line rendition into account.
const auto lineRendition = buffer.GetLineRendition(rect.Top);
til::rectangle r{ BufferToScreenLine(rect, lineRendition) };
r -= viewportOrigin;
til::rect r{ BufferToScreenLine(rect, lineRendition) };
r -= til::point{ viewportOrigin };
_getBoundingRect(r, coords);
}
}
@ -1038,7 +1038,7 @@ try
// If so, clamp each endpoint to the end of the document.
constexpr auto endpoint = TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start;
const auto bufferSize{ _pData->GetTextBuffer().GetSize() };
const COORD documentEnd = _getDocumentEnd();
const COORD documentEnd = _getDocumentEnd().to_win32_coord();
if (bufferSize.CompareInBounds(_start, documentEnd, true) > 0)
{
_start = documentEnd;
@ -1109,7 +1109,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin
auto documentEnd = bufferSize.EndExclusive();
try
{
documentEnd = _getDocumentEnd();
documentEnd = _getDocumentEnd().to_win32_coord();
}
CATCH_LOG();
@ -1381,20 +1381,20 @@ const til::point UiaTextRangeBase::_getDocumentEnd() const
// - coords - vector to add the calculated coords to
// Return Value:
// - <none>
void UiaTextRangeBase::_getBoundingRect(const til::rectangle textRect, _Inout_ std::vector<double>& coords) const
void UiaTextRangeBase::_getBoundingRect(const til::rect& textRect, _Inout_ std::vector<double>& coords) const
{
const til::size currentFontSize = _getScreenFontSize();
const til::size currentFontSize{ _getScreenFontSize() };
POINT topLeft{ 0 };
POINT bottomRight{ 0 };
// we want to clamp to a long (output type), not a short (input type)
// so we need to explicitly say <long,long>
topLeft.x = base::ClampMul(textRect.left(), currentFontSize.width());
topLeft.y = base::ClampMul(textRect.top(), currentFontSize.height());
topLeft.x = base::ClampMul(textRect.left, currentFontSize.width);
topLeft.y = base::ClampMul(textRect.top, currentFontSize.height);
bottomRight.x = base::ClampMul(textRect.right(), currentFontSize.width());
bottomRight.y = base::ClampMul(textRect.bottom(), currentFontSize.height());
bottomRight.x = base::ClampMul(textRect.right, currentFontSize.width);
bottomRight.y = base::ClampMul(textRect.bottom, currentFontSize.height);
// convert the coords to be relative to the screen instead of
// the client window
@ -1440,7 +1440,7 @@ void UiaTextRangeBase::_moveEndpointByUnitCharacter(_In_ const int moveCount,
const auto& buffer = _pData->GetTextBuffer();
bool success = true;
til::point target = GetEndpoint(endpoint);
til::point target{ GetEndpoint(endpoint) };
const auto documentEnd{ _getDocumentEnd() };
while (std::abs(*pAmountMoved) < std::abs(moveCount) && success)
{
@ -1465,7 +1465,7 @@ void UiaTextRangeBase::_moveEndpointByUnitCharacter(_In_ const int moveCount,
}
}
SetEndpoint(endpoint, target);
SetEndpoint(endpoint, target.to_win32_coord());
}
// Routine Description:
@ -1510,7 +1510,7 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
{
case MovementDirection::Forward:
{
if (bufferSize.CompareInBounds(nextPos, documentEnd, true) >= 0)
if (bufferSize.CompareInBounds(nextPos, documentEnd.to_win32_coord(), true) >= 0)
{
success = false;
}
@ -1521,7 +1521,7 @@ void UiaTextRangeBase::_moveEndpointByUnitWord(_In_ const int moveCount,
}
else if (allowBottomExclusive)
{
resultPos = documentEnd;
resultPos = documentEnd.to_win32_coord();
(*pAmountMoved)++;
}
else
@ -1615,7 +1615,7 @@ void UiaTextRangeBase::_moveEndpointByUnitLine(_In_ const int moveCount,
auto documentEnd{ bufferSize.EndExclusive() };
try
{
documentEnd = _getDocumentEnd();
documentEnd = _getDocumentEnd().to_win32_coord();
}
CATCH_LOG();
@ -1727,7 +1727,7 @@ void UiaTextRangeBase::_moveEndpointByUnitDocument(_In_ const int moveCount,
auto documentEnd{ bufferSize.EndExclusive() };
try
{
documentEnd = _getDocumentEnd();
documentEnd = _getDocumentEnd().to_win32_coord();
}
CATCH_LOG();

View File

@ -152,7 +152,7 @@ namespace Microsoft::Console::Types
const Viewport _getOptimizedBufferSize() const noexcept;
const til::point _getDocumentEnd() const;
void _getBoundingRect(const til::rectangle textRect, _Inout_ std::vector<double>& coords) const;
void _getBoundingRect(const til::rect& textRect, _Inout_ std::vector<double>& coords) const;
void _expandToEnclosingUnit(TextUnit unit);

View File

@ -85,7 +85,7 @@
<DisplayString>{{X: {_x,d}, Y: {_y,d}}}</DisplayString>
</Type>
<Type Name="til::rectangle">
<Type Name="til::rect">
<DisplayString>{{L: {_topLeft._x}, T: {_topLeft._y}, R: {_bottomRight._x} B: {_bottomRight._y} [W: {_bottomRight._x - _topLeft._x} x H: {_bottomRight._y - _topLeft._y} -> A: {(_bottomRight._x - _topLeft._x) * (_bottomRight._y - _topLeft._y)}]}}</DisplayString>
</Type>

View File

@ -10,7 +10,7 @@
[CmdletBinding()]
Param(
[Parameter(Position=0, ValueFromPipeline=$true)]
[string]$TestPath = "UiaTests.csv"
[string]$TestPath = "$PSScriptRoot/../../tools/TestTableWriter/UiaTests.csv"
)
# 0. Generate a comment telling people to not modify these tests in the .cpp
@ -24,7 +24,7 @@ $result = "// Copyright (c) Microsoft Corporation.
# 1. Define a few helpful variables to make life easier.
$result += "
// Define a few helpful variables
constexpr til::rectangle bufferSize{ 0, 0, 80, 300 };
constexpr til::rect bufferSize{ 0, 0, 80, 300 };
constexpr short midX{ 40 };
constexpr short midY{ 150 };
constexpr short midPopulatedY{ 75 };
@ -77,7 +77,7 @@ foreach ($var in $vars)
# i. Contains "segment" --> define point at the beginning of a text segment
if ($segmentHeuristic)
{
$result += "constexpr til::point {0}{{ {1}, {2}.y() }};" -f $var, $var.Substring(0, 8), $var.Substring(9, $var.Length - $var.IndexOf("L") - 1);
$result += "constexpr til::point {0}{{ {1}, {2}.y }};" -f $var, $var.Substring(0, 8), $var.Substring(9, $var.Length - $var.IndexOf("L") - 1);
}
# ii. Contains number --> requires movement
elseif ($movementHeuristic)
@ -125,7 +125,7 @@ foreach ($var in $vars)
elseif ($leftHeuristic)
{
$standardVar = $var.Split("Left")[0]
$result += "constexpr til::point {0}{{ bufferSize.left(), {1}.y() }};" -f $var, $standardVar;
$result += "constexpr til::point {0}{{ bufferSize.left, {1}.y }};" -f $var, $standardVar;
}
$result += "`n";
}
@ -182,4 +182,4 @@ foreach ($test in $tests)
}
$result += "};`n`n"
$result > "..\..\src\interactivity\win32\ut_interactivity_win32\GeneratedUiaTextRangeMovementTests.g.cpp";
$result > "$PSScriptRoot/../../src/interactivity/win32/ut_interactivity_win32/GeneratedUiaTextRangeMovementTests.g.cpp";