Exemplo n.º 1
0
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));
                    }
                }
            }
        }
    }
}
Exemplo n.º 2
0
// 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;
}