terminal/src/host/selectionState.cpp
James Holderness bb71179a24
Consolidate the color palette APIs (#11784)
This PR merges the default colors and cursor color into the main color
table, enabling us to simplify the `ConGetSet` and `ITerminalApi`
interfaces, with just two methods required for getting and setting any
form of color palette entry.

The is a follow-up to the color table standardization in #11602, and a
another small step towards de-duplicating `AdaptDispatch` and
`TerminalDispatch` for issue #3849. It should also make it easier to
support color queries (#3718) and a configurable bold color (#5682) in
the future.

On the conhost side, default colors could originally be either indexed
positions in the 16-color table, or separate standalone RGB values. With
the new system, the default colors will always be in the color table, so
we just need to track their index positions.

To make this work, those positions need to be calculated at startup
based on the loaded registry/shortcut settings, and updated when
settings are changed (this is handled in
`CalculateDefaultColorIndices`). But the plus side is that it's now much
easier to lookup the default color values for rendering.

For now the default colors in Windows Terminal use hardcoded positions,
because it doesn't need indexed default colors like conhost. But in the
future I'd like to extend the index handling to both terminals, so we
can eventually support the VT525 indexed color operations.

As for the cursor color, that was previously stored in the `Cursor`
class, which meant that it needed to be copied around in various places
where cursors were being instantiated. Now that it's managed separately
in the color table, a lot of that code is no longer required.

## Validation
Some of the unit test initialization code needed to be updated to setup
the color table and default index values as required for the new system.
There were also some adjustments needed to account for API changes, in
particular for methods that now take index values for the default colors
in place of COLORREFs. But for the most part, the essential behavior of
the tests remains unchanged.

I've also run a variety of manual tests looking at the legacy console
APIs as well as the various VT color sequences, and checking that
everything works as expected when color schemes are changed, both in
Windows Terminal and conhost, and in the latter case with both indexed
colors and RGB values.

Closes #11768
2021-11-23 18:28:55 +00:00

257 lines
8.2 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/viewport.hpp"
using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::Interactivity;
// Routine Description:
// - Determines whether the console is in a selecting state
// Arguments:
// - <none> (gets global state)
// Return Value:
// - True if the console is in a selecting state. False otherwise.
bool Selection::IsInSelectingState() const
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return WI_IsFlagSet(gci.Flags, CONSOLE_SELECTING);
}
// Routine Description:
// - Helps set the global selecting state.
// Arguments:
// - fSelectingOn - Set true to set the global flag on. False to turn the global flag off.
// Return Value:
// - <none>
void Selection::_SetSelectingState(const bool fSelectingOn)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
WI_UpdateFlag(gci.Flags, CONSOLE_SELECTING, fSelectingOn);
}
// Routine Description:
// - Determines whether the console should do selections with the mouse
// a.k.a. "Quick Edit" mode
// Arguments:
// - <none> (gets global state)
// Return Value:
// - True if quick edit mode is enabled. False otherwise.
bool Selection::IsInQuickEditMode() const
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return WI_IsFlagSet(gci.Flags, CONSOLE_QUICK_EDIT_MODE);
}
// Routine Description:
// - Determines whether we are performing a line selection right now
// Arguments:
// - <none>
// Return Value:
// - True if the selection is to be treated line by line. False if it is to be a block.
bool Selection::IsLineSelection() const
{
// if line selection is on and alternate is off -OR-
// if line selection is off and alternate is on...
return (_fLineSelection != _fUseAlternateSelection);
}
// Routine Description:
// - Assures that the alternate selection flag is flipped in line with the requested format.
// If true, we'll align to ensure line selection is used. If false, we'll make sure box selection is used.
// Arguments:
// - fAlignToLineSelect - whether or not to use line selection
// Return Value:
// - <none>
void Selection::_AlignAlternateSelection(const bool fAlignToLineSelect)
{
if (fAlignToLineSelect)
{
// states are opposite when in line selection.
// e.g. Line = true, Alternate = false.
// and Line = false, Alternate = true.
_fUseAlternateSelection = !_fLineSelection;
}
else
{
// states are the same when in box selection.
// e.g. Line = true, Alternate = true.
// and Line = false, Alternate = false.
_fUseAlternateSelection = _fLineSelection;
}
}
// Routine Description:
// - Determines whether the selection area is empty.
// Arguments:
// - <none>
// Return Value:
// - True if the selection variables contain valid selection data. False otherwise.
bool Selection::IsAreaSelected() const
{
return WI_IsFlagSet(_dwSelectionFlags, CONSOLE_SELECTION_NOT_EMPTY);
}
// Routine Description:
// - Determines whether mark mode specifically started this selection.
// Arguments:
// - <none>
// Return Value:
// - True if the selection was started as mark mode. False otherwise.
bool Selection::IsKeyboardMarkSelection() const
{
return WI_IsFlagClear(_dwSelectionFlags, CONSOLE_MOUSE_SELECTION);
}
// Routine Description:
// - Determines whether a mouse event was responsible for initiating this selection.
// - This primarily refers to mouse drag in QuickEdit mode.
// - However, it refers to any non-mark-mode selection, whether the mouse actually started it or not.
// Arguments:
// - <none>
// Return Value:
// - True if the selection is mouse-initiated. False otherwise.
bool Selection::IsMouseInitiatedSelection() const
{
return WI_IsFlagSet(_dwSelectionFlags, CONSOLE_MOUSE_SELECTION);
}
// Routine Description:
// - Determines whether the mouse button is currently being held down
// to extend or otherwise manipulate the selection area.
// Arguments:
// - <none>
// Return Value:
// - True if the mouse button is currently down. False otherwise.
bool Selection::IsMouseButtonDown() const
{
return WI_IsFlagSet(_dwSelectionFlags, CONSOLE_MOUSE_DOWN);
}
void Selection::MouseDown()
{
WI_SetFlag(_dwSelectionFlags, CONSOLE_MOUSE_DOWN);
// We must capture the mouse on button down to ensure we receive messages if
// it comes back up outside the window.
IConsoleWindow* const pWindow = ServiceLocator::LocateConsoleWindow();
if (pWindow != nullptr)
{
pWindow->CaptureMouse();
}
}
void Selection::MouseUp()
{
WI_ClearFlag(_dwSelectionFlags, CONSOLE_MOUSE_DOWN);
IConsoleWindow* const pWindow = ServiceLocator::LocateConsoleWindow();
if (pWindow != nullptr)
{
pWindow->ReleaseMouse();
}
}
// Routine Description:
// - Saves the current cursor position data so it can be manipulated during selection.
// Arguments:
// - textBuffer - text buffer to set cursor data
// Return Value:
// - <none>
void Selection::_SaveCursorData(const Cursor& cursor) noexcept
{
_coordSavedCursorPosition = cursor.GetPosition();
_ulSavedCursorSize = cursor.GetSize();
_fSavedCursorVisible = cursor.IsVisible();
_savedCursorType = cursor.GetType();
}
// Routine Description:
// - Restores the cursor position data that was captured when selection was started.
// Arguments:
// - <none> (Restores global state)
// Return Value:
// - <none>
void Selection::_RestoreDataToCursor(Cursor& cursor) noexcept
{
cursor.SetSize(_ulSavedCursorSize);
cursor.SetIsVisible(_fSavedCursorVisible);
cursor.SetType(_savedCursorType);
cursor.SetIsOn(true);
cursor.SetPosition(_coordSavedCursorPosition);
}
// Routine Description:
// - Gets the current selection anchor position
// Arguments:
// - none
// Return Value:
// - current selection anchor
COORD Selection::GetSelectionAnchor() const noexcept
{
return _coordSelectionAnchor;
}
// Routine Description:
// - Gets the current selection rectangle
// Arguments:
// - none
// Return Value:
// - The rectangle to fill with selection data.
SMALL_RECT Selection::GetSelectionRectangle() const noexcept
{
return _srSelectionRect;
}
// Routine Description:
// - Gets the publicly facing set of selection flags.
// Strips out any internal flags in use.
// Arguments:
// - none
// Return Value:
// - The public selection flags
DWORD Selection::GetPublicSelectionFlags() const noexcept
{
// CONSOLE_SELECTION_VALID is the union (binary OR) of all externally valid flags in wincon.h
return (_dwSelectionFlags & CONSOLE_SELECTION_VALID);
}
// Routine Description:
// - Sets the line selection status.
// If true, we'll use line selection. If false, we'll use traditional box selection.
// Arguments:
// - fLineSelectionOn - whether or not to use line selection
// Return Value:
// - <none>
void Selection::SetLineSelection(const bool fLineSelectionOn)
{
if (_fLineSelection != fLineSelectionOn)
{
// Ensure any existing selections are cleared so the draw state is updated appropriately.
ClearSelection();
_fLineSelection = fLineSelectionOn;
}
}
// Routine Description:
// - checks if the selection can be changed by a mouse drag.
// - this is to allow double-click selection and click-mouse-drag selection to play nice together instead of
// the click-mouse-drag selection overwriting the double-click selection in case the user moves the mouse
// while double-clicking.
// Arguments:
// - mousePosition - current mouse position
// Return Value:
// - true if the selection can be changed by a mouse drag
bool Selection::ShouldAllowMouseDragSelection(const COORD mousePosition) const noexcept
{
const Viewport viewport = Viewport::FromInclusive(_srSelectionRect);
const bool selectionContainsMouse = viewport.IsInBounds(mousePosition);
return _allowMouseDragSelection || !selectionContainsMouse;
}