void WriteConvRegionToScreen(const SCREEN_INFORMATION& ScreenInfo, const Viewport& convRegion) { CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); if (!ScreenInfo.IsActiveScreenBuffer()) { return; } ConsoleImeInfo* const pIme = &gci.ConsoleIme; for (unsigned int i = 0; i < pIme->ConvAreaCompStr.size(); ++i) { const auto& ConvAreaInfo = pIme->ConvAreaCompStr[i]; if (!ConvAreaInfo.IsHidden()) { const auto currentViewport = ScreenInfo.GetViewport().ToInclusive(); const auto areaInfo = ConvAreaInfo.GetAreaBufferInfo(); // Do clipping region SMALL_RECT Region; Region.Left = currentViewport.Left + areaInfo.rcViewCaWindow.Left + areaInfo.coordConView.X; Region.Right = Region.Left + (areaInfo.rcViewCaWindow.Right - areaInfo.rcViewCaWindow.Left); Region.Top = currentViewport.Top + areaInfo.rcViewCaWindow.Top + areaInfo.coordConView.Y; Region.Bottom = Region.Top + (areaInfo.rcViewCaWindow.Bottom - areaInfo.rcViewCaWindow.Top); SMALL_RECT ClippedRegion; ClippedRegion.Left = std::max(Region.Left, currentViewport.Left); ClippedRegion.Top = std::max(Region.Top, currentViewport.Top); ClippedRegion.Right = std::min(Region.Right, currentViewport.Right); ClippedRegion.Bottom = std::min(Region.Bottom, currentViewport.Bottom); if (IsValidSmallRect(&ClippedRegion)) { Region = ClippedRegion; ClippedRegion.Left = std::max(Region.Left, convRegion.Left()); ClippedRegion.Top = std::max(Region.Top, convRegion.Top()); ClippedRegion.Right = std::min(Region.Right, convRegion.RightInclusive()); ClippedRegion.Bottom = std::min(Region.Bottom, convRegion.BottomInclusive()); if (IsValidSmallRect(&ClippedRegion)) { // if we have a renderer, we need to update. // we've already confirmed (above with an early return) that we're on conversion areas that are a part of the active (visible/rendered) screen // so send invalidates to those regions such that we're queried for data on the next frame and repainted. if (ServiceLocator::LocateGlobals().pRender != nullptr) { // convert inclusive rectangle to exclusive rectangle SMALL_RECT srExclusive = ClippedRegion; srExclusive.Right++; srExclusive.Bottom++; ServiceLocator::LocateGlobals().pRender->TriggerRedraw(Viewport::FromExclusive(srExclusive)); } } } } } }
// Routine Description: // - Modifies the given selection point to the edge of the next (or previous) word. // - By default operates in a left-to-right fashion. // Arguments: // - fReverse: Specifies that this function should operate in reverse. E.g. Right-to-left. // - bufferSize: The dimensions of the screen buffer. // - coordAnchor: The point within the buffer (inside the edges) where this selection started. // - coordSelPoint: Defines selection region from coordAnchor to this point. Modified to define the new selection region. // Return Value: // - <none> COORD Selection::WordByWordSelection(const bool fReverse, const Viewport& bufferSize, const COORD coordAnchor, const COORD coordSelPoint) const { const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); const SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer(); COORD outCoord = coordSelPoint; // first move one character in the requested direction if (!fReverse) { bufferSize.IncrementInBounds(outCoord); } else { bufferSize.DecrementInBounds(outCoord); } // get the character at the new position auto charData = *screenInfo.GetTextDataAt(outCoord); // we want to go until the state change from delim to non-delim bool fCurrIsDelim = IsWordDelim(charData); bool fPrevIsDelim; // find the edit-line boundaries that we can highlight COORD coordMaxLeft; COORD coordMaxRight; const bool fSuccess = s_GetInputLineBoundaries(&coordMaxLeft, &coordMaxRight); // if line boundaries fail, then set them to the buffer corners so they don't restrict anything. if (!fSuccess) { coordMaxLeft.X = bufferSize.Left(); coordMaxLeft.Y = bufferSize.Top(); coordMaxRight.X = bufferSize.RightInclusive(); coordMaxRight.Y = bufferSize.BottomInclusive(); } // track whether we failed to move during an operation // if we failed to move, we hit the end of the buffer and should just highlight to there and be done. bool fMoveSucceeded = false; // determine if we're highlighting more text or unhighlighting already selected text. bool fUnhighlighting; if (!fReverse) { // if the selection point is left of the anchor, then we're unhighlighting when moving right fUnhighlighting = Utils::s_CompareCoords(outCoord, coordAnchor) < 0; } else { // if the selection point is right of the anchor, then we're unhighlighting when moving left fUnhighlighting = Utils::s_CompareCoords(outCoord, coordAnchor) > 0; } do { // store previous state fPrevIsDelim = fCurrIsDelim; // to make us "sticky" within the edit line, stop moving once we've reached a given max position left/right // users can repeat the command to move past the line and continue word selecting // if we're at the max position left, stop moving if (Utils::s_CompareCoords(outCoord, coordMaxLeft) == 0) { // set move succeeded to false as we can't move any further fMoveSucceeded = false; break; } // if we're at the max position right, stop moving. // we don't want them to "word select" past the end of the edit line as there's likely nothing there. // (thus >= and not == like left) if (Utils::s_CompareCoords(outCoord, coordMaxRight) >= 0) { // set move succeeded to false as we can't move any further. fMoveSucceeded = false; break; } if (!fReverse) { fMoveSucceeded = bufferSize.IncrementInBounds(outCoord); } else { fMoveSucceeded = bufferSize.DecrementInBounds(outCoord); } if (!fMoveSucceeded) { break; } // get the character associated with the new position charData = *screenInfo.GetTextDataAt(outCoord); fCurrIsDelim = IsWordDelim(charData); // This is a bit confusing. // If we're going Left to Right (!fReverse)... // - Then we want to keep going UNTIL (!) we move from a delimiter (fPrevIsDelim) to a normal character (!fCurrIsDelim) // This will then eat up all delimiters after a word and stop once we reach the first letter of the next word. // If we're going Right to Left (fReverse)... // - Then we want to keep going UNTIL (!) we move from a normal character (!fPrevIsDelim) to a delimeter (fCurrIsDelim) // This will eat up all letters of the word and stop once we see the delimiter before the word. } while (!fReverse ? !(fPrevIsDelim && !fCurrIsDelim) : !(!fPrevIsDelim && fCurrIsDelim)); // To stop the loop, we had to move the cursor one too far to figure out that the delta occurred from delimeter to not (or vice versa) // Therefore move back by one character after proceeding through the loop. // EXCEPT: // 1. If we broke out of the loop by reaching the beginning of the buffer, leave it alone. // 2. If we're un-highlighting a region, also leave it alone. // This is an oddity that occurs because our cursor is on a character, not between two characters like most text editors. // We want the current position to be ON the first letter of the word (or the last delimeter after the word) so it stays highlighted. if (fMoveSucceeded && !fUnhighlighting) { if (!fReverse) { bufferSize.DecrementInBounds(outCoord); } else { bufferSize.IncrementInBounds(outCoord); } FAIL_FAST_IF(!fMoveSucceeded); // we should never fail to move forward after having moved backward } return outCoord; }